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>
This commit is contained in:
parent
3f551f9436
commit
109f6e39fa
3 changed files with 42 additions and 16 deletions
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue