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 | |
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>
-rw-r--r-- | include/net/sock.h | 3 | ||||
-rw-r--r-- | net/core/sock.c | 18 | ||||
-rw-r--r-- | net/unix/af_unix.c | 37 |
3 files changed, 42 insertions, 16 deletions
diff --git a/include/net/sock.h b/include/net/sock.h index f8acf38f092f..4f26f2f83be9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -295,7 +295,8 @@ struct sock { | |||
295 | unsigned short sk_ack_backlog; | 295 | unsigned short sk_ack_backlog; |
296 | unsigned short sk_max_ack_backlog; | 296 | unsigned short sk_max_ack_backlog; |
297 | __u32 sk_priority; | 297 | __u32 sk_priority; |
298 | struct ucred sk_peercred; | 298 | struct pid *sk_peer_pid; |
299 | const struct cred *sk_peer_cred; | ||
299 | long sk_rcvtimeo; | 300 | long sk_rcvtimeo; |
300 | long sk_sndtimeo; | 301 | long sk_sndtimeo; |
301 | struct sk_filter *sk_filter; | 302 | struct sk_filter *sk_filter; |
diff --git a/net/core/sock.c b/net/core/sock.c index db8335ad7559..0229d5566a46 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -915,11 +915,15 @@ int sock_getsockopt(struct socket *sock, int level, int optname, | |||
915 | break; | 915 | break; |
916 | 916 | ||
917 | case SO_PEERCRED: | 917 | case SO_PEERCRED: |
918 | if (len > sizeof(sk->sk_peercred)) | 918 | { |
919 | len = sizeof(sk->sk_peercred); | 919 | struct ucred peercred; |
920 | if (copy_to_user(optval, &sk->sk_peercred, len)) | 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)) | ||
921 | return -EFAULT; | 924 | return -EFAULT; |
922 | goto lenout; | 925 | goto lenout; |
926 | } | ||
923 | 927 | ||
924 | case SO_PEERNAME: | 928 | case SO_PEERNAME: |
925 | { | 929 | { |
@@ -1133,6 +1137,9 @@ static void __sk_free(struct sock *sk) | |||
1133 | printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", | 1137 | printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", |
1134 | __func__, atomic_read(&sk->sk_omem_alloc)); | 1138 | __func__, atomic_read(&sk->sk_omem_alloc)); |
1135 | 1139 | ||
1140 | if (sk->sk_peer_cred) | ||
1141 | put_cred(sk->sk_peer_cred); | ||
1142 | put_pid(sk->sk_peer_pid); | ||
1136 | put_net(sock_net(sk)); | 1143 | put_net(sock_net(sk)); |
1137 | sk_prot_free(sk->sk_prot_creator, sk); | 1144 | sk_prot_free(sk->sk_prot_creator, sk); |
1138 | } | 1145 | } |
@@ -1968,9 +1975,8 @@ void sock_init_data(struct socket *sock, struct sock *sk) | |||
1968 | sk->sk_sndmsg_page = NULL; | 1975 | sk->sk_sndmsg_page = NULL; |
1969 | sk->sk_sndmsg_off = 0; | 1976 | sk->sk_sndmsg_off = 0; |
1970 | 1977 | ||
1971 | sk->sk_peercred.pid = 0; | 1978 | sk->sk_peer_pid = NULL; |
1972 | sk->sk_peercred.uid = -1; | 1979 | sk->sk_peer_cred = NULL; |
1973 | sk->sk_peercred.gid = -1; | ||
1974 | sk->sk_write_pending = 0; | 1980 | sk->sk_write_pending = 0; |
1975 | sk->sk_rcvlowat = 1; | 1981 | sk->sk_rcvlowat = 1; |
1976 | sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; | 1982 | sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; |
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; |