* 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:
Linus Torvalds 2024-11-29 11:10:30 -08:00
commit 29caf07e9d
18 changed files with 141 additions and 246 deletions

View File

@ -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

View File

@ -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),
{ }
};

View File

@ -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
*/

View File

@ -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");
}

View File

@ -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,

View File

@ -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);

View File

@ -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))

View File

@ -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 *));

View File

@ -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);

View File

@ -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 */

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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) &&

View File

@ -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)

View File

@ -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
*/