+ Features

- improve debug printing
   - carry mediation check on label (optimization)
   - improve ability for compiler to optimize __begin_current_label_crit_section
   - transition for a linked list of rulesets to a vector of rulesets
   - don't hardcode profile signal, allow it to be set by policy
   - ability to mediate caps via the state machine instead of lut
   - Add Ubuntu af_unix mediation, put it behind new v9 abi
 
 + Cleanups
   - fix typos and spelling errors
   - cleanup kernel doc and code inconsistencies
   - remove redundant checks/code
   - remove unused variables
   - Use str_yes_no() helper function
   - mark tables static where appropriate
   - make all generated string array headers const char *const
   - refactor to doc semantics of file_perm checks
   - replace macro calls to network/socket fns with explicit calls
   - refactor/cleanup socket mediation code preparing for finer grained
     mediation of different network families
   - several updates to kernel doc comments
 
 + Bug fixes
   - apparmor: Fix incorrect profile->signal range check
   - idmap mount fixes
   - policy unpack unaligned access fixes
   - kfree_sensitive() where appropriate
   - fix oops when freeing policy
   - fix conflicting attachment resolution
   - fix exec table look-ups when stacking isn't first
   - fix exec auditing
   - mitigate userspace generating overly large xtables
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE7cSDD705q2rFEEf7BS82cBjVw9gFAmiQmakACgkQBS82cBjV
 w9jvKBAAn/HblSPo112ycW/yjeonkyiCY56LvyeU1YWQ8m370xPqM3yK2SHcj2i1
 we1mx5beDxbH5xn7c6w0EtyoHP7FNhyHp7neG8/WaJ1JG4uxv9HvrDmEJeQEJn/3
 5AP1q2dZF9NwnKBfB5zjwZXBJzncWtYBoLUjYMbehWlQjufT2yElyM8YZZN8ziLE
 M5ILVX6UMGpBH/zuX5kN2idLcubnv5MvLo2IEt+/nGLPbed44w+mZTM5WOTbzPNq
 w8axyNdhGt9kcSGwWuM+48T4oLfwagoxIZ3RXSQ4eExk6I8ZaFXua8nknC9wENN4
 9vkzDSWAupQ+o1bLKVNMVkqvBIIqmvEWvwket/hiyxs3Y5PDckRqOgQ/4Wbmgp9B
 KhLXxzIrF9PXkZ/rpMzloxnvDtMwoSScDShhW4TCRCmpDo/GwPwoPIpgbnc3kTq0
 poomca9KZ7YEnX/90Bh92Duo5OBDOHYlbWVE7EWX01htcxExQJt47JK48C25cY5p
 /cVDVepoz7EnKjB7mm9k6K1gYGvDeu3W1whRZNEK74AQJ7p+CrBoU+WjeMmZqP5V
 s47cLF17hbnw4ZvfsxQDkPgSOP1kuJIVlwFV2lPQk5hDcT6V0kZtqUzczKJSqeJb
 CGOkKvM7ao/7Cn8pmDNG1ZuPl/HuJ6wjlxt7SVt4/3rzLFzwglo=
 =Djjn
 -----END PGP SIGNATURE-----

Merge tag 'apparmor-pr-2025-08-04' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "This has one major feature, it pulls in a cleaned up version of
  af_unix mediation that Ubuntu has been carrying for years. It is
  placed behind a new abi to ensure that it does cause policy
  regressions. With pulling in the af_unix mediation there have been
  cleanups and some refactoring of network socket mediation. This
  accounts for the majority of the changes in the diff.

  In addition there are a few improvements providing minor code
  optimizations. several code cleanups, and bug fixes.

  Features:
   - improve debug printing
   - carry mediation check on label (optimization)
   - improve ability for compiler to optimize
     __begin_current_label_crit_section
   - transition for a linked list of rulesets to a vector of rulesets
   - don't hardcode profile signal, allow it to be set by policy
   - ability to mediate caps via the state machine instead of lut
   - Add Ubuntu af_unix mediation, put it behind new v9 abi

  Cleanups:
   - fix typos and spelling errors
   - cleanup kernel doc and code inconsistencies
   - remove redundant checks/code
   - remove unused variables
   - Use str_yes_no() helper function
   - mark tables static where appropriate
   - make all generated string array headers const char *const
   - refactor to doc semantics of file_perm checks
   - replace macro calls to network/socket fns with explicit calls
   - refactor/cleanup socket mediation code preparing for finer grained
     mediation of different network families
   - several updates to kernel doc comments

  Bug fixes:
   - fix incorrect profile->signal range check
   - idmap mount fixes
   - policy unpack unaligned access fixes
   - kfree_sensitive() where appropriate
   - fix oops when freeing policy
   - fix conflicting attachment resolution
   - fix exec table look-ups when stacking isn't first
   - fix exec auditing
   - mitigate userspace generating overly large xtables"

* tag 'apparmor-pr-2025-08-04' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (60 commits)
  apparmor: fix: oops when trying to free null ruleset
  apparmor: fix Regression on linux-next (next-20250721)
  apparmor: fix test error: WARNING in apparmor_unix_stream_connect
  apparmor: Remove the unused variable rules
  apparmor: fix: accept2 being specifie even when permission table is presnt
  apparmor: transition from a list of rules to a vector of rules
  apparmor: fix documentation mismatches in val_mask_to_str and socket functions
  apparmor: remove redundant perms.allow MAY_EXEC bitflag set
  apparmor: fix kernel doc warnings for kernel test robot
  apparmor: Fix unaligned memory accesses in KUnit test
  apparmor: Fix 8-byte alignment for initial dfa blob streams
  apparmor: shift uid when mediating af_unix in userns
  apparmor: shift ouid when mediating hard links in userns
  apparmor: make sure unix socket labeling is correctly updated.
  apparmor: fix regression in fs based unix sockets when using old abi
  apparmor: fix AA_DEBUG_LABEL()
  apparmor: fix af_unix auditing to include all address information
  apparmor: Remove use of the double lock
  apparmor: update kernel doc comments for xxx_label_crit_section
  apparmor: make __begin_current_label_crit_section() indicate whether put is needed
  ...
This commit is contained in:
Linus Torvalds 2025-08-04 08:17:28 -07:00
commit 8b45c6c90a
38 changed files with 2179 additions and 431 deletions

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 \ 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 \ 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 \ 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 apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
@ -28,7 +28,7 @@ clean-files := capability_names.h rlim_names.h net_names.h
# to # to
# #define AA_SFS_AF_MASK "local inet" # #define AA_SFS_AF_MASK "local inet"
quiet_cmd_make-af = GEN $@ quiet_cmd_make-af = GEN $@
cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ cmd_make-af = echo "static const char *const address_family_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \
's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
echo "};" >> $@ ;\ echo "};" >> $@ ;\
@ -43,7 +43,7 @@ cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
# to # to
# [1] = "stream", # [1] = "stream",
quiet_cmd_make-sock = GEN $@ quiet_cmd_make-sock = GEN $@
cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ cmd_make-sock = echo "static const char *const sock_type_names[] = {" >> $@ ;\
sed $^ >>$@ -r -n \ sed $^ >>$@ -r -n \
-e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
echo "};" >> $@ echo "};" >> $@

799
security/apparmor/af_unix.c Normal file
View file

@ -0,0 +1,799 @@
// 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.
*/
#include <linux/fs.h>
#include <net/tcp_states.h>
#include "include/audit.h"
#include "include/af_unix.h"
#include "include/apparmor.h"
#include "include/file.h"
#include "include/label.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/cred.h"
static inline struct sock *aa_unix_sk(struct unix_sock *u)
{
return &u->sk;
}
static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred,
struct aa_label *label, struct path *path)
{
AA_BUG(!label);
AA_BUG(!path);
if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE))
return 0;
mask &= NET_FS_PERMS;
/* if !u->path.dentry socket is being shutdown - implicit delegation
* until obj delegation is supported
*/
if (path->dentry) {
/* the sunpath may not be valid for this ns so use the path */
struct inode *inode = path->dentry->d_inode;
vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(path->mnt), inode);
struct path_cond cond = {
.uid = vfsuid_into_kuid(vfsuid),
.mode = inode->i_mode,
};
return aa_path_perm(op, subj_cred, label, path,
PATH_SOCK_COND, mask, &cond);
} /* else implicitly delegated */
return 0;
}
/* match_addr special constants */
#define ABSTRACT_ADDR "\x00" /* abstract socket addr */
#define ANONYMOUS_ADDR "\x01" /* anonymous endpoint, no addr */
#define DISCONNECTED_ADDR "\x02" /* addr is another namespace */
#define SHUTDOWN_ADDR "\x03" /* path addr is shutdown and cleared */
#define FS_ADDR "/" /* path addr in fs */
static aa_state_t match_addr(struct aa_dfa *dfa, aa_state_t state,
struct sockaddr_un *addr, int addrlen)
{
if (addr)
/* include leading \0 */
state = aa_dfa_match_len(dfa, state, addr->sun_path,
unix_addr_len(addrlen));
else
state = aa_dfa_match_len(dfa, state, ANONYMOUS_ADDR, 1);
/* todo: could change to out of band for cleaner separation */
state = aa_dfa_null_transition(dfa, state);
return state;
}
static aa_state_t match_to_local(struct aa_policydb *policy,
aa_state_t state, u32 request,
int type, int protocol,
struct sockaddr_un *addr, int addrlen,
struct aa_perms **p,
const char **info)
{
state = aa_match_to_prot(policy, state, request, PF_UNIX, type,
protocol, NULL, info);
if (state) {
state = match_addr(policy->dfa, state, addr, addrlen);
if (state) {
/* todo: local label matching */
state = aa_dfa_null_transition(policy->dfa, state);
if (!state)
*info = "failed local label match";
} else {
*info = "failed local address match";
}
}
return state;
}
struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen)
{
struct unix_address *addr;
/* memory barrier is sufficient see note in net/unix/af_unix.c */
addr = smp_load_acquire(&u->addr);
if (addr) {
*addrlen = addr->len;
return addr->name;
}
*addrlen = 0;
return NULL;
}
static aa_state_t match_to_sk(struct aa_policydb *policy,
aa_state_t state, u32 request,
struct unix_sock *u, struct aa_perms **p,
const char **info)
{
int addrlen;
struct sockaddr_un *addr = aa_sunaddr(u, &addrlen);
return match_to_local(policy, state, request, u->sk.sk_type,
u->sk.sk_protocol, addr, addrlen, p, info);
}
#define CMD_ADDR 1
#define CMD_LISTEN 2
#define CMD_OPT 4
static aa_state_t match_to_cmd(struct aa_policydb *policy, aa_state_t state,
u32 request, struct unix_sock *u,
char cmd, struct aa_perms **p,
const char **info)
{
AA_BUG(!p);
state = match_to_sk(policy, state, request, u, p, info);
if (state && !*p) {
state = aa_dfa_match_len(policy->dfa, state, &cmd, 1);
if (!state)
*info = "failed cmd selection match";
}
return state;
}
static aa_state_t match_to_peer(struct aa_policydb *policy, aa_state_t state,
u32 request, struct unix_sock *u,
struct sockaddr_un *peer_addr, int peer_addrlen,
struct aa_perms **p, const char **info)
{
AA_BUG(!p);
state = match_to_cmd(policy, state, request, u, CMD_ADDR, p, info);
if (state && !*p) {
state = match_addr(policy->dfa, state, peer_addr, peer_addrlen);
if (!state)
*info = "failed peer address match";
}
return state;
}
static aa_state_t match_label(struct aa_profile *profile,
struct aa_ruleset *rule, aa_state_t state,
u32 request, struct aa_profile *peer,
struct aa_perms *p,
struct apparmor_audit_data *ad)
{
AA_BUG(!profile);
AA_BUG(!peer);
ad->peer = &peer->label;
if (state && !p) {
state = aa_dfa_match(rule->policy->dfa, state,
peer->base.hname);
if (!state)
ad->info = "failed peer label match";
}
return aa_do_perms(profile, rule->policy, state, request, p, ad);
}
/* unix sock creation comes before we know if the socket will be an fs
* socket
* v6 - semantics are handled by mapping in profile load
* v7 - semantics require sock create for tasks creating an fs socket.
* v8 - same as v7
*/
static int profile_create_perm(struct aa_profile *profile, int family,
int type, int protocol,
struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
aa_state_t state;
AA_BUG(!profile);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES_v9NET(rules);
if (state) {
state = aa_match_to_prot(rules->policy, state, AA_MAY_CREATE,
PF_UNIX, type, protocol, NULL,
&ad->info);
return aa_do_perms(profile, rules->policy, state, AA_MAY_CREATE,
NULL, ad);
}
return aa_profile_af_perm(profile, ad, AA_MAY_CREATE, family, type,
protocol);
}
static int profile_sk_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad,
u32 request, struct sock *sk, struct path *path)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms *p = NULL;
aa_state_t state;
AA_BUG(!profile);
AA_BUG(!sk);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES_v9NET(rules);
if (state) {
if (is_unix_fs(sk))
return unix_fs_perm(ad->op, request, ad->subj_cred,
&profile->label,
&unix_sk(sk)->path);
state = match_to_sk(rules->policy, state, request, unix_sk(sk),
&p, &ad->info);
return aa_do_perms(profile, rules->policy, state, request, p,
ad);
}
return aa_profile_af_sk_perm(profile, ad, request, sk);
}
static int profile_bind_perm(struct aa_profile *profile, struct sock *sk,
struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms *p = NULL;
aa_state_t state;
AA_BUG(!profile);
AA_BUG(!sk);
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES_v9NET(rules);
if (state) {
if (is_unix_addr_fs(ad->net.addr, ad->net.addrlen))
/* under v7-9 fs hook handles bind */
return 0;
/* bind for abstract socket */
state = match_to_local(rules->policy, state, AA_MAY_BIND,
sk->sk_type, sk->sk_protocol,
unix_addr(ad->net.addr),
ad->net.addrlen,
&p, &ad->info);
return aa_do_perms(profile, rules->policy, state, AA_MAY_BIND,
p, ad);
}
return aa_profile_af_sk_perm(profile, ad, AA_MAY_BIND, sk);
}
static int profile_listen_perm(struct aa_profile *profile, struct sock *sk,
int backlog, struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms *p = NULL;
aa_state_t state;
AA_BUG(!profile);
AA_BUG(!sk);
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES_v9NET(rules);
if (state) {
__be16 b = cpu_to_be16(backlog);
if (is_unix_fs(sk))
return unix_fs_perm(ad->op, AA_MAY_LISTEN,
ad->subj_cred, &profile->label,
&unix_sk(sk)->path);
state = match_to_cmd(rules->policy, state, AA_MAY_LISTEN,
unix_sk(sk), CMD_LISTEN, &p, &ad->info);
if (state && !p) {
state = aa_dfa_match_len(rules->policy->dfa, state,
(char *) &b, 2);
if (!state)
ad->info = "failed listen backlog match";
}
return aa_do_perms(profile, rules->policy, state, AA_MAY_LISTEN,
p, ad);
}
return aa_profile_af_sk_perm(profile, ad, AA_MAY_LISTEN, sk);
}
static int profile_accept_perm(struct aa_profile *profile,
struct sock *sk,
struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms *p = NULL;
aa_state_t state;
AA_BUG(!profile);
AA_BUG(!sk);
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES_v9NET(rules);
if (state) {
if (is_unix_fs(sk))
return unix_fs_perm(ad->op, AA_MAY_ACCEPT,
ad->subj_cred, &profile->label,
&unix_sk(sk)->path);
state = match_to_sk(rules->policy, state, AA_MAY_ACCEPT,
unix_sk(sk), &p, &ad->info);
return aa_do_perms(profile, rules->policy, state, AA_MAY_ACCEPT,
p, ad);
}
return aa_profile_af_sk_perm(profile, ad, AA_MAY_ACCEPT, sk);
}
static int profile_opt_perm(struct aa_profile *profile, u32 request,
struct sock *sk, int optname,
struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms *p = NULL;
aa_state_t state;
AA_BUG(!profile);
AA_BUG(!sk);
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
state = RULE_MEDIATES_v9NET(rules);
if (state) {
__be16 b = cpu_to_be16(optname);
if (is_unix_fs(sk))
return unix_fs_perm(ad->op, request,
ad->subj_cred, &profile->label,
&unix_sk(sk)->path);
state = match_to_cmd(rules->policy, state, request, unix_sk(sk),
CMD_OPT, &p, &ad->info);
if (state && !p) {
state = aa_dfa_match_len(rules->policy->dfa, state,
(char *) &b, 2);
if (!state)
ad->info = "failed sockopt match";
}
return aa_do_perms(profile, rules->policy, state, request, p,
ad);
}
return aa_profile_af_sk_perm(profile, ad, request, sk);
}
/* null peer_label is allowed, in which case the peer_sk label is used */
static int profile_peer_perm(struct aa_profile *profile, u32 request,
struct sock *sk, struct path *path,
struct sockaddr_un *peer_addr,
int peer_addrlen, struct path *peer_path,
struct aa_label *peer_label,
struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms *p = NULL;
aa_state_t state;
AA_BUG(!profile);
AA_BUG(profile_unconfined(profile));
AA_BUG(!sk);
AA_BUG(!peer_label);
AA_BUG(!ad);
state = RULE_MEDIATES_v9NET(rules);
if (state) {
struct aa_profile *peerp;
if (peer_path)
return unix_fs_perm(ad->op, request, ad->subj_cred,
&profile->label, peer_path);
else if (path)
return unix_fs_perm(ad->op, request, ad->subj_cred,
&profile->label, path);
state = match_to_peer(rules->policy, state, request,
unix_sk(sk),
peer_addr, peer_addrlen, &p, &ad->info);
return fn_for_each_in_ns(peer_label, peerp,
match_label(profile, rules, state, request,
peerp, p, ad));
}
return aa_profile_af_sk_perm(profile, ad, request, sk);
}
/* -------------------------------- */
int aa_unix_create_perm(struct aa_label *label, int family, int type,
int protocol)
{
if (!unconfined(label)) {
struct aa_profile *profile;
DEFINE_AUDIT_NET(ad, OP_CREATE, current_cred(), NULL, family,
type, protocol);
return fn_for_each_confined(label, profile,
profile_create_perm(profile, family, type,
protocol, &ad));
}
return 0;
}
static int aa_unix_label_sk_perm(const struct cred *subj_cred,
struct aa_label *label,
const char *op, u32 request, struct sock *sk,
struct path *path)
{
if (!unconfined(label)) {
struct aa_profile *profile;
DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
return fn_for_each_confined(label, profile,
profile_sk_perm(profile, &ad, request, sk,
path));
}
return 0;
}
/* revalidation, get/set attr, shutdown */
int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock)
{
struct aa_label *label;
int error;
label = begin_current_label_crit_section();
error = aa_unix_label_sk_perm(current_cred(), label, op,
request, sock->sk,
is_unix_fs(sock->sk) ? &unix_sk(sock->sk)->path : NULL);
end_current_label_crit_section(label);
return error;
}
static int valid_addr(struct sockaddr *addr, int addr_len)
{
struct sockaddr_un *sunaddr = unix_addr(addr);
/* addr_len == offsetof(struct sockaddr_un, sun_path) is autobind */
if (addr_len < offsetof(struct sockaddr_un, sun_path) ||
addr_len > sizeof(*sunaddr))
return -EINVAL;
return 0;
}
int aa_unix_bind_perm(struct socket *sock, struct sockaddr *addr,
int addrlen)
{
struct aa_profile *profile;
struct aa_label *label;
int error = 0;
error = valid_addr(addr, addrlen);
if (error)
return error;
label = begin_current_label_crit_section();
/* fs bind is handled by mknod */
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, OP_BIND, current_cred(), sock->sk);
ad.net.addr = unix_addr(addr);
ad.net.addrlen = addrlen;
error = fn_for_each_confined(label, profile,
profile_bind_perm(profile, sock->sk, &ad));
}
end_current_label_crit_section(label);
return error;
}
/*
* unix connections are covered by the
* - unix_stream_connect (stream) and unix_may_send hooks (dgram)
* - fs connect is handled by open
* This is just here to document this is not needed for af_unix
*
int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address,
int addrlen)
{
return 0;
}
*/
int aa_unix_listen_perm(struct socket *sock, int backlog)
{
struct aa_profile *profile;
struct aa_label *label;
int error = 0;
label = begin_current_label_crit_section();
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, OP_LISTEN, current_cred(), sock->sk);
error = fn_for_each_confined(label, profile,
profile_listen_perm(profile, sock->sk,
backlog, &ad));
}
end_current_label_crit_section(label);
return error;
}
/* ability of sock to connect, not peer address binding */
int aa_unix_accept_perm(struct socket *sock, struct socket *newsock)
{
struct aa_profile *profile;
struct aa_label *label;
int error = 0;
label = begin_current_label_crit_section();
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, OP_ACCEPT, current_cred(), sock->sk);
error = fn_for_each_confined(label, profile,
profile_accept_perm(profile, sock->sk, &ad));
}
end_current_label_crit_section(label);
return error;
}
/*
* dgram handled by unix_may_sendmsg, right to send on stream done at connect
* could do per msg unix_stream here, but connect + socket transfer is
* sufficient. This is just here to document this is not needed for af_unix
*
* sendmsg, recvmsg
int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
struct msghdr *msg, int size)
{
return 0;
}
*/
int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock,
int level, int optname)
{
struct aa_profile *profile;
struct aa_label *label;
int error = 0;
label = begin_current_label_crit_section();
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, op, current_cred(), sock->sk);
error = fn_for_each_confined(label, profile,
profile_opt_perm(profile, request, sock->sk,
optname, &ad));
}
end_current_label_crit_section(label);
return error;
}
static int unix_peer_perm(const struct cred *subj_cred,
struct aa_label *label, const char *op, u32 request,
struct sock *sk, struct path *path,
struct sockaddr_un *peer_addr, int peer_addrlen,
struct path *peer_path, struct aa_label *peer_label)
{
struct aa_profile *profile;
DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
ad.net.peer.addr = peer_addr;
ad.net.peer.addrlen = peer_addrlen;
return fn_for_each_confined(label, profile,
profile_peer_perm(profile, request, sk, path,
peer_addr, peer_addrlen, peer_path,
peer_label, &ad));
}
/**
*
* Requires: lock held on both @sk and @peer_sk
* called by unix_stream_connect, unix_may_send
*/
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)
{
struct unix_sock *peeru = unix_sk(peer_sk);
struct unix_sock *u = unix_sk(sk);
int plen;
struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk), &plen);
AA_BUG(!label);
AA_BUG(!sk);
AA_BUG(!peer_sk);
AA_BUG(!peer_label);
return unix_peer_perm(subj_cred, label, op, request, sk,
is_unix_fs(sk) ? &u->path : NULL,
paddr, plen,
is_unix_fs(peer_sk) ? &peeru->path : NULL,
peer_label);
}
/* sk_plabel for comparison only */
static void update_sk_ctx(struct sock *sk, struct aa_label *label,
struct aa_label *plabel)
{
struct aa_label *l, *old;
struct aa_sk_ctx *ctx = aa_sock(sk);
bool update_sk;
rcu_read_lock();
update_sk = (plabel &&
(plabel != rcu_access_pointer(ctx->peer_lastupdate) ||
!aa_label_is_subset(plabel, rcu_dereference(ctx->peer)))) ||
!__aa_subj_label_is_cached(label, rcu_dereference(ctx->label));
rcu_read_unlock();
if (!update_sk)
return;
spin_lock(&unix_sk(sk)->lock);
old = rcu_dereference_protected(ctx->label,
lockdep_is_held(&unix_sk(sk)->lock));
l = aa_label_merge(old, label, GFP_ATOMIC);
if (l) {
if (l != old) {
rcu_assign_pointer(ctx->label, l);
aa_put_label(old);
} else
aa_put_label(l);
}
if (plabel && rcu_access_pointer(ctx->peer_lastupdate) != plabel) {
old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock));
if (old == plabel) {
rcu_assign_pointer(ctx->peer_lastupdate, plabel);
} else if (aa_label_is_subset(plabel, old)) {
rcu_assign_pointer(ctx->peer_lastupdate, plabel);
rcu_assign_pointer(ctx->peer, aa_get_label(plabel));
aa_put_label(old);
} /* else race or a subset - don't update */
}
spin_unlock(&unix_sk(sk)->lock);
}
static void update_peer_ctx(struct sock *sk, struct aa_sk_ctx *ctx,
struct aa_label *label)
{
struct aa_label *l, *old;
spin_lock(&unix_sk(sk)->lock);
old = rcu_dereference_protected(ctx->peer,
lockdep_is_held(&unix_sk(sk)->lock));
l = aa_label_merge(old, label, GFP_ATOMIC);
if (l) {
if (l != old) {
rcu_assign_pointer(ctx->peer, l);
aa_put_label(old);
} else
aa_put_label(l);
}
spin_unlock(&unix_sk(sk)->lock);
}
/* This fn is only checked if something has changed in the security
* boundaries. Otherwise cached info off file is sufficient
*/
int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, struct file *file)
{
struct socket *sock = (struct socket *) file->private_data;
struct sockaddr_un *addr, *peer_addr;
int addrlen, peer_addrlen;
struct aa_label *plabel = NULL;
struct sock *peer_sk = NULL;
u32 sk_req = request & ~NET_PEER_MASK;
struct path path;
bool is_sk_fs;
int error = 0;
AA_BUG(!label);
AA_BUG(!sock);
AA_BUG(!sock->sk);
AA_BUG(sock->sk->sk_family != PF_UNIX);
/* investigate only using lock via unix_peer_get()
* addr only needs the memory barrier, but need to investigate
* path
*/
unix_state_lock(sock->sk);
peer_sk = unix_peer(sock->sk);
if (peer_sk)
sock_hold(peer_sk);
is_sk_fs = is_unix_fs(sock->sk);
addr = aa_sunaddr(unix_sk(sock->sk), &addrlen);
path = unix_sk(sock->sk)->path;
unix_state_unlock(sock->sk);
if (is_sk_fs && peer_sk)
sk_req = request;
if (sk_req) {
error = aa_unix_label_sk_perm(subj_cred, label, op,
sk_req, sock->sk,
is_sk_fs ? &path : NULL);
}
if (!peer_sk)
goto out;
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
struct path peer_path;
peer_path = unix_sk(peer_sk)->path;
if (!is_sk_fs && is_unix_fs(peer_sk)) {
last_error(error,
unix_fs_perm(op, request, subj_cred, label,
is_unix_fs(peer_sk) ? &peer_path : NULL));
} else if (!is_sk_fs) {
struct aa_label *plabel;
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
rcu_read_lock();
plabel = aa_get_label_rcu(&pctx->label);
rcu_read_unlock();
/* no fs check of aa_unix_peer_perm because conditions above
* ensure they will never be done
*/
last_error(error,
xcheck(unix_peer_perm(subj_cred, label, op,
MAY_READ | MAY_WRITE, sock->sk,
is_sk_fs ? &path : NULL,
peer_addr, peer_addrlen,
is_unix_fs(peer_sk) ?
&peer_path : NULL,
plabel),
unix_peer_perm(file->f_cred, plabel, op,
MAY_READ | MAY_WRITE, peer_sk,
is_unix_fs(peer_sk) ?
&peer_path : NULL,
addr, addrlen,
is_sk_fs ? &path : NULL,
label)));
if (!error && !__aa_subj_label_is_cached(plabel, label))
update_peer_ctx(peer_sk, pctx, label);
}
sock_put(peer_sk);
out:
/* update peer cache to latest successful perm check */
if (error == 0)
update_sk_ctx(sock->sk, label, plabel);
aa_put_label(plabel);
return error;
}

View file

@ -43,7 +43,7 @@
* The interface is split into two main components based on their function * The interface is split into two main components based on their function
* a securityfs component: * a securityfs component:
* used for static files that are always available, and which allows * used for static files that are always available, and which allows
* userspace to specificy the location of the security filesystem. * userspace to specify the location of the security filesystem.
* *
* fns and data are prefixed with * fns and data are prefixed with
* aa_sfs_ * aa_sfs_
@ -204,7 +204,7 @@ static struct file_system_type aafs_ops = {
/** /**
* __aafs_setup_d_inode - basic inode setup for apparmorfs * __aafs_setup_d_inode - basic inode setup for apparmorfs
* @dir: parent directory for the dentry * @dir: parent directory for the dentry
* @dentry: dentry we are seting the inode up for * @dentry: dentry we are setting the inode up for
* @mode: permissions the file should have * @mode: permissions the file should have
* @data: data to store on inode.i_private, available in open() * @data: data to store on inode.i_private, available in open()
* @link: if symlink, symlink target string * @link: if symlink, symlink target string
@ -612,8 +612,7 @@ static const struct file_operations aa_fs_ns_revision_fops = {
static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms, static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
const char *match_str, size_t match_len) const char *match_str, size_t match_len)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_perms tmp = { }; struct aa_perms tmp = { };
aa_state_t state = DFA_NOMATCH; aa_state_t state = DFA_NOMATCH;
@ -626,11 +625,20 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
if (state) { if (state) {
struct path_cond cond = { }; struct path_cond cond = { };
tmp = *(aa_lookup_fperms(rules->file, state, &cond)); tmp = *(aa_lookup_condperms(current_fsuid(),
rules->file, state, &cond));
} }
} else if (rules->policy->dfa) { } else if (rules->policy->dfa) {
if (!RULE_MEDIATES(rules, *match_str)) if (!RULE_MEDIATES(rules, *match_str))
return; /* no change to current perms */ return; /* no change to current perms */
/* old user space does not correctly detect dbus mediation
* support so we may get dbus policy and requests when
* the abi doesn't support it. This can cause mediation
* regressions, so explicitly test for this situation.
*/
if (*match_str == AA_CLASS_DBUS &&
!RULE_MEDIATES_v9NET(rules))
return; /* no change to current perms */
state = aa_dfa_match_len(rules->policy->dfa, state = aa_dfa_match_len(rules->policy->dfa,
rules->policy->start[0], rules->policy->start[0],
match_str, match_len); match_str, match_len);
@ -997,7 +1005,7 @@ static int aa_sfs_seq_show(struct seq_file *seq, void *v)
switch (fs_file->v_type) { switch (fs_file->v_type) {
case AA_SFS_TYPE_BOOLEAN: case AA_SFS_TYPE_BOOLEAN:
seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no"); seq_printf(seq, "%s\n", str_yes_no(fs_file->v.boolean));
break; break;
case AA_SFS_TYPE_STRING: case AA_SFS_TYPE_STRING:
seq_printf(seq, "%s\n", fs_file->v.string); seq_printf(seq, "%s\n", fs_file->v.string);
@ -1006,7 +1014,7 @@ static int aa_sfs_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%#08lx\n", fs_file->v.u64); seq_printf(seq, "%#08lx\n", fs_file->v.u64);
break; break;
default: default:
/* Ignore unpritable entry types. */ /* Ignore unprintable entry types. */
break; break;
} }
@ -1152,7 +1160,7 @@ static int seq_ns_stacked_show(struct seq_file *seq, void *v)
struct aa_label *label; struct aa_label *label;
label = begin_current_label_crit_section(); label = begin_current_label_crit_section();
seq_printf(seq, "%s\n", label->size > 1 ? "yes" : "no"); seq_printf(seq, "%s\n", str_yes_no(label->size > 1));
end_current_label_crit_section(label); end_current_label_crit_section(label);
return 0; return 0;
@ -1175,7 +1183,7 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
} }
} }
seq_printf(seq, "%s\n", count > 1 ? "yes" : "no"); seq_printf(seq, "%s\n", str_yes_no(count > 1));
end_current_label_crit_section(label); end_current_label_crit_section(label);
return 0; return 0;
@ -2244,7 +2252,7 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos)
/** /**
* p_stop - stop depth first traversal * p_stop - stop depth first traversal
* @f: seq_file we are filling * @f: seq_file we are filling
* @p: the last profile writen * @p: the last profile written
* *
* Release all locking done by p_start/p_next on namespace tree * Release all locking done by p_start/p_next on namespace tree
*/ */
@ -2332,6 +2340,7 @@ static struct aa_sfs_entry aa_sfs_entry_attach[] = {
static struct aa_sfs_entry aa_sfs_entry_domain[] = { static struct aa_sfs_entry aa_sfs_entry_domain[] = {
AA_SFS_FILE_BOOLEAN("change_hat", 1), AA_SFS_FILE_BOOLEAN("change_hat", 1),
AA_SFS_FILE_BOOLEAN("change_hatv", 1), AA_SFS_FILE_BOOLEAN("change_hatv", 1),
AA_SFS_FILE_BOOLEAN("unconfined_allowed_children", 1),
AA_SFS_FILE_BOOLEAN("change_onexec", 1), AA_SFS_FILE_BOOLEAN("change_onexec", 1),
AA_SFS_FILE_BOOLEAN("change_profile", 1), AA_SFS_FILE_BOOLEAN("change_profile", 1),
AA_SFS_FILE_BOOLEAN("stack", 1), AA_SFS_FILE_BOOLEAN("stack", 1),
@ -2340,6 +2349,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = {
AA_SFS_FILE_BOOLEAN("computed_longest_left", 1), AA_SFS_FILE_BOOLEAN("computed_longest_left", 1),
AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach), AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach),
AA_SFS_FILE_BOOLEAN("disconnected.path", 1), AA_SFS_FILE_BOOLEAN("disconnected.path", 1),
AA_SFS_FILE_BOOLEAN("kill.signal", 1),
AA_SFS_FILE_STRING("version", "1.2"), AA_SFS_FILE_STRING("version", "1.2"),
{ } { }
}; };
@ -2364,7 +2374,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_FILE_BOOLEAN("set_load", 1), AA_SFS_FILE_BOOLEAN("set_load", 1),
/* number of out of band transitions supported */ /* number of out of band transitions supported */
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
AA_SFS_FILE_U64("permstable32_version", 1), AA_SFS_FILE_U64("permstable32_version", 3),
AA_SFS_FILE_STRING("permstable32", PERMS32STR), AA_SFS_FILE_STRING("permstable32", PERMS32STR),
AA_SFS_FILE_U64("state32", 1), AA_SFS_FILE_U64("state32", 1),
AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined), AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
@ -2384,6 +2394,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[] = { static struct aa_sfs_entry aa_sfs_entry_query_label[] = {
AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), AA_SFS_FILE_STRING("perms", "allow deny audit quiet"),
AA_SFS_FILE_BOOLEAN("data", 1), AA_SFS_FILE_BOOLEAN("data", 1),
@ -2406,6 +2421,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("domain", aa_sfs_entry_domain),
AA_SFS_DIR("file", aa_sfs_entry_file), AA_SFS_DIR("file", aa_sfs_entry_file),
AA_SFS_DIR("network_v8", aa_sfs_entry_network), AA_SFS_DIR("network_v8", aa_sfs_entry_network),
AA_SFS_DIR("network_v9", aa_sfs_entry_networkv9),
AA_SFS_DIR("mount", aa_sfs_entry_mount), AA_SFS_DIR("mount", aa_sfs_entry_mount),
AA_SFS_DIR("namespaces", aa_sfs_entry_ns), AA_SFS_DIR("namespaces", aa_sfs_entry_ns),
AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
@ -2413,6 +2429,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
AA_SFS_DIR("caps", aa_sfs_entry_caps), AA_SFS_DIR("caps", aa_sfs_entry_caps),
AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
AA_SFS_DIR("signal", aa_sfs_entry_signal), 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("query", aa_sfs_entry_query),
AA_SFS_DIR("io_uring", aa_sfs_entry_io_uring), AA_SFS_DIR("io_uring", aa_sfs_entry_io_uring),
{ } { }

View file

@ -192,7 +192,7 @@ int aa_audit(int type, struct aa_profile *profile,
aa_audit_msg(type, ad, cb); aa_audit_msg(type, ad, cb);
if (ad->type == AUDIT_APPARMOR_KILL) if (ad->type == AUDIT_APPARMOR_KILL)
(void)send_sig_info(SIGKILL, NULL, (void)send_sig_info(profile->signal, NULL,
ad->common.type == LSM_AUDIT_DATA_TASK && ad->common.type == LSM_AUDIT_DATA_TASK &&
ad->common.u.tsk ? ad->common.u.tsk : current); ad->common.u.tsk ? ad->common.u.tsk : current);

View file

@ -27,6 +27,7 @@
struct aa_sfs_entry aa_sfs_entry_caps[] = { struct aa_sfs_entry aa_sfs_entry_caps[] = {
AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK), AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK),
AA_SFS_FILE_BOOLEAN("extended", 1),
{ } { }
}; };
@ -68,8 +69,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
{ {
const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */ const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct audit_cache *ent; struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO; int type = AUDIT_APPARMOR_AUTO;
@ -121,10 +121,32 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
static int profile_capable(struct aa_profile *profile, int cap, static int profile_capable(struct aa_profile *profile, int cap,
unsigned int opts, struct apparmor_audit_data *ad) unsigned int opts, struct apparmor_audit_data *ad)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list); aa_state_t state;
int error; int error;
state = RULE_MEDIATES(rules, ad->class);
if (state) {
struct aa_perms perms = { };
u32 request;
/* caps broken into 256 x 32 bit permission chunks */
state = aa_dfa_next(rules->policy->dfa, state, cap >> 5);
request = 1 << (cap & 0x1f);
perms = *aa_lookup_perms(rules->policy, state);
aa_apply_modes_to_perms(profile, &perms);
if (opts & CAP_OPT_NOAUDIT) {
if (perms.complain & request)
ad->info = "optional: no audit";
else
ad = NULL;
}
return aa_check_perms(profile, &perms, request, ad,
audit_cb);
}
/* fallback to old caps mediation that doesn't support conditionals */
if (cap_raised(rules->caps.allow, cap) && if (cap_raised(rules->caps.allow, cap) &&
!cap_raised(rules->caps.denied, cap)) !cap_raised(rules->caps.denied, cap))
error = 0; error = 0;
@ -168,3 +190,34 @@ int aa_capable(const struct cred *subj_cred, struct aa_label *label,
return error; return error;
} }
kernel_cap_t aa_profile_capget(struct aa_profile *profile)
{
struct aa_ruleset *rules = profile->label.rules[0];
aa_state_t state;
state = RULE_MEDIATES(rules, AA_CLASS_CAP);
if (state) {
kernel_cap_t caps = CAP_EMPTY_SET;
int i;
/* caps broken into up to 256, 32 bit permission chunks */
for (i = 0; i < (CAP_LAST_CAP >> 5); i++) {
struct aa_perms perms = { };
aa_state_t tmp;
tmp = aa_dfa_next(rules->policy->dfa, state, i);
perms = *aa_lookup_perms(rules->policy, tmp);
aa_apply_modes_to_perms(profile, &perms);
caps.val |= ((u64)(perms.allow)) << (i * 5);
caps.val |= ((u64)(perms.complain)) << (i * 5);
}
return caps;
}
/* fallback to old caps */
if (COMPLAIN_MODE(profile))
return CAP_FULL_SET;
return rules->caps.allow;
}

View file

@ -28,6 +28,12 @@
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h" #include "include/policy_ns.h"
static const char * const CONFLICTING_ATTACH_STR = "conflicting profile attachments";
static const char * const CONFLICTING_ATTACH_STR_IX =
"conflicting profile attachments - ix fallback";
static const char * const CONFLICTING_ATTACH_STR_UX =
"conflicting profile attachments - ux fallback";
/** /**
* may_change_ptraced_domain - check if can change profile on ptraced task * may_change_ptraced_domain - check if can change profile on ptraced task
* @to_cred: cred of task changing domain * @to_cred: cred of task changing domain
@ -87,8 +93,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
struct aa_profile *tp, struct aa_profile *tp,
bool stack, aa_state_t state) bool stack, aa_state_t state)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
const char *ns_name; const char *ns_name;
if (stack) if (stack)
@ -125,8 +130,7 @@ static int label_compound_match(struct aa_profile *profile,
aa_state_t state, bool subns, u32 request, aa_state_t state, bool subns, u32 request,
struct aa_perms *perms) struct aa_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_profile *tp; struct aa_profile *tp;
struct label_it i; struct label_it i;
struct path_cond cond = { }; struct path_cond cond = { };
@ -154,7 +158,8 @@ next:
if (!state) if (!state)
goto fail; goto fail;
} }
*perms = *(aa_lookup_fperms(rules->file, state, &cond)); *perms = *(aa_lookup_condperms(current_fsuid(), rules->file, state,
&cond));
aa_apply_modes_to_perms(profile, perms); aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request) if ((perms->allow & request) != request)
return -EACCES; return -EACCES;
@ -187,8 +192,7 @@ static int label_components_match(struct aa_profile *profile,
aa_state_t start, bool subns, u32 request, aa_state_t start, bool subns, u32 request,
struct aa_perms *perms) struct aa_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_profile *tp; struct aa_profile *tp;
struct label_it i; struct label_it i;
struct aa_perms tmp; struct aa_perms tmp;
@ -209,7 +213,8 @@ static int label_components_match(struct aa_profile *profile,
return 0; return 0;
next: next:
tmp = *(aa_lookup_fperms(rules->file, state, &cond)); tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state,
&cond));
aa_apply_modes_to_perms(profile, &tmp); aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp); aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) { label_for_each_cont(i, label, tp) {
@ -218,7 +223,8 @@ next:
state = match_component(profile, tp, stack, start); state = match_component(profile, tp, stack, start);
if (!state) if (!state)
goto fail; goto fail;
tmp = *(aa_lookup_fperms(rules->file, state, &cond)); tmp = *(aa_lookup_condperms(current_fsuid(), rules->file, state,
&cond));
aa_apply_modes_to_perms(profile, &tmp); aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp); aa_perms_accum(perms, &tmp);
} }
@ -323,7 +329,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i], size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i],
&value, value_size, GFP_KERNEL); &value, value_size, GFP_KERNEL);
if (size >= 0) { if (size >= 0) {
u32 index, perm; struct aa_perms *perms;
/* /*
* Check the xattr presence before value. This ensure * Check the xattr presence before value. This ensure
@ -335,9 +341,8 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
/* Check xattr value */ /* Check xattr value */
state = aa_dfa_match_len(attach->xmatch->dfa, state, state = aa_dfa_match_len(attach->xmatch->dfa, state,
value, size); value, size);
index = ACCEPT_TABLE(attach->xmatch->dfa)[state]; perms = aa_lookup_perms(attach->xmatch, state);
perm = attach->xmatch->perms[index].allow; if (!(perms->allow & MAY_EXEC)) {
if (!(perm & MAY_EXEC)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -415,15 +420,14 @@ restart:
if (attach->xmatch->dfa) { if (attach->xmatch->dfa) {
unsigned int count; unsigned int count;
aa_state_t state; aa_state_t state;
u32 index, perm; struct aa_perms *perms;
state = aa_dfa_leftmatch(attach->xmatch->dfa, state = aa_dfa_leftmatch(attach->xmatch->dfa,
attach->xmatch->start[AA_CLASS_XMATCH], attach->xmatch->start[AA_CLASS_XMATCH],
name, &count); name, &count);
index = ACCEPT_TABLE(attach->xmatch->dfa)[state]; perms = aa_lookup_perms(attach->xmatch, state);
perm = attach->xmatch->perms[index].allow;
/* any accepting state means a valid match. */ /* any accepting state means a valid match. */
if (perm & MAY_EXEC) { if (perms->allow & MAY_EXEC) {
int ret = 0; int ret = 0;
if (count < candidate_len) if (count < candidate_len)
@ -484,7 +488,7 @@ restart:
if (!candidate || conflict) { if (!candidate || conflict) {
if (conflict) if (conflict)
*info = "conflicting profile attachments"; *info = CONFLICTING_ATTACH_STR;
rcu_read_unlock(); rcu_read_unlock();
return NULL; return NULL;
} }
@ -508,15 +512,16 @@ static const char *next_name(int xtype, const char *name)
* @name: returns: name tested to find label (NOT NULL) * @name: returns: name tested to find label (NOT NULL)
* *
* Returns: refcounted label, or NULL on failure (MAYBE NULL) * Returns: refcounted label, or NULL on failure (MAYBE NULL)
* @name will always be set with the last name tried
*/ */
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name) const char **name)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_label *label = NULL; struct aa_label *label = NULL;
u32 xtype = xindex & AA_X_TYPE_MASK; u32 xtype = xindex & AA_X_TYPE_MASK;
int index = xindex & AA_X_INDEX_MASK; int index = xindex & AA_X_INDEX_MASK;
const char *next;
AA_BUG(!name); AA_BUG(!name);
@ -524,25 +529,27 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
/* TODO: move lookup parsing to unpack time so this is a straight /* TODO: move lookup parsing to unpack time so this is a straight
* index into the resultant label * index into the resultant label
*/ */
for (*name = rules->file->trans.table[index]; !label && *name; for (next = rules->file->trans.table[index]; next;
*name = next_name(xtype, *name)) { next = next_name(xtype, next)) {
const char *lookup = (*next == '&') ? next + 1 : next;
*name = next;
if (xindex & AA_X_CHILD) { if (xindex & AA_X_CHILD) {
struct aa_profile *new_profile; /* TODO: switich to parse to get stack of child */
struct aa_profile *new = aa_find_child(profile, lookup);
if (new)
/* release by caller */ /* release by caller */
new_profile = aa_find_child(profile, *name); return &new->label;
if (new_profile)
label = &new_profile->label;
continue; continue;
} }
label = aa_label_parse(&profile->label, *name, GFP_KERNEL, label = aa_label_parse(&profile->label, lookup, GFP_KERNEL,
true, false); true, false);
if (IS_ERR(label)) if (!IS_ERR_OR_NULL(label))
label = NULL; /* release by caller */
return label;
} }
/* released by caller */ return NULL;
return label;
} }
/** /**
@ -564,12 +571,12 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
const char **lookupname, const char **lookupname,
const char **info) const char **info)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new = NULL; struct aa_label *new = NULL;
struct aa_label *stack = NULL;
struct aa_ns *ns = profile->ns; struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK; u32 xtype = xindex & AA_X_TYPE_MASK;
const char *stack = NULL; /* Used for info checks during fallback handling */
const char *old_info = NULL;
switch (xtype) { switch (xtype) {
case AA_X_NONE: case AA_X_NONE:
@ -578,13 +585,14 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
break; break;
case AA_X_TABLE: case AA_X_TABLE:
/* TODO: fix when perm mapping done at unload */ /* TODO: fix when perm mapping done at unload */
stack = rules->file->trans.table[xindex & AA_X_INDEX_MASK]; /* released by caller
if (*stack != '&') { * if null for both stack and direct want to try fallback
/* released by caller */ */
new = x_table_lookup(profile, xindex, lookupname); new = x_table_lookup(profile, xindex, lookupname);
stack = NULL; if (!new || **lookupname != '&')
break; break;
} stack = new;
new = NULL;
fallthrough; /* to X_NAME */ fallthrough; /* to X_NAME */
case AA_X_NAME: case AA_X_NAME:
if (xindex & AA_X_CHILD) if (xindex & AA_X_CHILD)
@ -599,30 +607,51 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
break; break;
} }
/* fallback transition check */
if (!new) { if (!new) {
if (xindex & AA_X_INHERIT) { if (xindex & AA_X_INHERIT) {
/* (p|c|n)ix - don't change profile but do /* (p|c|n)ix - don't change profile but do
* use the newest version * use the newest version
*/ */
if (*info == CONFLICTING_ATTACH_STR) {
*info = CONFLICTING_ATTACH_STR_IX;
} else {
old_info = *info;
*info = "ix fallback"; *info = "ix fallback";
}
/* no profile && no error */ /* no profile && no error */
new = aa_get_newest_label(&profile->label); new = aa_get_newest_label(&profile->label);
} else if (xindex & AA_X_UNCONFINED) { } else if (xindex & AA_X_UNCONFINED) {
new = aa_get_newest_label(ns_unconfined(profile->ns)); new = aa_get_newest_label(ns_unconfined(profile->ns));
if (*info == CONFLICTING_ATTACH_STR) {
*info = CONFLICTING_ATTACH_STR_UX;
} else {
old_info = *info;
*info = "ux fallback"; *info = "ux fallback";
} }
} }
/* We set old_info on the code paths above where overwriting
* could have happened, so now check if info was set by
* find_attach as well (i.e. whether we actually overwrote)
* and warn accordingly.
*/
if (old_info && old_info != CONFLICTING_ATTACH_STR) {
pr_warn_ratelimited(
"AppArmor: find_attach (from profile %s) audit info \"%s\" dropped",
profile->base.hname, old_info);
}
}
if (new && stack) { if (new && stack) {
/* base the stack on post domain transition */ /* base the stack on post domain transition */
struct aa_label *base = new; struct aa_label *base = new;
new = aa_label_parse(base, stack, GFP_KERNEL, true, false); new = aa_label_merge(base, stack, GFP_KERNEL);
if (IS_ERR(new)) /* null on error */
new = NULL;
aa_put_label(base); aa_put_label(base);
} }
aa_put_label(stack);
/* released by caller */ /* released by caller */
return new; return new;
} }
@ -633,8 +662,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
char *buffer, struct path_cond *cond, char *buffer, struct path_cond *cond,
bool *secure_exec) bool *secure_exec)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_label *new = NULL; struct aa_label *new = NULL;
struct aa_profile *new_profile = NULL; struct aa_profile *new_profile = NULL;
const char *info = NULL, *name = NULL, *target = NULL; const char *info = NULL, *name = NULL, *target = NULL;
@ -652,7 +680,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
if (error) { if (error) {
if (profile_unconfined(profile) || if (profile_unconfined(profile) ||
(profile->label.flags & FLAG_IX_ON_NAME_ERROR)) { (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
AA_DEBUG("name lookup ix on error"); AA_DEBUG(DEBUG_DOMAIN, "name lookup ix on error");
error = 0; error = 0;
new = aa_get_newest_label(&profile->label); new = aa_get_newest_label(&profile->label);
} }
@ -663,11 +691,27 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
if (profile_unconfined(profile)) { if (profile_unconfined(profile)) {
new = find_attach(bprm, profile->ns, new = find_attach(bprm, profile->ns,
&profile->ns->base.profiles, name, &info); &profile->ns->base.profiles, name, &info);
/* info set -> something unusual that we should report
* Currently this is only conflicting attachments, but other
* infos added in the future should also be logged by default
* and only excluded on a case-by-case basis
*/
if (info) {
/* Because perms is never used again after this audit
* we don't need to care about clobbering it
*/
perms.audit |= MAY_EXEC;
perms.allow |= MAY_EXEC;
/* Don't cause error if auditing fails */
(void) aa_audit_file(subj_cred, profile, &perms,
OP_EXEC, MAY_EXEC, name, target, new, cond->uid,
info, error);
}
if (new) { if (new) {
AA_DEBUG("unconfined attached to new label"); AA_DEBUG(DEBUG_DOMAIN, "unconfined attached to new label");
return new; return new;
} }
AA_DEBUG("unconfined exec no attachment"); AA_DEBUG(DEBUG_DOMAIN, "unconfined exec no attachment");
return aa_get_newest_label(&profile->label); return aa_get_newest_label(&profile->label);
} }
@ -678,9 +722,21 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
new = x_to_label(profile, bprm, name, perms.xindex, &target, new = x_to_label(profile, bprm, name, perms.xindex, &target,
&info); &info);
if (new && new->proxy == profile->label.proxy && info) { if (new && new->proxy == profile->label.proxy && info) {
/* Force audit on conflicting attachment fallback
* Because perms is never used again after this audit
* we don't need to care about clobbering it
*/
if (info == CONFLICTING_ATTACH_STR_IX
|| info == CONFLICTING_ATTACH_STR_UX)
perms.audit |= MAY_EXEC;
/* hack ix fallback - improve how this is detected */ /* hack ix fallback - improve how this is detected */
goto audit; goto audit;
} else if (!new) { } else if (!new) {
if (info) {
pr_warn_ratelimited(
"AppArmor: %s (from profile %s) audit info \"%s\" dropped on missing transition",
__func__, profile->base.hname, info);
}
info = "profile transition not found"; info = "profile transition not found";
/* remove MAY_EXEC to audit as failure or complaint */ /* remove MAY_EXEC to audit as failure or complaint */
perms.allow &= ~MAY_EXEC; perms.allow &= ~MAY_EXEC;
@ -739,8 +795,7 @@ static int profile_onexec(const struct cred *subj_cred,
char *buffer, struct path_cond *cond, char *buffer, struct path_cond *cond,
bool *secure_exec) bool *secure_exec)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
aa_state_t state = rules->file->start[AA_CLASS_FILE]; aa_state_t state = rules->file->start[AA_CLASS_FILE];
struct aa_perms perms = {}; struct aa_perms perms = {};
const char *xname = NULL, *info = "change_profile onexec"; const char *xname = NULL, *info = "change_profile onexec";
@ -755,7 +810,7 @@ static int profile_onexec(const struct cred *subj_cred,
/* change_profile on exec already granted */ /* change_profile on exec already granted */
/* /*
* NOTE: Domain transitions from unconfined are allowed * NOTE: Domain transitions from unconfined are allowed
* even when no_new_privs is set because this aways results * even when no_new_privs is set because this always results
* in a further reduction of permissions. * in a further reduction of permissions.
*/ */
return 0; return 0;
@ -766,7 +821,7 @@ static int profile_onexec(const struct cred *subj_cred,
if (error) { if (error) {
if (profile_unconfined(profile) || if (profile_unconfined(profile) ||
(profile->label.flags & FLAG_IX_ON_NAME_ERROR)) { (profile->label.flags & FLAG_IX_ON_NAME_ERROR)) {
AA_DEBUG("name lookup ix on error"); AA_DEBUG(DEBUG_DOMAIN, "name lookup ix on error");
error = 0; error = 0;
} }
xname = bprm->filename; xname = bprm->filename;
@ -926,7 +981,7 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
* *
* NOTE: Domain transitions from unconfined and to stacked * NOTE: Domain transitions from unconfined and to stacked
* subsets are allowed even when no_new_privs is set because this * subsets are allowed even when no_new_privs is set because this
* aways results in a further reduction of permissions. * always results in a further reduction of permissions.
*/ */
if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) &&
!unconfined(label) && !unconfined(label) &&
@ -1188,11 +1243,25 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp) if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp)
ctx->nnp = aa_get_label(label); ctx->nnp = aa_get_label(label);
/* return -EPERM when unconfined doesn't have children to avoid
* changing the traditional error code for unconfined.
*/
if (unconfined(label)) { if (unconfined(label)) {
struct label_it i;
bool empty = true;
rcu_read_lock();
label_for_each_in_ns(i, labels_ns(label), label, profile) {
empty &= list_empty(&profile->base.profiles);
}
rcu_read_unlock();
if (empty) {
info = "unconfined can not change_hat"; info = "unconfined can not change_hat";
error = -EPERM; error = -EPERM;
goto fail; goto fail;
} }
}
if (count) { if (count) {
new = change_hat(subj_cred, label, hats, count, flags); new = change_hat(subj_cred, label, hats, count, flags);
@ -1216,7 +1285,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
if (task_no_new_privs(current) && !unconfined(label) && if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_unconfined_subset(new, ctx->nnp)) { !aa_label_is_unconfined_subset(new, ctx->nnp)) {
/* not an apparmor denial per se, so don't log it */ /* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied"); AA_DEBUG(DEBUG_DOMAIN,
"no_new_privs - change_hat denied");
error = -EPERM; error = -EPERM;
goto out; goto out;
} }
@ -1237,7 +1307,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
if (task_no_new_privs(current) && !unconfined(label) && if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_unconfined_subset(previous, ctx->nnp)) { !aa_label_is_unconfined_subset(previous, ctx->nnp)) {
/* not an apparmor denial per se, so don't log it */ /* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied"); AA_DEBUG(DEBUG_DOMAIN,
"no_new_privs - change_hat denied");
error = -EPERM; error = -EPERM;
goto out; goto out;
} }
@ -1282,8 +1353,7 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
struct aa_label *target, bool stack, struct aa_label *target, bool stack,
u32 request, struct aa_perms *perms) u32 request, struct aa_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
const char *info = NULL; const char *info = NULL;
int error = 0; int error = 0;
@ -1343,7 +1413,7 @@ int aa_change_profile(const char *fqname, int flags)
if (!fqname || !*fqname) { if (!fqname || !*fqname) {
aa_put_label(label); aa_put_label(label);
AA_DEBUG("no profile name"); AA_DEBUG(DEBUG_DOMAIN, "no profile name");
return -EINVAL; return -EINVAL;
} }
@ -1462,7 +1532,8 @@ check:
if (task_no_new_privs(current) && !unconfined(label) && if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_unconfined_subset(new, ctx->nnp)) { !aa_label_is_unconfined_subset(new, ctx->nnp)) {
/* not an apparmor denial per se, so don't log it */ /* not an apparmor denial per se, so don't log it */
AA_DEBUG("no_new_privs - change_hat denied"); AA_DEBUG(DEBUG_DOMAIN,
"no_new_privs - change_hat denied");
error = -EPERM; error = -EPERM;
goto out; goto out;
} }

View file

@ -14,6 +14,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mount.h> #include <linux/mount.h>
#include "include/af_unix.h"
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/audit.h" #include "include/audit.h"
#include "include/cred.h" #include "include/cred.h"
@ -168,8 +169,9 @@ static int path_name(const char *op, const struct cred *subj_cred,
struct aa_perms default_perms = {}; struct aa_perms default_perms = {};
/** /**
* aa_lookup_fperms - convert dfa compressed perms to internal perms * aa_lookup_condperms - convert dfa compressed perms to internal perms
* @file_rules: the aa_policydb to lookup perms for (NOT NULL) * @subj_uid: uid to use for subject owner test
* @rules: the aa_policydb to lookup perms for (NOT NULL)
* @state: state in dfa * @state: state in dfa
* @cond: conditions to consider (NOT NULL) * @cond: conditions to consider (NOT NULL)
* *
@ -177,18 +179,21 @@ struct aa_perms default_perms = {};
* *
* Returns: a pointer to a file permission set * Returns: a pointer to a file permission set
*/ */
struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules, struct aa_perms *aa_lookup_condperms(kuid_t subj_uid, struct aa_policydb *rules,
aa_state_t state, struct path_cond *cond) aa_state_t state, struct path_cond *cond)
{ {
unsigned int index = ACCEPT_TABLE(file_rules->dfa)[state]; unsigned int index = ACCEPT_TABLE(rules->dfa)[state];
if (!(file_rules->perms)) if (!(rules->perms))
return &default_perms; return &default_perms;
if (uid_eq(current_fsuid(), cond->uid)) if ((ACCEPT_TABLE2(rules->dfa)[state] & ACCEPT_FLAG_OWNER)) {
return &(file_rules->perms[index]); if (uid_eq(subj_uid, cond->uid))
return &(rules->perms[index]);
return &(rules->perms[index + 1]);
}
return &(file_rules->perms[index + 1]); return &(rules->perms[index]);
} }
/** /**
@ -207,21 +212,22 @@ aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
{ {
aa_state_t state; aa_state_t state;
state = aa_dfa_match(file_rules->dfa, start, name); state = aa_dfa_match(file_rules->dfa, start, name);
*perms = *(aa_lookup_fperms(file_rules, state, cond)); *perms = *(aa_lookup_condperms(current_fsuid(), file_rules, state,
cond));
return state; return state;
} }
static int __aa_path_perm(const char *op, const struct cred *subj_cred, int __aa_path_perm(const char *op, const struct cred *subj_cred,
struct aa_profile *profile, const char *name, struct aa_profile *profile, const char *name,
u32 request, struct path_cond *cond, int flags, u32 request, struct path_cond *cond, int flags,
struct aa_perms *perms) struct aa_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
int e = 0; int e = 0;
if (profile_unconfined(profile)) if (profile_unconfined(profile) ||
((flags & PATH_SOCK_COND) && !RULE_MEDIATES_v9NET(rules)))
return 0; return 0;
aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE], aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
name, cond, perms); name, cond, perms);
@ -316,8 +322,7 @@ static int profile_path_link(const struct cred *subj_cred,
const struct path *target, char *buffer2, const struct path *target, char *buffer2,
struct path_cond *cond) struct path_cond *cond)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
const char *lname, *tname = NULL; const char *lname, *tname = NULL;
struct aa_perms lperms = {}, perms; struct aa_perms lperms = {}, perms;
const char *info = NULL; const char *info = NULL;
@ -423,9 +428,11 @@ int aa_path_link(const struct cred *subj_cred,
{ {
struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
struct inode *inode = d_backing_inode(old_dentry);
vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(target.mnt), inode);
struct path_cond cond = { struct path_cond cond = {
d_backing_inode(old_dentry)->i_uid, .uid = vfsuid_into_kuid(vfsuid),
d_backing_inode(old_dentry)->i_mode .mode = inode->i_mode,
}; };
char *buffer = NULL, *buffer2 = NULL; char *buffer = NULL, *buffer2 = NULL;
struct aa_profile *profile; struct aa_profile *profile;
@ -534,22 +541,19 @@ static int __file_sock_perm(const char *op, const struct cred *subj_cred,
struct aa_label *flabel, struct file *file, struct aa_label *flabel, struct file *file,
u32 request, u32 denied) u32 request, u32 denied)
{ {
struct socket *sock = (struct socket *) file->private_data;
int error; int error;
AA_BUG(!sock);
/* revalidation due to label out of date. No revocation at this time */ /* revalidation due to label out of date. No revocation at this time */
if (!denied && aa_label_is_subset(flabel, label)) if (!denied && aa_label_is_subset(flabel, label))
return 0; return 0;
/* TODO: improve to skip profiles cached in flabel */ /* 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) { if (denied) {
/* TODO: improve to skip profiles checked above */ /* TODO: improve to skip profiles checked above */
/* check every profile in file label to is cached */ /* check every profile in file label to is cached */
last_error(error, aa_sock_file_perm(subj_cred, flabel, op, last_error(error, aa_sock_file_perm(subj_cred, flabel, op,
request, sock)); request, file));
} }
if (!error) if (!error)
update_file_ctx(file_ctx(file), label, request); update_file_ctx(file_ctx(file), label, request);
@ -557,6 +561,35 @@ static int __file_sock_perm(const char *op, const struct cred *subj_cred,
return error; return error;
} }
/* for now separate fn to indicate semantics of the check */
static bool __file_is_delegated(struct aa_label *obj_label)
{
return unconfined(obj_label);
}
static bool __unix_needs_revalidation(struct file *file, struct aa_label *label,
u32 request)
{
struct socket *sock = (struct socket *) file->private_data;
lockdep_assert_in_rcu_read_lock();
if (!S_ISSOCK(file_inode(file)->i_mode))
return false;
if (request & NET_PEER_MASK)
return false;
if (sock->sk->sk_family == PF_UNIX) {
struct aa_sk_ctx *ctx = aa_sock(sock->sk);
if (rcu_access_pointer(ctx->peer) !=
rcu_access_pointer(ctx->peer_lastupdate))
return true;
return !__aa_subj_label_is_cached(rcu_dereference(ctx->label),
label);
}
return false;
}
/** /**
* aa_file_perm - do permission revalidation check & audit for @file * aa_file_perm - do permission revalidation check & audit for @file
* @op: operation being checked * @op: operation being checked
@ -594,15 +627,16 @@ int aa_file_perm(const char *op, const struct cred *subj_cred,
* delegation from unconfined tasks * delegation from unconfined tasks
*/ */
denied = request & ~fctx->allow; denied = request & ~fctx->allow;
if (unconfined(label) || unconfined(flabel) || if (unconfined(label) || __file_is_delegated(flabel) ||
(!denied && aa_label_is_subset(flabel, label))) { __unix_needs_revalidation(file, label, request) ||
(!denied && __aa_subj_label_is_cached(label, flabel))) {
rcu_read_unlock(); rcu_read_unlock();
goto done; goto done;
} }
/* slow path - revalidate access */
flabel = aa_get_newest_label(flabel); flabel = aa_get_newest_label(flabel);
rcu_read_unlock(); rcu_read_unlock();
/* TODO: label cross check */
if (path_mediated_fs(file->f_path.dentry)) if (path_mediated_fs(file->f_path.dentry))
error = __file_path_perm(op, subj_cred, label, flabel, file, error = __file_path_perm(op, subj_cred, label, flabel, file,

View file

@ -0,0 +1,55 @@
/* 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)
struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen);
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_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 */

View file

@ -28,6 +28,7 @@
#define AA_CLASS_SIGNAL 10 #define AA_CLASS_SIGNAL 10
#define AA_CLASS_XMATCH 11 #define AA_CLASS_XMATCH 11
#define AA_CLASS_NET 14 #define AA_CLASS_NET 14
#define AA_CLASS_NETV9 15
#define AA_CLASS_LABEL 16 #define AA_CLASS_LABEL 16
#define AA_CLASS_POSIX_MQUEUE 17 #define AA_CLASS_POSIX_MQUEUE 17
#define AA_CLASS_MODULE 19 #define AA_CLASS_MODULE 19
@ -38,12 +39,13 @@
#define AA_CLASS_X 31 #define AA_CLASS_X 31
#define AA_CLASS_DBUS 32 #define AA_CLASS_DBUS 32
/* NOTE: if AA_CLASS_LAST > 63 need to update label->mediates */
#define AA_CLASS_LAST AA_CLASS_DBUS #define AA_CLASS_LAST AA_CLASS_DBUS
/* Control parameters settable through module/boot flags */ /* Control parameters settable through module/boot flags */
extern enum audit_mode aa_g_audit; extern enum audit_mode aa_g_audit;
extern bool aa_g_audit_header; extern bool aa_g_audit_header;
extern bool aa_g_debug; extern int aa_g_debug;
extern bool aa_g_hash_policy; extern bool aa_g_hash_policy;
extern bool aa_g_export_binary; extern bool aa_g_export_binary;
extern int aa_g_rawdata_compression_level; extern int aa_g_rawdata_compression_level;

View file

@ -138,9 +138,12 @@ struct apparmor_audit_data {
}; };
struct { struct {
int type, protocol; int type, protocol;
struct sock *peer_sk;
void *addr; void *addr;
int addrlen; int addrlen;
struct {
void *addr;
int addrlen;
} peer;
} net; } net;
}; };
}; };

