Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+90 −16
Original line number Diff line number Diff line
@@ -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
+2 −2
Original line number Diff line number Diff line
@@ -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;
+2 −5
Original line number Diff line number Diff line
@@ -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 @@ 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);
+15 −13
Original line number Diff line number Diff line
@@ -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;

+10 −2
Original line number Diff line number Diff line
@@ -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);
Loading