Tag summary

+ Features
   - Replace zero-length array with flexible-array
   - add a valid state flags check
   - add consistency check between state and dfa diff encode flags
   - add apparmor subdir to proc attr interface
   - fail unpack if profile mode is unknown
   - add outofband transition and use it in xattr match
   - ensure that dfa state tables have entries
 
 + Cleanups
   - Use true and false for bool variable
   - Remove semicolon
   - Clean code by removing redundant instructions
   - Replace two seq_printf() calls by seq_puts() in aa_label_seq_xprint()
   - remove duplicate check of xattrs on profile attachment
   - remove useless aafs_create_symlink
 
 + Bug fixes
   - Fix memory leak of profile proxy
   - fix introspection of of task mode for unconfined tasks
   - fix nnp subset test for unconfined
   - check/put label on apparmor_sk_clone_security()
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE7cSDD705q2rFEEf7BS82cBjVw9gFAl7dUf4ACgkQBS82cBjV
 w9j8rA//R3qbVeiN3SJtxLhiF3AAdP2cVbZ/mAhQLwYObI6flb1bliiahJHRf8Ey
 FaVb4srOH8NlmzNINZehXOvD3UDwX/sbpw8h0Y0JolO+v1m3UXkt/eRoMt6gRz7I
 jtaImY1/V+G4O5rV5fGA1HQI8Geg+W9Abt32d16vyKIIpnBS/Pfv8ppM0NcHCZ4G
 e8935T/dMNK5K0Y7HNb1nMjyzEr0LtEXvXznBOrGVpCtDQ45m0/NBvAqpfhuKsVm
 FE5Na8rgtiB9sU72LaoNXNr8Y5LVgkXPmBr/e1FqZtF01XEarKb7yJDGOLrLpp1o
 rGYpY9DQSBT/ZZrwMaLFqCd1XtnN1BAmhlM6TXfnm25ArEnQ49ReHFc7ZHZRSTZz
 LWVBD6atZbapvqckk1SU49eCLuGs5wmRj/CmwdoQUbZ+aOfR68zF+0PANbP5xDo4
 862MmeMsm8JHndeCelpZQRbhtXt0t9MDzwMBevKhxV9hbpt4g8DcnC5tNUc9AnJi
 qJDsMkytYhazIW+/4MsnLTo9wzhqzXq5kBeE++Xl7vDE/V+d5ocvQg73xtwQo9sx
 LzMlh3cPmBvOnlpYfnONZP8pJdjDAuESsi/H5+RKQL3cLz7NX31CLWR8dXLBHy80
 Dvxqvy84Cf7buigqwSzgAGKjDI5HmeOECAMjpLbEB2NS9xxQYuk=
 =U7d2
 -----END PGP SIGNATURE-----

Merge tag 'apparmor-pr-2020-06-07' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "Features:
   - Replace zero-length array with flexible-array
   - add a valid state flags check
   - add consistency check between state and dfa diff encode flags
   - add apparmor subdir to proc attr interface
   - fail unpack if profile mode is unknown
   - add outofband transition and use it in xattr match
   - ensure that dfa state tables have entries

  Cleanups:
   - Use true and false for bool variable
   - Remove semicolon
   - Clean code by removing redundant instructions
   - Replace two seq_printf() calls by seq_puts() in aa_label_seq_xprint()
   - remove duplicate check of xattrs on profile attachment
   - remove useless aafs_create_symlink

  Bug fixes:
   - Fix memory leak of profile proxy
   - fix introspection of of task mode for unconfined tasks
   - fix nnp subset test for unconfined
   - check/put label on apparmor_sk_clone_security()"

