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