Skip to content

Commit 109f6e3

Browse files
ebiedermdavem330
authored andcommitted
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>
1 parent 3f551f9 commit 109f6e3

File tree

3 files changed

+42
-16
lines changed

3 files changed

+42
-16
lines changed

include/net/sock.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ struct sock {
295295
unsigned short sk_ack_backlog;
296296
unsigned short sk_max_ack_backlog;
297297
__u32 sk_priority;
298-
struct ucred sk_peercred;
298+
struct pid *sk_peer_pid;
299+
const struct cred *sk_peer_cred;
299300
long sk_rcvtimeo;
300301
long sk_sndtimeo;
301302
struct sk_filter *sk_filter;

net/core/sock.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -915,11 +915,15 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
915915
break;
916916

917917
case SO_PEERCRED:
918-
if (len > sizeof(sk->sk_peercred))
919-
len = sizeof(sk->sk_peercred);
920-
if (copy_to_user(optval, &sk->sk_peercred, len))
918+
{
919+
struct ucred peercred;
920+
if (len > sizeof(peercred))
921+
len = sizeof(peercred);
922+
cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred);
923+
if (copy_to_user(optval, &peercred, len))
921924
return -EFAULT;
922925
goto lenout;
926+
}
923927

924928
case SO_PEERNAME:
925929
{
@@ -1133,6 +1137,9 @@ static void __sk_free(struct sock *sk)
11331137
printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n",
11341138
__func__, atomic_read(&sk->sk_omem_alloc));
11351139

1140+
if (sk->sk_peer_cred)
1141+
put_cred(sk->sk_peer_cred);
1142+
put_pid(sk->sk_peer_pid);
11361143
put_net(sock_net(sk));
11371144
sk_prot_free(sk->sk_prot_creator, sk);
11381145
}
@@ -1968,9 +1975,8 @@ void sock_init_data(struct socket *sock, struct sock *sk)
19681975
sk->sk_sndmsg_page = NULL;
19691976
sk->sk_sndmsg_off = 0;
19701977

1971-
sk->sk_peercred.pid = 0;
1972-
sk->sk_peercred.uid = -1;
1973-
sk->sk_peercred.gid = -1;
1978+
sk->sk_peer_pid = NULL;
1979+
sk->sk_peer_cred = NULL;
19741980
sk->sk_write_pending = 0;
19751981
sk->sk_rcvlowat = 1;
19761982
sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;

net/unix/af_unix.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,31 @@ static int unix_release_sock(struct sock *sk, int embrion)
450450
return 0;
451451
}
452452

453+
static void init_peercred(struct sock *sk)
454+
{
455+
put_pid(sk->sk_peer_pid);
456+
if (sk->sk_peer_cred)
457+
put_cred(sk->sk_peer_cred);
458+
sk->sk_peer_pid = get_pid(task_tgid(current));
459+
sk->sk_peer_cred = get_current_cred();
460+
}
461+
462+
static void copy_peercred(struct sock *sk, struct sock *peersk)
463+
{
464+
put_pid(sk->sk_peer_pid);
465+
if (sk->sk_peer_cred)
466+
put_cred(sk->sk_peer_cred);
467+
sk->sk_peer_pid = get_pid(peersk->sk_peer_pid);
468+
sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);
469+
}
470+
453471
static int unix_listen(struct socket *sock, int backlog)
454472
{
455473
int err;
456474
struct sock *sk = sock->sk;
457475
struct unix_sock *u = unix_sk(sk);
476+
struct pid *old_pid = NULL;
477+
const struct cred *old_cred = NULL;
458478

459479
err = -EOPNOTSUPP;
460480
if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
@@ -470,12 +490,14 @@ static int unix_listen(struct socket *sock, int backlog)
470490
sk->sk_max_ack_backlog = backlog;
471491
sk->sk_state = TCP_LISTEN;
472492
/* set credentials so connect can copy them */
473-
sk->sk_peercred.pid = task_tgid_vnr(current);
474-
current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid);
493+
init_peercred(sk);
475494
err = 0;
476495

477496
out_unlock:
478497
unix_state_unlock(sk);
498+
put_pid(old_pid);
499+
if (old_cred)
500+
put_cred(old_cred);
479501
out:
480502
return err;
481503
}
@@ -1140,8 +1162,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
11401162
unix_peer(newsk) = sk;
11411163
newsk->sk_state = TCP_ESTABLISHED;
11421164
newsk->sk_type = sk->sk_type;
1143-
newsk->sk_peercred.pid = task_tgid_vnr(current);
1144-
current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid);
1165+
init_peercred(newsk);
11451166
newu = unix_sk(newsk);
11461167
newsk->sk_wq = &newu->peer_wq;
11471168
otheru = unix_sk(other);
@@ -1157,7 +1178,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
11571178
}
11581179

11591180
/* Set credentials */
1160-
sk->sk_peercred = other->sk_peercred;
1181+
copy_peercred(sk, other);
11611182

11621183
sock->state = SS_CONNECTED;
11631184
sk->sk_state = TCP_ESTABLISHED;
@@ -1199,10 +1220,8 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
11991220
sock_hold(skb);
12001221
unix_peer(ska) = skb;
12011222
unix_peer(skb) = ska;
1202-
ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current);
1203-
current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid);
1204-
ska->sk_peercred.uid = skb->sk_peercred.uid;
1205-
ska->sk_peercred.gid = skb->sk_peercred.gid;
1223+
init_peercred(ska);
1224+
init_peercred(skb);
12061225

12071226
if (ska->sk_type != SOCK_DGRAM) {
12081227
ska->sk_state = TCP_ESTABLISHED;

0 commit comments

Comments
 (0)