All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kentaro Takeda <takedakn@nttdata.co.jp>
To: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org
Cc: chrisw@sous-sol.org
Subject: [TOMOYO 07/15](repost) File access control functions.
Date: Tue, 02 Oct 2007 16:34:26 +0900	[thread overview]
Message-ID: <4701F482.2010607@nttdata.co.jp> (raw)
In-Reply-To: <4701F285.5000206@nttdata.co.jp>

File access control functions for TOMOYO Linux.
TOMOYO Linux checks permission in
open/creat/unlink/truncate/ftruncate/mknod/mkdir/
rmdir/symlink/link/rename/uselib/sysctl .

Each permission can be automatically accumulated into
the policy of each domain using 'learning mode'.

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

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/security/tomoyo/file.c	2007-10-02 11:26:22.000000000 +0900
@@ -0,0 +1,1544 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * File access control functions for TOMOYO Linux.
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/*************************  VARIABLES  *************************/
+
+/***** The structure for globally readable files. *****/
+
+struct globally_readable_file_entry {
+	struct globally_readable_file_entry *next;
+	const struct path_info *filename;
+	u8 is_deleted;
+};
+
+/***** The structure for filename patterns. *****/
+
+struct pattern_entry {
+	struct pattern_entry *next;
+	const struct path_info *pattern;
+	u8 is_deleted;
+};
+
+/***** The structure for non-rewritable-by-default file patterns. *****/
+
+struct no_rewrite_entry {
+	struct no_rewrite_entry *next;
+	const struct path_info *pattern;
+	u8 is_deleted;
+};
+
+/***** The structure for detailed write operations. *****/
+
+static struct {
+	const char *keyword;
+	const int paths;
+} acl_type_array[] = {
+	{ "create",   1 }, /* TMY_TYPE_CREATE_ACL */
+	{ "unlink",   1 }, /* TMY_TYPE_UNLINK_ACL */
+	{ "mkdir",    1 }, /* TMY_TYPE_MKDIR_ACL */
+	{ "rmdir",    1 }, /* TMY_TYPE_RMDIR_ACL */
+	{ "mkfifo",   1 }, /* TMY_TYPE_MKFIFO_ACL */
+	{ "mksock",   1 }, /* TMY_TYPE_MKSOCK_ACL */
+	{ "mkblock",  1 }, /* TMY_TYPE_MKBLOCK_ACL */
+	{ "mkchar",   1 }, /* TMY_TYPE_MKCHAR_ACL */
+	{ "truncate", 1 }, /* TMY_TYPE_TRUNCATE_ACL */
+	{ "symlink",  1 }, /* TMY_TYPE_SYMLINK_ACL */
+	{ "link",     2 }, /* TMY_TYPE_LINK_ACL */
+	{ "rename",   2 }, /* TMY_TYPE_RENAME_ACL */
+	{ "rewrite",  1 }, /* TMY_TYPE_REWRITE_ACL */
+	{ NULL, 0 }
+};
+
+/*************************  UTILITY FUNCTIONS  *************************/
+
+/**
+ * tmy_acltype2keyword - get keyword from access control index.
+ * @acl_type: index number.
+ *
+ * Returns keyword that corresponds with @acl_type .
+ */
+const char *tmy_acltype2keyword(const unsigned int acl_type)
+{
+	return (acl_type < ARRAY_SIZE(acl_type_array))
+		? acl_type_array[acl_type].keyword : NULL;
+}
+
+/**
+ * tmy_acltype2paths - get number of arguments from access control index.
+ * @acl_type: index number.
+ *
+ * Returns number of arguments that corresponds with @acl_type .
+ */
+int tmy_acltype2paths(const unsigned int acl_type)
+{
+	return (acl_type < ARRAY_SIZE(acl_type_array))
+		? acl_type_array[acl_type].paths : 0;
+}
+
+static int tmy_strendswith(const char *name, const char *tail)
+{
+	int len;
+
+	if (!name || !tail)
+		return 0;
+
+	len = strlen(name) - strlen(tail);
+	return len >= 0 && strcmp(name + len, tail) == 0;
+}
+
+static struct path_info *tmy_get_path(struct dentry *dentry,
+				      struct vfsmount *mnt)
+{
+	/* sizeof(struct path_info_with_data) <= PAGE_SIZE */
+	struct path_info_with_data {
+		/* Keep this first, this pointer is passed to tmy_free(). */
+		struct path_info head;
+		char bariier1[16];
+		char body[TMY_MAX_PATHNAME_LEN];
+		char barrier2[16];
+	} *buf = tmy_alloc(sizeof(*buf));
+
+	if (buf) {
+		int error = tmy_realpath_dentry2(dentry,
+						 mnt,
+						 buf->body,
+						 sizeof(buf->body) - 1);
+
+		if (error == 0) {
+			buf->head.name = buf->body;
+			tmy_fill_path_info(&buf->head);
+			return &buf->head;
+		}
+
+		tmy_free(buf);
+		buf = NULL;
+		printk(KERN_INFO "tmy_realpath_dentry = %d\n", error);
+	}
+
+	return NULL;
+}
+
+/*************************  PROTOTYPES  *************************/
+
+static int tmy_add_double_write_acl(const u8 type,
+				    const char *filename1,
+				    const char *filename2,
+				    struct domain_info * const domain,
+				    const struct condition_list *cond,
+				    const u8 is_delete);
+static int tmy_add_single_write_acl(const u8 type,
+				    const char *filename,
+				    struct domain_info * const domain,
+				    const struct condition_list *cond,
+				    const u8 is_delete);
+
+/*************************  AUDIT FUNCTIONS  *************************/
+
+static int tmy_audit_file_log(const struct path_info *filename,
+			      const u8 perm,
+			      const u8 is_granted,
+			      const u8 is_enforce)
+{
+	char *buf;
+	int len;
+
+	if (is_granted) {
+		if (!tmy_audit_grant())
+			return 0;
+	} else {
+		if (!tmy_audit_reject())
+			return 0;
+	}
+
+	len = filename->total_len + 8;
+	buf = tmy_init_audit_log(&len);
+
+	if (!buf)
+		return -ENOMEM;
+
+	snprintf(buf + strlen(buf),
+		 len - strlen(buf) - 1,
+		 "%d %s",
+		 perm,
+		 filename->name);
+
+	return tmy_write_audit_log(buf, is_granted, is_enforce);
+}
+
+static int tmy_audit_write_log(const char *operation,
+			       const struct path_info *filename1,
+			       const struct path_info *filename2,
+			       const u8 is_granted,
+			       const u8 is_enforce)
+{
+	char *buf;
+	int len;
+
+	if (is_granted) {
+		if (!tmy_audit_grant())
+			return 0;
+	} else {
+		if (!tmy_audit_reject())
+			return 0;
+	}
+
+	len = strlen(operation) +
+		filename1->total_len +
+		(filename2 ? filename2->total_len : 0)
+		+ 16;
+
+	buf = tmy_init_audit_log(&len);
+	if (!buf)
+		return -ENOMEM;
+
+	snprintf(buf + strlen(buf), len - strlen(buf) - 1,
+		 "allow_%s %s %s",
+		 operation, filename1->name, filename2 ? filename2->name : "");
+
+	return tmy_write_audit_log(buf, is_granted, is_enforce);
+}
+
+/**********************  GLOBALLY READABLE FILE HANDLER  **********************/
+
+static struct globally_readable_file_entry *globally_readable_list;
+
+static int tmy_add_globally_readable_entry(const char *filename,
+					   const u8 is_delete)
+{
+	struct globally_readable_file_entry *new_entry;
+	struct globally_readable_file_entry *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved;
+	int error = -ENOMEM;
+
+	if (!tmy_correct_path(filename, 1, -1, -1, __FUNCTION__))
+		return -EINVAL; /* No patterns allowed. */
+	saved = tmy_save_name(filename);
+	if (!saved)
+		return -ENOMEM;
+
+	down(&lock);
+
+	for (ptr = globally_readable_list; ptr; ptr = ptr->next) {
+		if (ptr->filename == saved) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	new_entry = tmy_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+
+	new_entry->filename = saved;
+	mb(); /* Instead of using spinlock. */
+	ptr = globally_readable_list;
+	if (ptr) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else
+		globally_readable_list = new_entry;
+
+	error = 0;
+
+out: ;
+	up(&lock);
+
+	return error;
+}
+
+static int tmy_globally_readable(const struct path_info *filename)
+{
+	struct globally_readable_file_entry *ptr;
+
+	for (ptr = globally_readable_list; ptr; ptr = ptr->next) {
+		if (!ptr->is_deleted &&
+		    !tmy_pathcmp(filename, ptr->filename))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * tmy_add_globally_readable_policy - add or delete globally readable policy.
+ * @filename:  pointer to filename to add ore remove.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_globally_readable_policy(char *filename, const u8 is_delete)
+{
+	return tmy_add_globally_readable_entry(filename, is_delete);
+}
+
+/**
+ * tmy_read_globally_readable_policy - read globally readable policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_globally_readable_policy(struct io_buffer *head)
+{
+	struct globally_readable_file_entry *ptr = head->read_var2;
+
+	if (!ptr)
+		ptr = globally_readable_list;
+
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted &&
+		    tmy_io_printf(head, TMY_ALLOW_READ "%s\n",
+				  ptr->filename->name))
+			break;
+		ptr = ptr->next;
+	}
+
+	return ptr ? -ENOMEM : 0;
+}
+
+/*************************  FILE GROUP HANDLER  *************************/
+
+static struct group_entry *group_list;
+
+static int tmy_add_group_entry(const char *group_name,
+			       const char *member_name,
+			       const u8 is_delete)
+{
+	static DECLARE_MUTEX(lock);
+	struct group_entry *new_group;
+	struct group_entry *group;
+	struct group_member *new_member;
+	struct group_member *member;
+	const struct path_info *saved_group;
+	const struct path_info *saved_member;
+	int error = -ENOMEM;
+
+	if (!tmy_correct_path(group_name, 0, 0, 0, __FUNCTION__) ||
+	    !group_name[0] ||
+	    !tmy_correct_path(member_name, 0, 0, 0, __FUNCTION__) ||
+	    !member_name[0])
+		return -EINVAL;
+
+	saved_group = tmy_save_name(group_name);
+	saved_member = tmy_save_name(member_name);
+
+	if (!saved_group || !saved_member)
+		return -ENOMEM;
+
+	down(&lock);
+	for (group = group_list; group; group = group->next) {
+		if (saved_group != group->group_name)
+			continue;
+
+		for (member = group->first_member;
+		     member;
+		     member = member->next) {
+			if (member->member_name == saved_member) {
+				member->is_deleted = is_delete;
+				error = 0;
+				goto out;
+			}
+		}
+		break;
+	}
+
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	if (!group) {
+		new_group = tmy_alloc_element(sizeof(*new_group));
+		if (!new_group)
+			goto out;
+		new_group->group_name = saved_group;
+		mb(); /* Instead of using spinlock. */
+		group = group_list;
+
+		if (group) {
+			while (group->next)
+				group = group->next;
+			group->next = new_group;
+		} else
+			group_list = new_group;
+
+		group = new_group;
+	}
+
+	new_member = tmy_alloc_element(sizeof(*new_member));
+	if (!new_member)
+		goto out;
+
+	new_member->member_name = saved_member;
+	mb(); /* Instead of using spinlock. */
+	member = group->first_member;
+
+	if (member) {
+		while (member->next)
+			member = member->next;
+		member->next = new_member;
+	} else
+		group->first_member = new_member;
+
+	error = 0;
+out: ;
+	up(&lock);
+
+	return error;
+}
+
+/**
+ * tmy_add_group_policy - add or delete path group policy.
+ * @data:      a line to parse.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_group_policy(char *data, const u8 is_delete)
+{
+	char *cp = strchr(data, ' ');
+
+	if (!cp)
+		return -EINVAL;
+	*cp++ = '\0';
+	return tmy_add_group_entry(data, cp, is_delete);
+}
+
+static struct group_entry *tmy_new_path_group(const char *group_name)
+{
+	int i;
+	struct group_entry *group;
+
+	for (i = 0; i <= 1; i++) {
+		for (group = group_list; group; group = group->next) {
+			if (strcmp(group_name, group->group_name->name) == 0)
+				return group;
+		}
+
+		if (i == 0) {
+			/*
+			 * Add a dummy entry to create new path group
+			 * and delete that entry.
+			 */
+			tmy_add_group_entry(group_name, "/", 0);
+			tmy_add_group_entry(group_name, "/", 1);
+		}
+	}
+
+	return NULL;
+}
+
+static int tmy_path_match_group(const struct path_info *pathname,
+				const struct group_entry *group,
+				const int may_use_pattern)
+{
+	struct group_member *member;
+
+	for (member = group->first_member; member; member = member->next) {
+		if (member->is_deleted)
+			continue;
+
+		if (!member->member_name->is_patterned) {
+			if (!tmy_pathcmp(pathname, member->member_name))
+				return 1;
+		} else if (may_use_pattern) {
+			if (tmy_path_match(pathname, member->member_name))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * tmy_read_group_policy - read path group policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_group_policy(struct io_buffer *head)
+{
+	struct group_entry *group = head->read_var1;
+	struct group_member *member = head->read_var2;
+
+	if (!group)
+		group = group_list;
+
+	while (group) {
+		head->read_var1 = group;
+		if (!member)
+			member = group->first_member;
+		while (member) {
+			head->read_var2 = member;
+			if (!member->is_deleted &&
+			    tmy_io_printf(head,
+					  TMY_PATH_GROUP "%s %s\n",
+					  group->group_name->name,
+					  member->member_name->name))
+				break;
+			member = member->next;
+		}
+
+		if (member)
+			break;
+
+		head->read_var2 = NULL;
+		group = group->next;
+	}
+
+	return group ? -ENOMEM : 0;
+}
+
+/*************************  FILE PATTERN HANDLER  *************************/
+
+static struct pattern_entry *pattern_list;
+
+static int tmy_add_pattern_entry(const char *pattern, const u8 is_delete)
+{
+	struct pattern_entry *new_entry;
+	struct pattern_entry *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved;
+	int error = -ENOMEM;
+
+	if (!tmy_correct_path(pattern, 0, 1, 0, __FUNCTION__))
+		return -EINVAL;
+
+	saved = tmy_save_name(pattern);
+	if (!saved)
+		return -ENOMEM;
+
+	down(&lock);
+
+	for (ptr = pattern_list; ptr; ptr = ptr->next) {
+		if (saved == ptr->pattern) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	new_entry = tmy_alloc_element(sizeof(*new_entry));
+
+	if (!new_entry)
+		goto out;
+	new_entry->pattern = saved;
+	mb(); /* Instead of using spinlock. */
+
+	ptr = pattern_list;
+	if (ptr) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else
+		pattern_list = new_entry;
+
+	error = 0;
+out: ;
+	up(&lock);
+	return error;
+}
+
+static const struct path_info *tmy_get_pattern(const struct path_info *filename)
+{
+	struct pattern_entry *ptr;
+	const struct path_info *pattern = NULL;
+
+	for (ptr = pattern_list; ptr; ptr = ptr->next) {
+		if (ptr->is_deleted)
+			continue;
+		if (!tmy_path_match(filename, ptr->pattern))
+			continue;
+
+		pattern = ptr->pattern;
+		if (!tmy_strendswith(pattern->name, "/\\*"))
+			break;
+	}
+
+	if (pattern)
+		filename = pattern;
+
+	return filename;
+}
+
+/**
+ * tmy_add_pattern_policy - add or delete file pattern policy.
+ * @pattern:   pointer to file pattern entry.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_pattern_policy(char *pattern, const u8 is_delete)
+{
+	return tmy_add_pattern_entry(pattern, is_delete);
+}
+
+/**
+ * tmy_read_pattern_policy - read file pattern policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_pattern_policy(struct io_buffer *head)
+{
+	struct pattern_entry *ptr = head->read_var2;
+
+	if (!ptr)
+		ptr = pattern_list;
+
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted &&
+		    tmy_io_printf(head, TMY_FILE_PATTERN "%s\n",
+				  ptr->pattern->name))
+			break;
+		ptr = ptr->next;
+	}
+
+	return ptr ? -ENOMEM : 0;
+}
+
+/***********************  NON REWRITABLE FILE HANDLER  ***********************/
+
+static struct no_rewrite_entry *no_rewrite_list;
+
+static int tmy_add_no_rewrite_entry(const char *pattern, const u8 is_delete)
+{
+	struct no_rewrite_entry *new_entry;
+	struct no_rewrite_entry *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved;
+	int error = -ENOMEM;
+
+	if (!tmy_correct_path(pattern, 0, 0, 0, __FUNCTION__))
+		return -EINVAL;
+	saved = tmy_save_name(pattern);
+	if (!saved)
+		return -ENOMEM;
+
+	down(&lock);
+	for (ptr = no_rewrite_list; ptr; ptr = ptr->next) {
+		if (ptr->pattern == saved) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+
+	new_entry = tmy_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+
+	new_entry->pattern = saved;
+	mb(); /* Instead of using spinlock. */
+
+	ptr = no_rewrite_list;
+	if (ptr) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else
+		no_rewrite_list = new_entry;
+
+	error = 0;
+out: ;
+	up(&lock);
+
+	return error;
+}
+
+static int tmy_is_no_rewrite_file(const struct path_info *filename)
+{
+	struct no_rewrite_entry *ptr;
+
+	for (ptr = no_rewrite_list; ptr; ptr = ptr->next) {
+		if (ptr->is_deleted)
+			continue;
+		if (!tmy_path_match(filename, ptr->pattern))
+			continue;
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * tmy_add_no_rewrite_policy - add or delete no-rewrite policy.
+ * @pattern:   pointer to no-rewrite entry.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_no_rewrite_policy(char *pattern, const u8 is_delete)
+{
+	return tmy_add_no_rewrite_entry(pattern, is_delete);
+}
+
+/**
+ * tmy_read_no_rewrite_policy - read no-rewrite policy.
+ * @head: pointer to "struct io_buffer".
+ *
+ * Returns nonzero if reading incomplete.
+ * Returns zero otherwise.
+ */
+int tmy_read_no_rewrite_policy(struct io_buffer *head)
+{
+	struct no_rewrite_entry *ptr = head->read_var2;
+
+	if (!ptr)
+		ptr = no_rewrite_list;
+
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted &&
+		    tmy_io_printf(head, TMY_DENY_REWRITE "%s\n",
+				  ptr->pattern->name))
+			break;
+		ptr = ptr->next;
+	}
+
+	return ptr ? -ENOMEM : 0;
+}
+
+/*************************  FILE ACL HANDLER  *************************/
+
+static int tmy_add_file_acl(const char *filename,
+			    u8 perm,
+			    struct domain_info * const domain,
+			    const struct condition_list *cond,
+			    const u8 is_delete)
+{
+	const struct path_info *saved;
+	struct acl_info *ptr;
+	int error = -ENOMEM;
+	u8 is_group = 0;
+
+	if (!domain)
+		return -EINVAL;
+	if (perm > 7 || !perm) {
+		printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
+		       __FUNCTION__, perm, filename);
+		return -EINVAL;
+	}
+	if (!tmy_correct_path(filename, 0, 0, 0, __FUNCTION__))
+		return -EINVAL;
+
+	if (filename[0] == '@') {
+		/* This cast is OK because I don't dereference. */
+		saved = (struct path_info *) tmy_new_path_group(filename + 1);
+		if (!saved)
+			return -ENOMEM;
+		is_group = 1;
+	} else {
+
+		if (tmy_strendswith(filename, "/"))
+			/*
+			 * Valid permissions for directory are
+			 * only 'allow_mkdir' and 'allow_rmdir'.
+			 */
+			return 0;
+
+		saved = tmy_save_name(filename);
+		if (!saved)
+			return -ENOMEM;
+
+		if (!is_delete && perm == 4 &&
+		    tmy_globally_readable(saved))
+			return 0;
+
+	}
+
+	down(&domain_acl_lock);
+
+	if (is_delete)
+		goto remove;
+
+	ptr = domain->first_acl_ptr;
+	if (!ptr)
+		goto first_entry;
+
+	while (1) {
+		struct file_acl *acl = (struct file_acl *) ptr;
+
+		if ((ptr->type == TMY_TYPE_FILE_ACL) &&
+		    ptr->cond == cond &&
+		    (acl->u.filename == saved)) {
+			if (ptr->is_deleted) {
+				acl->perm = 0;
+				mb(); /* Instead of using spinlock. */
+				ptr->is_deleted = 0;
+			}
+			/* Found. Just 'OR' the permission bits. */
+			acl->perm |= perm;
+			error = 0;
+			tmy_update_counter(TMY_UPDATE_DOMAINPOLICY);
+			break;
+		}
+
+		if (ptr->next) {
+			ptr = ptr->next;
+			continue;
+		}
+
+first_entry: ;
+		/* Not found. Append it to the tail. */
+		acl = tmy_alloc_element(sizeof(*acl));
+		if (!acl)
+			break;
+
+		acl->head.type = TMY_TYPE_FILE_ACL;
+		acl->head.cond = cond;
+		acl->perm = perm;
+		acl->u_is_group = is_group;
+		acl->u.filename = saved;
+		error = tmy_add_acl(ptr, domain,
+				    (struct acl_info *) acl);
+		break;
+	}
+	goto ok;
+remove: ;
+	error = -ENOENT;
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		struct file_acl *acl = (struct file_acl *) ptr;
+
+		if (ptr->type != TMY_TYPE_FILE_ACL ||
+		    ptr->cond != cond ||
+		    ptr->is_deleted ||
+		    acl->perm != perm ||
+		    acl->u.filename != saved)
+			continue;
+
+		error = tmy_del_acl(ptr);
+		break;
+	}
+ok: ;
+	up(&domain_acl_lock);
+	return error;
+}
+
+static int tmy_file_acl(const struct path_info *filename, const u8 perm,
+			struct obj_info *obj)
+{
+	const struct domain_info *domain = TMY_SECURITY->domain;
+	struct acl_info *ptr;
+	const int may_use_pat = ((perm & 1) == 0);
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+	if (!filename->is_dir) {
+		if (perm == 4 && tmy_globally_readable(filename))
+			return 0;
+	}
+
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		struct file_acl *acl = (struct file_acl *) ptr;
+
+		if (ptr->type != TMY_TYPE_FILE_ACL ||
+		    ptr->is_deleted ||
+		    (acl->perm & perm) != perm ||
+		    tmy_check_condition(ptr->cond, obj))
+			continue;
+
+		if (acl->u_is_group) {
+			if (tmy_path_match_group(filename,
+						 acl->u.group,
+						 may_use_pat))
+				return 0;
+		} else {
+			if ((may_use_pat || !acl->u.filename->is_patterned) &&
+			    tmy_path_match(filename, acl->u.filename))
+				return 0;
+		}
+	}
+
+	return -EPERM;
+}
+
+static int tmy_file_perm2(const struct path_info *filename,
+			  const u8 perm,
+			  struct obj_info *obj,
+			  const char *operation)
+{
+	int error = 0;
+	struct domain_info * const domain = TMY_SECURITY->domain;
+	const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_FILE);
+
+	if (!filename)
+		return 0;
+
+	error = tmy_file_acl(filename, perm, obj);
+
+	tmy_audit_file_log(filename, perm, !error, is_enforce);
+
+	if (!error)
+		return error;
+
+	if (is_enforce)
+		error =	tmy_supervisor("%s\n%d %s\n",
+				       domain->domainname->name,
+				       perm, filename->name);
+
+	else if (tmy_accept(TMY_MAC_FOR_FILE, domain)) {
+		/* Don't use patterns if execution bit is on. */
+		const struct path_info *patterned =
+			((perm & 1) == 0) ?
+			tmy_get_pattern(filename) : filename;
+		tmy_add_file_acl(patterned->name, perm, domain, NULL, 0);
+	}
+
+	if (!is_enforce)
+		error = 0;
+
+	return error;
+}
+
+/**
+ * tmy_file_perm - check permission for sysctl(2) operation.
+ * @filename0: pointer to filename returned by sysctlpath_from_table().
+ * @perm:      mode (read = 4, write = 2, read-write = 6).
+ * @operation: pointer to error message.
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_file_perm(const char *filename0, const u8 perm, const char *operation)
+{
+	struct path_info filename;
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+
+	filename.name = filename0;
+	tmy_fill_path_info(&filename);
+
+	return tmy_file_perm2(&filename, perm, NULL, operation);
+}
+
+/**
+ * tmy_add_file_policy - add or delete file policy.
+ * @data:      a line to parse.
+ * @domain:    pointer to "struct domain_info".
+ * @cond:      pointer to "struct condition_list". May be NULL.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_file_policy(char *data,
+			struct domain_info *domain,
+			const struct condition_list *cond,
+			const u8 is_delete)
+{
+	char *filename = strchr(data, ' ');
+	unsigned int perm;
+	u8 type;
+
+	if (!filename)
+		return -EINVAL;
+	*filename++ = '\0';
+
+	if (sscanf(data, "%u", &perm) == 1)
+		return tmy_add_file_acl(filename, (u8) perm, domain, cond,
+					is_delete);
+
+	if (strncmp(data, "allow_", 6))
+		goto out;
+
+	data += 6;
+
+	for (type = 0; acl_type_array[type].keyword; type++) {
+		if (strcmp(data, acl_type_array[type].keyword))
+			continue;
+
+		if (acl_type_array[type].paths == 2) {
+			char *filename2 = strchr(filename, ' ');
+
+			if (!filename2)
+				break;
+			*filename2++ = '\0';
+			return tmy_add_double_write_acl(type, filename,
+							filename2,
+							domain, cond,
+							is_delete);
+		} else
+			return tmy_add_single_write_acl(type, filename,
+							domain, cond,
+							is_delete);
+
+		break;
+	}
+out: ;
+	return -EINVAL;
+}
+
+static int tmy_add_single_write_acl(const u8 type,
+				    const char *filename,
+				    struct domain_info * const domain,
+				    const struct condition_list *cond,
+				    const u8 is_delete)
+{
+	const struct path_info *saved;
+	struct acl_info *ptr;
+	int error = -ENOMEM;
+	u8 is_group = 0;
+
+	if (!domain)
+		return -EINVAL;
+	if (!tmy_correct_path(filename, 0, 0, 0, __FUNCTION__))
+		return -EINVAL;
+
+	if (filename[0] == '@') {
+		/* This cast is OK because I don't dereference. */
+		saved = (struct path_info *) tmy_new_path_group(filename + 1);
+		if (!saved)
+			return -ENOMEM;
+		is_group = 1;
+	} else {
+		saved = tmy_save_name(filename);
+		if (!saved)
+			return -ENOMEM;
+	}
+
+	down(&domain_acl_lock);
+	if (is_delete)
+		goto remove;
+
+	ptr = domain->first_acl_ptr;
+	if (!ptr)
+		goto first_entry;
+	while (1) {
+		struct single_acl *acl = (struct single_acl *) ptr;
+
+		if (ptr->type == type && ptr->cond == cond) {
+			if (acl->u.filename == saved) {
+				ptr->is_deleted = 0;
+				/* Found. Nothing to do. */
+				error = 0;
+				break;
+			}
+		}
+		if (ptr->next) {
+			ptr = ptr->next;
+			continue;
+		}
+
+first_entry: ;
+		/* Not found. Append it to the tail. */
+		acl = tmy_alloc_element(sizeof(*acl));
+		if (!acl)
+			break;
+
+		acl->head.type = type;
+		acl->head.cond = cond;
+		acl->u_is_group = is_group;
+		acl->u.filename = saved;
+		error = tmy_add_acl(ptr, domain,
+				    (struct acl_info *) acl);
+		break;
+	}
+	goto ok;
+remove: ;
+	error = -ENOENT;
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		struct single_acl *acl = (struct single_acl *) ptr;
+
+		if (ptr->type != type || ptr->is_deleted ||
+		    ptr->cond != cond || acl->u.filename != saved)
+			continue;
+
+		error = tmy_del_acl(ptr);
+		break;
+	}
+ok: ;
+	up(&domain_acl_lock);
+
+	return error;
+}
+
+static int tmy_add_double_write_acl(const u8 type,
+				    const char *filename1,
+				    const char *filename2,
+				    struct domain_info * const domain,
+				    const struct condition_list *cond,
+				    const u8 is_delete)
+{
+	const struct path_info *saved1;
+	const struct path_info *saved2;
+	struct acl_info *ptr;
+	int error = -ENOMEM;
+	u8 is_group1 = 0;
+	u8 is_group2 = 0;
+
+	if (!domain)
+		return -EINVAL;
+	if (!tmy_correct_path(filename1, 0, 0, 0, __FUNCTION__) ||
+	    !tmy_correct_path(filename2, 0, 0, 0, __FUNCTION__))
+		return -EINVAL;
+
+	if (filename1[0] == '@') {
+		/* This cast is OK because I don't dereference. */
+		saved1 = (struct path_info *) tmy_new_path_group(filename1 + 1);
+		if (!saved1)
+			return -ENOMEM;
+		is_group1 = 1;
+	} else {
+		saved1 = tmy_save_name(filename1);
+		if (!saved1)
+			return -ENOMEM;
+	}
+
+	if (filename2[0] == '@') {
+		/* This cast is OK because I don't dereference. */
+		saved2 = (struct path_info *) tmy_new_path_group(filename2 + 1);
+		if (!saved2)
+			return -ENOMEM;
+		is_group2 = 1;
+	} else {
+		saved2 = tmy_save_name(filename2);
+		if (!saved2)
+			return -ENOMEM;
+	}
+
+	down(&domain_acl_lock);
+
+	if (is_delete)
+		goto remove;
+
+	ptr = domain->first_acl_ptr;
+	if (!ptr)
+		goto first_entry;
+	while (1) {
+		struct double_acl *acl = (struct double_acl *) ptr;
+
+		if (ptr->type == type && ptr->cond == cond) {
+			if (acl->u1.filename1 == saved1 &&
+			    acl->u2.filename2 == saved2) {
+				ptr->is_deleted = 0;
+				/* Found. Nothing to do. */
+				error = 0;
+				break;
+			}
+		}
+
+		if (ptr->next) {
+			ptr = ptr->next;
+			continue;
+		}
+
+first_entry: ;
+		/* Not found. Append it to the tail. */
+		acl = tmy_alloc_element(sizeof(*acl));
+		if (!acl)
+			break;
+
+		acl->head.type = type;
+		acl->head.cond = cond;
+		acl->u1_is_group = is_group1;
+		acl->u2_is_group = is_group2;
+		acl->u1.filename1 = saved1;
+		acl->u2.filename2 = saved2;
+		error = tmy_add_acl(ptr, domain,
+				    (struct acl_info *) acl);
+		break;
+	}
+	goto ok;
+remove: ;
+	error = -ENOENT;
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		struct double_acl *acl = (struct double_acl *) ptr;
+
+		if (ptr->type != type || ptr->is_deleted ||
+		    ptr->cond != cond ||
+		    acl->u1.filename1 != saved1 ||
+		    acl->u2.filename2 != saved2)
+			continue;
+
+		error = tmy_del_acl(ptr);
+		break;
+	}
+ok: ;
+	up(&domain_acl_lock);
+	return error;
+}
+
+static int tmy_single_write_acl(const u8 type,
+				const struct path_info *filename,
+				struct obj_info *obj)
+{
+	const struct domain_info *domain = TMY_SECURITY->domain;
+	struct acl_info *ptr;
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		struct single_acl *acl = (struct single_acl *) ptr;
+
+		if (ptr->type != type || ptr->is_deleted ||
+		    tmy_check_condition(ptr->cond, obj))
+			continue;
+
+		if (acl->u_is_group) {
+			if (!tmy_path_match_group(filename, acl->u.group, 1))
+				continue;
+		} else {
+			if (!tmy_path_match(filename, acl->u.filename))
+				continue;
+		}
+		return 0;
+	}
+
+	return -EPERM;
+}
+
+static int tmy_double_write_acl(const u8 type,
+				const struct path_info *filename1,
+				const struct path_info *filename2,
+				struct obj_info *obj)
+{
+	const struct domain_info *domain = TMY_SECURITY->domain;
+	struct acl_info *ptr;
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		struct double_acl *acl = (struct double_acl *) ptr;
+
+		if (ptr->type != type || ptr->is_deleted ||
+		    tmy_check_condition(ptr->cond, obj))
+			continue;
+
+		if (acl->u1_is_group) {
+			if (!tmy_path_match_group(filename1,
+						  acl->u1.group1, 1))
+				continue;
+		} else {
+			if (!tmy_path_match(filename1, acl->u1.filename1))
+				continue;
+		}
+
+		if (acl->u2_is_group) {
+			if (!tmy_path_match_group(filename2,
+						  acl->u2.group2, 1))
+				continue;
+		} else {
+			if (!tmy_path_match(filename2, acl->u2.filename2))
+				continue;
+		}
+
+		return 0;
+	}
+
+	return -EPERM;
+}
+
+static int tmy_single_write_perm2(const unsigned int operation,
+				  const struct path_info *filename,
+				  struct obj_info *obj)
+{
+	int error;
+	struct domain_info * const domain = TMY_SECURITY->domain;
+	const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_FILE);
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+
+	error = tmy_single_write_acl(operation, filename, obj);
+
+	tmy_audit_write_log(tmy_acltype2keyword(operation),
+			    filename, NULL, !error, is_enforce);
+
+	if (!error)
+		goto ok;
+
+	if (is_enforce)
+		error = tmy_supervisor("%s\nallow_%s %s\n",
+				       domain->domainname->name,
+				       tmy_acltype2keyword(operation),
+				       filename->name);
+
+	else if (tmy_accept(TMY_MAC_FOR_FILE, domain))
+		tmy_add_single_write_acl(operation,
+					 tmy_get_pattern(filename)->name,
+					 domain, NULL, 0);
+
+	if (!is_enforce)
+		error = 0;
+
+ok: ;
+	if (!error && operation == TMY_TYPE_TRUNCATE_ACL &&
+	    tmy_is_no_rewrite_file(filename))
+		error = tmy_single_write_perm2(TMY_TYPE_REWRITE_ACL,
+					       filename, obj);
+
+	return error;
+}
+
+/**
+ * tmy_exec_perm - check permission for execve(2) operation.
+ * @filename: pointer to filename to execute.
+ * @filp:     pointer to "struct file".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_exec_perm(const struct path_info *filename, struct file *filp)
+{
+	struct obj_info obj;
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+	memset(&obj, 0, sizeof(obj));
+	obj.path1_dentry = filp->f_dentry;
+	obj.path1_vfsmnt = filp->f_vfsmnt;
+	return tmy_file_perm2(filename, 1, &obj, "do_execve");
+}
+
+/**
+ * tmy_open_perm - check permission for open(2) operation.
+ * @dentry: pointer to "struct dentry".
+ * @mnt:    pointer to "struct vfsmount".
+ * @flag:   open flags.
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_open_perm(struct dentry *dentry,
+		  struct vfsmount *mnt,
+		  const int flag)
+{
+	struct obj_info obj;
+	const int acc_mode = ACC_MODE(flag);
+	int error = -ENOMEM;
+	struct path_info *buf;
+	const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_FILE);
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+	if (acc_mode == 0)
+		return 0;
+	if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
+		/* I don't check directories here             */
+		/* because mkdir() and rmdir() don't call me. */
+		return 0;
+
+	buf = tmy_get_path(dentry, mnt);
+
+	if (!buf)
+		goto out;
+
+	memset(&obj, 0, sizeof(obj));
+	obj.path1_dentry = dentry;
+	obj.path1_vfsmnt = mnt;
+
+	error = 0;
+	if ((acc_mode & MAY_WRITE) &&
+	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
+	    tmy_is_no_rewrite_file(buf))
+		error = tmy_single_write_perm2(TMY_TYPE_REWRITE_ACL,
+					       buf, &obj);
+
+	if (error == 0)
+		error = tmy_file_perm2(buf, acc_mode, &obj, "open");
+
+	if (error == 0 && (flag & O_TRUNC))
+		error = tmy_single_write_perm2(TMY_TYPE_TRUNCATE_ACL,
+					       buf, &obj);
+
+	tmy_free(buf);
+
+out: ;
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tmy_single_write_perm - check permission for create(2) etc. operation.
+ * @operation: operation index number.
+ * @dentry:    pointer to "struct dentry".
+ * @mnt:       pointer to "struct vfsmount".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_single_write_perm(const unsigned int operation,
+			  struct dentry *dentry,
+			  struct vfsmount *mnt)
+{
+	struct obj_info obj;
+	int error = -ENOMEM;
+	struct path_info *buf;
+	const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_FILE);
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+
+	buf = tmy_get_path(dentry, mnt);
+
+	if (!buf)
+		goto out;
+
+	memset(&obj, 0, sizeof(obj));
+	obj.path1_dentry = dentry;
+	obj.path1_vfsmnt = mnt;
+
+	switch (operation) {
+	case TMY_TYPE_MKDIR_ACL:
+	case TMY_TYPE_RMDIR_ACL:
+		if (!buf->is_dir) {
+			strcat((char *) buf->name, "/");
+			tmy_fill_path_info(buf);
+		}
+	}
+	error = tmy_single_write_perm2(operation, buf, &obj);
+	tmy_free(buf);
+
+out: ;
+	if (!is_enforce)
+		error = 0;
+
+	return error;
+}
+
+/**
+ * tmy_rewrite_perm - check permission for truncate/overwrite operation.
+ * @filp: pointer to "struct file".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_rewrite_perm(struct file *filp)
+{
+	int error = -ENOMEM;
+	const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_FILE);
+	struct path_info *buf = tmy_get_path(filp->f_dentry, filp->f_vfsmnt);
+
+	if (!buf)
+		goto out;
+
+	if (tmy_is_no_rewrite_file(buf)) {
+		struct obj_info obj;
+		memset(&obj, 0, sizeof(obj));
+		obj.path1_dentry = filp->f_dentry;
+		obj.path1_vfsmnt = filp->f_vfsmnt;
+		error = tmy_single_write_perm2(TMY_TYPE_REWRITE_ACL,
+					       buf, &obj);
+	} else
+		error = 0;
+
+	tmy_free(buf);
+
+out: ;
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tmy_double_write_perm - check permission for link(2)/rename(2) operation.
+ * @operation: operation index number.
+ * @dentry1:   pointer to "struct dentry".
+ * @mnt1:      pointer to "struct vfsmount".
+ * @dentry2:   pointer to "struct dentry".
+ * @mnt2:      pointer to "struct vfsmount".
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_double_write_perm(const unsigned int operation,
+			  struct dentry *dentry1,
+			  struct vfsmount *mnt1,
+			  struct dentry *dentry2,
+			  struct vfsmount *mnt2)
+{
+	struct obj_info obj;
+	int error = -ENOMEM;
+	struct path_info *buf1;
+	struct path_info *buf2;
+	struct domain_info * const domain = TMY_SECURITY->domain;
+	const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_FILE);
+
+	if (!tmy_flags(TMY_MAC_FOR_FILE))
+		return 0;
+	buf1 = tmy_get_path(dentry1, mnt1);
+	buf2 = tmy_get_path(dentry2, mnt2);
+
+	if (!buf1 || !buf2)
+		goto out;
+
+	memset(&obj, 0, sizeof(obj));
+	obj.path1_dentry = dentry1;
+	obj.path1_vfsmnt = mnt1;
+	obj.path2_dentry = dentry2;
+	obj.path2_vfsmnt = mnt2;
+	if (operation == TMY_TYPE_RENAME_ACL) {
+		/* TMY_TYPE_LINK_ACL can't reach here for directory. */
+		if (dentry1->d_inode && S_ISDIR(dentry1->d_inode->i_mode)) {
+			if (!buf1->is_dir) {
+				strcat((char *) buf1->name, "/");
+				tmy_fill_path_info(buf1);
+			}
+			if (!buf2->is_dir) {
+				strcat((char *) buf2->name, "/");
+				tmy_fill_path_info(buf2);
+			}
+		}
+	}
+	error = tmy_double_write_acl(operation, buf1, buf2, &obj);
+
+	tmy_audit_write_log(tmy_acltype2keyword(operation),
+			    buf1, buf2, !error, is_enforce);
+
+	if (!error)
+		goto out;
+
+	if (is_enforce)
+		error = tmy_supervisor("%s\nallow_%s %s %s\n",
+				       domain->domainname->name,
+				       tmy_acltype2keyword(operation),
+				       buf1->name, buf2->name);
+	else if (tmy_accept(TMY_MAC_FOR_FILE, domain))
+		tmy_add_double_write_acl(operation,
+					 tmy_get_pattern(buf1)->name,
+					 tmy_get_pattern(buf2)->name,
+					 domain, NULL, 0);
+
+out: ;
+	tmy_free(buf1);
+	tmy_free(buf2);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}



  parent reply	other threads:[~2007-10-02  7:34 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-02  7:25 [TOMOYO 00/15](repost) TOMOYO Linux - MAC based on process invocation history Kentaro Takeda
2007-10-02  7:28 ` [TOMOYO 01/15](repost) Allow use of namespace_sem from LSM module Kentaro Takeda
2007-10-02  7:29 ` [TOMOYO 02/15](repost) Data structures and prototypes definition Kentaro Takeda
2007-10-02  7:30 ` [TOMOYO 03/15](repost) Memory and pathname management functions Kentaro Takeda
2007-10-03  7:39   ` James Morris
2007-10-03 11:12     ` Tetsuo Handa
2007-10-02  7:31 ` [TOMOYO 04/15](repost) Utility functions and securityfs interface for policy manipulation Kentaro Takeda
2007-10-02  8:05   ` Paul Mundt
2007-10-02 14:15     ` Greg KH
2007-10-02  7:32 ` [TOMOYO 05/15](repost) Domain transition handler functions Kentaro Takeda
2007-10-02 11:15   ` James Morris
2007-10-02 12:44     ` Tetsuo Handa
2007-10-02 13:00       ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-02 13:07       ` James Morris
2007-10-02 14:50         ` Andi Kleen
2007-10-03 11:24         ` Tetsuo Handa
2007-10-03 11:43           ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-03 12:37             ` James Morris
2007-10-03 13:04               ` Tetsuo Handa
2007-10-03 13:11                 ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-03 13:14                 ` KaiGai Kohei
2007-10-03 13:59                   ` Tetsuo Handa
2007-10-03 14:07                     ` Peter Zijlstra
2007-10-03 14:26                       ` Tetsuo Handa
2007-10-03 14:26                         ` Peter Zijlstra
2007-10-03 14:32                         ` YOSHIFUJI Hideaki / 吉藤英明
2007-10-03 14:39                           ` James Morris
2007-10-03 14:56                           ` Tetsuo Handa
2007-10-04 12:57                             ` Tetsuo Handa
2007-10-03 14:37                         ` Jiri Kosina
2007-10-07 10:38                       ` Sleeping in RCU list traversal Tetsuo Handa
2007-10-03 13:24                 ` [TOMOYO 05/15](repost) Domain transition handler functions Peter Zijlstra
2007-10-03 14:19                   ` Tetsuo Handa
2007-10-03 14:28                     ` Peter Zijlstra
2007-10-15 11:46                       ` Tetsuo Handa
2007-10-03 14:35                     ` David P. Quigley
2007-10-15 12:09               ` Tetsuo Handa
2007-10-02  7:33 ` [TOMOYO 06/15](repost) Auditing interface Kentaro Takeda
2007-10-02  7:34 ` Kentaro Takeda [this message]
2007-10-02  7:35 ` [TOMOYO 08/15](repost) Argv[0] access control functions Kentaro Takeda
2007-10-02  7:36 ` [TOMOYO 09/15](repost) Networking " Kentaro Takeda
2007-10-02  7:37 ` [TOMOYO 10/15](repost) Namespace manipulation " Kentaro Takeda
2007-10-02  7:37 ` [TOMOYO 11/15](repost) Signal transmission " Kentaro Takeda
2007-10-02  7:38 ` [TOMOYO 12/15](repost) LSM adapter for TOMOYO Kentaro Takeda
2007-10-02  7:39 ` [TOMOYO 13/15](repost) Conditional permission support Kentaro Takeda
2007-10-02  7:39 ` [TOMOYO 14/15](repost) LSM expansion for TOMOYO Linux Kentaro Takeda
2007-10-02 12:48   ` James Morris
2007-10-02 13:33     ` Tetsuo Handa
2007-10-02 14:36   ` James Morris
2007-10-02 21:49     ` Tetsuo Handa
2007-10-02  7:40 ` [TOMOYO 15/15](repost) Kconfig and Makefile " Kentaro Takeda
2007-10-02  7:42 ` [TOMOYO 00/15](repost) TOMOYO Linux - MAC based on process invocation history Kentaro Takeda
2007-10-02 10:37   ` James Morris
2007-10-02 10:58     ` Kentaro Takeda

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4701F482.2010607@nttdata.co.jp \
    --to=takedakn@nttdata.co.jp \
    --cc=chrisw@sous-sol.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.