xref: /openbmc/linux/security/selinux/xfrm.c (revision 90aa4f5e)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d28d1e08STrent Jaeger /*
3*90aa4f5eSStephen Smalley  *  Security-Enhanced Linux (SELinux) security module
4d28d1e08STrent Jaeger  *
5d28d1e08STrent Jaeger  *  This file contains the SELinux XFRM hook function implementations.
6d28d1e08STrent Jaeger  *
7d28d1e08STrent Jaeger  *  Authors:  Serge Hallyn <sergeh@us.ibm.com>
8d28d1e08STrent Jaeger  *	      Trent Jaeger <jaegert@us.ibm.com>
9d28d1e08STrent Jaeger  *
10e0d1caa7SVenkat Yekkirala  *  Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
11e0d1caa7SVenkat Yekkirala  *
12e0d1caa7SVenkat Yekkirala  *           Granular IPSec Associations for use in MLS environments.
13e0d1caa7SVenkat Yekkirala  *
14d28d1e08STrent Jaeger  *  Copyright (C) 2005 International Business Machines Corporation
15e0d1caa7SVenkat Yekkirala  *  Copyright (C) 2006 Trusted Computer Solutions, Inc.
16d28d1e08STrent Jaeger  */
17d28d1e08STrent Jaeger 
18d28d1e08STrent Jaeger /*
19d28d1e08STrent Jaeger  * USAGE:
20d28d1e08STrent Jaeger  * NOTES:
21d28d1e08STrent Jaeger  *   1. Make sure to enable the following options in your kernel config:
22d28d1e08STrent Jaeger  *	CONFIG_SECURITY=y
23d28d1e08STrent Jaeger  *	CONFIG_SECURITY_NETWORK=y
24d28d1e08STrent Jaeger  *	CONFIG_SECURITY_NETWORK_XFRM=y
25d28d1e08STrent Jaeger  *	CONFIG_SECURITY_SELINUX=m/y
26d28d1e08STrent Jaeger  * ISSUES:
27d28d1e08STrent Jaeger  *   1. Caching packets, so they are not dropped during negotiation
28d28d1e08STrent Jaeger  *   2. Emulating a reasonable SO_PEERSEC across machines
29d28d1e08STrent Jaeger  *   3. Testing addition of sk_policy's with security context via setsockopt
30d28d1e08STrent Jaeger  */
31d28d1e08STrent Jaeger #include <linux/kernel.h>
32d28d1e08STrent Jaeger #include <linux/init.h>
33d28d1e08STrent Jaeger #include <linux/security.h>
34d28d1e08STrent Jaeger #include <linux/types.h>
355a0e3ad6STejun Heo #include <linux/slab.h>
36d28d1e08STrent Jaeger #include <linux/ip.h>
37d28d1e08STrent Jaeger #include <linux/tcp.h>
38d28d1e08STrent Jaeger #include <linux/skbuff.h>
39d28d1e08STrent Jaeger #include <linux/xfrm.h>
40d28d1e08STrent Jaeger #include <net/xfrm.h>
41d28d1e08STrent Jaeger #include <net/checksum.h>
42d28d1e08STrent Jaeger #include <net/udp.h>
4360063497SArun Sharma #include <linux/atomic.h>
44d28d1e08STrent Jaeger 
45d28d1e08STrent Jaeger #include "avc.h"
46d28d1e08STrent Jaeger #include "objsec.h"
47d28d1e08STrent Jaeger #include "xfrm.h"
48d28d1e08STrent Jaeger 
49d621d35eSPaul Moore /* Labeled XFRM instance counter */
50e0de8a9aSOndrej Mosnacek atomic_t selinux_xfrm_refcount __read_mostly = ATOMIC_INIT(0);
51d28d1e08STrent Jaeger 
52d28d1e08STrent Jaeger /*
534baabeecSPaul Moore  * Returns true if the context is an LSM/SELinux context.
54d28d1e08STrent Jaeger  */
selinux_authorizable_ctx(struct xfrm_sec_ctx * ctx)55d28d1e08STrent Jaeger static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
56d28d1e08STrent Jaeger {
57d28d1e08STrent Jaeger 	return (ctx &&
58d28d1e08STrent Jaeger 		(ctx->ctx_doi == XFRM_SC_DOI_LSM) &&
59d28d1e08STrent Jaeger 		(ctx->ctx_alg == XFRM_SC_ALG_SELINUX));
60d28d1e08STrent Jaeger }
61d28d1e08STrent Jaeger 
62d28d1e08STrent Jaeger /*
634baabeecSPaul Moore  * Returns true if the xfrm contains a security blob for SELinux.
64d28d1e08STrent Jaeger  */
selinux_authorizable_xfrm(struct xfrm_state * x)65d28d1e08STrent Jaeger static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
66d28d1e08STrent Jaeger {
67d28d1e08STrent Jaeger 	return selinux_authorizable_ctx(x->security);
68d28d1e08STrent Jaeger }
69d28d1e08STrent Jaeger 
70d28d1e08STrent Jaeger /*
712e5aa866SPaul Moore  * Allocates a xfrm_sec_state and populates it using the supplied security
722e5aa866SPaul Moore  * xfrm_user_sec_ctx context.
732e5aa866SPaul Moore  */
selinux_xfrm_alloc_user(struct xfrm_sec_ctx ** ctxp,struct xfrm_user_sec_ctx * uctx,gfp_t gfp)742e5aa866SPaul Moore static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
7552a4c640SNikolay Aleksandrov 				   struct xfrm_user_sec_ctx *uctx,
7652a4c640SNikolay Aleksandrov 				   gfp_t gfp)
772e5aa866SPaul Moore {
782e5aa866SPaul Moore 	int rc;
790c6cfa62SCasey Schaufler 	const struct task_security_struct *tsec = selinux_cred(current_cred());
802e5aa866SPaul Moore 	struct xfrm_sec_ctx *ctx = NULL;
812e5aa866SPaul Moore 	u32 str_len;
822e5aa866SPaul Moore 
832e5aa866SPaul Moore 	if (ctxp == NULL || uctx == NULL ||
842e5aa866SPaul Moore 	    uctx->ctx_doi != XFRM_SC_DOI_LSM ||
852e5aa866SPaul Moore 	    uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
862e5aa866SPaul Moore 		return -EINVAL;
872e5aa866SPaul Moore 
882e5aa866SPaul Moore 	str_len = uctx->ctx_len;
892e5aa866SPaul Moore 	if (str_len >= PAGE_SIZE)
902e5aa866SPaul Moore 		return -ENOMEM;
912e5aa866SPaul Moore 
925fe37572SXiu Jianfeng 	ctx = kmalloc(struct_size(ctx, ctx_str, str_len + 1), gfp);
932e5aa866SPaul Moore 	if (!ctx)
942e5aa866SPaul Moore 		return -ENOMEM;
952e5aa866SPaul Moore 
962e5aa866SPaul Moore 	ctx->ctx_doi = XFRM_SC_DOI_LSM;
972e5aa866SPaul Moore 	ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
982e5aa866SPaul Moore 	ctx->ctx_len = str_len;
992e5aa866SPaul Moore 	memcpy(ctx->ctx_str, &uctx[1], str_len);
1002e5aa866SPaul Moore 	ctx->ctx_str[str_len] = '\0';
101e67b7985SStephen Smalley 	rc = security_context_to_sid(ctx->ctx_str, str_len,
102aa8e712cSStephen Smalley 				     &ctx->ctx_sid, gfp);
1032e5aa866SPaul Moore 	if (rc)
1042e5aa866SPaul Moore 		goto err;
1052e5aa866SPaul Moore 
106e67b7985SStephen Smalley 	rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
1072e5aa866SPaul Moore 			  SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
1082e5aa866SPaul Moore 	if (rc)
1092e5aa866SPaul Moore 		goto err;
1102e5aa866SPaul Moore 
1112e5aa866SPaul Moore 	*ctxp = ctx;
1122e5aa866SPaul Moore 	atomic_inc(&selinux_xfrm_refcount);
1132e5aa866SPaul Moore 	return 0;
1142e5aa866SPaul Moore 
1152e5aa866SPaul Moore err:
1162e5aa866SPaul Moore 	kfree(ctx);
1172e5aa866SPaul Moore 	return rc;
1182e5aa866SPaul Moore }
1192e5aa866SPaul Moore 
1202e5aa866SPaul Moore /*
121ccf17cc4SPaul Moore  * Free the xfrm_sec_ctx structure.
122ccf17cc4SPaul Moore  */
selinux_xfrm_free(struct xfrm_sec_ctx * ctx)123ccf17cc4SPaul Moore static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
124ccf17cc4SPaul Moore {
125ccf17cc4SPaul Moore 	if (!ctx)
126ccf17cc4SPaul Moore 		return;
127ccf17cc4SPaul Moore 
128ccf17cc4SPaul Moore 	atomic_dec(&selinux_xfrm_refcount);
129ccf17cc4SPaul Moore 	kfree(ctx);
130ccf17cc4SPaul Moore }
131ccf17cc4SPaul Moore 
132ccf17cc4SPaul Moore /*
133ccf17cc4SPaul Moore  * Authorize the deletion of a labeled SA or policy rule.
134ccf17cc4SPaul Moore  */
selinux_xfrm_delete(struct xfrm_sec_ctx * ctx)135ccf17cc4SPaul Moore static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
136ccf17cc4SPaul Moore {
1370c6cfa62SCasey Schaufler 	const struct task_security_struct *tsec = selinux_cred(current_cred());
138ccf17cc4SPaul Moore 
139ccf17cc4SPaul Moore 	if (!ctx)
140ccf17cc4SPaul Moore 		return 0;
141ccf17cc4SPaul Moore 
142e67b7985SStephen Smalley 	return avc_has_perm(tsec->sid, ctx->ctx_sid,
143ccf17cc4SPaul Moore 			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
144ccf17cc4SPaul Moore 			    NULL);
145ccf17cc4SPaul Moore }
146ccf17cc4SPaul Moore 
147ccf17cc4SPaul Moore /*
1484baabeecSPaul Moore  * LSM hook implementation that authorizes that a flow can use a xfrm policy
1494baabeecSPaul Moore  * rule.
150d28d1e08STrent Jaeger  */
selinux_xfrm_policy_lookup(struct xfrm_sec_ctx * ctx,u32 fl_secid)1518a922805SZhongjun Tan int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid)
152d28d1e08STrent Jaeger {
1535b368e61SVenkat Yekkirala 	int rc;
15496484348SPaul Moore 
15596484348SPaul Moore 	/* All flows should be treated as polmatch'ing an otherwise applicable
15696484348SPaul Moore 	 * "non-labeled" policy. This would prevent inadvertent "leaks". */
15796484348SPaul Moore 	if (!ctx)
15896484348SPaul Moore 		return 0;
159d28d1e08STrent Jaeger 
160d28d1e08STrent Jaeger 	/* Context sid is either set to label or ANY_ASSOC */
161d28d1e08STrent Jaeger 	if (!selinux_authorizable_ctx(ctx))
162d28d1e08STrent Jaeger 		return -EINVAL;
163d28d1e08STrent Jaeger 
164e67b7985SStephen Smalley 	rc = avc_has_perm(fl_secid, ctx->ctx_sid,
16596484348SPaul Moore 			  SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL);
16696484348SPaul Moore 	return (rc == -EACCES ? -ESRCH : rc);
167d28d1e08STrent Jaeger }
168d28d1e08STrent Jaeger 
169d28d1e08STrent Jaeger /*
170e0d1caa7SVenkat Yekkirala  * LSM hook implementation that authorizes that a state matches
171e0d1caa7SVenkat Yekkirala  * the given policy, flow combo.
172e0d1caa7SVenkat Yekkirala  */
selinux_xfrm_state_pol_flow_match(struct xfrm_state * x,struct xfrm_policy * xp,const struct flowi_common * flic)17396484348SPaul Moore int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
17496484348SPaul Moore 				      struct xfrm_policy *xp,
1753df98d79SPaul Moore 				      const struct flowi_common *flic)
176e0d1caa7SVenkat Yekkirala {
177e0d1caa7SVenkat Yekkirala 	u32 state_sid;
1783df98d79SPaul Moore 	u32 flic_sid;
179e0d1caa7SVenkat Yekkirala 
18067f83cbfSVenkat Yekkirala 	if (!xp->security)
1815b368e61SVenkat Yekkirala 		if (x->security)
1825b368e61SVenkat Yekkirala 			/* unlabeled policy and labeled SA can't match */
1835b368e61SVenkat Yekkirala 			return 0;
184e0d1caa7SVenkat Yekkirala 		else
1855b368e61SVenkat Yekkirala 			/* unlabeled policy and unlabeled SA match all flows */
1865b368e61SVenkat Yekkirala 			return 1;
18767f83cbfSVenkat Yekkirala 	else
18867f83cbfSVenkat Yekkirala 		if (!x->security)
18967f83cbfSVenkat Yekkirala 			/* unlabeled SA and labeled policy can't match */
19067f83cbfSVenkat Yekkirala 			return 0;
19167f83cbfSVenkat Yekkirala 		else
19267f83cbfSVenkat Yekkirala 			if (!selinux_authorizable_xfrm(x))
19367f83cbfSVenkat Yekkirala 				/* Not a SELinux-labeled SA */
194e0d1caa7SVenkat Yekkirala 				return 0;
195e0d1caa7SVenkat Yekkirala 
19667f83cbfSVenkat Yekkirala 	state_sid = x->security->ctx_sid;
1973df98d79SPaul Moore 	flic_sid = flic->flowic_secid;
19867f83cbfSVenkat Yekkirala 
1993df98d79SPaul Moore 	if (flic_sid != state_sid)
20067f83cbfSVenkat Yekkirala 		return 0;
20167f83cbfSVenkat Yekkirala 
20296484348SPaul Moore 	/* We don't need a separate SA Vs. policy polmatch check since the SA
20396484348SPaul Moore 	 * is now of the same label as the flow and a flow Vs. policy polmatch
20496484348SPaul Moore 	 * check had already happened in selinux_xfrm_policy_lookup() above. */
205e67b7985SStephen Smalley 	return (avc_has_perm(flic_sid, state_sid,
20696484348SPaul Moore 			     SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
20796484348SPaul Moore 			     NULL) ? 0 : 1);
208e0d1caa7SVenkat Yekkirala }
209e0d1caa7SVenkat Yekkirala 
selinux_xfrm_skb_sid_egress(struct sk_buff * skb)210817eff71SPaul Moore static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)
211817eff71SPaul Moore {
212817eff71SPaul Moore 	struct dst_entry *dst = skb_dst(skb);
213817eff71SPaul Moore 	struct xfrm_state *x;
214817eff71SPaul Moore 
215817eff71SPaul Moore 	if (dst == NULL)
216817eff71SPaul Moore 		return SECSID_NULL;
217817eff71SPaul Moore 	x = dst->xfrm;
218817eff71SPaul Moore 	if (x == NULL || !selinux_authorizable_xfrm(x))
219817eff71SPaul Moore 		return SECSID_NULL;
220817eff71SPaul Moore 
221817eff71SPaul Moore 	return x->security->ctx_sid;
222817eff71SPaul Moore }
223817eff71SPaul Moore 
selinux_xfrm_skb_sid_ingress(struct sk_buff * skb,u32 * sid,int ckall)224817eff71SPaul Moore static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb,
225817eff71SPaul Moore 					u32 *sid, int ckall)
226e0d1caa7SVenkat Yekkirala {
227e2193695SPaul Moore 	u32 sid_session = SECSID_NULL;
2282294be0fSFlorian Westphal 	struct sec_path *sp = skb_sec_path(skb);
229e0d1caa7SVenkat Yekkirala 
230e0d1caa7SVenkat Yekkirala 	if (sp) {
231e2193695SPaul Moore 		int i;
232e0d1caa7SVenkat Yekkirala 
233e0d1caa7SVenkat Yekkirala 		for (i = sp->len - 1; i >= 0; i--) {
234e0d1caa7SVenkat Yekkirala 			struct xfrm_state *x = sp->xvec[i];
235e0d1caa7SVenkat Yekkirala 			if (selinux_authorizable_xfrm(x)) {
236e0d1caa7SVenkat Yekkirala 				struct xfrm_sec_ctx *ctx = x->security;
237e0d1caa7SVenkat Yekkirala 
238e2193695SPaul Moore 				if (sid_session == SECSID_NULL) {
239e2193695SPaul Moore 					sid_session = ctx->ctx_sid;
240beb8d13bSVenkat Yekkirala 					if (!ckall)
241e2193695SPaul Moore 						goto out;
242e2193695SPaul Moore 				} else if (sid_session != ctx->ctx_sid) {
243e2193695SPaul Moore 					*sid = SECSID_NULL;
244e0d1caa7SVenkat Yekkirala 					return -EINVAL;
245e0d1caa7SVenkat Yekkirala 				}
246e0d1caa7SVenkat Yekkirala 			}
247e0d1caa7SVenkat Yekkirala 		}
248e2193695SPaul Moore 	}
249e0d1caa7SVenkat Yekkirala 
250e2193695SPaul Moore out:
251e2193695SPaul Moore 	*sid = sid_session;
252e0d1caa7SVenkat Yekkirala 	return 0;
253e0d1caa7SVenkat Yekkirala }
254e0d1caa7SVenkat Yekkirala 
255e0d1caa7SVenkat Yekkirala /*
256817eff71SPaul Moore  * LSM hook implementation that checks and/or returns the xfrm sid for the
257817eff71SPaul Moore  * incoming packet.
258817eff71SPaul Moore  */
selinux_xfrm_decode_session(struct sk_buff * skb,u32 * sid,int ckall)259817eff71SPaul Moore int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
260817eff71SPaul Moore {
261817eff71SPaul Moore 	if (skb == NULL) {
262817eff71SPaul Moore 		*sid = SECSID_NULL;
263817eff71SPaul Moore 		return 0;
264817eff71SPaul Moore 	}
265817eff71SPaul Moore 	return selinux_xfrm_skb_sid_ingress(skb, sid, ckall);
266817eff71SPaul Moore }
267817eff71SPaul Moore 
selinux_xfrm_skb_sid(struct sk_buff * skb,u32 * sid)268817eff71SPaul Moore int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
269817eff71SPaul Moore {
270817eff71SPaul Moore 	int rc;
271817eff71SPaul Moore 
272817eff71SPaul Moore 	rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0);
273817eff71SPaul Moore 	if (rc == 0 && *sid == SECSID_NULL)
274817eff71SPaul Moore 		*sid = selinux_xfrm_skb_sid_egress(skb);
275817eff71SPaul Moore 
276817eff71SPaul Moore 	return rc;
277817eff71SPaul Moore }
278817eff71SPaul Moore 
279817eff71SPaul Moore /*
2804baabeecSPaul Moore  * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.
281d28d1e08STrent Jaeger  */
selinux_xfrm_policy_alloc(struct xfrm_sec_ctx ** ctxp,struct xfrm_user_sec_ctx * uctx,gfp_t gfp)28203e1ad7bSPaul Moore int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
28352a4c640SNikolay Aleksandrov 			      struct xfrm_user_sec_ctx *uctx,
28452a4c640SNikolay Aleksandrov 			      gfp_t gfp)
285d28d1e08STrent Jaeger {
28652a4c640SNikolay Aleksandrov 	return selinux_xfrm_alloc_user(ctxp, uctx, gfp);
287d28d1e08STrent Jaeger }
288d28d1e08STrent Jaeger 
289d28d1e08STrent Jaeger /*
2904baabeecSPaul Moore  * LSM hook implementation that copies security data structure from old to new
2914baabeecSPaul Moore  * for policy cloning.
292d28d1e08STrent Jaeger  */
selinux_xfrm_policy_clone(struct xfrm_sec_ctx * old_ctx,struct xfrm_sec_ctx ** new_ctxp)29303e1ad7bSPaul Moore int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
29403e1ad7bSPaul Moore 			      struct xfrm_sec_ctx **new_ctxp)
295d28d1e08STrent Jaeger {
29603e1ad7bSPaul Moore 	struct xfrm_sec_ctx *new_ctx;
297d28d1e08STrent Jaeger 
298ccf17cc4SPaul Moore 	if (!old_ctx)
299ccf17cc4SPaul Moore 		return 0;
300ccf17cc4SPaul Moore 
3017d1db4b2SDuan Jiong 	new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len,
3027d1db4b2SDuan Jiong 			  GFP_ATOMIC);
303d28d1e08STrent Jaeger 	if (!new_ctx)
304d28d1e08STrent Jaeger 		return -ENOMEM;
305e4e8536fSPaul Moore 	atomic_inc(&selinux_xfrm_refcount);
30603e1ad7bSPaul Moore 	*new_ctxp = new_ctx;
307ccf17cc4SPaul Moore 
308d28d1e08STrent Jaeger 	return 0;
309d28d1e08STrent Jaeger }
310d28d1e08STrent Jaeger 
311d28d1e08STrent Jaeger /*
31203e1ad7bSPaul Moore  * LSM hook implementation that frees xfrm_sec_ctx security information.
313d28d1e08STrent Jaeger  */
selinux_xfrm_policy_free(struct xfrm_sec_ctx * ctx)31403e1ad7bSPaul Moore void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
315d28d1e08STrent Jaeger {
316ccf17cc4SPaul Moore 	selinux_xfrm_free(ctx);
317d28d1e08STrent Jaeger }
318d28d1e08STrent Jaeger 
319d28d1e08STrent Jaeger /*
320c8c05a8eSCatherine Zhang  * LSM hook implementation that authorizes deletion of labeled policies.
321c8c05a8eSCatherine Zhang  */
selinux_xfrm_policy_delete(struct xfrm_sec_ctx * ctx)32203e1ad7bSPaul Moore int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
323c8c05a8eSCatherine Zhang {
324ccf17cc4SPaul Moore 	return selinux_xfrm_delete(ctx);
325c8c05a8eSCatherine Zhang }
326c8c05a8eSCatherine Zhang 
327c8c05a8eSCatherine Zhang /*
3282e5aa866SPaul Moore  * LSM hook implementation that allocates a xfrm_sec_state, populates it using
3292e5aa866SPaul Moore  * the supplied security context, and assigns it to the xfrm_state.
330d28d1e08STrent Jaeger  */
selinux_xfrm_state_alloc(struct xfrm_state * x,struct xfrm_user_sec_ctx * uctx)3312e5aa866SPaul Moore int selinux_xfrm_state_alloc(struct xfrm_state *x,
3322e5aa866SPaul Moore 			     struct xfrm_user_sec_ctx *uctx)
333d28d1e08STrent Jaeger {
33452a4c640SNikolay Aleksandrov 	return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL);
3352e5aa866SPaul Moore }
336d28d1e08STrent Jaeger 
3372e5aa866SPaul Moore /*
3382e5aa866SPaul Moore  * LSM hook implementation that allocates a xfrm_sec_state and populates based
3392e5aa866SPaul Moore  * on a secid.
3402e5aa866SPaul Moore  */
selinux_xfrm_state_alloc_acquire(struct xfrm_state * x,struct xfrm_sec_ctx * polsec,u32 secid)3412e5aa866SPaul Moore int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
3422e5aa866SPaul Moore 				     struct xfrm_sec_ctx *polsec, u32 secid)
3432e5aa866SPaul Moore {
3442e5aa866SPaul Moore 	int rc;
3452e5aa866SPaul Moore 	struct xfrm_sec_ctx *ctx;
3462e5aa866SPaul Moore 	char *ctx_str = NULL;
347b97df7c0SChristian Göttsche 	u32 str_len;
348d28d1e08STrent Jaeger 
3492e5aa866SPaul Moore 	if (!polsec)
3502e5aa866SPaul Moore 		return 0;
3512e5aa866SPaul Moore 
3522e5aa866SPaul Moore 	if (secid == 0)
3532e5aa866SPaul Moore 		return -EINVAL;
3542e5aa866SPaul Moore 
355e67b7985SStephen Smalley 	rc = security_sid_to_context(secid, &ctx_str,
356aa8e712cSStephen Smalley 				     &str_len);
3572e5aa866SPaul Moore 	if (rc)
3582e5aa866SPaul Moore 		return rc;
3592e5aa866SPaul Moore 
3605fe37572SXiu Jianfeng 	ctx = kmalloc(struct_size(ctx, ctx_str, str_len), GFP_ATOMIC);
3610af90164SGeyslan G. Bem 	if (!ctx) {
3620af90164SGeyslan G. Bem 		rc = -ENOMEM;
3630af90164SGeyslan G. Bem 		goto out;
3640af90164SGeyslan G. Bem 	}
3652e5aa866SPaul Moore 
3662e5aa866SPaul Moore 	ctx->ctx_doi = XFRM_SC_DOI_LSM;
3672e5aa866SPaul Moore 	ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
3682e5aa866SPaul Moore 	ctx->ctx_sid = secid;
3692e5aa866SPaul Moore 	ctx->ctx_len = str_len;
3702e5aa866SPaul Moore 	memcpy(ctx->ctx_str, ctx_str, str_len);
3712e5aa866SPaul Moore 
3722e5aa866SPaul Moore 	x->security = ctx;
373d621d35eSPaul Moore 	atomic_inc(&selinux_xfrm_refcount);
3740af90164SGeyslan G. Bem out:
3750af90164SGeyslan G. Bem 	kfree(ctx_str);
3760af90164SGeyslan G. Bem 	return rc;
377d28d1e08STrent Jaeger }
378d28d1e08STrent Jaeger 
379d28d1e08STrent Jaeger /*
380d28d1e08STrent Jaeger  * LSM hook implementation that frees xfrm_state security information.
381d28d1e08STrent Jaeger  */
selinux_xfrm_state_free(struct xfrm_state * x)382d28d1e08STrent Jaeger void selinux_xfrm_state_free(struct xfrm_state *x)
383d28d1e08STrent Jaeger {
384ccf17cc4SPaul Moore 	selinux_xfrm_free(x->security);
385d28d1e08STrent Jaeger }
386d28d1e08STrent Jaeger 
3872c7946a7SCatherine Zhang /*
388c8c05a8eSCatherine Zhang  * LSM hook implementation that authorizes deletion of labeled SAs.
389c8c05a8eSCatherine Zhang  */
selinux_xfrm_state_delete(struct xfrm_state * x)390c8c05a8eSCatherine Zhang int selinux_xfrm_state_delete(struct xfrm_state *x)
391c8c05a8eSCatherine Zhang {
392ccf17cc4SPaul Moore 	return selinux_xfrm_delete(x->security);
393c8c05a8eSCatherine Zhang }
394c8c05a8eSCatherine Zhang 
395c8c05a8eSCatherine Zhang /*
396d28d1e08STrent Jaeger  * LSM hook that controls access to unlabelled packets.  If
397d28d1e08STrent Jaeger  * a xfrm_state is authorizable (defined by macro) then it was
398d28d1e08STrent Jaeger  * already authorized by the IPSec process.  If not, then
399d28d1e08STrent Jaeger  * we need to check for unlabelled access since this may not have
400d28d1e08STrent Jaeger  * gone thru the IPSec process.
401d28d1e08STrent Jaeger  */
selinux_xfrm_sock_rcv_skb(u32 sk_sid,struct sk_buff * skb,struct common_audit_data * ad)402eef9b416SPaul Moore int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
4032bf49690SThomas Liu 			      struct common_audit_data *ad)
404d28d1e08STrent Jaeger {
405eef9b416SPaul Moore 	int i;
4062294be0fSFlorian Westphal 	struct sec_path *sp = skb_sec_path(skb);
407eef9b416SPaul Moore 	u32 peer_sid = SECINITSID_UNLABELED;
408d28d1e08STrent Jaeger 
409d28d1e08STrent Jaeger 	if (sp) {
410d28d1e08STrent Jaeger 		for (i = 0; i < sp->len; i++) {
41167644726SDave Jones 			struct xfrm_state *x = sp->xvec[i];
412d28d1e08STrent Jaeger 
413e0d1caa7SVenkat Yekkirala 			if (x && selinux_authorizable_xfrm(x)) {
414e0d1caa7SVenkat Yekkirala 				struct xfrm_sec_ctx *ctx = x->security;
415eef9b416SPaul Moore 				peer_sid = ctx->ctx_sid;
416e0d1caa7SVenkat Yekkirala 				break;
417e0d1caa7SVenkat Yekkirala 			}
418d28d1e08STrent Jaeger 		}
419d28d1e08STrent Jaeger 	}
420d28d1e08STrent Jaeger 
421eef9b416SPaul Moore 	/* This check even when there's no association involved is intended,
422eef9b416SPaul Moore 	 * according to Trent Jaeger, to make sure a process can't engage in
423eef9b416SPaul Moore 	 * non-IPsec communication unless explicitly allowed by policy. */
424e67b7985SStephen Smalley 	return avc_has_perm(sk_sid, peer_sid,
425eef9b416SPaul Moore 			    SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
426d28d1e08STrent Jaeger }
427d28d1e08STrent Jaeger 
428d28d1e08STrent Jaeger /*
429d28d1e08STrent Jaeger  * POSTROUTE_LAST hook's XFRM processing:
430d28d1e08STrent Jaeger  * If we have no security association, then we need to determine
431d28d1e08STrent Jaeger  * whether the socket is allowed to send to an unlabelled destination.
432d28d1e08STrent Jaeger  * If we do have a authorizable security association, then it has already been
43367f83cbfSVenkat Yekkirala  * checked in the selinux_xfrm_state_pol_flow_match hook above.
434d28d1e08STrent Jaeger  */
selinux_xfrm_postroute_last(u32 sk_sid,struct sk_buff * skb,struct common_audit_data * ad,u8 proto)435eef9b416SPaul Moore int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
4362bf49690SThomas Liu 				struct common_audit_data *ad, u8 proto)
437d28d1e08STrent Jaeger {
438d28d1e08STrent Jaeger 	struct dst_entry *dst;
439d28d1e08STrent Jaeger 
44067f83cbfSVenkat Yekkirala 	switch (proto) {
44167f83cbfSVenkat Yekkirala 	case IPPROTO_AH:
44267f83cbfSVenkat Yekkirala 	case IPPROTO_ESP:
44367f83cbfSVenkat Yekkirala 	case IPPROTO_COMP:
444eef9b416SPaul Moore 		/* We should have already seen this packet once before it
445eef9b416SPaul Moore 		 * underwent xfrm(s). No need to subject it to the unlabeled
446eef9b416SPaul Moore 		 * check. */
447eef9b416SPaul Moore 		return 0;
44867f83cbfSVenkat Yekkirala 	default:
44967f83cbfSVenkat Yekkirala 		break;
45067f83cbfSVenkat Yekkirala 	}
45167f83cbfSVenkat Yekkirala 
452eef9b416SPaul Moore 	dst = skb_dst(skb);
453eef9b416SPaul Moore 	if (dst) {
454eef9b416SPaul Moore 		struct dst_entry *iter;
45567f83cbfSVenkat Yekkirala 
456b92cf4aaSDavid Miller 		for (iter = dst; iter != NULL; iter = xfrm_dst_child(iter)) {
457eef9b416SPaul Moore 			struct xfrm_state *x = iter->xfrm;
458eef9b416SPaul Moore 
459eef9b416SPaul Moore 			if (x && selinux_authorizable_xfrm(x))
460eef9b416SPaul Moore 				return 0;
461eef9b416SPaul Moore 		}
462eef9b416SPaul Moore 	}
463eef9b416SPaul Moore 
464eef9b416SPaul Moore 	/* This check even when there's no association involved is intended,
465eef9b416SPaul Moore 	 * according to Trent Jaeger, to make sure a process can't engage in
466eef9b416SPaul Moore 	 * non-IPsec communication unless explicitly allowed by policy. */
467e67b7985SStephen Smalley 	return avc_has_perm(sk_sid, SECINITSID_UNLABELED,
468eef9b416SPaul Moore 			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);
469d28d1e08STrent Jaeger }
470