diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/capability.c | 5 | ||||
-rw-r--r-- | security/security.c | 5 | ||||
-rw-r--r-- | security/selinux/hooks.c | 207 | ||||
-rw-r--r-- | security/selinux/include/netlabel.h | 27 | ||||
-rw-r--r-- | security/selinux/netlabel.c | 186 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 68 | ||||
-rw-r--r-- | security/smack/smack.h | 32 | ||||
-rw-r--r-- | security/smack/smack_access.c | 66 | ||||
-rw-r--r-- | security/smack/smack_lsm.c | 282 | ||||
-rw-r--r-- | security/smack/smackfs.c | 218 | ||||
-rw-r--r-- | security/tomoyo/common.h | 2 |
11 files changed, 441 insertions, 657 deletions
diff --git a/security/capability.c b/security/capability.c index c545bd1300b5..21b6cead6a8e 100644 --- a/security/capability.c +++ b/security/capability.c @@ -620,10 +620,6 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock) return 0; } -static void cap_socket_post_accept(struct socket *sock, struct socket *newsock) -{ -} - static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { return 0; @@ -1014,7 +1010,6 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, socket_connect); set_to_cap_if_null(ops, socket_listen); set_to_cap_if_null(ops, socket_accept); - set_to_cap_if_null(ops, socket_post_accept); set_to_cap_if_null(ops, socket_sendmsg); set_to_cap_if_null(ops, socket_recvmsg); set_to_cap_if_null(ops, socket_getsockname); diff --git a/security/security.c b/security/security.c index c3586c0d97e2..206e53844d2f 100644 --- a/security/security.c +++ b/security/security.c @@ -1007,11 +1007,6 @@ int security_socket_accept(struct socket *sock, struct socket *newsock) return security_ops->socket_accept(sock, newsock); } -void security_socket_post_accept(struct socket *sock, struct socket *newsock) -{ - security_ops->socket_post_accept(sock, newsock); -} - int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { return security_ops->socket_sendmsg(sock, msg, size); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7c52ba243c64..ba808ef6babb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -93,7 +93,6 @@ extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); -extern int selinux_compat_net; extern struct security_operations *security_ops; /* SECMARK reference count */ @@ -311,7 +310,7 @@ static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) ssec->sid = SECINITSID_UNLABELED; sk->sk_security = ssec; - selinux_netlbl_sk_security_reset(ssec, family); + selinux_netlbl_sk_security_reset(ssec); return 0; } @@ -2945,7 +2944,6 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid) static int selinux_revalidate_file_permission(struct file *file, int mask) { const struct cred *cred = current_cred(); - int rc; struct inode *inode = file->f_path.dentry->d_inode; if (!mask) { @@ -2957,29 +2955,15 @@ static int selinux_revalidate_file_permission(struct file *file, int mask) if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE)) mask |= MAY_APPEND; - rc = file_has_perm(cred, file, - file_mask_to_av(inode->i_mode, mask)); - if (rc) - return rc; - - return selinux_netlbl_inode_permission(inode, mask); + return file_has_perm(cred, file, + file_mask_to_av(inode->i_mode, mask)); } static int selinux_file_permission(struct file *file, int mask) { - struct inode *inode = file->f_path.dentry->d_inode; - struct file_security_struct *fsec = file->f_security; - struct inode_security_struct *isec = inode->i_security; - u32 sid = current_sid(); - - if (!mask) { + if (!mask) /* No permission to check. Existence test. */ return 0; - } - - if (sid == fsec->sid && fsec->isid == isec->sid - && fsec->pseqno == avc_policy_seqno()) - return selinux_netlbl_inode_permission(inode, mask); return selinux_revalidate_file_permission(file, mask); } @@ -3723,7 +3707,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, sksec = sock->sk->sk_security; sksec->sid = isec->sid; sksec->sclass = isec->sclass; - err = selinux_netlbl_socket_post_create(sock); + err = selinux_netlbl_socket_post_create(sock->sk, family); } return err; @@ -3914,13 +3898,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - int rc; - - rc = socket_has_perm(current, sock, SOCKET__WRITE); - if (rc) - return rc; - - return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE); + return socket_has_perm(current, sock, SOCKET__WRITE); } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, @@ -4040,72 +4018,6 @@ static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, SECCLASS_NODE, NODE__RECVFROM, ad); } -static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, - struct sk_buff *skb, - struct avc_audit_data *ad, - u16 family, - char *addrp) -{ - int err; - struct sk_security_struct *sksec = sk->sk_security; - u16 sk_class; - u32 netif_perm, node_perm, recv_perm; - u32 port_sid, node_sid, if_sid, sk_sid; - - sk_sid = sksec->sid; - sk_class = sksec->sclass; - - switch (sk_class) { - case SECCLASS_UDP_SOCKET: - netif_perm = NETIF__UDP_RECV; - node_perm = NODE__UDP_RECV; - recv_perm = UDP_SOCKET__RECV_MSG; - break; - case SECCLASS_TCP_SOCKET: - netif_perm = NETIF__TCP_RECV; - 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; - recv_perm = 0; - break; - } - - err = sel_netif_sid(skb->iif, &if_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); - if (err) - return err; - - err = sel_netnode_sid(addrp, family, &node_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); - if (err) - return err; - - if (!recv_perm) - return 0; - err = sel_netport_sid(sk->sk_protocol, - ntohs(ad->u.net.sport), &port_sid); - if (unlikely(err)) { - printk(KERN_WARNING - "SELinux: failure in" - " selinux_sock_rcv_skb_iptables_compat()," - " network port label not found\n"); - return err; - } - return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad); -} - static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, u16 family) { @@ -4123,14 +4035,12 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, if (err) return err; - if (selinux_compat_net) - err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad, - family, addrp); - else if (selinux_secmark_enabled()) + if (selinux_secmark_enabled()) { err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); - if (err) - return err; + if (err) + return err; + } if (selinux_policycap_netpeer) { err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); @@ -4172,7 +4082,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * to the selinux_sock_rcv_skb_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ - if (selinux_compat_net || !selinux_policycap_netpeer) + if (!selinux_policycap_netpeer) return selinux_sock_rcv_skb_compat(sk, skb, family); secmark_active = selinux_secmark_enabled(); @@ -4304,7 +4214,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) newssec->peer_sid = ssec->peer_sid; newssec->sclass = ssec->sclass; - selinux_netlbl_sk_security_reset(newssec, newsk->sk_family); + selinux_netlbl_sk_security_reset(newssec); } static void selinux_sk_getsecid(struct sock *sk, u32 *secid) @@ -4348,16 +4258,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, if (peersid == SECSID_NULL) { req->secid = sksec->sid; req->peer_secid = SECSID_NULL; - return 0; + } else { + err = security_sid_mls_copy(sksec->sid, peersid, &newsid); + if (err) + return err; + req->secid = newsid; + req->peer_secid = peersid; } - err = security_sid_mls_copy(sksec->sid, peersid, &newsid); - if (err) - return err; - - req->secid = newsid; - req->peer_secid = peersid; - return 0; + return selinux_netlbl_inet_conn_request(req, family); } static void selinux_inet_csk_clone(struct sock *newsk, @@ -4374,7 +4283,7 @@ static void selinux_inet_csk_clone(struct sock *newsk, /* 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); + selinux_netlbl_inet_csk_clone(newsk, req->rsk_ops->family); } static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) @@ -4387,8 +4296,6 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) family = PF_INET; selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); - - selinux_netlbl_inet_conn_established(sk, family); } static void selinux_req_classify_flow(const struct request_sock *req, @@ -4540,71 +4447,6 @@ static unsigned int selinux_ipv4_output(unsigned int hooknum, return selinux_ip_output(skb, PF_INET); } -static int selinux_ip_postroute_iptables_compat(struct sock *sk, - int ifindex, - struct avc_audit_data *ad, - u16 family, char *addrp) -{ - int err; - struct sk_security_struct *sksec = sk->sk_security; - u16 sk_class; - u32 netif_perm, node_perm, send_perm; - u32 port_sid, node_sid, if_sid, sk_sid; - - sk_sid = sksec->sid; - sk_class = sksec->sclass; - - switch (sk_class) { - case SECCLASS_UDP_SOCKET: - netif_perm = NETIF__UDP_SEND; - node_perm = NODE__UDP_SEND; - send_perm = UDP_SOCKET__SEND_MSG; - break; - case SECCLASS_TCP_SOCKET: - netif_perm = NETIF__TCP_SEND; - 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; - send_perm = 0; - break; - } - - err = sel_netif_sid(ifindex, &if_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); - return err; - - err = sel_netnode_sid(addrp, family, &node_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); - if (err) - return err; - - if (send_perm != 0) - return 0; - - err = sel_netport_sid(sk->sk_protocol, - ntohs(ad->u.net.dport), &port_sid); - if (unlikely(err)) { - printk(KERN_WARNING - "SELinux: failure in" - " selinux_ip_postroute_iptables_compat()," - " network port label not found\n"); - return err; - } - return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad); -} - static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, int ifindex, u16 family) @@ -4625,15 +4467,10 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) return NF_DROP; - if (selinux_compat_net) { - if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, - &ad, family, addrp)) - return NF_DROP; - } else if (selinux_secmark_enabled()) { + if (selinux_secmark_enabled()) if (avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, PACKET__SEND, &ad)) return NF_DROP; - } if (selinux_policycap_netpeer) if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) @@ -4657,7 +4494,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, * to the selinux_ip_postroute_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ - if (selinux_compat_net || !selinux_policycap_netpeer) + if (!selinux_policycap_netpeer) return selinux_ip_postroute_compat(skb, ifindex, family); #ifdef CONFIG_XFRM /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index b913c8d06038..b4b5b9b2f0be 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -32,6 +32,7 @@ #include <linux/net.h> #include <linux/skbuff.h> #include <net/sock.h> +#include <net/request_sock.h> #include "avc.h" #include "objsec.h" @@ -42,8 +43,7 @@ void selinux_netlbl_cache_invalidate(void); void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway); void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec); -void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, - int family); +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec); int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, @@ -53,9 +53,9 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, u16 family, u32 sid); -void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family); -int selinux_netlbl_socket_post_create(struct socket *sock); -int selinux_netlbl_inode_permission(struct inode *inode, int mask); +int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family); +void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family); +int selinux_netlbl_socket_post_create(struct sock *sk, u16 family); int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, u16 family, @@ -85,8 +85,7 @@ static inline void selinux_netlbl_sk_security_free( } static inline void selinux_netlbl_sk_security_reset( - struct sk_security_struct *ssec, - int family) + struct sk_security_struct *ssec) { return; } @@ -113,17 +112,17 @@ static inline int selinux_netlbl_conn_setsid(struct sock *sk, return 0; } -static inline void selinux_netlbl_inet_conn_established(struct sock *sk, - u16 family) +static inline int selinux_netlbl_inet_conn_request(struct request_sock *req, + u16 family) { - return; + return 0; } -static inline int selinux_netlbl_socket_post_create(struct socket *sock) +static inline void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family) { - return 0; + return; } -static inline int selinux_netlbl_inode_permission(struct inode *inode, - int mask) +static inline int selinux_netlbl_socket_post_create(struct sock *sk, + u16 family) { return 0; } diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 350794ab9b42..2e984413c7b2 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -100,41 +100,6 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk) } /** - * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism - * @sk: the socket to label - * - * Description: - * Attempt to label a socket using the NetLabel mechanism. Returns zero values - * on success, negative values on failure. - * - */ -static int selinux_netlbl_sock_setsid(struct sock *sk) -{ - int rc; - struct sk_security_struct *sksec = sk->sk_security; - struct netlbl_lsm_secattr *secattr; - - if (sksec->nlbl_state != NLBL_REQUIRE) - return 0; - - secattr = selinux_netlbl_sock_genattr(sk); - if (secattr == NULL) - return -ENOMEM; - rc = netlbl_sock_setattr(sk, secattr); - switch (rc) { - case 0: - sksec->nlbl_state = NLBL_LABELED; - break; - case -EDESTADDRREQ: - sksec->nlbl_state = NLBL_REQSKB; - rc = 0; - break; - } - - return rc; -} - -/** * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache * * Description: @@ -188,13 +153,9 @@ void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec) * 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) +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec) { - if (family == PF_INET) - ssec->nlbl_state = NLBL_REQUIRE; - else - ssec->nlbl_state = NLBL_UNSET; + ssec->nlbl_state = NLBL_UNSET; } /** @@ -281,127 +242,86 @@ skbuff_setsid_return: } /** - * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection - * @sk: the new connection + * selinux_netlbl_inet_conn_request - Label an incoming stream connection + * @req: incoming connection request socket * * Description: - * A new connection has been established on @sk so make sure it is labeled - * correctly with the NetLabel susbsystem. + * A new incoming connection request is represented by @req, we need to label + * the new request_sock here and the stack will ensure the on-the-wire label + * will get preserved when a full sock is created once the connection handshake + * is complete. Returns zero on success, negative values on failure. * */ -void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family) +int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family) { int rc; - struct sk_security_struct *sksec = sk->sk_security; - struct netlbl_lsm_secattr *secattr; - struct inet_sock *sk_inet = inet_sk(sk); - struct sockaddr_in addr; - - if (sksec->nlbl_state != NLBL_REQUIRE) - return; + struct netlbl_lsm_secattr secattr; - secattr = selinux_netlbl_sock_genattr(sk); - if (secattr == NULL) - return; + if (family != PF_INET) + return 0; - rc = netlbl_sock_setattr(sk, secattr); - switch (rc) { - case 0: - sksec->nlbl_state = NLBL_LABELED; - break; - case -EDESTADDRREQ: - /* no PF_INET6 support yet because we don't support any IPv6 - * labeling protocols */ - if (family != PF_INET) { - sksec->nlbl_state = NLBL_UNSET; - return; - } - - addr.sin_family = family; - addr.sin_addr.s_addr = sk_inet->daddr; - if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr, - secattr) != 0) { - /* we failed to label the connected socket (could be - * for a variety of reasons, the actual "why" isn't - * important here) so we have to go to our backup plan, - * labeling the packets individually in the netfilter - * local output hook. this is okay but we need to - * adjust the MSS of the connection to take into - * account any labeling overhead, since we don't know - * the exact overhead at this point we'll use the worst - * case value which is 40 bytes for IPv4 */ - struct inet_connection_sock *sk_conn = inet_csk(sk); - sk_conn->icsk_ext_hdr_len += 40 - - (sk_inet->opt ? sk_inet->opt->optlen : 0); - sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); - - sksec->nlbl_state = NLBL_REQSKB; - } else - sksec->nlbl_state = NLBL_CONNLABELED; - break; - default: - /* note that we are failing to label the socket which could be - * a bad thing since it means traffic could leave the system - * without the desired labeling, however, all is not lost as - * we have a check in selinux_netlbl_inode_permission() to - * pick up the pieces that we might drop here because we can't - * return an error code */ - break; - } + netlbl_secattr_init(&secattr); + rc = security_netlbl_sid_to_secattr(req->secid, &secattr); + if (rc != 0) + goto inet_conn_request_return; + rc = netlbl_req_setattr(req, &secattr); +inet_conn_request_return: + netlbl_secattr_destroy(&secattr); + return rc; } /** - * selinux_netlbl_socket_post_create - Label a socket using NetLabel - * @sock: the socket to label + * selinux_netlbl_inet_csk_clone - Initialize the newly created sock + * @sk: the new sock * * Description: - * Attempt to label a socket using the NetLabel mechanism using the given - * SID. Returns zero values on success, negative values on failure. + * A new connection has been established using @sk, we've already labeled the + * socket via the request_sock struct in selinux_netlbl_inet_conn_request() but + * we need to set the NetLabel state here since we now have a sock structure. * */ -int selinux_netlbl_socket_post_create(struct socket *sock) +void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family) { - return selinux_netlbl_sock_setsid(sock->sk); + struct sk_security_struct *sksec = sk->sk_security; + + if (family == PF_INET) + sksec->nlbl_state = NLBL_LABELED; + else + sksec->nlbl_state = NLBL_UNSET; } /** - * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled - * @inode: the file descriptor's inode - * @mask: the permission mask + * selinux_netlbl_socket_post_create - Label a socket using NetLabel + * @sock: the socket to label + * @family: protocol family * * Description: - * Looks at a file's inode and if it is marked as a socket protected by - * NetLabel then verify that the socket has been labeled, if not try to label - * the socket now with the inode's SID. Returns zero on success, negative - * values on failure. + * 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_inode_permission(struct inode *inode, int mask) +int selinux_netlbl_socket_post_create(struct sock *sk, u16 family) { int rc; - struct sock *sk; - struct socket *sock; - struct sk_security_struct *sksec; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr; - if (!S_ISSOCK(inode->i_mode) || - ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) - return 0; - sock = SOCKET_I(inode); - sk = sock->sk; - if (sk == NULL) - return 0; - sksec = sk->sk_security; - if (sksec == NULL || sksec->nlbl_state != NLBL_REQUIRE) + if (family != PF_INET) return 0; - local_bh_disable(); - bh_lock_sock_nested(sk); - if (likely(sksec->nlbl_state == NLBL_REQUIRE)) - rc = selinux_netlbl_sock_setsid(sk); - else + secattr = selinux_netlbl_sock_genattr(sk); + if (secattr == NULL) + return -ENOMEM; + rc = netlbl_sock_setattr(sk, family, secattr); + switch (rc) { + case 0: + sksec->nlbl_state = NLBL_LABELED; + break; + case -EDESTADDRREQ: + sksec->nlbl_state = NLBL_REQSKB; rc = 0; - bh_unlock_sock(sk); - local_bh_enable(); + break; + } return rc; } diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index d3c8b982cfb0..2d5136ec3d54 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -47,8 +47,6 @@ static char *policycap_names[] = { unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; -int selinux_compat_net = 0; - static int __init checkreqprot_setup(char *str) { unsigned long checkreqprot; @@ -58,16 +56,6 @@ static int __init checkreqprot_setup(char *str) } __setup("checkreqprot=", checkreqprot_setup); -static int __init selinux_compat_net_setup(char *str) -{ - unsigned long compat_net; - if (!strict_strtoul(str, 0, &compat_net)) - selinux_compat_net = compat_net ? 1 : 0; - return 1; -} -__setup("selinux_compat_net=", selinux_compat_net_setup); - - static DEFINE_MUTEX(sel_mutex); /* global data for booleans */ @@ -450,61 +438,6 @@ static const 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; - - if (new_value) { - printk(KERN_NOTICE - "SELinux: compat_net is deprecated, please use secmark" - " instead\n"); - selinux_compat_net = 1; - } else - selinux_compat_net = 0; - length = count; -out: - free_page((unsigned long) page); - return length; -} -static const 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 */ @@ -1665,7 +1598,6 @@ 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}, [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, /* last one */ {""} diff --git a/security/smack/smack.h b/security/smack/smack.h index b79582e4fbfd..42ef313f9856 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -18,6 +18,8 @@ #include <linux/security.h> #include <linux/in.h> #include <net/netlabel.h> +#include <linux/list.h> +#include <linux/rculist.h> /* * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is @@ -40,7 +42,6 @@ struct superblock_smack { struct socket_smack { char *smk_out; /* outbound label */ char *smk_in; /* inbound label */ - int smk_labeled; /* label scheme */ char smk_packet[SMK_LABELLEN]; /* TCP peer label */ }; @@ -59,17 +60,10 @@ struct inode_smack { * A label access rule. */ struct smack_rule { - char *smk_subject; - char *smk_object; - int smk_access; -}; - -/* - * An entry in the table of permitted label accesses. - */ -struct smk_list_entry { - struct smk_list_entry *smk_next; - struct smack_rule smk_rule; + struct list_head list; + char *smk_subject; + char *smk_object; + int smk_access; }; /* @@ -85,7 +79,7 @@ struct smack_cipso { * An entry in the table identifying hosts. */ struct smk_netlbladdr { - struct smk_netlbladdr *smk_next; + struct list_head list; struct sockaddr_in smk_host; /* network address */ struct in_addr smk_mask; /* network mask */ char *smk_label; /* label */ @@ -113,7 +107,7 @@ struct smk_netlbladdr { * the cipso direct mapping in used internally. */ struct smack_known { - struct smack_known *smk_next; + struct list_head list; char smk_known[SMK_LABELLEN]; u32 smk_secid; struct smack_cipso *smk_cipso; @@ -138,6 +132,8 @@ struct smack_known { #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT +#define SMACK_CIPSO_OPTION "-CIPSO" + /* * How communications on this socket are treated. * Usually it's determined by the underlying netlabel code @@ -205,8 +201,8 @@ u32 smack_to_secid(const char *); extern int smack_cipso_direct; extern char *smack_net_ambient; extern char *smack_onlycap; +extern const char *smack_cipso_option; -extern struct smack_known *smack_known; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; extern struct smack_known smack_known_huh; @@ -214,8 +210,10 @@ extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_star; extern struct smack_known smack_known_web; -extern struct smk_list_entry *smack_list; -extern struct smk_netlbladdr *smack_netlbladdrs; +extern struct list_head smack_known_list; +extern struct list_head smack_rule_list; +extern struct list_head smk_netlbladdr_list; + extern struct security_operations smack_ops; /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index cfa19ca125e3..ac0a2707f6d4 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -16,48 +16,42 @@ #include "smack.h" struct smack_known smack_known_huh = { - .smk_next = NULL, .smk_known = "?", .smk_secid = 2, .smk_cipso = NULL, }; struct smack_known smack_known_hat = { - .smk_next = &smack_known_huh, .smk_known = "^", .smk_secid = 3, .smk_cipso = NULL, }; struct smack_known smack_known_star = { - .smk_next = &smack_known_hat, .smk_known = "*", .smk_secid = 4, .smk_cipso = NULL, }; struct smack_known smack_known_floor = { - .smk_next = &smack_known_star, .smk_known = "_", .smk_secid = 5, .smk_cipso = NULL, }; struct smack_known smack_known_invalid = { - .smk_next = &smack_known_floor, .smk_known = "", .smk_secid = 6, .smk_cipso = NULL, }; struct smack_known smack_known_web = { - .smk_next = &smack_known_invalid, .smk_known = "@", .smk_secid = 7, .smk_cipso = NULL, }; -struct smack_known *smack_known = &smack_known_web; +LIST_HEAD(smack_known_list); /* * The initial value needs to be bigger than any of the @@ -87,7 +81,6 @@ static u32 smack_next_secid = 10; int smk_access(char *subject_label, char *object_label, int request) { u32 may = MAY_NOT; - struct smk_list_entry *sp; struct smack_rule *srp; /* @@ -139,9 +132,8 @@ int smk_access(char *subject_label, char *object_label, int request) * access (e.g. read is included in readwrite) it's * good. */ - for (sp = smack_list; sp != NULL; sp = sp->smk_next) { - srp = &sp->smk_rule; - + rcu_read_lock(); + list_for_each_entry_rcu(srp, &smack_rule_list, list) { if (srp->smk_subject == subject_label || strcmp(srp->smk_subject, subject_label) == 0) { if (srp->smk_object == object_label || @@ -151,6 +143,7 @@ int smk_access(char *subject_label, char *object_label, int request) } } } + rcu_read_unlock(); /* * This is a bit map operation. */ @@ -228,14 +221,17 @@ struct smack_known *smk_import_entry(const char *string, int len) mutex_lock(&smack_known_lock); - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) + found = 0; + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + found = 1; break; + } + } - if (skp == NULL) { + if (found == 0) { skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); if (skp != NULL) { - skp->smk_next = smack_known; strncpy(skp->smk_known, smack, SMK_MAXLEN); skp->smk_secid = smack_next_secid++; skp->smk_cipso = NULL; @@ -244,8 +240,7 @@ struct smack_known *smk_import_entry(const char *string, int len) * Make sure that the entry is actually * filled before putting it on the list. */ - smp_mb(); - smack_known = skp; + list_add_rcu(&skp->list, &smack_known_list); } } @@ -266,6 +261,9 @@ char *smk_import(const char *string, int len) { struct smack_known *skp; + /* labels cannot begin with a '-' */ + if (string[0] == '-') + return NULL; skp = smk_import_entry(string, len); if (skp == NULL) return NULL; @@ -283,14 +281,19 @@ char *smack_from_secid(const u32 secid) { struct smack_known *skp; - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (skp->smk_secid == secid) + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (skp->smk_secid == secid) { + rcu_read_unlock(); return skp->smk_known; + } + } /* * If we got this far someone asked for the translation * of a secid that is not on the list. */ + rcu_read_unlock(); return smack_known_invalid.smk_known; } @@ -305,9 +308,14 @@ u32 smack_to_secid(const char *smack) { struct smack_known *skp; - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + rcu_read_unlock(); return skp->smk_secid; + } + } + rcu_read_unlock(); return 0; } @@ -332,7 +340,8 @@ void smack_from_cipso(u32 level, char *cp, char *result) struct smack_known *kp; char *final = NULL; - for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) { + rcu_read_lock(); + list_for_each_entry(kp, &smack_known_list, list) { if (kp->smk_cipso == NULL) continue; @@ -344,6 +353,7 @@ void smack_from_cipso(u32 level, char *cp, char *result) spin_unlock_bh(&kp->smk_cipsolock); } + rcu_read_unlock(); if (final == NULL) final = smack_known_huh.smk_known; strncpy(result, final, SMK_MAXLEN); @@ -360,13 +370,19 @@ void smack_from_cipso(u32 level, char *cp, char *result) int smack_to_cipso(const char *smack, struct smack_cipso *cp) { struct smack_known *kp; + int found = 0; - for (kp = smack_known; kp != NULL; kp = kp->smk_next) + rcu_read_lock(); + list_for_each_entry_rcu(kp, &smack_known_list, list) { if (kp->smk_known == smack || - strcmp(kp->smk_known, smack) == 0) + strcmp(kp->smk_known, smack) == 0) { + found = 1; break; + } + } + rcu_read_unlock(); - if (kp == NULL || kp->smk_cipso == NULL) + if (found == 0 || kp->smk_cipso == NULL) return -ENOENT; memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso)); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 84b62b5e9e2c..921514902eca 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -7,6 +7,8 @@ * Casey Schaufler <casey@schaufler-ca.com> * * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Paul Moore <paul.moore@hp.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,6 +22,7 @@ #include <linux/ext2_fs.h> #include <linux/kd.h> #include <asm/ioctls.h> +#include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/mutex.h> @@ -606,6 +609,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) { if (!capable(CAP_MAC_ADMIN)) rc = -EPERM; + /* a label cannot be void and cannot begin with '-' */ + if (size == 0 || (size > 0 && ((char *)value)[0] == '-')) + rc = -EINVAL; } else rc = cap_inode_setxattr(dentry, name, value, size, flags); @@ -1275,7 +1281,6 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ssp->smk_in = csp; ssp->smk_out = csp; - ssp->smk_labeled = SMACK_CIPSO_SOCKET; ssp->smk_packet[0] = '\0'; sk->sk_security = ssp; @@ -1295,6 +1300,43 @@ static void smack_sk_free_security(struct sock *sk) } /** +* smack_host_label - check host based restrictions +* @sip: the object end +* +* looks for host based access restrictions +* +* This version will only be appropriate for really small sets of single label +* hosts. The caller is responsible for ensuring that the RCU read lock is +* taken before calling this function. +* +* Returns the label of the far end or NULL if it's not special. +*/ +static char *smack_host_label(struct sockaddr_in *sip) +{ + struct smk_netlbladdr *snp; + struct in_addr *siap = &sip->sin_addr; + + if (siap->s_addr == 0) + return NULL; + + list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) + /* + * we break after finding the first match because + * the list is sorted from longest to shortest mask + * so we have found the most specific match + */ + if ((&snp->smk_host.sin_addr)->s_addr == + (siap->s_addr & (&snp->smk_mask)->s_addr)) { + /* we have found the special CIPSO option */ + if (snp->smk_label == smack_cipso_option) + return NULL; + return snp->smk_label; + } + + return NULL; +} + +/** * smack_set_catset - convert a capset to netlabel mls categories * @catset: the Smack categories * @sap: where to put the netlabel categories @@ -1365,11 +1407,10 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) */ static int smack_netlabel(struct sock *sk, int labeled) { - struct socket_smack *ssp; + struct socket_smack *ssp = sk->sk_security; struct netlbl_lsm_secattr secattr; int rc = 0; - ssp = sk->sk_security; /* * Usually the netlabel code will handle changing the * packet labeling based on the label. @@ -1387,27 +1428,51 @@ static int smack_netlabel(struct sock *sk, int labeled) else { netlbl_secattr_init(&secattr); smack_to_secattr(ssp->smk_out, &secattr); - rc = netlbl_sock_setattr(sk, &secattr); + rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr); netlbl_secattr_destroy(&secattr); } bh_unlock_sock(sk); local_bh_enable(); - /* - * Remember the label scheme used so that it is not - * necessary to do the netlabel setting if it has not - * changed the next time through. - * - * The -EDESTADDRREQ case is an indication that there's - * a single level host involved. - */ - if (rc == 0) - ssp->smk_labeled = labeled; return rc; } /** + * smack_netlbel_send - Set the secattr on a socket and perform access checks + * @sk: the socket + * @sap: the destination address + * + * Set the correct secattr for the given socket based on the destination + * address and perform any outbound access checks needed. + * + * Returns 0 on success or an error code. + * + */ +static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) +{ + int rc; + int sk_lbl; + char *hostsp; + struct socket_smack *ssp = sk->sk_security; + + rcu_read_lock(); + hostsp = smack_host_label(sap); + if (hostsp != NULL) { + sk_lbl = SMACK_UNLABELED_SOCKET; + rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); + } else { + sk_lbl = SMACK_CIPSO_SOCKET; + rc = 0; + } + rcu_read_unlock(); + if (rc != 0) + return rc; + + return smack_netlabel(sk, sk_lbl); +} + +/** * smack_inode_setsecurity - set smack xattrs * @inode: the object * @name: attribute name @@ -1428,7 +1493,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, struct socket *sock; int rc = 0; - if (value == NULL || size > SMK_LABELLEN) + if (value == NULL || size > SMK_LABELLEN || size == 0) return -EACCES; sp = smk_import(value, size); @@ -1488,41 +1553,6 @@ static int smack_socket_post_create(struct socket *sock, int family, return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); } - -/** - * smack_host_label - check host based restrictions - * @sip: the object end - * - * looks for host based access restrictions - * - * This version will only be appropriate for really small - * sets of single label hosts. - * - * Returns the label of the far end or NULL if it's not special. - */ -static char *smack_host_label(struct sockaddr_in *sip) -{ - struct smk_netlbladdr *snp; - struct in_addr *siap = &sip->sin_addr; - - if (siap->s_addr == 0) - return NULL; - - for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) { - /* - * we break after finding the first match because - * the list is sorted from longest to shortest mask - * so we have found the most specific match - */ - if ((&snp->smk_host.sin_addr)->s_addr == - (siap->s_addr & (&snp->smk_mask)->s_addr)) { - return snp->smk_label; - } - } - - return NULL; -} - /** * smack_socket_connect - connect access check * @sock: the socket @@ -1536,30 +1566,12 @@ static char *smack_host_label(struct sockaddr_in *sip) static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, int addrlen) { - struct socket_smack *ssp = sock->sk->sk_security; - char *hostsp; - int rc; - if (sock->sk == NULL || sock->sk->sk_family != PF_INET) return 0; - if (addrlen < sizeof(struct sockaddr_in)) return -EINVAL; - hostsp = smack_host_label((struct sockaddr_in *)sap); - if (hostsp == NULL) { - if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) - return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); - return 0; - } - - rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); - if (rc != 0) - return rc; - - if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) - return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); - return 0; + return smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); } /** @@ -2260,9 +2272,6 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; - struct socket_smack *ssp = sock->sk->sk_security; - char *hostsp; - int rc; /* * Perfectly reasonable for this to be NULL @@ -2270,22 +2279,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, if (sip == NULL || sip->sin_family != PF_INET) return 0; - hostsp = smack_host_label(sip); - if (hostsp == NULL) { - if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) - return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); - return 0; - } - - rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); - if (rc != 0) - return rc; - - if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) - return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); - - return 0; - + return smack_netlabel_send(sock->sk, sip); } @@ -2490,31 +2484,24 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, } /** - * smack_sock_graft - graft access state between two sockets - * @sk: fresh sock - * @parent: donor socket + * smack_sock_graft - Initialize a newly created socket with an existing sock + * @sk: child sock + * @parent: parent socket * - * Sets the netlabel socket state on sk from parent + * Set the smk_{in,out} state of an existing sock based on the process that + * is creating the new socket. */ static void smack_sock_graft(struct sock *sk, struct socket *parent) { struct socket_smack *ssp; - int rc; - if (sk == NULL) - return; - - if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) + if (sk == NULL || + (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)) return; ssp = sk->sk_security; ssp->smk_in = ssp->smk_out = current_security(); - ssp->smk_packet[0] = '\0'; - - rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET); - if (rc != 0) - printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", - __func__, -rc); + /* cssp->smk_packet is already set in smack_inet_csk_clone() */ } /** @@ -2529,35 +2516,82 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent) static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { - struct netlbl_lsm_secattr skb_secattr; + u16 family = sk->sk_family; struct socket_smack *ssp = sk->sk_security; + struct netlbl_lsm_secattr secattr; + struct sockaddr_in addr; + struct iphdr *hdr; char smack[SMK_LABELLEN]; int rc; - if (skb == NULL) - return -EACCES; + /* handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; - netlbl_secattr_init(&skb_secattr); - rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr); + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) - smack_from_secattr(&skb_secattr, smack); + smack_from_secattr(&secattr, smack); else strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN); - netlbl_secattr_destroy(&skb_secattr); + netlbl_secattr_destroy(&secattr); + /* - * Receiving a packet requires that the other end - * be able to write here. Read access is not required. - * - * If the request is successful save the peer's label - * so that SO_PEERCRED can report it. + * Receiving a packet requires that the other end be able to write + * here. Read access is not required. */ rc = smk_access(smack, ssp->smk_in, MAY_WRITE); - if (rc == 0) - strncpy(ssp->smk_packet, smack, SMK_MAXLEN); + if (rc != 0) + return rc; + + /* + * Save the peer's label in the request_sock so we can later setup + * smk_packet in the child socket so that SO_PEERCRED can report it. + */ + req->peer_secid = smack_to_secid(smack); + + /* + * We need to decide if we want to label the incoming connection here + * if we do we only need to label the request_sock and the stack will + * propogate the wire-label to the sock when it is created. + */ + hdr = ip_hdr(skb); + addr.sin_addr.s_addr = hdr->saddr; + rcu_read_lock(); + if (smack_host_label(&addr) == NULL) { + rcu_read_unlock(); + netlbl_secattr_init(&secattr); + smack_to_secattr(smack, &secattr); + rc = netlbl_req_setattr(req, &secattr); + netlbl_secattr_destroy(&secattr); + } else { + rcu_read_unlock(); + netlbl_req_delattr(req); + } return rc; } +/** + * smack_inet_csk_clone - Copy the connection information to the new socket + * @sk: the new socket + * @req: the connection's request_sock + * + * Transfer the connection's peer label to the newly created socket. + */ +static void smack_inet_csk_clone(struct sock *sk, + const struct request_sock *req) +{ + struct socket_smack *ssp = sk->sk_security; + char *smack; + + if (req->peer_secid != 0) { + smack = smack_from_secid(req->peer_secid); + strncpy(ssp->smk_packet, smack, SMK_MAXLEN); + } else + ssp->smk_packet[0] = '\0'; +} + /* * Key management security hooks * @@ -2909,6 +2943,7 @@ struct security_operations smack_ops = { .sk_free_security = smack_sk_free_security, .sock_graft = smack_sock_graft, .inet_conn_request = smack_inet_conn_request, + .inet_csk_clone = smack_inet_csk_clone, /* key management security hooks */ #ifdef CONFIG_KEYS @@ -2930,6 +2965,17 @@ struct security_operations smack_ops = { .release_secctx = smack_release_secctx, }; + +static __init void init_smack_know_list(void) +{ + list_add(&smack_known_huh.list, &smack_known_list); + list_add(&smack_known_hat.list, &smack_known_list); + list_add(&smack_known_star.list, &smack_known_list); + list_add(&smack_known_floor.list, &smack_known_list); + list_add(&smack_known_invalid.list, &smack_known_list); + list_add(&smack_known_web.list, &smack_known_list); +} + /** * smack_init - initialize the smack system * @@ -2950,6 +2996,8 @@ static __init int smack_init(void) cred = (struct cred *) current->cred; cred->security = &smack_known_floor.smk_known; + /* initilize the smack_know_list */ + init_smack_know_list(); /* * Initialize locks */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index a1b57e4dba3e..e03a7e19c73b 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -80,10 +80,14 @@ char *smack_onlycap; * Packets are sent there unlabeled, but only from tasks that * can write to the specified label. */ -struct smk_netlbladdr *smack_netlbladdrs; + +LIST_HEAD(smk_netlbladdr_list); +LIST_HEAD(smack_rule_list); static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; -struct smk_list_entry *smack_list; + +const char *smack_cipso_option = SMACK_CIPSO_OPTION; + #define SEQ_READ_FINISHED 1 @@ -134,24 +138,27 @@ static void *load_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; - - return smack_list; + if (list_empty(&smack_rule_list)) + return NULL; + return smack_rule_list.next; } static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next; + struct list_head *list = v; - if (skp == NULL) + if (list_is_last(list, &smack_rule_list)) { *pos = SEQ_READ_FINISHED; - - return skp; + return NULL; + } + return list->next; } static int load_seq_show(struct seq_file *s, void *v) { - struct smk_list_entry *slp = (struct smk_list_entry *) v; - struct smack_rule *srp = &slp->smk_rule; + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); seq_printf(s, "%s %s", (char *)srp->smk_subject, (char *)srp->smk_object); @@ -212,32 +219,23 @@ static int smk_open_load(struct inode *inode, struct file *file) */ static int smk_set_access(struct smack_rule *srp) { - struct smk_list_entry *sp; - struct smk_list_entry *newp; + struct smack_rule *sp; int ret = 0; - + int found; mutex_lock(&smack_list_lock); - for (sp = smack_list; sp != NULL; sp = sp->smk_next) - if (sp->smk_rule.smk_subject == srp->smk_subject && - sp->smk_rule.smk_object == srp->smk_object) { - sp->smk_rule.smk_access = srp->smk_access; + found = 0; + list_for_each_entry_rcu(sp, &smack_rule_list, list) { + if (sp->smk_subject == srp->smk_subject && + sp->smk_object == srp->smk_object) { + found = 1; + sp->smk_access = srp->smk_access; break; } - - if (sp == NULL) { - newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL); - if (newp == NULL) { - ret = -ENOMEM; - goto out; - } - - newp->smk_rule = *srp; - newp->smk_next = smack_list; - smack_list = newp; } + if (found == 0) + list_add_rcu(&srp->list, &smack_rule_list); -out: mutex_unlock(&smack_list_lock); return ret; @@ -261,7 +259,7 @@ out: static ssize_t smk_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct smack_rule rule; + struct smack_rule *rule; char *data; int rc = -EINVAL; @@ -272,9 +270,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, */ if (!capable(CAP_MAC_ADMIN)) return -EPERM; - if (*ppos != 0) - return -EINVAL; - if (count != SMK_LOADLEN) + + if (*ppos != 0 || count != SMK_LOADLEN) return -EINVAL; data = kzalloc(count, GFP_KERNEL); @@ -286,25 +283,31 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, goto out; } - rule.smk_subject = smk_import(data, 0); - if (rule.smk_subject == NULL) + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (rule == NULL) { + rc = -ENOMEM; goto out; + } - rule.smk_object = smk_import(data + SMK_LABELLEN, 0); - if (rule.smk_object == NULL) - goto out; + rule->smk_subject = smk_import(data, 0); + if (rule->smk_subject == NULL) + goto out_free_rule; - rule.smk_access = 0; + rule->smk_object = smk_import(data + SMK_LABELLEN, 0); + if (rule->smk_object == NULL) + goto out_free_rule; + + rule->smk_access = 0; switch (data[SMK_LABELLEN + SMK_LABELLEN]) { case '-': break; case 'r': case 'R': - rule.smk_access |= MAY_READ; + rule->smk_access |= MAY_READ; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { @@ -312,10 +315,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'w': case 'W': - rule.smk_access |= MAY_WRITE; + rule->smk_access |= MAY_WRITE; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { @@ -323,10 +326,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'x': case 'X': - rule.smk_access |= MAY_EXEC; + rule->smk_access |= MAY_EXEC; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { @@ -334,17 +337,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'a': case 'A': - rule.smk_access |= MAY_APPEND; + rule->smk_access |= MAY_APPEND; break; default: - goto out; + goto out_free_rule; } - rc = smk_set_access(&rule); + rc = smk_set_access(rule); if (!rc) rc = count; + goto out; +out_free_rule: + kfree(rule); out: kfree(data); return rc; @@ -433,24 +439,26 @@ static void *cipso_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; + if (list_empty(&smack_known_list)) + return NULL; - return smack_known; + return smack_known_list.next; } static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smack_known *skp = ((struct smack_known *) v)->smk_next; + struct list_head *list = v; /* - * Omit labels with no associated cipso value + * labels with no associated cipso value wont be printed + * in cipso_seq_show */ - while (skp != NULL && !skp->smk_cipso) - skp = skp->smk_next; - - if (skp == NULL) + if (list_is_last(list, &smack_known_list)) { *pos = SEQ_READ_FINISHED; + return NULL; + } - return skp; + return list->next; } /* @@ -459,7 +467,9 @@ static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) */ static int cipso_seq_show(struct seq_file *s, void *v) { - struct smack_known *skp = (struct smack_known *) v; + struct list_head *list = v; + struct smack_known *skp = + list_entry(list, struct smack_known, list); struct smack_cipso *scp = skp->smk_cipso; char *cbp; char sep = '/'; @@ -558,6 +568,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, goto unlockedout; } + /* labels cannot begin with a '-' */ + if (data[0] == '-') { + rc = -EINVAL; + goto unlockedout; + } data[count] = '\0'; rule = data; /* @@ -638,18 +653,21 @@ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; - - return smack_netlbladdrs; + if (list_empty(&smk_netlbladdr_list)) + return NULL; + return smk_netlbladdr_list.next; } static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next; + struct list_head *list = v; - if (skp == NULL) + if (list_is_last(list, &smk_netlbladdr_list)) { *pos = SEQ_READ_FINISHED; + return NULL; + } - return skp; + return list->next; } #define BEBITS (sizeof(__be32) * 8) @@ -658,7 +676,9 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) */ static int netlbladdr_seq_show(struct seq_file *s, void *v) { - struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v; + struct list_head *list = v; + struct smk_netlbladdr *skp = + list_entry(list, struct smk_netlbladdr, list); unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; int maskn; u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); @@ -702,30 +722,36 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file) * * This helper insert netlabel in the smack_netlbladdrs list * sorted by netmask length (longest to smallest) + * locked by &smk_netlbladdr_lock in smk_write_netlbladdr + * */ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) { - struct smk_netlbladdr *m; + struct smk_netlbladdr *m, *m_next; - if (smack_netlbladdrs == NULL) { - smack_netlbladdrs = new; + if (list_empty(&smk_netlbladdr_list)) { + list_add_rcu(&new->list, &smk_netlbladdr_list); return; } + m = list_entry(rcu_dereference(smk_netlbladdr_list.next), + struct smk_netlbladdr, list); + /* the comparison '>' is a bit hacky, but works */ - if (new->smk_mask.s_addr > smack_netlbladdrs->smk_mask.s_addr) { - new->smk_next = smack_netlbladdrs; - smack_netlbladdrs = new; + if (new->smk_mask.s_addr > m->smk_mask.s_addr) { + list_add_rcu(&new->list, &smk_netlbladdr_list); return; } - for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) { - if (m->smk_next == NULL) { - m->smk_next = new; + + list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) { + if (list_is_last(&m->list, &smk_netlbladdr_list)) { + list_add_rcu(&new->list, &m->list); return; } - if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) { - new->smk_next = m->smk_next; - m->smk_next = new; + m_next = list_entry(rcu_dereference(m->list.next), + struct smk_netlbladdr, list); + if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) { + list_add_rcu(&new->list, &m->list); return; } } @@ -755,6 +781,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, struct netlbl_audit audit_info; struct in_addr mask; unsigned int m; + int found; u32 mask_bits = (1<<31); __be32 nsa; u32 temp_mask; @@ -789,9 +816,18 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, if (m > BEBITS) return -EINVAL; - sp = smk_import(smack, 0); - if (sp == NULL) - return -EINVAL; + /* if smack begins with '-', its an option, don't import it */ + if (smack[0] != '-') { + sp = smk_import(smack, 0); + if (sp == NULL) + return -EINVAL; + } else { + /* check known options */ + if (strcmp(smack, smack_cipso_option) == 0) + sp = (char *)smack_cipso_option; + else + return -EINVAL; + } for (temp_mask = 0; m > 0; m--) { temp_mask |= mask_bits; @@ -808,14 +844,17 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, nsa = newname.sin_addr.s_addr; /* try to find if the prefix is already in the list */ - for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next) + found = 0; + list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { if (skp->smk_host.sin_addr.s_addr == nsa && - skp->smk_mask.s_addr == mask.s_addr) + skp->smk_mask.s_addr == mask.s_addr) { + found = 1; break; - + } + } smk_netlabel_audit_set(&audit_info); - if (skp == NULL) { + if (found == 0) { skp = kzalloc(sizeof(*skp), GFP_KERNEL); if (skp == NULL) rc = -ENOMEM; @@ -827,18 +866,23 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, smk_netlbladdr_insert(skp); } } else { - rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, - PF_INET, &audit_info); + /* we delete the unlabeled entry, only if the previous label + * wasnt the special CIPSO option */ + if (skp->smk_label != smack_cipso_option) + rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, + &skp->smk_host.sin_addr, &skp->smk_mask, + PF_INET, &audit_info); + else + rc = 0; skp->smk_label = sp; } /* * Now tell netlabel about the single label nature of * this host so that incoming packets get labeled. + * but only if we didn't get the special CIPSO option */ - - if (rc == 0) + if (rc == 0 && sp != smack_cipso_option) rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, smack_to_secid(skp->smk_label), &audit_info); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 6dcb7cc0ed1d..26a76d67aa1c 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -55,7 +55,7 @@ struct tomoyo_path_info { struct tomoyo_path_info_with_data { /* Keep "head" first, for this pointer is passed to tomoyo_free(). */ struct tomoyo_path_info head; - char bariier1[16]; /* Safeguard for overrun. */ + char barrier1[16]; /* Safeguard for overrun. */ char body[TOMOYO_MAX_PATHNAME_LEN]; char barrier2[16]; /* Safeguard for overrun. */ }; |