lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 14 Jun 2007 16:38:47 +0900
From:	Kentaro Takeda <takedakn@...data.co.jp>
To:	linux-kernel@...r.kernel.org, linux-security-module@...r.kernel.org
Subject: [TOMOYO 8/9] File access control functions.

This is the main part for profiling and controlling file access.
We thought checking old pathname and new pathname separately
for rename() and link() operation is a too rough access control
and we are checking both pathnames using tomoyo_check_double_write_acl().

Signed-off-by: Kentaro Takeda <takedakn@...data.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>

---------------
 security/tomoyo/file.c | 1126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1126 insertions(+)

diff -ubBpErN linux-2.6.21.5/security/tomoyo/file.c linux-2.6.21.5-tomoyo/security/tomoyo/file.c
--- linux-2.6.21.5/security/tomoyo/file.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/file.c	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,1126 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * File access control functions for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/*************************  VARIABLES  *************************/
+
+extern struct semaphore domain_acl_lock;
+
+/***** The structure for globally readable files. *****/
+
+struct globally_readable_file_entry {
+	struct globally_readable_file_entry *next;
+	const struct path_info *filename;
+	int is_deleted;
+};
+
+/***** The structure for filename patterns. *****/
+
+struct pattern_entry {
+	struct pattern_entry *next;
+	const struct path_info *pattern;
+	int 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;
+	int is_deleted;
+};
+
+/***** The structure for detailed write operations. *****/
+
+static struct {
+	const char *keyword;
+	const int paths;
+	int check_type;
+} acl_type_array[] = { /* mapping.txt */
+	{ "create",   1, 1 }, /* TOMOYO_TYPE_CREATE_ACL */
+	{ "unlink",   1, 1 }, /* TOMOYO_TYPE_UNLINK_ACL */
+	{ "mkdir",    1, 1 }, /* TOMOYO_TYPE_MKDIR_ACL */
+	{ "rmdir",    1, 1 }, /* TOMOYO_TYPE_RMDIR_ACL */
+	{ "mkfifo",   1, 1 }, /* TOMOYO_TYPE_MKFIFO_ACL */
+	{ "mksock",   1, 1 }, /* TOMOYO_TYPE_MKSOCK_ACL */
+	{ "mkblock",  1, 1 }, /* TOMOYO_TYPE_MKBLOCK_ACL */
+	{ "mkchar",   1, 1 }, /* TOMOYO_TYPE_MKCHAR_ACL */
+	{ "truncate", 1, 1 }, /* TOMOYO_TYPE_TRUNCATE_ACL */
+	{ "symlink",  1, 1 }, /* TOMOYO_TYPE_SYMLINK_ACL */
+	{ "link",     2, 1 }, /* TOMOYO_TYPE_LINK_ACL */
+	{ "rename",   2, 1 }, /* TOMOYO_TYPE_RENAME_ACL */
+	{ "rewrite",  1, 1 }, /* TOMOYO_TYPE_REWRITE_ACL */
+	{ NULL, 0, 0 }
+};
+
+/*************************  UTILITY FUNCTIONS  *************************/
+
+const char *tomoyo_acltype2keyword(const unsigned int acl_type)
+{
+	return (acl_type < sizeof(acl_type_array) / sizeof(acl_type_array[0]))
+		? acl_type_array[acl_type].keyword : NULL;
+}
+
+int tomoyo_acltype2paths(const unsigned int acl_type)
+{
+	return (acl_type < sizeof(acl_type_array) / sizeof(acl_type_array[0]))
+		? acl_type_array[acl_type].paths : 0;
+}
+
+static unsigned int tomoyo_check_acl_flags(const unsigned int index)
+{
+	if (index < (sizeof(acl_type_array) / sizeof(acl_type_array[0])) - 1)
+		return acl_type_array[index].check_type;
+	printk("%s: Index %u is out of range. Fix the kernel source.\n", __FUNCTION__, index);
+	return 0;
+}
+
+static int tomoyo_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 *tomoyo_get_path(struct dentry *dentry, struct vfsmount *mnt)
+{
+	struct path_info_with_data { /* sizeof(struct path_info_with_data) <= PAGE_SIZE */
+		struct path_info head; /* Keep this first, this pointer is passed to tomoyo_free(). */
+		char bariier1[16];
+		char body[TOMOYO_MAX_PATHNAME_LEN];
+		char barrier2[16];
+	} *buf = tomoyo_alloc(sizeof(*buf));
+	if (buf) {
+		int error;
+		if ((error = tomoyo_realpath_from_dentry2(dentry,
+		                                          mnt,
+		                                          buf->body,
+		                                          sizeof(buf->body) - 1)) == 0) {
+			buf->head.name = buf->body;
+			tomoyo_fill_path_info(&buf->head);
+			return &buf->head;
+		}
+		tomoyo_free(buf);
+		buf = NULL;
+		printk("tomoyo_realpath_from_dentry = %d\n", error);
+	}
+	return NULL;
+}
+
+/*************************  PROTOTYPES  *************************/
+
+static int tomoyo_add_double_write_acl(const u8 type,
+                                       const char *filename1,
+                                       const char *filename2,
+                                       struct domain_info * const domain,
+                                       const u8 is_add);
+static int tomoyo_add_single_write_acl(const u8 type,
+                                       const char *filename,
+                                       struct domain_info * const domain,
+                                       const u8 is_add);
+
+/*************************  AUDIT FUNCTIONS  *************************/
+
+static int tomoyo_audit_file_log(const struct path_info *filename,
+                                 const u8 perm,
+                                 const int is_granted)
+{
+	char *buf;
+	int len;
+	len = filename->total_len + 8;
+	if ((buf = tomoyo_init_audit_log(&len)) == NULL) return -ENOMEM;
+	snprintf(buf + strlen(buf), len - strlen(buf) - 1, "%d %s\n", perm, filename->name);
+	return tomoyo_write_audit_log(buf, is_granted);
+}
+
+static int tomoyo_audit_write_log(const char *operation,
+                                  const struct path_info *filename1,
+                                  const struct path_info *filename2,
+                                  const int is_granted)
+{
+	char *buf;
+	int len;
+	len = strlen(operation) + filename1->total_len + (filename2 ? filename2->total_len : 0) + 16;
+	if ((buf = tomoyo_init_audit_log(&len)) == NULL) return -ENOMEM;
+	snprintf(buf + strlen(buf), len - strlen(buf) - 1, "allow_%s %s %s\n",
+	         operation, filename1->name, filename2 ? filename2->name : "");
+	return tomoyo_write_audit_log(buf, is_granted);
+}
+
+/*************************  PERMISSION MAP HANDLER  *************************/
+
+int tomoyo_set_permission_mapping(struct io_buffer *head)
+{
+	int i;
+	char *data = head->write_buf;
+	char *cp = NULL;
+	if ((cp = strchr(data, '=')) == NULL) {
+	out: ;
+		printk("ERROR: Invalid line '%s=%s'\n", data, cp);
+		printk("This line must be one of the following. The first is the default.\n");
+		printk("%s=%s if you want to check this permission using this permission.\n",
+		       data, data);
+		printk("%s=generic-write if you want to check this permission "
+		       "using generic-write permission.\n", data);
+		printk("%s=no-check if you don't want to check this permission.\n", data);
+		return -EINVAL;
+	}
+	*cp++ = '\0';
+	for (i = 0; acl_type_array[i].keyword; i++) {
+		if (strcmp(acl_type_array[i].keyword, data)) continue;
+		if (strcmp(cp, acl_type_array[i].keyword) == 0) acl_type_array[i].check_type = 1;
+		else if (strcmp(cp, "generic-write") == 0) acl_type_array[i].check_type = 0;
+		else if (strcmp(cp, "no-check") == 0) acl_type_array[i].check_type = -1;
+		else goto out;
+		return 0;
+	}
+	printk("WARNING: Unprocessed line '%s=%s'\n", data, cp);
+	return -EINVAL;
+}
+
+int tomoyo_read_permission_mapping(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		int i;
+		for (i = 0; acl_type_array[i].keyword; i++) {
+			tomoyo_io_printf(head,
+			                 "%s=%s\n",
+			                 acl_type_array[i].keyword,
+			                 acl_type_array[i].check_type > 0 ?
+			                 	acl_type_array[i].keyword :
+			                 	acl_type_array[i].check_type == 0 ?
+			                 		"generic-write" : "no-check");
+		}
+		head->read_eof = 1;
+	}
+	return 0;
+}
+
+/*************************  GLOBALLY READABLE FILE HANDLER  *************************/
+
+static struct globally_readable_file_entry *globally_readable_list = NULL;
+
+static int tomoyo_add_globally_readable_entry(const char *filename, const int is_delete)
+{
+	struct globally_readable_file_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_filename;
+	int error = -ENOMEM;
+	if (!tomoyo_is_correct_path(filename, 1, -1, -1, __FUNCTION__))
+		return -EINVAL; /* No patterns allowed. */
+	if ((saved_filename = tomoyo_save_name(filename)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (ptr = globally_readable_list; ptr; ptr = ptr->next) {
+		if (ptr->filename == saved_filename) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	if ((new_entry = tomoyo_alloc_element(sizeof(*new_entry))) == NULL) goto out;
+	new_entry->filename = saved_filename;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = globally_readable_list) != NULL) {
+		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 tomoyo_is_globally_readable_file(const struct path_info *filename)
+{
+	struct globally_readable_file_entry *ptr;
+	for (ptr = globally_readable_list; ptr; ptr = ptr->next) {
+		if (!ptr->is_deleted && !tomoyo_pathcmp(filename, ptr->filename)) return 1;
+	}
+	return 0;
+}
+
+int tomoyo_add_globally_readable_policy(char *filename, const int is_delete)
+{
+	return tomoyo_add_globally_readable_entry(filename, is_delete);
+}
+
+int tomoyo_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 && tomoyo_io_printf(head,
+		                                         TOMOYO_KEYWORD_ALLOW_READ "%s\n",
+		                                         ptr->filename->name))
+			break;
+		ptr = ptr->next;
+	}
+	return ptr ? -ENOMEM : 0;
+}
+
+/*************************  FILE GROUP HANDLER  *************************/
+
+static struct group_entry *group_list = NULL;
+
+static int tomoyo_add_group_entry(const char *group_name,
+                                  const char *member_name,
+                                  const int is_delete)
+{
+	static DECLARE_MUTEX(lock);
+	struct group_entry *new_group, *group;
+	struct group_member *new_member, *member;
+	const struct path_info *saved_group_name, *saved_member_name;
+	int error = -ENOMEM;
+	if (!tomoyo_is_correct_path(group_name, 0, 0, 0, __FUNCTION__) || !group_name[0] ||
+		!tomoyo_is_correct_path(member_name, 0, 0, 0, __FUNCTION__) || !member_name[0])
+			return -EINVAL;
+	if ((saved_group_name = tomoyo_save_name(group_name)) == NULL ||
+		(saved_member_name = tomoyo_save_name(member_name)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (group = group_list; group; group = group->next) {
+		if (saved_group_name != group->group_name) continue;
+		for (member = group->first_member; member; member = member->next) {
+			if (member->member_name == saved_member_name) {
+				member->is_deleted = is_delete;
+				error = 0;
+				goto out;
+			}
+		}
+		break;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	if (!group) {
+		if ((new_group = tomoyo_alloc_element(sizeof(*new_group))) == NULL) goto out;
+		new_group->group_name = saved_group_name;
+		mb(); /* Instead of using spinlock. */
+		if ((group = group_list) != NULL) {
+			while (group->next)
+				group = group->next;
+			group->next = new_group;
+		} else {
+			group_list= new_group;
+		}
+		group = new_group;
+	}
+	if ((new_member = tomoyo_alloc_element(sizeof(*new_member))) == NULL) goto out;
+	new_member->member_name = saved_member_name;
+	mb(); /* Instead of using spinlock. */
+	if ((member = group->first_member) != NULL) {
+		while (member->next)
+			member = member->next;
+		member->next = new_member;
+	} else {
+		group->first_member = new_member;
+	}
+	error = 0;
+ out:
+	up(&lock);
+	return error;
+}
+
+int tomoyo_add_group_policy(char *data, const int is_delete)
+{
+	char *cp = strchr(data, ' ');
+	if (!cp) return -EINVAL;
+	*cp++ = '\0';
+	return tomoyo_add_group_entry(data, cp, is_delete);
+}
+
+static struct group_entry *tomoyo_find_or_assign_new_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) {
+			tomoyo_add_group_entry(group_name, "/", 0);
+			tomoyo_add_group_entry(group_name, "/", 1);
+		}
+	}
+	return NULL;
+}
+
+static int tomoyo_path_matchies_to_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 (!tomoyo_pathcmp(pathname, member->member_name)) return 1;
+		} else if (may_use_pattern) {
+			if (tomoyo_path_matches_to_pattern(pathname, member->member_name)) return 1;
+		}
+	}
+	return 0;
+}
+
+int tomoyo_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 &&
+			    tomoyo_io_printf(head,
+			                     TOMOYO_KEYWORD_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 = NULL;
+
+static int tomoyo_add_pattern_entry(const char *pattern, const int is_delete)
+{
+	struct pattern_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_pattern;
+	int error = -ENOMEM;
+	if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __FUNCTION__)) return -EINVAL;
+	if ((saved_pattern = tomoyo_save_name(pattern)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (ptr = pattern_list; ptr; ptr = ptr->next) {
+		if (saved_pattern == ptr->pattern) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	if ((new_entry = tomoyo_alloc_element(sizeof(*new_entry))) == NULL) goto out;
+	new_entry->pattern = saved_pattern;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = pattern_list) != NULL) {
+		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 *tomoyo_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 (!tomoyo_path_matches_to_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;
+}
+
+int tomoyo_add_pattern_policy(char *pattern, const int is_delete)
+{
+	return tomoyo_add_pattern_entry(pattern, is_delete);
+}
+
+int tomoyo_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 &&
+		    tomoyo_io_printf(head,
+		                     TOMOYO_KEYWORD_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 = NULL;
+
+static int tomoyo_add_no_rewrite_entry(const char *pattern, const int is_delete)
+{
+	struct no_rewrite_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_pattern;
+	int error = -ENOMEM;
+	if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __FUNCTION__)) return -EINVAL;
+	if ((saved_pattern = tomoyo_save_name(pattern)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (ptr = no_rewrite_list; ptr; ptr = ptr->next) {
+		if (ptr->pattern == saved_pattern) {
+			ptr->is_deleted = is_delete;
+			error = 0;
+			goto out;
+		}
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	if ((new_entry = tomoyo_alloc_element(sizeof(*new_entry))) == NULL) goto out;
+	new_entry->pattern = saved_pattern;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = no_rewrite_list) != NULL) {
+		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 tomoyo_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 (!tomoyo_path_matches_to_pattern(filename, ptr->pattern)) continue;
+		return 1;
+	}
+	return 0;
+}
+
+int tomoyo_add_no_rewrite_policy(char *pattern, const int is_delete)
+{
+	return tomoyo_add_no_rewrite_entry(pattern, is_delete);
+}
+
+int tomoyo_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 &&
+		    tomoyo_io_printf(head,
+		                     TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
+		                     ptr->pattern->name))
+			break;
+		ptr = ptr->next;
+	}
+	return ptr ? -ENOMEM : 0;
+}
+
+/*************************  FILE ACL HANDLER  *************************/
+
+static int tomoyo_add_file_acl(const char *filename,
+                               u8 perm,
+                               struct domain_info * const domain,
+                               const u8 is_add)
+{
+	const struct path_info *saved_filename;
+	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 (!tomoyo_is_correct_path(filename, 0, 0, 0, __FUNCTION__)) return -EINVAL;
+	if (filename[0] == '@') {
+		/* This cast is OK because I don't dereference in this function. */
+		if ((saved_filename =
+			(struct path_info *) tomoyo_find_or_assign_new_group(filename + 1)) == NULL)
+			return -ENOMEM;
+		is_group = 1;
+	} else {
+		if ((saved_filename = tomoyo_save_name(filename)) == NULL) return -ENOMEM;
+		if (!saved_filename->is_dir) {
+			if (perm == 4 &&
+			    tomoyo_is_globally_readable_file(saved_filename) &&
+			    is_add) {
+				return 0;   /* Don't add if the file is globally readable files. */
+			}
+		} else {
+			if ((perm & 2) == 0)
+				return 0; /* Don't add if the directory doesn't have write permission. */
+		}
+	}
+	down(&domain_acl_lock);
+	if (is_add) {
+		if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry;
+		while (1) {
+			struct file_acl_record *new_ptr;
+			if (ptr->type == TOMOYO_TYPE_FILE_ACL) {
+				if (((struct file_acl_record *) ptr)->u.filename == saved_filename) {
+					if (ptr->is_deleted) {
+						ptr->u.b[0] = 0;
+						mb();
+						ptr->is_deleted = 0;
+					}
+					/* Found. Just 'OR' the permission bits. */
+					ptr->u.b[0] |= perm;
+					error = 0;
+					tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY);
+					break;
+				}
+			}
+			if (ptr->next) {
+				ptr = ptr->next;
+				continue;
+			}
+	first_entry: ;
+			if (is_add == 1 && tomoyo_too_many_domain_acl(domain)) break;
+			/* Not found. Append it to the tail. */
+			if ((new_ptr = tomoyo_alloc_element(sizeof(*new_ptr))) == NULL) break;
+			new_ptr->head.type = TOMOYO_TYPE_FILE_ACL;
+			new_ptr->head.u.b[0] = perm;
+			new_ptr->head.u.b[1] = is_group;
+			new_ptr->u.filename = saved_filename;
+			error = tomoyo_add_domain_acl(ptr, domain, (struct acl_info *) new_ptr);
+			break;
+		}
+	} else {
+		for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+			if (ptr->type != TOMOYO_TYPE_FILE_ACL ||
+			    ptr->is_deleted ||
+			    ptr->u.b[0] != perm)
+				continue;
+			if (((struct file_acl_record *) ptr)->u.filename != saved_filename) continue;
+			error = tomoyo_del_domain_acl(ptr);
+			break;
+		}
+	}
+	up(&domain_acl_lock);
+	return error;
+}
+
+static int tomoyo_check_file_acl(const struct path_info *filename, const u8 perm)
+{
+	const struct domain_info *domain =
+		((struct tomoyo_security *) current->security)->domain_info;
+	struct acl_info *ptr;
+	const int may_use_pattern = ((perm & 1) == 0);
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	if (!filename->is_dir) {
+		if (perm == 4 && tomoyo_is_globally_readable_file(filename)) return 0;
+	}
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		if (ptr->type != TOMOYO_TYPE_FILE_ACL ||
+		    ptr->is_deleted ||
+		    (ptr->u.b[0] & perm) != perm)
+			continue;
+		if (ptr->u.b[1]) {
+			if (tomoyo_path_matchies_to_group(filename,
+			                                  ((struct file_acl_record *) ptr)->u.group,
+			                                  may_use_pattern))
+				return 0;
+		} else if (may_use_pattern ||
+		           !((struct file_acl_record *) ptr)->u.filename->is_patterned) {
+			if (tomoyo_path_matches_to_pattern(filename,
+			                                   ((struct file_acl_record *) ptr)->u.filename))
+				return 0;
+		}
+	}
+	return -EPERM;
+}
+
+static int tomoyo_check_file_perm2(const struct path_info *filename, const u8 perm, const char *operation)
+{
+	int error = 0;
+	if (!filename) return 0;
+	error = tomoyo_check_file_acl(filename, perm);
+	tomoyo_audit_file_log(filename, perm, !error);
+	if (error) {
+		struct domain_info * const domain =
+			((struct tomoyo_security *) current->security)->domain_info;
+		const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+		if (tomoyo_verbose_mode()) {
+			printk("TOMOYO-%s: Access %d(%s) to %s denied for %s\n",
+			       tomoyo_get_msg(is_enforce), perm, operation,
+			       filename->name, tomoyo_get_last_name(domain));
+		}
+		if (is_enforce) error =
+			tomoyo_check_supervisor("%s\n%d %s\n",
+			                        domain->domainname->name, perm, filename->name);
+		else if (tomoyo_check_accept(TOMOYO_MAC_FOR_FILE)) {
+			/* Don't use patterns if execution bit is on. */
+			const struct path_info *patterned_file =
+				((perm & 1) == 0) ? tomoyo_get_pattern(filename) : filename;
+			tomoyo_add_file_acl(patterned_file->name, perm, domain, 1);
+		}
+		if (!is_enforce) error = 0;
+	}
+	return error;
+}
+
+int tomoyo_add_file_policy(char *data, struct domain_info *domain, const int is_delete)
+{
+	char *filename = strchr(data, ' ');
+	unsigned int perm;
+	u8 type;
+	if (!filename) return -EINVAL;
+	*filename++ = '\0';
+	if (sscanf(data, "%u", &perm) == 1) {
+		return tomoyo_add_file_acl(filename, (u8) perm, domain, is_delete ? 0 : -1);
+	}
+	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 tomoyo_add_double_write_acl(type,
+			                                   filename,
+			                                   filename2,
+			                                   domain,
+			                                   is_delete ? 0 : -1);
+		} else {
+			return tomoyo_add_single_write_acl(type,
+			                                   filename,
+			                                   domain,
+			                                   is_delete ? 0 : -1);
+		}
+		break;
+	}
+ out:
+	return -EINVAL;
+}
+
+static int tomoyo_add_single_write_acl(const u8 type,
+                                       const char *filename,
+                                       struct domain_info * const domain,
+                                       const u8 is_add)
+{
+	const struct path_info *saved_filename;
+	struct acl_info *ptr;
+	int error = -ENOMEM;
+	u8 is_group = 0;
+	if (!domain) return -EINVAL;
+	if (!tomoyo_is_correct_path(filename, 0, 0, 0, __FUNCTION__)) return -EINVAL;
+	if (filename[0] == '@') {
+		/* This cast is OK because I don't dereference in this function. */
+		if ((saved_filename =
+			(struct path_info *) tomoyo_find_or_assign_new_group(filename + 1)) == NULL)
+			return -ENOMEM;
+		is_group = 1;
+	} else {
+		if ((saved_filename = tomoyo_save_name(filename)) == NULL) return -ENOMEM;
+	}
+	down(&domain_acl_lock);
+	if (is_add) {
+		if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry;
+		while (1) {
+			struct single_acl_record *new_ptr;
+			if (ptr->type == type) {
+				if (((struct single_acl_record *) ptr)->u.filename ==
+				    saved_filename) {
+					ptr->is_deleted = 0;
+					/* Found. Nothing to do. */
+					error = 0;
+					break;
+				}
+			}
+			if (ptr->next) {
+				ptr = ptr->next;
+				continue;
+			}
+		first_entry: ;
+			if (is_add == 1 && tomoyo_too_many_domain_acl(domain)) break;
+			/* Not found. Append it to the tail. */
+			if ((new_ptr = tomoyo_alloc_element(sizeof(*new_ptr))) == NULL) break;
+			new_ptr->head.type = type;
+			new_ptr->head.u.w = is_group;
+			new_ptr->u.filename = saved_filename;
+			error = tomoyo_add_domain_acl(ptr, domain, (struct acl_info *) new_ptr);
+			break;
+		}
+	} else {
+		error = -ENOENT;
+		for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+			if (ptr->type != type || ptr->is_deleted) continue;
+			if (((struct single_acl_record *) ptr)->u.filename != saved_filename)
+				continue;
+			error = tomoyo_del_domain_acl(ptr);
+			break;
+		}
+	}
+	up(&domain_acl_lock);
+	return error;
+}
+
+static int tomoyo_add_double_write_acl(const u8 type,
+                                       const char *filename1,
+                                       const char *filename2,
+                                       struct domain_info * const domain,
+                                       const u8 is_add)
+{
+	const struct path_info *saved_filename1, *saved_filename2;
+	struct acl_info *ptr;
+	int error = -ENOMEM;
+	u8 is_group1 = 0, is_group2 = 0;
+	if (!domain) return -EINVAL;
+	if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __FUNCTION__) ||
+	    !tomoyo_is_correct_path(filename2, 0, 0, 0, __FUNCTION__))
+		return -EINVAL;
+	if (filename1[0] == '@') {
+		/* This cast is OK because I don't dereference in this function. */
+		if ((saved_filename1 =
+			(struct path_info *) tomoyo_find_or_assign_new_group(filename1 + 1)) == NULL)
+			return -ENOMEM;
+		is_group1 = 1;
+	} else {
+		if ((saved_filename1 = tomoyo_save_name(filename1)) == NULL) return -ENOMEM;
+	}
+	if (filename2[0] == '@') {
+		/* This cast is OK because I don't dereference in this function. */
+		if ((saved_filename2 =
+			(struct path_info *) tomoyo_find_or_assign_new_group(filename2 + 1)) == NULL)
+			return -ENOMEM;
+		is_group2 = 1;
+	} else {
+		if ((saved_filename2 = tomoyo_save_name(filename2)) == NULL) return -ENOMEM;
+	}
+	down(&domain_acl_lock);
+	if (is_add) {
+		if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry;
+		while (1) {
+			struct double_acl_record *new_ptr;
+			if (ptr->type == type) {
+				if (((struct double_acl_record *) ptr)->u1.filename1 ==
+					saved_filename1 &&
+				    ((struct double_acl_record *) ptr)->u2.filename2 ==
+					saved_filename2) {
+					ptr->is_deleted = 0;
+					/* Found. Nothing to do. */
+					error = 0;
+					break;
+				}
+			}
+			if (ptr->next) {
+				ptr = ptr->next;
+				continue;
+			}
+		first_entry: ;
+			if (is_add == 1 && tomoyo_too_many_domain_acl(domain)) break;
+			/* Not found. Append it to the tail. */
+			if ((new_ptr = tomoyo_alloc_element(sizeof(*new_ptr))) == NULL) break;
+			new_ptr->head.type = type;
+			new_ptr->head.u.b[0] = is_group1;
+			new_ptr->head.u.b[1] = is_group2;
+			new_ptr->u1.filename1 = saved_filename1;
+			new_ptr->u2.filename2 = saved_filename2;
+			error = tomoyo_add_domain_acl(ptr, domain, (struct acl_info *) new_ptr);
+			break;
+		}
+	} else {
+		error = -ENOENT;
+		for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+			if (ptr->type != type || ptr->is_deleted) continue;
+			if (((struct double_acl_record *) ptr)->u1.filename1 != saved_filename1 ||
+				((struct double_acl_record *) ptr)->u2.filename2 != saved_filename2)
+					continue;
+			error = tomoyo_del_domain_acl(ptr);
+			break;
+		}
+	}
+	up(&domain_acl_lock);
+	return error;
+}
+
+static int tomoyo_check_single_write_acl(const u8 type, const struct path_info *filename)
+{
+	const struct domain_info *domain =
+		((struct tomoyo_security *) current->security)->domain_info;
+	struct acl_info *ptr;
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		if (ptr->type != type || ptr->is_deleted) continue;
+		if (ptr->u.w) {
+			if (!tomoyo_path_matchies_to_group(filename,
+			                                   ((struct single_acl_record *) ptr)->u.group,
+			                                   1))
+				continue;
+		} else {
+			if (!tomoyo_path_matches_to_pattern(filename,
+			                                    ((struct single_acl_record *) ptr)->u.filename))
+				continue;
+		}
+		return 0;
+	}
+	return -EPERM;
+}
+
+static int tomoyo_check_double_write_acl(const u8 type,
+                                         const struct path_info *filename1,
+                                         const struct path_info *filename2)
+{
+	const struct domain_info *domain =
+		((struct tomoyo_security *) current->security)->domain_info;
+	struct acl_info *ptr;
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		if (ptr->type != type || ptr->is_deleted) continue;
+		if (ptr->u.b[0]) {
+			if (!tomoyo_path_matchies_to_group(filename1,
+			                                   ((struct double_acl_record *) ptr)->u1.group1,
+			                                   1))
+				continue;
+		} else {
+			if (!tomoyo_path_matches_to_pattern(filename1,
+			                                    ((struct double_acl_record *) ptr)->u1.filename1))
+				continue;
+		}
+		if (ptr->u.b[1]) {
+			if (!tomoyo_path_matchies_to_group(filename2,
+			                                   ((struct double_acl_record *) ptr)->u2.group2,
+			                                   1))
+				continue;
+		} else {
+			if (!tomoyo_path_matches_to_pattern(filename2,
+			                                    ((struct double_acl_record *) ptr)->u2.filename2))
+				continue;
+		}
+		return 0;
+	}
+	return -EPERM;
+}
+
+static int tomoyo_check_single_write_permission2(const unsigned int operation,
+                                                 const struct path_info *filename)
+{
+	int error;
+	struct domain_info * const domain =
+		((struct tomoyo_security *) current->security)->domain_info;
+	const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	if (tomoyo_check_acl_flags(operation) < 0) return 0;
+	if (tomoyo_check_acl_flags(operation) > 0) {
+		error = tomoyo_check_single_write_acl(operation, filename);
+		tomoyo_audit_write_log(tomoyo_acltype2keyword(operation), filename, NULL, !error);
+		if (error) {
+			if (tomoyo_verbose_mode()) {
+				printk("TOMOYO-%s: Access '%s %s' denied for %s\n",
+				       tomoyo_get_msg(is_enforce),
+				       tomoyo_acltype2keyword(operation),
+				       filename->name,
+				       tomoyo_get_last_name(domain));
+			}
+			if (is_enforce)
+				error = tomoyo_check_supervisor("%s\nallow_%s %s\n",
+				                                domain->domainname->name,
+				                                tomoyo_acltype2keyword(operation),
+				                                filename->name);
+			else if (tomoyo_check_accept(TOMOYO_MAC_FOR_FILE))
+				tomoyo_add_single_write_acl(operation,
+				                            tomoyo_get_pattern(filename)->name,
+				                            domain,
+				                            1);
+			if (!is_enforce)
+				error = 0;
+		}
+	} else {
+		error = tomoyo_check_file_perm2(filename, 2, tomoyo_acltype2keyword(operation));
+	}
+	if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL && tomoyo_is_no_rewrite_file(filename)) {
+		error = tomoyo_check_single_write_permission2(TOMOYO_TYPE_REWRITE_ACL, filename);
+	}
+	return error;
+}
+
+int tomoyo_check_exec_perm(const struct path_info *filename, struct file *filp)
+{
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	return tomoyo_check_file_perm2(filename, 1, "do_execve");
+}
+
+int tomoyo_check_open_permission(struct dentry *dentry, struct vfsmount *mnt, const int flag)
+{
+	const int acc_mode = ACC_MODE(flag);
+	int error = -ENOMEM;
+	struct path_info *buf;
+	const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+	if (!tomoyo_check_flags(TOMOYO_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 = tomoyo_get_path(dentry, mnt);
+	if (buf) {
+		error = 0;
+		if ((acc_mode & MAY_WRITE)) {
+			if ((flag & O_TRUNC) || !(flag & O_APPEND)) {
+				if (tomoyo_is_no_rewrite_file(buf)) {
+					error = tomoyo_check_single_write_permission2(TOMOYO_TYPE_REWRITE_ACL,
+					                                              buf);
+				}
+			}
+		}
+		if (error == 0) error = tomoyo_check_file_perm2(buf, acc_mode, "open");
+		if (error == 0 && (flag & O_TRUNC))
+			error = tomoyo_check_single_write_permission2(TOMOYO_TYPE_TRUNCATE_ACL, buf);
+		tomoyo_free(buf);
+	}
+	if (!is_enforce) error = 0;
+	return error;
+}
+
+int tomoyo_check_single_write_permission(const unsigned int operation,
+                                         struct dentry *dentry,
+                                         struct vfsmount *mnt)
+{
+	int error = -ENOMEM;
+	struct path_info *buf;
+	const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	buf = tomoyo_get_path(dentry, mnt);
+	if (buf) {
+		switch (operation) {
+		case TOMOYO_TYPE_MKDIR_ACL:
+		case TOMOYO_TYPE_RMDIR_ACL:
+			if (!buf->is_dir) {
+				strcat((char *) buf->name, "/");
+				tomoyo_fill_path_info(buf);
+			}
+		}
+		error = tomoyo_check_single_write_permission2(operation, buf);
+		tomoyo_free(buf);
+	}
+	if (!is_enforce) error = 0;
+	return error;
+}
+
+int tomoyo_check_rewrite_permission(struct file *filp)
+{
+	int error = -ENOMEM;
+	const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+	struct path_info *buf = tomoyo_get_path(filp->f_dentry, filp->f_vfsmnt);
+	if (buf) {
+		if (tomoyo_is_no_rewrite_file(buf)) {
+			error = tomoyo_check_single_write_permission2(TOMOYO_TYPE_REWRITE_ACL, buf);
+		} else {
+			error = 0;
+		}
+		tomoyo_free(buf);
+	}
+	if (!is_enforce) error = 0;
+	return error;
+}
+
+int tomoyo_check_double_write_permission(const unsigned int operation,
+                                         struct dentry *dentry1,
+                                         struct vfsmount *mnt1,
+                                         struct dentry *dentry2,
+                                         struct vfsmount *mnt2)
+{
+	int error = -ENOMEM;
+	struct path_info *buf1, *buf2;
+	struct domain_info * const domain =
+		((struct tomoyo_security *) current->security)->domain_info;
+	const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+	if (!tomoyo_check_flags(TOMOYO_MAC_FOR_FILE)) return 0;
+	if (tomoyo_check_acl_flags(operation) < 0) return 0;		
+	buf1 = tomoyo_get_path(dentry1, mnt1);
+	buf2 = tomoyo_get_path(dentry2, mnt2);
+	if (buf1 && buf2) {
+		if (operation == TOMOYO_TYPE_RENAME_ACL) {
+			/* TOMOYO_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, "/");
+					tomoyo_fill_path_info(buf1);
+				}
+				if (!buf2->is_dir) {
+					strcat((char *) buf2->name, "/");
+					tomoyo_fill_path_info(buf2);
+				}
+			}
+		}
+		if (tomoyo_check_acl_flags(operation) > 0) {
+			error = tomoyo_check_double_write_acl(operation, buf1, buf2);
+			tomoyo_audit_write_log(tomoyo_acltype2keyword(operation), buf1, buf2, !error);
+			if (error) {
+				if (tomoyo_verbose_mode()) {
+					printk("TOMOYO-%s: Access '%s %s %s' denied for %s\n",
+					       tomoyo_get_msg(is_enforce),
+					       tomoyo_acltype2keyword(operation),
+					       buf1->name,
+					       buf2->name,
+					       tomoyo_get_last_name(domain));
+				}
+				if (is_enforce)
+					error = tomoyo_check_supervisor("%s\nallow_%s %s %s\n",
+					                                domain->domainname->name,
+					                                tomoyo_acltype2keyword(operation),
+					                                buf1->name,
+					                                buf2->name);
+				else if (tomoyo_check_accept(TOMOYO_MAC_FOR_FILE))
+					tomoyo_add_double_write_acl(operation,
+					                            tomoyo_get_pattern(buf1)->name,
+					                            tomoyo_get_pattern(buf2)->name,
+					                            domain,
+					                            1);
+			}
+		} else {
+			error = tomoyo_check_file_perm2(buf1, 2, tomoyo_acltype2keyword(operation));
+			if (!error)
+				error = tomoyo_check_file_perm2(buf2,
+				                                2,
+				                                tomoyo_acltype2keyword(operation));
+		}
+	}
+	tomoyo_free(buf1);
+	tomoyo_free(buf2);
+	if (!is_enforce) error = 0;
+	return error;
+}
---------------

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists