18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  NSA Security-Enhanced Linux (SELinux) security module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  This file contains the SELinux XFRM hook function implementations.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Authors:  Serge Hallyn <sergeh@us.ibm.com>
88c2ecf20Sopenharmony_ci *	      Trent Jaeger <jaegert@us.ibm.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *           Granular IPSec Associations for use in MLS environments.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *  Copyright (C) 2005 International Business Machines Corporation
158c2ecf20Sopenharmony_ci *  Copyright (C) 2006 Trusted Computer Solutions, Inc.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * USAGE:
208c2ecf20Sopenharmony_ci * NOTES:
218c2ecf20Sopenharmony_ci *   1. Make sure to enable the following options in your kernel config:
228c2ecf20Sopenharmony_ci *	CONFIG_SECURITY=y
238c2ecf20Sopenharmony_ci *	CONFIG_SECURITY_NETWORK=y
248c2ecf20Sopenharmony_ci *	CONFIG_SECURITY_NETWORK_XFRM=y
258c2ecf20Sopenharmony_ci *	CONFIG_SECURITY_SELINUX=m/y
268c2ecf20Sopenharmony_ci * ISSUES:
278c2ecf20Sopenharmony_ci *   1. Caching packets, so they are not dropped during negotiation
288c2ecf20Sopenharmony_ci *   2. Emulating a reasonable SO_PEERSEC across machines
298c2ecf20Sopenharmony_ci *   3. Testing addition of sk_policy's with security context via setsockopt
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci#include <linux/kernel.h>
328c2ecf20Sopenharmony_ci#include <linux/init.h>
338c2ecf20Sopenharmony_ci#include <linux/security.h>
348c2ecf20Sopenharmony_ci#include <linux/types.h>
358c2ecf20Sopenharmony_ci#include <linux/slab.h>
368c2ecf20Sopenharmony_ci#include <linux/ip.h>
378c2ecf20Sopenharmony_ci#include <linux/tcp.h>
388c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
398c2ecf20Sopenharmony_ci#include <linux/xfrm.h>
408c2ecf20Sopenharmony_ci#include <net/xfrm.h>
418c2ecf20Sopenharmony_ci#include <net/checksum.h>
428c2ecf20Sopenharmony_ci#include <net/udp.h>
438c2ecf20Sopenharmony_ci#include <linux/atomic.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include "avc.h"
468c2ecf20Sopenharmony_ci#include "objsec.h"
478c2ecf20Sopenharmony_ci#include "xfrm.h"
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Labeled XFRM instance counter */
508c2ecf20Sopenharmony_ciatomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * Returns true if the context is an LSM/SELinux context.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistatic inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	return (ctx &&
588c2ecf20Sopenharmony_ci		(ctx->ctx_doi == XFRM_SC_DOI_LSM) &&
598c2ecf20Sopenharmony_ci		(ctx->ctx_alg == XFRM_SC_ALG_SELINUX));
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/*
638c2ecf20Sopenharmony_ci * Returns true if the xfrm contains a security blob for SELinux.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistatic inline int selinux_authorizable_xfrm(struct xfrm_state *x)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return selinux_authorizable_ctx(x->security);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/*
718c2ecf20Sopenharmony_ci * Allocates a xfrm_sec_state and populates it using the supplied security
728c2ecf20Sopenharmony_ci * xfrm_user_sec_ctx context.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
758c2ecf20Sopenharmony_ci				   struct xfrm_user_sec_ctx *uctx,
768c2ecf20Sopenharmony_ci				   gfp_t gfp)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int rc;
798c2ecf20Sopenharmony_ci	const struct task_security_struct *tsec = selinux_cred(current_cred());
808c2ecf20Sopenharmony_ci	struct xfrm_sec_ctx *ctx = NULL;
818c2ecf20Sopenharmony_ci	u32 str_len;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (ctxp == NULL || uctx == NULL ||
848c2ecf20Sopenharmony_ci	    uctx->ctx_doi != XFRM_SC_DOI_LSM ||
858c2ecf20Sopenharmony_ci	    uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
868c2ecf20Sopenharmony_ci		return -EINVAL;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	str_len = uctx->ctx_len;
898c2ecf20Sopenharmony_ci	if (str_len >= PAGE_SIZE)
908c2ecf20Sopenharmony_ci		return -ENOMEM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	ctx = kmalloc(sizeof(*ctx) + str_len + 1, gfp);
938c2ecf20Sopenharmony_ci	if (!ctx)
948c2ecf20Sopenharmony_ci		return -ENOMEM;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ctx->ctx_doi = XFRM_SC_DOI_LSM;
978c2ecf20Sopenharmony_ci	ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
988c2ecf20Sopenharmony_ci	ctx->ctx_len = str_len;
998c2ecf20Sopenharmony_ci	memcpy(ctx->ctx_str, &uctx[1], str_len);
1008c2ecf20Sopenharmony_ci	ctx->ctx_str[str_len] = '\0';
1018c2ecf20Sopenharmony_ci	rc = security_context_to_sid(&selinux_state, ctx->ctx_str, str_len,
1028c2ecf20Sopenharmony_ci				     &ctx->ctx_sid, gfp);
1038c2ecf20Sopenharmony_ci	if (rc)
1048c2ecf20Sopenharmony_ci		goto err;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	rc = avc_has_perm(&selinux_state,
1078c2ecf20Sopenharmony_ci			  tsec->sid, ctx->ctx_sid,
1088c2ecf20Sopenharmony_ci			  SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
1098c2ecf20Sopenharmony_ci	if (rc)
1108c2ecf20Sopenharmony_ci		goto err;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	*ctxp = ctx;
1138c2ecf20Sopenharmony_ci	atomic_inc(&selinux_xfrm_refcount);
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cierr:
1178c2ecf20Sopenharmony_ci	kfree(ctx);
1188c2ecf20Sopenharmony_ci	return rc;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * Free the xfrm_sec_ctx structure.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if (!ctx)
1278c2ecf20Sopenharmony_ci		return;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	atomic_dec(&selinux_xfrm_refcount);
1308c2ecf20Sopenharmony_ci	kfree(ctx);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*
1348c2ecf20Sopenharmony_ci * Authorize the deletion of a labeled SA or policy rule.
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_cistatic int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	const struct task_security_struct *tsec = selinux_cred(current_cred());
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (!ctx)
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return avc_has_perm(&selinux_state,
1448c2ecf20Sopenharmony_ci			    tsec->sid, ctx->ctx_sid,
1458c2ecf20Sopenharmony_ci			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
1468c2ecf20Sopenharmony_ci			    NULL);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * LSM hook implementation that authorizes that a flow can use a xfrm policy
1518c2ecf20Sopenharmony_ci * rule.
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_ciint selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int rc;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* All flows should be treated as polmatch'ing an otherwise applicable
1588c2ecf20Sopenharmony_ci	 * "non-labeled" policy. This would prevent inadvertent "leaks". */
1598c2ecf20Sopenharmony_ci	if (!ctx)
1608c2ecf20Sopenharmony_ci		return 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Context sid is either set to label or ANY_ASSOC */
1638c2ecf20Sopenharmony_ci	if (!selinux_authorizable_ctx(ctx))
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	rc = avc_has_perm(&selinux_state,
1678c2ecf20Sopenharmony_ci			  fl_secid, ctx->ctx_sid,
1688c2ecf20Sopenharmony_ci			  SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL);
1698c2ecf20Sopenharmony_ci	return (rc == -EACCES ? -ESRCH : rc);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * LSM hook implementation that authorizes that a state matches
1748c2ecf20Sopenharmony_ci * the given policy, flow combo.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_ciint selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
1778c2ecf20Sopenharmony_ci				      struct xfrm_policy *xp,
1788c2ecf20Sopenharmony_ci				      const struct flowi_common *flic)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	u32 state_sid;
1818c2ecf20Sopenharmony_ci	u32 flic_sid;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (!xp->security)
1848c2ecf20Sopenharmony_ci		if (x->security)
1858c2ecf20Sopenharmony_ci			/* unlabeled policy and labeled SA can't match */
1868c2ecf20Sopenharmony_ci			return 0;
1878c2ecf20Sopenharmony_ci		else
1888c2ecf20Sopenharmony_ci			/* unlabeled policy and unlabeled SA match all flows */
1898c2ecf20Sopenharmony_ci			return 1;
1908c2ecf20Sopenharmony_ci	else
1918c2ecf20Sopenharmony_ci		if (!x->security)
1928c2ecf20Sopenharmony_ci			/* unlabeled SA and labeled policy can't match */
1938c2ecf20Sopenharmony_ci			return 0;
1948c2ecf20Sopenharmony_ci		else
1958c2ecf20Sopenharmony_ci			if (!selinux_authorizable_xfrm(x))
1968c2ecf20Sopenharmony_ci				/* Not a SELinux-labeled SA */
1978c2ecf20Sopenharmony_ci				return 0;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	state_sid = x->security->ctx_sid;
2008c2ecf20Sopenharmony_ci	flic_sid = flic->flowic_secid;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (flic_sid != state_sid)
2038c2ecf20Sopenharmony_ci		return 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* We don't need a separate SA Vs. policy polmatch check since the SA
2068c2ecf20Sopenharmony_ci	 * is now of the same label as the flow and a flow Vs. policy polmatch
2078c2ecf20Sopenharmony_ci	 * check had already happened in selinux_xfrm_policy_lookup() above. */
2088c2ecf20Sopenharmony_ci	return (avc_has_perm(&selinux_state, flic_sid, state_sid,
2098c2ecf20Sopenharmony_ci			     SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
2108c2ecf20Sopenharmony_ci			     NULL) ? 0 : 1);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
2168c2ecf20Sopenharmony_ci	struct xfrm_state *x;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (dst == NULL)
2198c2ecf20Sopenharmony_ci		return SECSID_NULL;
2208c2ecf20Sopenharmony_ci	x = dst->xfrm;
2218c2ecf20Sopenharmony_ci	if (x == NULL || !selinux_authorizable_xfrm(x))
2228c2ecf20Sopenharmony_ci		return SECSID_NULL;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return x->security->ctx_sid;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb,
2288c2ecf20Sopenharmony_ci					u32 *sid, int ckall)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u32 sid_session = SECSID_NULL;
2318c2ecf20Sopenharmony_ci	struct sec_path *sp = skb_sec_path(skb);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (sp) {
2348c2ecf20Sopenharmony_ci		int i;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		for (i = sp->len - 1; i >= 0; i--) {
2378c2ecf20Sopenharmony_ci			struct xfrm_state *x = sp->xvec[i];
2388c2ecf20Sopenharmony_ci			if (selinux_authorizable_xfrm(x)) {
2398c2ecf20Sopenharmony_ci				struct xfrm_sec_ctx *ctx = x->security;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci				if (sid_session == SECSID_NULL) {
2428c2ecf20Sopenharmony_ci					sid_session = ctx->ctx_sid;
2438c2ecf20Sopenharmony_ci					if (!ckall)
2448c2ecf20Sopenharmony_ci						goto out;
2458c2ecf20Sopenharmony_ci				} else if (sid_session != ctx->ctx_sid) {
2468c2ecf20Sopenharmony_ci					*sid = SECSID_NULL;
2478c2ecf20Sopenharmony_ci					return -EINVAL;
2488c2ecf20Sopenharmony_ci				}
2498c2ecf20Sopenharmony_ci			}
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ciout:
2548c2ecf20Sopenharmony_ci	*sid = sid_session;
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/*
2598c2ecf20Sopenharmony_ci * LSM hook implementation that checks and/or returns the xfrm sid for the
2608c2ecf20Sopenharmony_ci * incoming packet.
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_ciint selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	if (skb == NULL) {
2658c2ecf20Sopenharmony_ci		*sid = SECSID_NULL;
2668c2ecf20Sopenharmony_ci		return 0;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	return selinux_xfrm_skb_sid_ingress(skb, sid, ckall);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciint selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	int rc;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0);
2768c2ecf20Sopenharmony_ci	if (rc == 0 && *sid == SECSID_NULL)
2778c2ecf20Sopenharmony_ci		*sid = selinux_xfrm_skb_sid_egress(skb);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return rc;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_ciint selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
2868c2ecf20Sopenharmony_ci			      struct xfrm_user_sec_ctx *uctx,
2878c2ecf20Sopenharmony_ci			      gfp_t gfp)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	return selinux_xfrm_alloc_user(ctxp, uctx, gfp);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/*
2938c2ecf20Sopenharmony_ci * LSM hook implementation that copies security data structure from old to new
2948c2ecf20Sopenharmony_ci * for policy cloning.
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_ciint selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
2978c2ecf20Sopenharmony_ci			      struct xfrm_sec_ctx **new_ctxp)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct xfrm_sec_ctx *new_ctx;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (!old_ctx)
3028c2ecf20Sopenharmony_ci		return 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len,
3058c2ecf20Sopenharmony_ci			  GFP_ATOMIC);
3068c2ecf20Sopenharmony_ci	if (!new_ctx)
3078c2ecf20Sopenharmony_ci		return -ENOMEM;
3088c2ecf20Sopenharmony_ci	atomic_inc(&selinux_xfrm_refcount);
3098c2ecf20Sopenharmony_ci	*new_ctxp = new_ctx;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/*
3158c2ecf20Sopenharmony_ci * LSM hook implementation that frees xfrm_sec_ctx security information.
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_civoid selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	selinux_xfrm_free(ctx);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/*
3238c2ecf20Sopenharmony_ci * LSM hook implementation that authorizes deletion of labeled policies.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_ciint selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	return selinux_xfrm_delete(ctx);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/*
3318c2ecf20Sopenharmony_ci * LSM hook implementation that allocates a xfrm_sec_state, populates it using
3328c2ecf20Sopenharmony_ci * the supplied security context, and assigns it to the xfrm_state.
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_ciint selinux_xfrm_state_alloc(struct xfrm_state *x,
3358c2ecf20Sopenharmony_ci			     struct xfrm_user_sec_ctx *uctx)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci/*
3418c2ecf20Sopenharmony_ci * LSM hook implementation that allocates a xfrm_sec_state and populates based
3428c2ecf20Sopenharmony_ci * on a secid.
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_ciint selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
3458c2ecf20Sopenharmony_ci				     struct xfrm_sec_ctx *polsec, u32 secid)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	int rc;
3488c2ecf20Sopenharmony_ci	struct xfrm_sec_ctx *ctx;
3498c2ecf20Sopenharmony_ci	char *ctx_str = NULL;
3508c2ecf20Sopenharmony_ci	u32 str_len;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (!polsec)
3538c2ecf20Sopenharmony_ci		return 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (secid == 0)
3568c2ecf20Sopenharmony_ci		return -EINVAL;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	rc = security_sid_to_context(&selinux_state, secid, &ctx_str,
3598c2ecf20Sopenharmony_ci				     &str_len);
3608c2ecf20Sopenharmony_ci	if (rc)
3618c2ecf20Sopenharmony_ci		return rc;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
3648c2ecf20Sopenharmony_ci	if (!ctx) {
3658c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3668c2ecf20Sopenharmony_ci		goto out;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	ctx->ctx_doi = XFRM_SC_DOI_LSM;
3708c2ecf20Sopenharmony_ci	ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
3718c2ecf20Sopenharmony_ci	ctx->ctx_sid = secid;
3728c2ecf20Sopenharmony_ci	ctx->ctx_len = str_len;
3738c2ecf20Sopenharmony_ci	memcpy(ctx->ctx_str, ctx_str, str_len);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	x->security = ctx;
3768c2ecf20Sopenharmony_ci	atomic_inc(&selinux_xfrm_refcount);
3778c2ecf20Sopenharmony_ciout:
3788c2ecf20Sopenharmony_ci	kfree(ctx_str);
3798c2ecf20Sopenharmony_ci	return rc;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci/*
3838c2ecf20Sopenharmony_ci * LSM hook implementation that frees xfrm_state security information.
3848c2ecf20Sopenharmony_ci */
3858c2ecf20Sopenharmony_civoid selinux_xfrm_state_free(struct xfrm_state *x)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	selinux_xfrm_free(x->security);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/*
3918c2ecf20Sopenharmony_ci * LSM hook implementation that authorizes deletion of labeled SAs.
3928c2ecf20Sopenharmony_ci */
3938c2ecf20Sopenharmony_ciint selinux_xfrm_state_delete(struct xfrm_state *x)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	return selinux_xfrm_delete(x->security);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/*
3998c2ecf20Sopenharmony_ci * LSM hook that controls access to unlabelled packets.  If
4008c2ecf20Sopenharmony_ci * a xfrm_state is authorizable (defined by macro) then it was
4018c2ecf20Sopenharmony_ci * already authorized by the IPSec process.  If not, then
4028c2ecf20Sopenharmony_ci * we need to check for unlabelled access since this may not have
4038c2ecf20Sopenharmony_ci * gone thru the IPSec process.
4048c2ecf20Sopenharmony_ci */
4058c2ecf20Sopenharmony_ciint selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
4068c2ecf20Sopenharmony_ci			      struct common_audit_data *ad)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	int i;
4098c2ecf20Sopenharmony_ci	struct sec_path *sp = skb_sec_path(skb);
4108c2ecf20Sopenharmony_ci	u32 peer_sid = SECINITSID_UNLABELED;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (sp) {
4138c2ecf20Sopenharmony_ci		for (i = 0; i < sp->len; i++) {
4148c2ecf20Sopenharmony_ci			struct xfrm_state *x = sp->xvec[i];
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci			if (x && selinux_authorizable_xfrm(x)) {
4178c2ecf20Sopenharmony_ci				struct xfrm_sec_ctx *ctx = x->security;
4188c2ecf20Sopenharmony_ci				peer_sid = ctx->ctx_sid;
4198c2ecf20Sopenharmony_ci				break;
4208c2ecf20Sopenharmony_ci			}
4218c2ecf20Sopenharmony_ci		}
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* This check even when there's no association involved is intended,
4258c2ecf20Sopenharmony_ci	 * according to Trent Jaeger, to make sure a process can't engage in
4268c2ecf20Sopenharmony_ci	 * non-IPsec communication unless explicitly allowed by policy. */
4278c2ecf20Sopenharmony_ci	return avc_has_perm(&selinux_state,
4288c2ecf20Sopenharmony_ci			    sk_sid, peer_sid,
4298c2ecf20Sopenharmony_ci			    SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/*
4338c2ecf20Sopenharmony_ci * POSTROUTE_LAST hook's XFRM processing:
4348c2ecf20Sopenharmony_ci * If we have no security association, then we need to determine
4358c2ecf20Sopenharmony_ci * whether the socket is allowed to send to an unlabelled destination.
4368c2ecf20Sopenharmony_ci * If we do have a authorizable security association, then it has already been
4378c2ecf20Sopenharmony_ci * checked in the selinux_xfrm_state_pol_flow_match hook above.
4388c2ecf20Sopenharmony_ci */
4398c2ecf20Sopenharmony_ciint selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
4408c2ecf20Sopenharmony_ci				struct common_audit_data *ad, u8 proto)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct dst_entry *dst;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	switch (proto) {
4458c2ecf20Sopenharmony_ci	case IPPROTO_AH:
4468c2ecf20Sopenharmony_ci	case IPPROTO_ESP:
4478c2ecf20Sopenharmony_ci	case IPPROTO_COMP:
4488c2ecf20Sopenharmony_ci		/* We should have already seen this packet once before it
4498c2ecf20Sopenharmony_ci		 * underwent xfrm(s). No need to subject it to the unlabeled
4508c2ecf20Sopenharmony_ci		 * check. */
4518c2ecf20Sopenharmony_ci		return 0;
4528c2ecf20Sopenharmony_ci	default:
4538c2ecf20Sopenharmony_ci		break;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	dst = skb_dst(skb);
4578c2ecf20Sopenharmony_ci	if (dst) {
4588c2ecf20Sopenharmony_ci		struct dst_entry *iter;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		for (iter = dst; iter != NULL; iter = xfrm_dst_child(iter)) {
4618c2ecf20Sopenharmony_ci			struct xfrm_state *x = iter->xfrm;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci			if (x && selinux_authorizable_xfrm(x))
4648c2ecf20Sopenharmony_ci				return 0;
4658c2ecf20Sopenharmony_ci		}
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* This check even when there's no association involved is intended,
4698c2ecf20Sopenharmony_ci	 * according to Trent Jaeger, to make sure a process can't engage in
4708c2ecf20Sopenharmony_ci	 * non-IPsec communication unless explicitly allowed by policy. */
4718c2ecf20Sopenharmony_ci	return avc_has_perm(&selinux_state, sk_sid, SECINITSID_UNLABELED,
4728c2ecf20Sopenharmony_ci			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);
4738c2ecf20Sopenharmony_ci}
474