[MLSXFRM]: Flow based matching of xfrm policy and state
Venkat Yekkirala [Tue, 25 Jul 2006 06:29:07 +0000 (23:29 -0700)]
This implements a seemless mechanism for xfrm policy selection and
state matching based on the flow sid. This also includes the necessary
SELinux enforcement pieces.

Signed-off-by: Venkat Yekkirala <vyekkirala@TrustedCS.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/linux/security.h
include/net/flow.h
net/core/flow.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c
security/dummy.c
security/selinux/hooks.c
security/selinux/include/xfrm.h
security/selinux/xfrm.c

index 4d7fb59..2c4921d 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/msg.h>
 #include <linux/sched.h>
 #include <linux/key.h>
+#include <linux/xfrm.h>
 
 struct ctl_table;
 
@@ -825,9 +826,8 @@ struct swap_info_struct;
  *     used by the XFRM system.
  *     @sec_ctx contains the security context information being provided by
  *     the user-level policy update program (e.g., setkey).
- *     Allocate a security structure to the xp->security field.
- *     The security field is initialized to NULL when the xfrm_policy is
- *     allocated.
+ *     Allocate a security structure to the xp->security field; the security
+ *     field is initialized to NULL when the xfrm_policy is allocated.
  *     Return 0 if operation was successful (memory to allocate, legal context)
  * @xfrm_policy_clone_security:
  *     @old contains an existing xfrm_policy in the SPD.
@@ -846,9 +846,14 @@ struct swap_info_struct;
  *     Database by the XFRM system.
  *     @sec_ctx contains the security context information being provided by
  *     the user-level SA generation program (e.g., setkey or racoon).
- *     Allocate a security structure to the x->security field.  The
- *     security field is initialized to NULL when the xfrm_state is
- *     allocated.
+ *     @polsec contains the security context information associated with a xfrm
+ *     policy rule from which to take the base context. polsec must be NULL
+ *     when sec_ctx is specified.
+ *     @secid contains the secid from which to take the mls portion of the context.
+ *     Allocate a security structure to the x->security field; the security
+ *     field is initialized to NULL when the xfrm_state is allocated. Set the
+ *     context to correspond to either sec_ctx or polsec, with the mls portion
+ *     taken from secid in the latter case.
  *     Return 0 if operation was successful (memory to allocate, legal context).
  * @xfrm_state_free_security:
  *     @x contains the xfrm_state.
@@ -859,13 +864,26 @@ struct swap_info_struct;
  * @xfrm_policy_lookup:
  *     @xp contains the xfrm_policy for which the access control is being
  *     checked.
- *     @sk_sid contains the sock security label that is used to authorize
+ *     @fl_secid contains the flow security label that is used to authorize
  *     access to the policy xp.
  *     @dir contains the direction of the flow (input or output).
- *     Check permission when a sock selects a xfrm_policy for processing
+ *     Check permission when a flow selects a xfrm_policy for processing
  *     XFRMs on a packet.  The hook is called when selecting either a
  *     per-socket policy or a generic xfrm policy.
  *     Return 0 if permission is granted.
+ * @xfrm_state_pol_flow_match:
+ *     @x contains the state to match.
+ *     @xp contains the policy to check for a match.
+ *     @fl contains the flow to check for a match.
+ *     Return 1 if there is a match.
+ * @xfrm_flow_state_match:
+ *     @fl contains the flow key to match.
+ *     @xfrm points to the xfrm_state to match.
+ *     Return 1 if there is a match.
+ * @xfrm_decode_session:
+ *     @skb points to skb to decode.
+ *     @fl points to the flow key to set.
+ *     Return 0 if successful decoding.
  *
  * Security hooks affecting all Key Management operations
  *
