[PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes
Casey Schaufler <[email protected]>
| Newsgroups | gmane.linux.kernel.lsm,gmane.linux.kernel,gmane.comp.security.firewalls.netfilter.devel |
|---|---|
| Message-ID | <[email protected]> |
Maintain a xarray of lsm_prop structures which represent the LSM security information passed via skb->secmark. Pass the xarray index of the appropriate lsm_prop (the secxa) instead of an LSM specific secid. Allow multiple LSMs to specify their components in xarray entries, or create new entries as necessary. Change uses of security_secctx_to_secid() to security_secctx_to_lsmprop() in the netfilter and iptables code. Change security_secmark_relabel_packet() to accept an lsm_prop pointer rather than a secid. Change secxa_set_secmark() to update and create new entries as necessary. Update the SELinux, Smack and AppArmor hooks that use secmarks to expect a secxa xarray index instead of a secid. Signed-off-by: Casey Schaufler <[email protected]> --- include/linux/lsm_hook_defs.h | 2 +- include/linux/security.h | 4 +- net/netfilter/nfnetlink_queue.c | 12 +++++- net/netfilter/nft_meta.c | 11 +++-- net/netfilter/xt_SECMARK.c | 12 ++++-- security/apparmor/net.c | 8 +++- security/lsm_secxa.c | 18 ++++++-- security/security.c | 6 +-- security/selinux/hooks.c | 71 +++++++++++++++++++++++++++----- security/smack/smack_lsm.c | 10 ++++- security/smack/smack_netfilter.c | 8 ++-- 11 files changed, 127 insertions(+), 35 deletions(-) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 3666d821b8a1..7ba4547daded 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -371,7 +371,7 @@ LSM_HOOK(void, LSM_RET_VOID, inet_csk_clone, struct sock *newsk, const struct request_sock *req) LSM_HOOK(void, LSM_RET_VOID, inet_conn_established, struct sock *sk, struct sk_buff *skb) -LSM_HOOK(int, 0, secmark_relabel_packet, u32 secid) +LSM_HOOK(int, 0, secmark_relabel_packet, struct lsm_prop *prop) LSM_HOOK(void, LSM_RET_VOID, secmark_refcount_inc, void) LSM_HOOK(void, LSM_RET_VOID, secmark_refcount_dec, void) LSM_HOOK(void, LSM_RET_VOID, req_classify_flow, const struct request_sock *req, diff --git a/include/linux/security.h b/include/linux/security.h index b209d681e79a..844fbef4a6a8 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1716,7 +1716,7 @@ void security_inet_csk_clone(struct sock *newsk, const struct request_sock *req); void security_inet_conn_established(struct sock *sk, struct sk_buff *skb); -int security_secmark_relabel_packet(u32 secid); +int security_secmark_relabel_packet(struct lsm_prop *prop); void security_secmark_refcount_inc(void); void security_secmark_refcount_dec(void); int security_tun_dev_alloc_security(void **security); @@ -1899,7 +1899,7 @@ static inline void security_inet_conn_established(struct sock *sk, { } -static inline int security_secmark_relabel_packet(u32 secid) +static inline int security_secmark_relabel_packet(struct lsm_prop *prop) { return 0; } diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index b8aaf39cb4d8..ebab037edc6b 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -32,6 +32,7 @@ #include <linux/cgroup-defs.h> #include <linux/rhashtable.h> #include <linux/jhash.h> +#include <linux/lsm_secxa.h> #include <net/gso.h> #include <net/sock.h> #include <net/tcp_states.h> @@ -606,8 +607,15 @@ static int nfqnl_get_sk_secctx(struct sk_buff *skb, struct lsm_context *ctx) { int seclen = 0; #if IS_ENABLED(CONFIG_NETWORK_SECMARK) - if (skb->secmark) - seclen = security_secid_to_secctx(skb->secmark, ctx); + struct lsm_prop *prop; + int rc; + + if (skb->secmark) { + rc = secxa_get_lsmprop(&prop, skb->secmark); + if (rc) + return 0; + seclen = security_lsmprop_to_secctx(prop, ctx, LSM_ID_UNDEF); + } #endif return seclen; } diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index bd0f7a0931f4..24535c45dab3 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -927,17 +927,20 @@ static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = { static int nft_secmark_compute_secid(struct nft_secmark *priv) { + struct lsm_prop tmp_prop; u32 tmp_secid = 0; int err; - err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid); + err = security_secctx_to_lsmprop(priv->ctx, strlen(priv->ctx), + &tmp_prop, LSM_ID_UNDEF); if (err) return err; - if (!tmp_secid) - return -ENOENT; + tmp_secid = secxa_from_lsmprop(&tmp_prop); + if (tmp_secid < 0) + return tmp_secid; - err = security_secmark_relabel_packet(tmp_secid); + err = security_secmark_relabel_packet(&tmp_prop); if (err) return err; diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index ea67aa92ddc2..5a7b83c67430 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -43,13 +43,15 @@ secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info) static int checkentry_lsm(struct xt_secmark_target_info_v1 *info) { + struct lsm_prop prop; int err; info->secctx[SECMARK_SECCTX_MAX - 1] = '\0'; info->secid = 0; - err = security_secctx_to_secid(info->secctx, strlen(info->secctx), - &info->secid); + err = security_secctx_to_lsmprop(info->secctx, strlen(info->secctx), + &prop, LSM_ID_UNDEF); + if (err) { if (err == -EINVAL) pr_info_ratelimited("invalid security context \'%s\'\n", @@ -57,18 +59,20 @@ static int checkentry_lsm(struct xt_secmark_target_info_v1 *info) return err; } - if (!info->secid) { + if (!lsmprop_is_set(&prop)) { pr_info_ratelimited("unable to map security context \'%s\'\n", info->secctx); return -ENOENT; } - err = security_secmark_relabel_packet(info->secid); + err = security_secmark_relabel_packet(&prop); if (err) { pr_info_ratelimited("unable to obtain relabeling permission\n"); return err; } + info->secid = secxa_from_lsmprop(&prop); + security_secmark_refcount_inc(); return 0; } diff --git a/security/apparmor/net.c b/security/apparmor/net.c index cf590dd08540..e26e15c2d947 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -8,6 +8,7 @@ * Copyright 2009-2017 Canonical Ltd. */ +#include <linux/lsm_secxa.h> #include "include/af_unix.h" #include "include/apparmor.h" #include "include/audit.h" @@ -365,12 +366,17 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid, struct apparmor_audit_data *ad) { int i, ret; + struct lsm_prop *prop; struct aa_perms perms = { }; struct aa_ruleset *rules = profile->label.rules[0]; if (rules->secmark_count == 0) return 0; + ret = secxa_get_lsmprop(&prop, secid); + if (ret) + return ret; + for (i = 0; i < rules->secmark_count; i++) { if (!rules->secmark[i].secid) { ret = apparmor_secmark_init(&rules->secmark[i]); @@ -378,7 +384,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid, return ret; } - if (rules->secmark[i].secid == secid || + if (rules->secmark[i].secid == prop->apparmor.label->secid || rules->secmark[i].secid == AA_SECID_WILDCARD) { if (rules->secmark[i].deny) perms.deny = ALL_PERMS_MASK; diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c index 5b67d8218fd2..2015dab01bdf 100644 --- a/security/lsm_secxa.c +++ b/security/lsm_secxa.c @@ -71,8 +71,6 @@ int secxa_from_lsmprop(struct lsm_prop *prop) int rc; xa_for_each(&secxa_xa, il, lp) { - if (!memcmp(prop, lp, sizeof(*prop))) - pr_info("%s found at index %lu\n", __func__, il); if (!memcmp(prop, lp, sizeof(*prop))) return il; } @@ -101,7 +99,21 @@ EXPORT_SYMBOL(secxa_from_lsmprop); */ void secxa_set_secmark(struct sk_buff *skb, u32 secxa) { - if (!skb->secmark) + struct lsm_prop *olp; + struct lsm_prop *nlp; + struct lsm_prop prop; + + if (!skb->secmark) { skb->secmark = secxa; + return; + } + + olp = xa_load(&secxa_xa, skb->secmark); + nlp = xa_load(&secxa_xa, secxa); + + prop = *olp; + security_update_lsmprop(&prop, nlp, LSM_ID_UNDEF); + + skb->secmark = secxa_from_lsmprop(&prop); } EXPORT_SYMBOL(secxa_set_secmark); diff --git a/security/security.c b/security/security.c index 932a2eca28b3..059cf0a97d2c 100644 --- a/security/security.c +++ b/security/security.c @@ -4646,15 +4646,15 @@ EXPORT_SYMBOL(security_inet_conn_established); /** * security_secmark_relabel_packet() - Check if setting a secmark is allowed - * @secid: new secmark value + * @lsmprop: new secmark value * * Check if the process should be allowed to relabel packets to @secid. * * Return: Returns 0 if permission is granted. */ -int security_secmark_relabel_packet(u32 secid) +int security_secmark_relabel_packet(struct lsm_prop *prop) { - return call_int_hook(secmark_relabel_packet, secid); + return call_int_hook(secmark_relabel_packet, prop); } EXPORT_SYMBOL(security_secmark_relabel_packet); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c05c05e71078..55d7679f3403 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -94,6 +94,7 @@ #include <linux/io_uring/cmd.h> #include <uapi/linux/lsm.h> #include <linux/memfd.h> +#include <linux/lsm_secxa.h> #include "initcalls.h" #include "avc.h" @@ -5414,7 +5415,16 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, return err; if (selinux_secmark_enabled()) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + struct lsm_prop *prop; + u32 secmark = 0; + + if (skb->secmark) { + err = secxa_get_lsmprop(&prop, skb->secmark); + if (!err) + secmark = prop->selinux.secid; + } + + err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) return err; @@ -5483,7 +5493,15 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } if (secmark_active) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + struct lsm_prop *prop; + u32 secmark = 0; + + if (skb->secmark) { + err = secxa_get_lsmprop(&prop, skb->secmark); + if (!err) + secmark = prop->selinux.secid; + } + err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) return err; @@ -5885,10 +5903,10 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); } -static int selinux_secmark_relabel_packet(u32 sid) +static int selinux_secmark_relabel_packet(struct lsm_prop *lsmprop) { - return avc_has_perm(current_sid(), sid, SECCLASS_PACKET, PACKET__RELABELTO, - NULL); + return avc_has_perm(current_sid(), lsmprop->selinux.secid, + SECCLASS_PACKET, PACKET__RELABELTO, NULL); } static void selinux_secmark_refcount_inc(void) @@ -6016,10 +6034,21 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb, } } - if (secmark_active) - if (avc_has_perm(peer_sid, skb->secmark, + if (secmark_active) { + struct lsm_prop *prop; + u32 secmark = 0; + int err; + + if (skb->secmark) { + err = secxa_get_lsmprop(&prop, skb->secmark); + if (!err) + secmark = prop->selinux.secid; + } + + if (avc_has_perm(peer_sid, secmark, SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) return NF_DROP; + } if (netlbl_enabled()) /* we do this in the FORWARD path and not the POST_ROUTING @@ -6093,10 +6122,20 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, if (selinux_parse_skb(skb, &ad, NULL, 0, &proto)) return NF_DROP; - if (selinux_secmark_enabled()) - if (avc_has_perm(sksec->sid, skb->secmark, + if (selinux_secmark_enabled()) { + struct lsm_prop *prop; + u32 secmark = 0; + int err; + + if (skb->secmark) { + err = secxa_get_lsmprop(&prop, skb->secmark); + if (!err) + secmark = prop->selinux.secid; + } + if (avc_has_perm(sksec->sid, secmark, SECCLASS_PACKET, PACKET__SEND, &ad)) return NF_DROP_ERR(-ECONNREFUSED); + } if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) return NF_DROP_ERR(-ECONNREFUSED); @@ -6215,10 +6254,20 @@ static unsigned int selinux_ip_postroute(void *priv, if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) return NF_DROP; - if (secmark_active) - if (avc_has_perm(peer_sid, skb->secmark, + if (secmark_active) { + struct lsm_prop *prop; + u32 secmark = 0; + int err; + + if (skb->secmark) { + err = secxa_get_lsmprop(&prop, skb->secmark); + if (!err) + secmark = prop->selinux.secid; + } + if (avc_has_perm(peer_sid, secmark, SECCLASS_PACKET, secmark_perm, &ad)) return NF_DROP_ERR(-ECONNREFUSED); + } if (peerlbl_active) { u32 if_sid; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8e3ab61dfdd9..38bd97c89179 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -42,6 +42,7 @@ #include <linux/fs_context.h> #include <linux/fs_parser.h> #include <linux/watch_queue.h> +#include <linux/lsm_secxa.h> #include <linux/io_uring/cmd.h> #include <uapi/linux/lsm.h> #include "smack.h" @@ -4189,10 +4190,17 @@ static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip) #ifdef CONFIG_NETWORK_SECMARK static struct smack_known *smack_from_skb(struct sk_buff *skb) { + struct lsm_prop *prop; + int rc; + if (skb == NULL || skb->secmark == 0) return NULL; - return smack_from_secid(skb->secmark); + rc = secxa_get_lsmprop(&prop, skb->secmark); + if (prop) + return prop->smack.skp; + + return NULL; } #else static inline struct smack_known *smack_from_skb(struct sk_buff *skb) diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index b363c42f252e..5270b55eaacf 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -24,13 +24,15 @@ static unsigned int smack_ip_output(void *priv, const struct nf_hook_state *state) { struct sock *sk = skb_to_full_sk(skb); + struct lsm_prop prop = { }; struct socket_smack *ssp; - struct smack_known *skp; + int secxa; if (sk) { ssp = smack_sock(sk); - skp = ssp->smk_out; - secxa_set_secmark(skb, skp->smk_secid); + prop.smack.skp = ssp->smk_out; + secxa = secxa_from_lsmprop(&prop); + secxa_set_secmark(skb, secxa); } return NF_ACCEPT; -- 2.54.0