Introduce selinux_state_has_perm() for checking permissions on a (ssid, tsid) pair for a namespace and its ancestors when there is no cred available, e.g. for the networking checks. To support selinux_state_has_perm(), introduce a creator SID field in the selinux_state structure to save the SID of the process that created the namespace, or SECINITSID_KERNEL for the initial namespace. This SID is used as the subject SID for checks in the parent namespace. Some of the checks previously converted to using cred_ssid_has_perm() may be candidates for switching to this new helper even though a cred was available in those hooks. Signed-off-by: Stephen Smalley <stephen.smalley.work@xxxxxxxxx> --- security/selinux/avc.c | 37 +++++++++++++++ security/selinux/hooks.c | 72 ++++++++++++++++------------- security/selinux/include/avc.h | 4 ++ security/selinux/include/security.h | 3 +- security/selinux/selinuxfs.c | 3 +- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 250d56dae487..a533385d0149 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -1662,6 +1662,43 @@ int cred_other_has_perm(const struct cred *cred, const struct cred *other, return 0; } +/** + * selinux_state_has_perm - Check and audit permissions on a (ssid, tsid) pair + * @state: SELinux state + * @ssid: source security identifier + * @tsid: target security identifier + * @tclass: target security class + * @requested: requested permissions, interpreted based on @tclass + * @auditdata: auxiliary audit data + * + * Check permissions between a source SID @ssid and a target SID @tsid for + * @state and all ancestors to determine whether the @requested permissions + * are granted, interpreting the permissions based on @tclass. + * For the ancestor checks, use the SID of the creator of the namespace + * as the source SID of the check. + * Audit the granting or denial of permissions in accordance with the policy. + * Return %0 if all @requested permissions are granted, -%EACCES if any + * permissions are denied, or another -errno upon other errors. + * DO NOT USE when a cred is available; use cred_*_has_perm() instead. + */ +int selinux_state_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct common_audit_data *ad) +{ + int rc; + + do { + rc = avc_has_perm(state, ssid, tsid, tclass, requested, ad); + if (rc) + return rc; + + ssid = state->creator_sid; + state = state->parent; + } while (state); + + return 0; +} + u32 avc_policy_seqno(struct selinux_state *state) { return state->avc->avc_cache.latest_notif; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9e09bfff8392..3fd787bf5cc6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5158,16 +5158,16 @@ static int selinux_inet_sys_rcv_skb(struct selinux_state *state, err = sel_netif_sid(state, ns, ifindex, &if_sid); if (err) return err; - err = avc_has_perm(state, peer_sid, if_sid, - SECCLASS_NETIF, NETIF__INGRESS, ad); + err = selinux_state_has_perm(state, peer_sid, if_sid, + SECCLASS_NETIF, NETIF__INGRESS, ad); if (err) return err; err = sel_netnode_sid(state, addrp, family, &node_sid); if (err) return err; - return avc_has_perm(state, peer_sid, node_sid, SECCLASS_NODE, - NODE__RECVFROM, ad); + return selinux_state_has_perm(state, peer_sid, node_sid, SECCLASS_NODE, + NODE__RECVFROM, ad); } static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, @@ -5187,8 +5187,9 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, return err; if (selinux_secmark_enabled()) { - err = avc_has_perm(state, sk_sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, &ad); + err = selinux_state_has_perm(state, sk_sid, skb->secmark, + SECCLASS_PACKET, PACKET__RECV, + &ad); if (err) return err; } @@ -5248,8 +5249,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) selinux_netlbl_err(skb, family, err, 0); return err; } - err = avc_has_perm(state, sk_sid, peer_sid, SECCLASS_PEER, - PEER__RECV, &ad); + err = selinux_state_has_perm(state, sk_sid, peer_sid, + SECCLASS_PEER, PEER__RECV, &ad); if (err) { selinux_netlbl_err(skb, family, err, 0); return err; @@ -5257,8 +5258,9 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } if (secmark_active) { - err = avc_has_perm(state, sk_sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, &ad); + err = selinux_state_has_perm(state, sk_sid, skb->secmark, + SECCLASS_PACKET, PACKET__RECV, + &ad); if (err) return err; } @@ -5439,9 +5441,9 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc, * consistency among the peer SIDs. */ ad_net_init_from_sk(&ad, &net, asoc->base.sk); - err = avc_has_perm(state, sksec->peer_sid, asoc->peer_secid, - sksec->sclass, SCTP_SOCKET__ASSOCIATION, - &ad); + err = selinux_state_has_perm(state, sksec->peer_sid, + asoc->peer_secid, sksec->sclass, + SCTP_SOCKET__ASSOCIATION, &ad); if (err) return err; } @@ -5803,8 +5805,9 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb, } if (secmark_active) - if (avc_has_perm(se_state, peer_sid, skb->secmark, - SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) + if (selinux_state_has_perm(se_state, peer_sid, skb->secmark, + SECCLASS_PACKET, + PACKET__FORWARD_IN, &ad)) return NF_DROP; if (netlbl_enabled()) @@ -5885,8 +5888,9 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_DROP; if (selinux_secmark_enabled()) - if (avc_has_perm(sksec->state, sksec->sid, skb->secmark, - SECCLASS_PACKET, PACKET__SEND, &ad)) + if (selinux_state_has_perm(sksec->state, sksec->sid, + skb->secmark, SECCLASS_PACKET, + PACKET__SEND, &ad)) return NF_DROP_ERR(-ECONNREFUSED); if (selinux_xfrm_postroute_last(sksec->sid, skb, sksec->state, &ad, @@ -6012,8 +6016,8 @@ static unsigned int selinux_ip_postroute(void *priv, return NF_DROP; if (secmark_active) - if (avc_has_perm(se_state, peer_sid, skb->secmark, - SECCLASS_PACKET, secmark_perm, &ad)) + if (selinux_state_has_perm(se_state, peer_sid, skb->secmark, + SECCLASS_PACKET, secmark_perm, &ad)) return NF_DROP_ERR(-ECONNREFUSED); if (peerlbl_active) { @@ -6022,14 +6026,14 @@ static unsigned int selinux_ip_postroute(void *priv, if (sel_netif_sid(se_state, state->net, ifindex, &if_sid)) return NF_DROP; - if (avc_has_perm(se_state, peer_sid, if_sid, - SECCLASS_NETIF, NETIF__EGRESS, &ad)) + if (selinux_state_has_perm(se_state, peer_sid, if_sid, + SECCLASS_NETIF, NETIF__EGRESS, &ad)) return NF_DROP_ERR(-ECONNREFUSED); if (sel_netnode_sid(se_state, addrp, family, &node_sid)) return NF_DROP; - if (avc_has_perm(se_state, peer_sid, node_sid, - SECCLASS_NODE, NODE__SENDTO, &ad)) + if (selinux_state_has_perm(se_state, peer_sid, node_sid, + SECCLASS_NODE, NODE__SENDTO, &ad)) return NF_DROP_ERR(-ECONNREFUSED); } @@ -6912,10 +6916,10 @@ static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val) ibpkey.subnet_prefix = subnet_prefix; ibpkey.pkey = pkey_val; ad.u.ibpkey = &ibpkey; - return avc_has_perm(current_selinux_state, - sec->sid, sid, - SECCLASS_INFINIBAND_PKEY, - INFINIBAND_PKEY__ACCESS, &ad); + return selinux_state_has_perm(current_selinux_state, + sec->sid, sid, + SECCLASS_INFINIBAND_PKEY, + INFINIBAND_PKEY__ACCESS, &ad); } static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, @@ -6937,10 +6941,10 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, ibendport.dev_name = dev_name; ibendport.port = port_num; ad.u.ibendport = &ibendport; - return avc_has_perm(current_selinux_state, - sec->sid, sid, - SECCLASS_INFINIBAND_ENDPORT, - INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad); + return selinux_state_has_perm(current_selinux_state, + sec->sid, sid, + SECCLASS_INFINIBAND_ENDPORT, + INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad); } static int selinux_ib_alloc_security(void *ib_sec) @@ -7530,6 +7534,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { static void selinux_state_free(struct work_struct *work); int selinux_state_create(struct selinux_state *parent, + u32 creator_sid, struct selinux_state **state) { struct selinux_state *newstate; @@ -7539,6 +7544,8 @@ int selinux_state_create(struct selinux_state *parent, if (!newstate) return -ENOMEM; + newstate->creator_sid = creator_sid; + refcount_set(&newstate->count, 1); INIT_WORK(&newstate->work, selinux_state_free); @@ -7584,7 +7591,8 @@ static __init int selinux_init(void) { pr_info("SELinux: Initializing.\n"); - if (selinux_state_create(NULL, &init_selinux_state)) + if (selinux_state_create(NULL, SECINITSID_KERNEL, + &init_selinux_state)) panic("SELinux: Could not create initial namespace\n"); enforcing_set(init_selinux_state, selinux_enforcing_boot); diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 967848594270..95fed265071d 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -174,6 +174,10 @@ int cred_other_has_perm(const struct cred *cred, const struct cred *other, int task_obj_has_perm(const struct task_struct *s, const struct task_struct *t, u16 tclass, u32 requested, struct common_audit_data *ad); +int selinux_state_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct common_audit_data *ad); + u32 avc_policy_seqno(struct selinux_state *state); #define AVC_CALLBACK_GRANT 1 diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 261a6971f262..bf2d13227113 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -110,11 +110,12 @@ struct selinux_state { refcount_t count; struct work_struct work; + u32 creator_sid; /* SID of namespace creator */ } __randomize_layout; extern struct selinux_state *init_selinux_state; -int selinux_state_create(struct selinux_state *parent, +int selinux_state_create(struct selinux_state *parent, u32 creator_sid, struct selinux_state **state); void __put_selinux_state(struct selinux_state *state); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index c3308a5c168d..8c159b88615f 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -367,7 +367,8 @@ static ssize_t sel_write_unshare(struct file *file, const char __user *buf, goto out; } tsec = selinux_cred(cred); - if (selinux_state_create(state, &tsec->state)) { + if (selinux_state_create(state, current_sid(), + &tsec->state)) { abort_creds(cred); length = -ENOMEM; goto out; -- 2.47.1