@@ -1343,10 +1361,16 @@ struct security_operations {
        int (*xfrm_policy_clone_security) (struct xfrm_policy *old, struct xfrm_policy *new);
        void (*xfrm_policy_free_security) (struct xfrm_policy *xp);
        int (*xfrm_policy_delete_security) (struct xfrm_policy *xp);
-       int (*xfrm_state_alloc_security) (struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx);
+       int (*xfrm_state_alloc_security) (struct xfrm_state *x,
+               struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *polsec,
+               u32 secid);
        void (*xfrm_state_free_security) (struct xfrm_state *x);
        int (*xfrm_state_delete_security) (struct xfrm_state *x);
-       int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 sk_sid, u8 dir);
+       int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
+       int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
+                       struct xfrm_policy *xp, struct flowi *fl);
+       int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm);
+       int (*xfrm_decode_session)(struct sk_buff *skb, struct flowi *fl);
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 
        /* key management security hooks */
@@ -3050,9 +3074,18 @@ static inline int security_xfrm_policy_delete(struct xfrm_policy *xp)
        return security_ops->xfrm_policy_delete_security(xp);
 }
 
-static inline int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+static inline int security_xfrm_state_alloc(struct xfrm_state *x,
+                       struct xfrm_user_sec_ctx *sec_ctx)
+{
+       return security_ops->xfrm_state_alloc_security(x, sec_ctx, NULL, 0);
+}
+
+static inline int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
+                               struct xfrm_sec_ctx *polsec, u32 secid)
 {
-       return security_ops->xfrm_state_alloc_security(x, sec_ctx);
+       if (!polsec)
+               return 0;
+       return security_ops->xfrm_state_alloc_security(x, NULL, polsec, secid);
 }
 
 static inline int security_xfrm_state_delete(struct xfrm_state *x)
@@ -3065,9 +3098,25 @@ static inline void security_xfrm_state_free(struct xfrm_state *x)
        security_ops->xfrm_state_free_security(x);
 }
 
-static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
+{
+       return security_ops->xfrm_policy_lookup(xp, fl_secid, dir);
+}
+
+static inline int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
+                       struct xfrm_policy *xp, struct flowi *fl)
+{
+       return security_ops->xfrm_state_pol_flow_match(x, xp, fl);
+}
+
+static inline int security_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+{
+       return security_ops->xfrm_flow_state_match(fl, xfrm);
+}
+
+static inline int security_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
 {
-       return security_ops->xfrm_policy_lookup(xp, sk_sid, dir);
+       return security_ops->xfrm_decode_session(skb, fl);
 }
 #else  /* CONFIG_SECURITY_NETWORK_XFRM */
 static inline int security_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
@@ -3089,7 +3138,14 @@ static inline int security_xfrm_policy_delete(struct xfrm_policy *xp)
        return 0;
 }
 
-static inline int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+static inline int security_xfrm_state_alloc(struct xfrm_state *x,
+                                       struct xfrm_user_sec_ctx *sec_ctx)
+{
+       return 0;
+}
+
+static inline int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
+                                       struct xfrm_sec_ctx *polsec, u32 secid)
 {
        return 0;
 }
@@ -3103,10 +3159,28 @@ static inline int security_xfrm_state_delete(struct xfrm_state *x)
        return 0;
 }
 
-static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
 {
        return 0;
 }
+
+static inline int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
+                       struct xfrm_policy *xp, struct flowi *fl)
+{
+       return 1;
+}
+
+static inline int security_xfrm_flow_state_match(struct flowi *fl,
+                                struct xfrm_state *xfrm)
+{
+       return 1;
+}
+
+static inline int security_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+{
+       return 0;
+}
+
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 
 #ifdef CONFIG_KEYS
index 1cee5a8..21d988b 100644 (file)
@@ -86,10 +86,10 @@ struct flowi {
 #define FLOW_DIR_FWD   2
 
 struct sock;
-typedef void (*flow_resolve_t)(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
+typedef void (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir,
                               void **objp, atomic_t **obj_refp);
 
-extern void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
+extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
                               flow_resolve_t resolver);
 extern void flow_cache_flush(void);
 extern atomic_t flow_cache_genid;
index 2191af5..6452411 100644 (file)
@@ -32,7 +32,6 @@ struct flow_cache_entry {
        u8                      dir;
        struct flowi            key;
        u32                     genid;
-       u32                     sk_sid;
        void                    *object;
        atomic_t                *object_ref;
 };
@@ -165,7 +164,7 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2)
        return 0;
 }
 
