mirror of
https://github.com/torvalds/linux.git
synced 2025-08-15 14:11:42 +02:00
+ 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:
commit
8b45c6c90a
38 changed files with 2179 additions and 431 deletions
|
@ -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
799
security/apparmor/af_unix.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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),
|
||||||
{ }
|
{ }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
55
security/apparmor/include/af_unix.h
Normal file
55
security/apparmor/include/af_unix.h
Normal 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 */
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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_); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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, \
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
19
security/apparmor/include/signal.h
Normal file
19
security/apparmor/include/signal.h
Normal 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 */
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = { };
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue