summaryrefslogtreecommitdiff
path: root/security/apparmor/lsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor/lsm.c')
-rw-r--r--security/apparmor/lsm.c163
1 files changed, 156 insertions, 7 deletions
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index f7b2d4bb1d97..0b4f7e2e4135 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -26,6 +26,7 @@
#include <uapi/linux/mount.h>
#include <uapi/linux/lsm.h>
+#include "include/af_unix.h"
#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
@@ -1088,6 +1089,94 @@ static void apparmor_sk_clone_security(const struct sock *sk,
new->peer = aa_get_label(ctx->peer);
}
+static int unix_connect_perm(const struct cred *cred, struct aa_label *label,
+ struct sock *sk, struct sock *peer_sk)
+{
+ struct aa_sk_ctx *peer_ctx = aa_sock(peer_sk);
+ int error;
+
+ error = aa_unix_peer_perm(cred, label, OP_CONNECT,
+ (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE),
+ sk, peer_sk, NULL);
+ if (!is_unix_fs(peer_sk)) {
+ last_error(error,
+ aa_unix_peer_perm(cred,
+ peer_ctx->label, OP_CONNECT,
+ (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE),
+ peer_sk, sk, label));
+ }
+
+ return error;
+}
+
+static void unix_connect_peers(struct aa_sk_ctx *sk_ctx,
+ struct aa_sk_ctx *peer_ctx)
+{
+ /* Cross reference the peer labels for SO_PEERSEC */
+ aa_put_label(peer_ctx->peer);
+ aa_put_label(sk_ctx->peer);
+
+ peer_ctx->peer = aa_get_label(sk_ctx->label);
+ sk_ctx->peer = aa_get_label(peer_ctx->label);
+}
+
+/**
+ * apparmor_unix_stream_connect - check perms before making unix domain conn
+ *
+ * peer is locked when this hook is called
+ */
+static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk,
+ struct sock *newsk)
+{
+ struct aa_sk_ctx *sk_ctx = aa_sock(sk);
+ struct aa_sk_ctx *peer_ctx = aa_sock(peer_sk);
+ struct aa_sk_ctx *new_ctx = aa_sock(newsk);
+ struct aa_label *label;
+ int error;
+
+ label = __begin_current_label_crit_section();
+ error = unix_connect_perm(current_cred(), label, sk, peer_sk);
+ __end_current_label_crit_section(label);
+
+ if (error)
+ return error;
+
+ /* newsk doesn't go through post_create */
+ AA_BUG(new_ctx->label);
+ new_ctx->label = aa_get_label(peer_ctx->label);
+
+ /* Cross reference the peer labels for SO_PEERSEC */
+ unix_connect_peers(sk_ctx, new_ctx);
+
+ return 0;
+}
+
+/**
+ * apparmor_unix_may_send - check perms before conn or sending unix dgrams
+ *
+ * sock and peer are locked when this hook is called
+ *
+ * called by: dgram_connect peer setup but path not copied to newsk
+ */
+static int apparmor_unix_may_send(struct socket *sock, struct socket *peer)
+{
+ struct aa_sk_ctx *peer_ctx = aa_sock(peer->sk);
+ struct aa_label *label;
+ int error;
+
+ label = __begin_current_label_crit_section();
+ error = xcheck(aa_unix_peer_perm(current_cred(),
+ label, OP_SENDMSG, AA_MAY_SEND,
+ sock->sk, peer->sk, NULL),
+ aa_unix_peer_perm(peer->file ? peer->file->f_cred : NULL,
+ peer_ctx->label, OP_SENDMSG,
+ AA_MAY_RECEIVE,
+ peer->sk, sock->sk, label));
+ __end_current_label_crit_section(label);
+
+ return error;
+}
+
static int apparmor_socket_create(int family, int type, int protocol, int kern)
{
struct aa_label *label;
@@ -1100,8 +1189,13 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern)
label = begin_current_label_crit_section();
if (!unconfined(label)) {
- error = aa_af_perm(current_cred(), label, OP_CREATE,
- AA_MAY_CREATE, family, type, protocol);
+ if (family == PF_UNIX)
+ error = aa_unix_create_perm(label, family, type,
+ protocol);
+ else
+ error = aa_af_perm(current_cred(), label, OP_CREATE,
+ AA_MAY_CREATE, family, type,
+ protocol);
}
end_current_label_crit_section(label);
@@ -1143,6 +1237,34 @@ static int apparmor_socket_post_create(struct socket *sock, int family,
return 0;
}
+static int apparmor_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+{
+ struct aa_sk_ctx *a_ctx = aa_sock(socka->sk);
+ struct aa_sk_ctx *b_ctx = aa_sock(sockb->sk);
+ struct aa_label *label;
+ int error = 0;
+
+ aa_put_label(a_ctx->label);
+ aa_put_label(b_ctx->label);
+
+ label = begin_current_label_crit_section();
+ a_ctx->label = aa_get_label(label);
+ b_ctx->label = aa_get_label(label);
+
+ if (socka->sk->sk_family == PF_UNIX) {
+ /* unix socket pairs by-pass unix_stream_connect */
+ if (!error)
+ unix_connect_peers(a_ctx, b_ctx);
+ }
+ end_current_label_crit_section(label);
+
+ return error;
+}
+
+/**
+ * apparmor_socket_bind - check perms before bind addr to socket
+ */
static int apparmor_socket_bind(struct socket *sock,
struct sockaddr *address, int addrlen)
{
@@ -1151,6 +1273,8 @@ static int apparmor_socket_bind(struct socket *sock,
AA_BUG(!address);
AA_BUG(in_interrupt());
+ if (sock->sk->sk_family == PF_UNIX)
+ return aa_unix_bind_perm(sock, address, addrlen);
return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk);
}
@@ -1162,6 +1286,9 @@ static int apparmor_socket_connect(struct socket *sock,
AA_BUG(!address);
AA_BUG(in_interrupt());
+ /* PF_UNIX goes through unix_stream_connect && unix_may_send */
+ if (sock->sk->sk_family == PF_UNIX)
+ return 0;
return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
}
@@ -1171,6 +1298,8 @@ static int apparmor_socket_listen(struct socket *sock, int backlog)
AA_BUG(!sock->sk);
AA_BUG(in_interrupt());
+ if (sock->sk->sk_family == PF_UNIX)
+ return aa_unix_listen_perm(sock, backlog);
return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk);
}
@@ -1185,6 +1314,8 @@ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
AA_BUG(!newsock);
AA_BUG(in_interrupt());
+ if (sock->sk->sk_family == PF_UNIX)
+ return aa_unix_accept_perm(sock, newsock);
return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk);
}
@@ -1196,6 +1327,9 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
AA_BUG(!msg);
AA_BUG(in_interrupt());
+ /* PF_UNIX goes through unix_may_send */
+ if (sock->sk->sk_family == PF_UNIX)
+ return 0;
return aa_sk_perm(op, request, sock->sk);
}
@@ -1218,6 +1352,8 @@ static int aa_sock_perm(const char *op, u32 request, struct socket *sock)
AA_BUG(!sock->sk);
AA_BUG(in_interrupt());
+ if (sock->sk->sk_family == PF_UNIX)
+ return aa_unix_sock_perm(op, request, sock);
return aa_sk_perm(op, request, sock->sk);
}
@@ -1239,6 +1375,8 @@ static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock,
AA_BUG(!sock->sk);
AA_BUG(in_interrupt());
+ if (sock->sk->sk_family == PF_UNIX)
+ return aa_unix_opt_perm(op, request, sock, level, optname);
return aa_sk_perm(op, request, sock->sk);
}
@@ -1292,14 +1430,18 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
#endif
-static struct aa_label *sk_peer_label(struct sock *sk)
+static struct aa_label *sk_peer_get_label(struct sock *sk)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
+ struct aa_label *label = ERR_PTR(-ENOPROTOOPT);
if (ctx->peer)
- return ctx->peer;
+ return aa_get_label(ctx->peer);
- return ERR_PTR(-ENOPROTOOPT);
+ if (sk->sk_family != PF_UNIX)
+ return ERR_PTR(-ENOPROTOOPT);
+
+ return label;
}
/**
@@ -1322,7 +1464,7 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
struct aa_label *peer;
label = begin_current_label_crit_section();
- peer = sk_peer_label(sock->sk);
+ peer = sk_peer_get_label(sock->sk);
if (IS_ERR(peer)) {
error = PTR_ERR(peer);
goto done;
@@ -1333,7 +1475,7 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
/* don't include terminating \0 in slen, it breaks some apps */
if (slen < 0) {
error = -ENOMEM;
- goto done;
+ goto done_put;
}
if (slen > len) {
error = -ERANGE;
@@ -1345,6 +1487,9 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
done_len:
if (copy_to_sockptr(optlen, &slen, sizeof(slen)))
error = -EFAULT;
+
+done_put:
+ aa_put_label(peer);
done:
end_current_label_crit_section(label);
kfree(name);
@@ -1456,8 +1601,12 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
+ LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect),
+ LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send),
+
LSM_HOOK_INIT(socket_create, apparmor_socket_create),
LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create),
+ LSM_HOOK_INIT(socket_socketpair, apparmor_socket_socketpair),
LSM_HOOK_INIT(socket_bind, apparmor_socket_bind),
LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),