View file

@ -36,6 +36,7 @@ struct aa_caps {
extern struct aa_sfs_entry aa_sfs_entry_caps[]; extern struct aa_sfs_entry aa_sfs_entry_caps[];
kernel_cap_t aa_profile_capget(struct aa_profile *profile);
int aa_capable(const struct cred *subj_cred, struct aa_label *label, int aa_capable(const struct cred *subj_cred, struct aa_label *label,
int cap, unsigned int opts); int cap, unsigned int opts);

View file

@ -114,10 +114,22 @@ static inline struct aa_label *aa_get_current_label(void)
return aa_get_label(l); return aa_get_label(l);
} }
#define __end_current_label_crit_section(X) end_current_label_crit_section(X) /**
* __end_current_label_crit_section - end crit section begun with __begin_...
* @label: label obtained from __begin_current_label_crit_section
* @needput: output: bool set by __begin_current_label_crit_section
*
* Returns: label to use for this crit section
*/
static inline void __end_current_label_crit_section(struct aa_label *label,
bool needput)
{
if (unlikely(needput))
aa_put_label(label);
}
/** /**
* end_label_crit_section - put a reference found with begin_current_label.. * end_current_label_crit_section - put a reference found with begin_current_label..
* @label: label reference to put * @label: label reference to put
* *
* Should only be used with a reference obtained with * Should only be used with a reference obtained with
@ -132,6 +144,7 @@ static inline void end_current_label_crit_section(struct aa_label *label)
/** /**
* __begin_current_label_crit_section - current's confining label * __begin_current_label_crit_section - current's confining label
* @needput: store whether the label needs to be put when ending crit section
* *
* Returns: up to date confining label or the ns unconfined label (NOT NULL) * Returns: up to date confining label or the ns unconfined label (NOT NULL)
* *
@ -142,13 +155,16 @@ static inline void end_current_label_crit_section(struct aa_label *label)
* critical section between __begin_current_label_crit_section() .. * critical section between __begin_current_label_crit_section() ..
* __end_current_label_crit_section() * __end_current_label_crit_section()
*/ */
static inline struct aa_label *__begin_current_label_crit_section(void) static inline struct aa_label *__begin_current_label_crit_section(bool *needput)
{ {
struct aa_label *label = aa_current_raw_label(); struct aa_label *label = aa_current_raw_label();
if (label_is_stale(label)) if (label_is_stale(label)) {
label = aa_get_newest_label(label); *needput = true;
return aa_get_newest_label(label);
}
*needput = false;
return label; return label;
} }
@ -184,10 +200,11 @@ static inline struct aa_ns *aa_get_current_ns(void)
{ {
struct aa_label *label; struct aa_label *label;
struct aa_ns *ns; struct aa_ns *ns;
bool needput;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
ns = aa_get_ns(labels_ns(label)); ns = aa_get_ns(labels_ns(label));
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return ns; return ns;
} }

View file

@ -77,12 +77,17 @@ int aa_audit_file(const struct cred *cred,
const char *target, struct aa_label *tlabel, kuid_t ouid, const char *target, struct aa_label *tlabel, kuid_t ouid,
const char *info, int error); const char *info, int error);
struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules, struct aa_perms *aa_lookup_condperms(kuid_t subj_uid,
struct aa_policydb *file_rules,
aa_state_t state, struct path_cond *cond); aa_state_t state, struct path_cond *cond);
aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start, aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
const char *name, struct path_cond *cond, const char *name, struct path_cond *cond,
struct aa_perms *perms); 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, int aa_path_perm(const char *op, const struct cred *subj_cred,
struct aa_label *label, const struct path *path, struct aa_label *label, const struct path *path,
int flags, u32 request, struct path_cond *cond); int flags, u32 request, struct path_cond *cond);
@ -99,7 +104,7 @@ void aa_inherit_files(const struct cred *cred, struct files_struct *files);
/** /**
* aa_map_file_perms - map file flags to AppArmor permissions * aa_map_file_to_perms - map file flags to AppArmor permissions
* @file: open file to map flags to AppArmor permissions * @file: open file to map flags to AppArmor permissions
* *
* Returns: apparmor permission set for the file * Returns: apparmor permission set for the file

View file

@ -13,6 +13,9 @@
#include <linux/sched.h> #include <linux/sched.h>
#define SIGUNKNOWN 0
#define MAXMAPPED_SIG 35
int aa_may_signal(const struct cred *subj_cred, struct aa_label *sender, int aa_may_signal(const struct cred *subj_cred, struct aa_label *sender,
const struct cred *target_cred, struct aa_label *target, const struct cred *target_cred, struct aa_label *target,
int sig); int sig);

View file

@ -19,6 +19,7 @@
#include "lib.h" #include "lib.h"
struct aa_ns; struct aa_ns;
struct aa_ruleset;
#define LOCAL_VEC_ENTRIES 8 #define LOCAL_VEC_ENTRIES 8
#define DEFINE_VEC(T, V) \ #define DEFINE_VEC(T, V) \
@ -109,7 +110,7 @@ struct label_it {
int i, j; int i, j;
}; };
/* struct aa_label - lazy labeling struct /* struct aa_label_base - base info of label
* @count: ref count of active users * @count: ref count of active users
* @node: rbtree position * @node: rbtree position
* @rcu: rcu callback struct * @rcu: rcu callback struct
@ -118,7 +119,10 @@ struct label_it {
* @flags: stale and other flags - values may change under label set lock * @flags: stale and other flags - values may change under label set lock
* @secid: secid that references this label * @secid: secid that references this label
* @size: number of entries in @ent[] * @size: number of entries in @ent[]
* @ent: set of profiles for label, actual size determined by @size * @mediates: bitmask for label_mediates
* profile: label vec when embedded in a profile FLAG_PROFILE is set
* rules: variable length rules in a profile FLAG_PROFILE is set
* vec: vector of profiles comprising the compound label
*/ */
struct aa_label { struct aa_label {
struct kref count; struct kref count;
@ -129,7 +133,18 @@ struct aa_label {
long flags; long flags;
u32 secid; u32 secid;
int size; int size;
struct aa_profile *vec[]; u64 mediates;
union {
struct {
/* only used is the label is a profile, size of
* rules[] is determined by the profile
* profile[1] is poison or null as guard
*/
struct aa_profile *profile[2];
DECLARE_FLEX_ARRAY(struct aa_ruleset *, rules);
};
DECLARE_FLEX_ARRAY(struct aa_profile *, vec);
};
}; };
#define last_error(E, FN) \ #define last_error(E, FN) \
@ -231,20 +246,17 @@ int aa_label_next_confined(struct aa_label *l, int i);
#define fn_for_each_not_in_set(L1, L2, P, FN) \ #define fn_for_each_not_in_set(L1, L2, P, FN) \
fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set) fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set)
#define LABEL_MEDIATES(L, C) \ static inline bool label_mediates(struct aa_label *L, unsigned char C)
({ \ {
struct aa_profile *profile; \ return (L)->mediates & (((u64) 1) << (C));
struct label_it i; \ }
int ret = 0; \
label_for_each(i, (L), profile) { \
if (RULE_MEDIATES(&profile->rules, (C))) { \
ret = 1; \
break; \
} \
} \
ret; \
})
static inline bool label_mediates_safe(struct aa_label *L, unsigned char C)
{
if (C > AA_CLASS_LAST)
return false;
return label_mediates(L, C);
}
void aa_labelset_destroy(struct aa_labelset *ls); void aa_labelset_destroy(struct aa_labelset *ls);
void aa_labelset_init(struct aa_labelset *ls); void aa_labelset_init(struct aa_labelset *ls);
@ -417,6 +429,13 @@ static inline void aa_put_label(struct aa_label *l)
kref_put(&l->count, aa_label_kref); kref_put(&l->count, aa_label_kref);
} }
/* wrapper fn to indicate semantics of the check */
static inline bool __aa_subj_label_is_cached(struct aa_label *subj_label,
struct aa_label *obj_label)
{
return aa_label_is_subset(obj_label, subj_label);
}
struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp); struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp);
void aa_proxy_kref(struct kref *kref); void aa_proxy_kref(struct kref *kref);

View file

@ -18,22 +18,34 @@
extern struct aa_dfa *stacksplitdfa; extern struct aa_dfa *stacksplitdfa;
/*
* DEBUG remains global (no per profile flag) since it is mostly used in sysctl
* which is not related to profile accesses.
*/
#define DEBUG_ON (aa_g_debug)
/* /*
* split individual debug cases out in preparation for finer grained * split individual debug cases out in preparation for finer grained
* debug controls in the future. * debug controls in the future.
*/ */
#define AA_DEBUG_LABEL DEBUG_ON
#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args)
#define AA_DEBUG(fmt, args...) \
#define DEBUG_NONE 0
#define DEBUG_LABEL_ABS_ROOT 1
#define DEBUG_LABEL 2
#define DEBUG_DOMAIN 4
#define DEBUG_POLICY 8
#define DEBUG_INTERFACE 0x10
#define DEBUG_ALL 0x1f /* update if new DEBUG_X added */
#define DEBUG_PARSE_ERROR (-1)
#define DEBUG_ON (aa_g_debug != DEBUG_NONE)
#define DEBUG_ABS_ROOT (aa_g_debug & DEBUG_LABEL_ABS_ROOT)
#define AA_DEBUG(opt, fmt, args...) \
do { \ do { \
if (DEBUG_ON) \ if (aa_g_debug & opt) \
pr_debug_ratelimited("AppArmor: " fmt, ##args); \ pr_warn_ratelimited("%s: " fmt, __func__, ##args); \
} while (0)
#define AA_DEBUG_LABEL(LAB, X, fmt, args...) \
do { \
if ((LAB)->flags & FLAG_DEBUG1) \
AA_DEBUG(X, fmt, args); \
} while (0) } while (0)
#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X)
@ -48,9 +60,16 @@ extern struct aa_dfa *stacksplitdfa;
#define AA_BUG_FMT(X, fmt, args...) \ #define AA_BUG_FMT(X, fmt, args...) \
WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args)
#else #else
#define AA_BUG_FMT(X, fmt, args...) no_printk(fmt, ##args) #define AA_BUG_FMT(X, fmt, args...) \
do { \
BUILD_BUG_ON_INVALID(X); \
no_printk(fmt, ##args); \
} while (0)
#endif #endif
int aa_parse_debug_params(const char *str);
int aa_print_debug_params(char *buffer);
#define AA_ERROR(fmt, args...) \ #define AA_ERROR(fmt, args...) \
pr_err_ratelimited("AppArmor: " fmt, ##args) pr_err_ratelimited("AppArmor: " fmt, ##args)
@ -106,6 +125,7 @@ struct aa_str_table {
}; };
void aa_free_str_table(struct aa_str_table *table); void aa_free_str_table(struct aa_str_table *table);
bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp);
struct counted_str { struct counted_str {
struct kref count; struct kref count;
@ -151,7 +171,7 @@ struct aa_policy {
/** /**
* basename - find the last component of an hname * basename - find the last component of an hname
* @name: hname to find the base profile name component of (NOT NULL) * @hname: hname to find the base profile name component of (NOT NULL)
* *
* Returns: the tail (base profile name) name component of an hname * Returns: the tail (base profile name) name component of an hname
*/ */
@ -281,7 +301,7 @@ __do_cleanup: \
} \ } \
__done: \ __done: \
if (!__new_) \ if (!__new_) \
AA_DEBUG("label build failed\n"); \ AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \
(__new_); \ (__new_); \
}) })

View file

@ -17,7 +17,7 @@
#define DFA_START 1 #define DFA_START 1
/** /*
* The format used for transition tables is based on the GNU flex table * The format used for transition tables is based on the GNU flex table
* file format (--tables-file option; see Table File Format in the flex * file format (--tables-file option; see Table File Format in the flex
* info pages and the flex sources for documentation). The magic number * info pages and the flex sources for documentation). The magic number
@ -137,17 +137,15 @@ aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
void aa_dfa_free_kref(struct kref *kref); void aa_dfa_free_kref(struct kref *kref);
#define WB_HISTORY_SIZE 24 /* This needs to be a power of 2 */
#define WB_HISTORY_SIZE 32
struct match_workbuf { struct match_workbuf {
unsigned int count;
unsigned int pos; unsigned int pos;
unsigned int len; unsigned int len;
unsigned int size; /* power of 2, same as history size */ aa_state_t history[WB_HISTORY_SIZE];
unsigned int history[WB_HISTORY_SIZE];
}; };
#define DEFINE_MATCH_WB(N) \ #define DEFINE_MATCH_WB(N) \
struct match_workbuf N = { \ struct match_workbuf N = { \
.count = 0, \
.pos = 0, \ .pos = 0, \
.len = 0, \ .len = 0, \
} }

View file

