Commit a2b44706 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'apparmor-pr-2020-06-07' of...

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()
parents 8b8c704d 3622ad25
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -2778,6 +2778,15 @@ static const struct pid_entry smack_attr_dir_stuff[] = {
LSM_DIR_OPS(smack);
#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[] = {
	ATTR(NULL, "current",		0666),
	ATTR(NULL, "prev",		0444),
@@ -2789,6 +2798,10 @@ static const struct pid_entry attr_dir_stuff[] = {
	DIR("smack",			0555,
	    proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
#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)
+13 −43
Original line number Diff line number Diff line
@@ -340,38 +340,6 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
			   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
 *
@@ -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)
{
	WRITE_ONCE(ns->revision, ns->revision + 1);
	WRITE_ONCE(ns->revision, READ_ONCE(ns->revision) + 1);
	wake_up_interruptible(&ns->wait);
}

@@ -840,7 +808,7 @@ static ssize_t query_label(char *buf, size_t buf_len,
struct multi_transaction {
	struct kref count;
	ssize_t size;
	char data[0];
	char data[];
};

#define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction))
@@ -1763,24 +1731,24 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
	}

	if (profile->rawdata) {
		dent = aafs_create_symlink("raw_sha1", dir, NULL,
					   profile->label.proxy,
		dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir,
				   profile->label.proxy, NULL, NULL,
				   &rawdata_link_sha1_iops);
		if (IS_ERR(dent))
			goto fail;
		aa_get_proxy(profile->label.proxy);
		profile->dents[AAFS_PROF_RAW_HASH] = dent;

		dent = aafs_create_symlink("raw_abi", dir, NULL,
					   profile->label.proxy,
		dent = aafs_create("raw_abi", S_IFLNK | 0444, dir,
				   profile->label.proxy, NULL, NULL,
				   &rawdata_link_abi_iops);
		if (IS_ERR(dent))
			goto fail;
		aa_get_proxy(profile->label.proxy);
		profile->dents[AAFS_PROF_RAW_ABI] = dent;

		dent = aafs_create_symlink("raw_data", dir, NULL,
					   profile->label.proxy,
		dent = aafs_create("raw_data", S_IFLNK | 0444, dir,
				   profile->label.proxy, NULL, NULL,
				   &rawdata_link_data_iops);
		if (IS_ERR(dent))
			goto fail;
@@ -2364,6 +2332,8 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = {
static struct aa_sfs_entry aa_sfs_entry_policy[] = {
	AA_SFS_DIR("versions",			aa_sfs_entry_versions),
	AA_SFS_FILE_BOOLEAN("set_load",		1),
	/* number of out of band transitions supported */
	AA_SFS_FILE_U64("outofband",		MAX_OOB_SUPPORTED),
	{ }
};

+14 −25
Original line number Diff line number Diff line
@@ -320,8 +320,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
	might_sleep();

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

	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) {
			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,
						 size);
			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 */
		state = aa_dfa_null_transition(profile->xmatch, state);
		state = aa_dfa_outofband_transition(profile->xmatch, state);
		if (size < 0) {
			/*
			 * 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)
{
	struct aa_label *new = NULL;
	struct aa_profile *component;
	struct label_it i;
	const char *info = NULL, *name = NULL, *target = NULL;
	unsigned int state = profile->file.start;
	struct aa_perms perms = {};
@@ -670,21 +673,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
			info = "profile transition not found";
			/* remove MAY_EXEC to audit as failure */
			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)) {
		/* 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.
	 */
	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;
		info = "no new privs";
		goto audit;
@@ -1204,7 +1193,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
		 * reduce restrictions.
		 */
		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 */
			AA_DEBUG("no_new_privs - change_hat denied");
			error = -EPERM;
@@ -1225,7 +1214,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
		 * reduce restrictions.
		 */
		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 */
			AA_DEBUG("no_new_privs - change_hat denied");
			error = -EPERM;
@@ -1420,7 +1409,7 @@ check:
		 * reduce restrictions.
		 */
		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 */
			AA_DEBUG("no_new_privs - change_hat denied");
			error = -EPERM;
+6 −6
Original line number Diff line number Diff line
@@ -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
 * @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)
{
	if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
		return 1;
	return 0;
		return true;
	return false;
}

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
 * 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)
{
	if (((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,
+2 −0
Original line number Diff line number Diff line
@@ -275,12 +275,14 @@ void aa_labelset_destroy(struct aa_labelset *ls);
void aa_labelset_init(struct aa_labelset *ls);
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_kref(struct kref *kref);
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);

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_label *set,
					     struct aa_label *sub);
Loading