162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Security-Enhanced Linux (SELinux) security module 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file contains the SELinux XFRM hook function implementations. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Serge Hallyn <sergeh@us.ibm.com> 862306a36Sopenharmony_ci * Trent Jaeger <jaegert@us.ibm.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Granular IPSec Associations for use in MLS environments. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Copyright (C) 2005 International Business Machines Corporation 1562306a36Sopenharmony_ci * Copyright (C) 2006 Trusted Computer Solutions, Inc. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * USAGE: 2062306a36Sopenharmony_ci * NOTES: 2162306a36Sopenharmony_ci * 1. Make sure to enable the following options in your kernel config: 2262306a36Sopenharmony_ci * CONFIG_SECURITY=y 2362306a36Sopenharmony_ci * CONFIG_SECURITY_NETWORK=y 2462306a36Sopenharmony_ci * CONFIG_SECURITY_NETWORK_XFRM=y 2562306a36Sopenharmony_ci * CONFIG_SECURITY_SELINUX=m/y 2662306a36Sopenharmony_ci * ISSUES: 2762306a36Sopenharmony_ci * 1. Caching packets, so they are not dropped during negotiation 2862306a36Sopenharmony_ci * 2. Emulating a reasonable SO_PEERSEC across machines 2962306a36Sopenharmony_ci * 3. Testing addition of sk_policy's with security context via setsockopt 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#include <linux/kernel.h> 3262306a36Sopenharmony_ci#include <linux/init.h> 3362306a36Sopenharmony_ci#include <linux/security.h> 3462306a36Sopenharmony_ci#include <linux/types.h> 3562306a36Sopenharmony_ci#include <linux/slab.h> 3662306a36Sopenharmony_ci#include <linux/ip.h> 3762306a36Sopenharmony_ci#include <linux/tcp.h> 3862306a36Sopenharmony_ci#include <linux/skbuff.h> 3962306a36Sopenharmony_ci#include <linux/xfrm.h> 4062306a36Sopenharmony_ci#include <net/xfrm.h> 4162306a36Sopenharmony_ci#include <net/checksum.h> 4262306a36Sopenharmony_ci#include <net/udp.h> 4362306a36Sopenharmony_ci#include <linux/atomic.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "avc.h" 4662306a36Sopenharmony_ci#include "objsec.h" 4762306a36Sopenharmony_ci#include "xfrm.h" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Labeled XFRM instance counter */ 5062306a36Sopenharmony_ciatomic_t selinux_xfrm_refcount __read_mostly = ATOMIC_INIT(0); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Returns true if the context is an LSM/SELinux context. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return (ctx && 5862306a36Sopenharmony_ci (ctx->ctx_doi == XFRM_SC_DOI_LSM) && 5962306a36Sopenharmony_ci (ctx->ctx_alg == XFRM_SC_ALG_SELINUX)); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * Returns true if the xfrm contains a security blob for SELinux. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistatic inline int selinux_authorizable_xfrm(struct xfrm_state *x) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return selinux_authorizable_ctx(x->security); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Allocates a xfrm_sec_state and populates it using the supplied security 7262306a36Sopenharmony_ci * xfrm_user_sec_ctx context. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, 7562306a36Sopenharmony_ci struct xfrm_user_sec_ctx *uctx, 7662306a36Sopenharmony_ci gfp_t gfp) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int rc; 7962306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 8062306a36Sopenharmony_ci struct xfrm_sec_ctx *ctx = NULL; 8162306a36Sopenharmony_ci u32 str_len; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (ctxp == NULL || uctx == NULL || 8462306a36Sopenharmony_ci uctx->ctx_doi != XFRM_SC_DOI_LSM || 8562306a36Sopenharmony_ci uctx->ctx_alg != XFRM_SC_ALG_SELINUX) 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci str_len = uctx->ctx_len; 8962306a36Sopenharmony_ci if (str_len >= PAGE_SIZE) 9062306a36Sopenharmony_ci return -ENOMEM; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ctx = kmalloc(struct_size(ctx, ctx_str, str_len + 1), gfp); 9362306a36Sopenharmony_ci if (!ctx) 9462306a36Sopenharmony_ci return -ENOMEM; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ctx->ctx_doi = XFRM_SC_DOI_LSM; 9762306a36Sopenharmony_ci ctx->ctx_alg = XFRM_SC_ALG_SELINUX; 9862306a36Sopenharmony_ci ctx->ctx_len = str_len; 9962306a36Sopenharmony_ci memcpy(ctx->ctx_str, &uctx[1], str_len); 10062306a36Sopenharmony_ci ctx->ctx_str[str_len] = '\0'; 10162306a36Sopenharmony_ci rc = security_context_to_sid(ctx->ctx_str, str_len, 10262306a36Sopenharmony_ci &ctx->ctx_sid, gfp); 10362306a36Sopenharmony_ci if (rc) 10462306a36Sopenharmony_ci goto err; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci rc = avc_has_perm(tsec->sid, ctx->ctx_sid, 10762306a36Sopenharmony_ci SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); 10862306a36Sopenharmony_ci if (rc) 10962306a36Sopenharmony_ci goto err; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci *ctxp = ctx; 11262306a36Sopenharmony_ci atomic_inc(&selinux_xfrm_refcount); 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cierr: 11662306a36Sopenharmony_ci kfree(ctx); 11762306a36Sopenharmony_ci return rc; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * Free the xfrm_sec_ctx structure. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci if (!ctx) 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci atomic_dec(&selinux_xfrm_refcount); 12962306a36Sopenharmony_ci kfree(ctx); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Authorize the deletion of a labeled SA or policy rule. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!ctx) 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return avc_has_perm(tsec->sid, ctx->ctx_sid, 14362306a36Sopenharmony_ci SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, 14462306a36Sopenharmony_ci NULL); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * LSM hook implementation that authorizes that a flow can use a xfrm policy 14962306a36Sopenharmony_ci * rule. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ciint selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int rc; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* All flows should be treated as polmatch'ing an otherwise applicable 15662306a36Sopenharmony_ci * "non-labeled" policy. This would prevent inadvertent "leaks". */ 15762306a36Sopenharmony_ci if (!ctx) 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Context sid is either set to label or ANY_ASSOC */ 16162306a36Sopenharmony_ci if (!selinux_authorizable_ctx(ctx)) 16262306a36Sopenharmony_ci return -EINVAL; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rc = avc_has_perm(fl_secid, ctx->ctx_sid, 16562306a36Sopenharmony_ci SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); 16662306a36Sopenharmony_ci return (rc == -EACCES ? -ESRCH : rc); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * LSM hook implementation that authorizes that a state matches 17162306a36Sopenharmony_ci * the given policy, flow combo. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ciint selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, 17462306a36Sopenharmony_ci struct xfrm_policy *xp, 17562306a36Sopenharmony_ci const struct flowi_common *flic) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci u32 state_sid; 17862306a36Sopenharmony_ci u32 flic_sid; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!xp->security) 18162306a36Sopenharmony_ci if (x->security) 18262306a36Sopenharmony_ci /* unlabeled policy and labeled SA can't match */ 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci /* unlabeled policy and unlabeled SA match all flows */ 18662306a36Sopenharmony_ci return 1; 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci if (!x->security) 18962306a36Sopenharmony_ci /* unlabeled SA and labeled policy can't match */ 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci else 19262306a36Sopenharmony_ci if (!selinux_authorizable_xfrm(x)) 19362306a36Sopenharmony_ci /* Not a SELinux-labeled SA */ 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci state_sid = x->security->ctx_sid; 19762306a36Sopenharmony_ci flic_sid = flic->flowic_secid; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (flic_sid != state_sid) 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* We don't need a separate SA Vs. policy polmatch check since the SA 20362306a36Sopenharmony_ci * is now of the same label as the flow and a flow Vs. policy polmatch 20462306a36Sopenharmony_ci * check had already happened in selinux_xfrm_policy_lookup() above. */ 20562306a36Sopenharmony_ci return (avc_has_perm(flic_sid, state_sid, 20662306a36Sopenharmony_ci SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, 20762306a36Sopenharmony_ci NULL) ? 0 : 1); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 21362306a36Sopenharmony_ci struct xfrm_state *x; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (dst == NULL) 21662306a36Sopenharmony_ci return SECSID_NULL; 21762306a36Sopenharmony_ci x = dst->xfrm; 21862306a36Sopenharmony_ci if (x == NULL || !selinux_authorizable_xfrm(x)) 21962306a36Sopenharmony_ci return SECSID_NULL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return x->security->ctx_sid; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb, 22562306a36Sopenharmony_ci u32 *sid, int ckall) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci u32 sid_session = SECSID_NULL; 22862306a36Sopenharmony_ci struct sec_path *sp = skb_sec_path(skb); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (sp) { 23162306a36Sopenharmony_ci int i; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = sp->len - 1; i >= 0; i--) { 23462306a36Sopenharmony_ci struct xfrm_state *x = sp->xvec[i]; 23562306a36Sopenharmony_ci if (selinux_authorizable_xfrm(x)) { 23662306a36Sopenharmony_ci struct xfrm_sec_ctx *ctx = x->security; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (sid_session == SECSID_NULL) { 23962306a36Sopenharmony_ci sid_session = ctx->ctx_sid; 24062306a36Sopenharmony_ci if (!ckall) 24162306a36Sopenharmony_ci goto out; 24262306a36Sopenharmony_ci } else if (sid_session != ctx->ctx_sid) { 24362306a36Sopenharmony_ci *sid = SECSID_NULL; 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciout: 25162306a36Sopenharmony_ci *sid = sid_session; 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * LSM hook implementation that checks and/or returns the xfrm sid for the 25762306a36Sopenharmony_ci * incoming packet. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ciint selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci if (skb == NULL) { 26262306a36Sopenharmony_ci *sid = SECSID_NULL; 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci return selinux_xfrm_skb_sid_ingress(skb, sid, ckall); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciint selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci int rc; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0); 27362306a36Sopenharmony_ci if (rc == 0 && *sid == SECSID_NULL) 27462306a36Sopenharmony_ci *sid = selinux_xfrm_skb_sid_egress(skb); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return rc; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ciint selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, 28362306a36Sopenharmony_ci struct xfrm_user_sec_ctx *uctx, 28462306a36Sopenharmony_ci gfp_t gfp) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci return selinux_xfrm_alloc_user(ctxp, uctx, gfp); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * LSM hook implementation that copies security data structure from old to new 29162306a36Sopenharmony_ci * for policy cloning. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ciint selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx, 29462306a36Sopenharmony_ci struct xfrm_sec_ctx **new_ctxp) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct xfrm_sec_ctx *new_ctx; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!old_ctx) 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len, 30262306a36Sopenharmony_ci GFP_ATOMIC); 30362306a36Sopenharmony_ci if (!new_ctx) 30462306a36Sopenharmony_ci return -ENOMEM; 30562306a36Sopenharmony_ci atomic_inc(&selinux_xfrm_refcount); 30662306a36Sopenharmony_ci *new_ctxp = new_ctx; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/* 31262306a36Sopenharmony_ci * LSM hook implementation that frees xfrm_sec_ctx security information. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_civoid selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci selinux_xfrm_free(ctx); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * LSM hook implementation that authorizes deletion of labeled policies. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ciint selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return selinux_xfrm_delete(ctx); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* 32862306a36Sopenharmony_ci * LSM hook implementation that allocates a xfrm_sec_state, populates it using 32962306a36Sopenharmony_ci * the supplied security context, and assigns it to the xfrm_state. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ciint selinux_xfrm_state_alloc(struct xfrm_state *x, 33262306a36Sopenharmony_ci struct xfrm_user_sec_ctx *uctx) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* 33862306a36Sopenharmony_ci * LSM hook implementation that allocates a xfrm_sec_state and populates based 33962306a36Sopenharmony_ci * on a secid. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ciint selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, 34262306a36Sopenharmony_ci struct xfrm_sec_ctx *polsec, u32 secid) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci int rc; 34562306a36Sopenharmony_ci struct xfrm_sec_ctx *ctx; 34662306a36Sopenharmony_ci char *ctx_str = NULL; 34762306a36Sopenharmony_ci u32 str_len; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!polsec) 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (secid == 0) 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci rc = security_sid_to_context(secid, &ctx_str, 35662306a36Sopenharmony_ci &str_len); 35762306a36Sopenharmony_ci if (rc) 35862306a36Sopenharmony_ci return rc; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ctx = kmalloc(struct_size(ctx, ctx_str, str_len), GFP_ATOMIC); 36162306a36Sopenharmony_ci if (!ctx) { 36262306a36Sopenharmony_ci rc = -ENOMEM; 36362306a36Sopenharmony_ci goto out; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ctx->ctx_doi = XFRM_SC_DOI_LSM; 36762306a36Sopenharmony_ci ctx->ctx_alg = XFRM_SC_ALG_SELINUX; 36862306a36Sopenharmony_ci ctx->ctx_sid = secid; 36962306a36Sopenharmony_ci ctx->ctx_len = str_len; 37062306a36Sopenharmony_ci memcpy(ctx->ctx_str, ctx_str, str_len); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci x->security = ctx; 37362306a36Sopenharmony_ci atomic_inc(&selinux_xfrm_refcount); 37462306a36Sopenharmony_ciout: 37562306a36Sopenharmony_ci kfree(ctx_str); 37662306a36Sopenharmony_ci return rc; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* 38062306a36Sopenharmony_ci * LSM hook implementation that frees xfrm_state security information. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_civoid selinux_xfrm_state_free(struct xfrm_state *x) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci selinux_xfrm_free(x->security); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * LSM hook implementation that authorizes deletion of labeled SAs. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ciint selinux_xfrm_state_delete(struct xfrm_state *x) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci return selinux_xfrm_delete(x->security); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* 39662306a36Sopenharmony_ci * LSM hook that controls access to unlabelled packets. If 39762306a36Sopenharmony_ci * a xfrm_state is authorizable (defined by macro) then it was 39862306a36Sopenharmony_ci * already authorized by the IPSec process. If not, then 39962306a36Sopenharmony_ci * we need to check for unlabelled access since this may not have 40062306a36Sopenharmony_ci * gone thru the IPSec process. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ciint selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, 40362306a36Sopenharmony_ci struct common_audit_data *ad) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci int i; 40662306a36Sopenharmony_ci struct sec_path *sp = skb_sec_path(skb); 40762306a36Sopenharmony_ci u32 peer_sid = SECINITSID_UNLABELED; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (sp) { 41062306a36Sopenharmony_ci for (i = 0; i < sp->len; i++) { 41162306a36Sopenharmony_ci struct xfrm_state *x = sp->xvec[i]; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (x && selinux_authorizable_xfrm(x)) { 41462306a36Sopenharmony_ci struct xfrm_sec_ctx *ctx = x->security; 41562306a36Sopenharmony_ci peer_sid = ctx->ctx_sid; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* This check even when there's no association involved is intended, 42262306a36Sopenharmony_ci * according to Trent Jaeger, to make sure a process can't engage in 42362306a36Sopenharmony_ci * non-IPsec communication unless explicitly allowed by policy. */ 42462306a36Sopenharmony_ci return avc_has_perm(sk_sid, peer_sid, 42562306a36Sopenharmony_ci SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/* 42962306a36Sopenharmony_ci * POSTROUTE_LAST hook's XFRM processing: 43062306a36Sopenharmony_ci * If we have no security association, then we need to determine 43162306a36Sopenharmony_ci * whether the socket is allowed to send to an unlabelled destination. 43262306a36Sopenharmony_ci * If we do have a authorizable security association, then it has already been 43362306a36Sopenharmony_ci * checked in the selinux_xfrm_state_pol_flow_match hook above. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ciint selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, 43662306a36Sopenharmony_ci struct common_audit_data *ad, u8 proto) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct dst_entry *dst; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (proto) { 44162306a36Sopenharmony_ci case IPPROTO_AH: 44262306a36Sopenharmony_ci case IPPROTO_ESP: 44362306a36Sopenharmony_ci case IPPROTO_COMP: 44462306a36Sopenharmony_ci /* We should have already seen this packet once before it 44562306a36Sopenharmony_ci * underwent xfrm(s). No need to subject it to the unlabeled 44662306a36Sopenharmony_ci * check. */ 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci default: 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci dst = skb_dst(skb); 45362306a36Sopenharmony_ci if (dst) { 45462306a36Sopenharmony_ci struct dst_entry *iter; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (iter = dst; iter != NULL; iter = xfrm_dst_child(iter)) { 45762306a36Sopenharmony_ci struct xfrm_state *x = iter->xfrm; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (x && selinux_authorizable_xfrm(x)) 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* This check even when there's no association involved is intended, 46562306a36Sopenharmony_ci * according to Trent Jaeger, to make sure a process can't engage in 46662306a36Sopenharmony_ci * non-IPsec communication unless explicitly allowed by policy. */ 46762306a36Sopenharmony_ci return avc_has_perm(sk_sid, SECINITSID_UNLABELED, 46862306a36Sopenharmony_ci SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad); 46962306a36Sopenharmony_ci} 470