-void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
+void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
                        flow_resolve_t resolver)
 {
        struct flow_cache_entry *fle, **head;
@@ -189,7 +188,6 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
        for (fle = *head; fle; fle = fle->next) {
                if (fle->family == family &&
                    fle->dir == dir &&
-                   fle->sk_sid == sk_sid &&
                    flow_key_compare(key, &fle->key) == 0) {
                        if (fle->genid == atomic_read(&flow_cache_genid)) {
                                void *ret = fle->object;
@@ -214,7 +212,6 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
                        *head = fle;
                        fle->family = family;
                        fle->dir = dir;
-                       fle->sk_sid = sk_sid;
                        memcpy(&fle->key, key, sizeof(*key));
                        fle->object = NULL;
                        flow_count(cpu)++;
@@ -226,7 +223,7 @@ nocache:
                void *obj;
                atomic_t *obj_ref;
 
-               resolver(key, sk_sid, family, dir, &obj, &obj_ref);
+               resolver(key, family, dir, &obj, &obj_ref);
 
                if (fle) {
                        fle->genid = atomic_read(&flow_cache_genid);
index 3da67ca..79405da 100644 (file)
@@ -597,7 +597,7 @@ EXPORT_SYMBOL(xfrm_policy_walk);
 
 /* Find policy to apply to this flow. */
 
-static void xfrm_policy_lookup(struct flowi *fl, u32 sk_sid, u16 family, u8 dir,
+static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
                               void **objp, atomic_t **obj_refp)
 {
        struct xfrm_policy *pol;
@@ -613,7 +613,7 @@ static void xfrm_policy_lookup(struct flowi *fl, u32 sk_sid, u16 family, u8 dir,
                match = xfrm_selector_match(sel, fl, family);
 
                if (match) {
-                       if (!security_xfrm_policy_lookup(pol, sk_sid, dir)) {
+                       if (!security_xfrm_policy_lookup(pol, fl->secid, dir)) {
                                xfrm_pol_hold(pol);
                                break;
                        }
@@ -641,7 +641,7 @@ static inline int policy_to_flow_dir(int dir)
        };
 }
 
-static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl, u32 sk_sid)
+static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl)
 {
        struct xfrm_policy *pol;
 
@@ -652,7 +652,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
                int err = 0;
 
                if (match)
-                 err = security_xfrm_policy_lookup(pol, sk_sid, policy_to_flow_dir(dir));
+                 err = security_xfrm_policy_lookup(pol, fl->secid, policy_to_flow_dir(dir));
 
                if (match && !err)
                        xfrm_pol_hold(pol);
@@ -862,19 +862,20 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
        u32 genid;
        u16 family;
        u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
-       u32 sk_sid = security_sk_sid(sk, fl, dir);
+
+       fl->secid = security_sk_sid(sk, fl, dir);
 restart:
        genid = atomic_read(&flow_cache_genid);
        policy = NULL;
        if (sk && sk->sk_policy[1])
-               policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, sk_sid);
+               policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
 
        if (!policy) {
                /* To accelerate a bit...  */
                if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
                        return 0;
 
-               policy = flow_cache_lookup(fl, sk_sid, dst_orig->ops->family,
+               policy = flow_cache_lookup(fl, dst_orig->ops->family,
                                           dir, xfrm_policy_lookup);
        }
 
@@ -1032,13 +1033,15 @@ int
 xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
 {
        struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       int err;
 
        if (unlikely(afinfo == NULL))
                return -EAFNOSUPPORT;
 
        afinfo->decode_session(skb, fl);
+       err = security_xfrm_decode_session(skb, fl);
        xfrm_policy_put_afinfo(afinfo);
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL(xfrm_decode_session);
 
@@ -1058,14 +1061,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        struct xfrm_policy *pol;
        struct flowi fl;
        u8 fl_dir = policy_to_flow_dir(dir);
-       u32 sk_sid;
 
        if (xfrm_decode_session(skb, &fl, family) < 0)
                return 0;
        nf_nat_decode_session(skb, &fl, family);
 
-       sk_sid = security_sk_sid(sk, &fl, fl_dir);
-
        /* First, check used SA against their selectors. */
        if (skb->sp) {
                int i;
@@ -1079,10 +1079,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
        pol = NULL;
        if (sk && sk->sk_policy[dir])
-               pol = xfrm_sk_policy_lookup(sk, dir, &fl, sk_sid);
+               pol = xfrm_sk_policy_lookup(sk, dir, &fl);
 
        if (!pol)
-               pol = flow_cache_lookup(&fl, sk_sid, family, fl_dir,
+               pol = flow_cache_lookup(&fl, family, fl_dir,
                                        xfrm_policy_lookup);
 
        if (!pol)
@@ -1298,6 +1298,8 @@ int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family)
 
                if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
                        return 0;
+               if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm))
+                       return 0;
                if (dst->xfrm->km.state != XFRM_STATE_VALID)
                        return 0;
 
index 0021aad..be02bd9 100644 (file)
@@ -367,7 +367,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                         */
                        if (x->km.state == XFRM_STATE_VALID) {
                                if (!xfrm_selector_match(&x->sel, fl, family) ||
-                                   !xfrm_sec_ctx_match(pol->security, x->security))
+                                   !security_xfrm_state_pol_flow_match(x, pol, fl))
                                        continue;
                                if (!best ||
                                    best->km.dying > x->km.dying ||
@@ -379,7 +379,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                        } else if (x->km.state == XFRM_STATE_ERROR ||
                                   x->km.state == XFRM_STATE_EXPIRED) {
                                if (xfrm_selector_match(&x->sel, fl, family) &&
-                                   xfrm_sec_ctx_match(pol->security, x->security))
+                                   security_xfrm_state_pol_flow_match(x, pol, fl))
                                        error = -ESRCH;
                        }
                }
@@ -403,6 +403,14 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                 * to current session. */
                xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
 
+               error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
+               if (error) {
+                       x->km.state = XFRM_STATE_DEAD;
+                       xfrm_state_put(x);
+                       x = NULL;
+                       goto out;
+               }
+
                if (km_query(x, tmpl, pol) == 0) {
                        x->km.state = XFRM_STATE_ACQ;
                        list_add_tail(&x->bydst, xfrm_state_bydst+h);
index bd3bc5f..c1f1065 100644 (file)
@@ -835,7 +835,8 @@ static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp)
        return 0;
 }
 
-static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+static int dummy_xfrm_state_alloc_security(struct xfrm_state *x,
+       struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid)
 {
        return 0;
 }
@@ -853,6 +854,23 @@ static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
 {
        return 0;
 }
+
+static int dummy_xfrm_state_pol_flow_match(struct xfrm_state *x,
+                               struct xfrm_policy *xp, struct flowi *fl)
+{
+       return 1;
+}
+
+static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+{
+       return 1;
+}
+
+static int dummy_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+{
+       return 0;
+}
+
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 static int dummy_register_security (const char *name, struct security_operations *ops)
 {
@@ -1076,6 +1094,9 @@ void security_fixup_ops (struct security_operations *ops)
        set_to_dummy_if_null(ops, xfrm_state_free_security);
        set_to_dummy_if_null(ops, xfrm_state_delete_security);
        set_to_dummy_if_null(ops, xfrm_policy_lookup);
+       set_to_dummy_if_null(ops, xfrm_state_pol_flow_match);
+       set_to_dummy_if_null(ops, xfrm_flow_state_match);
+       set_to_dummy_if_null(ops, xfrm_decode_session);
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 #ifdef CONFIG_KEYS
        set_to_dummy_if_null(ops, key_alloc);
index d67abf7..5c189da 100644 (file)
@@ -3468,7 +3468,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (err)
                goto out;
 
-       err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
+       err = selinux_xfrm_sock_rcv_skb(sock_sid, skb, &ad);
 out:   
        return err;
 }
@@ -3720,7 +3720,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        if (err)
                goto out;
 
-       err = selinux_xfrm_postroute_last(isec->sid, skb);
+       err = selinux_xfrm_postroute_last(isec->sid, skb, &ad);
 out:
        return err ? NF_DROP : NF_ACCEPT;
 }
@@ -4633,6 +4633,9 @@ static struct security_operations selinux_ops = {
        .xfrm_state_free_security =     selinux_xfrm_state_free,
        .xfrm_state_delete_security =   selinux_xfrm_state_delete,
        .xfrm_policy_lookup =           selinux_xfrm_policy_lookup,
+       .xfrm_state_pol_flow_match =    selinux_xfrm_state_pol_flow_match,
+       .xfrm_flow_state_match =        selinux_xfrm_flow_state_match,
+       .xfrm_decode_session =          selinux_xfrm_decode_session,
 #endif
 
 #ifdef CONFIG_KEYS
index c96498a..f51a3e8 100644 (file)
@@ -2,6 +2,7 @@
  * SELinux support for the XFRM LSM hooks
  *
  * Author : Trent Jaeger, <jaegert@us.ibm.com>
+ * Updated : Venkat Yekkirala, <vyekkirala@TrustedCS.com>
  */
 #ifndef _SELINUX_XFRM_H_
 #define _SELINUX_XFRM_H_
@@ -10,10 +11,16 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *
 int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new);
 void selinux_xfrm_policy_free(struct xfrm_policy *xp);
 int selinux_xfrm_policy_delete(struct xfrm_policy *xp);
-int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx);
+int selinux_xfrm_state_alloc(struct xfrm_state *x,
+       struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid);
 void selinux_xfrm_state_free(struct xfrm_state *x);
 int selinux_xfrm_state_delete(struct xfrm_state *x);
-int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir);
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
+                       struct xfrm_policy *xp, struct flowi *fl);
+int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm);
+int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl);
+
 
 /*
  * Extract the security blob from the sock (it's actually on the socket)
@@ -39,17 +46,21 @@ static inline u32 selinux_no_sk_sid(struct flowi *fl)
 }
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
-int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb);
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb);
+int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
+                       struct avc_audit_data *ad);
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+                       struct avc_audit_data *ad);
 u32 selinux_socket_getpeer_stream(struct sock *sk);
 u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);
 #else
-static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
+                       struct avc_audit_data *ad)
 {
        return 0;
 }
 
-static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+                       struct avc_audit_data *ad)
 {
        return 0;
 }
index 6c985ce..a502b05 100644 (file)
@@ -6,7 +6,12 @@
  *  Authors:  Serge Hallyn <sergeh@us.ibm.com>
  *           Trent Jaeger <jaegert@us.ibm.com>
  *
+ *  Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
+ *
+ *           Granular IPSec Associations for use in MLS environments.
+ *
  *  Copyright (C) 2005 International Business Machines Corporation
+ *  Copyright (C) 2006 Trusted Computer Solutions, Inc.
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
@@ -67,10 +72,10 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
 }
 
 /*
- * LSM hook implementation that authorizes that a socket can be used
- * with the corresponding xfrm_sec_ctx and direction.
+ * LSM hook implementation that authorizes that a flow can use
+ * a xfrm policy rule.
  */
-int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
 {
        int rc = 0;
        u32 sel_sid = SECINITSID_UNLABELED;
@@ -84,27 +89,129 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
                sel_sid = ctx->ctx_sid;
        }
 
-       rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION,
-                         ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM :
-                          ((dir == FLOW_DIR_OUT) ?  ASSOCIATION__SENDTO :
-                           (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))),
+       rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
+                         ASSOCIATION__POLMATCH,
                          NULL);
 
        return rc;
 }
 
 /*
+ * LSM hook implementation that authorizes that a state matches
+ * the given policy, flow combo.
+ */
+
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
+                       struct flowi *fl)
+{
+       u32 state_sid;
+       u32 pol_sid;
+       int err;
+
+       if (x->security)
+               state_sid = x->security->ctx_sid;
+       else
+               state_sid = SECINITSID_UNLABELED;
+
+       if (xp->security)
+               pol_sid = xp->security->ctx_sid;
+       else
+               pol_sid = SECINITSID_UNLABELED;
+
+       err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION,
+                         ASSOCIATION__POLMATCH,
+                         NULL);
+
+       if (err)
+               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,
+                         ASSOCIATION__SENDTO,
+                         NULL)? 0:1;
+
+       return rc;
+}
+
+/*
+ * LSM hook implementation that determines the sid for the session.
+ */
+
+int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+{
+       struct sec_path *sp;
+
+       fl->secid = SECSID_NULL;
+
+       if (skb == NULL)
+               return 0;
+
+       sp = skb->sp;
+       if (sp) {
+               int i, sid_set = 0;
+
+               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;
+
+                               if (!sid_set) {
+                                       fl->secid = ctx->ctx_sid;
+                                       sid_set = 1;
+                               }
+                               else if (fl->secid != ctx->ctx_sid)
+                                       return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
  * Security blob allocation for xfrm_policy and xfrm_state
  * 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)
+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)
 {
        int rc = 0;
        struct task_security_struct *tsec = current->security;
-       struct xfrm_sec_ctx *ctx;
+       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);
-       BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX);
+
+       if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
+               return -EINVAL;
 
        if (uctx->ctx_len >= PAGE_SIZE)
                return -ENOMEM;
