Commit e0d1caa7 authored by Venkat Yekkirala's avatar Venkat Yekkirala Committed by David S. Miller

[MLSXFRM]: Flow based matching of xfrm policy and state

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: default avatarVenkat Yekkirala <vyekkirala@TrustedCS.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b6340fcd
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/msg.h> #include <linux/msg.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/key.h> #include <linux/key.h>
#include <linux/xfrm.h>
struct ctl_table; struct ctl_table;
...@@ -825,9 +826,8 @@ struct swap_info_struct; ...@@ -825,9 +826,8 @@ struct swap_info_struct;
* used by the XFRM system. * used by the XFRM system.
* @sec_ctx contains the security context information being provided by * @sec_ctx contains the security context information being provided by
* the user-level policy update program (e.g., setkey). * the user-level policy update program (e.g., setkey).
* Allocate a security structure to the xp->security field. * Allocate a security structure to the xp->security field; the security
* The security field is initialized to NULL when the xfrm_policy is * field is initialized to NULL when the xfrm_policy is allocated.
* allocated.
* Return 0 if operation was successful (memory to allocate, legal context) * Return 0 if operation was successful (memory to allocate, legal context)
* @xfrm_policy_clone_security: * @xfrm_policy_clone_security:
* @old contains an existing xfrm_policy in the SPD. * @old contains an existing xfrm_policy in the SPD.
...@@ -846,9 +846,14 @@ struct swap_info_struct; ...@@ -846,9 +846,14 @@ struct swap_info_struct;
* Database by the XFRM system. * Database by the XFRM system.
* @sec_ctx contains the security context information being provided by * @sec_ctx contains the security context information being provided by
* the user-level SA generation program (e.g., setkey or racoon). * the user-level SA generation program (e.g., setkey or racoon).
* Allocate a security structure to the x->security field. The * @polsec contains the security context information associated with a xfrm
* security field is initialized to NULL when the xfrm_state is * policy rule from which to take the base context. polsec must be NULL
* allocated. * 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). * Return 0 if operation was successful (memory to allocate, legal context).
* @xfrm_state_free_security: * @xfrm_state_free_security:
* @x contains the xfrm_state. * @x contains the xfrm_state.
...@@ -859,13 +864,26 @@ struct swap_info_struct; ...@@ -859,13 +864,26 @@ struct swap_info_struct;
* @xfrm_policy_lookup: * @xfrm_policy_lookup:
* @xp contains the xfrm_policy for which the access control is being * @xp contains the xfrm_policy for which the access control is being
* checked. * 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. * access to the policy xp.
* @dir contains the direction of the flow (input or output). * @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 * XFRMs on a packet. The hook is called when selecting either a
* per-socket policy or a generic xfrm policy. * per-socket policy or a generic xfrm policy.
* Return 0 if permission is granted. * 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 * Security hooks affecting all Key Management operations
* *
...@@ -1343,10 +1361,16 @@ struct security_operations { ...@@ -1343,10 +1361,16 @@ struct security_operations {
int (*xfrm_policy_clone_security) (struct xfrm_policy *old, struct xfrm_policy *new); int (*xfrm_policy_clone_security) (struct xfrm_policy *old, struct xfrm_policy *new);
void (*xfrm_policy_free_security) (struct xfrm_policy *xp); void (*xfrm_policy_free_security) (struct xfrm_policy *xp);
int (*xfrm_policy_delete_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); void (*xfrm_state_free_security) (struct xfrm_state *x);
int (*xfrm_state_delete_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 */ #endif /* CONFIG_SECURITY_NETWORK_XFRM */
/* key management security hooks */ /* key management security hooks */
...@@ -3050,9 +3074,18 @@ static inline int security_xfrm_policy_delete(struct xfrm_policy *xp) ...@@ -3050,9 +3074,18 @@ static inline int security_xfrm_policy_delete(struct xfrm_policy *xp)
return security_ops->xfrm_policy_delete_security(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) 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) ...@@ -3065,9 +3098,25 @@ static inline void security_xfrm_state_free(struct xfrm_state *x)
security_ops->xfrm_state_free_security(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 */ #else /* CONFIG_SECURITY_NETWORK_XFRM */
static inline int security_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx) 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) ...@@ -3089,7 +3138,14 @@ static inline int security_xfrm_policy_delete(struct xfrm_policy *xp)
return 0; 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; return 0;
} }
...@@ -3103,10 +3159,28 @@ static inline int security_xfrm_state_delete(struct xfrm_state *x) ...@@ -3103,10 +3159,28 @@ static inline int security_xfrm_state_delete(struct xfrm_state *x)
return 0; 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; 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 */ #endif /* CONFIG_SECURITY_NETWORK_XFRM */
#ifdef CONFIG_KEYS #ifdef CONFIG_KEYS
......
...@@ -86,10 +86,10 @@ struct flowi { ...@@ -86,10 +86,10 @@ struct flowi {
#define FLOW_DIR_FWD 2 #define FLOW_DIR_FWD 2
struct sock; 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); 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); flow_resolve_t resolver);
extern void flow_cache_flush(void); extern void flow_cache_flush(void);
extern atomic_t flow_cache_genid; extern atomic_t flow_cache_genid;
......
...@@ -32,7 +32,6 @@ struct flow_cache_entry { ...@@ -32,7 +32,6 @@ struct flow_cache_entry {
u8 dir; u8 dir;
struct flowi key; struct flowi key;
u32 genid; u32 genid;
u32 sk_sid;
void *object; void *object;
atomic_t *object_ref; atomic_t *object_ref;
}; };
...@@ -165,7 +164,7 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2) ...@@ -165,7 +164,7 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2)
return 0; 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) flow_resolve_t resolver)
{ {
struct flow_cache_entry *fle, **head; struct flow_cache_entry *fle, **head;
...@@ -189,7 +188,6 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir, ...@@ -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) { for (fle = *head; fle; fle = fle->next) {
if (fle->family == family && if (fle->family == family &&
fle->dir == dir && fle->dir == dir &&
fle->sk_sid == sk_sid &&
flow_key_compare(key, &fle->key) == 0) { flow_key_compare(key, &fle->key) == 0) {
if (fle->genid == atomic_read(&flow_cache_genid)) { if (fle->genid == atomic_read(&flow_cache_genid)) {
void *ret = fle->object; void *ret = fle->object;
...@@ -214,7 +212,6 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir, ...@@ -214,7 +212,6 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
*head = fle; *head = fle;
fle->family = family; fle->family = family;
fle->dir = dir; fle->dir = dir;
fle->sk_sid = sk_sid;
memcpy(&fle->key, key, sizeof(*key)); memcpy(&fle->key, key, sizeof(*key));
fle->object = NULL; fle->object = NULL;
flow_count(cpu)++; flow_count(cpu)++;
...@@ -226,7 +223,7 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir, ...@@ -226,7 +223,7 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
void *obj; void *obj;
atomic_t *obj_ref; atomic_t *obj_ref;
resolver(key, sk_sid, family, dir, &obj, &obj_ref); resolver(key, family, dir, &obj, &obj_ref);
if (fle) { if (fle) {
fle->genid = atomic_read(&flow_cache_genid); fle->genid = atomic_read(&flow_cache_genid);
......
...@@ -597,7 +597,7 @@ EXPORT_SYMBOL(xfrm_policy_walk); ...@@ -597,7 +597,7 @@ EXPORT_SYMBOL(xfrm_policy_walk);
/* Find policy to apply to this flow. */ /* 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) void **objp, atomic_t **obj_refp)
{ {
struct xfrm_policy *pol; struct xfrm_policy *pol;
...@@ -613,7 +613,7 @@ static void xfrm_policy_lookup(struct flowi *fl, u32 sk_sid, u16 family, u8 dir, ...@@ -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); match = xfrm_selector_match(sel, fl, family);
if (match) { if (match) {
if (!security_xfrm_policy_lookup(pol, sk_sid, dir)) { if (!security_xfrm_policy_lookup(pol, fl->secid, dir)) {
xfrm_pol_hold(pol); xfrm_pol_hold(pol);
break; break;
} }
...@@ -641,7 +641,7 @@ static inline int policy_to_flow_dir(int dir) ...@@ -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; struct xfrm_policy *pol;
...@@ -652,7 +652,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc ...@@ -652,7 +652,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
int err = 0; int err = 0;
if (match) 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) if (match && !err)
xfrm_pol_hold(pol); xfrm_pol_hold(pol);
...@@ -862,19 +862,20 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -862,19 +862,20 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
u32 genid; u32 genid;
u16 family; u16 family;
u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); 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: restart:
genid = atomic_read(&flow_cache_genid); genid = atomic_read(&flow_cache_genid);
policy = NULL; policy = NULL;
if (sk && sk->sk_policy[1]) 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) { if (!policy) {
/* To accelerate a bit... */ /* To accelerate a bit... */
if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT]) if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
return 0; 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); dir, xfrm_policy_lookup);
} }
...@@ -1032,13 +1033,15 @@ int ...@@ -1032,13 +1033,15 @@ int
xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family) xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
{ {
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
int err;
if (unlikely(afinfo == NULL)) if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
afinfo->decode_session(skb, fl); afinfo->decode_session(skb, fl);
err = security_xfrm_decode_session(skb, fl);
xfrm_policy_put_afinfo(afinfo); xfrm_policy_put_afinfo(afinfo);
return 0; return err;
} }
EXPORT_SYMBOL(xfrm_decode_session); EXPORT_SYMBOL(xfrm_decode_session);
...@@ -1058,14 +1061,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -1058,14 +1061,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
struct xfrm_policy *pol; struct xfrm_policy *pol;
struct flowi fl; struct flowi fl;
u8 fl_dir = policy_to_flow_dir(dir); u8 fl_dir = policy_to_flow_dir(dir);
u32 sk_sid;
if (xfrm_decode_session(skb, &fl, family) < 0) if (xfrm_decode_session(skb, &fl, family) < 0)
return 0; return 0;
nf_nat_decode_session(skb, &fl, family); nf_nat_decode_session(skb, &fl, family);
sk_sid = security_sk_sid(sk, &fl, fl_dir);
/* First, check used SA against their selectors. */ /* First, check used SA against their selectors. */
if (skb->sp) { if (skb->sp) {
int i; int i;
...@@ -1079,10 +1079,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -1079,10 +1079,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
pol = NULL; pol = NULL;
if (sk && sk->sk_policy[dir]) 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) if (!pol)
pol = flow_cache_lookup(&fl, sk_sid, family, fl_dir, pol = flow_cache_lookup(&fl, family, fl_dir,
xfrm_policy_lookup); xfrm_policy_lookup);
if (!pol) if (!pol)
...@@ -1298,6 +1298,8 @@ int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family) ...@@ -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)) if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
return 0; return 0;
if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm))
return 0;
if (dst->xfrm->km.state != XFRM_STATE_VALID) if (dst->xfrm->km.state != XFRM_STATE_VALID)
return 0; return 0;
......
...@@ -367,7 +367,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -367,7 +367,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
*/ */
if (x->km.state == XFRM_STATE_VALID) { if (x->km.state == XFRM_STATE_VALID) {
if (!xfrm_selector_match(&x->sel, fl, family) || 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; continue;
if (!best || if (!best ||
best->km.dying > x->km.dying || best->km.dying > x->km.dying ||
...@@ -379,7 +379,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -379,7 +379,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
} else if (x->km.state == XFRM_STATE_ERROR || } else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) { x->km.state == XFRM_STATE_EXPIRED) {
if (xfrm_selector_match(&x->sel, fl, family) && 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; error = -ESRCH;
} }
} }
...@@ -403,6 +403,14 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -403,6 +403,14 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
* to current session. */ * to current session. */
xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); 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) { if (km_query(x, tmpl, pol) == 0) {
x->km.state = XFRM_STATE_ACQ; x->km.state = XFRM_STATE_ACQ;
list_add_tail(&x->bydst, xfrm_state_bydst+h); list_add_tail(&x->bydst, xfrm_state_bydst+h);
......
...@@ -835,7 +835,8 @@ static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp) ...@@ -835,7 +835,8 @@ static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp)
return 0; 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; return 0;
} }
...@@ -853,6 +854,23 @@ static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) ...@@ -853,6 +854,23 @@ static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
{ {
return 0; 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 */ #endif /* CONFIG_SECURITY_NETWORK_XFRM */
static int dummy_register_security (const char *name, struct security_operations *ops) static int dummy_register_security (const char *name, struct security_operations *ops)
{ {
...@@ -1076,6 +1094,9 @@ void security_fixup_ops (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_free_security);
set_to_dummy_if_null(ops, xfrm_state_delete_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_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 */ #endif /* CONFIG_SECURITY_NETWORK_XFRM */
#ifdef CONFIG_KEYS #ifdef CONFIG_KEYS
set_to_dummy_if_null(ops, key_alloc); set_to_dummy_if_null(ops, key_alloc);
......
...@@ -3468,7 +3468,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3468,7 +3468,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err) if (err)
goto out; goto out;
err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); err = selinux_xfrm_sock_rcv_skb(sock_sid, skb, &ad);
out: out:
return err; return err;
} }
...@@ -3720,7 +3720,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ...@@ -3720,7 +3720,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
if (err) if (err)
goto out; goto out;
err = selinux_xfrm_postroute_last(isec->sid, skb); err = selinux_xfrm_postroute_last(isec->sid, skb, &ad);
out: out:
return err ? NF_DROP : NF_ACCEPT; return err ? NF_DROP : NF_ACCEPT;
} }
...@@ -4633,6 +4633,9 @@ static struct security_operations selinux_ops = { ...@@ -4633,6 +4633,9 @@ static struct security_operations selinux_ops = {
.xfrm_state_free_security = selinux_xfrm_state_free, .xfrm_state_free_security = selinux_xfrm_state_free,
.xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_state_delete_security = selinux_xfrm_state_delete,
.xfrm_policy_lookup = selinux_xfrm_policy_lookup, .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 #endif
#ifdef CONFIG_KEYS #ifdef CONFIG_KEYS
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* SELinux support for the XFRM LSM hooks * SELinux support for the XFRM LSM hooks
* *
* Author : Trent Jaeger, <jaegert@us.ibm.com> * Author : Trent Jaeger, <jaegert@us.ibm.com>
* Updated : Venkat Yekkirala, <vyekkirala@TrustedCS.com>
*/ */
#ifndef _SELINUX_XFRM_H_ #ifndef _SELINUX_XFRM_H_
#define _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 * ...@@ -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); int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new);
void selinux_xfrm_policy_free(struct xfrm_policy *xp); void selinux_xfrm_policy_free(struct xfrm_policy *xp);
int selinux_xfrm_policy_delete(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); void selinux_xfrm_state_free(struct xfrm_state *x);
int selinux_xfrm_state_delete(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) * 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) ...@@ -39,17 +46,21 @@ static inline u32 selinux_no_sk_sid(struct flowi *fl)
} }
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb); int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
int selinux_xfrm_postroute_last(u32 isec_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_stream(struct sock *sk);
u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);
#else #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; 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; return 0;
} }
......
...@@ -6,7 +6,12 @@ ...@@ -6,7 +6,12 @@
* Authors: Serge Hallyn <sergeh@us.ibm.com> * Authors: Serge Hallyn <sergeh@us.ibm.com>
* Trent Jaeger <jaegert@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) 2005 International Business Machines Corporation
* Copyright (C) 2006 Trusted Computer Solutions, Inc.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, * 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) ...@@ -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 * LSM hook implementation that authorizes that a flow can use
* with the corresponding xfrm_sec_ctx and direction. * 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; int rc = 0;
u32 sel_sid = SECINITSID_UNLABELED; u32 sel_sid = SECINITSID_UNLABELED;
...@@ -84,27 +89,129 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) ...@@ -84,27 +89,129 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
sel_sid = ctx->ctx_sid; sel_sid = ctx->ctx_sid;
} }
rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION, rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM : ASSOCIATION__POLMATCH,
((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO :
(ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))),
NULL); NULL);
return rc; 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 * Security blob allocation for xfrm_policy and xfrm_state
* CTX does not have a meaningful value on input * 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; int rc = 0;
struct task_security_struct *tsec = current->security; 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);
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) if (uctx->ctx_len >= PAGE_SIZE)
return -ENOMEM; return -ENOMEM;
...@@ -141,9 +248,41 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_us ...@@ -141,9 +248,41 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_us
return rc; 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: out:
*ctxp = NULL; *ctxp = NULL;
kfree(ctx); kfree(ctx);
out2:
kfree(ctx_str);
return rc; return rc;
} }
...@@ -157,7 +296,7 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx * ...@@ -157,7 +296,7 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *
BUG_ON(!xp); 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; return err;
} }
...@@ -217,13 +356,14 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp) ...@@ -217,13 +356,14 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
* LSM hook implementation that allocs and transfers sec_ctx spec to * LSM hook implementation that allocs and transfers sec_ctx spec to
* xfrm_state. * 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; int err;
BUG_ON(!x); 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; return err;
} }
...@@ -329,38 +469,30 @@ int selinux_xfrm_state_delete(struct xfrm_state *x) ...@@ -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 * we need to check for unlabelled access since this may not have
* gone thru the IPSec process. * 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; int i, rc = 0;
struct sec_path *sp; struct sec_path *sp;
u32 sel_sid = SECINITSID_UNLABELED;
sp = skb->sp; sp = skb->sp;
if (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++) { for (i = 0; i < sp->len; i++) {
struct xfrm_state *x = sp->xvec[i]; struct xfrm_state *x = sp->xvec[i];
if (x && selinux_authorizable_xfrm(x)) if (x && selinux_authorizable_xfrm(x)) {
goto accept; 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, sel_sid, SECCLASS_ASSOCIATION,
rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
ASSOCIATION__RECVFROM, NULL);
if (rc)
goto drop;
accept:
return 0;
drop:
return rc; return rc;
} }
...@@ -371,7 +503,8 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) ...@@ -371,7 +503,8 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
* If we do have a authorizable security association, then it has already been * If we do have a authorizable security association, then it has already been
* checked in xfrm_policy_lookup hook. * 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; struct dst_entry *dst;
int rc = 0; int rc = 0;
...@@ -391,7 +524,7 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) ...@@ -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, rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
ASSOCIATION__SENDTO, NULL); ASSOCIATION__SENDTO, ad);
out: out:
return rc; return rc;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment