diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 99 | 
1 files changed, 62 insertions, 37 deletions
| diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index fef2cc5e9d2b..4414a18c63b4 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -282,7 +282,7 @@ static inline struct sock *unix_find_socket_byname(struct net *net,  	return s;  } -static struct sock *unix_find_socket_byinode(struct net *net, struct inode *i) +static struct sock *unix_find_socket_byinode(struct inode *i)  {  	struct sock *s;  	struct hlist_node *node; @@ -292,9 +292,6 @@ static struct sock *unix_find_socket_byinode(struct net *net, struct inode *i)  		    &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {  		struct dentry *dentry = unix_sk(s)->dentry; -		if (!net_eq(sock_net(s), net)) -			continue; -  		if (dentry && dentry->d_inode == i) {  			sock_hold(s);  			goto found; @@ -450,11 +447,31 @@ static int unix_release_sock(struct sock *sk, int embrion)  	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)  {  	int err;  	struct sock *sk = sock->sk;  	struct unix_sock *u = unix_sk(sk); +	struct pid *old_pid = NULL; +	const struct cred *old_cred = NULL;  	err = -EOPNOTSUPP;  	if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) @@ -470,12 +487,14 @@ static int unix_listen(struct socket *sock, int backlog)  	sk->sk_max_ack_backlog	= backlog;  	sk->sk_state		= TCP_LISTEN;  	/* set credentials so connect can copy them */ -	sk->sk_peercred.pid	= task_tgid_vnr(current); -	current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid); +	init_peercred(sk);  	err = 0;  out_unlock:  	unix_state_unlock(sk); +	put_pid(old_pid); +	if (old_cred) +		put_cred(old_cred);  out:  	return err;  } @@ -736,7 +755,7 @@ static struct sock *unix_find_other(struct net *net,  		err = -ECONNREFUSED;  		if (!S_ISSOCK(inode->i_mode))  			goto put_fail; -		u = unix_find_socket_byinode(net, inode); +		u = unix_find_socket_byinode(inode);  		if (!u)  			goto put_fail; @@ -1140,8 +1159,7 @@ restart:  	unix_peer(newsk)	= sk;  	newsk->sk_state		= TCP_ESTABLISHED;  	newsk->sk_type		= sk->sk_type; -	newsk->sk_peercred.pid	= task_tgid_vnr(current); -	current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid); +	init_peercred(newsk);  	newu = unix_sk(newsk);  	newsk->sk_wq		= &newu->peer_wq;  	otheru = unix_sk(other); @@ -1157,7 +1175,7 @@ restart:  	}  	/* Set credentials */ -	sk->sk_peercred = other->sk_peercred; +	copy_peercred(sk, other);  	sock->state	= SS_CONNECTED;  	sk->sk_state	= TCP_ESTABLISHED; @@ -1199,10 +1217,8 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)  	sock_hold(skb);  	unix_peer(ska) = skb;  	unix_peer(skb) = ska; -	ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current); -	current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid); -	ska->sk_peercred.uid = skb->sk_peercred.uid; -	ska->sk_peercred.gid = skb->sk_peercred.gid; +	init_peercred(ska); +	init_peercred(skb);  	if (ska->sk_type != SOCK_DGRAM) {  		ska->sk_state = TCP_ESTABLISHED; @@ -1297,18 +1313,20 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)  	int i;  	scm->fp = UNIXCB(skb).fp; -	skb->destructor = sock_wfree;  	UNIXCB(skb).fp = NULL;  	for (i = scm->fp->count-1; i >= 0; i--)  		unix_notinflight(scm->fp->fp[i]);  } -static void unix_destruct_fds(struct sk_buff *skb) +static void unix_destruct_scm(struct sk_buff *skb)  {  	struct scm_cookie scm;  	memset(&scm, 0, sizeof(scm)); -	unix_detach_fds(&scm, skb); +	scm.pid  = UNIXCB(skb).pid; +	scm.cred = UNIXCB(skb).cred; +	if (UNIXCB(skb).fp) +		unix_detach_fds(&scm, skb);  	/* Alas, it calls VFS */  	/* So fscking what? fput() had been SMP-safe since the last Summer */ @@ -1331,10 +1349,22 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)  	for (i = scm->fp->count-1; i >= 0; i--)  		unix_inflight(scm->fp->fp[i]); -	skb->destructor = unix_destruct_fds;  	return 0;  } +static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds) +{ +	int err = 0; +	UNIXCB(skb).pid  = get_pid(scm->pid); +	UNIXCB(skb).cred = get_cred(scm->cred); +	UNIXCB(skb).fp = NULL; +	if (scm->fp && send_fds) +		err = unix_attach_fds(scm, skb); + +	skb->destructor = unix_destruct_scm; +	return err; +} +  /*   *	Send AF_UNIX data.   */ @@ -1391,12 +1421,9 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,  	if (skb == NULL)  		goto out; -	memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); -	if (siocb->scm->fp) { -		err = unix_attach_fds(siocb->scm, skb); -		if (err) -			goto out_free; -	} +	err = unix_scm_to_skb(siocb->scm, skb, true); +	if (err) +		goto out_free;  	unix_get_secdata(siocb->scm, skb);  	skb_reset_transport_header(skb); @@ -1566,16 +1593,14 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,  		 */  		size = min_t(int, size, skb_tailroom(skb)); -		memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); +  		/* Only send the fds in the first buffer */ -		if (siocb->scm->fp && !fds_sent) { -			err = unix_attach_fds(siocb->scm, skb); -			if (err) { -				kfree_skb(skb); -				goto out_err; -			} -			fds_sent = true; +		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent); +		if (err) { +			kfree_skb(skb); +			goto out_err;  		} +		fds_sent = true;  		err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);  		if (err) { @@ -1692,7 +1717,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,  		siocb->scm = &tmp_scm;  		memset(&tmp_scm, 0, sizeof(tmp_scm));  	} -	siocb->scm->creds = *UNIXCREDS(skb); +	scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);  	unix_set_secdata(siocb->scm, skb);  	if (!(flags & MSG_PEEK)) { @@ -1841,14 +1866,14 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  		if (check_creds) {  			/* Never glue messages from different writers */ -			if (memcmp(UNIXCREDS(skb), &siocb->scm->creds, -				   sizeof(siocb->scm->creds)) != 0) { +			if ((UNIXCB(skb).pid  != siocb->scm->pid) || +			    (UNIXCB(skb).cred != siocb->scm->cred)) {  				skb_queue_head(&sk->sk_receive_queue, skb);  				break;  			}  		} else {  			/* Copy credentials */ -			siocb->scm->creds = *UNIXCREDS(skb); +			scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);  			check_creds = 1;  		} @@ -1881,7 +1906,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  				break;  			} -			kfree_skb(skb); +			consume_skb(skb);  			if (siocb->scm->fp)  				break; |