Skip to content

Commit

Permalink
af_unix: Allow SO_PEERCRED to work across namespaces.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric W. Biederman authored and David S. Miller committed Jun 16, 2010
1 parent 3f551f9 commit 109f6e3
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 16 deletions.
3 changes: 2 additions & 1 deletion include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ struct sock {
unsigned short sk_ack_backlog;
unsigned short sk_max_ack_backlog;
__u32 sk_priority;
struct ucred sk_peercred;
struct pid *sk_peer_pid;
const struct cred *sk_peer_cred;
long sk_rcvtimeo;
long sk_sndtimeo;
struct sk_filter *sk_filter;
Expand Down
18 changes: 12 additions & 6 deletions net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -915,11 +915,15 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;

case SO_PEERCRED:
if (len > sizeof(sk->sk_peercred))
len = sizeof(sk->sk_peercred);
if (copy_to_user(optval, &sk->sk_peercred, len))
{
struct ucred peercred;
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;
goto lenout;
}

case SO_PEERNAME:
{
Expand Down Expand Up @@ -1133,6 +1137,9 @@ static void __sk_free(struct sock *sk)
printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n",
__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));
sk_prot_free(sk->sk_prot_creator, sk);
}
Expand Down Expand Up @@ -1968,9 +1975,8 @@ void sock_init_data(struct socket *sock, struct sock *sk)
sk->sk_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0;

sk->sk_peercred.pid = 0;
sk->sk_peercred.uid = -1;
sk->sk_peercred.gid = -1;
sk->sk_peer_pid = NULL;
sk->sk_peer_cred = NULL;
sk->sk_write_pending = 0;
sk->sk_rcvlowat = 1;
sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
Expand Down
37 changes: 28 additions & 9 deletions net/unix/af_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,11 +450,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)
Expand All @@ -470,12 +490,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;
}
Expand Down Expand Up @@ -1140,8 +1162,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
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);
Expand All @@ -1157,7 +1178,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
}

/* Set credentials */
sk->sk_peercred = other->sk_peercred;
copy_peercred(sk, other);

sock->state = SS_CONNECTED;
sk->sk_state = TCP_ESTABLISHED;
Expand Down Expand Up @@ -1199,10 +1220,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;
Expand Down

0 comments on commit 109f6e3

Please sign in to comment.