diff options
| -rw-r--r-- | include/linux/in.h | 1 | ||||
| -rw-r--r-- | include/linux/security.h | 25 | ||||
| -rw-r--r-- | include/linux/socket.h | 1 | ||||
| -rw-r--r-- | net/core/sock.c | 2 | ||||
| -rw-r--r-- | net/ipv4/ip_sockglue.c | 31 | ||||
| -rw-r--r-- | security/dummy.c | 10 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 46 | ||||
| -rw-r--r-- | security/selinux/include/xfrm.h | 2 | ||||
| -rw-r--r-- | security/selinux/xfrm.c | 68 | 
9 files changed, 169 insertions, 17 deletions
diff --git a/include/linux/in.h b/include/linux/in.h index ba355384016..94f557fa463 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -72,6 +72,7 @@ struct in_addr {  #define IP_FREEBIND	15  #define IP_IPSEC_POLICY	16  #define IP_XFRM_POLICY	17 +#define IP_PASSSEC	18  /* BSD compatibility */  #define IP_RECVRETOPTS	IP_RETOPTS diff --git a/include/linux/security.h b/include/linux/security.h index 7cbef482e13..b18eb8cfa63 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1286,7 +1286,8 @@ struct security_operations {  	int (*socket_setsockopt) (struct socket * sock, int level, int optname);  	int (*socket_shutdown) (struct socket * sock, int how);  	int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb); -	int (*socket_getpeersec) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len); +	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len); +	int (*socket_getpeersec_dgram) (struct sk_buff *skb, char **secdata, u32 *seclen);  	int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);  	void (*sk_free_security) (struct sock *sk);  	unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir); @@ -2741,10 +2742,16 @@ static inline int security_sock_rcv_skb (struct sock * sk,  	return security_ops->socket_sock_rcv_skb (sk, skb);  } -static inline int security_socket_getpeersec(struct socket *sock, char __user *optval, -					     int __user *optlen, unsigned len) +static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, +						    int __user *optlen, unsigned len)  { -	return security_ops->socket_getpeersec(sock, optval, optlen, len); +	return security_ops->socket_getpeersec_stream(sock, optval, optlen, len); +} + +static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, +						   u32 *seclen) +{ +	return security_ops->socket_getpeersec_dgram(skb, secdata, seclen);  }  static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority) @@ -2863,8 +2870,14 @@ static inline int security_sock_rcv_skb (struct sock * sk,  	return 0;  } -static inline int security_socket_getpeersec(struct socket *sock, char __user *optval, -					     int __user *optlen, unsigned len) +static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, +						    int __user *optlen, unsigned len) +{ +	return -ENOPROTOOPT; +} + +static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, +						   u32 *seclen)  {  	return -ENOPROTOOPT;  } diff --git a/include/linux/socket.h b/include/linux/socket.h index b02dda4ee83..9ab2ddd8022 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -150,6 +150,7 @@ __KINLINE struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__  #define	SCM_RIGHTS	0x01		/* rw: access rights (array of int) */  #define SCM_CREDENTIALS 0x02		/* rw: struct ucred		*/ +#define SCM_SECURITY	0x03		/* rw: security label		*/  struct ucred {  	__u32	pid; diff --git a/net/core/sock.c b/net/core/sock.c index 6e00811d44b..5038a5a7bd8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -616,7 +616,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,  			break;  		case SO_PEERSEC: -			return security_socket_getpeersec(sock, optval, optlen, len); +			return security_socket_getpeersec_stream(sock, optval, optlen, len);  		default:  			return(-ENOPROTOOPT); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 2bf8d782f67..b5c4f61518e 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -50,6 +50,7 @@  #define IP_CMSG_TOS		4  #define IP_CMSG_RECVOPTS	8  #define IP_CMSG_RETOPTS		16 +#define IP_CMSG_PASSSEC		32  /*   *	SOL_IP control messages. @@ -109,6 +110,19 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)  	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);  } +static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) +{ +	char *secdata; +	u32 seclen; +	int err; + +	err = security_socket_getpeersec_dgram(skb, &secdata, &seclen); +	if (err) +		return; + +	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata); +} +  void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)  { @@ -138,6 +152,11 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)  	if (flags & 1)  		ip_cmsg_recv_retopts(msg, skb); +	if ((flags>>=1) == 0) +		return; + +	if (flags & 1) +		ip_cmsg_recv_security(msg, skb);  }  int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) @@ -393,7 +412,8 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,  			    (1<<IP_RETOPTS) | (1<<IP_TOS) |   			    (1<<IP_TTL) | (1<<IP_HDRINCL) |   			    (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) |  -			    (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) ||  +			    (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | +			    (1<<IP_PASSSEC))) ||  				optname == IP_MULTICAST_TTL ||   				optname == IP_MULTICAST_LOOP) {   		if (optlen >= sizeof(int)) { @@ -478,6 +498,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,  			else  				inet->cmsg_flags &= ~IP_CMSG_RETOPTS;  			break; +		case IP_PASSSEC: +			if (val) +				inet->cmsg_flags |= IP_CMSG_PASSSEC; +			else +				inet->cmsg_flags &= ~IP_CMSG_PASSSEC; +			break;  		case IP_TOS:	/* This sets both TOS and Precedence */  			if (sk->sk_type == SOCK_STREAM) {  				val &= ~3; @@ -932,6 +958,9 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,  		case IP_RETOPTS:  			val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;  			break; +		case IP_PASSSEC: +			val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; +			break;  		case IP_TOS:  			val = inet->tos;  			break; diff --git a/security/dummy.c b/security/dummy.c index f1a5bd98bf1..a326d6e0b6d 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -763,8 +763,14 @@ static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)  	return 0;  } -static int dummy_socket_getpeersec(struct socket *sock, char __user *optval, -				   int __user *optlen, unsigned len) +static int dummy_socket_getpeersec_stream(struct socket *sock, char __user *optval, +					  int __user *optlen, unsigned len) +{ +	return -ENOPROTOOPT; +} + +static int dummy_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, +					 u32 *seclen)  {  	return -ENOPROTOOPT;  } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b65c201e9ff..5b16196f282 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3318,24 +3318,38 @@ out:  	return err;  } -static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, -				     int __user *optlen, unsigned len) +static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, +					    int __user *optlen, unsigned len)  {  	int err = 0;  	char *scontext;  	u32 scontext_len;  	struct sk_security_struct *ssec;  	struct inode_security_struct *isec; +	u32 peer_sid = 0;  	isec = SOCK_INODE(sock)->i_security; -	if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { + +	/* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */ +	if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) { +		ssec = sock->sk->sk_security; +		peer_sid = ssec->peer_sid; +	} +	else if (isec->sclass == SECCLASS_TCP_SOCKET) { +		peer_sid = selinux_socket_getpeer_stream(sock->sk); + +		if (peer_sid == SECSID_NULL) { +			err = -ENOPROTOOPT; +			goto out; +		} +	} +	else {  		err = -ENOPROTOOPT;  		goto out;  	} -	ssec = sock->sk->sk_security; -	 -	err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); +	err = security_sid_to_context(peer_sid, &scontext, &scontext_len); +  	if (err)  		goto out; @@ -3356,6 +3370,23 @@ out:  	return err;  } +static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen) +{ +	int err = 0; +	u32 peer_sid = selinux_socket_getpeer_dgram(skb); + +	if (peer_sid == SECSID_NULL) +		return -EINVAL; + +	err = security_sid_to_context(peer_sid, secdata, seclen); +	if (err) +		return err; + +	return 0; +} + + +  static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)  {  	return sk_alloc_security(sk, family, priority); @@ -4344,7 +4375,8 @@ static struct security_operations selinux_ops = {  	.socket_setsockopt =		selinux_socket_setsockopt,  	.socket_shutdown =		selinux_socket_shutdown,  	.socket_sock_rcv_skb =		selinux_socket_sock_rcv_skb, -	.socket_getpeersec =		selinux_socket_getpeersec, +	.socket_getpeersec_stream =	selinux_socket_getpeersec_stream, +	.socket_getpeersec_dgram =	selinux_socket_getpeersec_dgram,  	.sk_alloc_security =		selinux_sk_alloc_security,  	.sk_free_security =		selinux_sk_free_security,  	.sk_getsid = 			selinux_sk_getsid_security, diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 8e87996c6dd..a7f388bff3f 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -39,6 +39,8 @@ static inline u32 selinux_no_sk_sid(struct flowi *fl)  #ifdef CONFIG_SECURITY_NETWORK_XFRM  int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb);  int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb); +u32 selinux_socket_getpeer_stream(struct sock *sk); +u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);  #else  static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)  { diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index b2af7ca496c..dfab6c88669 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -225,6 +225,74 @@ void selinux_xfrm_state_free(struct xfrm_state *x)  }  /* + * SELinux internal function to retrieve the context of a connected + * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security + * association used to connect to the remote socket. + * + * Retrieve via getsockopt SO_PEERSEC. + */ +u32 selinux_socket_getpeer_stream(struct sock *sk) +{ +	struct dst_entry *dst, *dst_test; +	u32 peer_sid = SECSID_NULL; + +	if (sk->sk_state != TCP_ESTABLISHED) +		goto out; + +	dst = sk_dst_get(sk); +	if (!dst) +		goto out; + + 	for (dst_test = dst; dst_test != 0; +      	     dst_test = dst_test->child) { +		struct xfrm_state *x = dst_test->xfrm; + + 		if (x && selinux_authorizable_xfrm(x)) { +	 	 	struct xfrm_sec_ctx *ctx = x->security; +			peer_sid = ctx->ctx_sid; +			break; +		} +	} +	dst_release(dst); + +out: +	return peer_sid; +} + +/* + * SELinux internal function to retrieve the context of a UDP packet + * based on its security association used to connect to the remote socket. + * + * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message + * type SCM_SECURITY. + */ +u32 selinux_socket_getpeer_dgram(struct sk_buff *skb) +{ +	struct sec_path *sp; + +	if (skb == NULL) +		return SECSID_NULL; + +	if (skb->sk->sk_protocol != IPPROTO_UDP) +		return SECSID_NULL; + +	sp = skb->sp; +	if (sp) { +		int i; + +		for (i = sp->len-1; i >= 0; i--) { +			struct xfrm_state *x = sp->x[i].xvec; +			if (selinux_authorizable_xfrm(x)) { +				struct xfrm_sec_ctx *ctx = x->security; +				return ctx->ctx_sid; +			} +		} +	} + +	return SECSID_NULL; +} + +/*   * LSM hook that controls access to unlabelled packets.  If   * a xfrm_state is authorizable (defined by macro) then it was   * already authorized by the IPSec process.  If not, then  |