mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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