Commit 79e178a5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'apparmor-pr-2019-12-03' of...

Merge tag 'apparmor-pr-2019-12-03' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "Features:

   - increase left match history buffer size to provide improved
     conflict resolution in overlapping execution rules.

   - switch buffer allocation to use a memory pool and GFP_KERNEL where
     possible.

   - add compression of policy blobs to reduce memory usage.

  Cleanups:

   - fix spelling mistake "immutible" -> "immutable"

  Bug fixes:

   - fix unsigned len comparison in update_for_len macro

   - fix sparse warning for type-casting of current->real_cred"

* tag 'apparmor-pr-2019-12-03' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor:
  apparmor: make it so work buffers can be allocated from atomic context
  apparmor: reduce rcu_read_lock scope for aa_file_perm mediation
  apparmor: fix wrong buffer allocation in aa_new_mount
  apparmor: fix unsigned len comparison with less than zero
  apparmor: increase left match history buffer size
  apparmor: Switch to GFP_KERNEL where possible
  apparmor: Use a memory pool instead per-CPU caches
  apparmor: Force type-casting of current->real_cred
  apparmor: fix spelling mistake "immutible" -> "immutable"
  apparmor: fix blob compression when ns is forced on a policy load
  apparmor: fix missing ZLIB defines
  apparmor: fix blob compression build failure on ppc
  apparmor: Initial implementation of raw policy blob compression
parents 01d1dff6 341c1fda
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ config SECURITY_APPARMOR
	select SECURITY_PATH
	select SECURITYFS
	select SECURITY_NETWORK
	select ZLIB_INFLATE
	select ZLIB_DEFLATE
	default n
	help
	  This enables the AppArmor security module.
+124 −6
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/poll.h>
#include <linux/zlib.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>

@@ -65,6 +66,35 @@
 * support fns
 */

struct rawdata_f_data {
	struct aa_loaddata *loaddata;
};

#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)

static void rawdata_f_data_free(struct rawdata_f_data *private)
{
	if (!private)
		return;

	aa_put_loaddata(private->loaddata);
	kvfree(private);
}

static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
{
	struct rawdata_f_data *ret;

	if (size > SIZE_MAX - sizeof(*ret))
		return ERR_PTR(-EINVAL);

	ret = kvzalloc(sizeof(*ret) + size, GFP_KERNEL);
	if (!ret)
		return ERR_PTR(-ENOMEM);

	return ret;
}

/**
 * aa_mangle_name - mangle a profile name to std profile layout form
 * @name: profile name to mangle  (NOT NULL)
@@ -1280,36 +1310,117 @@ static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
	return 0;
}

static int seq_rawdata_compressed_size_show(struct seq_file *seq, void *v)
{
	struct aa_loaddata *data = seq->private;

	seq_printf(seq, "%zu\n", data->compressed_size);

	return 0;
}

SEQ_RAWDATA_FOPS(abi);
SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size);

static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
{
	int error;
	struct z_stream_s strm;

	if (aa_g_rawdata_compression_level == 0) {
		if (dlen < slen)
			return -EINVAL;
		memcpy(dst, src, slen);
		return 0;
	}

	memset(&strm, 0, sizeof(strm));

	strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
	if (!strm.workspace)
		return -ENOMEM;

	strm.next_in = src;
	strm.avail_in = slen;

	error = zlib_inflateInit(&strm);
	if (error != Z_OK) {
		error = -ENOMEM;
		goto fail_inflate_init;
	}

	strm.next_out = dst;
	strm.avail_out = dlen;

	error = zlib_inflate(&strm, Z_FINISH);
	if (error != Z_STREAM_END)
		error = -EINVAL;
	else
		error = 0;

	zlib_inflateEnd(&strm);
fail_inflate_init:
	kvfree(strm.workspace);
	return error;
}

static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
			    loff_t *ppos)
{
	struct aa_loaddata *rawdata = file->private_data;
	struct rawdata_f_data *private = file->private_data;

	return simple_read_from_buffer(buf, size, ppos, rawdata->data,
				       rawdata->size);
	return simple_read_from_buffer(buf, size, ppos,
				       RAWDATA_F_DATA_BUF(private),
				       private->loaddata->size);
}

static int rawdata_release(struct inode *inode, struct file *file)
{
	aa_put_loaddata(file->private_data);
	rawdata_f_data_free(file->private_data);

	return 0;
}

static int rawdata_open(struct inode *inode, struct file *file)
{
	int error;
	struct aa_loaddata *loaddata;
	struct rawdata_f_data *private;

	if (!policy_view_capable(NULL))
		return -EACCES;
	file->private_data = __aa_get_loaddata(inode->i_private);
	if (!file->private_data)

	loaddata = __aa_get_loaddata(inode->i_private);
	if (!loaddata)
		/* lost race: this entry is being reaped */
		return -ENOENT;

	private = rawdata_f_data_alloc(loaddata->size);
	if (IS_ERR(private)) {
		error = PTR_ERR(private);
		goto fail_private_alloc;
	}

	private->loaddata = loaddata;

	error = deflate_decompress(loaddata->data, loaddata->compressed_size,
				   RAWDATA_F_DATA_BUF(private),
				   loaddata->size);
	if (error)
		goto fail_decompress;

	file->private_data = private;
	return 0;

