diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 202 | 
1 files changed, 132 insertions, 70 deletions
| diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e9969a2fc84..65fb5e8ea94 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;  		} @@ -1117,8 +1120,8 @@ static int file_has_perm(struct task_struct *tsk,  {  	struct task_security_struct *tsec = tsk->security;  	struct file_security_struct *fsec = file->f_security; -	struct vfsmount *mnt = file->f_vfsmnt; -	struct dentry *dentry = file->f_dentry; +	struct vfsmount *mnt = file->f_path.mnt; +	struct dentry *dentry = file->f_path.dentry;  	struct inode *inode = dentry->d_inode;  	struct avc_audit_data ad;  	int rc; @@ -1578,7 +1581,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)  static int selinux_bprm_set_security(struct linux_binprm *bprm)  {  	struct task_security_struct *tsec; -	struct inode *inode = bprm->file->f_dentry->d_inode; +	struct inode *inode = bprm->file->f_path.dentry->d_inode;  	struct inode_security_struct *isec;  	struct bprm_security_struct *bsec;  	u32 newsid; @@ -1618,10 +1621,10 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)  	}  	AVC_AUDIT_DATA_INIT(&ad, FS); -	ad.u.fs.mnt = bprm->file->f_vfsmnt; -	ad.u.fs.dentry = bprm->file->f_dentry; +	ad.u.fs.mnt = bprm->file->f_path.mnt; +	ad.u.fs.dentry = bprm->file->f_path.dentry; -	if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) +	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)  		newsid = tsec->sid;          if (tsec->sid == newsid) { @@ -1692,9 +1695,10 @@ static inline void flush_unauthorized_files(struct files_struct * files)  	struct tty_struct *tty;  	struct fdtable *fdt;  	long j = -1; +	int drop_tty = 0;  	mutex_lock(&tty_mutex); -	tty = current->signal->tty; +	tty = get_current_tty();  	if (tty) {  		file_list_lock();  		file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list); @@ -1704,15 +1708,17 @@ static inline void flush_unauthorized_files(struct files_struct * files)  			   than using file_has_perm, as this particular open  			   file may belong to another process and we are only  			   interested in the inode-based check here. */ -			struct inode *inode = file->f_dentry->d_inode; +			struct inode *inode = file->f_path.dentry->d_inode;  			if (inode_has_perm(current, inode,  					   FILE__READ | FILE__WRITE, NULL)) { -				/* Reset controlling tty. */ -				current->signal->tty = NULL; -				current->signal->tty_old_pgrp = 0; +				drop_tty = 1;  			}  		}  		file_list_unlock(); + +		/* Reset controlling tty. */ +		if (drop_tty) +			proc_set_tty(current, NULL);  	}  	mutex_unlock(&tty_mutex); @@ -1728,7 +1734,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)  		j++;  		i = j * __NFDBITS;  		fdt = files_fdtable(files); -		if (i >= fdt->max_fds || i >= fdt->max_fdset) +		if (i >= fdt->max_fds)  			break;  		set = fdt->open_fds->fds_bits[j];  		if (!set) @@ -1754,7 +1760,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; @@ -2413,7 +2420,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t  static int selinux_file_permission(struct file *file, int mask)  {  	int rc; -	struct inode *inode = file->f_dentry->d_inode; +	struct inode *inode = file->f_path.dentry->d_inode;  	if (!mask) {  		/* No permission to check.  Existence test. */ @@ -2590,7 +2597,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  	switch (cmd) {  	        case F_SETFL: -			if (!file->f_dentry || !file->f_dentry->d_inode) { +			if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {  				err = -EINVAL;  				break;  			} @@ -2616,7 +2623,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  		case F_SETLK64:  	        case F_SETLKW64:  #endif -			if (!file->f_dentry || !file->f_dentry->d_inode) { +			if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {  				err = -EINVAL;  				break;  			} @@ -2888,7 +2895,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 +2914,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 +2950,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 +2976,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 +2998,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 +3026,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 +3049,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 +3065,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 +3143,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 +3220,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 +3262,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 +3291,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;  	} @@ -3313,7 +3361,13 @@ static int selinux_socket_getpeername(struct socket *sock)  static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)  { -	return socket_has_perm(current, sock, SOCKET__SETOPT); +	int err; + +	err = socket_has_perm(current, sock, SOCKET__SETOPT); +	if (err) +		return err; + +	return selinux_netlbl_socket_setsockopt(sock, level, optname);  }  static int selinux_socket_getsockopt(struct socket *sock, int level, @@ -3431,7 +3485,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; @@ -3480,14 +3540,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; @@ -3517,25 +3577,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;  	} @@ -3567,13 +3618,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; @@ -3600,7 +3650,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) @@ -3634,17 +3684,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;  	} @@ -3653,6 +3696,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;  } @@ -3662,12 +3706,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, @@ -3750,7 +3805,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; @@ -3801,6 +3862,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) @@ -3812,7 +3874,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; @@ -3826,7 +3888,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;  } @@ -4732,6 +4794,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 @@ -4744,7 +4807,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 | 
