mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 16:56:26 +00:00
* Features
- extend next/check table to add support for 2^24 states to the state machine. - rework capability audit cache to use broader cred information instead of just the profile. Also add a time stamp so old entries can be aged out of the cache. * Bug Fixes - fix 'Do simple duplicate message elimination' to clear previous state when updating in capability audit cache - Fix memory leak for aa_unpack_strdup() - properly handle cx/px lookup failure when in complain mode - allocate xmatch for nullpdb inside aa_alloc_null fixing a NULL ptr deref of tracking profiles in when in complain mode * Cleanups - Remove everything being reported as deadcode - replace misleading 'scrubbing environment' phrase in debug print - Remove unnecessary NULL check before kvfree() - clean up duplicated parts of handle_onexec() - Use IS_ERR_OR_NULL() helper function - move new_profile declaration to top of block instead immediately after label to remove C23 extension warning * Documentation - add comment to document capability.c:profile_capable ad ptr parameter can not be NULL - add comment to document first entry is in packed perms struct is reserved for future planned expansion. - Update LSM/apparmor.rst add blurb for CONFIG_DEFAULT_SECURITY_APPARMOR -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE7cSDD705q2rFEEf7BS82cBjVw9gFAmdHgIgACgkQBS82cBjV w9jwFA//egzWWJtLKKgd4QJ/kfPJS/tYnnamZI7b+64Aqe2a+WP6tYZ7dNBrMFff Y5svjKDOkotLXKz01+rsnecf5o8SVNuU+6XSYYX+WIuSfeMHcxB3lI1SDEQF/tdk ODMfvmI0O9SVwXlkIw2BPA8S06HsrFSXj2KLBvZEGCHX4Ur4Dj2WrmOuZ8Otk9rK fUez9Om/Rc2cunaCEzZ53zfX5IjhN6yYYMc9ANDhsH5TaEvryIt1GzhnfSpKrUgm zJmK/h85ihgbTH+d5gwNuh4jfRMOqvDy6nBeNtSwp/AqDqMyHdtgSyX1oYRvS5nf 9EC94fyW22/DVRFF+DS4iUs9RBWvMyyeqdylpsxP66p+qGky6W72VUJi0+5JS6l8 CWelY65g2p3A6NKzgcxdBz35364g+0v1qNEoFTZUA3nz2mNfDAemjG6zgq7ABhLF hrF/RLyTNTOECI83KuHWuvKxpPYeZoSj/PFkCCQI+56/vpcdOlJooTFUJP5kUNyj WZK4X6uNbVIoRHlGOg0zHbC1eqAPEdGdBt0sYJb2DYSYu/fZ6xsAy3olk1FR2uhD K69LpUQNt1JqV3jlM1y6c4b+d9Rc9rMOVzW14oDLtMfTY3BeCKu2VAY0bJ3mPvXb eIU32XsZr83J7iWcVCQb+/frS44/I9yjawKQ89aPsAOC4G0IcSw= =2M1G -----END PGP SIGNATURE----- Merge tag 'apparmor-pr-2024-11-27' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor Pull apparmor updates from John Johansen: "Features: - extend next/check table to add support for 2^24 states to the state machine. - rework capability audit cache to use broader cred information instead of just the profile. Also add a time stamp so old entries can be aged out of the cache. Bug Fixes: - fix 'Do simple duplicate message elimination' to clear previous state when updating in capability audit cache - Fix memory leak for aa_unpack_strdup() - properly handle cx/px lookup failure when in complain mode - allocate xmatch for nullpdb inside aa_alloc_null fixing a NULL ptr deref of tracking profiles in when in complain mode Cleanups: - Remove everything being reported as deadcode - replace misleading 'scrubbing environment' phrase in debug print - Remove unnecessary NULL check before kvfree() - clean up duplicated parts of handle_onexec() - Use IS_ERR_OR_NULL() helper function - move new_profile declaration to top of block instead immediately after label to remove C23 extension warning Documentation: - add comment to document capability.c:profile_capable ad ptr parameter can not be NULL - add comment to document first entry is in packed perms struct is reserved for future planned expansion. - Update LSM/apparmor.rst add blurb for DEFAULT_SECURITY_APPARMOR" * tag 'apparmor-pr-2024-11-27' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: apparmor: lift new_profile declaration to remove C23 extension warning apparmor: replace misleading 'scrubbing environment' phrase in debug print parser: drop dead code for XXX_comb macros apparmor: Remove unused parameter L1 in macro next_comb Docs: Update LSM/apparmor.rst apparmor: audit_cap dedup based on subj_cred instead of profile apparmor: add a cache entry expiration time aging out capability audit cache apparmor: document capability.c:profile_capable ad ptr not being NULL apparmor: fix 'Do simple duplicate message elimination' apparmor: document first entry is in packed perms struct is reserved apparmor: test: Fix memory leak for aa_unpack_strdup() apparmor: Remove deadcode apparmor: Remove unnecessary NULL check before kvfree() apparmor: domain: clean up duplicated parts of handle_onexec() apparmor: Use IS_ERR_OR_NULL() helper function apparmor: add support for 2^24 states to the dfa state machine. apparmor: properly handle cx/px lookup failure for complain apparmor: allocate xmatch for nullpdb inside aa_alloc_null
This commit is contained in:
commit
29caf07e9d
@ -18,8 +18,11 @@ set ``CONFIG_SECURITY_APPARMOR=y``
|
||||
|
||||
If AppArmor should be selected as the default security module then set::
|
||||
|
||||
CONFIG_DEFAULT_SECURITY="apparmor"
|
||||
CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1
|
||||
CONFIG_DEFAULT_SECURITY_APPARMOR=y
|
||||
|
||||
The CONFIG_LSM parameter manages the order and selection of LSMs.
|
||||
Specify apparmor as the first "major" module (e.g. AppArmor, SELinux, Smack)
|
||||
in the list.
|
||||
|
||||
Build the kernel
|
||||
|
||||
|
@ -2366,6 +2366,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
|
||||
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
|
||||
AA_SFS_FILE_U64("permstable32_version", 1),
|
||||
AA_SFS_FILE_STRING("permstable32", PERMS32STR),
|
||||
AA_SFS_FILE_U64("state32", 1),
|
||||
AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
|
||||
{ }
|
||||
};
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include "include/apparmor.h"
|
||||
#include "include/capability.h"
|
||||
@ -30,8 +31,9 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = {
|
||||
};
|
||||
|
||||
struct audit_cache {
|
||||
struct aa_profile *profile;
|
||||
kernel_cap_t caps;
|
||||
const struct cred *ad_subj_cred;
|
||||
/* Capabilities go from 0 to CAP_LAST_CAP */
|
||||
u64 ktime_ns_expiration[CAP_LAST_CAP+1];
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct audit_cache, audit_cache);
|
||||
@ -64,6 +66,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile,
|
||||
int cap, int error)
|
||||
{
|
||||
const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */
|
||||
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct audit_cache *ent;
|
||||
@ -89,15 +93,16 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
|
||||
|
||||
/* Do simple duplicate message elimination */
|
||||
ent = &get_cpu_var(audit_cache);
|
||||
if (profile == ent->profile && cap_raised(ent->caps, cap)) {
|
||||
/* If the capability was never raised the timestamp check would also catch that */
|
||||
if (ad->subj_cred == ent->ad_subj_cred && ktime_get_ns() <= ent->ktime_ns_expiration[cap]) {
|
||||
put_cpu_var(audit_cache);
|
||||
if (COMPLAIN_MODE(profile))
|
||||
return complain_error(error);
|
||||
return error;
|
||||
} else {
|
||||
aa_put_profile(ent->profile);
|
||||
ent->profile = aa_get_profile(profile);
|
||||
cap_raise(ent->caps, cap);
|
||||
put_cred(ent->ad_subj_cred);
|
||||
ent->ad_subj_cred = get_cred(ad->subj_cred);
|
||||
ent->ktime_ns_expiration[cap] = ktime_get_ns() + AUDIT_CACHE_TIMEOUT_NS;
|
||||
}
|
||||
put_cpu_var(audit_cache);
|
||||
|
||||
@ -109,7 +114,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
|
||||
* @profile: profile being enforced (NOT NULL, NOT unconfined)
|
||||
* @cap: capability to test if allowed
|
||||
* @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
|
||||
* @ad: audit data (MAY BE NULL indicating no auditing)
|
||||
* @ad: audit data (NOT NULL)
|
||||
*
|
||||
* Returns: 0 if allowed else -EPERM
|
||||
*/
|
||||
|
@ -636,6 +636,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_label *new = NULL;
|
||||
struct aa_profile *new_profile = NULL;
|
||||
const char *info = NULL, *name = NULL, *target = NULL;
|
||||
aa_state_t state = rules->file->start[AA_CLASS_FILE];
|
||||
struct aa_perms perms = {};
|
||||
@ -680,15 +681,18 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
|
||||
/* hack ix fallback - improve how this is detected */
|
||||
goto audit;
|
||||
} else if (!new) {
|
||||
error = -EACCES;
|
||||
info = "profile transition not found";
|
||||
/* remove MAY_EXEC to audit as failure */
|
||||
/* remove MAY_EXEC to audit as failure or complaint */
|
||||
perms.allow &= ~MAY_EXEC;
|
||||
if (COMPLAIN_MODE(profile)) {
|
||||
/* create null profile instead of failing */
|
||||
goto create_learning_profile;
|
||||
}
|
||||
error = -EACCES;
|
||||
}
|
||||
} else if (COMPLAIN_MODE(profile)) {
|
||||
create_learning_profile:
|
||||
/* no exec permission - learning mode */
|
||||
struct aa_profile *new_profile = NULL;
|
||||
|
||||
new_profile = aa_new_learning_profile(profile, false, name,
|
||||
GFP_KERNEL);
|
||||
if (!new_profile) {
|
||||
@ -709,8 +713,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
|
||||
|
||||
if (!(perms.xindex & AA_X_UNSAFE)) {
|
||||
if (DEBUG_ON) {
|
||||
dbg_printk("apparmor: scrubbing environment variables"
|
||||
" for %s profile=", name);
|
||||
dbg_printk("apparmor: setting AT_SECURE for %s profile=",
|
||||
name);
|
||||
aa_label_printk(new, GFP_KERNEL);
|
||||
dbg_printk("\n");
|
||||
}
|
||||
@ -789,8 +793,8 @@ static int profile_onexec(const struct cred *subj_cred,
|
||||
|
||||
if (!(perms.xindex & AA_X_UNSAFE)) {
|
||||
if (DEBUG_ON) {
|
||||
dbg_printk("apparmor: scrubbing environment "
|
||||
"variables for %s label=", xname);
|
||||
dbg_printk("apparmor: setting AT_SECURE for %s label=",
|
||||
xname);
|
||||
aa_label_printk(onexec, GFP_KERNEL);
|
||||
dbg_printk("\n");
|
||||
}
|
||||
@ -821,33 +825,19 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
|
||||
AA_BUG(!bprm);
|
||||
AA_BUG(!buffer);
|
||||
|
||||
if (!stack) {
|
||||
error = fn_for_each_in_ns(label, profile,
|
||||
profile_onexec(subj_cred, profile, onexec, stack,
|
||||
bprm, buffer, cond, unsafe));
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
|
||||
aa_get_newest_label(onexec),
|
||||
profile_transition(subj_cred, profile, bprm,
|
||||
buffer,
|
||||
cond, unsafe));
|
||||
|
||||
} else {
|
||||
/* TODO: determine how much we want to loosen this */
|
||||
error = fn_for_each_in_ns(label, profile,
|
||||
profile_onexec(subj_cred, profile, onexec, stack, bprm,
|
||||
buffer, cond, unsafe));
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
|
||||
aa_label_merge(&profile->label, onexec,
|
||||
GFP_KERNEL),
|
||||
profile_transition(subj_cred, profile, bprm,
|
||||
buffer,
|
||||
cond, unsafe));
|
||||
}
|
||||
/* TODO: determine how much we want to loosen this */
|
||||
error = fn_for_each_in_ns(label, profile,
|
||||
profile_onexec(subj_cred, profile, onexec, stack,
|
||||
bprm, buffer, cond, unsafe));
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
|
||||
stack ? aa_label_merge(&profile->label, onexec,
|
||||
GFP_KERNEL)
|
||||
: aa_get_newest_label(onexec),
|
||||
profile_transition(subj_cred, profile, bprm,
|
||||
buffer, cond, unsafe));
|
||||
if (new)
|
||||
return new;
|
||||
|
||||
@ -960,8 +950,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||
|
||||
if (unsafe) {
|
||||
if (DEBUG_ON) {
|
||||
dbg_printk("scrubbing environment variables for %s "
|
||||
"label=", bprm->filename);
|
||||
dbg_printk("setting AT_SECURE for %s label=",
|
||||
bprm->filename);
|
||||
aa_label_printk(new, GFP_KERNEL);
|
||||
dbg_printk("\n");
|
||||
}
|
||||
@ -971,8 +961,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||
if (label->proxy != new->proxy) {
|
||||
/* when transitioning clear unsafe personality bits */
|
||||
if (DEBUG_ON) {
|
||||
dbg_printk("apparmor: clearing unsafe personality "
|
||||
"bits. %s label=", bprm->filename);
|
||||
dbg_printk("apparmor: clearing unsafe personality bits. %s label=",
|
||||
bprm->filename);
|
||||
aa_label_printk(new, GFP_KERNEL);
|
||||
dbg_printk("\n");
|
||||
}
|
||||
|
@ -160,32 +160,8 @@ int aa_label_next_confined(struct aa_label *l, int i);
|
||||
#define label_for_each_cont(I, L, P) \
|
||||
for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i))
|
||||
|
||||
#define next_comb(I, L1, L2) \
|
||||
do { \
|
||||
(I).j++; \
|
||||
if ((I).j >= (L2)->size) { \
|
||||
(I).i++; \
|
||||
(I).j = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* for each combination of P1 in L1, and P2 in L2 */
|
||||
#define label_for_each_comb(I, L1, L2, P1, P2) \
|
||||
for ((I).i = (I).j = 0; \
|
||||
((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \
|
||||
(I) = next_comb(I, L1, L2))
|
||||
|
||||
#define fn_for_each_comb(L1, L2, P1, P2, FN) \
|
||||
({ \
|
||||
struct label_it i; \
|
||||
int __E = 0; \
|
||||
label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \
|
||||
last_error(__E, (FN)); \
|
||||
} \
|
||||
__E; \
|
||||
})
|
||||
|
||||
/* for each profile that is enforcing confinement in a label */
|
||||
#define label_for_each_confined(I, L, P) \
|
||||
for ((I).i = aa_label_next_confined((L), 0); \
|
||||
@ -291,8 +267,6 @@ bool aa_label_replace(struct aa_label *old, struct aa_label *new);
|
||||
bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old,
|
||||
struct aa_label *new);
|
||||
|
||||
struct aa_label *aa_label_find(struct aa_label *l);
|
||||
|
||||
struct aa_profile *aa_label_next_in_merge(struct label_it *I,
|
||||
struct aa_label *a,
|
||||
struct aa_label *b);
|
||||
@ -320,8 +294,6 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
|
||||
struct aa_label *label, int flags, gfp_t gfp);
|
||||
void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
|
||||
gfp_t gfp);
|
||||
void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp);
|
||||
void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp);
|
||||
void aa_label_printk(struct aa_label *label, gfp_t gfp);
|
||||
|
||||
struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
|
||||
|
@ -59,7 +59,6 @@ extern int apparmor_initialized;
|
||||
|
||||
/* fn's in lib */
|
||||
const char *skipn_spaces(const char *str, size_t n);
|
||||
char *aa_split_fqname(char *args, char **ns_name);
|
||||
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
|
||||
size_t *ns_len);
|
||||
void aa_info_message(const char *str);
|
||||
|
@ -87,10 +87,12 @@ struct table_header {
|
||||
char td_data[];
|
||||
};
|
||||
|
||||
#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data))
|
||||
#define TABLE_DATAU16(TABLE) ((u16 *)((TABLE)->td_data))
|
||||
#define TABLE_DATAU32(TABLE) ((u32 *)((TABLE)->td_data))
|
||||
#define DEFAULT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_DEF]->td_data))
|
||||
#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data))
|
||||
#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data))
|
||||
#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data))
|
||||
#define NEXT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_NXT]->td_data))
|
||||
#define CHECK_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_CHK]->td_data))
|
||||
#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data))
|
||||
#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data))
|
||||
#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data))
|
||||
|
@ -213,9 +213,6 @@ void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
|
||||
void aa_profile_match_label(struct aa_profile *profile,
|
||||
struct aa_ruleset *rules, struct aa_label *label,
|
||||
int type, u32 request, struct aa_perms *perms);
|
||||
int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
|
||||
u32 request, int type, u32 *deny,
|
||||
struct apparmor_audit_data *ad);
|
||||
int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
|
||||
u32 request, struct apparmor_audit_data *ad,
|
||||
void (*cb)(struct audit_buffer *, void *));
|
||||
|
@ -264,7 +264,6 @@ void aa_free_profile(struct aa_profile *profile);
|
||||
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
|
||||
struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
|
||||
size_t n);
|
||||
struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
|
||||
struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
|
||||
const char *fqname, size_t n);
|
||||
|
||||
|
@ -34,6 +34,5 @@ void apparmor_release_secctx(char *secdata, u32 seclen);
|
||||
|
||||
int aa_alloc_secid(struct aa_label *label, gfp_t gfp);
|
||||
void aa_free_secid(u32 secid);
|
||||
void aa_secid_update(u32 secid, struct aa_label *label);
|
||||
|
||||
#endif /* __AA_SECID_H */
|
||||
|
@ -899,23 +899,6 @@ struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
|
||||
return vec_create_and_insert_label(vec, len, gfp);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_label_find - find label @label in label set
|
||||
* @label: label to find (NOT NULL)
|
||||
*
|
||||
* Requires: caller to hold a valid ref on l
|
||||
*
|
||||
* Returns: refcounted @label if @label is in tree
|
||||
* refcounted label that is equiv to @label in tree
|
||||
* else NULL if @label or equiv is not in tree
|
||||
*/
|
||||
struct aa_label *aa_label_find(struct aa_label *label)
|
||||
{
|
||||
AA_BUG(!label);
|
||||
|
||||
return vec_find(label->vec, label->size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* aa_label_insert - insert label @label into @ls or return existing label
|
||||
@ -1811,22 +1794,6 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
|
||||
pr_info("%s", label->hname);
|
||||
}
|
||||
|
||||
void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
|
||||
{
|
||||
struct aa_ns *ns = aa_get_current_ns();
|
||||
|
||||
aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
|
||||
aa_put_ns(ns);
|
||||
}
|
||||
|
||||
void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
|
||||
{
|
||||
struct aa_ns *ns = aa_get_current_ns();
|
||||
|
||||
aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
|
||||
aa_put_ns(ns);
|
||||
}
|
||||
|
||||
void aa_label_printk(struct aa_label *label, gfp_t gfp)
|
||||
{
|
||||
struct aa_ns *ns = aa_get_current_ns();
|
||||
|
@ -45,44 +45,6 @@ void aa_free_str_table(struct aa_str_table *t)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_split_fqname - split a fqname into a profile and namespace name
|
||||
* @fqname: a full qualified name in namespace profile format (NOT NULL)
|
||||
* @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
|
||||
*
|
||||
* Returns: profile name or NULL if one is not specified
|
||||
*
|
||||
* Split a namespace name from a profile name (see policy.c for naming
|
||||
* description). If a portion of the name is missing it returns NULL for
|
||||
* that portion.
|
||||
*
|
||||
* NOTE: may modify the @fqname string. The pointers returned point
|
||||
* into the @fqname string.
|
||||
*/
|
||||
char *aa_split_fqname(char *fqname, char **ns_name)
|
||||
{
|
||||
char *name = strim(fqname);
|
||||
|
||||
*ns_name = NULL;
|
||||
if (name[0] == ':') {
|
||||
char *split = strchr(&name[1], ':');
|
||||
*ns_name = skip_spaces(&name[1]);
|
||||
if (split) {
|
||||
/* overwrite ':' with \0 */
|
||||
*split++ = 0;
|
||||
if (strncmp(split, "//", 2) == 0)
|
||||
split += 2;
|
||||
name = skip_spaces(split);
|
||||
} else
|
||||
/* a ns name without a following profile is allowed */
|
||||
name = NULL;
|
||||
}
|
||||
if (name && *name == 0)
|
||||
name = NULL;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* skipn_spaces - Removes leading whitespace from @str.
|
||||
* @str: The string to be stripped.
|
||||
@ -275,33 +237,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
|
||||
audit_log_format(ab, "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_audit_perms_cb - generic callback fn for auditing perms
|
||||
* @ab: audit buffer (NOT NULL)
|
||||
* @va: audit struct to audit values of (NOT NULL)
|
||||
*/
|
||||
static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
|
||||
{
|
||||
struct common_audit_data *sa = va;
|
||||
struct apparmor_audit_data *ad = aad(sa);
|
||||
|
||||
if (ad->request) {
|
||||
audit_log_format(ab, " requested_mask=");
|
||||
aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs,
|
||||
PERMS_CHRS_MASK, aa_file_perm_names,
|
||||
PERMS_NAMES_MASK);
|
||||
}
|
||||
if (ad->denied) {
|
||||
audit_log_format(ab, "denied_mask=");
|
||||
aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs,
|
||||
PERMS_CHRS_MASK, aa_file_perm_names,
|
||||
PERMS_NAMES_MASK);
|
||||
}
|
||||
audit_log_format(ab, " peer=");
|
||||
aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
|
||||
FLAGS_NONE, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_apply_modes_to_perms - apply namespace and profile flags to perms
|
||||
* @profile: that perms where computed from
|
||||
@ -349,25 +284,6 @@ void aa_profile_match_label(struct aa_profile *profile,
|
||||
}
|
||||
|
||||
|
||||
/* currently unused */
|
||||
int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
|
||||
u32 request, int type, u32 *deny,
|
||||
struct apparmor_audit_data *ad)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms perms;
|
||||
|
||||
ad->peer = &target->label;
|
||||
ad->request = request;
|
||||
|
||||
aa_profile_match_label(profile, rules, &target->label, type, request,
|
||||
&perms);
|
||||
aa_apply_modes_to_perms(profile, &perms);
|
||||
*deny |= request & perms.deny;
|
||||
return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_check_perms - do audit mode selection based on perms set
|
||||
* @profile: profile being checked
|
||||
|
@ -247,6 +247,42 @@ void aa_dfa_free_kref(struct kref *kref)
|
||||
dfa_free(dfa);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* remap_data16_to_data32 - remap u16 @old table to a u32 based table
|
||||
* @old: table to remap
|
||||
*
|
||||
* Returns: new table with u32 entries instead of u16.
|
||||
*
|
||||
* Note: will free @old so caller does not have to
|
||||
*/
|
||||
static struct table_header *remap_data16_to_data32(struct table_header *old)
|
||||
{
|
||||
struct table_header *new;
|
||||
size_t tsize;
|
||||
u32 i;
|
||||
|
||||
tsize = table_size(old->td_lolen, YYTD_DATA32);
|
||||
new = kvzalloc(tsize, GFP_KERNEL);
|
||||
if (!new) {
|
||||
kvfree(old);
|
||||
return NULL;
|
||||
}
|
||||
new->td_id = old->td_id;
|
||||
new->td_flags = YYTD_DATA32;
|
||||
new->td_lolen = old->td_lolen;
|
||||
|
||||
for (i = 0; i < old->td_lolen; i++)
|
||||
TABLE_DATAU32(new)[i] = (u32) TABLE_DATAU16(old)[i];
|
||||
|
||||
kvfree(old);
|
||||
if (is_vmalloc_addr(new))
|
||||
vm_unmap_aliases();
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_dfa_unpack - unpack the binary tables of a serialized dfa
|
||||
* @blob: aligned serialized stream of data to unpack (NOT NULL)
|
||||
@ -326,8 +362,10 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
|
||||
case YYTD_ID_DEF:
|
||||
case YYTD_ID_NXT:
|
||||
case YYTD_ID_CHK:
|
||||
if (table->td_flags != YYTD_DATA16)
|
||||
if (!(table->td_flags == YYTD_DATA16 ||
|
||||
table->td_flags == YYTD_DATA32)) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case YYTD_ID_EC:
|
||||
if (table->td_flags != YYTD_DATA8)
|
||||
@ -342,6 +380,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
|
||||
dfa->tables[table->td_id] = table;
|
||||
data += table_size(table->td_lolen, table->td_flags);
|
||||
size -= table_size(table->td_lolen, table->td_flags);
|
||||
|
||||
/*
|
||||
* this remapping has to be done after incrementing data above
|
||||
* for now straight remap, later have dfa support both
|
||||
*/
|
||||
switch (table->td_id) {
|
||||
case YYTD_ID_DEF:
|
||||
case YYTD_ID_NXT:
|
||||
case YYTD_ID_CHK:
|
||||
if (table->td_flags == YYTD_DATA16) {
|
||||
table = remap_data16_to_data32(table);
|
||||
if (!table)
|
||||
goto fail;
|
||||
}
|
||||
dfa->tables[table->td_id] = table;
|
||||
break;
|
||||
}
|
||||
table = NULL;
|
||||
}
|
||||
error = verify_table_headers(dfa->tables, flags);
|
||||
@ -395,10 +450,10 @@ do { \
|
||||
aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
|
||||
const char *str, int len)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
aa_state_t state = start;
|
||||
|
||||
if (state == DFA_NOMATCH)
|
||||
@ -434,10 +489,10 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
|
||||
*/
|
||||
aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
aa_state_t state = start;
|
||||
|
||||
if (state == DFA_NOMATCH)
|
||||
@ -472,10 +527,10 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
|
||||
*/
|
||||
aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
@ -490,10 +545,10 @@ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
|
||||
|
||||
aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
u32 b = (base)[(state)];
|
||||
|
||||
if (!(b & MATCH_FLAG_OOB_TRANSITION))
|
||||
@ -521,10 +576,10 @@ aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
|
||||
aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
|
||||
const char *str, const char **retpos)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
u32 *accept = ACCEPT_TABLE(dfa);
|
||||
aa_state_t state = start, pos;
|
||||
|
||||
@ -582,10 +637,10 @@ aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
|
||||
aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
|
||||
const char *str, int n, const char **retpos)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
u32 *accept = ACCEPT_TABLE(dfa);
|
||||
aa_state_t state = start, pos;
|
||||
|
||||
@ -658,10 +713,10 @@ static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,
|
||||
const char *str, struct match_workbuf *wb,
|
||||
unsigned int *count)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *next = NEXT_TABLE(dfa);
|
||||
u32 *check = CHECK_TABLE(dfa);
|
||||
aa_state_t state = start, pos;
|
||||
|
||||
AA_BUG(!dfa);
|
||||
|
@ -130,7 +130,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
|
||||
/* handle error conditions - and still allow a partial path to
|
||||
* be returned.
|
||||
*/
|
||||
if (!res || IS_ERR(res)) {
|
||||
if (IS_ERR_OR_NULL(res)) {
|
||||
if (PTR_ERR(res) == -ENAMETOOLONG) {
|
||||
error = -ENAMETOOLONG;
|
||||
*name = buf;
|
||||
|
@ -103,8 +103,7 @@ static void aa_free_pdb(struct aa_policydb *pdb)
|
||||
{
|
||||
if (pdb) {
|
||||
aa_put_dfa(pdb->dfa);
|
||||
if (pdb->perms)
|
||||
kvfree(pdb->perms);
|
||||
kvfree(pdb->perms);
|
||||
aa_free_str_table(&pdb->trans);
|
||||
kfree(pdb);
|
||||
}
|
||||
@ -580,11 +579,6 @@ struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
|
||||
return profile;
|
||||
}
|
||||
|
||||
struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname)
|
||||
{
|
||||
return aa_lookupn_profile(ns, hname, strlen(hname));
|
||||
}
|
||||
|
||||
struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
|
||||
const char *fqname, size_t n)
|
||||
{
|
||||
@ -626,6 +620,7 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
|
||||
|
||||
/* TODO: ideally we should inherit abi from parent */
|
||||
profile->label.flags |= FLAG_NULL;
|
||||
profile->attach.xmatch = aa_get_pdb(nullpdb);
|
||||
rules = list_first_entry(&profile->rules, typeof(*rules), list);
|
||||
rules->file = aa_get_pdb(nullpdb);
|
||||
rules->policy = aa_get_pdb(nullpdb);
|
||||
|
@ -645,10 +645,13 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_ruleset *rules)
|
||||
|
||||
static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm)
|
||||
{
|
||||
u32 reserved;
|
||||
|
||||
if (version != 1)
|
||||
return false;
|
||||
|
||||
return aa_unpack_u32(e, &perm->allow, NULL) &&
|
||||
/* reserved entry is for later expansion, discard for now */
|
||||
return aa_unpack_u32(e, &reserved, NULL) &&
|
||||
aa_unpack_u32(e, &perm->allow, NULL) &&
|
||||
aa_unpack_u32(e, &perm->deny, NULL) &&
|
||||
aa_unpack_u32(e, &perm->subtree, NULL) &&
|
||||
|
@ -281,6 +281,8 @@ static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test)
|
||||
((uintptr_t)puf->e->start <= (uintptr_t)string)
|
||||
&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
|
||||
KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA);
|
||||
|
||||
kfree(string);
|
||||
}
|
||||
|
||||
static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test)
|
||||
@ -296,6 +298,8 @@ static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test)
|
||||
((uintptr_t)puf->e->start <= (uintptr_t)string)
|
||||
&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
|
||||
KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA);
|
||||
|
||||
kfree(string);
|
||||
}
|
||||
|
||||
static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test)
|
||||
@ -313,6 +317,8 @@ static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, size, 0);
|
||||
KUNIT_EXPECT_NULL(test, string);
|
||||
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start);
|
||||
|
||||
kfree(string);
|
||||
}
|
||||
|
||||
static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test)
|
||||
|
@ -39,20 +39,6 @@ int apparmor_display_secid_mode;
|
||||
* TODO: use secid_update in label replace
|
||||
*/
|
||||
|
||||
/**
|
||||
* aa_secid_update - update a secid mapping to a new label
|
||||
* @secid: secid to update
|
||||
* @label: label the secid will now map to
|
||||
*/
|
||||
void aa_secid_update(u32 secid, struct aa_label *label)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
xa_lock_irqsave(&aa_secids, flags);
|
||||
__xa_store(&aa_secids, secid, label, 0);
|
||||
xa_unlock_irqrestore(&aa_secids, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* see label for inverse aa_label_to_secid
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user