Skip to content

Commit

Permalink
[MLSXFRM]: Flow based matching of xfrm policy and state
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Venkat Yekkirala authored and David S. Miller committed Sep 22, 2006
1 parent b6340fc commit e0d1caa
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 80 deletions.
106 changes: 90 additions & 16 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <linux/msg.h>
#include <linux/sched.h>
#include <linux/key.h>
#include <linux/xfrm.h>

struct ctl_table;

Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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
*
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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;
}
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions include/net/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 2 additions & 5 deletions net/core/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ struct flow_cache_entry {
u8 dir;
struct flowi key;
u32 genid;
u32 sk_sid;
void *object;
atomic_t *object_ref;
};
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)++;
Expand All @@ -226,7 +223,7 @@ void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
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);
Expand Down
28 changes: 15 additions & 13 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;

Expand Down
12 changes: 10 additions & 2 deletions net/xfrm/xfrm_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Expand All @@ -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;
}
}
Expand All @@ -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);
Expand Down
Loading

0 comments on commit e0d1caa

Please sign in to comment.