linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [TOMOYO 0/9] TOMOYO Linux security module.
@ 2007-06-14  7:30 Kentaro Takeda
  2007-06-14  7:32 ` [TOMOYO 1/9] Allow use of namespace_sem from LSM module Kentaro Takeda
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:30 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

The following patches are TOMOYO Linux 2.0.
TOMOYO Linux 2.0 is implemented as a LSM module.

If you want to use older kernel, please download from
http://osdn.dl.sourceforge.jp/tomoyo/25693/tomoyo-lsm-2.0-20070605.tar.gz

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

* [TOMOYO 1/9] Allow use of namespace_sem from LSM module.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
@ 2007-06-14  7:32 ` Kentaro Takeda
  2007-06-14 16:13   ` Pavel Machek
  2007-06-14  7:33 ` [TOMOYO 2/9] Kconfig and Makefile for TOMOYO Linux Kentaro Takeda
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:32 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

TOMOYO Linux uses pathnames for auditing and controlling file access.
Therefore, namespace_sem is needed.

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

---------------
 fs/namespace.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff -ubBpErN linux-2.6.21.5/fs/namespace.c linux-2.6.21.5-tomoyo/fs/namespace.c
--- linux-2.6.21.5/fs/namespace.c	2007-06-12 03:37:06.000000000 +0900
+++ linux-2.6.21.5-tomoyo/fs/namespace.c	2007-06-14 15:02:38.000000000 +0900
@@ -37,7 +37,7 @@ static int event;
 static struct list_head *mount_hashtable __read_mostly;
 static int hash_mask __read_mostly, hash_bits __read_mostly;
 static struct kmem_cache *mnt_cache __read_mostly;
-static struct rw_semaphore namespace_sem;
+struct rw_semaphore namespace_sem;
 
 /* /sys/fs */
 decl_subsys(fs, NULL, NULL);
---------------


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

* [TOMOYO 2/9] Kconfig and Makefile for TOMOYO Linux.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
  2007-06-14  7:32 ` [TOMOYO 1/9] Allow use of namespace_sem from LSM module Kentaro Takeda
@ 2007-06-14  7:33 ` Kentaro Takeda
  2007-06-14  7:34 ` [TOMOYO 3/9] Data structures and prototypes definition Kentaro Takeda
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:33 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

TOMOYO Linux 2.0 is implemented using LSM and auditing subsystem.
When you use TOMOYO, you need to enable auditing support and disable all features
(other than TOMOYO Linux) that use LSM because TOMOYO Linux 2.0 has to be built-in.
If you don't want to disable any features that use LSM, please use TOMOYO Linux 1.4.1 instead.

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        |    1 +
 security/tomoyo/Kconfig  |   22 ++++++++++++++++++++++
 security/tomoyo/Makefile |    3 +++
 4 files changed, 27 insertions(+)

diff -ubBpErN linux-2.6.21.5/security/Kconfig linux-2.6.21.5-tomoyo/security/Kconfig
--- linux-2.6.21.5/security/Kconfig	2007-06-12 03:37:06.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/Kconfig	2007-06-14 15:02:38.000000000 +0900
@@ -94,6 +94,7 @@ config SECURITY_ROOTPLUG
 	  If you are unsure how to answer this question, answer N.
 
 source security/selinux/Kconfig
+source security/tomoyo/Kconfig
 
 endmenu
 
diff -ubBpErN linux-2.6.21.5/security/Makefile linux-2.6.21.5-tomoyo/security/Makefile
--- linux-2.6.21.5/security/Makefile	2007-06-12 03:37:06.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/Makefile	2007-06-14 15:02:38.000000000 +0900
@@ -16,3 +16,4 @@ obj-$(CONFIG_SECURITY)			+= security.o d
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_CAPABILITIES)	+= commoncap.o capability.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
+obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
diff -ubBpErN linux-2.6.21.5/security/tomoyo/Kconfig linux-2.6.21.5-tomoyo/security/tomoyo/Kconfig
--- linux-2.6.21.5/security/tomoyo/Kconfig	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/Kconfig	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,22 @@
+config SECURITY_TOMOYO
+	bool "TOMOYO Linux support"
+	depends on SECURITY && AUDIT
+	help
+	  Say Y here to support TOMOYO Linux.
+
+	  TOMOYO Linux is applicable to figuring out the system's behavior,
+	  for TOMOYO uses the canonicalized absolute pathnames and
+	  TreeView style domain transitions.
+
+config TOMOYO_MAX_ACCEPT_ENTRY
+	int "Default maximal count for accept mode"
+	default 2048
+	range 0 2147483647
+	depends on SECURITY_TOMOYO
+	help
+	  This is the default value for maximal ACL entries
+	  that are automatically appended into policy at "accept mode".
+	  Some programs access thousands of objects, so running
+	  such programs in "accept mode" dulls the system response
+	  and consumes much memory.
+	  This is the safeguard for such programs.
diff -ubBpErN linux-2.6.21.5/security/tomoyo/Makefile linux-2.6.21.5-tomoyo/security/tomoyo/Makefile
--- linux-2.6.21.5/security/tomoyo/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/Makefile	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo.o tomoyo_func.o
+tomoyo_func-objs := domain.o common.o realpath.o file.o audit.o
+EXTRA_CFLAGS += -Isecurity/tomoyo/include
---------------


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

* [TOMOYO 3/9] Data structures and prototypes definition.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
  2007-06-14  7:32 ` [TOMOYO 1/9] Allow use of namespace_sem from LSM module Kentaro Takeda
  2007-06-14  7:33 ` [TOMOYO 2/9] Kconfig and Makefile for TOMOYO Linux Kentaro Takeda
@ 2007-06-14  7:34 ` Kentaro Takeda
  2007-06-14  7:34 ` [TOMOYO 4/9] LSM adapter for TOMOYO Kentaro Takeda
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:34 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

This is prototypes and structures definition.

Many of structures are single-linked list and memory allocated for them are never freed,
because entries used for access control needn't to be removed from the list so frequently
compared to general other entries in the kernel. This saves the amount of memory needed by TOMOYO.

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

---------------
 security/tomoyo/include/tomoyo.h |  319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 319 insertions(+)

diff -ubBpErN linux-2.6.21.5/security/tomoyo/include/tomoyo.h linux-2.6.21.5-tomoyo/security/tomoyo/include/tomoyo.h
--- linux-2.6.21.5/security/tomoyo/include/tomoyo.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/include/tomoyo.h	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,319 @@
+/*
+ * security/tomoyo/include/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ *
+ */
+#ifndef _TOMOYO_H
+#define _TOMOYO_H
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/utime.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+#include <stdarg.h>
+#include <linux/delay.h>
+
+/***** TOMOYO Linux start. *****/
+
+struct tomoyo_security {
+	struct domain_info *domain_info;
+	u32 flags;
+};
+
+struct 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)            */
+	u8 is_dir;       /* = tomoyo_strendswith(name, "/")             */
+	u8 is_patterned; /* = PathContainsPattern(name)          */
+	u16 depth;       /* = tomoyo_path_depth(name)                    */
+};
+
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+struct group_member {
+	struct group_member *next;
+	const struct path_info *member_name;
+	int is_deleted;
+};
+
+struct group_entry {
+	struct group_entry *next;
+	const struct path_info *group_name;
+	struct group_member *first_member;
+};
+
+/*
+ *  TOMOYO uses the following structures.
+ *  Memory allocated for these structures are never kfree()ed.
+ *  Since no locks are used for reading, assignment must be performed atomically.
+ */
+
+/*************************  The structure for domains.  *************************/
+
+struct acl_info {
+	struct acl_info *next;
+	u8 type;
+	u8 is_deleted;
+	union {
+		u16 w;
+		u8 b[2];
+	} u;
+};
+
+struct domain_info {
+	struct domain_info *next;           /* Pointer to next record. NULL if none. */
+	struct acl_info *first_acl_ptr;     /* Pointer to first acl. NULL if none.   */
+	const struct path_info *domainname; /* Name of this domain. Never NULL.      */
+	u8 profile;                         /* Profile to use.                       */
+	u8 is_deleted;                      /* Delete flag.                          */
+	u8 quota_warned;                    /* Quota warnning done flag.             */
+};
+
+#define TOMOYO_MAX_PROFILES 256
+
+struct file_acl_record {
+	struct acl_info head; /* type = TOMOYO_TYPE_FILE_ACL, b[0] = perm, b[1] = u_is_group */
+	union {
+		const struct path_info *filename;   /* Pointer to single pathname. */
+		const struct group_entry *group;    /* Pointer to pathname group.  */
+	} u;
+};
+
+struct single_acl_record {
+	struct acl_info head;                 /* type = TOMOYO_TYPE_*, w = u_is_group */
+	union {
+		const struct path_info *filename; /* Pointer to single pathname. */
+		const struct group_entry *group;  /* Pointer to pathname group.  */
+	} u;
+};
+
+struct double_acl_record {
+	/* type = TOMOYO_TYPE_RENAME_ACL or TOMOYO_TYPE_LINK_ACL, */
+	/* b[0] = u1_is_group, b[1] = u2_is_group */
+	struct acl_info head;
+	union {
+		const struct path_info *filename1;  /* Pointer to single pathname. */
+		const struct group_entry *group1;   /* Pointer to pathname group.  */
+	} u1;
+	union {
+		const struct path_info *filename2;  /* Pointer to single pathname. */
+		const struct group_entry *group2;   /* Pointer to pathname group.  */
+	} u2;
+};
+
+/*************************  Keywords for ACLs.  *************************/
+
+#define TOMOYO_KEYWORD_AGGREGATOR               "aggregator "
+#define TOMOYO_KEYWORD_AGGREGATOR_LEN           (sizeof(TOMOYO_KEYWORD_AGGREGATOR) - 1)
+#define TOMOYO_KEYWORD_ALIAS                    "alias "
+#define TOMOYO_KEYWORD_ALIAS_LEN                (sizeof(TOMOYO_KEYWORD_ALIAS) - 1)
+#define TOMOYO_KEYWORD_ALLOW_READ               "allow_read "
+#define TOMOYO_KEYWORD_ALLOW_READ_LEN           (sizeof(TOMOYO_KEYWORD_ALLOW_READ) - 1)
+#define TOMOYO_KEYWORD_DELETE                   "delete "
+#define TOMOYO_KEYWORD_DELETE_LEN               (sizeof(TOMOYO_KEYWORD_DELETE) - 1)
+#define TOMOYO_KEYWORD_DENY_REWRITE             "deny_rewrite "
+#define TOMOYO_KEYWORD_DENY_REWRITE_LEN         (sizeof(TOMOYO_KEYWORD_DENY_REWRITE) - 1)
+#define TOMOYO_KEYWORD_FILE_PATTERN             "file_pattern "
+#define TOMOYO_KEYWORD_FILE_PATTERN_LEN         (sizeof(TOMOYO_KEYWORD_FILE_PATTERN) - 1)
+#define TOMOYO_KEYWORD_INITIALIZER              "initializer "
+#define TOMOYO_KEYWORD_INITIALIZER_LEN          (sizeof(TOMOYO_KEYWORD_INITIALIZER) - 1)
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN        "initialize_domain "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN_LEN    (sizeof(TOMOYO_KEYWORD_INITIALIZE_DOMAIN) - 1)
+#define TOMOYO_KEYWORD_KEEP_DOMAIN              "keep_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN_LEN          (sizeof(TOMOYO_KEYWORD_KEEP_DOMAIN) - 1)
+#define TOMOYO_KEYWORD_NO_INITIALIZER           "no_initializer "
+#define TOMOYO_KEYWORD_NO_INITIALIZER_LEN       (sizeof(TOMOYO_KEYWORD_NO_INITIALIZER) - 1)
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN     "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN_LEN (sizeof(TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN) - 1)
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN           "no_keep_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN_LEN       (sizeof(TOMOYO_KEYWORD_NO_KEEP_DOMAIN) - 1)
+#define TOMOYO_KEYWORD_PATH_GROUP               "path_group "
+#define TOMOYO_KEYWORD_PATH_GROUP_LEN           (sizeof(TOMOYO_KEYWORD_PATH_GROUP) - 1)
+#define TOMOYO_KEYWORD_SELECT                   "select "
+#define TOMOYO_KEYWORD_SELECT_LEN               (sizeof(TOMOYO_KEYWORD_SELECT) - 1)
+#define TOMOYO_KEYWORD_UNDELETE                 "undelete "
+#define TOMOYO_KEYWORD_UNDELETE_LEN             (sizeof(TOMOYO_KEYWORD_UNDELETE) - 1)
+
+#define TOMOYO_KEYWORD_USE_PROFILE              "use_profile "
+
+#define TOMOYO_ROOT_NAME "<kernel>"             /* A domain definition starts with <kernel> . */
+#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/*************************  Index numbers for Access Controls.  *************************/
+
+#define TOMOYO_PROFILE_COMMENT                      0  /* status.txt             */
+#define TOMOYO_MAC_FOR_FILE                  1  /* domain_policy.txt      */
+#define TOMOYO_MAX_ACCEPT_ENTRY              2
+#define TOMOYO_VERBOSE                       3
+#define TOMOYO_ALLOW_ENFORCE_GRACE                  4
+#define TOMOYO_MAX_CONTROL_INDEX                    5
+
+/*************************  Index numbers for updates counter.  *************************/
+
+#define TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY    1
+#define TOMOYO_UPDATES_COUNTER_EXCEPTION_POLICY 2
+#define TOMOYO_UPDATES_COUNTER_STATUS           3
+#define TOMOYO_UPDATES_COUNTER_QUERY            4
+#define TOMOYO_UPDATES_COUNTER_MANAGER          5
+#define TOMOYO_MAX_UPDATES_COUNTER              6
+
+/*************************  Indexes for /proc interfaces.  *************************/
+
+#define TOMOYO_POLICY_DOMAINPOLICY          0
+#define TOMOYO_POLICY_EXCEPTIONPOLICY       1
+#define TOMOYO_POLICY_DOMAIN_STATUS         2
+#define TOMOYO_INFO_PROCESS_STATUS          3
+#define TOMOYO_INFO_MEMINFO                 4
+#define TOMOYO_INFO_SELFDOMAIN              5
+#define TOMOYO_INFO_MAPPING                 6
+#define TOMOYO_STATUS                       7
+#define TOMOYO_POLICY_QUERY                 8
+#define TOMOYO_POLICY_MANAGER               9
+#define TOMOYO_INFO_UPDATESCOUNTER         10
+
+/*************************  The structure for /proc interfaces.  *************************/
+
+struct io_buffer {
+	int (*read) (struct io_buffer *);
+	struct semaphore read_sem;
+	int (*write) (struct io_buffer *);
+	struct semaphore write_sem;
+	int (*poll) (struct file *file, poll_table *wait);
+	void *read_var1;                  /* The position currently reading from. */
+	void *read_var2;                  /* Extra variables for reading.         */
+	struct domain_info *write_var1;   /* The position currently writing to.   */
+	int read_step;                    /* The step for reading.                */
+	char *read_buf;                   /* Buffer for reading.                  */
+	int read_eof;                     /* EOF flag for reading.                */
+	int read_avail;                   /* Bytes available for reading.         */
+	int readbuf_size;                 /* Size of read buffer.                 */
+	char *write_buf;                  /* Buffer for writing.                  */
+	int write_avail;                  /* Bytes available for writing.         */
+	int writebuf_size;                /* Size of write buffer.                */
+};
+
+/*************************  PROTOTYPES  *************************/
+
+char *tomoyo_init_audit_log(int *len);
+const char *tomoyo_get_exe(void);
+const char *tomoyo_get_last_name(const struct domain_info *domain);
+const char *tomoyo_get_msg(const int is_enforce);
+const char *tomoyo_acltype2keyword(const unsigned int acl_type);
+int tomoyo_add_aggregator_policy(char *data, const int is_delete);
+int tomoyo_add_alias_policy(char *data, const int is_delete);
+int tomoyo_add_domain_acl(struct acl_info *ptr,
+                          struct domain_info *domain,
+                          struct acl_info *new_ptr);
+int tomoyo_add_domain_initializer_policy(char *data,
+                                         const int is_not,
+                                         const int is_delete,
+                                         const int is_oldstyle);
+int tomoyo_add_domain_keeper_policy(char *data, const int is_not, const int is_delete);
+int tomoyo_add_file_policy(char *data, struct domain_info *domain, const int is_delete);
+int tomoyo_add_globally_readable_policy(char *data, const int is_delete);
+int tomoyo_add_group_policy(char *data, const int is_delete);
+int tomoyo_add_no_rewrite_policy(char *pattern, const int is_delete);
+int tomoyo_add_pattern_policy(char *data, const int is_delete);
+int tomoyo_check_supervisor(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
+int tomoyo_del_domain_acl(struct acl_info *ptr);
+int tomoyo_delete_domain(char *data);
+int tomoyo_is_correct_domain(const unsigned char *domainname, const char *function);
+int tomoyo_is_correct_path(const char *filename,
+                           const int start_type,
+                           const int pattern_type,
+                           const int end_type,
+                           const char *function);
+int tomoyo_is_domain_def(const unsigned char *buffer);
+int tomoyo_path_matches_to_pattern(const struct path_info *pathname0,
+                                   const struct path_info *pattern0);
+int tomoyo_read_aggregator_policy(struct io_buffer *head);
+int tomoyo_read_alias_policy(struct io_buffer *head);
+int tomoyo_read_domain_initializer_policy(struct io_buffer *head);
+int tomoyo_read_domain_keeper_policy(struct io_buffer *head);
+int tomoyo_read_globally_readable_policy(struct io_buffer *head);
+int tomoyo_read_group_policy(struct io_buffer *head);
+int tomoyo_read_no_rewrite_policy(struct io_buffer *head);
+int tomoyo_read_pattern_policy(struct io_buffer *head);
+int tomoyo_read_permission_mapping(struct io_buffer *head);
+int tomoyo_read_self_domain(struct io_buffer *head);
+int tomoyo_set_permission_mapping(struct io_buffer *head);
+int tomoyo_write_audit_log(char *log, const int is_granted);
+int tomoyo_acltype2paths(const unsigned int acl_type);
+int tomoyo_io_printf(struct io_buffer *head, const char *fmt, ...)
+	__attribute__ ((format(printf, 2, 3)));
+struct domain_info *tomoyo_find_domain(const char *domainname);
+struct domain_info *tomoyo_find_or_assign_new_domain(const char *domainname, const u8 profile);
+struct domain_info *tomoyo_undelete_domain(const char *domainname0);
+unsigned int tomoyo_check_accept(const unsigned int index);
+unsigned int tomoyo_check_enforce(const unsigned int index);
+unsigned int tomoyo_check_flags(const unsigned int index);
+unsigned int tomoyo_verbose_mode(void);
+void tomoyo_update_counter(const unsigned char index);
+void *tomoyo_alloc(const size_t size);
+void tomoyo_free(const void *p);
+void tomoyo_fill_path_info(struct path_info *ptr);
+
+static inline int tomoyo_pathcmp(const struct path_info *a, const struct path_info *b)
+{
+	return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+extern struct domain_info KERNEL_DOMAIN;
+
+struct path_info;
+
+int tomoyo_check_exec_perm(const struct path_info *filename, struct file *filp);
+/* Check whether the given dentry is allowed to read/write/execute. */
+int tomoyo_check_open_permission(struct dentry *dentry, struct vfsmount *mnt, const int flag);
+/* Check whether the given dentry is allowed to write. */
+int tomoyo_check_single_write_permission(const unsigned int operation,
+                                         struct dentry *dentry,
+                                         struct vfsmount *mnt);
+int tomoyo_check_double_write_permission(const unsigned int operation,
+                                         struct dentry *dentry1,
+                                         struct vfsmount *mnt1,
+                                         struct dentry *dentry2,
+                                         struct vfsmount *mnt2);
+int tomoyo_check_rewrite_permission(struct file *filp);
+
+struct inode;
+
+/*************************  Index numbers for Access Controls.  *************************/
+
+#define TOMOYO_TYPE_CREATE_ACL       0
+#define TOMOYO_TYPE_UNLINK_ACL       1
+#define TOMOYO_TYPE_MKDIR_ACL        2
+#define TOMOYO_TYPE_RMDIR_ACL        3
+#define TOMOYO_TYPE_MKFIFO_ACL       4
+#define TOMOYO_TYPE_MKSOCK_ACL       5
+#define TOMOYO_TYPE_MKBLOCK_ACL      6
+#define TOMOYO_TYPE_MKCHAR_ACL       7
+#define TOMOYO_TYPE_TRUNCATE_ACL     8
+#define TOMOYO_TYPE_SYMLINK_ACL      9
+#define TOMOYO_TYPE_LINK_ACL        10
+#define TOMOYO_TYPE_RENAME_ACL      11
+#define TOMOYO_TYPE_REWRITE_ACL     12
+
+#define TOMOYO_TYPE_FILE_ACL         100
+
+struct linux_binprm;
+struct pt_regs;
+
+#define TOMOYO_CHECK_READ_FOR_OPEN_EXEC 1
+
+int tomoyo_too_many_domain_acl(struct domain_info * const domain);
+/***** TOMOYO Linux end. *****/
+#endif
---------------


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

* [TOMOYO 4/9] LSM adapter for TOMOYO.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (2 preceding siblings ...)
  2007-06-14  7:34 ` [TOMOYO 3/9] Data structures and prototypes definition Kentaro Takeda
@ 2007-06-14  7:34 ` Kentaro Takeda
  2007-06-14  7:36 ` [TOMOYO 5/9] Memory and pathname management functions Kentaro Takeda
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:34 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

This file contains wrapper functions for TOMOYO's file access control functions.
The main job is to find "struct vfsmount" that corresponds to "struct dentry"
passed to LSM hooks.

Since "struct vfsmount" is not passed to LSM hooks,
TOMOYO can't determine which pathnames was requested by the process
if bind mounts are used.

If bind mounts are used, TOMOYO requires all permissions for
all possible pathnames (whereas AppArmor requires one of possible pathnames).
If "struct vfsmount" is passed to LSM hooks as AppArmor proposes,
this file will become more simpler and "namespace_sem" can remain "static".

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

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

diff -ubBpErN linux-2.6.21.5/security/tomoyo/tomoyo.c linux-2.6.21.5-tomoyo/security/tomoyo/tomoyo.c
--- linux-2.6.21.5/security/tomoyo/tomoyo.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/tomoyo.c	2007-06-14 15:11:57.000000000 +0900
@@ -0,0 +1,283 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/highmem.h>
+#include <linux/namei.h>
+#include <linux/mnt_namespace.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+
+#include "tomoyo.h"
+
+/* The initial domain. */
+struct domain_info KERNEL_DOMAIN = { NULL, NULL, NULL, 0, 0, 0 };
+extern struct rw_semaphore namespace_sem;
+
+static struct kmem_cache *tomoyo_cachep = NULL;
+
+static int tomoyo_task_alloc_security(struct task_struct *p)
+{
+	struct tomoyo_security *ptr = kmem_cache_alloc(tomoyo_cachep, GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+	memcpy(ptr, current->security, sizeof(*ptr));
+	p->security = ptr;
+	return 0;
+}
+
+static void tomoyo_task_free_security(struct task_struct *p)
+{
+	kmem_cache_free(tomoyo_cachep, p->security);
+}
+
+static int tomoyo_bprm_alloc_security(struct linux_binprm *bprm)
+{
+	bprm->security = ((struct tomoyo_security *) current->security)->domain_info;
+	return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm * bprm)
+{
+	struct domain_info *next_domain = NULL;
+ 	int retval = 0;
+	extern void tomoyo_load_policy(const char *filename);
+	extern int tomoyo_find_next_domain(struct linux_binprm *, struct domain_info **);
+	tomoyo_load_policy(bprm->filename);
+	if (!(((struct tomoyo_security *) current->security)->flags
+	      & TOMOYO_CHECK_READ_FOR_OPEN_EXEC)) {
+		retval = tomoyo_find_next_domain(bprm, &next_domain);
+		if (retval == 0) {
+			((struct tomoyo_security *) current->security)->domain_info = next_domain;
+			((struct tomoyo_security *) current->security)->flags |=
+				TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
+		}
+	}
+	
+	return retval;
+}
+
+static void tomoyo_bprm_post_apply_creds(struct linux_binprm * bprm)
+{
+	bprm->security = ((struct tomoyo_security *) current->security)->domain_info;
+}
+
+static void tomoyo_bprm_free_security(struct linux_binprm * bprm)
+{
+	((struct tomoyo_security *) current->security)->domain_info =
+		(struct domain_info *) bprm->security;
+	((struct tomoyo_security *) current->security)->flags &=
+		~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
+}
+
+static int tomoyo_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	int flag = 0;
+	if (S_ISDIR(inode->i_mode)) /* ignore because inode is directory */
+		return 0;
+	if (!nd || !nd->dentry || !nd->mnt)
+		return 0;
+	if ((mask == MAY_EXEC) && (((struct tomoyo_security *) current->security)->flags &
+	                           TOMOYO_CHECK_READ_FOR_OPEN_EXEC))
+		mask = MAY_READ;
+	if ((mask == MAY_EXEC) || (mask == 0))
+		return 0;
+
+	if (mask == (MAY_READ | MAY_EXEC)) {
+		flag |= O_RDONLY + 1;
+	} else {
+		if (mask & MAY_READ)
+			flag |= O_RDONLY + 1;
+		if (mask & MAY_WRITE)
+			flag |= O_WRONLY + 1;
+		if ((mask & MAY_APPEND))
+			flag |= O_APPEND;
+	}
+	return tomoyo_check_open_permission(nd->dentry, nd->mnt, flag);
+}
+
+static int tomoyo_do_check_single_write_permission(int operation, struct dentry *dentry)
+{
+	struct mnt_namespace *namespace = current->nsproxy->mnt_ns;
+	struct list_head *p;
+	int ret = 0, error = 0;
+	int index = 0, index2 = 0;
+	if (!dentry || !namespace)
+		return 0;
+ start:
+	index2 = 0;
+	/* lock namespace */
+	down_read(&namespace_sem);
+	list_for_each(p, &namespace->list) {
+		struct vfsmount *mnt = list_entry(p, struct vfsmount, mnt_list);
+		if (mnt->mnt_root->d_sb != dentry->d_sb) continue;
+		if (index2++ < index) continue;
+		/* unlock namespace */
+		up_read(&namespace_sem);
+		if ((error = tomoyo_check_single_write_permission(operation, dentry, mnt)) != 0)
+			ret = error;
+		index++;
+		goto start;
+	}
+	/* unlock namespace */
+	up_read(&namespace_sem);
+	return ret;
+}
+
+static int tomoyo_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+	if (iattr->ia_valid & ATTR_SIZE)
+		return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_TRUNCATE_ACL, dentry);
+	return 0;
+}
+
+static int tomoyo_inode_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+	return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_CREATE_ACL, dentry);
+}
+
+static int tomoyo_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+	return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_UNLINK_ACL, dentry);
+}
+
+static int tomoyo_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_MKDIR_ACL, dentry);
+}
+
+static int tomoyo_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_RMDIR_ACL, dentry);
+}
+
+static int tomoyo_inode_symlink(struct inode *dir, struct dentry *dentry, const char *old_name)
+{
+	return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_SYMLINK_ACL, dentry);
+}
+
+static int tomoyo_inode_mknod(struct inode *inode, struct dentry *dentry, int mode, dev_t dev)
+{
+	if (S_ISCHR(mode))
+		return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_MKCHAR_ACL, dentry);
+	if (S_ISBLK(mode))
+		return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_MKBLOCK_ACL, dentry);
+	if (S_ISFIFO(mode))
+		return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_MKFIFO_ACL, dentry);
+	if (S_ISSOCK(mode))
+		return tomoyo_do_check_single_write_permission(TOMOYO_TYPE_MKSOCK_ACL, dentry);
+	return 0;
+}
+
+static int tomoyo_do_check_double_write_permission(int operation,
+                                                   struct dentry *old_dentry,
+                                                   struct dentry *new_dentry)
+{
+	struct mnt_namespace *namespace = current->nsproxy->mnt_ns;
+	struct list_head *p;
+	int ret = 0, error = 0;
+	int index = 0, index2 = 0;
+	if (!old_dentry || !new_dentry)
+		return 0;
+ start:
+	index2 = 0;
+	/* lock namespace */
+	down_read(&namespace_sem);
+	list_for_each(p, &namespace->list) {
+		struct vfsmount *mnt = list_entry(p, struct vfsmount, mnt_list);
+		if (mnt->mnt_root->d_sb != old_dentry->d_sb) continue;
+		if (index2++ < index) continue;
+		/* unlock namespace */
+		up_read(&namespace_sem);
+		if ((error = tomoyo_check_double_write_permission(operation,
+		                                                  old_dentry,
+		                                                  mnt,
+		                                                  new_dentry,
+		                                                  mnt)) != 0)
+			ret = error;
+		index++;
+		goto start;
+	}
+	/* unlock namespace */
+	up_read(&namespace_sem);
+	return ret;
+}
+
+static int tomoyo_inode_link(struct dentry *old_dentry,
+                             struct inode *inode,
+                             struct dentry *new_dentry)
+{
+	return tomoyo_do_check_double_write_permission(TOMOYO_TYPE_LINK_ACL, old_dentry, new_dentry);
+}
+
+static int tomoyo_inode_rename(struct inode *old_inode,
+                               struct dentry *old_dentry,
+                               struct inode *new_inode,
+                               struct dentry *new_dentry)
+{
+	return tomoyo_do_check_double_write_permission(TOMOYO_TYPE_RENAME_ACL,
+	                                               old_dentry,
+	                                               new_dentry);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	if (!(arg & O_APPEND))
+		return tomoyo_check_rewrite_permission(file);
+	return 0;
+}
+
+struct security_operations tomoyo_security_ops = {
+	.task_alloc_security   = tomoyo_task_alloc_security,
+	.task_free_security    = tomoyo_task_free_security,
+	.bprm_alloc_security   = tomoyo_bprm_alloc_security,
+	.bprm_check_security   = tomoyo_bprm_check_security,
+	.bprm_post_apply_creds = tomoyo_bprm_post_apply_creds,
+	.bprm_free_security    = tomoyo_bprm_free_security,
+	.inode_permission      = tomoyo_inode_permission,
+	.inode_setattr         = tomoyo_inode_setattr,
+	.inode_create          = tomoyo_inode_create,
+	.inode_unlink          = tomoyo_inode_unlink,
+	.inode_mkdir           = tomoyo_inode_mkdir,
+	.inode_rmdir           = tomoyo_inode_rmdir,
+	.inode_symlink         = tomoyo_inode_symlink,
+	.inode_mknod           = tomoyo_inode_mknod,
+	.inode_link            = tomoyo_inode_link,
+	.inode_rename          = tomoyo_inode_rename,
+	.file_fcntl            = tomoyo_file_fcntl,
+};
+
+static int __init tomoyo_init(void)
+{
+	/* register ourselves with the security framework */
+	if (register_security(&tomoyo_security_ops)) {
+		printk(KERN_INFO "Failure registering TOMOYO Linux with the kernel\n");
+		return -EINVAL;
+	}
+	
+	printk ("TOMOYO Linux initialized\n");
+	{
+		extern void tomoyo_proc_init(void);
+		tomoyo_proc_init();
+	}
+	tomoyo_cachep = kmem_cache_create("tomoyo_security",
+	                                  sizeof(struct tomoyo_security),
+	                                  0, SLAB_PANIC, NULL, NULL);
+	init_task.security = kmem_cache_alloc(tomoyo_cachep, GFP_KERNEL);
+	((struct tomoyo_security *) init_task.security)->domain_info = &KERNEL_DOMAIN;
+	((struct tomoyo_security *) init_task.security)->flags = 0;
+	return 0;
+}
+
+security_initcall(tomoyo_init);
---------------


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

