diff options
author | Linus Torvalds | 2010-03-02 14:47:24 -0800 |
---|---|---|
committer | Linus Torvalds | 2010-03-02 14:47:24 -0800 |
commit | 832d30ca72c0a59058e66e097f5ea11f99640819 (patch) | |
tree | ab71581c4ad66b2a151298ed13c0eb2506fc8068 | |
parent | 3a5b27bf6f29574d667230c7e76e4b83fe3014e0 (diff) | |
parent | b4ccebdd37ff70d349321a198f416ba737a5e833 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (38 commits)
SELinux: Make selinux_kernel_create_files_as() shouldn't just always return 0
TOMOYO: Protect find_task_by_vpid() with RCU.
Security: add static to security_ops and default_security_ops variable
selinux: libsepol: remove dead code in check_avtab_hierarchy_callback()
TOMOYO: Remove __func__ from tomoyo_is_correct_path/domain
security: fix a couple of sparse warnings
TOMOYO: Remove unneeded parameter.
TOMOYO: Use shorter names.
TOMOYO: Use enum for index numbers.
TOMOYO: Add garbage collector.
TOMOYO: Add refcounter on domain structure.
TOMOYO: Merge headers.
TOMOYO: Add refcounter on string data.
TOMOYO: Reduce lines by using common path for addition and deletion.
selinux: fix memory leak in sel_make_bools
TOMOYO: Extract bitfield
syslog: clean up needless comment
syslog: use defined constants instead of raw numbers
syslog: distinguish between /proc/kmsg and syscalls
selinux: allow MLS->non-MLS and vice versa upon policy reload
...
30 files changed, 2030 insertions, 1708 deletions
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 7ca78346d3f0..cfe90a48a6e8 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -12,37 +12,37 @@ #include <linux/poll.h> #include <linux/proc_fs.h> #include <linux/fs.h> +#include <linux/syslog.h> #include <asm/uaccess.h> #include <asm/io.h> extern wait_queue_head_t log_wait; -extern int do_syslog(int type, char __user *bug, int count); - static int kmsg_open(struct inode * inode, struct file * file) { - return do_syslog(1,NULL,0); + return do_syslog(SYSLOG_ACTION_OPEN, NULL, 0, SYSLOG_FROM_FILE); } static int kmsg_release(struct inode * inode, struct file * file) { - (void) do_syslog(0,NULL,0); + (void) do_syslog(SYSLOG_ACTION_CLOSE, NULL, 0, SYSLOG_FROM_FILE); return 0; } static ssize_t kmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0)) + if ((file->f_flags & O_NONBLOCK) && + !do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_FILE)) return -EAGAIN; - return do_syslog(2, buf, count); + return do_syslog(SYSLOG_ACTION_READ, buf, count, SYSLOG_FROM_FILE); } static unsigned int kmsg_poll(struct file *file, poll_table *wait) { poll_wait(file, &log_wait, wait); - if (do_syslog(9, NULL, 0)) + if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_FILE)) return POLLIN | POLLRDNORM; return 0; } diff --git a/include/linux/security.h b/include/linux/security.h index 2c627d361c02..233d20b52c1b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -76,7 +76,7 @@ extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, extern int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp); extern int cap_task_setioprio(struct task_struct *p, int ioprio); extern int cap_task_setnice(struct task_struct *p, int nice); -extern int cap_syslog(int type); +extern int cap_syslog(int type, bool from_file); extern int cap_vm_enough_memory(struct mm_struct *mm, long pages); struct msghdr; @@ -95,6 +95,8 @@ struct seq_file; extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); extern int cap_netlink_recv(struct sk_buff *skb, int cap); +void reset_security_ops(void); + #ifdef CONFIG_MMU extern unsigned long mmap_min_addr; extern unsigned long dac_mmap_min_addr; @@ -985,6 +987,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Check permissions on incoming network packets. This hook is distinct * from Netfilter's IP input hooks since it is the first time that the * incoming sk_buff @skb has been associated with a particular socket, @sk. + * Must not sleep inside this hook because some callers hold spinlocks. * @sk contains the sock (not socket) associated with the incoming sk_buff. * @skb contains the incoming network data. * @socket_getpeersec_stream: @@ -1348,6 +1351,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * logging to the console. * See the syslog(2) manual page for an explanation of the @type values. * @type contains the type of action. + * @from_file indicates the context of action (if it came from /proc). * Return 0 if permission is granted. * @settime: * Check permission to change the system time. @@ -1462,7 +1466,7 @@ struct security_operations { int (*sysctl) (struct ctl_table *table, int op); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quota_on) (struct dentry *dentry); - int (*syslog) (int type); + int (*syslog) (int type, bool from_file); int (*settime) (struct timespec *ts, struct timezone *tz); int (*vm_enough_memory) (struct mm_struct *mm, long pages); @@ -1761,7 +1765,7 @@ int security_acct(struct file *file); int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); -int security_syslog(int type); +int security_syslog(int type, bool from_file); int security_settime(struct timespec *ts, struct timezone *tz); int security_vm_enough_memory(long pages); int security_vm_enough_memory_mm(struct mm_struct *mm, long pages); @@ -2007,9 +2011,9 @@ static inline int security_quota_on(struct dentry *dentry) return 0; } -static inline int security_syslog(int type) +static inline int security_syslog(int type, bool from_file) { - return cap_syslog(type); + return cap_syslog(type, from_file); } static inline int security_settime(struct timespec *ts, struct timezone *tz) diff --git a/include/linux/syslog.h b/include/linux/syslog.h new file mode 100644 index 000000000000..38911391a139 --- /dev/null +++ b/include/linux/syslog.h @@ -0,0 +1,52 @@ +/* Syslog internals + * + * Copyright 2010 Canonical, Ltd. + * Author: Kees Cook <kees.cook@canonical.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LINUX_SYSLOG_H +#define _LINUX_SYSLOG_H + +/* Close the log. Currently a NOP. */ +#define SYSLOG_ACTION_CLOSE 0 +/* Open the log. Currently a NOP. */ +#define SYSLOG_ACTION_OPEN 1 +/* Read from the log. */ +#define SYSLOG_ACTION_READ 2 +/* Read all messages remaining in the ring buffer. */ +#define SYSLOG_ACTION_READ_ALL 3 +/* Read and clear all messages remaining in the ring buffer */ +#define SYSLOG_ACTION_READ_CLEAR 4 +/* Clear ring buffer. */ +#define SYSLOG_ACTION_CLEAR 5 +/* Disable printk's to console */ +#define SYSLOG_ACTION_CONSOLE_OFF 6 +/* Enable printk's to console */ +#define SYSLOG_ACTION_CONSOLE_ON 7 +/* Set level of messages printed to console */ +#define SYSLOG_ACTION_CONSOLE_LEVEL 8 +/* Return number of unread characters in the log buffer */ +#define SYSLOG_ACTION_SIZE_UNREAD 9 +/* Return size of the log buffer */ +#define SYSLOG_ACTION_SIZE_BUFFER 10 + +#define SYSLOG_FROM_CALL 0 +#define SYSLOG_FROM_FILE 1 + +int do_syslog(int type, char __user *buf, int count, bool from_file); + +#endif /* _LINUX_SYSLOG_H */ diff --git a/kernel/capability.c b/kernel/capability.c index 7f876e60521f..9e4697e9b276 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -135,7 +135,7 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, if (pid && (pid != task_pid_vnr(current))) { struct task_struct *target; - read_lock(&tasklist_lock); + rcu_read_lock(); target = find_task_by_vpid(pid); if (!target) @@ -143,7 +143,7 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, else ret = security_capget(target, pEp, pIp, pPp); - read_unlock(&tasklist_lock); + rcu_read_unlock(); } else ret = security_capget(current, pEp, pIp, pPp); diff --git a/kernel/printk.c b/kernel/printk.c index 1751c456b71f..40674122ecf2 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -35,6 +35,7 @@ #include <linux/kexec.h> #include <linux/ratelimit.h> #include <linux/kmsg_dump.h> +#include <linux/syslog.h> #include <asm/uaccess.h> @@ -258,38 +259,23 @@ static inline void boot_delay_msec(void) } #endif -/* - * Commands to do_syslog: - * - * 0 -- Close the log. Currently a NOP. - * 1 -- Open the log. Currently a NOP. - * 2 -- Read from the log. - * 3 -- Read all messages remaining in the ring buffer. - * 4 -- Read and clear all messages remaining in the ring buffer - * 5 -- Clear ring buffer. - * 6 -- Disable printk's to console - * 7 -- Enable printk's to console - * 8 -- Set level of messages printed to console - * 9 -- Return number of unread characters in the log buffer - * 10 -- Return size of the log buffer - */ -int do_syslog(int type, char __user *buf, int len) +int do_syslog(int type, char __user *buf, int len, bool from_file) { unsigned i, j, limit, count; int do_clear = 0; char c; int error = 0; - error = security_syslog(type); + error = security_syslog(type, from_file); if (error) return error; switch (type) { - case 0: /* Close log */ + case SYSLOG_ACTION_CLOSE: /* Close log */ break; - case 1: /* Open log */ + case SYSLOG_ACTION_OPEN: /* Open log */ break; - case 2: /* Read from log */ + case SYSLOG_ACTION_READ: /* Read from log */ error = -EINVAL; if (!buf || len < 0) goto out; @@ -320,10 +306,12 @@ int do_syslog(int type, char __user *buf, int len) if (!error) error = i; break; - case 4: /* Read/clear last kernel messages */ + /* Read/clear last kernel messages */ + case SYSLOG_ACTION_READ_CLEAR: do_clear = 1; /* FALL THRU */ - case 3: /* Read last kernel messages */ + /* Read last kernel messages */ + case SYSLOG_ACTION_READ_ALL: error = -EINVAL; if (!buf || len < 0) goto out; @@ -376,21 +364,25 @@ int do_syslog(int type, char __user *buf, int len) } } break; - case 5: /* Clear ring buffer */ + /* Clear ring buffer */ + case SYSLOG_ACTION_CLEAR: logged_chars = 0; break; - case 6: /* Disable logging to console */ + /* Disable logging to console */ + case SYSLOG_ACTION_CONSOLE_OFF: if (saved_console_loglevel == -1) saved_console_loglevel = console_loglevel; console_loglevel = minimum_console_loglevel; break; - case 7: /* Enable logging to console */ + /* Enable logging to console */ + case SYSLOG_ACTION_CONSOLE_ON: if (saved_console_loglevel != -1) { console_loglevel = saved_console_loglevel; saved_console_loglevel = -1; } break; - case 8: /* Set level of messages printed to console */ + /* Set level of messages printed to console */ + case SYSLOG_ACTION_CONSOLE_LEVEL: error = -EINVAL; if (len < 1 || len > 8) goto out; @@ -401,10 +393,12 @@ int do_syslog(int type, char __user *buf, int len) saved_console_loglevel = -1; error = 0; break; - case 9: /* Number of chars in the log buffer */ + /* Number of chars in the log buffer */ + case SYSLOG_ACTION_SIZE_UNREAD: error = log_end - log_start; break; - case 10: /* Size of the log buffer */ + /* Size of the log buffer */ + case SYSLOG_ACTION_SIZE_BUFFER: error = log_buf_len; break; default: @@ -417,7 +411,7 @@ out: SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len) { - return do_syslog(type, buf, len); + return do_syslog(type, buf, len, SYSLOG_FROM_CALL); } /* diff --git a/security/capability.c b/security/capability.c index 5c700e1a4fd3..4875142b858d 100644 --- a/security/capability.c +++ b/security/capability.c @@ -906,10 +906,6 @@ static void cap_audit_rule_free(void *lsmrule) } #endif /* CONFIG_AUDIT */ -struct security_operations default_security_ops = { - .name = "default", -}; - #define set_to_cap_if_null(ops, function) \ do { \ if (!ops->function) { \ diff --git a/security/commoncap.c b/security/commoncap.c index f800fdb3de94..61669730da98 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -27,6 +27,7 @@ #include <linux/sched.h> #include <linux/prctl.h> #include <linux/securebits.h> +#include <linux/syslog.h> /* * If a non-root user executes a setuid-root binary in @@ -888,13 +889,17 @@ error: /** * cap_syslog - Determine whether syslog function is permitted * @type: Function requested + * @from_file: Whether this request came from an open file (i.e. /proc) * * Determine whether the current process is permitted to use a particular * syslog function, returning 0 if permission is granted, -ve if not. */ -int cap_syslog(int type) +int cap_syslog(int type, bool from_file) { - if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN)) + if (type != SYSLOG_ACTION_OPEN && from_file) + return 0; + if ((type != SYSLOG_ACTION_READ_ALL && + type != SYSLOG_ACTION_SIZE_BUFFER) && !capable(CAP_SYS_ADMIN)) return -EPERM; return 0; } diff --git a/security/security.c b/security/security.c index 122b748d0f4c..687c6fd14bb6 100644 --- a/security/security.c +++ b/security/security.c @@ -23,10 +23,12 @@ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_DEFAULT_SECURITY; /* things that live in capability.c */ -extern struct security_operations default_security_ops; extern void security_fixup_ops(struct security_operations *ops); -struct security_operations *security_ops; /* Initialized to NULL */ +static struct security_operations *security_ops; +static struct security_operations default_security_ops = { + .name = "default", +}; static inline int verify(struct security_operations *ops) { @@ -63,6 +65,11 @@ int __init security_init(void) return 0; } +void reset_security_ops(void) +{ + security_ops = &default_security_ops; +} + /* Save user chosen LSM */ static int __init choose_lsm(char *str) { @@ -203,9 +210,9 @@ int security_quota_on(struct dentry *dentry) return security_ops->quota_on(dentry); } -int security_syslog(int type) +int security_syslog(int type, bool from_file) { - return security_ops->syslog(type); + return security_ops->syslog(type, from_file); } int security_settime(struct timespec *ts, struct timezone *tz) @@ -389,42 +396,42 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, EXPORT_SYMBOL(security_inode_init_security); #ifdef CONFIG_SECURITY_PATH -int security_path_mknod(struct path *path, struct dentry *dentry, int mode, +int security_path_mknod(struct path *dir, struct dentry *dentry, int mode, unsigned int dev) { - if (unlikely(IS_PRIVATE(path->dentry->d_inode))) + if (unlikely(IS_PRIVATE(dir->dentry->d_inode))) return 0; - return security_ops->path_mknod(path, dentry, mode, dev); + return security_ops->path_mknod(dir, dentry, mode, dev); } EXPORT_SYMBOL(security_path_mknod); -int security_path_mkdir(struct path *path, struct dentry *dentry, int mode) +int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode) { - if (unlikely(IS_PRIVATE(path->dentry->d_inode))) + if (unlikely(IS_PRIVATE(dir->dentry->d_inode))) return 0; - return security_ops->path_mkdir(path, dentry, mode); + return security_ops->path_mkdir(dir, dentry, mode); } -int security_path_rmdir(struct path *path, struct dentry *dentry) +int security_path_rmdir(struct path *dir, struct dentry *dentry) { - if (unlikely(IS_PRIVATE(path->dentry->d_inode))) + if (unlikely(IS_PRIVATE(dir->dentry->d_inode))) return 0; - return security_ops->path_rmdir(path, dentry); + return security_ops->path_rmdir(dir, dentry); } -int security_path_unlink(struct path *path, struct dentry *dentry) +int security_path_unlink(struct path *dir, struct dentry *dentry) { - if (unlikely(IS_PRIVATE(path->dentry->d_inode))) + if (unlikely(IS_PRIVATE(dir->dentry->d_inode))) return 0; - return security_ops->path_unlink(path, dentry); + return security_ops->path_unlink(dir, dentry); } -int security_path_symlink(struct path *path, struct dentry *dentry, +int security_path_symlink(struct path *dir, struct dentry *dentry, const char *old_name) { - if (unlikely(IS_PRIVATE(path->dentry->d_inode))) + if (unlikely(IS_PRIVATE(dir->dentry->d_inode))) return 0; - return security_ops->path_symlink(path, dentry, old_name); + return security_ops->path_symlink(dir, dentry, old_name); } int security_path_link(struct dentry *old_dentry, struct path *new_dir, @@ -630,14 +637,14 @@ int security_inode_killpriv(struct dentry *dentry) int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) { if (unlikely(IS_PRIVATE(inode))) - return 0; + return -EOPNOTSUPP; return security_ops->inode_getsecurity(inode, name, buffer, alloc); } int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { if (unlikely(IS_PRIVATE(inode))) - return 0; + return -EOPNOTSUPP; return security_ops->inode_setsecurity(inode, name, value, size, flags); } diff --git a/security/selinux/avc.c b/security/selinux/avc.c index f2dde268165a..db0fd9f33499 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -489,17 +489,14 @@ void avc_audit(u32 ssid, u32 tsid, struct common_audit_data stack_data; u32 denied, audited; denied = requested & ~avd->allowed; - if (denied) { - audited = denied; - if (!(audited & avd->auditdeny)) - return; - } else if (result) { + if (denied) + audited = denied & avd->auditdeny; + else if (result) audited = denied = requested; - } else { - audited = requested; - if (!(audited & avd->auditallow)) - return; - } + else + audited = requested & avd->auditallow; + if (!audited) + return; if (!a) { a = &stack_data; memset(a, 0, sizeof(*a)); @@ -746,9 +743,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, else avd = &avd_entry; - rc = security_compute_av(ssid, tsid, tclass, requested, avd); - if (rc) - goto out; + security_compute_av(ssid, tsid, tclass, avd); rcu_read_lock(); node = avc_insert(ssid, tsid, tclass, avd); } else { @@ -770,7 +765,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, } rcu_read_unlock(); -out: return rc; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9a2ee845e9d4..5feecb41009d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -76,6 +76,7 @@ #include <linux/selinux.h> #include <linux/mutex.h> #include <linux/posix-timers.h> +#include <linux/syslog.h> #include "avc.h" #include "objsec.h" @@ -125,13 +126,6 @@ __setup("selinux=", selinux_enabled_setup); int selinux_enabled = 1; #endif - -/* - * Minimal support for a secondary security module, - * just to allow the use of the capability module. - */ -static struct security_operations *secondary_ops; - /* Lists of inode and superblock security structures initialized before the policy was loaded. */ static LIST_HEAD(superblock_security_head); @@ -2049,29 +2043,30 @@ static int selinux_quota_on(struct dentry *dentry) return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON); } -static int selinux_syslog(int type) +static int selinux_syslog(int type, bool from_file) { int rc; - rc = cap_syslog(type); + rc = cap_syslog(type, from_file); if (rc) return rc; switch (type) { - case 3: /* Read last kernel messages */ - case 10: /* Return size of the log buffer */ + case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ + case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ rc = task_has_system(current, SYSTEM__SYSLOG_READ); break; - case 6: /* Disable logging to console */ - case 7: /* Enable logging to console */ - case 8: /* Set level of messages printed to console */ + case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ + case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ + /* Set level of messages printed to console */ + case SYSLOG_ACTION_CONSOLE_LEVEL: rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE); break; - case 0: /* Close log */ - case 1: /* Open log */ - case 2: /* Read from log */ - case 4: /* Read/clear last kernel messages */ - case 5: /* Clear ring buffer */ + case SYSLOG_ACTION_CLOSE: /* Close log */ + case SYSLOG_ACTION_OPEN: /* Open log */ + case SYSLOG_ACTION_READ: /* Read from log */ + case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */ + case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */ default: rc = task_has_system(current, SYSTEM__SYSLOG_MOD); break; @@ -3334,7 +3329,7 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) if (ret == 0) tsec->create_sid = isec->sid; - return 0; + return ret; } static int selinux_kernel_module_request(char *kmod_name) @@ -5672,9 +5667,6 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); - secondary_ops = security_ops; - if (!secondary_ops) - panic("SELinux: No initial security operations\n"); if (register_security(&selinux_ops)) panic("SELinux: Unable to register with kernel.\n"); @@ -5835,8 +5827,7 @@ int selinux_disable(void) selinux_disabled = 1; selinux_enabled = 0; - /* Reset security_ops to the secondary module, dummy or capability. */ - security_ops = secondary_ops; + reset_security_ops(); /* Try to destroy the avc node cache */ avc_disable(); diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 2553266ad793..1f7c2491d3dc 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -57,7 +57,6 @@ struct netlbl_lsm_secattr; extern int selinux_enabled; -extern int selinux_mls_enabled; /* Policy capabilities */ enum { @@ -80,6 +79,8 @@ extern int selinux_policycap_openperm; /* limitation of boundary depth */ #define POLICYDB_BOUNDS_MAXDEPTH 4 +int security_mls_enabled(void); + int security_load_policy(void *data, size_t len); int security_policycap_supported(unsigned int req_cap); @@ -96,13 +97,11 @@ struct av_decision { /* definitions of av_decision.flags */ #define AVD_FLAGS_PERMISSIVE 0x0001 -int security_compute_av(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd); +void security_compute_av(u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd); -int security_compute_av_user(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd); +void security_compute_av_user(u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd); int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index fab36fdf2769..cd191bbec03c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -282,7 +282,8 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf, char tmpbuf[TMPBUFLEN]; ssize_t length; - length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled); + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", + security_mls_enabled()); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -494,7 +495,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) char *scon, *tcon; u32 ssid, tsid; u16 tclass; - u32 req; struct av_decision avd; ssize_t length; @@ -512,7 +512,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) goto out; length = -EINVAL; - if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) + if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out2; length = security_context_to_sid(scon, strlen(scon)+1, &ssid); @@ -522,9 +522,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) if (length < 0) goto out2; - length = security_compute_av_user(ssid, tsid, tclass, req, &avd); - if (length < 0) - goto out2; + security_compute_av_user(ssid, tsid, tclass, &avd); length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%x %x %x %x %u %x", @@ -979,6 +977,8 @@ static int sel_make_bools(void) u32 sid; /* remove any existing files */ + for (i = 0; i < bool_num; i++) + kfree(bool_pending_names[i]); kfree(bool_pending_names); kfree(bool_pending_values); bool_pending_names = NULL; diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index d9dd7a2f6a8a..45e8fb0515f8 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -41,9 +41,6 @@ static inline int mls_context_cpy(struct context *dst, struct context *src) { int rc; - if (!selinux_mls_enabled) - return 0; - dst->range.level[0].sens = src->range.level[0].sens; rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); if (rc) @@ -64,9 +61,6 @@ static inline int mls_context_cpy_low(struct context *dst, struct context *src) { int rc; - if (!selinux_mls_enabled) - return 0; - dst->range.level[0].sens = src->range.level[0].sens; rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); if (rc) @@ -82,9 +76,6 @@ out: static inline int mls_context_cmp(struct context *c1, struct context *c2) { - if (!selinux_mls_enabled) - return 1; - return ((c1->range.level[0].sens == c2->range.level[0].sens) && ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) && (c1->range.level[1].sens == c2->range.level[1].sens) && @@ -93,9 +84,6 @@ static inline int mls_context_cmp(struct context *c1, struct context *c2) static inline void mls_context_destroy(struct context *c) { - if (!selinux_mls_enabled) - return; - ebitmap_destroy(&c->range.level[0].cat); ebitmap_destroy(&c->range.level[1].cat); mls_context_init(c); diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 3f2b2706b5bb..372b773f8210 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -39,7 +39,7 @@ int mls_compute_context_len(struct context *context) struct ebitmap *e; struct ebitmap_node *node; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; len = 1; /* for the beginning ":" */ @@ -93,7 +93,7 @@ void mls_sid_to_context(struct context *context, struct ebitmap *e; struct ebitmap_node *node; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return; scontextp = *scontext; @@ -200,7 +200,7 @@ int mls_context_isvalid(struct policydb *p, struct context *c) { struct user_datum *usrdatum; - if (!selinux_mls_enabled) + if (!p->mls_enabled) return 1; if (!mls_range_isvalid(p, &c->range)) @@ -253,7 +253,7 @@ int mls_context_to_sid(struct policydb *pol, struct cat_datum *catdatum, *rngdatum; int l, rc = -EINVAL; - if (!selinux_mls_enabled) { + if (!pol->mls_enabled) { if (def_sid != SECSID_NULL && oldc) *scontext += strlen(*scontext)+1; return 0; @@ -387,7 +387,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask) char *tmpstr, *freestr; int rc; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return -EINVAL; /* we need freestr because mls_context_to_sid will change @@ -407,7 +407,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask) /* * Copies the MLS range `range' into `context'. */ -static inline int mls_range_set(struct context *context, +int mls_range_set(struct context *context, struct mls_range *range) { int l, rc = 0; @@ -427,7 +427,7 @@ static inline int mls_range_set(struct context *context, int mls_setup_user_range(struct context *fromcon, struct user_datum *user, struct context *usercon) { - if (selinux_mls_enabled) { + if (policydb.mls_enabled) { struct mls_level *fromcon_sen = &(fromcon->range.level[0]); struct mls_level *fromcon_clr = &(fromcon->range.level[1]); struct mls_level *user_low = &(user->range.level[0]); @@ -477,7 +477,7 @@ int mls_convert_context(struct policydb *oldp, struct ebitmap_node *node; int l, i; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; for (l = 0; l < 2; l++) { @@ -513,23 +513,21 @@ int mls_compute_sid(struct context *scontext, u32 specified, struct context *newcontext) { - struct range_trans *rtr; + struct range_trans rtr; + struct mls_range *r; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; switch (specified) { case AVTAB_TRANSITION: /* Look for a range transition rule. */ - for (rtr = policydb.range_tr; rtr; rtr = rtr->next) { - if (rtr->source_type == scontext->type && - rtr->target_type == tcontext->type && - rtr->target_class == tclass) { - /* Set the range from the rule */ - return mls_range_set(newcontext, - &rtr->target_range); - } - } + rtr.source_type = scontext->type; + rtr.target_type = tcontext->type; + rtr.target_class = tclass; + r = hashtab_search(policydb.range_tr, &rtr); + if (r) + return mls_range_set(newcontext, r); /* Fallthrough */ case AVTAB_CHANGE: if (tclass == policydb.process_class) @@ -541,8 +539,8 @@ int mls_compute_sid(struct context *scontext, case AVTAB_MEMBER: /* Use the process effective MLS attributes. */ return mls_context_cpy_low(newcontext, scontext); - default: - return -EINVAL; + + /* fall through */ } return -EINVAL; } @@ -561,7 +559,7 @@ int mls_compute_sid(struct context *scontext, void mls_export_netlbl_lvl(struct context *context, struct netlbl_lsm_secattr *secattr) { - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return; secattr->attr.mls.lvl = context->range.level[0].sens - 1; @@ -581,7 +579,7 @@ void mls_export_netlbl_lvl(struct context *context, void mls_import_netlbl_lvl(struct context *context, struct netlbl_lsm_secattr *secattr) { - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return; context->range.level[0].sens = secattr->attr.mls.lvl + 1; @@ -603,7 +601,7 @@ int mls_export_netlbl_cat(struct context *context, { int rc; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; rc = ebitmap_netlbl_export(&context->range.level[0].cat, @@ -631,7 +629,7 @@ int mls_import_netlbl_cat(struct context *context, { int rc; - if (!selinux_mls_enabled) + if (!policydb.mls_enabled) return 0; rc = ebitmap_netlbl_import(&context->range.level[0].cat, diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 1276715aaa8b..cd9152632e54 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -39,6 +39,8 @@ int mls_context_to_sid(struct policydb *p, int mls_from_string(char *str, struct context *context, gfp_t gfp_mask); +int mls_range_set(struct context *context, struct mls_range *range); + int mls_convert_context(struct policydb *oldp, struct policydb *newp, struct context *context); diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index b6e943a21061..03bed52a8052 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -15,6 +15,7 @@ #define _SS_MLS_TYPES_H_ #include "security.h" +#include "ebitmap.h" struct mls_level { u32 sens; /* sensitivity */ @@ -27,18 +28,12 @@ struct mls_range { static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2) { - if (!selinux_mls_enabled) - return 1; - return ((l1->sens == l2->sens) && ebitmap_cmp(&l1->cat, &l2->cat)); } static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2) { - if (!selinux_mls_enabled) - return 1; - return ((l1->sens >= l2->sens) && ebitmap_contains(&l1->cat, &l2->cat)); } diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index f03667213ea8..23c6e53c102c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -52,8 +52,6 @@ static char *symtab_name[SYM_NUM] = { }; #endif -int selinux_mls_enabled; - static unsigned int symtab_sizes[SYM_NUM] = { 2, 32, @@ -177,6 +175,21 @@ out_free_role: goto out; } +static u32 rangetr_hash(struct hashtab *h, const void *k) +{ + const struct range_trans *key = k; + return (key->source_type + (key->target_type << 3) + + (key->target_class << 5)) & (h->size - 1); +} + +static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct range_trans *key1 = k1, *key2 = k2; + return (key1->source_type != key2->source_type || + key1->target_type != key2->target_type || + key1->target_class != key2->target_class); +} + /* * Initialize a policy database structure. */ @@ -204,6 +217,10 @@ static int policydb_init(struct policydb *p) if (rc) goto out_free_symtab; + p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); + if (!p->range_tr) + goto out_free_symtab; + ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); @@ -408,6 +425,20 @@ static void symtab_hash_eval(struct symtab *s) info.slots_used, h->size, info.max_chain_len); } } + +static void rangetr_hash_eval(struct hashtab *h) +{ + struct hashtab_info info; + + hashtab_stat(h, &info); + printk(KERN_DEBUG "SELinux: rangetr: %d entries and %d/%d buckets used, " + "longest chain length %d\n", h->nel, + info.slots_used, h->size, info.max_chain_len); +} +#else +static inline void rangetr_hash_eval(struct hashtab *h) +{ +} #endif /* @@ -422,7 +453,7 @@ static int policydb_index_others(struct policydb *p) printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools", p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); - if (selinux_mls_enabled) + if (p->mls_enabled) printk(", %d sens, %d cats", p->p_levels.nprim, p->p_cats.nprim); printk("\n"); @@ -612,6 +643,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_destroy, }; +static int range_tr_destroy(void *key, void *datum, void *p) +{ + struct mls_range *rt = datum; + kfree(key); + ebitmap_destroy(&rt->level[0].cat); + ebitmap_destroy(&rt->level[1].cat); + kfree(datum); + cond_resched(); + return 0; +} + static void ocontext_destroy(struct ocontext *c, int i) { context_destroy(&c->context[0]); @@ -632,7 +674,6 @@ void policydb_destroy(struct policydb *p) int i; struct role_allow *ra, *lra = NULL; struct role_trans *tr, *ltr = NULL; - struct range_trans *rt, *lrt = NULL; for (i = 0; i < SYM_NUM; i++) { cond_resched(); @@ -693,20 +734,8 @@ void policydb_destroy(struct policydb *p) } kfree(lra); - for (rt = p->range_tr; rt; rt = rt->next) { - cond_resched(); - if (lrt) { - ebitmap_destroy(&lrt->target_range.level[0].cat); - ebitmap_destroy(&lrt->target_range.level[1].cat); - kfree(lrt); - } - lrt = rt; - } - if (lrt) { - ebitmap_destroy(&lrt->target_range.level[0].cat); - ebitmap_destroy(&lrt->target_range.level[1].cat); - kfree(lrt); - } + hashtab_map(p->range_tr, range_tr_destroy, NULL); + hashtab_destroy(p->range_tr); if (p->type_attr_map) { for (i = 0; i < p->p_types.nprim; i++) @@ -1686,12 +1715,11 @@ int policydb_read(struct policydb *p, void *fp) int i, j, rc; __le32 buf[4]; u32 nodebuf[8]; - u32 len, len2, config, nprim, nel, nel2; + u32 len, len2, nprim, nel, nel2; char *policydb_str; struct policydb_compat_info *info; - struct range_trans *rt, *lrt; - - config = 0; + struct range_trans *rt; + struct mls_range *r; rc = policydb_init(p); if (rc) @@ -1740,7 +1768,7 @@ int policydb_read(struct policydb *p, void *fp) kfree(policydb_str); policydb_str = NULL; - /* Read the version, config, and table sizes. */ + /* Read the version and table sizes. */ rc = next_entry(buf, fp, sizeof(u32)*4); if (rc < 0) goto bad; @@ -1755,13 +1783,7 @@ int policydb_read(struct policydb *p, void *fp) } if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) { - if (ss_initialized && !selinux_mls_enabled) { - printk(KERN_ERR "SELinux: Cannot switch between non-MLS" - " and MLS policies\n"); - goto bad; - } - selinux_mls_enabled = 1; - config |= POLICYDB_CONFIG_MLS; + p->mls_enabled = 1; if (p->policyvers < POLICYDB_VERSION_MLS) { printk(KERN_ERR "SELinux: security policydb version %d " @@ -1769,12 +1791,6 @@ int policydb_read(struct policydb *p, void *fp) p->policyvers); goto bad; } - } else { - if (ss_initialized && selinux_mls_enabled) { - printk(KERN_ERR "SELinux: Cannot switch between MLS and" - " non-MLS policies\n"); - goto bad; - } } p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN); p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN); @@ -2122,44 +2138,61 @@ int policydb_read(struct policydb *p, void *fp) if (rc < 0) goto bad; nel = le32_to_cpu(buf[0]); - lrt = NULL; for (i = 0; i < nel; i++) { rt = kzalloc(sizeof(*rt), GFP_KERNEL); if (!rt) { rc = -ENOMEM; goto bad; } - if (lrt) - lrt->next = rt; - else - p->range_tr = rt; rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) + if (rc < 0) { + kfree(rt); goto bad; + } rt->source_type = le32_to_cpu(buf[0]); rt->target_type = le32_to_cpu(buf[1]); if (new_rangetr) { rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + if (rc < 0) { + kfree(rt); goto bad; + } rt->target_class = le32_to_cpu(buf[0]); } else rt->target_class = p->process_class; if (!policydb_type_isvalid(p, rt->source_type) || !policydb_type_isvalid(p, rt->target_type) || !policydb_class_isvalid(p, rt->target_class)) { + kfree(rt); rc = -EINVAL; goto bad; } - rc = mls_read_range_helper(&rt->target_range, fp); - if (rc) + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + kfree(rt); + rc = -ENOMEM; goto bad; - if (!mls_range_isvalid(p, &rt->target_range)) { + } + rc = mls_read_range_helper(r, fp); + if (rc) { + kfree(rt); + kfree(r); + goto bad; + } + if (!mls_range_isvalid(p, r)) { printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); + kfree(rt); + kfree(r); + goto bad; + } + rc = hashtab_insert(p->range_tr, rt, r); + if (rc) { + kfree(rt); + kfree(r); goto bad; } - lrt = rt; } + rangetr_hash_eval(p->range_tr); } p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL); diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index cdcc5700946f..26d9adf8542b 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -27,6 +27,8 @@ #include "symtab.h" #include "avtab.h" #include "sidtab.h" +#include "ebitmap.h" +#include "mls_types.h" #include "context.h" #include "constraint.h" @@ -113,8 +115,6 @@ struct range_trans { u32 source_type; u32 target_type; u32 target_class; - struct mls_range target_range; - struct range_trans *next; }; /* Boolean data type */ @@ -187,6 +187,8 @@ struct genfs { /* The policy database */ struct policydb { + int mls_enabled; + /* symbol tables */ struct symtab symtab[SYM_NUM]; #define p_commons symtab[SYM_COMMONS] @@ -240,8 +242,8 @@ struct policydb { fixed labeling behavior. */ struct genfs *genfs; - /* range transitions */ - struct range_trans *range_tr; + /* range transitions table (range_trans_key -> mls_range) */ + struct hashtab *range_tr; /* type -> attribute reverse mapping */ struct ebitmap *type_attr_map; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b3efae204ac7..cf27b3ee1a95 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -26,6 +26,10 @@ * * Added support for bounds domain and audit messaged on masked permissions * + * Updated: Guido Trentalancia <guido@trentalancia.com> + * + * Added support for runtime switching of the policy type + * * Copyright (C) 2008, 2009 NEC Corporation * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. @@ -87,11 +91,10 @@ static u32 latest_granting; static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); -static int context_struct_compute_av(struct context *scontext, - struct context *tcontext, - u16 tclass, - u32 requested, - struct av_decision *avd); +static void context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd); struct selinux_mapping { u16 value; /* policy value */ @@ -196,23 +199,6 @@ static u16 unmap_class(u16 tclass) return tclass; } -static u32 unmap_perm(u16 tclass, u32 tperm) -{ - if (tclass < current_mapping_size) { - unsigned i; - u32 kperm = 0; - - for (i = 0; i < current_mapping[tclass].num_perms; i++) - if (tperm & (1<<i)) { - kperm |= current_mapping[tclass].perms[i]; - tperm &= ~(1<<i); - } - return kperm; - } - - return tperm; -} - static void map_decision(u16 tclass, struct av_decision *avd, int allow_unknown) { @@ -250,6 +236,10 @@ static void map_decision(u16 tclass, struct av_decision *avd, } } +int security_mls_enabled(void) +{ + return policydb.mls_enabled; +} /* * Return the boolean value of a constraint expression @@ -465,7 +455,8 @@ static void security_dump_masked_av(struct context *scontext, char *scontext_name = NULL; char *tcontext_name = NULL; char *permission_names[32]; - int index, length; + int index; + u32 length; bool need_comma = false; if (!permissions) @@ -532,7 +523,6 @@ out: static void type_attribute_bounds_av(struct context *scontext, struct context *tcontext, u16 tclass, - u32 requested, struct av_decision *avd) { struct context lo_scontext; @@ -553,7 +543,6 @@ static void type_attribute_bounds_av(struct context *scontext, context_struct_compute_av(&lo_scontext, tcontext, tclass, - requested, &lo_avd); if ((lo_avd.allowed & avd->allowed) == avd->allowed) return; /* no masked permission */ @@ -569,7 +558,6 @@ static void type_attribute_bounds_av(struct context *scontext, context_struct_compute_av(scontext, &lo_tcontext, tclass, - requested, &lo_avd); if ((lo_avd.allowed & avd->allowed) == avd->allowed) return; /* no masked permission */ @@ -586,7 +574,6 @@ static void type_attribute_bounds_av(struct context *scontext, context_struct_compute_av(&lo_scontext, &lo_tcontext, tclass, - requested, &lo_avd); if ((lo_avd.allowed & avd->allowed) == avd->allowed) return; /* no masked permission */ @@ -607,11 +594,10 @@ static void type_attribute_bounds_av(struct context *scontext, * Compute access vectors based on a context structure pair for * the permissions in a particular class. */ -static int context_struct_compute_av(struct context *scontext, - struct context *tcontext, - u16 tclass, - u32 requested, - struct av_decision *avd) +static void context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd) { struct constraint_node *constraint; struct role_allow *ra; @@ -622,19 +608,14 @@ static int context_struct_compute_av(struct context *scontext, struct ebitmap_node *snode, *tnode; unsigned int i, j; - /* - * Initialize the access vectors to the default values. - */ avd->allowed = 0; avd->auditallow = 0; avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; - avd->flags = 0; if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { if (printk_ratelimit()) printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); - return -EINVAL; + return; } tclass_datum = policydb.class_val_to_struct[tclass - 1]; @@ -705,9 +686,7 @@ static int context_struct_compute_av(struct context *scontext, * permission and notice it to userspace via audit. */ type_attribute_bounds_av(scontext, tcontext, - tclass, requested, avd); - - return 0; + tclass, avd); } static int security_validtrans_handle_fail(struct context *ocontext, @@ -864,7 +843,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) if (rc) { char *old_name = NULL; char *new_name = NULL; - int length; + u32 length; if (!context_struct_to_string(old_context, &old_name, &length) && @@ -886,110 +865,116 @@ out: return rc; } - -static int security_compute_av_core(u32 ssid, - u32 tsid, - u16 tclass, - u32 requested, - struct av_decision *avd) +static void avd_init(struct av_decision *avd) { - struct context *scontext = NULL, *tcontext = NULL; - int rc = 0; - - scontext = sidtab_search(&sidtab, ssid); - if (!scontext) { - printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", - __func__, ssid); - return -EINVAL; - } - tcontext = sidtab_search(&sidtab, tsid); - if (!tcontext) { - printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", - __func__, tsid); - return -EINVAL; - } - - rc = context_struct_compute_av(scontext, tcontext, tclass, - requested, avd); - - /* permissive domain? */ - if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) - avd->flags |= AVD_FLAGS_PERMISSIVE; - - return rc; + avd->allowed = 0; + avd->auditallow = 0; + avd->auditdeny = 0xffffffff; + avd->seqno = latest_granting; + avd->flags = 0; } + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class - * @requested: requested permissions * @avd: access vector decisions * * Compute a set of access vector decisions based on the * SID pair (@ssid, @tsid) for the permissions in @tclass. - * Return -%EINVAL if any of the parameters are invalid or %0 - * if the access vector decisions were computed successfully. */ -int security_compute_av(u32 ssid, - u32 tsid, - u16 orig_tclass, - u32 orig_requested, - struct av_decision *avd) +void security_compute_av(u32 ssid, + u32 tsid, + u16 orig_tclass, + struct av_decision *avd) { u16 tclass; - u32 requested; - int rc; + struct context *scontext = NULL, *tcontext = NULL; read_lock(&policy_rwlock); - + avd_init(avd); if (!ss_initialized) goto allow; - requested = unmap_perm(orig_tclass, orig_requested); + scontext = sidtab_search(&sidtab, ssid); + if (!scontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); + goto out; + } + + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + + tcontext = sidtab_search(&sidtab, tsid); + if (!tcontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); + goto out; + } + tclass = unmap_class(orig_tclass); if (unlikely(orig_tclass && !tclass)) { if (policydb.allow_unknown) goto allow; - rc = -EINVAL; goto out; } - rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); + context_struct_compute_av(scontext, tcontext, tclass, avd); map_decision(orig_tclass, avd, policydb.allow_unknown); out: read_unlock(&policy_rwlock); - return rc; + return; allow: avd->allowed = 0xffffffff; - avd->auditallow = 0; - avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; - avd->flags = 0; - rc = 0; goto out; } -int security_compute_av_user(u32 ssid, - u32 tsid, - u16 tclass, - u32 requested, - struct av_decision *avd) +void security_compute_av_user(u32 ssid, + u32 tsid, + u16 tclass, + struct av_decision *avd) { - int rc; + struct context *scontext = NULL, *tcontext = NULL; - if (!ss_initialized) { - avd->allowed = 0xffffffff; - avd->auditallow = 0; - avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; - return 0; + read_lock(&policy_rwlock); + avd_init(avd); + if (!ss_initialized) + goto allow; + + scontext = sidtab_search(&sidtab, ssid); + if (!scontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); + goto out; } - read_lock(&policy_rwlock); - rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + + tcontext = sidtab_search(&sidtab, tsid); + if (!tcontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); + goto out; + } + + if (unlikely(!tclass)) { + if (policydb.allow_unknown) + goto allow; + goto out; + } + + context_struct_compute_av(scontext, tcontext, tclass, avd); + out: read_unlock(&policy_rwlock); - return rc; + return; +allow: + avd->allowed = 0xffffffff; + goto out; } /* @@ -1565,7 +1550,10 @@ static int clone_sid(u32 sid, { struct sidtab *s = arg; - return sidtab_insert(s, sid, context); + if (sid > SECINITSID_NUM) + return sidtab_insert(s, sid, context); + else + return 0; } static inline int convert_context_handle_invalid_context(struct context *context) @@ -1606,12 +1594,17 @@ static int convert_context(u32 key, { struct convert_context_args *args; struct context oldc; + struct ocontext *oc; + struct mls_range *range; struct role_datum *role; struct type_datum *typdatum; struct user_datum *usrdatum; char *s; u32 len; - int rc; + int rc = 0; + + if (key <= SECINITSID_NUM) + goto out; args = p; @@ -1673,9 +1666,39 @@ static int convert_context(u32 key, goto bad; c->type = typdatum->value; - rc = mls_convert_context(args->oldp, args->newp, c); - if (rc) - goto bad; + /* Convert the MLS fields if dealing with MLS policies */ + if (args->oldp->mls_enabled && args->newp->mls_enabled) { + rc = mls_convert_context(args->oldp, args->newp, c); + if (rc) + goto bad; + } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) { + /* + * Switching between MLS and non-MLS policy: + * free any storage used by the MLS fields in the + * context for all existing entries in the sidtab. + */ + mls_context_destroy(c); + } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { + /* + * Switching between non-MLS and MLS policy: + * ensure that the MLS fields of the context for all + * existing entries in the sidtab are filled in with a + * suitable default value, likely taken from one of the + * initial SIDs. + */ + oc = args->newp->ocontexts[OCON_ISID]; + while (oc && oc->sid[0] != SECINITSID_UNLABELED) + oc = oc->next; + if (!oc) { + printk(KERN_ERR "SELinux: unable to look up" + " the initial SIDs list\n"); + goto bad; + } + range = &oc->context[0].range; + rc = mls_range_set(c, range); + if (rc) + goto bad; + } /* Check the validity of the new context. */ if (!policydb_context_isvalid(args->newp, c)) { @@ -1771,9 +1794,17 @@ int security_load_policy(void *data, size_t len) if (policydb_read(&newpolicydb, fp)) return -EINVAL; - if (sidtab_init(&newsidtab)) { + /* If switching between different policy types, log MLS status */ + if (policydb.mls_enabled && !newpolicydb.mls_enabled) + printk(KERN_INFO "SELinux: Disabling MLS support...\n"); + else if (!policydb.mls_enabled && newpolicydb.mls_enabled) + printk(KERN_INFO "SELinux: Enabling MLS support...\n"); + + rc = policydb_load_isids(&newpolicydb, &newsidtab); + if (rc) { + printk(KERN_ERR "SELinux: unable to load the initial SIDs\n"); policydb_destroy(&newpolicydb); - return -ENOMEM; + return rc; } if (selinux_set_mapping(&newpolicydb, secclass_map, @@ -1800,8 +1831,12 @@ int security_load_policy(void *data, size_t len) args.oldp = &policydb; args.newp = &newpolicydb; rc = sidtab_map(&newsidtab, convert_context, &args); - if (rc) + if (rc) { + printk(KERN_ERR "SELinux: unable to convert the internal" + " representation of contexts in the new SID" + " table\n"); goto err; + } /* Save the old policydb and SID table to free later. */ memcpy(&oldpolicydb, &policydb, sizeof policydb); @@ -2397,7 +2432,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) u32 len; int rc = 0; - if (!ss_initialized || !selinux_mls_enabled) { + if (!ss_initialized || !policydb.mls_enabled) { *new_sid = sid; goto out; } @@ -2498,7 +2533,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, /* we don't need to check ss_initialized here since the only way both * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the * security server was initialized and ss_initialized was true */ - if (!selinux_mls_enabled) { + if (!policydb.mls_enabled) { *peer_sid = SECSID_NULL; return 0; } @@ -2555,7 +2590,7 @@ int security_get_classes(char ***classes, int *nclasses) read_lock(&policy_rwlock); *nclasses = policydb.p_classes.nprim; - *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC); + *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC); if (!*classes) goto out; @@ -2602,7 +2637,7 @@ int security_get_permissions(char *class, char ***perms, int *nperms) } *nperms = match->permissions.nprim; - *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC); + *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC); if (!*perms) goto out; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 529c9ca65878..a5721b373f53 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -157,12 +157,12 @@ static int smack_ptrace_traceme(struct task_struct *ptp) * * Returns 0 on success, error code otherwise. */ -static int smack_syslog(int type) +static int smack_syslog(int type, bool from_file) { int rc; char *sp = current_security(); - rc = cap_syslog(type); + rc = cap_syslog(type, from_file); if (rc != 0) return rc; diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 10ccd686b290..60a9e2002da1 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o +obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index e0d0354008b7..ff51f1026b57 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -12,9 +12,10 @@ #include <linux/uaccess.h> #include <linux/security.h> #include <linux/hardirq.h> -#include "realpath.h" #include "common.h" -#include "tomoyo.h" + +/* Lock for protecting policy. */ +DEFINE_MUTEX(tomoyo_policy_lock); /* Has loading policy done? */ bool tomoyo_policy_loaded; @@ -178,14 +179,12 @@ static void tomoyo_normalize_line(unsigned char *buffer) * 1 = must / -1 = must not / 0 = don't care * @end_type: Should the pathname end with '/'? * 1 = must / -1 = must not / 0 = don't care - * @function: The name of function calling me. * * Check whether the given filename follows the naming rules. * Returns true if @filename follows the naming rules, false otherwise. */ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, - const s8 pattern_type, const s8 end_type, - const char *function) + const s8 pattern_type, const s8 end_type) { const char *const start = filename; bool in_repetition = false; @@ -193,7 +192,6 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, unsigned char c; unsigned char d; unsigned char e; - const char *original_filename = filename; if (!filename) goto out; @@ -282,25 +280,20 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, goto out; return true; out: - printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, - original_filename); return false; } /** * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. * @domainname: The domainname to check. - * @function: The name of function calling me. * * Returns true if @domainname follows the naming rules, false otherwise. */ -bool tomoyo_is_correct_domain(const unsigned char *domainname, - const char *function) +bool tomoyo_is_correct_domain(const unsigned char *domainname) { unsigned char c; unsigned char d; unsigned char e; - const char *org_domainname = domainname; if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN)) @@ -343,8 +336,6 @@ bool tomoyo_is_correct_domain(const unsigned char *domainname, } while (*domainname); return true; out: - printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function, - org_domainname); return false; } @@ -365,10 +356,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer) * * @domainname: The domainname to find. * - * Caller must call down_read(&tomoyo_domain_list_lock); or - * down_write(&tomoyo_domain_list_lock); . - * * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. + * + * Caller holds tomoyo_read_lock(). */ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) { @@ -377,7 +367,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) name.name = domainname; tomoyo_fill_path_info(&name); - list_for_each_entry(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { if (!domain->is_deleted && !tomoyo_pathcmp(&name, domain->domainname)) return domain; @@ -748,7 +738,7 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) * * Returns the tomoyo_realpath() of current process on success, NULL otherwise. * - * This function uses tomoyo_alloc(), so the caller must call tomoyo_free() + * This function uses kzalloc(), so the caller must call kfree() * if this function didn't return NULL. */ static const char *tomoyo_get_exe(void) @@ -829,6 +819,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) * @domain: Pointer to "struct tomoyo_domain_info". * * Returns true if the domain is not exceeded quota, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) { @@ -837,61 +829,29 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) if (!domain) return true; - down_read(&tomoyo_domain_acl_info_list_lock); - list_for_each_entry(ptr, &domain->acl_info_list, list) { - if (ptr->type & TOMOYO_ACL_DELETED) - continue; - switch (tomoyo_acl_type2(ptr)) { - struct tomoyo_single_path_acl_record *acl1; - struct tomoyo_double_path_acl_record *acl2; - u16 perm; - case TOMOYO_TYPE_SINGLE_PATH_ACL: - acl1 = container_of(ptr, - struct tomoyo_single_path_acl_record, - head); - perm = acl1->perm; - if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL)) - count++; - if (perm & - ((1 << TOMOYO_TYPE_READ_ACL) | - (1 << TOMOYO_TYPE_WRITE_ACL))) - count++; - if (perm & (1 << TOMOYO_TYPE_CREATE_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL)) - count++; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + switch (ptr->type) { + struct tomoyo_path_acl *acl; + u32 perm; + u8 i; + case TOMOYO_TYPE_PATH_ACL: + acl = container_of(ptr, struct tomoyo_path_acl, head); + perm = acl->perm | (((u32) acl->perm_high) << 16); + for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) + if (perm & (1 << i)) + count++; + if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) + count -= 2; break; - case TOMOYO_TYPE_DOUBLE_PATH_ACL: - acl2 = container_of(ptr, - struct tomoyo_double_path_acl_record, - head); - perm = acl2->perm; - if (perm & (1 << TOMOYO_TYPE_LINK_ACL)) - count++; - if (perm & (1 << TOMOYO_TYPE_RENAME_ACL)) - count++; + case TOMOYO_TYPE_PATH2_ACL: + perm = container_of(ptr, struct tomoyo_path2_acl, head) + ->perm; + for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) + if (perm & (1 << i)) + count++; break; } } - up_read(&tomoyo_domain_acl_info_list_lock); if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) return true; if (!domain->quota_warned) { @@ -923,9 +883,11 @@ static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned ptr = tomoyo_profile_ptr[profile]; if (ptr) goto ok; - ptr = tomoyo_alloc_element(sizeof(*ptr)); - if (!ptr) + ptr = kmalloc(sizeof(*ptr), GFP_KERNEL); + if (!tomoyo_memory_ok(ptr)) { + kfree(ptr); goto ok; + } for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) ptr->value[i] = tomoyo_control_array[i].current_value; mb(); /* Avoid out-of-order execution. */ @@ -966,7 +928,9 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) return -EINVAL; *cp = '\0'; if (!strcmp(data, "COMMENT")) { - profile->comment = tomoyo_save_name(cp + 1); + const struct tomoyo_path_info *old_comment = profile->comment; + profile->comment = tomoyo_get_name(cp + 1); + tomoyo_put_name(old_comment); return 0; } for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) { @@ -1061,27 +1025,6 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) } /* - * tomoyo_policy_manager_entry is a structure which is used for holding list of - * domainnames or programs which are permitted to modify configuration via - * /sys/kernel/security/tomoyo/ interface. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_policy_manager_list . - * (2) "manager" is a domainname or a program's pathname. - * (3) "is_domain" is a bool which is true if "manager" is a domainname, false - * otherwise. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - */ -struct tomoyo_policy_manager_entry { - struct list_head list; - /* A path to program or a domainname. */ - const struct tomoyo_path_info *manager; - bool is_domain; /* True if manager is a domainname. */ - bool is_deleted; /* True if this entry is deleted. */ -}; - -/* * tomoyo_policy_manager_list is used for holding list of domainnames or * programs which are permitted to modify configuration via * /sys/kernel/security/tomoyo/ interface. @@ -1111,8 +1054,7 @@ struct tomoyo_policy_manager_entry { * * # cat /sys/kernel/security/tomoyo/manager */ -static LIST_HEAD(tomoyo_policy_manager_list); -static DECLARE_RWSEM(tomoyo_policy_manager_list_lock); +LIST_HEAD(tomoyo_policy_manager_list); /** * tomoyo_update_manager_entry - Add a manager entry. @@ -1121,48 +1063,50 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { - struct tomoyo_policy_manager_entry *new_entry; + struct tomoyo_policy_manager_entry *entry = NULL; struct tomoyo_policy_manager_entry *ptr; const struct tomoyo_path_info *saved_manager; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; bool is_domain = false; if (tomoyo_is_domain_def(manager)) { - if (!tomoyo_is_correct_domain(manager, __func__)) + if (!tomoyo_is_correct_domain(manager)) return -EINVAL; is_domain = true; } else { - if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__)) + if (!tomoyo_is_correct_path(manager, 1, -1, -1)) return -EINVAL; } - saved_manager = tomoyo_save_name(manager); + saved_manager = tomoyo_get_name(manager); if (!saved_manager) return -ENOMEM; - down_write(&tomoyo_policy_manager_list_lock); - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) { + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { if (ptr->manager != saved_manager) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->manager = saved_manager; + saved_manager = NULL; + entry->is_domain = is_domain; + list_add_tail_rcu(&entry->list, &tomoyo_policy_manager_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->manager = saved_manager; - new_entry->is_domain = is_domain; - list_add_tail(&new_entry->list, &tomoyo_policy_manager_list); - error = 0; - out: - up_write(&tomoyo_policy_manager_list_lock); + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(saved_manager); + kfree(entry); return error; } @@ -1172,6 +1116,8 @@ static int tomoyo_update_manager_entry(const char *manager, * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) { @@ -1191,6 +1137,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) { @@ -1199,7 +1147,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) if (head->read_eof) return 0; - down_read(&tomoyo_policy_manager_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_policy_manager_list) { struct tomoyo_policy_manager_entry *ptr; @@ -1211,7 +1158,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_policy_manager_list_lock); head->read_eof = done; return 0; } @@ -1221,6 +1167,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) * * Returns true if the current process is permitted to modify policy * via /sys/kernel/security/tomoyo/ interface. + * + * Caller holds tomoyo_read_lock(). */ static bool tomoyo_is_policy_manager(void) { @@ -1234,29 +1182,25 @@ static bool tomoyo_is_policy_manager(void) return true; if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) return false; - down_read(&tomoyo_policy_manager_list_lock); - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { if (!ptr->is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; break; } } - up_read(&tomoyo_policy_manager_list_lock); if (found) return true; exe = tomoyo_get_exe(); if (!exe) return false; - down_read(&tomoyo_policy_manager_list_lock); - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { if (!ptr->is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; break; } } - up_read(&tomoyo_policy_manager_list_lock); if (!found) { /* Reduce error messages. */ static pid_t last_pid; const pid_t pid = current->pid; @@ -1266,7 +1210,7 @@ static bool tomoyo_is_policy_manager(void) last_pid = pid; } } - tomoyo_free(exe); + kfree(exe); return found; } @@ -1277,6 +1221,8 @@ static bool tomoyo_is_policy_manager(void) * @data: String to parse. * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, const char *data) @@ -1286,17 +1232,16 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, if (sscanf(data, "pid=%u", &pid) == 1) { struct task_struct *p; + rcu_read_lock(); read_lock(&tasklist_lock); p = find_task_by_vpid(pid); if (p) domain = tomoyo_real_domain(p); read_unlock(&tasklist_lock); + rcu_read_unlock(); } else if (!strncmp(data, "domain=", 7)) { - if (tomoyo_is_domain_def(data + 7)) { - down_read(&tomoyo_domain_list_lock); + if (tomoyo_is_domain_def(data + 7)) domain = tomoyo_find_domain(data + 7); - up_read(&tomoyo_domain_list_lock); - } } else return false; head->write_var1 = domain; @@ -1310,13 +1255,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, if (domain) { struct tomoyo_domain_info *d; head->read_var1 = NULL; - down_read(&tomoyo_domain_list_lock); - list_for_each_entry(d, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(d, &tomoyo_domain_list, list) { if (d == domain) break; head->read_var1 = &d->list; } - up_read(&tomoyo_domain_list_lock); head->read_var2 = NULL; head->read_bit = 0; head->read_step = 0; @@ -1332,6 +1275,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, * @domainname: The name of domain. * * Returns 0. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_delete_domain(char *domainname) { @@ -1340,9 +1285,9 @@ static int tomoyo_delete_domain(char *domainname) name.name = domainname; tomoyo_fill_path_info(&name); - down_write(&tomoyo_domain_list_lock); + mutex_lock(&tomoyo_policy_lock); /* Is there an active domain? */ - list_for_each_entry(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { /* Never delete tomoyo_kernel_domain */ if (domain == &tomoyo_kernel_domain) continue; @@ -1352,7 +1297,7 @@ static int tomoyo_delete_domain(char *domainname) domain->is_deleted = true; break; } - up_write(&tomoyo_domain_list_lock); + mutex_unlock(&tomoyo_policy_lock); return 0; } @@ -1362,6 +1307,8 @@ static int tomoyo_delete_domain(char *domainname) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) { @@ -1384,11 +1331,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain = NULL; if (is_delete) tomoyo_delete_domain(data); - else if (is_select) { - down_read(&tomoyo_domain_list_lock); + else if (is_select) domain = tomoyo_find_domain(data); - up_read(&tomoyo_domain_list_lock); - } else + else domain = tomoyo_find_or_assign_new_domain(data, 0); head->write_var1 = domain; return 0; @@ -1403,43 +1348,39 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) return 0; } if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) { - tomoyo_set_domain_flag(domain, is_delete, - TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ); + domain->ignore_global_allow_read = !is_delete; return 0; } return tomoyo_write_file_policy(data, domain, is_delete); } /** - * tomoyo_print_single_path_acl - Print a single path ACL entry. + * tomoyo_print_path_acl - Print a single path ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_single_path_acl_record". + * @ptr: Pointer to "struct tomoyo_path_acl". * * Returns true on success, false otherwise. */ -static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head, - struct tomoyo_single_path_acl_record * - ptr) +static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, + struct tomoyo_path_acl *ptr) { int pos; u8 bit; const char *atmark = ""; const char *filename; - const u16 perm = ptr->perm; + const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); filename = ptr->filename->name; - for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION; - bit++) { + for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { const char *msg; if (!(perm & (1 << bit))) continue; /* Print "read/write" instead of "read" and "write". */ - if ((bit == TOMOYO_TYPE_READ_ACL || - bit == TOMOYO_TYPE_WRITE_ACL) - && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))) + if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) + && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) continue; - msg = tomoyo_sp2keyword(bit); + msg = tomoyo_path2keyword(bit); pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg, atmark, filename)) @@ -1454,16 +1395,15 @@ static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head, } /** - * tomoyo_print_double_path_acl - Print a double path ACL entry. + * tomoyo_print_path2_acl - Print a double path ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_double_path_acl_record". + * @ptr: Pointer to "struct tomoyo_path2_acl". * * Returns true on success, false otherwise. */ -static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head, - struct tomoyo_double_path_acl_record * - ptr) +static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, + struct tomoyo_path2_acl *ptr) { int pos; const char *atmark1 = ""; @@ -1475,12 +1415,11 @@ static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head, filename1 = ptr->filename1->name; filename2 = ptr->filename2->name; - for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION; - bit++) { + for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { const char *msg; if (!(perm & (1 << bit))) continue; - msg = tomoyo_dp2keyword(bit); + msg = tomoyo_path22keyword(bit); pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg, atmark1, filename1, atmark2, filename2)) @@ -1505,23 +1444,17 @@ static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head, static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_acl_info *ptr) { - const u8 acl_type = tomoyo_acl_type2(ptr); + const u8 acl_type = ptr->type; - if (acl_type & TOMOYO_ACL_DELETED) - return true; - if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) { - struct tomoyo_single_path_acl_record *acl - = container_of(ptr, - struct tomoyo_single_path_acl_record, - head); - return tomoyo_print_single_path_acl(head, acl); + if (acl_type == TOMOYO_TYPE_PATH_ACL) { + struct tomoyo_path_acl *acl + = container_of(ptr, struct tomoyo_path_acl, head); + return tomoyo_print_path_acl(head, acl); } - if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) { - struct tomoyo_double_path_acl_record *acl - = container_of(ptr, - struct tomoyo_double_path_acl_record, - head); - return tomoyo_print_double_path_acl(head, acl); + if (acl_type == TOMOYO_TYPE_PATH2_ACL) { + struct tomoyo_path2_acl *acl + = container_of(ptr, struct tomoyo_path2_acl, head); + return tomoyo_print_path2_acl(head, acl); } BUG(); /* This must not happen. */ return false; @@ -1533,6 +1466,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) { @@ -1544,7 +1479,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) return 0; if (head->read_step == 0) head->read_step = 1; - down_read(&tomoyo_domain_list_lock); list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { struct tomoyo_domain_info *domain; const char *quota_exceeded = ""; @@ -1558,10 +1492,9 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) /* Print domainname and flags. */ if (domain->quota_warned) quota_exceeded = "quota_exceeded\n"; - if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED) + if (domain->transition_failed) transition_failed = "transition_failed\n"; - if (domain->flags & - TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) + if (domain->ignore_global_allow_read) ignore_global_allow_read = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n"; done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE @@ -1577,7 +1510,6 @@ acl_loop: if (head->read_step == 3) goto tail_mark; /* Print ACL entries in the domain. */ - down_read(&tomoyo_domain_acl_info_list_lock); list_for_each_cookie(apos, head->read_var2, &domain->acl_info_list) { struct tomoyo_acl_info *ptr @@ -1587,7 +1519,6 @@ acl_loop: if (!done) break; } - up_read(&tomoyo_domain_acl_info_list_lock); if (!done) break; head->read_step = 3; @@ -1599,7 +1530,6 @@ tail_mark: if (head->read_single_domain) break; } - up_read(&tomoyo_domain_list_lock); head->read_eof = done; return 0; } @@ -1615,6 +1545,8 @@ tail_mark: * * ( echo "select " $domainname; echo "use_profile " $profile ) | * /usr/lib/ccs/loadpolicy -d + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) { @@ -1626,9 +1558,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) if (!cp) return -EINVAL; *cp = '\0'; - down_read(&tomoyo_domain_list_lock); domain = tomoyo_find_domain(cp + 1); - up_read(&tomoyo_domain_list_lock); if (strict_strtoul(data, 10, &profile)) return -EINVAL; if (domain && profile < TOMOYO_MAX_PROFILES @@ -1650,6 +1580,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" ) * domainname = $0; } else if ( $1 == "use_profile" ) { * print $2 " " domainname; domainname = ""; } } ; ' + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) { @@ -1658,7 +1590,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) if (head->read_eof) return 0; - down_read(&tomoyo_domain_list_lock); list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { struct tomoyo_domain_info *domain; domain = list_entry(pos, struct tomoyo_domain_info, list); @@ -1669,7 +1600,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_domain_list_lock); head->read_eof = done; return 0; } @@ -1707,11 +1637,13 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head) const int pid = head->read_step; struct task_struct *p; struct tomoyo_domain_info *domain = NULL; + rcu_read_lock(); read_lock(&tasklist_lock); p = find_task_by_vpid(pid); if (p) domain = tomoyo_real_domain(p); read_unlock(&tasklist_lock); + rcu_read_unlock(); if (domain) tomoyo_io_printf(head, "%d %u %s", pid, domain->profile, domain->domainname->name); @@ -1726,6 +1658,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) { @@ -1760,6 +1694,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns 0 on success, -EINVAL otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) { @@ -1889,15 +1825,13 @@ void tomoyo_load_policy(const char *filename) tomoyo_policy_loaded = true; { /* Check all profiles currently assigned to domains are defined. */ struct tomoyo_domain_info *domain; - down_read(&tomoyo_domain_list_lock); - list_for_each_entry(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; if (tomoyo_profile_ptr[profile]) continue; panic("Profile %u (used by '%s') not defined.\n", profile, domain->domainname->name); } - up_read(&tomoyo_domain_list_lock); } } @@ -1945,10 +1879,12 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) * @file: Pointer to "struct file". * * Associates policy handler and returns 0 on success, -ENOMEM otherwise. + * + * Caller acquires tomoyo_read_lock(). */ static int tomoyo_open_control(const u8 type, struct file *file) { - struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head)); + struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_KERNEL); if (!head) return -ENOMEM; @@ -2009,9 +1945,9 @@ static int tomoyo_open_control(const u8 type, struct file *file) } else { if (!head->readbuf_size) head->readbuf_size = 4096 * 2; - head->read_buf = tomoyo_alloc(head->readbuf_size); + head->read_buf = kzalloc(head->readbuf_size, GFP_KERNEL); if (!head->read_buf) { - tomoyo_free(head); + kfree(head); return -ENOMEM; } } @@ -2023,13 +1959,14 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->write = NULL; } else if (head->write) { head->writebuf_size = 4096 * 2; - head->write_buf = tomoyo_alloc(head->writebuf_size); + head->write_buf = kzalloc(head->writebuf_size, GFP_KERNEL); if (!head->write_buf) { - tomoyo_free(head->read_buf); - tomoyo_free(head); + kfree(head->read_buf); + kfree(head); return -ENOMEM; } } + head->reader_idx = tomoyo_read_lock(); file->private_data = head; /* * Call the handler now if the file is @@ -2051,6 +1988,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) * @buffer_len: Size of @buffer. * * Returns bytes read on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_read_control(struct file *file, char __user *buffer, const int buffer_len) @@ -2094,6 +2033,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, * @buffer_len: Size of @buffer. * * Returns @buffer_len on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_write_control(struct file *file, const char __user *buffer, const int buffer_len) @@ -2144,52 +2085,29 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, * @file: Pointer to "struct file". * * Releases memory and returns 0. + * + * Caller looses tomoyo_read_lock(). */ static int tomoyo_close_control(struct file *file) { struct tomoyo_io_buffer *head = file->private_data; + const bool is_write = !!head->write_buf; + tomoyo_read_unlock(head->reader_idx); /* Release memory used for policy I/O. */ - tomoyo_free(head->read_buf); + kfree(head->read_buf); head->read_buf = NULL; - tomoyo_free(head->write_buf); + kfree(head->write_buf); head->write_buf = NULL; - tomoyo_free(head); + kfree(head); head = NULL; file->private_data = NULL; + if (is_write) + tomoyo_run_gc(); return 0; } /** - * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry. - * - * @acl_type: Type of ACL entry. - * - * Returns pointer to the ACL entry on success, NULL otherwise. - */ -void *tomoyo_alloc_acl_element(const u8 acl_type) -{ - int len; - struct tomoyo_acl_info *ptr; - - switch (acl_type) { - case TOMOYO_TYPE_SINGLE_PATH_ACL: - len = sizeof(struct tomoyo_single_path_acl_record); - break; - case TOMOYO_TYPE_DOUBLE_PATH_ACL: - len = sizeof(struct tomoyo_double_path_acl_record); - break; - default: - return NULL; - } - ptr = tomoyo_alloc_element(len); - if (!ptr) - return NULL; - ptr->type = acl_type; - return ptr; -} - -/** * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. * * @inode: Pointer to "struct inode". diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 92169d29b2db..67bd22dd3e68 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -1,12 +1,9 @@ /* * security/tomoyo/common.h * - * Common functions for TOMOYO. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 + * Header file for TOMOYO. * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #ifndef _SECURITY_TOMOYO_COMMON_H @@ -22,9 +19,119 @@ #include <linux/namei.h> #include <linux/mount.h> #include <linux/list.h> +#include <linux/cred.h> +struct linux_binprm; + +/********** Constants definitions. **********/ + +/* + * TOMOYO uses this hash only when appending a string into the string + * table. Frequency of appending strings is very low. So we don't need + * large (e.g. 64k) hash size. 256 will be sufficient. + */ +#define TOMOYO_HASH_BITS 8 +#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) + +/* + * This is the max length of a token. + * + * A token consists of only ASCII printable characters. + * Non printable characters in a token is represented in \ooo style + * octal string. Thus, \ itself is represented as \\. + */ +#define TOMOYO_MAX_PATHNAME_LEN 4000 + +/* Profile number is an integer between 0 and 255. */ +#define TOMOYO_MAX_PROFILES 256 + +/* Keywords for ACLs. */ +#define TOMOYO_KEYWORD_ALIAS "alias " +#define TOMOYO_KEYWORD_ALLOW_READ "allow_read " +#define TOMOYO_KEYWORD_DELETE "delete " +#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " +#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern " +#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain " +#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain " +#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " +#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " +#define TOMOYO_KEYWORD_SELECT "select " +#define TOMOYO_KEYWORD_USE_PROFILE "use_profile " +#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" +/* A domain definition starts with <kernel>. */ +#define TOMOYO_ROOT_NAME "<kernel>" +#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) + +/* Index numbers for Access Controls. */ +enum tomoyo_mac_index { + TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */ + TOMOYO_MAX_ACCEPT_ENTRY, + TOMOYO_VERBOSE, + TOMOYO_MAX_CONTROL_INDEX +}; + +/* Index numbers for Access Controls. */ +enum tomoyo_acl_entry_type_index { + TOMOYO_TYPE_PATH_ACL, + TOMOYO_TYPE_PATH2_ACL, +}; + +/* 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. + */ + +enum tomoyo_path_acl_index { + TOMOYO_TYPE_READ_WRITE, + TOMOYO_TYPE_EXECUTE, + TOMOYO_TYPE_READ, + TOMOYO_TYPE_WRITE, + TOMOYO_TYPE_CREATE, + TOMOYO_TYPE_UNLINK, + TOMOYO_TYPE_MKDIR, + TOMOYO_TYPE_RMDIR, + TOMOYO_TYPE_MKFIFO, + TOMOYO_TYPE_MKSOCK, + TOMOYO_TYPE_MKBLOCK, + TOMOYO_TYPE_MKCHAR, + TOMOYO_TYPE_TRUNCATE, + TOMOYO_TYPE_SYMLINK, + TOMOYO_TYPE_REWRITE, + TOMOYO_TYPE_IOCTL, + TOMOYO_TYPE_CHMOD, + TOMOYO_TYPE_CHOWN, + TOMOYO_TYPE_CHGRP, + TOMOYO_TYPE_CHROOT, + TOMOYO_TYPE_MOUNT, + TOMOYO_TYPE_UMOUNT, + TOMOYO_MAX_PATH_OPERATION +}; -struct dentry; -struct vfsmount; +enum tomoyo_path2_acl_index { + TOMOYO_TYPE_LINK, + TOMOYO_TYPE_RENAME, + TOMOYO_TYPE_PIVOT_ROOT, + TOMOYO_MAX_PATH2_OPERATION +}; + +enum tomoyo_securityfs_interface_index { + TOMOYO_DOMAINPOLICY, + TOMOYO_EXCEPTIONPOLICY, + TOMOYO_DOMAIN_STATUS, + TOMOYO_PROCESS_STATUS, + TOMOYO_MEMINFO, + TOMOYO_SELFDOMAIN, + TOMOYO_VERSION, + TOMOYO_PROFILE, + TOMOYO_MANAGER +}; + +/********** Structure definitions. **********/ /* * tomoyo_page_buffer is a structure which is used for holding a pathname @@ -66,13 +173,14 @@ struct tomoyo_path_info { }; /* - * This is the max length of a token. - * - * A token consists of only ASCII printable characters. - * Non printable characters in a token is represented in \ooo style - * octal string. Thus, \ itself is represented as \\. + * tomoyo_name_entry is a structure which is used for linking + * "struct tomoyo_path_info" into tomoyo_name_list . */ -#define TOMOYO_MAX_PATHNAME_LEN 4000 +struct tomoyo_name_entry { + struct list_head list; + atomic_t users; + struct tomoyo_path_info entry; +}; /* * tomoyo_path_info_with_data is a structure which is used for holding a @@ -89,7 +197,7 @@ struct tomoyo_path_info { * "struct tomoyo_path_info_with_data". */ struct tomoyo_path_info_with_data { - /* Keep "head" first, for this pointer is passed to tomoyo_free(). */ + /* Keep "head" first, for this pointer is passed to kfree(). */ struct tomoyo_path_info head; char barrier1[16]; /* Safeguard for overrun. */ char body[TOMOYO_MAX_PATHNAME_LEN]; @@ -101,30 +209,19 @@ struct tomoyo_path_info_with_data { * * (1) "list" which is linked to the ->acl_info_list of * "struct tomoyo_domain_info" - * (2) "type" which tells - * (a) type & 0x7F : type of the entry (either - * "struct tomoyo_single_path_acl_record" or - * "struct tomoyo_double_path_acl_record") - * (b) type & 0x80 : whether the entry is marked as "deleted". + * (2) "type" which tells type of the entry (either + * "struct tomoyo_path_acl" or "struct tomoyo_path2_acl"). * * Packing "struct tomoyo_acl_info" allows - * "struct tomoyo_single_path_acl_record" to embed "u16" and - * "struct tomoyo_double_path_acl_record" to embed "u8" + * "struct tomoyo_path_acl" to embed "u8" + "u16" and + * "struct tomoyo_path2_acl" to embed "u8" * without enlarging their structure size. */ struct tomoyo_acl_info { struct list_head list; - /* - * Type of this ACL entry. - * - * MSB is is_deleted flag. - */ u8 type; } __packed; -/* This ACL entry is deleted. */ -#define TOMOYO_ACL_DELETED 0x80 - /* * tomoyo_domain_info is a structure which is used for holding permissions * (e.g. "allow_read /lib/libc-2.5.so") given to each domain. @@ -138,7 +235,17 @@ struct tomoyo_acl_info { * "deleted", false otherwise. * (6) "quota_warned" is a bool which is used for suppressing warning message * when learning mode learned too much entries. - * (7) "flags" which remembers this domain's attributes. + * (7) "ignore_global_allow_read" is a bool which is true if this domain + * should ignore "allow_read" directive in exception policy. + * (8) "transition_failed" is a bool which is set to true when this domain was + * unable to create a new domain at tomoyo_find_next_domain() because the + * name of the domain to be created was too long or it could not allocate + * memory. If set to true, more than one process continued execve() + * without domain transition. + * (9) "users" is an atomic_t that holds how many "struct cred"->security + * are referring this "struct tomoyo_domain_info". If is_deleted == true + * and users == 0, this struct will be kfree()d upon next garbage + * collection. * * A domain's lifecycle is an analogy of files on / directory. * Multiple domains with the same domainname cannot be created (as with @@ -155,25 +262,13 @@ struct tomoyo_domain_info { u8 profile; /* Profile number to use. */ bool is_deleted; /* Delete flag. */ bool quota_warned; /* Quota warnning flag. */ - /* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */ - u8 flags; + bool ignore_global_allow_read; /* Ignore "allow_read" flag. */ + bool transition_failed; /* Domain transition failed flag. */ + atomic_t users; /* Number of referring credentials. */ }; -/* Profile number is an integer between 0 and 255. */ -#define TOMOYO_MAX_PROFILES 256 - -/* Ignore "allow_read" directive in exception policy. */ -#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1 -/* - * This domain was unable to create a new domain at tomoyo_find_next_domain() - * because the name of the domain to be created was too long or - * it could not allocate memory. - * More than one process continued execve() without domain transition. - */ -#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED 2 - /* - * tomoyo_single_path_acl_record is a structure which is used for holding an + * tomoyo_path_acl is a structure which is used for holding an * entry with one pathname operation (e.g. open(), mkdir()). * It has following fields. * @@ -184,18 +279,21 @@ struct tomoyo_domain_info { * Directives held by this structure are "allow_read/write", "allow_execute", * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", - * "allow_mkchar", "allow_truncate", "allow_symlink" and "allow_rewrite". + * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", + * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount" + * and "allow_unmount". */ -struct tomoyo_single_path_acl_record { - struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */ +struct tomoyo_path_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ + u8 perm_high; u16 perm; /* Pointer to single pathname. */ const struct tomoyo_path_info *filename; }; /* - * tomoyo_double_path_acl_record is a structure which is used for holding an - * entry with two pathnames operation (i.e. link() and rename()). + * tomoyo_path2_acl is a structure which is used for holding an + * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). * It has following fields. * * (1) "head" which is a "struct tomoyo_acl_info". @@ -203,10 +301,11 @@ struct tomoyo_single_path_acl_record { * (3) "filename1" is the source/old pathname. * (4) "filename2" is the destination/new pathname. * - * Directives held by this structure are "allow_rename" and "allow_link". + * Directives held by this structure are "allow_rename", "allow_link" and + * "allow_pivot_root". */ -struct tomoyo_double_path_acl_record { - struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */ +struct tomoyo_path2_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */ u8 perm; /* Pointer to single pathname. */ const struct tomoyo_path_info *filename1; @@ -214,29 +313,6 @@ struct tomoyo_double_path_acl_record { const struct tomoyo_path_info *filename2; }; -/* Keywords for ACLs. */ -#define TOMOYO_KEYWORD_ALIAS "alias " -#define TOMOYO_KEYWORD_ALLOW_READ "allow_read " -#define TOMOYO_KEYWORD_DELETE "delete " -#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " -#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern " -#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain " -#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain " -#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " -#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " -#define TOMOYO_KEYWORD_SELECT "select " -#define TOMOYO_KEYWORD_USE_PROFILE "use_profile " -#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" -/* A domain definition starts with <kernel>. */ -#define TOMOYO_ROOT_NAME "<kernel>" -#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) - -/* Index numbers for Access Controls. */ -#define TOMOYO_MAC_FOR_FILE 0 /* domain_policy.conf */ -#define TOMOYO_MAX_ACCEPT_ENTRY 1 -#define TOMOYO_VERBOSE 2 -#define TOMOYO_MAX_CONTROL_INDEX 3 - /* * tomoyo_io_buffer is a structure which is used for reading and modifying * configuration via /sys/kernel/security/tomoyo/ interface. @@ -265,6 +341,8 @@ struct tomoyo_io_buffer { int (*write) (struct tomoyo_io_buffer *); /* Exclusive lock for this structure. */ struct mutex io_sem; + /* Index returned by tomoyo_read_lock(). */ + int reader_idx; /* The position currently reading from. */ struct list_head *read_var1; /* Extra variables for reading. */ @@ -293,18 +371,159 @@ struct tomoyo_io_buffer { int writebuf_size; }; +/* + * tomoyo_globally_readable_file_entry is a structure which is used for holding + * "allow_read" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_globally_readable_list . + * (2) "filename" is a pathname which is allowed to open(O_RDONLY). + * (3) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_globally_readable_file_entry { + struct list_head list; + const struct tomoyo_path_info *filename; + bool is_deleted; +}; + +/* + * tomoyo_pattern_entry is a structure which is used for holding + * "tomoyo_pattern_list" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_pattern_list . + * (2) "pattern" is a pathname pattern which is used for converting pathnames + * to pathname patterns during learning mode. + * (3) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_pattern_entry { + struct list_head list; + const struct tomoyo_path_info *pattern; + bool is_deleted; +}; + +/* + * tomoyo_no_rewrite_entry is a structure which is used for holding + * "deny_rewrite" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_no_rewrite_list . + * (2) "pattern" is a pathname which is by default not permitted to modify + * already existing content. + * (3) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_no_rewrite_entry { + struct list_head list; + const struct tomoyo_path_info *pattern; + bool is_deleted; +}; + +/* + * tomoyo_domain_initializer_entry is a structure which is used for holding + * "initialize_domain" and "no_initialize_domain" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_domain_initializer_list . + * (2) "domainname" which is "a domainname" or "the last component of a + * domainname". This field is NULL if "from" clause is not specified. + * (3) "program" which is a program's pathname. + * (4) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + * (5) "is_not" is a bool which is true if "no_initialize_domain", false + * otherwise. + * (6) "is_last_name" is a bool which is true if "domainname" is "the last + * component of a domainname", false otherwise. + */ +struct tomoyo_domain_initializer_entry { + struct list_head list; + const struct tomoyo_path_info *domainname; /* This may be NULL */ + const struct tomoyo_path_info *program; + bool is_deleted; + bool is_not; /* True if this entry is "no_initialize_domain". */ + /* True if the domainname is tomoyo_get_last_name(). */ + bool is_last_name; +}; + +/* + * tomoyo_domain_keeper_entry is a structure which is used for holding + * "keep_domain" and "no_keep_domain" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_domain_keeper_list . + * (2) "domainname" which is "a domainname" or "the last component of a + * domainname". + * (3) "program" which is a program's pathname. + * This field is NULL if "from" clause is not specified. + * (4) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + * (5) "is_not" is a bool which is true if "no_initialize_domain", false + * otherwise. + * (6) "is_last_name" is a bool which is true if "domainname" is "the last + * component of a domainname", false otherwise. + */ +struct tomoyo_domain_keeper_entry { + struct list_head list; + const struct tomoyo_path_info *domainname; + const struct tomoyo_path_info *program; /* This may be NULL */ + bool is_deleted; + bool is_not; /* True if this entry is "no_keep_domain". */ + /* True if the domainname is tomoyo_get_last_name(). */ + bool is_last_name; +}; + +/* + * tomoyo_alias_entry is a structure which is used for holding "alias" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_alias_list . + * (2) "original_name" which is a dereferenced pathname. + * (3) "aliased_name" which is a symlink's pathname. + * (4) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_alias_entry { + struct list_head list; + const struct tomoyo_path_info *original_name; + const struct tomoyo_path_info *aliased_name; + bool is_deleted; +}; + +/* + * tomoyo_policy_manager_entry is a structure which is used for holding list of + * domainnames or programs which are permitted to modify configuration via + * /sys/kernel/security/tomoyo/ interface. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_policy_manager_list . + * (2) "manager" is a domainname or a program's pathname. + * (3) "is_domain" is a bool which is true if "manager" is a domainname, false + * otherwise. + * (4) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_policy_manager_entry { + struct list_head list; + /* A path to program or a domainname. */ + const struct tomoyo_path_info *manager; + bool is_domain; /* True if manager is a domainname. */ + bool is_deleted; /* True if this entry is deleted. */ +}; + +/********** Function prototypes. **********/ + /* Check whether the domain has too many ACL entries to hold. */ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain); /* Transactional sprintf() for policy dump. */ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); /* Check whether the domainname is correct. */ -bool tomoyo_is_correct_domain(const unsigned char *domainname, - const char *function); +bool tomoyo_is_correct_domain(const unsigned char *domainname); /* Check whether the token is correct. */ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, - const s8 pattern_type, const s8 end_type, - const char *function); + const s8 pattern_type, const s8 end_type); /* Check whether the token can be a domainname. */ bool tomoyo_is_domain_def(const unsigned char *buffer); /* Check whether the given filename matches the given pattern. */ @@ -328,13 +547,13 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head); /* Write domain policy violation warning message to console? */ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); /* Convert double path operation to operation name. */ -const char *tomoyo_dp2keyword(const u8 operation); +const char *tomoyo_path22keyword(const u8 operation); /* Get the last component of the given domainname. */ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); /* Get warning message. */ const char *tomoyo_get_msg(const bool is_enforce); /* Convert single path operation to operation name. */ -const char *tomoyo_sp2keyword(const u8 operation); +const char *tomoyo_path2keyword(const u8 operation); /* Create "alias" entry in exception policy. */ int tomoyo_write_alias_policy(char *data, const bool is_delete); /* @@ -370,33 +589,107 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * /* Check mode for specified functionality. */ unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, const u8 index); -/* Allocate memory for structures. */ -void *tomoyo_alloc_acl_element(const u8 acl_type); /* Fill in "struct tomoyo_path_info" members. */ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); /* Run policy loader when /sbin/init starts. */ void tomoyo_load_policy(const char *filename); -/* Change "struct tomoyo_domain_info"->flags. */ -void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain, - const bool is_delete, const u8 flags); -/* strcmp() for "struct tomoyo_path_info" structure. */ -static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, - const struct tomoyo_path_info *b) +/* Convert binary string to ascii string. */ +int tomoyo_encode(char *buffer, int buflen, const char *str); + +/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ +int tomoyo_realpath_from_path2(struct path *path, char *newname, + int newname_len); + +/* + * Returns realpath(3) of the given pathname but ignores chroot'ed root. + * These functions use kzalloc(), so the caller must call kfree() + * if these functions didn't return NULL. + */ +char *tomoyo_realpath(const char *pathname); +/* + * Same with tomoyo_realpath() except that it doesn't follow the final symlink. + */ +char *tomoyo_realpath_nofollow(const char *pathname); +/* Same with tomoyo_realpath() except that the pathname is already solved. */ +char *tomoyo_realpath_from_path(struct path *path); + +/* Check memory quota. */ +bool tomoyo_memory_ok(void *ptr); + +/* + * Keep the given name on the RAM. + * The RAM is shared, so NEVER try to modify or kfree() the returned name. + */ +const struct tomoyo_path_info *tomoyo_get_name(const char *name); + +/* Check for memory usage. */ +int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); + +/* Set memory quota. */ +int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); + +/* Initialize realpath related code. */ +void __init tomoyo_realpath_init(void); +int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, + const struct tomoyo_path_info *filename); +int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, + struct path *path, const int flag); +int tomoyo_path_perm(const u8 operation, struct path *path); +int tomoyo_path2_perm(const u8 operation, struct path *path1, + struct path *path2); +int tomoyo_check_rewrite_permission(struct file *filp); +int tomoyo_find_next_domain(struct linux_binprm *bprm); + +/* Run garbage collector. */ +void tomoyo_run_gc(void); + +void tomoyo_memory_free(void *ptr); + +/********** External variable definitions. **********/ + +/* Lock for GC. */ +extern struct srcu_struct tomoyo_ss; + +/* The list for "struct tomoyo_domain_info". */ +extern struct list_head tomoyo_domain_list; + +extern struct list_head tomoyo_domain_initializer_list; +extern struct list_head tomoyo_domain_keeper_list; +extern struct list_head tomoyo_alias_list; +extern struct list_head tomoyo_globally_readable_list; +extern struct list_head tomoyo_pattern_list; +extern struct list_head tomoyo_no_rewrite_list; +extern struct list_head tomoyo_policy_manager_list; +extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; +extern struct mutex tomoyo_name_list_lock; + +/* Lock for protecting policy. */ +extern struct mutex tomoyo_policy_lock; + +/* Has /sbin/init started? */ +extern bool tomoyo_policy_loaded; + +/* The kernel's domain. */ +extern struct tomoyo_domain_info tomoyo_kernel_domain; + +/********** Inlined functions. **********/ + +static inline int tomoyo_read_lock(void) { - return a->hash != b->hash || strcmp(a->name, b->name); + return srcu_read_lock(&tomoyo_ss); } -/* Get type of an ACL entry. */ -static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr) +static inline void tomoyo_read_unlock(int idx) { - return ptr->type & ~TOMOYO_ACL_DELETED; + srcu_read_unlock(&tomoyo_ss, idx); } -/* Get type of an ACL entry. */ -static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr) +/* strcmp() for "struct tomoyo_path_info" structure. */ +static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, + const struct tomoyo_path_info *b) { - return ptr->type; + return a->hash != b->hash || strcmp(a->name, b->name); } /** @@ -423,18 +716,25 @@ static inline bool tomoyo_is_invalid(const unsigned char c) return c && (c <= ' ' || c >= 127); } -/* The list for "struct tomoyo_domain_info". */ -extern struct list_head tomoyo_domain_list; -extern struct rw_semaphore tomoyo_domain_list_lock; - -/* Lock for domain->acl_info_list. */ -extern struct rw_semaphore tomoyo_domain_acl_info_list_lock; +static inline void tomoyo_put_name(const struct tomoyo_path_info *name) +{ + if (name) { + struct tomoyo_name_entry *ptr = + container_of(name, struct tomoyo_name_entry, entry); + atomic_dec(&ptr->users); + } +} -/* Has /sbin/init started? */ -extern bool tomoyo_policy_loaded; +static inline struct tomoyo_domain_info *tomoyo_domain(void) +{ + return current_cred()->security; +} -/* The kernel's domain. */ -extern struct tomoyo_domain_info tomoyo_kernel_domain; +static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct + *task) +{ + return task_cred_xxx(task, security); +} /** * list_for_each_cookie - iterate over a list with cookie. @@ -442,16 +742,16 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain; * @cookie: the &struct list_head to use as a cookie. * @head: the head for your list. * - * Same with list_for_each() except that this primitive uses @cookie + * Same with list_for_each_rcu() except that this primitive uses @cookie * so that we can continue iteration. * @cookie must be NULL when iteration starts, and @cookie will become * NULL when iteration finishes. */ -#define list_for_each_cookie(pos, cookie, head) \ - for (({ if (!cookie) \ - cookie = head; }), \ - pos = (cookie)->next; \ - prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ - (cookie) = pos, pos = pos->next) +#define list_for_each_cookie(pos, cookie, head) \ + for (({ if (!cookie) \ + cookie = head; }), \ + pos = rcu_dereference((cookie)->next); \ + prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ + (cookie) = pos, pos = rcu_dereference(pos->next)) #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index fcf52accce2b..66caaa1b842a 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -10,8 +10,6 @@ */ #include "common.h" -#include "tomoyo.h" -#include "realpath.h" #include <linux/binfmts.h> /* Variables definitions.*/ @@ -58,99 +56,6 @@ struct tomoyo_domain_info tomoyo_kernel_domain; * exceptions. */ LIST_HEAD(tomoyo_domain_list); -DECLARE_RWSEM(tomoyo_domain_list_lock); - -/* - * tomoyo_domain_initializer_entry is a structure which is used for holding - * "initialize_domain" and "no_initialize_domain" entries. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_domain_initializer_list . - * (2) "domainname" which is "a domainname" or "the last component of a - * domainname". This field is NULL if "from" clause is not specified. - * (3) "program" which is a program's pathname. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - * (5) "is_not" is a bool which is true if "no_initialize_domain", false - * otherwise. - * (6) "is_last_name" is a bool which is true if "domainname" is "the last - * component of a domainname", false otherwise. - */ -struct tomoyo_domain_initializer_entry { - struct list_head list; - const struct tomoyo_path_info *domainname; /* This may be NULL */ - const struct tomoyo_path_info *program; - bool is_deleted; - bool is_not; /* True if this entry is "no_initialize_domain". */ - /* True if the domainname is tomoyo_get_last_name(). */ - bool is_last_name; -}; - -/* - * tomoyo_domain_keeper_entry is a structure which is used for holding - * "keep_domain" and "no_keep_domain" entries. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_domain_keeper_list . - * (2) "domainname" which is "a domainname" or "the last component of a - * domainname". - * (3) "program" which is a program's pathname. - * This field is NULL if "from" clause is not specified. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - * (5) "is_not" is a bool which is true if "no_initialize_domain", false - * otherwise. - * (6) "is_last_name" is a bool which is true if "domainname" is "the last - * component of a domainname", false otherwise. - */ -struct tomoyo_domain_keeper_entry { - struct list_head list; - const struct tomoyo_path_info *domainname; - const struct tomoyo_path_info *program; /* This may be NULL */ - bool is_deleted; - bool is_not; /* True if this entry is "no_keep_domain". */ - /* True if the domainname is tomoyo_get_last_name(). */ - bool is_last_name; -}; - -/* - * tomoyo_alias_entry is a structure which is used for holding "alias" entries. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_alias_list . - * (2) "original_name" which is a dereferenced pathname. - * (3) "aliased_name" which is a symlink's pathname. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - */ -struct tomoyo_alias_entry { - struct list_head list; - const struct tomoyo_path_info *original_name; - const struct tomoyo_path_info *aliased_name; - bool is_deleted; -}; - -/** - * tomoyo_set_domain_flag - Set or clear domain's attribute flags. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. - * @flags: Flags to set or clear. - * - * Returns nothing. - */ -void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain, - const bool is_delete, const u8 flags) -{ - /* We need to serialize because this is bitfield operation. */ - static DEFINE_SPINLOCK(lock); - spin_lock(&lock); - if (!is_delete) - domain->flags |= flags; - else - domain->flags &= ~flags; - spin_unlock(&lock); -} /** * tomoyo_get_last_name - Get last component of a domainname. @@ -205,8 +110,7 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain. */ -static LIST_HEAD(tomoyo_domain_initializer_list); -static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock); +LIST_HEAD(tomoyo_domain_initializer_list); /** * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. @@ -217,59 +121,65 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_domain_initializer_entry(const char *domainname, const char *program, const bool is_not, const bool is_delete) { - struct tomoyo_domain_initializer_entry *new_entry; + struct tomoyo_domain_initializer_entry *entry = NULL; struct tomoyo_domain_initializer_entry *ptr; - const struct tomoyo_path_info *saved_program; + const struct tomoyo_path_info *saved_program = NULL; const struct tomoyo_path_info *saved_domainname = NULL; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; bool is_last_name = false; - if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__)) + if (!tomoyo_is_correct_path(program, 1, -1, -1)) return -EINVAL; /* No patterns allowed. */ if (domainname) { if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1, __func__)) + tomoyo_is_correct_path(domainname, 1, -1, -1)) is_last_name = true; - else if (!tomoyo_is_correct_domain(domainname, __func__)) + else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; - saved_domainname = tomoyo_save_name(domainname); + saved_domainname = tomoyo_get_name(domainname); if (!saved_domainname) - return -ENOMEM; + goto out; } - saved_program = tomoyo_save_name(program); + saved_program = tomoyo_get_name(program); if (!saved_program) - return -ENOMEM; - down_write(&tomoyo_domain_initializer_list_lock); - list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) { + goto out; + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { if (ptr->is_not != is_not || ptr->domainname != saved_domainname || ptr->program != saved_program) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->domainname = saved_domainname; + saved_domainname = NULL; + entry->program = saved_program; + saved_program = NULL; + entry->is_not = is_not; + entry->is_last_name = is_last_name; + list_add_tail_rcu(&entry->list, + &tomoyo_domain_initializer_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->domainname = saved_domainname; - new_entry->program = saved_program; - new_entry->is_not = is_not; - new_entry->is_last_name = is_last_name; - list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list); - error = 0; + mutex_unlock(&tomoyo_policy_lock); out: - up_write(&tomoyo_domain_initializer_list_lock); + tomoyo_put_name(saved_domainname); + tomoyo_put_name(saved_program); + kfree(entry); return error; } @@ -279,13 +189,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; - down_read(&tomoyo_domain_initializer_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_domain_initializer_list) { const char *no; @@ -308,7 +219,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_domain_initializer_list_lock); return done; } @@ -320,6 +230,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, const bool is_delete) @@ -345,6 +257,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, * * Returns true if executing @program reinitializes domain transition, * false otherwise. + * + * Caller holds tomoyo_read_lock(). */ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * domainname, @@ -355,8 +269,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * struct tomoyo_domain_initializer_entry *ptr; bool flag = false; - down_read(&tomoyo_domain_initializer_list_lock); - list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { if (ptr->is_deleted) continue; if (ptr->domainname) { @@ -376,7 +289,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * } flag = true; } - up_read(&tomoyo_domain_initializer_list_lock); return flag; } @@ -418,8 +330,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless * explicitly specified by "initialize_domain". */ -static LIST_HEAD(tomoyo_domain_keeper_list); -static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock); +LIST_HEAD(tomoyo_domain_keeper_list); /** * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. @@ -430,59 +341,64 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_domain_keeper_entry(const char *domainname, const char *program, const bool is_not, const bool is_delete) { - struct tomoyo_domain_keeper_entry *new_entry; + struct tomoyo_domain_keeper_entry *entry = NULL; struct tomoyo_domain_keeper_entry *ptr; - const struct tomoyo_path_info *saved_domainname; + const struct tomoyo_path_info *saved_domainname = NULL; const struct tomoyo_path_info *saved_program = NULL; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; bool is_last_name = false; if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1, __func__)) + tomoyo_is_correct_path(domainname, 1, -1, -1)) is_last_name = true; - else if (!tomoyo_is_correct_domain(domainname, __func__)) + else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; if (program) { - if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__)) + if (!tomoyo_is_correct_path(program, 1, -1, -1)) return -EINVAL; - saved_program = tomoyo_save_name(program); + saved_program = tomoyo_get_name(program); if (!saved_program) - return -ENOMEM; + goto out; } - saved_domainname = tomoyo_save_name(domainname); + saved_domainname = tomoyo_get_name(domainname); if (!saved_domainname) - return -ENOMEM; - down_write(&tomoyo_domain_keeper_list_lock); - list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) { + goto out; + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { if (ptr->is_not != is_not || ptr->domainname != saved_domainname || ptr->program != saved_program) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->domainname = saved_domainname; + saved_domainname = NULL; + entry->program = saved_program; + saved_program = NULL; + entry->is_not = is_not; + entry->is_last_name = is_last_name; + list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->domainname = saved_domainname; - new_entry->program = saved_program; - new_entry->is_not = is_not; - new_entry->is_last_name = is_last_name; - list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list); - error = 0; + mutex_unlock(&tomoyo_policy_lock); out: - up_write(&tomoyo_domain_keeper_list_lock); + tomoyo_put_name(saved_domainname); + tomoyo_put_name(saved_program); + kfree(entry); return error; } @@ -493,6 +409,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, * @is_not: True if it is "no_keep_domain" entry. * @is_delete: True if it is a delete request. * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, const bool is_delete) @@ -513,13 +430,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; - down_read(&tomoyo_domain_keeper_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_domain_keeper_list) { struct tomoyo_domain_keeper_entry *ptr; @@ -542,7 +460,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_domain_keeper_list_lock); return done; } @@ -555,6 +472,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) * * Returns true if executing @program supresses domain transition, * false otherwise. + * + * Caller holds tomoyo_read_lock(). */ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, const struct tomoyo_path_info *program, @@ -563,8 +482,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, struct tomoyo_domain_keeper_entry *ptr; bool flag = false; - down_read(&tomoyo_domain_keeper_list_lock); - list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { if (ptr->is_deleted) continue; if (!ptr->is_last_name) { @@ -582,7 +500,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, } flag = true; } - up_read(&tomoyo_domain_keeper_list_lock); return flag; } @@ -616,8 +533,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, * /bin/busybox and domainname which the current process will belong to after * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . */ -static LIST_HEAD(tomoyo_alias_list); -static DECLARE_RWSEM(tomoyo_alias_list_lock); +LIST_HEAD(tomoyo_alias_list); /** * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. @@ -627,46 +543,51 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_alias_entry(const char *original_name, const char *aliased_name, const bool is_delete) { - struct tomoyo_alias_entry *new_entry; + struct tomoyo_alias_entry *entry = NULL; struct tomoyo_alias_entry *ptr; const struct tomoyo_path_info *saved_original_name; const struct tomoyo_path_info *saved_aliased_name; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) || - !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__)) + if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || + !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) return -EINVAL; /* No patterns allowed. */ - saved_original_name = tomoyo_save_name(original_name); - saved_aliased_name = tomoyo_save_name(aliased_name); + saved_original_name = tomoyo_get_name(original_name); + saved_aliased_name = tomoyo_get_name(aliased_name); if (!saved_original_name || !saved_aliased_name) - return -ENOMEM; - down_write(&tomoyo_alias_list_lock); - list_for_each_entry(ptr, &tomoyo_alias_list, list) { + goto out; + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { if (ptr->original_name != saved_original_name || ptr->aliased_name != saved_aliased_name) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->original_name = saved_original_name; + saved_original_name = NULL; + entry->aliased_name = saved_aliased_name; + saved_aliased_name = NULL; + list_add_tail_rcu(&entry->list, &tomoyo_alias_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->original_name = saved_original_name; - new_entry->aliased_name = saved_aliased_name; - list_add_tail(&new_entry->list, &tomoyo_alias_list); - error = 0; + mutex_unlock(&tomoyo_policy_lock); out: - up_write(&tomoyo_alias_list_lock); + tomoyo_put_name(saved_original_name); + tomoyo_put_name(saved_aliased_name); + kfree(entry); return error; } @@ -676,13 +597,14 @@ static int tomoyo_update_alias_entry(const char *original_name, * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; - down_read(&tomoyo_alias_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { struct tomoyo_alias_entry *ptr; @@ -695,7 +617,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_alias_list_lock); return done; } @@ -706,6 +627,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_alias_policy(char *data, const bool is_delete) { @@ -724,63 +647,46 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete) * @profile: Profile number to assign if the domain was newly created. * * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. + * + * Caller holds tomoyo_read_lock(). */ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile) { - struct tomoyo_domain_info *domain = NULL; + struct tomoyo_domain_info *entry; + struct tomoyo_domain_info *domain; const struct tomoyo_path_info *saved_domainname; + bool found = false; - down_write(&tomoyo_domain_list_lock); - domain = tomoyo_find_domain(domainname); - if (domain) - goto out; - if (!tomoyo_is_correct_domain(domainname, __func__)) - goto out; - saved_domainname = tomoyo_save_name(domainname); + if (!tomoyo_is_correct_domain(domainname)) + return NULL; + saved_domainname = tomoyo_get_name(domainname); if (!saved_domainname) - goto out; - /* Can I reuse memory of deleted domain? */ - list_for_each_entry(domain, &tomoyo_domain_list, list) { - struct task_struct *p; - struct tomoyo_acl_info *ptr; - bool flag; - if (!domain->is_deleted || - domain->domainname != saved_domainname) + return NULL; + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + if (domain->is_deleted || + tomoyo_pathcmp(saved_domainname, domain->domainname)) continue; - flag = false; - read_lock(&tasklist_lock); - for_each_process(p) { - if (tomoyo_real_domain(p) != domain) - continue; - flag = true; - break; - } - read_unlock(&tasklist_lock); - if (flag) - continue; - list_for_each_entry(ptr, &domain->acl_info_list, list) { - ptr->type |= TOMOYO_ACL_DELETED; - } - tomoyo_set_domain_flag(domain, true, domain->flags); - domain->profile = profile; - domain->quota_warned = false; - mb(); /* Avoid out-of-order execution. */ - domain->is_deleted = false; - goto out; + found = true; + break; } - /* No memory reusable. Create using new memory. */ - domain = tomoyo_alloc_element(sizeof(*domain)); - if (domain) { - INIT_LIST_HEAD(&domain->acl_info_list); - domain->domainname = saved_domainname; - domain->profile = profile; - list_add_tail(&domain->list, &tomoyo_domain_list); + if (!found && tomoyo_memory_ok(entry)) { + INIT_LIST_HEAD(&entry->acl_info_list); + entry->domainname = saved_domainname; + saved_domainname = NULL; + entry->profile = profile; + list_add_tail_rcu(&entry->list, &tomoyo_domain_list); + domain = entry; + entry = NULL; + found = true; } - out: - up_write(&tomoyo_domain_list_lock); - return domain; + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(saved_domainname); + kfree(entry); + return found ? domain : NULL; } /** @@ -789,6 +695,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * * @bprm: Pointer to "struct linux_binprm". * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_find_next_domain(struct linux_binprm *bprm) { @@ -796,7 +704,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * This function assumes that the size of buffer returned by * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. */ - struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp)); + struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); struct tomoyo_domain_info *old_domain = tomoyo_domain(); struct tomoyo_domain_info *domain = NULL; const char *old_domain_name = old_domain->domainname->name; @@ -849,8 +757,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (tomoyo_pathcmp(&r, &s)) { struct tomoyo_alias_entry *ptr; /* Is this program allowed to be called via symbolic links? */ - down_read(&tomoyo_alias_list_lock); - list_for_each_entry(ptr, &tomoyo_alias_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { if (ptr->is_deleted || tomoyo_pathcmp(&r, ptr->original_name) || tomoyo_pathcmp(&s, ptr->aliased_name)) @@ -861,7 +768,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) tomoyo_fill_path_info(&r); break; } - up_read(&tomoyo_alias_list_lock); } /* Check execute permission. */ @@ -892,9 +798,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN) goto done; - down_read(&tomoyo_domain_list_lock); domain = tomoyo_find_domain(new_domain_name); - up_read(&tomoyo_domain_list_lock); if (domain) goto done; if (is_enforce) @@ -909,14 +813,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (is_enforce) retval = -EPERM; else - tomoyo_set_domain_flag(old_domain, false, - TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED); + old_domain->transition_failed = true; out: if (!domain) domain = old_domain; + /* Update reference count on "struct tomoyo_domain_info". */ + atomic_inc(&domain->users); bprm->cred->security = domain; - tomoyo_free(real_program_name); - tomoyo_free(symlink_program_name); - tomoyo_free(tmp); + kfree(real_program_name); + kfree(symlink_program_name); + kfree(tmp); return retval; } diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 9a6c58881c0a..1b24304edb7d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -10,108 +10,64 @@ */ #include "common.h" -#include "tomoyo.h" -#include "realpath.h" - -/* - * tomoyo_globally_readable_file_entry is a structure which is used for holding - * "allow_read" entries. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_globally_readable_list . - * (2) "filename" is a pathname which is allowed to open(O_RDONLY). - * (3) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - */ -struct tomoyo_globally_readable_file_entry { - struct list_head list; - const struct tomoyo_path_info *filename; - bool is_deleted; -}; - -/* - * tomoyo_pattern_entry is a structure which is used for holding - * "tomoyo_pattern_list" entries. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_pattern_list . - * (2) "pattern" is a pathname pattern which is used for converting pathnames - * to pathname patterns during learning mode. - * (3) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - */ -struct tomoyo_pattern_entry { - struct list_head list; - const struct tomoyo_path_info *pattern; - bool is_deleted; -}; - -/* - * tomoyo_no_rewrite_entry is a structure which is used for holding - * "deny_rewrite" entries. - * It has following fields. - * - * (1) "list" which is linked to tomoyo_no_rewrite_list . - * (2) "pattern" is a pathname which is by default not permitted to modify - * already existing content. - * (3) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. - */ -struct tomoyo_no_rewrite_entry { - struct list_head list; - const struct tomoyo_path_info *pattern; - bool is_deleted; -}; /* Keyword array for single path operations. */ -static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = { - [TOMOYO_TYPE_READ_WRITE_ACL] = "read/write", - [TOMOYO_TYPE_EXECUTE_ACL] = "execute", - [TOMOYO_TYPE_READ_ACL] = "read", - [TOMOYO_TYPE_WRITE_ACL] = "write", - [TOMOYO_TYPE_CREATE_ACL] = "create", - [TOMOYO_TYPE_UNLINK_ACL] = "unlink", - [TOMOYO_TYPE_MKDIR_ACL] = "mkdir", - [TOMOYO_TYPE_RMDIR_ACL] = "rmdir", - [TOMOYO_TYPE_MKFIFO_ACL] = "mkfifo", - [TOMOYO_TYPE_MKSOCK_ACL] = "mksock", - [TOMOYO_TYPE_MKBLOCK_ACL] = "mkblock", - [TOMOYO_TYPE_MKCHAR_ACL] = "mkchar", - [TOMOYO_TYPE_TRUNCATE_ACL] = "truncate", - [TOMOYO_TYPE_SYMLINK_ACL] = "symlink", - [TOMOYO_TYPE_REWRITE_ACL] = "rewrite", +static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_READ_WRITE] = "read/write", + [TOMOYO_TYPE_EXECUTE] = "execute", + [TOMOYO_TYPE_READ] = "read", + [TOMOYO_TYPE_WRITE] = "write", + [TOMOYO_TYPE_CREATE] = "create", + [TOMOYO_TYPE_UNLINK] = "unlink", + [TOMOYO_TYPE_MKDIR] = "mkdir", + [TOMOYO_TYPE_RMDIR] = "rmdir", + [TOMOYO_TYPE_MKFIFO] = "mkfifo", + [TOMOYO_TYPE_MKSOCK] = "mksock", + [TOMOYO_TYPE_MKBLOCK] = "mkblock", + [TOMOYO_TYPE_MKCHAR] = "mkchar", + [TOMOYO_TYPE_TRUNCATE] = "truncate", + [TOMOYO_TYPE_SYMLINK] = "symlink", + [TOMOYO_TYPE_REWRITE] = "rewrite", + [TOMOYO_TYPE_IOCTL] = "ioctl", + [TOMOYO_TYPE_CHMOD] = "chmod", + [TOMOYO_TYPE_CHOWN] = "chown", + [TOMOYO_TYPE_CHGRP] = "chgrp", + [TOMOYO_TYPE_CHROOT] = "chroot", + [TOMOYO_TYPE_MOUNT] = "mount", + [TOMOYO_TYPE_UMOUNT] = "unmount", }; /* Keyword array for double path operations. */ -static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = { - [TOMOYO_TYPE_LINK_ACL] = "link", - [TOMOYO_TYPE_RENAME_ACL] = "rename", +static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { + [TOMOYO_TYPE_LINK] = "link", + [TOMOYO_TYPE_RENAME] = "rename", + [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", }; /** - * tomoyo_sp2keyword - Get the name of single path operation. + * tomoyo_path2keyword - Get the name of single path operation. * * @operation: Type of operation. * * Returns the name of single path operation. */ -const char *tomoyo_sp2keyword(const u8 operation) +const char *tomoyo_path2keyword(const u8 operation) { - return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION) - ? tomoyo_sp_keyword[operation] : NULL; + return (operation < TOMOYO_MAX_PATH_OPERATION) + ? tomoyo_path_keyword[operation] : NULL; } /** - * tomoyo_dp2keyword - Get the name of double path operation. + * tomoyo_path22keyword - Get the name of double path operation. * * @operation: Type of operation. * * Returns the name of double path operation. */ -const char *tomoyo_dp2keyword(const u8 operation) +const char *tomoyo_path22keyword(const u8 operation) { - return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION) - ? tomoyo_dp_keyword[operation] : NULL; + return (operation < TOMOYO_MAX_PATH2_OPERATION) + ? tomoyo_path2_keyword[operation] : NULL; } /** @@ -142,7 +98,8 @@ static bool tomoyo_strendswith(const char *name, const char *tail) static struct tomoyo_path_info *tomoyo_get_path(struct path *path) { int error; - struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf)); + struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf), + GFP_KERNEL); if (!buf) return NULL; @@ -154,20 +111,17 @@ static struct tomoyo_path_info *tomoyo_get_path(struct path *path) tomoyo_fill_path_info(&buf->head); return &buf->head; } - tomoyo_free(buf); + kfree(buf); return NULL; } -/* Lock for domain->acl_info_list. */ -DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock); - -static int tomoyo_update_double_path_acl(const u8 type, const char *filename1, - const char *filename2, - struct tomoyo_domain_info * - const domain, const bool is_delete); -static int tomoyo_update_single_path_acl(const u8 type, const char *filename, - struct tomoyo_domain_info * - const domain, const bool is_delete); +static int tomoyo_update_path2_acl(const u8 type, const char *filename1, + const char *filename2, + struct tomoyo_domain_info *const domain, + const bool is_delete); +static int tomoyo_update_path_acl(const u8 type, const char *filename, + struct tomoyo_domain_info *const domain, + const bool is_delete); /* * tomoyo_globally_readable_list is used for holding list of pathnames which @@ -194,8 +148,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename, * given "allow_read /lib/libc-2.5.so" to the domain which current process * belongs to. */ -static LIST_HEAD(tomoyo_globally_readable_list); -static DECLARE_RWSEM(tomoyo_globally_readable_list_lock); +LIST_HEAD(tomoyo_globally_readable_list); /** * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list. @@ -204,40 +157,42 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_globally_readable_entry(const char *filename, const bool is_delete) { - struct tomoyo_globally_readable_file_entry *new_entry; + struct tomoyo_globally_readable_file_entry *entry = NULL; struct tomoyo_globally_readable_file_entry *ptr; const struct tomoyo_path_info *saved_filename; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__)) + if (!tomoyo_is_correct_path(filename, 1, 0, -1)) return -EINVAL; - saved_filename = tomoyo_save_name(filename); + saved_filename = tomoyo_get_name(filename); if (!saved_filename) return -ENOMEM; - down_write(&tomoyo_globally_readable_list_lock); - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) { + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { if (ptr->filename != saved_filename) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->filename = saved_filename; + saved_filename = NULL; + list_add_tail_rcu(&entry->list, &tomoyo_globally_readable_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->filename = saved_filename; - list_add_tail(&new_entry->list, &tomoyo_globally_readable_list); - error = 0; - out: - up_write(&tomoyo_globally_readable_list_lock); + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(saved_filename); + kfree(entry); return error; } @@ -247,21 +202,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename, * @filename: The filename to check. * * Returns true if any domain can open @filename for reading, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * filename) { struct tomoyo_globally_readable_file_entry *ptr; bool found = false; - down_read(&tomoyo_globally_readable_list_lock); - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) { + + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { if (!ptr->is_deleted && tomoyo_path_matches_pattern(filename, ptr->filename)) { found = true; break; } } - up_read(&tomoyo_globally_readable_list_lock); return found; } @@ -272,6 +228,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) { @@ -284,13 +242,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; - down_read(&tomoyo_globally_readable_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_globally_readable_list) { struct tomoyo_globally_readable_file_entry *ptr; @@ -304,7 +263,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_globally_readable_list_lock); return done; } @@ -337,8 +295,7 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) * which pretends as if /proc/self/ is not a symlink; so that we can forbid * current process from accessing other process's information. */ -static LIST_HEAD(tomoyo_pattern_list); -static DECLARE_RWSEM(tomoyo_pattern_list_lock); +LIST_HEAD(tomoyo_pattern_list); /** * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list. @@ -347,40 +304,43 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_file_pattern_entry(const char *pattern, const bool is_delete) { - struct tomoyo_pattern_entry *new_entry; + struct tomoyo_pattern_entry *entry = NULL; struct tomoyo_pattern_entry *ptr; const struct tomoyo_path_info *saved_pattern; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__)) - return -EINVAL; - saved_pattern = tomoyo_save_name(pattern); + saved_pattern = tomoyo_get_name(pattern); if (!saved_pattern) - return -ENOMEM; - down_write(&tomoyo_pattern_list_lock); - list_for_each_entry(ptr, &tomoyo_pattern_list, list) { + return error; + if (!saved_pattern->is_patterned) + goto out; + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { if (saved_pattern != ptr->pattern) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->pattern = saved_pattern; + saved_pattern = NULL; + list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->pattern = saved_pattern; - list_add_tail(&new_entry->list, &tomoyo_pattern_list); - error = 0; + mutex_unlock(&tomoyo_policy_lock); out: - up_write(&tomoyo_pattern_list_lock); + kfree(entry); + tomoyo_put_name(saved_pattern); return error; } @@ -390,6 +350,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, * @filename: The filename to find patterned pathname. * * Returns pointer to pathname pattern if matched, @filename otherwise. + * + * Caller holds tomoyo_read_lock(). */ static const struct tomoyo_path_info * tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) @@ -397,8 +359,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) struct tomoyo_pattern_entry *ptr; const struct tomoyo_path_info *pattern = NULL; - down_read(&tomoyo_pattern_list_lock); - list_for_each_entry(ptr, &tomoyo_pattern_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { if (ptr->is_deleted) continue; if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) @@ -411,7 +372,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) break; } } - up_read(&tomoyo_pattern_list_lock); if (pattern) filename = pattern; return filename; @@ -424,6 +384,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_pattern_policy(char *data, const bool is_delete) { @@ -436,13 +398,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; - down_read(&tomoyo_pattern_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { struct tomoyo_pattern_entry *ptr; ptr = list_entry(pos, struct tomoyo_pattern_entry, list); @@ -453,7 +416,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_pattern_list_lock); return done; } @@ -486,8 +448,7 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) * " (deleted)" suffix if the file is already unlink()ed; so that we don't * need to worry whether the file is already unlink()ed or not. */ -static LIST_HEAD(tomoyo_no_rewrite_list); -static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock); +LIST_HEAD(tomoyo_no_rewrite_list); /** * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list. @@ -496,39 +457,42 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock); * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_no_rewrite_entry(const char *pattern, const bool is_delete) { - struct tomoyo_no_rewrite_entry *new_entry, *ptr; + struct tomoyo_no_rewrite_entry *entry = NULL; + struct tomoyo_no_rewrite_entry *ptr; const struct tomoyo_path_info *saved_pattern; - int error = -ENOMEM; + int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__)) + if (!tomoyo_is_correct_path(pattern, 0, 0, 0)) return -EINVAL; - saved_pattern = tomoyo_save_name(pattern); + saved_pattern = tomoyo_get_name(pattern); if (!saved_pattern) - return -ENOMEM; - down_write(&tomoyo_no_rewrite_list_lock); - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) { + return error; + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { if (ptr->pattern != saved_pattern) continue; ptr->is_deleted = is_delete; error = 0; - goto out; + break; } - if (is_delete) { - error = -ENOENT; - goto out; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->pattern = saved_pattern; + saved_pattern = NULL; + list_add_tail_rcu(&entry->list, &tomoyo_no_rewrite_list); + entry = NULL; + error = 0; } - new_entry = tomoyo_alloc_element(sizeof(*new_entry)); - if (!new_entry) - goto out; - new_entry->pattern = saved_pattern; - list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list); - error = 0; - out: - up_write(&tomoyo_no_rewrite_list_lock); + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(saved_pattern); + kfree(entry); return error; } @@ -539,14 +503,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, * * Returns true if @filename is specified by "deny_rewrite" directive, * false otherwise. + * + * Caller holds tomoyo_read_lock(). */ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) { struct tomoyo_no_rewrite_entry *ptr; bool found = false; - down_read(&tomoyo_no_rewrite_list_lock); - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { if (ptr->is_deleted) continue; if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) @@ -554,7 +519,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) found = true; break; } - up_read(&tomoyo_no_rewrite_list_lock); return found; } @@ -565,6 +529,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) { @@ -577,13 +543,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; - down_read(&tomoyo_no_rewrite_list_lock); list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) { struct tomoyo_no_rewrite_entry *ptr; ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list); @@ -594,7 +561,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) if (!done) break; } - up_read(&tomoyo_no_rewrite_list_lock); return done; } @@ -612,6 +578,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) * Current policy syntax uses "allow_read/write" instead of "6", * "allow_read" instead of "4", "allow_write" instead of "2", * "allow_execute" instead of "1". + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_file_acl(const char *filename, u8 perm, struct tomoyo_domain_info * const domain, @@ -629,19 +597,19 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm, */ return 0; if (perm & 4) - tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename, - domain, is_delete); + tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain, + is_delete); if (perm & 2) - tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename, - domain, is_delete); + tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain, + is_delete); if (perm & 1) - tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL, - filename, domain, is_delete); + tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain, + is_delete); return 0; } /** - * tomoyo_check_single_path_acl2 - Check permission for single path operation. + * tomoyo_path_acl2 - Check permission for single path operation. * * @domain: Pointer to "struct tomoyo_domain_info". * @filename: Filename to check. @@ -649,26 +617,28 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm, * @may_use_pattern: True if patterned ACL is permitted. * * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info * - domain, - const struct tomoyo_path_info * - filename, - const u16 perm, - const bool may_use_pattern) +static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain, + const struct tomoyo_path_info *filename, + const u32 perm, const bool may_use_pattern) { struct tomoyo_acl_info *ptr; int error = -EPERM; - down_read(&tomoyo_domain_acl_info_list_lock); - list_for_each_entry(ptr, &domain->acl_info_list, list) { - struct tomoyo_single_path_acl_record *acl; - if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL) - continue; - acl = container_of(ptr, struct tomoyo_single_path_acl_record, - head); - if (!(acl->perm & perm)) + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_acl *acl; + if (ptr->type != TOMOYO_TYPE_PATH_ACL) continue; + acl = container_of(ptr, struct tomoyo_path_acl, head); + if (perm <= 0xFFFF) { + if (!(acl->perm & perm)) + continue; + } else { + if (!(acl->perm_high & (perm >> 16))) + continue; + } if (may_use_pattern || !acl->filename->is_patterned) { if (!tomoyo_path_matches_pattern(filename, acl->filename)) @@ -679,7 +649,6 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info * error = 0; break; } - up_read(&tomoyo_domain_acl_info_list_lock); return error; } @@ -691,27 +660,28 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info * * @operation: Mode ("read" or "write" or "read/write" or "execute"). * * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain, const struct tomoyo_path_info *filename, const u8 operation) { - u16 perm = 0; + u32 perm = 0; if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) return 0; if (operation == 6) - perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL; + perm = 1 << TOMOYO_TYPE_READ_WRITE; else if (operation == 4) - perm = 1 << TOMOYO_TYPE_READ_ACL; + perm = 1 << TOMOYO_TYPE_READ; else if (operation == 2) - perm = 1 << TOMOYO_TYPE_WRITE_ACL; + perm = 1 << TOMOYO_TYPE_WRITE; else if (operation == 1) - perm = 1 << TOMOYO_TYPE_EXECUTE_ACL; + perm = 1 << TOMOYO_TYPE_EXECUTE; else BUG(); - return tomoyo_check_single_path_acl2(domain, filename, perm, - operation != 1); + return tomoyo_path_acl2(domain, filename, perm, operation != 1); } /** @@ -724,6 +694,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain, * @mode: Access control mode. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, const struct tomoyo_path_info *filename, @@ -737,18 +709,17 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, if (!filename) return 0; error = tomoyo_check_file_acl(domain, filename, perm); - if (error && perm == 4 && - (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0 + if (error && perm == 4 && !domain->ignore_global_allow_read && tomoyo_is_globally_readable_file(filename)) error = 0; if (perm == 6) - msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL); + msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE); else if (perm == 4) - msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL); + msg = tomoyo_path2keyword(TOMOYO_TYPE_READ); else if (perm == 2) - msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL); + msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE); else if (perm == 1) - msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL); + msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE); else BUG(); if (!error) @@ -777,6 +748,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, const bool is_delete) @@ -795,28 +768,28 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, if (strncmp(data, "allow_", 6)) goto out; data += 6; - for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) { - if (strcmp(data, tomoyo_sp_keyword[type])) + for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { + if (strcmp(data, tomoyo_path_keyword[type])) continue; - return tomoyo_update_single_path_acl(type, filename, - domain, is_delete); + return tomoyo_update_path_acl(type, filename, domain, + is_delete); } filename2 = strchr(filename, ' '); if (!filename2) goto out; *filename2++ = '\0'; - for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) { - if (strcmp(data, tomoyo_dp_keyword[type])) + for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { + if (strcmp(data, tomoyo_path2_keyword[type])) continue; - return tomoyo_update_double_path_acl(type, filename, filename2, - domain, is_delete); + return tomoyo_update_path2_acl(type, filename, filename2, + domain, is_delete); } out: return -EINVAL; } /** - * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list. + * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. * * @type: Type of operation. * @filename: Filename. @@ -824,85 +797,82 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_single_path_acl(const u8 type, const char *filename, - struct tomoyo_domain_info * - const domain, const bool is_delete) +static int tomoyo_update_path_acl(const u8 type, const char *filename, + struct tomoyo_domain_info *const domain, + const bool is_delete) { - static const u16 rw_mask = - (1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL); + static const u32 rw_mask = + (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); const struct tomoyo_path_info *saved_filename; struct tomoyo_acl_info *ptr; - struct tomoyo_single_path_acl_record *acl; - int error = -ENOMEM; - const u16 perm = 1 << type; + struct tomoyo_path_acl *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; + const u32 perm = 1 << type; if (!domain) return -EINVAL; - if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__)) + if (!tomoyo_is_correct_path(filename, 0, 0, 0)) return -EINVAL; - saved_filename = tomoyo_save_name(filename); + saved_filename = tomoyo_get_name(filename); if (!saved_filename) return -ENOMEM; - down_write(&tomoyo_domain_acl_info_list_lock); - if (is_delete) - goto delete; - list_for_each_entry(ptr, &domain->acl_info_list, list) { - if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL) + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_acl *acl = + container_of(ptr, struct tomoyo_path_acl, head); + if (ptr->type != TOMOYO_TYPE_PATH_ACL) continue; - acl = container_of(ptr, struct tomoyo_single_path_acl_record, - head); if (acl->filename != saved_filename) continue; - /* Special case. Clear all bits if marked as deleted. */ - if (ptr->type & TOMOYO_ACL_DELETED) - acl->perm = 0; - acl->perm |= perm; - if ((acl->perm & rw_mask) == rw_mask) - acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL; - else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)) - acl->perm |= rw_mask; - ptr->type &= ~TOMOYO_ACL_DELETED; + if (is_delete) { + if (perm <= 0xFFFF) + acl->perm &= ~perm; + else + acl->perm_high &= ~(perm >> 16); + if ((acl->perm & rw_mask) != rw_mask) + acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); + else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) + acl->perm &= ~rw_mask; + } else { + if (perm <= 0xFFFF) + acl->perm |= perm; + else + acl->perm_high |= (perm >> 16); + if ((acl->perm & rw_mask) == rw_mask) + acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; + else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) + acl->perm |= rw_mask; + } error = 0; - goto out; + break; } - /* Not found. Append it to the tail. */ - acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL); - if (!acl) - goto out; - acl->perm = perm; - if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL)) - acl->perm |= rw_mask; - acl->filename = saved_filename; - list_add_tail(&acl->head.list, &domain->acl_info_list); - error = 0; - goto out; - delete: - error = -ENOENT; - list_for_each_entry(ptr, &domain->acl_info_list, list) { - if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL) - continue; - acl = container_of(ptr, struct tomoyo_single_path_acl_record, - head); - if (acl->filename != saved_filename) - continue; - acl->perm &= ~perm; - if ((acl->perm & rw_mask) != rw_mask) - acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL); - else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))) - acl->perm &= ~rw_mask; - if (!acl->perm) - ptr->type |= TOMOYO_ACL_DELETED; + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->head.type = TOMOYO_TYPE_PATH_ACL; + if (perm <= 0xFFFF) + entry->perm = perm; + else + entry->perm_high = (perm >> 16); + if (perm == (1 << TOMOYO_TYPE_READ_WRITE)) + entry->perm |= rw_mask; + entry->filename = saved_filename; + saved_filename = NULL; + list_add_tail_rcu(&entry->head.list, &domain->acl_info_list); + entry = NULL; error = 0; - break; } - out: - up_write(&tomoyo_domain_acl_info_list_lock); + mutex_unlock(&tomoyo_policy_lock); + kfree(entry); + tomoyo_put_name(saved_filename); return error; } /** - * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list. + * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. * * @type: Type of operation. * @filename1: First filename. @@ -911,98 +881,88 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename, * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_double_path_acl(const u8 type, const char *filename1, - const char *filename2, - struct tomoyo_domain_info * - const domain, const bool is_delete) +static int tomoyo_update_path2_acl(const u8 type, const char *filename1, + const char *filename2, + struct tomoyo_domain_info *const domain, + const bool is_delete) { const struct tomoyo_path_info *saved_filename1; const struct tomoyo_path_info *saved_filename2; struct tomoyo_acl_info *ptr; - struct tomoyo_double_path_acl_record *acl; - int error = -ENOMEM; + struct tomoyo_path2_acl *entry = NULL; + int error = is_delete ? -ENOENT : -ENOMEM; const u8 perm = 1 << type; if (!domain) return -EINVAL; - if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) || - !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__)) + if (!tomoyo_is_correct_path(filename1, 0, 0, 0) || + !tomoyo_is_correct_path(filename2, 0, 0, 0)) return -EINVAL; - saved_filename1 = tomoyo_save_name(filename1); - saved_filename2 = tomoyo_save_name(filename2); + saved_filename1 = tomoyo_get_name(filename1); + saved_filename2 = tomoyo_get_name(filename2); if (!saved_filename1 || !saved_filename2) - return -ENOMEM; - down_write(&tomoyo_domain_acl_info_list_lock); - if (is_delete) - goto delete; - list_for_each_entry(ptr, &domain->acl_info_list, list) { - if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL) - continue; - acl = container_of(ptr, struct tomoyo_double_path_acl_record, - head); - if (acl->filename1 != saved_filename1 || - acl->filename2 != saved_filename2) - continue; - /* Special case. Clear all bits if marked as deleted. */ - if (ptr->type & TOMOYO_ACL_DELETED) - acl->perm = 0; - acl->perm |= perm; - ptr->type &= ~TOMOYO_ACL_DELETED; - error = 0; goto out; - } - /* Not found. Append it to the tail. */ - acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL); - if (!acl) - goto out; - acl->perm = perm; - acl->filename1 = saved_filename1; - acl->filename2 = saved_filename2; - list_add_tail(&acl->head.list, &domain->acl_info_list); - error = 0; - goto out; - delete: - error = -ENOENT; - list_for_each_entry(ptr, &domain->acl_info_list, list) { - if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL) + if (!is_delete) + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path2_acl *acl = + container_of(ptr, struct tomoyo_path2_acl, head); + if (ptr->type != TOMOYO_TYPE_PATH2_ACL) continue; - acl = container_of(ptr, struct tomoyo_double_path_acl_record, - head); if (acl->filename1 != saved_filename1 || acl->filename2 != saved_filename2) continue; - acl->perm &= ~perm; - if (!acl->perm) - ptr->type |= TOMOYO_ACL_DELETED; + if (is_delete) + acl->perm &= ~perm; + else + acl->perm |= perm; error = 0; break; } + if (!is_delete && error && tomoyo_memory_ok(entry)) { + entry->head.type = TOMOYO_TYPE_PATH2_ACL; + entry->perm = perm; + entry->filename1 = saved_filename1; + saved_filename1 = NULL; + entry->filename2 = saved_filename2; + saved_filename2 = NULL; + list_add_tail_rcu(&entry->head.list, &domain->acl_info_list); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); out: - up_write(&tomoyo_domain_acl_info_list_lock); + tomoyo_put_name(saved_filename1); + tomoyo_put_name(saved_filename2); + kfree(entry); return error; } /** - * tomoyo_check_single_path_acl - Check permission for single path operation. + * tomoyo_path_acl - Check permission for single path operation. * * @domain: Pointer to "struct tomoyo_domain_info". * @type: Type of operation. * @filename: Filename to check. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain, - const u8 type, - const struct tomoyo_path_info *filename) +static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type, + const struct tomoyo_path_info *filename) { if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) return 0; - return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1); + return tomoyo_path_acl2(domain, filename, 1 << type, 1); } /** - * tomoyo_check_double_path_acl - Check permission for double path operation. + * tomoyo_path2_acl - Check permission for double path operation. * * @domain: Pointer to "struct tomoyo_domain_info". * @type: Type of operation. @@ -1010,13 +970,13 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain, * @filename2: Second filename to check. * * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain, - const u8 type, - const struct tomoyo_path_info * - filename1, - const struct tomoyo_path_info * - filename2) +static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain, + const u8 type, + const struct tomoyo_path_info *filename1, + const struct tomoyo_path_info *filename2) { struct tomoyo_acl_info *ptr; const u8 perm = 1 << type; @@ -1024,13 +984,11 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain, if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) return 0; - down_read(&tomoyo_domain_acl_info_list_lock); - list_for_each_entry(ptr, &domain->acl_info_list, list) { - struct tomoyo_double_path_acl_record *acl; - if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL) + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path2_acl *acl; + if (ptr->type != TOMOYO_TYPE_PATH2_ACL) continue; - acl = container_of(ptr, struct tomoyo_double_path_acl_record, - head); + acl = container_of(ptr, struct tomoyo_path2_acl, head); if (!(acl->perm & perm)) continue; if (!tomoyo_path_matches_pattern(filename1, acl->filename1)) @@ -1040,12 +998,11 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain, error = 0; break; } - up_read(&tomoyo_domain_acl_info_list_lock); return error; } /** - * tomoyo_check_single_path_permission2 - Check permission for single path operation. + * tomoyo_path_permission2 - Check permission for single path operation. * * @domain: Pointer to "struct tomoyo_domain_info". * @operation: Type of operation. @@ -1053,11 +1010,13 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain, * @mode: Access control mode. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info * - const domain, u8 operation, - const struct tomoyo_path_info * - filename, const u8 mode) +static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain, + u8 operation, + const struct tomoyo_path_info *filename, + const u8 mode) { const char *msg; int error; @@ -1066,8 +1025,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info * if (!mode) return 0; next: - error = tomoyo_check_single_path_acl(domain, operation, filename); - msg = tomoyo_sp2keyword(operation); + error = tomoyo_path_acl(domain, operation, filename); + msg = tomoyo_path2keyword(operation); if (!error) goto ok; if (tomoyo_verbose_mode(domain)) @@ -1076,7 +1035,7 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info * tomoyo_get_last_name(domain)); if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { const char *name = tomoyo_get_file_pattern(filename)->name; - tomoyo_update_single_path_acl(operation, name, domain, false); + tomoyo_update_path_acl(operation, name, domain, false); } if (!is_enforce) error = 0; @@ -1086,9 +1045,9 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info * * we need to check "allow_rewrite" permission if the filename is * specified by "deny_rewrite" keyword. */ - if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL && + if (!error && operation == TOMOYO_TYPE_TRUNCATE && tomoyo_is_no_rewrite_file(filename)) { - operation = TOMOYO_TYPE_REWRITE_ACL; + operation = TOMOYO_TYPE_REWRITE; goto next; } return error; @@ -1101,6 +1060,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info * * @filename: Check permission for "execute". * * Returns 0 on success, negativevalue otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, const struct tomoyo_path_info *filename) @@ -1129,6 +1090,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct tomoyo_path_info *buf; const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == 3); + int idx; if (!mode || !path->mnt) return 0; @@ -1140,6 +1102,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, * don't call me. */ return 0; + idx = tomoyo_read_lock(); buf = tomoyo_get_path(path); if (!buf) goto out; @@ -1152,49 +1115,50 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, if ((acc_mode & MAY_WRITE) && ((flag & O_TRUNC) || !(flag & O_APPEND)) && (tomoyo_is_no_rewrite_file(buf))) { - error = tomoyo_check_single_path_permission2(domain, - TOMOYO_TYPE_REWRITE_ACL, - buf, mode); + error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, + buf, mode); } if (!error) error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open", mode); if (!error && (flag & O_TRUNC)) - error = tomoyo_check_single_path_permission2(domain, - TOMOYO_TYPE_TRUNCATE_ACL, - buf, mode); + error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE, + buf, mode); out: - tomoyo_free(buf); + kfree(buf); + tomoyo_read_unlock(idx); if (!is_enforce) error = 0; return error; } /** - * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink". + * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". * - * @domain: Pointer to "struct tomoyo_domain_info". * @operation: Type of operation. * @path: Pointer to "struct path". * * Returns 0 on success, negative value otherwise. */ -int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain, - const u8 operation, struct path *path) +int tomoyo_path_perm(const u8 operation, struct path *path) { int error = -ENOMEM; struct tomoyo_path_info *buf; + struct tomoyo_domain_info *domain = tomoyo_domain(); const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == 3); + int idx; if (!mode || !path->mnt) return 0; + idx = tomoyo_read_lock(); buf = tomoyo_get_path(path); if (!buf) goto out; switch (operation) { - case TOMOYO_TYPE_MKDIR_ACL: - case TOMOYO_TYPE_RMDIR_ACL: + case TOMOYO_TYPE_MKDIR: + case TOMOYO_TYPE_RMDIR: + case TOMOYO_TYPE_CHROOT: if (!buf->is_dir) { /* * tomoyo_get_path() reserves space for appending "/." @@ -1203,10 +1167,10 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain, tomoyo_fill_path_info(buf); } } - error = tomoyo_check_single_path_permission2(domain, operation, buf, - mode); + error = tomoyo_path_permission2(domain, operation, buf, mode); out: - tomoyo_free(buf); + kfree(buf); + tomoyo_read_unlock(idx); if (!is_enforce) error = 0; return error; @@ -1215,21 +1179,23 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain, /** * tomoyo_check_rewrite_permission - Check permission for "rewrite". * - * @domain: Pointer to "struct tomoyo_domain_info". * @filp: Pointer to "struct file". * * Returns 0 on success, negative value otherwise. */ -int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain, - struct file *filp) +int tomoyo_check_rewrite_permission(struct file *filp) { int error = -ENOMEM; + struct tomoyo_domain_info *domain = tomoyo_domain(); const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == 3); struct tomoyo_path_info *buf; + int idx; if (!mode || !filp->f_path.mnt) return 0; + + idx = tomoyo_read_lock(); buf = tomoyo_get_path(&filp->f_path); if (!buf) goto out; @@ -1237,38 +1203,38 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain, error = 0; goto out; } - error = tomoyo_check_single_path_permission2(domain, - TOMOYO_TYPE_REWRITE_ACL, - buf, mode); + error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode); out: - tomoyo_free(buf); + kfree(buf); + tomoyo_read_unlock(idx); if (!is_enforce) error = 0; return error; } /** - * tomoyo_check_2path_perm - Check permission for "rename" and "link". + * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root". * - * @domain: Pointer to "struct tomoyo_domain_info". * @operation: Type of operation. * @path1: Pointer to "struct path". * @path2: Pointer to "struct path". * * Returns 0 on success, negative value otherwise. */ -int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain, - const u8 operation, struct path *path1, - struct path *path2) +int tomoyo_path2_perm(const u8 operation, struct path *path1, + struct path *path2) { int error = -ENOMEM; struct tomoyo_path_info *buf1, *buf2; + struct tomoyo_domain_info *domain = tomoyo_domain(); const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == 3); const char *msg; + int idx; if (!mode || !path1->mnt || !path2->mnt) return 0; + idx = tomoyo_read_lock(); buf1 = tomoyo_get_path(path1); buf2 = tomoyo_get_path(path2); if (!buf1 || !buf2) @@ -1289,8 +1255,8 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain, } } } - error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2); - msg = tomoyo_dp2keyword(operation); + error = tomoyo_path2_acl(domain, operation, buf1, buf2); + msg = tomoyo_path22keyword(operation); if (!error) goto out; if (tomoyo_verbose_mode(domain)) @@ -1301,12 +1267,13 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain, if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { const char *name1 = tomoyo_get_file_pattern(buf1)->name; const char *name2 = tomoyo_get_file_pattern(buf2)->name; - tomoyo_update_double_path_acl(operation, name1, name2, domain, - false); + tomoyo_update_path2_acl(operation, name1, name2, domain, + false); } out: - tomoyo_free(buf1); - tomoyo_free(buf2); + kfree(buf1); + kfree(buf2); + tomoyo_read_unlock(idx); if (!is_enforce) error = 0; return error; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c new file mode 100644 index 000000000000..9645525ccdd4 --- /dev/null +++ b/security/tomoyo/gc.c @@ -0,0 +1,370 @@ +/* + * security/tomoyo/gc.c + * + * Implementation of the Domain-Based Mandatory Access Control. + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + * + */ + +#include "common.h" +#include <linux/kthread.h> + +enum tomoyo_gc_id { + TOMOYO_ID_DOMAIN_INITIALIZER, + TOMOYO_ID_DOMAIN_KEEPER, + TOMOYO_ID_ALIAS, + TOMOYO_ID_GLOBALLY_READABLE, + TOMOYO_ID_PATTERN, + TOMOYO_ID_NO_REWRITE, + TOMOYO_ID_MANAGER, + TOMOYO_ID_NAME, + TOMOYO_ID_ACL, + TOMOYO_ID_DOMAIN +}; + +struct tomoyo_gc_entry { + struct list_head list; + int type; + void *element; +}; +static LIST_HEAD(tomoyo_gc_queue); +static DEFINE_MUTEX(tomoyo_gc_mutex); + +/* Caller holds tomoyo_policy_lock mutex. */ +static bool tomoyo_add_to_gc(const int type, void *element) +{ + struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return false; + entry->type = type; + entry->element = element; + list_add(&entry->list, &tomoyo_gc_queue); + return true; +} + +static void tomoyo_del_allow_read +(struct tomoyo_globally_readable_file_entry *ptr) +{ + tomoyo_put_name(ptr->filename); +} + +static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr) +{ + tomoyo_put_name(ptr->pattern); +} + +static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr) +{ + tomoyo_put_name(ptr->pattern); +} + +static void tomoyo_del_domain_initializer +(struct tomoyo_domain_initializer_entry *ptr) +{ + tomoyo_put_name(ptr->domainname); + tomoyo_put_name(ptr->program); +} + +static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr) +{ + tomoyo_put_name(ptr->domainname); + tomoyo_put_name(ptr->program); +} + +static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr) +{ + tomoyo_put_name(ptr->original_name); + tomoyo_put_name(ptr->aliased_name); +} + +static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr) +{ + tomoyo_put_name(ptr->manager); +} + +static void tomoyo_del_acl(struct tomoyo_acl_info *acl) +{ + switch (acl->type) { + case TOMOYO_TYPE_PATH_ACL: + { + struct tomoyo_path_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name(entry->filename); + } + break; + case TOMOYO_TYPE_PATH2_ACL: + { + struct tomoyo_path2_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name(entry->filename1); + tomoyo_put_name(entry->filename2); + } + break; + default: + printk(KERN_WARNING "Unknown type\n"); + break; + } +} + +static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) +{ + struct tomoyo_acl_info *acl; + struct tomoyo_acl_info *tmp; + /* + * Since we don't protect whole execve() operation using SRCU, + * we need to recheck domain->users at this point. + * + * (1) Reader starts SRCU section upon execve(). + * (2) Reader traverses tomoyo_domain_list and finds this domain. + * (3) Writer marks this domain as deleted. + * (4) Garbage collector removes this domain from tomoyo_domain_list + * because this domain is marked as deleted and used by nobody. + * (5) Reader saves reference to this domain into + * "struct linux_binprm"->cred->security . + * (6) Reader finishes SRCU section, although execve() operation has + * not finished yet. + * (7) Garbage collector waits for SRCU synchronization. + * (8) Garbage collector kfree() this domain because this domain is + * used by nobody. + * (9) Reader finishes execve() operation and restores this domain from + * "struct linux_binprm"->cred->security. + * + * By updating domain->users at (5), we can solve this race problem + * by rechecking domain->users at (8). + */ + if (atomic_read(&domain->users)) + return false; + list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { + tomoyo_del_acl(acl); + tomoyo_memory_free(acl); + } + tomoyo_put_name(domain->domainname); + return true; +} + + +static void tomoyo_del_name(const struct tomoyo_name_entry *ptr) +{ +} + +static void tomoyo_collect_entry(void) +{ + mutex_lock(&tomoyo_policy_lock); + { + struct tomoyo_globally_readable_file_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, + list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_pattern_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_no_rewrite_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_domain_initializer_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, + list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_domain_keeper_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_alias_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_policy_manager_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, + list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } + { + struct tomoyo_domain_info *domain; + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + struct tomoyo_acl_info *acl; + list_for_each_entry_rcu(acl, &domain->acl_info_list, + list) { + switch (acl->type) { + case TOMOYO_TYPE_PATH_ACL: + if (container_of(acl, + struct tomoyo_path_acl, + head)->perm || + container_of(acl, + struct tomoyo_path_acl, + head)->perm_high) + continue; + break; + case TOMOYO_TYPE_PATH2_ACL: + if (container_of(acl, + struct tomoyo_path2_acl, + head)->perm) + continue; + break; + default: + continue; + } + if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl)) + list_del_rcu(&acl->list); + else + break; + } + if (!domain->is_deleted || atomic_read(&domain->users)) + continue; + /* + * Nobody is referring this domain. But somebody may + * refer this domain after successful execve(). + * We recheck domain->users after SRCU synchronization. + */ + if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain)) + list_del_rcu(&domain->list); + else + break; + } + } + mutex_unlock(&tomoyo_policy_lock); + mutex_lock(&tomoyo_name_list_lock); + { + int i; + for (i = 0; i < TOMOYO_MAX_HASH; i++) { + struct tomoyo_name_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], + list) { + if (atomic_read(&ptr->users)) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) + list_del_rcu(&ptr->list); + else { + i = TOMOYO_MAX_HASH; + break; + } + } + } + } + mutex_unlock(&tomoyo_name_list_lock); +} + +static void tomoyo_kfree_entry(void) +{ + struct tomoyo_gc_entry *p; + struct tomoyo_gc_entry *tmp; + + list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { + switch (p->type) { + case TOMOYO_ID_DOMAIN_INITIALIZER: + tomoyo_del_domain_initializer(p->element); + break; + case TOMOYO_ID_DOMAIN_KEEPER: + tomoyo_del_domain_keeper(p->element); + break; + case TOMOYO_ID_ALIAS: + tomoyo_del_alias(p->element); + break; + case TOMOYO_ID_GLOBALLY_READABLE: + tomoyo_del_allow_read(p->element); + break; + case TOMOYO_ID_PATTERN: + tomoyo_del_file_pattern(p->element); + break; + case TOMOYO_ID_NO_REWRITE: + tomoyo_del_no_rewrite(p->element); + break; + case TOMOYO_ID_MANAGER: + tomoyo_del_manager(p->element); + break; + case TOMOYO_ID_NAME: + tomoyo_del_name(p->element); + break; + case TOMOYO_ID_ACL: + tomoyo_del_acl(p->element); + break; + case TOMOYO_ID_DOMAIN: + if (!tomoyo_del_domain(p->element)) + continue; + break; + default: + printk(KERN_WARNING "Unknown type\n"); + break; + } + tomoyo_memory_free(p->element); + list_del(&p->list); + kfree(p); + } +} + +static int tomoyo_gc_thread(void *unused) +{ + daemonize("GC for TOMOYO"); + if (mutex_trylock(&tomoyo_gc_mutex)) { + int i; + for (i = 0; i < 10; i++) { + tomoyo_collect_entry(); + if (list_empty(&tomoyo_gc_queue)) + break; + synchronize_srcu(&tomoyo_ss); + tomoyo_kfree_entry(); + } + mutex_unlock(&tomoyo_gc_mutex); + } + do_exit(0); +} + +void tomoyo_run_gc(void) +{ + struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, + "GC for TOMOYO"); + if (!IS_ERR(task)) + wake_up_process(task); +} diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 18369d497eb8..c00df45c7ede 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -14,9 +14,8 @@ #include <linux/mnt_namespace.h> #include <linux/fs_struct.h> #include <linux/hash.h> - +#include <linux/magic.h> #include "common.h" -#include "realpath.h" /** * tomoyo_encode: Convert binary string to ascii string. @@ -112,7 +111,7 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, path_put(&ns_root); /* Prepend "/proc" prefix if using internal proc vfs mount. */ if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) && - (strcmp(path->mnt->mnt_sb->s_type->name, "proc") == 0)) { + (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { sp -= 5; if (sp >= newname) memcpy(sp, "/proc", 5); @@ -149,12 +148,12 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, * * Returns the realpath of the given @path on success, NULL otherwise. * - * These functions use tomoyo_alloc(), so the caller must call tomoyo_free() + * These functions use kzalloc(), so the caller must call kfree() * if these functions didn't return NULL. */ char *tomoyo_realpath_from_path(struct path *path) { - char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer)); + char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_KERNEL); BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) <= TOMOYO_MAX_PATHNAME_LEN - 1); @@ -163,7 +162,7 @@ char *tomoyo_realpath_from_path(struct path *path) if (tomoyo_realpath_from_path2(path, buf, TOMOYO_MAX_PATHNAME_LEN - 1) == 0) return buf; - tomoyo_free(buf); + kfree(buf); return NULL; } @@ -206,98 +205,47 @@ char *tomoyo_realpath_nofollow(const char *pathname) } /* Memory allocated for non-string data. */ -static unsigned int tomoyo_allocated_memory_for_elements; -/* Quota for holding non-string data. */ -static unsigned int tomoyo_quota_for_elements; +static atomic_t tomoyo_policy_memory_size; +/* Quota for holding policy. */ +static unsigned int tomoyo_quota_for_policy; /** - * tomoyo_alloc_element - Allocate permanent memory for structures. + * tomoyo_memory_ok - Check memory quota. * - * @size: Size in bytes. + * @ptr: Pointer to allocated memory. * - * Returns pointer to allocated memory on success, NULL otherwise. + * Returns true on success, false otherwise. * - * Memory has to be zeroed. - * The RAM is chunked, so NEVER try to kfree() the returned pointer. + * Caller holds tomoyo_policy_lock. + * Memory pointed by @ptr will be zeroed on success. */ -void *tomoyo_alloc_element(const unsigned int size) +bool tomoyo_memory_ok(void *ptr) { - static char *buf; - static DEFINE_MUTEX(lock); - static unsigned int buf_used_len = PATH_MAX; - char *ptr = NULL; - /*Assumes sizeof(void *) >= sizeof(long) is true. */ - const unsigned int word_aligned_size - = roundup(size, max(sizeof(void *), sizeof(long))); - if (word_aligned_size > PATH_MAX) - return NULL; - mutex_lock(&lock); - if (buf_used_len + word_aligned_size > PATH_MAX) { - if (!tomoyo_quota_for_elements || - tomoyo_allocated_memory_for_elements - + PATH_MAX <= tomoyo_quota_for_elements) - ptr = kzalloc(PATH_MAX, GFP_KERNEL); - if (!ptr) { - printk(KERN_WARNING "ERROR: Out of memory " - "for tomoyo_alloc_element().\n"); - if (!tomoyo_policy_loaded) - panic("MAC Initialization failed.\n"); - } else { - buf = ptr; - tomoyo_allocated_memory_for_elements += PATH_MAX; - buf_used_len = word_aligned_size; - ptr = buf; - } - } else if (word_aligned_size) { - int i; - ptr = buf + buf_used_len; - buf_used_len += word_aligned_size; - for (i = 0; i < word_aligned_size; i++) { - if (!ptr[i]) - continue; - printk(KERN_ERR "WARNING: Reserved memory was tainted! " - "The system might go wrong.\n"); - ptr[i] = '\0'; - } + int allocated_len = ptr ? ksize(ptr) : 0; + atomic_add(allocated_len, &tomoyo_policy_memory_size); + if (ptr && (!tomoyo_quota_for_policy || + atomic_read(&tomoyo_policy_memory_size) + <= tomoyo_quota_for_policy)) { + memset(ptr, 0, allocated_len); + return true; } - mutex_unlock(&lock); - return ptr; + printk(KERN_WARNING "ERROR: Out of memory " + "for tomoyo_alloc_element().\n"); + if (!tomoyo_policy_loaded) + panic("MAC Initialization failed.\n"); + return false; } -/* Memory allocated for string data in bytes. */ -static unsigned int tomoyo_allocated_memory_for_savename; -/* Quota for holding string data in bytes. */ -static unsigned int tomoyo_quota_for_savename; - -/* - * TOMOYO uses this hash only when appending a string into the string - * table. Frequency of appending strings is very low. So we don't need - * large (e.g. 64k) hash size. 256 will be sufficient. - */ -#define TOMOYO_HASH_BITS 8 -#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) - -/* - * tomoyo_name_entry is a structure which is used for linking - * "struct tomoyo_path_info" into tomoyo_name_list . +/** + * tomoyo_memory_free - Free memory for elements. * - * Since tomoyo_name_list manages a list of strings which are shared by - * multiple processes (whereas "struct tomoyo_path_info" inside - * "struct tomoyo_path_info_with_data" is not shared), a reference counter will - * be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info" - * when TOMOYO starts supporting garbage collector. + * @ptr: Pointer to allocated memory. */ -struct tomoyo_name_entry { - struct list_head list; - struct tomoyo_path_info entry; -}; - -/* Structure for available memory region. */ -struct tomoyo_free_memory_block_list { - struct list_head list; - char *ptr; /* Pointer to a free area. */ - int len; /* Length of the area. */ -}; +void tomoyo_memory_free(void *ptr) +{ + atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); + kfree(ptr); +} /* * tomoyo_name_list is used for holding string data used by TOMOYO. @@ -305,87 +253,58 @@ struct tomoyo_free_memory_block_list { * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of * "const struct tomoyo_path_info *". */ -static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; +struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; +/* Lock for protecting tomoyo_name_list . */ +DEFINE_MUTEX(tomoyo_name_list_lock); /** - * tomoyo_save_name - Allocate permanent memory for string data. + * tomoyo_get_name - Allocate permanent memory for string data. * * @name: The string to store into the permernent memory. * * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. - * - * The RAM is shared, so NEVER try to modify or kfree() the returned name. */ -const struct tomoyo_path_info *tomoyo_save_name(const char *name) +const struct tomoyo_path_info *tomoyo_get_name(const char *name) { - static LIST_HEAD(fmb_list); - static DEFINE_MUTEX(lock); struct tomoyo_name_entry *ptr; unsigned int hash; - /* fmb contains available size in bytes. - fmb is removed from the fmb_list when fmb->len becomes 0. */ - struct tomoyo_free_memory_block_list *fmb; int len; - char *cp; + int allocated_len; struct list_head *head; if (!name) return NULL; len = strlen(name) + 1; - if (len > TOMOYO_MAX_PATHNAME_LEN) { - printk(KERN_WARNING "ERROR: Name too long " - "for tomoyo_save_name().\n"); - return NULL; - } hash = full_name_hash((const unsigned char *) name, len - 1); head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; - - mutex_lock(&lock); + mutex_lock(&tomoyo_name_list_lock); list_for_each_entry(ptr, head, list) { - if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name)) - goto out; - } - list_for_each_entry(fmb, &fmb_list, list) { - if (len <= fmb->len) - goto ready; + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) + continue; + atomic_inc(&ptr->users); + goto out; } - if (!tomoyo_quota_for_savename || - tomoyo_allocated_memory_for_savename + PATH_MAX - <= tomoyo_quota_for_savename) - cp = kzalloc(PATH_MAX, GFP_KERNEL); - else - cp = NULL; - fmb = kzalloc(sizeof(*fmb), GFP_KERNEL); - if (!cp || !fmb) { - kfree(cp); - kfree(fmb); + ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL); + allocated_len = ptr ? ksize(ptr) : 0; + if (!ptr || (tomoyo_quota_for_policy && + atomic_read(&tomoyo_policy_memory_size) + allocated_len + > tomoyo_quota_for_policy)) { + kfree(ptr); printk(KERN_WARNING "ERROR: Out of memory " - "for tomoyo_save_name().\n"); + "for tomoyo_get_name().\n"); if (!tomoyo_policy_loaded) panic("MAC Initialization failed.\n"); ptr = NULL; goto out; } - tomoyo_allocated_memory_for_savename += PATH_MAX; - list_add(&fmb->list, &fmb_list); - fmb->ptr = cp; - fmb->len = PATH_MAX; - ready: - ptr = tomoyo_alloc_element(sizeof(*ptr)); - if (!ptr) - goto out; - ptr->entry.name = fmb->ptr; - memmove(fmb->ptr, name, len); + atomic_add(allocated_len, &tomoyo_policy_memory_size); + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); + memmove((char *) ptr->entry.name, name, len); + atomic_set(&ptr->users, 1); tomoyo_fill_path_info(&ptr->entry); - fmb->ptr += len; - fmb->len -= len; list_add_tail(&ptr->list, head); - if (fmb->len == 0) { - list_del(&fmb->list); - kfree(fmb); - } out: - mutex_unlock(&lock); + mutex_unlock(&tomoyo_name_list_lock); return ptr ? &ptr->entry : NULL; } @@ -400,45 +319,14 @@ void __init tomoyo_realpath_init(void) for (i = 0; i < TOMOYO_MAX_HASH; i++) INIT_LIST_HEAD(&tomoyo_name_list[i]); INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); - tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME); - list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list); - down_read(&tomoyo_domain_list_lock); + tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); + /* + * tomoyo_read_lock() is not needed because this function is + * called before the first "delete" request. + */ + list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) panic("Can't register tomoyo_kernel_domain"); - up_read(&tomoyo_domain_list_lock); -} - -/* Memory allocated for temporary purpose. */ -static atomic_t tomoyo_dynamic_memory_size; - -/** - * tomoyo_alloc - Allocate memory for temporary purpose. - * - * @size: Size in bytes. - * - * Returns pointer to allocated memory on success, NULL otherwise. - */ -void *tomoyo_alloc(const size_t size) -{ - void *p = kzalloc(size, GFP_KERNEL); - if (p) - atomic_add(ksize(p), &tomoyo_dynamic_memory_size); - return p; -} - -/** - * tomoyo_free - Release memory allocated by tomoyo_alloc(). - * - * @p: Pointer returned by tomoyo_alloc(). May be NULL. - * - * Returns nothing. - */ -void tomoyo_free(const void *p) -{ - if (p) { - atomic_sub(ksize(p), &tomoyo_dynamic_memory_size); - kfree(p); - } } /** @@ -451,32 +339,19 @@ void tomoyo_free(const void *p) int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) { if (!head->read_eof) { - const unsigned int shared - = tomoyo_allocated_memory_for_savename; - const unsigned int private - = tomoyo_allocated_memory_for_elements; - const unsigned int dynamic - = atomic_read(&tomoyo_dynamic_memory_size); + const unsigned int policy + = atomic_read(&tomoyo_policy_memory_size); char buffer[64]; memset(buffer, 0, sizeof(buffer)); - if (tomoyo_quota_for_savename) - snprintf(buffer, sizeof(buffer) - 1, - " (Quota: %10u)", - tomoyo_quota_for_savename); - else - buffer[0] = '\0'; - tomoyo_io_printf(head, "Shared: %10u%s\n", shared, buffer); - if (tomoyo_quota_for_elements) + if (tomoyo_quota_for_policy) snprintf(buffer, sizeof(buffer) - 1, " (Quota: %10u)", - tomoyo_quota_for_elements); + tomoyo_quota_for_policy); else buffer[0] = '\0'; - tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer); - tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic); - tomoyo_io_printf(head, "Total: %10u\n", - shared + private + dynamic); + tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer); + tomoyo_io_printf(head, "Total: %10u\n", policy); head->read_eof = true; } return 0; @@ -494,9 +369,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) char *data = head->write_buf; unsigned int size; - if (sscanf(data, "Shared: %u", &size) == 1) - tomoyo_quota_for_savename = size; - else if (sscanf(data, "Private: %u", &size) == 1) - tomoyo_quota_for_elements = size; + if (sscanf(data, "Policy: %u", &size) == 1) + tomoyo_quota_for_policy = size; return 0; } diff --git a/security/tomoyo/realpath.h b/security/tomoyo/realpath.h deleted file mode 100644 index 78217a37960b..000000000000 --- a/security/tomoyo/realpath.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * security/tomoyo/realpath.h - * - * Get the canonicalized absolute pathnames. The basis for TOMOYO. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 - * - */ - -#ifndef _SECURITY_TOMOYO_REALPATH_H -#define _SECURITY_TOMOYO_REALPATH_H - -struct path; -struct tomoyo_path_info; -struct tomoyo_io_buffer; - -/* Convert binary string to ascii string. */ -int tomoyo_encode(char *buffer, int buflen, const char *str); - -/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ -int tomoyo_realpath_from_path2(struct path *path, char *newname, - int newname_len); - -/* - * Returns realpath(3) of the given pathname but ignores chroot'ed root. - * These functions use tomoyo_alloc(), so the caller must call tomoyo_free() - * if these functions didn't return NULL. - */ -char *tomoyo_realpath(const char *pathname); -/* - * Same with tomoyo_realpath() except that it doesn't follow the final symlink. - */ -char *tomoyo_realpath_nofollow(const char *pathname); -/* Same with tomoyo_realpath() except that the pathname is already solved. */ -char *tomoyo_realpath_from_path(struct path *path); - -/* - * Allocate memory for ACL entry. - * The RAM is chunked, so NEVER try to kfree() the returned pointer. - */ -void *tomoyo_alloc_element(const unsigned int size); - -/* - * Keep the given name on the RAM. - * The RAM is shared, so NEVER try to modify or kfree() the returned name. - */ -const struct tomoyo_path_info *tomoyo_save_name(const char *name); - -/* Allocate memory for temporary use (e.g. permission checks). */ -void *tomoyo_alloc(const size_t size); - -/* Free memory allocated by tomoyo_alloc(). */ -void tomoyo_free(const void *p); - -/* Check for memory usage. */ -int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); - -/* Set memory quota. */ -int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); - -/* Initialize realpath related code. */ -void __init tomoyo_realpath_init(void); - -#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */ diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 2aceebf5f354..dedd97d0c163 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -11,8 +11,6 @@ #include <linux/security.h> #include "common.h" -#include "tomoyo.h" -#include "realpath.h" static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) { @@ -23,21 +21,23 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) 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; + struct tomoyo_domain_info *domain = old->security; + new->security = domain; + if (domain) + atomic_inc(&domain->users); return 0; } static void tomoyo_cred_transfer(struct cred *new, const struct cred *old) { - /* - * Since "struct tomoyo_domain_info *" is a sharable pointer, - * we don't need to duplicate. - */ - new->security = old->security; + tomoyo_cred_prepare(new, old, 0); +} + +static void tomoyo_cred_free(struct cred *cred) +{ + struct tomoyo_domain_info *domain = cred->security; + if (domain) + atomic_dec(&domain->users); } static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) @@ -61,6 +61,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) if (!tomoyo_policy_loaded) tomoyo_load_policy(bprm->filename); /* + * Release reference to "struct tomoyo_domain_info" stored inside + * "bprm->cred->security". New reference to "struct tomoyo_domain_info" + * stored inside "bprm->cred->security" will be acquired later inside + * tomoyo_find_next_domain(). + */ + atomic_dec(&((struct tomoyo_domain_info *) + bprm->cred->security)->users); + /* * Tell tomoyo_bprm_check_security() is called for the first time of an * execve operation. */ @@ -76,8 +84,12 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) * Execute permission is checked against pathname passed to do_execve() * using current domain. */ - if (!domain) - return tomoyo_find_next_domain(bprm); + if (!domain) { + const int idx = tomoyo_read_lock(); + const int err = tomoyo_find_next_domain(bprm); + tomoyo_read_unlock(idx); + return err; + } /* * Read permission is checked against interpreters using next domain. */ @@ -87,67 +99,56 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) static int tomoyo_path_truncate(struct path *path, loff_t length, unsigned int time_attrs) { - return tomoyo_check_1path_perm(tomoyo_domain(), - TOMOYO_TYPE_TRUNCATE_ACL, - path); + return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); } static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry) { struct path path = { parent->mnt, dentry }; - return tomoyo_check_1path_perm(tomoyo_domain(), - TOMOYO_TYPE_UNLINK_ACL, - &path); + return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path); } static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, int mode) { struct path path = { parent->mnt, dentry }; - return tomoyo_check_1path_perm(tomoyo_domain(), - TOMOYO_TYPE_MKDIR_ACL, - &path); + return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path); } static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) { struct path path = { parent->mnt, dentry }; - return tomoyo_check_1path_perm(tomoyo_domain(), - TOMOYO_TYPE_RMDIR_ACL, - &path); + return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path); } static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry, const char *old_name) { struct path path = { parent->mnt, dentry }; - return tomoyo_check_1path_perm(tomoyo_domain(), - TOMOYO_TYPE_SYMLINK_ACL, - &path); + return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path); } static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, int mode, unsigned int dev) { struct path path = { parent->mnt, dentry }; - int type = TOMOYO_TYPE_CREATE_ACL; + int type = TOMOYO_TYPE_CREATE; switch (mode & S_IFMT) { case S_IFCHR: - type = TOMOYO_TYPE_MKCHAR_ACL; + type = TOMOYO_TYPE_MKCHAR; break; case S_IFBLK: - type = TOMOYO_TYPE_MKBLOCK_ACL; + type = TOMOYO_TYPE_MKBLOCK; break; case S_IFIFO: - type = TOMOYO_TYPE_MKFIFO_ACL; + type = TOMOYO_TYPE_MKFIFO; break; case S_IFSOCK: - type = TOMOYO_TYPE_MKSOCK_ACL; + type = TOMOYO_TYPE_MKSOCK; break; } - return tomoyo_check_1path_perm(tomoyo_domain(), - type, &path); + return tomoyo_path_perm(type, &path); } static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, @@ -155,9 +156,7 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, { struct path path1 = { new_dir->mnt, old_dentry }; struct path path2 = { new_dir->mnt, new_dentry }; - return tomoyo_check_2path_perm(tomoyo_domain(), - TOMOYO_TYPE_LINK_ACL, - &path1, &path2); + return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2); } static int tomoyo_path_rename(struct path *old_parent, @@ -167,16 +166,14 @@ static int tomoyo_path_rename(struct path *old_parent, { struct path path1 = { old_parent->mnt, old_dentry }; struct path path2 = { new_parent->mnt, new_dentry }; - return tomoyo_check_2path_perm(tomoyo_domain(), - TOMOYO_TYPE_RENAME_ACL, - &path1, &path2); + return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2); } static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) { if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) - return tomoyo_check_rewrite_permission(tomoyo_domain(), file); + return tomoyo_check_rewrite_permission(file); return 0; } @@ -189,6 +186,51 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred) return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags); } +static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path); +} + +static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, + mode_t mode) +{ + struct path path = { mnt, dentry }; + return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path); +} + +static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) +{ + int error = 0; + if (uid != (uid_t) -1) + error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path); + if (!error && gid != (gid_t) -1) + error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path); + return error; +} + +static int tomoyo_path_chroot(struct path *path) +{ + return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path); +} + +static int tomoyo_sb_mount(char *dev_name, struct path *path, + char *type, unsigned long flags, void *data) +{ + return tomoyo_path_perm(TOMOYO_TYPE_MOUNT, path); +} + +static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) +{ + struct path path = { mnt, mnt->mnt_root }; + return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path); +} + +static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path) +{ + return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path); +} + /* * tomoyo_security_ops is a "struct security_operations" which is used for * registering TOMOYO. @@ -198,6 +240,7 @@ static struct security_operations tomoyo_security_ops = { .cred_alloc_blank = tomoyo_cred_alloc_blank, .cred_prepare = tomoyo_cred_prepare, .cred_transfer = tomoyo_cred_transfer, + .cred_free = tomoyo_cred_free, .bprm_set_creds = tomoyo_bprm_set_creds, .bprm_check_security = tomoyo_bprm_check_security, .file_fcntl = tomoyo_file_fcntl, @@ -210,8 +253,18 @@ static struct security_operations tomoyo_security_ops = { .path_mknod = tomoyo_path_mknod, .path_link = tomoyo_path_link, .path_rename = tomoyo_path_rename, + .file_ioctl = tomoyo_file_ioctl, + .path_chmod = tomoyo_path_chmod, + .path_chown = tomoyo_path_chown, + .path_chroot = tomoyo_path_chroot, + .sb_mount = tomoyo_sb_mount, + .sb_umount = tomoyo_sb_umount, + .sb_pivotroot = tomoyo_sb_pivotroot, }; +/* Lock for GC. */ +struct srcu_struct tomoyo_ss; + static int __init tomoyo_init(void) { struct cred *cred = (struct cred *) current_cred(); @@ -219,7 +272,8 @@ static int __init tomoyo_init(void) if (!security_module_enable(&tomoyo_security_ops)) return 0; /* register ourselves with the security framework */ - if (register_security(&tomoyo_security_ops)) + if (register_security(&tomoyo_security_ops) || + init_srcu_struct(&tomoyo_ss)) panic("Failure registering TOMOYO Linux"); printk(KERN_INFO "TOMOYO Linux initialized\n"); cred->security = &tomoyo_kernel_domain; diff --git a/security/tomoyo/tomoyo.h b/security/tomoyo/tomoyo.h deleted file mode 100644 index ed758325b1ae..000000000000 --- a/security/tomoyo/tomoyo.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * security/tomoyo/tomoyo.h - * - * Implementation of the Domain-Based Mandatory Access Control. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 - * - */ - -#ifndef _SECURITY_TOMOYO_TOMOYO_H -#define _SECURITY_TOMOYO_TOMOYO_H - -struct tomoyo_path_info; -struct path; -struct inode; -struct linux_binprm; -struct pt_regs; - -int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, - const struct tomoyo_path_info *filename); -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); - -/* Index numbers for Access Controls. */ - -#define TOMOYO_TYPE_SINGLE_PATH_ACL 0 -#define TOMOYO_TYPE_DOUBLE_PATH_ACL 1 - -/* Index numbers for File Controls. */ - -/* - * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set - * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and - * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set. - * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or - * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are - * automatically cleared if TYPE_READ_WRITE_ACL is cleared. - */ - -#define TOMOYO_TYPE_READ_WRITE_ACL 0 -#define TOMOYO_TYPE_EXECUTE_ACL 1 -#define TOMOYO_TYPE_READ_ACL 2 -#define TOMOYO_TYPE_WRITE_ACL 3 -#define TOMOYO_TYPE_CREATE_ACL 4 -#define TOMOYO_TYPE_UNLINK_ACL 5 -#define TOMOYO_TYPE_MKDIR_ACL 6 -#define TOMOYO_TYPE_RMDIR_ACL 7 -#define TOMOYO_TYPE_MKFIFO_ACL 8 -#define TOMOYO_TYPE_MKSOCK_ACL 9 -#define TOMOYO_TYPE_MKBLOCK_ACL 10 -#define TOMOYO_TYPE_MKCHAR_ACL 11 -#define TOMOYO_TYPE_TRUNCATE_ACL 12 -#define TOMOYO_TYPE_SYMLINK_ACL 13 -#define TOMOYO_TYPE_REWRITE_ACL 14 -#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15 - -#define TOMOYO_TYPE_LINK_ACL 0 -#define TOMOYO_TYPE_RENAME_ACL 1 -#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2 - -#define TOMOYO_DOMAINPOLICY 0 -#define TOMOYO_EXCEPTIONPOLICY 1 -#define TOMOYO_DOMAIN_STATUS 2 -#define TOMOYO_PROCESS_STATUS 3 -#define TOMOYO_MEMINFO 4 -#define TOMOYO_SELFDOMAIN 5 -#define TOMOYO_VERSION 6 -#define TOMOYO_PROFILE 7 -#define TOMOYO_MANAGER 8 - -extern struct tomoyo_domain_info tomoyo_kernel_domain; - -static inline struct tomoyo_domain_info *tomoyo_domain(void) -{ - return current_cred()->security; -} - -static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct - *task) -{ - return task_cred_xxx(task, security); -} - -#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */ |