This is LSM adapter for TOMOYO.
This version of TOMOYO assigns only one variable "struct tomoyo_domain_info *"
to "current->cred->security".
Future version will require two variables "struct tomoyo_domain_info *" and
"u32".
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/include/linux/sched.h#L1311
Well, how to implement this while "current->cred->security" cannot be modified
freely?
To be honest, the only object which TOMOYO needs to assign variables is
"struct task_struct". TOMOYO's "T" means "task".
TOMOYO utilizes characteristics of "struct task_struct".
For TOMOYO, use of per task variables is vital.
Removal of security_task_alloc() and security_task_free() by introduction of
COW credentials will become a nightmare when TOMOYO tries to add "u32".
Of cource, if TOMOYO is allowed to add "u32" to "struct task_struct",
COW credentials won't become a nightmare.
Signed-off-by: Kentaro Takeda <[email protected]>
Signed-off-by: Tetsuo Handa <[email protected]>
Signed-off-by: Toshiharu Harada <[email protected]>
---
security/tomoyo/tomoyo.c | 453 +++++++++++++++++++++++++++++++++++++++++++++++
security/tomoyo/tomoyo.h | 107 +++++++++++
2 files changed, 560 insertions(+)
--- /dev/null
+++ linux-2.6.28-rc5-mm1/security/tomoyo/tomoyo.c
@@ -0,0 +1,453 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2008 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2008/11/11
+ *
+ */
+
+#include <linux/security.h>
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ /*
+ * Since "struct tomoyo_domain_info *" is a sharable pointer,
+ * we don't need to duplicate.
+ */
+ new->security = old->security;
+ return 0;
+}
+
+static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
+{
+ /*
+ * Do only if this function is called for the first time of an execve
+ * operation.
+ */
+ if (bprm->cred_prepared)
+ return 0;
+ /*
+ * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
+ * for the first time.
+ */
+ if (!tomoyo_policy_loaded)
+ tomoyo_load_policy(bprm->filename);
+ /*
+ * Tell tomoyo_bprm_check_security() is called for the first time of an
+ * execve operation.
+ */
+ bprm->cred->security = NULL;
+ return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct tomoyo_domain_info *domain = bprm->cred->security;
+
+ /*
+ * Execute permission is checked against pathname passed to do_execve()
+ * using current domain.
+ */
+ if (!domain) {
+ struct tomoyo_domain_info *next_domain = NULL;
+ int retval = tomoyo_find_next_domain(bprm, &next_domain);
+
+ if (!retval)
+ bprm->cred->security = next_domain;
+ return retval;
+ }
+ /*
+ * Read permission is checked against interpreters using next domain.
+ * '1' is the result of open_to_namei_flags(O_RDONLY).
+ */
+ return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1);
+}
+
+#ifdef CONFIG_SYSCTL
+
+static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
+{
+ int namelen = strlen(str);
+
+ if (*buflen < namelen)
+ return -ENOMEM;
+ *buflen -= namelen;
+ *buffer -= namelen;
+ memcpy(*buffer, str, namelen);
+ return 0;
+}
+
+/**
+ * sysctlpath_from_table - return the realpath of a ctl_table.
+ * @table: pointer to "struct ctl_table".
+ *
+ * Returns realpath(3) of the @table on success.
+ * Returns NULL on failure.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static char *sysctl_path(struct ctl_table *table)
+{
+ int buflen = TOMOYO_MAX_PATHNAME_LEN;
+ char *buf = tomoyo_alloc(buflen);
+ char *end = buf + buflen;
+ int error = -ENOMEM;
+
+ if (!buf)
+ return NULL;
+
+ *--end = '\0';
+ buflen--;
+ while (table) {
+ char buf[32];
+ const char *sp = table->procname;
+
+ if (!sp) {
+ memset(buf, 0, sizeof(buf));
+ snprintf(buf, sizeof(buf) - 1, "=%d=", table->ctl_name);
+ sp = buf;
+ }
+ if (tomoyo_prepend(&end, &buflen, sp) ||
+ tomoyo_prepend(&end, &buflen, "/"))
+ goto out;
+ table = table->parent;
+ }
+ if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
+ goto out;
+ error = tomoyo_encode(buf, end - buf, end);
+ out:
+ if (!error)
+ return buf;
+ tomoyo_free(buf);
+ return NULL;
+}
+
+static int tomoyo_sysctl(struct ctl_table *table, int op)
+{
+ int error;
+ char *name;
+
+ op &= MAY_READ | MAY_WRITE;
+ if (!op)
+ return 0;
+ name = sysctl_path(table);
+ if (!name)
+ return -ENOMEM;
+ error = tomoyo_check_file_perm(tomoyo_domain(), name, op);
+ tomoyo_free(name);
+ return error;
+}
+#endif
+
+/**
+ * tomoyo_update_result - Update error code.
+ *
+ * @error: Return code from security_path_*().
+ *
+ * To be able to return DAC's error (if any) to the caller instead of
+ * MAC's error, we don't return MAC's error at security_path_*().
+ *
+ * We remember MAC's error only if security_path_*() returned an error.
+ *
+ * Returns 0 on success, -ENOMEM otherwise if @error != 0.
+ * Returns previously saved error code and clears it if @error == 0.
+ */
+static int tomoyo_update_result(int error)
+{
+ /* Structure for holding the result of security_path_*(). */
+ struct tomoyo_check_result_entry {
+ struct list_head list;
+ struct task_struct *task; /* = current */
+ int error; /* != 0 */
+ };
+ static LIST_HEAD(list);
+ static DEFINE_SPINLOCK(lock);
+ struct task_struct *task = current;
+ struct tomoyo_check_result_entry *entry;
+ if (!error) {
+ if (!list_empty(&list)) {
+ struct tomoyo_check_result_entry *p;
+ entry = NULL;
+ /***** CRITICAL SECTION START *****/
+ spin_lock(&lock);
+ list_for_each_entry(p, &list, list) {
+ if (p->task != task)
+ continue;
+ list_del(&p->list);
+ entry = p;
+ break;
+ }
+ spin_unlock(&lock);
+ /***** CRITICAL SECTION END *****/
+ if (entry) {
+ error = entry->error;
+ kfree(entry);
+ }
+ }
+ return error;
+ }
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ entry->task = task;
+ entry->error = error;
+ /***** CRITICAL SECTION START *****/
+ spin_lock(&lock);
+ list_add(&entry->list, &list);
+ spin_unlock(&lock);
+ /***** CRITICAL SECTION END *****/
+ return 0;
+}
+
+/**
+ * tomoyo_save_result - Remember error code for security_inode_*() if any.
+ *
+ * @error: Return code from security_path_*().
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ *
+ * We don't save if @error == 0.
+ */
+static int tomoyo_save_result(const int error)
+{
+ return error ? tomoyo_update_result(error) : 0;
+}
+
+/**
+ * tomoyo_load_result - Fetch error code for security_inode_*().
+ *
+ * Returns error code saved by security_path_*().
+ */
+static int tomoyo_load_result(void)
+{
+ return tomoyo_update_result(0);
+}
+
+/* Clear error code in case security_inode_*() was not called. */
+static void tomoyo_path_clear(void)
+{
+ tomoyo_load_result();
+}
+
+static int tomoyo_path_truncate(struct path *path, loff_t length,
+ unsigned int time_attrs, struct file *filp)
+{
+ const int ret = tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_TRUNCATE_ACL,
+ path);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+ struct path path = { parent->mnt, dentry };
+ const int ret = tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_UNLINK_ACL,
+ &path);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+ int mode)
+{
+ struct path path = { parent->mnt, dentry };
+ const int ret = tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_MKDIR_ACL,
+ &path);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+ struct path path = { parent->mnt, dentry };
+ const int ret = tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_RMDIR_ACL,
+ &path);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+ const char *old_name)
+{
+ struct path path = { parent->mnt, dentry };
+ const int ret = tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_SYMLINK_ACL,
+ &path);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+ int mode, unsigned int dev)
+{
+ struct path path = { parent->mnt, dentry };
+ int type = TOMOYO_TYPE_CREATE_ACL;
+
+ switch (mode & S_IFMT) {
+ case S_IFCHR:
+ type = TOMOYO_TYPE_MKCHAR_ACL;
+ break;
+ case S_IFBLK:
+ type = TOMOYO_TYPE_MKBLOCK_ACL;
+ break;
+ case S_IFIFO:
+ type = TOMOYO_TYPE_MKFIFO_ACL;
+ break;
+ case S_IFSOCK:
+ type = TOMOYO_TYPE_MKSOCK_ACL;
+ break;
+ }
+ return tomoyo_save_result(tomoyo_check_1path_perm(tomoyo_domain(),
+ type, &path));
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+ struct dentry *new_dentry)
+{
+ struct path path1 = { new_dir->mnt, old_dentry };
+ struct path path2 = { new_dir->mnt, new_dentry };
+ const int ret = tomoyo_check_2path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_LINK_ACL,
+ &path1, &path2);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+ struct dentry *old_dentry,
+ struct path *new_parent,
+ struct dentry *new_dentry)
+{
+ struct path path1 = { old_parent->mnt, old_dentry };
+ struct path path2 = { new_parent->mnt, new_dentry };
+ const int ret = tomoyo_check_2path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_RENAME_ACL,
+ &path1, &path2);
+ return tomoyo_save_result(ret);
+}
+
+static int tomoyo_inode_link(struct dentry *old_dentry, struct inode *inode,
+ struct dentry *new_dentry)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_unlink(struct inode *inode, struct dentry *dentry)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_symlink(struct inode *inode, struct dentry *dentry,
+ const char *name)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_mkdir(struct inode *inode, struct dentry *dentry,
+ int mask)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_rmdir(struct inode *inode, struct dentry *dentry)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_create(struct inode *dir, struct dentry *dentry,
+ int mode)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_mknod(struct inode *inode, struct dentry *dentry,
+ int mode, dev_t dev)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_rename(struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct inode *new_inode,
+ struct dentry *new_dentry)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ return tomoyo_load_result();
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+ return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+ return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+ int flags = f->f_flags;
+
+ if ((flags + 1) & O_ACCMODE)
+ flags++;
+ flags |= f->f_flags & (O_APPEND | O_TRUNC);
+ /* Don't check read permission here if called from do_execve(). */
+ if (current->in_execve)
+ return 0;
+ return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+}
+
+static struct security_operations tomoyo_security_ops = {
+ .name = "tomoyo",
+ .cred_prepare = tomoyo_cred_prepare,
+ .bprm_set_creds = tomoyo_bprm_set_creds,
+ .bprm_check_security = tomoyo_bprm_check_security,
+#ifdef CONFIG_SYSCTL
+ .sysctl = tomoyo_sysctl,
+#endif
+ .file_fcntl = tomoyo_file_fcntl,
+ .dentry_open = tomoyo_dentry_open,
+ .path_truncate = tomoyo_path_truncate,
+ .path_unlink = tomoyo_path_unlink,
+ .path_mkdir = tomoyo_path_mkdir,
+ .path_rmdir = tomoyo_path_rmdir,
+ .path_symlink = tomoyo_path_symlink,
+ .path_mknod = tomoyo_path_mknod,
+ .path_link = tomoyo_path_link,
+ .path_rename = tomoyo_path_rename,
+ .inode_create = tomoyo_inode_create,
+ .inode_setattr = tomoyo_inode_setattr,
+ .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,
+ .path_clear = tomoyo_path_clear,
+};
+
+static int __init tomoyo_init(void)
+{
+ struct cred *cred = (struct cred *) current_cred();
+
+ if (!security_module_enable(&tomoyo_security_ops))
+ return 0;
+ /* register ourselves with the security framework */
+ if (register_security(&tomoyo_security_ops))
+ panic("Failure registering TOMOYO Linux");
+ printk(KERN_INFO "TOMOYO Linux initialized\n");
+ cred->security = &tomoyo_kernel_domain;
+ return 0;
+}
+
+security_initcall(tomoyo_init);
--- /dev/null
+++ linux-2.6.28-rc5-mm1/security/tomoyo/tomoyo.h
@@ -0,0 +1,107 @@
+/*
+ * security/tomoyo/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2008 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2008/11/11
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_TOMOYO_H
+#define _SECURITY_TOMOYO_TOMOYO_H
+
+struct tomoyo_path_info;
+struct path;
+struct inode;
+struct linux_binprm;
+struct pt_regs;
+struct tomoyo_page_buffer;
+
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+ const char *filename, const u8 perm);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+ const struct tomoyo_path_info *filename,
+ struct tomoyo_page_buffer *buf);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+ struct path *path, const int flag);
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+ const u8 operation, struct path *path);
+int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
+ const u8 operation, struct path *path1,
+ struct path *path2);
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+ struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+ struct tomoyo_domain_info **next_domain);
+
+/* Index numbers for Access Controls. */
+
+#define TOMOYO_TYPE_SINGLE_PATH_ACL 0
+#define TOMOYO_TYPE_DOUBLE_PATH_ACL 1
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+#define TOMOYO_TYPE_READ_WRITE_ACL 0
+#define TOMOYO_TYPE_EXECUTE_ACL 1
+#define TOMOYO_TYPE_READ_ACL 2
+#define TOMOYO_TYPE_WRITE_ACL 3
+#define TOMOYO_TYPE_CREATE_ACL 4
+#define TOMOYO_TYPE_UNLINK_ACL 5
+#define TOMOYO_TYPE_MKDIR_ACL 6
+#define TOMOYO_TYPE_RMDIR_ACL 7
+#define TOMOYO_TYPE_MKFIFO_ACL 8
+#define TOMOYO_TYPE_MKSOCK_ACL 9
+#define TOMOYO_TYPE_MKBLOCK_ACL 10
+#define TOMOYO_TYPE_MKCHAR_ACL 11
+#define TOMOYO_TYPE_TRUNCATE_ACL 12
+#define TOMOYO_TYPE_SYMLINK_ACL 13
+#define TOMOYO_TYPE_REWRITE_ACL 14
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
+
+#define TOMOYO_TYPE_LINK_ACL 0
+#define TOMOYO_TYPE_RENAME_ACL 1
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+
+#define TOMOYO_DOMAINPOLICY 0
+#define TOMOYO_EXCEPTIONPOLICY 1
+#define TOMOYO_DOMAIN_STATUS 2
+#define TOMOYO_PROCESS_STATUS 3
+#define TOMOYO_MEMINFO 4
+#define TOMOYO_SELFDOMAIN 5
+#define TOMOYO_VERSION 6
+#define TOMOYO_PROFILE 7
+#define TOMOYO_MANAGER 8
+#define TOMOYO_UPDATESCOUNTER 9
+
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+ return current_cred()->security;
+}
+
+/* Caller holds tasklist_lock spinlock. */
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+ *task)
+{
+ /***** CRITICAL SECTION START *****/
+ const struct cred *cred = get_task_cred(task);
+ struct tomoyo_domain_info *domain = cred->security;
+
+ put_cred(cred);
+ return domain;
+ /***** CRITICAL SECTION END *****/
+}
+
+#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */
--
On Thu, 2008-11-20 at 20:25 +0900, Tetsuo Handa wrote:
> plain text document attachment (tomoyo-hooks.patch)
> This is LSM adapter for TOMOYO.
> This version of TOMOYO assigns only one variable "struct tomoyo_domain_info *"
> to "current->cred->security".
>
> Future version will require two variables "struct tomoyo_domain_info *" and
> "u32".
> http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/include/linux/sched.h#L1311
> Well, how to implement this while "current->cred->security" cannot be modified
> freely?
>
> To be honest, the only object which TOMOYO needs to assign variables is
> "struct task_struct". TOMOYO's "T" means "task".
> TOMOYO utilizes characteristics of "struct task_struct".
> For TOMOYO, use of per task variables is vital.
>
> Removal of security_task_alloc() and security_task_free() by introduction of
> COW credentials will become a nightmare when TOMOYO tries to add "u32".
> Of cource, if TOMOYO is allowed to add "u32" to "struct task_struct",
> COW credentials won't become a nightmare.
Can you explain exactly how you are using this per-task state and why it
is a problem to create a new cred in the situations where you need to
modify it? I think that this issue needs to be addressed up front.
--
Stephen Smalley
National Security Agency
Hello.
Stephen Smalley wrote:
> > Removal of security_task_alloc() and security_task_free() by introduction of
> > COW credentials will become a nightmare when TOMOYO tries to add "u32".
> > Of cource, if TOMOYO is allowed to add "u32" to "struct task_struct",
> > COW credentials won't become a nightmare.
>
> Can you explain exactly how you are using this per-task state and why it
> is a problem to create a new cred in the situations where you need to
> modify it? I think that this issue needs to be addressed up front.
>
Use of COW credentials saves memory usage as long as per-task variable doesn't
change, but creating a new cred after fork() introduces new error paths
(i.e. memory allocation failure) when per-task variable changes, and COW
credentials will no longer save memory if per-task variable differs each other.
I use this per-task variable for tracking and restricting access requests
issued by a process.
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/fs/tomoyo_audit.c#L284
TOMOYO (version 1.6.x) provides "ability to change what request a process can
issue without involving execve()" according to "requests that process issued in
the past".
It is similar to AppArmor's chhat(), but it is done by the kernel and it is
done without modifying userland programs.
I use this per-task variable for remembering "whether the process is allowed to
modify policy or not". Since "the pathname of a program which is allowed to
modify policy" can be changed by package manager (e.g. "rpm" and "dpkg"),
I have to remember "whether the process ever had a pathname which is allowed to
modify policy or not".
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/fs/ccs_common.c#L1420
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/fs/tomoyo_domain.c#L1700
I use this per-task variable for holding "type of the process".
TOMOYO (version 1.6.x) provides "ability to record/verify/modify parameters
passed to execve()" by executing a userland helper instead of a program
passed to execve().
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/fs/tomoyo_domain.c#L1736
And maybe more in future.
Worrying about memory allocation failure complicates things. Thus I want to
allocate memory on fork() and not after fork(). For that purpose, directly
assigning "u32" to "struct task_struct" is preferable over allocating on
demand.
Regards.