| Some applications, like gdb, are able to ptrace both children or other |
| completely unrelated tasks. We would like to be able to discern these two |
| things and to be able to allow gdb to ptrace it's children, but not to be |
| able to ptrace unrelated tasks for security reasons. |
| |
| Upstream is a bit weary of this patch as it may be incomplete. They are |
| not fundamentally opposed to the patch, I was just ask to see if I could |
| flush out any needed refinement in Fedora where we already had the |
| problem. We may find that we need to emulate the YAMA non-child |
| registration module in order to completely deal with 'normal' ptrace on |
| a system. At the moment however, this patch will at least let us get |
| gdb working for many users in Fedora (See fedora-devel-list for a |
| discussion of the current issues people are complaining about in F17 |
| without this) |
| |
| |
| |
| security/selinux/hooks.c | 38 +++++++++++++++++++++++++++++++++++ |
| security/selinux/include/classmap.h | 2 +- |
| security/selinux/include/security.h | 2 ++ |
| security/selinux/selinuxfs.c | 3 ++- |
| security/selinux/ss/services.c | 3 +++ |
| 5 files changed, 46 insertions(+), 2 deletions(-) |
| |
| diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c |
| index 1a4acf4..b226f26 100644 |
| |
| |
| @@ -1805,6 +1805,39 @@ static inline u32 open_file_to_av(struct file *file) |
| |
| /* Hook functions begin here. */ |
| |
| +/** |
| + * 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; |
| +} |
| + |
| static int selinux_ptrace_access_check(struct task_struct *child, |
| unsigned int mode) |
| { |
| @@ -1820,6 +1853,9 @@ static int selinux_ptrace_access_check(struct task_struct *child, |
| return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); |
| } |
| |
| + |
| + if (selinux_policycap_ptrace_child && task_is_descendant(current, child)) |
| + return current_has_perm(child, PROCESS__PTRACE_CHILD); |
| return current_has_perm(child, PROCESS__PTRACE); |
| } |
| |
| @@ -1831,6 +1867,8 @@ static int selinux_ptrace_traceme(struct task_struct *parent) |
| if (rc) |
| return rc; |
| |
| + if (selinux_policycap_ptrace_child && task_is_descendant(parent, current)) |
| + return task_has_perm(parent, current, PROCESS__PTRACE_CHILD); |
| return task_has_perm(parent, current, PROCESS__PTRACE); |
| } |
| |
| diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h |
| index 39e678c..72c08b9 100644 |
| |
| |
| @@ -29,7 +29,7 @@ struct security_class_mapping secclass_map[] = { |
| "getattr", "setexec", "setfscreate", "noatsecure", "siginh", |
| "setrlimit", "rlimitinh", "dyntransition", "setcurrent", |
| "execmem", "execstack", "execheap", "setkeycreate", |
| - "setsockcreate", NULL } }, |
| + "setsockcreate", "ptrace_child", NULL } }, |
| { "system", |
| { "ipc_info", "syslog_read", "syslog_mod", |
| "syslog_console", "module_request", NULL } }, |
| diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h |
| index dde2005..ac14b0a 100644 |
| |
| |
| @@ -68,12 +68,14 @@ extern int selinux_enabled; |
| enum { |
| POLICYDB_CAPABILITY_NETPEER, |
| POLICYDB_CAPABILITY_OPENPERM, |
| + POLICYDB_CAPABILITY_PTRACE_CHILD, |
| __POLICYDB_CAPABILITY_MAX |
| }; |
| #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) |
| |
| extern int selinux_policycap_netpeer; |
| extern int selinux_policycap_openperm; |
| +extern int selinux_policycap_ptrace_child; |
| |
| /* |
| * type_datum properties |
| diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c |
| index 4e93f9e..3379765 100644 |
| |
| |
| @@ -44,7 +44,8 @@ |
| /* Policy capability filenames */ |
| static char *policycap_names[] = { |
| "network_peer_controls", |
| - "open_perms" |
| + "open_perms", |
| + "ptrace_child", |
| }; |
| |
| unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; |
| diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c |
| index 9b7e7ed..4d12a6e 100644 |
| |
| |
| @@ -72,6 +72,7 @@ |
| |
| int selinux_policycap_netpeer; |
| int selinux_policycap_openperm; |
| +int selinux_policycap_ptrace_child; |
| |
| static DEFINE_RWLOCK(policy_rwlock); |
| |
| @@ -1812,6 +1813,8 @@ static void security_load_policycaps(void) |
| POLICYDB_CAPABILITY_NETPEER); |
| selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, |
| POLICYDB_CAPABILITY_OPENPERM); |
| + selinux_policycap_ptrace_child = ebitmap_get_bit(&policydb.policycaps, |
| + POLICYDB_CAPABILITY_PTRACE_CHILD); |
| } |
| |
| static int security_preserve_bools(struct policydb *p); |
| |
| |
| |
| |
| _______________________________________________ |
| kernel mailing list |
| kernel@lists.fedoraproject.org |
| https://admin.fedoraproject.org/mailman/listinfo/kernel |