@@ -141,9 +248,41 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_us
 
        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);
+       if (rc)
+               goto out;
+
+       *ctxp = ctx = kmalloc(sizeof(*ctx) +
+                             str_len,
+                             GFP_ATOMIC);
+
+       if (!ctx) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+
+       ctx->ctx_doi = XFRM_SC_DOI_LSM;
+       ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
+       ctx->ctx_sid = ctx_sid;
+       ctx->ctx_len = str_len;
+       memcpy(ctx->ctx_str,
+              ctx_str,
+              str_len);
+
+       goto out2;
+
 out:
        *ctxp = NULL;
        kfree(ctx);
+out2:
+       kfree(ctx_str);
        return rc;
 }
 
@@ -157,7 +296,7 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *
 
        BUG_ON(!xp);
 
-       err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx);
+       err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, 0);
        return err;
 }
 
@@ -217,13 +356,14 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
  * LSM hook implementation that allocs and transfers sec_ctx spec to
  * xfrm_state.
  */
-int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx)
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
+               struct xfrm_sec_ctx *pol, u32 secid)
 {
        int err;
 
        BUG_ON(!x);
 
-       err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx);
+       err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid);
        return err;
 }
 
@@ -329,38 +469,30 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
  * we need to check for unlabelled access since this may not have
  * gone thru the IPSec process.
  */