@ -47,8 +47,9 @@
#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ #define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \
AA_MAY_ACCEPT) AA_MAY_ACCEPT)
struct aa_sk_ctx { struct aa_sk_ctx {
struct aa_label *label; struct aa_label __rcu *label;
struct aa_label *peer; struct aa_label __rcu *peer;
struct aa_label __rcu *peer_lastupdate; /* ptr cmp only, no deref */
}; };
static inline struct aa_sk_ctx *aa_sock(const struct sock *sk) static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
@ -56,7 +57,7 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
return sk->sk_security + apparmor_blob_sizes.lbs_sock; 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), \ struct lsm_network_audit NAME ## _net = { .sk = (SK), \
.family = (F)}; \ .family = (F)}; \
DEFINE_AUDIT_DATA(NAME, \ DEFINE_AUDIT_DATA(NAME, \
@ -65,24 +66,15 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
AA_CLASS_NET, \ AA_CLASS_NET, \
OP); \ OP); \
NAME.common.u.net = &(NAME ## _net); \ NAME.common.u.net = &(NAME ## _net); \
NAME.subj_cred = (CRED); \
NAME.net.type = (T); \ NAME.net.type = (T); \
NAME.net.protocol = (P) NAME.net.protocol = (P)
#define DEFINE_AUDIT_SK(NAME, OP, SK) \ #define DEFINE_AUDIT_SK(NAME, OP, CRED, SK) \
DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ DEFINE_AUDIT_NET(NAME, OP, CRED, SK, (SK)->sk_family, (SK)->sk_type, \
(SK)->sk_protocol) (SK)->sk_protocol)
#define af_select(FAMILY, FN, DEF_FN) \
({ \
int __e; \
switch ((FAMILY)) { \
default: \
__e = DEF_FN; \
} \
__e; \
})
struct aa_secmark { struct aa_secmark {
u8 audit; u8 audit;
u8 deny; u8 deny;
@ -91,11 +83,19 @@ struct aa_secmark {
}; };
extern struct aa_sfs_entry aa_sfs_entry_network[]; extern struct aa_sfs_entry aa_sfs_entry_network[];
extern struct aa_sfs_entry aa_sfs_entry_networkv9[];
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 af, int type, int protocol,
struct aa_perms **p, const char **info);
void audit_net_cb(struct audit_buffer *ab, void *va); void audit_net_cb(struct audit_buffer *ab, void *va);
int aa_profile_af_perm(struct aa_profile *profile, int aa_profile_af_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad, struct apparmor_audit_data *ad,
u32 request, u16 family, int type); u32 request, u16 family, int type, int protocol);
int aa_af_perm(const struct cred *subj_cred, struct aa_label *label, int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, u16 family, const char *op, u32 request, u16 family,
int type, int protocol); int type, int protocol);
@ -105,13 +105,13 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile,
struct sock *sk) struct sock *sk)
{ {
return aa_profile_af_perm(profile, ad, request, sk->sk_family, return aa_profile_af_perm(profile, ad, request, sk->sk_family,
sk->sk_type); sk->sk_type, sk->sk_protocol);
} }
int aa_sk_perm(const char *op, u32 request, struct sock *sk); 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, int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, const char *op, u32 request,
struct socket *sock); struct file *file);
int apparmor_secmark_check(struct aa_label *label, char *op, u32 request, int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, const struct sock *sk); u32 secid, const struct sock *sk);

View file

@ -13,6 +13,7 @@
enum path_flags { enum path_flags {
PATH_IS_DIR = 0x1, /* path is a directory */ PATH_IS_DIR = 0x1, /* path is a directory */
PATH_SOCK_COND = 0x2,
PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */
PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */
PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */

View file

@ -101,8 +101,8 @@ extern struct aa_perms allperms;
/** /**
* aa_perms_accum_raw - accumulate perms with out masking off overlapping perms * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
* @accum - perms struct to accumulate into * @accum: perms struct to accumulate into
* @addend - perms struct to add to @accum * @addend: perms struct to add to @accum
*/ */
static inline void aa_perms_accum_raw(struct aa_perms *accum, static inline void aa_perms_accum_raw(struct aa_perms *accum,
struct aa_perms *addend) struct aa_perms *addend)
@ -128,8 +128,8 @@ static inline void aa_perms_accum_raw(struct aa_perms *accum,
/** /**
* aa_perms_accum - accumulate perms, masking off overlapping perms * aa_perms_accum - accumulate perms, masking off overlapping perms
* @accum - perms struct to accumulate into * @accum: perms struct to accumulate into
* @addend - perms struct to add to @accum * @addend: perms struct to add to @accum
*/ */
static inline void aa_perms_accum(struct aa_perms *accum, static inline void aa_perms_accum(struct aa_perms *accum,
struct aa_perms *addend) struct aa_perms *addend)

View file

@ -59,6 +59,11 @@ extern const char *const aa_profile_mode_names[];
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
/* flags in the dfa accept2 table */
enum dfa_accept_flags {
ACCEPT_FLAG_OWNER = 1,
};
/* /*
* FIXME: currently need a clean way to replace and remove profiles as a * FIXME: currently need a clean way to replace and remove profiles as a
* set. It should be done at the namespace level. * set. It should be done at the namespace level.
@ -124,6 +129,7 @@ static inline void aa_put_pdb(struct aa_policydb *pdb)
kref_put(&pdb->count, aa_pdb_free_kref); kref_put(&pdb->count, aa_pdb_free_kref);
} }
/* lookup perm that doesn't have and object conditional */
static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy, static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy,
aa_state_t state) aa_state_t state)
{ {
@ -135,7 +141,6 @@ static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy,
return &(policy->perms[index]); return &(policy->perms[index]);
} }
/* struct aa_data - generic data structure /* struct aa_data - generic data structure
* key: name for retrieving this data * key: name for retrieving this data
* size: size of data in bytes * size: size of data in bytes
@ -160,8 +165,6 @@ struct aa_data {
* @secmark: secmark label match info * @secmark: secmark label match info
*/ */
struct aa_ruleset { struct aa_ruleset {
struct list_head list;
int size; int size;
/* TODO: merge policy and file */ /* TODO: merge policy and file */
@ -175,6 +178,7 @@ struct aa_ruleset {
struct aa_secmark *secmark; struct aa_secmark *secmark;
}; };
/* struct aa_attachment - data and rules for a profiles attachment /* struct aa_attachment - data and rules for a profiles attachment
* @list: * @list:
* @xmatch_str: human readable attachment string * @xmatch_str: human readable attachment string
@ -193,7 +197,6 @@ struct aa_attachment {
/* struct aa_profile - basic confinement data /* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...) * @base - base components of the profile (name, refcount, lists, lock ...)
* @label - label this profile is an extension of
* @parent: parent of profile * @parent: parent of profile
* @ns: namespace the profile is in * @ns: namespace the profile is in
* @rename: optional profile name that this profile renamed * @rename: optional profile name that this profile renamed
@ -201,13 +204,20 @@ struct aa_attachment {
* @audit: the auditing mode of the profile * @audit: the auditing mode of the profile
* @mode: the enforcement mode of the profile * @mode: the enforcement mode of the profile
* @path_flags: flags controlling path generation behavior * @path_flags: flags controlling path generation behavior
* @signal: the signal that should be used when kill is used
* @disconnected: what to prepend if attach_disconnected is specified * @disconnected: what to prepend if attach_disconnected is specified
* @attach: attachment rules for the profile * @attach: attachment rules for the profile
* @rules: rules to be enforced * @rules: rules to be enforced
* *
* learning_cache: the accesses learned in complain mode
* raw_data: rawdata of the loaded profile policy
* hash: cryptographic hash of the profile
* @dents: dentries for the profiles file entries in apparmorfs * @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs * @dirname: name of the profile dir in apparmorfs
* @dents: set of dentries associated with the profile
* @data: hashtable for free-form policy aa_data * @data: hashtable for free-form policy aa_data
* @label - label this profile is an extension of
* @rules - label with the rule vec on its end
* *
* The AppArmor profile contains the basic confinement data. Each profile * The AppArmor profile contains the basic confinement data. Each profile
* has a name, and exists in a namespace. The @name and @exec_match are * has a name, and exists in a namespace. The @name and @exec_match are
@ -231,16 +241,19 @@ struct aa_profile {
enum audit_mode audit; enum audit_mode audit;
long mode; long mode;
u32 path_flags; u32 path_flags;
int signal;
const char *disconnected; const char *disconnected;
struct aa_attachment attach; struct aa_attachment attach;
struct list_head rules;
struct aa_loaddata *rawdata; struct aa_loaddata *rawdata;
unsigned char *hash; unsigned char *hash;
char *dirname; char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF]; struct dentry *dents[AAFS_PROF_SIZEOF];
struct rhashtable *data; struct rhashtable *data;
int n_rules;
/* special - variable length must be last entry in profile */
struct aa_label label; struct aa_label label;
}; };
@ -298,24 +311,38 @@ static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules,
rules->policy->start[0], &class, 1); 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_v9NET(struct aa_ruleset *rules)
{ {
aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET); return RULE_MEDIATES(rules, AA_CLASS_NETV9);
__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);
} }
static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head, static inline aa_state_t RULE_MEDIATES_NET(struct aa_ruleset *rules)
{
/* can not use RULE_MEDIATE_v9AF here, because AF match fail
* can not be distiguished from class match fail, and we only
* fallback to checking older class on class match failure
*/
aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NETV9);
/* fallback and check v7/8 if v9 is NOT mediated */
if (!state)
state = RULE_MEDIATES(rules, AA_CLASS_NET);
return state;
}
void aa_compute_profile_mediates(struct aa_profile *profile);
static inline bool profile_mediates(struct aa_profile *profile,
unsigned char class) unsigned char class)
{ {
struct aa_ruleset *rule; return label_mediates(&profile->label, class);
}
/* TODO: change to list walk */ static inline bool profile_mediates_safe(struct aa_profile *profile,
rule = list_first_entry(head, typeof(*rule), list); unsigned char class)
return RULE_MEDIATES(rule, class); {
return label_mediates_safe(&profile->label, class);
} }
/** /**

View file

@ -1,9 +1,5 @@
#include <linux/signal.h> #include <linux/signal.h>
#include "signal.h"
#define SIGUNKNOWN 0
#define MAXMAPPED_SIG 35
#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1)
#define SIGRT_BASE 128
/* provide a mapping of arch signal to internal signal # for mediation /* provide a mapping of arch signal to internal signal # for mediation
* those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO

View file

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor ipc mediation function definitions.
*
* Copyright 2023 Canonical Ltd.
*/
#ifndef __AA_SIGNAL_H
#define __AA_SIGNAL_H
#define SIGUNKNOWN 0
#define MAXMAPPED_SIG 35
#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1)
#define SIGRT_BASE 128
#endif /* __AA_SIGNAL_H */

View file

@ -80,21 +80,20 @@ static int profile_signal_perm(const struct cred *cred,
struct aa_label *peer, u32 request, struct aa_label *peer, u32 request,
struct apparmor_audit_data *ad) struct apparmor_audit_data *ad)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_perms perms; struct aa_perms perms;
aa_state_t state; aa_state_t state;
if (profile_unconfined(profile) || if (profile_unconfined(profile))
!ANY_RULE_MEDIATES(&profile->rules, AA_CLASS_SIGNAL))
return 0; return 0;
ad->subj_cred = cred; ad->subj_cred = cred;
ad->peer = peer; ad->peer = peer;
/* TODO: secondary cache check <profile, profile, perm> */ /* TODO: secondary cache check <profile, profile, perm> */
state = aa_dfa_next(rules->policy->dfa, state = RULE_MEDIATES(rules, AA_CLASS_SIGNAL);
rules->policy->start[AA_CLASS_SIGNAL], if (!state)
ad->signal); return 0;
state = aa_dfa_next(rules->policy->dfa, state, ad->signal);
aa_label_match(profile, rules, peer, state, false, request, &perms); aa_label_match(profile, rules, peer, state, false, request, &perms);
aa_apply_modes_to_perms(profile, &perms); aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, ad, audit_signal_cb); return aa_check_perms(profile, &perms, request, ad, audit_signal_cb);

View file

@ -198,21 +198,25 @@ static bool vec_is_stale(struct aa_profile **vec, int n)
return false; return false;
} }
static long accum_vec_flags(struct aa_profile **vec, int n) static void accum_label_info(struct aa_label *new)
{ {
long u = FLAG_UNCONFINED; long u = FLAG_UNCONFINED;
int i; int i;
AA_BUG(!vec); AA_BUG(!new);
for (i = 0; i < n; i++) { /* size == 1 is a profile and flags must be set as part of creation */
u |= vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 | if (new->size == 1)
return;
for (i = 0; i < new->size; i++) {
u |= new->vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 |
FLAG_STALE); FLAG_STALE);
if (!(u & vec[i]->label.flags & FLAG_UNCONFINED)) if (!(u & new->vec[i]->label.flags & FLAG_UNCONFINED))
u &= ~FLAG_UNCONFINED; u &= ~FLAG_UNCONFINED;
new->mediates |= new->vec[i]->label.mediates;
} }
new->flags |= u;
return u;
} }
static int sort_cmp(const void *a, const void *b) static int sort_cmp(const void *a, const void *b)
@ -431,7 +435,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
/* + 1 for null terminator entry on vec */ /* + 1 for null terminator entry on vec */
new = kzalloc(struct_size(new, vec, size + 1), gfp); new = kzalloc(struct_size(new, vec, size + 1), gfp);
AA_DEBUG("%s (%p)\n", __func__, new); AA_DEBUG(DEBUG_LABEL, "%s (%p)\n", __func__, new);
if (!new) if (!new)
goto fail; goto fail;
@ -645,6 +649,7 @@ static bool __label_replace(struct aa_label *old, struct aa_label *new)
rb_replace_node(&old->node, &new->node, &ls->root); rb_replace_node(&old->node, &new->node, &ls->root);
old->flags &= ~FLAG_IN_TREE; old->flags &= ~FLAG_IN_TREE;
new->flags |= FLAG_IN_TREE; new->flags |= FLAG_IN_TREE;
accum_label_info(new);
return true; return true;
} }
@ -705,6 +710,7 @@ static struct aa_label *__label_insert(struct aa_labelset *ls,
rb_link_node(&label->node, parent, new); rb_link_node(&label->node, parent, new);
rb_insert_color(&label->node, &ls->root); rb_insert_color(&label->node, &ls->root);
label->flags |= FLAG_IN_TREE; label->flags |= FLAG_IN_TREE;
accum_label_info(label);
return aa_get_label(label); return aa_get_label(label);
} }
@ -1085,7 +1091,6 @@ static struct aa_label *label_merge_insert(struct aa_label *new,
else if (k == b->size) else if (k == b->size)
return aa_get_label(b); return aa_get_label(b);
} }
new->flags |= accum_vec_flags(new->vec, new->size);
ls = labels_set(new); ls = labels_set(new);
write_lock_irqsave(&ls->lock, flags); write_lock_irqsave(&ls->lock, flags);
label = __label_insert(labels_set(new), new, false); label = __label_insert(labels_set(new), new, false);
@ -1456,7 +1461,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
/* /*
* cached label name is present and visible * cached label name is present and visible
* @label->hname only exists if label is namespace hierachical * @label->hname only exists if label is namespace hierarchical
*/ */
static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label, static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
int flags) int flags)
@ -1617,7 +1622,7 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
AA_BUG(!str && size != 0); AA_BUG(!str && size != 0);
AA_BUG(!label); AA_BUG(!label);
if (AA_DEBUG_LABEL && (flags & FLAG_ABS_ROOT)) { if (DEBUG_ABS_ROOT && (flags & FLAG_ABS_ROOT)) {
ns = root_ns; ns = root_ns;
len = snprintf(str, size, "_"); len = snprintf(str, size, "_");
update_for_len(total, len, size, str); update_for_len(total, len, size, str);
@ -1731,7 +1736,7 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
display_mode(ns, label, flags)) { display_mode(ns, label, flags)) {
len = aa_label_asxprint(&name, ns, label, flags, gfp); len = aa_label_asxprint(&name, ns, label, flags, gfp);
if (len < 0) { if (len < 0) {
AA_DEBUG("label print error"); AA_DEBUG(DEBUG_LABEL, "label print error");
return; return;
} }
str = name; str = name;
@ -1759,7 +1764,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
len = aa_label_asxprint(&str, ns, label, flags, gfp); len = aa_label_asxprint(&str, ns, label, flags, gfp);
if (len < 0) { if (len < 0) {
AA_DEBUG("label print error"); AA_DEBUG(DEBUG_LABEL, "label print error");
return; return;
} }
seq_puts(f, str); seq_puts(f, str);
@ -1782,7 +1787,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
len = aa_label_asxprint(&str, ns, label, flags, gfp); len = aa_label_asxprint(&str, ns, label, flags, gfp);
if (len < 0) { if (len < 0) {
AA_DEBUG("label print error"); AA_DEBUG(DEBUG_LABEL, "label print error");
return; return;
} }
pr_info("%s", str); pr_info("%s", str);
@ -1865,7 +1870,7 @@ struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
AA_BUG(!str); AA_BUG(!str);
str = skipn_spaces(str, n); str = skipn_spaces(str, n);
if (str == NULL || (AA_DEBUG_LABEL && *str == '_' && if (str == NULL || (DEBUG_ABS_ROOT && *str == '_' &&
base != &root_ns->unconfined->label)) base != &root_ns->unconfined->label))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);

View file

@ -25,6 +25,120 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
.quiet = ALL_PERMS_MASK, .quiet = ALL_PERMS_MASK,
.hide = ALL_PERMS_MASK }; .hide = ALL_PERMS_MASK };
struct val_table_ent {
const char *str;
int value;
};
static struct val_table_ent debug_values_table[] = {
{ "N", DEBUG_NONE },
{ "none", DEBUG_NONE },
{ "n", DEBUG_NONE },
{ "0", DEBUG_NONE },
{ "all", DEBUG_ALL },
{ "Y", DEBUG_ALL },
{ "y", DEBUG_ALL },
{ "1", DEBUG_ALL },
{ "abs_root", DEBUG_LABEL_ABS_ROOT },
{ "label", DEBUG_LABEL },
{ "domain", DEBUG_DOMAIN },
{ "policy", DEBUG_POLICY },
{ "interface", DEBUG_INTERFACE },
{ NULL, 0 }
};
static struct val_table_ent *val_table_find_ent(struct val_table_ent *table,
const char *name, size_t len)
{
struct val_table_ent *entry;
for (entry = table; entry->str != NULL; entry++) {
if (strncmp(entry->str, name, len) == 0 &&
strlen(entry->str) == len)
return entry;
}
return NULL;
}
int aa_parse_debug_params(const char *str)
{
struct val_table_ent *ent;
const char *next;
int val = 0;
do {
size_t n = strcspn(str, "\r\n,");
next = str + n;
ent = val_table_find_ent(debug_values_table, str, next - str);
if (ent)
val |= ent->value;
else
AA_DEBUG(DEBUG_INTERFACE, "unknown debug type '%.*s'",
(int)(next - str), str);
str = next + 1;
} while (*next != 0);
return val;
}
/**
* val_mask_to_str - convert a perm mask to its short string
* @str: character buffer to store string in (at least 10 characters)
* @size: size of the @str buffer
* @table: NUL-terminated character buffer of permission characters (NOT NULL)
* @mask: permission mask to convert
*/
static int val_mask_to_str(char *str, size_t size,
const struct val_table_ent *table, u32 mask)
{
const struct val_table_ent *ent;
int total = 0;
for (ent = table; ent->str; ent++) {
if (ent->value && (ent->value & mask) == ent->value) {
int len = scnprintf(str, size, "%s%s", total ? "," : "",
ent->str);
size -= len;
str += len;
total += len;
mask &= ~ent->value;
}
}
return total;
}
int aa_print_debug_params(char *buffer)
{
if (!aa_g_debug)
return sprintf(buffer, "N");
return val_mask_to_str(buffer, PAGE_SIZE, debug_values_table,
aa_g_debug);
}
bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp)
{
char **n;
int i;
if (t->size == newsize)
return true;
n = kcalloc(newsize, sizeof(*n), gfp);
if (!n)
return false;
for (i = 0; i < min(t->size, newsize); i++)
n[i] = t->table[i];
for (; i < t->size; i++)
kfree_sensitive(t->table[i]);
if (newsize > t->size)
memset(&n[t->size], 0, (newsize-t->size)*sizeof(*n));
kfree_sensitive(t->table);
t->table = n;
t->size = newsize;
return true;
}
/** /**
* aa_free_str_table - free entries str table * aa_free_str_table - free entries str table
* @t: the string table to free (MAYBE NULL) * @t: the string table to free (MAYBE NULL)

View file

@ -26,6 +26,7 @@
#include <uapi/linux/mount.h> #include <uapi/linux/mount.h>
#include <uapi/linux/lsm.h> #include <uapi/linux/lsm.h>
#include "include/af_unix.h"
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/apparmorfs.h" #include "include/apparmorfs.h"
#include "include/audit.h" #include "include/audit.h"
@ -126,14 +127,15 @@ static int apparmor_ptrace_access_check(struct task_struct *child,
struct aa_label *tracer, *tracee; struct aa_label *tracer, *tracee;
const struct cred *cred; const struct cred *cred;
int error; int error;
bool needput;
cred = get_task_cred(child); cred = get_task_cred(child);
tracee = cred_label(cred); /* ref count on cred */ tracee = cred_label(cred); /* ref count on cred */
tracer = __begin_current_label_crit_section(); tracer = __begin_current_label_crit_section(&needput);
error = aa_may_ptrace(current_cred(), tracer, cred, tracee, error = aa_may_ptrace(current_cred(), tracer, cred, tracee,
(mode & PTRACE_MODE_READ) ? AA_PTRACE_READ (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ
: AA_PTRACE_TRACE); : AA_PTRACE_TRACE);
__end_current_label_crit_section(tracer); __end_current_label_crit_section(tracer, needput);
put_cred(cred); put_cred(cred);
return error; return error;
@ -144,14 +146,15 @@ static int apparmor_ptrace_traceme(struct task_struct *parent)
struct aa_label *tracer, *tracee; struct aa_label *tracer, *tracee;
const struct cred *cred; const struct cred *cred;
int error; int error;
bool needput;
tracee = __begin_current_label_crit_section(); tracee = __begin_current_label_crit_section(&needput);
cred = get_task_cred(parent); cred = get_task_cred(parent);
tracer = cred_label(cred); /* ref count on cred */ tracer = cred_label(cred); /* ref count on cred */
error = aa_may_ptrace(cred, tracer, current_cred(), tracee, error = aa_may_ptrace(cred, tracer, current_cred(), tracee,
AA_PTRACE_TRACE); AA_PTRACE_TRACE);
put_cred(cred); put_cred(cred);
__end_current_label_crit_section(tracee); __end_current_label_crit_section(tracee, needput);
return error; return error;
} }
@ -176,15 +179,11 @@ static int apparmor_capget(const struct task_struct *target, kernel_cap_t *effec
struct label_it i; struct label_it i;
label_for_each_confined(i, label, profile) { label_for_each_confined(i, label, profile) {
struct aa_ruleset *rules; kernel_cap_t allowed;
if (COMPLAIN_MODE(profile))
continue; allowed = aa_profile_capget(profile);
rules = list_first_entry(&profile->rules, *effective = cap_intersect(*effective, allowed);
typeof(*rules), list); *permitted = cap_intersect(*permitted, allowed);
*effective = cap_intersect(*effective,
rules->caps.allow);
*permitted = cap_intersect(*permitted,
rules->caps.allow);
} }
} }
rcu_read_unlock(); rcu_read_unlock();
@ -221,12 +220,13 @@ static int common_perm(const char *op, const struct path *path, u32 mask,
{ {
struct aa_label *label; struct aa_label *label;
int error = 0; int error = 0;
bool needput;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
if (!unconfined(label)) if (!unconfined(label))
error = aa_path_perm(op, current_cred(), label, path, 0, mask, error = aa_path_perm(op, current_cred(), label, path, 0, mask,
cond); cond);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -524,14 +524,15 @@ static int common_file_perm(const char *op, struct file *file, u32 mask,
{ {
struct aa_label *label; struct aa_label *label;
int error = 0; int error = 0;
bool needput;
/* don't reaudit files closed during inheritance */ /* don't reaudit files closed during inheritance */
if (file->f_path.dentry == aa_null.dentry) if (unlikely(file->f_path.dentry == aa_null.dentry))
return -EACCES; return -EACCES;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
error = aa_file_perm(op, current_cred(), label, file, mask, in_atomic); error = aa_file_perm(op, current_cred(), label, file, mask, in_atomic);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -633,7 +634,7 @@ static int profile_uring(struct aa_profile *profile, u32 request,
AA_BUG(!profile); AA_BUG(!profile);
rules = list_first_entry(&profile->rules, typeof(*rules), list); rules = profile->label.rules[0];
state = RULE_MEDIATES(rules, AA_CLASS_IO_URING); state = RULE_MEDIATES(rules, AA_CLASS_IO_URING);
if (state) { if (state) {
struct aa_perms perms = { }; struct aa_perms perms = { };
@ -664,15 +665,16 @@ static int apparmor_uring_override_creds(const struct cred *new)
struct aa_profile *profile; struct aa_profile *profile;
struct aa_label *label; struct aa_label *label;
int error; int error;
bool needput;
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING, DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING,
OP_URING_OVERRIDE); OP_URING_OVERRIDE);
ad.uring.target = cred_label(new); ad.uring.target = cred_label(new);
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
error = fn_for_each(label, profile, error = fn_for_each(label, profile,
profile_uring(profile, AA_MAY_OVERRIDE_CRED, profile_uring(profile, AA_MAY_OVERRIDE_CRED,
cred_label(new), CAP_SYS_ADMIN, &ad)); cred_label(new), CAP_SYS_ADMIN, &ad));
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -688,14 +690,15 @@ static int apparmor_uring_sqpoll(void)
struct aa_profile *profile; struct aa_profile *profile;
struct aa_label *label; struct aa_label *label;
int error; int error;
bool needput;
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING, DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING,
OP_URING_SQPOLL); OP_URING_SQPOLL);
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
error = fn_for_each(label, profile, error = fn_for_each(label, profile,
profile_uring(profile, AA_MAY_CREATE_SQPOLL, profile_uring(profile, AA_MAY_CREATE_SQPOLL,
NULL, CAP_SYS_ADMIN, &ad)); NULL, CAP_SYS_ADMIN, &ad));
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -706,6 +709,7 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path,
{ {
struct aa_label *label; struct aa_label *label;
int error = 0; int error = 0;
bool needput;
/* Discard magic */ /* Discard magic */
if ((flags & MS_MGC_MSK) == MS_MGC_VAL) if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
@ -713,7 +717,7 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path,
flags &= ~AA_MS_IGNORE_MASK; flags &= ~AA_MS_IGNORE_MASK;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
if (!unconfined(label)) { if (!unconfined(label)) {
if (flags & MS_REMOUNT) if (flags & MS_REMOUNT)
error = aa_remount(current_cred(), label, path, flags, error = aa_remount(current_cred(), label, path, flags,
@ -732,7 +736,7 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path,
error = aa_new_mount(current_cred(), label, dev_name, error = aa_new_mount(current_cred(), label, dev_name,
path, type, flags, data); path, type, flags, data);
} }
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -742,12 +746,13 @@ static int apparmor_move_mount(const struct path *from_path,
{ {
struct aa_label *label; struct aa_label *label;
int error = 0; int error = 0;
bool needput;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
if (!unconfined(label)) if (!unconfined(label))
error = aa_move_mount(current_cred(), label, from_path, error = aa_move_mount(current_cred(), label, from_path,
to_path); to_path);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -756,11 +761,12 @@ static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
{ {
struct aa_label *label; struct aa_label *label;
int error = 0; int error = 0;
bool needput;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
if (!unconfined(label)) if (!unconfined(label))
error = aa_umount(current_cred(), label, mnt, flags); error = aa_umount(current_cred(), label, mnt, flags);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -984,10 +990,12 @@ static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm)
static void apparmor_current_getlsmprop_subj(struct lsm_prop *prop) static void apparmor_current_getlsmprop_subj(struct lsm_prop *prop)
{ {
struct aa_label *label = __begin_current_label_crit_section(); struct aa_label *label;
bool needput;
label = __begin_current_label_crit_section(&needput);
prop->apparmor.label = label; prop->apparmor.label = label;
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
} }
static void apparmor_task_getlsmprop_obj(struct task_struct *p, static void apparmor_task_getlsmprop_obj(struct task_struct *p,
@ -1002,13 +1010,16 @@ static void apparmor_task_getlsmprop_obj(struct task_struct *p,
static int apparmor_task_setrlimit(struct task_struct *task, static int apparmor_task_setrlimit(struct task_struct *task,
unsigned int resource, struct rlimit *new_rlim) unsigned int resource, struct rlimit *new_rlim)
{ {
struct aa_label *label = __begin_current_label_crit_section(); struct aa_label *label;
int error = 0; int error = 0;
bool needput;
label = __begin_current_label_crit_section(&needput);
if (!unconfined(label)) if (!unconfined(label))
error = aa_task_setrlimit(current_cred(), label, task, error = aa_task_setrlimit(current_cred(), label, task,
resource, new_rlim); resource, new_rlim);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return error; return error;
} }
@ -1019,6 +1030,7 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo
const struct cred *tc; const struct cred *tc;
struct aa_label *cl, *tl; struct aa_label *cl, *tl;
int error; int error;
bool needput;
tc = get_task_cred(target); tc = get_task_cred(target);
tl = aa_get_newest_cred_label(tc); tl = aa_get_newest_cred_label(tc);
@ -1030,9 +1042,9 @@ static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo
error = aa_may_signal(cred, cl, tc, tl, sig); error = aa_may_signal(cred, cl, tc, tl, sig);
aa_put_label(cl); aa_put_label(cl);
} else { } else {
cl = __begin_current_label_crit_section(); cl = __begin_current_label_crit_section(&needput);
error = aa_may_signal(current_cred(), cl, tc, tl, sig); error = aa_may_signal(current_cred(), cl, tc, tl, sig);
__end_current_label_crit_section(cl); __end_current_label_crit_section(cl, needput);
} }
aa_put_label(tl); aa_put_label(tl);
put_cred(tc); put_cred(tc);
@ -1061,12 +1073,29 @@ static int apparmor_userns_create(const struct cred *cred)
return error; return error;
} }
static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t gfp)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
struct aa_label *label;
bool needput;
label = __begin_current_label_crit_section(&needput);
//spin_lock_init(&ctx->lock);
rcu_assign_pointer(ctx->label, aa_get_label(label));
rcu_assign_pointer(ctx->peer, NULL);
rcu_assign_pointer(ctx->peer_lastupdate, NULL);
__end_current_label_crit_section(label, needput);
return 0;
}
static void apparmor_sk_free_security(struct sock *sk) static void apparmor_sk_free_security(struct sock *sk)
{ {
struct aa_sk_ctx *ctx = aa_sock(sk); struct aa_sk_ctx *ctx = aa_sock(sk);
aa_put_label(ctx->label); /* dead these won't be updated any more */
aa_put_label(ctx->peer); aa_put_label(rcu_dereference_protected(ctx->label, true));
aa_put_label(rcu_dereference_protected(ctx->peer, true));
aa_put_label(rcu_dereference_protected(ctx->peer_lastupdate, true));
} }
/** /**
@ -1080,13 +1109,153 @@ static void apparmor_sk_clone_security(const struct sock *sk,
struct aa_sk_ctx *ctx = aa_sock(sk); struct aa_sk_ctx *ctx = aa_sock(sk);
struct aa_sk_ctx *new = aa_sock(newsk); struct aa_sk_ctx *new = aa_sock(newsk);
if (new->label) /* not actually in use yet */
aa_put_label(new->label); if (rcu_access_pointer(ctx->label) != rcu_access_pointer(new->label)) {
new->label = aa_get_label(ctx->label); aa_put_label(rcu_dereference_protected(new->label, true));
rcu_assign_pointer(new->label, aa_get_label_rcu(&ctx->label));
}
if (new->peer) if (rcu_access_pointer(ctx->peer) != rcu_access_pointer(new->peer)) {
aa_put_label(new->peer); aa_put_label(rcu_dereference_protected(new->peer, true));
new->peer = aa_get_label(ctx->peer); rcu_assign_pointer(new->peer, aa_get_label_rcu(&ctx->peer));
}
if (rcu_access_pointer(ctx->peer_lastupdate) != rcu_access_pointer(new->peer_lastupdate)) {
aa_put_label(rcu_dereference_protected(new->peer_lastupdate, true));
rcu_assign_pointer(new->peer_lastupdate,
aa_get_label_rcu(&ctx->peer_lastupdate));
}
}
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,
rcu_dereference_protected(peer_ctx->label,
lockdep_is_held(&unix_sk(peer_sk)->lock)));
if (!is_unix_fs(peer_sk)) {
last_error(error,
aa_unix_peer_perm(cred,
rcu_dereference_protected(peer_ctx->label,
lockdep_is_held(&unix_sk(peer_sk)->lock)),
OP_CONNECT,
(AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE),
peer_sk, sk, label));
}
return error;
}
/* lockdep check in unix_connect_perm - push sks here to check */
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 */
struct aa_label *label = rcu_dereference_protected(sk_ctx->label, true);
aa_get_label(label);
aa_put_label(rcu_dereference_protected(peer_ctx->peer,
true));
rcu_assign_pointer(peer_ctx->peer, label); /* transfer cnt */
label = aa_get_label(rcu_dereference_protected(peer_ctx->label,
true));
//spin_unlock(&peer_ctx->lock);
//spin_lock(&sk_ctx->lock);
aa_put_label(rcu_dereference_protected(sk_ctx->peer,
true));
aa_put_label(rcu_dereference_protected(sk_ctx->peer_lastupdate,
true));
rcu_assign_pointer(sk_ctx->peer, aa_get_label(label));
rcu_assign_pointer(sk_ctx->peer_lastupdate, label); /* transfer cnt */
//spin_unlock(&sk_ctx->lock);
}
/**
* apparmor_unix_stream_connect - check perms before making unix domain conn
* @sk: sk attempting to connect
* @peer_sk: sk that is accepting the connection
* @newsk: new sk created for this connection
* peer is locked when this hook is called
*
* Return:
* 0 if connection is permitted
* error code on denial or failure
*/
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;
bool needput;
label = __begin_current_label_crit_section(&needput);
error = unix_connect_perm(current_cred(), label, sk, peer_sk);
__end_current_label_crit_section(label, needput);
if (error)
return error;
/* newsk doesn't go through post_create, but does go through
* security_sk_alloc()
*/
rcu_assign_pointer(new_ctx->label,
aa_get_label(rcu_dereference_protected(peer_ctx->label,
true)));
/* 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: socket sending the message
* @peer: socket message is being send to
*
* Performs bidirectional permission checks for Unix domain socket communication:
* 1. Verifies sender has AA_MAY_SEND to target socket
* 2. Verifies receiver has AA_MAY_RECEIVE from source socket
*
* sock and peer are locked when this hook is called
* called by: dgram_connect peer setup but path not copied to newsk
*
* Return:
* 0 if transmission is permitted
* error code on denial or failure
*/
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;
bool needput;
label = __begin_current_label_crit_section(&needput);
error = xcheck(aa_unix_peer_perm(current_cred(),
label, OP_SENDMSG, AA_MAY_SEND,
sock->sk, peer->sk,
rcu_dereference_protected(peer_ctx->label,
true)),
aa_unix_peer_perm(peer->file ? peer->file->f_cred : NULL,
rcu_dereference_protected(peer_ctx->label,
true),
OP_SENDMSG, AA_MAY_RECEIVE, peer->sk,
sock->sk, label));
__end_current_label_crit_section(label, needput);
return error;
} }
static int apparmor_socket_create(int family, int type, int protocol, int kern) static int apparmor_socket_create(int family, int type, int protocol, int kern)
@ -1096,13 +1265,19 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern)
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
if (kern)
return 0;
label = begin_current_label_crit_section(); label = begin_current_label_crit_section();
if (!(kern || unconfined(label))) if (!unconfined(label)) {
error = af_select(family, if (family == PF_UNIX)
create_perm(label, family, type, protocol), error = aa_unix_create_perm(label, family, type,
aa_af_perm(current_cred(), label, protocol);
OP_CREATE, AA_MAY_CREATE, else
family, type, protocol)); error = aa_af_perm(current_cred(), label, OP_CREATE,
AA_MAY_CREATE, family, type,
protocol);
}
end_current_label_crit_section(label); end_current_label_crit_section(label);
return error; return error;
@ -1135,14 +1310,58 @@ static int apparmor_socket_post_create(struct socket *sock, int family,
if (sock->sk) { if (sock->sk) {
struct aa_sk_ctx *ctx = aa_sock(sock->sk); struct aa_sk_ctx *ctx = aa_sock(sock->sk);
aa_put_label(ctx->label); /* still not live */
ctx->label = aa_get_label(label); aa_put_label(rcu_dereference_protected(ctx->label, true));
rcu_assign_pointer(ctx->label, aa_get_label(label));
} }
aa_put_label(label); aa_put_label(label);
return 0; 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;
/* socks not live yet - initial values set in sk_alloc */
label = begin_current_label_crit_section();
if (rcu_access_pointer(a_ctx->label) != label) {
AA_BUG("a_ctx != label");
aa_put_label(rcu_dereference_protected(a_ctx->label, true));
rcu_assign_pointer(a_ctx->label, aa_get_label(label));
}
if (rcu_access_pointer(b_ctx->label) != label) {
AA_BUG("b_ctx != label");
aa_put_label(rcu_dereference_protected(b_ctx->label, true));
rcu_assign_pointer(b_ctx->label, aa_get_label(label));
}
if (socka->sk->sk_family == PF_UNIX) {
/* unix socket pairs by-pass unix_stream_connect */
unix_connect_peers(a_ctx, b_ctx);
}
end_current_label_crit_section(label);
return 0;
}
/**
* apparmor_socket_bind - check perms before bind addr to socket
* @sock: socket to bind the address to (must be non-NULL)
* @address: address that is being bound (must be non-NULL)
* @addrlen: length of @address
*
* Performs security checks before allowing a socket to bind to an address.
* Handles Unix domain sockets specially through aa_unix_bind_perm().
* For other socket families, uses generic permission check via aa_sk_perm().
*
* Return:
* 0 if binding is permitted
* error code on denial or invalid parameters
*/
static int apparmor_socket_bind(struct socket *sock, static int apparmor_socket_bind(struct socket *sock,
struct sockaddr *address, int addrlen) struct sockaddr *address, int addrlen)
{ {
@ -1151,9 +1370,9 @@ static int apparmor_socket_bind(struct socket *sock,
AA_BUG(!address); AA_BUG(!address);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, if (sock->sk->sk_family == PF_UNIX)
bind_perm(sock, address, addrlen), return aa_unix_bind_perm(sock, address, addrlen);
aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk);
} }
static int apparmor_socket_connect(struct socket *sock, static int apparmor_socket_connect(struct socket *sock,
@ -1164,9 +1383,10 @@ static int apparmor_socket_connect(struct socket *sock,
AA_BUG(!address); AA_BUG(!address);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, /* PF_UNIX goes through unix_stream_connect && unix_may_send */
connect_perm(sock, address, addrlen), if (sock->sk->sk_family == PF_UNIX)
aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); return 0;
return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
} }
static int apparmor_socket_listen(struct socket *sock, int backlog) static int apparmor_socket_listen(struct socket *sock, int backlog)
@ -1175,9 +1395,9 @@ static int apparmor_socket_listen(struct socket *sock, int backlog)
AA_BUG(!sock->sk); AA_BUG(!sock->sk);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, if (sock->sk->sk_family == PF_UNIX)
listen_perm(sock, backlog), return aa_unix_listen_perm(sock, backlog);
aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk);
} }
/* /*
@ -1191,9 +1411,9 @@ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
AA_BUG(!newsock); AA_BUG(!newsock);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, if (sock->sk->sk_family == PF_UNIX)
accept_perm(sock, newsock), return aa_unix_accept_perm(sock, newsock);
aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk);
} }
static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
@ -1204,9 +1424,10 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
AA_BUG(!msg); AA_BUG(!msg);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, /* PF_UNIX goes through unix_may_send */
msg_perm(op, request, sock, msg, size), if (sock->sk->sk_family == PF_UNIX)
aa_sk_perm(op, request, sock->sk)); return 0;
return aa_sk_perm(op, request, sock->sk);
} }
static int apparmor_socket_sendmsg(struct socket *sock, static int apparmor_socket_sendmsg(struct socket *sock,
@ -1228,9 +1449,9 @@ static int aa_sock_perm(const char *op, u32 request, struct socket *sock)
AA_BUG(!sock->sk); AA_BUG(!sock->sk);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, if (sock->sk->sk_family == PF_UNIX)
sock_perm(op, request, sock), return aa_unix_sock_perm(op, request, sock);
aa_sk_perm(op, request, sock->sk)); return aa_sk_perm(op, request, sock->sk);
} }
static int apparmor_socket_getsockname(struct socket *sock) static int apparmor_socket_getsockname(struct socket *sock)
@ -1251,9 +1472,9 @@ static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock,
AA_BUG(!sock->sk); AA_BUG(!sock->sk);
AA_BUG(in_interrupt()); AA_BUG(in_interrupt());
return af_select(sock->sk->sk_family, if (sock->sk->sk_family == PF_UNIX)
opt_perm(op, request, sock, level, optname), return aa_unix_opt_perm(op, request, sock, level, optname);
aa_sk_perm(op, request, sock->sk)); return aa_sk_perm(op, request, sock->sk);
} }
static int apparmor_socket_getsockopt(struct socket *sock, int level, static int apparmor_socket_getsockopt(struct socket *sock, int level,
@ -1289,6 +1510,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{ {
struct aa_sk_ctx *ctx = aa_sock(sk); struct aa_sk_ctx *ctx = aa_sock(sk);
int error;
if (!skb->secmark) if (!skb->secmark)
return 0; return 0;
@ -1297,23 +1519,31 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
* If reach here before socket_post_create hook is called, in which * If reach here before socket_post_create hook is called, in which
* case label is null, drop the packet. * case label is null, drop the packet.
*/ */
if (!ctx->label) if (!rcu_access_pointer(ctx->label))
return -EACCES; return -EACCES;
return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE, rcu_read_lock();
skb->secmark, sk); error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_RECVMSG,
AA_MAY_RECEIVE, skb->secmark, sk);
rcu_read_unlock();
return error;
} }
#endif #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_sk_ctx *ctx = aa_sock(sk);
struct aa_label *label = ERR_PTR(-ENOPROTOOPT);
if (ctx->peer) if (rcu_access_pointer(ctx->peer))
return ctx->peer; return aa_get_label_rcu(&ctx->peer);
if (sk->sk_family != PF_UNIX)
return ERR_PTR(-ENOPROTOOPT); return ERR_PTR(-ENOPROTOOPT);
return label;
} }
/** /**
@ -1335,19 +1565,19 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
struct aa_label *label; struct aa_label *label;
struct aa_label *peer; struct aa_label *peer;
label = begin_current_label_crit_section(); peer = sk_peer_get_label(sock->sk);
peer = sk_peer_label(sock->sk);
if (IS_ERR(peer)) { if (IS_ERR(peer)) {
error = PTR_ERR(peer); error = PTR_ERR(peer);
goto done; goto done;
} }
label = begin_current_label_crit_section();
slen = aa_label_asxprint(&name, labels_ns(label), peer, slen = aa_label_asxprint(&name, labels_ns(label), peer,
FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); FLAG_HIDDEN_UNCONFINED, GFP_KERNEL);
/* don't include terminating \0 in slen, it breaks some apps */ /* don't include terminating \0 in slen, it breaks some apps */
if (slen < 0) { if (slen < 0) {
error = -ENOMEM; error = -ENOMEM;
goto done; goto done_put;
} }
if (slen > len) { if (slen > len) {
error = -ERANGE; error = -ERANGE;
@ -1359,8 +1589,11 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
done_len: done_len:
if (copy_to_sockptr(optlen, &slen, sizeof(slen))) if (copy_to_sockptr(optlen, &slen, sizeof(slen)))
error = -EFAULT; error = -EFAULT;
done:
done_put:
end_current_label_crit_section(label); end_current_label_crit_section(label);
aa_put_label(peer);
done:
kfree(name); kfree(name);
return error; return error;
} }
@ -1396,8 +1629,9 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
{ {
struct aa_sk_ctx *ctx = aa_sock(sk); struct aa_sk_ctx *ctx = aa_sock(sk);
if (!ctx->label) /* setup - not live */
ctx->label = aa_get_current_label(); if (!rcu_access_pointer(ctx->label))
rcu_assign_pointer(ctx->label, aa_get_current_label());
} }
#ifdef CONFIG_NETWORK_SECMARK #ifdef CONFIG_NETWORK_SECMARK
@ -1405,12 +1639,17 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb
struct request_sock *req) struct request_sock *req)
{ {
struct aa_sk_ctx *ctx = aa_sock(sk); struct aa_sk_ctx *ctx = aa_sock(sk);
int error;
if (!skb->secmark) if (!skb->secmark)
return 0; return 0;
return apparmor_secmark_check(ctx->label, OP_CONNECT, AA_MAY_CONNECT, rcu_read_lock();
skb->secmark, sk); error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_CONNECT,
AA_MAY_CONNECT, skb->secmark, sk);
rcu_read_unlock();
return error;
} }
#endif #endif
@ -1467,11 +1706,16 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_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_create, apparmor_socket_create),
LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_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_bind, apparmor_socket_bind),
LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), LSM_HOOK_INIT(socket_connect, apparmor_socket_connect),
LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), LSM_HOOK_INIT(socket_listen, apparmor_socket_listen),
@ -1571,6 +1815,9 @@ static const struct kernel_param_ops param_ops_aalockpolicy = {
.get = param_get_aalockpolicy .get = param_get_aalockpolicy
}; };
static int param_set_debug(const char *val, const struct kernel_param *kp);
static int param_get_debug(char *buffer, const struct kernel_param *kp);
static int param_set_audit(const char *val, const struct kernel_param *kp); static int param_set_audit(const char *val, const struct kernel_param *kp);
static int param_get_audit(char *buffer, const struct kernel_param *kp); static int param_get_audit(char *buffer, const struct kernel_param *kp);
@ -1604,8 +1851,9 @@ module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
aacompressionlevel, 0400); aacompressionlevel, 0400);
/* Debug mode */ /* Debug mode */
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES); int aa_g_debug;
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); module_param_call(debug, param_set_debug, param_get_debug,
&aa_g_debug, 0600);
/* Audit mode */ /* Audit mode */
enum audit_mode aa_g_audit; enum audit_mode aa_g_audit;
@ -1798,6 +2046,34 @@ static int param_get_aacompressionlevel(char *buffer,
return param_get_int(buffer, kp); return param_get_int(buffer, kp);
} }
static int param_get_debug(char *buffer, const struct kernel_param *kp)
{
if (!apparmor_enabled)
return -EINVAL;
if (apparmor_initialized && !aa_current_policy_view_capable(NULL))
return -EPERM;
return aa_print_debug_params(buffer);
}
static int param_set_debug(const char *val, const struct kernel_param *kp)
{
int i;
if (!apparmor_enabled)
return -EINVAL;
if (!val)
return -EINVAL;
if (apparmor_initialized && !aa_current_policy_admin_capable(NULL))
return -EPERM;
i = aa_parse_debug_params(val);
if (i == DEBUG_PARSE_ERROR)
return -EINVAL;
aa_g_debug = i;
return 0;
}
static int param_get_audit(char *buffer, const struct kernel_param *kp) static int param_get_audit(char *buffer, const struct kernel_param *kp)
{ {
if (!apparmor_enabled) if (!apparmor_enabled)
@ -2006,7 +2282,7 @@ static int __init alloc_buffers(void)
* two should be enough, with more CPUs it is possible that more * two should be enough, with more CPUs it is possible that more
* buffers will be used simultaneously. The preallocated pool may grow. * buffers will be used simultaneously. The preallocated pool may grow.
* This preallocation has also the side-effect that AppArmor will be * This preallocation has also the side-effect that AppArmor will be
* disabled early at boot if aa_g_path_max is extremly high. * disabled early at boot if aa_g_path_max is extremely high.
*/ */
if (num_online_cpus() > 1) if (num_online_cpus() > 1)
num = 4 + RESERVE_COUNT; num = 4 + RESERVE_COUNT;
@ -2082,6 +2358,7 @@ static unsigned int apparmor_ip_postroute(void *priv,
{ {
struct aa_sk_ctx *ctx; struct aa_sk_ctx *ctx;
struct sock *sk; struct sock *sk;
int error;
if (!skb->secmark) if (!skb->secmark)
return NF_ACCEPT; return NF_ACCEPT;
@ -2091,8 +2368,11 @@ static unsigned int apparmor_ip_postroute(void *priv,
return NF_ACCEPT; return NF_ACCEPT;
ctx = aa_sock(sk); ctx = aa_sock(sk);
if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND, rcu_read_lock();
skb->secmark, sk)) error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_SENDMSG,
AA_MAY_SEND, skb->secmark, sk);
rcu_read_unlock();
if (!error)
return NF_ACCEPT; return NF_ACCEPT;
return NF_DROP_ERR(-ECONNREFUSED); return NF_DROP_ERR(-ECONNREFUSED);
@ -2149,12 +2429,12 @@ static int __init apparmor_nf_ip_init(void)
__initcall(apparmor_nf_ip_init); __initcall(apparmor_nf_ip_init);
#endif #endif
static char nulldfa_src[] = { static char nulldfa_src[] __aligned(8) = {
#include "nulldfa.in" #include "nulldfa.in"
}; };
static struct aa_dfa *nulldfa; static struct aa_dfa *nulldfa;
static char stacksplitdfa_src[] = { static char stacksplitdfa_src[] __aligned(8) = {
#include "stacksplitdfa.in" #include "stacksplitdfa.in"
}; };
struct aa_dfa *stacksplitdfa; struct aa_dfa *stacksplitdfa;

View file

@ -681,32 +681,33 @@ aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
#define inc_wb_pos(wb) \ #define inc_wb_pos(wb) \
do { \ do { \
BUILD_BUG_ON_NOT_POWER_OF_2(WB_HISTORY_SIZE); \
wb->pos = (wb->pos + 1) & (WB_HISTORY_SIZE - 1); \ wb->pos = (wb->pos + 1) & (WB_HISTORY_SIZE - 1); \
wb->len = (wb->len + 1) & (WB_HISTORY_SIZE - 1); \ wb->len = (wb->len + 1) > WB_HISTORY_SIZE ? WB_HISTORY_SIZE : \
wb->len + 1; \
} while (0) } while (0)
/* For DFAs that don't support extended tagging of states */ /* For DFAs that don't support extended tagging of states */
/* adjust is only set if is_loop returns true */
static bool is_loop(struct match_workbuf *wb, aa_state_t state, static bool is_loop(struct match_workbuf *wb, aa_state_t state,
unsigned int *adjust) unsigned int *adjust)
{ {
aa_state_t pos = wb->pos; int pos = wb->pos;
aa_state_t i; int i;
if (wb->history[pos] < state) if (wb->history[pos] < state)
return false; return false;
for (i = 0; i <= wb->len; i++) { for (i = 0; i < wb->len; i++) {
if (wb->history[pos] == state) { if (wb->history[pos] == state) {
*adjust = i; *adjust = i;
return true; return true;
} }
if (pos == 0) /* -1 wraps to WB_HISTORY_SIZE - 1 */
pos = WB_HISTORY_SIZE; pos = (pos - 1) & (WB_HISTORY_SIZE - 1);
pos--;
} }
*adjust = i; return false;
return true;
} }
static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start, static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,

View file

@ -311,8 +311,7 @@ static int match_mnt_path_str(const struct cred *subj_cred,
{ {
struct aa_perms perms = { }; struct aa_perms perms = { };
const char *mntpnt = NULL, *info = NULL; const char *mntpnt = NULL, *info = NULL;
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
int pos, error; int pos, error;
AA_BUG(!profile); AA_BUG(!profile);
@ -371,8 +370,7 @@ static int match_mnt(const struct cred *subj_cred,
bool binary) bool binary)
{ {
const char *devname = NULL, *info = NULL; const char *devname = NULL, *info = NULL;
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
int error = -EACCES; int error = -EACCES;
AA_BUG(!profile); AA_BUG(!profile);
@ -604,8 +602,7 @@ static int profile_umount(const struct cred *subj_cred,
struct aa_profile *profile, const struct path *path, struct aa_profile *profile, const struct path *path,
char *buffer) char *buffer)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_perms perms = { }; struct aa_perms perms = { };
const char *name = NULL, *info = NULL; const char *name = NULL, *info = NULL;
aa_state_t state; aa_state_t state;
@ -668,8 +665,7 @@ static struct aa_label *build_pivotroot(const struct cred *subj_cred,
const struct path *old_path, const struct path *old_path,
char *old_buffer) char *old_buffer)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
const char *old_name, *new_name = NULL, *info = NULL; const char *old_name, *new_name = NULL, *info = NULL;
const char *trans_name = NULL; const char *trans_name = NULL;
struct aa_perms perms = { }; struct aa_perms perms = { };

View file

@ -8,6 +8,7 @@
* Copyright 2009-2017 Canonical Ltd. * Copyright 2009-2017 Canonical Ltd.
*/ */
#include "include/af_unix.h"
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/audit.h" #include "include/audit.h"
#include "include/cred.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[] = { static const char * const net_mask_names[] = {
"unknown", "unknown",
"send", "send",
@ -66,6 +73,42 @@ static const char * const net_mask_names[] = {
"unknown", "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) {
int addrlen;
struct sockaddr_un *addr = aa_sunaddr(u, &addrlen);
audit_unix_addr(ab, str, addr, addrlen);
} else {
audit_unix_addr(ab, str, NULL, 0);
}
}
/* audit callback for net specific fields */ /* audit callback for net specific fields */
void audit_net_cb(struct audit_buffer *ab, void *va) void audit_net_cb(struct audit_buffer *ab, void *va)
@ -73,12 +116,12 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va; struct common_audit_data *sa = va;
struct apparmor_audit_data *ad = aad(sa); 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\"", audit_log_format(ab, " family=\"%s\"",
address_family_names[sa->u.net->family]); address_family_names[ad->common.u.net->family]);
else else
audit_log_format(ab, " family=\"unknown(%d)\"", audit_log_format(ab, " family=\"unknown(%d)\"",
sa->u.net->family); ad->common.u.net->family);
if (sock_type_names[ad->net.type]) if (sock_type_names[ad->net.type])
audit_log_format(ab, " sock_type=\"%s\"", audit_log_format(ab, " sock_type=\"%s\"",
sock_type_names[ad->net.type]); sock_type_names[ad->net.type]);
@ -98,6 +141,19 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
net_mask_names, NET_PERMS_MASK); net_mask_names, NET_PERMS_MASK);
} }
} }
if (ad->common.u.net->family == PF_UNIX) {
if (ad->net.addr || !ad->common.u.net->sk)
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) {
audit_unix_addr(ab, "peer_addr",
unix_addr(ad->net.peer.addr),
ad->net.peer.addrlen);
}
}
if (ad->peer) { if (ad->peer) {
audit_log_format(ab, " peer="); audit_log_format(ab, " peer=");
aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
@ -105,45 +161,123 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
} }
} }
/* standard permission lookup pattern - supports early bailout */
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;
AA_BUG(!profile);
AA_BUG(!policy);
if (state || !p)
p = aa_lookup_perms(policy, state);
perms = *p;
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, ad,
audit_net_cb);
}
/* only continue match if
* insufficient current perms at current state
* indicates there are more perms in later state
* Returns: perms struct if early match
*/
static struct aa_perms *early_match(struct aa_policydb *policy,
aa_state_t state, u32 request)
{
struct aa_perms *p;
p = aa_lookup_perms(policy, state);
if (((p->allow & request) != request) && (p->allow & AA_CONT_MATCH))
return NULL;
return p;
}
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 af, int type, int protocol,
struct aa_perms **p, const char **info)
{
state = aa_dfa_match_be16(policy->dfa, state, (u16)af);
if (!state) {
*info = "failed af match";
return 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";
}
return state;
}
/* Generic af perm */ /* Generic af perm */
int aa_profile_af_perm(struct aa_profile *profile, int aa_profile_af_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad, u32 request, u16 family, struct apparmor_audit_data *ad, u32 request, u16 family,
int type) int type, int protocol)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list); struct aa_perms *p = NULL;
struct aa_perms perms = { };
aa_state_t state; aa_state_t state;
__be16 buffer[2];
AA_BUG(family >= AF_MAX); AA_BUG(family >= AF_MAX);
AA_BUG(type < 0 || type >= SOCK_MAX); AA_BUG(type < 0 || type >= SOCK_MAX);
AA_BUG(profile_unconfined(profile));
if (profile_unconfined(profile)) if (profile_unconfined(profile))
return 0; return 0;
state = RULE_MEDIATES(rules, AA_CLASS_NET); state = RULE_MEDIATES_NET(rules);
if (!state) if (!state)
return 0; return 0;
state = aa_match_to_prot(rules->policy, state, request, family, type,
buffer[0] = cpu_to_be16(family); protocol, &p, &ad->info);
buffer[1] = cpu_to_be16((u16) type); return aa_do_perms(profile, rules->policy, state, request, p, ad);
state = aa_dfa_match_len(rules->policy->dfa, state, (char *) &buffer,
4);
perms = *aa_lookup_perms(rules->policy, state);
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, ad, audit_net_cb);
} }
int aa_af_perm(const struct cred *subj_cred, struct aa_label *label, int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, u16 family, int type, int protocol) const char *op, u32 request, u16 family, int type, int protocol)
{ {
struct aa_profile *profile; 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, return fn_for_each_confined(label, profile,
aa_profile_af_perm(profile, &ad, request, family, aa_profile_af_perm(profile, &ad, request, family,
type)); type, protocol));
} }
static int aa_label_sk_perm(const struct cred *subj_cred, static int aa_label_sk_perm(const struct cred *subj_cred,
@ -157,9 +291,9 @@ static int aa_label_sk_perm(const struct cred *subj_cred,
AA_BUG(!label); AA_BUG(!label);
AA_BUG(!sk); AA_BUG(!sk);
if (ctx->label != kernel_t && !unconfined(label)) { if (rcu_access_pointer(ctx->label) != kernel_t && !unconfined(label)) {
struct aa_profile *profile; struct aa_profile *profile;
DEFINE_AUDIT_SK(ad, op, sk); DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
ad.subj_cred = subj_cred; ad.subj_cred = subj_cred;
error = fn_for_each_confined(label, profile, error = fn_for_each_confined(label, profile,
@ -187,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, 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(!label);
AA_BUG(!sock); AA_BUG(!sock);
AA_BUG(!sock->sk); 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); return aa_label_sk_perm(subj_cred, label, op, request, sock->sk);
} }
@ -223,8 +361,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
{ {
int i, ret; int i, ret;
struct aa_perms perms = { }; struct aa_perms perms = { };
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
if (rules->secmark_count == 0) if (rules->secmark_count == 0)
return 0; return 0;
@ -257,7 +394,7 @@ int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, const struct sock *sk) u32 secid, const struct sock *sk)
{ {
struct aa_profile *profile; struct aa_profile *profile;
DEFINE_AUDIT_SK(ad, op, sk); DEFINE_AUDIT_SK(ad, op, NULL, sk);
return fn_for_each_confined(label, profile, return fn_for_each_confined(label, profile,
aa_secmark_perm(profile, request, secid, aa_secmark_perm(profile, request, secid,

View file

@ -243,6 +243,9 @@ static void free_ruleset(struct aa_ruleset *rules)
{ {
int i; int i;
if (!rules)
return;
aa_put_pdb(rules->file); aa_put_pdb(rules->file);
aa_put_pdb(rules->policy); aa_put_pdb(rules->policy);
aa_free_cap_rules(&rules->caps); aa_free_cap_rules(&rules->caps);
@ -259,8 +262,6 @@ struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp)
struct aa_ruleset *rules; struct aa_ruleset *rules;
rules = kzalloc(sizeof(*rules), gfp); rules = kzalloc(sizeof(*rules), gfp);
if (rules)
INIT_LIST_HEAD(&rules->list);
return rules; return rules;
} }
@ -277,10 +278,9 @@ struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp)
*/ */
void aa_free_profile(struct aa_profile *profile) void aa_free_profile(struct aa_profile *profile)
{ {
struct aa_ruleset *rule, *tmp;
struct rhashtable *rht; struct rhashtable *rht;
AA_DEBUG("%s(%p)\n", __func__, profile); AA_DEBUG(DEBUG_POLICY, "%s(%p)\n", __func__, profile);
if (!profile) if (!profile)
return; return;
@ -299,10 +299,9 @@ void aa_free_profile(struct aa_profile *profile)
* at this point there are no tasks that can have a reference * at this point there are no tasks that can have a reference
* to rules * to rules
*/ */
list_for_each_entry_safe(rule, tmp, &profile->rules, list) { for (int i = 0; i < profile->n_rules; i++)
list_del_init(&rule->list); free_ruleset(profile->label.rules[i]);
free_ruleset(rule);
}
kfree_sensitive(profile->dirname); kfree_sensitive(profile->dirname);
if (profile->data) { if (profile->data) {
@ -331,10 +330,12 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
gfp_t gfp) gfp_t gfp)
{ {
struct aa_profile *profile; struct aa_profile *profile;
struct aa_ruleset *rules;
/* freed by free_profile - usually through aa_put_profile */ /* freed by free_profile - usually through aa_put_profile
profile = kzalloc(struct_size(profile, label.vec, 2), gfp); * this adds space for a single ruleset in the rules section of the
* label
*/
profile = kzalloc(struct_size(profile, label.rules, 1), gfp);
if (!profile) if (!profile)
return NULL; return NULL;
@ -343,13 +344,11 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
if (!aa_label_init(&profile->label, 1, gfp)) if (!aa_label_init(&profile->label, 1, gfp))
goto fail; goto fail;
INIT_LIST_HEAD(&profile->rules);
/* allocate the first ruleset, but leave it empty */ /* allocate the first ruleset, but leave it empty */
rules = aa_alloc_ruleset(gfp); profile->label.rules[0] = aa_alloc_ruleset(gfp);
if (!rules) if (!profile->label.rules[0])
goto fail; goto fail;
list_add(&rules->list, &profile->rules); profile->n_rules = 1;
/* update being set needed by fs interface */ /* update being set needed by fs interface */
if (!proxy) { if (!proxy) {
@ -364,6 +363,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
profile->label.flags |= FLAG_PROFILE; profile->label.flags |= FLAG_PROFILE;
profile->label.vec[0] = profile; profile->label.vec[0] = profile;
profile->signal = SIGKILL;
/* refcount released by caller */ /* refcount released by caller */
return profile; return profile;
@ -373,6 +373,41 @@ fail:
return NULL; return NULL;
} }
static inline bool ANY_RULE_MEDIATES(struct aa_profile *profile,
unsigned char class)
{
int i;
for (i = 0; i < profile->n_rules; i++) {
if (RULE_MEDIATES(profile->label.rules[i], class))
return true;
}
return false;
}
/* set of rules that are mediated by unconfined */
static int unconfined_mediates[] = { AA_CLASS_NS, AA_CLASS_IO_URING, 0 };
/* must be called after profile rulesets and start information is setup */
void aa_compute_profile_mediates(struct aa_profile *profile)
{
int c;
if (profile_unconfined(profile)) {
int *pos;
for (pos = unconfined_mediates; *pos; pos++) {
if (ANY_RULE_MEDIATES(profile, *pos))
profile->label.mediates |= ((u64) 1) << AA_CLASS_NS;
}
return;
}
for (c = 0; c <= AA_CLASS_LAST; c++) {
if (ANY_RULE_MEDIATES(profile, c))
profile->label.mediates |= ((u64) 1) << c;
}
}
/* TODO: profile accounting - setup in remove */ /* TODO: profile accounting - setup in remove */
/** /**
@ -463,7 +498,7 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
} }
/** /**
* __create_missing_ancestors - create place holders for missing ancestores * __create_missing_ancestors - create place holders for missing ancestors
* @ns: namespace to lookup profile in (NOT NULL) * @ns: namespace to lookup profile in (NOT NULL)
* @hname: hierarchical profile name to find parent of (NOT NULL) * @hname: hierarchical profile name to find parent of (NOT NULL)
* @gfp: type of allocation. * @gfp: type of allocation.
@ -621,13 +656,15 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
/* TODO: ideally we should inherit abi from parent */ /* TODO: ideally we should inherit abi from parent */
profile->label.flags |= FLAG_NULL; profile->label.flags |= FLAG_NULL;
profile->attach.xmatch = aa_get_pdb(nullpdb); profile->attach.xmatch = aa_get_pdb(nullpdb);
rules = list_first_entry(&profile->rules, typeof(*rules), list); rules = profile->label.rules[0];
rules->file = aa_get_pdb(nullpdb); rules->file = aa_get_pdb(nullpdb);
rules->policy = aa_get_pdb(nullpdb); rules->policy = aa_get_pdb(nullpdb);
aa_compute_profile_mediates(profile);
if (parent) { if (parent) {
profile->path_flags = parent->path_flags; profile->path_flags = parent->path_flags;
/* override/inherit what is mediated from parent */
profile->label.mediates = parent->label.mediates;
/* released on free_profile */ /* released on free_profile */
rcu_assign_pointer(profile->parent, aa_get_profile(parent)); rcu_assign_pointer(profile->parent, aa_get_profile(parent));
profile->ns = aa_get_ns(parent->ns); profile->ns = aa_get_ns(parent->ns);
@ -833,8 +870,8 @@ bool aa_policy_admin_capable(const struct cred *subj_cred,
bool capable = policy_ns_capable(subj_cred, label, user_ns, bool capable = policy_ns_capable(subj_cred, label, user_ns,
CAP_MAC_ADMIN) == 0; CAP_MAC_ADMIN) == 0;
AA_DEBUG("cap_mac_admin? %d\n", capable); AA_DEBUG(DEBUG_POLICY, "cap_mac_admin? %d\n", capable);
AA_DEBUG("policy locked? %d\n", aa_g_lock_policy); AA_DEBUG(DEBUG_POLICY, "policy locked? %d\n", aa_g_lock_policy);
return aa_policy_view_capable(subj_cred, label, ns) && capable && return aa_policy_view_capable(subj_cred, label, ns) && capable &&
!aa_g_lock_policy; !aa_g_lock_policy;
@ -843,11 +880,11 @@ bool aa_policy_admin_capable(const struct cred *subj_cred,
bool aa_current_policy_view_capable(struct aa_ns *ns) bool aa_current_policy_view_capable(struct aa_ns *ns)
{ {
struct aa_label *label; struct aa_label *label;
bool res; bool needput, res;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
res = aa_policy_view_capable(current_cred(), label, ns); res = aa_policy_view_capable(current_cred(), label, ns);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return res; return res;
} }
@ -855,11 +892,11 @@ bool aa_current_policy_view_capable(struct aa_ns *ns)
bool aa_current_policy_admin_capable(struct aa_ns *ns) bool aa_current_policy_admin_capable(struct aa_ns *ns)
{ {
struct aa_label *label; struct aa_label *label;
bool res; bool needput, res;
label = __begin_current_label_crit_section(); label = __begin_current_label_crit_section(&needput);
res = aa_policy_admin_capable(current_cred(), label, ns); res = aa_policy_admin_capable(current_cred(), label, ns);
__end_current_label_crit_section(label); __end_current_label_crit_section(label, needput);
return res; return res;
} }
@ -1068,7 +1105,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
goto out; goto out;
/* ensure that profiles are all for the same ns /* ensure that profiles are all for the same ns
* TODO: update locking to remove this constaint. All profiles in * TODO: update locking to remove this constraint. All profiles in
* the load set must succeed as a set or the load will * the load set must succeed as a set or the load will
* fail. Sort ent list and take ns locks in hierarchy order * fail. Sort ent list and take ns locks in hierarchy order
*/ */

View file

@ -286,10 +286,10 @@ static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor)
AA_BUG(!dfa); AA_BUG(!dfa);
for (state = 0; state < state_count; state++) for (state = 0; state < state_count; state++) {
ACCEPT_TABLE(dfa)[state] = state * factor; ACCEPT_TABLE(dfa)[state] = state * factor;
kvfree(dfa->tables[YYTD_ID_ACCEPT2]); ACCEPT_TABLE2(dfa)[state] = factor > 1 ? ACCEPT_FLAG_OWNER : 0;
dfa->tables[YYTD_ID_ACCEPT2] = NULL; }
} }
/* TODO: merge different dfa mappings into single map_policy fn */ /* TODO: merge different dfa mappings into single map_policy fn */

View file

@ -107,7 +107,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
struct aa_ns *ns; struct aa_ns *ns;
ns = kzalloc(sizeof(*ns), GFP_KERNEL); ns = kzalloc(sizeof(*ns), GFP_KERNEL);
AA_DEBUG("%s(%p)\n", __func__, ns); AA_DEBUG(DEBUG_POLICY, "%s(%p)\n", __func__, ns);
if (!ns) if (!ns)
return NULL; return NULL;
if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL)) if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL))

View file

@ -29,6 +29,7 @@
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
#include "include/policy_compat.h" #include "include/policy_compat.h"
#include "include/signal.h"
/* audit callback for unpack fields */ /* audit callback for unpack fields */
static void audit_cb(struct audit_buffer *ab, void *va) static void audit_cb(struct audit_buffer *ab, void *va)
@ -598,8 +599,8 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules)
fail: fail:
if (rules->secmark) { if (rules->secmark) {
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
kfree(rules->secmark[i].label); kfree_sensitive(rules->secmark[i].label);
kfree(rules->secmark); kfree_sensitive(rules->secmark);
rules->secmark_count = 0; rules->secmark_count = 0;
rules->secmark = NULL; rules->secmark = NULL;
} }
@ -716,6 +717,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
void *pos = e->pos; void *pos = e->pos;
int i, flags, error = -EPROTO; int i, flags, error = -EPROTO;
ssize_t size; ssize_t size;
u32 version = 0;
pdb = aa_alloc_pdb(GFP_KERNEL); pdb = aa_alloc_pdb(GFP_KERNEL);
if (!pdb) if (!pdb)
@ -733,6 +735,9 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
if (pdb->perms) { if (pdb->perms) {
/* perms table present accept is index */ /* perms table present accept is index */
flags = TO_ACCEPT1_FLAG(YYTD_DATA32); flags = TO_ACCEPT1_FLAG(YYTD_DATA32);
if (aa_unpack_u32(e, &version, "permsv") && version > 2)
/* accept2 used for dfa flags */
flags |= TO_ACCEPT2_FLAG(YYTD_DATA32);
} else { } else {
/* packed perms in accept1 and accept2 */ /* packed perms in accept1 and accept2 */
flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
@ -770,6 +775,21 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
} }
} }
/* accept2 is in some cases being allocated, even with perms */
if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
/* add dfa flags table missing in v2 */
u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen;
u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags;
size_t tsize = table_size(noents, tdflags);
pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL);
if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
*info = "failed to alloc dfa flags table";
goto out;
}
pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents;
pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags;
}
/* /*
* Unfortunately due to a bug in earlier userspaces, a * Unfortunately due to a bug in earlier userspaces, a
* transition table may be present even when the dfa is * transition table may be present even when the dfa is
@ -783,9 +803,13 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
if (!pdb->dfa && pdb->trans.table) if (!pdb->dfa && pdb->trans.table)
aa_free_str_table(&pdb->trans); aa_free_str_table(&pdb->trans);
/* TODO: move compat mapping here, requires dfa merging first */ /* TODO:
/* TODO: move verify here, it has to be done after compat mappings */ * - move compat mapping here, requires dfa merging first
* - move verify here, it has to be done after compat mappings
* - move free of unneeded trans table here, has to be done
* after perm mapping.
*/
out:
*policy = pdb; *policy = pdb;
return 0; return 0;
@ -862,7 +886,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
error = -ENOMEM; error = -ENOMEM;
goto fail; goto fail;
} }
rules = list_first_entry(&profile->rules, typeof(*rules), list); rules = profile->label.rules[0];
/* profile renaming is optional */ /* profile renaming is optional */
(void) aa_unpack_str(e, &profile->rename, "rename"); (void) aa_unpack_str(e, &profile->rename, "rename");
@ -898,6 +922,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
(void) aa_unpack_strdup(e, &disconnected, "disconnected"); (void) aa_unpack_strdup(e, &disconnected, "disconnected");
profile->disconnected = disconnected; profile->disconnected = disconnected;
/* optional */
(void) aa_unpack_u32(e, &profile->signal, "kill");
if (profile->signal < 1 || profile->signal > MAXMAPPED_SIG) {
info = "profile kill.signal invalid value";
goto fail;
}
/* per profile debug flags (complain, audit) */ /* per profile debug flags (complain, audit) */
if (!aa_unpack_nameX(e, AA_STRUCT, "flags")) { if (!aa_unpack_nameX(e, AA_STRUCT, "flags")) {
info = "profile missing flags"; info = "profile missing flags";
@ -1101,6 +1131,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail; goto fail;
} }
aa_compute_profile_mediates(profile);
return profile; return profile;
fail: fail:
@ -1215,21 +1247,32 @@ static bool verify_perm(struct aa_perms *perm)
static bool verify_perms(struct aa_policydb *pdb) static bool verify_perms(struct aa_policydb *pdb)
{ {
int i; int i;
int xidx, xmax = -1;
for (i = 0; i < pdb->size; i++) { for (i = 0; i < pdb->size; i++) {
if (!verify_perm(&pdb->perms[i])) if (!verify_perm(&pdb->perms[i]))
return false; return false;
/* verify indexes into str table */ /* verify indexes into str table */
if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE && if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE) {
(pdb->perms[i].xindex & AA_X_INDEX_MASK) >= pdb->trans.size) xidx = pdb->perms[i].xindex & AA_X_INDEX_MASK;
if (xidx >= pdb->trans.size)
return false; return false;
if (xmax < xidx)
xmax = xidx;
}
if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size)
return false; return false;
if (pdb->perms[i].label && if (pdb->perms[i].label &&
pdb->perms[i].label >= pdb->trans.size) pdb->perms[i].label >= pdb->trans.size)
return false; return false;
} }
/* deal with incorrectly constructed string tables */
if (xmax == -1) {
aa_free_str_table(&pdb->trans);
} else if (pdb->trans.size > xmax + 1) {
if (!aa_resize_str_table(&pdb->trans, xmax + 1, GFP_KERNEL))
return false;
}
return true; return true;
} }
@ -1243,8 +1286,8 @@ static bool verify_perms(struct aa_policydb *pdb)
*/ */
static int verify_profile(struct aa_profile *profile) static int verify_profile(struct aa_profile *profile)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
if (!rules) if (!rules)
return 0; return 0;

View file

@ -9,6 +9,8 @@
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
#include <linux/unaligned.h>
#define TEST_STRING_NAME "TEST_STRING" #define TEST_STRING_NAME "TEST_STRING"
#define TEST_STRING_DATA "testing" #define TEST_STRING_DATA "testing"
#define TEST_STRING_BUF_OFFSET \ #define TEST_STRING_BUF_OFFSET \
@ -80,7 +82,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
*(buf + 1) = strlen(TEST_U32_NAME) + 1; *(buf + 1) = strlen(TEST_U32_NAME) + 1;
strscpy(buf + 3, TEST_U32_NAME, e->end - (void *)(buf + 3)); strscpy(buf + 3, TEST_U32_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32; *(buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32;
*((__le32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = cpu_to_le32(TEST_U32_DATA); put_unaligned_le32(TEST_U32_DATA, buf + 3 + strlen(TEST_U32_NAME) + 2);
buf = e->start + TEST_NAMED_U64_BUF_OFFSET; buf = e->start + TEST_NAMED_U64_BUF_OFFSET;
*buf = AA_NAME; *buf = AA_NAME;
@ -103,7 +105,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
*(buf + 1) = strlen(TEST_ARRAY_NAME) + 1; *(buf + 1) = strlen(TEST_ARRAY_NAME) + 1;
strscpy(buf + 3, TEST_ARRAY_NAME, e->end - (void *)(buf + 3)); strscpy(buf + 3, TEST_ARRAY_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY; *(buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY;
*((__le16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = cpu_to_le16(TEST_ARRAY_SIZE); put_unaligned_le16(TEST_ARRAY_SIZE, buf + 3 + strlen(TEST_ARRAY_NAME) + 2);
return e; return e;
} }

View file

@ -125,12 +125,14 @@ int aa_setprocattr_changehat(char *args, size_t size, int flags)
for (count = 0; (hat < end) && count < 16; ++count) { for (count = 0; (hat < end) && count < 16; ++count) {
char *next = hat + strlen(hat) + 1; char *next = hat + strlen(hat) + 1;
hats[count] = hat; hats[count] = hat;
AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n" AA_DEBUG(DEBUG_DOMAIN,
"%s: (pid %d) Magic 0x%llx count %d hat '%s'\n"
, __func__, current->pid, token, count, hat); , __func__, current->pid, token, count, hat);
hat = next; hat = next;
} }
} else } else
AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n", AA_DEBUG(DEBUG_DOMAIN,
"%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n",
__func__, current->pid, token, count, "<NULL>"); __func__, current->pid, token, count, "<NULL>");
return aa_change_hat(hats, count, token, flags); return aa_change_hat(hats, count, token, flags);

View file

@ -89,8 +89,7 @@ static int profile_setrlimit(const struct cred *subj_cred,
struct aa_profile *profile, unsigned int resource, struct aa_profile *profile, unsigned int resource,
struct rlimit *new_rlim) struct rlimit *new_rlim)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
int e = 0; int e = 0;
if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max > if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
@ -165,9 +164,7 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
* to the lesser of the tasks hard limit and the init tasks soft limit * to the lesser of the tasks hard limit and the init tasks soft limit
*/ */
label_for_each_confined(i, old_l, old) { label_for_each_confined(i, old_l, old) {
struct aa_ruleset *rules = list_first_entry(&old->rules, struct aa_ruleset *rules = old->label.rules[0];
typeof(*rules),
list);
if (rules->rlimits.mask) { if (rules->rlimits.mask) {
int j; int j;
@ -185,9 +182,7 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
/* set any new hard limits as dictated by the new profile */ /* set any new hard limits as dictated by the new profile */
label_for_each_confined(i, new_l, new) { label_for_each_confined(i, new_l, new) {
struct aa_ruleset *rules = list_first_entry(&new->rules, struct aa_ruleset *rules = new->label.rules[0];
typeof(*rules),
list);
int j; int j;
if (!rules->rlimits.mask) if (!rules->rlimits.mask)

View file

@ -228,8 +228,7 @@ static int profile_ptrace_perm(const struct cred *cred,
struct aa_label *peer, u32 request, struct aa_label *peer, u32 request,
struct apparmor_audit_data *ad) struct apparmor_audit_data *ad)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules), list);
struct aa_perms perms = { }; struct aa_perms perms = { };
ad->subj_cred = cred; ad->subj_cred = cred;
@ -246,7 +245,7 @@ static int profile_tracee_perm(const struct cred *cred,
struct apparmor_audit_data *ad) struct apparmor_audit_data *ad)
{ {
if (profile_unconfined(tracee) || unconfined(tracer) || if (profile_unconfined(tracee) || unconfined(tracer) ||
!ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE)) !label_mediates(&tracee->label, AA_CLASS_PTRACE))
return 0; return 0;
return profile_ptrace_perm(cred, tracee, tracer, request, ad); return profile_ptrace_perm(cred, tracee, tracer, request, ad);
@ -260,7 +259,7 @@ static int profile_tracer_perm(const struct cred *cred,
if (profile_unconfined(tracer)) if (profile_unconfined(tracer))
return 0; return 0;
if (ANY_RULE_MEDIATES(&tracer->rules, AA_CLASS_PTRACE)) if (label_mediates(&tracer->label, AA_CLASS_PTRACE))
return profile_ptrace_perm(cred, tracer, tracee, request, ad); return profile_ptrace_perm(cred, tracer, tracee, request, ad);
/* profile uses the old style capability check for ptrace */ /* profile uses the old style capability check for ptrace */
@ -324,9 +323,7 @@ int aa_profile_ns_perm(struct aa_profile *profile,
ad->request = request; ad->request = request;
if (!profile_unconfined(profile)) { if (!profile_unconfined(profile)) {
struct aa_ruleset *rules = list_first_entry(&profile->rules, struct aa_ruleset *rules = profile->label.rules[0];
typeof(*rules),
list);
aa_state_t state; aa_state_t state;
state = RULE_MEDIATES(rules, ad->class); state = RULE_MEDIATES(rules, ad->class);