* [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (3 preceding siblings ...)
  2007-06-14  7:34 ` [TOMOYO 4/9] LSM adapter for TOMOYO Kentaro Takeda
@ 2007-06-14  7:36 ` Kentaro Takeda
  2007-06-14 17:34   ` Christoph Hellwig
  2007-06-14  7:37 ` [TOMOYO 6/9] Utility functions and /proc interface for policy manipulation Kentaro Takeda
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:36 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

We limit the maximum length of any string data (such as domainname and pathnames)
to TOMOYO_MAX_PATHNAME_LEN (which is 4000) bytes to fit within a single page.

Userland programs can obtain the amount of RAM currently used by TOMOYO from /proc interface.

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

---------------
 security/tomoyo/include/realpath.h |   46 +++++
 security/tomoyo/realpath.c         |  445 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 491 insertions(+)

diff -ubBpErN linux-2.6.21.5/security/tomoyo/include/realpath.h linux-2.6.21.5-tomoyo/security/tomoyo/include/realpath.h
--- linux-2.6.21.5/security/tomoyo/include/realpath.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/include/realpath.h	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,46 @@
+/*
+ * security/tomoyo/include/realpath.h
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ *
+ */
+
+#ifndef _TOMOYO_REALPATH_H
+#define _TOMOYO_REALPATH_H
+
+struct path_info;
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_dentry2(struct dentry *dentry,
+                                 struct vfsmount *mnt,
+                                 char *newname,
+                                 int newname_len);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+/* These functions use tomoyo_alloc(), so caller must tomoyo_free() */
+/* if these functions didn't return NULL. */
+char *tomoyo_realpath(const char *pathname);
+char *tomoyo_realpath_nofollow(const char *pathname);
+char *tomoyo_realpath_from_dentry(struct dentry *dentry, struct vfsmount *mnt);
+
+/* Allocate memory for structures. */
+/* The RAM is chunked, so NEVER try to kfree() the returned pointer. */
+void *tomoyo_alloc_element(const unsigned int size);
+
+/* Get used RAM size for tomoyo_alloc_elements(). */
+unsigned int tomoyo_get_memory_used_for_elements(void);
+
+/* Keep the given name on the RAM. */
+/* The RAM is shared, so NEVER try to modify or kfree() the returned name. */
+const struct path_info *tomoyo_save_name(const char *name);
+
+/* Get used RAM size for tomoyo_save_name(). */
+unsigned int tomoyo_get_memory_used_for_save_name(void);
+
+unsigned int tomoyo_get_memory_used_for_dynamic(void);
+
+#endif
diff -ubBpErN linux-2.6.21.5/security/tomoyo/realpath.c linux-2.6.21.5-tomoyo/security/tomoyo/realpath.c
--- linux-2.6.21.5/security/tomoyo/realpath.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/realpath.c	2007-06-14 15:06:37.000000000 +0900
@@ -0,0 +1,445 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Get the canonicalized absolute pathnames.
+ * The basis for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/utime.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/proc_fs.h>
+#include "realpath.h"
+#include "tomoyo.h"
+
+extern int sbin_init_started;
+
+/***** realpath handler *****/
+
+/*
+ * tomoyo_get_absolute_path - return the path of a dentry but ignores chroot'ed root.
+ * @dentry: dentry to report
+ * @vfsmnt: vfsmnt to which the dentry belongs
+ * @buffer: buffer to return value in
+ * @buflen: buffer length
+ *
+ * Caller holds the dcache_lock.
+ * Based on __d_path() in fs/dcache.c
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters other than ' ' < c < 127 are converted to \ooo style octal string.
+ * Character \ is converted to \\ string.
+ */
+static int tomoyo_get_absolute_path(struct dentry *dentry,
+                                    struct vfsmount *vfsmnt,
+                                    char *buffer,
+                                    int buflen)
+{
+	char *start = buffer;
+	char *end = buffer + buflen;
+	int is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));
+
+	if (buflen < 256) goto out;
+
+	*--end = '\0';
+	buflen--;
+
+	for (;;) {
+		struct dentry *parent;
+
+		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+			/* Global root? */
+			spin_lock(&vfsmount_lock);
+			if (vfsmnt->mnt_parent == vfsmnt) {
+				spin_unlock(&vfsmount_lock);
+				break;
+			}
+			dentry = vfsmnt->mnt_mountpoint;
+			vfsmnt = vfsmnt->mnt_parent;
+			spin_unlock(&vfsmount_lock);
+			continue;
+		}
+		if (is_dir) {
+			is_dir = 0;
+			*--end = '/';
+			buflen--;
+		}
+		parent = dentry->d_parent;
+		{
+			const char *sp = dentry->d_name.name;
+			const char *cp = sp + dentry->d_name.len - 1;
+			unsigned char c;
+
+			/* Exception: Use /proc/self/ rather than /proc/\$/ for current process. */
+			if (IS_ROOT(parent) && *sp > '0' && *sp <= '9' && parent->d_sb
+			    && parent->d_sb->s_magic == PROC_SUPER_MAGIC) {
+				char *ep;
+				const pid_t pid = (pid_t) simple_strtoul(sp, &ep, 10);
+				if (!*ep && pid == current->tgid) {
+					sp = "self";
+					cp = sp + 3;
+				}
+				if (!*ep && pid == current->pid) {
+					sp = "self";
+					cp = sp + 3;
+				}
+			}
+
+			while (sp <= cp) {
+				c = * (unsigned char *) cp;
+				if (c == '\\') {
+					buflen -= 2;
+					if (buflen < 0) goto out;
+					*--end = '\\';
+					*--end = '\\';
+				} else if (c > ' ' && c < 127) {
+					if (--buflen < 0) goto out;
+					*--end = (char) c;
+				} else {
+					buflen -= 4;
+					if (buflen < 0) goto out;
+					*--end = (c & 7) + '0';
+					*--end = ((c >> 3) & 7) + '0';
+					*--end = (c >> 6) + '0';
+					*--end = '\\';
+				}
+				cp--;
+			}
+			if (--buflen < 0) goto out;
+			*--end = '/';
+		}
+		dentry = parent;
+	}
+	if (*end == '/') {
+		buflen++;
+		end++;
+	}
+	{
+		const char *sp = dentry->d_name.name;
+		const char *cp = sp + dentry->d_name.len - 1;
+		unsigned char c;
+		while (sp <= cp) {
+			c = * (unsigned char *) cp;
+			if (c == '\\') {
+				buflen -= 2;
+				if (buflen < 0) goto out;
+				*--end = '\\';
+				*--end = '\\';
+			} else if (c > ' ' && c < 127) {
+				if (--buflen < 0) goto out;
+				*--end = (char) c;
+			} else {
+				buflen -= 4;
+				if (buflen < 0) goto out;
+				*--end = (c & 7) + '0';
+				*--end = ((c >> 3) & 7) + '0';
+				*--end = (c >> 6) + '0';
+				*--end = '\\';
+			}
+			cp--;
+		}
+	}
+	/* Move the pathname to the top of the buffer. */
+	memmove(start, end, strlen(end) + 1);
+	return 0;
+ out:
+	return -ENOMEM;
+}
+
+/* Returns realpath(3) of the given dentry but ignores chroot'ed root. */
+int tomoyo_realpath_from_dentry2(struct dentry *dentry,
+                                 struct vfsmount *mnt,
+                                 char *newname,
+                                 int newname_len)
+{
+	int error;
+	struct dentry *d_dentry;
+	struct vfsmount *d_mnt;
+	if (!dentry || !mnt || !newname || newname_len <= 0) return -EINVAL;
+	if (!current->fs) {
+		printk("%s: current->fs == NULL for pid=%d\n", __FUNCTION__, current->pid);
+		return -ENOENT;
+	}
+	d_dentry = dget(dentry);
+	d_mnt = mntget(mnt);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&dcache_lock);
+	error = tomoyo_get_absolute_path(d_dentry, d_mnt, newname, newname_len);
+	spin_unlock(&dcache_lock);
+	/***** CRITICAL SECTION END *****/
+	dput(d_dentry);
+	mntput(d_mnt);
+	return error;
+}
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+/* These functions use tomoyo_alloc(), so caller must tomoyo_free() */
+/* if these functions didn't return NULL. */
+char *tomoyo_realpath_from_dentry(struct dentry *dentry, struct vfsmount *mnt)
+{
+	char *buf = tomoyo_alloc(TOMOYO_MAX_PATHNAME_LEN);
+	if (buf && tomoyo_realpath_from_dentry2(dentry, mnt, buf, TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
+		return buf;
+	tomoyo_free(buf);
+	return NULL;
+}
+
+char *tomoyo_realpath(const char *pathname)
+{
+	struct nameidata nd;
+	if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) {
+		char *buf = tomoyo_realpath_from_dentry(nd.dentry, nd.mnt);
+		path_release(&nd);
+		return buf;
+	}
+	return NULL;
+}
+
+char *tomoyo_realpath_nofollow(const char *pathname)
+{
+	struct nameidata nd;
+	if (pathname && path_lookup(pathname, 0, &nd) == 0) {
+		char *buf = tomoyo_realpath_from_dentry(nd.dentry, nd.mnt);
+		path_release(&nd);
+		return buf;
+	}
+	return NULL;
+}
+
+/***** Private memory allocator. *****/
+
+/*
+ * Round up an integer so that the returned pointers are appropriately aligned.
+ * FIXME: Are there more requirements that is needed for assigning value atomically?
+ */
+static inline unsigned int tomoyo_roundup(const unsigned int size) {
+	if (sizeof(void *) >= sizeof(long)) {
+		return ((size + sizeof(void *) - 1) / sizeof(void *)) * sizeof(void *);
+	} else {
+		return ((size + sizeof(long) - 1) / sizeof(long)) * sizeof(long);
+	}
+}
+
+static unsigned int allocated_memory_for_elements = 0;
+
+unsigned int tomoyo_get_memory_used_for_elements(void)
+{
+	return allocated_memory_for_elements;
+}
+
+/* Allocate memory for structures. */
+/* The RAM is chunked, so NEVER try to kfree() the returned pointer. */
+void *tomoyo_alloc_element(const unsigned int size)
+{
+	static DECLARE_MUTEX(lock);
+	static char *buf = NULL;
+	static unsigned int buf_used_len = PAGE_SIZE;
+	char *ptr = NULL;
+	const unsigned int word_aligned_size = tomoyo_roundup(size);
+	if (word_aligned_size > PAGE_SIZE) return NULL;
+	down(&lock);
+	if (buf_used_len + word_aligned_size > PAGE_SIZE) {
+		if ((ptr = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) {
+			printk("ERROR: Out of memory for tomoyo_alloc_element().\n");
+			if (!sbin_init_started) panic("MAC Initialization failed.\n");
+		} else {
+			memset(ptr, 0, PAGE_SIZE);
+			buf = ptr;
+			allocated_memory_for_elements += PAGE_SIZE;
+			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]) {
+				printk(KERN_ERR "WARNING: Reserved memory was tainted! "
+				                "The system might go wrong.\n");
+				ptr[i] = '\0';
+			}
+		}
+	}
+	up(&lock);
+	return ptr;
+}
+
+/***** Shared memory allocator. *****/
+
+static unsigned int allocated_memory_for_savename = 0;
+
+unsigned int tomoyo_get_memory_used_for_save_name(void)
+{
+	return allocated_memory_for_savename;
+}
+
+#define MAX_HASH 256
+
+struct name_entry {
+	struct name_entry *next; /* Pointer to next record. NULL if none.             */
+	struct path_info entry;
+};
+
+struct free_memory_block_list {
+	struct free_memory_block_list *next; /* Pointer to next record. NULL if none. */
+	char *ptr;                           /* Pointer to a free area.               */
+	int len;                             /* Length of the area.                   */
+};
+
+/* Keep the given name on the RAM. */
+/* The RAM is shared, so NEVER try to modify or kfree() the returned name. */
+const struct path_info *tomoyo_save_name(const char *name)
+{
+	static struct free_memory_block_list fmb_list = { NULL, NULL, 0 };
+	static struct name_entry name_list[MAX_HASH]; /* The list of names. */
+	static DECLARE_MUTEX(lock);
+	struct name_entry *ptr, *prev = NULL;
+	unsigned int hash;
+	struct free_memory_block_list *fmb = &fmb_list;
+	int len;
+	static int first_call = 1;
+	if (!name) return NULL;
+	len = strlen(name) + 1;
+	if (len > TOMOYO_MAX_PATHNAME_LEN) {
+		printk("ERROR: Name too long for tomoyo_save_name().\n");
+		return NULL;
+	}
+	hash = full_name_hash((const unsigned char *) name, len - 1);
+	down(&lock);
+	if (first_call) {
+		int i;
+		first_call = 0;
+		memset(&name_list, 0, sizeof(name_list));
+		for (i = 0; i < MAX_HASH; i++) {
+			name_list[i].entry.name = "/";
+			tomoyo_fill_path_info(&name_list[i].entry);
+		}
+		if (TOMOYO_MAX_PATHNAME_LEN > PAGE_SIZE) panic("Bad size.");
+	}
+	ptr = &name_list[hash % MAX_HASH];
+	while (ptr) {
+		if (hash == ptr->entry.hash && strcmp(name, ptr->entry.name) == 0) goto out;
+		prev = ptr;
+		ptr = ptr->next;
+	}
+	while (len > fmb->len) {
+		if (fmb->next) {
+			fmb = fmb->next;
+		} else {
+			char *cp;
+			if ((cp = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL ||
+			    (fmb->next = tomoyo_alloc_element(sizeof(*fmb))) == NULL) {
+				kfree(cp);
+				printk("ERROR: Out of memory for tomoyo_save_name().\n");
+				if (!sbin_init_started) panic("MAC Initialization failed.\n");
+				goto out; /* ptr == NULL */
+			}
+			memset(cp, 0, PAGE_SIZE);
+			allocated_memory_for_savename += PAGE_SIZE;
+			fmb = fmb->next;
+			fmb->ptr = cp;
+			fmb->len = PAGE_SIZE;
+		}
+	}
+	if ((ptr = tomoyo_alloc_element(sizeof(*ptr))) == NULL) goto out;
+	ptr->entry.name = fmb->ptr;
+	memmove(fmb->ptr, name, len);
+	tomoyo_fill_path_info(&ptr->entry);
+	fmb->ptr += len;
+	fmb->len -= len;
+	prev->next = ptr; /* prev != NULL because name_list is not empty. */
+	if (fmb->len == 0) {
+		struct free_memory_block_list *ptr = &fmb_list;
+		while (ptr->next != fmb)
+			ptr = ptr->next;
+		ptr->next = fmb->next;
+	}
+ out:
+	up(&lock);
+	return ptr ? &ptr->entry : NULL;
+}
+
+/***** Dynamic memory allocator. *****/
+
+struct cache_entry {
+	struct list_head list;
+	void *ptr;
+	int size;
+};
+
+static struct kmem_cache *ccs_cachep = NULL;
+
+void tomoyo_realpath_init(void)
+{
+	ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry), 0, 0, NULL, NULL);
+	if (!ccs_cachep) panic("Can't create cache.\n");
+}
+
+static LIST_HEAD(cache_list);
+static spinlock_t cache_list_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int dynamic_memory_size = 0;
+
+unsigned int tomoyo_get_memory_used_for_dynamic(void)
+{
+	return dynamic_memory_size;
+}
+
+void *tomoyo_alloc(const size_t size)
+{
+	void *ret = kmalloc(size, GFP_KERNEL);
+	if (ret) {
+		struct cache_entry *new_entry = kmem_cache_alloc(ccs_cachep, GFP_KERNEL);
+		if (!new_entry) {
+			kfree(ret);
+			ret = NULL;
+		} else {
+			INIT_LIST_HEAD(&new_entry->list);
+			new_entry->ptr = ret;
+			new_entry->size = size;
+			spin_lock(&cache_list_lock);
+			list_add_tail(&new_entry->list, &cache_list);
+			dynamic_memory_size += size;
+			spin_unlock(&cache_list_lock);
+			memset(ret, 0, size);
+		}
+	}
+	return ret;
+}
+
+void tomoyo_free(const void *p)
+{
+	struct list_head *v;
+	struct cache_entry *entry = NULL;
+	if (!p) return;
+	spin_lock(&cache_list_lock);
+	list_for_each(v, &cache_list) {
+		entry = list_entry(v, struct cache_entry, list);
+		if (entry->ptr != p) {
+			entry = NULL;
+			continue;
+		}
+		list_del(&entry->list);
+		dynamic_memory_size -= entry->size;
+		break;
+	}
+	spin_unlock(&cache_list_lock);
+	if (entry) {
+		kfree(p);
+		kmem_cache_free(ccs_cachep, entry);
+	} else {
+		printk("BUG: tomoyo_free() with invalid pointer.\n");
+	}
+}
---------------



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

* [TOMOYO 6/9] Utility functions and /proc interface for policy manipulation.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (4 preceding siblings ...)
  2007-06-14  7:36 ` [TOMOYO 5/9] Memory and pathname management functions Kentaro Takeda
@ 2007-06-14  7:37 ` Kentaro Takeda
  2007-06-14  7:38 ` [TOMOYO 7/9] Auditing interface Kentaro Takeda
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:37 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

This file contains utility functions for TOMOYO.

Any string data in TOMOYO Linux consists with ASCII printable characters (0x21 to 0x7E)
so that userland application can separate monotonically using whitespaces and carrige returns.
Any wildcard character consists with "\" + one ASCII printable character,
so that wildcard characters can be expanded without changing existing names.

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

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

diff -ubBpErN linux-2.6.21.5/security/tomoyo/common.c linux-2.6.21.5-tomoyo/security/tomoyo/common.c
--- linux-2.6.21.5/security/tomoyo/common.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/common.c	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,1576 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/utime.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <stdarg.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/proc_fs.h>
+#include "realpath.h"
+#include "tomoyo.h"
+
+#if defined (CONFIG_TOMOYO_MAX_ACCEPT_ENTRY)
+#define MAX_ACCEPT_ENTRY (CONFIG_TOMOYO_MAX_ACCEPT_ENTRY)
+#else
+#define MAX_ACCEPT_ENTRY 2048
+#endif
+
+static int tomoyo_read_control(struct file *file, char __user *buffer, const int buffer_len);
+
+/*************************  VARIABLES  *************************/
+
+/* /sbin/init started? */
+int sbin_init_started = 0;
+
+const char *ccs_log_level = KERN_DEBUG;
+
+static struct {
+	const char *keyword;
+	unsigned int current_value;
+	const unsigned int max_value;
+} ccs_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
+	[TOMOYO_PROFILE_COMMENT]     = { "COMMENT",             0, 0 }, /* Reserved for string. */
+	[TOMOYO_MAC_FOR_FILE]        = { "MAC_FOR_FILE",        0, 3 },
+	[TOMOYO_MAX_ACCEPT_ENTRY]    = { "MAX_ACCEPT_ENTRY",    MAX_ACCEPT_ENTRY, INT_MAX },
+	[TOMOYO_VERBOSE]             = { "TOMOYO_VERBOSE",      1, 1 },
+	[TOMOYO_ALLOW_ENFORCE_GRACE] = { "ALLOW_ENFORCE_GRACE", 0, 1 },
+};
+
+struct profile {
+	unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
+	const struct path_info *comment;
+};
+
+static struct profile *profile_ptr[TOMOYO_MAX_PROFILES];
+
+/*************************  UTILITY FUNCTIONS  *************************/
+
+static int  tomoyo_quiet_setup(char *str)
+{
+	ccs_control_array[TOMOYO_VERBOSE].current_value = 0;
+	return 0;
+}
+
+__setup("TOMOYO_QUIET", tomoyo_quiet_setup);
+
+/* Am I root? */
+static int tomoyo_is_root(void)
+{
+	return !current->uid && !current->euid;
+}
+
+/*
+ * Format string.
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ */
+static void tomoyo_normalize_line(unsigned char *buffer)
+{
+	unsigned char *sp = buffer, *dp = buffer;
+	int first = 1;
+	while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;
+	while (*sp) {
+		if (!first) *dp++ = ' ';
+		first = 0;
+		while (*sp > ' ' && *sp < 127) *dp++ = *sp++;
+		while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;
+	}
+	*dp = '\0';
+}
+
+/*
+ *  Check whether the given filename follows the naming rules.
+ *  Returns nonzero if follows, zero otherwise.
+ */
+int tomoyo_is_correct_path(const char *filename,
+                           const int start_type,
+                           const int pattern_type,
+                           const int end_type,
+                           const char *function)
+{
+	int contains_pattern = 0;
+	char c, d, 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 = * (strchr(filename, '\0') - 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 = 1;
+				continue;
+			case '0':   /* "\ooo" */
+			case '1':
+			case '2':
+			case '3':
+				if ((d = *filename++) >= '0' &&
+				    d <= '7' &&
+				    (e = *filename++) >= '0' &&
+				    e <= '7') {
+					const unsigned char f =
+						(((unsigned char) (c - '0')) << 6) +
+						(((unsigned char) (d - '0')) << 3) +
+						(((unsigned char) (e - '0')));
+					if (f && (f <= ' ' || f >= 127))
+						continue; /* pattern is not \000 */
+				}
+			}
+			goto out;
+		} else if (c <= ' ' || c >= 127) {
+			goto out;
+		}
+	}
+	if (pattern_type == 1) { /* Must contain pattern */
+		if (!contains_pattern) goto out;
+	}
+	return 1;
+ out:
+	printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, original_filename);
+	return 0;
+}
+
+/*
+ *  Check whether the given domainname follows the naming rules.
+ *  Returns nonzero if follows, zero otherwise.
+ */
+int tomoyo_is_correct_domain(const unsigned char *domainname, const char *function)
+{
+	unsigned char c, d, 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 1;
+	do {
+		if (*domainname++ != ' ') goto out;
+		if (*domainname++ != '/') goto out;
+		while ((c = *domainname) != '\0' && c != ' ') {
+			domainname++;
+			if (c == '\\') {
+				switch ((c = *domainname++)) {
+				case '\\':  /* "\\" */
+					continue;
+				case '0':   /* "\ooo" */
+				case '1':
+				case '2':
+				case '3':
+					if ((d = *domainname++) >= '0' &&
+					    d <= '7' &&
+					    (e = *domainname++) >= '0' &&
+					    e <= '7') {
+						const unsigned char f =
+							(((unsigned char) (c - '0')) << 6) +
+							(((unsigned char) (d - '0')) << 3) +
+							(((unsigned char) (e - '0')));
+						if (f && (f <= ' ' || f >= 127))
+							continue; /* pattern is not \000 */
+					}
+				}
+				goto out;
+			} else if (c < ' ' || c >= 127) {
+				goto out;
+			}
+		}
+	} while (*domainname);
+	return 1;
+ out:
+	printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function, org_domainname);
+	return 0;
+}
+
+static int tomoyo_path_depth(const char *pathname)
+{
+	int i = 0;
+	if (pathname) {
+		char *ep = strchr(pathname, '\0');
+		if (pathname < ep--) {
+			if (*ep != '/') i++;
+			while (pathname <= ep) if (*ep-- == '/') i += 2;
+		}
+	}
+	return i;
+}
+
+static int tomoyo_const_part_length(const char *filename)
+{
+	int len = 0;
+	if (filename) {
+		char c;
+		while ((c = *filename++) != '\0') {
+			if (c != '\\') {
+				len++;
+				continue;
+			}
+			switch (c = *filename++) {
+			case '\\':  /* "\\" */
+				len += 2;
+				continue;
+			case '0':   /* "\ooo" */
+			case '1':
+			case '2':
+			case '3':
+				if ((c = *filename++) >= '0' &&
+				    c <= '7' &&
+				    (c = *filename++) >= '0' &&
+				    c <= '7') {
+					len += 4;
+					continue;
+				}
+			}
+			break;
+		}
+	}
+	return len;
+}
+
+void tomoyo_fill_path_info(struct 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);
+}
+
+static int 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) {
+		if (*pattern != '\\') {
+			if (*filename++ != *pattern++) return 0;
+		} else {
+			char c = *filename;
+			pattern++;
+			switch (*pattern) {
+			case '?':
+				if (c == '/') {
+					return 0;
+				} else if (c == '\\') {
+					if ((c = filename[1]) == '\\') {
+						filename++; /* safe because filename is \\ */
+					} else if (c >= '0' &&
+					           c <= '3' &&
+					           (c = filename[2]) >= '0' &&
+					           c <= '7' &&
+					           (c = filename[3]) >= '0' &&
+					           c <= '7') {
+						filename += 3; /* safe because filename is \ooo */
+					} else {
+						return 0;
+					}
+				}
+				break;
+			case '\\':
+				if (c != '\\') return 0;
+				if (*++filename != '\\') return 0; /* safe because *filename != '\0' */
+				break;
+			case '+':
+				if (c < '0' || c > '9') return 0;
+				break;
+			case 'x':
+				if (!((c >= '0' && c <= '9') ||
+				    (c >= 'A' && c <= 'F') ||
+				    (c >= 'a' && c <= 'f')))
+					return 0;
+				break;
+			case 'a':
+				if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) return 0;
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+				if (c == '\\' &&
+				    (c = filename[1]) >= '0' &&
+				    c <= '3' &&
+				    c == *pattern &&
+				    (c = filename[2]) >= '0' &&
+				    c <= '7' &&
+				    c == pattern[1] &&
+				    (c = filename[3]) >= '0' &&
+				    c <= '7' &&
+				    c == pattern[2]) {
+					filename += 3; /* safe because filename is \ooo */
+					pattern += 2; /* safe because pattern is \ooo  */
+					break;
+				}
+				return 0; /* Not matched. */
+			case '*':
+			case '@':
+				{
+					int i;
+					for (i = 0; i <= filename_end - filename; i++) {
+						if (tomoyo_file_matches_to_pattern2(filename + i,
+						                                    filename_end,
+						                                    pattern + 1,
+						                                    pattern_end))
+							return 1;
+						if ((c = filename[i]) == '.' && *pattern == '@')
+							break;
+						if (c == '\\') {
+							if ((c = filename[i + 1]) == '\\') {
+								/* safe because filename is \\ */
+								i++;
+							} else if (c >= '0' &&
+							           c <= '3' &&
+							           (c = filename[i + 2]) >= '0' &&
+							           c <= '7' &&
+							           (c = filename[i + 3]) >= '0' &&
+							           c <= '7') {
+								/* safe because filename is \ooo */
+								i += 3;
+							} else {
+								break; /* Bad pattern. */
+							}
+						}
+					}
+					return 0; /* Not matched. */
+				}
+			default:
+				{
+					int i, j = 0;
+					if ((c = *pattern) == '$') {
+						while ((c = filename[j]) >= '0' && c <= '9') j++;
+					} else if (c == 'X') {
+						while (((c = filename[j]) >= '0' && c <= '9') ||
+						       (c >= 'A' && c <= 'F') ||
+						       (c >= 'a' && c <= 'f'))
+							j++;
+					} else if (c == 'A') {
+						while (((c = filename[j]) >= 'A' && c <= 'Z') ||
+						       (c >= 'a' && c <= 'z'))
+							j++;
+					}
+					for (i = 1; i <= j; i++) {
+						if (tomoyo_file_matches_to_pattern2(filename + i,
+						                                    filename_end,
+						                                    pattern + 1,
+						                                    pattern_end))
+							return 1;
+					}
+				}
+				return 0; /* Not matched or bad pattern. */
+			}
+			filename++; /* safe because *filename != '\0' */
+			pattern++; /* safe because *pattern != '\0' */
+		}
+	}
+	while (*pattern == '\\' && (*(pattern + 1) == '*' || *(pattern + 1) == '@')) pattern += 2;
+	return (filename == filename_end && pattern == pattern_end);
+}
+
+static int tomoyo_file_matches_to_pattern(const char *filename,
+                                          const char *filename_end,
+                                          const char *pattern,
+                                          const char *pattern_end)
+{
+	const char *pattern_start = pattern;
+	int first = 1;
+	int result;
+	while (pattern < pattern_end - 1) {
+		if (*pattern++ != '\\' || *pattern++ != '-') continue;
+		result = tomoyo_file_matches_to_pattern2(filename,
+		                                         filename_end,
+		                                         pattern_start,
+		                                         pattern - 2);
+		if (first) result = !result;
+		if (result) return 0;
+		first = 0;
+		pattern_start = pattern;
+	}
+	result = tomoyo_file_matches_to_pattern2(filename, filename_end, pattern_start, pattern_end);
+	return first ? result : !result;
+}
+
+/*
+ *  Check whether the given pathname matches to the given pattern.
+ *  Returns nonzero if matches, zero 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.
+ */
+
+int tomoyo_path_matches_to_pattern(const struct path_info *pathname0,
+                                   const struct path_info *pattern0)
+{
+	const char *pathname = pathname0->name, *pattern = pattern0->name;
+	const int len = pattern0->const_len;
+	if (!pattern0->is_patterned) return !tomoyo_pathcmp(pathname0, pattern0);
+	if (pathname0->depth != pattern0->depth) return 0;
+	if (strncmp(pathname, pattern, len)) return 0;
+	pathname += len;
+	pattern += len;
+	while (*pathname && *pattern) {
+		const char *pathname_delimiter =
+			strchr(pathname, '/'), *pattern_delimiter = strchr(pattern, '/');
+		if (!pathname_delimiter) pathname_delimiter = strchr(pathname, '\0');
+		if (!pattern_delimiter) pattern_delimiter = strchr(pattern, '\0');
+		if (!tomoyo_file_matches_to_pattern(pathname,
+		                                    pathname_delimiter,
+		                                    pattern,
+		                                    pattern_delimiter))
+			return 0;
+		pathname = *pathname_delimiter ? pathname_delimiter + 1 : pathname_delimiter;
+		pattern = *pattern_delimiter ? pattern_delimiter + 1 : pattern_delimiter;
+	}
+	while (*pattern == '\\' && (*(pattern + 1) == '*' || *(pattern + 1) == '@')) pattern += 2;
+	return (!*pathname && !*pattern);
+}
+
+/*
+ *  Transactional printf() to struct io_buffer structure.
+ *  snprintf() will truncate, but tomoyo_io_printf() won't.
+ *  Returns zero on success, nonzero otherwise.
+ */
+int tomoyo_io_printf(struct io_buffer *head, const char *fmt, ...)
+{
+	va_list args;
+	int len, pos = head->read_avail, size = head->readbuf_size - pos;
+	if (size <= 0) return -ENOMEM;
+	va_start(args, fmt);
+	len = vsnprintf(head->read_buf + pos, size, fmt, args);
+	va_end(args);
+	if (pos + len >= head->readbuf_size) return -ENOMEM;
+	head->read_avail += len;
+	return 0;
+}
+
+/*
+ * Get tomoyo_realpath() of current process.
+ * This function uses tomoyo_alloc(), so caller must tomoyo_free() if this function didn't return NULL.
+ */
+const char *tomoyo_get_exe(void)
+{
+	if (current->mm) {
+		struct vm_area_struct *vma = current->mm->mmap;
+		while (vma) {
+			if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+				return tomoyo_realpath_from_dentry(vma->vm_file->f_dentry,
+				                                   vma->vm_file->f_vfsmnt);
+			}
+			vma = vma->vm_next;
+		}
+	}
+	return NULL;
+}
+
+const char *tomoyo_get_msg(const int is_enforce)
+{
+	if (is_enforce)
+		return "ERROR";
+	else
+		return "WARNING";
+}
+
+/*************************  DOMAIN POLICY HANDLER  *************************/
+
+/* Check whether the given access control is enabled. */
+unsigned int tomoyo_check_flags(const unsigned int index)
+{
+	const u8 profile = ((struct tomoyo_security *) current->security)->domain_info->profile;
+	return sbin_init_started && index < TOMOYO_MAX_CONTROL_INDEX
+#if TOMOYO_MAX_PROFILES != 256
+		&& profile < TOMOYO_MAX_PROFILES
+#endif
+		&& profile_ptr[profile] ? profile_ptr[profile]->value[index] : 0;
+}
+
+unsigned int tomoyo_verbose_mode(void)
+{
+	return tomoyo_check_flags(TOMOYO_VERBOSE);
+}
+
+/* Check whether the given access control is enforce mode. */
+unsigned int tomoyo_check_enforce(const unsigned int index)
+{
+	return tomoyo_check_flags(index) == 3;
+}
+
+/* Check whether the given access control is accept mode. */
+unsigned int tomoyo_check_accept(const unsigned int index)
+{
+	return tomoyo_check_flags(index) == 1;
+}
+
+static struct profile *tomoyo_find_or_assign_new_profile(const unsigned int profile)
+{
+	static DECLARE_MUTEX(profile_lock);
+	struct profile *ptr = NULL;
+	down(&profile_lock);
+	if (profile < TOMOYO_MAX_PROFILES && (ptr = profile_ptr[profile]) == NULL) {
+		if ((ptr = tomoyo_alloc_element(sizeof(*ptr))) != NULL) {
+			int i;
+			for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+				ptr->value[i] = ccs_control_array[i].current_value;
+			mb(); /* Instead of using spinlock. */
+			profile_ptr[profile] = ptr;
+		}
+	}
+	up(&profile_lock);
+	return ptr;
+}
+
+static int profile_loaded = 0;
+
+static int tomoyo_set_status(struct io_buffer *head)
+{
+	char *data = head->write_buf;
+	unsigned int i, value;
+	char *cp;
+	struct profile *profile;
+	if (!tomoyo_is_root()) return -EPERM;
+	i = simple_strtoul(data, &cp, 10);
+	if (data != cp) {
+		if (*cp != '-') return -EINVAL;
+		data= cp + 1;
+	}
+	profile = tomoyo_find_or_assign_new_profile(i);
+	if (!profile) return -EINVAL;
+	cp = strchr(data, '=');
+	if (!cp) return -EINVAL;
+	*cp = '\0';
+	profile_loaded = 1;
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_STATUS);
+	if (strcmp(data, ccs_control_array[TOMOYO_PROFILE_COMMENT].keyword) == 0) {
+		profile->comment = tomoyo_save_name(cp + 1);
+		return 0;
+	}
+	if (sscanf(cp + 1, "%u", &value) != 1) return -EINVAL;
+	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
+		if (strcmp(data, ccs_control_array[i].keyword)) continue;
+		if (value > ccs_control_array[i].max_value) value = ccs_control_array[i].max_value;
+		profile->value[i] = value;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int tomoyo_read_status(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		if (!tomoyo_is_root()) return -EPERM;
+		if (!head->read_var2) {
+			int step;
+			for (step = head->read_step;
+			     step < TOMOYO_MAX_PROFILES * TOMOYO_MAX_CONTROL_INDEX;
+			     step++) {
+				const int i = step / TOMOYO_MAX_CONTROL_INDEX,
+					  j = step % TOMOYO_MAX_CONTROL_INDEX;
+				const struct profile *profile = profile_ptr[i];
+				head->read_step = step;
+				if (!profile) continue;
+				if (j == TOMOYO_PROFILE_COMMENT) {
+					if (tomoyo_io_printf(head,
+					                     "%u-%s=%s\n",
+					                     i,
+					                     ccs_control_array[TOMOYO_PROFILE_COMMENT].keyword,
+					                     profile->comment ? profile->comment->name : ""))
+						break;
+				} else {
+					if (tomoyo_io_printf(head,
+					                     "%u-%s=%u\n",
+					                     i,
+					                     ccs_control_array[j].keyword,
+					                     profile->value[j]))
+						break;
+				}
+			}
+			if (step == TOMOYO_MAX_PROFILES * TOMOYO_MAX_CONTROL_INDEX) {
+				head->read_var2 = "";
+				head->read_step = 0;
+			}
+		}
+		if (head->read_var2) {
+			head->read_eof = 1;
+		}
+	}
+	return 0;
+}
+
+/*************************  POLICY MANAGER HANDLER  *************************/
+
+struct policy_manager_entry {
+	struct policy_manager_entry *next;
+	const struct path_info *manager;
+	u8 is_domain;
+	u8 is_deleted;
+};
+
+static struct policy_manager_entry *policy_manager_list = NULL;
+
+static int tomoyo_add_manager_entry(const char *manager, u8 is_delete)
+{
+	struct policy_manager_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_manager;
+	int error = -ENOMEM;
+	u8 is_domain = 0;
+	if (!tomoyo_is_root()) return -EPERM;
+	if (tomoyo_is_domain_def(manager)) {
+		if (!tomoyo_is_correct_domain(manager, __FUNCTION__)) return -EINVAL;
+		is_domain = 1;
+	} else {
+		if (!tomoyo_is_correct_path(manager, 1, -1, -1, __FUNCTION__)) return -EINVAL;
+	}
+	if ((saved_manager = tomoyo_save_name(manager)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (ptr = policy_manager_list; ptr; ptr = ptr->next) {
+		if (ptr->manager == saved_manager) {
+			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->manager = saved_manager;
+	new_entry->is_domain = is_domain;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = policy_manager_list) != NULL) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else {
+		policy_manager_list = new_entry;
+	}
+	error = 0;
+ out:
+	up(&lock);
+	if (!error) tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_MANAGER);
+	return error;
+}
+
+static int tomoyo_add_manager_policy(struct io_buffer *head)
+{
+	const char *data = head->write_buf;
+	int is_delete = 0;
+	if (!tomoyo_is_root()) return -EPERM;
+	if (strncmp(data, TOMOYO_KEYWORD_DELETE, TOMOYO_KEYWORD_DELETE_LEN) == 0) {
+		data += TOMOYO_KEYWORD_DELETE_LEN;
+		is_delete = 1;
+	}
+	return tomoyo_add_manager_entry(data, is_delete);
+}
+
+static int tomoyo_read_manager_policy(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		struct policy_manager_entry *ptr = head->read_var2;
+		if (!tomoyo_is_root()) return -EPERM;
+		if (!ptr) ptr = policy_manager_list;
+		while (ptr) {
+			head->read_var2 = ptr;
+			if (!ptr->is_deleted && tomoyo_io_printf(head, "%s\n", ptr->manager->name))
+				break;
+			ptr = ptr->next;
+		}
+		if (!ptr) head->read_eof = 1;
+	}
+	return 0;
+}
+
+/* Check whether the current process is a policy manager. */
+static int tomoyo_is_policy_manager(void)
+{
+	struct policy_manager_entry *ptr;
+	const char *exe;
+	const struct path_info *domainname =
+		((struct tomoyo_security *) current->security)->domain_info->domainname;
+	if (!sbin_init_started) return 1;
+	for (ptr = policy_manager_list; ptr; ptr = ptr->next) {
+		if (!ptr->is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager))
+			return 1;
+	}
+	if ((exe = tomoyo_get_exe()) == NULL) return 0;
+	for (ptr = policy_manager_list; ptr; ptr = ptr->next) {
+		if (!ptr->is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name))
+			break;
+	}
+	if (!ptr) { /* Reduce error messages. */
+		static pid_t last_pid = 0;
+		const pid_t pid = current->pid;
+		if (last_pid != pid) {
+			printk("%s is not permitted to update policies.\n", exe);
+			last_pid = pid;
+		}
+	}
+	tomoyo_free(exe);
+	return ptr ? 1 : 0;
+}
+
+/*************************  DOMAIN POLICY HANDLER  *************************/
+
+static int tomoyo_add_domain_policy(struct io_buffer *head)
+{
+	char *data = head->write_buf;
+	struct domain_info *domain = head->write_var1;
+	int is_delete = 0, is_select = 0, is_undelete = 0;
+	unsigned int profile;
+	if (!tomoyo_is_root()) return -EPERM;
+	if (strncmp(data, TOMOYO_KEYWORD_DELETE, TOMOYO_KEYWORD_DELETE_LEN) == 0) {
+		data += TOMOYO_KEYWORD_DELETE_LEN;
+		is_delete = 1;
+	} else if (strncmp(data, TOMOYO_KEYWORD_SELECT, TOMOYO_KEYWORD_SELECT_LEN) == 0) {
+		data += TOMOYO_KEYWORD_SELECT_LEN;
+		is_select = 1;
+	} else if (strncmp(data, TOMOYO_KEYWORD_UNDELETE, TOMOYO_KEYWORD_UNDELETE_LEN) == 0) {
+		data += TOMOYO_KEYWORD_UNDELETE_LEN;
+		is_undelete = 1;
+	}
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY);
+	if (tomoyo_is_domain_def(data)) {
+		if (is_delete) {
+			tomoyo_delete_domain(data);
+			domain = NULL;
+		} 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 (profile_ptr[profile] || !sbin_init_started) domain->profile = (u8) profile;
+	} else {
+		return tomoyo_add_file_policy(data, domain, is_delete);
+	}
+	return -EINVAL;
+}
+
+static int tomoyo_read_domain_policy(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		struct domain_info *domain = head->read_var1;
+		switch (head->read_step) {
+		case 0: break;
+		case 1: goto step1;
+		case 2: goto step2;
+		case 3: goto step3;
+		default: return -EINVAL;
+		}
+		if (!tomoyo_is_root()) return -EPERM;
+		for (domain = &KERNEL_DOMAIN; domain; domain = domain->next) {
+			struct acl_info *ptr;
+			if (domain->is_deleted) continue;
+			head->read_var1 = domain;
+			head->read_var2 = NULL;
+			head->read_step = 1;
+		step1:
+			if (tomoyo_io_printf(head,
+			                     "%s\n" TOMOYO_KEYWORD_USE_PROFILE "%u\n%s\n",
+			                     domain->domainname->name,
+			                     domain->profile,
+			                     domain->quota_warned ? "quota_exceeded\n" : ""))
+				break;
+			head->read_var2 = domain->first_acl_ptr;
+			head->read_step = 2;
+		step2:
+			for (ptr = head->read_var2; ptr; ptr = ptr->next) {
+				const u8 acl_type = ptr->type;
+				const int pos = head->read_avail;
+				head->read_var2 = ptr;
+				if (ptr->is_deleted) continue;
+				if (acl_type == TOMOYO_TYPE_FILE_ACL) {
+					const unsigned char b = ptr->u.b[1];
+					if (tomoyo_io_printf(head,
+					                     "%d %s%s\n",
+					                     ptr->u.b[0],
+					                     b ? "@" : "",
+					                     b ? ((struct file_acl_record *) ptr)->u.group->group_name->name :
+					                         ((struct file_acl_record *) ptr)->u.filename->name)) {
+						head->read_avail = pos;
+						break;
+					}
+				} else {
+					const char *keyword = tomoyo_acltype2keyword(acl_type);
+					if (keyword) {
+						if (tomoyo_acltype2paths(acl_type) == 2) {
+							const u8 b0 = ptr->u.b[0], b1 = ptr->u.b[1];
+							if (tomoyo_io_printf(head,
+							                     "allow_%s %s%s %s%s\n",
+							                     keyword,
+							                     b0 ? "@" : "",
+							                     b0 ? ((struct double_acl_record *) ptr)->u1.group1->group_name->name :
+							                          ((struct double_acl_record *) ptr)->u1.filename1->name,
+							                     b1 ? "@" : "",
+							                     b1 ? ((struct double_acl_record *) ptr)->u2.group2->group_name->name :
+							                          ((struct double_acl_record *) ptr)->u2.filename2->name)) {
+								head->read_avail = pos;
+								break;
+							}
+						} else {
+							const u8 b = ptr->u.b[0];
+							if (tomoyo_io_printf(head,
+							                     "allow_%s %s%s\n",
+							                     keyword,
+							                     b ? "@" : "",
+							                     b ? ((struct single_acl_record *) ptr)->u.group->group_name->name :
+							                         ((struct single_acl_record *) ptr)->u.filename->name)) {
+								head->read_avail = pos;
+								break;
+							}
+						}
+					}
+				}
+			}
+			if (ptr) break;
+			head->read_var2 = NULL;
+			head->read_step = 3;
+		step3:
+			if (tomoyo_io_printf(head, "\n")) break;
+		}
+		if (!domain) head->read_eof = 1;
+	}
+	return 0;
+}
+
+static int tomoyo_read_domain_profile(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		struct domain_info *domain;
+		if (head->read_step == 0) {
+			head->read_var1 = &KERNEL_DOMAIN;
+			head->read_step = 1;
+		}
+		if (!tomoyo_is_root()) return -EPERM;
+		for (domain = head->read_var1; domain; domain = domain->next) {
+			if (domain->is_deleted) continue;
+			head->read_var1 = domain;
+			if (tomoyo_io_printf(head,
+			                     "%u %s\n",
+			                     domain->profile,
+			                     domain->domainname->name))
+				break;
+		}
+		if (!domain) head->read_eof = 1;
+	}
+	return 0;
+}
+
+static int tomoyo_write_pid(struct io_buffer *head)
+{
+	head->read_step = (int) simple_strtoul(head->write_buf, NULL, 10);
+	head->read_eof = 0;
+	return 0;
+}
+
+static int tomoyo_read_pid(struct io_buffer *head)
+{
+	if (head->read_avail == 0 && !head->read_eof) {
+		const int pid = head->read_step;
+		struct task_struct *p;
+		struct domain_info *domain = NULL;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		p = find_task_by_pid(pid);
+		if (p) {
+			struct tomoyo_security *tmp = p->security;
+			if (tmp) {
+				domain = tmp->domain_info;
+				if (!domain) domain = &KERNEL_DOMAIN;
+			}
+		}
+		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 = 1;
+	}
+	return 0;
+}
+
+static int tomoyo_update_domain_profile(struct io_buffer *head)
+{
+	char *data = head->write_buf;
+	char *cp = strchr(data, ' ');
+	struct domain_info *domain;
+	unsigned int profile;
+	if (!tomoyo_is_root()) return -EPERM;
+	if (!cp) return -EINVAL;
+	*cp = '\0';
+	domain = tomoyo_find_domain(cp + 1);
+	profile = simple_strtoul(data, NULL, 10);
+	if (domain && profile < TOMOYO_MAX_PROFILES && (profile_ptr[profile] || !sbin_init_started))
+		domain->profile = (u8) profile;
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY);
+	return 0;
+}
+
+/*************************  EXCEPTION POLICY HANDLER  *************************/
+
+static int tomoyo_add_exception_policy(struct io_buffer *head)
+{
+	char *data = head->write_buf;
+	int is_delete = 0;
+	if (!tomoyo_is_root()) return -EPERM;
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_EXCEPTION_POLICY);
+	if (strncmp(data, TOMOYO_KEYWORD_DELETE, TOMOYO_KEYWORD_DELETE_LEN) == 0) {
+		data += TOMOYO_KEYWORD_DELETE_LEN;
+		is_delete = 1;
+	}
+	if (strncmp(data, TOMOYO_KEYWORD_KEEP_DOMAIN, TOMOYO_KEYWORD_KEEP_DOMAIN_LEN) == 0) {
+		return tomoyo_add_domain_keeper_policy(data + TOMOYO_KEYWORD_KEEP_DOMAIN_LEN, 0, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN, TOMOYO_KEYWORD_NO_KEEP_DOMAIN_LEN) == 0) {
+		return tomoyo_add_domain_keeper_policy(data + TOMOYO_KEYWORD_NO_KEEP_DOMAIN_LEN, 1, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN, TOMOYO_KEYWORD_INITIALIZE_DOMAIN_LEN) == 0) {
+		return tomoyo_add_domain_initializer_policy(data + TOMOYO_KEYWORD_INITIALIZE_DOMAIN_LEN, 0, is_delete, 0);
+	} else if (strncmp(data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN_LEN) == 0) {
+		return tomoyo_add_domain_initializer_policy(data + TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN_LEN, 1, is_delete, 0);
+	} else if (strncmp(data, TOMOYO_KEYWORD_INITIALIZER, TOMOYO_KEYWORD_INITIALIZER_LEN) == 0) {
+		return tomoyo_add_domain_initializer_policy(data + TOMOYO_KEYWORD_INITIALIZER_LEN, 0, is_delete, 1);
+	} else if (strncmp(data, TOMOYO_KEYWORD_NO_INITIALIZER, TOMOYO_KEYWORD_NO_INITIALIZER_LEN) == 0) {
+		return tomoyo_add_domain_initializer_policy(data + TOMOYO_KEYWORD_NO_INITIALIZER_LEN, 1, is_delete, 1);
+	} else if (strncmp(data, TOMOYO_KEYWORD_ALIAS, TOMOYO_KEYWORD_ALIAS_LEN) == 0) {
+		return tomoyo_add_alias_policy(data + TOMOYO_KEYWORD_ALIAS_LEN, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_AGGREGATOR, TOMOYO_KEYWORD_AGGREGATOR_LEN) == 0) {
+		return tomoyo_add_aggregator_policy(data + TOMOYO_KEYWORD_AGGREGATOR_LEN, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_ALLOW_READ, TOMOYO_KEYWORD_ALLOW_READ_LEN) == 0) {
+		return tomoyo_add_globally_readable_policy(data + TOMOYO_KEYWORD_ALLOW_READ_LEN, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_FILE_PATTERN, TOMOYO_KEYWORD_FILE_PATTERN_LEN) == 0) {
+		return tomoyo_add_pattern_policy(data + TOMOYO_KEYWORD_FILE_PATTERN_LEN, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_PATH_GROUP, TOMOYO_KEYWORD_PATH_GROUP_LEN) == 0) {
+		return tomoyo_add_group_policy(data + TOMOYO_KEYWORD_PATH_GROUP_LEN, is_delete);
+	} else if (strncmp(data, TOMOYO_KEYWORD_DENY_REWRITE, TOMOYO_KEYWORD_DENY_REWRITE_LEN) == 0) {
+		return tomoyo_add_no_rewrite_policy(data + TOMOYO_KEYWORD_DENY_REWRITE_LEN, is_delete);
+	}
+	return -EINVAL;
+}
+
+static int tomoyo_read_exception_policy(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		switch (head->read_step) {
+		case 0:
+			if (!tomoyo_is_root()) return -EPERM;
+			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:
+			if (tomoyo_read_domain_initializer_policy(head)) break;
+			head->read_var2 = NULL;
+			head->read_step = 4;
+		case 4:
+			if (tomoyo_read_alias_policy(head)) break;
+			head->read_var2 = NULL;
+			head->read_step = 5;
+		case 5:
+			if (tomoyo_read_aggregator_policy(head)) break;
+			head->read_var2 = NULL;
+			head->read_step = 6;
+		case 6:
+			if (tomoyo_read_pattern_policy(head)) break;
+			head->read_var2 = NULL;
+			head->read_step = 7;
+		case 7:
+			if (tomoyo_read_no_rewrite_policy(head)) break;
+			head->read_var2 = NULL;
+			head->read_step = 8;
+		case 8:
+			if (tomoyo_read_group_policy(head)) break;
+			head->read_var2 = NULL;
+			head->read_step = 9;
+		case 9:
+			head->read_eof = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/*************************  POLICY LOADER  *************************/
+
+static const char *ccs_loader = NULL;
+
+static int  tomoyo_loader_setup(char *str)
+{
+	ccs_loader = str;
+	return 0;
+}
+
+__setup("TOMOYO_loader=", tomoyo_loader_setup);
+
+void tomoyo_load_policy(const char *filename)
+{
+	if (sbin_init_started) return;
+	/*
+	 * Check filename is /sbin/init or /sbin/ccs-start .
+	 * /sbin/ccs-start is a dummy filename in case where /sbin/init can't be passed.
+	 * You can create /sbin/ccs-start by "ln -s /bin/true /sbin/ccs-start", for
+	 * only the pathname is needed to activate Mandatory Access Control.
+	 */
+	if (strcmp(filename, "/sbin/init") != 0 && strcmp(filename, "/sbin/ccs-start") != 0) return;
+	/*
+	 * Don't activate MAC if the path given by 'TOMOYO_loader=' option doesn't exist.
+	 * If initrd.img includes /sbin/init but real-root-dev has not mounted on / yet,
+	 * activating MAC will block the system since policies are not loaded yet.
+	 * So let do_execve() call this function everytime.
+	 */
+	{
+		struct nameidata nd;
+		if (!ccs_loader) ccs_loader = "/.init";
+		if (path_lookup(ccs_loader, LOOKUP_FOLLOW, &nd)) {
+			printk("Not activating Mandatory Access Control now since %s doesn't exist.\n",
+			       ccs_loader);
+			return;
+		}
+		path_release(&nd);
+	}
+
+	printk("TOMOYO: 2.0   2007/06/05\n");
+	if (!profile_loaded) panic("No profiles loaded. Run policy loader using 'init=' option.\n");
+	printk("Mandatory Access Control activated.\n");
+	sbin_init_started = 1;
+	ccs_log_level = KERN_WARNING;
+	{ /* Check all profiles currently assigned to domains are defined. */
+		struct domain_info *domain;
+		for (domain = &KERNEL_DOMAIN; domain; domain = domain->next) {
+			const u8 profile = domain->profile;
+			if (!profile_ptr[profile])
+				panic("Profile %u (used by '%s') not defined.\n",
+				      profile,
+				      domain->domainname->name); 
+		}
+	}
+}
+
+
+/*************************  MAC Decision Delayer  *************************/
+
+static DECLARE_WAIT_QUEUE_HEAD(query_wait);
+
+static spinlock_t query_lock = SPIN_LOCK_UNLOCKED;
+
+struct query_entry {
+	struct list_head list;
+	char *query;
+	int query_len;
+	unsigned int serial;
+	int timer;
+	int answer;
+};
+
+static LIST_HEAD(query_list);
+static atomic_t queryd_watcher = ATOMIC_INIT(0);
+
+int tomoyo_check_supervisor(const char *fmt, ...)
+{
+	va_list args;
+	int error = -EPERM;
+	int pos, len;
+	static unsigned int serial = 0;
+	struct query_entry *query_entry;
+	if (!tomoyo_check_flags(TOMOYO_ALLOW_ENFORCE_GRACE)) return -EPERM;
+	if (!atomic_read(&queryd_watcher)) return -EPERM;
+	va_start(args, fmt);
+	len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+	va_end(args);
+	if ((query_entry = tomoyo_alloc(sizeof(*query_entry))) == NULL ||
+		(query_entry->query = tomoyo_alloc(len)) == NULL) goto out;
+	INIT_LIST_HEAD(&query_entry->list);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	query_entry->serial = serial++;
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	pos = snprintf(query_entry->query, len - 1, "Q%u\n", query_entry->serial);
+	va_start(args, fmt);
+	vsnprintf(query_entry->query + pos, len - 1 - pos, fmt, args);
+	query_entry->query_len = strlen(query_entry->query) + 1;
+	va_end(args);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	list_add_tail(&query_entry->list, &query_list);
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_QUERY);
+	/* Give 10 seconds for supervisor's opinion. */
+	for (query_entry->timer = 0;
+	     atomic_read(&queryd_watcher) &&
+	     	tomoyo_check_flags(TOMOYO_ALLOW_ENFORCE_GRACE) &&
+	     	query_entry->timer < 100;
+	     query_entry->timer++) {
+		wake_up(&query_wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ / 10);
+		if (query_entry->answer) break;
+	}
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_QUERY);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	list_del(&query_entry->list);
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	switch (query_entry->answer) {
+	case 1:
+		/* Granted by administrator. */
+		error = 0;
+		break;
+	case 0:
+		/* Timed out. */
+		break;
+	default:
+		/* Rejected by administrator. */
+		break;
+	}
+ out: ;
+	if (query_entry) tomoyo_free(query_entry->query);
+	tomoyo_free(query_entry);
+	return error;
+}
+
+static int tomoyo_poll_query(struct file *file, poll_table *wait)
+{
+	int found;
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	found = !list_empty(&query_list);
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	if (found) return POLLIN | POLLRDNORM;
+	poll_wait(file, &query_wait, wait);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	found = !list_empty(&query_list);
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	if (found) return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int tomoyo_read_query(struct io_buffer *head)
+{
+	struct list_head *tmp;
+	int pos = 0, len = 0;
+	char *buf;
+	if (head->read_avail) return 0;
+	if (head->read_buf) {
+		tomoyo_free(head->read_buf);
+		head->read_buf = NULL;
+		head->readbuf_size = 0;
+	}
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	list_for_each(tmp, &query_list) {
+		struct query_entry *ptr = list_entry(tmp, struct query_entry, list);
+		if (pos++ == head->read_step) {
+			len = ptr->query_len;
+			break;
+		}
+	}
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	if (!len) {
+		head->read_step = 0;
+		return 0;
+	}
+	if ((buf = tomoyo_alloc(len)) != NULL) {
+		pos = 0;
+		/***** CRITICAL SECTION START *****/
+		spin_lock(&query_lock);
+		list_for_each(tmp, &query_list) {
+			struct query_entry *ptr = list_entry(tmp, struct query_entry, list);
+			if (pos++ == head->read_step) {
+				/* Some query can be skiipped since query_list can change, */
+				/* but I don't care. */
+				if (len == ptr->query_len) memmove(buf, ptr->query, len);
+				break;
+			}
+		}
+		spin_unlock(&query_lock);
+		/***** CRITICAL SECTION END *****/
+		if (buf[0]) {
+			head->readbuf_size = head->read_avail = len;
+			head->read_buf = buf;
+			head->read_step++;
+		} else {
+			tomoyo_free(buf);
+		}
+	}
+	return 0;
+}
+
+static int tomoyo_write_answer(struct io_buffer *head)
+{
+	char *data = head->write_buf;
+	struct list_head *tmp;
+	unsigned int serial, answer;
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	list_for_each(tmp, &query_list) {
+		struct query_entry *ptr = list_entry(tmp, struct query_entry, list);
+		ptr->timer = 0;
+	}
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	if (sscanf(data, "A%u=%u", &serial, &answer) != 2) return -EINVAL;
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&query_lock);
+	list_for_each(tmp, &query_list) {
+		struct query_entry *ptr = list_entry(tmp, struct query_entry, list);
+		if (ptr->serial != serial) continue;
+		if (!ptr->answer) ptr->answer = answer;
+		break;
+	}
+	spin_unlock(&query_lock);
+	/***** CRITICAL SECTION END *****/
+	return 0;
+}
+
+/*************************  /proc INTERFACE HANDLER  *************************/
+
+/* Policy updates counter. */
+static unsigned int updates_counter[TOMOYO_MAX_UPDATES_COUNTER];
+static spinlock_t updates_counter_lock = SPIN_LOCK_UNLOCKED;
+
+void tomoyo_update_counter(const unsigned char index)
+{
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&updates_counter_lock);
+	if (index < TOMOYO_MAX_UPDATES_COUNTER) updates_counter[index]++;
+	spin_unlock(&updates_counter_lock);
+	/***** CRITICAL SECTION END *****/
+}
+
+static int tomoyo_read_updates_counter(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		unsigned int counter[TOMOYO_MAX_UPDATES_COUNTER];
+		/***** CRITICAL SECTION START *****/
+		spin_lock(&updates_counter_lock);
+		memmove(counter, updates_counter, sizeof(updates_counter));
+		memset(updates_counter, 0, sizeof(updates_counter));
+		spin_unlock(&updates_counter_lock);
+		/***** CRITICAL SECTION END *****/
+		tomoyo_io_printf(head,
+				  "/proc/ccs/policy/domain_policy:    %10u\n"
+				  "/proc/ccs/policy/exception_policy: %10u\n"
+				  "/proc/ccs/status:                  %10u\n"
+				  "/proc/ccs/policy/query:            %10u\n"
+				  "/proc/ccs/policy/manager:          %10u\n",
+				  counter[TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY],
+				  counter[TOMOYO_UPDATES_COUNTER_EXCEPTION_POLICY],
+				  counter[TOMOYO_UPDATES_COUNTER_STATUS],
+				  counter[TOMOYO_UPDATES_COUNTER_QUERY],
+				  counter[TOMOYO_UPDATES_COUNTER_MANAGER]);
+		head->read_eof = 1;
+	}
+	return 0;
+}
+
+static int tomoyo_read_memory_counter(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		const int shared = tomoyo_get_memory_used_for_save_name(),
+		          private = tomoyo_get_memory_used_for_elements(),
+		          dynamic = tomoyo_get_memory_used_for_dynamic();
+		if (tomoyo_io_printf(head,
+		                     "Shared:  %10u\n"
+		                     "Private: %10u\n"
+		                     "Dynamic: %10u\n"
+		                     "Total:   %10u\n",
+		                     shared,
+		                     private,
+		                     dynamic,
+		                     shared + private + dynamic) == 0)
+			head->read_eof = 1;
+	}
+	return 0;
+}
+
+static int tomoyo_open_control(const int type, struct file *file)
+{
+	struct io_buffer *head = tomoyo_alloc(sizeof(*head));
+	if (!head) return -ENOMEM;
+	init_MUTEX(&head->read_sem);
+	init_MUTEX(&head->write_sem);
+	switch (type) {
+	case TOMOYO_POLICY_DOMAINPOLICY:
+		head->write = tomoyo_add_domain_policy;
+		head->read = tomoyo_read_domain_policy;
+		break;
+	case TOMOYO_POLICY_EXCEPTIONPOLICY:
+		head->write = tomoyo_add_exception_policy;
+		head->read = tomoyo_read_exception_policy;
+		break;
+	case TOMOYO_POLICY_DOMAIN_STATUS:
+		head->write = tomoyo_update_domain_profile;
+		head->read = tomoyo_read_domain_profile;
+		break;
+	case TOMOYO_INFO_PROCESS_STATUS:
+		head->write = tomoyo_write_pid;
+		head->read = tomoyo_read_pid;
+		break;
+	case TOMOYO_INFO_SELFDOMAIN:
+		head->read = tomoyo_read_self_domain;
+		break;
+	case TOMOYO_INFO_MAPPING:
+		if (!sbin_init_started) head->write = tomoyo_set_permission_mapping;
+		head->read = tomoyo_read_permission_mapping;
+		break;
+	case TOMOYO_INFO_MEMINFO:
+		head->read = tomoyo_read_memory_counter;
+		head->readbuf_size = 128;
+		break;
+	case TOMOYO_STATUS:
+		head->write = tomoyo_set_status;
+		head->read = tomoyo_read_status;
+		break;
+	case TOMOYO_POLICY_QUERY:
+		head->poll = tomoyo_poll_query;
+		head->write = tomoyo_write_answer;
+		head->read = tomoyo_read_query;
+		break;
+	case TOMOYO_POLICY_MANAGER:
+		head->write = tomoyo_add_manager_policy;
+		head->read = tomoyo_read_manager_policy;
+		break;
+	case TOMOYO_INFO_UPDATESCOUNTER:
+		head->read = tomoyo_read_updates_counter;
+		break;
+	}
+	if (type != TOMOYO_POLICY_QUERY) {
+		if (!head->readbuf_size) head->readbuf_size = PAGE_SIZE * 2;
+		if ((head->read_buf = tomoyo_alloc(head->readbuf_size)) == NULL) {
+			tomoyo_free(head);
+			return -ENOMEM;
+		}
+	}
+	if (head->write) {
+		head->writebuf_size = PAGE_SIZE * 2;
+		if ((head->write_buf = tomoyo_alloc(head->writebuf_size)) == NULL) {
+			tomoyo_free(head->read_buf);
+			tomoyo_free(head);
+			return -ENOMEM;
+		}
+	}
+	file->private_data = head;
+	if (type == TOMOYO_INFO_SELFDOMAIN) tomoyo_read_control(file, NULL, 0);
+	else if (head->write == tomoyo_write_answer) atomic_inc(&queryd_watcher);
+	return 0;
+}
+
+static int tomoyo_copy_to_user(struct io_buffer *head, char __user * buffer, int buffer_len)
+{
+	int len = head->read_avail;
+	char *cp = head->read_buf;
+	if (len > buffer_len) len = buffer_len;
+	if (len) {
+		if (copy_to_user(buffer, cp, len)) return -EFAULT;
+		head->read_avail -= len;
+		memmove(cp, cp + len, head->read_avail);
+	}
+	return len;
+}
+
+static int tomoyo_poll_control(struct file *file, poll_table *wait)
+{
+	struct io_buffer *head = (struct io_buffer *) file->private_data;
+	if (!head->poll) return -ENOSYS;
+	return head->poll(file, wait);
+}
+
+static int tomoyo_read_control(struct file *file, char __user *buffer, const int buffer_len)
+{
+	int len = 0;
+	struct io_buffer *head = (struct io_buffer *) file->private_data;
+	if (!head->read) return -ENOSYS;
+	if (!access_ok(VERIFY_WRITE, buffer, buffer_len)) return -EFAULT;
+	if (down_interruptible(&head->read_sem)) return -EINTR;
+	len = head->read(head);
+	if (len >= 0) len = tomoyo_copy_to_user(head, buffer, buffer_len);
+	up(&head->read_sem);
+	return len;
+}
+
+static int tomoyo_write_control(struct file *file, const char __user *buffer, const int buffer_len)
+{
+	struct io_buffer *head = (struct io_buffer *) 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;
+	if (!tomoyo_is_root()) return -EPERM;
+	if (head->write != tomoyo_write_pid && !tomoyo_is_policy_manager()) {
+		return -EPERM; /* Forbid updating policies for non manager programs. */
+	}
+	if (down_interruptible(&head->write_sem)) return -EINTR;
+	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);
+	}
+	up(&head->write_sem);
+	return error;
+}
+
+
+static int tomoyo_close_control(struct file *file)
+{
+	struct io_buffer *head = file->private_data;
+	if (head->write == tomoyo_write_answer) atomic_dec(&queryd_watcher);
+	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;
+}
+
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+	return tomoyo_open_control(((u8 *) PDE(inode)->data) - ((u8 *) NULL), file);
+}
+
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+	return tomoyo_close_control(file);
+}
+
+static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
+{
+	return tomoyo_poll_control(file, wait);
+}
+
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	return tomoyo_read_control(file, buf, count);
+}
+
+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);
+}
+
+static struct file_operations ccs_operations = {
+	open:    tomoyo_open,
+	release: tomoyo_release,
+	poll:    tomoyo_poll,
+	read:    tomoyo_read,
+	write:   tomoyo_write
+};
+
+static void tomoyo_create_entry(const char *name,
+                                const mode_t mode,
+                                struct proc_dir_entry *parent,
+                                const int key)
+{
+	struct proc_dir_entry *entry = create_proc_entry(name, mode, parent);
+	if (entry) {
+		entry->proc_fops = &ccs_operations;
+		entry->data = ((u8 *) NULL) + key;
+	}
+}
+
+void tomoyo_proc_init(void)
+{
+	static int initialized = 0;
+	struct proc_dir_entry *ccs_dir;
+	struct proc_dir_entry *policy_dir, *info_dir;
+	extern void tomoyo_realpath_init(void);
+	if (initialized)
+		return;
+	initialized = 1;
+	ccs_dir = proc_mkdir("ccs", NULL);
+	policy_dir = proc_mkdir("policy", ccs_dir);
+	info_dir = proc_mkdir("info", ccs_dir);
+	tomoyo_realpath_init();
+	tomoyo_find_domain(""); /* Set domainname of KERNEL domain. */
+	tomoyo_create_entry("query",            0600, policy_dir, TOMOYO_POLICY_QUERY);
+	tomoyo_create_entry("domain_policy",    0600, policy_dir, TOMOYO_POLICY_DOMAINPOLICY);
+	tomoyo_create_entry("exception_policy", 0600, policy_dir, TOMOYO_POLICY_EXCEPTIONPOLICY);
+	tomoyo_create_entry(".domain_status",   0600, policy_dir, TOMOYO_POLICY_DOMAIN_STATUS);
+	tomoyo_create_entry(".process_status",  0400, info_dir, TOMOYO_INFO_PROCESS_STATUS);
+	tomoyo_create_entry("self_domain",      0400, info_dir, TOMOYO_INFO_SELFDOMAIN);
+	tomoyo_create_entry("mapping",          0400, info_dir, TOMOYO_INFO_MAPPING);
+	tomoyo_create_entry("meminfo",          0400, info_dir, TOMOYO_INFO_MEMINFO);
+	tomoyo_create_entry("status",           0600, ccs_dir, TOMOYO_STATUS);
+	tomoyo_create_entry("manager",          0600, policy_dir, TOMOYO_POLICY_MANAGER);
+	tomoyo_create_entry(".updates_counter", 0400, info_dir, TOMOYO_INFO_UPDATESCOUNTER);
+}
---------------


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

* [TOMOYO 7/9] Auditing interface.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (5 preceding siblings ...)
  2007-06-14  7:37 ` [TOMOYO 6/9] Utility functions and /proc interface for policy manipulation Kentaro Takeda
