Yama: add additional ptrace scopes

This expands the available Yama ptrace restrictions to include two more
modes. Mode 2 requires CAP_SYS_PTRACE for PTRACE_ATTACH, and mode 3
completely disables PTRACE_ATTACH (and locks the sysctl).

Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: James Morris <james.l.morris@oracle.com>
This commit is contained in:
Kees Cook 2012-04-16 11:56:45 -07:00 committed by James Morris
parent 8156b451f3
commit 389da25f93
2 changed files with 60 additions and 12 deletions

View File

@ -34,7 +34,7 @@ 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" work), or with CAP_SYS_PTRACE (i.e. "gdb --pid=PID", and "strace -p PID"
still work as root). still work as root).
For software that has defined application-specific relationships In mode 1, software that has defined application-specific relationships
between a debugging process and its inferior (crash handlers, etc), between a debugging process and its inferior (crash handlers, etc),
prctl(PR_SET_PTRACER, pid, ...) can be used. An inferior can declare which prctl(PR_SET_PTRACER, pid, ...) can be used. An inferior can declare which
other process (and its descendents) are allowed to call PTRACE_ATTACH other process (and its descendents) are allowed to call PTRACE_ATTACH
@ -46,6 +46,8 @@ restrictions, it can call prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, ...)
so that any otherwise allowed process (even those in external pid namespaces) so that any otherwise allowed process (even those in external pid namespaces)
may attach. may attach.
These restrictions do not change how ptrace via PTRACE_TRACEME operates.
The sysctl settings are: The sysctl settings are:
0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other
@ -60,6 +62,12 @@ The sysctl settings are:
inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
an allowed debugger PID to call PTRACE_ATTACH on the inferior. an allowed debugger PID to call PTRACE_ATTACH on the inferior.
2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace
with PTRACE_ATTACH.
3 - no attach: no processes may use ptrace with PTRACE_ATTACH. Once set,
this sysctl cannot be changed to a lower value.
The original children-only logic was based on the restrictions in grsecurity. The original children-only logic was based on the restrictions in grsecurity.
============================================================== ==============================================================

View File

@ -18,7 +18,12 @@
#include <linux/prctl.h> #include <linux/prctl.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
static int ptrace_scope = 1; #define YAMA_SCOPE_DISABLED 0
#define YAMA_SCOPE_RELATIONAL 1
#define YAMA_SCOPE_CAPABILITY 2
#define YAMA_SCOPE_NO_ATTACH 3
static int ptrace_scope = YAMA_SCOPE_RELATIONAL;
/* describe a ptrace relationship for potential exception */ /* describe a ptrace relationship for potential exception */
struct ptrace_relation { struct ptrace_relation {
@ -251,17 +256,32 @@ static int yama_ptrace_access_check(struct task_struct *child,
return rc; return rc;
/* require ptrace target be a child of ptracer on attach */ /* require ptrace target be a child of ptracer on attach */
if (mode == PTRACE_MODE_ATTACH && if (mode == PTRACE_MODE_ATTACH) {
ptrace_scope && switch (ptrace_scope) {
!task_is_descendant(current, child) && case YAMA_SCOPE_DISABLED:
/* No additional restrictions. */
break;
case YAMA_SCOPE_RELATIONAL:
if (!task_is_descendant(current, child) &&
!ptracer_exception_found(current, child) && !ptracer_exception_found(current, child) &&
!capable(CAP_SYS_PTRACE)) !capable(CAP_SYS_PTRACE))
rc = -EPERM; rc = -EPERM;
break;
case YAMA_SCOPE_CAPABILITY:
if (!capable(CAP_SYS_PTRACE))
rc = -EPERM;
break;
case YAMA_SCOPE_NO_ATTACH:
default:
rc = -EPERM;
break;
}
}
if (rc) { if (rc) {
char name[sizeof(current->comm)]; char name[sizeof(current->comm)];
printk_ratelimited(KERN_NOTICE "ptrace of non-child" printk_ratelimited(KERN_NOTICE
" pid %d was attempted by: %s (pid %d)\n", "ptrace of pid %d was attempted by: %s (pid %d)\n",
child->pid, child->pid,
get_task_comm(name, current), get_task_comm(name, current),
current->pid); current->pid);
@ -279,8 +299,28 @@ static struct security_operations yama_ops = {
}; };
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
static int yama_dointvec_minmax(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int rc;
if (write && !capable(CAP_SYS_PTRACE))
return -EPERM;
rc = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
if (rc)
return rc;
/* Lock the max value if it ever gets set. */
if (write && *(int *)table->data == *(int *)table->extra2)
table->extra1 = table->extra2;
return rc;
}
static int zero; static int zero;
static int one = 1; static int one = 1;
static int max_scope = YAMA_SCOPE_NO_ATTACH;
struct ctl_path yama_sysctl_path[] = { struct ctl_path yama_sysctl_path[] = {
{ .procname = "kernel", }, { .procname = "kernel", },
@ -294,9 +334,9 @@ static struct ctl_table yama_sysctl_table[] = {
.data = &ptrace_scope, .data = &ptrace_scope,
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_minmax, .proc_handler = yama_dointvec_minmax,
.extra1 = &zero, .extra1 = &zero,
.extra2 = &one, .extra2 = &max_scope,
}, },
{ } { }
}; };