All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <kees.cook@canonical.com>
To: linux-security-module@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH 2/2] Yama: add PTRACE exception tracking
Date: Tue, 29 Jun 2010 17:40:27 -0700	[thread overview]
Message-ID: <20100630004027.GG4837@outflux.net> (raw)
In-Reply-To: <20100630003844.GE4837@outflux.net>

Some application suites have external crash handlers that depend on
being able to use PTRACE to generate crash reports (KDE, Chromium, etc).
Since the inferior process generally knows the PID of the debugger,
it can use PR_SET_PTRACER to allow a specific PID and its descendants
to perform the PTRACE instead of only a direct ancestor.

Signed-off-by: Kees Cook <kees.cook@canonical.com>
---
 include/linux/prctl.h    |    6 ++
 security/yama/yama_lsm.c |  205 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 193 insertions(+), 18 deletions(-)

diff --git a/include/linux/prctl.h b/include/linux/prctl.h
index a3baeb2..e435624 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -102,4 +102,10 @@
 
 #define PR_MCE_KILL_GET 34
 
+/*
+ * Set specific pid that is allowed to PTRACE the current task.
+ * A value of 0 mean "no process".
+ */
+#define PR_SET_PTRACER 35
+
 #endif /* _LINUX_PRCTL_H */
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 72929d2..0e989c4 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -14,15 +14,187 @@
 #include <linux/security.h>
 #include <linux/sysctl.h>
 #include <linux/ptrace.h>
+#include <linux/prctl.h>
 #include <linux/ratelimit.h>
 
 static int ptrace_scope = 1;
 static int protected_sticky_symlinks = 1;
 static int protected_nonaccess_hardlinks = 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 spinlock_t ptracer_relations_lock;
+
+/**
+ * yama_ptracer_add - add 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
+ *
+ * Returns 0 if relationship was added, -ve on error.
+ */
+static int yama_ptracer_add(struct task_struct *tracer,
+			    struct task_struct *tracee)
+{
+	struct ptrace_relation *relation;
+
+	relation = kzalloc(sizeof(*relation), GFP_KERNEL);
+	if (!relation)
+		return -ENOMEM;
+	relation->tracer = tracer;
+	relation->tracee = tracee;
+	spin_lock(&ptracer_relations_lock);
+	list_add(&relation->node, &ptracer_relations);
+	spin_unlock(&ptracer_relations_lock);
+
+	return 0;
+}
+
+/**
+ * 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;
+	struct list_head *list, *safe;
+
+	spin_lock(&ptracer_relations_lock);
+	list_for_each_safe(list, safe, &ptracer_relations) {
+		relation = list_entry(list, struct ptrace_relation, node);
+		if (relation->tracee == tracee ||
+		    relation->tracer == tracer) {
+			list_del(&relation->node);
+			kfree(relation);
+		}
+	}
+	spin_unlock(&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;
+
+	rc = cap_task_prctl(option, arg2, arg3, arg4, arg5);
+	if (rc != -ENOSYS)
+		return rc;
+
+	switch (option) {
+	case PR_SET_PTRACER:
+		if (arg2 == 0) {
+			yama_ptracer_del(NULL, current);
+			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, current);
+				put_task_struct(tracer);
+			}
+		}
+		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();
+	read_lock(&tasklist_lock);
+	while (walker->pid > 0) {
+		if (walker == parent) {
+			rc = 1;
+			break;
+		}
+		walker = walker->real_parent;
+	}
+	read_unlock(&tasklist_lock);
+	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(&ptracer_relations_lock);
+	list_for_each_entry(relation, &ptracer_relations, node)
+		if (relation->tracee == tracee) {
+			parent = relation->tracer;
+			break;
+		}
+	if (task_is_descendant(parent, tracer))
+		rc = 1;
+	spin_unlock(&ptracer_relations_lock);
+
+	return rc;
+}
+
 /**
  * yama_ptrace_access_check - validate PTRACE_ATTACH calls
- * @child: child task pointer
+ * @child: task that current task is attempting to PTRACE
  * @mode: ptrace attach mode
  *
  * Returns 0 if following the ptrace is allowed, -ve on error.
@@ -32,27 +204,20 @@ static int yama_ptrace_access_check(struct task_struct *child,
 {
 	int rc;
 
+	/* If standard caps disallows it, so does Yama.  We should
+	 * should only tighten restrictions further.
+	 */
 	rc = cap_ptrace_access_check(child, mode);
-	if (rc != 0)
+	if (rc)
 		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 (mode == PTRACE_MODE_ATTACH &&
+	    ptrace_scope &&
+	    !capable(CAP_SYS_PTRACE) &&
+	    !task_is_descendant(current, child) &&
+	    !ptracer_exception_found(current, child))
+		rc = -EPERM;
 
 	if (rc) {
 		char name[sizeof(current->comm)];
@@ -170,6 +335,8 @@ static struct security_operations yama_ops = {
 	.ptrace_access_check =	yama_ptrace_access_check,
 	.inode_follow_link =	yama_inode_follow_link,
 	.path_link =		yama_path_link,
+	.task_prctl =		yama_task_prctl,
+	.task_free =		yama_task_free,
 };
 
 #ifdef CONFIG_SYSCTL
@@ -221,6 +388,8 @@ static __init int yama_init(void)
 
 	printk(KERN_INFO "Yama: becoming mindful.\n");
 
+	spin_lock_init(&ptracer_relations_lock);
+
 	if (register_security(&yama_ops))
 		panic("Yama: kernel registration failed.\n");
 
-- 
1.7.1


-- 
Kees Cook
Ubuntu Security Team

  parent reply	other threads:[~2010-06-30  0:40 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-06-30  0:38 [PATCH 0/2] Yama: add PTRACE exception tracking Kees Cook
2010-06-30  0:39 ` [PATCH 1/2] security: create task_free security callback Kees Cook
2010-06-30  0:40 ` Kees Cook [this message]
2010-06-30  1:09   ` [PATCH 2/2] Yama: add PTRACE exception tracking Tetsuo Handa
2010-06-30  3:51     ` Kees Cook
2010-06-30  3:56   ` Serge E. Hallyn
2010-06-30  5:27     ` Kees Cook
2010-06-30 12:40       ` Serge E. Hallyn
2010-06-30 15:41   ` Eric Paris
2010-06-30 15:53     ` Kees Cook
2010-06-30 21:39       ` Tetsuo Handa
2010-06-30  7:31 ` [PATCH 0/2] " Christoph Hellwig
2010-06-30 15:45   ` Kees Cook
2010-07-01  1:39   ` James Morris
2010-07-01  4:44     ` Kees Cook
2010-07-01 13:20       ` Serge E. Hallyn
2010-07-01 15:22         ` Stephen Smalley
2010-07-01 17:16         ` Kees Cook
2010-07-01 19:41           ` Serge E. Hallyn
2010-07-01 19:57             ` Stephen Smalley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20100630004027.GG4837@outflux.net \
    --to=kees.cook@canonical.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.