forked from mirrors/linux
		
	af_unix: Allow SO_PEERCRED to work across namespaces.
Use struct pid and struct cred to store the peer credentials on struct sock. This gives enough information to convert the peer credential information to a value relative to whatever namespace the socket is in at the time. This removes nasty surprises when using SO_PEERCRED on socket connetions where the processes on either side are in different pid and user namespaces. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Acked-by: Daniel Lezcano <daniel.lezcano@free.fr> Acked-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3f551f9436
								
							
						
					
					
						commit
						109f6e39fa
					
				
					 3 changed files with 42 additions and 16 deletions
				
			
		|  | @ -295,7 +295,8 @@ struct sock { | ||||||
| 	unsigned short		sk_ack_backlog; | 	unsigned short		sk_ack_backlog; | ||||||
| 	unsigned short		sk_max_ack_backlog; | 	unsigned short		sk_max_ack_backlog; | ||||||
| 	__u32			sk_priority; | 	__u32			sk_priority; | ||||||
| 	struct ucred		sk_peercred; | 	struct pid		*sk_peer_pid; | ||||||
|  | 	const struct cred	*sk_peer_cred; | ||||||
| 	long			sk_rcvtimeo; | 	long			sk_rcvtimeo; | ||||||
| 	long			sk_sndtimeo; | 	long			sk_sndtimeo; | ||||||
| 	struct sk_filter      	*sk_filter; | 	struct sk_filter      	*sk_filter; | ||||||
|  |  | ||||||
|  | @ -915,11 +915,15 @@ int sock_getsockopt(struct socket *sock, int level, int optname, | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case SO_PEERCRED: | 	case SO_PEERCRED: | ||||||
| 		if (len > sizeof(sk->sk_peercred)) | 	{ | ||||||
| 			len = sizeof(sk->sk_peercred); | 		struct ucred peercred; | ||||||
| 		if (copy_to_user(optval, &sk->sk_peercred, len)) | 		if (len > sizeof(peercred)) | ||||||
|  | 			len = sizeof(peercred); | ||||||
|  | 		cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred); | ||||||
|  | 		if (copy_to_user(optval, &peercred, len)) | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
| 		goto lenout; | 		goto lenout; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	case SO_PEERNAME: | 	case SO_PEERNAME: | ||||||
| 	{ | 	{ | ||||||
|  | @ -1133,6 +1137,9 @@ static void __sk_free(struct sock *sk) | ||||||
| 		printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", | 		printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", | ||||||
| 		       __func__, atomic_read(&sk->sk_omem_alloc)); | 		       __func__, atomic_read(&sk->sk_omem_alloc)); | ||||||
| 
 | 
 | ||||||
|  | 	if (sk->sk_peer_cred) | ||||||
|  | 		put_cred(sk->sk_peer_cred); | ||||||
|  | 	put_pid(sk->sk_peer_pid); | ||||||
| 	put_net(sock_net(sk)); | 	put_net(sock_net(sk)); | ||||||
| 	sk_prot_free(sk->sk_prot_creator, sk); | 	sk_prot_free(sk->sk_prot_creator, sk); | ||||||
| } | } | ||||||
|  | @ -1968,9 +1975,8 @@ void sock_init_data(struct socket *sock, struct sock *sk) | ||||||
| 	sk->sk_sndmsg_page	=	NULL; | 	sk->sk_sndmsg_page	=	NULL; | ||||||
| 	sk->sk_sndmsg_off	=	0; | 	sk->sk_sndmsg_off	=	0; | ||||||
| 
 | 
 | ||||||
