Jamie Nguyen
dysco****@gmail*****
Thu Dec 2 00:46:09 JST 2010
Hi, I actually falsely thought that yama had already been integrated, but now realise that this is not the case. I can't tell from the discussions on LKML whether yama will make 2.6.37 or not. The latest patch I could find was here [1]. It couldn't apply, so after manually applying changes, I have posted my own working patch for 2.6.36.1 Linux kernel in case anyone is interested [2]. I can confirm that both ccs-patch and yama are doing their job correctly. It shouldn't matter which order ccs-patch and yama are applied in. I also copy this to tomoyo-users-en for those interested (which is probably more appropriate place for this thread anyway). Now I have working ccs-patched kernel with extra symlink, hardlink and ptrace scope restrictions that can in particular defend against /tmp race conditions (among other things) :-) Kind regards [1] http://lkml.org/lkml/2010/6/28/233 [2] diff -uNr linux-2.6.36/Documentation/Yama.txt linux-2.6.36.new/Documentation/Yama.txt --- linux-2.6.36/Documentation/Yama.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.36.new/Documentation/Yama.txt 2010-12-01 14:51:55.459000003 +0000 @@ -0,0 +1,91 @@ +Yama is a Linux Security Module that collects a number of 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: + +- protected_sticky_symlinks +- protected_nonaccess_hardlinks +- ptrace_scope + +============================================================== + +protected_sticky_symlinks: + +A long-standing class of security issues is the symlink-based +time-of-check-time-of-use race, most commonly seen in world-writable +directories like /tmp. The common method of exploitation of this flaw +is to cross privilege boundaries when following a given symlink (i.e. a +root process follows a symlink belonging to another user). For a likely +incomplete list of hundreds of examples across the years, please see: +http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp + +When set to "0", symlink following behavior is unrestricted. + +When set to "1" symlinks are permitted to be followed only when outside +a sticky world-writable directory, or when the uid of the symlink and +follower match, or when the directory owner matches the symlink's owner. + +This protection is based on the restrictions in Openwall and grsecurity. + +============================================================== + +protected_nonaccess_hardlinks: + +Hardlinks can be abused in a similar fashion to symlinks in sticky +world-writable directories, but their weakness is not limited to +just that scenario. For example, if /etc and /home are on the same +partition, a regular user can create a hardlink to /etc/shadow in their +home directory. While it retains the original owner and permissions, +it is possible for privileged programs that are otherwise symlink-safe +to mistakenly access the file through its hardlink. Additionally, a very +minor untraceable quota-bypassing local denial of service is possible by +an attacker exhausting disk space by filling a world-writable directory +with hardlinks. + +When set to "0", hardlink creation behavior is unrestricted. + +When set to "1", hardlinks cannot be created to files that a given user +would be unable to read and write originally, or are otherwise sensitive. + +This protection is based on the restrictions in Openwall and grsecurity. + +============================================================== + +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. +PTRACE is not commonly used by non-developers and non-admins, so 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). + +0 - classic PTRACE permissions: a process can PTRACE any other process + running under the same uid, as long as it is dumpable (i.e. did not + transition uids, start privileged, or have prctl(PR_SET_DUMPABLE...) + called). + +1 - child-only PTRACE: a process can PTRACE only its descendants when + the above classic criteria is also met. + +This protection is based on the restrictions in grsecurity. + +============================================================== diff -uNr linux-2.6.36/security/Kconfig linux-2.6.36.new/security/Kconfig --- linux-2.6.36/security/Kconfig 2010-12-01 14:54:31.538000003 +0000 +++ linux-2.6.36.new/security/Kconfig 2010-12-01 14:50:19.547000003 +0000 @@ -140,6 +140,7 @@ source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig +source security/yama/Kconfig source security/apparmor/Kconfig source security/integrity/ima/Kconfig @@ -165,6 +166,9 @@ config DEFAULT_SECURITY_TOMOYO bool "TOMOYO" if SECURITY_TOMOYO=y + config DEFAULT_SECURITY_YAMA + bool "Yama" if SECURITY_YAMA=y + config DEFAULT_SECURITY_APPARMOR bool "AppArmor" if SECURITY_APPARMOR=y @@ -178,6 +182,7 @@ default "selinux" if DEFAULT_SECURITY_SELINUX default "smack" if DEFAULT_SECURITY_SMACK default "tomoyo" if DEFAULT_SECURITY_TOMOYO + default "yama" if DEFAULT_SECURITY_YAMA default "apparmor" if DEFAULT_SECURITY_APPARMOR default "" if DEFAULT_SECURITY_DAC diff -uNr linux-2.6.36/security/Makefile linux-2.6.36.new/security/Makefile --- linux-2.6.36/security/Makefile 2010-12-01 14:54:31.538000003 +0000 +++ linux-2.6.36.new/security/Makefile 2010-12-01 14:51:15.227000003 +0000 @@ -6,6 +6,7 @@ subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo +subdir-$(CONFIG_SECURITY_YAMA) += yama subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor # always enable default capabilities @@ -20,6 +21,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_YAMA) += yama/built-in.o obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o diff -uNr linux-2.6.36/security/yama/Kconfig linux-2.6.36.new/security/yama/Kconfig --- linux-2.6.36/security/yama/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.36.new/security/yama/Kconfig 2010-12-01 14:52:02.340000003 +0000 @@ -0,0 +1,13 @@ +config SECURITY_YAMA + bool "Yama NAC Support" + depends on SECURITY + select SECURITYFS + select SECURITY_PATH + default n + help + This selects Yama, the NAKed Access Control system which + provides additional global security settings above regular + Linux discretionary access controls. Currently available + are symlink, hardlink, and PTRACE scope restrictions. + + If you are unsure how to answer this question, answer N. diff -uNr linux-2.6.36/security/yama/Makefile linux-2.6.36.new/security/yama/Makefile --- linux-2.6.36/security/yama/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.36.new/security/yama/Makefile 2010-12-01 14:52:04.387000003 +0000 @@ -0,0 +1,3 @@ +obj-$(CONFIG_SECURITY_YAMA) := yama.o + +yama-y := yama_lsm.o diff -uNr linux-2.6.36/security/yama/yama_lsm.c linux-2.6.36.new/security/yama/yama_lsm.c --- linux-2.6.36/security/yama/yama_lsm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.36.new/security/yama/yama_lsm.c 2010-12-01 14:52:04.387000003 +0000 @@ -0,0 +1,235 @@ +/* + * Yama Linux Security Module + * + * Author: Kees Cook <kees.****@canon*****> + * + * Copyright (C) 2010 Canonical, Ltd. + * + * 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 <linux/security.h> +#include <linux/sysctl.h> +#include <linux/ptrace.h> +#include <linux/ratelimit.h> + +static int ptrace_scope = 1; +static int protected_sticky_symlinks = 1; +static int protected_nonaccess_hardlinks = 1; + +/** + * yama_ptrace_access_check - validate PTRACE_ATTACH calls + * @child: child task pointer + * @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; + + rc = cap_ptrace_access_check(child, mode); + if (rc != 0) + return rc; + + /* require ptrace target be a child of ptracer on attach */ + if (mode == PTRACE_MODE_ATTACH && ptrace_scope && + !capable(CAP_SYS_PTRACE)) { + struct task_struct *walker = child; + + rcu_read_lock(); + read_lock(&tasklist_lock); + while (walker->pid > 0) { + if (walker == current) + break; + walker = walker->real_parent; + } + if (walker->pid == 0) + rc = -EPERM; + read_unlock(&tasklist_lock); + rcu_read_unlock(); + } + + if (rc) { + char name[sizeof(current->comm)]; + printk_ratelimited(KERN_INFO "ptrace of non-child" + " pid %d was attempted by: %s (pid %d)\n", + child->pid, + get_task_comm(name, current), + current->pid); + } + + return rc; +} + +/** + * yama_inode_follow_link - check for symlinks in sticky world-writeable dirs + * @dentry: The inode/dentry of the symlink + * @nameidata: The path data of the symlink + * + * In the case of the protected_sticky_symlinks sysctl being enabled, + * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is + * in a sticky world-writable directory. This is to protect privileged + * processes from failing races against path names that may change out + * from under them by way of other users creating malicious symlinks. + * It will permit symlinks to only be followed when outside a sticky + * world-writable directory, or when the uid of the symlink and follower + * match, or when the directory owner matches the symlink's owner. + * + * Returns 0 if following the symlink is allowed, -ve on error. + */ +static int yama_inode_follow_link(struct dentry *dentry, + struct nameidata *nameidata) +{ + int rc = 0; + const struct inode *parent; + const struct inode *inode; + const struct cred *cred; + + if (!protected_sticky_symlinks) + return 0; + + /* owner and follower match? */ + cred = current_cred(); + inode = dentry->d_inode; + if (cred->fsuid == inode->i_uid) + return 0; + + /* check parent directory mode and owner */ + spin_lock(&dentry->d_lock); + parent = dentry->d_parent->d_inode; + if ((parent->i_mode & (S_ISVTX|S_IWOTH)) == (S_ISVTX|S_IWOTH) && + parent->i_uid != inode->i_uid) { + rc = -EACCES; + } + spin_unlock(&dentry->d_lock); + + if (rc) { + char name[sizeof(current->comm)]; + printk_ratelimited(KERN_NOTICE "non-matching-uid symlink " + "following attempted in sticky world-writable " + "directory by %s (fsuid %d != %d)\n", + get_task_comm(name, current), + cred->fsuid, inode->i_uid); + } + + return rc; +} + +/** + * yama_path_link - verify that hardlinking is allowed + * @old_dentry: the source inode/dentry to hardlink from + * @new_dir: target directory + * @new_dentry: the target inode/dentry to hardlink to + * + * Block hardlink when all of: + * - fsuid does not match inode + * - not CAP_FOWNER + * - and at least one of: + * - inode is not a regular file + * - inode is setuid + * - inode is setgid and group-exec + * - access failure for read and write + * + * Returns 0 if successful, -ve on error. + */ +static int yama_path_link(struct dentry *old_dentry, struct path *new_dir, + struct dentry *new_dentry) +{ + int rc = 0; + struct inode *inode = old_dentry->d_inode; + const int mode = inode->i_mode; + const struct cred *cred = current_cred(); + + if (!protected_nonaccess_hardlinks) + return 0; + + if (cred->fsuid != inode->i_uid && + (!S_ISREG(mode) || (mode & S_ISUID) || + ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) || + (generic_permission(inode, MAY_READ | MAY_WRITE, NULL))) && + !capable(CAP_FOWNER)) { + char name[sizeof(current->comm)]; + printk_ratelimited(KERN_INFO "non-accessible hardlink" + " creation was attempted by: %s (fsuid %d)\n", + get_task_comm(name, current), + cred->fsuid); + rc = -EPERM; + } + + return rc; +} + +static struct security_operations yama_ops = { + .name = "yama", + + .ptrace_access_check = yama_ptrace_access_check, + .inode_follow_link = yama_inode_follow_link, + .path_link = yama_path_link, +}; + +#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 = "protected_sticky_symlinks", + .data = &protected_sticky_symlinks, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { + .procname = "protected_nonaccess_hardlinks", + .data = &protected_nonaccess_hardlinks, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { + .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);