diff options
Diffstat (limited to 'security')
29 files changed, 750 insertions, 736 deletions
diff --git a/security/dummy.c b/security/dummy.c index 43874c1e6e23..558795b237d6 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -828,6 +828,11 @@ static inline void dummy_inet_csk_clone(struct sock *newsk, { } +static inline void dummy_inet_conn_established(struct sock *sk, + struct sk_buff *skb) +{ +} + static inline void dummy_req_classify_flow(const struct request_sock *req, struct flowi *fl) { @@ -836,7 +841,7 @@ static inline void dummy_req_classify_flow(const struct request_sock *req, #ifdef CONFIG_SECURITY_NETWORK_XFRM static int dummy_xfrm_policy_alloc_security(struct xfrm_policy *xp, - struct xfrm_user_sec_ctx *sec_ctx, struct sock *sk) + struct xfrm_user_sec_ctx *sec_ctx) { return 0; } @@ -856,7 +861,7 @@ static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp) } static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, - struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid) + struct xfrm_user_sec_ctx *sec_ctx, u32 secid) { return 0; } @@ -881,12 +886,6 @@ static int dummy_xfrm_state_pol_flow_match(struct xfrm_state *x, return 1; } -static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, - struct xfrm_policy *xp) -{ - return 1; -} - static int dummy_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall) { return 0; @@ -1108,6 +1107,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, sock_graft); set_to_dummy_if_null(ops, inet_conn_request); set_to_dummy_if_null(ops, inet_csk_clone); + set_to_dummy_if_null(ops, inet_conn_established); set_to_dummy_if_null(ops, req_classify_flow); #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM @@ -1120,7 +1120,6 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, xfrm_state_delete_security); set_to_dummy_if_null(ops, xfrm_policy_lookup); set_to_dummy_if_null(ops, xfrm_state_pol_flow_match); - set_to_dummy_if_null(ops, xfrm_flow_state_match); set_to_dummy_if_null(ops, xfrm_decode_session); #endif /* CONFIG_SECURITY_NETWORK_XFRM */ #ifdef CONFIG_KEYS diff --git a/security/keys/key.c b/security/keys/key.c index 80de8c3e9cc3..ac9326c5f1da 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -20,7 +20,7 @@ #include <linux/err.h> #include "internal.h" -static kmem_cache_t *key_jar; +static struct kmem_cache *key_jar; struct rb_root key_serial_tree; /* tree of keys indexed by serial */ DEFINE_SPINLOCK(key_serial_lock); @@ -30,8 +30,8 @@ DEFINE_SPINLOCK(key_user_lock); static LIST_HEAD(key_types_list); static DECLARE_RWSEM(key_types_sem); -static void key_cleanup(void *data); -static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL); +static void key_cleanup(struct work_struct *work); +static DECLARE_WORK(key_cleanup_task, key_cleanup); /* we serialise key instantiation and link */ DECLARE_RWSEM(key_construction_sem); @@ -285,16 +285,14 @@ struct key *key_alloc(struct key_type *type, const char *desc, } /* allocate and initialise the key and its description */ - key = kmem_cache_alloc(key_jar, SLAB_KERNEL); + key = kmem_cache_alloc(key_jar, GFP_KERNEL); if (!key) goto no_memory_2; if (desc) { - key->description = kmalloc(desclen, GFP_KERNEL); + key->description = kmemdup(desc, desclen, GFP_KERNEL); if (!key->description) goto no_memory_3; - - memcpy(key->description, desc, desclen); } atomic_set(&key->usage, 1); @@ -552,7 +550,7 @@ EXPORT_SYMBOL(key_negate_and_link); * do cleaning up in process context so that we don't have to disable * interrupts all over the place */ -static void key_cleanup(void *data) +static void key_cleanup(struct work_struct *work) { struct rb_node *_n; struct key *key; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index e8d02acc51e7..ad45ce73964b 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -706,12 +706,10 @@ int __key_link(struct key *keyring, struct key *key) BUG_ON(size > PAGE_SIZE); ret = -ENOMEM; - nklist = kmalloc(size, GFP_KERNEL); + nklist = kmemdup(klist, size, GFP_KERNEL); if (!nklist) goto error2; - memcpy(nklist, klist, size); - /* replace matched key */ atomic_inc(&key->usage); nklist->keys[loop] = key; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 32150cf7c37f..b6f86808475a 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -27,7 +27,7 @@ static DEFINE_MUTEX(key_session_mutex); struct key_user root_key_user = { .usage = ATOMIC_INIT(3), .consq = LIST_HEAD_INIT(root_key_user.consq), - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock), .nkeys = ATOMIC_INIT(2), .nikeys = ATOMIC_INIT(2), .uid = 0, diff --git a/security/selinux/avc.c b/security/selinux/avc.c index a300702da527..e7c0b5e2066b 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -32,12 +32,7 @@ #include "avc.h" #include "avc_ss.h" -static const struct av_perm_to_string -{ - u16 tclass; - u32 value; - const char *name; -} av_perm_to_string[] = { +static const struct av_perm_to_string av_perm_to_string[] = { #define S_(c, v, s) { c, v, s }, #include "av_perm_to_string.h" #undef S_ @@ -57,17 +52,21 @@ static const char *class_to_string[] = { #undef TE_ #undef S_ -static const struct av_inherit -{ - u16 tclass; - const char **common_pts; - u32 common_base; -} av_inherit[] = { +static const struct av_inherit av_inherit[] = { #define S_(c, i, b) { c, common_##i##_perm_to_string, b }, #include "av_inherit.h" #undef S_ }; +const struct selinux_class_perm selinux_class_perm = { + av_perm_to_string, + ARRAY_SIZE(av_perm_to_string), + class_to_string, + ARRAY_SIZE(class_to_string), + av_inherit, + ARRAY_SIZE(av_inherit) +}; + #define AVC_CACHE_SLOTS 512 #define AVC_DEF_CACHE_THRESHOLD 512 #define AVC_CACHE_RECLAIM 16 @@ -125,7 +124,7 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; static struct avc_cache avc_cache; static struct avc_callback_node *avc_callbacks; -static kmem_cache_t *avc_node_cachep; +static struct kmem_cache *avc_node_cachep; static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) { @@ -333,7 +332,7 @@ static struct avc_node *avc_alloc_node(void) { struct avc_node *node; - node = kmem_cache_alloc(avc_node_cachep, SLAB_ATOMIC); + node = kmem_cache_alloc(avc_node_cachep, GFP_ATOMIC); if (!node) goto out; @@ -497,7 +496,7 @@ static inline void avc_print_ipv6_addr(struct audit_buffer *ab, audit_log_format(ab, " %s=%d", name2, ntohs(port)); } -static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr, +static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr, __be16 port, char *name1, char *name2) { if (addr) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8ab5679a37a3..44e9cd470543 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -58,6 +58,7 @@ #include <linux/netlink.h> #include <linux/tcp.h> #include <linux/udp.h> +#include <linux/dccp.h> #include <linux/quota.h> #include <linux/un.h> /* for Unix socket types */ #include <net/af_unix.h> /* for Unix socket types */ @@ -123,7 +124,7 @@ static struct security_operations *secondary_ops = NULL; static LIST_HEAD(superblock_security_head); static DEFINE_SPINLOCK(sb_security_lock); -static kmem_cache_t *sel_inode_cache; +static struct kmem_cache *sel_inode_cache; /* Return security context for a given sid or just the context length if the buffer is null or length is 0 */ @@ -180,7 +181,7 @@ static int inode_alloc_security(struct inode *inode) struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; - isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL); + isec = kmem_cache_alloc(sel_inode_cache, GFP_KERNEL); if (!isec) return -ENOMEM; @@ -751,6 +752,8 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_UDP_SOCKET; else return SECCLASS_RAWIP_SOCKET; + case SOCK_DCCP: + return SECCLASS_DCCP_SOCKET; default: return SECCLASS_RAWIP_SOCKET; } @@ -1754,7 +1757,8 @@ static inline void flush_unauthorized_files(struct files_struct * files) get_file(devnull); } else { devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); - if (!devnull) { + if (IS_ERR(devnull)) { + devnull = NULL; put_unused_fd(fd); fput(file); continue; @@ -2888,7 +2892,8 @@ static void selinux_task_to_inode(struct task_struct *p, } /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv4(struct sk_buff *skb, + struct avc_audit_data *ad, u8 *proto) { int offset, ihlen, ret = -EINVAL; struct iphdr _iph, *ih; @@ -2906,6 +2911,9 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad ad->u.net.v4info.daddr = ih->daddr; ret = 0; + if (proto) + *proto = ih->protocol; + switch (ih->protocol) { case IPPROTO_TCP: { struct tcphdr _tcph, *th; @@ -2939,6 +2947,22 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad break; } + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + offset += ihlen; + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net.sport = dh->dccph_sport; + ad->u.net.dport = dh->dccph_dport; + break; + } + default: break; } @@ -2949,7 +2973,8 @@ out: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv6(struct sk_buff *skb, + struct avc_audit_data *ad, u8 *proto) { u8 nexthdr; int ret = -EINVAL, offset; @@ -2970,6 +2995,9 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad if (offset < 0) goto out; + if (proto) + *proto = nexthdr; + switch (nexthdr) { case IPPROTO_TCP: { struct tcphdr _tcph, *th; @@ -2995,6 +3023,18 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad break; } + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net.sport = dh->dccph_sport; + ad->u.net.dport = dh->dccph_dport; + break; + } + /* includes fragments */ default: break; @@ -3006,13 +3046,13 @@ out: #endif /* IPV6 */ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, - char **addrp, int *len, int src) + char **addrp, int *len, int src, u8 *proto) { int ret = 0; switch (ad->u.net.family) { case PF_INET: - ret = selinux_parse_skb_ipv4(skb, ad); + ret = selinux_parse_skb_ipv4(skb, ad, proto); if (ret || !addrp) break; *len = 4; @@ -3022,7 +3062,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case PF_INET6: - ret = selinux_parse_skb_ipv6(skb, ad); + ret = selinux_parse_skb_ipv6(skb, ad, proto); if (ret || !addrp) break; *len = 16; @@ -3100,9 +3140,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, if (sock->sk) { sksec = sock->sk->sk_security; sksec->sid = isec->sid; - err = selinux_netlbl_socket_post_create(sock, - family, - isec->sid); + err = selinux_netlbl_socket_post_create(sock); } return err; @@ -3179,7 +3217,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in case SECCLASS_UDP_SOCKET: node_perm = UDP_SOCKET__NODE_BIND; break; - + + case SECCLASS_DCCP_SOCKET: + node_perm = DCCP_SOCKET__NODE_BIND; + break; + default: node_perm = RAWIP_SOCKET__NODE_BIND; break; @@ -3217,16 +3259,17 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, return err; /* - * If a TCP socket, check name_connect permission for the port. + * If a TCP or DCCP socket, check name_connect permission for the port. */ isec = SOCK_INODE(sock)->i_security; - if (isec->sclass == SECCLASS_TCP_SOCKET) { + if (isec->sclass == SECCLASS_TCP_SOCKET || + isec->sclass == SECCLASS_DCCP_SOCKET) { struct sock *sk = sock->sk; struct avc_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - u32 sid; + u32 sid, perm; if (sk->sk_family == PF_INET) { addr4 = (struct sockaddr_in *)address; @@ -3245,11 +3288,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (err) goto out; + perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? + TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; + AVC_AUDIT_DATA_INIT(&ad,NET); ad.u.net.dport = htons(snum); ad.u.net.family = sk->sk_family; - err = avc_has_perm(isec->sid, sid, isec->sclass, - TCP_SOCKET__NAME_CONNECT, &ad); + err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); if (err) goto out; } @@ -3437,7 +3482,13 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, node_perm = NODE__TCP_RECV; recv_perm = TCP_SOCKET__RECV_MSG; break; - + + case SECCLASS_DCCP_SOCKET: + netif_perm = NETIF__DCCP_RECV; + node_perm = NODE__DCCP_RECV; + recv_perm = DCCP_SOCKET__RECV_MSG; + break; + default: netif_perm = NETIF__RAWIP_RECV; node_perm = NODE__RAWIP_RECV; @@ -3486,14 +3537,14 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) goto out; /* Handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) family = PF_INET; 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); + err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL); if (err) goto out; @@ -3523,25 +3574,16 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op u32 scontext_len; struct sk_security_struct *ssec; struct inode_security_struct *isec; - u32 peer_sid = 0; + u32 peer_sid = SECSID_NULL; isec = SOCK_INODE(sock)->i_security; - /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */ - if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) { + if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || + isec->sclass == SECCLASS_TCP_SOCKET) { ssec = sock->sk->sk_security; peer_sid = ssec->peer_sid; } - else if (isec->sclass == SECCLASS_TCP_SOCKET) { - peer_sid = selinux_netlbl_socket_getpeersec_stream(sock); - if (peer_sid == SECSID_NULL) - peer_sid = selinux_socket_getpeer_stream(sock->sk); - if (peer_sid == SECSID_NULL) { - err = -ENOPROTOOPT; - goto out; - } - } - else { + if (peer_sid == SECSID_NULL) { err = -ENOPROTOOPT; goto out; } @@ -3573,13 +3615,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * u32 peer_secid = SECSID_NULL; int err = 0; - if (sock && (sock->sk->sk_family == PF_UNIX)) + if (sock && sock->sk->sk_family == PF_UNIX) selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); - else if (skb) { - peer_secid = selinux_netlbl_socket_getpeersec_dgram(skb); - if (peer_secid == SECSID_NULL) - peer_secid = selinux_socket_getpeer_dgram(skb); - } + else if (skb) + security_skb_extlbl_sid(skb, + SECINITSID_UNLABELED, + &peer_secid); if (peer_secid == SECSID_NULL) err = -EINVAL; @@ -3606,7 +3647,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) newssec->sid = ssec->sid; newssec->peer_sid = ssec->peer_sid; - selinux_netlbl_sk_clone_security(ssec, newssec); + selinux_netlbl_sk_security_clone(ssec, newssec); } static void selinux_sk_getsecid(struct sock *sk, u32 *secid) @@ -3640,17 +3681,10 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, u32 newsid; u32 peersid; - newsid = selinux_netlbl_inet_conn_request(skb, sksec->sid); - if (newsid != SECSID_NULL) { - req->secid = newsid; - return 0; - } - - err = selinux_xfrm_decode_session(skb, &peersid, 0); - BUG_ON(err); - + security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid); if (peersid == SECSID_NULL) { req->secid = sksec->sid; + req->peer_secid = SECSID_NULL; return 0; } @@ -3659,6 +3693,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, return err; req->secid = newsid; + req->peer_secid = peersid; return 0; } @@ -3668,12 +3703,23 @@ static void selinux_inet_csk_clone(struct sock *newsk, struct sk_security_struct *newsksec = newsk->sk_security; newsksec->sid = req->secid; + newsksec->peer_sid = req->peer_secid; /* NOTE: Ideally, we should also get the isec->sid for the new socket in sync, but we don't have the isec available yet. So we will wait until sock_graft to do it, by which time it will have been created and available. */ - selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family); + /* We don't need to take any sort of lock here as we are the only + * thread with access to newsksec */ + selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family); +} + +static void selinux_inet_conn_established(struct sock *sk, + struct sk_buff *skb) +{ + struct sk_security_struct *sksec = sk->sk_security; + + security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid); } static void selinux_req_classify_flow(const struct request_sock *req, @@ -3756,7 +3802,13 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device * node_perm = NODE__TCP_SEND; send_perm = TCP_SOCKET__SEND_MSG; break; - + + case SECCLASS_DCCP_SOCKET: + netif_perm = NETIF__DCCP_SEND; + node_perm = NODE__DCCP_SEND; + send_perm = DCCP_SOCKET__SEND_MSG; + break; + default: netif_perm = NETIF__RAWIP_SEND; node_perm = NODE__RAWIP_SEND; @@ -3807,6 +3859,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, struct avc_audit_data ad; struct net_device *dev = (struct net_device *)out; struct sk_security_struct *sksec; + u8 proto; sk = skb->sk; if (!sk) @@ -3818,7 +3871,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ad.u.net.netif = dev->name; ad.u.net.family = family; - err = selinux_parse_skb(skb, &ad, &addrp, &len, 0); + err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto); if (err) goto out; @@ -3832,7 +3885,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, if (err) goto out; - err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad); + err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto); out: return err ? NF_DROP : NF_ACCEPT; } @@ -4738,6 +4791,7 @@ static struct security_operations selinux_ops = { .sock_graft = selinux_sock_graft, .inet_conn_request = selinux_inet_conn_request, .inet_csk_clone = selinux_inet_csk_clone, + .inet_conn_established = selinux_inet_conn_established, .req_classify_flow = selinux_req_classify_flow, #ifdef CONFIG_SECURITY_NETWORK_XFRM @@ -4750,7 +4804,6 @@ static struct security_operations selinux_ops = { .xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_policy_lookup = selinux_xfrm_policy_lookup, .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match, - .xfrm_flow_state_match = selinux_xfrm_flow_state_match, .xfrm_decode_session = selinux_xfrm_decode_session, #endif diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h index a68fdd55597f..8377a4ba3b95 100644 --- a/security/selinux/include/av_inherit.h +++ b/security/selinux/include/av_inherit.h @@ -30,3 +30,4 @@ S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL) S_(SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET, socket, 0x00400000UL) S_(SECCLASS_APPLETALK_SOCKET, socket, 0x00400000UL) + S_(SECCLASS_DCCP_SOCKET, socket, 0x00400000UL) diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index 09fc8a2345eb..ad9fb2d69b50 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -35,12 +35,16 @@ S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv") S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send") S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest") + S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv") + S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send") S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv") S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send") S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv") S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send") S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv") S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send") + S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv") + S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom") @@ -252,3 +256,7 @@ S_(SECCLASS_KEY, KEY__LINK, "link") S_(SECCLASS_KEY, KEY__SETATTR, "setattr") S_(SECCLASS_KEY, KEY__CREATE, "create") + S_(SECCLASS_CONTEXT, CONTEXT__TRANSLATE, "translate") + S_(SECCLASS_CONTEXT, CONTEXT__CONTAINS, "contains") + S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind") + S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 81f4f526c8b1..2de4b5fe3aa1 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -312,6 +312,8 @@ #define NODE__RAWIP_RECV 0x00000010UL #define NODE__RAWIP_SEND 0x00000020UL #define NODE__ENFORCE_DEST 0x00000040UL +#define NODE__DCCP_RECV 0x00000080UL +#define NODE__DCCP_SEND 0x00000100UL #define NETIF__TCP_RECV 0x00000001UL #define NETIF__TCP_SEND 0x00000002UL @@ -319,6 +321,8 @@ #define NETIF__UDP_SEND 0x00000008UL #define NETIF__RAWIP_RECV 0x00000010UL #define NETIF__RAWIP_SEND 0x00000020UL +#define NETIF__DCCP_RECV 0x00000040UL +#define NETIF__DCCP_SEND 0x00000080UL #define NETLINK_SOCKET__IOCTL 0x00000001UL #define NETLINK_SOCKET__READ 0x00000002UL @@ -970,3 +974,31 @@ #define KEY__LINK 0x00000010UL #define KEY__SETATTR 0x00000020UL #define KEY__CREATE 0x00000040UL + +#define CONTEXT__TRANSLATE 0x00000001UL +#define CONTEXT__CONTAINS 0x00000002UL + +#define DCCP_SOCKET__IOCTL 0x00000001UL +#define DCCP_SOCKET__READ 0x00000002UL +#define DCCP_SOCKET__WRITE 0x00000004UL +#define DCCP_SOCKET__CREATE 0x00000008UL +#define DCCP_SOCKET__GETATTR 0x00000010UL +#define DCCP_SOCKET__SETATTR 0x00000020UL +#define DCCP_SOCKET__LOCK 0x00000040UL +#define DCCP_SOCKET__RELABELFROM 0x00000080UL +#define DCCP_SOCKET__RELABELTO 0x00000100UL +#define DCCP_SOCKET__APPEND 0x00000200UL +#define DCCP_SOCKET__BIND 0x00000400UL +#define DCCP_SOCKET__CONNECT 0x00000800UL +#define DCCP_SOCKET__LISTEN 0x00001000UL +#define DCCP_SOCKET__ACCEPT 0x00002000UL +#define DCCP_SOCKET__GETOPT 0x00004000UL +#define DCCP_SOCKET__SETOPT 0x00008000UL +#define DCCP_SOCKET__SHUTDOWN 0x00010000UL +#define DCCP_SOCKET__RECVFROM 0x00020000UL +#define DCCP_SOCKET__SENDTO 0x00040000UL +#define DCCP_SOCKET__RECV_MSG 0x00080000UL +#define DCCP_SOCKET__SEND_MSG 0x00100000UL +#define DCCP_SOCKET__NAME_BIND 0x00200000UL +#define DCCP_SOCKET__NODE_BIND 0x00400000UL +#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 960ef18ddc41..6ed10c3d3339 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -54,12 +54,12 @@ struct avc_audit_data { char *netif; struct sock *sk; u16 family; - u16 dport; - u16 sport; + __be16 dport; + __be16 sport; union { struct { - u32 daddr; - u32 saddr; + __be32 daddr; + __be32 saddr; } v4; struct { struct in6_addr daddr; diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index 450a2831e2e3..ff869e8b6f4a 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h @@ -10,5 +10,29 @@ int avc_ss_reset(u32 seqno); +struct av_perm_to_string +{ + u16 tclass; + u32 value; + const char *name; +}; + +struct av_inherit +{ + u16 tclass; + const char **common_pts; + u32 common_base; +}; + +struct selinux_class_perm +{ + const struct av_perm_to_string *av_perm_to_string; + u32 av_pts_len; + const char **class_to_string; + u32 cts_len; + const struct av_inherit *av_inherit; + u32 av_inherit_len; +}; + #endif /* _SELINUX_AVC_SS_H_ */ diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index 24303b61309f..9f3ebb1bfae6 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h @@ -61,3 +61,5 @@ S_("appletalk_socket") S_("packet") S_("key") + S_("context") + S_("dccp_socket") diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index 95887aed2a68..67cef371ee00 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h @@ -63,6 +63,8 @@ #define SECCLASS_APPLETALK_SOCKET 56 #define SECCLASS_PACKET 57 #define SECCLASS_KEY 58 +#define SECCLASS_CONTEXT 59 +#define SECCLASS_DCCP_SOCKET 60 /* * Security identifier indices for initial entities diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index ef2267fea8bd..91b88f0ba20c 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -23,6 +23,7 @@ #include <linux/fs.h> #include <linux/binfmts.h> #include <linux/in.h> +#include <linux/spinlock.h> #include "flask.h" #include "avc.h" @@ -108,6 +109,7 @@ struct sk_security_struct { NLBL_REQUIRE, NLBL_LABELED, } nlbl_state; + spinlock_t nlbl_lock; /* protects nlbl_state */ #endif }; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 1ef79172cc8c..210eec77e7ff 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -34,6 +34,8 @@ #define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS #endif +struct sk_buff; + extern int selinux_enabled; extern int selinux_mls_enabled; @@ -80,6 +82,8 @@ int security_netif_sid(char *name, u32 *if_sid, int security_node_sid(u16 domain, void *addr, u32 addrlen, u32 *out_sid); +void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid); + int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/selinux_netlabel.h index 9de10cc2cef2..2a732c9033e3 100644 --- a/security/selinux/include/selinux_netlabel.h +++ b/security/selinux/include/selinux_netlabel.h @@ -38,19 +38,17 @@ #ifdef CONFIG_NETLABEL void selinux_netlbl_cache_invalidate(void); -int selinux_netlbl_socket_post_create(struct socket *sock, - int sock_family, - u32 sid); +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid); +int selinux_netlbl_socket_post_create(struct socket *sock); void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); -u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid); int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, struct avc_audit_data *ad); -u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock); -u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb); +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, + int family); void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, int family); -void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, +void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, struct sk_security_struct *newssec); int selinux_netlbl_inode_permission(struct inode *inode, int mask); int selinux_netlbl_socket_setsockopt(struct socket *sock, @@ -62,23 +60,23 @@ static inline void selinux_netlbl_cache_invalidate(void) return; } -static inline int selinux_netlbl_socket_post_create(struct socket *sock, - int sock_family, - u32 sid) +static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u32 base_sid, + u32 *sid) { + *sid = SECSID_NULL; return 0; } -static inline void selinux_netlbl_sock_graft(struct sock *sk, - struct socket *sock) +static inline int selinux_netlbl_socket_post_create(struct socket *sock) { - return; + return 0; } -static inline u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, - u32 sock_sid) +static inline void selinux_netlbl_sock_graft(struct sock *sk, + struct socket *sock) { - return SECSID_NULL; + return; } static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, @@ -88,14 +86,11 @@ static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, return 0; } -static inline u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock) -{ - return SECSID_NULL; -} - -static inline u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) +static inline void selinux_netlbl_sk_security_reset( + struct sk_security_struct *ssec, + int family) { - return SECSID_NULL; + return; } static inline void selinux_netlbl_sk_security_init( @@ -105,7 +100,7 @@ static inline void selinux_netlbl_sk_security_init( return; } -static inline void selinux_netlbl_sk_clone_security( +static inline void selinux_netlbl_sk_security_clone( struct sk_security_struct *ssec, struct sk_security_struct *newssec) { diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 526b28019aca..161eb571c82d 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -8,20 +8,17 @@ #define _SELINUX_XFRM_H_ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, - struct xfrm_user_sec_ctx *sec_ctx, struct sock *sk); + struct xfrm_user_sec_ctx *sec_ctx); int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new); void selinux_xfrm_policy_free(struct xfrm_policy *xp); int selinux_xfrm_policy_delete(struct xfrm_policy *xp); int selinux_xfrm_state_alloc(struct xfrm_state *x, - struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid); + struct xfrm_user_sec_ctx *sec_ctx, u32 secid); void selinux_xfrm_state_free(struct xfrm_state *x); int selinux_xfrm_state_delete(struct xfrm_state *x); int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir); int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, struct flowi *fl); -int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, - struct xfrm_policy *xp); - /* * Extract the security blob from the sock (it's actually on the socket) @@ -38,9 +35,7 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk) int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, struct avc_audit_data *ad); int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, - struct avc_audit_data *ad); -u32 selinux_socket_getpeer_stream(struct sock *sk); -u32 selinux_socket_getpeer_dgram(struct sk_buff *skb); + struct avc_audit_data *ad, u8 proto); int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); #else static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, @@ -50,20 +45,11 @@ 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, - struct avc_audit_data *ad) + struct avc_audit_data *ad, u8 proto) { return 0; } -static inline int selinux_socket_getpeer_stream(struct sock *sk) -{ - return SECSID_NULL; -} - -static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb) -{ - return SECSID_NULL; -} static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) { *sid = SECSID_NULL; @@ -71,4 +57,10 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int } #endif +static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid) +{ + int err = selinux_xfrm_decode_session(skb, sid, 0); + BUG_ON(err); +} + #endif /* _SELINUX_XFRM_H_ */ diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index b8f4d25cf335..ccfe8755735e 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -60,7 +60,6 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, - { RTM_GETPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index d049c7acbc8b..ebb993c5c244 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -28,7 +28,7 @@ (keyp->source_type << 9)) & \ AVTAB_HASH_MASK) -static kmem_cache_t *avtab_node_cachep; +static struct kmem_cache *avtab_node_cachep; static struct avtab_node* avtab_insert_node(struct avtab *h, int hvalue, @@ -36,7 +36,7 @@ avtab_insert_node(struct avtab *h, int hvalue, struct avtab_key *key, struct avtab_datum *datum) { struct avtab_node * newnode; - newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL); + newnode = kmem_cache_alloc(avtab_node_cachep, GFP_KERNEL); if (newnode == NULL) return NULL; memset(newnode, 0, sizeof(struct avtab_node)); diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index d539346ab3a2..ce492a6b38ed 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -6,7 +6,7 @@ /* * Updated: Hewlett-Packard <paul.moore@hp.com> * - * Added ebitmap_export() and ebitmap_import() + * Added support to import/export the NetLabel category bitmap * * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/errno.h> +#include <net/netlabel.h> #include "ebitmap.h" #include "policydb.h" @@ -67,141 +68,120 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return 0; } +#ifdef CONFIG_NETLABEL /** - * ebitmap_export - Export an ebitmap to a unsigned char bitmap string - * @src: the ebitmap to export - * @dst: the resulting bitmap string - * @dst_len: length of dst in bytes + * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap + * @ebmap: the ebitmap to export + * @catmap: the NetLabel category bitmap * * Description: - * Allocate a buffer at least src->highbit bits long and export the extensible - * bitmap into the buffer. The bitmap string will be in little endian format, - * i.e. LSB first. The value returned in dst_len may not the true size of the - * buffer as the length of the buffer is rounded up to a multiple of MAPTYPE. - * The caller must free the buffer when finished. Returns zero on success, - * negative values on failure. + * Export a SELinux extensibile bitmap into a NetLabel category bitmap. + * Returns zero on success, negative values on error. * */ -int ebitmap_export(const struct ebitmap *src, - unsigned char **dst, - size_t *dst_len) +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) { - size_t bitmap_len; - unsigned char *bitmap; - struct ebitmap_node *iter_node; - MAPTYPE node_val; - size_t bitmap_byte; - unsigned char bitmask; - - if (src->highbit == 0) { - *dst = NULL; - *dst_len = 0; + struct ebitmap_node *e_iter = ebmap->node; + struct netlbl_lsm_secattr_catmap *c_iter; + u32 cmap_idx; + + /* This function is a much simpler because SELinux's MAPTYPE happens + * to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is + * changed from a u64 this function will most likely need to be changed + * as well. It's not ideal but I think the tradeoff in terms of + * neatness and speed is worth it. */ + + if (e_iter == NULL) { + *catmap = NULL; return 0; } - bitmap_len = src->highbit / 8; - if (src->highbit % 7) - bitmap_len += 1; - - bitmap = kzalloc((bitmap_len & ~(sizeof(MAPTYPE) - 1)) + - sizeof(MAPTYPE), - GFP_ATOMIC); - if (bitmap == NULL) + c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter == NULL) return -ENOMEM; + *catmap = c_iter; + c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); + + while (e_iter != NULL) { + if (e_iter->startbit >= + (c_iter->startbit + NETLBL_CATMAP_SIZE)) { + c_iter->next = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (c_iter->next == NULL) + goto netlbl_export_failure; + c_iter = c_iter->next; + c_iter->startbit = e_iter->startbit & + ~(NETLBL_CATMAP_SIZE - 1); + } + cmap_idx = (e_iter->startbit - c_iter->startbit) / + NETLBL_CATMAP_MAPSIZE; + c_iter->bitmap[cmap_idx] = e_iter->map; + e_iter = e_iter->next; + } - iter_node = src->node; - do { - bitmap_byte = iter_node->startbit / 8; - bitmask = 0x80; - node_val = iter_node->map; - do { - if (bitmask == 0) { - bitmap_byte++; - bitmask = 0x80; - } - if (node_val & (MAPTYPE)0x01) - bitmap[bitmap_byte] |= bitmask; - node_val >>= 1; - bitmask >>= 1; - } while (node_val > 0); - iter_node = iter_node->next; - } while (iter_node); - - *dst = bitmap; - *dst_len = bitmap_len; return 0; + +netlbl_export_failure: + netlbl_secattr_catmap_free(*catmap); + return -ENOMEM; } /** - * ebitmap_import - Import an unsigned char bitmap string into an ebitmap - * @src: the bitmap string - * @src_len: the bitmap length in bytes - * @dst: the empty ebitmap + * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap + * @ebmap: the ebitmap to export + * @catmap: the NetLabel category bitmap * * Description: - * This function takes a little endian bitmap string in src and imports it into - * the ebitmap pointed to by dst. Returns zero on success, negative values on - * failure. + * Import a NetLabel category bitmap into a SELinux extensibile bitmap. + * Returns zero on success, negative values on error. * */ -int ebitmap_import(const unsigned char *src, - size_t src_len, - struct ebitmap *dst) +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) { - size_t src_off = 0; - size_t node_limit; - struct ebitmap_node *node_new; - struct ebitmap_node *node_last = NULL; - u32 i_byte; - u32 i_bit; - unsigned char src_byte; - - while (src_off < src_len) { - if (src_len - src_off >= sizeof(MAPTYPE)) { - if (*(MAPTYPE *)&src[src_off] == 0) { - src_off += sizeof(MAPTYPE); - continue; - } - node_limit = sizeof(MAPTYPE); - } else { - for (src_byte = 0, i_byte = src_off; - i_byte < src_len && src_byte == 0; - i_byte++) - src_byte |= src[i_byte]; - if (src_byte == 0) - break; - node_limit = src_len - src_off; - } + struct ebitmap_node *e_iter = NULL; + struct ebitmap_node *emap_prev = NULL; + struct netlbl_lsm_secattr_catmap *c_iter = catmap; + u32 c_idx; - node_new = kzalloc(sizeof(*node_new), GFP_ATOMIC); - if (unlikely(node_new == NULL)) { - ebitmap_destroy(dst); - return -ENOMEM; - } - node_new->startbit = src_off * 8; - for (i_byte = 0; i_byte < node_limit; i_byte++) { - src_byte = src[src_off++]; - for (i_bit = i_byte * 8; src_byte != 0; i_bit++) { - if (src_byte & 0x80) - node_new->map |= MAPBIT << i_bit; - src_byte <<= 1; - } - } + /* This function is a much simpler because SELinux's MAPTYPE happens + * to be the same as NetLabel's NETLBL_CATMAP_MAPTYPE, if MAPTYPE is + * changed from a u64 this function will most likely need to be changed + * as well. It's not ideal but I think the tradeoff in terms of + * neatness and speed is worth it. */ - if (node_last != NULL) - node_last->next = node_new; - else - dst->node = node_new; - node_last = node_new; - } + do { + for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { + if (c_iter->bitmap[c_idx] == 0) + continue; + + e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + if (e_iter == NULL) + goto netlbl_import_failure; + if (emap_prev == NULL) + ebmap->node = e_iter; + else + emap_prev->next = e_iter; + emap_prev = e_iter; - if (likely(node_last != NULL)) - dst->highbit = node_last->startbit + MAPSIZE; + e_iter->startbit = c_iter->startbit + + NETLBL_CATMAP_MAPSIZE * c_idx; + e_iter->map = c_iter->bitmap[c_idx]; + } + c_iter = c_iter->next; + } while (c_iter != NULL); + if (e_iter != NULL) + ebmap->highbit = e_iter->startbit + MAPSIZE; else - ebitmap_init(dst); + ebitmap_destroy(ebmap); return 0; + +netlbl_import_failure: + ebitmap_destroy(ebmap); + return -ENOMEM; } +#endif /* CONFIG_NETLABEL */ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) { diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index da2d4651b10d..1270e34b61c1 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -14,6 +14,8 @@ #ifndef _SS_EBITMAP_H_ #define _SS_EBITMAP_H_ +#include <net/netlabel.h> + #define MAPTYPE u64 /* portion of bitmap in each node */ #define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */ #define MAPBIT 1ULL /* a bit in the node bitmap */ @@ -69,16 +71,28 @@ static inline int ebitmap_node_get_bit(struct ebitmap_node * n, int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_export(const struct ebitmap *src, - unsigned char **dst, - size_t *dst_len); -int ebitmap_import(const unsigned char *src, - size_t src_len, - struct ebitmap *dst); int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); +#ifdef CONFIG_NETLABEL +int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap); +int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap); +#else +static inline int ebitmap_netlbl_export(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap **catmap) +{ + return -ENOMEM; +} +static inline int ebitmap_netlbl_import(struct ebitmap *ebmap, + struct netlbl_lsm_secattr_catmap *catmap) +{ + return -ENOMEM; +} +#endif + #endif /* _SS_EBITMAP_H_ */ diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 24e5ec957630..77b530c3bbce 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -8,8 +8,8 @@ #include <linux/errno.h> #include "hashtab.h" -struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), - int (*keycmp)(struct hashtab *h, void *key1, void *key2), +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), u32 size) { struct hashtab *p; @@ -71,7 +71,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) return 0; } -void *hashtab_search(struct hashtab *h, void *key) +void *hashtab_search(struct hashtab *h, const void *key) { u32 hvalue; struct hashtab_node *cur; diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 4cc85816a718..7e2ff3e3c6d2 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -22,9 +22,9 @@ struct hashtab { struct hashtab_node **htable; /* hash table */ u32 size; /* number of slots in hash table */ u32 nel; /* number of elements in hash table */ - u32 (*hash_value)(struct hashtab *h, void *key); + u32 (*hash_value)(struct hashtab *h, const void *key); /* hash function */ - int (*keycmp)(struct hashtab *h, void *key1, void *key2); + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2); /* key comparison function */ }; @@ -39,8 +39,8 @@ struct hashtab_info { * Returns NULL if insufficent space is available or * the new hash table otherwise. */ -struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), - int (*keycmp)(struct hashtab *h, void *key1, void *key2), +struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), + int (*keycmp)(struct hashtab *h, const void *key1, const void *key2), u32 size); /* @@ -59,7 +59,7 @@ int hashtab_insert(struct hashtab *h, void *k, void *d); * Returns NULL if no entry has the specified key or * the datum of the entry otherwise. */ -void *hashtab_search(struct hashtab *h, void *k); +void *hashtab_search(struct hashtab *h, const void *k); /* * Destroys the specified hash table. diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 2cca8e251624..b4f682dc13ff 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -13,7 +13,7 @@ /* * Updated: Hewlett-Packard <paul.moore@hp.com> * - * Added support to import/export the MLS label + * Added support to import/export the MLS label from NetLabel * * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <net/netlabel.h> #include "sidtab.h" #include "mls.h" #include "policydb.h" @@ -571,163 +572,108 @@ int mls_compute_sid(struct context *scontext, return -EINVAL; } +#ifdef CONFIG_NETLABEL /** - * mls_export_lvl - Export the MLS sensitivity levels + * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel * @context: the security context - * @low: the low sensitivity level - * @high: the high sensitivity level + * @secattr: the NetLabel security attributes * * Description: - * Given the security context copy the low MLS sensitivity level into lvl_low - * and the high sensitivity level in lvl_high. The MLS levels are only - * exported if the pointers are not NULL, if they are NULL then that level is - * not exported. + * Given the security context copy the low MLS sensitivity level into the + * NetLabel MLS sensitivity level field. * */ -void mls_export_lvl(const struct context *context, u32 *low, u32 *high) +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) { if (!selinux_mls_enabled) return; - if (low != NULL) - *low = context->range.level[0].sens - 1; - if (high != NULL) - *high = context->range.level[1].sens - 1; + secattr->mls_lvl = context->range.level[0].sens - 1; + secattr->flags |= NETLBL_SECATTR_MLS_LVL; } /** - * mls_import_lvl - Import the MLS sensitivity levels + * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels * @context: the security context - * @low: the low sensitivity level - * @high: the high sensitivity level + * @secattr: the NetLabel security attributes * * Description: - * Given the security context and the two sensitivty levels, set the MLS levels - * in the context according the two given as parameters. Returns zero on - * success, negative values on failure. + * Given the security context and the NetLabel security attributes, copy the + * NetLabel MLS sensitivity level into the context. * */ -void mls_import_lvl(struct context *context, u32 low, u32 high) +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) { if (!selinux_mls_enabled) return; - context->range.level[0].sens = low + 1; - context->range.level[1].sens = high + 1; + context->range.level[0].sens = secattr->mls_lvl + 1; + context->range.level[1].sens = context->range.level[0].sens; } /** - * mls_export_cat - Export the MLS categories + * mls_export_netlbl_cat - Export the MLS categories to NetLabel * @context: the security context - * @low: the low category - * @low_len: length of the cat_low bitmap in bytes - * @high: the high category - * @high_len: length of the cat_high bitmap in bytes + * @secattr: the NetLabel security attributes * * Description: - * Given the security context export the low MLS category bitmap into cat_low - * and the high category bitmap into cat_high. The MLS categories are only - * exported if the pointers are not NULL, if they are NULL then that level is - * not exported. The caller is responsibile for freeing the memory when - * finished. Returns zero on success, negative values on failure. + * Given the security context copy the low MLS categories into the NetLabel + * MLS category field. Returns zero on success, negative values on failure. * */ -int mls_export_cat(const struct context *context, - unsigned char **low, - size_t *low_len, - unsigned char **high, - size_t *high_len) +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) { - int rc = -EPERM; + int rc; - if (!selinux_mls_enabled) { - *low = NULL; - *low_len = 0; - *high = NULL; - *high_len = 0; + if (!selinux_mls_enabled) return 0; - } - if (low != NULL) { - rc = ebitmap_export(&context->range.level[0].cat, - low, - low_len); - if (rc != 0) - goto export_cat_failure; - } - if (high != NULL) { - rc = ebitmap_export(&context->range.level[1].cat, - high, - high_len); - if (rc != 0) - goto export_cat_failure; - } - - return 0; + rc = ebitmap_netlbl_export(&context->range.level[0].cat, + &secattr->mls_cat); + if (rc == 0 && secattr->mls_cat != NULL) + secattr->flags |= NETLBL_SECATTR_MLS_CAT; -export_cat_failure: - if (low != NULL) { - kfree(*low); - *low = NULL; - *low_len = 0; - } - if (high != NULL) { - kfree(*high); - *high = NULL; - *high_len = 0; - } return rc; } /** - * mls_import_cat - Import the MLS categories + * mls_import_netlbl_cat - Import the MLS categories from NetLabel * @context: the security context - * @low: the low category - * @low_len: length of the cat_low bitmap in bytes - * @high: the high category - * @high_len: length of the cat_high bitmap in bytes + * @secattr: the NetLabel security attributes * * Description: - * Given the security context and the two category bitmap strings import the - * categories into the security context. The MLS categories are only imported - * if the pointers are not NULL, if they are NULL they are skipped. Returns - * zero on success, negative values on failure. + * Copy the NetLabel security attributes into the SELinux context; since the + * NetLabel security attribute only contains a single MLS category use it for + * both the low and high categories of the context. Returns zero on success, + * negative values on failure. * */ -int mls_import_cat(struct context *context, - const unsigned char *low, - size_t low_len, - const unsigned char *high, - size_t high_len) +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) { - int rc = -EPERM; + int rc; if (!selinux_mls_enabled) return 0; - if (low != NULL) { - rc = ebitmap_import(low, - low_len, - &context->range.level[0].cat); - if (rc != 0) - goto import_cat_failure; - } - if (high != NULL) { - if (high == low) - rc = ebitmap_cpy(&context->range.level[1].cat, - &context->range.level[0].cat); - else - rc = ebitmap_import(high, - high_len, - &context->range.level[1].cat); - if (rc != 0) - goto import_cat_failure; - } + rc = ebitmap_netlbl_import(&context->range.level[0].cat, + secattr->mls_cat); + if (rc != 0) + goto import_netlbl_cat_failure; + + rc = ebitmap_cpy(&context->range.level[1].cat, + &context->range.level[0].cat); + if (rc != 0) + goto import_netlbl_cat_failure; return 0; -import_cat_failure: +import_netlbl_cat_failure: ebitmap_destroy(&context->range.level[0].cat); ebitmap_destroy(&context->range.level[1].cat); return rc; } +#endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index df6032c6d492..661d6fc76966 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -13,7 +13,7 @@ /* * Updated: Hewlett-Packard <paul.moore@hp.com> * - * Added support to import/export the MLS label + * Added support to import/export the MLS label from NetLabel * * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 */ @@ -69,19 +69,37 @@ int mls_compute_sid(struct context *scontext, int mls_setup_user_range(struct context *fromcon, struct user_datum *user, struct context *usercon); -void mls_export_lvl(const struct context *context, u32 *low, u32 *high); -void mls_import_lvl(struct context *context, u32 low, u32 high); - -int mls_export_cat(const struct context *context, - unsigned char **low, - size_t *low_len, - unsigned char **high, - size_t *high_len); -int mls_import_cat(struct context *context, - const unsigned char *low, - size_t low_len, - const unsigned char *high, - size_t high_len); +#ifdef CONFIG_NETLABEL +void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr); +#else +static inline void mls_export_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline void mls_import_netlbl_lvl(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return; +} +static inline int mls_export_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +static inline int mls_import_netlbl_cat(struct context *context, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOMEM; +} +#endif #endif /* _SS_MLS_H */ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index ba48961f9d05..cd79c6338aa0 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -468,7 +468,7 @@ static int common_destroy(void *key, void *datum, void *p) return 0; } -static int class_destroy(void *key, void *datum, void *p) +static int cls_destroy(void *key, void *datum, void *p) { struct class_datum *cladatum; struct constraint_node *constraint, *ctemp; @@ -566,7 +566,7 @@ static int cat_destroy(void *key, void *datum, void *p) static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = { common_destroy, - class_destroy, + cls_destroy, role_destroy, type_destroy, user_destroy, @@ -1124,7 +1124,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) out: return rc; bad: - class_destroy(key, cladatum, NULL); + cls_destroy(key, cladatum, NULL); goto out; } diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index bfe122764c98..bdb7070dd3dc 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -17,9 +17,13 @@ * * Added support for NetLabel * + * Updated: Chad Sellers <csellers@tresys.com> + * + * Added validation of kernel classes and permissions + * * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. - * Copyright (C) 2003 - 2004 Tresys Technology, LLC + * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +33,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/spinlock.h> +#include <linux/rcupdate.h> #include <linux/errno.h> #include <linux/in.h> #include <linux/sched.h> @@ -49,10 +54,17 @@ #include "mls.h" #include "objsec.h" #include "selinux_netlabel.h" +#include "xfrm.h" +#include "ebitmap.h" extern void selnl_notify_policyload(u32 seqno); unsigned int policydb_loaded_version; +/* + * This is declared in avc.c + */ +extern const struct selinux_class_perm selinux_class_perm; + static DEFINE_RWLOCK(policy_rwlock); #define POLICY_RDLOCK read_lock(&policy_rwlock) #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) @@ -1019,86 +1031,112 @@ int security_change_sid(u32 ssid, } /* - * Verify that each permission that is defined under the - * existing policy is still defined with the same value - * in the new policy. - */ -static int validate_perm(void *key, void *datum, void *p) -{ - struct hashtab *h; - struct perm_datum *perdatum, *perdatum2; - int rc = 0; - - - h = p; - perdatum = datum; - - perdatum2 = hashtab_search(h, key); - if (!perdatum2) { - printk(KERN_ERR "security: permission %s disappeared", - (char *)key); - rc = -ENOENT; - goto out; - } - if (perdatum->value != perdatum2->value) { - printk(KERN_ERR "security: the value of permission %s changed", - (char *)key); - rc = -EINVAL; - } -out: - return rc; -} - -/* - * Verify that each class that is defined under the - * existing policy is still defined with the same - * attributes in the new policy. + * Verify that each kernel class that is defined in the + * policy is correct */ -static int validate_class(void *key, void *datum, void *p) +static int validate_classes(struct policydb *p) { - struct policydb *newp; - struct class_datum *cladatum, *cladatum2; - int rc; - - newp = p; - cladatum = datum; - - cladatum2 = hashtab_search(newp->p_classes.table, key); - if (!cladatum2) { - printk(KERN_ERR "security: class %s disappeared\n", - (char *)key); - rc = -ENOENT; - goto out; - } - if (cladatum->value != cladatum2->value) { - printk(KERN_ERR "security: the value of class %s changed\n", - (char *)key); - rc = -EINVAL; - goto out; + int i, j; + struct class_datum *cladatum; + struct perm_datum *perdatum; + u32 nprim, tmp, common_pts_len, perm_val, pol_val; + u16 class_val; + const struct selinux_class_perm *kdefs = &selinux_class_perm; + const char *def_class, *def_perm, *pol_class; + struct symtab *perms; + + for (i = 1; i < kdefs->cts_len; i++) { + def_class = kdefs->class_to_string[i]; + if (i > p->p_classes.nprim) { + printk(KERN_INFO + "security: class %s not defined in policy\n", + def_class); + continue; + } + pol_class = p->p_class_val_to_name[i-1]; + if (strcmp(pol_class, def_class)) { + printk(KERN_ERR + "security: class %d is incorrect, found %s but should be %s\n", + i, pol_class, def_class); + return -EINVAL; + } } - if ((cladatum->comdatum && !cladatum2->comdatum) || - (!cladatum->comdatum && cladatum2->comdatum)) { - printk(KERN_ERR "security: the inherits clause for the access " - "vector definition for class %s changed\n", (char *)key); - rc = -EINVAL; - goto out; + for (i = 0; i < kdefs->av_pts_len; i++) { + class_val = kdefs->av_perm_to_string[i].tclass; + perm_val = kdefs->av_perm_to_string[i].value; + def_perm = kdefs->av_perm_to_string[i].name; + if (class_val > p->p_classes.nprim) + continue; + pol_class = p->p_class_val_to_name[class_val-1]; + cladatum = hashtab_search(p->p_classes.table, pol_class); + BUG_ON(!cladatum); + perms = &cladatum->permissions; + nprim = 1 << (perms->nprim - 1); + if (perm_val > nprim) { + printk(KERN_INFO + "security: permission %s in class %s not defined in policy\n", + def_perm, pol_class); + continue; + } + perdatum = hashtab_search(perms->table, def_perm); + if (perdatum == NULL) { + printk(KERN_ERR + "security: permission %s in class %s not found in policy\n", + def_perm, pol_class); + return -EINVAL; + } + pol_val = 1 << (perdatum->value - 1); + if (pol_val != perm_val) { + printk(KERN_ERR + "security: permission %s in class %s has incorrect value\n", + def_perm, pol_class); + return -EINVAL; + } } - if (cladatum->comdatum) { - rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, - cladatum2->comdatum->permissions.table); - if (rc) { - printk(" in the access vector definition for class " - "%s\n", (char *)key); - goto out; + for (i = 0; i < kdefs->av_inherit_len; i++) { + class_val = kdefs->av_inherit[i].tclass; + if (class_val > p->p_classes.nprim) + continue; + pol_class = p->p_class_val_to_name[class_val-1]; + cladatum = hashtab_search(p->p_classes.table, pol_class); + BUG_ON(!cladatum); + if (!cladatum->comdatum) { + printk(KERN_ERR + "security: class %s should have an inherits clause but does not\n", + pol_class); + return -EINVAL; + } + tmp = kdefs->av_inherit[i].common_base; + common_pts_len = 0; + while (!(tmp & 0x01)) { + common_pts_len++; + tmp >>= 1; + } + perms = &cladatum->comdatum->permissions; + for (j = 0; j < common_pts_len; j++) { + def_perm = kdefs->av_inherit[i].common_pts[j]; + if (j >= perms->nprim) { + printk(KERN_INFO + "security: permission %s in class %s not defined in policy\n", + def_perm, pol_class); + continue; + } + perdatum = hashtab_search(perms->table, def_perm); + if (perdatum == NULL) { + printk(KERN_ERR + "security: permission %s in class %s not found in policy\n", + def_perm, pol_class); + return -EINVAL; + } + if (perdatum->value != j + 1) { + printk(KERN_ERR + "security: permission %s in class %s has incorrect value\n", + def_perm, pol_class); + return -EINVAL; + } } } - rc = hashtab_map(cladatum->permissions.table, validate_perm, - cladatum2->permissions.table); - if (rc) - printk(" in access vector definition for class %s\n", - (char *)key); -out: - return rc; + return 0; } /* Clone the SID into the new SID table. */ @@ -1243,6 +1281,16 @@ int security_load_policy(void *data, size_t len) avtab_cache_destroy(); return -EINVAL; } + /* Verify that the kernel defined classes are correct. */ + if (validate_classes(&policydb)) { + printk(KERN_ERR + "security: the definition of a class is incorrect\n"); + LOAD_UNLOCK; + sidtab_destroy(&sidtab); + policydb_destroy(&policydb); + avtab_cache_destroy(); + return -EINVAL; + } policydb_loaded_version = policydb.policyvers; ss_initialized = 1; seqno = ++latest_granting; @@ -1265,10 +1313,10 @@ int security_load_policy(void *data, size_t len) sidtab_init(&newsidtab); - /* Verify that the existing classes did not change. */ - if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { - printk(KERN_ERR "security: the definition of an existing " - "class changed\n"); + /* Verify that the kernel defined classes are correct. */ + if (validate_classes(&newpolicydb)) { + printk(KERN_ERR + "security: the definition of a class is incorrect\n"); rc = -EINVAL; goto err; } @@ -2145,6 +2193,32 @@ void selinux_audit_set_callback(int (*callback)(void)) aurule_callback = callback; } +/** + * security_skb_extlbl_sid - Determine the external label of a packet + * @skb: the packet + * @base_sid: the SELinux SID to use as a context for MLS only external labels + * @sid: the packet's SID + * + * Description: + * Check the various different forms of external packet labeling and determine + * the external SID for the packet. + * + */ +void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid) +{ + u32 xfrm_sid; + u32 nlbl_sid; + + selinux_skb_xfrm_sid(skb, &xfrm_sid); + if (selinux_netlbl_skbuff_getsid(skb, + (xfrm_sid == SECSID_NULL ? + base_sid : xfrm_sid), + &nlbl_sid) != 0) + nlbl_sid = SECSID_NULL; + + *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); +} + #ifdef CONFIG_NETLABEL /* * This is the structure we store inside the NetLabel cache block. @@ -2209,8 +2283,6 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) cache = kzalloc(sizeof(*cache), GFP_ATOMIC); if (cache == NULL) goto netlbl_cache_add_return; - secattr.cache->free = selinux_netlbl_cache_free; - secattr.cache->data = (void *)cache; cache->type = NETLBL_CACHE_T_MLS; if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, @@ -2223,6 +2295,10 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx) cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; + secattr.cache->free = selinux_netlbl_cache_free; + secattr.cache->data = (void *)cache; + secattr.flags = NETLBL_SECATTR_CACHE; + netlbl_cache_add(skb, &secattr); netlbl_cache_add_return: @@ -2268,7 +2344,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, POLICY_RDLOCK; - if (secattr->cache) { + if (secattr->flags & NETLBL_SECATTR_CACHE) { cache = NETLBL_CACHE(secattr->cache->data); switch (cache->type) { case NETLBL_CACHE_T_SID: @@ -2301,7 +2377,7 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, default: goto netlbl_secattr_to_sid_return; } - } else if (secattr->mls_lvl_vld) { + } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { ctx = sidtab_search(&sidtab, base_sid); if (ctx == NULL) goto netlbl_secattr_to_sid_return; @@ -2309,13 +2385,10 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb, ctx_new.user = ctx->user; ctx_new.role = ctx->role; ctx_new.type = ctx->type; - mls_import_lvl(&ctx_new, secattr->mls_lvl, secattr->mls_lvl); - if (secattr->mls_cat) { - if (mls_import_cat(&ctx_new, - secattr->mls_cat, - secattr->mls_cat_len, - NULL, - 0) != 0) + mls_import_netlbl_lvl(&ctx_new, secattr); + if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { + if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat, + secattr->mls_cat) != 0) goto netlbl_secattr_to_sid_return; ctx_new.range.level[1].cat.highbit = ctx_new.range.level[0].cat.highbit; @@ -2360,20 +2433,20 @@ netlbl_secattr_to_sid_return_cleanup: * assign to the packet. Returns zero on success, negative values on failure. * */ -static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, - u32 base_sid, - u32 *sid) +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) { int rc; struct netlbl_lsm_secattr secattr; netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, &secattr); - if (rc == 0) + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) rc = selinux_netlbl_secattr_to_sid(skb, &secattr, base_sid, sid); + else + *sid = SECSID_NULL; netlbl_secattr_destroy(&secattr); return rc; @@ -2386,7 +2459,9 @@ static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, * * Description: * Attempt to label a socket using the NetLabel mechanism using the given - * SID. Returns zero values on success, negative values on failure. + * SID. Returns zero values on success, negative values on failure. The + * caller is responsibile for calling rcu_read_lock() before calling this + * this function and rcu_read_unlock() after this function returns. * */ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) @@ -2409,19 +2484,18 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], GFP_ATOMIC); - mls_export_lvl(ctx, &secattr.mls_lvl, NULL); - secattr.mls_lvl_vld = 1; - rc = mls_export_cat(ctx, - &secattr.mls_cat, - &secattr.mls_cat_len, - NULL, - NULL); + secattr.flags |= NETLBL_SECATTR_DOMAIN; + mls_export_netlbl_lvl(ctx, &secattr); + rc = mls_export_netlbl_cat(ctx, &secattr); if (rc != 0) goto netlbl_socket_setsid_return; rc = netlbl_socket_setattr(sock, &secattr); - if (rc == 0) + if (rc == 0) { + spin_lock(&sksec->nlbl_lock); sksec->nlbl_state = NLBL_LABELED; + spin_unlock(&sksec->nlbl_lock); + } netlbl_socket_setsid_return: POLICY_RDUNLOCK; @@ -2430,6 +2504,25 @@ netlbl_socket_setsid_return: } /** + * selinux_netlbl_sk_security_reset - Reset the NetLabel fields + * @ssec: the sk_security_struct + * @family: the socket family + * + * Description: + * Called when the NetLabel state of a sk_security_struct needs to be reset. + * The caller is responsibile for all the NetLabel sk_security_struct locking. + * + */ +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, + int family) +{ + if (family == PF_INET) + ssec->nlbl_state = NLBL_REQUIRE; + else + ssec->nlbl_state = NLBL_UNSET; +} + +/** * selinux_netlbl_sk_security_init - Setup the NetLabel fields * @ssec: the sk_security_struct * @family: the socket family @@ -2442,14 +2535,13 @@ netlbl_socket_setsid_return: void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, int family) { - if (family == PF_INET) - ssec->nlbl_state = NLBL_REQUIRE; - else - ssec->nlbl_state = NLBL_UNSET; + /* No locking needed, we are the only one who has access to ssec */ + selinux_netlbl_sk_security_reset(ssec, family); + spin_lock_init(&ssec->nlbl_lock); } /** - * selinux_netlbl_sk_clone_security - Copy the NetLabel fields + * selinux_netlbl_sk_security_clone - Copy the NetLabel fields * @ssec: the original sk_security_struct * @newssec: the cloned sk_security_struct * @@ -2458,41 +2550,41 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, * @newssec. * */ -void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, +void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, struct sk_security_struct *newssec) { + /* We don't need to take newssec->nlbl_lock because we are the only + * thread with access to newssec, but we do need to take the RCU read + * lock as other threads could have access to ssec */ + rcu_read_lock(); + selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); newssec->sclass = ssec->sclass; - if (ssec->nlbl_state != NLBL_UNSET) - newssec->nlbl_state = NLBL_REQUIRE; - else - newssec->nlbl_state = NLBL_UNSET; + rcu_read_unlock(); } /** * selinux_netlbl_socket_post_create - Label a socket using NetLabel * @sock: the socket to label - * @sock_family: the socket family - * @sid: the SID to use * * Description: * Attempt to label a socket using the NetLabel mechanism using the given * SID. Returns zero values on success, negative values on failure. * */ -int selinux_netlbl_socket_post_create(struct socket *sock, - int sock_family, - u32 sid) +int selinux_netlbl_socket_post_create(struct socket *sock) { + int rc = 0; struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec = sock->sk->sk_security; sksec->sclass = isec->sclass; - if (sock_family != PF_INET) - return 0; + rcu_read_lock(); + if (sksec->nlbl_state == NLBL_REQUIRE) + rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + rcu_read_unlock(); - sksec->nlbl_state = NLBL_REQUIRE; - return selinux_netlbl_socket_setsid(sock, sid); + return rc; } /** @@ -2514,11 +2606,16 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) sksec->sclass = isec->sclass; - if (sk->sk_family != PF_INET) + rcu_read_lock(); + + if (sksec->nlbl_state != NLBL_REQUIRE) { + rcu_read_unlock(); return; + } netlbl_secattr_init(&secattr); if (netlbl_sock_getattr(sk, &secattr) == 0 && + secattr.flags != NETLBL_SECATTR_NONE && selinux_netlbl_secattr_to_sid(NULL, &secattr, SECINITSID_UNLABELED, @@ -2526,35 +2623,12 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) sksec->peer_sid = nlbl_peer_sid; netlbl_secattr_destroy(&secattr); - sksec->nlbl_state = NLBL_REQUIRE; - /* Try to set the NetLabel on the socket to save time later, if we fail * here we will pick up the pieces in later calls to * selinux_netlbl_inode_permission(). */ selinux_netlbl_socket_setsid(sock, sksec->sid); -} - -/** - * selinux_netlbl_inet_conn_request - Handle a new connection request - * @skb: the packet - * @sock_sid: the SID of the parent socket - * - * Description: - * If present, use the security attributes of the packet in @skb and the - * parent sock's SID to arrive at a SID for the new child sock. Returns the - * SID of the connection or SECSID_NULL on failure. - * - */ -u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) -{ - int rc; - u32 peer_sid; - - rc = selinux_netlbl_skbuff_getsid(skb, sock_sid, &peer_sid); - if (rc != 0) - return SECSID_NULL; - return peer_sid; + rcu_read_unlock(); } /** @@ -2572,25 +2646,24 @@ u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) int selinux_netlbl_inode_permission(struct inode *inode, int mask) { int rc; - struct inode_security_struct *isec; struct sk_security_struct *sksec; struct socket *sock; - if (!S_ISSOCK(inode->i_mode)) + if (!S_ISSOCK(inode->i_mode) || + ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) return 0; - sock = SOCKET_I(inode); - isec = inode->i_security; sksec = sock->sk->sk_security; - mutex_lock(&isec->lock); - if (unlikely(sksec->nlbl_state == NLBL_REQUIRE && - (mask & (MAY_WRITE | MAY_APPEND)))) { - lock_sock(sock->sk); - rc = selinux_netlbl_socket_setsid(sock, sksec->sid); - release_sock(sock->sk); - } else - rc = 0; - mutex_unlock(&isec->lock); + + rcu_read_lock(); + if (sksec->nlbl_state != NLBL_REQUIRE) { + rcu_read_unlock(); + return 0; + } + lock_sock(sock->sk); + rc = selinux_netlbl_socket_setsid(sock, sksec->sid); + release_sock(sock->sk); + rcu_read_unlock(); return rc; } @@ -2648,42 +2721,6 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, } /** - * selinux_netlbl_socket_getpeersec_stream - Return the connected peer's SID - * @sock: the socket - * - * Description: - * Examine @sock to find the connected peer's SID. Returns the SID on success - * or SECSID_NULL on error. - * - */ -u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock) -{ - struct sk_security_struct *sksec = sock->sk->sk_security; - return sksec->peer_sid; -} - -/** - * selinux_netlbl_socket_getpeersec_dgram - Return the SID of a NetLabel packet - * @skb: the packet - * - * Description: - * Examine @skb to find the SID assigned to it by NetLabel. Returns the SID on - * success, SECSID_NULL on error. - * - */ -u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb) -{ - int peer_sid; - - if (selinux_netlbl_skbuff_getsid(skb, - SECINITSID_UNLABELED, - &peer_sid) != 0) - return SECSID_NULL; - - return peer_sid; -} - -/** * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel * @sock: the socket * @level: the socket level or protocol @@ -2701,21 +2738,19 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, int optname) { int rc = 0; - struct inode *inode = SOCK_INODE(sock); struct sk_security_struct *sksec = sock->sk->sk_security; - struct inode_security_struct *isec = inode->i_security; struct netlbl_lsm_secattr secattr; - mutex_lock(&isec->lock); + rcu_read_lock(); if (level == IPPROTO_IP && optname == IP_OPTIONS && sksec->nlbl_state == NLBL_LABELED) { netlbl_secattr_init(&secattr); rc = netlbl_socket_getattr(sock, &secattr); - if (rc == 0 && (secattr.cache || secattr.mls_lvl_vld)) + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) rc = -EACCES; netlbl_secattr_destroy(&secattr); } - mutex_unlock(&isec->lock); + rcu_read_unlock(); return rc; } diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index 24a10d36d3b6..837658a98a54 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -9,9 +9,9 @@ #include <linux/errno.h> #include "symtab.h" -static unsigned int symhash(struct hashtab *h, void *key) +static unsigned int symhash(struct hashtab *h, const void *key) { - char *p, *keyp; + const char *p, *keyp; unsigned int size; unsigned int val; @@ -23,9 +23,9 @@ static unsigned int symhash(struct hashtab *h, void *key) return val & (h->size - 1); } -static int symcmp(struct hashtab *h, void *key1, void *key2) +static int symcmp(struct hashtab *h, const void *key1, const void *key2) { - char *keyp1, *keyp2; + const char *keyp1, *keyp2; keyp1 = key1; keyp2 = key2; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 675b995a67c3..bd8d1ef40a90 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -115,76 +115,46 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy * struct flowi *fl) { u32 state_sid; - u32 pol_sid; - int err; + int rc; - if (xp->security) { - if (!x->security) - /* unlabeled SA and labeled policy can't match */ - return 0; - else - state_sid = x->security->ctx_sid; - pol_sid = xp->security->ctx_sid; - } else + if (!xp->security) if (x->security) /* unlabeled policy and labeled SA can't match */ return 0; else /* unlabeled policy and unlabeled SA match all flows */ return 1; - - err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION, - ASSOCIATION__POLMATCH, - NULL); - - if (err) - return 0; - - err = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION, - ASSOCIATION__SENDTO, - NULL)? 0:1; - - return err; -} - -/* - * LSM hook implementation that authorizes that a particular outgoing flow - * can use a given security association. - */ - -int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm, - struct xfrm_policy *xp) -{ - int rc = 0; - u32 sel_sid = SECINITSID_UNLABELED; - struct xfrm_sec_ctx *ctx; - - if (!xp->security) - if (!xfrm->security) - return 1; - else - return 0; else - if (!xfrm->security) + if (!x->security) + /* unlabeled SA and labeled policy can't match */ return 0; + else + if (!selinux_authorizable_xfrm(x)) + /* Not a SELinux-labeled SA */ + return 0; - /* Context sid is either set to label or ANY_ASSOC */ - if ((ctx = xfrm->security)) { - if (!selinux_authorizable_ctx(ctx)) - return 0; + state_sid = x->security->ctx_sid; - sel_sid = ctx->ctx_sid; - } + if (fl->secid != state_sid) + return 0; - rc = avc_has_perm(fl->secid, sel_sid, SECCLASS_ASSOCIATION, + rc = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, NULL)? 0:1; + /* + * We don't need a separate SA Vs. policy polmatch check + * since the SA is now of the same label as the flow and + * a flow Vs. policy polmatch check had already happened + * in selinux_xfrm_policy_lookup() above. + */ + return rc; } /* - * LSM hook implementation that determines the sid for the session. + * LSM hook implementation that checks and/or returns the xfrm sid for the + * incoming packet. */ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) @@ -226,16 +196,15 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) * CTX does not have a meaningful value on input */ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, - struct xfrm_user_sec_ctx *uctx, struct xfrm_sec_ctx *pol, u32 sid) + struct xfrm_user_sec_ctx *uctx, u32 sid) { int rc = 0; struct task_security_struct *tsec = current->security; struct xfrm_sec_ctx *ctx = NULL; char *ctx_str = NULL; u32 str_len; - u32 ctx_sid; - BUG_ON(uctx && pol); + BUG_ON(uctx && sid); if (!uctx) goto not_from_user; @@ -279,15 +248,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, return rc; not_from_user: - if (pol) { - rc = security_sid_mls_copy(pol->ctx_sid, sid, &ctx_sid); - if (rc) - goto out; - } - else - ctx_sid = sid; - - rc = security_sid_to_context(ctx_sid, &ctx_str, &str_len); + rc = security_sid_to_context(sid, &ctx_str, &str_len); if (rc) goto out; @@ -302,7 +263,7 @@ not_from_user: ctx->ctx_doi = XFRM_SC_DOI_LSM; ctx->ctx_alg = XFRM_SC_ALG_SELINUX; - ctx->ctx_sid = ctx_sid; + ctx->ctx_sid = sid; ctx->ctx_len = str_len; memcpy(ctx->ctx_str, ctx_str, @@ -323,22 +284,14 @@ out2: * xfrm_policy. */ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, - struct xfrm_user_sec_ctx *uctx, struct sock *sk) + struct xfrm_user_sec_ctx *uctx) { int err; - u32 sid; BUG_ON(!xp); - BUG_ON(uctx && sk); - - if (sk) { - struct sk_security_struct *ssec = sk->sk_security; - sid = ssec->sid; - } - else - sid = SECSID_NULL; + BUG_ON(!uctx); - err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, sid); + err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, 0); return err; } @@ -399,13 +352,13 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp) * xfrm_state. */ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx, - struct xfrm_sec_ctx *pol, u32 secid) + u32 secid) { int err; BUG_ON(!x); - err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid); + err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid); return err; } @@ -419,74 +372,6 @@ void selinux_xfrm_state_free(struct xfrm_state *x) kfree(ctx); } -/* - * SELinux internal function to retrieve the context of a connected - * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security - * association used to connect to the remote socket. - * - * Retrieve via getsockopt SO_PEERSEC. - */ -u32 selinux_socket_getpeer_stream(struct sock *sk) -{ - struct dst_entry *dst, *dst_test; - u32 peer_sid = SECSID_NULL; - - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - dst = sk_dst_get(sk); - if (!dst) - goto out; - - for (dst_test = dst; dst_test != 0; - dst_test = dst_test->child) { - struct xfrm_state *x = dst_test->xfrm; - - if (x && selinux_authorizable_xfrm(x)) { - struct xfrm_sec_ctx *ctx = x->security; - peer_sid = ctx->ctx_sid; - break; - } - } - dst_release(dst); - -out: - return peer_sid; -} - -/* - * SELinux internal function to retrieve the context of a UDP packet - * based on its security association used to connect to the remote socket. - * - * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message - * type SCM_SECURITY. - */ -u32 selinux_socket_getpeer_dgram(struct sk_buff *skb) -{ - struct sec_path *sp; - - if (skb == NULL) - return SECSID_NULL; - - if (skb->sk->sk_protocol != IPPROTO_UDP) - return SECSID_NULL; - - sp = skb->sp; - if (sp) { - int i; - - for (i = sp->len-1; i >= 0; i--) { - struct xfrm_state *x = sp->xvec[i]; - if (selinux_authorizable_xfrm(x)) { - struct xfrm_sec_ctx *ctx = x->security; - return ctx->ctx_sid; - } - } - } - - return SECSID_NULL; -} - /* * LSM hook implementation that authorizes deletion of labeled SAs. */ @@ -532,6 +417,13 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, } } + /* + * This check even when there's no association involved is + * intended, according to Trent Jaeger, to make sure a + * process can't engage in non-ipsec communication unless + * explicitly allowed by policy. + */ + rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad); @@ -543,10 +435,10 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, * If we have no security association, then we need to determine * whether the socket is allowed to send to an unlabelled destination. * If we do have a authorizable security association, then it has already been - * checked in xfrm_policy_lookup hook. + * checked in the selinux_xfrm_state_pol_flow_match hook above. */ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, - struct avc_audit_data *ad) + struct avc_audit_data *ad, u8 proto) { struct dst_entry *dst; int rc = 0; @@ -565,6 +457,27 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, } } + switch (proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_COMP: + /* + * We should have already seen this packet once before + * it underwent xfrm(s). No need to subject it to the + * unlabeled check. + */ + goto out; + default: + break; + } + + /* + * This check even when there's no association involved is + * intended, according to Trent Jaeger, to make sure a + * process can't engage in non-ipsec communication unless + * explicitly allowed by policy. + */ + rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad); out: |