diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index b3a6187e5305..a9d3a1794b23 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1402,6 +1402,15 @@ running once the system is up. If enabled at boot time, /selinux/disable can be used later to disable prior to initial policy load. + selinux_compat_net = + [SELINUX] Set initial selinux_compat_net flag value. + Format: { "0" | "1" } + 0 -- use new secmark-based packet controls + 1 -- use legacy packet controls + Default value is 0 (preferred). + Value can be changed at runtime via + /selinux/compat_net. + serialnumber [BUGS=IA-32] sg_def_reserved_size= [SCSI] diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index f636f53ca544..814ddc42f1f4 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -1,6 +1,7 @@ config SECURITY_SELINUX bool "NSA SELinux Support" depends on SECURITY_NETWORK && AUDIT && NET && INET + select NETWORK_SECMARK default n help This selects NSA Security-Enhanced Linux (SELinux). @@ -95,3 +96,31 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE via /selinux/checkreqprot if authorized by policy. If you are unsure how to answer this question, answer 1. + +config SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT + bool "NSA SELinux enable new secmark network controls by default" + depends on SECURITY_SELINUX + default n + help + This option determines whether the new secmark-based network + controls will be enabled by default. If not, the old internal + per-packet controls will be enabled by default, preserving + old behavior. + + If you enable the new controls, you will need updated + SELinux userspace libraries, tools and policy. Typically, + your distribution will provide these and enable the new controls + in the kernel they also distribute. + + Note that this option can be overriden at boot with the + selinux_compat_net parameter, and after boot via + /selinux/compat_net. See Documentation/kernel-parameters.txt + for details on this parameter. + + If you enable the new network controls, you will likely + also require the SECMARK and CONNSECMARK targets, as + well as any conntrack helpers for protocols which you + wish to control. + + If you are unsure what do do here, select N. + diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 41b6f5d31945..54adc9d31e92 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -80,6 +80,7 @@ extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); +extern int selinux_compat_net; #ifdef CONFIG_SECURITY_SELINUX_DEVELOP int selinux_enforcing = 0; @@ -3216,47 +3217,17 @@ static int selinux_socket_unix_may_send(struct socket *sock, return 0; } -static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, + struct avc_audit_data *ad, u32 sock_sid, u16 sock_class, + u16 family, char *addrp, int len) { - u16 family; - char *addrp; - int len, err = 0; + int err = 0; u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; - u32 sock_sid = 0; - u16 sock_class = 0; - struct socket *sock; - struct net_device *dev; - struct avc_audit_data ad; - family = sk->sk_family; - if (family != PF_INET && family != PF_INET6) + if (!skb->dev) goto out; - /* Handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) - family = PF_INET; - - read_lock_bh(&sk->sk_callback_lock); - sock = sk->sk_socket; - if (sock) { - struct inode *inode; - inode = SOCK_INODE(sock); - if (inode) { - struct inode_security_struct *isec; - isec = inode->i_security; - sock_sid = isec->sid; - sock_class = isec->sclass; - } - } - read_unlock_bh(&sk->sk_callback_lock); - if (!sock_sid) - goto out; - - dev = skb->dev; - if (!dev) - goto out; - - err = sel_netif_sids(dev, &if_sid, NULL); + err = sel_netif_sids(skb->dev, &if_sid, NULL); if (err) goto out; @@ -3279,44 +3250,88 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) break; } - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); - if (err) - goto out; - - err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad); + err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); if (err) goto out; - /* Fixme: this lookup is inefficient */ err = security_node_sid(family, addrp, len, &node_sid); if (err) goto out; - err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad); + err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad); if (err) goto out; if (recv_perm) { u32 port_sid; - /* Fixme: make this more efficient */ err = security_port_sid(sk->sk_family, sk->sk_type, - sk->sk_protocol, ntohs(ad.u.net.sport), + sk->sk_protocol, ntohs(ad->u.net.sport), &port_sid); if (err) goto out; err = avc_has_perm(sock_sid, port_sid, - sock_class, recv_perm, &ad); + sock_class, recv_perm, ad); } - if (!err) - err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); +out: + return err; +} +static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + u16 family; + u16 sock_class = 0; + char *addrp; + int len, err = 0; + u32 sock_sid = 0; + struct socket *sock; + struct avc_audit_data ad; + + family = sk->sk_family; + if (family != PF_INET && family != PF_INET6) + goto out; + + /* Handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) + family = PF_INET; + + read_lock_bh(&sk->sk_callback_lock); + sock = sk->sk_socket; + if (sock) { + struct inode *inode; + inode = SOCK_INODE(sock); + if (inode) { + struct inode_security_struct *isec; + isec = inode->i_security; + sock_sid = isec->sid; + sock_class = isec->sclass; + } + } + read_unlock_bh(&sk->sk_callback_lock); + if (!sock_sid) + goto out; + + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]"; + ad.u.net.family = family; + + err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); + if (err) + goto out; + + if (selinux_compat_net) + err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid, + sock_class, family, + addrp, len); + else + err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + if (err) + goto out; + + err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); out: return err; } @@ -3456,42 +3471,18 @@ out: #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_postroute_last(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *), - u16 family) +static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev, + struct inode_security_struct *isec, + struct avc_audit_data *ad, + u16 family, char *addrp, int len) { - char *addrp; - int len, err = NF_ACCEPT; + int err; u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; - struct sock *sk; - struct socket *sock; - struct inode *inode; - struct sk_buff *skb = *pskb; - struct inode_security_struct *isec; - struct avc_audit_data ad; - struct net_device *dev = (struct net_device *)out; - sk = skb->sk; - if (!sk) - goto out; - - sock = sk->sk_socket; - if (!sock) - goto out; - - inode = SOCK_INODE(sock); - if (!inode) - goto out; - err = sel_netif_sids(dev, &if_sid, NULL); if (err) goto out; - isec = inode->i_security; - switch (isec->sclass) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_SEND; @@ -3511,55 +3502,88 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, break; } - - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, - &len, 0) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; - - err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, - netif_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + if (err) goto out; - /* Fixme: this lookup is inefficient */ - err = security_node_sid(family, addrp, len, - &node_sid) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = security_node_sid(family, addrp, len, &node_sid); + if (err) goto out; - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, - node_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad); + if (err) goto out; if (send_perm) { u32 port_sid; - /* Fixme: make this more efficient */ err = security_port_sid(sk->sk_family, sk->sk_type, sk->sk_protocol, - ntohs(ad.u.net.dport), - &port_sid) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + ntohs(ad->u.net.dport), + &port_sid); + if (err) goto out; err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, &ad) ? NF_DROP : NF_ACCEPT; + send_perm, ad); } +out: + return err; +} - if (err != NF_ACCEPT) +static unsigned int selinux_ip_postroute_last(unsigned int hooknum, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *), + u16 family) +{ + char *addrp; + int len, err = 0; + struct sock *sk; + struct socket *sock; + struct inode *inode; + struct sk_buff *skb = *pskb; + struct inode_security_struct *isec; + struct avc_audit_data ad; + struct net_device *dev = (struct net_device *)out; + + sk = skb->sk; + if (!sk) + goto out; + + sock = sk->sk_socket; + if (!sock) + goto out; + + inode = SOCK_INODE(sock); + if (!inode) + goto out; + + isec = inode->i_security; + + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = dev->name; + ad.u.net.family = family; + + err = selinux_parse_skb(skb, &ad, &addrp, &len, 0); + if (err) + goto out; + + if (selinux_compat_net) + err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad, + family, addrp, len); + else + err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET, + PACKET__SEND, &ad); + + if (err) goto out; err = selinux_xfrm_postroute_last(isec->sid, skb); - out: - return err; + return err ? NF_DROP : NF_ACCEPT; } static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index f0f4e480ff99..c96498a10eb8 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -51,7 +51,7 @@ static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb) static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) { - return NF_ACCEPT; + return 0; } static inline int selinux_socket_getpeer_stream(struct sock *sk) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index a4efc966f065..2e73d3279f2d 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -38,6 +38,14 @@ unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; +#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT +#define SELINUX_COMPAT_NET_VALUE 0 +#else +#define SELINUX_COMPAT_NET_VALUE 1 +#endif + +int selinux_compat_net = SELINUX_COMPAT_NET_VALUE; + static int __init checkreqprot_setup(char *str) { selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; @@ -45,6 +53,13 @@ static int __init checkreqprot_setup(char *str) } __setup("checkreqprot=", checkreqprot_setup); +static int __init selinux_compat_net_setup(char *str) +{ + selinux_compat_net = simple_strtoul(str,NULL,0) ? 1 : 0; + return 1; +} +__setup("selinux_compat_net=", selinux_compat_net_setup); + static DEFINE_MUTEX(sel_mutex); @@ -85,6 +100,7 @@ enum sel_inos { SEL_AVC, /* AVC management directory */ SEL_MEMBER, /* compute polyinstantiation membership decision */ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ + SEL_COMPAT_NET, /* whether to use old compat network packet controls */ }; #define TMPBUFLEN 12 @@ -364,6 +380,55 @@ static struct file_operations sel_checkreqprot_ops = { .write = sel_write_checkreqprot, }; +static ssize_t sel_read_compat_net(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmpbuf[TMPBUFLEN]; + ssize_t length; + + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_compat_net); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t sel_write_compat_net(struct file * file, const char __user * buf, + size_t count, loff_t *ppos) +{ + char *page; + ssize_t length; + int new_value; + + length = task_has_security(current, SECURITY__LOAD_POLICY); + if (length) + return length; + + if (count >= PAGE_SIZE) + return -ENOMEM; + if (*ppos != 0) { + /* No partial writes. */ + return -EINVAL; + } + page = (char*)get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + length = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + length = -EINVAL; + if (sscanf(page, "%d", &new_value) != 1) + goto out; + + selinux_compat_net = new_value ? 1 : 0; + length = count; +out: + free_page((unsigned long) page); + return length; +} +static struct file_operations sel_compat_net_ops = { + .read = sel_read_compat_net, + .write = sel_write_compat_net, +}; + /* * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c */ @@ -1219,6 +1284,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, + [SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 0e24df41099f..6633fb059313 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -387,18 +387,12 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) struct xfrm_state *x = dst_test->xfrm; if (x && selinux_authorizable_xfrm(x)) - goto accept; + goto out; } } rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, NULL); - if (rc) - goto drop; - -accept: - return NF_ACCEPT; - -drop: - return NF_DROP; +out: + return rc; }