--- /dev/null
+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.
+
+==============================================================
--- /dev/null
+/*
+ * Yama Linux Security Module
+ *
+ * Author: Kees Cook <kees.cook@canonical.com>
+ *
+ * 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);