Commit 109f6e39 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by David S. Miller

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: default avatarEric W. Biederman <ebiederm@xmission.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@free.fr>
Acked-by: default avatarPavel Emelyanov <xemul@openvz.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3f551f94
...@@ -295,7 +295,8 @@ struct sock { ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment