diff options
| author | Paul Moore <pmoore@redhat.com> | 2013-09-18 21:52:20 +0400 | 
|---|---|---|
| committer | Paul Moore <pmoore@redhat.com> | 2013-09-18 21:52:20 +0400 | 
| commit | 98f700f317967d45cf60c9843b3c42ce3c286f7c (patch) | |
| tree | 2e68b189ceb954182af56b8f6febe644119b7cd7 /security/selinux/xfrm.c | |
| parent | 6e4664525b1db28f8c4e1130957f70a94c19213e (diff) | |
| parent | 0b4bdb3573a86a88c829b9e4ad702859eb923e7e (diff) | |
| download | linux-98f700f317967d45cf60c9843b3c42ce3c286f7c.tar.xz | |
Merge git://git.infradead.org/users/eparis/selinux
Conflicts:
	security/selinux/hooks.c
Pull Eric's existing SELinux tree as there are a number of patches in
there that are not yet upstream.  There was some minor fixup needed to
resolve a conflict in security/selinux/hooks.c:selinux_set_mnt_opts()
between the labeled NFS patches and Eric's security_fs_use()
simplification patch.
Diffstat (limited to 'security/selinux/xfrm.c')
| -rw-r--r-- | security/selinux/xfrm.c | 453 | 
1 files changed, 202 insertions, 251 deletions
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index d03081886214..425b9f91d755 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -56,7 +56,7 @@  atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);  /* - * Returns true if an LSM/SELinux context + * Returns true if the context is an LSM/SELinux context.   */  static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)  { @@ -66,7 +66,7 @@ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)  }  /* - * Returns true if the xfrm contains a security blob for SELinux + * Returns true if the xfrm contains a security blob for SELinux.   */  static inline int selinux_authorizable_xfrm(struct xfrm_state *x)  { @@ -74,48 +74,111 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)  }  /* - * LSM hook implementation that authorizes that a flow can use - * a xfrm policy rule. + * Allocates a xfrm_sec_state and populates it using the supplied security + * xfrm_user_sec_ctx context.   */ -int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, +				   struct xfrm_user_sec_ctx *uctx)  {  	int rc; -	u32 sel_sid; +	const struct task_security_struct *tsec = current_security(); +	struct xfrm_sec_ctx *ctx = NULL; +	u32 str_len; -	/* Context sid is either set to label or ANY_ASSOC */ -	if (ctx) { -		if (!selinux_authorizable_ctx(ctx)) -			return -EINVAL; - -		sel_sid = ctx->ctx_sid; -	} else -		/* -		 * All flows should be treated as polmatch'ing an -		 * otherwise applicable "non-labeled" policy. This -		 * would prevent inadvertent "leaks". -		 */ -		return 0; +	if (ctxp == NULL || uctx == NULL || +	    uctx->ctx_doi != XFRM_SC_DOI_LSM || +	    uctx->ctx_alg != XFRM_SC_ALG_SELINUX) +		return -EINVAL; -	rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__POLMATCH, -			  NULL); +	str_len = uctx->ctx_len; +	if (str_len >= PAGE_SIZE) +		return -ENOMEM; -	if (rc == -EACCES) -		return -ESRCH; +	ctx = kmalloc(sizeof(*ctx) + str_len + 1, GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; +	ctx->ctx_doi = XFRM_SC_DOI_LSM; +	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; +	ctx->ctx_len = str_len; +	memcpy(ctx->ctx_str, &uctx[1], str_len); +	ctx->ctx_str[str_len] = '\0'; +	rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid); +	if (rc) +		goto err; + +	rc = avc_has_perm(tsec->sid, ctx->ctx_sid, +			  SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); +	if (rc) +		goto err; + +	*ctxp = ctx; +	atomic_inc(&selinux_xfrm_refcount); +	return 0; + +err: +	kfree(ctx);  	return rc;  }  /* + * Free the xfrm_sec_ctx structure. + */ +static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) +{ +	if (!ctx) +		return; + +	atomic_dec(&selinux_xfrm_refcount); +	kfree(ctx); +} + +/* + * Authorize the deletion of a labeled SA or policy rule. + */ +static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) +{ +	const struct task_security_struct *tsec = current_security(); + +	if (!ctx) +		return 0; + +	return avc_has_perm(tsec->sid, ctx->ctx_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, +			    NULL); +} + +/* + * LSM hook implementation that authorizes that a flow can use a xfrm policy + * rule. + */ +int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +{ +	int rc; + +	/* All flows should be treated as polmatch'ing an otherwise applicable +	 * "non-labeled" policy. This would prevent inadvertent "leaks". */ +	if (!ctx) +		return 0; + +	/* Context sid is either set to label or ANY_ASSOC */ +	if (!selinux_authorizable_ctx(ctx)) +		return -EINVAL; + +	rc = avc_has_perm(fl_secid, ctx->ctx_sid, +			  SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); +	return (rc == -EACCES ? -ESRCH : 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, -			const struct flowi *fl) +int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, +				      struct xfrm_policy *xp, +				      const struct flowi *fl)  {  	u32 state_sid; -	int rc;  	if (!xp->security)  		if (x->security) @@ -138,187 +201,80 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *  	if (fl->flowi_secid != state_sid)  		return 0; -	rc = avc_has_perm(fl->flowi_secid, state_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__SENDTO, -			  NULL)? 0:1; - -	/* -	 * We don't need a separate SA Vs. policy polmatch check -	 * since the SA is now of the same label as the flow and -	 * a flow Vs. policy polmatch check had already happened -	 * in selinux_xfrm_policy_lookup() above. -	 */ - -	return rc; +	/* We don't need a separate SA Vs. policy polmatch check since the SA +	 * is now of the same label as the flow and a flow Vs. policy polmatch +	 * check had already happened in selinux_xfrm_policy_lookup() above. */ +	return (avc_has_perm(fl->flowi_secid, state_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, +			    NULL) ? 0 : 1);  }  /*   * LSM hook implementation that checks and/or returns the xfrm sid for the   * incoming packet.   */ -  int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)  { +	u32 sid_session = SECSID_NULL;  	struct sec_path *sp; -	*sid = SECSID_NULL; -  	if (skb == NULL) -		return 0; +		goto out;  	sp = skb->sp;  	if (sp) { -		int i, sid_set = 0; +		int i; -		for (i = sp->len-1; i >= 0; i--) { +		for (i = sp->len - 1; i >= 0; i--) {  			struct xfrm_state *x = sp->xvec[i];  			if (selinux_authorizable_xfrm(x)) {  				struct xfrm_sec_ctx *ctx = x->security; -				if (!sid_set) { -					*sid = ctx->ctx_sid; -					sid_set = 1; - +				if (sid_session == SECSID_NULL) { +					sid_session = ctx->ctx_sid;  					if (!ckall) -						break; -				} else if (*sid != ctx->ctx_sid) +						goto out; +				} else if (sid_session != ctx->ctx_sid) { +					*sid = SECSID_NULL;  					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, u32 sid) -{ -	int rc = 0; -	const struct task_security_struct *tsec = current_security(); -	struct xfrm_sec_ctx *ctx = NULL; -	char *ctx_str = NULL; -	u32 str_len; - -	BUG_ON(uctx && sid); - -	if (!uctx) -		goto not_from_user; - -	if (uctx->ctx_alg != XFRM_SC_ALG_SELINUX) -		return -EINVAL; - -	str_len = uctx->ctx_len; -	if (str_len >= PAGE_SIZE) -		return -ENOMEM; - -	*ctxp = ctx = kmalloc(sizeof(*ctx) + -			      str_len + 1, -			      GFP_KERNEL); - -	if (!ctx) -		return -ENOMEM; - -	ctx->ctx_doi = uctx->ctx_doi; -	ctx->ctx_len = str_len; -	ctx->ctx_alg = uctx->ctx_alg; - -	memcpy(ctx->ctx_str, -	       uctx+1, -	       str_len); -	ctx->ctx_str[str_len] = 0; -	rc = security_context_to_sid(ctx->ctx_str, -				     str_len, -				     &ctx->ctx_sid); - -	if (rc) -		goto out; - -	/* -	 * Does the subject have permission to set security context? -	 */ -	rc = avc_has_perm(tsec->sid, ctx->ctx_sid, -			  SECCLASS_ASSOCIATION, -			  ASSOCIATION__SETCONTEXT, NULL); -	if (rc) -		goto out; - -	return rc; - -not_from_user: -	rc = security_sid_to_context(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 = 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; +	*sid = sid_session; +	return 0;  }  /* - * LSM hook implementation that allocs and transfers uctx spec to - * xfrm_policy. + * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.   */  int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,  			      struct xfrm_user_sec_ctx *uctx)  { -	int err; - -	BUG_ON(!uctx); - -	err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0); -	if (err == 0) -		atomic_inc(&selinux_xfrm_refcount); - -	return err; +	return selinux_xfrm_alloc_user(ctxp, uctx);  } -  /* - * LSM hook implementation that copies security data structure from old to - * new for policy cloning. + * LSM hook implementation that copies security data structure from old to new + * for policy cloning.   */  int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,  			      struct xfrm_sec_ctx **new_ctxp)  {  	struct xfrm_sec_ctx *new_ctx; -	if (old_ctx) { -		new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len, -				  GFP_ATOMIC); -		if (!new_ctx) -			return -ENOMEM; +	if (!old_ctx) +		return 0; + +	new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len, GFP_ATOMIC); +	if (!new_ctx) +		return -ENOMEM; +	memcpy(new_ctx, old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len); +	atomic_inc(&selinux_xfrm_refcount); +	*new_ctxp = new_ctx; -		memcpy(new_ctx, old_ctx, sizeof(*new_ctx)); -		memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len); -		atomic_inc(&selinux_xfrm_refcount); -		*new_ctxp = new_ctx; -	}  	return 0;  } @@ -327,8 +283,7 @@ int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,   */  void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)  { -	atomic_dec(&selinux_xfrm_refcount); -	kfree(ctx); +	selinux_xfrm_free(ctx);  }  /* @@ -336,31 +291,55 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)   */  int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)  { -	const struct task_security_struct *tsec = current_security(); - -	if (!ctx) -		return 0; +	return selinux_xfrm_delete(ctx); +} -	return avc_has_perm(tsec->sid, ctx->ctx_sid, -			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, -			    NULL); +/* + * LSM hook implementation that allocates a xfrm_sec_state, populates it using + * the supplied security context, and assigns it to the xfrm_state. + */ +int selinux_xfrm_state_alloc(struct xfrm_state *x, +			     struct xfrm_user_sec_ctx *uctx) +{ +	return selinux_xfrm_alloc_user(&x->security, uctx);  }  /* - * LSM hook implementation that allocs and transfers sec_ctx spec to - * xfrm_state. + * LSM hook implementation that allocates a xfrm_sec_state and populates based + * on a secid.   */ -int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx, -		u32 secid) +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, +				     struct xfrm_sec_ctx *polsec, u32 secid)  { -	int err; +	int rc; +	struct xfrm_sec_ctx *ctx; +	char *ctx_str = NULL; +	int str_len; + +	if (!polsec) +		return 0; -	BUG_ON(!x); +	if (secid == 0) +		return -EINVAL; -	err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid); -	if (err == 0) -		atomic_inc(&selinux_xfrm_refcount); -	return err; +	rc = security_sid_to_context(secid, &ctx_str, &str_len); +	if (rc) +		return rc; + +	ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); +	if (!ctx) +		return -ENOMEM; + +	ctx->ctx_doi = XFRM_SC_DOI_LSM; +	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; +	ctx->ctx_sid = secid; +	ctx->ctx_len = str_len; +	memcpy(ctx->ctx_str, ctx_str, str_len); +	kfree(ctx_str); + +	x->security = ctx; +	atomic_inc(&selinux_xfrm_refcount); +	return 0;  }  /* @@ -368,24 +347,15 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct   */  void selinux_xfrm_state_free(struct xfrm_state *x)  { -	atomic_dec(&selinux_xfrm_refcount); -	kfree(x->security); +	selinux_xfrm_free(x->security);  } - /* -  * LSM hook implementation that authorizes deletion of labeled SAs. -  */ +/* + * LSM hook implementation that authorizes deletion of labeled SAs. + */  int selinux_xfrm_state_delete(struct xfrm_state *x)  { -	const struct task_security_struct *tsec = current_security(); -	struct xfrm_sec_ctx *ctx = x->security; - -	if (!ctx) -		return 0; - -	return avc_has_perm(tsec->sid, ctx->ctx_sid, -			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, -			    NULL); +	return selinux_xfrm_delete(x->security);  }  /* @@ -395,14 +365,12 @@ 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, -				struct common_audit_data *ad) +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +			      struct common_audit_data *ad)  { -	int i, rc = 0; -	struct sec_path *sp; -	u32 sel_sid = SECINITSID_UNLABELED; - -	sp = skb->sp; +	int i; +	struct sec_path *sp = skb->sp; +	u32 peer_sid = SECINITSID_UNLABELED;  	if (sp) {  		for (i = 0; i < sp->len; i++) { @@ -410,23 +378,17 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,  			if (x && selinux_authorizable_xfrm(x)) {  				struct xfrm_sec_ctx *ctx = x->security; -				sel_sid = ctx->ctx_sid; +				peer_sid = ctx->ctx_sid;  				break;  			}  		}  	} -	/* -	 * This check even when there's no association involved is -	 * intended, according to Trent Jaeger, to make sure a -	 * process can't engage in non-ipsec communication unless -	 * explicitly allowed by policy. -	 */ - -	rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__RECVFROM, ad); - -	return rc; +	/* This check even when there's no association involved is intended, +	 * according to Trent Jaeger, to make sure a process can't engage in +	 * non-IPsec communication unless explicitly allowed by policy. */ +	return avc_has_perm(sk_sid, peer_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);  }  /* @@ -436,49 +398,38 @@ 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   * checked in the selinux_xfrm_state_pol_flow_match hook above.   */ -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -					struct common_audit_data *ad, u8 proto) +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +				struct common_audit_data *ad, u8 proto)  {  	struct dst_entry *dst; -	int rc = 0; - -	dst = skb_dst(skb); - -	if (dst) { -		struct dst_entry *dst_test; - -		for (dst_test = dst; dst_test != NULL; -		     dst_test = dst_test->child) { -			struct xfrm_state *x = dst_test->xfrm; - -			if (x && selinux_authorizable_xfrm(x)) -				goto out; -		} -	}  	switch (proto) {  	case IPPROTO_AH:  	case IPPROTO_ESP:  	case IPPROTO_COMP: -		/* -		 * We should have already seen this packet once before -		 * it underwent xfrm(s). No need to subject it to the -		 * unlabeled check. -		 */ -		goto out; +		/* We should have already seen this packet once before it +		 * underwent xfrm(s). No need to subject it to the unlabeled +		 * check. */ +		return 0;  	default:  		break;  	} -	/* -	 * This check even when there's no association involved is -	 * intended, according to Trent Jaeger, to make sure a -	 * process can't engage in non-ipsec communication unless -	 * explicitly allowed by policy. -	 */ +	dst = skb_dst(skb); +	if (dst) { +		struct dst_entry *iter; -	rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, -			  ASSOCIATION__SENDTO, ad); -out: -	return rc; +		for (iter = dst; iter != NULL; iter = iter->child) { +			struct xfrm_state *x = iter->xfrm; + +			if (x && selinux_authorizable_xfrm(x)) +				return 0; +		} +	} + +	/* This check even when there's no association involved is intended, +	 * according to Trent Jaeger, to make sure a process can't engage in +	 * non-IPsec communication unless explicitly allowed by policy. */ +	return avc_has_perm(sk_sid, SECINITSID_UNLABELED, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);  }  | 