| 	sk->sk_peercred.pid 	=	0; | 	sk->sk_peer_pid 	=	NULL; | ||||||
| 	sk->sk_peercred.uid	=	-1; | 	sk->sk_peer_cred	=	NULL; | ||||||
| 	sk->sk_peercred.gid	=	-1; |  | ||||||
| 	sk->sk_write_pending	=	0; | 	sk->sk_write_pending	=	0; | ||||||
| 	sk->sk_rcvlowat		=	1; | 	sk->sk_rcvlowat		=	1; | ||||||
| 	sk->sk_rcvtimeo		=	MAX_SCHEDULE_TIMEOUT; | 	sk->sk_rcvtimeo		=	MAX_SCHEDULE_TIMEOUT; | ||||||
|  |  | ||||||
|  | @ -450,11 +450,31 @@ static int unix_release_sock(struct sock *sk, int embrion) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void init_peercred(struct sock *sk) | ||||||
|  | { | ||||||
|  | 	put_pid(sk->sk_peer_pid); | ||||||
|  | 	if (sk->sk_peer_cred) | ||||||
|  | 		put_cred(sk->sk_peer_cred); | ||||||
|  | 	sk->sk_peer_pid  = get_pid(task_tgid(current)); | ||||||
|  | 	sk->sk_peer_cred = get_current_cred(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void copy_peercred(struct sock *sk, struct sock *peersk) | ||||||
|  | { | ||||||
|  | 	put_pid(sk->sk_peer_pid); | ||||||
|  | 	if (sk->sk_peer_cred) | ||||||
|  | 		put_cred(sk->sk_peer_cred); | ||||||
|  | 	sk->sk_peer_pid  = get_pid(peersk->sk_peer_pid); | ||||||
|  | 	sk->sk_peer_cred = get_cred(peersk->sk_peer_cred); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int unix_listen(struct socket *sock, int backlog) | static int unix_listen(struct socket *sock, int backlog) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
| 	struct sock *sk = sock->sk; | 	struct sock *sk = sock->sk; | ||||||
| 	struct unix_sock *u = unix_sk(sk); | 	struct unix_sock *u = unix_sk(sk); | ||||||
|  | 	struct pid *old_pid = NULL; | ||||||
|  | 	const struct cred *old_cred = NULL; | ||||||
| 
 | 
 | ||||||
| 	err = -EOPNOTSUPP; | 	err = -EOPNOTSUPP; | ||||||
| 	if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) | 	if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) | ||||||
|  | @ -470,12 +490,14 @@ static int unix_listen(struct socket *sock, int backlog) | ||||||
| 	sk->sk_max_ack_backlog	= backlog; | 	sk->sk_max_ack_backlog	= backlog; | ||||||
| 	sk->sk_state		= TCP_LISTEN; | 	sk->sk_state		= TCP_LISTEN; | ||||||
| 	/* set credentials so connect can copy them */ | 	/* set credentials so connect can copy them */ | ||||||
| 	sk->sk_peercred.pid	= task_tgid_vnr(current); | 	init_peercred(sk); | ||||||
| 	current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid); |  | ||||||
| 	err = 0; | 	err = 0; | ||||||
| 
 | 
 | ||||||
| out_unlock: | out_unlock: | ||||||
| 	unix_state_unlock(sk); | 	unix_state_unlock(sk); | ||||||
|  | 	put_pid(old_pid); | ||||||
|  | 	if (old_cred) | ||||||
|  | 		put_cred(old_cred); | ||||||
| out: | out: | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
|  | @ -1140,8 +1162,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, | ||||||
| 	unix_peer(newsk)	= sk; | 	unix_peer(newsk)	= sk; | ||||||
| 	newsk->sk_state		= TCP_ESTABLISHED; | 	newsk->sk_state		= TCP_ESTABLISHED; | ||||||
| 	newsk->sk_type		= sk->sk_type; | 	newsk->sk_type		= sk->sk_type; | ||||||
| 	newsk->sk_peercred.pid	= task_tgid_vnr(current); | 	init_peercred(newsk); | ||||||
| 	current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid); |  | ||||||
| 	newu = unix_sk(newsk); | 	newu = unix_sk(newsk); | ||||||
| 	newsk->sk_wq		= &newu->peer_wq; | 	newsk->sk_wq		= &newu->peer_wq; | ||||||
| 	otheru = unix_sk(other); | 	otheru = unix_sk(other); | ||||||
|  | @ -1157,7 +1178,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Set credentials */ | 	/* Set credentials */ | ||||||
| 	sk->sk_peercred = other->sk_peercred; | 	copy_peercred(sk, other); | ||||||
| 
 | 
 | ||||||
| 	sock->state	= SS_CONNECTED; | 	sock->state	= SS_CONNECTED; | ||||||
| 	sk->sk_state	= TCP_ESTABLISHED; | 	sk->sk_state	= TCP_ESTABLISHED; | ||||||
|  | @ -1199,10 +1220,8 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb) | ||||||
| 	sock_hold(skb); | 	sock_hold(skb); | ||||||
| 	unix_peer(ska) = skb; | 	unix_peer(ska) = skb; | ||||||
| 	unix_peer(skb) = ska; | 	unix_peer(skb) = ska; | ||||||
| 	ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current); | 	init_peercred(ska); | ||||||
| 	current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid); | 	init_peercred(skb); | ||||||
| 	ska->sk_peercred.uid = skb->sk_peercred.uid; |  | ||||||
| 	ska->sk_peercred.gid = skb->sk_peercred.gid; |  | ||||||
| 
 | 
 | ||||||
| 	if (ska->sk_type != SOCK_DGRAM) { | 	if (ska->sk_type != SOCK_DGRAM) { | ||||||
| 		ska->sk_state = TCP_ESTABLISHED; | 		ska->sk_state = TCP_ESTABLISHED; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric W. Biederman
						Eric W. Biederman