@ 2007-06-14  7:38 ` Kentaro Takeda
  2007-06-14  7:38 ` [TOMOYO 8/9] File access control functions Kentaro Takeda
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:38 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

This patch makes access logs sent to auditing subsystem.
Although TOMOYO Linux has /proc interface for access logs,
we were advised to use auditing subsystem (after we introduced
TOMOYO Linux 1.0 on December 2005, http://lkml.org/lkml/2005/12/21/63 )
and we did so in TOMOYO Linux 2.0.

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

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

diff -ubBpErN linux-2.6.21.5/security/tomoyo/audit.c linux-2.6.21.5-tomoyo/security/tomoyo/audit.c
--- linux-2.6.21.5/security/tomoyo/audit.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/audit.c	2007-06-14 15:06:10.000000000 +0900
@@ -0,0 +1,52 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Audit functions for TOMOYO Linux
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include "tomoyo.h"
+#include <linux/audit.h>
+
+/* move to include/linux/audit.h */
+#define AUDIT_TOMOYO 2001
+
+char *tomoyo_init_audit_log(int *len)
+{
+	char *buf;
+	struct timeval tv;
+	struct task_struct *task = current;
+	const char *domainname =
+		((struct tomoyo_security *) current->security)->domain_info->domainname->name;
+	do_gettimeofday(&tv);
+	*len += strlen(domainname) + 256;
+	if ((buf = tomoyo_alloc(*len)) != NULL)
+		snprintf(buf,
+		         (*len) - 1,
+		         "#timestamp=%lu pid=%d uid=%d gid=%d euid=%d egid=%d "
+		         "suid=%d sgid=%d fsuid=%d fsgid=%d\n%s\n",
+		         tv.tv_sec, task->pid, task->uid, task->gid, task->euid, task->egid,
+		         task->suid, task->sgid, task->fsuid, task->fsgid, domainname);
+	return buf;
+}
+
+/*
+ * Write audit log.
+ * Caller must allocate buf with tomoyo_init_audit_log().
+ */
+int tomoyo_write_audit_log(char *buf, const int is_granted)
+{
+	struct audit_buffer *ab;
+	ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_TOMOYO);
+	if (ab) {
+		audit_log_format(ab,
+		                 "%s\n%s",
+		                 is_granted ? "TOMOYO_GRANT_LOG" : "TOMOYO_REJECT_LOG", buf);
+		audit_log_end(ab);
+	}
+	tomoyo_free(buf);
+	return ab ? 0 : -ENOMEM;
+}
---------------


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

* [TOMOYO 8/9] File access control functions.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (6 preceding siblings ...)
  2007-06-14  7:38 ` [TOMOYO 7/9] Auditing interface Kentaro Takeda
@ 2007-06-14  7:38 ` Kentaro Takeda
  2007-06-14  7:39 ` [TOMOYO 9/9] Domain transition handler functions Kentaro Takeda
  2007-06-14 16:15 ` [TOMOYO 0/9] TOMOYO Linux security module Pavel Machek
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:38 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

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@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.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;
+}
---------------


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

* [TOMOYO 9/9] Domain transition handler functions.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (7 preceding siblings ...)
  2007-06-14  7:38 ` [TOMOYO 8/9] File access control functions Kentaro Takeda
@ 2007-06-14  7:39 ` Kentaro Takeda
  2007-06-14 16:15 ` [TOMOYO 0/9] TOMOYO Linux security module Pavel Machek
  9 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-14  7:39 UTC (permalink / raw)
  To: linux-kernel, linux-security-module

This is the main part for domain transition.
In TOMOYO Linux, domains are automatically created at runtime.

To make discussion smooth by reducing the amount of patches,
we pruned argv[0] checks (although we referred the need of argv[0] checking
at AppArmor's thread, http://lkml.org/lkml/2007/5/26/52 ).

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

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

diff -ubBpErN linux-2.6.21.5/security/tomoyo/domain.c linux-2.6.21.5-tomoyo/security/tomoyo/domain.c
--- linux-2.6.21.5/security/tomoyo/domain.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.21.5-tomoyo/security/tomoyo/domain.c	2007-06-05 00:00:00.000000000 +0900
@@ -0,0 +1,782 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Domain transition functions for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2007  NTT DATA CORPORATION
+ *
+ * Version: 2.0   2007/06/05
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/highmem.h>
+#include <linux/binfmts.h>
+
+#ifndef for_each_process
+#define for_each_process for_each_task
+#endif
+
+/*************************  VARIABLES  *************************/
+
+/* /sbin/init started? */
+extern int sbin_init_started;
+
+/* Lock for appending domain's ACL. */
+DECLARE_MUTEX(domain_acl_lock);
+
+/***** The structure for program files to force domain reconstruction. *****/
+
+struct domain_initializer_entry {
+	struct domain_initializer_entry *next;
+	const struct path_info *domainname;    /* This may be NULL */
+	const struct path_info *program;
+	u8 is_deleted;
+	u8 is_not;
+	u8 is_last_name;
+	u8 is_oldstyle;
+};
+
+/***** The structure for domains to not to transit domains. *****/
+
+struct domain_keeper_entry {
+	struct domain_keeper_entry *next;
+	const struct path_info *domainname;
+	const struct path_info *program;       /* This may be NULL */
+	u8 is_deleted;
+	u8 is_not;
+	u8 is_last_name;
+};
+
+/***** The structure for program files that should be aggregated. *****/
+
+struct aggregator_entry {
+	struct aggregator_entry *next;
+	const struct path_info *original_name;
+	const struct path_info *aggregated_name;
+	int is_deleted;
+};
+
+/***** The structure for program files that should be aliased. *****/
+
+struct alias_entry {
+	struct alias_entry *next;
+	const struct path_info *original_name;
+	const struct path_info *aliased_name;
+	int is_deleted;
+};
+
+/*************************  VARIABLES  *************************/
+
+/* Domain creation lock. */
+static DECLARE_MUTEX(new_domain_assign_lock);
+
+/*************************  UTILITY FUNCTIONS  *************************/
+
+int tomoyo_is_domain_def(const unsigned char *buffer)
+{
+	/* while (*buffer && (*buffer <= ' ' || *buffer >= 127)) buffer++; */
+	return strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN) == 0;
+}
+
+const char *tomoyo_get_last_name(const struct domain_info *domain)
+{
+	const char *cp0 = domain->domainname->name, *cp1;
+	if ((cp1 = strrchr(cp0, ' ')) != NULL) return cp1 + 1;
+	return cp0;
+}
+
+int tomoyo_read_self_domain(struct io_buffer *head)
+{
+	if (!head->read_eof) {
+		tomoyo_io_printf(head,
+		                 "%s",
+		                 ((struct tomoyo_security *) current->security)->domain_info->domainname->name);
+		head->read_eof = 1;
+	}
+	return 0;
+}
+
+int tomoyo_add_domain_acl(struct acl_info *ptr, struct domain_info *domain, struct acl_info *new_ptr)
+{
+	mb(); /* Instead of using spinlock. */
+	if (!ptr) domain->first_acl_ptr = (struct acl_info *) new_ptr;
+	else ptr->next = (struct acl_info *) new_ptr;
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY);
+	return 0;
+}
+
+int tomoyo_del_domain_acl(struct acl_info *ptr)
+{
+	ptr->is_deleted = 1;
+	tomoyo_update_counter(TOMOYO_UPDATES_COUNTER_DOMAIN_POLICY);
+	return 0;
+}
+
+int tomoyo_too_many_domain_acl(struct domain_info * const domain) {
+	unsigned int count = 0;
+	struct acl_info *ptr;
+	for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+		if (!ptr->is_deleted) count++;
+	}
+	/* If there are so many entries, don't append if accept mode. */
+	if (count < tomoyo_check_flags(TOMOYO_MAX_ACCEPT_ENTRY)) return 0;
+	if (!domain->quota_warned) {
+		printk("TOMOYO-WARNING: Domain '%s' has so many ACLs to hold. "
+		       "Stopped auto-append mode.\n", domain->domainname->name);
+		domain->quota_warned = 1;
+	}
+	return 1;
+}
+
+
+/*************************  DOMAIN INITIALIZER HANDLER  *************************/
+
+static struct domain_initializer_entry *domain_initializer_list = NULL;
+
+static int tomoyo_add_domain_initializer_entry(const char *domainname,
+                                               const char *program,
+                                               const int is_not,
+                                               const int is_delete,
+                                               const int is_oldstyle)
+{
+	struct domain_initializer_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_program, *saved_domainname = NULL;
+	int error = -ENOMEM;
+	int is_last_name = 0;
+	if (!tomoyo_is_correct_path(program, 1, -1, -1, __FUNCTION__))
+		return -EINVAL; /* No patterns allowed. */
+	if (domainname) {
+		if (!tomoyo_is_domain_def(domainname) &&
+		    tomoyo_is_correct_path(domainname, 1, -1, -1, __FUNCTION__)) {
+			is_last_name = 1;
+		} else if (!tomoyo_is_correct_domain(domainname, __FUNCTION__)) {
+			return -EINVAL;
+		}
+		if ((saved_domainname = tomoyo_save_name(domainname)) == NULL) return -ENOMEM;
+	}
+	if ((saved_program = tomoyo_save_name(program)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (ptr = domain_initializer_list; ptr; ptr = ptr->next) {
+		if (ptr->is_not == is_not &&
+		    ptr->is_oldstyle == is_oldstyle &&
+		    ptr->domainname == saved_domainname &&
+		    ptr->program == saved_program) {
+			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->domainname = saved_domainname;
+	new_entry->program = saved_program;
+	new_entry->is_not = is_not;
+	new_entry->is_last_name = is_last_name;
+	new_entry->is_oldstyle = is_oldstyle;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = domain_initializer_list) != NULL) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else {
+		domain_initializer_list = new_entry;
+	}
+	error = 0;
+ out:
+	up(&lock);
+	return error;
+}
+
+int tomoyo_read_domain_initializer_policy(struct io_buffer *head)
+{
+	struct domain_initializer_entry *ptr = head->read_var2;
+	if (!ptr) ptr = domain_initializer_list;
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted) {
+			if (ptr->domainname) {
+				if (tomoyo_io_printf(head,
+				                     "%s%s%s from %s\n",
+				                     ptr->is_not ? "no_" : "", 
+				                     ptr->is_oldstyle ?
+				                     	TOMOYO_KEYWORD_INITIALIZER : TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
+				                     ptr->program->name, ptr->domainname->name))
+					break;
+			} else {
+				if (tomoyo_io_printf(head,
+				                     "%s%s%s\n",
+				                     ptr->is_not ? "no_" : "",
+				                     ptr->is_oldstyle ?
+				                     	TOMOYO_KEYWORD_INITIALIZER : TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
+				                     ptr->program->name))
+					break;
+			}
+		}
+		ptr = ptr->next;
+	}
+	return ptr ? -ENOMEM : 0;
+}
+
+int tomoyo_add_domain_initializer_policy(char *data,
+                                         const int is_not,
+                                         const int is_delete,
+                                         const int is_oldstyle)
+{
+	char *cp = strstr(data, " from ");
+	if (cp) {
+		*cp = '\0';
+		return tomoyo_add_domain_initializer_entry(cp + 6,
+		                                           data,
+		                                           is_not,
+		                                           is_delete,
+		                                           is_oldstyle);
+	} else {
+		return tomoyo_add_domain_initializer_entry(NULL,
+		                                           data,
+		                                           is_not,
+		                                           is_delete,
+		                                           is_oldstyle);
+	}
+}
+
+static int tomoyo_is_domain_initializer(const struct path_info *domainname,
+                                        const struct path_info *program,
+                                        const struct path_info *last_name)
+{
+	struct domain_initializer_entry *ptr;
+	int flag = 0;
+	for (ptr = domain_initializer_list; ptr; ptr = ptr->next) {
+		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 0;
+		flag = 1;
+	}
+	return flag;
+}
+
+/*************************  DOMAIN KEEPER HANDLER  *************************/
+
+static struct domain_keeper_entry *domain_keeper_list = NULL;
+
+static int tomoyo_add_domain_keeper_entry(const char *domainname,
+                                          const char *program,
+                                          const int is_not,
+                                          const int is_delete)
+{
+	struct domain_keeper_entry *new_entry, *ptr;
+	const struct path_info *saved_domainname, *saved_program = NULL;
+	static DECLARE_MUTEX(lock);
+	int error = -ENOMEM;
+	int is_last_name = 0;
+	if (!tomoyo_is_domain_def(domainname) &&
+	    tomoyo_is_correct_path(domainname, 1, -1, -1, __FUNCTION__)) {
+		is_last_name = 1;
+	} else if (!tomoyo_is_correct_domain(domainname, __FUNCTION__)) {
+		return -EINVAL;
+	}
+	if (program) {
+		if (!tomoyo_is_correct_path(program, 1, -1, -1, __FUNCTION__)) return -EINVAL;
+		if ((saved_program = tomoyo_save_name(program)) == NULL) return -ENOMEM;
+	}
+	if ((saved_domainname = tomoyo_save_name(domainname)) == NULL) return -ENOMEM;
+	down(&lock);
+	for (ptr = domain_keeper_list; ptr; ptr = ptr->next) {
+		if (ptr->is_not == is_not && ptr->domainname == saved_domainname &&
+		    ptr->program == saved_program) {
+			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->domainname = saved_domainname;
+	new_entry->program = saved_program;
+	new_entry->is_not = is_not;
+	new_entry->is_last_name = is_last_name;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = domain_keeper_list) != NULL) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else {
+		domain_keeper_list = new_entry;
+	}
+	error = 0;
+ out:
+	up(&lock);
+	return error;
+}
+
+int tomoyo_add_domain_keeper_policy(char *data, const int is_not, const int is_delete)
+{
+	char *cp = strstr(data, " from ");
+	if (cp) {
+		*cp = '\0';
+		return tomoyo_add_domain_keeper_entry(cp + 6, data, is_not, is_delete);
+	} else {
+		return tomoyo_add_domain_keeper_entry(data, NULL, is_not, is_delete);
+	}
+}
+
+int tomoyo_read_domain_keeper_policy(struct io_buffer *head)
+{
+	struct domain_keeper_entry *ptr = head->read_var2;
+	if (!ptr) ptr = domain_keeper_list;
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted) {
+			if (ptr->program) {
+				if (tomoyo_io_printf(head,
+				                     "%s" TOMOYO_KEYWORD_KEEP_DOMAIN "%s from %s\n",
+				                     ptr->is_not ? "no_" : "",
+				                     ptr->program->name,
+				                     ptr->domainname->name))
+					break;
+			} else {
+				if (tomoyo_io_printf(head,
+				                     "%s" TOMOYO_KEYWORD_KEEP_DOMAIN "%s\n",
+				                     ptr->is_not ? "no_" : "",
+				                     ptr->domainname->name))
+					break;
+			}
+		}
+		ptr = ptr->next;
+	}
+	return ptr ? -ENOMEM : 0;
+}
+
+static int tomoyo_is_domain_keeper(const struct path_info *domainname,
+                                   const struct path_info *program,
+                                   const struct path_info *last_name)
+{
+	struct domain_keeper_entry *ptr;
+	int flag = 0;
+	for (ptr = domain_keeper_list; ptr; ptr = ptr->next) {
+		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 0;
+		flag = 1;
+	}
+	return flag;
+}
+
+/*************************  SYMBOLIC LINKED PROGRAM HANDLER  *************************/
+
+static struct alias_entry *alias_list = NULL;
+
+static int tomoyo_add_alias_entry(const char *original_name, const char *aliased_name, const int is_delete)
+{
+	struct alias_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_original_name, *saved_aliased_name;
+	int error = -ENOMEM;
+	if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __FUNCTION__) ||
+	    !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __FUNCTION__))
+		return -EINVAL; /* No patterns allowed. */
+	if ((saved_original_name = tomoyo_save_name(original_name)) == NULL ||
+	    (saved_aliased_name = tomoyo_save_name(aliased_name)) == NULL)
+		return -ENOMEM;
+	down(&lock);
+	for (ptr = alias_list; ptr; ptr = ptr->next) {
+		if (ptr->original_name == saved_original_name &&
+		    ptr->aliased_name == saved_aliased_name) {
+			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->original_name = saved_original_name;
+	new_entry->aliased_name = saved_aliased_name;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = alias_list) != NULL) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else {
+		alias_list = new_entry;
+	}
+	error = 0;
+ out:
+	up(&lock);
+	return error;
+}
+
+int tomoyo_read_alias_policy(struct io_buffer *head)
+{
+	struct alias_entry *ptr = head->read_var2;
+	if (!ptr) ptr = alias_list;
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted &&
+		    tomoyo_io_printf(head,
+		                     TOMOYO_KEYWORD_ALIAS "%s %s\n",
+		                     ptr->original_name->name,
+		                     ptr->aliased_name->name))
+			break;
+		ptr = ptr->next;
+	}
+	return ptr ? -ENOMEM : 0;
+}
+
+int tomoyo_add_alias_policy(char *data, const int is_delete)
+{
+	char *cp = strchr(data, ' ');
+	if (!cp) return -EINVAL;
+	*cp++ = '\0';
+	return tomoyo_add_alias_entry(data, cp, is_delete);
+}
+
+/*************************  DOMAIN AGGREGATOR HANDLER  *************************/
+
+static struct aggregator_entry *aggregator_list = NULL;
+
+static int tomoyo_add_aggregator_entry(const char *original_name,
+                                       const char *aggregated_name,
+                                       const int is_delete)
+{
+	struct aggregator_entry *new_entry, *ptr;
+	static DECLARE_MUTEX(lock);
+	const struct path_info *saved_original_name, *saved_aggregated_name;
+	int error = -ENOMEM;
+	if (!tomoyo_is_correct_path(original_name, 1, 0, -1, __FUNCTION__) ||
+	    !tomoyo_is_correct_path(aggregated_name, 1, -1, -1, __FUNCTION__))
+		return -EINVAL;
+	if ((saved_original_name = tomoyo_save_name(original_name)) == NULL ||
+	    (saved_aggregated_name = tomoyo_save_name(aggregated_name)) == NULL)
+		return -ENOMEM;
+	down(&lock);
+	for (ptr = aggregator_list; ptr; ptr = ptr->next) {
+		if (ptr->original_name == saved_original_name &&
+		    ptr->aggregated_name == saved_aggregated_name) {
+			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->original_name = saved_original_name;
+	new_entry->aggregated_name = saved_aggregated_name;
+	mb(); /* Instead of using spinlock. */
+	if ((ptr = aggregator_list) != NULL) {
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = new_entry;
+	} else {
+		aggregator_list = new_entry;
+	}
+	error = 0;
+ out:
+	up(&lock);
+	return error;
+}
+
+int tomoyo_read_aggregator_policy(struct io_buffer *head)
+{
+	struct aggregator_entry *ptr = head->read_var2;
+	if (!ptr) ptr = aggregator_list;
+	while (ptr) {
+		head->read_var2 = ptr;
+		if (!ptr->is_deleted &&
+		    tomoyo_io_printf(head,
+		                     TOMOYO_KEYWORD_AGGREGATOR "%s %s\n",
+		                     ptr->original_name->name,
+		                     ptr->aggregated_name->name))
+			break;
+		ptr = ptr->next;
+	}
+	return ptr ? -ENOMEM : 0;
+}
+
+int tomoyo_add_aggregator_policy(char *data, const int is_delete)
+{
+	char *cp = strchr(data, ' ');
+	if (!cp) return -EINVAL;
+	*cp++ = '\0';
+	return tomoyo_add_aggregator_entry(data, cp, is_delete);
+}
+
+/*************************  DOMAIN DELETION HANDLER  *************************/
+
+int tomoyo_delete_domain(char *domainname0)
+{
+	struct domain_info *domain;
+	struct path_info domainname;
+	domainname.name = domainname0;
+	tomoyo_fill_path_info(&domainname);
+	down(&new_domain_assign_lock);
+	/* Is there an active domain? */ /* Never delete KERNEL_DOMAIN */
+	for (domain = KERNEL_DOMAIN.next; domain; domain = domain->next) {
+		if (domain->is_deleted || tomoyo_pathcmp(domain->domainname, &domainname)) continue;
+		break;
+	}
+	if (domain) {
+		struct domain_info *domain2;
+		/* Mark already deleted domains as non undeletable. */
+		for (domain2 = KERNEL_DOMAIN.next; domain2; domain2 = domain2->next) {
+			if (!domain2->is_deleted || tomoyo_pathcmp(domain2->domainname, &domainname))
+				continue;
+			domain2->is_deleted = 255;
+		}
+		/* Delete and mark active domain as undeletable. */
+		domain->is_deleted = 1;
+	}
+	up(&new_domain_assign_lock);
+	return 0;
+}
+
+struct domain_info *tomoyo_undelete_domain(const char *domainname0)
+{
+	struct domain_info *domain, *candidate_domain = NULL;
+	struct path_info domainname;
+	domainname.name = domainname0;
+	tomoyo_fill_path_info(&domainname);
+	down(&new_domain_assign_lock);
+	for (domain = KERNEL_DOMAIN.next; domain; domain = domain->next) {
+		if (tomoyo_pathcmp(&domainname, domain->domainname)) continue;
+		if (!domain->is_deleted) {
+			/* This domain is active. I can't undelete. */
+			candidate_domain = NULL;
+			break;
+		}
+		/* Is this domain undeletable? */
+		if (domain->is_deleted == 1) candidate_domain = domain;
+	}
+	if (candidate_domain) {
+		candidate_domain->is_deleted = 0;
+	}
+	up(&new_domain_assign_lock);
+	return candidate_domain;
+}
+
+/*************************  DOMAIN TRANSITION HANDLER  *************************/
+
+struct domain_info *tomoyo_find_domain(const char *domainname0)
+{
+	struct domain_info *domain;
+	static int first = 1;
+	struct path_info domainname;
+	domainname.name = domainname0;
+	tomoyo_fill_path_info(&domainname);
+	if (first) {
+		KERNEL_DOMAIN.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
+		first = 0;
+	}
+	for (domain = &KERNEL_DOMAIN; domain; domain = domain->next) {
+		if (!domain->is_deleted && !tomoyo_pathcmp(&domainname, domain->domainname))
+			return domain;
+	}
+	return NULL;
+}
+
+struct domain_info *tomoyo_find_or_assign_new_domain(const char *domainname, const u8 profile)
+{
+	struct domain_info *domain = NULL;
+	const struct path_info *saved_domainname;
+	down(&new_domain_assign_lock);
+	if ((domain = tomoyo_find_domain(domainname)) != NULL) goto out;
+	if (!tomoyo_is_correct_domain(domainname, __FUNCTION__)) goto out;
+	if ((saved_domainname = tomoyo_save_name(domainname)) == NULL) goto out;
+	/* Can I reuse memory of deleted domain? */
+	for (domain = KERNEL_DOMAIN.next; domain; domain = domain->next) {
+		struct task_struct *p;
+		struct acl_info *ptr;
+		int flag;
+		if (!domain->is_deleted || domain->domainname != saved_domainname) continue;
+		flag = 0;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		for_each_process(p) {
+			if (p->security == domain) {
+				flag = 1;
+				break;
+			}
+		}
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+		if (flag) continue;
+		for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) ptr->is_deleted = 1;
+		domain->profile = profile;
+		domain->quota_warned = 0;
+		mb(); /* Instead of using spinlock. */
+		domain->is_deleted = 0;
+		goto out;
+	}
+	/* No memory reusable. Create using new memory. */
+	if ((domain = tomoyo_alloc_element(sizeof(*domain))) != NULL) {
+		struct domain_info *ptr = &KERNEL_DOMAIN;
+		domain->domainname = saved_domainname;
+		domain->profile = profile;
+		mb(); /* Instead of using spinlock. */
+		while (ptr->next)
+			ptr = ptr->next;
+		ptr->next = domain;
+	}
+ out: ;
+	up(&new_domain_assign_lock);
+	return domain;
+}
+
+int tomoyo_find_next_domain(struct linux_binprm *bprm, struct domain_info **next_domain)
+{
+	/* This function assumes that the size of buffer returned */
+	/* by tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. */
+	struct domain_info *old_domain =
+		((struct tomoyo_security *) current->security)->domain_info, *domain = NULL;
+	const char *old_domain_name = old_domain->domainname->name;
+	const char *original_name = bprm->filename;
+	struct file *filp = bprm->file;
+	char *new_domain_name = NULL;
+	char *real_program_name = NULL, *symlink_program_name = NULL;
+	const int is_enforce = tomoyo_check_enforce(TOMOYO_MAC_FOR_FILE);
+	int retval;
+	struct path_info r, s, l;
+
+	{
+		/*
+		 * Built-in initializers.
+		 * This is needed because policies are not loaded until starting /sbin/init .
+		 */
+		static int first = 1;
+		if (first) {
+			tomoyo_add_domain_initializer_entry(NULL, "/sbin/hotplug", 0, 0, 0);
+			tomoyo_add_domain_initializer_entry(NULL, "/sbin/modprobe", 0, 0, 0);
+			first = 0;
+		}
+	}
+
+	/* Get realpath of program. */
+	retval = -ENOENT; /* I hope tomoyo_realpath() won't fail with -ENOMEM. */
+	if ((real_program_name = tomoyo_realpath(original_name)) == NULL) goto out;
+	/* Get realpath of symbolic link. */
+	if ((symlink_program_name = tomoyo_realpath_nofollow(original_name)) == NULL) goto out;
+
+	r.name = real_program_name;
+	tomoyo_fill_path_info(&r);
+	s.name = symlink_program_name;
+	tomoyo_fill_path_info(&s);
+	if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;
+	else l.name = old_domain_name;
+	tomoyo_fill_path_info(&l);
+
+	/* Check 'alias' directive. */
+	if (tomoyo_pathcmp(&r, &s)) {
+		struct alias_entry *ptr;
+		/* Is this program allowed to be called via symbolic links? */
+		for (ptr = alias_list; ptr; ptr = ptr->next) {
+			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 'aggregator' directive. */
+	{
+		struct aggregator_entry *ptr;
+		/* Is this program allowed to be aggregated? */
+		for (ptr = aggregator_list; ptr; ptr = ptr->next) {
+			if (ptr->is_deleted ||
+			    !tomoyo_path_matches_to_pattern(&r, ptr->original_name))
+				continue;
+			memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
+			strncpy(real_program_name,
+			        ptr->aggregated_name->name,
+			        TOMOYO_MAX_PATHNAME_LEN - 1);
+			tomoyo_fill_path_info(&r);
+			break;
+		}
+	}
+	
+	/* Check execute permission. */
+	if ((retval = tomoyo_check_exec_perm(&r, filp)) < 0) goto out;
+
+	/* Allocate memory for calcurating domain name. */
+	retval = -ENOMEM;
+	if ((new_domain_name = tomoyo_alloc(TOMOYO_MAX_PATHNAME_LEN + 16)) == NULL) goto out;
+	
+	if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) {
+		/* Transit to the child of KERNEL_DOMAIN domain. */
+		snprintf(new_domain_name,
+		         TOMOYO_MAX_PATHNAME_LEN + 1,
+		         TOMOYO_ROOT_NAME " " "%s",
+		         real_program_name);
+	} else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {
+		/*
+		 * Needn't to transit from kernel domain before starting /sbin/init .
+		 * But transit from kernel domain if executing initializers,
+		 * for they might start before /sbin/init .
+		 */
+		domain = old_domain;
+	} else if (tomoyo_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) {
+		if (is_enforce) {
+			domain = tomoyo_find_domain(new_domain_name);
+			if (!domain)
+				if (tomoyo_check_supervisor("#Need to create domain\n%s\n",
+				    new_domain_name) == 0)
+					domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+						                                  ((struct tomoyo_security *) current->security)->domain_info->profile);
+		} else {
+			domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+			                                          ((struct tomoyo_security *) current->security)->domain_info->profile);
+		}
+	}
+	if (!domain) {
+		printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);
+		if (is_enforce) retval = -EPERM;
+	} else {
+		retval = 0;
+	}
+ out: ;
+	tomoyo_free(new_domain_name);
+	tomoyo_free(real_program_name);
+	tomoyo_free(symlink_program_name);
+	*next_domain = domain ? domain : old_domain;
+	return retval;
+}
---------------


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

* Re: [TOMOYO 1/9] Allow use of namespace_sem from LSM module.
  2007-06-14  7:32 ` [TOMOYO 1/9] Allow use of namespace_sem from LSM module Kentaro Takeda
@ 2007-06-14 16:13   ` Pavel Machek
  2007-06-15  2:53     ` Kentaro Takeda
  0 siblings, 1 reply; 21+ messages in thread
From: Pavel Machek @ 2007-06-14 16:13 UTC (permalink / raw)
  To: Kentaro Takeda; +Cc: linux-kernel, linux-security-module

Hi!

> TOMOYO Linux uses pathnames for auditing and controlling 
> file access.
> Therefore, namespace_sem is needed.
> 
> Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
> Signed-off-by: Tetsuo Handa 
> <penguin-kernel@I-love.SAKURA.ne.jp>
> 
> ---------------
> fs/namespace.c |    2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff -ubBpErN linux-2.6.21.5/fs/namespace.c 
> linux-2.6.21.5-tomoyo/fs/namespace.c
> --- linux-2.6.21.5/fs/namespace.c	2007-06-12 
> 03:37:06.000000000 +0900
> +++ linux-2.6.21.5-tomoyo/fs/namespace.c 2007-06-14 
> 15:02:38.000000000 +0900
> @@ -37,7 +37,7 @@ static int event;
> static struct list_head *mount_hashtable __read_mostly;
> static int hash_mask __read_mostly, hash_bits 
> __read_mostly;
> static struct kmem_cache *mnt_cache __read_mostly;
> -static struct rw_semaphore namespace_sem;
> +struct rw_semaphore namespace_sem;
> 

Looks whitespace-damaged to me.
							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [TOMOYO 0/9] TOMOYO Linux security module.
  2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
                   ` (8 preceding siblings ...)
  2007-06-14  7:39 ` [TOMOYO 9/9] Domain transition handler functions Kentaro Takeda
@ 2007-06-14 16:15 ` Pavel Machek
  2007-06-15  1:27   ` Kentaro Takeda
  9 siblings, 1 reply; 21+ messages in thread
From: Pavel Machek @ 2007-06-14 16:15 UTC (permalink / raw)
  To: Kentaro Takeda; +Cc: linux-kernel, linux-security-module

Hi!

> The following patches are TOMOYO Linux 2.0.
> TOMOYO Linux 2.0 is implemented as a LSM module.
> 
> If you want to use older kernel, please download from
> http://osdn.dl.sourceforge.jp/tomoyo/25693/tomoyo-lsm-2.0-20070605.tar.gz

Uh, can we get some docs? Like how this is better than selinux, what
it does, how is it configured...?
							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-14  7:36 ` [TOMOYO 5/9] Memory and pathname management functions Kentaro Takeda
@ 2007-06-14 17:34   ` Christoph Hellwig
  2007-06-15  1:19     ` Toshiharu Harada
  0 siblings, 1 reply; 21+ messages in thread
From: Christoph Hellwig @ 2007-06-14 17:34 UTC (permalink / raw)
  To: Kentaro Takeda; +Cc: linux-kernel, linux-security-module

On Thu, Jun 14, 2007 at 04:36:09PM +0900, Kentaro Takeda wrote:
> We limit the maximum length of any string data (such as domainname and 
> pathnames)
> to TOMOYO_MAX_PATHNAME_LEN (which is 4000) bytes to fit within a single 
> page.
> 
> Userland programs can obtain the amount of RAM currently used by TOMOYO 
> from /proc interface.


Same NACK for this as for AppArmor, on exactly the same grounds.  Please
stop wasting your time on pathname-based non-solutions.


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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-14 17:34   ` Christoph Hellwig
@ 2007-06-15  1:19     ` Toshiharu Harada
  0 siblings, 0 replies; 21+ messages in thread
From: Toshiharu Harada @ 2007-06-15  1:19 UTC (permalink / raw)
  To: Christoph Hellwig, Kentaro Takeda, linux-kernel, linux-security-module

Christoph Hellwig wrote:
> On Thu, Jun 14, 2007 at 04:36:09PM +0900, Kentaro Takeda wrote:
>> We limit the maximum length of any string data (such as domainname and 
>> pathnames)
>> to TOMOYO_MAX_PATHNAME_LEN (which is 4000) bytes to fit within a single 
>> page.
>>
>> Userland programs can obtain the amount of RAM currently used by TOMOYO 
>> from /proc interface.
> 
> Same NACK for this as for AppArmor, on exactly the same grounds.  Please
> stop wasting your time on pathname-based non-solutions.

TOMOYO Linux is a pathname-based MAC, that is true.
But what that patch aimed for was sharing the idea of having
Linux kernel to keep "process invocation history" information
for each process. In that sense, TOMOYO Linux is just
a sample implementation.

Please take a look at the following message:
http://lkml.org/lkml/2007/6/13/58

Best regards,
Toshiharu Harada


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

* Re: [TOMOYO 0/9] TOMOYO Linux security module.
  2007-06-14 16:15 ` [TOMOYO 0/9] TOMOYO Linux security module Pavel Machek
@ 2007-06-15  1:27   ` Kentaro Takeda
  0 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-15  1:27 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, linux-security-module

> Uh, can we get some docs? Like how this is better than selinux, what
> it does, how is it configured...?
> 							Pavel

That message and its children were meant to be put
under the bellow message. Sorry for the confusion.
http://lkml.org/lkml/2007/6/13/58

Kentaro Takeda


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

* Re: [TOMOYO 1/9] Allow use of namespace_sem from LSM module.
  2007-06-14 16:13   ` Pavel Machek
@ 2007-06-15  2:53     ` Kentaro Takeda
  0 siblings, 0 replies; 21+ messages in thread
From: Kentaro Takeda @ 2007-06-15  2:53 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, linux-security-module

> Looks whitespace-damaged to me.
> 							Pavel
Oops, I sent patches with "Content-type: format=flowed" header.
I think your mail client converted tabs into spaces.
The orignal patches themselves are not whitespace-damaged.

http://kb.mozillazine.org/Plain_text_e-mail_(Thunderbird)

Kentaro Takeda


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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-21 18:22     ` Pavel Machek
@ 2007-06-22 14:45       ` Albert Cahalan
  0 siblings, 0 replies; 21+ messages in thread
From: Albert Cahalan @ 2007-06-22 14:45 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, linux-security-module, takedakn, hch

On 6/21/07, Pavel Machek <pavel@ucw.cz> wrote:

> > >> It's really not worth getting bothered by. Truth is, big
> > >> giant
> > >> pathnames break lots of stuff already, both kernel and
> > >> userspace.
> > >
> > >> Just look in /proc for some nice juicy kernel breakage:
> > >> cwd, exe, fd/*, maps, mounts, mountstats, root, smaps
> > >
> > >Well, but we should be fixing that, not adding more. And /proc is
> > >info-only, while this is security related code.
> >
> > Security tools read from /proc, so /proc is security-related.
>
> If some tool relies on pathnames in /proc, that tool is broken... as
> is /proc. We should be fixing that.

Running TOMOYO or AppArmor fixes the bug. :-)
You can't get long paths that break /proc if you are
running either. Therefore, one of those is required.

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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-16  9:08   ` Albert Cahalan
@ 2007-06-21 18:22     ` Pavel Machek
  2007-06-22 14:45       ` Albert Cahalan
  0 siblings, 1 reply; 21+ messages in thread
From: Pavel Machek @ 2007-06-21 18:22 UTC (permalink / raw)
  To: Albert Cahalan; +Cc: linux-kernel, linux-security-module, takedakn, hch

Hi!

> >> It's really not worth getting bothered by. Truth is, big
> >> giant
> >> pathnames break lots of stuff already, both kernel and
> >> userspace.
> >
> >> Just look in /proc for some nice juicy kernel breakage:
> >> cwd, exe, fd/*, maps, mounts, mountstats, root, smaps
> >
> >Well, but we should be fixing that, not adding more. And /proc is
> >info-only, while this is security related code.
> 
> Security tools read from /proc, so /proc is security-related.

If some tool relies on pathnames in /proc, that tool is broken... as
is /proc. We should be fixing that.

> The limit imposed by TOMOYO (or AppArmor) is fine,
> despite being security-related. It just needs to fail in

It is not "fine", but maybe it will not get us a bugtraq posting.
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-15 13:00 ` Pavel Machek
@ 2007-06-16  9:08   ` Albert Cahalan
  2007-06-21 18:22     ` Pavel Machek
  0 siblings, 1 reply; 21+ messages in thread
From: Albert Cahalan @ 2007-06-16  9:08 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, linux-security-module, takedakn, hch

On 6/15/07, Pavel Machek <pavel@ucw.cz> wrote:
> [Albert Cahalan]

> > It's really not worth getting bothered by. Truth is, big
> > giant
> > pathnames break lots of stuff already, both kernel and
> > userspace.
>
> > Just look in /proc for some nice juicy kernel breakage:
> > cwd, exe, fd/*, maps, mounts, mountstats, root, smaps
>
> Well, but we should be fixing that, not adding more. And /proc is
> info-only, while this is security related code.

Security tools read from /proc, so /proc is security-related.

The limit imposed by TOMOYO (or AppArmor) is fine,
despite being security-related. It just needs to fail in
the safe direction: access denied.

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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
  2007-06-15  7:16 [TOMOYO 5/9] Memory and pathname management functions Albert Cahalan
@ 2007-06-15 13:00 ` Pavel Machek
  2007-06-16  9:08   ` Albert Cahalan
  0 siblings, 1 reply; 21+ messages in thread
From: Pavel Machek @ 2007-06-15 13:00 UTC (permalink / raw)
  To: Albert Cahalan; +Cc: linux-kernel, linux-security-module, takedakn, hch

Hi!

> >>We limit the maximum length of any string data (such as
> >>domainname and pathnames) to TOMOYO_MAX_PATHNAME_LEN
> >>(which is 4000) bytes to fit within a single page.
> >>
> >>Userland programs can obtain the amount of RAM 
> >>currently
> >>used by TOMOYO from /proc interface.
> >
> >Same NACK for this as for AppArmor, on exactly the same 
> >grounds.
> >Please stop wasting your time on pathname-based 
> >non-solutions.
> 
> This issue is a very very small wart on an otherwise 
> fine idea.
> It's really not worth getting bothered by. Truth is, big 
> giant
> pathnames break lots of stuff already, both kernel and 
> userspace.

> Just look in /proc for some nice juicy kernel breakage:
> cwd, exe, fd/*, maps, mounts, mountstats, root, smaps

Well, but we should be fixing that, not adding more. And /proc is
info-only, while this is security related code.

							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [TOMOYO 5/9] Memory and pathname management functions.
@ 2007-06-15  7:16 Albert Cahalan
  2007-06-15 13:00 ` Pavel Machek
  0 siblings, 1 reply; 21+ messages in thread
From: Albert Cahalan @ 2007-06-15  7:16 UTC (permalink / raw)
  To: linux-kernel, linux-security-module, takedakn, hch

Christoph Hellwig writes:
> On Thu, Jun 14, 2007 at 04:36:09PM +0900, Kentaro Takeda wrote:

>> We limit the maximum length of any string data (such as
>> domainname and pathnames) to TOMOYO_MAX_PATHNAME_LEN
>> (which is 4000) bytes to fit within a single page.
>>
>> Userland programs can obtain the amount of RAM currently
>> used by TOMOYO from /proc interface.
>
> Same NACK for this as for AppArmor, on exactly the same grounds.
> Please stop wasting your time on pathname-based non-solutions.

This issue is a very very small wart on an otherwise fine idea.
It's really not worth getting bothered by. Truth is, big giant
pathnames break lots of stuff already, both kernel and userspace.

Just look in /proc for some nice juicy kernel breakage:
cwd, exe, fd/*, maps, mounts, mountstats, root, smaps

So, is that a NACK for the /proc filesystem too? :-)

We even limit filenames to 255 chars; just the other day
a Russian guy was complaining that his monstrous filenames
on a vfat filesystem could not be represented in UTF-8 mode.

Both TOMOYO and AppArmor are good ideas. At minimum, one of
them ought to be accepted. My preference would be TOMOYO,
having origins untainted by Novell's Microsoft dealings.

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

end of thread, other threads:[~2007-06-22 14:45 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-14  7:30 [TOMOYO 0/9] TOMOYO Linux security module Kentaro Takeda
2007-06-14  7:32 ` [TOMOYO 1/9] Allow use of namespace_sem from LSM module Kentaro Takeda
2007-06-14 16:13   ` Pavel Machek
2007-06-15  2:53     ` Kentaro Takeda
2007-06-14  7:33 ` [TOMOYO 2/9] Kconfig and Makefile for TOMOYO Linux Kentaro Takeda
2007-06-14  7:34 ` [TOMOYO 3/9] Data structures and prototypes definition Kentaro Takeda
2007-06-14  7:34 ` [TOMOYO 4/9] LSM adapter for TOMOYO Kentaro Takeda
2007-06-14  7:36 ` [TOMOYO 5/9] Memory and pathname management functions Kentaro Takeda
2007-06-14 17:34   ` Christoph Hellwig
2007-06-15  1:19     ` Toshiharu Harada
2007-06-14  7:37 ` [TOMOYO 6/9] Utility functions and /proc interface for policy manipulation Kentaro Takeda
2007-06-14  7:38 ` [TOMOYO 7/9] Auditing interface Kentaro Takeda
2007-06-14  7:38 ` [TOMOYO 8/9] File access control functions Kentaro Takeda
2007-06-14  7:39 ` [TOMOYO 9/9] Domain transition handler functions Kentaro Takeda
2007-06-14 16:15 ` [TOMOYO 0/9] TOMOYO Linux security module Pavel Machek
2007-06-15  1:27   ` Kentaro Takeda
2007-06-15  7:16 [TOMOYO 5/9] Memory and pathname management functions Albert Cahalan
2007-06-15 13:00 ` Pavel Machek
2007-06-16  9:08   ` Albert Cahalan
2007-06-21 18:22     ` Pavel Machek
2007-06-22 14:45       ` Albert Cahalan

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).