-int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
+                               struct avc_audit_data *ad)
 {
        int i, rc = 0;
        struct sec_path *sp;
+       u32 sel_sid = SECINITSID_UNLABELED;
 
        sp = skb->sp;
 
        if (sp) {
-               /*
-                * __xfrm_policy_check does not approve unless xfrm_policy_ok
-                * says that spi's match for policy and the socket.
-                *
-                *  Only need to verify the existence of an authorizable sp.
-                */
                for (i = 0; i < sp->len; i++) {
                        struct xfrm_state *x = sp->xvec[i];
 
-                       if (x && selinux_authorizable_xfrm(x))
-                               goto accept;
+                       if (x && selinux_authorizable_xfrm(x)) {
+                               struct xfrm_sec_ctx *ctx = x->security;
+                               sel_sid = ctx->ctx_sid;
+                               break;
+                       }
                }
        }
 
-       /* check SELinux sock for unlabelled access */
-       rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__RECVFROM, NULL);
-       if (rc)
-               goto drop;
-
-accept:
-       return 0;
+       rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
+                         ASSOCIATION__RECVFROM, ad);
 
-drop:
        return rc;
 }
 
@@ -371,7 +503,8 @@ drop:
  * If we do have a authorizable security association, then it has already been
  * checked in xfrm_policy_lookup hook.
  */
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+                                       struct avc_audit_data *ad)
 {
        struct dst_entry *dst;
        int rc = 0;
@@ -391,7 +524,7 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
        }
 
        rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
-                         ASSOCIATION__SENDTO, NULL);
+                         ASSOCIATION__SENDTO, ad);
 out:
        return rc;
 }