163e2b423SJohn Johansen /*
263e2b423SJohn Johansen  * AppArmor security module
363e2b423SJohn Johansen  *
463e2b423SJohn Johansen  * This file contains AppArmor /sys/kernel/security/apparmor interface functions
563e2b423SJohn Johansen  *
663e2b423SJohn Johansen  * Copyright (C) 1998-2008 Novell/SUSE
763e2b423SJohn Johansen  * Copyright 2009-2010 Canonical Ltd.
863e2b423SJohn Johansen  *
963e2b423SJohn Johansen  * This program is free software; you can redistribute it and/or
1063e2b423SJohn Johansen  * modify it under the terms of the GNU General Public License as
1163e2b423SJohn Johansen  * published by the Free Software Foundation, version 2 of the
1263e2b423SJohn Johansen  * License.
1363e2b423SJohn Johansen  */
1463e2b423SJohn Johansen 
150d259f04SJohn Johansen #include <linux/ctype.h>
1663e2b423SJohn Johansen #include <linux/security.h>
1763e2b423SJohn Johansen #include <linux/vmalloc.h>
1863e2b423SJohn Johansen #include <linux/module.h>
1963e2b423SJohn Johansen #include <linux/seq_file.h>
2063e2b423SJohn Johansen #include <linux/uaccess.h>
21a71ada30SJohn Johansen #include <linux/mount.h>
2263e2b423SJohn Johansen #include <linux/namei.h>
23e74abcf3SKees Cook #include <linux/capability.h>
2429b3822fSJohn Johansen #include <linux/rcupdate.h>
25a71ada30SJohn Johansen #include <uapi/linux/major.h>
26a71ada30SJohn Johansen #include <linux/fs.h>
2763e2b423SJohn Johansen 
2863e2b423SJohn Johansen #include "include/apparmor.h"
2963e2b423SJohn Johansen #include "include/apparmorfs.h"
3063e2b423SJohn Johansen #include "include/audit.h"
3163e2b423SJohn Johansen #include "include/context.h"
32f8eb8a13SJohn Johansen #include "include/crypto.h"
3363e2b423SJohn Johansen #include "include/policy.h"
34cff281f6SJohn Johansen #include "include/policy_ns.h"
35d384b0a1SKees Cook #include "include/resource.h"
365ac8c355SJohn Johansen #include "include/policy_unpack.h"
3763e2b423SJohn Johansen 
3863e2b423SJohn Johansen /**
390d259f04SJohn Johansen  * aa_mangle_name - mangle a profile name to std profile layout form
400d259f04SJohn Johansen  * @name: profile name to mangle  (NOT NULL)
410d259f04SJohn Johansen  * @target: buffer to store mangled name, same length as @name (MAYBE NULL)
420d259f04SJohn Johansen  *
430d259f04SJohn Johansen  * Returns: length of mangled name
440d259f04SJohn Johansen  */
45bbe4a7c8SJohn Johansen static int mangle_name(const char *name, char *target)
460d259f04SJohn Johansen {
470d259f04SJohn Johansen 	char *t = target;
480d259f04SJohn Johansen 
490d259f04SJohn Johansen 	while (*name == '/' || *name == '.')
500d259f04SJohn Johansen 		name++;
510d259f04SJohn Johansen 
520d259f04SJohn Johansen 	if (target) {
530d259f04SJohn Johansen 		for (; *name; name++) {
540d259f04SJohn Johansen 			if (*name == '/')
550d259f04SJohn Johansen 				*(t)++ = '.';
560d259f04SJohn Johansen 			else if (isspace(*name))
570d259f04SJohn Johansen 				*(t)++ = '_';
580d259f04SJohn Johansen 			else if (isalnum(*name) || strchr("._-", *name))
590d259f04SJohn Johansen 				*(t)++ = *name;
600d259f04SJohn Johansen 		}
610d259f04SJohn Johansen 
620d259f04SJohn Johansen 		*t = 0;
630d259f04SJohn Johansen 	} else {
640d259f04SJohn Johansen 		int len = 0;
650d259f04SJohn Johansen 		for (; *name; name++) {
660d259f04SJohn Johansen 			if (isalnum(*name) || isspace(*name) ||
670d259f04SJohn Johansen 			    strchr("/._-", *name))
680d259f04SJohn Johansen 				len++;
690d259f04SJohn Johansen 		}
700d259f04SJohn Johansen 
710d259f04SJohn Johansen 		return len;
720d259f04SJohn Johansen 	}
730d259f04SJohn Johansen 
740d259f04SJohn Johansen 	return t - target;
750d259f04SJohn Johansen }
760d259f04SJohn Johansen 
770d259f04SJohn Johansen /**
7863e2b423SJohn Johansen  * aa_simple_write_to_buffer - common routine for getting policy from user
7963e2b423SJohn Johansen  * @userbuf: user buffer to copy data from  (NOT NULL)
803ed02adaSJohn Johansen  * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
8163e2b423SJohn Johansen  * @copy_size: size of data to copy from user buffer
8263e2b423SJohn Johansen  * @pos: position write is at in the file (NOT NULL)
8363e2b423SJohn Johansen  *
8463e2b423SJohn Johansen  * Returns: kernel buffer containing copy of user buffer data or an
8563e2b423SJohn Johansen  *          ERR_PTR on failure.
8663e2b423SJohn Johansen  */
875ef50d01SJohn Johansen static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
885ac8c355SJohn Johansen 						     size_t alloc_size,
895ac8c355SJohn Johansen 						     size_t copy_size,
9063e2b423SJohn Johansen 						     loff_t *pos)
9163e2b423SJohn Johansen {
925ac8c355SJohn Johansen 	struct aa_loaddata *data;
9363e2b423SJohn Johansen 
94e6bfa25dSJohn Johansen 	AA_BUG(copy_size > alloc_size);
953ed02adaSJohn Johansen 
9663e2b423SJohn Johansen 	if (*pos != 0)
9763e2b423SJohn Johansen 		/* only writes from pos 0, that is complete writes */
9863e2b423SJohn Johansen 		return ERR_PTR(-ESPIPE);
9963e2b423SJohn Johansen 
10063e2b423SJohn Johansen 	/* freed by caller to simple_write_to_buffer */
1015d5182caSJohn Johansen 	data = aa_loaddata_alloc(alloc_size);
1025d5182caSJohn Johansen 	if (IS_ERR(data))
1035d5182caSJohn Johansen 		return data;
10463e2b423SJohn Johansen 
1055d5182caSJohn Johansen 	data->size = copy_size;
1065ac8c355SJohn Johansen 	if (copy_from_user(data->data, userbuf, copy_size)) {
10763e2b423SJohn Johansen 		kvfree(data);
10863e2b423SJohn Johansen 		return ERR_PTR(-EFAULT);
10963e2b423SJohn Johansen 	}
11063e2b423SJohn Johansen 
11163e2b423SJohn Johansen 	return data;
11263e2b423SJohn Johansen }
11363e2b423SJohn Johansen 
1145ac8c355SJohn Johansen static ssize_t policy_update(int binop, const char __user *buf, size_t size,
115b7fd2c03SJohn Johansen 			     loff_t *pos, struct aa_ns *ns)
1165ac8c355SJohn Johansen {
1175ac8c355SJohn Johansen 	ssize_t error;
1185ac8c355SJohn Johansen 	struct aa_loaddata *data;
1195ac8c355SJohn Johansen 	struct aa_profile *profile = aa_current_profile();
12047f6e5ccSJohn Johansen 	const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
1215ac8c355SJohn Johansen 	/* high level check about policy management - fine grained in
1225ac8c355SJohn Johansen 	 * below after unpack
1235ac8c355SJohn Johansen 	 */
124b7fd2c03SJohn Johansen 	error = aa_may_manage_policy(profile, ns, op);
1255ac8c355SJohn Johansen 	if (error)
1265ac8c355SJohn Johansen 		return error;
12763e2b423SJohn Johansen 
1285ef50d01SJohn Johansen 	data = aa_simple_write_to_buffer(buf, size, size, pos);
1295ac8c355SJohn Johansen 	error = PTR_ERR(data);
1305ac8c355SJohn Johansen 	if (!IS_ERR(data)) {
131b7fd2c03SJohn Johansen 		error = aa_replace_profiles(ns ? ns : profile->ns, profile,
132b7fd2c03SJohn Johansen 					    binop, data);
1335ac8c355SJohn Johansen 		aa_put_loaddata(data);
1345ac8c355SJohn Johansen 	}
1355ac8c355SJohn Johansen 
1365ac8c355SJohn Johansen 	return error;
1375ac8c355SJohn Johansen }
1385ac8c355SJohn Johansen 
139b7fd2c03SJohn Johansen /* .load file hook fn to load policy */
14063e2b423SJohn Johansen static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
14163e2b423SJohn Johansen 			    loff_t *pos)
14263e2b423SJohn Johansen {
143b7fd2c03SJohn Johansen 	struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
144b7fd2c03SJohn Johansen 	int error = policy_update(PROF_ADD, buf, size, pos, ns);
145b7fd2c03SJohn Johansen 
146b7fd2c03SJohn Johansen 	aa_put_ns(ns);
14763e2b423SJohn Johansen 
14863e2b423SJohn Johansen 	return error;
14963e2b423SJohn Johansen }
15063e2b423SJohn Johansen 
15163e2b423SJohn Johansen static const struct file_operations aa_fs_profile_load = {
1526038f373SArnd Bergmann 	.write = profile_load,
1536038f373SArnd Bergmann 	.llseek = default_llseek,
15463e2b423SJohn Johansen };
15563e2b423SJohn Johansen 
15663e2b423SJohn Johansen /* .replace file hook fn to load and/or replace policy */
15763e2b423SJohn Johansen static ssize_t profile_replace(struct file *f, const char __user *buf,
15863e2b423SJohn Johansen 			       size_t size, loff_t *pos)
15963e2b423SJohn Johansen {
160b7fd2c03SJohn Johansen 	struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
161b7fd2c03SJohn Johansen 	int error = policy_update(PROF_REPLACE, buf, size, pos, ns);
162b7fd2c03SJohn Johansen 
163b7fd2c03SJohn Johansen 	aa_put_ns(ns);
16463e2b423SJohn Johansen 
16563e2b423SJohn Johansen 	return error;
16663e2b423SJohn Johansen }
16763e2b423SJohn Johansen 
16863e2b423SJohn Johansen static const struct file_operations aa_fs_profile_replace = {
1696038f373SArnd Bergmann 	.write = profile_replace,
1706038f373SArnd Bergmann 	.llseek = default_llseek,
17163e2b423SJohn Johansen };
17263e2b423SJohn Johansen 
173b7fd2c03SJohn Johansen /* .remove file hook fn to remove loaded policy */
17463e2b423SJohn Johansen static ssize_t profile_remove(struct file *f, const char __user *buf,
17563e2b423SJohn Johansen 			      size_t size, loff_t *pos)
17663e2b423SJohn Johansen {
1775ac8c355SJohn Johansen 	struct aa_loaddata *data;
1785ac8c355SJohn Johansen 	struct aa_profile *profile;
17963e2b423SJohn Johansen 	ssize_t error;
180b7fd2c03SJohn Johansen 	struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
18163e2b423SJohn Johansen 
1825ac8c355SJohn Johansen 	profile = aa_current_profile();
1835ac8c355SJohn Johansen 	/* high level check about policy management - fine grained in
1845ac8c355SJohn Johansen 	 * below after unpack
1855ac8c355SJohn Johansen 	 */
186b7fd2c03SJohn Johansen 	error = aa_may_manage_policy(profile, ns, OP_PROF_RM);
1875ac8c355SJohn Johansen 	if (error)
1885ac8c355SJohn Johansen 		goto out;
1895ac8c355SJohn Johansen 
19063e2b423SJohn Johansen 	/*
19163e2b423SJohn Johansen 	 * aa_remove_profile needs a null terminated string so 1 extra
19263e2b423SJohn Johansen 	 * byte is allocated and the copied data is null terminated.
19363e2b423SJohn Johansen 	 */
1945ef50d01SJohn Johansen 	data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
19563e2b423SJohn Johansen 
19663e2b423SJohn Johansen 	error = PTR_ERR(data);
19763e2b423SJohn Johansen 	if (!IS_ERR(data)) {
1985ac8c355SJohn Johansen 		data->data[size] = 0;
199b7fd2c03SJohn Johansen 		error = aa_remove_profiles(ns ? ns : profile->ns, profile,
200b7fd2c03SJohn Johansen 					   data->data, size);
2015ac8c355SJohn Johansen 		aa_put_loaddata(data);
20263e2b423SJohn Johansen 	}
2035ac8c355SJohn Johansen  out:
204b7fd2c03SJohn Johansen 	aa_put_ns(ns);
20563e2b423SJohn Johansen 	return error;
20663e2b423SJohn Johansen }
20763e2b423SJohn Johansen 
20863e2b423SJohn Johansen static const struct file_operations aa_fs_profile_remove = {
2096038f373SArnd Bergmann 	.write = profile_remove,
2106038f373SArnd Bergmann 	.llseek = default_llseek,
21163e2b423SJohn Johansen };
21263e2b423SJohn Johansen 
2135d5182caSJohn Johansen void __aa_bump_ns_revision(struct aa_ns *ns)
2145d5182caSJohn Johansen {
2155d5182caSJohn Johansen 	ns->revision++;
2165d5182caSJohn Johansen }
2175d5182caSJohn Johansen 
218e025be0fSWilliam Hua /**
219e025be0fSWilliam Hua  * query_data - queries a policy and writes its data to buf
220e025be0fSWilliam Hua  * @buf: the resulting data is stored here (NOT NULL)
221e025be0fSWilliam Hua  * @buf_len: size of buf
222e025be0fSWilliam Hua  * @query: query string used to retrieve data
223e025be0fSWilliam Hua  * @query_len: size of query including second NUL byte
224e025be0fSWilliam Hua  *
225e025be0fSWilliam Hua  * The buffers pointed to by buf and query may overlap. The query buffer is
226e025be0fSWilliam Hua  * parsed before buf is written to.
227e025be0fSWilliam Hua  *
228e025be0fSWilliam Hua  * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
229e025be0fSWilliam Hua  * the security confinement context and <KEY> is the name of the data to
230e025be0fSWilliam Hua  * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
231e025be0fSWilliam Hua  *
232e025be0fSWilliam Hua  * Don't expect the contents of buf to be preserved on failure.
233e025be0fSWilliam Hua  *
234e025be0fSWilliam Hua  * Returns: number of characters written to buf or -errno on failure
235e025be0fSWilliam Hua  */
236e025be0fSWilliam Hua static ssize_t query_data(char *buf, size_t buf_len,
237e025be0fSWilliam Hua 			  char *query, size_t query_len)
238e025be0fSWilliam Hua {
239e025be0fSWilliam Hua 	char *out;
240e025be0fSWilliam Hua 	const char *key;
241e025be0fSWilliam Hua 	struct aa_profile *profile;
242e025be0fSWilliam Hua 	struct aa_data *data;
243e025be0fSWilliam Hua 	u32 bytes, blocks;
244e025be0fSWilliam Hua 	__le32 outle32;
245e025be0fSWilliam Hua 
246e025be0fSWilliam Hua 	if (!query_len)
247e025be0fSWilliam Hua 		return -EINVAL; /* need a query */
248e025be0fSWilliam Hua 
249e025be0fSWilliam Hua 	key = query + strnlen(query, query_len) + 1;
250e025be0fSWilliam Hua 	if (key + 1 >= query + query_len)
251e025be0fSWilliam Hua 		return -EINVAL; /* not enough space for a non-empty key */
252e025be0fSWilliam Hua 	if (key + strnlen(key, query + query_len - key) >= query + query_len)
253e025be0fSWilliam Hua 		return -EINVAL; /* must end with NUL */
254e025be0fSWilliam Hua 
255e025be0fSWilliam Hua 	if (buf_len < sizeof(bytes) + sizeof(blocks))
256e025be0fSWilliam Hua 		return -EINVAL; /* not enough space */
257e025be0fSWilliam Hua 
258e025be0fSWilliam Hua 	profile = aa_current_profile();
259e025be0fSWilliam Hua 
260e025be0fSWilliam Hua 	/* We are going to leave space for two numbers. The first is the total
261e025be0fSWilliam Hua 	 * number of bytes we are writing after the first number. This is so
262e025be0fSWilliam Hua 	 * users can read the full output without reallocation.
263e025be0fSWilliam Hua 	 *
264e025be0fSWilliam Hua 	 * The second number is the number of data blocks we're writing. An
265e025be0fSWilliam Hua 	 * application might be confined by multiple policies having data in
266e025be0fSWilliam Hua 	 * the same key.
267e025be0fSWilliam Hua 	 */
268e025be0fSWilliam Hua 	memset(buf, 0, sizeof(bytes) + sizeof(blocks));
269e025be0fSWilliam Hua 	out = buf + sizeof(bytes) + sizeof(blocks);
270e025be0fSWilliam Hua 
271e025be0fSWilliam Hua 	blocks = 0;
272e025be0fSWilliam Hua 	if (profile->data) {
273e025be0fSWilliam Hua 		data = rhashtable_lookup_fast(profile->data, &key,
274e025be0fSWilliam Hua 					      profile->data->p);
275e025be0fSWilliam Hua 
276e025be0fSWilliam Hua 		if (data) {
277e025be0fSWilliam Hua 			if (out + sizeof(outle32) + data->size > buf + buf_len)
278e025be0fSWilliam Hua 				return -EINVAL; /* not enough space */
279e025be0fSWilliam Hua 			outle32 = __cpu_to_le32(data->size);
280e025be0fSWilliam Hua 			memcpy(out, &outle32, sizeof(outle32));
281e025be0fSWilliam Hua 			out += sizeof(outle32);
282e025be0fSWilliam Hua 			memcpy(out, data->data, data->size);
283e025be0fSWilliam Hua 			out += data->size;
284e025be0fSWilliam Hua 			blocks++;
285e025be0fSWilliam Hua 		}
286e025be0fSWilliam Hua 	}
287e025be0fSWilliam Hua 
288e025be0fSWilliam Hua 	outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
289e025be0fSWilliam Hua 	memcpy(buf, &outle32, sizeof(outle32));
290e025be0fSWilliam Hua 	outle32 = __cpu_to_le32(blocks);
291e025be0fSWilliam Hua 	memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
292e025be0fSWilliam Hua 
293e025be0fSWilliam Hua 	return out - buf;
294e025be0fSWilliam Hua }
295e025be0fSWilliam Hua 
296e025be0fSWilliam Hua #define QUERY_CMD_DATA		"data\0"
297e025be0fSWilliam Hua #define QUERY_CMD_DATA_LEN	5
298e025be0fSWilliam Hua 
299e025be0fSWilliam Hua /**
300e025be0fSWilliam Hua  * aa_write_access - generic permissions and data query
301e025be0fSWilliam Hua  * @file: pointer to open apparmorfs/access file
302e025be0fSWilliam Hua  * @ubuf: user buffer containing the complete query string (NOT NULL)
303e025be0fSWilliam Hua  * @count: size of ubuf
304e025be0fSWilliam Hua  * @ppos: position in the file (MUST BE ZERO)
305e025be0fSWilliam Hua  *
306e025be0fSWilliam Hua  * Allows for one permissions or data query per open(), write(), and read()
307e025be0fSWilliam Hua  * sequence. The only queries currently supported are label-based queries for
308e025be0fSWilliam Hua  * permissions or data.
309e025be0fSWilliam Hua  *
310e025be0fSWilliam Hua  * For permissions queries, ubuf must begin with "label\0", followed by the
311e025be0fSWilliam Hua  * profile query specific format described in the query_label() function
312e025be0fSWilliam Hua  * documentation.
313e025be0fSWilliam Hua  *
314e025be0fSWilliam Hua  * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
315e025be0fSWilliam Hua  * <LABEL> is the name of the security confinement context and <KEY> is the
316e025be0fSWilliam Hua  * name of the data to retrieve.
317e025be0fSWilliam Hua  *
318e025be0fSWilliam Hua  * Returns: number of bytes written or -errno on failure
319e025be0fSWilliam Hua  */
320e025be0fSWilliam Hua static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
321e025be0fSWilliam Hua 			       size_t count, loff_t *ppos)
322e025be0fSWilliam Hua {
323e025be0fSWilliam Hua 	char *buf;
324e025be0fSWilliam Hua 	ssize_t len;
325e025be0fSWilliam Hua 
326e025be0fSWilliam Hua 	if (*ppos)
327e025be0fSWilliam Hua 		return -ESPIPE;
328e025be0fSWilliam Hua 
329e025be0fSWilliam Hua 	buf = simple_transaction_get(file, ubuf, count);
330e025be0fSWilliam Hua 	if (IS_ERR(buf))
331e025be0fSWilliam Hua 		return PTR_ERR(buf);
332e025be0fSWilliam Hua 
333e025be0fSWilliam Hua 	if (count > QUERY_CMD_DATA_LEN &&
334e025be0fSWilliam Hua 		   !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
335e025be0fSWilliam Hua 		len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
336e025be0fSWilliam Hua 				 buf + QUERY_CMD_DATA_LEN,
337e025be0fSWilliam Hua 				 count - QUERY_CMD_DATA_LEN);
338e025be0fSWilliam Hua 	} else
339e025be0fSWilliam Hua 		len = -EINVAL;
340e025be0fSWilliam Hua 
341e025be0fSWilliam Hua 	if (len < 0)
342e025be0fSWilliam Hua 		return len;
343e025be0fSWilliam Hua 
344e025be0fSWilliam Hua 	simple_transaction_set(file, len);
345e025be0fSWilliam Hua 
346e025be0fSWilliam Hua 	return count;
347e025be0fSWilliam Hua }
348e025be0fSWilliam Hua 
349e025be0fSWilliam Hua static const struct file_operations aa_fs_access = {
350e025be0fSWilliam Hua 	.write		= aa_write_access,
351e025be0fSWilliam Hua 	.read		= simple_transaction_read,
352e025be0fSWilliam Hua 	.release	= simple_transaction_release,
353e025be0fSWilliam Hua 	.llseek		= generic_file_llseek,
354e025be0fSWilliam Hua };
355e025be0fSWilliam Hua 
356e74abcf3SKees Cook static int aa_fs_seq_show(struct seq_file *seq, void *v)
357e74abcf3SKees Cook {
358e74abcf3SKees Cook 	struct aa_fs_entry *fs_file = seq->private;
359e74abcf3SKees Cook 
360e74abcf3SKees Cook 	if (!fs_file)
361e74abcf3SKees Cook 		return 0;
362e74abcf3SKees Cook 
363e74abcf3SKees Cook 	switch (fs_file->v_type) {
364e74abcf3SKees Cook 	case AA_FS_TYPE_BOOLEAN:
365e74abcf3SKees Cook 		seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no");
366e74abcf3SKees Cook 		break;
367a9bf8e9fSKees Cook 	case AA_FS_TYPE_STRING:
368a9bf8e9fSKees Cook 		seq_printf(seq, "%s\n", fs_file->v.string);
369a9bf8e9fSKees Cook 		break;
370e74abcf3SKees Cook 	case AA_FS_TYPE_U64:
371e74abcf3SKees Cook 		seq_printf(seq, "%#08lx\n", fs_file->v.u64);
372e74abcf3SKees Cook 		break;
373e74abcf3SKees Cook 	default:
374e74abcf3SKees Cook 		/* Ignore unpritable entry types. */
375e74abcf3SKees Cook 		break;
376e74abcf3SKees Cook 	}
377e74abcf3SKees Cook 
378e74abcf3SKees Cook 	return 0;
379e74abcf3SKees Cook }
380e74abcf3SKees Cook 
381e74abcf3SKees Cook static int aa_fs_seq_open(struct inode *inode, struct file *file)
382e74abcf3SKees Cook {
383e74abcf3SKees Cook 	return single_open(file, aa_fs_seq_show, inode->i_private);
384e74abcf3SKees Cook }
385e74abcf3SKees Cook 
386e74abcf3SKees Cook const struct file_operations aa_fs_seq_file_ops = {
387e74abcf3SKees Cook 	.owner		= THIS_MODULE,
388e74abcf3SKees Cook 	.open		= aa_fs_seq_open,
389e74abcf3SKees Cook 	.read		= seq_read,
390e74abcf3SKees Cook 	.llseek		= seq_lseek,
391e74abcf3SKees Cook 	.release	= single_release,
392e74abcf3SKees Cook };
393e74abcf3SKees Cook 
3940d259f04SJohn Johansen static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
3950d259f04SJohn Johansen 				  int (*show)(struct seq_file *, void *))
3960d259f04SJohn Johansen {
3978399588aSJohn Johansen 	struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
3988399588aSJohn Johansen 	int error = single_open(file, show, proxy);
39963e2b423SJohn Johansen 
4000d259f04SJohn Johansen 	if (error) {
4010d259f04SJohn Johansen 		file->private_data = NULL;
4028399588aSJohn Johansen 		aa_put_proxy(proxy);
4030d259f04SJohn Johansen 	}
4040d259f04SJohn Johansen 
4050d259f04SJohn Johansen 	return error;
4060d259f04SJohn Johansen }
4070d259f04SJohn Johansen 
4080d259f04SJohn Johansen static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
4090d259f04SJohn Johansen {
4100d259f04SJohn Johansen 	struct seq_file *seq = (struct seq_file *) file->private_data;
4110d259f04SJohn Johansen 	if (seq)
4128399588aSJohn Johansen 		aa_put_proxy(seq->private);
4130d259f04SJohn Johansen 	return single_release(inode, file);
4140d259f04SJohn Johansen }
4150d259f04SJohn Johansen 
4160d259f04SJohn Johansen static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
4170d259f04SJohn Johansen {
4188399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4198399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
4200d259f04SJohn Johansen 	seq_printf(seq, "%s\n", profile->base.name);
4210d259f04SJohn Johansen 	aa_put_profile(profile);
4220d259f04SJohn Johansen 
4230d259f04SJohn Johansen 	return 0;
4240d259f04SJohn Johansen }
4250d259f04SJohn Johansen 
4260d259f04SJohn Johansen static int aa_fs_seq_profname_open(struct inode *inode, struct file *file)
4270d259f04SJohn Johansen {
4280d259f04SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show);
4290d259f04SJohn Johansen }
4300d259f04SJohn Johansen 
4310d259f04SJohn Johansen static const struct file_operations aa_fs_profname_fops = {
4320d259f04SJohn Johansen 	.owner		= THIS_MODULE,
4330d259f04SJohn Johansen 	.open		= aa_fs_seq_profname_open,
4340d259f04SJohn Johansen 	.read		= seq_read,
4350d259f04SJohn Johansen 	.llseek		= seq_lseek,
4360d259f04SJohn Johansen 	.release	= aa_fs_seq_profile_release,
4370d259f04SJohn Johansen };
4380d259f04SJohn Johansen 
4390d259f04SJohn Johansen static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
4400d259f04SJohn Johansen {
4418399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4428399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
4430d259f04SJohn Johansen 	seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
4440d259f04SJohn Johansen 	aa_put_profile(profile);
4450d259f04SJohn Johansen 
4460d259f04SJohn Johansen 	return 0;
4470d259f04SJohn Johansen }
4480d259f04SJohn Johansen 
4490d259f04SJohn Johansen static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file)
4500d259f04SJohn Johansen {
4510d259f04SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show);
4520d259f04SJohn Johansen }
4530d259f04SJohn Johansen 
4540d259f04SJohn Johansen static const struct file_operations aa_fs_profmode_fops = {
4550d259f04SJohn Johansen 	.owner		= THIS_MODULE,
4560d259f04SJohn Johansen 	.open		= aa_fs_seq_profmode_open,
4570d259f04SJohn Johansen 	.read		= seq_read,
4580d259f04SJohn Johansen 	.llseek		= seq_lseek,
4590d259f04SJohn Johansen 	.release	= aa_fs_seq_profile_release,
4600d259f04SJohn Johansen };
4610d259f04SJohn Johansen 
462556d0be7SJohn Johansen static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
463556d0be7SJohn Johansen {
4648399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4658399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
466556d0be7SJohn Johansen 	if (profile->attach)
467556d0be7SJohn Johansen 		seq_printf(seq, "%s\n", profile->attach);
468556d0be7SJohn Johansen 	else if (profile->xmatch)
469556d0be7SJohn Johansen 		seq_puts(seq, "<unknown>\n");
470556d0be7SJohn Johansen 	else
471556d0be7SJohn Johansen 		seq_printf(seq, "%s\n", profile->base.name);
472556d0be7SJohn Johansen 	aa_put_profile(profile);
473556d0be7SJohn Johansen 
474556d0be7SJohn Johansen 	return 0;
475556d0be7SJohn Johansen }
476556d0be7SJohn Johansen 
477556d0be7SJohn Johansen static int aa_fs_seq_profattach_open(struct inode *inode, struct file *file)
478556d0be7SJohn Johansen {
479556d0be7SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profattach_show);
480556d0be7SJohn Johansen }
481556d0be7SJohn Johansen 
482556d0be7SJohn Johansen static const struct file_operations aa_fs_profattach_fops = {
483556d0be7SJohn Johansen 	.owner		= THIS_MODULE,
484556d0be7SJohn Johansen 	.open		= aa_fs_seq_profattach_open,
485556d0be7SJohn Johansen 	.read		= seq_read,
486556d0be7SJohn Johansen 	.llseek		= seq_lseek,
487556d0be7SJohn Johansen 	.release	= aa_fs_seq_profile_release,
488556d0be7SJohn Johansen };
489556d0be7SJohn Johansen 
490f8eb8a13SJohn Johansen static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
491f8eb8a13SJohn Johansen {
4928399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4938399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
494f8eb8a13SJohn Johansen 	unsigned int i, size = aa_hash_size();
495f8eb8a13SJohn Johansen 
496f8eb8a13SJohn Johansen 	if (profile->hash) {
497f8eb8a13SJohn Johansen 		for (i = 0; i < size; i++)
498f8eb8a13SJohn Johansen 			seq_printf(seq, "%.2x", profile->hash[i]);
49947dbd1cdSMarkus Elfring 		seq_putc(seq, '\n');
500f8eb8a13SJohn Johansen 	}
5010b938a2eSJohn Johansen 	aa_put_profile(profile);
502f8eb8a13SJohn Johansen 
503f8eb8a13SJohn Johansen 	return 0;
504f8eb8a13SJohn Johansen }
505f8eb8a13SJohn Johansen 
506f8eb8a13SJohn Johansen static int aa_fs_seq_hash_open(struct inode *inode, struct file *file)
507f8eb8a13SJohn Johansen {
508f8eb8a13SJohn Johansen 	return single_open(file, aa_fs_seq_hash_show, inode->i_private);
509f8eb8a13SJohn Johansen }
510f8eb8a13SJohn Johansen 
511f8eb8a13SJohn Johansen static const struct file_operations aa_fs_seq_hash_fops = {
512f8eb8a13SJohn Johansen 	.owner		= THIS_MODULE,
513f8eb8a13SJohn Johansen 	.open		= aa_fs_seq_hash_open,
514f8eb8a13SJohn Johansen 	.read		= seq_read,
515f8eb8a13SJohn Johansen 	.llseek		= seq_lseek,
516f8eb8a13SJohn Johansen 	.release	= single_release,
517f8eb8a13SJohn Johansen };
518f8eb8a13SJohn Johansen 
5193e3e5695SJohn Johansen 
520a71ada30SJohn Johansen static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v)
521a71ada30SJohn Johansen {
522a71ada30SJohn Johansen 	struct aa_ns *ns = aa_current_profile()->ns;
523a71ada30SJohn Johansen 
524a71ada30SJohn Johansen 	seq_printf(seq, "%d\n", ns->level);
525a71ada30SJohn Johansen 
526a71ada30SJohn Johansen 	return 0;
527a71ada30SJohn Johansen }
528a71ada30SJohn Johansen 
529a71ada30SJohn Johansen static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file)
530a71ada30SJohn Johansen {
531a71ada30SJohn Johansen 	return single_open(file, aa_fs_seq_show_ns_level, inode->i_private);
532a71ada30SJohn Johansen }
533a71ada30SJohn Johansen 
534a71ada30SJohn Johansen static const struct file_operations aa_fs_ns_level = {
535a71ada30SJohn Johansen 	.owner		= THIS_MODULE,
536a71ada30SJohn Johansen 	.open		= aa_fs_seq_open_ns_level,
537a71ada30SJohn Johansen 	.read		= seq_read,
538a71ada30SJohn Johansen 	.llseek		= seq_lseek,
539a71ada30SJohn Johansen 	.release	= single_release,
540a71ada30SJohn Johansen };
541a71ada30SJohn Johansen 
5423e3e5695SJohn Johansen static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v)
5433e3e5695SJohn Johansen {
5443e3e5695SJohn Johansen 	struct aa_ns *ns = aa_current_profile()->ns;
5453e3e5695SJohn Johansen 
5463e3e5695SJohn Johansen 	seq_printf(seq, "%s\n", ns->base.name);
5473e3e5695SJohn Johansen 
5483e3e5695SJohn Johansen 	return 0;
5493e3e5695SJohn Johansen }
5503e3e5695SJohn Johansen 
5513e3e5695SJohn Johansen static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file)
5523e3e5695SJohn Johansen {
5533e3e5695SJohn Johansen 	return single_open(file, aa_fs_seq_show_ns_name, inode->i_private);
5543e3e5695SJohn Johansen }
5553e3e5695SJohn Johansen 
5563e3e5695SJohn Johansen static const struct file_operations aa_fs_ns_name = {
5573e3e5695SJohn Johansen 	.owner		= THIS_MODULE,
5583e3e5695SJohn Johansen 	.open		= aa_fs_seq_open_ns_name,
5593e3e5695SJohn Johansen 	.read		= seq_read,
5603e3e5695SJohn Johansen 	.llseek		= seq_lseek,
5613e3e5695SJohn Johansen 	.release	= single_release,
5623e3e5695SJohn Johansen };
5633e3e5695SJohn Johansen 
5645d5182caSJohn Johansen 
5655d5182caSJohn Johansen /* policy/raw_data/ * file ops */
5665d5182caSJohn Johansen 
5675d5182caSJohn Johansen #define SEQ_RAWDATA_FOPS(NAME)						      \
5685d5182caSJohn Johansen static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
5695d5182caSJohn Johansen {									      \
5705d5182caSJohn Johansen 	return seq_rawdata_open(inode, file, seq_rawdata_ ##NAME ##_show);    \
5715d5182caSJohn Johansen }									      \
5725d5182caSJohn Johansen 									      \
5735d5182caSJohn Johansen static const struct file_operations seq_rawdata_ ##NAME ##_fops = {	      \
5745d5182caSJohn Johansen 	.owner		= THIS_MODULE,					      \
5755d5182caSJohn Johansen 	.open		= seq_rawdata_ ##NAME ##_open,			      \
5765d5182caSJohn Johansen 	.read		= seq_read,					      \
5775d5182caSJohn Johansen 	.llseek		= seq_lseek,					      \
5785d5182caSJohn Johansen 	.release	= seq_rawdata_release,				      \
5795d5182caSJohn Johansen }									      \
5805d5182caSJohn Johansen 
5815d5182caSJohn Johansen static int seq_rawdata_open(struct inode *inode, struct file *file,
5825d5182caSJohn Johansen 			    int (*show)(struct seq_file *, void *))
5835ac8c355SJohn Johansen {
5845d5182caSJohn Johansen 	struct aa_loaddata *data = __aa_get_loaddata(inode->i_private);
5855d5182caSJohn Johansen 	int error;
5865d5182caSJohn Johansen 
5875d5182caSJohn Johansen 	if (!data)
5885d5182caSJohn Johansen 		/* lost race this ent is being reaped */
5895d5182caSJohn Johansen 		return -ENOENT;
5905d5182caSJohn Johansen 
5915d5182caSJohn Johansen 	error = single_open(file, show, data);
5925d5182caSJohn Johansen 	if (error) {
5935d5182caSJohn Johansen 		AA_BUG(file->private_data &&
5945d5182caSJohn Johansen 		       ((struct seq_file *)file->private_data)->private);
5955d5182caSJohn Johansen 		aa_put_loaddata(data);
5965d5182caSJohn Johansen 	}
5975d5182caSJohn Johansen 
5985d5182caSJohn Johansen 	return error;
5995d5182caSJohn Johansen }
6005d5182caSJohn Johansen 
6015d5182caSJohn Johansen static int seq_rawdata_release(struct inode *inode, struct file *file)
6025d5182caSJohn Johansen {
6035d5182caSJohn Johansen 	struct seq_file *seq = (struct seq_file *) file->private_data;
6045d5182caSJohn Johansen 
6055d5182caSJohn Johansen 	if (seq)
6065d5182caSJohn Johansen 		aa_put_loaddata(seq->private);
6075d5182caSJohn Johansen 
6085d5182caSJohn Johansen 	return single_release(inode, file);
6095d5182caSJohn Johansen }
6105d5182caSJohn Johansen 
6115d5182caSJohn Johansen static int seq_rawdata_abi_show(struct seq_file *seq, void *v)
6125d5182caSJohn Johansen {
6135d5182caSJohn Johansen 	struct aa_loaddata *data = seq->private;
6145d5182caSJohn Johansen 
6155d5182caSJohn Johansen 	seq_printf(seq, "v%d\n", data->abi);
6165ac8c355SJohn Johansen 
6175ac8c355SJohn Johansen 	return 0;
6185ac8c355SJohn Johansen }
6195ac8c355SJohn Johansen 
6205d5182caSJohn Johansen static int seq_rawdata_revision_show(struct seq_file *seq, void *v)
6215ac8c355SJohn Johansen {
6225d5182caSJohn Johansen 	struct aa_loaddata *data = seq->private;
6235ac8c355SJohn Johansen 
6245d5182caSJohn Johansen 	seq_printf(seq, "%ld\n", data->revision);
6255ac8c355SJohn Johansen 
6265ac8c355SJohn Johansen 	return 0;
6275ac8c355SJohn Johansen }
6285ac8c355SJohn Johansen 
6295d5182caSJohn Johansen static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
6305ac8c355SJohn Johansen {
6315d5182caSJohn Johansen 	struct aa_loaddata *data = seq->private;
6325ac8c355SJohn Johansen 	unsigned int i, size = aa_hash_size();
6335ac8c355SJohn Johansen 
6345d5182caSJohn Johansen 	if (data->hash) {
6355ac8c355SJohn Johansen 		for (i = 0; i < size; i++)
6365d5182caSJohn Johansen 			seq_printf(seq, "%.2x", data->hash[i]);
63747dbd1cdSMarkus Elfring 		seq_putc(seq, '\n');
6385ac8c355SJohn Johansen 	}
6395ac8c355SJohn Johansen 
6405ac8c355SJohn Johansen 	return 0;
6415ac8c355SJohn Johansen }
6425ac8c355SJohn Johansen 
6435d5182caSJohn Johansen SEQ_RAWDATA_FOPS(abi);
6445d5182caSJohn Johansen SEQ_RAWDATA_FOPS(revision);
6455d5182caSJohn Johansen SEQ_RAWDATA_FOPS(hash);
6465ac8c355SJohn Johansen 
6475ac8c355SJohn Johansen static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
6485ac8c355SJohn Johansen 			    loff_t *ppos)
6495ac8c355SJohn Johansen {
6505ac8c355SJohn Johansen 	struct aa_loaddata *rawdata = file->private_data;
6515ac8c355SJohn Johansen 
6525ac8c355SJohn Johansen 	return simple_read_from_buffer(buf, size, ppos, rawdata->data,
6535ac8c355SJohn Johansen 				       rawdata->size);
6545ac8c355SJohn Johansen }
6555ac8c355SJohn Johansen 
6565d5182caSJohn Johansen static int rawdata_release(struct inode *inode, struct file *file)
6575ac8c355SJohn Johansen {
6585d5182caSJohn Johansen 	aa_put_loaddata(file->private_data);
6595ac8c355SJohn Johansen 
6605ac8c355SJohn Johansen 	return 0;
6615ac8c355SJohn Johansen }
6625ac8c355SJohn Johansen 
6635d5182caSJohn Johansen static int rawdata_open(struct inode *inode, struct file *file)
6645d5182caSJohn Johansen {
6655d5182caSJohn Johansen 	if (!policy_view_capable(NULL))
6665d5182caSJohn Johansen 		return -EACCES;
6675d5182caSJohn Johansen 	file->private_data = __aa_get_loaddata(inode->i_private);
6685d5182caSJohn Johansen 	if (!file->private_data)
6695d5182caSJohn Johansen 		/* lost race: this entry is being reaped */
6705d5182caSJohn Johansen 		return -ENOENT;
6715d5182caSJohn Johansen 
6725d5182caSJohn Johansen 	return 0;
6735d5182caSJohn Johansen }
6745d5182caSJohn Johansen 
6755d5182caSJohn Johansen static const struct file_operations rawdata_fops = {
6765ac8c355SJohn Johansen 	.open = rawdata_open,
6775ac8c355SJohn Johansen 	.read = rawdata_read,
6785ac8c355SJohn Johansen 	.llseek = generic_file_llseek,
6795ac8c355SJohn Johansen 	.release = rawdata_release,
6805ac8c355SJohn Johansen };
6815ac8c355SJohn Johansen 
6825d5182caSJohn Johansen static void remove_rawdata_dents(struct aa_loaddata *rawdata)
6835d5182caSJohn Johansen {
6845d5182caSJohn Johansen 	int i;
6855d5182caSJohn Johansen 
6865d5182caSJohn Johansen 	for (i = 0; i < AAFS_LOADDATA_NDENTS; i++) {
6875d5182caSJohn Johansen 		if (!IS_ERR_OR_NULL(rawdata->dents[i])) {
6885d5182caSJohn Johansen 			/* no refcounts on i_private */
6895d5182caSJohn Johansen 			securityfs_remove(rawdata->dents[i]);
6905d5182caSJohn Johansen 			rawdata->dents[i] = NULL;
6915d5182caSJohn Johansen 		}
6925d5182caSJohn Johansen 	}
6935d5182caSJohn Johansen }
6945d5182caSJohn Johansen 
6955d5182caSJohn Johansen void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
6965d5182caSJohn Johansen {
6975d5182caSJohn Johansen 	AA_BUG(rawdata->ns && !mutex_is_locked(&rawdata->ns->lock));
6985d5182caSJohn Johansen 
6995d5182caSJohn Johansen 	if (rawdata->ns) {
7005d5182caSJohn Johansen 		remove_rawdata_dents(rawdata);
7015d5182caSJohn Johansen 		list_del_init(&rawdata->list);
7025d5182caSJohn Johansen 		aa_put_ns(rawdata->ns);
7035d5182caSJohn Johansen 		rawdata->ns = NULL;
7045d5182caSJohn Johansen 	}
7055d5182caSJohn Johansen }
7065d5182caSJohn Johansen 
7075d5182caSJohn Johansen int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
7085d5182caSJohn Johansen {
7095d5182caSJohn Johansen 	struct dentry *dent, *dir;
7105d5182caSJohn Johansen 
7115d5182caSJohn Johansen 	AA_BUG(!ns);
7125d5182caSJohn Johansen 	AA_BUG(!rawdata);
7135d5182caSJohn Johansen 	AA_BUG(!mutex_is_locked(&ns->lock));
7145d5182caSJohn Johansen 	AA_BUG(!ns_subdata_dir(ns));
7155d5182caSJohn Johansen 
7165d5182caSJohn Johansen 	/*
7175d5182caSJohn Johansen 	 * just use ns revision dir was originally created at. This is
7185d5182caSJohn Johansen 	 * under ns->lock and if load is successful revision will be
7195d5182caSJohn Johansen 	 * bumped and is guaranteed to be unique
7205d5182caSJohn Johansen 	 */
7215d5182caSJohn Johansen 	rawdata->name = kasprintf(GFP_KERNEL, "%ld", ns->revision);
7225d5182caSJohn Johansen 	if (!rawdata->name)
7235d5182caSJohn Johansen 		return -ENOMEM;
7245d5182caSJohn Johansen 
7255d5182caSJohn Johansen 	dir = securityfs_create_dir(rawdata->name, ns_subdata_dir(ns));
7265d5182caSJohn Johansen 	if (IS_ERR(dir))
7275d5182caSJohn Johansen 		/* ->name freed when rawdata freed */
7285d5182caSJohn Johansen 		return PTR_ERR(dir);
7295d5182caSJohn Johansen 	rawdata->dents[AAFS_LOADDATA_DIR] = dir;
7305d5182caSJohn Johansen 
7315d5182caSJohn Johansen 	dent = securityfs_create_file("abi", S_IFREG | 0444, dir, rawdata,
7325d5182caSJohn Johansen 				      &seq_rawdata_abi_fops);
7335d5182caSJohn Johansen 	if (IS_ERR(dent))
7345d5182caSJohn Johansen 		goto fail;
7355d5182caSJohn Johansen 	rawdata->dents[AAFS_LOADDATA_ABI] = dent;
7365d5182caSJohn Johansen 
7375d5182caSJohn Johansen 	dent = securityfs_create_file("revision", S_IFREG | 0444, dir, rawdata,
7385d5182caSJohn Johansen 				      &seq_rawdata_revision_fops);
7395d5182caSJohn Johansen 	if (IS_ERR(dent))
7405d5182caSJohn Johansen 		goto fail;
7415d5182caSJohn Johansen 	rawdata->dents[AAFS_LOADDATA_REVISION] = dent;
7425d5182caSJohn Johansen 
7435d5182caSJohn Johansen 	if (aa_g_hash_policy) {
7445d5182caSJohn Johansen 		dent = securityfs_create_file("sha1", S_IFREG | 0444, dir,
7455d5182caSJohn Johansen 					      rawdata, &seq_rawdata_hash_fops);
7465d5182caSJohn Johansen 		if (IS_ERR(dent))
7475d5182caSJohn Johansen 			goto fail;
7485d5182caSJohn Johansen 		rawdata->dents[AAFS_LOADDATA_HASH] = dent;
7495d5182caSJohn Johansen 	}
7505d5182caSJohn Johansen 
7515d5182caSJohn Johansen 	dent = securityfs_create_file("raw_data", S_IFREG | 0444,
7525d5182caSJohn Johansen 				      dir, rawdata, &rawdata_fops);
7535d5182caSJohn Johansen 	if (IS_ERR(dent))
7545d5182caSJohn Johansen 		goto fail;
7555d5182caSJohn Johansen 	rawdata->dents[AAFS_LOADDATA_DATA] = dent;
7565d5182caSJohn Johansen 	d_inode(dent)->i_size = rawdata->size;
7575d5182caSJohn Johansen 
7585d5182caSJohn Johansen 	rawdata->ns = aa_get_ns(ns);
7595d5182caSJohn Johansen 	list_add(&rawdata->list, &ns->rawdata_list);
7605d5182caSJohn Johansen 	/* no refcount on inode rawdata */
7615d5182caSJohn Johansen 
7625d5182caSJohn Johansen 	return 0;
7635d5182caSJohn Johansen 
7645d5182caSJohn Johansen fail:
7655d5182caSJohn Johansen 	remove_rawdata_dents(rawdata);
7665d5182caSJohn Johansen 
7675d5182caSJohn Johansen 	return PTR_ERR(dent);
7685d5182caSJohn Johansen }
7695d5182caSJohn Johansen 
7700d259f04SJohn Johansen /** fns to setup dynamic per profile/namespace files **/
7710d259f04SJohn Johansen void __aa_fs_profile_rmdir(struct aa_profile *profile)
7720d259f04SJohn Johansen {
7730d259f04SJohn Johansen 	struct aa_profile *child;
7740d259f04SJohn Johansen 	int i;
7750d259f04SJohn Johansen 
7760d259f04SJohn Johansen 	if (!profile)
7770d259f04SJohn Johansen 		return;
7780d259f04SJohn Johansen 
7790d259f04SJohn Johansen 	list_for_each_entry(child, &profile->base.profiles, base.list)
7800d259f04SJohn Johansen 		__aa_fs_profile_rmdir(child);
7810d259f04SJohn Johansen 
7820d259f04SJohn Johansen 	for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
7838399588aSJohn Johansen 		struct aa_proxy *proxy;
7840d259f04SJohn Johansen 		if (!profile->dents[i])
7850d259f04SJohn Johansen 			continue;
7860d259f04SJohn Johansen 
7878399588aSJohn Johansen 		proxy = d_inode(profile->dents[i])->i_private;
7880d259f04SJohn Johansen 		securityfs_remove(profile->dents[i]);
7898399588aSJohn Johansen 		aa_put_proxy(proxy);
7900d259f04SJohn Johansen 		profile->dents[i] = NULL;
7910d259f04SJohn Johansen 	}
7920d259f04SJohn Johansen }
7930d259f04SJohn Johansen 
7940d259f04SJohn Johansen void __aa_fs_profile_migrate_dents(struct aa_profile *old,
7950d259f04SJohn Johansen 				   struct aa_profile *new)
7960d259f04SJohn Johansen {
7970d259f04SJohn Johansen 	int i;
7980d259f04SJohn Johansen 
7990d259f04SJohn Johansen 	for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
8000d259f04SJohn Johansen 		new->dents[i] = old->dents[i];
801d671e890SJohn Johansen 		if (new->dents[i])
802078cd827SDeepa Dinamani 			new->dents[i]->d_inode->i_mtime = current_time(new->dents[i]->d_inode);
8030d259f04SJohn Johansen 		old->dents[i] = NULL;
8040d259f04SJohn Johansen 	}
8050d259f04SJohn Johansen }
8060d259f04SJohn Johansen 
8070d259f04SJohn Johansen static struct dentry *create_profile_file(struct dentry *dir, const char *name,
8080d259f04SJohn Johansen 					  struct aa_profile *profile,
8090d259f04SJohn Johansen 					  const struct file_operations *fops)
8100d259f04SJohn Johansen {
8118399588aSJohn Johansen 	struct aa_proxy *proxy = aa_get_proxy(profile->proxy);
8120d259f04SJohn Johansen 	struct dentry *dent;
8130d259f04SJohn Johansen 
8148399588aSJohn Johansen 	dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
8150d259f04SJohn Johansen 	if (IS_ERR(dent))
8168399588aSJohn Johansen 		aa_put_proxy(proxy);
8170d259f04SJohn Johansen 
8180d259f04SJohn Johansen 	return dent;
8190d259f04SJohn Johansen }
8200d259f04SJohn Johansen 
8215d5182caSJohn Johansen static int profile_depth(struct aa_profile *profile)
8225d5182caSJohn Johansen {
8235d5182caSJohn Johansen 	int depth = 0;
8245d5182caSJohn Johansen 
8255d5182caSJohn Johansen 	rcu_read_lock();
8265d5182caSJohn Johansen 	for (depth = 0; profile; profile = rcu_access_pointer(profile->parent))
8275d5182caSJohn Johansen 		depth++;
8285d5182caSJohn Johansen 	rcu_read_unlock();
8295d5182caSJohn Johansen 
8305d5182caSJohn Johansen 	return depth;
8315d5182caSJohn Johansen }
8325d5182caSJohn Johansen 
8335d5182caSJohn Johansen static int gen_symlink_name(char *buffer, size_t bsize, int depth,
8345d5182caSJohn Johansen 			    const char *dirname, const char *fname)
8355d5182caSJohn Johansen {
8365d5182caSJohn Johansen 	int error;
8375d5182caSJohn Johansen 
8385d5182caSJohn Johansen 	for (; depth > 0; depth--) {
8395d5182caSJohn Johansen 		if (bsize < 7)
8405d5182caSJohn Johansen 			return -ENAMETOOLONG;
8415d5182caSJohn Johansen 		strcpy(buffer, "../../");
8425d5182caSJohn Johansen 		buffer += 6;
8435d5182caSJohn Johansen 		bsize -= 6;
8445d5182caSJohn Johansen 	}
8455d5182caSJohn Johansen 
8465d5182caSJohn Johansen 	error = snprintf(buffer, bsize, "raw_data/%s/%s", dirname, fname);
8475d5182caSJohn Johansen 	if (error >= bsize || error < 0)
8485d5182caSJohn Johansen 		return -ENAMETOOLONG;
8495d5182caSJohn Johansen 
8505d5182caSJohn Johansen 	return 0;
8515d5182caSJohn Johansen }
8525d5182caSJohn Johansen 
8535d5182caSJohn Johansen /*
8545d5182caSJohn Johansen  * Requires: @profile->ns->lock held
8555d5182caSJohn Johansen  */
8560d259f04SJohn Johansen int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
8570d259f04SJohn Johansen {
8580d259f04SJohn Johansen 	struct aa_profile *child;
8590d259f04SJohn Johansen 	struct dentry *dent = NULL, *dir;
8600d259f04SJohn Johansen 	int error;
8610d259f04SJohn Johansen 
8620d259f04SJohn Johansen 	if (!parent) {
8630d259f04SJohn Johansen 		struct aa_profile *p;
8640d259f04SJohn Johansen 		p = aa_deref_parent(profile);
8650d259f04SJohn Johansen 		dent = prof_dir(p);
8660d259f04SJohn Johansen 		/* adding to parent that previously didn't have children */
8670d259f04SJohn Johansen 		dent = securityfs_create_dir("profiles", dent);
8680d259f04SJohn Johansen 		if (IS_ERR(dent))
8690d259f04SJohn Johansen 			goto fail;
8700d259f04SJohn Johansen 		prof_child_dir(p) = parent = dent;
8710d259f04SJohn Johansen 	}
8720d259f04SJohn Johansen 
8730d259f04SJohn Johansen 	if (!profile->dirname) {
8740d259f04SJohn Johansen 		int len, id_len;
8750d259f04SJohn Johansen 		len = mangle_name(profile->base.name, NULL);
8760d259f04SJohn Johansen 		id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);
8770d259f04SJohn Johansen 
8780d259f04SJohn Johansen 		profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
879ffac1de6SDan Carpenter 		if (!profile->dirname) {
880ffac1de6SDan Carpenter 			error = -ENOMEM;
881ffac1de6SDan Carpenter 			goto fail2;
882ffac1de6SDan Carpenter 		}
8830d259f04SJohn Johansen 
8840d259f04SJohn Johansen 		mangle_name(profile->base.name, profile->dirname);
8850d259f04SJohn Johansen 		sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
8860d259f04SJohn Johansen 	}
8870d259f04SJohn Johansen 
8880d259f04SJohn Johansen 	dent = securityfs_create_dir(profile->dirname, parent);
8890d259f04SJohn Johansen 	if (IS_ERR(dent))
8900d259f04SJohn Johansen 		goto fail;
8910d259f04SJohn Johansen 	prof_dir(profile) = dir = dent;
8920d259f04SJohn Johansen 
8930d259f04SJohn Johansen 	dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops);
8940d259f04SJohn Johansen 	if (IS_ERR(dent))
8950d259f04SJohn Johansen 		goto fail;
8960d259f04SJohn Johansen 	profile->dents[AAFS_PROF_NAME] = dent;
8970d259f04SJohn Johansen 
8980d259f04SJohn Johansen 	dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops);
8990d259f04SJohn Johansen 	if (IS_ERR(dent))
9000d259f04SJohn Johansen 		goto fail;
9010d259f04SJohn Johansen 	profile->dents[AAFS_PROF_MODE] = dent;
9020d259f04SJohn Johansen 
903556d0be7SJohn Johansen 	dent = create_profile_file(dir, "attach", profile,
904556d0be7SJohn Johansen 				   &aa_fs_profattach_fops);
905556d0be7SJohn Johansen 	if (IS_ERR(dent))
906556d0be7SJohn Johansen 		goto fail;
907556d0be7SJohn Johansen 	profile->dents[AAFS_PROF_ATTACH] = dent;
908556d0be7SJohn Johansen 
909f8eb8a13SJohn Johansen 	if (profile->hash) {
910f8eb8a13SJohn Johansen 		dent = create_profile_file(dir, "sha1", profile,
911f8eb8a13SJohn Johansen 					   &aa_fs_seq_hash_fops);
912f8eb8a13SJohn Johansen 		if (IS_ERR(dent))
913f8eb8a13SJohn Johansen 			goto fail;
914f8eb8a13SJohn Johansen 		profile->dents[AAFS_PROF_HASH] = dent;
915f8eb8a13SJohn Johansen 	}
916f8eb8a13SJohn Johansen 
9175ac8c355SJohn Johansen 	if (profile->rawdata) {
9185d5182caSJohn Johansen 		char target[64];
9195d5182caSJohn Johansen 		int depth = profile_depth(profile);
9205d5182caSJohn Johansen 
9215d5182caSJohn Johansen 		error = gen_symlink_name(target, sizeof(target), depth,
9225d5182caSJohn Johansen 					 profile->rawdata->name, "sha1");
9235d5182caSJohn Johansen 		if (error < 0)
9245d5182caSJohn Johansen 			goto fail2;
9255d5182caSJohn Johansen 		dent = securityfs_create_symlink("raw_sha1", dir, target, NULL);
9265ac8c355SJohn Johansen 		if (IS_ERR(dent))
9275ac8c355SJohn Johansen 			goto fail;
9285ac8c355SJohn Johansen 		profile->dents[AAFS_PROF_RAW_HASH] = dent;
9295ac8c355SJohn Johansen 
9305d5182caSJohn Johansen 		error = gen_symlink_name(target, sizeof(target), depth,
9315d5182caSJohn Johansen 					 profile->rawdata->name, "abi");
9325d5182caSJohn Johansen 		if (error < 0)
9335d5182caSJohn Johansen 			goto fail2;
9345d5182caSJohn Johansen 		dent = securityfs_create_symlink("raw_abi", dir, target, NULL);
9355ac8c355SJohn Johansen 		if (IS_ERR(dent))
9365ac8c355SJohn Johansen 			goto fail;
9375ac8c355SJohn Johansen 		profile->dents[AAFS_PROF_RAW_ABI] = dent;
9385ac8c355SJohn Johansen 
9395d5182caSJohn Johansen 		error = gen_symlink_name(target, sizeof(target), depth,
9405d5182caSJohn Johansen 					 profile->rawdata->name, "raw_data");
9415d5182caSJohn Johansen 		if (error < 0)
9425d5182caSJohn Johansen 			goto fail2;
9435d5182caSJohn Johansen 		dent = securityfs_create_symlink("raw_data", dir, target, NULL);
9445ac8c355SJohn Johansen 		if (IS_ERR(dent))
9455ac8c355SJohn Johansen 			goto fail;
9465ac8c355SJohn Johansen 		profile->dents[AAFS_PROF_RAW_DATA] = dent;
9475ac8c355SJohn Johansen 	}
9485ac8c355SJohn Johansen 
9490d259f04SJohn Johansen 	list_for_each_entry(child, &profile->base.profiles, base.list) {
9500d259f04SJohn Johansen 		error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
9510d259f04SJohn Johansen 		if (error)
9520d259f04SJohn Johansen 			goto fail2;
9530d259f04SJohn Johansen 	}
9540d259f04SJohn Johansen 
9550d259f04SJohn Johansen 	return 0;
9560d259f04SJohn Johansen 
9570d259f04SJohn Johansen fail:
9580d259f04SJohn Johansen 	error = PTR_ERR(dent);
9590d259f04SJohn Johansen 
9600d259f04SJohn Johansen fail2:
9610d259f04SJohn Johansen 	__aa_fs_profile_rmdir(profile);
9620d259f04SJohn Johansen 
9630d259f04SJohn Johansen 	return error;
9640d259f04SJohn Johansen }
9650d259f04SJohn Johansen 
9665d5182caSJohn Johansen static void __aa_fs_list_remove_rawdata(struct aa_ns *ns)
9675d5182caSJohn Johansen {
9685d5182caSJohn Johansen 	struct aa_loaddata *ent, *tmp;
9695d5182caSJohn Johansen 
9705d5182caSJohn Johansen 	AA_BUG(!mutex_is_locked(&ns->lock));
9715d5182caSJohn Johansen 
9725d5182caSJohn Johansen 	list_for_each_entry_safe(ent, tmp, &ns->rawdata_list, list)
9735d5182caSJohn Johansen 		__aa_fs_remove_rawdata(ent);
9745d5182caSJohn Johansen }
9755d5182caSJohn Johansen 
97698849dffSJohn Johansen void __aa_fs_ns_rmdir(struct aa_ns *ns)
9770d259f04SJohn Johansen {
97898849dffSJohn Johansen 	struct aa_ns *sub;
9790d259f04SJohn Johansen 	struct aa_profile *child;
9800d259f04SJohn Johansen 	int i;
9810d259f04SJohn Johansen 
9820d259f04SJohn Johansen 	if (!ns)
9830d259f04SJohn Johansen 		return;
9840d259f04SJohn Johansen 
9850d259f04SJohn Johansen 	list_for_each_entry(child, &ns->base.profiles, base.list)
9860d259f04SJohn Johansen 		__aa_fs_profile_rmdir(child);
9870d259f04SJohn Johansen 
9880d259f04SJohn Johansen 	list_for_each_entry(sub, &ns->sub_ns, base.list) {
9890d259f04SJohn Johansen 		mutex_lock(&sub->lock);
99098849dffSJohn Johansen 		__aa_fs_ns_rmdir(sub);
9910d259f04SJohn Johansen 		mutex_unlock(&sub->lock);
9920d259f04SJohn Johansen 	}
9930d259f04SJohn Johansen 
9945d5182caSJohn Johansen 	__aa_fs_list_remove_rawdata(ns);
9955d5182caSJohn Johansen 
996b7fd2c03SJohn Johansen 	if (ns_subns_dir(ns)) {
997b7fd2c03SJohn Johansen 		sub = d_inode(ns_subns_dir(ns))->i_private;
998b7fd2c03SJohn Johansen 		aa_put_ns(sub);
999b7fd2c03SJohn Johansen 	}
1000b7fd2c03SJohn Johansen 	if (ns_subload(ns)) {
1001b7fd2c03SJohn Johansen 		sub = d_inode(ns_subload(ns))->i_private;
1002b7fd2c03SJohn Johansen 		aa_put_ns(sub);
1003b7fd2c03SJohn Johansen 	}
1004b7fd2c03SJohn Johansen 	if (ns_subreplace(ns)) {
1005b7fd2c03SJohn Johansen 		sub = d_inode(ns_subreplace(ns))->i_private;
1006b7fd2c03SJohn Johansen 		aa_put_ns(sub);
1007b7fd2c03SJohn Johansen 	}
1008b7fd2c03SJohn Johansen 	if (ns_subremove(ns)) {
1009b7fd2c03SJohn Johansen 		sub = d_inode(ns_subremove(ns))->i_private;
1010b7fd2c03SJohn Johansen 		aa_put_ns(sub);
1011b7fd2c03SJohn Johansen 	}
1012b7fd2c03SJohn Johansen 
10130d259f04SJohn Johansen 	for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
10140d259f04SJohn Johansen 		securityfs_remove(ns->dents[i]);
10150d259f04SJohn Johansen 		ns->dents[i] = NULL;
10160d259f04SJohn Johansen 	}
10170d259f04SJohn Johansen }
10180d259f04SJohn Johansen 
1019b7fd2c03SJohn Johansen /* assumes cleanup in caller */
1020b7fd2c03SJohn Johansen static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
1021b7fd2c03SJohn Johansen {
1022b7fd2c03SJohn Johansen 	struct dentry *dent;
1023b7fd2c03SJohn Johansen 
1024b7fd2c03SJohn Johansen 	AA_BUG(!ns);
1025b7fd2c03SJohn Johansen 	AA_BUG(!dir);
1026b7fd2c03SJohn Johansen 
1027b7fd2c03SJohn Johansen 	dent = securityfs_create_dir("profiles", dir);
1028b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
1029b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
1030b7fd2c03SJohn Johansen 	ns_subprofs_dir(ns) = dent;
1031b7fd2c03SJohn Johansen 
1032b7fd2c03SJohn Johansen 	dent = securityfs_create_dir("raw_data", dir);
1033b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
1034b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
1035b7fd2c03SJohn Johansen 	ns_subdata_dir(ns) = dent;
1036b7fd2c03SJohn Johansen 
1037b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".load", 0640, dir, ns,
1038b7fd2c03SJohn Johansen 				      &aa_fs_profile_load);
1039b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
1040b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
1041b7fd2c03SJohn Johansen 	aa_get_ns(ns);
1042b7fd2c03SJohn Johansen 	ns_subload(ns) = dent;
1043b7fd2c03SJohn Johansen 
1044b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".replace", 0640, dir, ns,
1045b7fd2c03SJohn Johansen 				      &aa_fs_profile_replace);
1046b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
1047b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
1048b7fd2c03SJohn Johansen 	aa_get_ns(ns);
1049b7fd2c03SJohn Johansen 	ns_subreplace(ns) = dent;
1050b7fd2c03SJohn Johansen 
1051b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".remove", 0640, dir, ns,
1052b7fd2c03SJohn Johansen 				      &aa_fs_profile_remove);
1053b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
1054b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
1055b7fd2c03SJohn Johansen 	aa_get_ns(ns);
1056b7fd2c03SJohn Johansen 	ns_subremove(ns) = dent;
1057b7fd2c03SJohn Johansen 
1058b7fd2c03SJohn Johansen 	dent = securityfs_create_dir("namespaces", dir);
1059b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
1060b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
1061b7fd2c03SJohn Johansen 	aa_get_ns(ns);
1062b7fd2c03SJohn Johansen 	ns_subns_dir(ns) = dent;
1063b7fd2c03SJohn Johansen 
1064b7fd2c03SJohn Johansen 	return 0;
1065b7fd2c03SJohn Johansen }
1066b7fd2c03SJohn Johansen 
106798849dffSJohn Johansen int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
10680d259f04SJohn Johansen {
106998849dffSJohn Johansen 	struct aa_ns *sub;
10700d259f04SJohn Johansen 	struct aa_profile *child;
10710d259f04SJohn Johansen 	struct dentry *dent, *dir;
10720d259f04SJohn Johansen 	int error;
10730d259f04SJohn Johansen 
1074b7fd2c03SJohn Johansen 	AA_BUG(!ns);
1075b7fd2c03SJohn Johansen 	AA_BUG(!parent);
1076b7fd2c03SJohn Johansen 	AA_BUG(!mutex_is_locked(&ns->lock));
1077b7fd2c03SJohn Johansen 
10780d259f04SJohn Johansen 	if (!name)
10790d259f04SJohn Johansen 		name = ns->base.name;
10800d259f04SJohn Johansen 
1081b7fd2c03SJohn Johansen 	/* create ns dir if it doesn't already exist */
10820d259f04SJohn Johansen 	dent = securityfs_create_dir(name, parent);
10830d259f04SJohn Johansen 	if (IS_ERR(dent))
10840d259f04SJohn Johansen 		goto fail;
1085b7fd2c03SJohn Johansen 
10860d259f04SJohn Johansen 	ns_dir(ns) = dir = dent;
1087b7fd2c03SJohn Johansen 	error = __aa_fs_ns_mkdir_entries(ns, dir);
1088b7fd2c03SJohn Johansen 	if (error)
1089b7fd2c03SJohn Johansen 		goto fail2;
10900d259f04SJohn Johansen 
1091b7fd2c03SJohn Johansen 	/* profiles */
10920d259f04SJohn Johansen 	list_for_each_entry(child, &ns->base.profiles, base.list) {
10930d259f04SJohn Johansen 		error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
10940d259f04SJohn Johansen 		if (error)
10950d259f04SJohn Johansen 			goto fail2;
10960d259f04SJohn Johansen 	}
10970d259f04SJohn Johansen 
1098b7fd2c03SJohn Johansen 	/* subnamespaces */
10990d259f04SJohn Johansen 	list_for_each_entry(sub, &ns->sub_ns, base.list) {
11000d259f04SJohn Johansen 		mutex_lock(&sub->lock);
110198849dffSJohn Johansen 		error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
11020d259f04SJohn Johansen 		mutex_unlock(&sub->lock);
11030d259f04SJohn Johansen 		if (error)
11040d259f04SJohn Johansen 			goto fail2;
11050d259f04SJohn Johansen 	}
11060d259f04SJohn Johansen 
11070d259f04SJohn Johansen 	return 0;
11080d259f04SJohn Johansen 
11090d259f04SJohn Johansen fail:
11100d259f04SJohn Johansen 	error = PTR_ERR(dent);
11110d259f04SJohn Johansen 
11120d259f04SJohn Johansen fail2:
111398849dffSJohn Johansen 	__aa_fs_ns_rmdir(ns);
11140d259f04SJohn Johansen 
11150d259f04SJohn Johansen 	return error;
11160d259f04SJohn Johansen }
11170d259f04SJohn Johansen 
11180d259f04SJohn Johansen 
111929b3822fSJohn Johansen #define list_entry_is_head(pos, head, member) (&pos->member == (head))
112029b3822fSJohn Johansen 
112129b3822fSJohn Johansen /**
112298849dffSJohn Johansen  * __next_ns - find the next namespace to list
112329b3822fSJohn Johansen  * @root: root namespace to stop search at (NOT NULL)
112429b3822fSJohn Johansen  * @ns: current ns position (NOT NULL)
112529b3822fSJohn Johansen  *
112629b3822fSJohn Johansen  * Find the next namespace from @ns under @root and handle all locking needed
112729b3822fSJohn Johansen  * while switching current namespace.
112829b3822fSJohn Johansen  *
112929b3822fSJohn Johansen  * Returns: next namespace or NULL if at last namespace under @root
113029b3822fSJohn Johansen  * Requires: ns->parent->lock to be held
113129b3822fSJohn Johansen  * NOTE: will not unlock root->lock
113229b3822fSJohn Johansen  */
113398849dffSJohn Johansen static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
113429b3822fSJohn Johansen {
113598849dffSJohn Johansen 	struct aa_ns *parent, *next;
113629b3822fSJohn Johansen 
113729b3822fSJohn Johansen 	/* is next namespace a child */
113829b3822fSJohn Johansen 	if (!list_empty(&ns->sub_ns)) {
113929b3822fSJohn Johansen 		next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
114029b3822fSJohn Johansen 		mutex_lock(&next->lock);
114129b3822fSJohn Johansen 		return next;
114229b3822fSJohn Johansen 	}
114329b3822fSJohn Johansen 
114429b3822fSJohn Johansen 	/* check if the next ns is a sibling, parent, gp, .. */
114529b3822fSJohn Johansen 	parent = ns->parent;
1146ed2c7da3SJohn Johansen 	while (ns != root) {
114729b3822fSJohn Johansen 		mutex_unlock(&ns->lock);
114838dbd7d8SGeliang Tang 		next = list_next_entry(ns, base.list);
114929b3822fSJohn Johansen 		if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
115029b3822fSJohn Johansen 			mutex_lock(&next->lock);
115129b3822fSJohn Johansen 			return next;
115229b3822fSJohn Johansen 		}
115329b3822fSJohn Johansen 		ns = parent;
115429b3822fSJohn Johansen 		parent = parent->parent;
115529b3822fSJohn Johansen 	}
115629b3822fSJohn Johansen 
115729b3822fSJohn Johansen 	return NULL;
115829b3822fSJohn Johansen }
115929b3822fSJohn Johansen 
116029b3822fSJohn Johansen /**
116129b3822fSJohn Johansen  * __first_profile - find the first profile in a namespace
116229b3822fSJohn Johansen  * @root: namespace that is root of profiles being displayed (NOT NULL)
116329b3822fSJohn Johansen  * @ns: namespace to start in   (NOT NULL)
116429b3822fSJohn Johansen  *
116529b3822fSJohn Johansen  * Returns: unrefcounted profile or NULL if no profile
116629b3822fSJohn Johansen  * Requires: profile->ns.lock to be held
116729b3822fSJohn Johansen  */
116898849dffSJohn Johansen static struct aa_profile *__first_profile(struct aa_ns *root,
116998849dffSJohn Johansen 					  struct aa_ns *ns)
117029b3822fSJohn Johansen {
117198849dffSJohn Johansen 	for (; ns; ns = __next_ns(root, ns)) {
117229b3822fSJohn Johansen 		if (!list_empty(&ns->base.profiles))
117329b3822fSJohn Johansen 			return list_first_entry(&ns->base.profiles,
117429b3822fSJohn Johansen 						struct aa_profile, base.list);
117529b3822fSJohn Johansen 	}
117629b3822fSJohn Johansen 	return NULL;
117729b3822fSJohn Johansen }
117829b3822fSJohn Johansen 
117929b3822fSJohn Johansen /**
118029b3822fSJohn Johansen  * __next_profile - step to the next profile in a profile tree
118129b3822fSJohn Johansen  * @profile: current profile in tree (NOT NULL)
118229b3822fSJohn Johansen  *
118329b3822fSJohn Johansen  * Perform a depth first traversal on the profile tree in a namespace
118429b3822fSJohn Johansen  *
118529b3822fSJohn Johansen  * Returns: next profile or NULL if done
118629b3822fSJohn Johansen  * Requires: profile->ns.lock to be held
118729b3822fSJohn Johansen  */
118829b3822fSJohn Johansen static struct aa_profile *__next_profile(struct aa_profile *p)
118929b3822fSJohn Johansen {
119029b3822fSJohn Johansen 	struct aa_profile *parent;
119198849dffSJohn Johansen 	struct aa_ns *ns = p->ns;
119229b3822fSJohn Johansen 
119329b3822fSJohn Johansen 	/* is next profile a child */
119429b3822fSJohn Johansen 	if (!list_empty(&p->base.profiles))
119529b3822fSJohn Johansen 		return list_first_entry(&p->base.profiles, typeof(*p),
119629b3822fSJohn Johansen 					base.list);
119729b3822fSJohn Johansen 
119829b3822fSJohn Johansen 	/* is next profile a sibling, parent sibling, gp, sibling, .. */
119929b3822fSJohn Johansen 	parent = rcu_dereference_protected(p->parent,
120029b3822fSJohn Johansen 					   mutex_is_locked(&p->ns->lock));
120129b3822fSJohn Johansen 	while (parent) {
120238dbd7d8SGeliang Tang 		p = list_next_entry(p, base.list);
120329b3822fSJohn Johansen 		if (!list_entry_is_head(p, &parent->base.profiles, base.list))
120429b3822fSJohn Johansen 			return p;
120529b3822fSJohn Johansen 		p = parent;
120629b3822fSJohn Johansen 		parent = rcu_dereference_protected(parent->parent,
120729b3822fSJohn Johansen 					    mutex_is_locked(&parent->ns->lock));
120829b3822fSJohn Johansen 	}
120929b3822fSJohn Johansen 
121029b3822fSJohn Johansen 	/* is next another profile in the namespace */
121138dbd7d8SGeliang Tang 	p = list_next_entry(p, base.list);
121229b3822fSJohn Johansen 	if (!list_entry_is_head(p, &ns->base.profiles, base.list))
121329b3822fSJohn Johansen 		return p;
121429b3822fSJohn Johansen 
121529b3822fSJohn Johansen 	return NULL;
121629b3822fSJohn Johansen }
121729b3822fSJohn Johansen 
121829b3822fSJohn Johansen /**
121929b3822fSJohn Johansen  * next_profile - step to the next profile in where ever it may be
122029b3822fSJohn Johansen  * @root: root namespace  (NOT NULL)
122129b3822fSJohn Johansen  * @profile: current profile  (NOT NULL)
122229b3822fSJohn Johansen  *
122329b3822fSJohn Johansen  * Returns: next profile or NULL if there isn't one
122429b3822fSJohn Johansen  */
122598849dffSJohn Johansen static struct aa_profile *next_profile(struct aa_ns *root,
122629b3822fSJohn Johansen 				       struct aa_profile *profile)
122729b3822fSJohn Johansen {
122829b3822fSJohn Johansen 	struct aa_profile *next = __next_profile(profile);
122929b3822fSJohn Johansen 	if (next)
123029b3822fSJohn Johansen 		return next;
123129b3822fSJohn Johansen 
123229b3822fSJohn Johansen 	/* finished all profiles in namespace move to next namespace */
123398849dffSJohn Johansen 	return __first_profile(root, __next_ns(root, profile->ns));
123429b3822fSJohn Johansen }
123529b3822fSJohn Johansen 
123629b3822fSJohn Johansen /**
123729b3822fSJohn Johansen  * p_start - start a depth first traversal of profile tree
123829b3822fSJohn Johansen  * @f: seq_file to fill
123929b3822fSJohn Johansen  * @pos: current position
124029b3822fSJohn Johansen  *
124129b3822fSJohn Johansen  * Returns: first profile under current namespace or NULL if none found
124229b3822fSJohn Johansen  *
124329b3822fSJohn Johansen  * acquires first ns->lock
124429b3822fSJohn Johansen  */
124529b3822fSJohn Johansen static void *p_start(struct seq_file *f, loff_t *pos)
124629b3822fSJohn Johansen {
124729b3822fSJohn Johansen 	struct aa_profile *profile = NULL;
124898849dffSJohn Johansen 	struct aa_ns *root = aa_current_profile()->ns;
124929b3822fSJohn Johansen 	loff_t l = *pos;
125098849dffSJohn Johansen 	f->private = aa_get_ns(root);
125129b3822fSJohn Johansen 
125229b3822fSJohn Johansen 
125329b3822fSJohn Johansen 	/* find the first profile */
125429b3822fSJohn Johansen 	mutex_lock(&root->lock);
125529b3822fSJohn Johansen 	profile = __first_profile(root, root);
125629b3822fSJohn Johansen 
125729b3822fSJohn Johansen 	/* skip to position */
125829b3822fSJohn Johansen 	for (; profile && l > 0; l--)
125929b3822fSJohn Johansen 		profile = next_profile(root, profile);
126029b3822fSJohn Johansen 
126129b3822fSJohn Johansen 	return profile;
126229b3822fSJohn Johansen }
126329b3822fSJohn Johansen 
126429b3822fSJohn Johansen /**
126529b3822fSJohn Johansen  * p_next - read the next profile entry
126629b3822fSJohn Johansen  * @f: seq_file to fill
126729b3822fSJohn Johansen  * @p: profile previously returned
126829b3822fSJohn Johansen  * @pos: current position
126929b3822fSJohn Johansen  *
127029b3822fSJohn Johansen  * Returns: next profile after @p or NULL if none
127129b3822fSJohn Johansen  *
127229b3822fSJohn Johansen  * may acquire/release locks in namespace tree as necessary
127329b3822fSJohn Johansen  */
127429b3822fSJohn Johansen static void *p_next(struct seq_file *f, void *p, loff_t *pos)
127529b3822fSJohn Johansen {
127629b3822fSJohn Johansen 	struct aa_profile *profile = p;
127798849dffSJohn Johansen 	struct aa_ns *ns = f->private;
127829b3822fSJohn Johansen 	(*pos)++;
127929b3822fSJohn Johansen 
128029b3822fSJohn Johansen 	return next_profile(ns, profile);
128129b3822fSJohn Johansen }
128229b3822fSJohn Johansen 
128329b3822fSJohn Johansen /**
128429b3822fSJohn Johansen  * p_stop - stop depth first traversal
128529b3822fSJohn Johansen  * @f: seq_file we are filling
128629b3822fSJohn Johansen  * @p: the last profile writen
128729b3822fSJohn Johansen  *
128829b3822fSJohn Johansen  * Release all locking done by p_start/p_next on namespace tree
128929b3822fSJohn Johansen  */
129029b3822fSJohn Johansen static void p_stop(struct seq_file *f, void *p)
129129b3822fSJohn Johansen {
129229b3822fSJohn Johansen 	struct aa_profile *profile = p;
129398849dffSJohn Johansen 	struct aa_ns *root = f->private, *ns;
129429b3822fSJohn Johansen 
129529b3822fSJohn Johansen 	if (profile) {
129629b3822fSJohn Johansen 		for (ns = profile->ns; ns && ns != root; ns = ns->parent)
129729b3822fSJohn Johansen 			mutex_unlock(&ns->lock);
129829b3822fSJohn Johansen 	}
129929b3822fSJohn Johansen 	mutex_unlock(&root->lock);
130098849dffSJohn Johansen 	aa_put_ns(root);
130129b3822fSJohn Johansen }
130229b3822fSJohn Johansen 
130329b3822fSJohn Johansen /**
130429b3822fSJohn Johansen  * seq_show_profile - show a profile entry
130529b3822fSJohn Johansen  * @f: seq_file to file
130629b3822fSJohn Johansen  * @p: current position (profile)    (NOT NULL)
130729b3822fSJohn Johansen  *
130829b3822fSJohn Johansen  * Returns: error on failure
130929b3822fSJohn Johansen  */
131029b3822fSJohn Johansen static int seq_show_profile(struct seq_file *f, void *p)
131129b3822fSJohn Johansen {
131229b3822fSJohn Johansen 	struct aa_profile *profile = (struct aa_profile *)p;
131398849dffSJohn Johansen 	struct aa_ns *root = f->private;
131429b3822fSJohn Johansen 
131529b3822fSJohn Johansen 	if (profile->ns != root)
131692b6d8efSJohn Johansen 		seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true));
131729b3822fSJohn Johansen 	seq_printf(f, "%s (%s)\n", profile->base.hname,
131829b3822fSJohn Johansen 		   aa_profile_mode_names[profile->mode]);
131929b3822fSJohn Johansen 
132029b3822fSJohn Johansen 	return 0;
132129b3822fSJohn Johansen }
132229b3822fSJohn Johansen 
132329b3822fSJohn Johansen static const struct seq_operations aa_fs_profiles_op = {
132429b3822fSJohn Johansen 	.start = p_start,
132529b3822fSJohn Johansen 	.next = p_next,
132629b3822fSJohn Johansen 	.stop = p_stop,
132729b3822fSJohn Johansen 	.show = seq_show_profile,
132829b3822fSJohn Johansen };
132929b3822fSJohn Johansen 
133029b3822fSJohn Johansen static int profiles_open(struct inode *inode, struct file *file)
133129b3822fSJohn Johansen {
13325ac8c355SJohn Johansen 	if (!policy_view_capable(NULL))
13335ac8c355SJohn Johansen 		return -EACCES;
13345ac8c355SJohn Johansen 
133529b3822fSJohn Johansen 	return seq_open(file, &aa_fs_profiles_op);
133629b3822fSJohn Johansen }
133729b3822fSJohn Johansen 
133829b3822fSJohn Johansen static int profiles_release(struct inode *inode, struct file *file)
133929b3822fSJohn Johansen {
134029b3822fSJohn Johansen 	return seq_release(inode, file);
134129b3822fSJohn Johansen }
134229b3822fSJohn Johansen 
134329b3822fSJohn Johansen static const struct file_operations aa_fs_profiles_fops = {
134429b3822fSJohn Johansen 	.open = profiles_open,
134529b3822fSJohn Johansen 	.read = seq_read,
134629b3822fSJohn Johansen 	.llseek = seq_lseek,
134729b3822fSJohn Johansen 	.release = profiles_release,
134829b3822fSJohn Johansen };
134929b3822fSJohn Johansen 
135029b3822fSJohn Johansen 
13510d259f04SJohn Johansen /** Base file system setup **/
1352a9bf8e9fSKees Cook static struct aa_fs_entry aa_fs_entry_file[] = {
1353a9bf8e9fSKees Cook 	AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
1354a9bf8e9fSKees Cook 				  "link lock"),
1355a9bf8e9fSKees Cook 	{ }
1356a9bf8e9fSKees Cook };
1357a9bf8e9fSKees Cook 
1358e74abcf3SKees Cook static struct aa_fs_entry aa_fs_entry_domain[] = {
1359e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_hat",	1),
1360e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_hatv",	1),
1361e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_onexec",	1),
1362e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_profile",	1),
136334c426acSJohn Johansen 	AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap",	1),
1364aa9a39adSJohn Johansen 	AA_FS_FILE_STRING("version", "1.2"),
1365e74abcf3SKees Cook 	{ }
1366e74abcf3SKees Cook };
1367e74abcf3SKees Cook 
1368474d6b75SJohn Johansen static struct aa_fs_entry aa_fs_entry_versions[] = {
1369474d6b75SJohn Johansen 	AA_FS_FILE_BOOLEAN("v5",	1),
1370474d6b75SJohn Johansen 	{ }
1371474d6b75SJohn Johansen };
1372474d6b75SJohn Johansen 
13739d910a3bSJohn Johansen static struct aa_fs_entry aa_fs_entry_policy[] = {
1374474d6b75SJohn Johansen 	AA_FS_DIR("versions",                   aa_fs_entry_versions),
1375dd51c848SJohn Johansen 	AA_FS_FILE_BOOLEAN("set_load",		1),
13769d910a3bSJohn Johansen 	{ }
13779d910a3bSJohn Johansen };
13789d910a3bSJohn Johansen 
1379e74abcf3SKees Cook static struct aa_fs_entry aa_fs_entry_features[] = {
13809d910a3bSJohn Johansen 	AA_FS_DIR("policy",			aa_fs_entry_policy),
1381e74abcf3SKees Cook 	AA_FS_DIR("domain",			aa_fs_entry_domain),
1382a9bf8e9fSKees Cook 	AA_FS_DIR("file",			aa_fs_entry_file),
1383e74abcf3SKees Cook 	AA_FS_FILE_U64("capability",		VFS_CAP_FLAGS_MASK),
1384d384b0a1SKees Cook 	AA_FS_DIR("rlimit",			aa_fs_entry_rlimit),
138584f1f787SJohn Johansen 	AA_FS_DIR("caps",			aa_fs_entry_caps),
1386e74abcf3SKees Cook 	{ }
1387e74abcf3SKees Cook };
1388e74abcf3SKees Cook 
13899acd494bSKees Cook static struct aa_fs_entry aa_fs_entry_apparmor[] = {
1390e025be0fSWilliam Hua 	AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
1391a71ada30SJohn Johansen 	AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
13923e3e5695SJohn Johansen 	AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
1393b7fd2c03SJohn Johansen 	AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
1394e74abcf3SKees Cook 	AA_FS_DIR("features", aa_fs_entry_features),
13959acd494bSKees Cook 	{ }
13969acd494bSKees Cook };
139763e2b423SJohn Johansen 
13989acd494bSKees Cook static struct aa_fs_entry aa_fs_entry =
13999acd494bSKees Cook 	AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
14009acd494bSKees Cook 
14019acd494bSKees Cook /**
14029acd494bSKees Cook  * aafs_create_file - create a file entry in the apparmor securityfs
14039acd494bSKees Cook  * @fs_file: aa_fs_entry to build an entry for (NOT NULL)
14049acd494bSKees Cook  * @parent: the parent dentry in the securityfs
14059acd494bSKees Cook  *
14069acd494bSKees Cook  * Use aafs_remove_file to remove entries created with this fn.
14079acd494bSKees Cook  */
14089acd494bSKees Cook static int __init aafs_create_file(struct aa_fs_entry *fs_file,
14099acd494bSKees Cook 				   struct dentry *parent)
141063e2b423SJohn Johansen {
14119acd494bSKees Cook 	int error = 0;
141263e2b423SJohn Johansen 
14139acd494bSKees Cook 	fs_file->dentry = securityfs_create_file(fs_file->name,
14149acd494bSKees Cook 						 S_IFREG | fs_file->mode,
14159acd494bSKees Cook 						 parent, fs_file,
14169acd494bSKees Cook 						 fs_file->file_ops);
14179acd494bSKees Cook 	if (IS_ERR(fs_file->dentry)) {
14189acd494bSKees Cook 		error = PTR_ERR(fs_file->dentry);
14199acd494bSKees Cook 		fs_file->dentry = NULL;
142063e2b423SJohn Johansen 	}
14219acd494bSKees Cook 	return error;
142263e2b423SJohn Johansen }
142363e2b423SJohn Johansen 
14240d259f04SJohn Johansen static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
142563e2b423SJohn Johansen /**
14269acd494bSKees Cook  * aafs_create_dir - recursively create a directory entry in the securityfs
14279acd494bSKees Cook  * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
14289acd494bSKees Cook  * @parent: the parent dentry in the securityfs
142963e2b423SJohn Johansen  *
14309acd494bSKees Cook  * Use aafs_remove_dir to remove entries created with this fn.
143163e2b423SJohn Johansen  */
14329acd494bSKees Cook static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
14339acd494bSKees Cook 				  struct dentry *parent)
143463e2b423SJohn Johansen {
14359acd494bSKees Cook 	struct aa_fs_entry *fs_file;
14360d259f04SJohn Johansen 	struct dentry *dir;
14370d259f04SJohn Johansen 	int error;
143863e2b423SJohn Johansen 
14390d259f04SJohn Johansen 	dir = securityfs_create_dir(fs_dir->name, parent);
14400d259f04SJohn Johansen 	if (IS_ERR(dir))
14410d259f04SJohn Johansen 		return PTR_ERR(dir);
14420d259f04SJohn Johansen 	fs_dir->dentry = dir;
144363e2b423SJohn Johansen 
14440d259f04SJohn Johansen 	for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
14459acd494bSKees Cook 		if (fs_file->v_type == AA_FS_TYPE_DIR)
14469acd494bSKees Cook 			error = aafs_create_dir(fs_file, fs_dir->dentry);
14479acd494bSKees Cook 		else
14489acd494bSKees Cook 			error = aafs_create_file(fs_file, fs_dir->dentry);
14499acd494bSKees Cook 		if (error)
14509acd494bSKees Cook 			goto failed;
14519acd494bSKees Cook 	}
14529acd494bSKees Cook 
14539acd494bSKees Cook 	return 0;
14549acd494bSKees Cook 
14559acd494bSKees Cook failed:
14560d259f04SJohn Johansen 	aafs_remove_dir(fs_dir);
14570d259f04SJohn Johansen 
14589acd494bSKees Cook 	return error;
14599acd494bSKees Cook }
14609acd494bSKees Cook 
14619acd494bSKees Cook /**
14629acd494bSKees Cook  * aafs_remove_file - drop a single file entry in the apparmor securityfs
14639acd494bSKees Cook  * @fs_file: aa_fs_entry to detach from the securityfs (NOT NULL)
14649acd494bSKees Cook  */
14659acd494bSKees Cook static void __init aafs_remove_file(struct aa_fs_entry *fs_file)
14669acd494bSKees Cook {
14679acd494bSKees Cook 	if (!fs_file->dentry)
14689acd494bSKees Cook 		return;
14699acd494bSKees Cook 
14709acd494bSKees Cook 	securityfs_remove(fs_file->dentry);
14719acd494bSKees Cook 	fs_file->dentry = NULL;
14729acd494bSKees Cook }
14739acd494bSKees Cook 
14749acd494bSKees Cook /**
14759acd494bSKees Cook  * aafs_remove_dir - recursively drop a directory entry from the securityfs
14769acd494bSKees Cook  * @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL)
14779acd494bSKees Cook  */
14789acd494bSKees Cook static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
14799acd494bSKees Cook {
14809acd494bSKees Cook 	struct aa_fs_entry *fs_file;
14819acd494bSKees Cook 
14820d259f04SJohn Johansen 	for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
14839acd494bSKees Cook 		if (fs_file->v_type == AA_FS_TYPE_DIR)
14849acd494bSKees Cook 			aafs_remove_dir(fs_file);
14859acd494bSKees Cook 		else
14869acd494bSKees Cook 			aafs_remove_file(fs_file);
14879acd494bSKees Cook 	}
14889acd494bSKees Cook 
14899acd494bSKees Cook 	aafs_remove_file(fs_dir);
149063e2b423SJohn Johansen }
149163e2b423SJohn Johansen 
149263e2b423SJohn Johansen /**
149363e2b423SJohn Johansen  * aa_destroy_aafs - cleanup and free aafs
149463e2b423SJohn Johansen  *
149563e2b423SJohn Johansen  * releases dentries allocated by aa_create_aafs
149663e2b423SJohn Johansen  */
149763e2b423SJohn Johansen void __init aa_destroy_aafs(void)
149863e2b423SJohn Johansen {
14999acd494bSKees Cook 	aafs_remove_dir(&aa_fs_entry);
150063e2b423SJohn Johansen }
150163e2b423SJohn Johansen 
1502a71ada30SJohn Johansen 
1503a71ada30SJohn Johansen #define NULL_FILE_NAME ".null"
1504a71ada30SJohn Johansen struct path aa_null;
1505a71ada30SJohn Johansen 
1506a71ada30SJohn Johansen static int aa_mk_null_file(struct dentry *parent)
1507a71ada30SJohn Johansen {
1508a71ada30SJohn Johansen 	struct vfsmount *mount = NULL;
1509a71ada30SJohn Johansen 	struct dentry *dentry;
1510a71ada30SJohn Johansen 	struct inode *inode;
1511a71ada30SJohn Johansen 	int count = 0;
1512a71ada30SJohn Johansen 	int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
1513a71ada30SJohn Johansen 
1514a71ada30SJohn Johansen 	if (error)
1515a71ada30SJohn Johansen 		return error;
1516a71ada30SJohn Johansen 
1517a71ada30SJohn Johansen 	inode_lock(d_inode(parent));
1518a71ada30SJohn Johansen 	dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
1519a71ada30SJohn Johansen 	if (IS_ERR(dentry)) {
1520a71ada30SJohn Johansen 		error = PTR_ERR(dentry);
1521a71ada30SJohn Johansen 		goto out;
1522a71ada30SJohn Johansen 	}
1523a71ada30SJohn Johansen 	inode = new_inode(parent->d_inode->i_sb);
1524a71ada30SJohn Johansen 	if (!inode) {
1525a71ada30SJohn Johansen 		error = -ENOMEM;
1526a71ada30SJohn Johansen 		goto out1;
1527a71ada30SJohn Johansen 	}
1528a71ada30SJohn Johansen 
1529a71ada30SJohn Johansen 	inode->i_ino = get_next_ino();
1530a71ada30SJohn Johansen 	inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
153124d0d03cSDeepa Dinamani 	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
1532a71ada30SJohn Johansen 	init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
1533a71ada30SJohn Johansen 			   MKDEV(MEM_MAJOR, 3));
1534a71ada30SJohn Johansen 	d_instantiate(dentry, inode);
1535a71ada30SJohn Johansen 	aa_null.dentry = dget(dentry);
1536a71ada30SJohn Johansen 	aa_null.mnt = mntget(mount);
1537a71ada30SJohn Johansen 
1538a71ada30SJohn Johansen 	error = 0;
1539a71ada30SJohn Johansen 
1540a71ada30SJohn Johansen out1:
1541a71ada30SJohn Johansen 	dput(dentry);
1542a71ada30SJohn Johansen out:
1543a71ada30SJohn Johansen 	inode_unlock(d_inode(parent));
1544a71ada30SJohn Johansen 	simple_release_fs(&mount, &count);
1545a71ada30SJohn Johansen 	return error;
1546a71ada30SJohn Johansen }
1547a71ada30SJohn Johansen 
154863e2b423SJohn Johansen /**
154963e2b423SJohn Johansen  * aa_create_aafs - create the apparmor security filesystem
155063e2b423SJohn Johansen  *
155163e2b423SJohn Johansen  * dentries created here are released by aa_destroy_aafs
155263e2b423SJohn Johansen  *
155363e2b423SJohn Johansen  * Returns: error on failure
155463e2b423SJohn Johansen  */
15553417d8d5SJames Morris static int __init aa_create_aafs(void)
155663e2b423SJohn Johansen {
1557b7fd2c03SJohn Johansen 	struct dentry *dent;
155863e2b423SJohn Johansen 	int error;
155963e2b423SJohn Johansen 
156063e2b423SJohn Johansen 	if (!apparmor_initialized)
156163e2b423SJohn Johansen 		return 0;
156263e2b423SJohn Johansen 
15639acd494bSKees Cook 	if (aa_fs_entry.dentry) {
156463e2b423SJohn Johansen 		AA_ERROR("%s: AppArmor securityfs already exists\n", __func__);
156563e2b423SJohn Johansen 		return -EEXIST;
156663e2b423SJohn Johansen 	}
156763e2b423SJohn Johansen 
15689acd494bSKees Cook 	/* Populate fs tree. */
15699acd494bSKees Cook 	error = aafs_create_dir(&aa_fs_entry, NULL);
157063e2b423SJohn Johansen 	if (error)
157163e2b423SJohn Johansen 		goto error;
157263e2b423SJohn Johansen 
1573b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry,
1574b7fd2c03SJohn Johansen 				      NULL, &aa_fs_profile_load);
1575b7fd2c03SJohn Johansen 	if (IS_ERR(dent)) {
1576b7fd2c03SJohn Johansen 		error = PTR_ERR(dent);
1577b7fd2c03SJohn Johansen 		goto error;
1578b7fd2c03SJohn Johansen 	}
1579b7fd2c03SJohn Johansen 	ns_subload(root_ns) = dent;
1580b7fd2c03SJohn Johansen 
1581b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry,
1582b7fd2c03SJohn Johansen 				      NULL, &aa_fs_profile_replace);
1583b7fd2c03SJohn Johansen 	if (IS_ERR(dent)) {
1584b7fd2c03SJohn Johansen 		error = PTR_ERR(dent);
1585b7fd2c03SJohn Johansen 		goto error;
1586b7fd2c03SJohn Johansen 	}
1587b7fd2c03SJohn Johansen 	ns_subreplace(root_ns) = dent;
1588b7fd2c03SJohn Johansen 
1589b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry,
1590b7fd2c03SJohn Johansen 				      NULL, &aa_fs_profile_remove);
1591b7fd2c03SJohn Johansen 	if (IS_ERR(dent)) {
1592b7fd2c03SJohn Johansen 		error = PTR_ERR(dent);
1593b7fd2c03SJohn Johansen 		goto error;
1594b7fd2c03SJohn Johansen 	}
1595b7fd2c03SJohn Johansen 	ns_subremove(root_ns) = dent;
1596b7fd2c03SJohn Johansen 
1597b7fd2c03SJohn Johansen 	mutex_lock(&root_ns->lock);
159898849dffSJohn Johansen 	error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
1599b7fd2c03SJohn Johansen 	mutex_unlock(&root_ns->lock);
1600b7fd2c03SJohn Johansen 
16010d259f04SJohn Johansen 	if (error)
16020d259f04SJohn Johansen 		goto error;
16030d259f04SJohn Johansen 
1604a71ada30SJohn Johansen 	error = aa_mk_null_file(aa_fs_entry.dentry);
1605a71ada30SJohn Johansen 	if (error)
1606a71ada30SJohn Johansen 		goto error;
1607a71ada30SJohn Johansen 
1608a71ada30SJohn Johansen 	/* TODO: add default profile to apparmorfs */
160963e2b423SJohn Johansen 
161063e2b423SJohn Johansen 	/* Report that AppArmor fs is enabled */
161163e2b423SJohn Johansen 	aa_info_message("AppArmor Filesystem Enabled");
161263e2b423SJohn Johansen 	return 0;
161363e2b423SJohn Johansen 
161463e2b423SJohn Johansen error:
161563e2b423SJohn Johansen 	aa_destroy_aafs();
161663e2b423SJohn Johansen 	AA_ERROR("Error creating AppArmor securityfs\n");
161763e2b423SJohn Johansen 	return error;
161863e2b423SJohn Johansen }
161963e2b423SJohn Johansen 
162063e2b423SJohn Johansen fs_initcall(aa_create_aafs);
1621