apparmor: add fine grained af_unix mediation

Extend af_unix mediation to support fine grained controls based on
the type (abstract, anonymous, fs), the address, and the labeling
on the socket.

This allows for using socket addresses to label and the socket and
control which subjects can communicate.

The unix rule format follows standard apparmor rules except that fs
based unix sockets can be mediated by existing file rules. None fs
unix sockets can be mediated by a unix socket rule. Where The address
of an abstract unix domain socket begins with the @ character, similar
to how they are reported (as paths) by netstat -x.  The address then
follows and may contain pattern matching and any characters including
the null character. In apparmor null characters must be specified by
using an escape sequence \000 or \x00. The pattern matching is the
same as is used by file path matching so * will not match / even
though it has no special meaning with in an abstract socket name. Eg.

     allow unix addr=@*,

Autobound unix domain sockets have a unix sun_path assigned to them by
the kernel, as such specifying a policy based address is not possible.
The autobinding of sockets can be controlled by specifying the special
auto keyword. Eg.

     allow unix addr=auto,

To indicate that the rule only applies to auto binding of unix domain
sockets.  It is important to note this only applies to the bind
permission as once the socket is bound to an address it is
indistinguishable from a socket that have an addr bound with a
specified name. When the auto keyword is used with other permissions
or as part of a peer addr it will be replaced with a pattern that can
match an autobound socket. Eg. For some kernels

    allow unix rw addr=auto,

It is important to note, this pattern may match abstract sockets that
were not autobound but have an addr that fits what is generated by the
kernel when autobinding a socket.

Anonymous unix domain sockets have no sun_path associated with the
socket address, however it can be specified with the special none
keyword to indicate the rule only applies to anonymous unix domain
sockets. Eg.

    allow unix addr=none,

If the address component of a rule is not specified then the rule
applies to autobind, abstract and anonymous sockets.

The label on the socket can be compared using the standard label=
rule conditional. Eg.

    allow unix addr=@foo peer=(label=bar),

see man apparmor.d for full syntax description.

Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen
2025-01-18 06:47:12 -08:00
parent b4940d913c
commit c05e705812
12 changed files with 1063 additions and 58 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o secid.o file.o policy_ns.o label.o mount.o net.o \
policy_compat.o
policy_compat.o af_unix.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
File diff suppressed because it is too large Load Diff
+7
View File
@@ -2387,6 +2387,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = {
{ }
};
static struct aa_sfs_entry aa_sfs_entry_dbus[] = {
AA_SFS_FILE_STRING("mask", "acquire send receive"),
{ }
};
static struct aa_sfs_entry aa_sfs_entry_query_label[] = {
AA_SFS_FILE_STRING("perms", "allow deny audit quiet"),
AA_SFS_FILE_BOOLEAN("data", 1),
@@ -2409,6 +2414,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
AA_SFS_DIR("domain", aa_sfs_entry_domain),
AA_SFS_DIR("file", aa_sfs_entry_file),
AA_SFS_DIR("network_v8", aa_sfs_entry_network),
AA_SFS_DIR("network", aa_sfs_entry_networkv9),
AA_SFS_DIR("mount", aa_sfs_entry_mount),
AA_SFS_DIR("namespaces", aa_sfs_entry_ns),
AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
@@ -2416,6 +2422,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
AA_SFS_DIR("caps", aa_sfs_entry_caps),
AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
AA_SFS_DIR("signal", aa_sfs_entry_signal),
AA_SFS_DIR("dbus", aa_sfs_entry_dbus),
AA_SFS_DIR("query", aa_sfs_entry_query),
AA_SFS_DIR("io_uring", aa_sfs_entry_io_uring),
{ }
+9 -7
View File
@@ -14,6 +14,7 @@
#include <linux/fs.h>
#include <linux/mount.h>
#include "include/af_unix.h"
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/cred.h"
@@ -217,16 +218,17 @@ aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
return state;
}
static int __aa_path_perm(const char *op, const struct cred *subj_cred,
struct aa_profile *profile, const char *name,
u32 request, struct path_cond *cond, int flags,
struct aa_perms *perms)
int __aa_path_perm(const char *op, const struct cred *subj_cred,
struct aa_profile *profile, const char *name,
u32 request, struct path_cond *cond, int flags,
struct aa_perms *perms)
{
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
int e = 0;
if (profile_unconfined(profile))
if (profile_unconfined(profile) ||
((flags & PATH_SOCK_COND) && !RULE_MEDIATES_NET(rules)))
return 0;
aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
name, cond, perms);
@@ -549,12 +551,12 @@ static int __file_sock_perm(const char *op, const struct cred *subj_cred,
return 0;
/* TODO: improve to skip profiles cached in flabel */
error = aa_sock_file_perm(subj_cred, label, op, request, sock);
error = aa_sock_file_perm(subj_cred, label, op, request, file);
if (denied) {
/* TODO: improve to skip profiles checked above */
/* check every profile in file label to is cached */
last_error(error, aa_sock_file_perm(subj_cred, flabel, op,
request, sock));
request, file));
}
if (!error)
update_file_ctx(file_ctx(file), label, request);
+57
View File
@@ -0,0 +1,57 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor af_unix fine grained mediation
*
* Copyright 2023 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*/
#ifndef __AA_AF_UNIX_H
#include <net/af_unix.h>
#include "label.h"
#define unix_addr(A) ((struct sockaddr_un *)(A))
#define unix_addr_len(L) ((L) - sizeof(sa_family_t))
#define unix_peer(sk) (unix_sk(sk)->peer)
#define is_unix_addr_abstract_name(B) ((B)[0] == 0)
#define is_unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0)
#define is_unix_addr_fs(A, L) (!is_unix_addr_anon(A, L) && \
!is_unix_addr_abstract_name(unix_addr(A)->sun_path))
#define is_unix_anonymous(U) (!unix_sk(U)->addr)
#define is_unix_fs(U) (!is_unix_anonymous(U) && \
unix_sk(U)->addr->name->sun_path[0])
#define is_unix_connected(S) ((S)->state == SS_CONNECTED)
int aa_unix_peer_perm(const struct cred *subj_cred,
struct aa_label *label, const char *op, u32 request,
struct sock *sk, struct sock *peer_sk,
struct aa_label *peer_label);
int aa_unix_label_sk_perm(const struct cred *subj_cred,
struct aa_label *label, const char *op, u32 request,
struct sock *sk);
int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock);
int aa_unix_create_perm(struct aa_label *label, int family, int type,
int protocol);
int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address,
int addrlen);
int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
int addrlen);
int aa_unix_listen_perm(struct socket *sock, int backlog);
int aa_unix_accept_perm(struct socket *sock, struct socket *newsock);
int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
struct msghdr *msg, int size);
int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level,
int optname);
int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, struct file *file);
#endif /* __AA_AF_UNIX_H */
+1
View File
@@ -28,6 +28,7 @@
#define AA_CLASS_SIGNAL 10
#define AA_CLASS_XMATCH 11
#define AA_CLASS_NET 14
#define AA_CLASS_NETV9 15
#define AA_CLASS_LABEL 16
#define AA_CLASS_POSIX_MQUEUE 17
#define AA_CLASS_MODULE 19
+4
View File
@@ -84,6 +84,10 @@ aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
const char *name, struct path_cond *cond,
struct aa_perms *perms);
int __aa_path_perm(const char *op, const struct cred *subj_cred,
struct aa_profile *profile, const char *name,
u32 request, struct path_cond *cond, int flags,
struct aa_perms *perms);
int aa_path_perm(const char *op, const struct cred *subj_cred,
struct aa_label *label, const struct path *path,
int flags, u32 request, struct path_cond *cond);
+11 -6
View File
@@ -56,7 +56,7 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
return sk->sk_security + apparmor_blob_sizes.lbs_sock;
}
#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
#define DEFINE_AUDIT_NET(NAME, OP, CRED, SK, F, T, P) \
struct lsm_network_audit NAME ## _net = { .sk = (SK), \
.family = (F)}; \
DEFINE_AUDIT_DATA(NAME, \
@@ -65,11 +65,12 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
AA_CLASS_NET, \
OP); \
NAME.common.u.net = &(NAME ## _net); \
NAME.subj_cred = (CRED); \
NAME.net.type = (T); \
NAME.net.protocol = (P)
#define DEFINE_AUDIT_SK(NAME, OP, SK) \
DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \
#define DEFINE_AUDIT_SK(NAME, OP, CRED, SK) \
DEFINE_AUDIT_NET(NAME, OP, CRED, SK, (SK)->sk_family, (SK)->sk_type, \
(SK)->sk_protocol)
@@ -81,10 +82,14 @@ struct aa_secmark {
};
extern struct aa_sfs_entry aa_sfs_entry_network[];
extern struct aa_sfs_entry aa_sfs_entry_networkv9[];
/* passing in state returned by XXX_mediates(class) */
int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy,
aa_state_t state, u32 request, struct aa_perms *p,
struct apparmor_audit_data *ad);
/* passing in state returned by XXX_mediates_AF() */
aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state,
u32 request, u16 family, int type, int protocol,
u32 request, u16 af, int type, int protocol,
struct aa_perms **p, const char **info);
void audit_net_cb(struct audit_buffer *ab, void *va);
int aa_profile_af_perm(struct aa_profile *profile,
@@ -105,7 +110,7 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk);
int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request,
struct socket *sock);
struct file *file);
int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, const struct sock *sk);
+1
View File
@@ -13,6 +13,7 @@
enum path_flags {
PATH_IS_DIR = 0x1, /* path is a directory */
PATH_SOCK_COND = 0x2,
PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */
PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */
PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */
+2 -7
View File
@@ -304,14 +304,9 @@ static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules,
rules->policy->start[0], &class, 1);
}
static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
static inline aa_state_t RULE_MEDIATES_NET(struct aa_ruleset *rules)
{
aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET);
__be16 be_af = cpu_to_be16(AF);
if (!state)
return DFA_NOMATCH;
return aa_dfa_match_len(rules->policy->dfa, state, (char *) &be_af, 2);
return RULE_MEDIATES(rules, AA_CLASS_NET);
}
static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head,
+156 -7
View File
@@ -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),
+112 -30
View File
@@ -8,6 +8,7 @@
* Copyright 2009-2017 Canonical Ltd.
*/
#include "include/af_unix.h"
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/cred.h"
@@ -24,6 +25,12 @@ struct aa_sfs_entry aa_sfs_entry_network[] = {
{ }
};
struct aa_sfs_entry aa_sfs_entry_networkv9[] = {
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
AA_SFS_FILE_BOOLEAN("af_unix", 1),
{ }
};
static const char * const net_mask_names[] = {
"unknown",
"send",
@@ -66,6 +73,37 @@ static const char * const net_mask_names[] = {
"unknown",
};
static void audit_unix_addr(struct audit_buffer *ab, const char *str,
struct sockaddr_un *addr, int addrlen)
{
int len = unix_addr_len(addrlen);
if (!addr || len <= 0) {
audit_log_format(ab, " %s=none", str);
} else if (addr->sun_path[0]) {
audit_log_format(ab, " %s=", str);
audit_log_untrustedstring(ab, addr->sun_path);
} else {
audit_log_format(ab, " %s=\"@", str);
if (audit_string_contains_control(&addr->sun_path[1], len - 1))
audit_log_n_hex(ab, &addr->sun_path[1], len - 1);
else
audit_log_format(ab, "%.*s", len - 1,
&addr->sun_path[1]);
audit_log_format(ab, "\"");
}
}
static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str,
const struct sock *sk)
{
const struct unix_sock *u = unix_sk(sk);
if (u && u->addr)
audit_unix_addr(ab, str, u->addr->name, u->addr->len);
else
audit_unix_addr(ab, str, NULL, 0);
}
/* audit callback for net specific fields */
void audit_net_cb(struct audit_buffer *ab, void *va)
@@ -73,12 +111,12 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va;
struct apparmor_audit_data *ad = aad(sa);
if (address_family_names[sa->u.net->family])
if (address_family_names[ad->common.u.net->family])
audit_log_format(ab, " family=\"%s\"",
address_family_names[sa->u.net->family]);
address_family_names[ad->common.u.net->family]);
else
audit_log_format(ab, " family=\"unknown(%d)\"",
sa->u.net->family);
ad->common.u.net->family);
if (sock_type_names[ad->net.type])
audit_log_format(ab, " sock_type=\"%s\"",
sock_type_names[ad->net.type]);
@@ -98,6 +136,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
net_mask_names, NET_PERMS_MASK);
}
}
if (ad->common.u.net->family == PF_UNIX) {
if ((ad->request & ~NET_PEER_MASK) && ad->net.addr)
audit_unix_addr(ab, "addr",
unix_addr(ad->net.addr),
ad->net.addrlen);
else
audit_unix_sk_addr(ab, "addr", ad->common.u.net->sk);
if (ad->request & NET_PEER_MASK) {
if (ad->net.addr)
audit_unix_addr(ab, "peer_addr",
unix_addr(ad->net.addr),
ad->net.addrlen);
else
audit_unix_sk_addr(ab, "peer_addr",
ad->net.peer_sk);
}
}
if (ad->peer) {
audit_log_format(ab, " peer=");
aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
@@ -106,9 +161,9 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
}
/* standard permission lookup pattern - supports early bailout */
static int do_perms(struct aa_profile *profile, struct aa_policydb *policy,
unsigned int state, u32 request,
struct aa_perms *p, struct apparmor_audit_data *ad)
int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy,
aa_state_t state, u32 request,
struct aa_perms *p, struct apparmor_audit_data *ad)
{
struct aa_perms perms;
@@ -140,31 +195,53 @@ static struct aa_perms *early_match(struct aa_policydb *policy,
return p;
}
/* passing in state returned by PROFILE_MEDIATES_AF */
static aa_state_t aa_dfa_match_be16(struct aa_dfa *dfa, aa_state_t state,
u16 data)
{
__be16 buffer = cpu_to_be16(data);
return aa_dfa_match_len(dfa, state, (char *) &buffer, 2);
}
/**
* aa_match_to_prot - match the af, type, protocol triplet
* @policy: policy being matched
* @state: state to start in
* @request: permissions being requested, ignored if @p == NULL
* @af: socket address family
* @type: socket type
* @protocol: socket protocol
* @p: output - pointer to permission associated with match
* @info: output - pointer to string describing failure
*
* RETURNS: state match stopped in.
*
* If @(p) is assigned a value the returned state will be the
* corresponding state. Will not set @p on failure or if match completes
* only if an early match occurs
*/
aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state,
u32 request, u16 family, int type, int protocol,
u32 request, u16 af, int type, int protocol,
struct aa_perms **p, const char **info)
{
__be16 buffer;
buffer = cpu_to_be16(family);
state = aa_dfa_match_len(policy->dfa, state, (char *) &buffer, 2);
state = aa_dfa_match_be16(policy->dfa, state, (u16)af);
if (!state) {
*info = "failed af match";
return DFA_NOMATCH;
return state;
}
buffer = cpu_to_be16((u16)type);
state = aa_dfa_match_len(policy->dfa, state, (char *) &buffer, 2);
if (!state)
state = aa_dfa_match_be16(policy->dfa, state, (u16)type);
if (state) {
if (p)
*p = early_match(policy, state, request);
if (!p || !*p) {
state = aa_dfa_match_be16(policy->dfa, state, (u16)protocol);
if (!state)
*info = "failed protocol match";
}
} else {
*info = "failed type match";
*p = early_match(policy, state, request);
if (!*p) {
buffer = cpu_to_be16((u16)protocol);
state = aa_dfa_match_len(policy->dfa, state, (char *) &buffer,
2);
if (!state)
*info = "failed protocol match";
}
return state;
}
@@ -182,20 +259,21 @@ int aa_profile_af_perm(struct aa_profile *profile,
AA_BUG(type < 0 || type >= SOCK_MAX);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES(rules, AA_CLASS_NET);
if (profile_unconfined(profile))
return 0;
state = RULE_MEDIATES_NET(rules);
if (!state)
return 0;
state = aa_match_to_prot(rules->policy, state, request, family, type,
protocol, &p, &ad->info);
return do_perms(profile, rules->policy, state, request, p, ad);
return aa_do_perms(profile, rules->policy, state, request, p, ad);
}
int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, u16 family, int type, int protocol)
{
struct aa_profile *profile;
DEFINE_AUDIT_NET(ad, op, NULL, family, type, protocol);
DEFINE_AUDIT_NET(ad, op, subj_cred, NULL, family, type, protocol);
return fn_for_each_confined(label, profile,
aa_profile_af_perm(profile, &ad, request, family,
@@ -215,7 +293,7 @@ static int aa_label_sk_perm(const struct cred *subj_cred,
if (ctx->label != kernel_t && !unconfined(label)) {
struct aa_profile *profile;
DEFINE_AUDIT_SK(ad, op, sk);
DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
ad.subj_cred = subj_cred;
error = fn_for_each_confined(label, profile,
@@ -243,12 +321,16 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk)
int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, struct socket *sock)
const char *op, u32 request, struct file *file)
{
struct socket *sock = (struct socket *) file->private_data;
AA_BUG(!label);
AA_BUG(!sock);
AA_BUG(!sock->sk);
if (sock->sk->sk_family == PF_UNIX)
return aa_unix_file_perm(subj_cred, label, op, request, file);
return aa_label_sk_perm(subj_cred, label, op, request, sock->sk);
}
@@ -313,7 +395,7 @@ int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, const struct sock *sk)
{
struct aa_profile *profile;
DEFINE_AUDIT_SK(ad, op, sk);
DEFINE_AUDIT_SK(ad, op, NULL, sk);
return fn_for_each_confined(label, profile,
aa_secmark_perm(profile, request, secid,