From 700920eb5ba4de5417b446c9a8bb008df2b973e0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 18 Jan 2012 15:31:45 +0000 Subject: KEYS: Allow special keyrings to be cleared The kernel contains some special internal keyrings, for instance the DNS resolver keyring : 2a93faf1 I----- 1 perm 1f030000 0 0 keyring .dns_resolver: empty It would occasionally be useful to allow the contents of such keyrings to be flushed by root (cache invalidation). Allow a flag to be set on a keyring to mark that someone possessing the sysadmin capability can clear the keyring, even without normal write access to the keyring. Set this flag on the special keyrings created by the DNS resolver, the NFS identity mapper and the CIFS identity mapper. Signed-off-by: David Howells Acked-by: Jeff Layton Acked-by: Steve Dickson Signed-off-by: James Morris --- security/keys/keyctl.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 0b3f5d72af1..6523599e9ac 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -388,11 +388,24 @@ long keyctl_keyring_clear(key_serial_t ringid) keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); + + /* Root is permitted to invalidate certain special keyrings */ + if (capable(CAP_SYS_ADMIN)) { + keyring_ref = lookup_user_key(ringid, 0, 0); + if (IS_ERR(keyring_ref)) + goto error; + if (test_bit(KEY_FLAG_ROOT_CAN_CLEAR, + &key_ref_to_ptr(keyring_ref)->flags)) + goto clear; + goto error_put; + } + goto error; } +clear: ret = keyring_clear(key_ref_to_ptr(keyring_ref)); - +error_put: key_ref_put(keyring_ref); error: return ret; -- cgit v1.2.3 From f4a0391dfa91155bd961673b31eb42d9d45c799d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 5 Jan 2012 12:49:54 -0200 Subject: ima: fix Kconfig dependencies Fix the following build warning: warning: (IMA) selects TCG_TPM which has unmet direct dependencies (HAS_IOMEM && EXPERIMENTAL) Suggested-by: Rajiv Andrade Signed-off-by: Fabio Estevam Signed-off-by: Rajiv Andrade Cc: Signed-off-by: Mimi Zohar --- drivers/char/tpm/Kconfig | 1 - security/integrity/ima/Kconfig | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 7fc75e47e6d..a048199ce86 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -5,7 +5,6 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM - depends on EXPERIMENTAL select SECURITYFS ---help--- If you have a TPM security chip in your system, which diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 4f554f20dc9..063298a797e 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -9,7 +9,7 @@ config IMA select CRYPTO_HMAC select CRYPTO_MD5 select CRYPTO_SHA1 - select TCG_TPM if !S390 && !UML + select TCG_TPM if HAS_IOMEM && !UML select TCG_TIS if TCG_TPM help The Trusted Computing Group(TCG) runtime Integrity -- cgit v1.2.3 From 4c2c392763a682354fac65b6a569adec4e4b5387 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 18 Oct 2011 14:16:28 +0300 Subject: ima: policy for RAMFS Don't measure ramfs files. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_policy.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index d661afbe474..1b422bc5626 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -62,6 +62,7 @@ static struct ima_measure_rule_entry default_rules[] = { {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC, -- cgit v1.2.3 From 1a2a4d06e1e95260c470ebe3a945f61bbe8c1fd8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 21 Dec 2011 12:17:03 -0800 Subject: security: create task_free security callback The current LSM interface to cred_free is not sufficient for allowing an LSM to track the life and death of a task. This patch adds the task_free hook so that an LSM can clean up resources on task death. Signed-off-by: Kees Cook Signed-off-by: James Morris --- include/linux/security.h | 9 +++++++++ kernel/fork.c | 1 + security/capability.c | 5 +++++ security/security.c | 5 +++++ 4 files changed, 20 insertions(+) (limited to 'security') diff --git a/include/linux/security.h b/include/linux/security.h index 83c18e8c846..8325eddd9ee 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -651,6 +651,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * manual page for definitions of the @clone_flags. * @clone_flags contains the flags indicating what should be shared. * Return 0 if permission is granted. + * @task_free: + * @task task being freed + * Handle release of task-related resources. (Note that this can be called + * from interrupt context.) * @cred_alloc_blank: * @cred points to the credentials. * @gfp indicates the atomicity of any memory allocations. @@ -1493,6 +1497,7 @@ struct security_operations { int (*dentry_open) (struct file *file, const struct cred *cred); int (*task_create) (unsigned long clone_flags); + void (*task_free) (struct task_struct *task); int (*cred_alloc_blank) (struct cred *cred, gfp_t gfp); void (*cred_free) (struct cred *cred); int (*cred_prepare)(struct cred *new, const struct cred *old, @@ -1752,6 +1757,7 @@ int security_file_send_sigiotask(struct task_struct *tsk, int security_file_receive(struct file *file); int security_dentry_open(struct file *file, const struct cred *cred); int security_task_create(unsigned long clone_flags); +void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); void security_cred_free(struct cred *cred); int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); @@ -2245,6 +2251,9 @@ static inline int security_task_create(unsigned long clone_flags) return 0; } +static inline void security_task_free(struct task_struct *task) +{ } + static inline int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) { return 0; diff --git a/kernel/fork.c b/kernel/fork.c index 1b2ef3c23ae..f0e7781ba9b 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -192,6 +192,7 @@ void __put_task_struct(struct task_struct *tsk) WARN_ON(atomic_read(&tsk->usage)); WARN_ON(tsk == current); + security_task_free(tsk); exit_creds(tsk); delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); diff --git a/security/capability.c b/security/capability.c index 2f680eb02b5..5bb21b1c448 100644 --- a/security/capability.c +++ b/security/capability.c @@ -358,6 +358,10 @@ static int cap_task_create(unsigned long clone_flags) return 0; } +static void cap_task_free(struct task_struct *task) +{ +} + static int cap_cred_alloc_blank(struct cred *cred, gfp_t gfp) { return 0; @@ -954,6 +958,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, file_receive); set_to_cap_if_null(ops, dentry_open); set_to_cap_if_null(ops, task_create); + set_to_cap_if_null(ops, task_free); set_to_cap_if_null(ops, cred_alloc_blank); set_to_cap_if_null(ops, cred_free); set_to_cap_if_null(ops, cred_prepare); diff --git a/security/security.c b/security/security.c index d7542493454..7d9426bb744 100644 --- a/security/security.c +++ b/security/security.c @@ -729,6 +729,11 @@ int security_task_create(unsigned long clone_flags) return security_ops->task_create(clone_flags); } +void security_task_free(struct task_struct *task) +{ + security_ops->task_free(task); +} + int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) { return security_ops->cred_alloc_blank(cred, gfp); -- cgit v1.2.3 From 2d514487faf188938a4ee4fb3464eeecfbdcf8eb Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 21 Dec 2011 12:17:04 -0800 Subject: security: Yama LSM This adds the Yama Linux Security Module to collect DAC security improvements (specifically just ptrace restrictions for now) that have existed in various forms over the years and have been carried outside the mainline kernel by other Linux distributions like Openwall and grsecurity. Signed-off-by: Kees Cook Acked-by: John Johansen Signed-off-by: James Morris --- Documentation/security/00-INDEX | 2 + Documentation/security/Yama.txt | 60 ++++++++ include/linux/prctl.h | 6 + security/Kconfig | 6 + security/Makefile | 2 + security/yama/Kconfig | 13 ++ security/yama/Makefile | 3 + security/yama/yama_lsm.c | 319 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 411 insertions(+) create mode 100644 Documentation/security/Yama.txt create mode 100644 security/yama/Kconfig create mode 100644 security/yama/Makefile create mode 100644 security/yama/yama_lsm.c (limited to 'security') diff --git a/Documentation/security/00-INDEX b/Documentation/security/00-INDEX index 99b85d39751..eeed1de546d 100644 --- a/Documentation/security/00-INDEX +++ b/Documentation/security/00-INDEX @@ -6,6 +6,8 @@ SELinux.txt - how to get started with the SELinux security enhancement. Smack.txt - documentation on the Smack Linux Security Module. +Yama.txt + - documentation on the Yama Linux Security Module. apparmor.txt - documentation on the AppArmor security extension. credentials.txt diff --git a/Documentation/security/Yama.txt b/Documentation/security/Yama.txt new file mode 100644 index 00000000000..4f0b7896a21 --- /dev/null +++ b/Documentation/security/Yama.txt @@ -0,0 +1,60 @@ +Yama is a Linux Security Module that collects a number of system-wide DAC +security protections that are not handled by the core kernel itself. To +select it at boot time, specify "security=yama" (though this will disable +any other LSM). + +Yama is controlled through sysctl in /proc/sys/kernel/yama: + +- ptrace_scope + +============================================================== + +ptrace_scope: + +As Linux grows in popularity, it will become a larger target for +malware. One particularly troubling weakness of the Linux process +interfaces is that a single user is able to examine the memory and +running state of any of their processes. For example, if one application +(e.g. Pidgin) was compromised, it would be possible for an attacker to +attach to other running processes (e.g. Firefox, SSH sessions, GPG agent, +etc) to extract additional credentials and continue to expand the scope +of their attack without resorting to user-assisted phishing. + +This is not a theoretical problem. SSH session hijacking +(http://www.storm.net.nz/projects/7) and arbitrary code injection +(http://c-skills.blogspot.com/2007/05/injectso.html) attacks already +exist and remain possible if ptrace is allowed to operate as before. +Since ptrace is not commonly used by non-developers and non-admins, system +builders should be allowed the option to disable this debugging system. + +For a solution, some applications use prctl(PR_SET_DUMPABLE, ...) to +specifically disallow such ptrace attachment (e.g. ssh-agent), but many +do not. A more general solution is to only allow ptrace directly from a +parent to a child process (i.e. direct "gdb EXE" and "strace EXE" still +work), or with CAP_SYS_PTRACE (i.e. "gdb --pid=PID", and "strace -p PID" +still work as root). + +For software that has defined application-specific relationships +between a debugging process and its inferior (crash handlers, etc), +prctl(PR_SET_PTRACER, pid, ...) can be used. An inferior can declare which +other process (and its descendents) are allowed to call PTRACE_ATTACH +against it. Only one such declared debugging process can exists for +each inferior at a time. For example, this is used by KDE, Chromium, and +Firefox's crash handlers, and by Wine for allowing only Wine processes +to ptrace each other. + +0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other + process running under the same uid, as long as it is dumpable (i.e. + did not transition uids, start privileged, or have called + prctl(PR_SET_DUMPABLE...) already). + +1 - restricted ptrace: a process must have a predefined relationship + with the inferior it wants to call PTRACE_ATTACH on. By default, + this relationship is that of only its descendants when the above + classic criteria is also met. To change the relationship, an + inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare + an allowed debugger PID to call PTRACE_ATTACH on the inferior. + +The original children-only logic was based on the restrictions in grsecurity. + +============================================================== diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 7ddc7f1b480..4d0e5bc5458 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -114,4 +114,10 @@ # define PR_SET_MM_START_BRK 6 # define PR_SET_MM_BRK 7 +/* + * Set specific pid that is allowed to ptrace the current task. + * A value of 0 mean "no process". + */ +#define PR_SET_PTRACER 0x59616d61 + #endif /* _LINUX_PRCTL_H */ diff --git a/security/Kconfig b/security/Kconfig index 51bd5a0b69a..ccc61f8006b 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -187,6 +187,7 @@ source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig source security/apparmor/Kconfig +source security/yama/Kconfig source security/integrity/Kconfig @@ -196,6 +197,7 @@ choice default DEFAULT_SECURITY_SMACK if SECURITY_SMACK default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR + default DEFAULT_SECURITY_YAMA if SECURITY_YAMA default DEFAULT_SECURITY_DAC help @@ -214,6 +216,9 @@ choice config DEFAULT_SECURITY_APPARMOR bool "AppArmor" if SECURITY_APPARMOR=y + config DEFAULT_SECURITY_YAMA + bool "Yama" if SECURITY_YAMA=y + config DEFAULT_SECURITY_DAC bool "Unix Discretionary Access Controls" @@ -225,6 +230,7 @@ config DEFAULT_SECURITY default "smack" if DEFAULT_SECURITY_SMACK default "tomoyo" if DEFAULT_SECURITY_TOMOYO default "apparmor" if DEFAULT_SECURITY_APPARMOR + default "yama" if DEFAULT_SECURITY_YAMA default "" if DEFAULT_SECURITY_DAC endmenu diff --git a/security/Makefile b/security/Makefile index a5e502f8a05..c26c81e9257 100644 --- a/security/Makefile +++ b/security/Makefile @@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor +subdir-$(CONFIG_SECURITY_YAMA) += yama # always enable default capabilities obj-y += commoncap.o @@ -21,6 +22,7 @@ obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o obj-$(CONFIG_AUDIT) += lsm_audit.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o +obj-$(CONFIG_SECURITY_YAMA) += yama/built-in.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/yama/Kconfig b/security/yama/Kconfig new file mode 100644 index 00000000000..51d6709d8bb --- /dev/null +++ b/security/yama/Kconfig @@ -0,0 +1,13 @@ +config SECURITY_YAMA + bool "Yama support" + depends on SECURITY + select SECURITYFS + select SECURITY_PATH + default n + help + This selects Yama, which extends DAC support with additional + system-wide security settings beyond regular Linux discretionary + access controls. Currently available is ptrace scope restriction. + Further information can be found in Documentation/security/Yama.txt. + + If you are unsure how to answer this question, answer N. diff --git a/security/yama/Makefile b/security/yama/Makefile new file mode 100644 index 00000000000..8b5e0658845 --- /dev/null +++ b/security/yama/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SECURITY_YAMA) := yama.o + +yama-y := yama_lsm.o diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c new file mode 100644 index 00000000000..dd4d36067c5 --- /dev/null +++ b/security/yama/yama_lsm.c @@ -0,0 +1,319 @@ +/* + * Yama Linux Security Module + * + * Author: Kees Cook + * + * Copyright (C) 2010 Canonical, Ltd. + * Copyright (C) 2011 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +static int ptrace_scope = 1; + +/* describe a ptrace relationship for potential exception */ +struct ptrace_relation { + struct task_struct *tracer; + struct task_struct *tracee; + struct list_head node; +}; + +static LIST_HEAD(ptracer_relations); +static DEFINE_SPINLOCK(ptracer_relations_lock); + +/** + * yama_ptracer_add - add/replace an exception for this tracer/tracee pair + * @tracer: the task_struct of the process doing the ptrace + * @tracee: the task_struct of the process to be ptraced + * + * Each tracee can have, at most, one tracer registered. Each time this + * is called, the prior registered tracer will be replaced for the tracee. + * + * Returns 0 if relationship was added, -ve on error. + */ +static int yama_ptracer_add(struct task_struct *tracer, + struct task_struct *tracee) +{ + int rc = 0; + struct ptrace_relation *added; + struct ptrace_relation *entry, *relation = NULL; + + added = kmalloc(sizeof(*added), GFP_KERNEL); + if (!added) + return -ENOMEM; + + spin_lock_bh(&ptracer_relations_lock); + list_for_each_entry(entry, &ptracer_relations, node) + if (entry->tracee == tracee) { + relation = entry; + break; + } + if (!relation) { + relation = added; + relation->tracee = tracee; + list_add(&relation->node, &ptracer_relations); + } + relation->tracer = tracer; + + spin_unlock_bh(&ptracer_relations_lock); + if (added != relation) + kfree(added); + + return rc; +} + +/** + * yama_ptracer_del - remove exceptions related to the given tasks + * @tracer: remove any relation where tracer task matches + * @tracee: remove any relation where tracee task matches + */ +static void yama_ptracer_del(struct task_struct *tracer, + struct task_struct *tracee) +{ + struct ptrace_relation *relation, *safe; + + spin_lock_bh(&ptracer_relations_lock); + list_for_each_entry_safe(relation, safe, &ptracer_relations, node) + if (relation->tracee == tracee || + relation->tracer == tracer) { + list_del(&relation->node); + kfree(relation); + } + spin_unlock_bh(&ptracer_relations_lock); +} + +/** + * yama_task_free - check for task_pid to remove from exception list + * @task: task being removed + */ +static void yama_task_free(struct task_struct *task) +{ + yama_ptracer_del(task, task); +} + +/** + * yama_task_prctl - check for Yama-specific prctl operations + * @option: operation + * @arg2: argument + * @arg3: argument + * @arg4: argument + * @arg5: argument + * + * Return 0 on success, -ve on error. -ENOSYS is returned when Yama + * does not handle the given option. + */ +static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + int rc; + struct task_struct *myself = current; + + rc = cap_task_prctl(option, arg2, arg3, arg4, arg5); + if (rc != -ENOSYS) + return rc; + + switch (option) { + case PR_SET_PTRACER: + /* Since a thread can call prctl(), find the group leader + * before calling _add() or _del() on it, since we want + * process-level granularity of control. The tracer group + * leader checking is handled later when walking the ancestry + * at the time of PTRACE_ATTACH check. + */ + rcu_read_lock(); + if (!thread_group_leader(myself)) + myself = rcu_dereference(myself->group_leader); + get_task_struct(myself); + rcu_read_unlock(); + + if (arg2 == 0) { + yama_ptracer_del(NULL, myself); + rc = 0; + } else { + struct task_struct *tracer; + + rcu_read_lock(); + tracer = find_task_by_vpid(arg2); + if (tracer) + get_task_struct(tracer); + else + rc = -EINVAL; + rcu_read_unlock(); + + if (tracer) { + rc = yama_ptracer_add(tracer, myself); + put_task_struct(tracer); + } + } + + put_task_struct(myself); + break; + } + + return rc; +} + +/** + * task_is_descendant - walk up a process family tree looking for a match + * @parent: the process to compare against while walking up from child + * @child: the process to start from while looking upwards for parent + * + * Returns 1 if child is a descendant of parent, 0 if not. + */ +static int task_is_descendant(struct task_struct *parent, + struct task_struct *child) +{ + int rc = 0; + struct task_struct *walker = child; + + if (!parent || !child) + return 0; + + rcu_read_lock(); + if (!thread_group_leader(parent)) + parent = rcu_dereference(parent->group_leader); + while (walker->pid > 0) { + if (!thread_group_leader(walker)) + walker = rcu_dereference(walker->group_leader); + if (walker == parent) { + rc = 1; + break; + } + walker = rcu_dereference(walker->real_parent); + } + rcu_read_unlock(); + + return rc; +} + +/** + * ptracer_exception_found - tracer registered as exception for this tracee + * @tracer: the task_struct of the process attempting ptrace + * @tracee: the task_struct of the process to be ptraced + * + * Returns 1 if tracer has is ptracer exception ancestor for tracee. + */ +static int ptracer_exception_found(struct task_struct *tracer, + struct task_struct *tracee) +{ + int rc = 0; + struct ptrace_relation *relation; + struct task_struct *parent = NULL; + + spin_lock_bh(&ptracer_relations_lock); + rcu_read_lock(); + if (!thread_group_leader(tracee)) + tracee = rcu_dereference(tracee->group_leader); + list_for_each_entry(relation, &ptracer_relations, node) + if (relation->tracee == tracee) { + parent = relation->tracer; + break; + } + + if (task_is_descendant(parent, tracer)) + rc = 1; + rcu_read_unlock(); + spin_unlock_bh(&ptracer_relations_lock); + + return rc; +} + +/** + * yama_ptrace_access_check - validate PTRACE_ATTACH calls + * @child: task that current task is attempting to ptrace + * @mode: ptrace attach mode + * + * Returns 0 if following the ptrace is allowed, -ve on error. + */ +static int yama_ptrace_access_check(struct task_struct *child, + unsigned int mode) +{ + int rc; + + /* If standard caps disallows it, so does Yama. We should + * only tighten restrictions further. + */ + rc = cap_ptrace_access_check(child, mode); + if (rc) + return rc; + + /* require ptrace target be a child of ptracer on attach */ + if (mode == PTRACE_MODE_ATTACH && + ptrace_scope && + !task_is_descendant(current, child) && + !ptracer_exception_found(current, child) && + !capable(CAP_SYS_PTRACE)) + rc = -EPERM; + + if (rc) { + char name[sizeof(current->comm)]; + printk_ratelimited(KERN_NOTICE "ptrace of non-child" + " pid %d was attempted by: %s (pid %d)\n", + child->pid, + get_task_comm(name, current), + current->pid); + } + + return rc; +} + +static struct security_operations yama_ops = { + .name = "yama", + + .ptrace_access_check = yama_ptrace_access_check, + .task_prctl = yama_task_prctl, + .task_free = yama_task_free, +}; + +#ifdef CONFIG_SYSCTL +static int zero; +static int one = 1; + +struct ctl_path yama_sysctl_path[] = { + { .procname = "kernel", }, + { .procname = "yama", }, + { } +}; + +static struct ctl_table yama_sysctl_table[] = { + { + .procname = "ptrace_scope", + .data = &ptrace_scope, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { } +}; +#endif /* CONFIG_SYSCTL */ + +static __init int yama_init(void) +{ + if (!security_module_enable(&yama_ops)) + return 0; + + printk(KERN_INFO "Yama: becoming mindful.\n"); + + if (register_security(&yama_ops)) + panic("Yama: kernel registration failed.\n"); + +#ifdef CONFIG_SYSCTL + if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table)) + panic("Yama: sysctl registration failed.\n"); +#endif + + return 0; +} + +security_initcall(yama_init); -- cgit v1.2.3 From 191c542442fdf53cc3c496c00be13367fd9cd42d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 13 Feb 2012 03:58:52 +0000 Subject: mm: collapse security_vm_enough_memory() variants into a single function Collapse security_vm_enough_memory() variants into a single function. Signed-off-by: Al Viro Signed-off-by: James Morris --- include/linux/security.h | 16 ---------------- kernel/fork.c | 2 +- mm/mmap.c | 4 ++-- mm/mprotect.c | 2 +- mm/mremap.c | 2 +- mm/shmem.c | 4 ++-- mm/swapfile.c | 4 +++- security/security.c | 14 -------------- 8 files changed, 10 insertions(+), 38 deletions(-) (limited to 'security') diff --git a/include/linux/security.h b/include/linux/security.h index 8325eddd9ee..2fefad6d27a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1679,9 +1679,7 @@ 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_settime(const struct timespec *ts, const struct timezone *tz); -int security_vm_enough_memory(long pages); int security_vm_enough_memory_mm(struct mm_struct *mm, long pages); -int security_vm_enough_memory_kern(long pages); int security_bprm_set_creds(struct linux_binprm *bprm); int security_bprm_check(struct linux_binprm *bprm); void security_bprm_committing_creds(struct linux_binprm *bprm); @@ -1902,25 +1900,11 @@ static inline int security_settime(const struct timespec *ts, return cap_settime(ts, tz); } -static inline int security_vm_enough_memory(long pages) -{ - WARN_ON(current->mm == NULL); - return cap_vm_enough_memory(current->mm, pages); -} - static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) { - WARN_ON(mm == NULL); return cap_vm_enough_memory(mm, pages); } -static inline int security_vm_enough_memory_kern(long pages) -{ - /* If current->mm is a kernel thread then we will pass NULL, - for this specific case that is fine */ - return cap_vm_enough_memory(current->mm, pages); -} - static inline int security_bprm_set_creds(struct linux_binprm *bprm) { return cap_bprm_set_creds(bprm); diff --git a/kernel/fork.c b/kernel/fork.c index f0e7781ba9b..d5ebddf317a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -355,7 +355,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) charge = 0; if (mpnt->vm_flags & VM_ACCOUNT) { unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; - if (security_vm_enough_memory(len)) + if (security_vm_enough_memory_mm(oldmm, len)) /* sic */ goto fail_nomem; charge = len; } diff --git a/mm/mmap.c b/mm/mmap.c index 3f758c7f4c8..db05495d6d0 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1235,7 +1235,7 @@ munmap_back: */ if (accountable_mapping(file, vm_flags)) { charged = len >> PAGE_SHIFT; - if (security_vm_enough_memory(charged)) + if (security_vm_enough_memory_mm(mm, charged)) return -ENOMEM; vm_flags |= VM_ACCOUNT; } @@ -2169,7 +2169,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len) if (mm->map_count > sysctl_max_map_count) return -ENOMEM; - if (security_vm_enough_memory(len >> PAGE_SHIFT)) + if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT)) return -ENOMEM; /* Can we just expand an old private anonymous mapping? */ diff --git a/mm/mprotect.c b/mm/mprotect.c index 5a688a2756b..9599fa2d0e9 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -168,7 +168,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB| VM_SHARED|VM_NORESERVE))) { charged = nrpages; - if (security_vm_enough_memory(charged)) + if (security_vm_enough_memory_mm(mm, charged)) return -ENOMEM; newflags |= VM_ACCOUNT; } diff --git a/mm/mremap.c b/mm/mremap.c index 87bb8393e7d..db8d983b5a7 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -329,7 +329,7 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr, if (vma->vm_flags & VM_ACCOUNT) { unsigned long charged = (new_len - old_len) >> PAGE_SHIFT; - if (security_vm_enough_memory(charged)) + if (security_vm_enough_memory_mm(mm, charged)) goto Efault; *p = charged; } diff --git a/mm/shmem.c b/mm/shmem.c index 269d049294a..d9c29395275 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -127,7 +127,7 @@ static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb) static inline int shmem_acct_size(unsigned long flags, loff_t size) { return (flags & VM_NORESERVE) ? - 0 : security_vm_enough_memory_kern(VM_ACCT(size)); + 0 : security_vm_enough_memory_mm(current->mm, VM_ACCT(size)); } static inline void shmem_unacct_size(unsigned long flags, loff_t size) @@ -145,7 +145,7 @@ static inline void shmem_unacct_size(unsigned long flags, loff_t size) static inline int shmem_acct_block(unsigned long flags) { return (flags & VM_NORESERVE) ? - security_vm_enough_memory_kern(VM_ACCT(PAGE_CACHE_SIZE)) : 0; + security_vm_enough_memory_mm(current->mm, VM_ACCT(PAGE_CACHE_SIZE)) : 0; } static inline void shmem_unacct_blocks(unsigned long flags, long pages) diff --git a/mm/swapfile.c b/mm/swapfile.c index d999f090dfd..f0d79296dd5 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1563,6 +1563,8 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + BUG_ON(!current->mm); + pathname = getname(specialfile); err = PTR_ERR(pathname); if (IS_ERR(pathname)) @@ -1590,7 +1592,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) spin_unlock(&swap_lock); goto out_dput; } - if (!security_vm_enough_memory(p->pages)) + if (!security_vm_enough_memory_mm(current->mm, p->pages)) vm_unacct_memory(p->pages); else { err = -ENOMEM; diff --git a/security/security.c b/security/security.c index 7d9426bb744..44177add471 100644 --- a/security/security.c +++ b/security/security.c @@ -187,25 +187,11 @@ int security_settime(const struct timespec *ts, const struct timezone *tz) return security_ops->settime(ts, tz); } -int security_vm_enough_memory(long pages) -{ - WARN_ON(current->mm == NULL); - return security_ops->vm_enough_memory(current->mm, pages); -} - int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) { - WARN_ON(mm == NULL); return security_ops->vm_enough_memory(mm, pages); } -int security_vm_enough_memory_kern(long pages) -{ - /* If current->mm is a kernel thread then we will pass NULL, - for this specific case that is fine */ - return security_ops->vm_enough_memory(current->mm, pages); -} - int security_bprm_set_creds(struct linux_binprm *bprm) { return security_ops->bprm_set_creds(bprm); -- cgit v1.2.3 From 4040153087478993cbf0809f444400a3c808074c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 13 Feb 2012 03:58:52 +0000 Subject: security: trim security.h Trim security.h Signed-off-by: Al Viro Signed-off-by: James Morris --- drivers/net/macvtap.c | 1 + drivers/target/iscsi/iscsi_target.c | 1 + drivers/target/iscsi/iscsi_target_login.c | 1 + fs/nfs/client.c | 1 + fs/proc/proc_sysctl.c | 2 ++ fs/quota/dquot.c | 1 + fs/super.c | 1 + include/linux/security.h | 55 ++++++++++++++++--------------- include/net/sock.h | 2 ++ ipc/msgutil.c | 2 ++ kernel/cred.c | 1 + kernel/exit.c | 1 + kernel/sched/core.c | 1 + kernel/sysctl.c | 1 + mm/mmap.c | 13 ++++++++ security/commoncap.c | 1 + security/security.c | 2 ++ security/selinux/hooks.c | 2 ++ security/smack/smack_lsm.c | 3 ++ 19 files changed, 66 insertions(+), 26 deletions(-) (limited to 'security') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 58dc117a8d7..0427c6561c8 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 44262908def..33df66d91aa 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 38cb7ce8469..1ee33a8c3fa 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 31778f74357..d4f772ebd1e 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index a6b62173d4c..67bbf6e4e19 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include "internal.h" static const struct dentry_operations proc_sys_dentry_operations; diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 46741970371..8b4f12b33f5 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/super.c b/fs/super.c index 6015c02296b..18660532909 100644 --- a/fs/super.c +++ b/fs/super.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "internal.h" diff --git a/include/linux/security.h b/include/linux/security.h index 2fefad6d27a..339b3b120f6 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -22,22 +22,36 @@ #ifndef __LINUX_SECURITY_H #define __LINUX_SECURITY_H -#include -#include -#include -#include -#include -#include -#include -#include -#include /* PAGE_ALIGN */ -#include -#include #include -#include +#include #include -#include -#include +#include + +struct linux_binprm; +struct cred; +struct rlimit; +struct siginfo; +struct sem_array; +struct sembuf; +struct kern_ipc_perm; +struct audit_context; +struct super_block; +struct inode; +struct dentry; +struct file; +struct vfsmount; +struct path; +struct qstr; +struct nameidata; +struct iattr; +struct fown_struct; +struct file_operations; +struct shmid_kernel; +struct msg_msg; +struct msg_queue; +struct xattr; +struct xfrm_sec_ctx; +struct mm_struct; /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 @@ -49,6 +63,7 @@ struct ctl_table; struct audit_krule; struct user_namespace; +struct timezone; /* * These functions are in security/capability.c and are used @@ -131,18 +146,6 @@ struct request_sock; #define LSM_UNSAFE_PTRACE_CAP 4 #ifdef CONFIG_MMU -/* - * If a hint addr is less than mmap_min_addr change hint to be as - * low as possible but still greater than mmap_min_addr - */ -static inline unsigned long round_hint_to_min(unsigned long hint) -{ - hint &= PAGE_MASK; - if (((void *)hint != NULL) && - (hint < mmap_min_addr)) - return PAGE_ALIGN(mmap_min_addr); - return hint; -} extern int mmap_min_addr_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif diff --git a/include/net/sock.h b/include/net/sock.h index 91c1c8baf02..27508f07ead 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include #include diff --git a/ipc/msgutil.c b/ipc/msgutil.c index 5652101cdac..26143d377c9 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include "util.h" diff --git a/kernel/cred.c b/kernel/cred.c index 5791612a404..97b36eeca4c 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #if 0 diff --git a/kernel/exit.c b/kernel/exit.c index 4b4042f9bc6..5ad867a3685 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5255c9d2e05..78682bfb340 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -71,6 +71,7 @@ #include #include #include +#include #include #include diff --git a/kernel/sysctl.c b/kernel/sysctl.c index f487f257e05..11d53046b90 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include diff --git a/mm/mmap.c b/mm/mmap.c index db05495d6d0..694a8625ab0 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -935,6 +935,19 @@ void vm_stat_account(struct mm_struct *mm, unsigned long flags, } #endif /* CONFIG_PROC_FS */ +/* + * If a hint addr is less than mmap_min_addr change hint to be as + * low as possible but still greater than mmap_min_addr + */ +static inline unsigned long round_hint_to_min(unsigned long hint) +{ + hint &= PAGE_MASK; + if (((void *)hint != NULL) && + (hint < mmap_min_addr)) + return PAGE_ALIGN(mmap_min_addr); + return hint; +} + /* * The caller must hold down_write(¤t->mm->mmap_sem). */ diff --git a/security/commoncap.c b/security/commoncap.c index 7ce191ea29a..0cf4b53480a 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -28,6 +28,7 @@ #include #include #include +#include /* * If a non-root user executes a setuid-root binary in diff --git a/security/security.c b/security/security.c index 44177add471..bf619ffc9a4 100644 --- a/security/security.c +++ b/security/security.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #define MAX_LSM_EVM_XATTR 2 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6a3683e2842..30492990937 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -81,6 +81,8 @@ #include #include #include +#include +#include #include "avc.h" #include "objsec.h" diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index e8af5b0ba80..cd667b4089a 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include "smack.h" #define task_security(task) (task_cred_xxx((task), security)) -- cgit v1.2.3 From bf06189e4d14641c0148bea16e9dd24943862215 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 14 Feb 2012 16:48:09 -0800 Subject: Yama: add PR_SET_PTRACER_ANY For a process to entirely disable Yama ptrace restrictions, it can use the special PR_SET_PTRACER_ANY pid to indicate that any otherwise allowed process may ptrace it. This is stronger than calling PR_SET_PTRACER with pid "1" because it includes processes in external pid namespaces. This is currently needed by the Chrome renderer, since its crash handler (Breakpad) runs external to the renderer's pid namespace. Signed-off-by: Kees Cook Signed-off-by: James Morris --- Documentation/security/Yama.txt | 7 ++++++- include/linux/prctl.h | 1 + security/yama/yama_lsm.c | 8 ++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/Documentation/security/Yama.txt b/Documentation/security/Yama.txt index 4f0b7896a21..a9511f17906 100644 --- a/Documentation/security/Yama.txt +++ b/Documentation/security/Yama.txt @@ -41,7 +41,12 @@ other process (and its descendents) are allowed to call PTRACE_ATTACH against it. Only one such declared debugging process can exists for each inferior at a time. For example, this is used by KDE, Chromium, and Firefox's crash handlers, and by Wine for allowing only Wine processes -to ptrace each other. +to ptrace each other. If a process wishes to entirely disable these ptrace +restrictions, it can call prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, ...) +so that any otherwise allowed process (even those in external pid namespaces) +may attach. + +The sysctl settings are: 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other process running under the same uid, as long as it is dumpable (i.e. diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 4d0e5bc5458..a0413ac3abe 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -119,5 +119,6 @@ * A value of 0 mean "no process". */ #define PR_SET_PTRACER 0x59616d61 +# define PR_SET_PTRACER_ANY ((unsigned long)-1) #endif /* _LINUX_PRCTL_H */ diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index dd4d36067c5..573723843a0 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -84,7 +84,7 @@ static void yama_ptracer_del(struct task_struct *tracer, spin_lock_bh(&ptracer_relations_lock); list_for_each_entry_safe(relation, safe, &ptracer_relations, node) if (relation->tracee == tracee || - relation->tracer == tracer) { + (tracer && relation->tracer == tracer)) { list_del(&relation->node); kfree(relation); } @@ -138,6 +138,8 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, if (arg2 == 0) { yama_ptracer_del(NULL, myself); rc = 0; + } else if (arg2 == PR_SET_PTRACER_ANY) { + rc = yama_ptracer_add(NULL, myself); } else { struct task_struct *tracer; @@ -208,6 +210,7 @@ static int ptracer_exception_found(struct task_struct *tracer, int rc = 0; struct ptrace_relation *relation; struct task_struct *parent = NULL; + bool found = false; spin_lock_bh(&ptracer_relations_lock); rcu_read_lock(); @@ -216,10 +219,11 @@ static int ptracer_exception_found(struct task_struct *tracer, list_for_each_entry(relation, &ptracer_relations, node) if (relation->tracee == tracee) { parent = relation->tracer; + found = true; break; } - if (task_is_descendant(parent, tracer)) + if (found && (parent == NULL || task_is_descendant(parent, tracer))) rc = 1; rcu_read_unlock(); spin_unlock_bh(&ptracer_relations_lock); -- cgit v1.2.3 From b0d5de4d58803bbcce2b8175a8dd21c559a3abc1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 14 Feb 2012 17:11:07 -0500 Subject: IMA: fix audit res field to indicate 1 for success and 0 for failure The audit res field ususally indicates success with a 1 and 0 for a failure. So make IMA do it the same way. Signed-off-by: Eric Paris Signed-off-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_audit.c | 2 +- security/integrity/ima/ima_policy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c index 2ad942fb1e2..21e96bf188d 100644 --- a/security/integrity/ima/ima_audit.c +++ b/security/integrity/ima/ima_audit.c @@ -61,6 +61,6 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_format(ab, " ino=%lu", inode->i_ino); } - audit_log_format(ab, " res=%d", !result ? 0 : 1); + audit_log_format(ab, " res=%d", !result); audit_log_end(ab); } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0fb643a9c91..d8edff209bf 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -418,7 +418,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) if (!result && (entry->action == UNKNOWN)) result = -EINVAL; - audit_log_format(ab, "res=%d", !!result); + audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; } -- cgit v1.2.3 From 9acd494be9387b0608612cd139967201dd7a4e12 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 26 Jan 2012 16:29:20 -0800 Subject: AppArmor: refactor securityfs to use structures Use a file tree structure to represent the AppArmor securityfs. Signed-off-by: Kees Cook Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 132 ++++++++++++++++++++++----------- security/apparmor/include/apparmorfs.h | 24 ++++++ 2 files changed, 114 insertions(+), 42 deletions(-) (limited to 'security') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index e39df6d4377..1e22bb3a885 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -144,36 +144,103 @@ static const struct file_operations aa_fs_profile_remove = { /** Base file system setup **/ -static struct dentry *aa_fs_dentry __initdata; +static struct aa_fs_entry aa_fs_entry_apparmor[] = { + AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), + AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), + AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), + { } +}; -static void __init aafs_remove(const char *name) -{ - struct dentry *dentry; +static struct aa_fs_entry aa_fs_entry = + AA_FS_DIR("apparmor", aa_fs_entry_apparmor); - dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); - if (!IS_ERR(dentry)) { - securityfs_remove(dentry); - dput(dentry); +/** + * aafs_create_file - create a file entry in the apparmor securityfs + * @fs_file: aa_fs_entry to build an entry for (NOT NULL) + * @parent: the parent dentry in the securityfs + * + * Use aafs_remove_file to remove entries created with this fn. + */ +static int __init aafs_create_file(struct aa_fs_entry *fs_file, + struct dentry *parent) +{ + int error = 0; + + fs_file->dentry = securityfs_create_file(fs_file->name, + S_IFREG | fs_file->mode, + parent, fs_file, + fs_file->file_ops); + if (IS_ERR(fs_file->dentry)) { + error = PTR_ERR(fs_file->dentry); + fs_file->dentry = NULL; } + return error; } /** - * aafs_create - create an entry in the apparmor filesystem - * @name: name of the entry (NOT NULL) - * @mask: file permission mask of the file - * @fops: file operations for the file (NOT NULL) + * aafs_create_dir - recursively create a directory entry in the securityfs + * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) + * @parent: the parent dentry in the securityfs * - * Used aafs_remove to remove entries created with this fn. + * Use aafs_remove_dir to remove entries created with this fn. */ -static int __init aafs_create(const char *name, umode_t mask, - const struct file_operations *fops) +static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, + struct dentry *parent) { - struct dentry *dentry; + int error; + struct aa_fs_entry *fs_file; - dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, - NULL, fops); + fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent); + if (IS_ERR(fs_dir->dentry)) { + error = PTR_ERR(fs_dir->dentry); + fs_dir->dentry = NULL; + goto failed; + } - return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; + for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { + if (fs_file->v_type == AA_FS_TYPE_DIR) + error = aafs_create_dir(fs_file, fs_dir->dentry); + else + error = aafs_create_file(fs_file, fs_dir->dentry); + if (error) + goto failed; + } + + return 0; + +failed: + return error; +} + +/** + * aafs_remove_file - drop a single file entry in the apparmor securityfs + * @fs_file: aa_fs_entry to detach from the securityfs (NOT NULL) + */ +static void __init aafs_remove_file(struct aa_fs_entry *fs_file) +{ + if (!fs_file->dentry) + return; + + securityfs_remove(fs_file->dentry); + fs_file->dentry = NULL; +} + +/** + * aafs_remove_dir - recursively drop a directory entry from the securityfs + * @fs_dir: aa_fs_entry (and all child entries) to detach (NOT NULL) + */ +static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir) +{ + struct aa_fs_entry *fs_file; + + for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { + if (fs_file->v_type == AA_FS_TYPE_DIR) + aafs_remove_dir(fs_file); + else + aafs_remove_file(fs_file); + } + + aafs_remove_file(fs_dir); } /** @@ -183,14 +250,7 @@ static int __init aafs_create(const char *name, umode_t mask, */ void __init aa_destroy_aafs(void) { - if (aa_fs_dentry) { - aafs_remove(".remove"); - aafs_remove(".replace"); - aafs_remove(".load"); - - securityfs_remove(aa_fs_dentry); - aa_fs_dentry = NULL; - } + aafs_remove_dir(&aa_fs_entry); } /** @@ -207,25 +267,13 @@ static int __init aa_create_aafs(void) if (!apparmor_initialized) return 0; - if (aa_fs_dentry) { + if (aa_fs_entry.dentry) { AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); return -EEXIST; } - aa_fs_dentry = securityfs_create_dir("apparmor", NULL); - if (IS_ERR(aa_fs_dentry)) { - error = PTR_ERR(aa_fs_dentry); - aa_fs_dentry = NULL; - goto error; - } - - error = aafs_create(".load", 0640, &aa_fs_profile_load); - if (error) - goto error; - error = aafs_create(".replace", 0640, &aa_fs_profile_replace); - if (error) - goto error; - error = aafs_create(".remove", 0640, &aa_fs_profile_remove); + /* Populate fs tree. */ + error = aafs_create_dir(&aa_fs_entry, NULL); if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index cb1e93a114d..4fdf02f26a3 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -15,6 +15,30 @@ #ifndef __AA_APPARMORFS_H #define __AA_APPARMORFS_H +enum aa_fs_type { + AA_FS_TYPE_FOPS, + AA_FS_TYPE_DIR, +}; + +struct aa_fs_entry; + +struct aa_fs_entry { + const char *name; + struct dentry *dentry; + umode_t mode; + enum aa_fs_type v_type; + union { + struct aa_fs_entry *files; + } v; + const struct file_operations *file_ops; +}; + +#define AA_FS_FILE_FOPS(_name, _mode, _fops) \ + { .name = (_name), .v_type = AA_FS_TYPE_FOPS, \ + .mode = (_mode), .file_ops = (_fops) } +#define AA_FS_DIR(_name, _value) \ + { .name = (_name), .v_type = AA_FS_TYPE_DIR, .v.files = (_value) } + extern void __init aa_destroy_aafs(void); #endif /* __AA_APPARMORFS_H */ -- cgit v1.2.3 From e74abcf3359d0130e99a6511ac484a3ea9e6e988 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 26 Jan 2012 16:29:21 -0800 Subject: AppArmor: add initial "features" directory to securityfs This adds the "features" subdirectory to the AppArmor securityfs to display boolean features flags and the known capability mask. Signed-off-by: Kees Cook Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 51 ++++++++++++++++++++++++++++++++++ security/apparmor/include/apparmorfs.h | 14 ++++++++++ 2 files changed, 65 insertions(+) (limited to 'security') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 1e22bb3a885..f30dada0dca 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "include/apparmor.h" #include "include/apparmorfs.h" @@ -142,12 +143,62 @@ static const struct file_operations aa_fs_profile_remove = { .llseek = default_llseek, }; +static int aa_fs_seq_show(struct seq_file *seq, void *v) +{ + struct aa_fs_entry *fs_file = seq->private; + + if (!fs_file) + return 0; + + switch (fs_file->v_type) { + case AA_FS_TYPE_BOOLEAN: + seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no"); + break; + case AA_FS_TYPE_U64: + seq_printf(seq, "%#08lx\n", fs_file->v.u64); + break; + default: + /* Ignore unpritable entry types. */ + break; + } + + return 0; +} + +static int aa_fs_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_show, inode->i_private); +} + +const struct file_operations aa_fs_seq_file_ops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** Base file system setup **/ +static struct aa_fs_entry aa_fs_entry_domain[] = { + AA_FS_FILE_BOOLEAN("change_hat", 1), + AA_FS_FILE_BOOLEAN("change_hatv", 1), + AA_FS_FILE_BOOLEAN("change_onexec", 1), + AA_FS_FILE_BOOLEAN("change_profile", 1), + { } +}; + +static struct aa_fs_entry aa_fs_entry_features[] = { + AA_FS_DIR("domain", aa_fs_entry_domain), + AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), + { } +}; + static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), + AA_FS_DIR("features", aa_fs_entry_features), { } }; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 4fdf02f26a3..16e654530f3 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -16,6 +16,8 @@ #define __AA_APPARMORFS_H enum aa_fs_type { + AA_FS_TYPE_BOOLEAN, + AA_FS_TYPE_U64, AA_FS_TYPE_FOPS, AA_FS_TYPE_DIR, }; @@ -28,11 +30,23 @@ struct aa_fs_entry { umode_t mode; enum aa_fs_type v_type; union { + bool boolean; + unsigned long u64; struct aa_fs_entry *files; } v; const struct file_operations *file_ops; }; +extern const struct file_operations aa_fs_seq_file_ops; + +#define AA_FS_FILE_BOOLEAN(_name, _value) \ + { .name = (_name), .mode = 0444, \ + .v_type = AA_FS_TYPE_BOOLEAN, .v.boolean = (_value), \ + .file_ops = &aa_fs_seq_file_ops } +#define AA_FS_FILE_U64(_name, _value) \ + { .name = (_name), .mode = 0444, \ + .v_type = AA_FS_TYPE_U64, .v.u64 = (_value), \ + .file_ops = &aa_fs_seq_file_ops } #define AA_FS_FILE_FOPS(_name, _mode, _fops) \ { .name = (_name), .v_type = AA_FS_TYPE_FOPS, \ .mode = (_mode), .file_ops = (_fops) } -- cgit v1.2.3 From a9bf8e9fd561ba9ff1f0f2a1d96e439fcedaaaa4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 26 Jan 2012 16:29:22 -0800 Subject: AppArmor: add "file" details to securityfs Create the "file" directory in the securityfs for tracking features related to files. Signed-off-by: Kees Cook Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 10 ++++++++++ security/apparmor/include/apparmorfs.h | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'security') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index f30dada0dca..f9d0b5087be 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -154,6 +154,9 @@ static int aa_fs_seq_show(struct seq_file *seq, void *v) case AA_FS_TYPE_BOOLEAN: seq_printf(seq, "%s\n", fs_file->v.boolean ? "yes" : "no"); break; + case AA_FS_TYPE_STRING: + seq_printf(seq, "%s\n", fs_file->v.string); + break; case AA_FS_TYPE_U64: seq_printf(seq, "%#08lx\n", fs_file->v.u64); break; @@ -180,6 +183,12 @@ const struct file_operations aa_fs_seq_file_ops = { /** Base file system setup **/ +static struct aa_fs_entry aa_fs_entry_file[] = { + AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ + "link lock"), + { } +}; + static struct aa_fs_entry aa_fs_entry_domain[] = { AA_FS_FILE_BOOLEAN("change_hat", 1), AA_FS_FILE_BOOLEAN("change_hatv", 1), @@ -190,6 +199,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), + AA_FS_DIR("file", aa_fs_entry_file), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), { } }; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 16e654530f3..7ea4769fab3 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,6 +17,7 @@ enum aa_fs_type { AA_FS_TYPE_BOOLEAN, + AA_FS_TYPE_STRING, AA_FS_TYPE_U64, AA_FS_TYPE_FOPS, AA_FS_TYPE_DIR, @@ -31,6 +32,7 @@ struct aa_fs_entry { enum aa_fs_type v_type; union { bool boolean; + char *string; unsigned long u64; struct aa_fs_entry *files; } v; @@ -43,6 +45,10 @@ extern const struct file_operations aa_fs_seq_file_ops; { .name = (_name), .mode = 0444, \ .v_type = AA_FS_TYPE_BOOLEAN, .v.boolean = (_value), \ .file_ops = &aa_fs_seq_file_ops } +#define AA_FS_FILE_STRING(_name, _value) \ + { .name = (_name), .mode = 0444, \ + .v_type = AA_FS_TYPE_STRING, .v.string = (_value), \ + .file_ops = &aa_fs_seq_file_ops } #define AA_FS_FILE_U64(_name, _value) \ { .name = (_name), .mode = 0444, \ .v_type = AA_FS_TYPE_U64, .v.u64 = (_value), \ -- cgit v1.2.3 From d384b0a1a35f87f0ad70c29518f98f922b1c15cb Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 26 Jan 2012 16:29:23 -0800 Subject: AppArmor: export known rlimit names/value mappings in securityfs Since the parser needs to know which rlimits are known to the kernel, export the list via a mask file in the "rlimit" subdirectory in the securityfs "features" directory. Signed-off-by: Kees Cook Signed-off-by: John Johansen --- security/apparmor/Makefile | 24 ++++++++++++++++++------ security/apparmor/apparmorfs.c | 2 ++ security/apparmor/include/resource.h | 4 ++++ security/apparmor/resource.c | 5 +++++ 4 files changed, 29 insertions(+), 6 deletions(-) (limited to 'security') diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 2dafe50a2e2..86103ce5b7a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -28,25 +28,37 @@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\ # [RLIMIT_STACK] = "stack", # # and build a second integer table (with the second sed cmd), that maps -# RLIMIT defines to the order defined in asm-generic/resource.h Thi is +# RLIMIT defines to the order defined in asm-generic/resource.h This is # required by policy load to map policy ordering of RLIMITs to internal # ordering for architectures that redefine an RLIMIT. # Transforms lines from # #define RLIMIT_STACK 3 /* max stack size */ # to # RLIMIT_STACK, +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define RLIMIT_FSIZE 1 /* Maximum filesize */ +# #define RLIMIT_STACK 3 /* max stack size */ +# to +# #define AA_FS_RLIMIT_MASK "fsize stack" quiet_cmd_make-rlim = GEN $@ -cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\ +cmd_make-rlim = echo "static const char *rlim_names[RLIM_NLIMITS] = {" > $@ ;\ sed $< >> $@ -r -n \ -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\ echo "};" >> $@ ;\ - echo "static const int rlim_map[] = {" >> $@ ;\ + echo "static const int rlim_map[RLIM_NLIMITS] = {" >> $@ ;\ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ - echo "};" >> $@ + echo "};" >> $@ ; \ + echo -n '\#define AA_FS_RLIMIT_MASK "' >> $@ ;\ + sed -r -n 's/^\# ?define[ \t]+RLIMIT_([A-Z0-9_]+).*/\L\1/p' $< | \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ $(obj)/capability.o : $(obj)/capability_names.h $(obj)/resource.o : $(obj)/rlim_names.h -$(obj)/capability_names.h : $(srctree)/include/linux/capability.h +$(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ + $(src)/Makefile $(call cmd,make-caps) -$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h +$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \ + $(src)/Makefile $(call cmd,make-rlim) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index f9d0b5087be..16c15ec6f67 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -25,6 +25,7 @@ #include "include/audit.h" #include "include/context.h" #include "include/policy.h" +#include "include/resource.h" /** * aa_simple_write_to_buffer - common routine for getting policy from user @@ -201,6 +202,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { AA_FS_DIR("domain", aa_fs_entry_domain), AA_FS_DIR("file", aa_fs_entry_file), AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), + AA_FS_DIR("rlimit", aa_fs_entry_rlimit), { } }; diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h index 02baec732bb..d3f4cf02795 100644 --- a/security/apparmor/include/resource.h +++ b/security/apparmor/include/resource.h @@ -18,6 +18,8 @@ #include #include +#include "apparmorfs.h" + struct aa_profile; /* struct aa_rlimit - rlimit settings for the profile @@ -32,6 +34,8 @@ struct aa_rlimit { struct rlimit limits[RLIM_NLIMITS]; }; +extern struct aa_fs_entry aa_fs_entry_rlimit[]; + int aa_map_resource(int resource); int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *, unsigned int resource, struct rlimit *new_rlim); diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index a4136c10b1c..72c25a4f2cf 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -23,6 +23,11 @@ */ #include "rlim_names.h" +struct aa_fs_entry aa_fs_entry_rlimit[] = { + AA_FS_FILE_STRING("mask", AA_FS_RLIMIT_MASK), + { } +}; + /* audit callback for resource specific fields */ static void audit_cb(struct audit_buffer *ab, void *va) { -- cgit v1.2.3 From cdbd2884df8ad026143bb482a96d38e616947b17 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 07:06:41 -0800 Subject: AppArmor: Add mising end of structure test to caps unpacking The unpacking of struct capsx is missing a check for the end of the caps structure. This can lead to unpack failures depending on what else is packed into the policy file being unpacked. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/policy_unpack.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'security') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 741dd13e089..5c46acf5aa6 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -554,6 +554,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) goto fail; if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) goto fail; + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; } if (!unpack_rlimits(e, profile)) -- cgit v1.2.3 From ade3ddc01e2e426cc24c744be85dcaad4e8f8aba Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Feb 2012 00:20:26 -0800 Subject: AppArmor: Fix dropping of allowed operations that are force audited The audit permission flag, that specifies an audit message should be provided when an operation is allowed, was being ignored in some cases. This is because the auto audit mode (which determines the audit mode from system flags) was incorrectly assigned the same value as audit mode. The shared value would result in messages that should be audited going through a second evaluation as to whether they should be audited based on the auto audit, resulting in some messages being dropped. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/audit.c | 1 + security/apparmor/include/audit.h | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index f3fafedd798..61344b56722 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -89,6 +89,7 @@ static char *aa_audit_type[] = { "STATUS", "ERROR", "KILLED" + "AUTO" }; /* diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 1951786d32e..9317cd81416 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -28,8 +28,6 @@ struct aa_profile; extern const char *audit_mode_names[]; #define AUDIT_MAX_INDEX 5 -#define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ - enum audit_mode { AUDIT_NORMAL, /* follow normal auditing of accesses */ AUDIT_QUIET_DENIED, /* quiet all denied access messages */ @@ -45,7 +43,8 @@ enum audit_type { AUDIT_APPARMOR_HINT, AUDIT_APPARMOR_STATUS, AUDIT_APPARMOR_ERROR, - AUDIT_APPARMOR_KILL + AUDIT_APPARMOR_KILL, + AUDIT_APPARMOR_AUTO }; extern const char *op_table[]; -- cgit v1.2.3 From 8b964eae204d791421677ec56b94a7b18cf8740d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Feb 2012 00:32:30 -0800 Subject: AppArmor: Fix underflow in xindex calculation If the xindex value stored in the accept tables is 0, the extraction of that value will result in an underflow (0 - 4). In properly compiled policy this should not happen for file rules but it may be possible for other rule types in the future. To exploit this underflow a user would have to be able to load a corrupt policy, which requires CAP_MAC_ADMIN, overwrite system policy in kernel memory or know of a compiler error resulting in the flaw being present for loaded policy (no such flaw is known at this time). Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/include/file.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index ab8c6d87f75..f98fd4701d8 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -117,7 +117,7 @@ static inline u16 dfa_map_xindex(u16 mask) index |= AA_X_NAME; } else if (old_index == 3) { index |= AA_X_NAME | AA_X_CHILD; - } else { + } else if (old_index) { index |= AA_X_TABLE; index |= old_index - 4; } -- cgit v1.2.3 From 38305a4bab4be5d278443b057f7f5e97afb07f26 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Feb 2012 00:42:08 -0800 Subject: AppArmor: fix mapping of META_READ to audit and quiet flags The mapping of AA_MAY_META_READ for the allow mask was also being mapped to the audit and quiet masks. This would result in some operations being audited when the should not. This flaw was hidden by the previous audit bug which would drop some messages that where supposed to be audited. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/file.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 7312db74121..bba875c4d06 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -173,8 +173,6 @@ static u32 map_old_perms(u32 old) if (old & 0x40) /* AA_EXEC_MMAP */ new |= AA_EXEC_MMAP; - new |= AA_MAY_META_READ; - return new; } @@ -212,6 +210,7 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); perms.xindex = dfa_other_xindex(dfa, state); } + perms.allow |= AA_MAY_META_READ; /* change_profile wasn't determined by ownership in old mapping */ if (ACCEPT_TABLE(dfa)[state] & 0x80000000) -- cgit v1.2.3 From 28042fabf43b9a8ccfaa38f8c8187cc525e53fd3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 06:21:30 -0800 Subject: AppArmor: Fix the error case for chroot relative path name lookup When a chroot relative pathname lookup fails it is falling through to do a d_absolute_path lookup. This is incorrect as d_absolute_path should only be used to lookup names for namespace absolute paths. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/path.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 9d070a7c3ff..c31ce837fef 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -91,9 +91,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, } path_put(&root); connected = 0; - } - - res = d_absolute_path(path, buf, buflen); + } else + res = d_absolute_path(path, buf, buflen); *name = res; /* handle error conditions - and still allow a partial path to -- cgit v1.2.3 From a69f15890292b5449f9056b4bb322b044e6ce0c6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 24 Feb 2012 11:28:05 -0800 Subject: security: fix ima kconfig warning Fix IMA kconfig warning on non-X86 architectures: warning: (IMA) selects TCG_TIS which has unmet direct dependencies (TCG_TPM && X86) Signed-off-by: Randy Dunlap Reported-by: Geert Uytterhoeven Acked-by: Rajiv Andrade Signed-off-by: James Morris --- security/integrity/ima/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 063298a797e..35664fe6daa 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -10,7 +10,7 @@ config IMA select CRYPTO_MD5 select CRYPTO_SHA1 select TCG_TPM if HAS_IOMEM && !UML - select TCG_TIS if TCG_TPM + select TCG_TIS if TCG_TPM && X86 help The Trusted Computing Group(TCG) runtime Integrity Measurement Architecture(IMA) maintains a list of hash -- cgit v1.2.3 From df91e49477a9be15921cb2854e1d12a3bdb5e425 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 29 Feb 2012 21:53:22 +0900 Subject: TOMOYO: Fix mount flags checking order. Userspace can pass in arbitrary combinations of MS_* flags to mount(). If both MS_BIND and one of MS_SHARED/MS_PRIVATE/MS_SLAVE/MS_UNBINDABLE are passed, device name which should be checked for MS_BIND was not checked because MS_SHARED/MS_PRIVATE/MS_SLAVE/MS_UNBINDABLE had higher priority than MS_BIND. If both one of MS_BIND/MS_MOVE and MS_REMOUNT are passed, device name which should not be checked for MS_REMOUNT was checked because MS_BIND/MS_MOVE had higher priority than MS_REMOUNT. Fix these bugs by changing priority to MS_REMOUNT -> MS_BIND -> MS_SHARED/MS_PRIVATE/MS_SLAVE/MS_UNBINDABLE -> MS_MOVE as with do_mount() does. Also, unconditionally return -EINVAL if more than one of MS_SHARED/MS_PRIVATE/MS_SLAVE/MS_UNBINDABLE is passed so that TOMOYO will not generate inaccurate audit logs, for commit 7a2e8a8f "VFS: Sanity check mount flags passed to change_mnt_propagation()" clarified that these flags must be exclusively passed. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/mount.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'security') diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index bee09d06205..fe00cdfd026 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -199,30 +199,32 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, if (flags & MS_REMOUNT) { type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]; flags &= ~MS_REMOUNT; - } - if (flags & MS_MOVE) { - type = tomoyo_mounts[TOMOYO_MOUNT_MOVE]; - flags &= ~MS_MOVE; - } - if (flags & MS_BIND) { + } else if (flags & MS_BIND) { type = tomoyo_mounts[TOMOYO_MOUNT_BIND]; flags &= ~MS_BIND; - } - if (flags & MS_UNBINDABLE) { - type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE]; - flags &= ~MS_UNBINDABLE; - } - if (flags & MS_PRIVATE) { + } else if (flags & MS_SHARED) { + if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) + return -EINVAL; + type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]; + flags &= ~MS_SHARED; + } else if (flags & MS_PRIVATE) { + if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) + return -EINVAL; type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE]; flags &= ~MS_PRIVATE; - } - if (flags & MS_SLAVE) { + } else if (flags & MS_SLAVE) { + if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) + return -EINVAL; type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE]; flags &= ~MS_SLAVE; - } - if (flags & MS_SHARED) { - type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]; - flags &= ~MS_SHARED; + } else if (flags & MS_UNBINDABLE) { + if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) + return -EINVAL; + type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE]; + flags &= ~MS_UNBINDABLE; + } else if (flags & MS_MOVE) { + type = tomoyo_mounts[TOMOYO_MOUNT_MOVE]; + flags &= ~MS_MOVE; } if (!type) type = ""; -- cgit v1.2.3 From f67dabbdde1fe112dfff05d02890f1e0d54117a8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 6 Mar 2012 13:32:16 +0000 Subject: KEYS: testing wrong bit for KEY_FLAG_REVOKED The test for "if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {" should actually testing that the (1 << KEY_FLAG_REVOKED) bit is set. The current code actually checks for KEY_FLAG_DEAD. Signed-off-by: Dan Carpenter Signed-off-by: David Howells Signed-off-by: James Morris --- security/keys/process_keys.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 1068cb1939b..be7ecb2018d 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -657,7 +657,8 @@ try_again: goto error; down_read(&cred->request_key_auth->sem); - if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) { + if (test_bit(KEY_FLAG_REVOKED, + &cred->request_key_auth->flags)) { key_ref = ERR_PTR(-EKEYREVOKED); key = NULL; } else { -- cgit v1.2.3 From ef9a762279c9ce98c592fb144b31898411feb94d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 10 Mar 2012 11:19:51 -0800 Subject: AppArmor: Fix error returned when a path lookup is disconnected The returning of -ESATLE when a path lookup fails as disconnected is wrong. Since AppArmor is rejecting the access return -EACCES instead. This also fixes a bug in complain (learning) mode where disconnected paths are denied because -ESTALE errors are not ignored causing failures that can change application behavior. Signed-off-by: John Johansen --- security/apparmor/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/apparmor/path.c b/security/apparmor/path.c index c31ce837fef..3dd605c6997 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -137,7 +137,7 @@ ok: /* disconnected path, don't return pathname starting * with '/' */ - error = -ESTALE; + error = -EACCES; if (*res == '/') *name = res + 1; } -- cgit v1.2.3 From b1b4bc2ed94d157f3ed60c17a12b658ccb96a76f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 10 Mar 2012 11:25:30 -0800 Subject: AppArmor: Fix oops in policy unpack auditing Post unpacking of policy a verification pass is made on x transition indexes. When this fails a call to audit_iface is made resulting in an oops, because audit_iface is expecting a valid buffer position but since the failure comes from post unpack verification there is none. Make the position argument optional so that audit_iface can be called from post unpack verification. Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 5c46acf5aa6..c50634b724b 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -84,7 +84,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) * @new: profile if it has been allocated (MAYBE NULL) * @name: name of the profile being manipulated (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL) - * @e: buffer position info (NOT NULL) + * @e: buffer position info * @error: error code * * Returns: %0 or error @@ -95,7 +95,8 @@ static int audit_iface(struct aa_profile *new, const char *name, struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; COMMON_AUDIT_DATA_INIT(&sa, NONE); - sa.aad.iface.pos = e->pos - e->start; + if (e) + sa.aad.iface.pos = e->pos - e->start; sa.aad.iface.target = new; sa.aad.name = name; sa.aad.info = info; -- cgit v1.2.3 From 33e521acff709d275950ec5bf8dd577d873cd61e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 14 Mar 2012 05:53:40 -0700 Subject: AppArmor: Add const qualifiers to generated string tables Signed-off-by: John Johansen --- security/apparmor/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 86103ce5b7a..7e14edd9ec6 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -15,7 +15,7 @@ clean-files := capability_names.h rlim_names.h # to # [1] = "dac_override", quiet_cmd_make-caps = GEN $@ -cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\ +cmd_make-caps = echo "static const char const *capability_names[] = {" > $@ ;\ sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ @@ -43,7 +43,8 @@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\ # to # #define AA_FS_RLIMIT_MASK "fsize stack" quiet_cmd_make-rlim = GEN $@ -cmd_make-rlim = echo "static const char *rlim_names[RLIM_NLIMITS] = {" > $@ ;\ +cmd_make-rlim = echo "static const char const *rlim_names[RLIM_NLIMITS] = {" \ + > $@ ;\ sed $< >> $@ -r -n \ -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\ echo "};" >> $@ ;\ -- cgit v1.2.3 From fbba8d89acea5d628d1d076b1d8962db438ff832 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 06:28:50 -0800 Subject: AppArmor: Retrieve the dentry_path for error reporting when path lookup fails When __d_path and d_absolute_path fail due to the name being outside of the current namespace no name is reported. Use dentry_path to provide some hint as to which file was being accessed. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/path.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'security') diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 3dd605c6997..8c90fd0f49c 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -94,18 +94,21 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, } else res = d_absolute_path(path, buf, buflen); - *name = res; /* handle error conditions - and still allow a partial path to * be returned. */ if (IS_ERR(res)) { - error = PTR_ERR(res); - *name = buf; - goto out; - } - if (!our_mnt(path->mnt)) + res = dentry_path_raw(path->dentry, buf, buflen); + if (IS_ERR(res)) { + error = PTR_ERR(res); + *name = buf; + goto out; + }; + } else if (!our_mnt(path->mnt)) connected = 0; + *name = res; + ok: /* Handle two cases: * 1. A deleted dentry && profile is not allowing mediation of deleted -- cgit v1.2.3 From 3372b68a3c982611dcc30b3c872f8bbdee019e5e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 06:32:47 -0800 Subject: AppArmor: Minor cleanup of d_namespace_path to consolidate error handling Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/path.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 8c90fd0f49c..a7936dfe0e6 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -83,21 +83,18 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, struct path root; get_fs_root(current->fs, &root); res = __d_path(path, &root, buf, buflen); - if (res && !IS_ERR(res)) { - /* everything's fine */ - *name = res; - path_put(&root); - goto ok; - } path_put(&root); - connected = 0; - } else + } else { res = d_absolute_path(path, buf, buflen); + if (!our_mnt(path->mnt)) + connected = 0; + } /* handle error conditions - and still allow a partial path to * be returned. */ - if (IS_ERR(res)) { + if (!res || IS_ERR(res)) { + connected = 0; res = dentry_path_raw(path->dentry, buf, buflen); if (IS_ERR(res)) { error = PTR_ERR(res); @@ -109,7 +106,6 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, *name = res; -ok: /* Handle two cases: * 1. A deleted dentry && profile is not allowing mediation of deleted * 2. On some filesystems, newly allocated dentries appear to the -- cgit v1.2.3 From 0fe1212d0539eb6c1e27d388711172d786e299cc Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 06:20:26 -0800 Subject: AppArmor: Update dfa matching routines. Update aa_dfa_match so that it doesn't result in an input string being walked twice (once to get its length and another time to match) Add a single step functions aa_dfa_next Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/include/apparmor.h | 2 +- security/apparmor/include/match.h | 3 ++ security/apparmor/match.c | 80 ++++++++++++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index df364956081..248c408ddc1 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -81,7 +81,7 @@ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start) { /* the null transition only needs the string's null terminator byte */ - return aa_dfa_match_len(dfa, start, "", 1); + return aa_dfa_next(dfa, start, 0); } static inline bool mediated_filesystem(struct inode *inode) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index a4a863997bd..775843e7f98 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -116,6 +116,9 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, const char *str, int len); unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, const char *str); +unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, + const char c); + void aa_dfa_free_kref(struct kref *kref); /** diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 94de6b4907c..90971a8c378 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -335,12 +335,12 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, } /** - * aa_dfa_next_state - traverse @dfa to find state @str stops at + * aa_dfa_match - traverse @dfa to find state @str stops at * @dfa: the dfa to match @str against (NOT NULL) * @start: the state of the dfa to start matching in * @str: the null terminated string of bytes to match against the dfa (NOT NULL) * - * aa_dfa_next_state will match @str against the dfa and return the state it + * aa_dfa_match will match @str against the dfa and return the state it * finished matching in. The final state can be used to look up the accepting * label, or as the start state of a continuing match. * @@ -349,5 +349,79 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, const char *str) { - return aa_dfa_match_len(dfa, start, str, strlen(str)); + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + unsigned int state = start, pos; + + if (state == 0) + return 0; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC]) { + /* Equivalence class table defined */ + u8 *equiv = EQUIV_TABLE(dfa); + /* default is direct to next state */ + while (*str) { + pos = base[state] + equiv[(u8) *str++]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + } else { + /* default is direct to next state */ + while (*str) { + pos = base[state] + (u8) *str++; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + } + + return state; +} + +/** + * aa_dfa_next - step one character to the next state in the dfa + * @dfa: the dfa to tranverse (NOT NULL) + * @state: the state to start in + * @c: the input character to transition on + * + * aa_dfa_match will step through the dfa by one input character @c + * + * Returns: state reach after input @c + */ +unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, + const char c) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + unsigned int pos; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC]) { + /* Equivalence class table defined */ + u8 *equiv = EQUIV_TABLE(dfa); + /* default is direct to next state */ + + pos = base[state] + equiv[(u8) c]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } else { + /* default is direct to next state */ + pos = base[state] + (u8) c; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + + return state; } -- cgit v1.2.3 From 57fa1e18091e66b7e1002816523cb218196a882e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 06:20:33 -0800 Subject: AppArmor: Move path failure information into aa_get_name and rename Move the path name lookup failure messages into the main path name lookup routine, as the information is useful in more than just aa_path_perm. Also rename aa_get_name to aa_path_name as it is not getting a reference counted object with a corresponding put fn. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/domain.c | 5 ++--- security/apparmor/file.c | 18 +++++++----------- security/apparmor/include/path.h | 3 ++- security/apparmor/path.c | 22 ++++++++++++++++++---- 4 files changed, 29 insertions(+), 19 deletions(-) (limited to 'security') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index c1e18ba5bdc..7c69599a69e 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -372,13 +372,12 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) state = profile->file.start; /* buffer freed below, name is pointer into buffer */ - error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer, - &name); + error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, + &name, &info); if (error) { if (profile->flags & (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) error = 0; - info = "Exec failed name resolution"; name = bprm->filename; goto audit; } diff --git a/security/apparmor/file.c b/security/apparmor/file.c index bba875c4d06..3022c0f4f0d 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -278,22 +278,16 @@ int aa_path_perm(int op, struct aa_profile *profile, struct path *path, int error; flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); - error = aa_get_name(path, flags, &buffer, &name); + error = aa_path_name(path, flags, &buffer, &name, &info); if (error) { if (error == -ENOENT && is_deleted(path->dentry)) { /* Access to open files that are deleted are * give a pass (implicit delegation) */ error = 0; + info = NULL; perms.allow = request; - } else if (error == -ENOENT) - info = "Failed name lookup - deleted entry"; - else if (error == -ESTALE) - info = "Failed name lookup - disconnected path"; - else if (error == -ENAMETOOLONG) - info = "Failed name lookup - name too long"; - else - info = "Failed name lookup"; + } } else { aa_str_perms(profile->file.dfa, profile->file.start, name, cond, &perms); @@ -364,12 +358,14 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, lperms = nullperms; /* buffer freed below, lname is pointer in buffer */ - error = aa_get_name(&link, profile->path_flags, &buffer, &lname); + error = aa_path_name(&link, profile->path_flags, &buffer, &lname, + &info); if (error) goto audit; /* buffer2 freed below, tname is pointer in buffer2 */ - error = aa_get_name(&target, profile->path_flags, &buffer2, &tname); + error = aa_path_name(&target, profile->path_flags, &buffer2, &tname, + &info); if (error) goto audit; diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 27b327a7fae..286ac75dc88 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -26,6 +26,7 @@ enum path_flags { PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ }; -int aa_get_name(struct path *path, int flags, char **buffer, const char **name); +int aa_path_name(struct path *path, int flags, char **buffer, + const char **name, const char **info); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/path.c b/security/apparmor/path.c index a7936dfe0e6..2daeea4f926 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -157,7 +157,7 @@ out: * Returns: %0 else error on failure */ static int get_name_to_buffer(struct path *path, int flags, char *buffer, - int size, char **name) + int size, char **name, const char **info) { int adjust = (flags & PATH_IS_DIR) ? 1 : 0; int error = d_namespace_path(path, buffer, size - adjust, name, flags); @@ -169,15 +169,27 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, */ strcpy(&buffer[size - 2], "/"); + if (info && error) { + if (error == -ENOENT) + *info = "Failed name lookup - deleted entry"; + else if (error == -ESTALE) + *info = "Failed name lookup - disconnected path"; + else if (error == -ENAMETOOLONG) + *info = "Failed name lookup - name too long"; + else + *info = "Failed name lookup"; + } + return error; } /** - * aa_get_name - compute the pathname of a file + * aa_path_name - compute the pathname of a file * @path: path the file (NOT NULL) * @flags: flags controlling path name generation * @buffer: buffer that aa_get_name() allocated (NOT NULL) * @name: Returns - the generated path name if !error (NOT NULL) + * @info: Returns - information on why the path lookup failed (MAYBE NULL) * * @name is a pointer to the beginning of the pathname (which usually differs * from the beginning of the buffer), or NULL. If there is an error @name @@ -190,7 +202,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, * * Returns: %0 else error code if could retrieve name */ -int aa_get_name(struct path *path, int flags, char **buffer, const char **name) +int aa_path_name(struct path *path, int flags, char **buffer, const char **name, + const char **info) { char *buf, *str = NULL; int size = 256; @@ -204,7 +217,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name) if (!buf) return -ENOMEM; - error = get_name_to_buffer(path, flags, buf, size, &str); + error = get_name_to_buffer(path, flags, buf, size, &str, info); if (error != -ENAMETOOLONG) break; @@ -212,6 +225,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name) size <<= 1; if (size > aa_g_path_max) return -ENAMETOOLONG; + *info = NULL; } *buffer = buf; *name = str; -- cgit v1.2.3 From 6041e8346f2165679c2184cab60db768d6a26a1d Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 14 Mar 2012 18:27:49 +0900 Subject: TOMOYO: Return appropriate value to poll(). "struct file_operations"->poll() expects "unsigned int" return value. All files in /sys/kernel/security/tomoyo/ directory other than /sys/kernel/security/tomoyo/query and /sys/kernel/security/tomoyo/audit should return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM rather than -ENOSYS. Also, /sys/kernel/security/tomoyo/query and /sys/kernel/security/tomoyo/audit should return POLLOUT | POLLWRNORM rather than 0 when there is no data to read. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/audit.c | 4 +-- security/tomoyo/common.c | 54 ++++++++++++++--------------------------- security/tomoyo/common.h | 6 ++--- security/tomoyo/securityfs_if.c | 5 ++-- 4 files changed, 26 insertions(+), 43 deletions(-) (limited to 'security') diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 5ca47ea3049..7ef9fa3e37e 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -446,11 +446,11 @@ void tomoyo_read_log(struct tomoyo_io_buffer *head) * tomoyo_poll_log - Wait for an audit log. * * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". + * @wait: Pointer to "poll_table". Maybe NULL. * * Returns POLLIN | POLLRDNORM when ready to read an audit log. */ -int tomoyo_poll_log(struct file *file, poll_table *wait) +unsigned int tomoyo_poll_log(struct file *file, poll_table *wait) { if (tomoyo_log_count) return POLLIN | POLLRDNORM; diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index c47d3ce6c73..d8561c30fbf 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2111,7 +2111,7 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid struct tomoyo_domain_info *domain = NULL; spin_lock(&tomoyo_query_list_lock); list_for_each_entry(ptr, &tomoyo_query_list, list) { - if (ptr->serial != serial || ptr->answer) + if (ptr->serial != serial) continue; domain = ptr->domain; break; @@ -2130,28 +2130,13 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid * * Waits for access requests which violated policy in enforcing mode. */ -static int tomoyo_poll_query(struct file *file, poll_table *wait) +static unsigned int tomoyo_poll_query(struct file *file, poll_table *wait) { - struct list_head *tmp; - bool found = false; - u8 i; - for (i = 0; i < 2; i++) { - spin_lock(&tomoyo_query_list_lock); - list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query *ptr = - list_entry(tmp, typeof(*ptr), list); - if (ptr->answer) - continue; - found = true; - break; - } - spin_unlock(&tomoyo_query_list_lock); - if (found) - return POLLIN | POLLRDNORM; - if (i) - break; - poll_wait(file, &tomoyo_query_wait, wait); - } + if (!list_empty(&tomoyo_query_list)) + return POLLIN | POLLRDNORM; + poll_wait(file, &tomoyo_query_wait, wait); + if (!list_empty(&tomoyo_query_list)) + return POLLIN | POLLRDNORM; return 0; } @@ -2175,8 +2160,6 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); - if (ptr->answer) - continue; if (pos++ != head->r.query_index) continue; len = ptr->query_len; @@ -2194,8 +2177,6 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); - if (ptr->answer) - continue; if (pos++ != head->r.query_index) continue; /* @@ -2243,8 +2224,10 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); if (ptr->serial != serial) continue; - if (!ptr->answer) - ptr->answer = answer; + ptr->answer = answer; + /* Remove from tomoyo_query_list. */ + if (ptr->answer) + list_del_init(&ptr->list); break; } spin_unlock(&tomoyo_query_list_lock); @@ -2477,18 +2460,17 @@ int tomoyo_open_control(const u8 type, struct file *file) * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. * * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". + * @wait: Pointer to "poll_table". Maybe NULL. * - * Waits for read readiness. - * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and - * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd. + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, + * POLLOUT | POLLWRNORM otherwise. */ -int tomoyo_poll_control(struct file *file, poll_table *wait) +unsigned int tomoyo_poll_control(struct file *file, poll_table *wait) { struct tomoyo_io_buffer *head = file->private_data; - if (!head->poll) - return -ENOSYS; - return head->poll(file, wait); + if (head->poll) + return head->poll(file, wait) | POLLOUT | POLLWRNORM; + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; } /** diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9512222d558..30fd9836970 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -788,7 +788,7 @@ struct tomoyo_acl_param { struct tomoyo_io_buffer { void (*read) (struct tomoyo_io_buffer *); int (*write) (struct tomoyo_io_buffer *); - int (*poll) (struct file *file, poll_table *wait); + unsigned int (*poll) (struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ struct mutex io_sem; char __user *read_user_buf; @@ -981,8 +981,8 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path, unsigned long number); int tomoyo_path_perm(const u8 operation, struct path *path, const char *target); -int tomoyo_poll_control(struct file *file, poll_table *wait); -int tomoyo_poll_log(struct file *file, poll_table *wait); +unsigned int tomoyo_poll_control(struct file *file, poll_table *wait); +unsigned int tomoyo_poll_log(struct file *file, poll_table *wait); int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, int addr_len); int tomoyo_socket_connect_permission(struct socket *sock, diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 482b2a5f48f..8592f2fc6eb 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -157,9 +157,10 @@ static int tomoyo_release(struct inode *inode, struct file *file) * tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface. * * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". + * @wait: Pointer to "poll_table". Maybe NULL. * - * Returns 0 on success, negative value otherwise. + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, + * POLLOUT | POLLWRNORM otherwise. */ static unsigned int tomoyo_poll(struct file *file, poll_table *wait) { -- cgit v1.2.3 From ad5ff3db53c68c2f12936bc74ea5dfe0af943592 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 16 Feb 2012 07:07:53 -0800 Subject: AppArmor: Add ability to load extended policy Add the base support for the new policy extensions. This does not bring any additional functionality, or change current semantics. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/include/apparmor.h | 13 +++++++++++++ security/apparmor/include/policy.h | 13 +++++++++++++ security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 24 +++++++++++++++++++++++- 4 files changed, 50 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 248c408ddc1..40aedd9f73e 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -19,6 +19,19 @@ #include "match.h" +/* + * Class of mediation types in the AppArmor policy db + */ +#define AA_CLASS_ENTRY 0 +#define AA_CLASS_UNKNOWN 1 +#define AA_CLASS_FILE 2 +#define AA_CLASS_CAP 3 +#define AA_CLASS_NET 4 +#define AA_CLASS_RLIMITS 5 +#define AA_CLASS_DOMAIN 6 + +#define AA_CLASS_LAST AA_CLASS_DOMAIN + /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; extern bool aa_g_audit_header; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index aeda5cf5690..9e18e9609e2 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -129,6 +129,17 @@ struct aa_namespace { struct list_head sub_ns; }; +/* struct aa_policydb - match engine for a policy + * dfa: dfa pattern match + * start: set of start states for the different classes of data + */ +struct aa_policydb { + /* Generic policy DFA specific rule types will be subsections of it */ + struct aa_dfa *dfa; + unsigned int start[AA_CLASS_LAST + 1]; + +}; + /* struct aa_profile - basic confinement data * @base - base components of the profile (name, refcount, lists, lock ...) * @parent: parent of profile @@ -143,6 +154,7 @@ struct aa_namespace { * @flags: flags controlling profile behavior * @path_flags: flags controlling path generation behavior * @size: the memory consumed by this profiles rules + * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile * @rlimits: rlimits for the profile @@ -179,6 +191,7 @@ struct aa_profile { u32 path_flags; int size; + struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; struct aa_rlimit rlimits; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4f0eadee78b..73288d0b541 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -749,6 +749,7 @@ static void free_profile(struct aa_profile *profile) aa_free_sid(profile->sid); aa_put_dfa(profile->xmatch); + aa_put_dfa(profile->policy.dfa); aa_put_profile(profile->replacedby); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c50634b724b..25fd51edc8d 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -469,7 +469,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; - int error = -EPROTO; + int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; @@ -562,6 +562,28 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; + if (unpack_nameX(e, AA_STRUCT, "policydb")) { + /* generic policy dfa - optional and may be NULL */ + profile->policy.dfa = unpack_dfa(e); + if (IS_ERR(profile->policy.dfa)) { + error = PTR_ERR(profile->policy.dfa); + profile->policy.dfa = NULL; + goto fail; + } + if (!unpack_u32(e, &profile->policy.start[0], "start")) + /* default start state */ + profile->policy.start[0] = DFA_START; + /* setup class index */ + for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) { + profile->policy.start[i] = + aa_dfa_next(profile->policy.dfa, + profile->policy.start[0], + i); + } + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } + /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { -- cgit v1.2.3 From 2d4cee7e3a2b9f9c3237672cc136e20dbad0e2ce Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 14 Mar 2012 13:30:36 +0100 Subject: AppArmor: add const qualifiers to string arrays Signed-off-by: Jan Engelhardt Signed-off-by: John Johansen --- security/apparmor/audit.c | 6 +++--- security/apparmor/include/audit.h | 4 ++-- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'security') diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 61344b56722..5ff67776a5a 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -19,7 +19,7 @@ #include "include/audit.h" #include "include/policy.h" -const char *op_table[] = { +const char *const op_table[] = { "null", "sysctl", @@ -73,7 +73,7 @@ const char *op_table[] = { "profile_remove" }; -const char *audit_mode_names[] = { +const char *const audit_mode_names[] = { "normal", "quiet_denied", "quiet", @@ -81,7 +81,7 @@ const char *audit_mode_names[] = { "all" }; -static char *aa_audit_type[] = { +static const char *const aa_audit_type[] = { "AUDIT", "ALLOWED", "DENIED", diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 9317cd81416..4ba78c203af 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -25,7 +25,7 @@ struct aa_profile; -extern const char *audit_mode_names[]; +extern const char *const audit_mode_names[]; #define AUDIT_MAX_INDEX 5 enum audit_mode { @@ -47,7 +47,7 @@ enum audit_type { AUDIT_APPARMOR_AUTO }; -extern const char *op_table[]; +extern const char *const op_table[]; enum aa_ops { OP_NULL, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 9e18e9609e2..bda4569fdd8 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -29,7 +29,7 @@ #include "file.h" #include "resource.h" -extern const char *profile_mode_names[]; +extern const char *const profile_mode_names[]; #define APPARMOR_NAMES_MAX_INDEX 3 #define COMPLAIN_MODE(_profile) \ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 73288d0b541..90641438302 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -93,7 +93,7 @@ /* root profile namespace */ struct aa_namespace *root_ns; -const char *profile_mode_names[] = { +const char *const profile_mode_names[] = { "enforce", "complain", "kill", -- cgit v1.2.3 From 7d7473dbdb9121dd1b5939566660d51130ecda3a Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 17 Mar 2012 20:33:38 +0900 Subject: TOMOYO: Return error if fails to delete a domain Call sequence: tomoyo_write_domain() --> tomoyo_delete_domain() In 'tomoyo_delete_domain', return -EINTR if locking attempt is interrupted by signal. At present it returns success to its caller 'tomoyo_write_domain()' even though domain is not deleted. 'tomoyo_write_domain()' assumes domain is deleted and returns success to its caller. This is wrong behaviour. 'tomoyo_write_domain' should return error from tomoyo_delete_domain() to its caller. Signed-off-by: Santosh Nayak Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index d8561c30fbf..8656b16eef7 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1069,7 +1069,7 @@ static int tomoyo_write_task(struct tomoyo_acl_param *param) * * @domainname: The name of domain. * - * Returns 0. + * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ @@ -1081,7 +1081,7 @@ static int tomoyo_delete_domain(char *domainname) name.name = domainname; tomoyo_fill_path_info(&name); if (mutex_lock_interruptible(&tomoyo_policy_lock)) - return 0; + return -EINTR; /* Is there an active domain? */ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { /* Never delete tomoyo_kernel_domain */ @@ -1164,15 +1164,16 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); unsigned int profile; if (*data == '<') { + int ret = 0; domain = NULL; if (is_delete) - tomoyo_delete_domain(data); + ret = tomoyo_delete_domain(data); else if (is_select) domain = tomoyo_find_domain(data); else domain = tomoyo_assign_domain(data, false); head->w.domain = domain; - return 0; + return ret; } if (!domain) return -EINVAL; -- cgit v1.2.3 From 7e570145cb022beeb58e3f691e0418477b670223 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 14 Mar 2012 23:41:17 -0700 Subject: AppArmor: Fix location of const qualifier on generated string tables Signed-off-by: Tetsuo Handa Signed-off-by: John Johansen --- security/apparmor/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 7e14edd9ec6..806bd19af7f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -15,7 +15,7 @@ clean-files := capability_names.h rlim_names.h # to # [1] = "dac_override", quiet_cmd_make-caps = GEN $@ -cmd_make-caps = echo "static const char const *capability_names[] = {" > $@ ;\ +cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ echo "};" >> $@ @@ -43,7 +43,7 @@ cmd_make-caps = echo "static const char const *capability_names[] = {" > $@ ;\ # to # #define AA_FS_RLIMIT_MASK "fsize stack" quiet_cmd_make-rlim = GEN $@ -cmd_make-rlim = echo "static const char const *rlim_names[RLIM_NLIMITS] = {" \ +cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ > $@ ;\ sed $< >> $@ -r -n \ -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\ -- cgit v1.2.3