diff options
| author | James Morris <jmorris@namei.org> | 2008-10-13 09:35:14 +1100 | 
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2008-10-13 09:35:14 +1100 | 
| commit | 93db628658197aa46bd7f83d429908b6f187ec9c (patch) | |
| tree | b09496f42570a5864212eb8cfde4ba2ea9fa7a40 /security | |
| parent | f1b2a5ace996de339292d4035f9f5b294aecd11e (diff) | |
| parent | 0da939b0058742ad2d8580b7db6b966d0fc72252 (diff) | |
| download | linux-93db628658197aa46bd7f83d429908b6f187ec9c.tar.bz2 | |
Merge branch 'next' into for-linus
Diffstat (limited to 'security')
| -rw-r--r-- | security/selinux/hooks.c | 229 | ||||
| -rw-r--r-- | security/selinux/include/netlabel.h | 44 | ||||
| -rw-r--r-- | security/selinux/include/objsec.h | 9 | ||||
| -rw-r--r-- | security/selinux/netlabel.c | 280 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 13 | ||||
| -rw-r--r-- | security/smack/smack_lsm.c | 5 | ||||
| -rw-r--r-- | security/smack/smackfs.c | 4 | 
7 files changed, 472 insertions, 112 deletions
| diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4a7374c12d9c..c679ba653e1d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -291,6 +291,7 @@ static void sk_free_security(struct sock *sk)  	struct sk_security_struct *ssec = sk->sk_security;  	sk->sk_security = NULL; +	selinux_netlbl_sk_security_free(ssec);  	kfree(ssec);  } @@ -3801,6 +3802,7 @@ out:  static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)  { +	struct sock *sk = sock->sk;  	struct inode_security_struct *isec;  	int err; @@ -3814,7 +3816,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,  	isec = SOCK_INODE(sock)->i_security;  	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; @@ -3848,6 +3849,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,  			goto out;  	} +	err = selinux_netlbl_socket_connect(sk, address); +  out:  	return err;  } @@ -4077,20 +4080,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,  }  static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, -				       struct avc_audit_data *ad, -				       u16 family, char *addrp) +				       u16 family)  {  	int err;  	struct sk_security_struct *sksec = sk->sk_security;  	u32 peer_sid;  	u32 sk_sid = sksec->sid; +	struct avc_audit_data ad; +	char *addrp; + +	AVC_AUDIT_DATA_INIT(&ad, NET); +	ad.u.net.netif = skb->iif; +	ad.u.net.family = family; +	err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); +	if (err) +		return err;  	if (selinux_compat_net) -		err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad, +		err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,  							   family, addrp);  	else  		err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, -				   PACKET__RECV, ad); +				   PACKET__RECV, &ad);  	if (err)  		return err; @@ -4099,12 +4110,14 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,  		if (err)  			return err;  		err = avc_has_perm(sk_sid, peer_sid, -				   SECCLASS_PEER, PEER__RECV, ad); +				   SECCLASS_PEER, PEER__RECV, &ad); +		if (err) +			selinux_netlbl_err(skb, err, 0);  	} else { -		err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad); +		err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);  		if (err)  			return err; -		err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad); +		err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);  	}  	return err; @@ -4118,6 +4131,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  	u32 sk_sid = sksec->sid;  	struct avc_audit_data ad;  	char *addrp; +	u8 secmark_active; +	u8 peerlbl_active;  	if (family != PF_INET && family != PF_INET6)  		return 0; @@ -4126,6 +4141,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  	if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))  		family = PF_INET; +	/* If any sort of compatibility mode is enabled then handoff processing +	 * 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) +		return selinux_sock_rcv_skb_compat(sk, skb, family); + +	secmark_active = selinux_secmark_enabled(); +	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); +	if (!secmark_active && !peerlbl_active) +		return 0; +  	AVC_AUDIT_DATA_INIT(&ad, NET);  	ad.u.net.netif = skb->iif;  	ad.u.net.family = family; @@ -4133,15 +4160,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  	if (err)  		return err; -	/* If any sort of compatibility mode is enabled then handoff processing -	 * 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) -		return selinux_sock_rcv_skb_compat(sk, skb, &ad, -						   family, addrp); - -	if (netlbl_enabled() || selinux_xfrm_enabled()) { +	if (peerlbl_active) {  		u32 peer_sid;  		err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); @@ -4149,13 +4168,17 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  			return err;  		err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,  					       peer_sid, &ad); -		if (err) +		if (err) { +			selinux_netlbl_err(skb, err, 0);  			return err; +		}  		err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,  				   PEER__RECV, &ad); +		if (err) +			selinux_netlbl_err(skb, err, 0);  	} -	if (selinux_secmark_enabled()) { +	if (secmark_active) {  		err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,  				   PACKET__RECV, &ad);  		if (err) @@ -4214,10 +4237,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *  	u32 peer_secid = SECSID_NULL;  	u16 family; -	if (sock) +	if (skb && skb->protocol == htons(ETH_P_IP)) +		family = PF_INET; +	else if (skb && skb->protocol == htons(ETH_P_IPV6)) +		family = PF_INET6; +	else if (sock)  		family = sock->sk->sk_family; -	else if (skb && skb->sk) -		family = skb->sk->sk_family;  	else  		goto out; @@ -4275,8 +4300,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)  	    sk->sk_family == PF_UNIX)  		isec->sid = sksec->sid;  	sksec->sclass = isec->sclass; - -	selinux_netlbl_sock_graft(sk, parent);  }  static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, @@ -4284,10 +4307,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,  {  	struct sk_security_struct *sksec = sk->sk_security;  	int err; +	u16 family = sk->sk_family;  	u32 newsid;  	u32 peersid; -	err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid); +	/* handle mapped IPv4 packets arriving via IPv6 sockets */ +	if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) +		family = PF_INET; + +	err = selinux_skb_peerlbl_sid(skb, family, &peersid);  	if (err)  		return err;  	if (peersid == SECSID_NULL) { @@ -4322,12 +4350,18 @@ static void selinux_inet_csk_clone(struct sock *newsk,  	selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);  } -static void selinux_inet_conn_established(struct sock *sk, -				struct sk_buff *skb) +static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)  { +	u16 family = sk->sk_family;  	struct sk_security_struct *sksec = sk->sk_security; -	selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid); +	/* handle mapped IPv4 packets arriving via IPv6 sockets */ +	if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) +		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, @@ -4377,39 +4411,54 @@ out:  static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,  				       u16 family)  { +	int err;  	char *addrp;  	u32 peer_sid;  	struct avc_audit_data ad;  	u8 secmark_active; +	u8 netlbl_active;  	u8 peerlbl_active;  	if (!selinux_policycap_netpeer)  		return NF_ACCEPT;  	secmark_active = selinux_secmark_enabled(); -	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); +	netlbl_active = netlbl_enabled(); +	peerlbl_active = netlbl_active || selinux_xfrm_enabled();  	if (!secmark_active && !peerlbl_active)  		return NF_ACCEPT; +	if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) +		return NF_DROP; +  	AVC_AUDIT_DATA_INIT(&ad, NET);  	ad.u.net.netif = ifindex;  	ad.u.net.family = family;  	if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)  		return NF_DROP; -	if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) -		return NF_DROP; - -	if (peerlbl_active) -		if (selinux_inet_sys_rcv_skb(ifindex, addrp, family, -					     peer_sid, &ad) != 0) +	if (peerlbl_active) { +		err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, +					       peer_sid, &ad); +		if (err) { +			selinux_netlbl_err(skb, err, 1);  			return NF_DROP; +		} +	}  	if (secmark_active)  		if (avc_has_perm(peer_sid, skb->secmark,  				 SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))  			return NF_DROP; +	if (netlbl_active) +		/* we do this in the FORWARD path and not the POST_ROUTING +		 * path because we want to make sure we apply the necessary +		 * labeling before IPsec is applied so we can leverage AH +		 * protection */ +		if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0) +			return NF_DROP; +  	return NF_ACCEPT;  } @@ -4433,6 +4482,37 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,  }  #endif	/* IPV6 */ +static unsigned int selinux_ip_output(struct sk_buff *skb, +				      u16 family) +{ +	u32 sid; + +	if (!netlbl_enabled()) +		return NF_ACCEPT; + +	/* we do this in the LOCAL_OUT path and not the POST_ROUTING path +	 * because we want to make sure we apply the necessary labeling +	 * before IPsec is applied so we can leverage AH protection */ +	if (skb->sk) { +		struct sk_security_struct *sksec = skb->sk->sk_security; +		sid = sksec->sid; +	} else +		sid = SECINITSID_KERNEL; +	if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0) +		return NF_DROP; + +	return NF_ACCEPT; +} + +static unsigned int selinux_ipv4_output(unsigned int hooknum, +					struct sk_buff *skb, +					const struct net_device *in, +					const struct net_device *out, +					int (*okfn)(struct sk_buff *)) +{ +	return selinux_ip_output(skb, PF_INET); +} +  static int selinux_ip_postroute_iptables_compat(struct sock *sk,  						int ifindex,  						struct avc_audit_data *ad, @@ -4500,30 +4580,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk,  static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,  						int ifindex, -						struct avc_audit_data *ad, -						u16 family, -						char *addrp, -						u8 proto) +						u16 family)  {  	struct sock *sk = skb->sk;  	struct sk_security_struct *sksec; +	struct avc_audit_data ad; +	char *addrp; +	u8 proto;  	if (sk == NULL)  		return NF_ACCEPT;  	sksec = sk->sk_security; +	AVC_AUDIT_DATA_INIT(&ad, NET); +	ad.u.net.netif = ifindex; +	ad.u.net.family = family; +	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)) +							 &ad, family, addrp))  			return NF_DROP;  	} else {  		if (avc_has_perm(sksec->sid, skb->secmark, -				 SECCLASS_PACKET, PACKET__SEND, ad)) +				 SECCLASS_PACKET, PACKET__SEND, &ad))  			return NF_DROP;  	}  	if (selinux_policycap_netpeer) -		if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto)) +		if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))  			return NF_DROP;  	return NF_ACCEPT; @@ -4537,23 +4623,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	struct sock *sk;  	struct avc_audit_data ad;  	char *addrp; -	u8 proto;  	u8 secmark_active;  	u8 peerlbl_active; -	AVC_AUDIT_DATA_INIT(&ad, NET); -	ad.u.net.netif = ifindex; -	ad.u.net.family = family; -	if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) -		return NF_DROP; -  	/* If any sort of compatibility mode is enabled then handoff processing  	 * 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) -		return selinux_ip_postroute_compat(skb, ifindex, &ad, -						   family, addrp, proto); +		return selinux_ip_postroute_compat(skb, ifindex, family);  	/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec  	 * packet transformation so allow the packet to pass without any checks @@ -4569,21 +4647,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	if (!secmark_active && !peerlbl_active)  		return NF_ACCEPT; -	/* if the packet is locally generated (skb->sk != NULL) then use the -	 * socket's label as the peer label, otherwise the packet is being -	 * forwarded through this system and we need to fetch the peer label -	 * directly from the packet */ +	/* if the packet is being forwarded then get the peer label from the +	 * packet itself; otherwise check to see if it is from a local +	 * application or the kernel, if from an application get the peer label +	 * from the sending socket, otherwise use the kernel's sid */  	sk = skb->sk; -	if (sk) { +	if (sk == NULL) { +		switch (family) { +		case PF_INET: +			if (IPCB(skb)->flags & IPSKB_FORWARDED) +				secmark_perm = PACKET__FORWARD_OUT; +			else +				secmark_perm = PACKET__SEND; +			break; +		case PF_INET6: +			if (IP6CB(skb)->flags & IP6SKB_FORWARDED) +				secmark_perm = PACKET__FORWARD_OUT; +			else +				secmark_perm = PACKET__SEND; +			break; +		default: +			return NF_DROP; +		} +		if (secmark_perm == PACKET__FORWARD_OUT) { +			if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) +				return NF_DROP; +		} else +			peer_sid = SECINITSID_KERNEL; +	} else {  		struct sk_security_struct *sksec = sk->sk_security;  		peer_sid = sksec->sid;  		secmark_perm = PACKET__SEND; -	} else { -		if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) -				return NF_DROP; -		secmark_perm = PACKET__FORWARD_OUT;  	} +	AVC_AUDIT_DATA_INIT(&ad, NET); +	ad.u.net.netif = ifindex; +	ad.u.net.family = family; +	if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) +		return NF_DROP; +  	if (secmark_active)  		if (avc_has_perm(peer_sid, skb->secmark,  				 SECCLASS_PACKET, secmark_perm, &ad)) @@ -5657,6 +5759,13 @@ static struct nf_hook_ops selinux_ipv4_ops[] = {  		.pf =		PF_INET,  		.hooknum =	NF_INET_FORWARD,  		.priority =	NF_IP_PRI_SELINUX_FIRST, +	}, +	{ +		.hook =		selinux_ipv4_output, +		.owner =	THIS_MODULE, +		.pf =		PF_INET, +		.hooknum =	NF_INET_LOCAL_OUT, +		.priority =	NF_IP_PRI_SELINUX_FIRST,  	}  }; diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 487a7d81fe20..b913c8d06038 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -39,6 +39,9 @@  #ifdef CONFIG_NETLABEL  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); @@ -46,8 +49,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,  				 u16 family,  				 u32 *type,  				 u32 *sid); +int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, +				 u16 family, +				 u32 sid); -void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); +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_sock_rcv_skb(struct sk_security_struct *sksec, @@ -57,12 +63,27 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,  int selinux_netlbl_socket_setsockopt(struct socket *sock,  				     int level,  				     int optname); +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr); +  #else  static inline void selinux_netlbl_cache_invalidate(void)  {  	return;  } +static inline void selinux_netlbl_err(struct sk_buff *skb, +				      int error, +				      int gateway) +{ +	return; +} + +static inline void selinux_netlbl_sk_security_free( +					       struct sk_security_struct *ssec) +{ +	return; +} +  static inline void selinux_netlbl_sk_security_reset(  					       struct sk_security_struct *ssec,  					       int family) @@ -79,9 +100,21 @@ static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,  	*sid = SECSID_NULL;  	return 0;  } +static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, +					       u16 family, +					       u32 sid) +{ +	return 0; +} -static inline void selinux_netlbl_sock_graft(struct sock *sk, -					     struct socket *sock) +static inline int selinux_netlbl_conn_setsid(struct sock *sk, +					     struct sockaddr *addr) +{ +	return 0; +} + +static inline void selinux_netlbl_inet_conn_established(struct sock *sk, +							u16 family)  {  	return;  } @@ -107,6 +140,11 @@ static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,  {  	return 0;  } +static inline int selinux_netlbl_socket_connect(struct sock *sk, +						struct sockaddr *addr) +{ +	return 0; +}  #endif /* CONFIG_NETLABEL */  #endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 91070ab874ce..f8be8d7fa26d 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -109,16 +109,19 @@ struct netport_security_struct {  };  struct sk_security_struct { -	u32 sid;			/* SID of this object */ -	u32 peer_sid;			/* SID of peer */ -	u16 sclass;			/* sock security class */  #ifdef CONFIG_NETLABEL  	enum {				/* NetLabel state */  		NLBL_UNSET = 0,  		NLBL_REQUIRE,  		NLBL_LABELED, +		NLBL_REQSKB, +		NLBL_CONNLABELED,  	} nlbl_state; +	struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */  #endif +	u32 sid;			/* SID of this object */ +	u32 peer_sid;			/* SID of peer */ +	u16 sclass;			/* sock security class */  };  struct key_security_struct { diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 89b418392f11..f58701a7b728 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -9,7 +9,7 @@   */  /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008   *   * 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,8 +29,12 @@  #include <linux/spinlock.h>  #include <linux/rcupdate.h> +#include <linux/ip.h> +#include <linux/ipv6.h>  #include <net/sock.h>  #include <net/netlabel.h> +#include <net/ip.h> +#include <net/ipv6.h>  #include "objsec.h"  #include "security.h" @@ -64,32 +68,69 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,  }  /** + * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr + * @sk: the socket + * + * Description: + * Generate the NetLabel security attributes for a socket, making full use of + * the socket's attribute cache.  Returns a pointer to the security attributes + * on success, NULL on failure. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk) +{ +	int rc; +	struct sk_security_struct *sksec = sk->sk_security; +	struct netlbl_lsm_secattr *secattr; + +	if (sksec->nlbl_secattr != NULL) +		return sksec->nlbl_secattr; + +	secattr = netlbl_secattr_alloc(GFP_ATOMIC); +	if (secattr == NULL) +		return NULL; +	rc = security_netlbl_sid_to_secattr(sksec->sid, secattr); +	if (rc != 0) { +		netlbl_secattr_free(secattr); +		return NULL; +	} +	sksec->nlbl_secattr = secattr; + +	return secattr; +} + +/**   * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism   * @sk: the socket to label - * @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. + * 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, u32 sid) +static int selinux_netlbl_sock_setsid(struct sock *sk)  {  	int rc;  	struct sk_security_struct *sksec = sk->sk_security; -	struct netlbl_lsm_secattr secattr; +	struct netlbl_lsm_secattr *secattr; -	netlbl_secattr_init(&secattr); +	if (sksec->nlbl_state != NLBL_REQUIRE) +		return 0; -	rc = security_netlbl_sid_to_secattr(sid, &secattr); -	if (rc != 0) -		goto sock_setsid_return; -	rc = netlbl_sock_setattr(sk, &secattr); -	if (rc == 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; +	} -sock_setsid_return: -	netlbl_secattr_destroy(&secattr);  	return rc;  } @@ -106,6 +147,38 @@ void selinux_netlbl_cache_invalidate(void)  }  /** + * selinux_netlbl_err - Handle a NetLabel packet error + * @skb: the packet + * @error: the error code + * @gateway: true if host is acting as a gateway, false otherwise + * + * Description: + * When a packet is dropped due to a call to avc_has_perm() pass the error + * code to the NetLabel subsystem so any protocol specific processing can be + * done.  This is safe to call even if you are unsure if NetLabel labeling is + * present on the packet, NetLabel is smart enough to only act when it should. + * + */ +void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway) +{ +	netlbl_skbuff_err(skb, error, gateway); +} + +/** + * selinux_netlbl_sk_security_free - Free the NetLabel fields + * @sssec: the sk_security_struct + * + * Description: + * Free all of the memory in the NetLabel fields of a sk_security_struct. + * + */ +void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec) +{ +	if (ssec->nlbl_secattr != NULL) +		netlbl_secattr_free(ssec->nlbl_secattr); +} + +/**   * selinux_netlbl_sk_security_reset - Reset the NetLabel fields   * @ssec: the sk_security_struct   * @family: the socket family @@ -163,35 +236,118 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,  }  /** - * selinux_netlbl_sock_graft - Netlabel the new socket + * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid + * @skb: the packet + * @family: protocol family + * @sid: the SID + * + * Description + * Call the NetLabel mechanism to set the label of a packet using @sid. + * Returns zero on auccess, negative values on failure. + * + */ +int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, +				 u16 family, +				 u32 sid) +{ +	int rc; +	struct netlbl_lsm_secattr secattr_storage; +	struct netlbl_lsm_secattr *secattr = NULL; +	struct sock *sk; + +	/* if this is a locally generated packet check to see if it is already +	 * being labeled by it's parent socket, if it is just exit */ +	sk = skb->sk; +	if (sk != NULL) { +		struct sk_security_struct *sksec = sk->sk_security; +		if (sksec->nlbl_state != NLBL_REQSKB) +			return 0; +		secattr = sksec->nlbl_secattr; +	} +	if (secattr == NULL) { +		secattr = &secattr_storage; +		netlbl_secattr_init(secattr); +		rc = security_netlbl_sid_to_secattr(sid, secattr); +		if (rc != 0) +			goto skbuff_setsid_return; +	} + +	rc = netlbl_skbuff_setattr(skb, family, secattr); + +skbuff_setsid_return: +	if (secattr == &secattr_storage) +		netlbl_secattr_destroy(secattr); +	return rc; +} + +/** + * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection   * @sk: the new connection - * @sock: the new socket   *   * Description: - * The connection represented by @sk is being grafted onto @sock so set the - * socket's NetLabel to match the SID of @sk. + * A new connection has been established on @sk so make sure it is labeled + * correctly with the NetLabel susbsystem.   *   */ -void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) +void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)  { +	int rc;  	struct sk_security_struct *sksec = sk->sk_security; -	struct netlbl_lsm_secattr secattr; -	u32 nlbl_peer_sid; +	struct netlbl_lsm_secattr *secattr; +	struct inet_sock *sk_inet = inet_sk(sk); +	struct sockaddr_in addr;  	if (sksec->nlbl_state != NLBL_REQUIRE)  		return; -	netlbl_secattr_init(&secattr); -	if (netlbl_sock_getattr(sk, &secattr) == 0 && -	    secattr.flags != NETLBL_SECATTR_NONE && -	    security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0) -		sksec->peer_sid = nlbl_peer_sid; -	netlbl_secattr_destroy(&secattr); +	secattr = selinux_netlbl_sock_genattr(sk); +	if (secattr == NULL) +		return; -	/* 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_sock_setsid(sk, sksec->sid); +	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; +	}  }  /** @@ -205,13 +361,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)   */  int selinux_netlbl_socket_post_create(struct socket *sock)  { -	struct sock *sk = sock->sk; -	struct sk_security_struct *sksec = sk->sk_security; - -	if (sksec->nlbl_state != NLBL_REQUIRE) -		return 0; - -	return selinux_netlbl_sock_setsid(sk, sksec->sid); +	return selinux_netlbl_sock_setsid(sock->sk);  }  /** @@ -246,7 +396,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)  	local_bh_disable();  	bh_lock_sock_nested(sk);  	if (likely(sksec->nlbl_state == NLBL_REQUIRE)) -		rc = selinux_netlbl_sock_setsid(sk, sksec->sid); +		rc = selinux_netlbl_sock_setsid(sk);  	else  		rc = 0;  	bh_unlock_sock(sk); @@ -307,7 +457,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,  		return 0;  	if (nlbl_sid != SECINITSID_UNLABELED) -		netlbl_skbuff_err(skb, rc); +		netlbl_skbuff_err(skb, rc, 0);  	return rc;  } @@ -334,7 +484,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,  	struct netlbl_lsm_secattr secattr;  	if (level == IPPROTO_IP && optname == IP_OPTIONS && -	    sksec->nlbl_state == NLBL_LABELED) { +	    (sksec->nlbl_state == NLBL_LABELED || +	     sksec->nlbl_state == NLBL_CONNLABELED)) {  		netlbl_secattr_init(&secattr);  		lock_sock(sk);  		rc = netlbl_sock_getattr(sk, &secattr); @@ -346,3 +497,50 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,  	return rc;  } + +/** + * selinux_netlbl_socket_connect - Label a client-side socket on connect + * @sk: the socket to label + * @addr: the destination address + * + * Description: + * Attempt to label a connected socket with NetLabel using the given address. + * Returns zero values on success, negative values on failure. + * + */ +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) +{ +	int rc; +	struct sk_security_struct *sksec = sk->sk_security; +	struct netlbl_lsm_secattr *secattr; + +	if (sksec->nlbl_state != NLBL_REQSKB && +	    sksec->nlbl_state != NLBL_CONNLABELED) +		return 0; + +	local_bh_disable(); +	bh_lock_sock_nested(sk); + +	/* connected sockets are allowed to disconnect when the address family +	 * is set to AF_UNSPEC, if that is what is happening we want to reset +	 * the socket */ +	if (addr->sa_family == AF_UNSPEC) { +		netlbl_sock_delattr(sk); +		sksec->nlbl_state = NLBL_REQSKB; +		rc = 0; +		goto socket_connect_return; +	} +	secattr = selinux_netlbl_sock_genattr(sk); +	if (secattr == NULL) { +		rc = -ENOMEM; +		goto socket_connect_return; +	} +	rc = netlbl_conn_setattr(sk, addr, secattr); +	if (rc == 0) +		sksec->nlbl_state = NLBL_CONNLABELED; + +socket_connect_return: +	bh_unlock_sock(sk); +	local_bh_enable(); +	return rc; +} diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ab0cc0c7b944..343c8ab14af0 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2955,7 +2955,7 @@ netlbl_secattr_to_sid_return_cleanup:   */  int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)  { -	int rc = -ENOENT; +	int rc;  	struct context *ctx;  	if (!ss_initialized) @@ -2963,11 +2963,18 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)  	read_lock(&policy_rwlock);  	ctx = sidtab_search(&sidtab, sid); -	if (ctx == NULL) +	if (ctx == NULL) { +		rc = -ENOENT;  		goto netlbl_sid_to_secattr_failure; +	}  	secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],  				  GFP_ATOMIC); -	secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY; +	if (secattr->domain == NULL) { +		rc = -ENOMEM; +		goto netlbl_sid_to_secattr_failure; +	} +	secattr->attr.secid = sid; +	secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;  	mls_export_netlbl_lvl(ctx, secattr);  	rc = mls_export_netlbl_cat(ctx, secattr);  	if (rc != 0) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 87d75417ea93..6e2dc0bab70d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2179,7 +2179,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  	 * This is the simplist possible security model  	 * for networking.  	 */ -	return smk_access(smack, ssp->smk_in, MAY_WRITE); +	rc = smk_access(smack, ssp->smk_in, MAY_WRITE); +	if (rc != 0) +		netlbl_skbuff_err(skb, rc, 0); +	return rc;  }  /** diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index e7c642458ec9..c21d8c8bf0c7 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -354,9 +354,11 @@ static void smk_cipso_doi(void)  		doip->tags[rc] = CIPSO_V4_TAG_INVALID;  	rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info); -	if (rc != 0) +	if (rc != 0) {  		printk(KERN_WARNING "%s:%d add rc = %d\n",  		       __func__, __LINE__, rc); +		kfree(doip); +	}  }  /** |