[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87ppj1qexz.fsf_-_@x220.int.ebiederm.org>
Date: Sun, 25 May 2014 22:36:24 -0700
From: ebiederm@...ssion.com (Eric W. Biederman)
To: Linus Torvalds <torvalds@...ux-foundation.org>
Cc: Andy Lutomirski <luto@...capital.net>,
"Jorge Boncompte \[DTI2\]" <jorge@...2.net>,
Jiri Benc <jbenc@...hat.com>,
David Miller <davem@...emloft.net>,
Vivek Goyal <vgoyal@...hat.com>,
Simo Sorce <ssorce@...hat.com>,
"security\@kernel.org" <security@...nel.org>,
Network Development <netdev@...r.kernel.org>,
"Serge E. Hallyn" <serge@...lyn.com>,
Michael Kerrisk-manpages <mtk.manpages@...il.com>
Subject: [RFC][PATCH 2/1] netlink: Use the credential at the time the destination address was set.
When sending a message over a netlink socket and then checking to see if
the person is authorized to send the message it is important that we verify
both the sender of the message and the whoever it is that set the destination
of the message both have permission. Otherwise it becomes possible for an
unpriivleged user to set the message destination and trick an suid process
to write to the socket and change the network connection.
For netlink sockets socket() sets the default destination address to 0 (the kernel)
so we need to remember the credentials when a socket is created.
For netlink sockets connect() changes the default destination address so
we need to remember the credentials of the last changer of the default destination
with connect.
This results is there always being a valid remembered credential on the socket
and so that credential is unconditionally freed in netlink_release().
This change makes the semantics of the permission checks of netlink sockets
make sense,and removes the possibility of an unprivileged user getting access
to a root own socket and changing the destination address with connect.
Signed-off-by: "Eric W. Biederman" <ebiederm@...ssion.com>
---
This winds up continuig to grab credentials when socket() is called
because we actually set the destination address in socket() for netlink
sockets, but now we update those credentials when connect() is called.
include/linux/netlink.h | 2 +-
net/netlink/af_netlink.c | 27 ++++++++++++++++++++++++---
net/netlink/af_netlink.h | 1 +
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index f289d085f87f..4f4607c0a1a1 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -19,7 +19,7 @@ enum netlink_skb_flags {
NETLINK_SKB_MMAPED = 0x1, /* Packet data is mmaped */
NETLINK_SKB_TX = 0x2, /* Packet was sent by userspace */
NETLINK_SKB_DELIVERED = 0x4, /* Packet was delivered */
- NETLINK_SKB_DST = 0x8, /* Packet not socket destination */
+ NETLINK_SKB_DST = 0x8, /* Dst set in sendto or sendmsg */
};
struct netlink_skb_parms {
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 15c731f03fa6..5b886ed6a648 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1191,6 +1191,7 @@ static int __netlink_create(struct net *net, struct socket *sock,
mutex_init(nlk->cb_mutex);
}
init_waitqueue_head(&nlk->wait);
+ nlk->cred = get_current_cred();
#ifdef CONFIG_NETLINK_MMAP
mutex_init(&nlk->pg_vec_lock);
#endif
@@ -1291,6 +1292,7 @@ static int netlink_release(struct socket *sock)
NETLINK_URELEASE, &n);
}
+ put_cred(nlk->cred);
module_put(nlk->module);
netlink_table_grab();
@@ -1377,9 +1379,17 @@ retry:
bool __netlink_ns_capable(const struct netlink_skb_parms *nsp,
struct user_namespace *user_ns, int cap)
{
- return ((nsp->flags & NETLINK_SKB_DST) ||
- file_ns_capable(nsp->sk->sk_socket->file, user_ns, cap)) &&
- ns_capable(user_ns, cap);
+ const struct cred *cred;
+ bool capable;
+
+ rcu_read_lock();
+ cred = nlk_sk(nsp->sk)->cred;
+ capable = ((nsp->flags & NETLINK_SKB_DST) ||
+ security_capable(cred, user_ns, cap)) &&
+ ns_capable(user_ns, cap);
+ rcu_read_unlock();
+
+ return capable;
}
EXPORT_SYMBOL(__netlink_ns_capable);
@@ -1569,14 +1579,20 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr,
struct sock *sk = sock->sk;
struct netlink_sock *nlk = nlk_sk(sk);
struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
+ const struct cred *old_cred;
if (alen < sizeof(addr->sa_family))
return -EINVAL;
if (addr->sa_family == AF_UNSPEC) {
+ lock_sock(sk);
sk->sk_state = NETLINK_UNCONNECTED;
nlk->dst_portid = 0;
nlk->dst_group = 0;
+ old_cred = nlk->cred;
+ nlk->cred = get_current_cred();
+ put_cred(old_cred);
+ release_sock(sk);
return 0;
}
if (addr->sa_family != AF_NETLINK)
@@ -1590,9 +1606,14 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr,
err = netlink_autobind(sock);
if (err == 0) {
+ lock_sock(sk);
sk->sk_state = NETLINK_CONNECTED;
nlk->dst_portid = nladdr->nl_pid;
nlk->dst_group = ffs(nladdr->nl_groups);
+ old_cred = nlk->cred;
+ nlk->cred = get_current_cred();
+ put_cred(old_cred);
+ release_sock(sk);
}
return err;
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index 0b59d441f5b6..d3c9d319e9e6 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -40,6 +40,7 @@ struct netlink_sock {
void (*netlink_rcv)(struct sk_buff *skb);
int (*netlink_bind)(int group);
void (*netlink_unbind)(int group);
+ const struct cred *cred;
struct module *module;
#ifdef CONFIG_NETLINK_MMAP
struct mutex pg_vec_lock;
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists