linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux
@ 2009-01-01  5:07 Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct Tetsuo Handa
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-security-module, linux-kernel

Happy new year!

TOMOYO Linux is a name-based MAC extension (LSM module) for the Linux kernel.

The security_path_* LSM hooks have just got merged into Linus's git tree!
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=be6d3e56a6b9b3a4ee44a0685e39e595073c6f0d

Now, it's time to repost #13.
This patchset is for mmotm 2008-12-30-16-05 .

How to try:
1. Compile kernel with CONFIG_SECURITY_TOMOYO=y.
2. 'make' and 'make install' userspace tools (ccs-tools) available at
   http://sourceforge.jp/projects/tomoyo/releases/30298/ .
3. Run /usr/lib/ccs/tomoyo_init_policy.sh .
4. Run following commands to set learning-mode as default.
   (This step is optional but recommended on your first try.)
   # echo '<kernel>' > /etc/tomoyo/domain_policy.conf
   # echo 'use_profile 1' >> /etc/tomoyo/domain_policy.conf
5. Reboot.
   (If you compiled kernel with CONFIG_SECURITY_{SELINUX,SMACK}=y,
    add 'security=tomoyo' to kernel's command line.)
6. Run /usr/sbin/ccs-editpolicy to browse and edit policy.

LiveCD-based tutorials are available at
http://tomoyo.sourceforge.jp/en/1.6.x/1st-step/ubuntu8.04-live/
http://tomoyo.sourceforge.jp/en/1.6.x/1st-step/centos5-live/ .
Though these tutorials use non-LSM version of TOMOYO,
they are useful for you to know what TOMOYO is.

Regards.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-05  8:25   ` James Morris
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation Tetsuo Handa
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Tetsuo Handa, David Howells

[-- Attachment #1: add-in_execve-flag-into-task_struct.patch --]
[-- Type: text/plain, Size: 2419 bytes --]

This patch allows LSM modules to determine whether current process is in an
execve operation or not so that they can behave differently while an execve
operation is in progress.

This allows TOMOYO to dispense with a readability check on a file to be
executed under the process's current credentials, and to do it instead under
the proposed credentials.

This is required with the new COW credentials because TOMOYO is no longer
allowed to mark the state temporarily in the security struct attached to the
task_struct.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: David Howells <dhowells@redhat.com>
---
 fs/compat.c           |    3 +++
 fs/exec.c             |    3 +++
 include/linux/sched.h |    2 ++
 3 files changed, 8 insertions(+)

--- linux-2.6.28-mm1.orig/fs/compat.c
+++ linux-2.6.28-mm1/fs/compat.c
@@ -1402,6 +1402,7 @@ int compat_do_execve(char * filename,
 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
 	if (retval < 0)
 		goto out_free;
+	current->in_execve = 1;
 
 	retval = -ENOMEM;
 	bprm->cred = prepare_exec_creds();
@@ -1454,6 +1455,7 @@ int compat_do_execve(char * filename,
 		goto out;
 
 	/* execve succeeded */
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 	acct_update_integrals(current);
 	free_bprm(bprm);
@@ -1470,6 +1472,7 @@ out_file:
 	}
 
 out_unlock:
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 
 out_free:
--- linux-2.6.28-mm1.orig/fs/exec.c
+++ linux-2.6.28-mm1/fs/exec.c
@@ -1306,6 +1306,7 @@ int do_execve(char * filename,
 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
 	if (retval < 0)
 		goto out_free;
+	current->in_execve = 1;
 
 	retval = -ENOMEM;
 	bprm->cred = prepare_exec_creds();
@@ -1359,6 +1360,7 @@ int do_execve(char * filename,
 		goto out;
 
 	/* execve succeeded */
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 	acct_update_integrals(current);
 	free_bprm(bprm);
@@ -1377,6 +1379,7 @@ out_file:
 	}
 
 out_unlock:
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 
 out_free:
--- linux-2.6.28-mm1.orig/include/linux/sched.h
+++ linux-2.6.28-mm1/include/linux/sched.h
@@ -1155,6 +1155,8 @@ struct task_struct {
 	/* ??? */
 	unsigned int personality;
 	unsigned did_exec:1;
+	unsigned in_execve:1;	/* Tell the LSMs that the process is doing an
+				 * execve */
 	pid_t pid;
 	pid_t tgid;
 

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-05  9:07   ` James Morris
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath() Tetsuo Handa
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-security-module, linux-kernel, Tetsuo Handa

[-- Attachment #1: introduce-write-once-read-many-linked-list.patch --]
[-- Type: text/plain, Size: 3642 bytes --]

This patch introduces new list structure named "list1".

TOMOYO wants to use "write once read many" list.
Since only two operations

 (1) Append an entry to the tail of the list.
 (2) Read all entries starting from the head of the list.

are needed for that purpose, this list doesn't have pointer to previous
element.

While "hlist" is defined as

 struct hlist_head {
 	struct hlist_node *first;
 };

 struct hlist_node {
 	struct hlist_node *next, **pprev;
 };

I don't use "hlist_node" because I don't need pointer to previous element.

By ommiting pointer to previous element, the reader becomes read lock free,
which is good thing for implementing "write once read many" list.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
---
 include/linux/list1.h |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)

--- /dev/null
+++ linux-2.6.28-mm1/include/linux/list1.h
@@ -0,0 +1,81 @@
+#ifndef _LINUX_LIST1_H
+#define _LINUX_LIST1_H
+
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+
+/*
+ * Singly linked list implementation.
+ *
+ * This list supports only two operations.
+ * (1) Append an entry to the tail of the list.
+ * (2) Read all entries starting from the head of the list.
+ *
+ * This list is designed for holding "write once, read many" entries.
+ * This list requires no locks for read operation.
+ * This list doesn't support "remove an entry from the list" operation.
+ */
+
+/* To reduce memory usage, this list doesn't use "->prev" pointer. */
+struct list1_head {
+	struct list1_head *next;
+};
+
+#define LIST1_HEAD_INIT(name) { &(name) }
+#define LIST1_HEAD(name) struct list1_head name = LIST1_HEAD_INIT(name)
+
+static inline void INIT_LIST1_HEAD(struct list1_head *list)
+{
+	list->next = list;
+}
+
+/* Reuse list_entry because it doesn't use "->prev" pointer. */
+#define list1_entry list_entry
+
+/* Reuse list_for_each_rcu because it doesn't use "->prev" pointer. */
+#define list1_for_each list_for_each_rcu
+/* Reuse list_for_each_entry_rcu because it doesn't use "->prev" pointer. */
+#define list1_for_each_entry list_for_each_entry_rcu
+
+/**
+ * list1_for_each_cookie - iterate over a list with cookie.
+ * @pos:        the &struct list1_head to use as a loop cursor.
+ * @cookie:     the &struct list1_head to use as a cookie.
+ * @head:       the head for your list.
+ *
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
+ * so that we can continue iteration.
+ * @cookie must be NULL when iteration starts, and @cookie will become
+ * NULL when iteration finishes.
+ *
+ * Since list elements are never removed, we don't need to get a lock
+ * or a reference count.
+ */
+#define list1_for_each_cookie(pos, cookie, head)                      \
+	for (({ if (!cookie)                                          \
+				     cookie = head; }),               \
+	     pos = rcu_dereference((cookie)->next);                   \
+	     prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+	     (cookie) = pos, pos = rcu_dereference(pos->next))
+
+/**
+ * list1_add_tail - add a new entry to list1 list.
+ * @new: new entry to be added.
+ * @head: list head to add it before.
+ *
+ * Same with list_add_tail_rcu() without "->prev" pointer.
+ *
+ * Caller must hold a lock for protecting @head.
+ */
+static inline void list1_add_tail(struct list1_head *new,
+				  struct list1_head *head)
+{
+	struct list1_head *prev = head;
+
+	new->next = head;
+	while (prev->next != head)
+		prev = prev->next;
+	rcu_assign_pointer(prev->next, new);
+}
+
+#endif

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath().
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-05  9:08   ` James Morris
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 04/10] Memory and pathname management functions Tetsuo Handa
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda,
	Tetsuo Handa, Toshiharu Harada

[-- Attachment #1: introduce-d_realpath.patch --]
[-- Type: text/plain, Size: 3847 bytes --]

To remove factors that make pathname based access control difficult
(e.g. symbolic links, "..", "//", chroot() etc.), a variant of d_path()
which traverses up to the root of the namespace is needed.

This patch introduces d_realpath(), a variant of d_path().
While d_path() stops traversing at current->fs->root,
d_realpath() doesn't stop traversiong at current->fs->root.

Three differences compared to d_path().
(1) Ignores current process's root directory.
(2) Trailing '/' is added if the pathname refers to a directory.
(3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 fs/dcache.c            |   84 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dcache.h |    1 
 2 files changed, 85 insertions(+)

--- linux-2.6.28-mm1.orig/fs/dcache.c
+++ linux-2.6.28-mm1/fs/dcache.c
@@ -32,6 +32,7 @@
 #include <linux/seqlock.h>
 #include <linux/swap.h>
 #include <linux/bootmem.h>
+#include <linux/magic.h>
 #include "internal.h"
 
 
@@ -1978,6 +1979,89 @@ Elong:
 }
 
 /**
+ * d_realpath - Get the realpath of a dentry.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns pointer to the realpath on success, an error code othersize.
+ *
+ * If @dentry is a directory, trailing '/' is appended.
+ * /proc/PID/ is replaced by /proc/self/ if PID == task_tgid_nr_ns(current).
+ */
+char *d_realpath(struct path *path, char *buffer, int buflen)
+{
+	struct dentry *dentry = path->dentry;
+	struct vfsmount *vfsmnt = path->mnt;
+	char *end = buffer + buflen;
+
+	spin_lock(&dcache_lock);
+	spin_lock(&vfsmount_lock);
+	if (buflen < 1 || prepend(&end, &buflen, "", 1))
+		goto Elong;
+	/*
+	 * Exception: Add trailing '/' for directory.
+	 */
+	if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) &&
+	    prepend(&end, &buflen, "/", 1))
+		goto Elong;
+	for (;;) {
+		struct dentry *parent;
+		const char *name;
+		int name_len;
+		unsigned long pid;
+
+		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+			/* Global root? */
+			if (vfsmnt->mnt_parent == vfsmnt)
+				break;
+			dentry = vfsmnt->mnt_mountpoint;
+			vfsmnt = vfsmnt->mnt_parent;
+			continue;
+		}
+		parent = dentry->d_parent;
+		prefetch(parent);
+		/*
+		 * Exception: Use /proc/self/ rather than /proc/\$/
+		 * for current process.
+		 */
+		name = dentry->d_name.name;
+		name_len = dentry->d_name.len;
+		if (IS_ROOT(parent) &&
+		    parent->d_sb->s_magic == PROC_SUPER_MAGIC &&
+		    !strict_strtoul(name, 10, &pid)) {
+			const pid_t tgid
+				= task_tgid_nr_ns(current,
+						  dentry->d_sb->s_fs_info);
+			if (tgid && (pid_t) pid == tgid) {
+				name = "self";
+				name_len = 4;
+			}
+		}
+		if (prepend(&end, &buflen, name, name_len))
+			goto Elong;
+		if (prepend(&end, &buflen, "/", 1))
+			goto Elong;
+		dentry = parent;
+	}
+	if (*end == '/') {
+		/* hit the slash */
+		buflen++;
+		end++;
+	}
+	if (prepend_name(&end, &buflen, &dentry->d_name))
+		goto Elong;
+ out:
+	spin_unlock(&vfsmount_lock);
+	spin_unlock(&dcache_lock);
+	return end;
+ Elong:
+	end = ERR_PTR(-ENAMETOOLONG);
+	goto out;
+}
+
+/**
  * d_path - return the path of a dentry
  * @path: path to report
  * @buf: buffer to return value in
--- linux-2.6.28-mm1.orig/include/linux/dcache.h
+++ linux-2.6.28-mm1/include/linux/dcache.h
@@ -305,6 +305,7 @@ extern char *dynamic_dname(struct dentry
 extern char *__d_path(const struct path *path, struct path *root, char *, int);
 extern char *d_path(const struct path *, char *, int);
 extern char *dentry_path(struct dentry *, char *, int);
+extern char *d_realpath(struct path *, char *, int);
 
 /* Allocation counts.. */
 

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 04/10] Memory and pathname management functions.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (2 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath() Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 05/10] Common functions for TOMOYO Linux Tetsuo Handa
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda,
	Tetsuo Handa, Toshiharu Harada

[-- Attachment #1: tomoyo-realpath.patch --]
[-- Type: text/plain, Size: 16208 bytes --]

TOMOYO Linux performs pathname based access control.
To remove factors that make pathname based access control difficult
(e.g. symbolic links, "..", "//" etc.), TOMOYO Linux derives realpath
of requested pathname from "struct dentry" and "struct vfsmount".

The maximum length of string data is limited to 4000 including trailing '\0'.
Since TOMOYO Linux uses '\ooo' style representation for non ASCII printable
characters, may be TOMOYO Linux should be able to support 16336 (which means
(NAME_MAX * (PATH_MAX / (NAME_MAX + 1)) * 4 + (PATH_MAX / (NAME_MAX + 1)))
including trailing '\0'), but I think 4000 is enough for practical use.

TOMOYO uses only 0x21 - 0x7E (as printable characters) and 0x20 (as word
delimiter) and 0x0A (as line delimiter).
0x01 - 0x20 and 0x80 - 0xFF is handled in \ooo style representation.
The reason to use \ooo is to guarantee that "%s" won't damage logs.
Userland program can request

 open("/tmp/file granted.\nAccess /tmp/file ", O_WRONLY | O_CREAT, 0600)

and logging such crazy pathname using "Access %s denied.\n" format will cause
"fabrication of logs" like

 Access /tmp/file granted.
 Access /tmp/file denied.

TOMOYO converts such characters to \ooo so that the logs will become

 Access /tmp/file\040granted.\012Access\040/tmp/file denied.

and the administrator can read the logs safely using /bin/cat .
Likewise, a crazy request like

 open("/tmp/\x01\x02\x03\x04\x05\x06\x07\x08\x09", O_WRONLY | O_CREAT, 0600)

will be processed safely by converting to

 Access /tmp/\001\002\003\004\005\006\007\010\011 denied.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 security/tomoyo/realpath.c |  453 +++++++++++++++++++++++++++++++++++++++++++++
 security/tomoyo/realpath.h |   63 ++++++
 2 files changed, 516 insertions(+)

--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/realpath.c
@@ -0,0 +1,453 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mount.h>
+#include "common.h"
+#include "realpath.h"
+
+/**
+ * tomoyo_encode: Convert binary string to ascii string.
+ *
+ * @buffer:  Buffer for ASCII string.
+ * @buflen:  Size of @buffer.
+ * @str:     Binary string.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_encode(char *buffer, int buflen, const char *str)
+{
+	while (1) {
+		const unsigned char c = *(unsigned char *) str++;
+
+		if (tomoyo_is_valid(c)) {
+			if (--buflen <= 0)
+				break;
+			*buffer++ = (char) c;
+			if (c != '\\')
+				continue;
+			if (--buflen <= 0)
+				break;
+			*buffer++ = (char) c;
+			continue;
+		}
+		if (!c) {
+			if (--buflen <= 0)
+				break;
+			*buffer = '\0';
+			return 0;
+		}
+		buflen -= 4;
+		if (buflen <= 0)
+			break;
+		*buffer++ = '\\';
+		*buffer++ = (c >> 6) + '0';
+		*buffer++ = ((c >> 3) & 7) + '0';
+		*buffer++ = (c & 7) + '0';
+	}
+	return -ENOMEM;
+}
+
+/**
+ * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
+ *
+ * @path:        Pointer to "struct path".
+ * @newname:     Pointer to buffer to return value in.
+ * @newname_len: Size of @newname.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters out of 0x20 < c < 0x7F range are converted to
+ * \ooo style octal string.
+ * Character \ is converted to \\ string.
+ */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+			       int newname_len)
+{
+	int error = -ENOMEM;
+	struct dentry *dentry = path->dentry;
+	char *sp;
+
+	if (!dentry || !path->mnt || !newname || newname_len <= 2048)
+		return -EINVAL;
+	if (dentry->d_op && dentry->d_op->d_dname) {
+		/* For "socket:[\$]" and "pipe:[\$]". */
+		static const int offset = 1536;
+		sp = dentry->d_op->d_dname(dentry, newname + offset,
+					   newname_len - offset);
+	} else {
+		path_get(path);
+		sp = d_realpath(path, newname, newname_len);
+		path_put(path);
+	}
+	if (IS_ERR(sp))
+		error = PTR_ERR(sp);
+	else
+		error = tomoyo_encode(newname, sp - newname, sp);
+	if (error)
+		printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n");
+	return error;
+}
+
+/**
+ * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath_from_path(struct path *path)
+{
+	char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer));
+
+	BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
+		     <= TOMOYO_MAX_PATHNAME_LEN - 1);
+	if (!buf)
+		return NULL;
+	if (tomoyo_realpath_from_path2(path, buf,
+				       TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
+		return buf;
+	tomoyo_free(buf);
+	return NULL;
+}
+
+/**
+ * tomoyo_realpath - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath(const char *pathname)
+{
+	struct nameidata nd;
+
+	if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) {
+		char *buf = tomoyo_realpath_from_path(&nd.path);
+		path_put(&nd.path);
+		return buf;
+	}
+	return NULL;
+}
+
+/**
+ * tomoyo_realpath_nofollow - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname)
+{
+	struct nameidata nd;
+
+	if (pathname && path_lookup(pathname, 0, &nd) == 0) {
+		char *buf = tomoyo_realpath_from_path(&nd.path);
+		path_put(&nd.path);
+		return buf;
+	}
+	return NULL;
+}
+
+/* Memory allocated for non-string data. */
+static unsigned int tomoyo_allocated_memory_for_elements;
+/* Quota for holding non-string data. */
+static unsigned int tomoyo_quota_for_elements;
+
+/**
+ * tomoyo_alloc_element - Allocate permanent memory for structures.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ *
+ * Memory has to be zeroed.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size)
+{
+	static char *buf;
+	static DEFINE_MUTEX(lock);
+	static unsigned int buf_used_len = PATH_MAX;
+	char *ptr = NULL;
+	/*Assumes sizeof(void *) >= sizeof(long) is true. */
+	const unsigned int word_aligned_size
+		= roundup(size, max(sizeof(void *), sizeof(long)));
+	if (word_aligned_size > PATH_MAX)
+		return NULL;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	if (buf_used_len + word_aligned_size > PATH_MAX) {
+		if (!tomoyo_quota_for_elements ||
+		    tomoyo_allocated_memory_for_elements
+		    + PATH_MAX <= tomoyo_quota_for_elements)
+			ptr = kzalloc(PATH_MAX, GFP_KERNEL);
+		if (!ptr) {
+			printk(KERN_WARNING "ERROR: Out of memory "
+			       "for tomoyo_alloc_element().\n");
+			if (!tomoyo_policy_loaded)
+				panic("MAC Initialization failed.\n");
+		} else {
+			buf = ptr;
+			tomoyo_allocated_memory_for_elements += PATH_MAX;
+			buf_used_len = word_aligned_size;
+			ptr = buf;
+		}
+	} else if (word_aligned_size) {
+		int i;
+		ptr = buf + buf_used_len;
+		buf_used_len += word_aligned_size;
+		for (i = 0; i < word_aligned_size; i++) {
+			if (!ptr[i])
+				continue;
+			printk(KERN_ERR "WARNING: Reserved memory was tainted! "
+			       "The system might go wrong.\n");
+			ptr[i] = '\0';
+		}
+	}
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return ptr;
+}
+
+/* Memory allocated for string data in bytes. */
+static unsigned int tomoyo_allocated_memory_for_savename;
+/* Quota for holding string data in bytes. */
+static unsigned int tomoyo_quota_for_savename;
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_MAX_HASH 256
+
+/* Structure for string data. */
+struct tomoyo_name_entry {
+	struct list1_head list;
+	struct tomoyo_path_info entry;
+};
+
+/* Structure for available memory region. */
+struct tomoyo_free_memory_block_list {
+	struct list_head list;
+	char *ptr;             /* Pointer to a free area. */
+	int len;               /* Length of the area.     */
+};
+
+/*
+ * The list for "struct tomoyo_name_entry".
+ *
+ * This list is updated only inside tomoyo_save_name(), thus
+ * no global mutex exists.
+ */
+static struct list1_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/**
+ * tomoyo_save_name - Allocate permanent memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ *
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name)
+{
+	static LIST_HEAD(fmb_list);
+	static DEFINE_MUTEX(lock);
+	struct tomoyo_name_entry *ptr;
+	unsigned int hash;
+	/* fmb contains available size in bytes.
+	   fmb is removed from the fmb_list when fmb->len becomes 0. */
+	struct tomoyo_free_memory_block_list *fmb;
+	int len;
+	char *cp;
+
+	if (!name)
+		return NULL;
+	len = strlen(name) + 1;
+	if (len > TOMOYO_MAX_PATHNAME_LEN) {
+		printk(KERN_WARNING "ERROR: Name too long "
+		       "for tomoyo_save_name().\n");
+		return NULL;
+	}
+	hash = full_name_hash((const unsigned char *) name, len - 1);
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
+			     list) {
+		if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
+			goto out;
+	}
+	list_for_each_entry(fmb, &fmb_list, list) {
+		if (len <= fmb->len)
+			goto ready;
+	}
+	if (!tomoyo_quota_for_savename ||
+	    tomoyo_allocated_memory_for_savename + PATH_MAX
+	    <= tomoyo_quota_for_savename)
+		cp = kzalloc(PATH_MAX, GFP_KERNEL);
+	else
+		cp = NULL;
+	fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
+	if (!cp || !fmb) {
+		kfree(cp);
+		kfree(fmb);
+		printk(KERN_WARNING "ERROR: Out of memory "
+		       "for tomoyo_save_name().\n");
+		if (!tomoyo_policy_loaded)
+			panic("MAC Initialization failed.\n");
+		ptr = NULL;
+		goto out;
+	}
+	tomoyo_allocated_memory_for_savename += PATH_MAX;
+	list_add(&fmb->list, &fmb_list);
+	fmb->ptr = cp;
+	fmb->len = PATH_MAX;
+ ready:
+	ptr = tomoyo_alloc_element(sizeof(*ptr));
+	if (!ptr)
+		goto out;
+	ptr->entry.name = fmb->ptr;
+	memmove(fmb->ptr, name, len);
+	tomoyo_fill_path_info(&ptr->entry);
+	fmb->ptr += len;
+	fmb->len -= len;
+	list1_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
+	if (fmb->len == 0) {
+		list_del(&fmb->list);
+		kfree(fmb);
+	}
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * tomoyo_realpath_init - Initialize realpath related code.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_realpath_init(void)
+{
+	int i;
+
+	BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
+	for (i = 0; i < TOMOYO_MAX_HASH; i++)
+		INIT_LIST1_HEAD(&tomoyo_name_list[i]);
+	INIT_LIST1_HEAD(&tomoyo_kernel_domain.acl_info_list);
+	tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
+	list1_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
+		panic("Can't register tomoyo_kernel_domain");
+	return 0;
+}
+
+security_initcall(tomoyo_realpath_init);
+
+/* Memory allocated for temporary purpose. */
+static atomic_t tomoyo_dynamic_memory_size;
+
+/**
+ * tomoyo_alloc - Allocate memory for temporary purpose.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ */
+void *tomoyo_alloc(const size_t size)
+{
+	void *p = kzalloc(size, GFP_KERNEL);
+	if (p)
+		atomic_add(ksize(p), &tomoyo_dynamic_memory_size);
+	return p;
+}
+
+/**
+ * tomoyo_free - Release memory allocated by tomoyo_alloc().
+ *
+ * @p: Pointer returned by tomoyo_alloc(). May be NULL.
+ *
+ * Returns nothing.
+ */
+void tomoyo_free(const void *p)
+{
+	if (p) {
+		atomic_sub(ksize(p), &tomoyo_dynamic_memory_size);
+		kfree(p);
+	}
+}
+
+/**
+ * tomoyo_read_memory_counter - Check for memory usage in bytes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns memory usage.
+ */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		const unsigned int shared
+			= tomoyo_allocated_memory_for_savename;
+		const unsigned int private
+			= tomoyo_allocated_memory_for_elements;
+		const unsigned int dynamic
+			= atomic_read(&tomoyo_dynamic_memory_size);
+		char buffer[64];
+
+		memset(buffer, 0, sizeof(buffer));
+		if (tomoyo_quota_for_savename)
+			snprintf(buffer, sizeof(buffer) - 1,
+				 "   (Quota: %10u)",
+				 tomoyo_quota_for_savename);
+		else
+			buffer[0] = '\0';
+		tomoyo_io_printf(head, "Shared:  %10u%s\n", shared, buffer);
+		if (tomoyo_quota_for_elements)
+			snprintf(buffer, sizeof(buffer) - 1,
+				 "   (Quota: %10u)",
+				 tomoyo_quota_for_elements);
+		else
+			buffer[0] = '\0';
+		tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer);
+		tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic);
+		tomoyo_io_printf(head, "Total:   %10u\n",
+				 shared + private + dynamic);
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_write_memory_quota - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	unsigned int size;
+
+	if (sscanf(data, "Shared: %u", &size) == 1)
+		tomoyo_quota_for_savename = size;
+	else if (sscanf(data, "Private: %u", &size) == 1)
+		tomoyo_quota_for_elements = size;
+	return 0;
+}
--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/realpath.h
@@ -0,0 +1,63 @@
+/*
+ * security/tomoyo/realpath.h
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_REALPATH_H
+#define _SECURITY_TOMOYO_REALPATH_H
+
+struct path;
+struct tomoyo_path_info;
+struct tomoyo_io_buffer;
+
+/* Convert binary string to ascii string. */
+int tomoyo_encode(char *buffer, int buflen, const char *str);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+			       int newname_len);
+
+/*
+ * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath(const char *pathname);
+/*
+ * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/* Same with tomoyo_realpath() except that the pathname is already solved. */
+char *tomoyo_realpath_from_path(struct path *path);
+
+/*
+ * Allocate memory for ACL entry.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name);
+
+/* Allocate memory for temporary use (e.g. permission checks). */
+void *tomoyo_alloc(const size_t size);
+
+/* Free memory allocated by tomoyo_alloc(). */
+void tomoyo_free(const void *p);
+
+/* Check for memory usage. */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 05/10] Common functions for TOMOYO Linux.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (3 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 04/10] Memory and pathname management functions Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 06/10] File operation restriction part Tetsuo Handa
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda,
	Tetsuo Handa, Toshiharu Harada

[-- Attachment #1: tomoyo-common.patch --]
[-- Type: text/plain, Size: 82789 bytes --]

This file contains common functions (e.g. policy I/O, pattern matching).

----- About pattern matching -----

In TOMOYO, all wildcard characters are in the form of '\' + one character.
Many programs interpret a character with a backslash as literal character, but
TOMOYO interprets all characters without a backslash as literal characters.

Since the characters which are interpreted as wildcard are program dependent
(e.g. For shells, '*' and '?' are wildcards but '.' isn't. For sed, '.' is
a wildcard). Those who worry about some characters being interpreted as
wildcard characters will add a backslash to every characters
(e.g. \/\t\m\p\/\* ) without knowing whether '/' 't' 'm' 'p' '*' are wildcard
characters or not.
Not only this approach requires the interpreter to remove unneeded backslashes
but also this approach keeps introduction of new wildcard characters away.
For example, until yesterday, 'p' was a literal character and users had been
allowed to add a backslash before 'p'. But, from today, '\p' is now a wildcard
character and users are no longer allowed to add a backslash before 'p' if the
users want to represent literal 'p'.
If the interpreter forces users not to add a backslash to represent literal
character, users must know all wildcard characters beforehand.

On the contrary, TOMOYO's approach allows introduction of new wildcard
characters as needed, for users won't add a backslash to represent literal
characters. If the interpreter forces users to add a backslash to represent
wildcard character, users needn't to know all wildcard characters beforehand.
Users can learn the meaning of new wildcard characters on first encounter.


You would go crazy with functions that handle string data.
But these functions are needed to stay inside the kernel for validating,
hashing and comparing string data.

To speeds up string comparison, TOMOYO hashes strings using "depth" (number of
'/' characters). This is calculated by tomoyo_path_depth() and used by
tomoyo_path_matches_pattern().

To be able to use strncmp() where possible, TOMOYO remembers initial length of
strings without wildcard characters. This is calculated by
tomoyo_const_part_length().

To be able to use strcmp() where possible, TOMOYO remembers whether strings
contains wildcard character or not.

----- About policy interface -----

TOMOYO Linux creates the following files on securityfs (normally 
mounted on /sys/kernel/security) as interfaces between kernel and 
userspace. These files are for TOMOYO Linux management tools *only*, 
not for general programs.

  * profile
  * exception_policy
  * domain_policy
  * manager
  * meminfo
  * self_domain
  * version
  * .domain_status
  * .process_status

** /sys/kernel/security/tomoyo/profile **

This file is used to read or write profiles.

"profile" means a running mode of process. A profile lists up 
functions and their modes in "$number-$variable=$value" format. The 
$number is profile number between 0 and 255. Each domain is assigned 
one profile. To assign profile to domains, use "ccs-setprofile" or 
"ccs-editpolicy" or "ccs-loadpolicy" commands.

(Example)
[root@tomoyo]# cat /sys/kernel/security/tomoyo/profile
0-COMMENT=-----Disabled Mode-----
0-MAC_FOR_FILE=disabled
0-MAX_ACCEPT_ENTRY=2048
0-TOMOYO_VERBOSE=disabled
1-COMMENT=-----Learning Mode-----
1-MAC_FOR_FILE=learning
1-MAX_ACCEPT_ENTRY=2048
1-TOMOYO_VERBOSE=disabled
2-COMMENT=-----Permissive Mode-----
2-MAC_FOR_FILE=permissive
2-MAX_ACCEPT_ENTRY=2048
2-TOMOYO_VERBOSE=enabled
3-COMMENT=-----Enforcing Mode-----
3-MAC_FOR_FILE=enforcing
3-MAX_ACCEPT_ENTRY=2048
3-TOMOYO_VERBOSE=enabled

- MAC_FOR_FILE:
Specifies access control level regarding file access requests.
- MAX_ACCEPT_ENTRY:
Limits the max number of ACL entries that are automatically appended 
during learning mode. Default is 2048.
- TOMOYO_VERBOSE:
Specifies whether to print domain policy violation messages or not.

** /sys/kernel/security/tomoyo/manager **

This file is used to read or append the list of programs or domains 
that can write to /sys/kernel/security/tomoyo interface. By default, 
only processes with both UID = 0 and EUID = 0 can modify policy via 
/sys/kernel/security/tomoyo interface. You can use keyword 
"manage_by_non_root" to allow policy modification by non root user.

(Example)
[root@tomoyo]# cat /sys/kernel/security/tomoyo/manager
/usr/lib/ccs/loadpolicy
/usr/lib/ccs/editpolicy
/usr/lib/ccs/setlevel
/usr/lib/ccs/setprofile
/usr/lib/ccs/ld-watch
/usr/lib/ccs/ccs-queryd

** /sys/kernel/security/tomoyo/exception_policy **

This file is used to read and write system global settings. Each line 
has a directive and operand pair. Directives are listed below.

- initialize_domain:
To initialize domain transition when specific program is executed, 
use initialize_domain directive.
  * initialize_domain "program" from "domain"
  * initialize_domain "program" from "the last program part of domain"
  * initialize_domain "program"
If the part "from" and after is not given, the entry is applied to 
all domain. If the "domain" doesn't start with "<kernel>", the entry 
is applied to all domain whose domainname ends with "the last program 
part of domain".
This directive is intended to aggregate domain transitions for daemon 
program and program that are invoked by the kernel on demand, by 
transiting to different domain.

- keep_domain
To prevent domain transition when program is executed from specific 
domain, use keep_domain directive.
  * keep_domain "program" from "domain"
  * keep_domain "program" from "the last program part of domain"
  * keep_domain "domain"
  * keep_domain "the last program part of domain" 
If the part "from" and before is not given, this entry is applied to 
all program. If the "domain" doesn't start with "<kernel>", the entry 
is applied to all domain whose domainname ends with "the last program 
part of domain".
This directive is intended to reduce total number of domains and 
memory usage by suppressing unneeded domain transitions.
To declare domain keepers, use keep_domain directive followed by 
domain definition.
Any process that belongs to any domain declared with this directive, 
the process stays at the same domain unless any program registered 
with initialize_domain directive is executed.

In order to control domain transition in detail, you can use 
no_keep_domain/no_initialize_domain keywrods.

- alias: 
To allow executing programs using the name of symbolic links, use 
alias keyword followed by dereferenced pathname and reference 
pathname. For example, /sbin/pidof is a symbolic link to 
/sbin/killall5 . In normal case, if /sbin/pidof is executed, the 
domain is defined as if /sbin/killall5 is executed. By specifying 
"alias /sbin/killall5 /sbin/pidof", you can run /sbin/pidof in the 
domain for /sbin/pidof .
(Example)
alias /sbin/killall5 /sbin/pidof

- allow_read:
To grant unconditionally readable permissions, use allow_read keyword 
followed by canonicalized file. This keyword is intended to reduce 
size of domain policy by granting read access to library files such 
as GLIBC and locale files. Exception is, if ignore_global_allow_read 
keyword is given to a domain, entries specified by this keyword are 
ignored.
(Example)
allow_read /lib/libc-2.5.so

- file_pattern:
To declare pathname pattern, use file_pattern keyword followed by 
pathname pattern. The pathname pattern must be a canonicalized 
Pathname. This keyword is not applicable to neither granting execute 
permissions nor domain definitions.
For example, canonicalized pathname that contains a process ID 
(i.e. /proc/PID/ files) needs to be grouped in order to make access 
control work well.
(Example)
file_pattern /proc/\$/cmdline

- path_group
To declare pathname group, use path_group keyword followed by name of 
the group and pathname pattern. For example, if you want to group all 
files under home directory, you can define
   path_group HOME-DIR-FILE /home/\*/\*
   path_group HOME-DIR-FILE /home/\*/\*/\*
   path_group HOME-DIR-FILE /home/\*/\*/\*/\*
in the exception policy and use like
   allow_read @HOME-DIR-FILE
to grant file access permission.

- deny_rewrite:
To deny overwriting already written contents of file (such as log 
files) by default, use deny_rewrite keyword followed by pathname 
pattern. Files whose pathname match the patterns are not permitted to 
open for writing without append mode or truncate unless the pathnames 
are explicitly granted using allow_rewrite keyword in domain policy.
(Example)
deny_rewrite /var/log/\*

- aggregator
To deal multiple programs as a single program, use aggregator keyword 
followed by name of original program and aggregated program. This 
keyword is intended to aggregate similar programs.
For example, /usr/bin/tac and /bin/cat are similar. By specifying 
"aggregator /usr/bin/tac /bin/cat", you can run /usr/bin/tac in the 
domain for /bin/cat .
For example, /usr/sbin/logrotate for Fedora Core 3 generates programs 
like /tmp/logrotate.\?\?\?\?\?\? and run them, but TOMOYO Linux 
doesn't allow using patterns for granting execute permission and 
defining domains. By specifying 
"aggregator /tmp/logrotate.\?\?\?\?\?\? /tmp/logrotate.tmp", you can 
run /tmp/logrotate.\?\?\?\?\?\? as if /tmp/logrotate.tmp is running.

** /sys/kernel/security/tomoyo/domain_policy **

This file contains definition of all domains and permissions that are 
granted to each domain.

Lines from the next line to a domain definition ( any lines starting 
with "<kernel>") to the previous line to the next domain definitions 
are interpreted as access permissions for that domain.

** /sys/kernel/security/tomoyo/meminfo **

This file is to show the total RAM used to keep policy in the kernel 
by TOMOYO Linux in bytes.
(Example)
[root@tomoyo]# cat /sys/kernel/security/tomoyo/meminfo
Shared:       61440
Private:      69632
Dynamic:        768
Total:       131840

You can set memory quota by writing to this file.
(Example)
[root@tomoyo]# echo Shared: 2097152 > /sys/kernel/security/tomoyo/meminfo
[root@tomoyo]# echo Private: 2097152 > /sys/kernel/security/tomoyo/meminfo

** /sys/kernel/security/tomoyo/self_domain **

This file is to show the name of domain the caller process belongs to.
(Example)
[root@etch]# cat /sys/kernel/security/tomoyo/self_domain
<kernel> /usr/sbin/sshd /bin/zsh /bin/cat

** /sys/kernel/security/tomoyo/version **

This file is used for getting TOMOYO Linux's version.
(Example)
[root@etch]# cat /sys/kernel/security/tomoyo/version
2.2.0-pre

** /sys/kernel/security/tomoyo/.domain_status **

This is a view (of a DBMS) that contains only profile number and 
domainnames of domain so that "ccs-setprofile" command can do 
line-oriented processing easily.

** /sys/kernel/security/tomoyo/.process_status **

This file is used by "ccs-ccstree" command to show "list of processes 
currently running" and "domains which each process belongs to" and 
"profile number which the domain is currently assigned" like "pstree" 
command. This file is writable by programs that aren't registered as 
policy manager.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 security/tomoyo/common.c | 2160 +++++++++++++++++++++++++++++++++++++++++++++++
 security/tomoyo/common.h |  337 +++++++
 2 files changed, 2497 insertions(+)

--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/common.c
@@ -0,0 +1,2160 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/hardirq.h>
+#include "realpath.h"
+#include "common.h"
+#include "tomoyo.h"
+
+/* Has loading policy done? */
+bool tomoyo_policy_loaded;
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode_4[4] = {
+	"disabled", "learning", "permissive", "enforcing"
+};
+/* String table for functionality that takes 2 modes. */
+static const char *tomoyo_mode_2[4] = {
+	"disabled", "enabled", "enabled", "enabled"
+};
+
+/* Table for profile. */
+static struct {
+	const char *keyword;
+	unsigned int current_value;
+	const unsigned int max_value;
+} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
+	[TOMOYO_MAC_FOR_FILE]     = { "MAC_FOR_FILE",        0,       3 },
+	[TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX },
+	[TOMOYO_VERBOSE]          = { "TOMOYO_VERBOSE",      1,       1 },
+};
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile {
+	unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
+	const struct tomoyo_path_info *comment;
+} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/* Utility functions. */
+
+/* Open operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_open_control(const u8 type, struct file *file);
+/* Close /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_close_control(struct file *file);
+/* Read operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+			       const int buffer_len);
+/* Write operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+				const int buffer_len);
+
+/**
+ * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ *
+ * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF.
+ * This function verifies that \ooo is in valid range.
+ */
+static inline bool tomoyo_is_byte_range(const char *str)
+{
+	return *str >= '0' && *str++ <= '3' &&
+		*str >= '0' && *str++ <= '7' &&
+		*str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_is_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_is_alphabet_char(const char c)
+{
+	return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+	return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src:  Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+static bool tomoyo_str_starts(char **src, const char *find)
+{
+	const int len = strlen(find);
+	char *tmp = *src;
+
+	if (strncmp(tmp, find, len))
+		return false;
+	tmp += len;
+	*src = tmp;
+	return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_normalize_line(unsigned char *buffer)
+{
+	unsigned char *sp = buffer;
+	unsigned char *dp = buffer;
+	bool first = true;
+
+	while (tomoyo_is_invalid(*sp))
+		sp++;
+	while (*sp) {
+		if (!first)
+			*dp++ = ' ';
+		first = false;
+		while (tomoyo_is_valid(*sp))
+			*dp++ = *sp++;
+		while (tomoyo_is_invalid(*sp))
+			sp++;
+	}
+	*dp = '\0';
+}
+
+/**
+ * tomoyo_is_correct_path - Validate a pathname.
+ * @filename:     The pathname to check.
+ * @start_type:   Should the pathname start with '/'?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @pattern_type: Can the pathname contain a wildcard?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @end_type:     Should the pathname end with '/'?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @function:     The name of function calling me.
+ *
+ * Check whether the given filename follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+			    const s8 pattern_type, const s8 end_type,
+			    const char *function)
+{
+	bool contains_pattern = false;
+	unsigned char c;
+	unsigned char d;
+	unsigned char e;
+	const char *original_filename = filename;
+
+	if (!filename)
+		goto out;
+	c = *filename;
+	if (start_type == 1) { /* Must start with '/' */
+		if (c != '/')
+			goto out;
+	} else if (start_type == -1) { /* Must not start with '/' */
+		if (c == '/')
+			goto out;
+	}
+	if (c)
+		c = *(filename + strlen(filename) - 1);
+	if (end_type == 1) { /* Must end with '/' */
+		if (c != '/')
+			goto out;
+	} else if (end_type == -1) { /* Must not end with '/' */
+		if (c == '/')
+			goto out;
+	}
+	while ((c = *filename++) != '\0') {
+		if (c == '\\') {
+			switch ((c = *filename++)) {
+			case '\\':  /* "\\" */
+				continue;
+			case '$':   /* "\$" */
+			case '+':   /* "\+" */
+			case '?':   /* "\?" */
+			case '*':   /* "\*" */
+			case '@':   /* "\@" */
+			case 'x':   /* "\x" */
+			case 'X':   /* "\X" */
+			case 'a':   /* "\a" */
+			case 'A':   /* "\A" */
+			case '-':   /* "\-" */
+				if (pattern_type == -1)
+					break; /* Must not contain pattern */
+				contains_pattern = true;
+				continue;
+			case '0':   /* "\ooo" */
+			case '1':
+			case '2':
+			case '3':
+				d = *filename++;
+				if (d < '0' || d > '7')
+					break;
+				e = *filename++;
+				if (e < '0' || e > '7')
+					break;
+				c = tomoyo_make_byte(c, d, e);
+				if (tomoyo_is_invalid(c))
+					continue; /* pattern is not \000 */
+			}
+			goto out;
+		} else if (tomoyo_is_invalid(c)) {
+			goto out;
+		}
+	}
+	if (pattern_type == 1) { /* Must contain pattern */
+		if (!contains_pattern)
+			goto out;
+	}
+	return true;
+ out:
+	printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
+	       original_filename);
+	return false;
+}
+
+/**
+ * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
+ * @domainname:   The domainname to check.
+ * @function:     The name of function calling me.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+			      const char *function)
+{
+	unsigned char c;
+	unsigned char d;
+	unsigned char e;
+	const char *org_domainname = domainname;
+
+	if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
+				   TOMOYO_ROOT_NAME_LEN))
+		goto out;
+	domainname += TOMOYO_ROOT_NAME_LEN;
+	if (!*domainname)
+		return true;
+	do {
+		if (*domainname++ != ' ')
+			goto out;
+		if (*domainname++ != '/')
+			goto out;
+		while ((c = *domainname) != '\0' && c != ' ') {
+			domainname++;
+			if (c == '\\') {
+				c = *domainname++;
+				switch ((c)) {
+				case '\\':  /* "\\" */
+					continue;
+				case '0':   /* "\ooo" */
+				case '1':
+				case '2':
+				case '3':
+					d = *domainname++;
+					if (d < '0' || d > '7')
+						break;
+					e = *domainname++;
+					if (e < '0' || e > '7')
+						break;
+					c = tomoyo_make_byte(c, d, e);
+					if (tomoyo_is_invalid(c))
+						/* pattern is not \000 */
+						continue;
+				}
+				goto out;
+			} else if (tomoyo_is_invalid(c)) {
+				goto out;
+			}
+		}
+	} while (*domainname);
+	return true;
+ out:
+	printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
+	       org_domainname);
+	return false;
+}
+
+/**
+ * tomoyo_is_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_is_domain_def(const unsigned char *buffer)
+{
+	return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_path_info name;
+
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (!domain->is_deleted &&
+		    !tomoyo_pathcmp(&name, domain->domainname))
+			return domain;
+	}
+	return NULL;
+}
+
+/**
+ * tomoyo_path_depth - Evaluate the number of '/' in a string.
+ *
+ * @pathname: The string to evaluate.
+ *
+ * Returns path depth of the string.
+ *
+ * I score 2 for each of the '/' in the @pathname
+ * and score 1 if the @pathname ends with '/'.
+ */
+static int tomoyo_path_depth(const char *pathname)
+{
+	int i = 0;
+
+	if (pathname) {
+		const char *ep = pathname + strlen(pathname);
+		if (pathname < ep--) {
+			if (*ep != '/')
+				i++;
+			while (pathname <= ep)
+				if (*ep-- == '/')
+					i += 2;
+		}
+	}
+	return i;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+	char c;
+	int len = 0;
+
+	if (!filename)
+		return 0;
+	while ((c = *filename++) != '\0') {
+		if (c != '\\') {
+			len++;
+			continue;
+		}
+		c = *filename++;
+		switch (c) {
+		case '\\':  /* "\\" */
+			len += 2;
+			continue;
+		case '0':   /* "\ooo" */
+		case '1':
+		case '2':
+		case '3':
+			c = *filename++;
+			if (c < '0' || c > '7')
+				break;
+			c = *filename++;
+			if (c < '0' || c > '7')
+				break;
+			len += 4;
+			continue;
+		}
+		break;
+	}
+	return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+	const char *name = ptr->name;
+	const int len = strlen(name);
+
+	ptr->total_len = len;
+	ptr->const_len = tomoyo_const_part_length(name);
+	ptr->is_dir = len && (name[len - 1] == '/');
+	ptr->is_patterned = (ptr->const_len < len);
+	ptr->hash = full_name_hash(name, len);
+	ptr->depth = tomoyo_path_depth(name);
+}
+
+/**
+ * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * and "\-" pattern.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern2(const char *filename,
+					    const char *filename_end,
+					    const char *pattern,
+					    const char *pattern_end)
+{
+	while (filename < filename_end && pattern < pattern_end) {
+		char c;
+		if (*pattern != '\\') {
+			if (*filename++ != *pattern++)
+				return false;
+			continue;
+		}
+		c = *filename;
+		pattern++;
+		switch (*pattern) {
+			int i;
+			int j;
+		case '?':
+			if (c == '/') {
+				return false;
+			} else if (c == '\\') {
+				if (filename[1] == '\\')
+					filename++;
+				else if (tomoyo_is_byte_range(filename + 1))
+					filename += 3;
+				else
+					return false;
+			}
+			break;
+		case '\\':
+			if (c != '\\')
+				return false;
+			if (*++filename != '\\')
+				return false;
+			break;
+		case '+':
+			if (!isdigit(c))
+				return false;
+			break;
+		case 'x':
+			if (!isxdigit(c))
+				return false;
+			break;
+		case 'a':
+			if (!tomoyo_is_alphabet_char(c))
+				return false;
+			break;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+			if (c == '\\' && tomoyo_is_byte_range(filename + 1)
+			    && strncmp(filename + 1, pattern, 3) == 0) {
+				filename += 3;
+				pattern += 2;
+				break;
+			}
+			return false; /* Not matched. */
+		case '*':
+		case '@':
+			for (i = 0; i <= filename_end - filename; i++) {
+				if (tomoyo_file_matches_to_pattern2(
+						    filename + i, filename_end,
+						    pattern + 1, pattern_end))
+					return true;
+				c = filename[i];
+				if (c == '.' && *pattern == '@')
+					break;
+				if (c != '\\')
+					continue;
+				if (filename[i + 1] == '\\')
+					i++;
+				else if (tomoyo_is_byte_range(filename + i + 1))
+					i += 3;
+				else
+					break; /* Bad pattern. */
+			}
+			return false; /* Not matched. */
+		default:
+			j = 0;
+			c = *pattern;
+			if (c == '$') {
+				while (isdigit(filename[j]))
+					j++;
+			} else if (c == 'X') {
+				while (isxdigit(filename[j]))
+					j++;
+			} else if (c == 'A') {
+				while (tomoyo_is_alphabet_char(filename[j]))
+					j++;
+			}
+			for (i = 1; i <= j; i++) {
+				if (tomoyo_file_matches_to_pattern2(
+						    filename + i, filename_end,
+						    pattern + 1, pattern_end))
+					return true;
+			}
+			return false; /* Not matched or bad pattern. */
+		}
+		filename++;
+		pattern++;
+	}
+	while (*pattern == '\\' &&
+	       (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+		pattern += 2;
+	return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern(const char *filename,
+					   const char *filename_end,
+					   const char *pattern,
+					   const char *pattern_end)
+{
+	const char *pattern_start = pattern;
+	bool first = true;
+	bool result;
+
+	while (pattern < pattern_end - 1) {
+		/* Split at "\-" pattern. */
+		if (*pattern++ != '\\' || *pattern++ != '-')
+			continue;
+		result = tomoyo_file_matches_to_pattern2(filename,
+							 filename_end,
+							 pattern_start,
+							 pattern - 2);
+		if (first)
+			result = !result;
+		if (result)
+			return false;
+		first = false;
+		pattern_start = pattern;
+	}
+	result = tomoyo_file_matches_to_pattern2(filename, filename_end,
+						 pattern_start, pattern_end);
+	return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ * @filename: The filename to check.
+ * @pattern:  The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ *   \\     \ itself.
+ *   \ooo   Octal representation of a byte.
+ *   \*     More than or equals to 0 character other than '/'.
+ *   \@     More than or equals to 0 character other than '/' or '.'.
+ *   \?     1 byte character other than '/'.
+ *   \$     More than or equals to 1 decimal digit.
+ *   \+     1 decimal digit.
+ *   \X     More than or equals to 1 hexadecimal digit.
+ *   \x     1 hexadecimal digit.
+ *   \A     More than or equals to 1 alphabet character.
+ *   \a     1 alphabet character.
+ *   \-     Subtraction operator.
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+				 const struct tomoyo_path_info *pattern)
+{
+	/*
+	  if (!filename || !pattern)
+	  return false;
+	*/
+	const char *f = filename->name;
+	const char *p = pattern->name;
+	const int len = pattern->const_len;
+
+	/* If @pattern doesn't contain pattern, I can use strcmp(). */
+	if (!pattern->is_patterned)
+		return !tomoyo_pathcmp(filename, pattern);
+	/* Dont compare if the number of '/' differs. */
+	if (filename->depth != pattern->depth)
+		return false;
+	/* Compare the initial length without patterns. */
+	if (strncmp(f, p, len))
+		return false;
+	f += len;
+	p += len;
+	/* Main loop. Compare each directory component. */
+	while (*f && *p) {
+		const char *f_delimiter = strchr(f, '/');
+		const char *p_delimiter = strchr(p, '/');
+		if (!f_delimiter)
+			f_delimiter = f + strlen(f);
+		if (!p_delimiter)
+			p_delimiter = p + strlen(p);
+		if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
+						    p, p_delimiter))
+			return false;
+		f = f_delimiter;
+		if (*f)
+			f++;
+		p = p_delimiter;
+		if (*p)
+			p++;
+	}
+	/* Ignore trailing "\*" and "\@" in @pattern. */
+	while (*p == '\\' &&
+	       (*(p + 1) == '*' || *(p + 1) == '@'))
+		p += 2;
+	return !*f && !*p;
+}
+
+/**
+ * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt:  The printf()'s format string, followed by parameters.
+ *
+ * Returns true if output was written, false otherwise.
+ *
+ * The snprintf() will truncate, but tomoyo_io_printf() won't.
+ */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+	va_list args;
+	int len;
+	int pos = head->read_avail;
+	int size = head->readbuf_size - pos;
+
+	if (size <= 0)
+		return false;
+	va_start(args, fmt);
+	len = vsnprintf(head->read_buf + pos, size, fmt, args);
+	va_end(args);
+	if (pos + len >= head->readbuf_size)
+		return false;
+	head->read_avail += len;
+	return true;
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static const char *tomoyo_get_exe(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	const char *cp = NULL;
+
+	if (!mm)
+		return NULL;
+	down_read(&mm->mmap_sem);
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+			cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+			break;
+		}
+	}
+	up_read(&mm->mmap_sem);
+	return cp;
+}
+
+/**
+ * tomoyo_get_msg - Get warning message.
+ *
+ * @is_enforce: Is it enforcing mode?
+ *
+ * Returns "ERROR" or "WARNING".
+ */
+const char *tomoyo_get_msg(const bool is_enforce)
+{
+	if (is_enforce)
+		return "ERROR";
+	else
+		return "WARNING";
+}
+
+/**
+ * tomoyo_check_flags - Check mode for specified functionality.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @index:  The functionality to check mode.
+ *
+ * TOMOYO checks only process context.
+ * This code disables TOMOYO's enforcement in case the function is called from
+ * interrupt context.
+ */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+				const u8 index)
+{
+	const u8 profile = domain->profile;
+
+	if (WARN_ON(in_interrupt()))
+		return 0;
+	return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX
+#if TOMOYO_MAX_PROFILES != 256
+		&& profile < TOMOYO_MAX_PROFILES
+#endif
+		&& tomoyo_profile_ptr[profile] ?
+		tomoyo_profile_ptr[profile]->value[index] : 0;
+}
+
+/**
+ * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if domain policy violation warning should be printed to
+ * console.
+ */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
+{
+	return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0;
+}
+
+/**
+ * tomoyo_domain_quota_is_ok - Check for domain's quota.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
+{
+	unsigned int count = 0;
+	struct tomoyo_acl_info *ptr;
+
+	if (!domain)
+		return true;
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (ptr->type & TOMOYO_ACL_DELETED)
+			continue;
+		switch (tomoyo_acl_type2(ptr)) {
+			struct tomoyo_single_path_acl_record *acl1;
+			struct tomoyo_double_path_acl_record *acl2;
+			u16 perm;
+		case TOMOYO_TYPE_SINGLE_PATH_ACL:
+			acl1 = container_of(ptr,
+				    struct tomoyo_single_path_acl_record,
+					    head);
+			perm = acl1->perm;
+			if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
+				count++;
+			if (perm &
+			    ((1 << TOMOYO_TYPE_READ_ACL) |
+			     (1 << TOMOYO_TYPE_WRITE_ACL)))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
+				count++;
+			break;
+		case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+			acl2 = container_of(ptr,
+				    struct tomoyo_double_path_acl_record,
+					    head);
+			perm = acl2->perm;
+			if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
+				count++;
+			break;
+		}
+	}
+	if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
+		return true;
+	if (!domain->quota_warned) {
+		domain->quota_warned = true;
+		printk(KERN_WARNING "TOMOYO-WARNING: "
+		       "Domain '%s' has so many ACLs to hold. "
+		       "Stopped learning mode.\n", domain->domainname->name);
+	}
+	return false;
+}
+
+/**
+ * tomoyo_find_or_assign_new_profile - Create a new profile.
+ *
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
+ */
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
+								int profile)
+{
+	static DEFINE_MUTEX(lock);
+	struct tomoyo_profile *ptr = NULL;
+	int i;
+
+	if (profile >= TOMOYO_MAX_PROFILES)
+		return NULL;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	ptr = tomoyo_profile_ptr[profile];
+	if (ptr)
+		goto ok;
+	ptr = tomoyo_alloc_element(sizeof(*ptr));
+	if (!ptr)
+		goto ok;
+	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+		ptr->value[i] = tomoyo_control_array[i].current_value;
+	mb(); /* Avoid out-of-order execution. */
+	tomoyo_profile_ptr[profile] = ptr;
+ ok:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write to profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	unsigned int i;
+	unsigned int value;
+	char *cp;
+	struct tomoyo_profile *profile;
+	unsigned long num;
+
+	cp = strchr(data, '-');
+	if (cp)
+		*cp = '\0';
+	if (strict_strtoul(data, 10, &num))
+		return -EINVAL;
+	if (cp)
+		data = cp + 1;
+	profile = tomoyo_find_or_assign_new_profile(num);
+	if (!profile)
+		return -EINVAL;
+	cp = strchr(data, '=');
+	if (!cp)
+		return -EINVAL;
+	*cp = '\0';
+	if (!strcmp(data, "COMMENT")) {
+		profile->comment = tomoyo_save_name(cp + 1);
+		return 0;
+	}
+	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
+		if (strcmp(data, tomoyo_control_array[i].keyword))
+			continue;
+		if (sscanf(cp + 1, "%u", &value) != 1) {
+			int j;
+			const char **modes;
+			switch (i) {
+			case TOMOYO_VERBOSE:
+				modes = tomoyo_mode_2;
+				break;
+			default:
+				modes = tomoyo_mode_4;
+				break;
+			}
+			for (j = 0; j < 4; j++) {
+				if (strcmp(cp + 1, modes[j]))
+					continue;
+				value = j;
+				break;
+			}
+			if (j == 4)
+				return -EINVAL;
+		} else if (value > tomoyo_control_array[i].max_value) {
+			value = tomoyo_control_array[i].max_value;
+		}
+		profile->value[i] = value;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+/**
+ * tomoyo_read_profile - Read from profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+	static const int total = TOMOYO_MAX_CONTROL_INDEX + 1;
+	int step;
+
+	if (head->read_eof)
+		return 0;
+	for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total;
+	     step++) {
+		const u8 index = step / total;
+		u8 type = step % total;
+		const struct tomoyo_profile *profile
+			= tomoyo_profile_ptr[index];
+		head->read_step = step;
+		if (!profile)
+			continue;
+		if (!type) { /* Print profile' comment tag. */
+			if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+					      index, profile->comment ?
+					      profile->comment->name : ""))
+				break;
+			continue;
+		}
+		type--;
+		if (type < TOMOYO_MAX_CONTROL_INDEX) {
+			const unsigned int value = profile->value[type];
+			const char **modes = NULL;
+			const char *keyword
+				= tomoyo_control_array[type].keyword;
+			switch (tomoyo_control_array[type].max_value) {
+			case 3:
+				modes = tomoyo_mode_4;
+				break;
+			case 1:
+				modes = tomoyo_mode_2;
+				break;
+			}
+			if (modes) {
+				if (!tomoyo_io_printf(head, "%u-%s=%s\n", index,
+						      keyword, modes[value]))
+					break;
+			} else {
+				if (!tomoyo_io_printf(head, "%u-%s=%u\n", index,
+						      keyword, value))
+					break;
+			}
+		}
+	}
+	if (step == TOMOYO_MAX_PROFILES * total)
+		head->read_eof = true;
+	return 0;
+}
+
+/* Structure for policy manager. */
+struct tomoyo_policy_manager_entry {
+	struct list1_head list;
+	/* A path to program or a domainname. */
+	const struct tomoyo_path_info *manager;
+	bool is_domain;  /* True if manager is a domainname. */
+	bool is_deleted; /* True if this entry is deleted. */
+};
+
+/*
+ * The list for "struct tomoyo_policy_manager_entry".
+ *
+ * This list is updated only inside update_manager_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_policy_manager_list);
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager:   The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+				       const bool is_delete)
+{
+	struct tomoyo_policy_manager_entry *new_entry;
+	struct tomoyo_policy_manager_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_manager;
+	int error = -ENOMEM;
+	bool is_domain = false;
+
+	if (tomoyo_is_domain_def(manager)) {
+		if (!tomoyo_is_correct_domain(manager, __func__))
+			return -EINVAL;
+		is_domain = true;
+	} else {
+		if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+			return -EINVAL;
+	}
+	saved_manager = tomoyo_save_name(manager);
+	if (!saved_manager)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+		if (ptr->manager != saved_manager)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->manager = saved_manager;
+	new_entry->is_domain = is_domain;
+	list1_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_write_manager_policy - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+	if (!strcmp(data, "manage_by_non_root")) {
+		tomoyo_manage_by_non_root = !is_delete;
+		return 0;
+	}
+	return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager_policy - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	if (head->read_eof)
+		return 0;
+	list1_for_each_cookie(pos, head->read_var2,
+			      &tomoyo_policy_manager_list) {
+		struct tomoyo_policy_manager_entry *ptr;
+		ptr = list1_entry(pos, struct tomoyo_policy_manager_entry,
+				  list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name))
+			return 0;
+	}
+	head->read_eof = true;
+	return 0;
+}
+
+/**
+ * tomoyo_is_policy_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ */
+static bool tomoyo_is_policy_manager(void)
+{
+	struct tomoyo_policy_manager_entry *ptr;
+	const char *exe;
+	const struct task_struct *task = current;
+	const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
+	bool found = false;
+
+	if (!tomoyo_policy_loaded)
+		return true;
+	if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
+		return false;
+	list1_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+		if (!ptr->is_deleted && ptr->is_domain
+		    && !tomoyo_pathcmp(domainname, ptr->manager))
+			return true;
+	}
+	exe = tomoyo_get_exe();
+	if (!exe)
+		return false;
+	list1_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+		if (!ptr->is_deleted && !ptr->is_domain
+		    && !strcmp(exe, ptr->manager->name)) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) { /* Reduce error messages. */
+		static pid_t last_pid;
+		const pid_t pid = current->pid;
+		if (last_pid != pid) {
+			printk(KERN_WARNING "%s ( %s ) is not permitted to "
+			       "update policies.\n", domainname->name, exe);
+			last_pid = pid;
+		}
+	}
+	tomoyo_free(exe);
+	return found;
+}
+
+/**
+ * tomoyo_is_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
+				 const char *data)
+{
+	unsigned int pid;
+	struct tomoyo_domain_info *domain = NULL;
+
+	if (sscanf(data, "pid=%u", &pid) == 1) {
+		struct task_struct *p;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		p = find_task_by_vpid(pid);
+		if (p)
+			domain = tomoyo_real_domain(p);
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+	} else if (!strncmp(data, "domain=", 7)) {
+		if (tomoyo_is_domain_def(data + 7))
+			domain = tomoyo_find_domain(data + 7);
+	} else
+		return false;
+	head->write_var1 = domain;
+	/* Accessing read_buf is safe because head->io_sem is held. */
+	if (!head->read_buf)
+		return true; /* Do nothing if open(O_WRONLY). */
+	head->read_avail = 0;
+	tomoyo_io_printf(head, "# select %s\n", data);
+	head->read_single_domain = true;
+	head->read_eof = !domain;
+	if (domain) {
+		struct tomoyo_domain_info *d;
+		head->read_var1 = NULL;
+		list1_for_each_entry(d, &tomoyo_domain_list, list) {
+			if (d == domain)
+				break;
+			head->read_var1 = &d->list;
+		}
+		head->read_var2 = NULL;
+		head->read_bit = 0;
+		head->read_step = 0;
+		if (domain->is_deleted)
+			tomoyo_io_printf(head, "# This is a deleted domain.\n");
+	}
+	return true;
+}
+
+/**
+ * tomoyo_write_domain_policy - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	struct tomoyo_domain_info *domain = head->write_var1;
+	bool is_delete = false;
+	bool is_select = false;
+	bool is_undelete = false;
+	unsigned int profile;
+
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
+		is_delete = true;
+	else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
+		is_select = true;
+	else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_UNDELETE))
+		is_undelete = true;
+	if (is_select && tomoyo_is_select_one(head, data))
+		return 0;
+	/* Don't allow updating policies by non manager programs. */
+	if (!tomoyo_is_policy_manager())
+		return -EPERM;
+	if (tomoyo_is_domain_def(data)) {
+		domain = NULL;
+		if (is_delete)
+			tomoyo_delete_domain(data);
+		else if (is_select)
+			domain = tomoyo_find_domain(data);
+		else if (is_undelete)
+			domain = tomoyo_undelete_domain(data);
+		else
+			domain = tomoyo_find_or_assign_new_domain(data, 0);
+		head->write_var1 = domain;
+		return 0;
+	}
+	if (!domain)
+		return -EINVAL;
+
+	if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+	    && profile < TOMOYO_MAX_PROFILES) {
+		if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+			domain->profile = (u8) profile;
+		return 0;
+	}
+	if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+		tomoyo_set_domain_flag(domain, is_delete,
+			       TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
+		return 0;
+	}
+	return tomoyo_write_file_policy(data, domain, is_delete);
+}
+
+/**
+ * tomoyo_print_single_path_acl - Print a single path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_single_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
+					 struct tomoyo_single_path_acl_record *
+					 ptr)
+{
+	int pos;
+	u8 bit;
+	const char *atmark = "";
+	const char *filename;
+	const u16 perm = ptr->perm;
+
+	filename = ptr->filename->name;
+	for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
+	     bit++) {
+		const char *msg;
+		if (!(perm & (1 << bit)))
+			continue;
+		/* Print "read/write" instead of "read" and "write". */
+		if ((bit == TOMOYO_TYPE_READ_ACL ||
+		     bit == TOMOYO_TYPE_WRITE_ACL)
+		    && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+			continue;
+		msg = tomoyo_sp2keyword(bit);
+		pos = head->read_avail;
+		if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg,
+				      atmark, filename))
+			goto out;
+	}
+	head->read_bit = 0;
+	return true;
+ out:
+	head->read_bit = bit;
+	head->read_avail = pos;
+	return false;
+}
+
+/**
+ * tomoyo_print_double_path_acl - Print a double path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_double_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
+					 struct tomoyo_double_path_acl_record *
+					 ptr)
+{
+	int pos;
+	const char *atmark1 = "";
+	const char *atmark2 = "";
+	const char *filename1;
+	const char *filename2;
+	const u8 perm = ptr->perm;
+	u8 bit;
+
+	filename1 = ptr->filename1->name;
+	filename2 = ptr->filename2->name;
+	for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION;
+	     bit++) {
+		const char *msg;
+		if (!(perm & (1 << bit)))
+			continue;
+		msg = tomoyo_dp2keyword(bit);
+		pos = head->read_avail;
+		if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg,
+				      atmark1, filename1, atmark2, filename2))
+			goto out;
+	}
+	head->read_bit = 0;
+	return true;
+ out:
+	head->read_bit = bit;
+	head->read_avail = pos;
+	return false;
+}
+
+/**
+ * tomoyo_print_entry - Print an ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
+			       struct tomoyo_acl_info *ptr)
+{
+	const u8 acl_type = tomoyo_acl_type2(ptr);
+
+	if (acl_type & TOMOYO_ACL_DELETED)
+		return true;
+	if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) {
+		struct tomoyo_single_path_acl_record *acl
+			= container_of(ptr,
+				       struct tomoyo_single_path_acl_record,
+				       head);
+		return tomoyo_print_single_path_acl(head, acl);
+	}
+	if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) {
+		struct tomoyo_double_path_acl_record *acl
+			= container_of(ptr,
+				       struct tomoyo_double_path_acl_record,
+				       head);
+		return tomoyo_print_double_path_acl(head, acl);
+	}
+	BUG(); /* This must not happen. */
+	return false;
+}
+
+/**
+ * tomoyo_read_domain_policy - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *dpos;
+	struct list1_head *apos;
+
+	if (head->read_eof)
+		return 0;
+	if (head->read_step == 0)
+		head->read_step = 1;
+	list1_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
+		struct tomoyo_domain_info *domain;
+		const char *quota_exceeded = "";
+		const char *transition_failed = "";
+		const char *ignore_global_allow_read = "";
+		domain = list1_entry(dpos, struct tomoyo_domain_info, list);
+		if (head->read_step != 1)
+			goto acl_loop;
+		if (domain->is_deleted && !head->read_single_domain)
+			continue;
+		/* Print domainname and flags. */
+		if (domain->quota_warned)
+			quota_exceeded = "quota_exceeded\n";
+		if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+			transition_failed = "transition_failed\n";
+		if (domain->flags &
+		    TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+			ignore_global_allow_read
+				= TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
+		if (!tomoyo_io_printf(head,
+				      "%s\n" TOMOYO_KEYWORD_USE_PROFILE "%u\n"
+				      "%s%s%s\n", domain->domainname->name,
+				      domain->profile, quota_exceeded,
+				      transition_failed,
+				      ignore_global_allow_read))
+			return 0;
+		head->read_step = 2;
+acl_loop:
+		if (head->read_step == 3)
+			goto tail_mark;
+		/* Print ACL entries in the domain. */
+		list1_for_each_cookie(apos, head->read_var2,
+				      &domain->acl_info_list) {
+			struct tomoyo_acl_info *ptr
+				= list1_entry(apos, struct tomoyo_acl_info,
+					      list);
+			if (!tomoyo_print_entry(head, ptr))
+				return 0;
+		}
+		head->read_step = 3;
+tail_mark:
+		if (!tomoyo_io_printf(head, "\n"))
+			return 0;
+		head->read_step = 1;
+		if (head->read_single_domain)
+			break;
+	}
+	head->read_eof = true;
+	return 0;
+}
+
+/**
+ *tomoyo_write_domain_profile - Assign profile for specified domain.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ *
+ * This is equivalent to doing
+ *
+ *     ( echo "select " $domainname; echo "use_profile " $profile ) |
+ *     /usr/lib/ccs/loadpolicy -d
+ */
+static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	char *cp = strchr(data, ' ');
+	struct tomoyo_domain_info *domain;
+	unsigned long profile;
+
+	if (!cp)
+		return -EINVAL;
+	*cp = '\0';
+	domain = tomoyo_find_domain(cp + 1);
+	if (strict_strtoul(data, 10, &profile))
+		return -EINVAL;
+	if (domain && profile < TOMOYO_MAX_PROFILES
+	    && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+		domain->profile = (u8) profile;
+	return 0;
+}
+
+/**
+ * tomoyo_read_domain_profile - Read only domainname and profile.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns list of profile number and domainname pairs.
+ *
+ * This is equivalent to doing
+ *
+ *     grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
+ *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
+ *     domainname = $0; } else if ( $1 == "use_profile" ) {
+ *     print $2 " " domainname; domainname = ""; } } ; '
+ */
+static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	if (head->read_eof)
+		return 0;
+	list1_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
+		struct tomoyo_domain_info *domain;
+		domain = list1_entry(pos, struct tomoyo_domain_info, list);
+		if (domain->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, "%u %s\n", domain->profile,
+				      domain->domainname->name))
+			return 0;
+	}
+	head->read_eof = true;
+	return 0;
+}
+
+/**
+ * tomoyo_write_pid: Specify PID to obtain domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
+{
+	unsigned long pid;
+	/* No error check. */
+	strict_strtoul(head->write_buf, 10, &pid);
+	head->read_step = (int) pid;
+	head->read_eof = false;
+	return 0;
+}
+
+/**
+ * tomoyo_read_pid - Get domainname of the specified PID.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the domainname which the specified PID is in on success,
+ * empty string otherwise.
+ * The PID is specified by tomoyo_write_pid() so that the user can obtain
+ * using read()/write() interface rather than sysctl() interface.
+ */
+static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
+{
+	if (head->read_avail == 0 && !head->read_eof) {
+		const int pid = head->read_step;
+		struct task_struct *p;
+		struct tomoyo_domain_info *domain = NULL;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		p = find_task_by_vpid(pid);
+		if (p)
+			domain = tomoyo_real_domain(p);
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+		if (domain)
+			tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
+					 domain->domainname->name);
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_write_exception_policy - Write exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN))
+		return tomoyo_write_domain_keeper_policy(data, false,
+							 is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN))
+		return tomoyo_write_domain_keeper_policy(data, true, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN))
+		return tomoyo_write_domain_initializer_policy(data, false,
+							      is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN))
+		return tomoyo_write_domain_initializer_policy(data, true,
+							      is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS))
+		return tomoyo_write_alias_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ))
+		return tomoyo_write_globally_readable_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN))
+		return tomoyo_write_pattern_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
+		return tomoyo_write_no_rewrite_policy(data, is_delete);
+	return -EINVAL;
+}
+
+/**
+ * tomoyo_read_exception_policy - Read exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		switch (head->read_step) {
+		case 0:
+			head->read_var2 = NULL;
+			head->read_step = 1;
+		case 1:
+			if (!tomoyo_read_domain_keeper_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 2;
+		case 2:
+			if (!tomoyo_read_globally_readable_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 3;
+		case 3:
+			head->read_var2 = NULL;
+			head->read_step = 4;
+		case 4:
+			if (!tomoyo_read_domain_initializer_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 5;
+		case 5:
+			if (!tomoyo_read_alias_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 6;
+		case 6:
+			head->read_var2 = NULL;
+			head->read_step = 7;
+		case 7:
+			if (!tomoyo_read_file_pattern(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 8;
+		case 8:
+			if (!tomoyo_read_no_rewrite_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 9;
+		case 9:
+			head->read_eof = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* path to policy loader */
+static const char *tomoyo_loader = "/sbin/tomoyo-init";
+
+/**
+ * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
+ *
+ * Returns true if /sbin/tomoyo-init exists, false otherwise.
+ */
+static bool tomoyo_policy_loader_exists(void)
+{
+	/*
+	 * Don't activate MAC if the policy loader doesn't exist.
+	 * If the initrd includes /sbin/init but real-root-dev has not
+	 * mounted on / yet, activating MAC will block the system since
+	 * policies are not loaded yet.
+	 * Thus, let do_execve() call this function everytime.
+	 */
+	struct nameidata nd;
+
+	if (path_lookup(tomoyo_loader, LOOKUP_FOLLOW, &nd)) {
+		printk(KERN_INFO "Not activating Mandatory Access Control now "
+		       "since %s doesn't exist.\n", tomoyo_loader);
+		return false;
+	}
+	path_put(&nd.path);
+	return true;
+}
+
+/**
+ * tomoyo_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * This function checks whether @filename is /sbin/init , and if so
+ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
+ * and then continues invocation of /sbin/init.
+ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
+ * writes to /sys/kernel/security/tomoyo/ interfaces.
+ *
+ * Returns nothing.
+ */
+void tomoyo_load_policy(const char *filename)
+{
+	char *argv[2];
+	char *envp[3];
+
+	if (tomoyo_policy_loaded)
+		return;
+	/*
+	 * Check filename is /sbin/init or /sbin/tomoyo-start.
+	 * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
+	 * be passed.
+	 * You can create /sbin/tomoyo-start by
+	 * "ln -s /bin/true /sbin/tomoyo-start".
+	 */
+	if (strcmp(filename, "/sbin/init") &&
+	    strcmp(filename, "/sbin/tomoyo-start"))
+		return;
+	if (!tomoyo_policy_loader_exists())
+		return;
+
+	printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+	       tomoyo_loader);
+	argv[0] = (char *) tomoyo_loader;
+	argv[1] = NULL;
+	envp[0] = "HOME=/";
+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[2] = NULL;
+	call_usermodehelper(argv[0], argv, envp, 1);
+
+	printk(KERN_INFO "TOMOYO: 2.2.0-pre   2009/01/01\n");
+	printk(KERN_INFO "Mandatory Access Control activated.\n");
+	tomoyo_policy_loaded = true;
+	{ /* Check all profiles currently assigned to domains are defined. */
+		struct tomoyo_domain_info *domain;
+		list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+			const u8 profile = domain->profile;
+			if (tomoyo_profile_ptr[profile])
+				continue;
+			panic("Profile %u (used by '%s') not defined.\n",
+			      profile, domain->domainname->name);
+		}
+	}
+}
+
+/**
+ * tomoyo_read_version: Get version.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns version information.
+ */
+static int tomoyo_read_version(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		tomoyo_io_printf(head, "2.2.0-pre");
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_read_self_domain - Get the current process's domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the current process's domainname.
+ */
+static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		/*
+		 * tomoyo_domain()->domainname != NULL
+		 * because every process belongs to a domain and
+		 * the domain's name cannot be NULL.
+		 */
+		tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @type: Type of interface.
+ * @file: Pointer to "struct file".
+ *
+ * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ */
+static int tomoyo_open_control(const u8 type, struct file *file)
+{
+	struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head));
+
+	if (!head)
+		return -ENOMEM;
+	mutex_init(&head->io_sem);
+	switch (type) {
+	case TOMOYO_DOMAINPOLICY:
+		/* /sys/kernel/security/tomoyo/domain_policy */
+		head->write = tomoyo_write_domain_policy;
+		head->read = tomoyo_read_domain_policy;
+		break;
+	case TOMOYO_EXCEPTIONPOLICY:
+		/* /sys/kernel/security/tomoyo/exception_policy */
+		head->write = tomoyo_write_exception_policy;
+		head->read = tomoyo_read_exception_policy;
+		break;
+	case TOMOYO_SELFDOMAIN:
+		/* /sys/kernel/security/tomoyo/self_domain */
+		head->read = tomoyo_read_self_domain;
+		break;
+	case TOMOYO_DOMAIN_STATUS:
+		/* /sys/kernel/security/tomoyo/.domain_status */
+		head->write = tomoyo_write_domain_profile;
+		head->read = tomoyo_read_domain_profile;
+		break;
+	case TOMOYO_PROCESS_STATUS:
+		/* /sys/kernel/security/tomoyo/.process_status */
+		head->write = tomoyo_write_pid;
+		head->read = tomoyo_read_pid;
+		break;
+	case TOMOYO_VERSION:
+		/* /sys/kernel/security/tomoyo/version */
+		head->read = tomoyo_read_version;
+		head->readbuf_size = 128;
+		break;
+	case TOMOYO_MEMINFO:
+		/* /sys/kernel/security/tomoyo/meminfo */
+		head->write = tomoyo_write_memory_quota;
+		head->read = tomoyo_read_memory_counter;
+		head->readbuf_size = 512;
+		break;
+	case TOMOYO_PROFILE:
+		/* /sys/kernel/security/tomoyo/profile */
+		head->write = tomoyo_write_profile;
+		head->read = tomoyo_read_profile;
+		break;
+	case TOMOYO_MANAGER:
+		/* /sys/kernel/security/tomoyo/manager */
+		head->write = tomoyo_write_manager_policy;
+		head->read = tomoyo_read_manager_policy;
+		break;
+	}
+	if (!(file->f_mode & FMODE_READ)) {
+		/*
+		 * No need to allocate read_buf since it is not opened
+		 * for reading.
+		 */
+		head->read = NULL;
+	} else {
+		if (!head->readbuf_size)
+			head->readbuf_size = 4096 * 2;
+		head->read_buf = tomoyo_alloc(head->readbuf_size);
+		if (!head->read_buf) {
+			tomoyo_free(head);
+			return -ENOMEM;
+		}
+	}
+	if (!(file->f_mode & FMODE_WRITE)) {
+		/*
+		 * No need to allocate write_buf since it is not opened
+		 * for writing.
+		 */
+		head->write = NULL;
+	} else if (head->write) {
+		head->writebuf_size = 4096 * 2;
+		head->write_buf = tomoyo_alloc(head->writebuf_size);
+		if (!head->write_buf) {
+			tomoyo_free(head->read_buf);
+			tomoyo_free(head);
+			return -ENOMEM;
+		}
+	}
+	file->private_data = head;
+	/*
+	 * Call the handler now if the file is
+	 * /sys/kernel/security/tomoyo/self_domain
+	 * so that the user can use
+	 * cat < /sys/kernel/security/tomoyo/self_domain"
+	 * to know the current process's domainname.
+	 */
+	if (type == TOMOYO_SELFDOMAIN)
+		tomoyo_read_control(file, NULL, 0);
+	return 0;
+}
+
+/**
+ * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:       Pointer to "struct file".
+ * @buffer:     Poiner to buffer to write to.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+			       const int buffer_len)
+{
+	int len = 0;
+	struct tomoyo_io_buffer *head = file->private_data;
+	char *cp;
+
+	if (!head->read)
+		return -ENOSYS;
+	if (mutex_lock_interruptible(&head->io_sem))
+		return -EINTR;
+	/* Call the policy handler. */
+	len = head->read(head);
+	if (len < 0)
+		goto out;
+	/* Write to buffer. */
+	len = head->read_avail;
+	if (len > buffer_len)
+		len = buffer_len;
+	if (!len)
+		goto out;
+	/* head->read_buf changes by some functions. */
+	cp = head->read_buf;
+	if (copy_to_user(buffer, cp, len)) {
+		len = -EFAULT;
+		goto out;
+	}
+	head->read_avail -= len;
+	memmove(cp, cp + len, head->read_avail);
+ out:
+	mutex_unlock(&head->io_sem);
+	return len;
+}
+
+/**
+ * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:       Pointer to "struct file".
+ * @buffer:     Pointer to buffer to read from.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns @buffer_len on success, negative value otherwise.
+ */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+				const int buffer_len)
+{
+	struct tomoyo_io_buffer *head = file->private_data;
+	int error = buffer_len;
+	int avail_len = buffer_len;
+	char *cp0 = head->write_buf;
+
+	if (!head->write)
+		return -ENOSYS;
+	if (!access_ok(VERIFY_READ, buffer, buffer_len))
+		return -EFAULT;
+	/* Don't allow updating policies by non manager programs. */
+	if (head->write != tomoyo_write_pid &&
+	    head->write != tomoyo_write_domain_policy &&
+	    !tomoyo_is_policy_manager())
+		return -EPERM;
+	if (mutex_lock_interruptible(&head->io_sem))
+		return -EINTR;
+	/* Read a line and dispatch it to the policy handler. */
+	while (avail_len > 0) {
+		char c;
+		if (head->write_avail >= head->writebuf_size - 1) {
+			error = -ENOMEM;
+			break;
+		} else if (get_user(c, buffer)) {
+			error = -EFAULT;
+			break;
+		}
+		buffer++;
+		avail_len--;
+		cp0[head->write_avail++] = c;
+		if (c != '\n')
+			continue;
+		cp0[head->write_avail - 1] = '\0';
+		head->write_avail = 0;
+		tomoyo_normalize_line(cp0);
+		head->write(head);
+	}
+	mutex_unlock(&head->io_sem);
+	return error;
+}
+
+/**
+ * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ *
+ * Releases memory and returns 0.
+ */
+static int tomoyo_close_control(struct file *file)
+{
+	struct tomoyo_io_buffer *head = file->private_data;
+
+	/* Release memory used for policy I/O. */
+	tomoyo_free(head->read_buf);
+	head->read_buf = NULL;
+	tomoyo_free(head->write_buf);
+	head->write_buf = NULL;
+	tomoyo_free(head);
+	head = NULL;
+	file->private_data = NULL;
+	return 0;
+}
+
+/**
+ * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
+ *
+ * @acl_type:  Type of ACL entry.
+ *
+ * Returns pointer to the ACL entry on success, NULL otherwise.
+ */
+void *tomoyo_alloc_acl_element(const u8 acl_type)
+{
+	int len;
+	struct tomoyo_acl_info *ptr;
+
+	switch (acl_type) {
+	case TOMOYO_TYPE_SINGLE_PATH_ACL:
+		len = sizeof(struct tomoyo_single_path_acl_record);
+		break;
+	case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+		len = sizeof(struct tomoyo_double_path_acl_record);
+		break;
+	default:
+		return NULL;
+	}
+	ptr = tomoyo_alloc_element(len);
+	if (!ptr)
+		return NULL;
+	ptr->type = acl_type;
+	return ptr;
+}
+
+/**
+ * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+	const int key = ((u8 *) file->f_path.dentry->d_inode->i_private)
+		- ((u8 *) NULL);
+	return tomoyo_open_control(key, file);
+}
+
+/**
+ * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+	return tomoyo_close_control(file);
+}
+
+/**
+ * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
+			   loff_t *ppos)
+{
+	return tomoyo_read_control(file, buf, count);
+}
+
+/**
+ * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t tomoyo_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	return tomoyo_write_control(file, buf, count);
+}
+
+/* Operations for /sys/kernel/security/tomoyo/ interface. */
+static struct file_operations tomoyo_operations = {
+	.open    = tomoyo_open,
+	.release = tomoyo_release,
+	.read    = tomoyo_read,
+	.write   = tomoyo_write,
+};
+
+/**
+ * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
+ *
+ * @name:   The name of the interface file.
+ * @mode:   The permission of the interface file.
+ * @parent: The parent directory.
+ * @key:    Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init tomoyo_create_entry(const char *name, const mode_t mode,
+				       struct dentry *parent, const u8 key)
+{
+	securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
+			       &tomoyo_operations);
+}
+
+/**
+ * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_initerface_init(void)
+{
+	struct dentry *tomoyo_dir;
+
+	tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+	tomoyo_create_entry("domain_policy",    0600, tomoyo_dir,
+			    TOMOYO_DOMAINPOLICY);
+	tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
+			    TOMOYO_EXCEPTIONPOLICY);
+	tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
+			    TOMOYO_SELFDOMAIN);
+	tomoyo_create_entry(".domain_status",   0600, tomoyo_dir,
+			    TOMOYO_DOMAIN_STATUS);
+	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
+			    TOMOYO_PROCESS_STATUS);
+	tomoyo_create_entry("meminfo",          0600, tomoyo_dir,
+			    TOMOYO_MEMINFO);
+	tomoyo_create_entry("profile",          0600, tomoyo_dir,
+			    TOMOYO_PROFILE);
+	tomoyo_create_entry("manager",          0600, tomoyo_dir,
+			    TOMOYO_MANAGER);
+	tomoyo_create_entry("version",          0400, tomoyo_dir,
+			    TOMOYO_VERSION);
+	return 0;
+}
+
+fs_initcall(tomoyo_initerface_init);
--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/common.h
@@ -0,0 +1,337 @@
+/*
+ * security/tomoyo/common.h
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_COMMON_H
+#define _SECURITY_TOMOYO_COMMON_H
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list1.h>
+
+struct dentry;
+struct vfsmount;
+
+/* Temporary buffer for holding pathnames. */
+struct tomoyo_page_buffer {
+	char buffer[4096];
+};
+
+/* Structure for holding a token. */
+struct tomoyo_path_info {
+	const char *name;
+	u32 hash;          /* = full_name_hash(name, strlen(name)) */
+	u16 total_len;     /* = strlen(name)                       */
+	u16 const_len;     /* = tomoyo_const_part_length(name)     */
+	bool is_dir;       /* = tomoyo_strendswith(name, "/")      */
+	bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
+	u16 depth;         /* = tomoyo_path_depth(name)            */
+};
+
+/*
+ * This is the max length of a token.
+ *
+ * A token consists of only ASCII printable characters.
+ * Non printable characters in a token is represented in \ooo style
+ * octal string. Thus, \ itself is represented as \\.
+ */
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+/* Structure for holding requested pathname. */
+struct tomoyo_path_info_with_data {
+	/* Keep "head" first, for this pointer is passed to tomoyo_free(). */
+	struct tomoyo_path_info head;
+	char bariier1[16]; /* Safeguard for overrun. */
+	char body[TOMOYO_MAX_PATHNAME_LEN];
+	char barrier2[16]; /* Safeguard for overrun. */
+};
+
+/*
+ * Common header for holding ACL entries.
+ *
+ * Packing "struct tomoyo_acl_info" allows
+ * "struct tomoyo_single_path_acl_record" to embed "u16" and
+ * "struct tomoyo_double_path_acl_record" to embed "u8"
+ * without enlarging their structure size.
+ */
+struct tomoyo_acl_info {
+	struct list1_head list;
+	/*
+	 * Type of this ACL entry.
+	 *
+	 * MSB is is_deleted flag.
+	 */
+	u8 type;
+} __packed;
+
+/* This ACL entry is deleted.           */
+#define TOMOYO_ACL_DELETED        0x80
+
+/* Structure for domain information. */
+struct tomoyo_domain_info {
+	struct list1_head list;
+	struct list1_head acl_info_list;
+	/* Name of this domain. Never NULL.          */
+	const struct tomoyo_path_info *domainname;
+	u8 profile;        /* Profile number to use. */
+	u8 is_deleted;     /* Delete flag.
+			      0 = active.
+			      1 = deleted but undeletable.
+			      255 = deleted and no longer undeletable. */
+	bool quota_warned; /* Quota warnning flag.   */
+	/* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
+	u8 flags;
+};
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+/* Ignore "allow_read" directive in exception policy. */
+#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
+/*
+ * This domain was unable to create a new domain at tomoyo_find_next_domain()
+ * because the name of the domain to be created was too long or
+ * it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED        2
+
+/*
+ * Structure for "allow_read/write", "allow_execute", "allow_read",
+ * "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink" and "allow_rewrite" directive.
+ */
+struct tomoyo_single_path_acl_record {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+	u16 perm;
+	/* Pointer to single pathname. */
+	const struct tomoyo_path_info *filename;
+};
+
+/* Structure for "allow_rename" and "allow_link" directive. */
+struct tomoyo_double_path_acl_record {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
+	u8 perm;
+	/* Pointer to single pathname. */
+	const struct tomoyo_path_info *filename1;
+	/* Pointer to single pathname. */
+	const struct tomoyo_path_info *filename2;
+};
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ALIAS                     "alias "
+#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
+#define TOMOYO_KEYWORD_DELETE                    "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
+#define TOMOYO_KEYWORD_SELECT                    "select "
+#define TOMOYO_KEYWORD_UNDELETE                  "undelete "
+#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME                         "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Index numbers for Access Controls. */
+#define TOMOYO_MAC_FOR_FILE                  0  /* domain_policy.conf */
+#define TOMOYO_MAX_ACCEPT_ENTRY              1
+#define TOMOYO_VERBOSE                       2
+#define TOMOYO_MAX_CONTROL_INDEX             3
+
+/* Structure for reading/writing policy via securityfs interfaces. */
+struct tomoyo_io_buffer {
+	int (*read) (struct tomoyo_io_buffer *);
+	int (*write) (struct tomoyo_io_buffer *);
+	/* Exclusive lock for this structure.   */
+	struct mutex io_sem;
+	/* The position currently reading from. */
+	struct list1_head *read_var1;
+	/* Extra variables for reading.         */
+	struct list1_head *read_var2;
+	/* The position currently writing to.   */
+	struct tomoyo_domain_info *write_var1;
+	/* The step for reading.                */
+	int read_step;
+	/* Buffer for reading.                  */
+	char *read_buf;
+	/* EOF flag for reading.                */
+	bool read_eof;
+	/* Read domain ACL of specified PID?    */
+	bool read_single_domain;
+	/* Extra variable for reading.          */
+	u8 read_bit;
+	/* Bytes available for reading.         */
+	int read_avail;
+	/* Size of read buffer.                 */
+	int readbuf_size;
+	/* Buffer for writing.                  */
+	char *write_buf;
+	/* Bytes available for writing.         */
+	int write_avail;
+	/* Size of write buffer.                */
+	int writebuf_size;
+};
+
+/* Check whether the domain has too many ACL entries to hold. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
+/* Transactional sprintf() for policy dump. */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+	__attribute__ ((format(printf, 2, 3)));
+/* Check whether the domainname is correct. */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+			      const char *function);
+/* Check whether the token is correct. */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+			    const s8 pattern_type, const s8 end_type,
+			    const char *function);
+/* Check whether the token can be a domainname. */
+bool tomoyo_is_domain_def(const unsigned char *buffer);
+/* Check whether the given filename matches the given pattern. */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+				 const struct tomoyo_path_info *pattern);
+/* Read "alias" entry in exception policy. */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head);
+/*
+ * Read "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
+/* Read "keep_domain" and "no_keep_domain" entry in exception policy. */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
+/* Read "file_pattern" entry in exception policy. */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
+/* Read "allow_read" entry in exception policy. */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
+/* Read "deny_rewrite" entry in exception policy. */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
+/* Write domain policy violation warning message to console? */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
+/* Convert double path operation to operation name. */
+const char *tomoyo_dp2keyword(const u8 operation);
+/* Get the last component of the given domainname. */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
+/* Get warning message. */
+const char *tomoyo_get_msg(const bool is_enforce);
+/* Convert single path operation to operation name. */
+const char *tomoyo_sp2keyword(const u8 operation);
+/* Delete a domain. */
+int tomoyo_delete_domain(char *data);
+/* Create "alias" entry in exception policy. */
+int tomoyo_write_alias_policy(char *data, const bool is_delete);
+/*
+ * Create "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+					   const bool is_delete);
+/* Create "keep_domain" and "no_keep_domain" entry in exception policy. */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+				      const bool is_delete);
+/*
+ * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
+ * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
+ * "allow_link" entry in domain policy.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+			     const bool is_delete);
+/* Create "allow_read" entry in exception policy. */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
+/* Create "deny_rewrite" entry in exception policy. */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
+/* Create "file_pattern" entry in exception policy. */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete);
+/* Find a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+/* Find or create a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+							    domainname,
+							    const u8 profile);
+/* Undelete a domain. */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname);
+/* Check mode for specified functionality. */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+				const u8 index);
+/* Allocate memory for structures. */
+void *tomoyo_alloc_acl_element(const u8 acl_type);
+/* Fill in "struct tomoyo_path_info" members. */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+/* Run policy loader when /sbin/init starts. */
+void tomoyo_load_policy(const char *filename);
+/* Change "struct tomoyo_domain_info"->flags. */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+			    const bool is_delete, const u8 flags);
+
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+				  const struct tomoyo_path_info *b)
+{
+	return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr)
+{
+	return ptr->type & ~TOMOYO_ACL_DELETED;
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr)
+{
+	return ptr->type;
+}
+
+/**
+ * tomoyo_is_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_is_valid(const unsigned char c)
+{
+	return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_is_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_is_invalid(const unsigned char c)
+{
+	return c && (c <= ' ' || c >= 127);
+}
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list1_head tomoyo_domain_list;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 06/10] File operation restriction part.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (4 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 05/10] Common functions for TOMOYO Linux Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 07/10] Domain transition handler Tetsuo Handa
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda,
	Tetsuo Handa, Toshiharu Harada

[-- Attachment #1: tomoyo-file-restriction-part.patch --]
[-- Type: text/plain, Size: 36969 bytes --]

This file controls file related operations.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 security/tomoyo/file.c | 1258 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1258 insertions(+)

--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/file.c
@@ -0,0 +1,1258 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* Structure for "allow_read" keyword. */
+struct tomoyo_globally_readable_file_entry {
+	struct list1_head list;
+	const struct tomoyo_path_info *filename;
+	bool is_deleted;
+};
+
+/* Structure for "file_pattern" keyword. */
+struct tomoyo_pattern_entry {
+	struct list1_head list;
+	const struct tomoyo_path_info *pattern;
+	bool is_deleted;
+};
+
+/* Structure for "deny_rewrite" keyword. */
+struct tomoyo_no_rewrite_entry {
+	struct list1_head list;
+	const struct tomoyo_path_info *pattern;
+	bool is_deleted;
+};
+
+/* Keyword array for single path operations. */
+static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
+	[TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
+	[TOMOYO_TYPE_EXECUTE_ACL]    = "execute",
+	[TOMOYO_TYPE_READ_ACL]       = "read",
+	[TOMOYO_TYPE_WRITE_ACL]      = "write",
+	[TOMOYO_TYPE_CREATE_ACL]     = "create",
+	[TOMOYO_TYPE_UNLINK_ACL]     = "unlink",
+	[TOMOYO_TYPE_MKDIR_ACL]      = "mkdir",
+	[TOMOYO_TYPE_RMDIR_ACL]      = "rmdir",
+	[TOMOYO_TYPE_MKFIFO_ACL]     = "mkfifo",
+	[TOMOYO_TYPE_MKSOCK_ACL]     = "mksock",
+	[TOMOYO_TYPE_MKBLOCK_ACL]    = "mkblock",
+	[TOMOYO_TYPE_MKCHAR_ACL]     = "mkchar",
+	[TOMOYO_TYPE_TRUNCATE_ACL]   = "truncate",
+	[TOMOYO_TYPE_SYMLINK_ACL]    = "symlink",
+	[TOMOYO_TYPE_REWRITE_ACL]    = "rewrite",
+};
+
+/* Keyword array for double path operations. */
+static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
+	[TOMOYO_TYPE_LINK_ACL]    = "link",
+	[TOMOYO_TYPE_RENAME_ACL]  = "rename",
+};
+
+/**
+ * tomoyo_sp2keyword - Get the name of single path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of single path operation.
+ */
+const char *tomoyo_sp2keyword(const u8 operation)
+{
+	return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION)
+		? tomoyo_sp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_dp2keyword - Get the name of double path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of double path operation.
+ */
+const char *tomoyo_dp2keyword(const u8 operation)
+{
+	return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION)
+		? tomoyo_dp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_strendswith - Check whether the token ends with the given token.
+ *
+ * @name: The token to check.
+ * @tail: The token to find.
+ *
+ * Returns true if @name ends with @tail, false otherwise.
+ */
+static bool tomoyo_strendswith(const char *name, const char *tail)
+{
+	int len;
+
+	if (!name || !tail)
+		return false;
+	len = strlen(name) - strlen(tail);
+	return len >= 0 && !strcmp(name + len, tail);
+}
+
+/**
+ * tomoyo_get_path - Get realpath.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
+{
+	int error;
+	struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf));
+
+	if (!buf)
+		return NULL;
+	/* Reserve one byte for appending "/". */
+	error = tomoyo_realpath_from_path2(path, buf->body,
+					   sizeof(buf->body) - 2);
+	if (!error) {
+		buf->head.name = buf->body;
+		tomoyo_fill_path_info(&buf->head);
+		return &buf->head;
+	}
+	tomoyo_free(buf);
+	return NULL;
+}
+
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+					 const char *filename2,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete);
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete);
+
+/**
+ * tomoyo_add_domain_acl - Add the given ACL to the given domain.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info". May be NULL.
+ * @acl:    Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns 0.
+ */
+static int tomoyo_add_domain_acl(struct tomoyo_domain_info *domain,
+				 struct tomoyo_acl_info *acl)
+{
+	if (domain) {
+		/*
+		 * We need to serialize because this function is called by
+		 * tomoyo_update_single_path_acl() and
+		 * tomoyo_update_double_path_acl().
+		 */
+		static DEFINE_SPINLOCK(lock);
+		/***** CRITICAL SECTION START *****/
+		spin_lock(&lock);
+		list1_add_tail(&acl->list, &domain->acl_info_list);
+		spin_unlock(&lock);
+		/***** CRITICAL SECTION END *****/
+	} else {
+		acl->type &= ~TOMOYO_ACL_DELETED;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_del_domain_acl - Delete the given ACL from the domain.
+ *
+ * @acl: Pointer to "struct tomoyo_acl_info". May be NULL.
+ *
+ * Returns 0.
+ */
+static int tomoyo_del_domain_acl(struct tomoyo_acl_info *acl)
+{
+	if (acl)
+		acl->type |= TOMOYO_ACL_DELETED;
+	return 0;
+}
+
+/*
+ * The list for "struct tomoyo_globally_readable_file_entry".
+ *
+ * This list is updated only inside update_globally_readable_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_globally_readable_list);
+
+/**
+ * update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @filename:  Filename unconditionally permitted to open() for reading.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int update_globally_readable_entry(const char *filename,
+					  const bool is_delete)
+{
+	struct tomoyo_globally_readable_file_entry *new_entry;
+	struct tomoyo_globally_readable_file_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_filename;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__))
+		return -EINVAL;
+	saved_filename = tomoyo_save_name(filename);
+	if (!saved_filename)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+		if (ptr->filename != saved_filename)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->filename = saved_filename;
+	list1_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
+ *
+ * @filename: The filename to check.
+ *
+ * Returns true if any domain can open @filename for reading, false otherwise.
+ */
+static bool is_globally_readable_file(const struct tomoyo_path_info *filename)
+{
+	struct tomoyo_globally_readable_file_entry *ptr;
+
+	list1_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+		if (!ptr->is_deleted &&
+		    tomoyo_path_matches_pattern(filename, ptr->filename))
+			return true;
+	}
+	return false;
+}
+
+/**
+ * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
+{
+	return update_globally_readable_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2,
+			      &tomoyo_globally_readable_list) {
+		struct tomoyo_globally_readable_file_entry *ptr;
+		ptr = list1_entry(pos,
+				  struct tomoyo_globally_readable_file_entry,
+				  list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
+				      ptr->filename->name))
+			goto out;
+	}
+	return true;
+ out:
+	return false;
+}
+
+/*
+ * The list for "struct tomoyo_pattern_entry".
+ *
+ * This list is updated only inside tomoyo_update_file_pattern_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_pattern_list);
+
+/**
+ * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
+ *
+ * @pattern:   Pathname pattern.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_file_pattern_entry(const char *pattern,
+					    const bool is_delete)
+{
+	struct tomoyo_pattern_entry *new_entry;
+	struct tomoyo_pattern_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_pattern;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__))
+		return -EINVAL;
+	saved_pattern = tomoyo_save_name(pattern);
+	if (!saved_pattern)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+		if (saved_pattern != ptr->pattern)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->pattern = saved_pattern;
+	list1_add_tail(&new_entry->list, &tomoyo_pattern_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_get_file_pattern - Get patterned pathname.
+ *
+ * @filename: The filename to find patterned pathname.
+ *
+ * Returns pointer to pathname pattern if matched, @filename otherwise.
+ */
+static const struct tomoyo_path_info *
+tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
+{
+	struct tomoyo_pattern_entry *ptr;
+	const struct tomoyo_path_info *pattern = NULL;
+
+	list1_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+			continue;
+		pattern = ptr->pattern;
+		if (tomoyo_strendswith(pattern->name, "/\\*")) {
+			/* Do nothing. Try to find the better match. */
+		} else {
+			/* This would be the better match. Use this. */
+			break;
+		}
+	}
+	if (pattern)
+		filename = pattern;
+	return filename;
+}
+
+/**
+ * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete)
+{
+	return tomoyo_update_file_pattern_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
+		struct tomoyo_pattern_entry *ptr;
+		ptr = list1_entry(pos, struct tomoyo_pattern_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN "%s\n",
+				      ptr->pattern->name))
+			goto out;
+	}
+	return true;
+ out:
+	return false;
+}
+
+/*
+ * The list for "struct tomoyo_no_rewrite_entry".
+ *
+ * This list is updated only inside tomoyo_update_no_rewrite_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_no_rewrite_list);
+
+/**
+ * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @pattern:   Pathname pattern that are not rewritable by default.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_no_rewrite_entry(const char *pattern,
+					  const bool is_delete)
+{
+	struct tomoyo_no_rewrite_entry *new_entry, *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_pattern;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__))
+		return -EINVAL;
+	saved_pattern = tomoyo_save_name(pattern);
+	if (!saved_pattern)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+		if (ptr->pattern != saved_pattern)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->pattern = saved_pattern;
+	list1_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ *
+ * @filename: Filename to check.
+ *
+ * Returns true if @filename is specified by "deny_rewrite" directive,
+ * false otherwise.
+ */
+static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
+{
+	struct tomoyo_no_rewrite_entry *ptr;
+
+	list1_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+			continue;
+		return true;
+	}
+	return false;
+}
+
+/**
+ * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
+{
+	return tomoyo_update_no_rewrite_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
+		struct tomoyo_no_rewrite_entry *ptr;
+		ptr = list1_entry(pos, struct tomoyo_no_rewrite_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
+				      ptr->pattern->name))
+			goto out;
+	}
+	return true;
+ out:
+	return false;
+}
+
+/**
+ * tomoyo_update_file_acl - Update file's read/write/execute ACL.
+ *
+ * @filename:  Filename.
+ * @perm:      Permission (between 1 to 7).
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This is legacy support interface for older policy syntax.
+ * Current policy syntax uses "allow_read/write" instead of "6",
+ * "allow_read" instead of "4", "allow_write" instead of "2",
+ * "allow_execute" instead of "1".
+ */
+static int tomoyo_update_file_acl(const char *filename, u8 perm,
+				  struct tomoyo_domain_info * const domain,
+				  const bool is_delete)
+{
+	if (perm > 7 || !perm) {
+		printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
+		       __func__, perm, filename);
+		return -EINVAL;
+	}
+	if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
+		/*
+		 * Only 'allow_mkdir' and 'allow_rmdir' are valid for
+		 * directory permissions.
+		 */
+		return 0;
+	if (perm & 4)
+		tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename,
+					      domain, is_delete);
+	if (perm & 2)
+		tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename,
+					      domain, is_delete);
+	if (perm & 1)
+		tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL,
+					      filename, domain, is_delete);
+	return 0;
+}
+
+/**
+ * tomoyo_check_single_path_acl2 - Check permission for single path operation.
+ *
+ * @domain:          Pointer to "struct tomoyo_domain_info".
+ * @filename:        Filename to check.
+ * @perm:            Permission.
+ * @may_use_pattern: True if patterned ACL is permitted.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
+					 domain,
+					 const struct tomoyo_path_info *
+					 filename,
+					 const u16 perm,
+					 const bool may_use_pattern)
+{
+	struct tomoyo_acl_info *ptr;
+
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		struct tomoyo_single_path_acl_record *acl;
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+				   head);
+		if (!(acl->perm & perm))
+			continue;
+		if (may_use_pattern || !acl->filename->is_patterned) {
+			if (!tomoyo_path_matches_pattern(filename,
+							 acl->filename))
+				continue;
+		} else {
+			continue;
+		}
+		return 0;
+	}
+	return -EPERM;
+}
+
+/**
+ * check_file_acl - Check permission for opening files.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @operation: Mode ("read" or "write" or "read/write" or "execute").
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int check_file_acl(const struct tomoyo_domain_info *domain,
+			  const struct tomoyo_path_info *filename,
+			  const u8 operation)
+{
+	u16 perm = 0;
+
+	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+		return 0;
+	if (operation == 6)
+		perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+	else if (operation == 4)
+		perm = 1 << TOMOYO_TYPE_READ_ACL;
+	else if (operation == 2)
+		perm = 1 << TOMOYO_TYPE_WRITE_ACL;
+	else if (operation == 1)
+		perm = 1 << TOMOYO_TYPE_EXECUTE_ACL;
+	else
+		BUG();
+	return tomoyo_check_single_path_acl2(domain, filename, perm,
+					     operation != 1);
+}
+
+/**
+ * tomoyo_check_file_perm2 - Check permission for opening files.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @perm:      Mode ("read" or "write" or "read/write" or "execute").
+ * @operation: Operation name passed used for verbose mode.
+ * @mode:      Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
+				   const struct tomoyo_path_info *filename,
+				   const u8 perm, const char *operation,
+				   const u8 mode)
+{
+	const bool is_enforce = (mode == 3);
+	const char *msg = "<unknown>";
+	int error = 0;
+
+	if (!filename)
+		return 0;
+	error = check_file_acl(domain, filename, perm);
+	if (error && perm == 4 &&
+	    (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+	    && is_globally_readable_file(filename))
+		error = 0;
+	if (perm == 6)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL);
+	else if (perm == 4)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL);
+	else if (perm == 2)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL);
+	else if (perm == 1)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL);
+	else
+		BUG();
+	if (!error)
+		return 0;
+	if (tomoyo_verbose_mode(domain))
+		printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied "
+		       "for %s\n", tomoyo_get_msg(is_enforce), msg, operation,
+		       filename->name, tomoyo_get_last_name(domain));
+	if (is_enforce)
+		return error;
+	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		/* Don't use patterns for execute permission. */
+		const struct tomoyo_path_info *patterned_file = (perm != 1) ?
+			tomoyo_get_file_pattern(filename) : filename;
+		tomoyo_update_file_acl(patterned_file->name, perm,
+				       domain, false);
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_write_file_policy - Update file related list.
+ *
+ * @data:      String to parse.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+			     const bool is_delete)
+{
+	char *filename = strchr(data, ' ');
+	char *filename2;
+	unsigned int perm;
+	u8 type;
+
+	if (!filename)
+		return -EINVAL;
+	*filename++ = '\0';
+	if (sscanf(data, "%u", &perm) == 1)
+		return tomoyo_update_file_acl(filename, (u8) perm, domain,
+					      is_delete);
+	if (strncmp(data, "allow_", 6))
+		goto out;
+	data += 6;
+	for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) {
+		if (strcmp(data, tomoyo_sp_keyword[type]))
+			continue;
+		return tomoyo_update_single_path_acl(type, filename,
+						     domain, is_delete);
+	}
+	filename2 = strchr(filename, ' ');
+	if (!filename2)
+		goto out;
+	*filename2++ = '\0';
+	for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) {
+		if (strcmp(data, tomoyo_dp_keyword[type]))
+			continue;
+		return tomoyo_update_double_path_acl(type, filename, filename2,
+						     domain, is_delete);
+	}
+ out:
+	return -EINVAL;
+}
+
+/**
+ * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list.
+ *
+ * @type:      Type of operation.
+ * @filename:  Filename.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete)
+{
+	static DEFINE_MUTEX(lock);
+	static const u16 rw_mask =
+		(1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
+	const struct tomoyo_path_info *saved_filename;
+	struct tomoyo_acl_info *ptr;
+	struct tomoyo_single_path_acl_record *acl;
+	int error = -ENOMEM;
+	const u16 perm = 1 << type;
+
+	if (!domain)
+		return -EINVAL;
+	if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__))
+		return -EINVAL;
+	saved_filename = tomoyo_save_name(filename);
+	if (!saved_filename)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	if (is_delete)
+		goto delete;
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+				   head);
+		if (acl->filename != saved_filename)
+			continue;
+		/* Special case. Clear all bits if marked as deleted. */
+		if (ptr->type & TOMOYO_ACL_DELETED)
+			acl->perm = 0;
+		acl->perm |= perm;
+		if ((acl->perm & rw_mask) == rw_mask)
+			acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+		else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+			acl->perm |= rw_mask;
+		error = tomoyo_add_domain_acl(NULL, ptr);
+		goto out;
+	}
+	/* Not found. Append it to the tail. */
+	acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
+	if (!acl)
+		goto out;
+	acl->perm = perm;
+	acl->filename = saved_filename;
+	error = tomoyo_add_domain_acl(domain, &acl->head);
+	goto out;
+ delete:
+	error = -ENOENT;
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+				   head);
+		if (acl->filename != saved_filename)
+			continue;
+		acl->perm &= ~perm;
+		if ((acl->perm & rw_mask) != rw_mask)
+			acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
+		else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+			acl->perm &= ~rw_mask;
+		error = tomoyo_del_domain_acl(acl->perm ? NULL : ptr);
+		break;
+	}
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list.
+ *
+ * @type:      Type of operation.
+ * @filename1: First filename.
+ * @filename2: Second filename.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+					 const char *filename2,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete)
+{
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_filename1;
+	const struct tomoyo_path_info *saved_filename2;
+	struct tomoyo_acl_info *ptr;
+	struct tomoyo_double_path_acl_record *acl;
+	int error = -ENOMEM;
+	const u8 perm = 1 << type;
+
+	if (!domain)
+		return -EINVAL;
+	if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) ||
+	    !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__))
+		return -EINVAL;
+	saved_filename1 = tomoyo_save_name(filename1);
+	saved_filename2 = tomoyo_save_name(filename2);
+	if (!saved_filename1 || !saved_filename2)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	if (is_delete)
+		goto delete;
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+				   head);
+		if (acl->filename1 != saved_filename1 ||
+		    acl->filename2 != saved_filename2)
+			continue;
+		/* Special case. Clear all bits if marked as deleted. */
+		if (ptr->type & TOMOYO_ACL_DELETED)
+			acl->perm = 0;
+		acl->perm |= perm;
+		error = tomoyo_add_domain_acl(NULL, ptr);
+		goto out;
+	}
+	/* Not found. Append it to the tail. */
+	acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
+	if (!acl)
+		goto out;
+	acl->perm = perm;
+	acl->filename1 = saved_filename1;
+	acl->filename2 = saved_filename2;
+	error = tomoyo_add_domain_acl(domain, &acl->head);
+	goto out;
+ delete:
+	error = -ENOENT;
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+				   head);
+		if (acl->filename1 != saved_filename1 ||
+		    acl->filename2 != saved_filename2)
+			continue;
+		acl->perm &= ~perm;
+		error = tomoyo_del_domain_acl(acl->perm ? NULL : ptr);
+		break;
+	}
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * check_single_path_acl - Check permission for single path operation.
+ *
+ * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @type:     Type of operation.
+ * @filename: Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int check_single_path_acl(struct tomoyo_domain_info *domain,
+				 const u8 type,
+				 const struct tomoyo_path_info *filename)
+{
+	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+		return 0;
+	return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1);
+}
+
+/**
+ * check_double_path_acl - Check permission for double path operation.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @type:      Type of operation.
+ * @filename1: First filename to check.
+ * @filename2: Second filename to check.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int check_double_path_acl(const struct tomoyo_domain_info *domain,
+				 const u8 type,
+				 const struct tomoyo_path_info *filename1,
+				 const struct tomoyo_path_info *filename2)
+{
+	struct tomoyo_acl_info *ptr;
+	const u8 perm = 1 << type;
+
+	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+		return 0;
+	list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+		struct tomoyo_double_path_acl_record *acl;
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+				   head);
+		if (!(acl->perm & perm))
+			continue;
+		if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
+			continue;
+		if (!tomoyo_path_matches_pattern(filename2, acl->filename2))
+			continue;
+		return 0;
+	}
+	return -EPERM;
+}
+
+/**
+ * tomoyo_check_single_path_permission2 - Check permission for single path operation.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @filename:  Filename to check.
+ * @mode:      Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
+						const domain, u8 operation,
+						const struct tomoyo_path_info *
+						filename, const u8 mode)
+{
+	const char *msg;
+	int error;
+	const bool is_enforce = (mode == 3);
+
+	if (!mode)
+		return 0;
+ next:
+	error = check_single_path_acl(domain, operation, filename);
+	msg = tomoyo_sp2keyword(operation);
+	if (!error)
+		goto ok;
+	if (tomoyo_verbose_mode(domain))
+		printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n",
+		       tomoyo_get_msg(is_enforce), msg, filename->name,
+		       tomoyo_get_last_name(domain));
+	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		const char *name = tomoyo_get_file_pattern(filename)->name;
+		tomoyo_update_single_path_acl(operation, name, domain, false);
+	}
+	if (!is_enforce)
+		error = 0;
+ ok:
+	/*
+	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
+	 * we need to check "allow_rewrite" permission if the filename is
+	 * specified by "deny_rewrite" keyword.
+	 */
+	if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL &&
+	    tomoyo_is_no_rewrite_file(filename)) {
+		operation = TOMOYO_TYPE_REWRITE_ACL;
+		goto next;
+	}
+	return error;
+}
+
+/**
+ * tomoyo_check_file_perm - Check permission for sysctl()'s "read" and "write".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @perm:      Mode ("read" or "write" or "read/write").
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+			   const char *filename, const u8 perm)
+{
+	struct tomoyo_path_info name;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+	if (!mode)
+		return 0;
+	name.name = filename;
+	tomoyo_fill_path_info(&name);
+	return tomoyo_check_file_perm2(domain, &name, perm, "sysctl", mode);
+}
+
+/**
+ * tomoyo_check_exec_perm - Check permission for "execute".
+ *
+ * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @filename: Check permission for "execute".
+ * @tmp:      Buffer for temporary use.
+ *
+ * Returns 0 on success, negativevalue otherwise.
+ */
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+			   const struct tomoyo_path_info *filename,
+			   struct tomoyo_page_buffer *tmp)
+{
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+	if (!mode)
+		return 0;
+	return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode);
+}
+
+/**
+ * tomoyo_check_open_permission - Check permission for "read" and "write".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @path:   Pointer to "struct path".
+ * @flag:   Flags for open().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+				 struct path *path, const int flag)
+{
+	const u8 acc_mode = ACC_MODE(flag);
+	int error = -ENOMEM;
+	struct tomoyo_path_info *buf;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+
+	if (!mode || !path->mnt)
+		return 0;
+	if (acc_mode == 0)
+		return 0;
+	if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
+		/*
+		 * I don't check directories here because mkdir() and rmdir()
+		 * don't call me.
+		 */
+		return 0;
+	buf = tomoyo_get_path(path);
+	if (!buf)
+		goto out;
+	error = 0;
+	/*
+	 * If the filename is specified by "deny_rewrite" keyword,
+	 * we need to check "allow_rewrite" permission when the filename is not
+	 * opened for append mode or the filename is truncated at open time.
+	 */
+	if ((acc_mode & MAY_WRITE) &&
+	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
+	    (tomoyo_is_no_rewrite_file(buf))) {
+		error = tomoyo_check_single_path_permission2(domain,
+						     TOMOYO_TYPE_REWRITE_ACL,
+							     buf, mode);
+	}
+	if (!error)
+		error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
+						mode);
+	if (!error && (flag & O_TRUNC))
+		error = tomoyo_check_single_path_permission2(domain,
+						     TOMOYO_TYPE_TRUNCATE_ACL,
+							     buf, mode);
+ out:
+	tomoyo_free(buf);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path:      Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+			    const u8 operation, struct path *path)
+{
+	int error = -ENOMEM;
+	struct tomoyo_path_info *buf;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+
+	if (!mode || !path->mnt)
+		return 0;
+	buf = tomoyo_get_path(path);
+	if (!buf)
+		goto out;
+	switch (operation) {
+	case TOMOYO_TYPE_MKDIR_ACL:
+	case TOMOYO_TYPE_RMDIR_ACL:
+		if (!buf->is_dir) {
+			/*
+			 * tomoyo_get_path() reserves space for appending "/."
+			 */
+			strcat((char *) buf->name, "/");
+			tomoyo_fill_path_info(buf);
+		}
+	}
+	error = tomoyo_check_single_path_permission2(domain, operation, buf,
+						     mode);
+ out:
+	tomoyo_free(buf);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tomoyo_check_rewrite_permission - Check permission for "rewrite".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filp: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+				    struct file *filp)
+{
+	int error = -ENOMEM;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	struct tomoyo_path_info *buf;
+
+	if (!mode || !filp->f_path.mnt)
+		return 0;
+	buf = tomoyo_get_path(&filp->f_path);
+	if (!buf)
+		goto out;
+	if (!tomoyo_is_no_rewrite_file(buf)) {
+		error = 0;
+		goto out;
+	}
+	error = tomoyo_check_single_path_permission2(domain,
+						     TOMOYO_TYPE_REWRITE_ACL,
+						     buf, mode);
+ out:
+	tomoyo_free(buf);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path1:      Pointer to "struct path".
+ * @path2:      Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
+			    const u8 operation, struct path *path1,
+			    struct path *path2)
+{
+	int error = -ENOMEM;
+	struct tomoyo_path_info *buf1, *buf2;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	const char *msg;
+
+	if (!mode || !path1->mnt || !path2->mnt)
+		return 0;
+	buf1 = tomoyo_get_path(path1);
+	buf2 = tomoyo_get_path(path2);
+	if (!buf1 || !buf2)
+		goto out;
+	{
+		struct dentry *dentry = path1->dentry;
+		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+			/*
+			 * tomoyo_get_path() reserves space for appending "/."
+			 */
+			if (!buf1->is_dir) {
+				strcat((char *) buf1->name, "/");
+				tomoyo_fill_path_info(buf1);
+			}
+			if (!buf2->is_dir) {
+				strcat((char *) buf2->name, "/");
+				tomoyo_fill_path_info(buf2);
+			}
+		}
+	}
+	error = check_double_path_acl(domain, operation, buf1, buf2);
+	msg = tomoyo_dp2keyword(operation);
+	if (!error)
+		goto out;
+	if (tomoyo_verbose_mode(domain))
+		printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' "
+		       "denied for %s\n", tomoyo_get_msg(is_enforce),
+		       msg, buf1->name, buf2->name,
+		       tomoyo_get_last_name(domain));
+	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		const char *name1 = tomoyo_get_file_pattern(buf1)->name;
+		const char *name2 = tomoyo_get_file_pattern(buf2)->name;
+		tomoyo_update_double_path_acl(operation, name1, name2, domain,
+					      false);
+	}
+ out:
+	tomoyo_free(buf1);
+	tomoyo_free(buf2);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 07/10] Domain transition handler.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (5 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 06/10] File operation restriction part Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 08/10] LSM adapter functions Tetsuo Handa
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda,
	Tetsuo Handa, Toshiharu Harada

[-- Attachment #1: tomoyo-domain-transition-part.patch --]
[-- Type: text/plain, Size: 26407 bytes --]

This file controls domain creation/deletion/transition.

Every process belongs to a domain in TOMOYO Linux.
Domain transition occurs when execve(2) is called
and the domain is expressed as 'process invocation history',
such as '<kernel> /sbin/init /etc/init.d/rc'.
Domain information is stored in current->cred->security field.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 security/tomoyo/domain.c |  874 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 874 insertions(+)

--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/domain.c
@@ -0,0 +1,874 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/binfmts.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/*
+ * The list for "struct tomoyo_domain_info".
+ *
+ * The tomoyo_domain_list_lock mutex protects the tomoyo_domain_list list.
+ */
+LIST1_HEAD(tomoyo_domain_list);
+static DEFINE_MUTEX(tomoyo_domain_list_lock);
+
+/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
+struct tomoyo_domain_initializer_entry {
+	struct list1_head list;
+	const struct tomoyo_path_info *domainname;    /* This may be NULL */
+	const struct tomoyo_path_info *program;
+	bool is_deleted;
+	bool is_not;       /* True if this entry is "no_initialize_domain".  */
+	/* True if the domainname is tomoyo_get_last_name(). */
+	bool is_last_name;
+};
+
+/* Structure for "keep_domain" and "no_keep_domain" keyword. */
+struct tomoyo_domain_keeper_entry {
+	struct list1_head list;
+	const struct tomoyo_path_info *domainname;
+	const struct tomoyo_path_info *program;       /* This may be NULL */
+	bool is_deleted;
+	bool is_not;       /* True if this entry is "no_keep_domain".        */
+	/* True if the domainname is tomoyo_get_last_name(). */
+	bool is_last_name;
+};
+
+/* Structure for "alias" keyword. */
+struct tomoyo_alias_entry {
+	struct list1_head list;
+	const struct tomoyo_path_info *original_name;
+	const struct tomoyo_path_info *aliased_name;
+	bool is_deleted;
+};
+
+/**
+ * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ * @flags:     Flags to set or clear.
+ *
+ * Returns nothing.
+ */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+			    const bool is_delete, const u8 flags)
+{
+	/* We need to serialize because this is bitfield operation. */
+	static DEFINE_SPINLOCK(lock);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&lock);
+	if (!is_delete)
+		domain->flags |= flags;
+	else
+		domain->flags &= ~flags;
+	spin_unlock(&lock);
+	/***** CRITICAL SECTION END *****/
+}
+
+/**
+ * tomoyo_get_last_name - Get last component of a domainname.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns the last component of the domainname.
+ */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
+{
+	const char *cp0 = domain->domainname->name;
+	const char *cp1 = strrchr(cp0, ' ');
+
+	if (cp1)
+		return cp1 + 1;
+	return cp0;
+}
+
+/*
+ * The list for "struct tomoyo_domain_initializer_entry".
+ *
+ * This list is updated only inside
+ * tomoyo_update_domain_initializer_entry(),
+ * thus no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_domain_initializer_list);
+
+/**
+ * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @domainname: The name of domain. May be NULL.
+ * @program:    The name of program.
+ * @is_not:     True if it is "no_initialize_domain" entry.
+ * @is_delete:  True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_initializer_entry(const char *domainname,
+						  const char *program,
+						  const bool is_not,
+						  const bool is_delete)
+{
+	struct tomoyo_domain_initializer_entry *new_entry;
+	struct tomoyo_domain_initializer_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_program;
+	const struct tomoyo_path_info *saved_domainname = NULL;
+	int error = -ENOMEM;
+	bool is_last_name = false;
+
+	if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+		return -EINVAL; /* No patterns allowed. */
+	if (domainname) {
+		if (!tomoyo_is_domain_def(domainname) &&
+		    tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+			is_last_name = true;
+		else if (!tomoyo_is_correct_domain(domainname, __func__))
+			return -EINVAL;
+		saved_domainname = tomoyo_save_name(domainname);
+		if (!saved_domainname)
+			return -ENOMEM;
+	}
+	saved_program = tomoyo_save_name(program);
+	if (!saved_program)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+		if (ptr->is_not != is_not ||
+		    ptr->domainname != saved_domainname ||
+		    ptr->program != saved_program)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->domainname = saved_domainname;
+	new_entry->program = saved_program;
+	new_entry->is_not = is_not;
+	new_entry->is_last_name = is_last_name;
+	list1_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2,
+			      &tomoyo_domain_initializer_list) {
+		const char *no;
+		const char *from = "";
+		const char *domain = "";
+		struct tomoyo_domain_initializer_entry *ptr;
+		ptr = list1_entry(pos, struct tomoyo_domain_initializer_entry,
+				  list);
+		if (ptr->is_deleted)
+			continue;
+		no = ptr->is_not ? "no_" : "";
+		if (ptr->domainname) {
+			from = " from ";
+			domain = ptr->domainname->name;
+		}
+		if (!tomoyo_io_printf(head,
+				      "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+				      "%s%s%s\n", no, ptr->program->name, from,
+				      domain))
+			goto out;
+	}
+	return true;
+ out:
+	return false;
+}
+
+/**
+ * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_not:    True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+					   const bool is_delete)
+{
+	char *cp = strstr(data, " from ");
+
+	if (cp) {
+		*cp = '\0';
+		return tomoyo_update_domain_initializer_entry(cp + 6, data,
+							      is_not,
+							      is_delete);
+	}
+	return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
+						      is_delete);
+}
+
+/**
+ * is_domain_initializer - Check whether the given program causes domainname reinitialization.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program.
+ * @last_name:  The last component of @domainname.
+ *
+ * Returns true if executing @program reinitializes domain transition,
+ * false otherwise.
+ */
+static bool is_domain_initializer(const struct tomoyo_path_info *domainname,
+				  const struct tomoyo_path_info *program,
+				  const struct tomoyo_path_info *last_name)
+{
+	struct tomoyo_domain_initializer_entry *ptr;
+	bool flag = false;
+
+	list1_for_each_entry(ptr,  &tomoyo_domain_initializer_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (ptr->domainname) {
+			if (!ptr->is_last_name) {
+				if (ptr->domainname != domainname)
+					continue;
+			} else {
+				if (tomoyo_pathcmp(ptr->domainname, last_name))
+					continue;
+			}
+		}
+		if (tomoyo_pathcmp(ptr->program, program))
+			continue;
+		if (ptr->is_not)
+			return false;
+		flag = true;
+	}
+	return flag;
+}
+
+/*
+ * The list for "struct tomoyo_domain_keeper_entry".
+ *
+ * This list is updated only inside tomoyo_update_domain_keeper_entry(),
+ * thus no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_domain_keeper_list);
+
+/**
+ * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program. May be NULL.
+ * @is_not:     True if it is "no_keep_domain" entry.
+ * @is_delete:  True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_keeper_entry(const char *domainname,
+					     const char *program,
+					     const bool is_not,
+					     const bool is_delete)
+{
+	struct tomoyo_domain_keeper_entry *new_entry;
+	struct tomoyo_domain_keeper_entry *ptr;
+	const struct tomoyo_path_info *saved_domainname;
+	const struct tomoyo_path_info *saved_program = NULL;
+	static DEFINE_MUTEX(lock);
+	int error = -ENOMEM;
+	bool is_last_name = false;
+
+	if (!tomoyo_is_domain_def(domainname) &&
+	    tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+		is_last_name = true;
+	else if (!tomoyo_is_correct_domain(domainname, __func__))
+		return -EINVAL;
+	if (program) {
+		if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+			return -EINVAL;
+		saved_program = tomoyo_save_name(program);
+		if (!saved_program)
+			return -ENOMEM;
+	}
+	saved_domainname = tomoyo_save_name(domainname);
+	if (!saved_domainname)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+		if (ptr->is_not != is_not ||
+		    ptr->domainname != saved_domainname ||
+		    ptr->program != saved_program)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->domainname = saved_domainname;
+	new_entry->program = saved_program;
+	new_entry->is_not = is_not;
+	new_entry->is_last_name = is_last_name;
+	list1_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_not:    True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+				      const bool is_delete)
+{
+	char *cp = strstr(data, " from ");
+
+	if (cp) {
+		*cp = '\0';
+		return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not,
+							 is_delete);
+	}
+	return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
+}
+
+/**
+ * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2,
+			      &tomoyo_domain_keeper_list) {
+		struct tomoyo_domain_keeper_entry *ptr;
+		const char *no;
+		const char *from = "";
+		const char *program = "";
+
+		ptr = list1_entry(pos, struct tomoyo_domain_keeper_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		no = ptr->is_not ? "no_" : "";
+		if (ptr->program) {
+			from = " from ";
+			program = ptr->program->name;
+		}
+		if (!tomoyo_io_printf(head,
+				      "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+				      "%s%s%s\n", no, program, from,
+				      ptr->domainname->name))
+			goto out;
+	}
+	return true;
+ out:
+	return false;
+}
+
+/**
+ * is_domain_keeper - Check whether the given program causes domain transition suppression.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program.
+ * @last_name:  The last component of @domainname.
+ *
+ * Returns true if executing @program supresses domain transition,
+ * false otherwise.
+ */
+static bool is_domain_keeper(const struct tomoyo_path_info *domainname,
+			     const struct tomoyo_path_info *program,
+			     const struct tomoyo_path_info *last_name)
+{
+	struct tomoyo_domain_keeper_entry *ptr;
+	bool flag = false;
+
+	list1_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!ptr->is_last_name) {
+			if (ptr->domainname != domainname)
+				continue;
+		} else {
+			if (tomoyo_pathcmp(ptr->domainname, last_name))
+				continue;
+		}
+		if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+			continue;
+		if (ptr->is_not)
+			return false;
+		flag = true;
+	}
+	return flag;
+}
+
+/*
+ * The list for "struct tomoyo_alias_entry".
+ *
+ * This list is updated only inside tomoyo_update_alias_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(tomoyo_alias_list);
+
+/**
+ * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
+ *
+ * @original_name: The original program's real name.
+ * @aliased_name:  The symbolic program's symbolic link's name.
+ * @is_delete:     True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_alias_entry(const char *original_name,
+				     const char *aliased_name,
+				     const bool is_delete)
+{
+	struct tomoyo_alias_entry *new_entry;
+	struct tomoyo_alias_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct tomoyo_path_info *saved_original_name;
+	const struct tomoyo_path_info *saved_aliased_name;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
+	    !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+		return -EINVAL; /* No patterns allowed. */
+	saved_original_name = tomoyo_save_name(original_name);
+	saved_aliased_name = tomoyo_save_name(aliased_name);
+	if (!saved_original_name || !saved_aliased_name)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &tomoyo_alias_list, list) {
+		if (ptr->original_name != saved_original_name ||
+		    ptr->aliased_name != saved_aliased_name)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->original_name = saved_original_name;
+	new_entry->aliased_name = saved_aliased_name;
+	list1_add_tail(&new_entry->list, &tomoyo_alias_list);
+	error = 0;
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
+		struct tomoyo_alias_entry *ptr;
+
+		ptr = list1_entry(pos, struct tomoyo_alias_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
+				      ptr->original_name->name,
+				      ptr->aliased_name->name))
+			goto out;
+	}
+	return true;
+ out:
+	return false;
+}
+
+/**
+ * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_alias_policy(char *data, const bool is_delete)
+{
+	char *cp = strchr(data, ' ');
+
+	if (!cp)
+		return -EINVAL;
+	*cp++ = '\0';
+	return tomoyo_update_alias_entry(data, cp, is_delete);
+}
+
+/* Domain create/delete/undelete handler. */
+
+/* #define TOMOYO_DEBUG_DOMAIN_UNDELETE */
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+int tomoyo_delete_domain(char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_path_info name;
+
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+	printk(KERN_DEBUG "tomoyo_delete_domain %s\n", domainname);
+	list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+	}
+#endif
+	/* Is there an active domain? */
+	list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+		struct tomoyo_domain_info *domain2;
+		/* Never delete tomoyo_kernel_domain */
+		if (domain == &tomoyo_kernel_domain)
+			continue;
+		if (domain->is_deleted ||
+		    tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		/* Mark already deleted domains as non undeletable. */
+		list1_for_each_entry(domain2, &tomoyo_domain_list, list) {
+			if (!domain2->is_deleted ||
+			    tomoyo_pathcmp(domain2->domainname, &name))
+				continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+			if (domain2->is_deleted != 255)
+				printk(KERN_DEBUG
+				       "Marked %p as non undeletable\n",
+				       domain2);
+#endif
+			domain2->is_deleted = 255;
+		}
+		/* Delete and mark active domain as undeletable. */
+		domain->is_deleted = 1;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "Marked %p as undeletable\n", domain);
+#endif
+		break;
+	}
+	mutex_unlock(&tomoyo_domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return 0;
+}
+
+/**
+ * tomoyo_undelete_domain - Undelete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_domain_info *candidate_domain = NULL;
+	struct tomoyo_path_info name;
+
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+	printk(KERN_DEBUG "tomoyo_undelete_domain %s\n", domainname);
+	list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+	}
+#endif
+	list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (tomoyo_pathcmp(&name, domain->domainname))
+			continue;
+		if (!domain->is_deleted) {
+			/* This domain is active. I can't undelete. */
+			candidate_domain = NULL;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+			printk(KERN_DEBUG "%p is active. I can't undelete.\n",
+			       domain);
+#endif
+			break;
+		}
+		/* Is this domain undeletable? */
+		if (domain->is_deleted == 1)
+			candidate_domain = domain;
+	}
+	if (candidate_domain) {
+		candidate_domain->is_deleted = 0;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);
+#endif
+	}
+	mutex_unlock(&tomoyo_domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return candidate_domain;
+}
+
+/**
+ * tomoyo_find_or_assign_new_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile:    Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+							    domainname,
+							    const u8 profile)
+{
+	struct tomoyo_domain_info *domain = NULL;
+	const struct tomoyo_path_info *saved_domainname;
+
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&tomoyo_domain_list_lock);
+	domain = tomoyo_find_domain(domainname);
+	if (domain)
+		goto out;
+	if (!tomoyo_is_correct_domain(domainname, __func__))
+		goto out;
+	saved_domainname = tomoyo_save_name(domainname);
+	if (!saved_domainname)
+		goto out;
+	/* Can I reuse memory of deleted domain? */
+	list1_for_each_entry(domain, &tomoyo_domain_list, list) {
+		struct task_struct *p;
+		struct tomoyo_acl_info *ptr;
+		bool flag;
+		if (!domain->is_deleted ||
+		    domain->domainname != saved_domainname)
+			continue;
+		flag = false;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		for_each_process(p) {
+			if (tomoyo_real_domain(p) != domain)
+				continue;
+			flag = true;
+			break;
+		}
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+		if (flag)
+			continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "Reusing %p %s\n", domain,
+		       domain->domainname->name);
+#endif
+		list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+			ptr->type |= TOMOYO_ACL_DELETED;
+		}
+		tomoyo_set_domain_flag(domain, true, domain->flags);
+		domain->profile = profile;
+		domain->quota_warned = false;
+		mb(); /* Avoid out-of-order execution. */
+		domain->is_deleted = 0;
+		goto out;
+	}
+	/* No memory reusable. Create using new memory. */
+	domain = tomoyo_alloc_element(sizeof(*domain));
+	if (domain) {
+		INIT_LIST1_HEAD(&domain->acl_info_list);
+		domain->domainname = saved_domainname;
+		domain->profile = profile;
+		list1_add_tail(&domain->list, &tomoyo_domain_list);
+	}
+ out:
+	mutex_unlock(&tomoyo_domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return domain;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @bprm:           Pointer to "struct linux_binprm".
+ * @next_domain:    Pointer to pointer to "struct tomoyo_domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+			    struct tomoyo_domain_info **next_domain)
+{
+	/*
+	 * This function assumes that the size of buffer returned by
+	 * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
+	 */
+	struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+	struct tomoyo_domain_info *old_domain = tomoyo_domain();
+	struct tomoyo_domain_info *domain = NULL;
+	const char *old_domain_name = old_domain->domainname->name;
+	const char *original_name = bprm->filename;
+	char *new_domain_name = NULL;
+	char *real_program_name = NULL;
+	char *symlink_program_name = NULL;
+	const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	int retval = -ENOMEM;
+	struct tomoyo_path_info r; /* real name */
+	struct tomoyo_path_info s; /* symlink name */
+	struct tomoyo_path_info l; /* last name */
+	static bool initialized;
+
+	if (!tmp)
+		goto out;
+
+	if (!initialized) {
+		/*
+		 * Built-in initializers. This is needed because policies are
+		 * not loaded until starting /sbin/init.
+		 */
+		tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug",
+						       false, false);
+		tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe",
+						       false, false);
+		initialized = true;
+	}
+
+	/* Get tomoyo_realpath of program. */
+	retval = -ENOENT;
+	/* I hope tomoyo_realpath() won't fail with -ENOMEM. */
+	real_program_name = tomoyo_realpath(original_name);
+	if (!real_program_name)
+		goto out;
+	/* Get tomoyo_realpath of symbolic link. */
+	symlink_program_name = tomoyo_realpath_nofollow(original_name);
+	if (!symlink_program_name)
+		goto out;
+
+	r.name = real_program_name;
+	tomoyo_fill_path_info(&r);
+	s.name = symlink_program_name;
+	tomoyo_fill_path_info(&s);
+	l.name = tomoyo_get_last_name(old_domain);
+	tomoyo_fill_path_info(&l);
+
+	/* Check 'alias' directive. */
+	if (tomoyo_pathcmp(&r, &s)) {
+		struct tomoyo_alias_entry *ptr;
+		/* Is this program allowed to be called via symbolic links? */
+		list1_for_each_entry(ptr, &tomoyo_alias_list, list) {
+			if (ptr->is_deleted ||
+			    tomoyo_pathcmp(&r, ptr->original_name) ||
+			    tomoyo_pathcmp(&s, ptr->aliased_name))
+				continue;
+			memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
+			strncpy(real_program_name, ptr->aliased_name->name,
+				TOMOYO_MAX_PATHNAME_LEN - 1);
+			tomoyo_fill_path_info(&r);
+			break;
+		}
+	}
+
+	/* Check execute permission. */
+	retval = tomoyo_check_exec_perm(old_domain, &r, tmp);
+	if (retval < 0)
+		goto out;
+
+	new_domain_name = tmp->buffer;
+	if (is_domain_initializer(old_domain->domainname, &r, &l)) {
+		/* Transit to the child of tomoyo_kernel_domain domain. */
+		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+			 TOMOYO_ROOT_NAME " " "%s", real_program_name);
+	} else if (old_domain == &tomoyo_kernel_domain &&
+		   !tomoyo_policy_loaded) {
+		/*
+		 * Needn't to transit from kernel domain before starting
+		 * /sbin/init. But transit from kernel domain if executing
+		 * initializers because they might start before /sbin/init.
+		 */
+		domain = old_domain;
+	} else if (is_domain_keeper(old_domain->domainname, &r, &l)) {
+		/* Keep current domain. */
+		domain = old_domain;
+	} else {
+		/* Normal domain transition. */
+		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+			 "%s %s", old_domain_name, real_program_name);
+	}
+	if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
+		goto done;
+	domain = tomoyo_find_domain(new_domain_name);
+	if (domain)
+		goto done;
+	if (is_enforce)
+		goto done;
+	domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+						  old_domain->profile);
+ done:
+	if (domain)
+		goto out;
+	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
+	       new_domain_name);
+	if (is_enforce)
+		retval = -EPERM;
+	else
+		tomoyo_set_domain_flag(old_domain, false,
+				       TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+ out:
+	tomoyo_free(real_program_name);
+	tomoyo_free(symlink_program_name);
+	*next_domain = domain ? domain : old_domain;
+	tomoyo_free(tmp);
+	return retval;
+}

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 08/10] LSM adapter functions.
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (6 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 07/10] Domain transition handler Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 09/10] Kconfig and Makefile Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 10/10] MAINTAINERS info Tetsuo Handa
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda,
	Tetsuo Handa, Toshiharu Harada

[-- Attachment #1: tomoyo-hooks.patch --]
[-- Type: text/plain, Size: 12723 bytes --]

This is LSM adapter for TOMOYO.
This version of TOMOYO assigns only one variable "struct tomoyo_domain_info *"
to "current->cred->security".

Future version will require two variables "struct tomoyo_domain_info *" and
"u32".
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/include/linux/sched.h#L1311
Well, how to implement this while "current->cred->security" cannot be modified
freely?

To be honest, the only object which TOMOYO needs to assign variables is
"struct task_struct". TOMOYO's "T" means "task".
TOMOYO utilizes characteristics of "struct task_struct".
For TOMOYO, use of per task variables is vital.

Removal of security_task_alloc() and security_task_free() by introduction of
COW credentials will become a nightmare when TOMOYO tries to add "u32".
Of cource, if TOMOYO is allowed to add "u32" to "struct task_struct",
COW credentials won't become a nightmare.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 security/tomoyo/tomoyo.c |  293 +++++++++++++++++++++++++++++++++++++++++++++++
 security/tomoyo/tomoyo.h |  106 +++++++++++++++++
 2 files changed, 399 insertions(+)

--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/tomoyo.c
@@ -0,0 +1,293 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#include <linux/security.h>
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+			       gfp_t gfp)
+{
+	/*
+	 * Since "struct tomoyo_domain_info *" is a sharable pointer,
+	 * we don't need to duplicate.
+	 */
+	new->security = old->security;
+	return 0;
+}
+
+static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
+{
+	/*
+	 * Do only if this function is called for the first time of an execve
+	 * operation.
+	 */
+	if (bprm->cred_prepared)
+		return 0;
+	/*
+	 * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
+	 * for the first time.
+	 */
+	if (!tomoyo_policy_loaded)
+		tomoyo_load_policy(bprm->filename);
+	/*
+	 * Tell tomoyo_bprm_check_security() is called for the first time of an
+	 * execve operation.
+	 */
+	bprm->cred->security = NULL;
+	return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+	struct tomoyo_domain_info *domain = bprm->cred->security;
+
+	/*
+	 * Execute permission is checked against pathname passed to do_execve()
+	 * using current domain.
+	 */
+	if (!domain) {
+		struct tomoyo_domain_info *next_domain = NULL;
+		int retval = tomoyo_find_next_domain(bprm, &next_domain);
+
+		if (!retval)
+			bprm->cred->security = next_domain;
+		return retval;
+	}
+	/*
+	 * Read permission is checked against interpreters using next domain.
+	 * '1' is the result of open_to_namei_flags(O_RDONLY).
+	 */
+	return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1);
+}
+
+#ifdef CONFIG_SYSCTL
+
+static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
+{
+	int namelen = strlen(str);
+
+	if (*buflen < namelen)
+		return -ENOMEM;
+	*buflen -= namelen;
+	*buffer -= namelen;
+	memcpy(*buffer, str, namelen);
+	return 0;
+}
+
+/**
+ * sysctlpath_from_table - return the realpath of a ctl_table.
+ * @table: pointer to "struct ctl_table".
+ *
+ * Returns realpath(3) of the @table on success.
+ * Returns NULL on failure.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static char *sysctl_path(struct ctl_table *table)
+{
+	int buflen = TOMOYO_MAX_PATHNAME_LEN;
+	char *buf = tomoyo_alloc(buflen);
+	char *end = buf + buflen;
+	int error = -ENOMEM;
+
+	if (!buf)
+		return NULL;
+
+	*--end = '\0';
+	buflen--;
+	while (table) {
+		char buf[32];
+		const char *sp = table->procname;
+
+		if (!sp) {
+			memset(buf, 0, sizeof(buf));
+			snprintf(buf, sizeof(buf) - 1, "=%d=", table->ctl_name);
+			sp = buf;
+		}
+		if (tomoyo_prepend(&end, &buflen, sp) ||
+		    tomoyo_prepend(&end, &buflen, "/"))
+			goto out;
+		table = table->parent;
+	}
+	if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
+		goto out;
+	error = tomoyo_encode(buf, end - buf, end);
+ out:
+	if (!error)
+		return buf;
+	tomoyo_free(buf);
+	return NULL;
+}
+
+static int tomoyo_sysctl(struct ctl_table *table, int op)
+{
+	int error;
+	char *name;
+
+	op &= MAY_READ | MAY_WRITE;
+	if (!op)
+		return 0;
+	name = sysctl_path(table);
+	if (!name)
+		return -ENOMEM;
+	error = tomoyo_check_file_perm(tomoyo_domain(), name, op);
+	tomoyo_free(name);
+	return error;
+}
+#endif
+
+static int tomoyo_path_truncate(struct path *path, loff_t length,
+				unsigned int time_attrs)
+{
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_TRUNCATE_ACL,
+				       path);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_UNLINK_ACL,
+				       &path);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+			     int mode)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_MKDIR_ACL,
+				       &path);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_RMDIR_ACL,
+				       &path);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+			       const char *old_name)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_SYMLINK_ACL,
+				       &path);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+			     int mode, unsigned int dev)
+{
+	struct path path = { parent->mnt, dentry };
+	int type = TOMOYO_TYPE_CREATE_ACL;
+
+	switch (mode & S_IFMT) {
+	case S_IFCHR:
+		type = TOMOYO_TYPE_MKCHAR_ACL;
+		break;
+	case S_IFBLK:
+		type = TOMOYO_TYPE_MKBLOCK_ACL;
+		break;
+	case S_IFIFO:
+		type = TOMOYO_TYPE_MKFIFO_ACL;
+		break;
+	case S_IFSOCK:
+		type = TOMOYO_TYPE_MKSOCK_ACL;
+		break;
+	}
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       type, &path);
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+			    struct dentry *new_dentry)
+{
+	struct path path1 = { new_dir->mnt, old_dentry };
+	struct path path2 = { new_dir->mnt, new_dentry };
+	return tomoyo_check_2path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_LINK_ACL,
+				       &path1, &path2);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+			      struct dentry *old_dentry,
+			      struct path *new_parent,
+			      struct dentry *new_dentry)
+{
+	struct path path1 = { old_parent->mnt, old_dentry };
+	struct path path2 = { new_parent->mnt, new_dentry };
+	return tomoyo_check_2path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_RENAME_ACL,
+				       &path1, &path2);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+			     unsigned long arg)
+{
+	if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+		return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+	return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+	int flags = f->f_flags;
+
+	if ((flags + 1) & O_ACCMODE)
+		flags++;
+	flags |= f->f_flags & (O_APPEND | O_TRUNC);
+	/* Don't check read permission here if called from do_execve(). */
+	if (current->in_execve)
+		return 0;
+	return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+}
+
+static struct security_operations tomoyo_security_ops = {
+	.name                = "tomoyo",
+	.cred_prepare        = tomoyo_cred_prepare,
+	.bprm_set_creds      = tomoyo_bprm_set_creds,
+	.bprm_check_security = tomoyo_bprm_check_security,
+#ifdef CONFIG_SYSCTL
+	.sysctl              = tomoyo_sysctl,
+#endif
+	.file_fcntl          = tomoyo_file_fcntl,
+	.dentry_open         = tomoyo_dentry_open,
+	.path_truncate       = tomoyo_path_truncate,
+	.path_unlink         = tomoyo_path_unlink,
+	.path_mkdir          = tomoyo_path_mkdir,
+	.path_rmdir          = tomoyo_path_rmdir,
+	.path_symlink        = tomoyo_path_symlink,
+	.path_mknod          = tomoyo_path_mknod,
+	.path_link           = tomoyo_path_link,
+	.path_rename         = tomoyo_path_rename,
+};
+
+static int __init tomoyo_init(void)
+{
+	struct cred *cred = (struct cred *) current_cred();
+
+	if (!security_module_enable(&tomoyo_security_ops))
+		return 0;
+	/* register ourselves with the security framework */
+	if (register_security(&tomoyo_security_ops))
+		panic("Failure registering TOMOYO Linux");
+	printk(KERN_INFO "TOMOYO Linux initialized\n");
+	cred->security = &tomoyo_kernel_domain;
+	return 0;
+}
+
+security_initcall(tomoyo_init);
--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/tomoyo.h
@@ -0,0 +1,106 @@
+/*
+ * security/tomoyo/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/01/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_TOMOYO_H
+#define _SECURITY_TOMOYO_TOMOYO_H
+
+struct tomoyo_path_info;
+struct path;
+struct inode;
+struct linux_binprm;
+struct pt_regs;
+struct tomoyo_page_buffer;
+
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+			   const char *filename, const u8 perm);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+			   const struct tomoyo_path_info *filename,
+			   struct tomoyo_page_buffer *buf);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+				 struct path *path, const int flag);
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+			    const u8 operation, struct path *path);
+int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
+			    const u8 operation, struct path *path1,
+			    struct path *path2);
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+				    struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+			    struct tomoyo_domain_info **next_domain);
+
+/* Index numbers for Access Controls. */
+
+#define TOMOYO_TYPE_SINGLE_PATH_ACL                 0
+#define TOMOYO_TYPE_DOUBLE_PATH_ACL                 1
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+#define TOMOYO_TYPE_READ_WRITE_ACL    0
+#define TOMOYO_TYPE_EXECUTE_ACL       1
+#define TOMOYO_TYPE_READ_ACL          2
+#define TOMOYO_TYPE_WRITE_ACL         3
+#define TOMOYO_TYPE_CREATE_ACL        4
+#define TOMOYO_TYPE_UNLINK_ACL        5
+#define TOMOYO_TYPE_MKDIR_ACL         6
+#define TOMOYO_TYPE_RMDIR_ACL         7
+#define TOMOYO_TYPE_MKFIFO_ACL        8
+#define TOMOYO_TYPE_MKSOCK_ACL        9
+#define TOMOYO_TYPE_MKBLOCK_ACL      10
+#define TOMOYO_TYPE_MKCHAR_ACL       11
+#define TOMOYO_TYPE_TRUNCATE_ACL     12
+#define TOMOYO_TYPE_SYMLINK_ACL      13
+#define TOMOYO_TYPE_REWRITE_ACL      14
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
+
+#define TOMOYO_TYPE_LINK_ACL         0
+#define TOMOYO_TYPE_RENAME_ACL       1
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+
+#define TOMOYO_DOMAINPOLICY          0
+#define TOMOYO_EXCEPTIONPOLICY       1
+#define TOMOYO_DOMAIN_STATUS         2
+#define TOMOYO_PROCESS_STATUS        3
+#define TOMOYO_MEMINFO               4
+#define TOMOYO_SELFDOMAIN            5
+#define TOMOYO_VERSION               6
+#define TOMOYO_PROFILE               7
+#define TOMOYO_MANAGER               8
+
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+	return current_cred()->security;
+}
+
+/* Caller holds tasklist_lock spinlock. */
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+							    *task)
+{
+	/***** CRITICAL SECTION START *****/
+	const struct cred *cred = get_task_cred(task);
+	struct tomoyo_domain_info *domain = cred->security;
+
+	put_cred(cred);
+	return domain;
+	/***** CRITICAL SECTION END *****/
+}
+
+#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 09/10] Kconfig and Makefile
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (7 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 08/10] LSM adapter functions Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 10/10] MAINTAINERS info Tetsuo Handa
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-security-module, linux-kernel, Kentaro Takeda, Tetsuo Handa

[-- Attachment #1: tomoyo-makefile.patch --]
[-- Type: text/plain, Size: 1812 bytes --]

TOMOYO uses LSM hooks for pathname based access control and securityfs support.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/Kconfig         |    1 +
 security/Makefile        |    2 ++
 security/tomoyo/Kconfig  |   11 +++++++++++
 security/tomoyo/Makefile |    1 +
 4 files changed, 15 insertions(+)

--- linux-2.6.28-mm1.orig/security/Kconfig
+++ linux-2.6.28-mm1/security/Kconfig
@@ -134,6 +134,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
 
 source security/selinux/Kconfig
 source security/smack/Kconfig
+source security/tomoyo/Kconfig
 
 endmenu
 
--- linux-2.6.28-mm1.orig/security/Makefile
+++ linux-2.6.28-mm1/security/Makefile
@@ -5,6 +5,7 @@
 obj-$(CONFIG_KEYS)			+= keys/
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
+subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 
 # always enable default capabilities
 obj-y		+= commoncap.o
@@ -17,3 +18,4 @@ obj-$(CONFIG_SECURITY_SELINUX)		+= selin
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
+obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/Kconfig
@@ -0,0 +1,11 @@
+config SECURITY_TOMOYO
+	bool "TOMOYO Linux Support"
+	depends on SECURITY
+	select SECURITYFS
+	select SECURITY_PATH
+	default n
+	help
+	  This selects TOMOYO Linux, pathname-based access control.
+	  Required userspace tools and further information may be
+	  found at <http://tomoyo.sourceforge.jp/>.
+	  If you are unsure how to answer this question, answer N.
--- /dev/null
+++ linux-2.6.28-mm1/security/tomoyo/Makefile
@@ -0,0 +1 @@
+obj-y = common.o realpath.o tomoyo.o domain.o file.o

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [TOMOYO #14 (mmotm 2008-12-30-16-05) 10/10] MAINTAINERS info
  2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
                   ` (8 preceding siblings ...)
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 09/10] Kconfig and Makefile Tetsuo Handa
@ 2009-01-01  5:07 ` Tetsuo Handa
  9 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-01  5:07 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-security-module, linux-kernel, Kentaro Takeda

[-- Attachment #1: tomoyo-maintainers.patch --]
[-- Type: text/plain, Size: 1326 bytes --]

The archive of tomoyo-users-en mailing list is available at
http://lists.sourceforge.jp/mailman/archives/tomoyo-users-en/ .
Mailing lists for Japanese users are at
http://lists.sourceforge.jp/mailman/archives/tomoyo-users/ and
http://lists.sourceforge.jp/mailman/archives/tomoyo-dev/ .

TOMOYO Linux English portal is at
http://elinux.org/TomoyoLinux .

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
---
 MAINTAINERS |   13 +++++++++++++
 1 file changed, 13 insertions(+)

--- linux-2.6.28-mm1.orig/MAINTAINERS
+++ linux-2.6.28-mm1/MAINTAINERS
@@ -4251,6 +4251,19 @@ L:	tlan-devel@lists.sourceforge.net (sub
 W:	http://sourceforge.net/projects/tlan/
 S:	Maintained
 
+TOMOYO SECURITY MODULE
+P:	Kentaro Takeda
+M:	takedakn@nttdata.co.jp
+P:	Tetsuo Handa
+M:	penguin-kernel@I-love.SAKURA.ne.jp
+L:	linux-kernel@vger.kernel.org (kernel issues)
+L:	tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and users in English)
+L:	tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
+L:	tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
+W:	http://tomoyo.sourceforge.jp/
+T:	quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.2.x/tomoyo-lsm/patches/
+S:	Maintained
+
 TOSHIBA ACPI EXTRAS DRIVER
 P:	John Belmonte
 M:	toshiba_acpi@memebeam.org

-- 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct.
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct Tetsuo Handa
@ 2009-01-05  8:25   ` James Morris
  0 siblings, 0 replies; 21+ messages in thread
From: James Morris @ 2009-01-05  8:25 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Andrew Morton, linux-security-module, linux-kernel,
	David Howells, Serge E. Hallyn

On Thu, 1 Jan 2009, Tetsuo Handa wrote:

> This patch allows LSM modules to determine whether current process is in an
> execve operation or not so that they can behave differently while an execve
> operation is in progress.
> 
> This allows TOMOYO to dispense with a readability check on a file to be
> executed under the process's current credentials, and to do it instead under
> the proposed credentials.
> 
> This is required with the new COW credentials because TOMOYO is no longer
> allowed to mark the state temporarily in the security struct attached to the
> task_struct.

Serge previously queried this flag:

http://article.gmane.org/gmane.linux.kernel.lsm/7611

The issue does not appear to have been resolved.


> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>  fs/compat.c           |    3 +++
>  fs/exec.c             |    3 +++
>  include/linux/sched.h |    2 ++
>  3 files changed, 8 insertions(+)
> 
> --- linux-2.6.28-mm1.orig/fs/compat.c
> +++ linux-2.6.28-mm1/fs/compat.c
> @@ -1402,6 +1402,7 @@ int compat_do_execve(char * filename,
>  	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
>  	if (retval < 0)
>  		goto out_free;
> +	current->in_execve = 1;
>  
>  	retval = -ENOMEM;
>  	bprm->cred = prepare_exec_creds();
> @@ -1454,6 +1455,7 @@ int compat_do_execve(char * filename,
>  		goto out;
>  
>  	/* execve succeeded */
> +	current->in_execve = 0;
>  	mutex_unlock(&current->cred_exec_mutex);
>  	acct_update_integrals(current);
>  	free_bprm(bprm);
> @@ -1470,6 +1472,7 @@ out_file:
>  	}
>  
>  out_unlock:
> +	current->in_execve = 0;
>  	mutex_unlock(&current->cred_exec_mutex);
>  
>  out_free:
> --- linux-2.6.28-mm1.orig/fs/exec.c
> +++ linux-2.6.28-mm1/fs/exec.c
> @@ -1306,6 +1306,7 @@ int do_execve(char * filename,
>  	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
>  	if (retval < 0)
>  		goto out_free;
> +	current->in_execve = 1;
>  
>  	retval = -ENOMEM;
>  	bprm->cred = prepare_exec_creds();
> @@ -1359,6 +1360,7 @@ int do_execve(char * filename,
>  		goto out;
>  
>  	/* execve succeeded */
> +	current->in_execve = 0;
>  	mutex_unlock(&current->cred_exec_mutex);
>  	acct_update_integrals(current);
>  	free_bprm(bprm);
> @@ -1377,6 +1379,7 @@ out_file:
>  	}
>  
>  out_unlock:
> +	current->in_execve = 0;
>  	mutex_unlock(&current->cred_exec_mutex);
>  
>  out_free:
> --- linux-2.6.28-mm1.orig/include/linux/sched.h
> +++ linux-2.6.28-mm1/include/linux/sched.h
> @@ -1155,6 +1155,8 @@ struct task_struct {
>  	/* ??? */
>  	unsigned int personality;
>  	unsigned did_exec:1;
> +	unsigned in_execve:1;	/* Tell the LSMs that the process is doing an
> +				 * execve */
>  	pid_t pid;
>  	pid_t tgid;
>  
> 
> -- 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation Tetsuo Handa
@ 2009-01-05  9:07   ` James Morris
  2009-01-06  8:13     ` Tetsuo Handa
  2009-01-14  8:57     ` Tetsuo Handa
  0 siblings, 2 replies; 21+ messages in thread
From: James Morris @ 2009-01-05  9:07 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Andrew Morton, linux-security-module, linux-kernel, Paul E. McKenney

On Thu, 1 Jan 2009, Tetsuo Handa wrote:

> This patch introduces new list structure named "list1".
> 
> TOMOYO wants to use "write once read many" list.
> Since only two operations
> 
>  (1) Append an entry to the tail of the list.
>  (2) Read all entries starting from the head of the list.
> 
> are needed for that purpose, this list doesn't have pointer to previous
> element.
> 
> While "hlist" is defined as
> 
>  struct hlist_head {
>  	struct hlist_node *first;
>  };
> 
>  struct hlist_node {
>  	struct hlist_node *next, **pprev;
>  };
> 
> I don't use "hlist_node" because I don't need pointer to previous element.
> 
> By ommiting pointer to previous element, the reader becomes read lock free,
> which is good thing for implementing "write once read many" list.

This has a technical ack from Paul, but what about Linus' long-standing 
objection to singly-linked lists in the kernel?  I'm sure this has been 
discussed re. your patches, but I can't find a reference.

Also, should this be folded into list.h, and is "list1" an appropriate 
name?  ("slist" might be better).

 > 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
> ---
>  include/linux/list1.h |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 81 insertions(+)
> 
> --- /dev/null
> +++ linux-2.6.28-mm1/include/linux/list1.h
> @@ -0,0 +1,81 @@
> +#ifndef _LINUX_LIST1_H
> +#define _LINUX_LIST1_H
> +
> +#include <linux/list.h>
> +#include <linux/rcupdate.h>
> +
> +/*
> + * Singly linked list implementation.
> + *
> + * This list supports only two operations.
> + * (1) Append an entry to the tail of the list.
> + * (2) Read all entries starting from the head of the list.
> + *
> + * This list is designed for holding "write once, read many" entries.
> + * This list requires no locks for read operation.
> + * This list doesn't support "remove an entry from the list" operation.
> + */
> +
> +/* To reduce memory usage, this list doesn't use "->prev" pointer. */
> +struct list1_head {
> +	struct list1_head *next;
> +};
> +
> +#define LIST1_HEAD_INIT(name) { &(name) }
> +#define LIST1_HEAD(name) struct list1_head name = LIST1_HEAD_INIT(name)
> +
> +static inline void INIT_LIST1_HEAD(struct list1_head *list)
> +{
> +	list->next = list;
> +}
> +
> +/* Reuse list_entry because it doesn't use "->prev" pointer. */
> +#define list1_entry list_entry
> +
> +/* Reuse list_for_each_rcu because it doesn't use "->prev" pointer. */
> +#define list1_for_each list_for_each_rcu
> +/* Reuse list_for_each_entry_rcu because it doesn't use "->prev" pointer. */
> +#define list1_for_each_entry list_for_each_entry_rcu
> +
> +/**
> + * list1_for_each_cookie - iterate over a list with cookie.
> + * @pos:        the &struct list1_head to use as a loop cursor.
> + * @cookie:     the &struct list1_head to use as a cookie.
> + * @head:       the head for your list.
> + *
> + * Same with list_for_each_rcu() except that this primitive uses @cookie
> + * so that we can continue iteration.
> + * @cookie must be NULL when iteration starts, and @cookie will become
> + * NULL when iteration finishes.
> + *
> + * Since list elements are never removed, we don't need to get a lock
> + * or a reference count.
> + */
> +#define list1_for_each_cookie(pos, cookie, head)                      \
> +	for (({ if (!cookie)                                          \
> +				     cookie = head; }),               \
> +	     pos = rcu_dereference((cookie)->next);                   \
> +	     prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
> +	     (cookie) = pos, pos = rcu_dereference(pos->next))
> +
> +/**
> + * list1_add_tail - add a new entry to list1 list.
> + * @new: new entry to be added.
> + * @head: list head to add it before.
> + *
> + * Same with list_add_tail_rcu() without "->prev" pointer.
> + *
> + * Caller must hold a lock for protecting @head.
> + */
> +static inline void list1_add_tail(struct list1_head *new,
> +				  struct list1_head *head)
> +{
> +	struct list1_head *prev = head;
> +
> +	new->next = head;
> +	while (prev->next != head)
> +		prev = prev->next;
> +	rcu_assign_pointer(prev->next, new);
> +}
> +
> +#endif
> 
> -- 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath().
  2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath() Tetsuo Handa
@ 2009-01-05  9:08   ` James Morris
  2009-01-14  8:58     ` Tetsuo Handa
  0 siblings, 1 reply; 21+ messages in thread
From: James Morris @ 2009-01-05  9:08 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Andrew Morton, linux-security-module, linux-kernel,
	Kentaro Takeda, Toshiharu Harada

On Thu, 1 Jan 2009, Tetsuo Handa wrote:

> To remove factors that make pathname based access control difficult
> (e.g. symbolic links, "..", "//", chroot() etc.), a variant of d_path()
> which traverses up to the root of the namespace is needed.
> 
> This patch introduces d_realpath(), a variant of d_path().
> While d_path() stops traversing at current->fs->root,
> d_realpath() doesn't stop traversiong at current->fs->root.
> 
> Three differences compared to d_path().
> (1) Ignores current process's root directory.
> (2) Trailing '/' is added if the pathname refers to a directory.
> (3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid.
> 

This needs an ack from Al and/or Christoph.

> Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
> ---
>  fs/dcache.c            |   84 +++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/dcache.h |    1 
>  2 files changed, 85 insertions(+)
> 
> --- linux-2.6.28-mm1.orig/fs/dcache.c
> +++ linux-2.6.28-mm1/fs/dcache.c
> @@ -32,6 +32,7 @@
>  #include <linux/seqlock.h>
>  #include <linux/swap.h>
>  #include <linux/bootmem.h>
> +#include <linux/magic.h>
>  #include "internal.h"
>  
>  
> @@ -1978,6 +1979,89 @@ Elong:
>  }
>  
>  /**
> + * d_realpath - Get the realpath of a dentry.
> + *
> + * @path: Pointer to "struct path".
> + * @buffer: Pointer to buffer to return value in.
> + * @buflen: Sizeof @buffer.
> + *
> + * Returns pointer to the realpath on success, an error code othersize.
> + *
> + * If @dentry is a directory, trailing '/' is appended.
> + * /proc/PID/ is replaced by /proc/self/ if PID == task_tgid_nr_ns(current).
> + */
> +char *d_realpath(struct path *path, char *buffer, int buflen)
> +{
> +	struct dentry *dentry = path->dentry;
> +	struct vfsmount *vfsmnt = path->mnt;
> +	char *end = buffer + buflen;
> +
> +	spin_lock(&dcache_lock);
> +	spin_lock(&vfsmount_lock);
> +	if (buflen < 1 || prepend(&end, &buflen, "", 1))
> +		goto Elong;
> +	/*
> +	 * Exception: Add trailing '/' for directory.
> +	 */
> +	if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) &&
> +	    prepend(&end, &buflen, "/", 1))
> +		goto Elong;
> +	for (;;) {
> +		struct dentry *parent;
> +		const char *name;
> +		int name_len;
> +		unsigned long pid;
> +
> +		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
> +			/* Global root? */
> +			if (vfsmnt->mnt_parent == vfsmnt)
> +				break;
> +			dentry = vfsmnt->mnt_mountpoint;
> +			vfsmnt = vfsmnt->mnt_parent;
> +			continue;
> +		}
> +		parent = dentry->d_parent;
> +		prefetch(parent);
> +		/*
> +		 * Exception: Use /proc/self/ rather than /proc/\$/
> +		 * for current process.
> +		 */
> +		name = dentry->d_name.name;
> +		name_len = dentry->d_name.len;
> +		if (IS_ROOT(parent) &&
> +		    parent->d_sb->s_magic == PROC_SUPER_MAGIC &&
> +		    !strict_strtoul(name, 10, &pid)) {
> +			const pid_t tgid
> +				= task_tgid_nr_ns(current,
> +						  dentry->d_sb->s_fs_info);
> +			if (tgid && (pid_t) pid == tgid) {
> +				name = "self";
> +				name_len = 4;
> +			}
> +		}
> +		if (prepend(&end, &buflen, name, name_len))
> +			goto Elong;
> +		if (prepend(&end, &buflen, "/", 1))
> +			goto Elong;
> +		dentry = parent;
> +	}
> +	if (*end == '/') {
> +		/* hit the slash */
> +		buflen++;
> +		end++;
> +	}
> +	if (prepend_name(&end, &buflen, &dentry->d_name))
> +		goto Elong;
> + out:
> +	spin_unlock(&vfsmount_lock);
> +	spin_unlock(&dcache_lock);
> +	return end;
> + Elong:
> +	end = ERR_PTR(-ENAMETOOLONG);
> +	goto out;
> +}
> +
> +/**
>   * d_path - return the path of a dentry
>   * @path: path to report
>   * @buf: buffer to return value in
> --- linux-2.6.28-mm1.orig/include/linux/dcache.h
> +++ linux-2.6.28-mm1/include/linux/dcache.h
> @@ -305,6 +305,7 @@ extern char *dynamic_dname(struct dentry
>  extern char *__d_path(const struct path *path, struct path *root, char *, int);
>  extern char *d_path(const struct path *, char *, int);
>  extern char *dentry_path(struct dentry *, char *, int);
> +extern char *d_realpath(struct path *, char *, int);
>  
>  /* Allocation counts.. */
>  
> 
> -- 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-05  9:07   ` James Morris
@ 2009-01-06  8:13     ` Tetsuo Handa
  2009-01-06  9:11       ` James Morris
  2009-01-14  8:57     ` Tetsuo Handa
  1 sibling, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-06  8:13 UTC (permalink / raw)
  To: jmorris; +Cc: akpm, linux-security-module, linux-kernel, paulmck


James Morris wrote:
> This has a technical ack from Paul, but what about Linus' long-standing 
> objection to singly-linked lists in the kernel?  I'm sure this has been 
> discussed re. your patches, but I can't find a reference.
I couldn't find one neither. (What does "re." mean?)

But optimistically speaking, there are many in-tree users who define "struct"
without "prev" pointer.

  # grep -hr 'struct [a-zA-Z0-9_]* \*next;' linux-2.6.28/include/linux/ | wc -l
  42
  # grep -hr 'struct [a-zA-Z0-9_]* \*prev;' linux-2.6.28/include/linux/ | wc -l
  7

Not all structures listed below are used as singly linked list,
but many of them are used as singly linked list.

  struct sched_class;
  struct acpi_pci_driver;
  struct adb_request;
  struct atalk_route;
  struct core_thread;
  struct dma_async_tx_descriptor;
  struct erase_info;
  struct esp_pio_buffer;
  struct fdtable;
  struct floppy_raw_cmd;
  struct ftrace_ops;
  struct hdlc_proto;
  struct hwif_s;
  struct ippp_buf_queue;
  struct irqaction;
  struct isdn_net_local_s;
  struct kcore_list;
  struct mfc6_cache;
  struct mfc_cache;
  struct nls_table;
  struct notifier_block;
  struct page_list;
  struct pbe;
  struct phone_device;
  struct pnp_id;
  struct r3964_block_header;
  struct r3964_client_info;
  struct r3964_message;
  struct rcu_head;
  struct resource_list;
  struct sched_group;
  struct sdio_func_tuple;
  struct tasklet_struct;
  struct tty_buffer;
  struct xor_block_template;

Thus, I believe it is acceptable that TOMOYO uses singly linked list
unless Linus comes out and yell "No!".

Regards.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-06  8:13     ` Tetsuo Handa
@ 2009-01-06  9:11       ` James Morris
  2009-01-07  6:36         ` Tetsuo Handa
  0 siblings, 1 reply; 21+ messages in thread
From: James Morris @ 2009-01-06  9:11 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: akpm, linux-security-module, linux-kernel, paulmck

On Tue, 6 Jan 2009, Tetsuo Handa wrote:

> 
> James Morris wrote:
> > This has a technical ack from Paul, but what about Linus' long-standing 
> > objection to singly-linked lists in the kernel?  I'm sure this has been 
> > discussed re. your patches, but I can't find a reference.
> I couldn't find one neither. (What does "re." mean?)

http://en.wiktionary.org/wiki/re#Etymology_1

> But optimistically speaking, there are many in-tree users who define "struct"
> without "prev" pointer.
> 
>   # grep -hr 'struct [a-zA-Z0-9_]* \*next;' linux-2.6.28/include/linux/ | wc -l
>   42
>   # grep -hr 'struct [a-zA-Z0-9_]* \*prev;' linux-2.6.28/include/linux/ | wc -l
>   7
> 
> Not all structures listed below are used as singly linked list,
> but many of them are used as singly linked list.

Can any of these be converted to your singly linked list implementation ?

> 
>   struct sched_class;
>   struct acpi_pci_driver;
>   struct adb_request;
>   struct atalk_route;
>   struct core_thread;
>   struct dma_async_tx_descriptor;
>   struct erase_info;
>   struct esp_pio_buffer;
>   struct fdtable;
>   struct floppy_raw_cmd;
>   struct ftrace_ops;
>   struct hdlc_proto;
>   struct hwif_s;
>   struct ippp_buf_queue;
>   struct irqaction;
>   struct isdn_net_local_s;
>   struct kcore_list;
>   struct mfc6_cache;
>   struct mfc_cache;
>   struct nls_table;
>   struct notifier_block;
>   struct page_list;
>   struct pbe;
>   struct phone_device;
>   struct pnp_id;
>   struct r3964_block_header;
>   struct r3964_client_info;
>   struct r3964_message;
>   struct rcu_head;
>   struct resource_list;
>   struct sched_group;
>   struct sdio_func_tuple;
>   struct tasklet_struct;
>   struct tty_buffer;
>   struct xor_block_template;
> 
> Thus, I believe it is acceptable that TOMOYO uses singly linked list
> unless Linus comes out and yell "No!".

He's yelled no in the past, so there needs to be a convincing argument 
which he'll accept.


- James
-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-06  9:11       ` James Morris
@ 2009-01-07  6:36         ` Tetsuo Handa
  2009-01-07 19:09           ` Serge E. Hallyn
  0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-07  6:36 UTC (permalink / raw)
  To: jmorris; +Cc: akpm, paulmck, linux-security-module, linux-kernel

James Morris wrote:
> > Not all structures listed below are used as singly linked list,
> > but many of them are used as singly linked list.
> 
> Can any of these be converted to your singly linked list implementation ?

Maybe, but will be few.

Regular singly linked list (which is known as "slist") implementation has
below characteristics.

 (1) Supports "add", "read" and "remove" operations.
 (2) Caller holds read lock when reading, and holds write lock when adding or
     removing.
 (3) Iteration method (for_each_*) needn't to call rcu_dereference() because
     caller holds locks as needed.

TOMOYO's singly linked list (which is named as "list1") implementation has
below characteristics.

 (1) Supports "add" and "read" operations.
 (2) Caller holds a lock when adding, but doesn't hold a lock when reading.
 (3) Iteration method (for_each_*) needs to call rcu_dereference() because
     caller doesn't hold a lock when reading.

I think it is not a good thing to rename "list1" to "slist".

Many of these in-tree singly linked list users need to use regular singly
linked list because they need to remove elements from their lists.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-07  6:36         ` Tetsuo Handa
@ 2009-01-07 19:09           ` Serge E. Hallyn
  2009-01-08  6:19             ` Tetsuo Handa
  0 siblings, 1 reply; 21+ messages in thread
From: Serge E. Hallyn @ 2009-01-07 19:09 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: jmorris, akpm, paulmck, linux-security-module, linux-kernel

Quoting Tetsuo Handa (penguin-kernel@i-love.sakura.ne.jp):
> James Morris wrote:
> > > Not all structures listed below are used as singly linked list,
> > > but many of them are used as singly linked list.
> > 
> > Can any of these be converted to your singly linked list implementation ?
> 
> Maybe, but will be few.
> 
> Regular singly linked list (which is known as "slist") implementation has
> below characteristics.
> 
>  (1) Supports "add", "read" and "remove" operations.
>  (2) Caller holds read lock when reading, and holds write lock when adding or
>      removing.
>  (3) Iteration method (for_each_*) needn't to call rcu_dereference() because
>      caller holds locks as needed.
> 
> TOMOYO's singly linked list (which is named as "list1") implementation has
> below characteristics.
> 
>  (1) Supports "add" and "read" operations.
>  (2) Caller holds a lock when adding, but doesn't hold a lock when reading.
>  (3) Iteration method (for_each_*) needs to call rcu_dereference() because
>      caller doesn't hold a lock when reading.
> 
> I think it is not a good thing to rename "list1" to "slist".

How about alist (or aolist) for append-only list?

The problem with list1 is that it *really* doesn't imply the
characteristics you cite.

-serge

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-07 19:09           ` Serge E. Hallyn
@ 2009-01-08  6:19             ` Tetsuo Handa
  0 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-08  6:19 UTC (permalink / raw)
  To: serue; +Cc: jmorris, akpm, paulmck, linux-security-module, linux-kernel

Serge E. Hallyn wrote:
> > TOMOYO's singly linked list (which is named as "list1") implementation has
> > below characteristics.
> > 
> >  (1) Supports "add" and "read" operations.
> >  (2) Caller holds a lock when adding, but doesn't hold a lock when reading.
> >  (3) Iteration method (for_each_*) needs to call rcu_dereference() because
> >      caller doesn't hold a lock when reading.
> > 
> > I think it is not a good thing to rename "list1" to "slist".
> 
> How about alist (or aolist) for append-only list?
> 
> The problem with list1 is that it *really* doesn't imply the
> characteristics you cite.
> 
OK.

A developer who reads a code using this list likely wonders
"Why no read_lock() before reading this list?".
Thus, I'd like to rename to "rlfl" (Read-Lock-Free List).

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation.
  2009-01-05  9:07   ` James Morris
  2009-01-06  8:13     ` Tetsuo Handa
@ 2009-01-14  8:57     ` Tetsuo Handa
  1 sibling, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-14  8:57 UTC (permalink / raw)
  To: jmorris; +Cc: akpm, linux-security-module, linux-kernel, paulmck

James Morris wrote:
> > By ommiting pointer to previous element, the reader becomes read lock free,
> > which is good thing for implementing "write once read many" list.
> 
> This has a technical ack from Paul, but what about Linus' long-standing 
> objection to singly-linked lists in the kernel?  I'm sure this has been 
> discussed re. your patches, but I can't find a reference.
> 
OK, for reviewers' ease, I purged list1 for now.

Next posting (#15) will use standard doubly linked list with rw_semaphore.

Thanks.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath().
  2009-01-05  9:08   ` James Morris
@ 2009-01-14  8:58     ` Tetsuo Handa
  0 siblings, 0 replies; 21+ messages in thread
From: Tetsuo Handa @ 2009-01-14  8:58 UTC (permalink / raw)
  To: jmorris; +Cc: akpm, linux-security-module, linux-kernel

James Morris wrote:
> > (3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid.
> 
> This needs an ack from Al and/or Christoph.
> 
It is a sad thing that I cannot use /proc/self/ (which is the only part where
a pathname based access control can prevent current process from accessing
other process's information), but I purged d_realpath() for now.

Next posting (#15) will embed AppArmor's d_namespace_path()-like function
into TOMOYO's code.

/* d_namespace_path() in OpenSUSE 11.1's 2.6.27.7-9.1 kernel. */
char *d_namespace_path(struct dentry *dentry, struct vfsmount *vfsmnt,
		       char *buf, int buflen)
{
	struct path root, tmp, ns_root = { };
	struct path path = { .mnt = vfsmnt, .dentry = dentry };
	char *res;

	read_lock(&current->fs->lock);
	root = current->fs->root;
	path_get(&current->fs->root);
	read_unlock(&current->fs->lock);
	spin_lock(&vfsmount_lock);
	if (root.mnt && root.mnt->mnt_ns)
		ns_root.mnt = mntget(root.mnt->mnt_ns->root);
	if (ns_root.mnt)
		ns_root.dentry = dget(ns_root.mnt->mnt_root);
	spin_unlock(&vfsmount_lock);
	tmp = ns_root;
	res = __d_path(&path, &tmp, buf, buflen,
		       D_PATH_FAIL_DELETED | D_PATH_DISCONNECT);
	path_put(&root);
	path_put(&ns_root);

	/* Prevent empty path for lazily unmounted filesystems. */
	if (!IS_ERR(res) && *res == '\0')
		*--res = '.';
	return res;
}

Thanks.

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2009-01-14  8:59 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-01  5:07 [TOMOYO #14 (mmotm 2008-12-30-16-05) 00/10] TOMOYO Linux Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 01/10] Add in_execve flag into task_struct Tetsuo Handa
2009-01-05  8:25   ` James Morris
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 02/10] Singly linked list implementation Tetsuo Handa
2009-01-05  9:07   ` James Morris
2009-01-06  8:13     ` Tetsuo Handa
2009-01-06  9:11       ` James Morris
2009-01-07  6:36         ` Tetsuo Handa
2009-01-07 19:09           ` Serge E. Hallyn
2009-01-08  6:19             ` Tetsuo Handa
2009-01-14  8:57     ` Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath() Tetsuo Handa
2009-01-05  9:08   ` James Morris
2009-01-14  8:58     ` Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 04/10] Memory and pathname management functions Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 05/10] Common functions for TOMOYO Linux Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 06/10] File operation restriction part Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 07/10] Domain transition handler Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 08/10] LSM adapter functions Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 09/10] Kconfig and Makefile Tetsuo Handa
2009-01-01  5:07 ` [TOMOYO #14 (mmotm 2008-12-30-16-05) 10/10] MAINTAINERS info Tetsuo Handa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).