Hi
This is v2 of the socketpair(2) LSM hook introduction. Changes
since v1 are:
- Added ACKs from previous series.
- Moved the hook into generic socketpair(2) handling. The hook is now
called security_socket_socketpair(), just like the other hooks on
the socket layer.
There is no AF_UNIX specific code, anymore.
- Added SMACK support.
- Still *NO* AppArmor support, since upstream AppArmor lacks
SO_PEERSEC support and requires downstream patches (in particular,
the apparmor_unix_stream_connect() function is mostly a stub and
was never synced with Ubuntu downstream modifications).
Old cover letter follows (only trivial changes).
Thanks
David
This series adds a new LSM hook for the socketpair(2) syscall. The idea
is to allow SO_PEERSEC to be called on AF_UNIX sockets created via
socketpair(2), and return the same information as if you emulated
socketpair(2) via a temporary listener socket. Right now SO_PEERSEC
will return the unlabeled credentials for a socketpair, rather than the
actual credentials of the creating process.
A simple call to:
socketpair(AF_UNIX, SOCK_STREAM, 0, out);
can be emulated via a temporary listener socket bound to a unique,
random name in the abstract namespace. By connecting to this listener
socket, accept(2) will return the second part of the pair. If
SO_PEERSEC is queried on these, the correct credentials of the creating
process are returned. A simple comparison between the behavior of
SO_PEERSEC on socketpair(2) and an emulated socketpair is included in
the dbus-broker test-suite [1].
This patch series tries to close this gap and makes both behave the
same. A new LSM-hook is added which allows LSMs to cache the correct
peer information on newly created socket-pairs.
Apart from fixing this behavioral difference, the dbus-broker project
actually needs to query the credentials of socketpairs, and currently
must resort to racy procfs(2) queries to get the LSM credentials of its
controller socket. Several parts of the dbus-broker project allow you
to pass in a socket during execve(2), which will be used by the child
process to accept control-commands from its parent. One natural way to
create this communication channel is to use socketpair(2). However,
right now SO_PEERSEC does not return any useful information, hence, the
child-process would need other means to retrieve this information. By
avoiding socketpair(2) and using the hacky-emulated version, this is not
an issue.
There was a previous discussion on this matter [2] roughly a year ago.
Back then there was the suspicion that proper SO_PEERSEC would confuse
applications. However, we could not find any evidence backing this
suspicion. Furthermore, we now actually see the contrary. Lack of
SO_PEERSEC makes it a hassle to use socketpairs with LSM credentials.
Hence, we propose to implement full SO_PEERSEC for socketpairs.
Thanks
David
[1] https://github.com/bus1/dbus-broker/blob/master/src/util/test-peersec.c
[2] https://www.spinics.net/lists/selinux/msg22674.html
David Herrmann (3):
security: add hook for socketpair()
net: hook socketpair() into LSM
selinux: provide socketpair callback
Tom Gundersen (1):
smack: provide socketpair callback
include/linux/lsm_hooks.h | 7 +++++++
include/linux/security.h | 7 +++++++
net/socket.c | 7 +++++++
security/security.c | 6 ++++++
security/selinux/hooks.c | 13 +++++++++++++
security/smack/smack_lsm.c | 22 ++++++++++++++++++++++
6 files changed, 62 insertions(+)
--
2.17.0
Use the newly created LSM-hook for socketpair(). The default hook
return-value is 0, so behavior stays the same unless LSMs start using
this hook.
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Tom Gundersen <[email protected]>
Signed-off-by: David Herrmann <[email protected]>
---
net/socket.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/net/socket.c b/net/socket.c
index f10f1d947c78..667a7b397134 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1420,6 +1420,13 @@ int __sys_socketpair(int family, int type, int protocol, int __user *usockvec)
goto out;
}
+ err = security_socket_socketpair(sock1, sock2);
+ if (unlikely(err)) {
+ sock_release(sock2);
+ sock_release(sock1);
+ goto out;
+ }
+
err = sock1->ops->socketpair(sock1, sock2);
if (unlikely(err < 0)) {
sock_release(sock2);
--
2.17.0
Make sure to implement the new socketpair callback so the SO_PEERSEC
call on socketpair(2)s will return correct information.
Acked-by: Serge Hallyn <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
Signed-off-by: Tom Gundersen <[email protected]>
Signed-off-by: David Herrmann <[email protected]>
---
security/selinux/hooks.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4cafe6a19167..02ebd1585eaf 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4569,6 +4569,18 @@ static int selinux_socket_post_create(struct socket *sock, int family,
return err;
}
+static int selinux_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+{
+ struct sk_security_struct *sksec_a = socka->sk->sk_security;
+ struct sk_security_struct *sksec_b = sockb->sk->sk_security;
+
+ sksec_a->peer_sid = sksec_b->sid;
+ sksec_b->peer_sid = sksec_a->sid;
+
+ return 0;
+}
+
/* Range of port numbers used to automatically bind.
Need to determine whether we should perform a name_bind
permission check between the socket and the port number. */
@@ -6999,6 +7011,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(socket_create, selinux_socket_create),
LSM_HOOK_INIT(socket_post_create, selinux_socket_post_create),
+ LSM_HOOK_INIT(socket_socketpair, selinux_socket_socketpair),
LSM_HOOK_INIT(socket_bind, selinux_socket_bind),
LSM_HOOK_INIT(socket_connect, selinux_socket_connect),
LSM_HOOK_INIT(socket_listen, selinux_socket_listen),
--
2.17.0
Right now the LSM labels for socketpairs are always uninitialized,
since there is no security hook for the socketpair() syscall. This
patch adds the required hooks so LSMs can properly label socketpairs.
This allows SO_PEERSEC to return useful information on those sockets.
Note that the behavior of socketpair() can be emulated by creating a
listener socket, connecting to it, and then discarding the initial
listener socket. With this workaround, SO_PEERSEC would return the
caller's security context. However, with socketpair(), the uninitialized
context is returned unconditionally. This is unexpected and makes
socketpair() less useful in situations where the security context is
crucial to the application.
With the new socketpair-hook this disparity can be solved by making
socketpair() return the expected security context.
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Tom Gundersen <[email protected]>
Signed-off-by: David Herrmann <[email protected]>
---
include/linux/lsm_hooks.h | 7 +++++++
include/linux/security.h | 7 +++++++
security/security.c | 6 ++++++
3 files changed, 20 insertions(+)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 9d0b286f3dba..8f1131c8dd54 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -757,6 +757,11 @@
* @type contains the requested communications type.
* @protocol contains the requested protocol.
* @kern set to 1 if a kernel socket.
+ * @socket_socketpair:
+ * Check permissions before creating a fresh pair of sockets.
+ * @socka contains the first socket structure.
+ * @sockb contains the second socket structure.
+ * Return 0 if permission is granted and the connection was established.
* @socket_bind:
* Check permission before socket protocol layer bind operation is
* performed and the socket @sock is bound to the address specified in the
@@ -1656,6 +1661,7 @@ union security_list_options {
int (*socket_create)(int family, int type, int protocol, int kern);
int (*socket_post_create)(struct socket *sock, int family, int type,
int protocol, int kern);
+ int (*socket_socketpair)(struct socket *socka, struct socket *sockb);
int (*socket_bind)(struct socket *sock, struct sockaddr *address,
int addrlen);
int (*socket_connect)(struct socket *sock, struct sockaddr *address,
@@ -1922,6 +1928,7 @@ struct security_hook_heads {
struct hlist_head unix_may_send;
struct hlist_head socket_create;
struct hlist_head socket_post_create;
+ struct hlist_head socket_socketpair;
struct hlist_head socket_bind;
struct hlist_head socket_connect;
struct hlist_head socket_listen;
diff --git a/include/linux/security.h b/include/linux/security.h
index 200920f521a1..4ff3ba457e56 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1191,6 +1191,7 @@ int security_unix_may_send(struct socket *sock, struct socket *other);
int security_socket_create(int family, int type, int protocol, int kern);
int security_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern);
+int security_socket_socketpair(struct socket *socka, struct socket *sockb);
int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen);
int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
int security_socket_listen(struct socket *sock, int backlog);
@@ -1262,6 +1263,12 @@ static inline int security_socket_post_create(struct socket *sock,
return 0;
}
+static inline int security_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+{
+ return 0;
+}
+
static inline int security_socket_bind(struct socket *sock,
struct sockaddr *address,
int addrlen)
diff --git a/security/security.c b/security/security.c
index 7bc2fde023a7..68f46d849abe 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1358,6 +1358,12 @@ int security_socket_post_create(struct socket *sock, int family,
protocol, kern);
}
+int security_socket_socketpair(struct socket *socka, struct socket *sockb)
+{
+ return call_int_hook(socket_socketpair, 0, socka, sockb);
+}
+EXPORT_SYMBOL(security_socket_socketpair);
+
int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{
return call_int_hook(socket_bind, 0, sock, address, addrlen);
--
2.17.0
From: Tom Gundersen <[email protected]>
Make sure to implement the new socketpair callback so the SO_PEERSEC
call on socketpair(2)s will return correct information.
Signed-off-by: Tom Gundersen <[email protected]>
Signed-off-by: David Herrmann <[email protected]>
---
security/smack/smack_lsm.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 0b414836bebd..dcb976f98df2 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2842,6 +2842,27 @@ static int smack_socket_post_create(struct socket *sock, int family,
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
}
+/**
+ * smack_socket_socketpair - create socket pair
+ * @socka: one socket
+ * @sockb: another socket
+ *
+ * Cross reference the peer labels for SO_PEERSEC
+ *
+ * Returns 0 on success, and error code otherwise
+ */
+static int smack_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+{
+ struct socket_smack *asp = socka->sk->sk_security;
+ struct socket_smack *bsp = sockb->sk->sk_security;
+
+ asp->smk_packet = bsp->smk_out;
+ bsp->smk_packet = asp->smk_out;
+
+ return 0;
+}
+
#ifdef SMACK_IPV6_PORT_LABELING
/**
* smack_socket_bind - record port binding information.
@@ -4724,6 +4745,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
+ LSM_HOOK_INIT(socket_socketpair, smack_socket_socketpair),
#ifdef SMACK_IPV6_PORT_LABELING
LSM_HOOK_INIT(socket_bind, smack_socket_bind),
#endif
--
2.17.0
From: David Herrmann <[email protected]>
Date: Fri, 4 May 2018 16:28:20 +0200
> Use the newly created LSM-hook for socketpair(). The default hook
> return-value is 0, so behavior stays the same unless LSMs start using
> this hook.
>
> Acked-by: Serge Hallyn <[email protected]>
> Signed-off-by: Tom Gundersen <[email protected]>
> Signed-off-by: David Herrmann <[email protected]>
Acked-by: David S. Miller <[email protected]>
On Fri, 4 May 2018, David Herrmann wrote:
> Hi
>
> This is v2 of the socketpair(2) LSM hook introduction.
Thanks, all applied to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next-general
--
James Morris
<[email protected]>
On 5/4/2018 7:28 AM, David Herrmann wrote:
> From: Tom Gundersen <[email protected]>
>
> Make sure to implement the new socketpair callback so the SO_PEERSEC
> call on socketpair(2)s will return correct information.
>
> Signed-off-by: Tom Gundersen <[email protected]>
> Signed-off-by: David Herrmann <[email protected]>
This doesn't look like it will cause any problems.
I've only been able to test it in a general way. I
haven't created specific tests, but it passes the
usual Smack use cases.
Acked-by: Casey Schaufler <[email protected]>
> ---
> security/smack/smack_lsm.c | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 0b414836bebd..dcb976f98df2 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -2842,6 +2842,27 @@ static int smack_socket_post_create(struct socket *sock, int family,
> return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
> }
>
> +/**
> + * smack_socket_socketpair - create socket pair
> + * @socka: one socket
> + * @sockb: another socket
> + *
> + * Cross reference the peer labels for SO_PEERSEC
> + *
> + * Returns 0 on success, and error code otherwise
> + */
> +static int smack_socket_socketpair(struct socket *socka,
> + struct socket *sockb)
> +{
> + struct socket_smack *asp = socka->sk->sk_security;
> + struct socket_smack *bsp = sockb->sk->sk_security;
> +
> + asp->smk_packet = bsp->smk_out;
> + bsp->smk_packet = asp->smk_out;
> +
> + return 0;
> +}
> +
> #ifdef SMACK_IPV6_PORT_LABELING
> /**
> * smack_socket_bind - record port binding information.
> @@ -4724,6 +4745,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
>
> LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
> + LSM_HOOK_INIT(socket_socketpair, smack_socket_socketpair),
> #ifdef SMACK_IPV6_PORT_LABELING
> LSM_HOOK_INIT(socket_bind, smack_socket_bind),
> #endif