fail_decompress:
	rawdata_f_data_free(private);
	return error;

fail_private_alloc:
	aa_put_loaddata(loaddata);
	return error;
}

static const struct file_operations rawdata_fops = {
@@ -1388,6 +1499,13 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
		rawdata->dents[AAFS_LOADDATA_HASH] = dent;
	}

	dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir,
				rawdata,
				&seq_rawdata_compressed_size_fops);
	if (IS_ERR(dent))
		goto fail;
	rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent;

	dent = aafs_create_file("raw_data", S_IFREG | 0444,
				      dir, rawdata, &rawdata_fops);
	if (IS_ERR(dent))
+20 −26
Original line number Diff line number Diff line
@@ -520,7 +520,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
				label = &new_profile->label;
			continue;
		}
		label = aa_label_parse(&profile->label, *name, GFP_ATOMIC,
		label = aa_label_parse(&profile->label, *name, GFP_KERNEL,
				       true, false);
		if (IS_ERR(label))
			label = NULL;
@@ -600,7 +600,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
		/* base the stack on post domain transition */
		struct aa_label *base = new;

		new = aa_label_parse(base, stack, GFP_ATOMIC, true, false);
		new = aa_label_parse(base, stack, GFP_KERNEL, true, false);
		if (IS_ERR(new))
			new = NULL;
		aa_put_label(base);
@@ -685,20 +685,9 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
	} else if (COMPLAIN_MODE(profile)) {
		/* no exec permission - learning mode */
		struct aa_profile *new_profile = NULL;
		char *n = kstrdup(name, GFP_ATOMIC);

		if (n) {
			/* name is ptr into buffer */
			long pos = name - buffer;
			/* break per cpu buffer hold */
			put_buffers(buffer);
			new_profile = aa_new_null_profile(profile, false, n,

		new_profile = aa_new_null_profile(profile, false, name,
						  GFP_KERNEL);
			get_buffers(buffer);
			name = buffer + pos;
			strcpy((char *)name, n);
			kfree(n);
		}
		if (!new_profile) {
			error = -ENOMEM;
			info = "could not create null profile";
@@ -719,7 +708,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
		if (DEBUG_ON) {
			dbg_printk("apparmor: scrubbing environment variables"
				   " for %s profile=", name);
			aa_label_printk(new, GFP_ATOMIC);
			aa_label_printk(new, GFP_KERNEL);
			dbg_printk("\n");
		}
		*secure_exec = true;
@@ -795,7 +784,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
		if (DEBUG_ON) {
			dbg_printk("apparmor: scrubbing environment "
				   "variables for %s label=", xname);
			aa_label_printk(onexec, GFP_ATOMIC);
			aa_label_printk(onexec, GFP_KERNEL);
			dbg_printk("\n");
		}
		*secure_exec = true;
@@ -829,7 +818,7 @@ static struct aa_label *handle_onexec(struct aa_label *label,
					       bprm, buffer, cond, unsafe));
		if (error)
			return ERR_PTR(error);
		new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
				aa_get_newest_label(onexec),
				profile_transition(profile, bprm, buffer,
						   cond, unsafe));
@@ -841,9 +830,9 @@ static struct aa_label *handle_onexec(struct aa_label *label,
					       buffer, cond, unsafe));
		if (error)
			return ERR_PTR(error);
		new = fn_label_build_in_ns(label, profile, GFP_ATOMIC,
		new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
				aa_label_merge(&profile->label, onexec,
					       GFP_ATOMIC),
					       GFP_KERNEL),
				profile_transition(profile, bprm, buffer,
						   cond, unsafe));
	}
@@ -903,13 +892,18 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
		ctx->nnp = aa_get_label(label);

	/* buffer freed below, name is pointer into buffer */
	get_buffers(buffer);
	buffer = aa_get_buffer(false);
	if (!buffer) {
		error = -ENOMEM;
		goto done;
	}

	/* Test for onexec first as onexec override other x transitions. */
	if (ctx->onexec)
		new = handle_onexec(label, ctx->onexec, ctx->token,
				    bprm, buffer, &cond, &unsafe);
	else
		new = fn_label_build(label, profile, GFP_ATOMIC,
		new = fn_label_build(label, profile, GFP_KERNEL,
				profile_transition(profile, bprm, buffer,
						   &cond, &unsafe));

@@ -953,7 +947,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
		if (DEBUG_ON) {
			dbg_printk("scrubbing environment variables for %s "
				   "label=", bprm->filename);
			aa_label_printk(new, GFP_ATOMIC);
			aa_label_printk(new, GFP_KERNEL);
			dbg_printk("\n");
		}
		bprm->secureexec = 1;
@@ -964,7 +958,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
		if (DEBUG_ON) {
			dbg_printk("apparmor: clearing unsafe personality "
				   "bits. %s label=", bprm->filename);
			aa_label_printk(new, GFP_ATOMIC);
			aa_label_printk(new, GFP_KERNEL);
			dbg_printk("\n");
		}
		bprm->per_clear |= PER_CLEAR_ON_SETID;
@@ -975,7 +969,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)

done:
	aa_put_label(label);
	put_buffers(buffer);
	aa_put_buffer(buffer);

	return error;

+29 −16
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
	if (aad(sa)->peer) {
		audit_log_format(ab, " target=");
		aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
				FLAG_VIEW_SUBNS, GFP_ATOMIC);
				FLAG_VIEW_SUBNS, GFP_KERNEL);
	} else if (aad(sa)->fs.target) {
		audit_log_format(ab, " target=");
		audit_log_untrustedstring(ab, aad(sa)->fs.target);
@@ -332,12 +332,14 @@ int aa_path_perm(const char *op, struct aa_label *label,

	flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR :
								0);
	get_buffers(buffer);
	buffer = aa_get_buffer(false);
	if (!buffer)
		return -ENOMEM;
	error = fn_for_each_confined(label, profile,
			profile_path_perm(op, profile, path, buffer, request,
					  cond, flags, &perms));

	put_buffers(buffer);
	aa_put_buffer(buffer);

	return error;
}
@@ -475,12 +477,18 @@ int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
	int error;

	/* buffer freed below, lname is pointer in buffer */
	get_buffers(buffer, buffer2);
	buffer = aa_get_buffer(false);
	buffer2 = aa_get_buffer(false);
	error = -ENOMEM;
	if (!buffer || !buffer2)
		goto out;

	error = fn_for_each_confined(label, profile,
			profile_path_link(profile, &link, buffer, &target,
					  buffer2, &cond));
	put_buffers(buffer, buffer2);

out:
	aa_put_buffer(buffer);
	aa_put_buffer(buffer2);
	return error;
}

@@ -507,7 +515,7 @@ static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,

static int __file_path_perm(const char *op, struct aa_label *label,
			    struct aa_label *flabel, struct file *file,
			    u32 request, u32 denied)
			    u32 request, u32 denied, bool in_atomic)
{
	struct aa_profile *profile;
	struct aa_perms perms = {};
@@ -524,7 +532,9 @@ static int __file_path_perm(const char *op, struct aa_label *label,
		return 0;

	flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
	get_buffers(buffer);
	buffer = aa_get_buffer(in_atomic);
	if (!buffer)
		return -ENOMEM;

	/* check every profile in task label not in current cache */
	error = fn_for_each_not_in_set(flabel, label, profile,
@@ -553,7 +563,7 @@ static int __file_path_perm(const char *op, struct aa_label *label,
	if (!error)
		update_file_ctx(file_ctx(file), label, request);

	put_buffers(buffer);
	aa_put_buffer(buffer);

	return error;
}
@@ -590,11 +600,12 @@ static int __file_sock_perm(const char *op, struct aa_label *label,
 * @label: label being enforced   (NOT NULL)
 * @file: file to revalidate access permissions on  (NOT NULL)
 * @request: requested permissions
 * @in_atomic: whether allocations need to be done in atomic context
 *
 * Returns: %0 if access allowed else error
 */
int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
		 u32 request)
		 u32 request, bool in_atomic)
{
	struct aa_file_ctx *fctx;
	struct aa_label *flabel;
@@ -607,7 +618,8 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
	fctx = file_ctx(file);

	rcu_read_lock();
	flabel  = rcu_dereference(fctx->label);
	flabel  = aa_get_newest_label(rcu_dereference(fctx->label));
	rcu_read_unlock();
	AA_BUG(!flabel);

	/* revalidate access, if task is unconfined, or the cached cred
@@ -626,14 +638,13 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,

	if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry))
		error = __file_path_perm(op, label, flabel, file, request,
					 denied);
					 denied, in_atomic);

	else if (S_ISSOCK(file_inode(file)->i_mode))
		error = __file_sock_perm(op, label, flabel, file, request,
					 denied);
done:
	rcu_read_unlock();

	aa_put_label(flabel);
	return error;
}

@@ -655,7 +666,8 @@ static void revalidate_tty(struct aa_label *label)
					     struct tty_file_private, list);
		file = file_priv->file;

		if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE))
		if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE,
				 IN_ATOMIC))
			drop_tty = 1;
	}
	spin_unlock(&tty->files_lock);
@@ -669,7 +681,8 @@ static int match_file(const void *p, struct file *file, unsigned int fd)
{
	struct aa_label *label = (struct aa_label *)p;

	if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file)))
	if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file),
			 IN_ATOMIC))
		return fd + 1;
	return 0;
}
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ extern enum audit_mode aa_g_audit;
extern bool aa_g_audit_header;
extern bool aa_g_debug;
extern bool aa_g_hash_policy;
extern int aa_g_rawdata_compression_level;
extern bool aa_g_lock_policy;
extern bool aa_g_logsyscall;
extern bool aa_g_paranoid_load;
Loading