* tag 'apparmor-pr-2020-06-07' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor:
  apparmor: Fix memory leak of profile proxy
  apparmor: fix introspection of of task mode for unconfined tasks
  apparmor: check/put label on apparmor_sk_clone_security()
  apparmor: Use true and false for bool variable
  security/apparmor/label.c: Clean code by removing redundant instructions
  apparmor: Replace zero-length array with flexible-array
  apparmor: ensure that dfa state tables have entries
  apparmor: remove duplicate check of xattrs on profile attachment.
  apparmor: add outofband transition and use it in xattr match
  apparmor: fail unpack if profile mode is unknown
  apparmor: fix nnp subset test for unconfined
  apparmor: remove useless aafs_create_symlink
  apparmor: add proc subdir to attrs
  apparmor: add consistency check between state and dfa diff encode flags
  apparmor: add a valid state flags check
  AppArmor: Remove semicolon
  apparmor: Replace two seq_printf() calls by seq_puts() in aa_label_seq_xprint()
This commit is contained in:
Linus Torvalds 2020-06-07 16:04:49 -07:00
commit a2b447066c
12 changed files with 198 additions and 119 deletions

View File

@ -2778,6 +2778,15 @@ static const struct pid_entry smack_attr_dir_stuff[] = {
LSM_DIR_OPS(smack); LSM_DIR_OPS(smack);
#endif #endif
#ifdef CONFIG_SECURITY_APPARMOR
static const struct pid_entry apparmor_attr_dir_stuff[] = {
ATTR("apparmor", "current", 0666),
ATTR("apparmor", "prev", 0444),
ATTR("apparmor", "exec", 0666),
};
LSM_DIR_OPS(apparmor);
#endif
static const struct pid_entry attr_dir_stuff[] = { static const struct pid_entry attr_dir_stuff[] = {
ATTR(NULL, "current", 0666), ATTR(NULL, "current", 0666),
ATTR(NULL, "prev", 0444), ATTR(NULL, "prev", 0444),
@ -2789,6 +2798,10 @@ static const struct pid_entry attr_dir_stuff[] = {
DIR("smack", 0555, DIR("smack", 0555,
proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops), proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
#endif #endif
#ifdef CONFIG_SECURITY_APPARMOR
DIR("apparmor", 0555,
proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
#endif
}; };
static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)

View File

@ -340,38 +340,6 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
NULL); NULL);
} }
/**
* aafs_create_symlink - create a symlink in the apparmorfs filesystem
* @name: name of dentry to create
* @parent: parent directory for this dentry
* @target: if symlink, symlink target string
* @private: private data
* @iops: struct of inode_operations that should be used
*
* If @target parameter is %NULL, then the @iops parameter needs to be
* setup to handle .readlink and .get_link inode_operations.
*/
static struct dentry *aafs_create_symlink(const char *name,
struct dentry *parent,
const char *target,
void *private,
const struct inode_operations *iops)
{
struct dentry *dent;
char *link = NULL;
if (target) {
if (!link)
return ERR_PTR(-ENOMEM);
}
dent = aafs_create(name, S_IFLNK | 0444, parent, private, link, NULL,
iops);
if (IS_ERR(dent))
kfree(link);
return dent;
}
/** /**
* aafs_remove - removes a file or directory from the apparmorfs filesystem * aafs_remove - removes a file or directory from the apparmorfs filesystem
* *
@ -624,7 +592,7 @@ static __poll_t ns_revision_poll(struct file *file, poll_table *pt)
void __aa_bump_ns_revision(struct aa_ns *ns) void __aa_bump_ns_revision(struct aa_ns *ns)
{ {
WRITE_ONCE(ns->revision, ns->revision + 1); WRITE_ONCE(ns->revision, READ_ONCE(ns->revision) + 1);
wake_up_interruptible(&ns->wait); wake_up_interruptible(&ns->wait);
} }
@ -840,7 +808,7 @@ static ssize_t query_label(char *buf, size_t buf_len,
struct multi_transaction { struct multi_transaction {
struct kref count; struct kref count;
ssize_t size; ssize_t size;
char data[0]; char data[];
}; };
#define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction)) #define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction))
@ -1763,25 +1731,25 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
} }
if (profile->rawdata) { if (profile->rawdata) {
dent = aafs_create_symlink("raw_sha1", dir, NULL, dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir,
profile->label.proxy, profile->label.proxy, NULL, NULL,
&rawdata_link_sha1_iops); &rawdata_link_sha1_iops);
if (IS_ERR(dent)) if (IS_ERR(dent))
goto fail; goto fail;
aa_get_proxy(profile->label.proxy); aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_HASH] = dent; profile->dents[AAFS_PROF_RAW_HASH] = dent;
dent = aafs_create_symlink("raw_abi", dir, NULL, dent = aafs_create("raw_abi", S_IFLNK | 0444, dir,
profile->label.proxy, profile->label.proxy, NULL, NULL,
&rawdata_link_abi_iops); &rawdata_link_abi_iops);
if (IS_ERR(dent)) if (IS_ERR(dent))
goto fail; goto fail;
aa_get_proxy(profile->label.proxy); aa_get_proxy(profile->label.proxy);
profile->dents[AAFS_PROF_RAW_ABI] = dent; profile->dents[AAFS_PROF_RAW_ABI] = dent;
dent = aafs_create_symlink("raw_data", dir, NULL, dent = aafs_create("raw_data", S_IFLNK | 0444, dir,
profile->label.proxy, profile->label.proxy, NULL, NULL,
&rawdata_link_data_iops); &rawdata_link_data_iops);
if (IS_ERR(dent)) if (IS_ERR(dent))
goto fail; goto fail;
aa_get_proxy(profile->label.proxy); aa_get_proxy(profile->label.proxy);
@ -2364,6 +2332,8 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = {
static struct aa_sfs_entry aa_sfs_entry_policy[] = { static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_DIR("versions", aa_sfs_entry_versions), AA_SFS_DIR("versions", aa_sfs_entry_versions),
AA_SFS_FILE_BOOLEAN("set_load", 1), AA_SFS_FILE_BOOLEAN("set_load", 1),
/* number of out of band transitions supported */
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
{ } { }
}; };

View File

@ -320,8 +320,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
might_sleep(); might_sleep();
/* transition from exec match to xattr set */ /* transition from exec match to xattr set */
state = aa_dfa_null_transition(profile->xmatch, state); state = aa_dfa_outofband_transition(profile->xmatch, state);
d = bprm->file->f_path.dentry; d = bprm->file->f_path.dentry;
for (i = 0; i < profile->xattr_count; i++) { for (i = 0; i < profile->xattr_count; i++) {
@ -330,7 +329,13 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
if (size >= 0) { if (size >= 0) {
u32 perm; u32 perm;
/* Check the xattr value, not just presence */ /*
* Check the xattr presence before value. This ensure
* that not present xattr can be distinguished from a 0
* length value or rule that matches any value
*/
state = aa_dfa_null_transition(profile->xmatch, state);
/* Check xattr value */
state = aa_dfa_match_len(profile->xmatch, state, value, state = aa_dfa_match_len(profile->xmatch, state, value,
size); size);
perm = dfa_user_allow(profile->xmatch, state); perm = dfa_user_allow(profile->xmatch, state);
@ -340,7 +345,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
} }
} }
/* transition to next element */ /* transition to next element */
state = aa_dfa_null_transition(profile->xmatch, state); state = aa_dfa_outofband_transition(profile->xmatch, state);
if (size < 0) { if (size < 0) {
/* /*
* No xattr match, so verify if transition to * No xattr match, so verify if transition to
@ -620,8 +625,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
bool *secure_exec) bool *secure_exec)
{ {
struct aa_label *new = NULL; struct aa_label *new = NULL;
struct aa_profile *component;
struct label_it i;
const char *info = NULL, *name = NULL, *target = NULL; const char *info = NULL, *name = NULL, *target = NULL;
unsigned int state = profile->file.start; unsigned int state = profile->file.start;
struct aa_perms perms = {}; struct aa_perms perms = {};
@ -670,21 +673,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
info = "profile transition not found"; info = "profile transition not found";
/* remove MAY_EXEC to audit as failure */ /* remove MAY_EXEC to audit as failure */
perms.allow &= ~MAY_EXEC; perms.allow &= ~MAY_EXEC;
} else {
/* verify that each component's xattr requirements are
* met, and fail execution otherwise
*/
label_for_each(i, new, component) {
if (aa_xattrs_match(bprm, component, state) <
0) {
error = -EACCES;
info = "required xattrs not present";
perms.allow &= ~MAY_EXEC;
aa_put_label(new);
new = NULL;
goto audit;
}
}
} }
} else if (COMPLAIN_MODE(profile)) { } else if (COMPLAIN_MODE(profile)) {
/* no exec permission - learning mode */ /* no exec permission - learning mode */
@ -926,7 +914,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
* aways results in a further reduction of permissions. * aways 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) && !aa_label_is_subset(new, ctx->nnp)) { !unconfined(label) &&
!aa_label_is_unconfined_subset(new, ctx->nnp)) {
error = -EPERM; error = -EPERM;
info = "no new privs"; info = "no new privs";
goto audit; goto audit;
@ -1204,7 +1193,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
* reduce restrictions. * reduce restrictions.
*/ */
if (task_no_new_privs(current) && !unconfined(label) && if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_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("no_new_privs - change_hat denied");
error = -EPERM; error = -EPERM;
@ -1225,7 +1214,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
* reduce restrictions. * reduce restrictions.
*/ */
if (task_no_new_privs(current) && !unconfined(label) && if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_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("no_new_privs - change_hat denied");
error = -EPERM; error = -EPERM;
@ -1420,7 +1409,7 @@ check:
* reduce restrictions. * reduce restrictions.
*/ */
if (task_no_new_privs(current) && !unconfined(label) && if (task_no_new_privs(current) && !unconfined(label) &&
!aa_label_is_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("no_new_privs - change_hat denied");
error = -EPERM; error = -EPERM;

View File

@ -154,13 +154,13 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
* is_deleted - test if a file has been completely unlinked * is_deleted - test if a file has been completely unlinked
* @dentry: dentry of file to test for deletion (NOT NULL) * @dentry: dentry of file to test for deletion (NOT NULL)
* *
* Returns: %1 if deleted else %0 * Returns: true if deleted else false
*/ */
static inline bool is_deleted(struct dentry *dentry) static inline bool is_deleted(struct dentry *dentry)
{ {
if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
return 1; return true;
return 0; return false;
} }
static int path_name(const char *op, struct aa_label *label, static int path_name(const char *op, struct aa_label *label,
@ -353,15 +353,15 @@ int aa_path_perm(const char *op, struct aa_label *label,
* this is done as part of the subset test, where a hardlink must have * this is done as part of the subset test, where a hardlink must have
* a subset of permissions that the target has. * a subset of permissions that the target has.
* *
* Returns: %1 if subset else %0 * Returns: true if subset else false
*/ */
static inline bool xindex_is_subset(u32 link, u32 target) static inline bool xindex_is_subset(u32 link, u32 target)
{ {
if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) ||
((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE)))
return 0; return false;
return 1; return true;
} }
static int profile_path_link(struct aa_profile *profile, static int profile_path_link(struct aa_profile *profile,

View File

@ -275,12 +275,14 @@ void aa_labelset_destroy(struct aa_labelset *ls);
void aa_labelset_init(struct aa_labelset *ls); void aa_labelset_init(struct aa_labelset *ls);
void __aa_labelset_update_subtree(struct aa_ns *ns); void __aa_labelset_update_subtree(struct aa_ns *ns);
void aa_label_destroy(struct aa_label *label);
void aa_label_free(struct aa_label *label); void aa_label_free(struct aa_label *label);
void aa_label_kref(struct kref *kref); void aa_label_kref(struct kref *kref);
bool aa_label_init(struct aa_label *label, int size, gfp_t gfp); bool aa_label_init(struct aa_label *label, int size, gfp_t gfp);
struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp); struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp);
bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub); bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub);
bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub);
struct aa_profile *__aa_label_next_not_in_set(struct label_it *I, struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
struct aa_label *set, struct aa_label *set,
struct aa_label *sub); struct aa_label *sub);

View File

@ -37,6 +37,10 @@
#define YYTH_MAGIC 0x1B5E783D #define YYTH_MAGIC 0x1B5E783D
#define YYTH_FLAG_DIFF_ENCODE 1 #define YYTH_FLAG_DIFF_ENCODE 1
#define YYTH_FLAG_OOB_TRANS 2
#define YYTH_FLAGS (YYTH_FLAG_DIFF_ENCODE | YYTH_FLAG_OOB_TRANS)
#define MAX_OOB_SUPPORTED 1
struct table_set_header { struct table_set_header {
u32 th_magic; /* YYTH_MAGIC */ u32 th_magic; /* YYTH_MAGIC */
@ -94,6 +98,7 @@ struct table_header {
struct aa_dfa { struct aa_dfa {
struct kref count; struct kref count;
u16 flags; u16 flags;
u32 max_oob;
struct table_header *tables[YYTD_ID_TSIZE]; struct table_header *tables[YYTD_ID_TSIZE];
}; };
@ -127,6 +132,8 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
const char *str); const char *str);
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
const char c); const char c);
unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa,
unsigned int state);
unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
const char *str, const char **retpos); const char *str, const char **retpos);
unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
@ -181,5 +188,9 @@ static inline void aa_put_dfa(struct aa_dfa *dfa)
#define MATCH_FLAG_DIFF_ENCODE 0x80000000 #define MATCH_FLAG_DIFF_ENCODE 0x80000000
#define MARK_DIFF_ENCODE 0x40000000 #define MARK_DIFF_ENCODE 0x40000000
#define MATCH_FLAG_OOB_TRANSITION 0x20000000
#define MATCH_FLAGS_MASK 0xff000000
#define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION)
#define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID)
#endif /* __AA_MATCH_H */ #endif /* __AA_MATCH_H */

View File

@ -309,10 +309,8 @@ out:
} }
static void label_destroy(struct aa_label *label) void aa_label_destroy(struct aa_label *label)
{ {
struct aa_label *tmp;
AA_BUG(!label); AA_BUG(!label);
if (!label_isprofile(label)) { if (!label_isprofile(label)) {
@ -328,16 +326,13 @@ static void label_destroy(struct aa_label *label)
} }
} }
if (rcu_dereference_protected(label->proxy->label, true) == label) if (label->proxy) {
rcu_assign_pointer(label->proxy->label, NULL); if (rcu_dereference_protected(label->proxy->label, true) == label)
rcu_assign_pointer(label->proxy->label, NULL);
aa_put_proxy(label->proxy);
}
aa_free_secid(label->secid); aa_free_secid(label->secid);
tmp = rcu_dereference_protected(label->proxy->label, true);
if (tmp == label)
rcu_assign_pointer(label->proxy->label, NULL);
aa_put_proxy(label->proxy);
label->proxy = (struct aa_proxy *) PROXY_POISON + 1; label->proxy = (struct aa_proxy *) PROXY_POISON + 1;
} }
@ -346,7 +341,7 @@ void aa_label_free(struct aa_label *label)
if (!label) if (!label)
return; return;
label_destroy(label); aa_label_destroy(label);
kfree(label); kfree(label);
} }
@ -550,6 +545,39 @@ bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
return __aa_label_next_not_in_set(&i, set, sub) == NULL; return __aa_label_next_not_in_set(&i, set, sub) == NULL;
} }
/**
* aa_label_is_unconfined_subset - test if @sub is a subset of @set
* @set: label to test against
* @sub: label to test if is subset of @set
*
* This checks for subset but taking into account unconfined. IF
* @sub contains an unconfined profile that does not have a matching
* unconfined in @set then this will not cause the test to fail.
* Conversely we don't care about an unconfined in @set that is not in
* @sub
*
* Returns: true if @sub is special_subset of @set
* else false
*/
bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub)
{
struct label_it i = { };
struct aa_profile *p;
AA_BUG(!set);
AA_BUG(!sub);
if (sub == set)
return true;
do {
p = __aa_label_next_not_in_set(&i, set, sub);
if (p && !profile_unconfined(p))
break;
} while (p);
return p == NULL;
}
/** /**
@ -1531,13 +1559,13 @@ static const char *label_modename(struct aa_ns *ns, struct aa_label *label,
label_for_each(i, label, profile) { label_for_each(i, label, profile) {
if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) { if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
if (profile->mode == APPARMOR_UNCONFINED) count++;
if (profile == profile->ns->unconfined)
/* special case unconfined so stacks with /* special case unconfined so stacks with
* unconfined don't report as mixed. ie. * unconfined don't report as mixed. ie.
* profile_foo//&:ns1:unconfined (mixed) * profile_foo//&:ns1:unconfined (mixed)
*/ */
continue; continue;
count++;
if (mode == -1) if (mode == -1)
mode = profile->mode; mode = profile->mode;
else if (mode != profile->mode) else if (mode != profile->mode)
@ -1749,13 +1777,13 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
AA_DEBUG("label print error"); AA_DEBUG("label print error");
return; return;
} }
seq_printf(f, "%s", str); seq_puts(f, str);
kfree(str); kfree(str);
} else if (display_mode(ns, label, flags)) } else if (display_mode(ns, label, flags))
seq_printf(f, "%s (%s)", label->hname, seq_printf(f, "%s (%s)", label->hname,
label_modename(ns, label, flags)); label_modename(ns, label, flags));
else else
seq_printf(f, "%s", label->hname); seq_puts(f, label->hname);
} }
void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,

View File

@ -804,7 +804,12 @@ static void apparmor_sk_clone_security(const struct sock *sk,
struct aa_sk_ctx *ctx = SK_CTX(sk); struct aa_sk_ctx *ctx = SK_CTX(sk);
struct aa_sk_ctx *new = SK_CTX(newsk); struct aa_sk_ctx *new = SK_CTX(newsk);
if (new->label)
aa_put_label(new->label);
new->label = aa_get_label(ctx->label); new->label = aa_get_label(ctx->label);
if (new->peer)
aa_put_label(new->peer);
new->peer = aa_get_label(ctx->peer); new->peer = aa_get_label(ctx->peer);
} }

View File

@ -97,6 +97,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
th.td_flags == YYTD_DATA8)) th.td_flags == YYTD_DATA8))
goto out; goto out;
/* if we have a table it must have some entries */
if (th.td_lolen == 0)
goto out;
tsize = table_size(th.td_lolen, th.td_flags); tsize = table_size(th.td_lolen, th.td_flags);
if (bsize < tsize) if (bsize < tsize)
goto out; goto out;
@ -198,10 +201,32 @@ static int verify_dfa(struct aa_dfa *dfa)
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen;
if (state_count == 0)
goto out;
for (i = 0; i < state_count; i++) { for (i = 0; i < state_count; i++) {
if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) &&
(DEFAULT_TABLE(dfa)[i] >= state_count)) (DEFAULT_TABLE(dfa)[i] >= state_count))
goto out; goto out;
if (BASE_TABLE(dfa)[i] & MATCH_FLAGS_INVALID) {
pr_err("AppArmor DFA state with invalid match flags");
goto out;
}
if ((BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE)) {
if (!(dfa->flags & YYTH_FLAG_DIFF_ENCODE)) {
pr_err("AppArmor DFA diff encoded transition state without header flag");
goto out;
}
}
if ((BASE_TABLE(dfa)[i] & MATCH_FLAG_OOB_TRANSITION)) {
if (base_idx(BASE_TABLE(dfa)[i]) < dfa->max_oob) {
pr_err("AppArmor DFA out of bad transition out of range");
goto out;
}
if (!(dfa->flags & YYTH_FLAG_OOB_TRANS)) {
pr_err("AppArmor DFA out of bad transition state without header flag");
goto out;
}
}
if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) {
pr_err("AppArmor DFA next/check upper bounds error\n"); pr_err("AppArmor DFA next/check upper bounds error\n");
goto out; goto out;
@ -304,9 +329,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
goto fail; goto fail;
dfa->flags = ntohs(*(__be16 *) (data + 12)); dfa->flags = ntohs(*(__be16 *) (data + 12));
if (dfa->flags != 0 && dfa->flags != YYTH_FLAG_DIFF_ENCODE) if (dfa->flags & ~(YYTH_FLAGS))
goto fail; goto fail;
/*
* TODO: needed for dfa to support more than 1 oob
* if (dfa->flags & YYTH_FLAGS_OOB_TRANS) {
* if (hsize < 16 + 4)
* goto fail;
* dfa->max_oob = ntol(*(__be32 *) (data + 16));
* if (dfa->max <= MAX_OOB_SUPPORTED) {
* pr_err("AppArmor DFA OOB greater than supported\n");
* goto fail;
* }
* }
*/
dfa->max_oob = 1;
data += hsize; data += hsize;
size -= hsize; size -= hsize;
@ -495,6 +534,23 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
return state; return state;
} }
unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
u32 b = (base)[(state)];
if (!(b & MATCH_FLAG_OOB_TRANSITION))
return DFA_NOMATCH;
/* No Equivalence class remapping for outofband transitions */
match_char(state, def, base, next, check, -1);
return state;
}
/** /**
* aa_dfa_match_until - traverse @dfa until accept state or end of input * aa_dfa_match_until - traverse @dfa until accept state or end of input
* @dfa: the dfa to match @str against (NOT NULL) * @dfa: the dfa to match @str against (NOT NULL)

View File

@ -142,7 +142,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
error = PTR_ERR(res); error = PTR_ERR(res);
*name = buf; *name = buf;
goto out; goto out;
}; }
} else if (!our_mnt(path->mnt)) } else if (!our_mnt(path->mnt))
connected = 0; connected = 0;

View File

@ -242,6 +242,7 @@ void aa_free_profile(struct aa_profile *profile)
kzfree(profile->hash); kzfree(profile->hash);
aa_put_loaddata(profile->rawdata); aa_put_loaddata(profile->rawdata);
aa_label_destroy(&profile->label);
kzfree(profile); kzfree(profile);
} }

View File

@ -243,11 +243,11 @@ fail:
static bool unpack_X(struct aa_ext *e, enum aa_code code) static bool unpack_X(struct aa_ext *e, enum aa_code code)
{ {
if (!inbounds(e, 1)) if (!inbounds(e, 1))
return 0; return false;
if (*(u8 *) e->pos != code) if (*(u8 *) e->pos != code)
return 0; return false;
e->pos++; e->pos++;
return 1; return true;
} }
/** /**
@ -261,10 +261,10 @@ static bool unpack_X(struct aa_ext *e, enum aa_code code)
* name element in the stream. If @name is NULL any name element will be * name element in the stream. If @name is NULL any name element will be
* skipped and only the typecode will be tested. * skipped and only the typecode will be tested.
* *
* Returns 1 on success (both type code and name tests match) and the read * Returns true on success (both type code and name tests match) and the read
* head is advanced past the headers * head is advanced past the headers
* *
* Returns: 0 if either match fails, the read head does not move * Returns: false if either match fails, the read head does not move
*/ */
static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
{ {
@ -289,11 +289,11 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name)
/* now check if type code matches */ /* now check if type code matches */
if (unpack_X(e, code)) if (unpack_X(e, code))
return 1; return true;
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name) static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name)
@ -306,12 +306,12 @@ static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name)
if (data) if (data)
*data = get_unaligned((u8 *)e->pos); *data = get_unaligned((u8 *)e->pos);
e->pos += sizeof(u8); e->pos += sizeof(u8);
return 1; return true;
} }
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
@ -324,12 +324,12 @@ static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
if (data) if (data)
*data = le32_to_cpu(get_unaligned((__le32 *) e->pos)); *data = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32); e->pos += sizeof(u32);
return 1; return true;
} }
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
@ -342,12 +342,12 @@ static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
if (data) if (data)
*data = le64_to_cpu(get_unaligned((__le64 *) e->pos)); *data = le64_to_cpu(get_unaligned((__le64 *) e->pos));
e->pos += sizeof(u64); e->pos += sizeof(u64);
return 1; return true;
} }
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static size_t unpack_array(struct aa_ext *e, const char *name) static size_t unpack_array(struct aa_ext *e, const char *name)
@ -472,7 +472,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
* @e: serialized data extent information (NOT NULL) * @e: serialized data extent information (NOT NULL)
* @profile: profile to add the accept table to (NOT NULL) * @profile: profile to add the accept table to (NOT NULL)
* *
* Returns: 1 if table successfully unpacked * Returns: true if table successfully unpacked
*/ */
static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
{ {
@ -535,12 +535,12 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
} }
return 1; return true;
fail: fail:
aa_free_domain_entries(&profile->file.trans); aa_free_domain_entries(&profile->file.trans);
e->pos = saved_pos; e->pos = saved_pos;
return 0; return false;
} }
static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
@ -565,11 +565,11 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
goto fail; goto fail;
} }
return 1; return true;
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile) static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
@ -601,7 +601,7 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
goto fail; goto fail;
} }
return 1; return true;
fail: fail:
if (profile->secmark) { if (profile->secmark) {
@ -613,7 +613,7 @@ fail:
} }
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
@ -643,11 +643,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
} }
return 1; return true;
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
static u32 strhash(const void *data, u32 len, u32 seed) static u32 strhash(const void *data, u32 len, u32 seed)
@ -748,10 +748,14 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail; goto fail;
if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG))
profile->mode = APPARMOR_COMPLAIN; profile->mode = APPARMOR_COMPLAIN;
else if (tmp == PACKED_MODE_ENFORCE)
profile->mode = APPARMOR_ENFORCE;
else if (tmp == PACKED_MODE_KILL) else if (tmp == PACKED_MODE_KILL)
profile->mode = APPARMOR_KILL; profile->mode = APPARMOR_KILL;
else if (tmp == PACKED_MODE_UNCONFINED) else if (tmp == PACKED_MODE_UNCONFINED)
profile->mode = APPARMOR_UNCONFINED; profile->mode = APPARMOR_UNCONFINED;
else
goto fail;
if (!unpack_u32(e, &tmp, NULL)) if (!unpack_u32(e, &tmp, NULL))
goto fail; goto fail;
if (tmp) if (tmp)
@ -990,8 +994,8 @@ static bool verify_xindex(int xindex, int table_size)
xtype = xindex & AA_X_TYPE_MASK; xtype = xindex & AA_X_TYPE_MASK;
index = xindex & AA_X_INDEX_MASK; index = xindex & AA_X_INDEX_MASK;
if (xtype == AA_X_TABLE && index >= table_size) if (xtype == AA_X_TABLE && index >= table_size)
return 0; return false;
return 1; return true;
} }
/* verify dfa xindexes are in range of transition tables */ /* verify dfa xindexes are in range of transition tables */
@ -1000,11 +1004,11 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
int i; int i;
for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) if (!verify_xindex(dfa_user_xindex(dfa, i), table_size))
return 0; return false;
if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) if (!verify_xindex(dfa_other_xindex(dfa, i), table_size))
return 0; return false;
} }
return 1; return true;
} }
/** /**