include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / security / selinux / xfrm.c
index c750ef7..fff78d3 100644 (file)
@@ -31,7 +31,6 @@
  *   2. Emulating a reasonable SO_PEERSEC across machines
  *   3. Testing addition of sk_policy's with security context via setsockopt
  */
-#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/security.h>
@@ -39,6 +38,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
+#include <linux/slab.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/skbuff.h>
 #include <net/xfrm.h>
 #include <net/checksum.h>
 #include <net/udp.h>
-#include <asm/semaphore.h>
+#include <asm/atomic.h>
 
 #include "avc.h"
 #include "objsec.h"
 #include "xfrm.h"
 
+/* Labeled XFRM instance counter */
+atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
 
 /*
  * Returns true if an LSM/SELinux context
@@ -75,24 +77,32 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
  * LSM hook implementation that authorizes that a flow can use
  * a xfrm policy rule.
  */
-int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
+int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
 {
-       int rc = 0;
-       u32 sel_sid = SECINITSID_UNLABELED;
-       struct xfrm_sec_ctx *ctx;
+       int rc;
+       u32 sel_sid;
 
        /* Context sid is either set to label or ANY_ASSOC */
-       if ((ctx = xp->security)) {
+       if (ctx) {
                if (!selinux_authorizable_ctx(ctx))
                        return -EINVAL;
 
                sel_sid = ctx->ctx_sid;
-       }
+       } else
+               /*
+                * All flows should be treated as polmatch'ing an
+                * otherwise applicable "non-labeled" policy. This
+                * would prevent inadvertent "leaks".
+                */
+               return 0;
 
        rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
                          ASSOCIATION__POLMATCH,
                          NULL);
 
+       if (rc == -EACCES)
+               return -ESRCH;
+
        return rc;
 }
 
@@ -105,57 +115,46 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *
                        struct flowi *fl)
 {
        u32 state_sid;
-       u32 pol_sid;
-       int err;
+       int rc;
 
-       if (x->security)
-               state_sid = x->security->ctx_sid;
-       else
-               state_sid = SECINITSID_UNLABELED;
-
-       if (xp->security)
-               pol_sid = xp->security->ctx_sid;
+       if (!xp->security)
+               if (x->security)
+                       /* unlabeled policy and labeled SA can't match */
+                       return 0;
+               else
+                       /* unlabeled policy and unlabeled SA match all flows */
+                       return 1;
        else
-               pol_sid = SECINITSID_UNLABELED;
+               if (!x->security)
+                       /* unlabeled SA and labeled policy can't match */
+                       return 0;
+               else
+                       if (!selinux_authorizable_xfrm(x))
+                               /* Not a SELinux-labeled SA */
+                               return 0;
 
-       err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__POLMATCH,
-                         NULL);
+       state_sid = x->security->ctx_sid;
 
-       if (err)
+       if (fl->secid != state_sid)
                return 0;
 
-       return selinux_xfrm_flow_state_match(fl, x);
-}
-
-/*
- * LSM hook implementation that authorizes that a particular outgoing flow
- * can use a given security association.
- */
-
-int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
-{
-       int rc = 0;
-       u32 sel_sid = SECINITSID_UNLABELED;
-       struct xfrm_sec_ctx *ctx;
-
-       /* Context sid is either set to label or ANY_ASSOC */
-       if ((ctx = xfrm->security)) {
-               if (!selinux_authorizable_ctx(ctx))
-                       return 0;
-
-               sel_sid = ctx->ctx_sid;
-       }
-
-       rc = avc_has_perm(fl->secid, sel_sid, SECCLASS_ASSOCIATION,
+       rc = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION,
                          ASSOCIATION__SENDTO,
                          NULL)? 0:1;
 
+       /*
+        * We don't need a separate SA Vs. policy polmatch check
+        * since the SA is now of the same label as the flow and
+        * a flow Vs. policy polmatch check had already happened
+        * in selinux_xfrm_policy_lookup() above.
+        */
+
        return rc;
 }
 
 /*
- * LSM hook implementation that determines the sid for the session.
+ * LSM hook implementation that checks and/or returns the xfrm sid for the
+ * incoming packet.
  */
 
 int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
@@ -182,8 +181,7 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
 
                                        if (!ckall)
                                                break;
-                               }
-                               else if (*sid != ctx->ctx_sid)
+                               } else if (*sid != ctx->ctx_sid)
                                        return -EINVAL;
                        }
                }
@@ -197,44 +195,43 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
  * CTX does not have a meaningful value on input
  */
 static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
-       struct xfrm_user_sec_ctx *uctx, struct xfrm_sec_ctx *pol, u32 sid)
+       struct xfrm_user_sec_ctx *uctx, u32 sid)
 {
        int rc = 0;
-       struct task_security_struct *tsec = current->security;
+       const struct task_security_struct *tsec = current_security();
        struct xfrm_sec_ctx *ctx = NULL;
        char *ctx_str = NULL;
        u32 str_len;
-       u32 ctx_sid;
-
-       BUG_ON(uctx && pol);
 
-       if (pol)
-               goto from_policy;
+       BUG_ON(uctx && sid);
 
-       BUG_ON(!uctx);
+       if (!uctx)
+               goto not_from_user;
 
        if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
                return -EINVAL;
 
-       if (uctx->ctx_len >= PAGE_SIZE)
+       str_len = uctx->ctx_len;
+       if (str_len >= PAGE_SIZE)
                return -ENOMEM;
 
        *ctxp = ctx = kmalloc(sizeof(*ctx) +
-                             uctx->ctx_len,
+                             str_len + 1,
                              GFP_KERNEL);
 
        if (!ctx)
                return -ENOMEM;
 
        ctx->ctx_doi = uctx->ctx_doi;
-       ctx->ctx_len = uctx->ctx_len;
+       ctx->ctx_len = str_len;
        ctx->ctx_alg = uctx->ctx_alg;
 
        memcpy(ctx->ctx_str,
               uctx+1,
-              ctx->ctx_len);
+              str_len);
+       ctx->ctx_str[str_len] = 0;
        rc = security_context_to_sid(ctx->ctx_str,
-                                    ctx->ctx_len,
+                                    str_len,
                                     &ctx->ctx_sid);
 
        if (rc)
@@ -251,13 +248,8 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
 
        return rc;
 
-from_policy:
-       BUG_ON(!pol);
-       rc = security_sid_mls_copy(pol->ctx_sid, sid, &ctx_sid);
-       if (rc)
-               goto out;
-
-       rc = security_sid_to_context(ctx_sid, &ctx_str, &str_len);
+not_from_user:
+       rc = security_sid_to_context(sid, &ctx_str, &str_len);
        if (rc)
                goto out;
 
@@ -270,10 +262,9 @@ from_policy:
                goto out;
        }
 
-
        ctx->ctx_doi = XFRM_SC_DOI_LSM;
        ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
-       ctx->ctx_sid = ctx_sid;
+       ctx->ctx_sid = sid;
        ctx->ctx_len = str_len;
        memcpy(ctx->ctx_str,
               ctx_str,
@@ -293,13 +284,17 @@ out2:
  * LSM hook implementation that allocs and transfers uctx spec to
  * xfrm_policy.
  */
-int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *uctx)
+int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
+                             struct xfrm_user_sec_ctx *uctx)
 {
        int err;
 
-       BUG_ON(!xp);
+       BUG_ON(!uctx);
+
+       err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0);
+       if (err == 0)
+               atomic_inc(&selinux_xfrm_refcount);
 
-       err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, 0);
        return err;
 }
 
@@ -308,49 +303,47 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *
  * LSM hook implementation that copies security data structure from old to
  * new for policy cloning.
  */
-int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
+int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
+                             struct xfrm_sec_ctx **new_ctxp)
 {
-       struct xfrm_sec_ctx *old_ctx, *new_ctx;
-
-       old_ctx = old->security;
+       struct xfrm_sec_ctx *new_ctx;
 
        if (old_ctx) {
-               new_ctx = new->security = kmalloc(sizeof(*new_ctx) +
-                                                 old_ctx->ctx_len,
-                                                 GFP_KERNEL);
-
+               new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len,
+                                 GFP_KERNEL);
                if (!new_ctx)
                        return -ENOMEM;
 
                memcpy(new_ctx, old_ctx, sizeof(*new_ctx));
                memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len);
+               *new_ctxp = new_ctx;
        }
        return 0;
 }
 
 /*
- * LSM hook implementation that frees xfrm_policy security information.
+ * LSM hook implementation that frees xfrm_sec_ctx security information.
  */
-void selinux_xfrm_policy_free(struct xfrm_policy *xp)
+void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
 {
-       struct xfrm_sec_ctx *ctx = xp->security;
-       if (ctx)
-               kfree(ctx);
+       kfree(ctx);
 }
 
 /*
  * LSM hook implementation that authorizes deletion of labeled policies.
  */
-int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
+int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
 {
-       struct task_security_struct *tsec = current->security;
-       struct xfrm_sec_ctx *ctx = xp->security;
+       const struct task_security_struct *tsec = current_security();
        int rc = 0;
 
-       if (ctx)
+       if (ctx) {
                rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
                                  SECCLASS_ASSOCIATION,
                                  ASSOCIATION__SETCONTEXT, NULL);
+               if (rc == 0)
+                       atomic_dec(&selinux_xfrm_refcount);
+       }
 
        return rc;
 }
@@ -360,13 +353,15 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
  * xfrm_state.
  */
 int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
-               struct xfrm_sec_ctx *pol, u32 secid)
+               u32 secid)
 {
        int err;
 
        BUG_ON(!x);
 
-       err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid);
+       err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
+       if (err == 0)
+               atomic_inc(&selinux_xfrm_refcount);
        return err;
 }
 
@@ -376,76 +371,7 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct
 void selinux_xfrm_state_free(struct xfrm_state *x)
 {
        struct xfrm_sec_ctx *ctx = x->security;
-       if (ctx)
-               kfree(ctx);
-}
-
-/*
- * SELinux internal function to retrieve the context of a connected
- * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security
- * association used to connect to the remote socket.
- *
- * Retrieve via getsockopt SO_PEERSEC.
- */
-u32 selinux_socket_getpeer_stream(struct sock *sk)
-{
-       struct dst_entry *dst, *dst_test;
-       u32 peer_sid = SECSID_NULL;
-
-       if (sk->sk_state != TCP_ESTABLISHED)
-               goto out;
-
-       dst = sk_dst_get(sk);
-       if (!dst)
-               goto out;
-
-       for (dst_test = dst; dst_test != 0;
-            dst_test = dst_test->child) {
-               struct xfrm_state *x = dst_test->xfrm;
-
-               if (x && selinux_authorizable_xfrm(x)) {
-                       struct xfrm_sec_ctx *ctx = x->security;
-                       peer_sid = ctx->ctx_sid;
-                       break;
-               }
-       }
-       dst_release(dst);
-
-out:
-       return peer_sid;
-}
-
-/*
- * SELinux internal function to retrieve the context of a UDP packet
- * based on its security association used to connect to the remote socket.
- *
- * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message
- * type SCM_SECURITY.
- */
-u32 selinux_socket_getpeer_dgram(struct sk_buff *skb)
-{
-       struct sec_path *sp;
-
-       if (skb == NULL)
-               return SECSID_NULL;
-
-       if (skb->sk->sk_protocol != IPPROTO_UDP)
-               return SECSID_NULL;
-
-       sp = skb->sp;
-       if (sp) {
-               int i;
-
-               for (i = sp->len-1; i >= 0; i--) {
-                       struct xfrm_state *x = sp->xvec[i];
-                       if (selinux_authorizable_xfrm(x)) {
-                               struct xfrm_sec_ctx *ctx = x->security;
-                               return ctx->ctx_sid;
-                       }
-               }
-       }
-
-       return SECSID_NULL;
+       kfree(ctx);
 }
 
  /*
@@ -453,14 +379,17 @@ u32 selinux_socket_getpeer_dgram(struct sk_buff *skb)
   */
 int selinux_xfrm_state_delete(struct xfrm_state *x)
 {
-       struct task_security_struct *tsec = current->security;
+       const struct task_security_struct *tsec = current_security();
        struct xfrm_sec_ctx *ctx = x->security;
        int rc = 0;
 
-       if (ctx)
+       if (ctx) {
                rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
                                  SECCLASS_ASSOCIATION,
                                  ASSOCIATION__SETCONTEXT, NULL);
+               if (rc == 0)
+                       atomic_dec(&selinux_xfrm_refcount);
+       }
 
        return rc;
 }
@@ -473,7 +402,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
  * gone thru the IPSec process.
  */
 int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
-                               struct avc_audit_data *ad)
+                               struct common_audit_data *ad)
 {
        int i, rc = 0;
        struct sec_path *sp;
@@ -493,6 +422,13 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
                }
        }
 
+       /*
+        * This check even when there's no association involved is
+        * intended, according to Trent Jaeger, to make sure a
+        * process can't engage in non-ipsec communication unless
+        * explicitly allowed by policy.
+        */
+
        rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
                          ASSOCIATION__RECVFROM, ad);
 
@@ -504,20 +440,20 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
  * If we have no security association, then we need to determine
  * whether the socket is allowed to send to an unlabelled destination.
  * If we do have a authorizable security association, then it has already been
- * checked in xfrm_policy_lookup hook.
+ * checked in the selinux_xfrm_state_pol_flow_match hook above.
  */
 int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
-                                       struct avc_audit_data *ad)
+                                       struct common_audit_data *ad, u8 proto)
 {
        struct dst_entry *dst;
        int rc = 0;
 
-       dst = skb->dst;
+       dst = skb_dst(skb);
 
        if (dst) {
                struct dst_entry *dst_test;
 
-               for (dst_test = dst; dst_test != 0;
+               for (dst_test = dst; dst_test != NULL;
                     dst_test = dst_test->child) {
                        struct xfrm_state *x = dst_test->xfrm;
 
@@ -526,6 +462,27 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
                }
        }
 
+       switch (proto) {
+       case IPPROTO_AH:
+       case IPPROTO_ESP:
+       case IPPROTO_COMP:
+               /*
+                * We should have already seen this packet once before
+                * it underwent xfrm(s). No need to subject it to the
+                * unlabeled check.
+                */
+               goto out;
+       default:
+               break;
+       }
+
+       /*
+        * This check even when there's no association involved is
+        * intended, according to Trent Jaeger, to make sure a
+        * process can't engage in non-ipsec communication unless
+        * explicitly allowed by policy.
+        */
+
        rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
                          ASSOCIATION__SENDTO, ad);
 out: