diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 288 |
1 files changed, 75 insertions, 213 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4b34847208cc..5b5231068516 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -53,7 +53,6 @@ #include <net/ip.h> /* for local_port_range[] */ #include <net/sock.h> #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ -#include <net/inet_connection_sock.h> #include <net/net_namespace.h> #include <net/netlabel.h> #include <linux/uaccess.h> @@ -82,6 +81,7 @@ #include <linux/syslog.h> #include <linux/user_namespace.h> #include <linux/export.h> +#include <linux/security.h> #include <linux/msg.h> #include <linux/shm.h> @@ -95,6 +95,8 @@ #include "audit.h" #include "avc_ss.h" +#define NUM_SEL_MNT_OPTS 5 + extern struct security_operations *security_ops; /* SECMARK reference count */ @@ -137,28 +139,12 @@ static struct kmem_cache *sel_inode_cache; * This function checks the SECMARK reference counter to see if any SECMARK * targets are currently configured, if the reference counter is greater than * zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is - * enabled, false (0) if SECMARK is disabled. If the always_check_network - * policy capability is enabled, SECMARK is always considered enabled. + * enabled, false (0) if SECMARK is disabled. * */ static int selinux_secmark_enabled(void) { - return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount)); -} - -/** - * selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled - * - * Description: - * This function checks if NetLabel or labeled IPSEC is enabled. Returns true - * (1) if any are enabled or false (0) if neither are enabled. If the - * always_check_network policy capability is enabled, peer labeling - * is always considered enabled. - * - */ -static int selinux_peerlbl_enabled(void) -{ - return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); + return (atomic_read(&selinux_secmark_refcount) > 0); } /* @@ -233,14 +219,6 @@ static int inode_alloc_security(struct inode *inode) return 0; } -static void inode_free_rcu(struct rcu_head *head) -{ - struct inode_security_struct *isec; - - isec = container_of(head, struct inode_security_struct, rcu); - kmem_cache_free(sel_inode_cache, isec); -} - static void inode_free_security(struct inode *inode) { struct inode_security_struct *isec = inode->i_security; @@ -251,16 +229,8 @@ static void inode_free_security(struct inode *inode) list_del_init(&isec->list); spin_unlock(&sbsec->isec_lock); - /* - * The inode may still be referenced in a path walk and - * a call to selinux_inode_permission() can be made - * after inode_free_security() is called. Ideally, the VFS - * wouldn't do this, but fixing that is a much harder - * job. For now, simply free the i_security via RCU, and - * leave the current inode->i_security pointer intact. - * The inode will be freed after the RCU grace period too. - */ - call_rcu(&isec->rcu, inode_free_rcu); + inode->i_security = NULL; + kmem_cache_free(sel_inode_cache, isec); } static int file_alloc_security(struct file *file) @@ -339,11 +309,8 @@ enum { Opt_defcontext = 3, Opt_rootcontext = 4, Opt_labelsupport = 5, - Opt_nextmntopt = 6, }; -#define NUM_SEL_MNT_OPTS (Opt_nextmntopt - 1) - static const match_table_t tokens = { {Opt_context, CONTEXT_STR "%s"}, {Opt_fscontext, FSCONTEXT_STR "%s"}, @@ -388,29 +355,6 @@ static int may_context_mount_inode_relabel(u32 sid, return rc; } -static int selinux_is_sblabel_mnt(struct super_block *sb) -{ - struct superblock_security_struct *sbsec = sb->s_security; - - if (sbsec->behavior == SECURITY_FS_USE_XATTR || - sbsec->behavior == SECURITY_FS_USE_TRANS || - sbsec->behavior == SECURITY_FS_USE_TASK) - return 1; - - /* Special handling for sysfs. Is genfs but also has setxattr handler*/ - if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) - return 1; - - /* - * Special handling for rootfs. Is genfs but supports - * setting SELinux context on in-core inodes. - */ - if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) - return 1; - - return 0; -} - static int sb_finish_set_opts(struct super_block *sb) { struct superblock_security_struct *sbsec = sb->s_security; @@ -444,6 +388,8 @@ static int sb_finish_set_opts(struct super_block *sb) } } + sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP); + if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n", sb->s_id, sb->s_type->name); @@ -452,9 +398,15 @@ static int sb_finish_set_opts(struct super_block *sb) sb->s_id, sb->s_type->name, labeling_behaviors[sbsec->behavior-1]); - sbsec->flags |= SE_SBINITIALIZED; - if (selinux_is_sblabel_mnt(sb)) - sbsec->flags |= SBLABEL_MNT; + if (sbsec->behavior == SECURITY_FS_USE_GENFS || + sbsec->behavior == SECURITY_FS_USE_MNTPOINT || + sbsec->behavior == SECURITY_FS_USE_NONE || + sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) + sbsec->flags &= ~SE_SBLABELSUPP; + + /* Special handling for sysfs. Is genfs but also has setxattr handler*/ + if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) + sbsec->flags |= SE_SBLABELSUPP; /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); @@ -508,18 +460,15 @@ static int selinux_get_mnt_opts(const struct super_block *sb, if (!ss_initialized) return -EINVAL; - /* make sure we always check enough bits to cover the mask */ - BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS)); - tmp = sbsec->flags & SE_MNTMASK; /* count the number of mount options for this sb */ - for (i = 0; i < NUM_SEL_MNT_OPTS; i++) { + for (i = 0; i < 8; i++) { if (tmp & 0x01) opts->num_mnt_opts++; tmp >>= 1; } /* Check if the Label support flag is set */ - if (sbsec->flags & SBLABEL_MNT) + if (sbsec->flags & SE_SBLABELSUPP) opts->num_mnt_opts++; opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC); @@ -566,9 +515,9 @@ static int selinux_get_mnt_opts(const struct super_block *sb, opts->mnt_opts[i] = context; opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT; } - if (sbsec->flags & SBLABEL_MNT) { + if (sbsec->flags & SE_SBLABELSUPP) { opts->mnt_opts[i] = NULL; - opts->mnt_opts_flags[i++] = SBLABEL_MNT; + opts->mnt_opts_flags[i++] = SE_SBLABELSUPP; } BUG_ON(i != opts->num_mnt_opts); @@ -665,7 +614,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, for (i = 0; i < num_opts; i++) { u32 sid; - if (flags[i] == SBLABEL_MNT) + if (flags[i] == SE_SBLABELSUPP) continue; rc = security_context_to_sid(mount_options[i], strlen(mount_options[i]), &sid); @@ -736,7 +685,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, * Determine the labeling behavior to use for this * filesystem type. */ - rc = security_fs_use(sb); + rc = security_fs_use((sbsec->flags & SE_SBPROC) ? + "proc" : sb->s_type->name, + &sbsec->behavior, &sbsec->sid); if (rc) { printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", @@ -1086,7 +1037,7 @@ static void selinux_write_opts(struct seq_file *m, case DEFCONTEXT_MNT: prefix = DEFCONTEXT_STR; break; - case SBLABEL_MNT: + case SE_SBLABELSUPP: seq_putc(m, ','); seq_puts(m, LABELSUPP_STR); continue; @@ -1698,7 +1649,7 @@ static int may_create(struct inode *dir, if (rc) return rc; - if (!newsid || !(sbsec->flags & SBLABEL_MNT)) { + if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) { rc = security_transition_sid(sid, dsec->sid, tclass, &dentry->d_name, &newsid); if (rc) @@ -2486,7 +2437,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data) u32 sid; size_t len; - if (flags[i] == SBLABEL_MNT) + if (flags[i] == SE_SBLABELSUPP) continue; len = strlen(mount_options[i]); rc = security_context_to_sid(mount_options[i], len, &sid); @@ -2655,7 +2606,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, if ((sbsec->flags & SE_SBINITIALIZED) && (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) newsid = sbsec->mntpoint_sid; - else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) { + else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) { rc = security_transition_sid(sid, dsec->sid, inode_mode_to_security_class(inode->i_mode), qstr, &newsid); @@ -2677,7 +2628,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, isec->initialized = 1; } - if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT)) + if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) return -EOPNOTSUPP; if (name) @@ -2879,7 +2830,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return selinux_inode_setotherxattr(dentry, name); sbsec = inode->i_sb->s_security; - if (!(sbsec->flags & SBLABEL_MNT)) + if (!(sbsec->flags & SE_SBLABELSUPP)) return -EOPNOTSUPP; if (!inode_owner_or_capable(inode)) @@ -3840,12 +3791,8 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) u32 nlbl_sid; u32 nlbl_type; - err = selinux_xfrm_skb_sid(skb, &xfrm_sid); - if (unlikely(err)) - return -EACCES; - err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); - if (unlikely(err)) - return -EACCES; + selinux_skb_xfrm_sid(skb, &xfrm_sid); + selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid); if (unlikely(err)) { @@ -3858,30 +3805,6 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) return 0; } -/** - * selinux_conn_sid - Determine the child socket label for a connection - * @sk_sid: the parent socket's SID - * @skb_sid: the packet's SID - * @conn_sid: the resulting connection SID - * - * If @skb_sid is valid then the user:role:type information from @sk_sid is - * combined with the MLS information from @skb_sid in order to create - * @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy - * of @sk_sid. Returns zero on success, negative values on failure. - * - */ -static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) -{ - int err = 0; - - if (skb_sid != SECSID_NULL) - err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); - else - *conn_sid = sk_sid; - - return err; -} - /* socket security operations */ static int socket_sockcreate_sid(const struct task_security_struct *tsec, @@ -4005,7 +3928,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (snum) { int low, high; - inet_get_local_port_range(sock_net(sk), &low, &high); + inet_get_local_port_range(&low, &high); if (snum < max(PROT_SOCK, low) || snum > high) { err = sel_netport_sid(sk->sk_protocol, @@ -4323,7 +4246,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return selinux_sock_rcv_skb_compat(sk, skb, family); secmark_active = selinux_secmark_enabled(); - peerlbl_active = selinux_peerlbl_enabled(); + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); if (!secmark_active && !peerlbl_active) return 0; @@ -4349,10 +4272,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); - if (err) { + if (err) selinux_netlbl_err(skb, err, 0); - return err; - } } if (secmark_active) { @@ -4489,18 +4410,27 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, { struct sk_security_struct *sksec = sk->sk_security; int err; - u16 family = req->rsk_ops->family; - u32 connsid; + u16 family = sk->sk_family; + u32 newsid; u32 peersid; + /* handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + err = selinux_skb_peerlbl_sid(skb, family, &peersid); if (err) return err; - err = selinux_conn_sid(sksec->sid, peersid, &connsid); - if (err) - return err; - req->secid = connsid; - req->peer_secid = peersid; + if (peersid == SECSID_NULL) { + req->secid = sksec->sid; + req->peer_secid = SECSID_NULL; + } else { + err = security_sid_mls_copy(sksec->sid, peersid, &newsid); + if (err) + return err; + req->secid = newsid; + req->peer_secid = peersid; + } return selinux_netlbl_inet_conn_request(req, family); } @@ -4698,7 +4628,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, secmark_active = selinux_secmark_enabled(); netlbl_active = netlbl_enabled(); - peerlbl_active = selinux_peerlbl_enabled(); + peerlbl_active = netlbl_active || selinux_xfrm_enabled(); if (!secmark_active && !peerlbl_active) return NF_ACCEPT; @@ -4737,7 +4667,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, return NF_ACCEPT; } -static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, +static unsigned int selinux_ipv4_forward(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4747,7 +4677,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, +static unsigned int selinux_ipv6_forward(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4760,7 +4690,6 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, static unsigned int selinux_ip_output(struct sk_buff *skb, u16 family) { - struct sock *sk; u32 sid; if (!netlbl_enabled()) @@ -4769,27 +4698,8 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, /* we do this in the LOCAL_OUT path and not the POST_ROUTING path * because we want to make sure we apply the necessary labeling * before IPsec is applied so we can leverage AH protection */ - sk = skb->sk; - if (sk) { - struct sk_security_struct *sksec; - - if (sk->sk_state == TCP_LISTEN) - /* if the socket is the listening state then this - * packet is a SYN-ACK packet which means it needs to - * be labeled based on the connection/request_sock and - * not the parent socket. unfortunately, we can't - * lookup the request_sock yet as it isn't queued on - * the parent socket until after the SYN-ACK is sent. - * the "solution" is to simply pass the packet as-is - * as any IP option based labeling should be copied - * from the initial connection request (in the IP - * layer). it is far from ideal, but until we get a - * security label in the packet itself this is the - * best we can do. */ - return NF_ACCEPT; - - /* standard practice, label using the parent socket */ - sksec = sk->sk_security; + if (skb->sk) { + struct sk_security_struct *sksec = skb->sk->sk_security; sid = sksec->sid; } else sid = SECINITSID_KERNEL; @@ -4799,7 +4709,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops, +static unsigned int selinux_ipv4_output(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4859,36 +4769,27 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, * as fast and as clean as possible. */ if (!selinux_policycap_netpeer) return selinux_ip_postroute_compat(skb, ifindex, family); - - secmark_active = selinux_secmark_enabled(); - peerlbl_active = selinux_peerlbl_enabled(); - if (!secmark_active && !peerlbl_active) - return NF_ACCEPT; - - sk = skb->sk; - #ifdef CONFIG_XFRM /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec * packet transformation so allow the packet to pass without any checks * since we'll have another chance to perform access control checks * when the packet is on it's final way out. * NOTE: there appear to be some IPv6 multicast cases where skb->dst - * is NULL, in this case go ahead and apply access control. - * NOTE: if this is a local socket (skb->sk != NULL) that is in the - * TCP listening state we cannot wait until the XFRM processing - * is done as we will miss out on the SA label if we do; - * unfortunately, this means more work, but it is only once per - * connection. */ - if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && - !(sk != NULL && sk->sk_state == TCP_LISTEN)) + * is NULL, in this case go ahead and apply access control. */ + if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL) return NF_ACCEPT; #endif + secmark_active = selinux_secmark_enabled(); + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); + if (!secmark_active && !peerlbl_active) + return NF_ACCEPT; + /* if the packet is being forwarded then get the peer label from the + * packet itself; otherwise check to see if it is from a local + * application or the kernel, if from an application get the peer label + * from the sending socket, otherwise use the kernel's sid */ + sk = skb->sk; if (sk == NULL) { - /* Without an associated socket the packet is either coming - * from the kernel or it is being forwarded; check the packet - * to determine which and if the packet is being forwarded - * query the packet directly to determine the security label. */ if (skb->skb_iif) { secmark_perm = PACKET__FORWARD_OUT; if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) @@ -4897,45 +4798,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; peer_sid = SECINITSID_KERNEL; } - } else if (sk->sk_state == TCP_LISTEN) { - /* Locally generated packet but the associated socket is in the - * listening state which means this is a SYN-ACK packet. In - * this particular case the correct security label is assigned - * to the connection/request_sock but unfortunately we can't - * query the request_sock as it isn't queued on the parent - * socket until after the SYN-ACK packet is sent; the only - * viable choice is to regenerate the label like we do in - * selinux_inet_conn_request(). See also selinux_ip_output() - * for similar problems. */ - u32 skb_sid; - struct sk_security_struct *sksec = sk->sk_security; - if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) - return NF_DROP; - /* At this point, if the returned skb peerlbl is SECSID_NULL - * and the packet has been through at least one XFRM - * transformation then we must be dealing with the "final" - * form of labeled IPsec packet; since we've already applied - * all of our access controls on this packet we can safely - * pass the packet. */ - if (skb_sid == SECSID_NULL) { - switch (family) { - case PF_INET: - if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) - return NF_ACCEPT; - break; - case PF_INET6: - if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) - return NF_ACCEPT; - default: - return NF_DROP_ERR(-ECONNREFUSED); - } - } - if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) - return NF_DROP; - secmark_perm = PACKET__SEND; } else { - /* Locally generated packet, fetch the security label from the - * associated socket. */ struct sk_security_struct *sksec = sk->sk_security; peer_sid = sksec->sid; secmark_perm = PACKET__SEND; @@ -4973,7 +4836,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, return NF_ACCEPT; } -static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, +static unsigned int selinux_ipv4_postroute(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4983,7 +4846,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, +static unsigned int selinux_ipv6_postroute(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -5599,11 +5462,11 @@ static int selinux_setprocattr(struct task_struct *p, /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and fail. */ ptsid = 0; - rcu_read_lock(); + task_lock(p); tracer = ptrace_parent(p); if (tracer) ptsid = task_sid(tracer); - rcu_read_unlock(); + task_unlock(p); if (tracer) { error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, @@ -5921,8 +5784,7 @@ static struct security_operations selinux_ops = { .xfrm_policy_clone_security = selinux_xfrm_policy_clone, .xfrm_policy_free_security = selinux_xfrm_policy_free, .xfrm_policy_delete_security = selinux_xfrm_policy_delete, - .xfrm_state_alloc = selinux_xfrm_state_alloc, - .xfrm_state_alloc_acquire = selinux_xfrm_state_alloc_acquire, + .xfrm_state_alloc_security = selinux_xfrm_state_alloc, .xfrm_state_free_security = selinux_xfrm_state_free, .xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_policy_lookup = selinux_xfrm_policy_lookup, |