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 */
101a7c3e901SMichal Hocko 	data = kvmalloc(sizeof(*data) + alloc_size, GFP_KERNEL);
10263e2b423SJohn Johansen 	if (data == NULL)
10363e2b423SJohn Johansen 		return ERR_PTR(-ENOMEM);
1045ac8c355SJohn Johansen 	kref_init(&data->count);
1055ac8c355SJohn Johansen 	data->size = copy_size;
1065ac8c355SJohn Johansen 	data->hash = NULL;
1075ac8c355SJohn Johansen 	data->abi = 0;
10863e2b423SJohn Johansen 
1095ac8c355SJohn Johansen 	if (copy_from_user(data->data, userbuf, copy_size)) {
11063e2b423SJohn Johansen 		kvfree(data);
11163e2b423SJohn Johansen 		return ERR_PTR(-EFAULT);
11263e2b423SJohn Johansen 	}
11363e2b423SJohn Johansen 
11463e2b423SJohn Johansen 	return data;
11563e2b423SJohn Johansen }
11663e2b423SJohn Johansen 
1175ac8c355SJohn Johansen static ssize_t policy_update(int binop, const char __user *buf, size_t size,
118b7fd2c03SJohn Johansen 			     loff_t *pos, struct aa_ns *ns)
1195ac8c355SJohn Johansen {
1205ac8c355SJohn Johansen 	ssize_t error;
1215ac8c355SJohn Johansen 	struct aa_loaddata *data;
1225ac8c355SJohn Johansen 	struct aa_profile *profile = aa_current_profile();
12347f6e5ccSJohn Johansen 	const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
1245ac8c355SJohn Johansen 	/* high level check about policy management - fine grained in
1255ac8c355SJohn Johansen 	 * below after unpack
1265ac8c355SJohn Johansen 	 */
127b7fd2c03SJohn Johansen 	error = aa_may_manage_policy(profile, ns, op);
1285ac8c355SJohn Johansen 	if (error)
1295ac8c355SJohn Johansen 		return error;
13063e2b423SJohn Johansen 
1315ef50d01SJohn Johansen 	data = aa_simple_write_to_buffer(buf, size, size, pos);
1325ac8c355SJohn Johansen 	error = PTR_ERR(data);
1335ac8c355SJohn Johansen 	if (!IS_ERR(data)) {
134b7fd2c03SJohn Johansen 		error = aa_replace_profiles(ns ? ns : profile->ns, profile,
135b7fd2c03SJohn Johansen 					    binop, data);
1365ac8c355SJohn Johansen 		aa_put_loaddata(data);
1375ac8c355SJohn Johansen 	}
1385ac8c355SJohn Johansen 
1395ac8c355SJohn Johansen 	return error;
1405ac8c355SJohn Johansen }
1415ac8c355SJohn Johansen 
142b7fd2c03SJohn Johansen /* .load file hook fn to load policy */
14363e2b423SJohn Johansen static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
14463e2b423SJohn Johansen 			    loff_t *pos)
14563e2b423SJohn Johansen {
146b7fd2c03SJohn Johansen 	struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
147b7fd2c03SJohn Johansen 	int error = policy_update(PROF_ADD, buf, size, pos, ns);
148b7fd2c03SJohn Johansen 
149b7fd2c03SJohn Johansen 	aa_put_ns(ns);
15063e2b423SJohn Johansen 
15163e2b423SJohn Johansen 	return error;
15263e2b423SJohn Johansen }
15363e2b423SJohn Johansen 
15463e2b423SJohn Johansen static const struct file_operations aa_fs_profile_load = {
1556038f373SArnd Bergmann 	.write = profile_load,
1566038f373SArnd Bergmann 	.llseek = default_llseek,
15763e2b423SJohn Johansen };
15863e2b423SJohn Johansen 
15963e2b423SJohn Johansen /* .replace file hook fn to load and/or replace policy */
16063e2b423SJohn Johansen static ssize_t profile_replace(struct file *f, const char __user *buf,
16163e2b423SJohn Johansen 			       size_t size, loff_t *pos)
16263e2b423SJohn Johansen {
163b7fd2c03SJohn Johansen 	struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
164b7fd2c03SJohn Johansen 	int error = policy_update(PROF_REPLACE, buf, size, pos, ns);
165b7fd2c03SJohn Johansen 
166b7fd2c03SJohn Johansen 	aa_put_ns(ns);
16763e2b423SJohn Johansen 
16863e2b423SJohn Johansen 	return error;
16963e2b423SJohn Johansen }
17063e2b423SJohn Johansen 
17163e2b423SJohn Johansen static const struct file_operations aa_fs_profile_replace = {
1726038f373SArnd Bergmann 	.write = profile_replace,
1736038f373SArnd Bergmann 	.llseek = default_llseek,
17463e2b423SJohn Johansen };
17563e2b423SJohn Johansen 
176b7fd2c03SJohn Johansen /* .remove file hook fn to remove loaded policy */
17763e2b423SJohn Johansen static ssize_t profile_remove(struct file *f, const char __user *buf,
17863e2b423SJohn Johansen 			      size_t size, loff_t *pos)
17963e2b423SJohn Johansen {
1805ac8c355SJohn Johansen 	struct aa_loaddata *data;
1815ac8c355SJohn Johansen 	struct aa_profile *profile;
18263e2b423SJohn Johansen 	ssize_t error;
183b7fd2c03SJohn Johansen 	struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
18463e2b423SJohn Johansen 
1855ac8c355SJohn Johansen 	profile = aa_current_profile();
1865ac8c355SJohn Johansen 	/* high level check about policy management - fine grained in
1875ac8c355SJohn Johansen 	 * below after unpack
1885ac8c355SJohn Johansen 	 */
189b7fd2c03SJohn Johansen 	error = aa_may_manage_policy(profile, ns, OP_PROF_RM);
1905ac8c355SJohn Johansen 	if (error)
1915ac8c355SJohn Johansen 		goto out;
1925ac8c355SJohn Johansen 
19363e2b423SJohn Johansen 	/*
19463e2b423SJohn Johansen 	 * aa_remove_profile needs a null terminated string so 1 extra
19563e2b423SJohn Johansen 	 * byte is allocated and the copied data is null terminated.
19663e2b423SJohn Johansen 	 */
1975ef50d01SJohn Johansen 	data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
19863e2b423SJohn Johansen 
19963e2b423SJohn Johansen 	error = PTR_ERR(data);
20063e2b423SJohn Johansen 	if (!IS_ERR(data)) {
2015ac8c355SJohn Johansen 		data->data[size] = 0;
202b7fd2c03SJohn Johansen 		error = aa_remove_profiles(ns ? ns : profile->ns, profile,
203b7fd2c03SJohn Johansen 					   data->data, size);
2045ac8c355SJohn Johansen 		aa_put_loaddata(data);
20563e2b423SJohn Johansen 	}
2065ac8c355SJohn Johansen  out:
207b7fd2c03SJohn Johansen 	aa_put_ns(ns);
20863e2b423SJohn Johansen 	return error;
20963e2b423SJohn Johansen }
21063e2b423SJohn Johansen 
21163e2b423SJohn Johansen static const struct file_operations aa_fs_profile_remove = {
2126038f373SArnd Bergmann 	.write = profile_remove,
2136038f373SArnd Bergmann 	.llseek = default_llseek,
21463e2b423SJohn Johansen };
21563e2b423SJohn Johansen 
216e025be0fSWilliam Hua /**
217e025be0fSWilliam Hua  * query_data - queries a policy and writes its data to buf
218e025be0fSWilliam Hua  * @buf: the resulting data is stored here (NOT NULL)
219e025be0fSWilliam Hua  * @buf_len: size of buf
220e025be0fSWilliam Hua  * @query: query string used to retrieve data
221e025be0fSWilliam Hua  * @query_len: size of query including second NUL byte
222e025be0fSWilliam Hua  *
223e025be0fSWilliam Hua  * The buffers pointed to by buf and query may overlap. The query buffer is
224e025be0fSWilliam Hua  * parsed before buf is written to.
225e025be0fSWilliam Hua  *
226e025be0fSWilliam Hua  * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
227e025be0fSWilliam Hua  * the security confinement context and <KEY> is the name of the data to
228e025be0fSWilliam Hua  * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
229e025be0fSWilliam Hua  *
230e025be0fSWilliam Hua  * Don't expect the contents of buf to be preserved on failure.
231e025be0fSWilliam Hua  *
232e025be0fSWilliam Hua  * Returns: number of characters written to buf or -errno on failure
233e025be0fSWilliam Hua  */
234e025be0fSWilliam Hua static ssize_t query_data(char *buf, size_t buf_len,
235e025be0fSWilliam Hua 			  char *query, size_t query_len)
236e025be0fSWilliam Hua {
237e025be0fSWilliam Hua 	char *out;
238e025be0fSWilliam Hua 	const char *key;
239e025be0fSWilliam Hua 	struct aa_profile *profile;
240e025be0fSWilliam Hua 	struct aa_data *data;
241e025be0fSWilliam Hua 	u32 bytes, blocks;
242e025be0fSWilliam Hua 	__le32 outle32;
243e025be0fSWilliam Hua 
244e025be0fSWilliam Hua 	if (!query_len)
245e025be0fSWilliam Hua 		return -EINVAL; /* need a query */
246e025be0fSWilliam Hua 
247e025be0fSWilliam Hua 	key = query + strnlen(query, query_len) + 1;
248e025be0fSWilliam Hua 	if (key + 1 >= query + query_len)
249e025be0fSWilliam Hua 		return -EINVAL; /* not enough space for a non-empty key */
250e025be0fSWilliam Hua 	if (key + strnlen(key, query + query_len - key) >= query + query_len)
251e025be0fSWilliam Hua 		return -EINVAL; /* must end with NUL */
252e025be0fSWilliam Hua 
253e025be0fSWilliam Hua 	if (buf_len < sizeof(bytes) + sizeof(blocks))
254e025be0fSWilliam Hua 		return -EINVAL; /* not enough space */
255e025be0fSWilliam Hua 
256e025be0fSWilliam Hua 	profile = aa_current_profile();
257e025be0fSWilliam Hua 
258e025be0fSWilliam Hua 	/* We are going to leave space for two numbers. The first is the total
259e025be0fSWilliam Hua 	 * number of bytes we are writing after the first number. This is so
260e025be0fSWilliam Hua 	 * users can read the full output without reallocation.
261e025be0fSWilliam Hua 	 *
262e025be0fSWilliam Hua 	 * The second number is the number of data blocks we're writing. An
263e025be0fSWilliam Hua 	 * application might be confined by multiple policies having data in
264e025be0fSWilliam Hua 	 * the same key.
265e025be0fSWilliam Hua 	 */
266e025be0fSWilliam Hua 	memset(buf, 0, sizeof(bytes) + sizeof(blocks));
267e025be0fSWilliam Hua 	out = buf + sizeof(bytes) + sizeof(blocks);
268e025be0fSWilliam Hua 
269e025be0fSWilliam Hua 	blocks = 0;
270e025be0fSWilliam Hua 	if (profile->data) {
271e025be0fSWilliam Hua 		data = rhashtable_lookup_fast(profile->data, &key,
272e025be0fSWilliam Hua 					      profile->data->p);
273e025be0fSWilliam Hua 
274e025be0fSWilliam Hua 		if (data) {
275e025be0fSWilliam Hua 			if (out + sizeof(outle32) + data->size > buf + buf_len)
276e025be0fSWilliam Hua 				return -EINVAL; /* not enough space */
277e025be0fSWilliam Hua 			outle32 = __cpu_to_le32(data->size);
278e025be0fSWilliam Hua 			memcpy(out, &outle32, sizeof(outle32));
279e025be0fSWilliam Hua 			out += sizeof(outle32);
280e025be0fSWilliam Hua 			memcpy(out, data->data, data->size);
281e025be0fSWilliam Hua 			out += data->size;
282e025be0fSWilliam Hua 			blocks++;
283e025be0fSWilliam Hua 		}
284e025be0fSWilliam Hua 	}
285e025be0fSWilliam Hua 
286e025be0fSWilliam Hua 	outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
287e025be0fSWilliam Hua 	memcpy(buf, &outle32, sizeof(outle32));
288e025be0fSWilliam Hua 	outle32 = __cpu_to_le32(blocks);
289e025be0fSWilliam Hua 	memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
290e025be0fSWilliam Hua 
291e025be0fSWilliam Hua 	return out - buf;
292e025be0fSWilliam Hua }
293e025be0fSWilliam Hua 
294e025be0fSWilliam Hua #define QUERY_CMD_DATA		"data\0"
295e025be0fSWilliam Hua #define QUERY_CMD_DATA_LEN	5
296e025be0fSWilliam Hua 
297e025be0fSWilliam Hua /**
298e025be0fSWilliam Hua  * aa_write_access - generic permissions and data query
299e025be0fSWilliam Hua  * @file: pointer to open apparmorfs/access file
300e025be0fSWilliam Hua  * @ubuf: user buffer containing the complete query string (NOT NULL)
301e025be0fSWilliam Hua  * @count: size of ubuf
302e025be0fSWilliam Hua  * @ppos: position in the file (MUST BE ZERO)
303e025be0fSWilliam Hua  *
304e025be0fSWilliam Hua  * Allows for one permissions or data query per open(), write(), and read()
305e025be0fSWilliam Hua  * sequence. The only queries currently supported are label-based queries for
306e025be0fSWilliam Hua  * permissions or data.
307e025be0fSWilliam Hua  *
308e025be0fSWilliam Hua  * For permissions queries, ubuf must begin with "label\0", followed by the
309e025be0fSWilliam Hua  * profile query specific format described in the query_label() function
310e025be0fSWilliam Hua  * documentation.
311e025be0fSWilliam Hua  *
312e025be0fSWilliam Hua  * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
313e025be0fSWilliam Hua  * <LABEL> is the name of the security confinement context and <KEY> is the
314e025be0fSWilliam Hua  * name of the data to retrieve.
315e025be0fSWilliam Hua  *
316e025be0fSWilliam Hua  * Returns: number of bytes written or -errno on failure
317e025be0fSWilliam Hua  */
318e025be0fSWilliam Hua static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
319e025be0fSWilliam Hua 			       size_t count, loff_t *ppos)
320e025be0fSWilliam Hua {
321e025be0fSWilliam Hua 	char *buf;
322e025be0fSWilliam Hua 	ssize_t len;
323e025be0fSWilliam Hua 
324e025be0fSWilliam Hua 	if (*ppos)
325e025be0fSWilliam Hua 		return -ESPIPE;
326e025be0fSWilliam Hua 
327e025be0fSWilliam Hua 	buf = simple_transaction_get(file, ubuf, count);
328e025be0fSWilliam Hua 	if (IS_ERR(buf))
329e025be0fSWilliam Hua 		return PTR_ERR(buf);
330e025be0fSWilliam Hua 
331e025be0fSWilliam Hua 	if (count > QUERY_CMD_DATA_LEN &&
332e025be0fSWilliam Hua 		   !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
333e025be0fSWilliam Hua 		len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
334e025be0fSWilliam Hua 				 buf + QUERY_CMD_DATA_LEN,
335e025be0fSWilliam Hua 				 count - QUERY_CMD_DATA_LEN);
336e025be0fSWilliam Hua 	} else
337e025be0fSWilliam Hua 		len = -EINVAL;
338e025be0fSWilliam Hua 
339e025be0fSWilliam Hua 	if (len < 0)
340e025be0fSWilliam Hua 		return len;
341e025be0fSWilliam Hua 
342e025be0fSWilliam Hua 	simple_transaction_set(file, len);
343e025be0fSWilliam Hua 
344e025be0fSWilliam Hua 	return count;
345e025be0fSWilliam Hua }
346e025be0fSWilliam Hua 
347e025be0fSWilliam Hua static const struct file_operations aa_fs_access = {
348e025be0fSWilliam Hua 	.write		= aa_write_access,
349e025be0fSWilliam Hua 	.read		= simple_transaction_read,
350e025be0fSWilliam Hua 	.release	= simple_transaction_release,
351e025be0fSWilliam Hua 	.llseek		= generic_file_llseek,
352e025be0fSWilliam Hua };
353e025be0fSWilliam Hua 
354e74abcf3SKees Cook static int aa_fs_seq_show(struct seq_file *seq, void *v)
355e74abcf3SKees Cook {
356e74abcf3SKees Cook 	struct aa_fs_entry *fs_file = seq->private;
357e74abcf3SKees Cook 
358e74abcf3SKees Cook 	if (!fs_file)
359e74abcf3SKees Cook 		return 0;
360e74abcf3SKees Cook 
361e74abcf3SKees Cook 	switch (fs_file->v_type) {
362e74abcf3SKees Cook 	case AA_FS_TYPE_BOOLEAN:
363e74abcf3SKees Cook 		seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no");
364e74abcf3SKees Cook 		break;
365a9bf8e9fSKees Cook 	case AA_FS_TYPE_STRING:
366a9bf8e9fSKees Cook 		seq_printf(seq, "%s\n", fs_file->v.string);
367a9bf8e9fSKees Cook 		break;
368e74abcf3SKees Cook 	case AA_FS_TYPE_U64:
369e74abcf3SKees Cook 		seq_printf(seq, "%#08lx\n", fs_file->v.u64);
370e74abcf3SKees Cook 		break;
371e74abcf3SKees Cook 	default:
372e74abcf3SKees Cook 		/* Ignore unpritable entry types. */
373e74abcf3SKees Cook 		break;
374e74abcf3SKees Cook 	}
375e74abcf3SKees Cook 
376e74abcf3SKees Cook 	return 0;
377e74abcf3SKees Cook }
378e74abcf3SKees Cook 
379e74abcf3SKees Cook static int aa_fs_seq_open(struct inode *inode, struct file *file)
380e74abcf3SKees Cook {
381e74abcf3SKees Cook 	return single_open(file, aa_fs_seq_show, inode->i_private);
382e74abcf3SKees Cook }
383e74abcf3SKees Cook 
384e74abcf3SKees Cook const struct file_operations aa_fs_seq_file_ops = {
385e74abcf3SKees Cook 	.owner		= THIS_MODULE,
386e74abcf3SKees Cook 	.open		= aa_fs_seq_open,
387e74abcf3SKees Cook 	.read		= seq_read,
388e74abcf3SKees Cook 	.llseek		= seq_lseek,
389e74abcf3SKees Cook 	.release	= single_release,
390e74abcf3SKees Cook };
391e74abcf3SKees Cook 
3920d259f04SJohn Johansen static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
3930d259f04SJohn Johansen 				  int (*show)(struct seq_file *, void *))
3940d259f04SJohn Johansen {
3958399588aSJohn Johansen 	struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
3968399588aSJohn Johansen 	int error = single_open(file, show, proxy);
39763e2b423SJohn Johansen 
3980d259f04SJohn Johansen 	if (error) {
3990d259f04SJohn Johansen 		file->private_data = NULL;
4008399588aSJohn Johansen 		aa_put_proxy(proxy);
4010d259f04SJohn Johansen 	}
4020d259f04SJohn Johansen 
4030d259f04SJohn Johansen 	return error;
4040d259f04SJohn Johansen }
4050d259f04SJohn Johansen 
4060d259f04SJohn Johansen static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
4070d259f04SJohn Johansen {
4080d259f04SJohn Johansen 	struct seq_file *seq = (struct seq_file *) file->private_data;
4090d259f04SJohn Johansen 	if (seq)
4108399588aSJohn Johansen 		aa_put_proxy(seq->private);
4110d259f04SJohn Johansen 	return single_release(inode, file);
4120d259f04SJohn Johansen }
4130d259f04SJohn Johansen 
4140d259f04SJohn Johansen static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
4150d259f04SJohn Johansen {
4168399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4178399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
4180d259f04SJohn Johansen 	seq_printf(seq, "%s\n", profile->base.name);
4190d259f04SJohn Johansen 	aa_put_profile(profile);
4200d259f04SJohn Johansen 
4210d259f04SJohn Johansen 	return 0;
4220d259f04SJohn Johansen }
4230d259f04SJohn Johansen 
4240d259f04SJohn Johansen static int aa_fs_seq_profname_open(struct inode *inode, struct file *file)
4250d259f04SJohn Johansen {
4260d259f04SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show);
4270d259f04SJohn Johansen }
4280d259f04SJohn Johansen 
4290d259f04SJohn Johansen static const struct file_operations aa_fs_profname_fops = {
4300d259f04SJohn Johansen 	.owner		= THIS_MODULE,
4310d259f04SJohn Johansen 	.open		= aa_fs_seq_profname_open,
4320d259f04SJohn Johansen 	.read		= seq_read,
4330d259f04SJohn Johansen 	.llseek		= seq_lseek,
4340d259f04SJohn Johansen 	.release	= aa_fs_seq_profile_release,
4350d259f04SJohn Johansen };
4360d259f04SJohn Johansen 
4370d259f04SJohn Johansen static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
4380d259f04SJohn Johansen {
4398399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4408399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
4410d259f04SJohn Johansen 	seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
4420d259f04SJohn Johansen 	aa_put_profile(profile);
4430d259f04SJohn Johansen 
4440d259f04SJohn Johansen 	return 0;
4450d259f04SJohn Johansen }
4460d259f04SJohn Johansen 
4470d259f04SJohn Johansen static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file)
4480d259f04SJohn Johansen {
4490d259f04SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show);
4500d259f04SJohn Johansen }
4510d259f04SJohn Johansen 
4520d259f04SJohn Johansen static const struct file_operations aa_fs_profmode_fops = {
4530d259f04SJohn Johansen 	.owner		= THIS_MODULE,
4540d259f04SJohn Johansen 	.open		= aa_fs_seq_profmode_open,
4550d259f04SJohn Johansen 	.read		= seq_read,
4560d259f04SJohn Johansen 	.llseek		= seq_lseek,
4570d259f04SJohn Johansen 	.release	= aa_fs_seq_profile_release,
4580d259f04SJohn Johansen };
4590d259f04SJohn Johansen 
460556d0be7SJohn Johansen static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
461556d0be7SJohn Johansen {
4628399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4638399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
464556d0be7SJohn Johansen 	if (profile->attach)
465556d0be7SJohn Johansen 		seq_printf(seq, "%s\n", profile->attach);
466556d0be7SJohn Johansen 	else if (profile->xmatch)
467556d0be7SJohn Johansen 		seq_puts(seq, "<unknown>\n");
468556d0be7SJohn Johansen 	else
469556d0be7SJohn Johansen 		seq_printf(seq, "%s\n", profile->base.name);
470556d0be7SJohn Johansen 	aa_put_profile(profile);
471556d0be7SJohn Johansen 
472556d0be7SJohn Johansen 	return 0;
473556d0be7SJohn Johansen }
474556d0be7SJohn Johansen 
475556d0be7SJohn Johansen static int aa_fs_seq_profattach_open(struct inode *inode, struct file *file)
476556d0be7SJohn Johansen {
477556d0be7SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profattach_show);
478556d0be7SJohn Johansen }
479556d0be7SJohn Johansen 
480556d0be7SJohn Johansen static const struct file_operations aa_fs_profattach_fops = {
481556d0be7SJohn Johansen 	.owner		= THIS_MODULE,
482556d0be7SJohn Johansen 	.open		= aa_fs_seq_profattach_open,
483556d0be7SJohn Johansen 	.read		= seq_read,
484556d0be7SJohn Johansen 	.llseek		= seq_lseek,
485556d0be7SJohn Johansen 	.release	= aa_fs_seq_profile_release,
486556d0be7SJohn Johansen };
487556d0be7SJohn Johansen 
488f8eb8a13SJohn Johansen static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
489f8eb8a13SJohn Johansen {
4908399588aSJohn Johansen 	struct aa_proxy *proxy = seq->private;
4918399588aSJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
492f8eb8a13SJohn Johansen 	unsigned int i, size = aa_hash_size();
493f8eb8a13SJohn Johansen 
494f8eb8a13SJohn Johansen 	if (profile->hash) {
495f8eb8a13SJohn Johansen 		for (i = 0; i < size; i++)
496f8eb8a13SJohn Johansen 			seq_printf(seq, "%.2x", profile->hash[i]);
49747dbd1cdSMarkus Elfring 		seq_putc(seq, '\n');
498f8eb8a13SJohn Johansen 	}
4990b938a2eSJohn Johansen 	aa_put_profile(profile);
500f8eb8a13SJohn Johansen 
501f8eb8a13SJohn Johansen 	return 0;
502f8eb8a13SJohn Johansen }
503f8eb8a13SJohn Johansen 
504f8eb8a13SJohn Johansen static int aa_fs_seq_hash_open(struct inode *inode, struct file *file)
505f8eb8a13SJohn Johansen {
506f8eb8a13SJohn Johansen 	return single_open(file, aa_fs_seq_hash_show, inode->i_private);
507f8eb8a13SJohn Johansen }
508f8eb8a13SJohn Johansen 
509f8eb8a13SJohn Johansen static const struct file_operations aa_fs_seq_hash_fops = {
510f8eb8a13SJohn Johansen 	.owner		= THIS_MODULE,
511f8eb8a13SJohn Johansen 	.open		= aa_fs_seq_hash_open,
512f8eb8a13SJohn Johansen 	.read		= seq_read,
513f8eb8a13SJohn Johansen 	.llseek		= seq_lseek,
514f8eb8a13SJohn Johansen 	.release	= single_release,
515f8eb8a13SJohn Johansen };
516f8eb8a13SJohn Johansen 
5173e3e5695SJohn Johansen 
518a71ada30SJohn Johansen static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v)
519a71ada30SJohn Johansen {
520a71ada30SJohn Johansen 	struct aa_ns *ns = aa_current_profile()->ns;
521a71ada30SJohn Johansen 
522a71ada30SJohn Johansen 	seq_printf(seq, "%d\n", ns->level);
523a71ada30SJohn Johansen 
524a71ada30SJohn Johansen 	return 0;
525a71ada30SJohn Johansen }
526a71ada30SJohn Johansen 
527a71ada30SJohn Johansen static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file)
528a71ada30SJohn Johansen {
529a71ada30SJohn Johansen 	return single_open(file, aa_fs_seq_show_ns_level, inode->i_private);
530a71ada30SJohn Johansen }
531a71ada30SJohn Johansen 
532a71ada30SJohn Johansen static const struct file_operations aa_fs_ns_level = {
533a71ada30SJohn Johansen 	.owner		= THIS_MODULE,
534a71ada30SJohn Johansen 	.open		= aa_fs_seq_open_ns_level,
535a71ada30SJohn Johansen 	.read		= seq_read,
536a71ada30SJohn Johansen 	.llseek		= seq_lseek,
537a71ada30SJohn Johansen 	.release	= single_release,
538a71ada30SJohn Johansen };
539a71ada30SJohn Johansen 
5403e3e5695SJohn Johansen static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v)
5413e3e5695SJohn Johansen {
5423e3e5695SJohn Johansen 	struct aa_ns *ns = aa_current_profile()->ns;
5433e3e5695SJohn Johansen 
5443e3e5695SJohn Johansen 	seq_printf(seq, "%s\n", ns->base.name);
5453e3e5695SJohn Johansen 
5463e3e5695SJohn Johansen 	return 0;
5473e3e5695SJohn Johansen }
5483e3e5695SJohn Johansen 
5493e3e5695SJohn Johansen static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file)
5503e3e5695SJohn Johansen {
5513e3e5695SJohn Johansen 	return single_open(file, aa_fs_seq_show_ns_name, inode->i_private);
5523e3e5695SJohn Johansen }
5533e3e5695SJohn Johansen 
5543e3e5695SJohn Johansen static const struct file_operations aa_fs_ns_name = {
5553e3e5695SJohn Johansen 	.owner		= THIS_MODULE,
5563e3e5695SJohn Johansen 	.open		= aa_fs_seq_open_ns_name,
5573e3e5695SJohn Johansen 	.read		= seq_read,
5583e3e5695SJohn Johansen 	.llseek		= seq_lseek,
5593e3e5695SJohn Johansen 	.release	= single_release,
5603e3e5695SJohn Johansen };
5613e3e5695SJohn Johansen 
5625ac8c355SJohn Johansen static int rawdata_release(struct inode *inode, struct file *file)
5635ac8c355SJohn Johansen {
5645ac8c355SJohn Johansen 	/* TODO: switch to loaddata when profile switched to symlink */
5655ac8c355SJohn Johansen 	aa_put_loaddata(file->private_data);
5665ac8c355SJohn Johansen 
5675ac8c355SJohn Johansen 	return 0;
5685ac8c355SJohn Johansen }
5695ac8c355SJohn Johansen 
5705ac8c355SJohn Johansen static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v)
5715ac8c355SJohn Johansen {
5725ac8c355SJohn Johansen 	struct aa_proxy *proxy = seq->private;
5735ac8c355SJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
5745ac8c355SJohn Johansen 
5750ff3d97fSMarkus Elfring 	if (profile->rawdata->abi)
5760ff3d97fSMarkus Elfring 		seq_printf(seq, "v%d\n", profile->rawdata->abi);
5770ff3d97fSMarkus Elfring 
5785ac8c355SJohn Johansen 	aa_put_profile(profile);
5795ac8c355SJohn Johansen 
5805ac8c355SJohn Johansen 	return 0;
5815ac8c355SJohn Johansen }
5825ac8c355SJohn Johansen 
5835ac8c355SJohn Johansen static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file)
5845ac8c355SJohn Johansen {
5855ac8c355SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show);
5865ac8c355SJohn Johansen }
5875ac8c355SJohn Johansen 
5885ac8c355SJohn Johansen static const struct file_operations aa_fs_seq_raw_abi_fops = {
5895ac8c355SJohn Johansen 	.owner		= THIS_MODULE,
5905ac8c355SJohn Johansen 	.open		= aa_fs_seq_raw_abi_open,
5915ac8c355SJohn Johansen 	.read		= seq_read,
5925ac8c355SJohn Johansen 	.llseek		= seq_lseek,
5935ac8c355SJohn Johansen 	.release	= aa_fs_seq_profile_release,
5945ac8c355SJohn Johansen };
5955ac8c355SJohn Johansen 
5965ac8c355SJohn Johansen static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v)
5975ac8c355SJohn Johansen {
5985ac8c355SJohn Johansen 	struct aa_proxy *proxy = seq->private;
5995ac8c355SJohn Johansen 	struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
6005ac8c355SJohn Johansen 	unsigned int i, size = aa_hash_size();
6015ac8c355SJohn Johansen 
6025ac8c355SJohn Johansen 	if (profile->rawdata->hash) {
6035ac8c355SJohn Johansen 		for (i = 0; i < size; i++)
6045ac8c355SJohn Johansen 			seq_printf(seq, "%.2x", profile->rawdata->hash[i]);
60547dbd1cdSMarkus Elfring 		seq_putc(seq, '\n');
6065ac8c355SJohn Johansen 	}
6075ac8c355SJohn Johansen 	aa_put_profile(profile);
6085ac8c355SJohn Johansen 
6095ac8c355SJohn Johansen 	return 0;
6105ac8c355SJohn Johansen }
6115ac8c355SJohn Johansen 
6125ac8c355SJohn Johansen static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file)
6135ac8c355SJohn Johansen {
6145ac8c355SJohn Johansen 	return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show);
6155ac8c355SJohn Johansen }
6165ac8c355SJohn Johansen 
6175ac8c355SJohn Johansen static const struct file_operations aa_fs_seq_raw_hash_fops = {
6185ac8c355SJohn Johansen 	.owner		= THIS_MODULE,
6195ac8c355SJohn Johansen 	.open		= aa_fs_seq_raw_hash_open,
6205ac8c355SJohn Johansen 	.read		= seq_read,
6215ac8c355SJohn Johansen 	.llseek		= seq_lseek,
6225ac8c355SJohn Johansen 	.release	= aa_fs_seq_profile_release,
6235ac8c355SJohn Johansen };
6245ac8c355SJohn Johansen 
6255ac8c355SJohn Johansen static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
6265ac8c355SJohn Johansen 			    loff_t *ppos)
6275ac8c355SJohn Johansen {
6285ac8c355SJohn Johansen 	struct aa_loaddata *rawdata = file->private_data;
6295ac8c355SJohn Johansen 
6305ac8c355SJohn Johansen 	return simple_read_from_buffer(buf, size, ppos, rawdata->data,
6315ac8c355SJohn Johansen 				       rawdata->size);
6325ac8c355SJohn Johansen }
6335ac8c355SJohn Johansen 
6345ac8c355SJohn Johansen static int rawdata_open(struct inode *inode, struct file *file)
6355ac8c355SJohn Johansen {
6365ac8c355SJohn Johansen 	struct aa_proxy *proxy = inode->i_private;
6375ac8c355SJohn Johansen 	struct aa_profile *profile;
6385ac8c355SJohn Johansen 
6395ac8c355SJohn Johansen 	if (!policy_view_capable(NULL))
6405ac8c355SJohn Johansen 		return -EACCES;
6415ac8c355SJohn Johansen 	profile = aa_get_profile_rcu(&proxy->profile);
6425ac8c355SJohn Johansen 	file->private_data = aa_get_loaddata(profile->rawdata);
6435ac8c355SJohn Johansen 	aa_put_profile(profile);
6445ac8c355SJohn Johansen 
6455ac8c355SJohn Johansen 	return 0;
6465ac8c355SJohn Johansen }
6475ac8c355SJohn Johansen 
6485ac8c355SJohn Johansen static const struct file_operations aa_fs_rawdata_fops = {
6495ac8c355SJohn Johansen 	.open = rawdata_open,
6505ac8c355SJohn Johansen 	.read = rawdata_read,
6515ac8c355SJohn Johansen 	.llseek = generic_file_llseek,
6525ac8c355SJohn Johansen 	.release = rawdata_release,
6535ac8c355SJohn Johansen };
6545ac8c355SJohn Johansen 
6550d259f04SJohn Johansen /** fns to setup dynamic per profile/namespace files **/
6560d259f04SJohn Johansen void __aa_fs_profile_rmdir(struct aa_profile *profile)
6570d259f04SJohn Johansen {
6580d259f04SJohn Johansen 	struct aa_profile *child;
6590d259f04SJohn Johansen 	int i;
6600d259f04SJohn Johansen 
6610d259f04SJohn Johansen 	if (!profile)
6620d259f04SJohn Johansen 		return;
6630d259f04SJohn Johansen 
6640d259f04SJohn Johansen 	list_for_each_entry(child, &profile->base.profiles, base.list)
6650d259f04SJohn Johansen 		__aa_fs_profile_rmdir(child);
6660d259f04SJohn Johansen 
6670d259f04SJohn Johansen 	for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
6688399588aSJohn Johansen 		struct aa_proxy *proxy;
6690d259f04SJohn Johansen 		if (!profile->dents[i])
6700d259f04SJohn Johansen 			continue;
6710d259f04SJohn Johansen 
6728399588aSJohn Johansen 		proxy = d_inode(profile->dents[i])->i_private;
6730d259f04SJohn Johansen 		securityfs_remove(profile->dents[i]);
6748399588aSJohn Johansen 		aa_put_proxy(proxy);
6750d259f04SJohn Johansen 		profile->dents[i] = NULL;
6760d259f04SJohn Johansen 	}
6770d259f04SJohn Johansen }
6780d259f04SJohn Johansen 
6790d259f04SJohn Johansen void __aa_fs_profile_migrate_dents(struct aa_profile *old,
6800d259f04SJohn Johansen 				   struct aa_profile *new)
6810d259f04SJohn Johansen {
6820d259f04SJohn Johansen 	int i;
6830d259f04SJohn Johansen 
6840d259f04SJohn Johansen 	for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
6850d259f04SJohn Johansen 		new->dents[i] = old->dents[i];
686d671e890SJohn Johansen 		if (new->dents[i])
687078cd827SDeepa Dinamani 			new->dents[i]->d_inode->i_mtime = current_time(new->dents[i]->d_inode);
6880d259f04SJohn Johansen 		old->dents[i] = NULL;
6890d259f04SJohn Johansen 	}
6900d259f04SJohn Johansen }
6910d259f04SJohn Johansen 
6920d259f04SJohn Johansen static struct dentry *create_profile_file(struct dentry *dir, const char *name,
6930d259f04SJohn Johansen 					  struct aa_profile *profile,
6940d259f04SJohn Johansen 					  const struct file_operations *fops)
6950d259f04SJohn Johansen {
6968399588aSJohn Johansen 	struct aa_proxy *proxy = aa_get_proxy(profile->proxy);
6970d259f04SJohn Johansen 	struct dentry *dent;
6980d259f04SJohn Johansen 
6998399588aSJohn Johansen 	dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
7000d259f04SJohn Johansen 	if (IS_ERR(dent))
7018399588aSJohn Johansen 		aa_put_proxy(proxy);
7020d259f04SJohn Johansen 
7030d259f04SJohn Johansen 	return dent;
7040d259f04SJohn Johansen }
7050d259f04SJohn Johansen 
7060d259f04SJohn Johansen /* requires lock be held */
7070d259f04SJohn Johansen int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
7080d259f04SJohn Johansen {
7090d259f04SJohn Johansen 	struct aa_profile *child;
7100d259f04SJohn Johansen 	struct dentry *dent = NULL, *dir;
7110d259f04SJohn Johansen 	int error;
7120d259f04SJohn Johansen 
7130d259f04SJohn Johansen 	if (!parent) {
7140d259f04SJohn Johansen 		struct aa_profile *p;
7150d259f04SJohn Johansen 		p = aa_deref_parent(profile);
7160d259f04SJohn Johansen 		dent = prof_dir(p);
7170d259f04SJohn Johansen 		/* adding to parent that previously didn't have children */
7180d259f04SJohn Johansen 		dent = securityfs_create_dir("profiles", dent);
7190d259f04SJohn Johansen 		if (IS_ERR(dent))
7200d259f04SJohn Johansen 			goto fail;
7210d259f04SJohn Johansen 		prof_child_dir(p) = parent = dent;
7220d259f04SJohn Johansen 	}
7230d259f04SJohn Johansen 
7240d259f04SJohn Johansen 	if (!profile->dirname) {
7250d259f04SJohn Johansen 		int len, id_len;
7260d259f04SJohn Johansen 		len = mangle_name(profile->base.name, NULL);
7270d259f04SJohn Johansen 		id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);
7280d259f04SJohn Johansen 
7290d259f04SJohn Johansen 		profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
730ffac1de6SDan Carpenter 		if (!profile->dirname) {
731ffac1de6SDan Carpenter 			error = -ENOMEM;
732ffac1de6SDan Carpenter 			goto fail2;
733ffac1de6SDan Carpenter 		}
7340d259f04SJohn Johansen 
7350d259f04SJohn Johansen 		mangle_name(profile->base.name, profile->dirname);
7360d259f04SJohn Johansen 		sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
7370d259f04SJohn Johansen 	}
7380d259f04SJohn Johansen 
7390d259f04SJohn Johansen 	dent = securityfs_create_dir(profile->dirname, parent);
7400d259f04SJohn Johansen 	if (IS_ERR(dent))
7410d259f04SJohn Johansen 		goto fail;
7420d259f04SJohn Johansen 	prof_dir(profile) = dir = dent;
7430d259f04SJohn Johansen 
7440d259f04SJohn Johansen 	dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops);
7450d259f04SJohn Johansen 	if (IS_ERR(dent))
7460d259f04SJohn Johansen 		goto fail;
7470d259f04SJohn Johansen 	profile->dents[AAFS_PROF_NAME] = dent;
7480d259f04SJohn Johansen 
7490d259f04SJohn Johansen 	dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops);
7500d259f04SJohn Johansen 	if (IS_ERR(dent))
7510d259f04SJohn Johansen 		goto fail;
7520d259f04SJohn Johansen 	profile->dents[AAFS_PROF_MODE] = dent;
7530d259f04SJohn Johansen 
754556d0be7SJohn Johansen 	dent = create_profile_file(dir, "attach", profile,
755556d0be7SJohn Johansen 				   &aa_fs_profattach_fops);
756556d0be7SJohn Johansen 	if (IS_ERR(dent))
757556d0be7SJohn Johansen 		goto fail;
758556d0be7SJohn Johansen 	profile->dents[AAFS_PROF_ATTACH] = dent;
759556d0be7SJohn Johansen 
760f8eb8a13SJohn Johansen 	if (profile->hash) {
761f8eb8a13SJohn Johansen 		dent = create_profile_file(dir, "sha1", profile,
762f8eb8a13SJohn Johansen 					   &aa_fs_seq_hash_fops);
763f8eb8a13SJohn Johansen 		if (IS_ERR(dent))
764f8eb8a13SJohn Johansen 			goto fail;
765f8eb8a13SJohn Johansen 		profile->dents[AAFS_PROF_HASH] = dent;
766f8eb8a13SJohn Johansen 	}
767f8eb8a13SJohn Johansen 
7685ac8c355SJohn Johansen 	if (profile->rawdata) {
7695ac8c355SJohn Johansen 		dent = create_profile_file(dir, "raw_sha1", profile,
7705ac8c355SJohn Johansen 					   &aa_fs_seq_raw_hash_fops);
7715ac8c355SJohn Johansen 		if (IS_ERR(dent))
7725ac8c355SJohn Johansen 			goto fail;
7735ac8c355SJohn Johansen 		profile->dents[AAFS_PROF_RAW_HASH] = dent;
7745ac8c355SJohn Johansen 
7755ac8c355SJohn Johansen 		dent = create_profile_file(dir, "raw_abi", profile,
7765ac8c355SJohn Johansen 					   &aa_fs_seq_raw_abi_fops);
7775ac8c355SJohn Johansen 		if (IS_ERR(dent))
7785ac8c355SJohn Johansen 			goto fail;
7795ac8c355SJohn Johansen 		profile->dents[AAFS_PROF_RAW_ABI] = dent;
7805ac8c355SJohn Johansen 
7815ac8c355SJohn Johansen 		dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir,
7825ac8c355SJohn Johansen 					      profile->proxy,
7835ac8c355SJohn Johansen 					      &aa_fs_rawdata_fops);
7845ac8c355SJohn Johansen 		if (IS_ERR(dent))
7855ac8c355SJohn Johansen 			goto fail;
7865ac8c355SJohn Johansen 		profile->dents[AAFS_PROF_RAW_DATA] = dent;
7875ac8c355SJohn Johansen 		d_inode(dent)->i_size = profile->rawdata->size;
7885ac8c355SJohn Johansen 		aa_get_proxy(profile->proxy);
7895ac8c355SJohn Johansen 	}
7905ac8c355SJohn Johansen 
7910d259f04SJohn Johansen 	list_for_each_entry(child, &profile->base.profiles, base.list) {
7920d259f04SJohn Johansen 		error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
7930d259f04SJohn Johansen 		if (error)
7940d259f04SJohn Johansen 			goto fail2;
7950d259f04SJohn Johansen 	}
7960d259f04SJohn Johansen 
7970d259f04SJohn Johansen 	return 0;
7980d259f04SJohn Johansen 
7990d259f04SJohn Johansen fail:
8000d259f04SJohn Johansen 	error = PTR_ERR(dent);
8010d259f04SJohn Johansen 
8020d259f04SJohn Johansen fail2:
8030d259f04SJohn Johansen 	__aa_fs_profile_rmdir(profile);
8040d259f04SJohn Johansen 
8050d259f04SJohn Johansen 	return error;
8060d259f04SJohn Johansen }
8070d259f04SJohn Johansen 
80898849dffSJohn Johansen void __aa_fs_ns_rmdir(struct aa_ns *ns)
8090d259f04SJohn Johansen {
81098849dffSJohn Johansen 	struct aa_ns *sub;
8110d259f04SJohn Johansen 	struct aa_profile *child;
8120d259f04SJohn Johansen 	int i;
8130d259f04SJohn Johansen 
8140d259f04SJohn Johansen 	if (!ns)
8150d259f04SJohn Johansen 		return;
8160d259f04SJohn Johansen 
8170d259f04SJohn Johansen 	list_for_each_entry(child, &ns->base.profiles, base.list)
8180d259f04SJohn Johansen 		__aa_fs_profile_rmdir(child);
8190d259f04SJohn Johansen 
8200d259f04SJohn Johansen 	list_for_each_entry(sub, &ns->sub_ns, base.list) {
8210d259f04SJohn Johansen 		mutex_lock(&sub->lock);
82298849dffSJohn Johansen 		__aa_fs_ns_rmdir(sub);
8230d259f04SJohn Johansen 		mutex_unlock(&sub->lock);
8240d259f04SJohn Johansen 	}
8250d259f04SJohn Johansen 
826b7fd2c03SJohn Johansen 	if (ns_subns_dir(ns)) {
827b7fd2c03SJohn Johansen 		sub = d_inode(ns_subns_dir(ns))->i_private;
828b7fd2c03SJohn Johansen 		aa_put_ns(sub);
829b7fd2c03SJohn Johansen 	}
830b7fd2c03SJohn Johansen 	if (ns_subload(ns)) {
831b7fd2c03SJohn Johansen 		sub = d_inode(ns_subload(ns))->i_private;
832b7fd2c03SJohn Johansen 		aa_put_ns(sub);
833b7fd2c03SJohn Johansen 	}
834b7fd2c03SJohn Johansen 	if (ns_subreplace(ns)) {
835b7fd2c03SJohn Johansen 		sub = d_inode(ns_subreplace(ns))->i_private;
836b7fd2c03SJohn Johansen 		aa_put_ns(sub);
837b7fd2c03SJohn Johansen 	}
838b7fd2c03SJohn Johansen 	if (ns_subremove(ns)) {
839b7fd2c03SJohn Johansen 		sub = d_inode(ns_subremove(ns))->i_private;
840b7fd2c03SJohn Johansen 		aa_put_ns(sub);
841b7fd2c03SJohn Johansen 	}
842b7fd2c03SJohn Johansen 
8430d259f04SJohn Johansen 	for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
8440d259f04SJohn Johansen 		securityfs_remove(ns->dents[i]);
8450d259f04SJohn Johansen 		ns->dents[i] = NULL;
8460d259f04SJohn Johansen 	}
8470d259f04SJohn Johansen }
8480d259f04SJohn Johansen 
849b7fd2c03SJohn Johansen /* assumes cleanup in caller */
850b7fd2c03SJohn Johansen static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
851b7fd2c03SJohn Johansen {
852b7fd2c03SJohn Johansen 	struct dentry *dent;
853b7fd2c03SJohn Johansen 
854b7fd2c03SJohn Johansen 	AA_BUG(!ns);
855b7fd2c03SJohn Johansen 	AA_BUG(!dir);
856b7fd2c03SJohn Johansen 
857b7fd2c03SJohn Johansen 	dent = securityfs_create_dir("profiles", dir);
858b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
859b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
860b7fd2c03SJohn Johansen 	ns_subprofs_dir(ns) = dent;
861b7fd2c03SJohn Johansen 
862b7fd2c03SJohn Johansen 	dent = securityfs_create_dir("raw_data", dir);
863b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
864b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
865b7fd2c03SJohn Johansen 	ns_subdata_dir(ns) = dent;
866b7fd2c03SJohn Johansen 
867b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".load", 0640, dir, ns,
868b7fd2c03SJohn Johansen 				      &aa_fs_profile_load);
869b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
870b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
871b7fd2c03SJohn Johansen 	aa_get_ns(ns);
872b7fd2c03SJohn Johansen 	ns_subload(ns) = dent;
873b7fd2c03SJohn Johansen 
874b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".replace", 0640, dir, ns,
875b7fd2c03SJohn Johansen 				      &aa_fs_profile_replace);
876b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
877b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
878b7fd2c03SJohn Johansen 	aa_get_ns(ns);
879b7fd2c03SJohn Johansen 	ns_subreplace(ns) = dent;
880b7fd2c03SJohn Johansen 
881b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".remove", 0640, dir, ns,
882b7fd2c03SJohn Johansen 				      &aa_fs_profile_remove);
883b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
884b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
885b7fd2c03SJohn Johansen 	aa_get_ns(ns);
886b7fd2c03SJohn Johansen 	ns_subremove(ns) = dent;
887b7fd2c03SJohn Johansen 
888b7fd2c03SJohn Johansen 	dent = securityfs_create_dir("namespaces", dir);
889b7fd2c03SJohn Johansen 	if (IS_ERR(dent))
890b7fd2c03SJohn Johansen 		return PTR_ERR(dent);
891b7fd2c03SJohn Johansen 	aa_get_ns(ns);
892b7fd2c03SJohn Johansen 	ns_subns_dir(ns) = dent;
893b7fd2c03SJohn Johansen 
894b7fd2c03SJohn Johansen 	return 0;
895b7fd2c03SJohn Johansen }
896b7fd2c03SJohn Johansen 
89798849dffSJohn Johansen int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
8980d259f04SJohn Johansen {
89998849dffSJohn Johansen 	struct aa_ns *sub;
9000d259f04SJohn Johansen 	struct aa_profile *child;
9010d259f04SJohn Johansen 	struct dentry *dent, *dir;
9020d259f04SJohn Johansen 	int error;
9030d259f04SJohn Johansen 
904b7fd2c03SJohn Johansen 	AA_BUG(!ns);
905b7fd2c03SJohn Johansen 	AA_BUG(!parent);
906b7fd2c03SJohn Johansen 	AA_BUG(!mutex_is_locked(&ns->lock));
907b7fd2c03SJohn Johansen 
9080d259f04SJohn Johansen 	if (!name)
9090d259f04SJohn Johansen 		name = ns->base.name;
9100d259f04SJohn Johansen 
911b7fd2c03SJohn Johansen 	/* create ns dir if it doesn't already exist */
9120d259f04SJohn Johansen 	dent = securityfs_create_dir(name, parent);
9130d259f04SJohn Johansen 	if (IS_ERR(dent))
9140d259f04SJohn Johansen 		goto fail;
915b7fd2c03SJohn Johansen 
9160d259f04SJohn Johansen 	ns_dir(ns) = dir = dent;
917b7fd2c03SJohn Johansen 	error = __aa_fs_ns_mkdir_entries(ns, dir);
918b7fd2c03SJohn Johansen 	if (error)
919b7fd2c03SJohn Johansen 		goto fail2;
9200d259f04SJohn Johansen 
921b7fd2c03SJohn Johansen 	/* profiles */
9220d259f04SJohn Johansen 	list_for_each_entry(child, &ns->base.profiles, base.list) {
9230d259f04SJohn Johansen 		error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
9240d259f04SJohn Johansen 		if (error)
9250d259f04SJohn Johansen 			goto fail2;
9260d259f04SJohn Johansen 	}
9270d259f04SJohn Johansen 
928b7fd2c03SJohn Johansen 	/* subnamespaces */
9290d259f04SJohn Johansen 	list_for_each_entry(sub, &ns->sub_ns, base.list) {
9300d259f04SJohn Johansen 		mutex_lock(&sub->lock);
93198849dffSJohn Johansen 		error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
9320d259f04SJohn Johansen 		mutex_unlock(&sub->lock);
9330d259f04SJohn Johansen 		if (error)
9340d259f04SJohn Johansen 			goto fail2;
9350d259f04SJohn Johansen 	}
9360d259f04SJohn Johansen 
9370d259f04SJohn Johansen 	return 0;
9380d259f04SJohn Johansen 
9390d259f04SJohn Johansen fail:
9400d259f04SJohn Johansen 	error = PTR_ERR(dent);
9410d259f04SJohn Johansen 
9420d259f04SJohn Johansen fail2:
94398849dffSJohn Johansen 	__aa_fs_ns_rmdir(ns);
9440d259f04SJohn Johansen 
9450d259f04SJohn Johansen 	return error;
9460d259f04SJohn Johansen }
9470d259f04SJohn Johansen 
9480d259f04SJohn Johansen 
94929b3822fSJohn Johansen #define list_entry_is_head(pos, head, member) (&pos->member == (head))
95029b3822fSJohn Johansen 
95129b3822fSJohn Johansen /**
95298849dffSJohn Johansen  * __next_ns - find the next namespace to list
95329b3822fSJohn Johansen  * @root: root namespace to stop search at (NOT NULL)
95429b3822fSJohn Johansen  * @ns: current ns position (NOT NULL)
95529b3822fSJohn Johansen  *
95629b3822fSJohn Johansen  * Find the next namespace from @ns under @root and handle all locking needed
95729b3822fSJohn Johansen  * while switching current namespace.
95829b3822fSJohn Johansen  *
95929b3822fSJohn Johansen  * Returns: next namespace or NULL if at last namespace under @root
96029b3822fSJohn Johansen  * Requires: ns->parent->lock to be held
96129b3822fSJohn Johansen  * NOTE: will not unlock root->lock
96229b3822fSJohn Johansen  */
96398849dffSJohn Johansen static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
96429b3822fSJohn Johansen {
96598849dffSJohn Johansen 	struct aa_ns *parent, *next;
96629b3822fSJohn Johansen 
96729b3822fSJohn Johansen 	/* is next namespace a child */
96829b3822fSJohn Johansen 	if (!list_empty(&ns->sub_ns)) {
96929b3822fSJohn Johansen 		next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
97029b3822fSJohn Johansen 		mutex_lock(&next->lock);
97129b3822fSJohn Johansen 		return next;
97229b3822fSJohn Johansen 	}
97329b3822fSJohn Johansen 
97429b3822fSJohn Johansen 	/* check if the next ns is a sibling, parent, gp, .. */
97529b3822fSJohn Johansen 	parent = ns->parent;
976ed2c7da3SJohn Johansen 	while (ns != root) {
97729b3822fSJohn Johansen 		mutex_unlock(&ns->lock);
97838dbd7d8SGeliang Tang 		next = list_next_entry(ns, base.list);
97929b3822fSJohn Johansen 		if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
98029b3822fSJohn Johansen 			mutex_lock(&next->lock);
98129b3822fSJohn Johansen 			return next;
98229b3822fSJohn Johansen 		}
98329b3822fSJohn Johansen 		ns = parent;
98429b3822fSJohn Johansen 		parent = parent->parent;
98529b3822fSJohn Johansen 	}
98629b3822fSJohn Johansen 
98729b3822fSJohn Johansen 	return NULL;
98829b3822fSJohn Johansen }
98929b3822fSJohn Johansen 
99029b3822fSJohn Johansen /**
99129b3822fSJohn Johansen  * __first_profile - find the first profile in a namespace
99229b3822fSJohn Johansen  * @root: namespace that is root of profiles being displayed (NOT NULL)
99329b3822fSJohn Johansen  * @ns: namespace to start in   (NOT NULL)
99429b3822fSJohn Johansen  *
99529b3822fSJohn Johansen  * Returns: unrefcounted profile or NULL if no profile
99629b3822fSJohn Johansen  * Requires: profile->ns.lock to be held
99729b3822fSJohn Johansen  */
99898849dffSJohn Johansen static struct aa_profile *__first_profile(struct aa_ns *root,
99998849dffSJohn Johansen 					  struct aa_ns *ns)
100029b3822fSJohn Johansen {
100198849dffSJohn Johansen 	for (; ns; ns = __next_ns(root, ns)) {
100229b3822fSJohn Johansen 		if (!list_empty(&ns->base.profiles))
100329b3822fSJohn Johansen 			return list_first_entry(&ns->base.profiles,
100429b3822fSJohn Johansen 						struct aa_profile, base.list);
100529b3822fSJohn Johansen 	}
100629b3822fSJohn Johansen 	return NULL;
100729b3822fSJohn Johansen }
100829b3822fSJohn Johansen 
100929b3822fSJohn Johansen /**
101029b3822fSJohn Johansen  * __next_profile - step to the next profile in a profile tree
101129b3822fSJohn Johansen  * @profile: current profile in tree (NOT NULL)
101229b3822fSJohn Johansen  *
101329b3822fSJohn Johansen  * Perform a depth first traversal on the profile tree in a namespace
101429b3822fSJohn Johansen  *
101529b3822fSJohn Johansen  * Returns: next profile or NULL if done
101629b3822fSJohn Johansen  * Requires: profile->ns.lock to be held
101729b3822fSJohn Johansen  */
101829b3822fSJohn Johansen static struct aa_profile *__next_profile(struct aa_profile *p)
101929b3822fSJohn Johansen {
102029b3822fSJohn Johansen 	struct aa_profile *parent;
102198849dffSJohn Johansen 	struct aa_ns *ns = p->ns;
102229b3822fSJohn Johansen 
102329b3822fSJohn Johansen 	/* is next profile a child */
102429b3822fSJohn Johansen 	if (!list_empty(&p->base.profiles))
102529b3822fSJohn Johansen 		return list_first_entry(&p->base.profiles, typeof(*p),
102629b3822fSJohn Johansen 					base.list);
102729b3822fSJohn Johansen 
102829b3822fSJohn Johansen 	/* is next profile a sibling, parent sibling, gp, sibling, .. */
102929b3822fSJohn Johansen 	parent = rcu_dereference_protected(p->parent,
103029b3822fSJohn Johansen 					   mutex_is_locked(&p->ns->lock));
103129b3822fSJohn Johansen 	while (parent) {
103238dbd7d8SGeliang Tang 		p = list_next_entry(p, base.list);
103329b3822fSJohn Johansen 		if (!list_entry_is_head(p, &parent->base.profiles, base.list))
103429b3822fSJohn Johansen 			return p;
103529b3822fSJohn Johansen 		p = parent;
103629b3822fSJohn Johansen 		parent = rcu_dereference_protected(parent->parent,
103729b3822fSJohn Johansen 					    mutex_is_locked(&parent->ns->lock));
103829b3822fSJohn Johansen 	}
103929b3822fSJohn Johansen 
104029b3822fSJohn Johansen 	/* is next another profile in the namespace */
104138dbd7d8SGeliang Tang 	p = list_next_entry(p, base.list);
104229b3822fSJohn Johansen 	if (!list_entry_is_head(p, &ns->base.profiles, base.list))
104329b3822fSJohn Johansen 		return p;
104429b3822fSJohn Johansen 
104529b3822fSJohn Johansen 	return NULL;
104629b3822fSJohn Johansen }
104729b3822fSJohn Johansen 
104829b3822fSJohn Johansen /**
104929b3822fSJohn Johansen  * next_profile - step to the next profile in where ever it may be
105029b3822fSJohn Johansen  * @root: root namespace  (NOT NULL)
105129b3822fSJohn Johansen  * @profile: current profile  (NOT NULL)
105229b3822fSJohn Johansen  *
105329b3822fSJohn Johansen  * Returns: next profile or NULL if there isn't one
105429b3822fSJohn Johansen  */
105598849dffSJohn Johansen static struct aa_profile *next_profile(struct aa_ns *root,
105629b3822fSJohn Johansen 				       struct aa_profile *profile)
105729b3822fSJohn Johansen {
105829b3822fSJohn Johansen 	struct aa_profile *next = __next_profile(profile);
105929b3822fSJohn Johansen 	if (next)
106029b3822fSJohn Johansen 		return next;
106129b3822fSJohn Johansen 
106229b3822fSJohn Johansen 	/* finished all profiles in namespace move to next namespace */
106398849dffSJohn Johansen 	return __first_profile(root, __next_ns(root, profile->ns));
106429b3822fSJohn Johansen }
106529b3822fSJohn Johansen 
106629b3822fSJohn Johansen /**
106729b3822fSJohn Johansen  * p_start - start a depth first traversal of profile tree
106829b3822fSJohn Johansen  * @f: seq_file to fill
106929b3822fSJohn Johansen  * @pos: current position
107029b3822fSJohn Johansen  *
107129b3822fSJohn Johansen  * Returns: first profile under current namespace or NULL if none found
107229b3822fSJohn Johansen  *
107329b3822fSJohn Johansen  * acquires first ns->lock
107429b3822fSJohn Johansen  */
107529b3822fSJohn Johansen static void *p_start(struct seq_file *f, loff_t *pos)
107629b3822fSJohn Johansen {
107729b3822fSJohn Johansen 	struct aa_profile *profile = NULL;
107898849dffSJohn Johansen 	struct aa_ns *root = aa_current_profile()->ns;
107929b3822fSJohn Johansen 	loff_t l = *pos;
108098849dffSJohn Johansen 	f->private = aa_get_ns(root);
108129b3822fSJohn Johansen 
108229b3822fSJohn Johansen 
108329b3822fSJohn Johansen 	/* find the first profile */
108429b3822fSJohn Johansen 	mutex_lock(&root->lock);
108529b3822fSJohn Johansen 	profile = __first_profile(root, root);
108629b3822fSJohn Johansen 
108729b3822fSJohn Johansen 	/* skip to position */
108829b3822fSJohn Johansen 	for (; profile && l > 0; l--)
108929b3822fSJohn Johansen 		profile = next_profile(root, profile);
109029b3822fSJohn Johansen 
109129b3822fSJohn Johansen 	return profile;
109229b3822fSJohn Johansen }
109329b3822fSJohn Johansen 
109429b3822fSJohn Johansen /**
109529b3822fSJohn Johansen  * p_next - read the next profile entry
109629b3822fSJohn Johansen  * @f: seq_file to fill
109729b3822fSJohn Johansen  * @p: profile previously returned
109829b3822fSJohn Johansen  * @pos: current position
109929b3822fSJohn Johansen  *
110029b3822fSJohn Johansen  * Returns: next profile after @p or NULL if none
110129b3822fSJohn Johansen  *
110229b3822fSJohn Johansen  * may acquire/release locks in namespace tree as necessary
110329b3822fSJohn Johansen  */
110429b3822fSJohn Johansen static void *p_next(struct seq_file *f, void *p, loff_t *pos)
110529b3822fSJohn Johansen {
110629b3822fSJohn Johansen 	struct aa_profile *profile = p;
110798849dffSJohn Johansen 	struct aa_ns *ns = f->private;
110829b3822fSJohn Johansen 	(*pos)++;
110929b3822fSJohn Johansen 
111029b3822fSJohn Johansen 	return next_profile(ns, profile);
111129b3822fSJohn Johansen }
111229b3822fSJohn Johansen 
111329b3822fSJohn Johansen /**
111429b3822fSJohn Johansen  * p_stop - stop depth first traversal
111529b3822fSJohn Johansen  * @f: seq_file we are filling
111629b3822fSJohn Johansen  * @p: the last profile writen
111729b3822fSJohn Johansen  *
111829b3822fSJohn Johansen  * Release all locking done by p_start/p_next on namespace tree
111929b3822fSJohn Johansen  */
112029b3822fSJohn Johansen static void p_stop(struct seq_file *f, void *p)
112129b3822fSJohn Johansen {
112229b3822fSJohn Johansen 	struct aa_profile *profile = p;
112398849dffSJohn Johansen 	struct aa_ns *root = f->private, *ns;
112429b3822fSJohn Johansen 
112529b3822fSJohn Johansen 	if (profile) {
112629b3822fSJohn Johansen 		for (ns = profile->ns; ns && ns != root; ns = ns->parent)
112729b3822fSJohn Johansen 			mutex_unlock(&ns->lock);
112829b3822fSJohn Johansen 	}
112929b3822fSJohn Johansen 	mutex_unlock(&root->lock);
113098849dffSJohn Johansen 	aa_put_ns(root);
113129b3822fSJohn Johansen }
113229b3822fSJohn Johansen 
113329b3822fSJohn Johansen /**
113429b3822fSJohn Johansen  * seq_show_profile - show a profile entry
113529b3822fSJohn Johansen  * @f: seq_file to file
113629b3822fSJohn Johansen  * @p: current position (profile)    (NOT NULL)
113729b3822fSJohn Johansen  *
113829b3822fSJohn Johansen  * Returns: error on failure
113929b3822fSJohn Johansen  */
114029b3822fSJohn Johansen static int seq_show_profile(struct seq_file *f, void *p)
114129b3822fSJohn Johansen {
114229b3822fSJohn Johansen 	struct aa_profile *profile = (struct aa_profile *)p;
114398849dffSJohn Johansen 	struct aa_ns *root = f->private;
114429b3822fSJohn Johansen 
114529b3822fSJohn Johansen 	if (profile->ns != root)
114692b6d8efSJohn Johansen 		seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true));
114729b3822fSJohn Johansen 	seq_printf(f, "%s (%s)\n", profile->base.hname,
114829b3822fSJohn Johansen 		   aa_profile_mode_names[profile->mode]);
114929b3822fSJohn Johansen 
115029b3822fSJohn Johansen 	return 0;
115129b3822fSJohn Johansen }
115229b3822fSJohn Johansen 
115329b3822fSJohn Johansen static const struct seq_operations aa_fs_profiles_op = {
115429b3822fSJohn Johansen 	.start = p_start,
115529b3822fSJohn Johansen 	.next = p_next,
115629b3822fSJohn Johansen 	.stop = p_stop,
115729b3822fSJohn Johansen 	.show = seq_show_profile,
115829b3822fSJohn Johansen };
115929b3822fSJohn Johansen 
116029b3822fSJohn Johansen static int profiles_open(struct inode *inode, struct file *file)
116129b3822fSJohn Johansen {
11625ac8c355SJohn Johansen 	if (!policy_view_capable(NULL))
11635ac8c355SJohn Johansen 		return -EACCES;
11645ac8c355SJohn Johansen 
116529b3822fSJohn Johansen 	return seq_open(file, &aa_fs_profiles_op);
116629b3822fSJohn Johansen }
116729b3822fSJohn Johansen 
116829b3822fSJohn Johansen static int profiles_release(struct inode *inode, struct file *file)
116929b3822fSJohn Johansen {
117029b3822fSJohn Johansen 	return seq_release(inode, file);
117129b3822fSJohn Johansen }
117229b3822fSJohn Johansen 
117329b3822fSJohn Johansen static const struct file_operations aa_fs_profiles_fops = {
117429b3822fSJohn Johansen 	.open = profiles_open,
117529b3822fSJohn Johansen 	.read = seq_read,
117629b3822fSJohn Johansen 	.llseek = seq_lseek,
117729b3822fSJohn Johansen 	.release = profiles_release,
117829b3822fSJohn Johansen };
117929b3822fSJohn Johansen 
118029b3822fSJohn Johansen 
11810d259f04SJohn Johansen /** Base file system setup **/
1182a9bf8e9fSKees Cook static struct aa_fs_entry aa_fs_entry_file[] = {
1183a9bf8e9fSKees Cook 	AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
1184a9bf8e9fSKees Cook 				  "link lock"),
1185a9bf8e9fSKees Cook 	{ }
1186a9bf8e9fSKees Cook };
1187a9bf8e9fSKees Cook 
1188e74abcf3SKees Cook static struct aa_fs_entry aa_fs_entry_domain[] = {
1189e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_hat",	1),
1190e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_hatv",	1),
1191e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_onexec",	1),
1192e74abcf3SKees Cook 	AA_FS_FILE_BOOLEAN("change_profile",	1),
119334c426acSJohn Johansen 	AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap",	1),
1194aa9a39adSJohn Johansen 	AA_FS_FILE_STRING("version", "1.2"),
1195e74abcf3SKees Cook 	{ }
1196e74abcf3SKees Cook };
1197e74abcf3SKees Cook 
1198474d6b75SJohn Johansen static struct aa_fs_entry aa_fs_entry_versions[] = {
1199474d6b75SJohn Johansen 	AA_FS_FILE_BOOLEAN("v5",	1),
1200474d6b75SJohn Johansen 	{ }
1201474d6b75SJohn Johansen };
1202474d6b75SJohn Johansen 
12039d910a3bSJohn Johansen static struct aa_fs_entry aa_fs_entry_policy[] = {
1204474d6b75SJohn Johansen 	AA_FS_DIR("versions",                   aa_fs_entry_versions),
1205dd51c848SJohn Johansen 	AA_FS_FILE_BOOLEAN("set_load",		1),
12069d910a3bSJohn Johansen 	{ }
12079d910a3bSJohn Johansen };
12089d910a3bSJohn Johansen 
1209e74abcf3SKees Cook static struct aa_fs_entry aa_fs_entry_features[] = {
12109d910a3bSJohn Johansen 	AA_FS_DIR("policy",			aa_fs_entry_policy),
1211e74abcf3SKees Cook 	AA_FS_DIR("domain",			aa_fs_entry_domain),
1212a9bf8e9fSKees Cook 	AA_FS_DIR("file",			aa_fs_entry_file),
1213e74abcf3SKees Cook 	AA_FS_FILE_U64("capability",		VFS_CAP_FLAGS_MASK),
1214d384b0a1SKees Cook 	AA_FS_DIR("rlimit",			aa_fs_entry_rlimit),
121584f1f787SJohn Johansen 	AA_FS_DIR("caps",			aa_fs_entry_caps),
1216e74abcf3SKees Cook 	{ }
1217e74abcf3SKees Cook };
1218e74abcf3SKees Cook 
12199acd494bSKees Cook static struct aa_fs_entry aa_fs_entry_apparmor[] = {
1220e025be0fSWilliam Hua 	AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
1221a71ada30SJohn Johansen 	AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
12223e3e5695SJohn Johansen 	AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
1223b7fd2c03SJohn Johansen 	AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
1224e74abcf3SKees Cook 	AA_FS_DIR("features", aa_fs_entry_features),
12259acd494bSKees Cook 	{ }
12269acd494bSKees Cook };
122763e2b423SJohn Johansen 
12289acd494bSKees Cook static struct aa_fs_entry aa_fs_entry =
12299acd494bSKees Cook 	AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
12309acd494bSKees Cook 
12319acd494bSKees Cook /**
12329acd494bSKees Cook  * aafs_create_file - create a file entry in the apparmor securityfs
12339acd494bSKees Cook  * @fs_file: aa_fs_entry to build an entry for (NOT NULL)
12349acd494bSKees Cook  * @parent: the parent dentry in the securityfs
12359acd494bSKees Cook  *
12369acd494bSKees Cook  * Use aafs_remove_file to remove entries created with this fn.
12379acd494bSKees Cook  */
12389acd494bSKees Cook static int __init aafs_create_file(struct aa_fs_entry *fs_file,
12399acd494bSKees Cook 				   struct dentry *parent)
124063e2b423SJohn Johansen {
12419acd494bSKees Cook 	int error = 0;
124263e2b423SJohn Johansen 
12439acd494bSKees Cook 	fs_file->dentry = securityfs_create_file(fs_file->name,
12449acd494bSKees Cook 						 S_IFREG | fs_file->mode,
12459acd494bSKees Cook 						 parent, fs_file,
12469acd494bSKees Cook 						 fs_file->file_ops);
12479acd494bSKees Cook 	if (IS_ERR(fs_file->dentry)) {
12489acd494bSKees Cook 		error = PTR_ERR(fs_file->dentry);
12499acd494bSKees Cook 		fs_file->dentry = NULL;
125063e2b423SJohn Johansen 	}
12519acd494bSKees Cook 	return error;
125263e2b423SJohn Johansen }
125363e2b423SJohn Johansen 
12540d259f04SJohn Johansen static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
125563e2b423SJohn Johansen /**
12569acd494bSKees Cook  * aafs_create_dir - recursively create a directory entry in the securityfs
12579acd494bSKees Cook  * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
12589acd494bSKees Cook  * @parent: the parent dentry in the securityfs
125963e2b423SJohn Johansen  *
12609acd494bSKees Cook  * Use aafs_remove_dir to remove entries created with this fn.
126163e2b423SJohn Johansen  */
12629acd494bSKees Cook static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
12639acd494bSKees Cook 				  struct dentry *parent)
126463e2b423SJohn Johansen {
12659acd494bSKees Cook 	struct aa_fs_entry *fs_file;
12660d259f04SJohn Johansen 	struct dentry *dir;
12670d259f04SJohn Johansen 	int error;
126863e2b423SJohn Johansen 
12690d259f04SJohn Johansen 	dir = securityfs_create_dir(fs_dir->name, parent);
12700d259f04SJohn Johansen 	if (IS_ERR(dir))
12710d259f04SJohn Johansen 		return PTR_ERR(dir);
12720d259f04SJohn Johansen 	fs_dir->dentry = dir;
127363e2b423SJohn Johansen 
12740d259f04SJohn Johansen 	for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
12759acd494bSKees Cook 		if (fs_file->v_type == AA_FS_TYPE_DIR)
12769acd494bSKees Cook 			error = aafs_create_dir(fs_file, fs_dir->dentry);
12779acd494bSKees Cook 		else
12789acd494bSKees Cook 			error = aafs_create_file(fs_file, fs_dir->dentry);
12799acd494bSKees Cook 		if (error)
12809acd494bSKees Cook 			goto failed;
12819acd494bSKees Cook 	}
12829acd494bSKees Cook 
12839acd494bSKees Cook 	return 0;
12849acd494bSKees Cook 
12859acd494bSKees Cook failed:
12860d259f04SJohn Johansen 	aafs_remove_dir(fs_dir);
12870d259f04SJohn Johansen 
12889acd494bSKees Cook 	return error;
12899acd494bSKees Cook }
12909acd494bSKees Cook 
12919acd494bSKees Cook /**
12929acd494bSKees Cook  * aafs_remove_file - drop a single file entry in the apparmor securityfs
12939acd494bSKees Cook  * @fs_file: aa_fs_entry to detach from the securityfs (NOT NULL)
12949acd494bSKees Cook  */
12959acd494bSKees Cook static void __init aafs_remove_file(struct aa_fs_entry *fs_file)
12969acd494bSKees Cook {
12979acd494bSKees Cook 	if (!fs_file->dentry)
12989acd494bSKees Cook 		return;
12999acd494bSKees Cook 
13009acd494bSKees Cook 	securityfs_remove(fs_file->dentry);
13019acd494bSKees Cook 	fs_file->dentry = NULL;
13029acd494bSKees Cook }
13039acd494bSKees Cook 
13049acd494bSKees Cook /**
13059acd494bSKees Cook  * aafs_remove_dir - recursively drop a directory entry from the securityfs
13069acd494bSKees Cook  * @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL)
13079acd494bSKees Cook  */
13089acd494bSKees Cook static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
13099acd494bSKees Cook {
13109acd494bSKees Cook 	struct aa_fs_entry *fs_file;
13119acd494bSKees Cook 
13120d259f04SJohn Johansen 	for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
13139acd494bSKees Cook 		if (fs_file->v_type == AA_FS_TYPE_DIR)
13149acd494bSKees Cook 			aafs_remove_dir(fs_file);
13159acd494bSKees Cook 		else
13169acd494bSKees Cook 			aafs_remove_file(fs_file);
13179acd494bSKees Cook 	}
13189acd494bSKees Cook 
13199acd494bSKees Cook 	aafs_remove_file(fs_dir);
132063e2b423SJohn Johansen }
132163e2b423SJohn Johansen 
132263e2b423SJohn Johansen /**
132363e2b423SJohn Johansen  * aa_destroy_aafs - cleanup and free aafs
132463e2b423SJohn Johansen  *
132563e2b423SJohn Johansen  * releases dentries allocated by aa_create_aafs
132663e2b423SJohn Johansen  */
132763e2b423SJohn Johansen void __init aa_destroy_aafs(void)
132863e2b423SJohn Johansen {
13299acd494bSKees Cook 	aafs_remove_dir(&aa_fs_entry);
133063e2b423SJohn Johansen }
133163e2b423SJohn Johansen 
1332a71ada30SJohn Johansen 
1333a71ada30SJohn Johansen #define NULL_FILE_NAME ".null"
1334a71ada30SJohn Johansen struct path aa_null;
1335a71ada30SJohn Johansen 
1336a71ada30SJohn Johansen static int aa_mk_null_file(struct dentry *parent)
1337a71ada30SJohn Johansen {
1338a71ada30SJohn Johansen 	struct vfsmount *mount = NULL;
1339a71ada30SJohn Johansen 	struct dentry *dentry;
1340a71ada30SJohn Johansen 	struct inode *inode;
1341a71ada30SJohn Johansen 	int count = 0;
1342a71ada30SJohn Johansen 	int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
1343a71ada30SJohn Johansen 
1344a71ada30SJohn Johansen 	if (error)
1345a71ada30SJohn Johansen 		return error;
1346a71ada30SJohn Johansen 
1347a71ada30SJohn Johansen 	inode_lock(d_inode(parent));
1348a71ada30SJohn Johansen 	dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
1349a71ada30SJohn Johansen 	if (IS_ERR(dentry)) {
1350a71ada30SJohn Johansen 		error = PTR_ERR(dentry);
1351a71ada30SJohn Johansen 		goto out;
1352a71ada30SJohn Johansen 	}
1353a71ada30SJohn Johansen 	inode = new_inode(parent->d_inode->i_sb);
1354a71ada30SJohn Johansen 	if (!inode) {
1355a71ada30SJohn Johansen 		error = -ENOMEM;
1356a71ada30SJohn Johansen 		goto out1;
1357a71ada30SJohn Johansen 	}
1358a71ada30SJohn Johansen 
1359a71ada30SJohn Johansen 	inode->i_ino = get_next_ino();
1360a71ada30SJohn Johansen 	inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
136124d0d03cSDeepa Dinamani 	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
1362a71ada30SJohn Johansen 	init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
1363a71ada30SJohn Johansen 			   MKDEV(MEM_MAJOR, 3));
1364a71ada30SJohn Johansen 	d_instantiate(dentry, inode);
1365a71ada30SJohn Johansen 	aa_null.dentry = dget(dentry);
1366a71ada30SJohn Johansen 	aa_null.mnt = mntget(mount);
1367a71ada30SJohn Johansen 
1368a71ada30SJohn Johansen 	error = 0;
1369a71ada30SJohn Johansen 
1370a71ada30SJohn Johansen out1:
1371a71ada30SJohn Johansen 	dput(dentry);
1372a71ada30SJohn Johansen out:
1373a71ada30SJohn Johansen 	inode_unlock(d_inode(parent));
1374a71ada30SJohn Johansen 	simple_release_fs(&mount, &count);
1375a71ada30SJohn Johansen 	return error;
1376a71ada30SJohn Johansen }
1377a71ada30SJohn Johansen 
137863e2b423SJohn Johansen /**
137963e2b423SJohn Johansen  * aa_create_aafs - create the apparmor security filesystem
138063e2b423SJohn Johansen  *
138163e2b423SJohn Johansen  * dentries created here are released by aa_destroy_aafs
138263e2b423SJohn Johansen  *
138363e2b423SJohn Johansen  * Returns: error on failure
138463e2b423SJohn Johansen  */
13853417d8d5SJames Morris static int __init aa_create_aafs(void)
138663e2b423SJohn Johansen {
1387b7fd2c03SJohn Johansen 	struct dentry *dent;
138863e2b423SJohn Johansen 	int error;
138963e2b423SJohn Johansen 
139063e2b423SJohn Johansen 	if (!apparmor_initialized)
139163e2b423SJohn Johansen 		return 0;
139263e2b423SJohn Johansen 
13939acd494bSKees Cook 	if (aa_fs_entry.dentry) {
139463e2b423SJohn Johansen 		AA_ERROR("%s: AppArmor securityfs already exists\n", __func__);
139563e2b423SJohn Johansen 		return -EEXIST;
139663e2b423SJohn Johansen 	}
139763e2b423SJohn Johansen 
13989acd494bSKees Cook 	/* Populate fs tree. */
13999acd494bSKees Cook 	error = aafs_create_dir(&aa_fs_entry, NULL);
140063e2b423SJohn Johansen 	if (error)
140163e2b423SJohn Johansen 		goto error;
140263e2b423SJohn Johansen 
1403b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry,
1404b7fd2c03SJohn Johansen 				      NULL, &aa_fs_profile_load);
1405b7fd2c03SJohn Johansen 	if (IS_ERR(dent)) {
1406b7fd2c03SJohn Johansen 		error = PTR_ERR(dent);
1407b7fd2c03SJohn Johansen 		goto error;
1408b7fd2c03SJohn Johansen 	}
1409b7fd2c03SJohn Johansen 	ns_subload(root_ns) = dent;
1410b7fd2c03SJohn Johansen 
1411b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry,
1412b7fd2c03SJohn Johansen 				      NULL, &aa_fs_profile_replace);
1413b7fd2c03SJohn Johansen 	if (IS_ERR(dent)) {
1414b7fd2c03SJohn Johansen 		error = PTR_ERR(dent);
1415b7fd2c03SJohn Johansen 		goto error;
1416b7fd2c03SJohn Johansen 	}
1417b7fd2c03SJohn Johansen 	ns_subreplace(root_ns) = dent;
1418b7fd2c03SJohn Johansen 
1419b7fd2c03SJohn Johansen 	dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry,
1420b7fd2c03SJohn Johansen 				      NULL, &aa_fs_profile_remove);
1421b7fd2c03SJohn Johansen 	if (IS_ERR(dent)) {
1422b7fd2c03SJohn Johansen 		error = PTR_ERR(dent);
1423b7fd2c03SJohn Johansen 		goto error;
1424b7fd2c03SJohn Johansen 	}
1425b7fd2c03SJohn Johansen 	ns_subremove(root_ns) = dent;
1426b7fd2c03SJohn Johansen 
1427b7fd2c03SJohn Johansen 	mutex_lock(&root_ns->lock);
142898849dffSJohn Johansen 	error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
1429b7fd2c03SJohn Johansen 	mutex_unlock(&root_ns->lock);
1430b7fd2c03SJohn Johansen 
14310d259f04SJohn Johansen 	if (error)
14320d259f04SJohn Johansen 		goto error;
14330d259f04SJohn Johansen 
1434a71ada30SJohn Johansen 	error = aa_mk_null_file(aa_fs_entry.dentry);
1435a71ada30SJohn Johansen 	if (error)
1436a71ada30SJohn Johansen 		goto error;
1437a71ada30SJohn Johansen 
1438a71ada30SJohn Johansen 	/* TODO: add default profile to apparmorfs */
143963e2b423SJohn Johansen 
144063e2b423SJohn Johansen 	/* Report that AppArmor fs is enabled */
144163e2b423SJohn Johansen 	aa_info_message("AppArmor Filesystem Enabled");
144263e2b423SJohn Johansen 	return 0;
144363e2b423SJohn Johansen 
144463e2b423SJohn Johansen error:
144563e2b423SJohn Johansen 	aa_destroy_aafs();
144663e2b423SJohn Johansen 	AA_ERROR("Error creating AppArmor securityfs\n");
144763e2b423SJohn Johansen 	return error;
144863e2b423SJohn Johansen }
144963e2b423SJohn Johansen 
145063e2b423SJohn Johansen fs_initcall(aa_create_aafs);
1451