diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2010-06-12 23:30:14 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-16 17:55:55 -0400 |
commit | 109f6e39fa07c48f580125f531f46cb7c245b528 (patch) | |
tree | 9ec97679c227394a44ebede5768f92d970cea6eb /net/unix/af_unix.c | |
parent | 3f551f9436c05a3b5eccdd6e94733df5bb98d2a5 (diff) |
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>
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r-- | net/unix/af_unix.c | 37 |
1 files changed, 28 insertions, 9 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index fef2cc5e9d2b..e1f1349fae86 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -450,11 +450,31 @@ static int unix_release_sock(struct sock *sk, int embrion) | |||
450 | return 0; | 450 | return 0; |
451 | } | 451 | } |
452 | 452 | ||
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 | |||
453 | static int unix_listen(struct socket *sock, int backlog) | 471 | static int unix_listen(struct socket *sock, int backlog) |
454 | { | 472 | { |
455 | int err; | 473 | int err; |
456 | struct sock *sk = sock->sk; | 474 | struct sock *sk = sock->sk; |
457 | struct unix_sock *u = unix_sk(sk); | 475 | struct unix_sock *u = unix_sk(sk); |
476 | struct pid *old_pid = NULL; | ||
477 | const struct cred *old_cred = NULL; | ||
458 | 478 | ||
459 | err = -EOPNOTSUPP; | 479 | err = -EOPNOTSUPP; |
460 | if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) | 480 | if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) |
@@ -470,12 +490,14 @@ static int unix_listen(struct socket *sock, int backlog) | |||
470 | sk->sk_max_ack_backlog = backlog; | 490 | sk->sk_max_ack_backlog = backlog; |
471 | sk->sk_state = TCP_LISTEN; | 491 | sk->sk_state = TCP_LISTEN; |
472 | /* set credentials so connect can copy them */ | 492 | /* set credentials so connect can copy them */ |
473 | sk->sk_peercred.pid = task_tgid_vnr(current); | 493 | init_peercred(sk); |
474 | current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid); | ||
475 | err = 0; | 494 | err = 0; |
476 | 495 | ||
477 | out_unlock: | 496 | out_unlock: |
478 | unix_state_unlock(sk); | 497 | unix_state_unlock(sk); |
498 | put_pid(old_pid); | ||
499 | if (old_cred) | ||
500 | put_cred(old_cred); | ||
479 | out: | 501 | out: |
480 | return err; | 502 | return err; |
481 | } | 503 | } |
@@ -1140,8 +1162,7 @@ restart: | |||
1140 | unix_peer(newsk) = sk; | 1162 | unix_peer(newsk) = sk; |
1141 | newsk->sk_state = TCP_ESTABLISHED; | 1163 | newsk->sk_state = TCP_ESTABLISHED; |
1142 | newsk->sk_type = sk->sk_type; | 1164 | newsk->sk_type = sk->sk_type; |
1143 | newsk->sk_peercred.pid = task_tgid_vnr(current); | 1165 | init_peercred(newsk); |
1144 | current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid); | ||
1145 | newu = unix_sk(newsk); | 1166 | newu = unix_sk(newsk); |
1146 | newsk->sk_wq = &newu->peer_wq; | 1167 | newsk->sk_wq = &newu->peer_wq; |
1147 | otheru = unix_sk(other); | 1168 | otheru = unix_sk(other); |
@@ -1157,7 +1178,7 @@ restart: | |||
1157 | } | 1178 | } |
1158 | 1179 | ||
1159 | /* Set credentials */ | 1180 | /* Set credentials */ |
1160 | sk->sk_peercred = other->sk_peercred; | 1181 | copy_peercred(sk, other); |
1161 | 1182 | ||
1162 | sock->state = SS_CONNECTED; | 1183 | sock->state = SS_CONNECTED; |
1163 | sk->sk_state = TCP_ESTABLISHED; | 1184 | sk->sk_state = TCP_ESTABLISHED; |
@@ -1199,10 +1220,8 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb) | |||
1199 | sock_hold(skb); | 1220 | sock_hold(skb); |
1200 | unix_peer(ska) = skb; | 1221 | unix_peer(ska) = skb; |
1201 | unix_peer(skb) = ska; | 1222 | unix_peer(skb) = ska; |
1202 | ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current); | 1223 | init_peercred(ska); |
1203 | current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid); | 1224 | init_peercred(skb); |
1204 | ska->sk_peercred.uid = skb->sk_peercred.uid; | ||
1205 | ska->sk_peercred.gid = skb->sk_peercred.gid; | ||
1206 | 1225 | ||
1207 | if (ska->sk_type != SOCK_DGRAM) { | 1226 | if (ska->sk_type != SOCK_DGRAM) { |
1208 | ska->sk_state = TCP_ESTABLISHED; | 1227 | ska->sk_state = TCP_ESTABLISHED; |