| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Yama Linux Security Module | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: Kees Cook <keescook@chromium.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2010 Canonical, Ltd. | 
					
						
							|  |  |  |  * Copyright (C) 2011 The Chromium OS Authors. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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/prctl.h>
 | 
					
						
							|  |  |  | #include <linux/ratelimit.h>
 | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | #include <linux/workqueue.h>
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | #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; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* describe a ptrace relationship for potential exception */ | 
					
						
							|  |  |  | struct ptrace_relation { | 
					
						
							|  |  |  | 	struct task_struct *tracer; | 
					
						
							|  |  |  | 	struct task_struct *tracee; | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	bool invalid; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 	struct list_head node; | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	struct rcu_head rcu; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static LIST_HEAD(ptracer_relations); | 
					
						
							|  |  |  | static DEFINE_SPINLOCK(ptracer_relations_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | static void yama_relation_cleanup(struct work_struct *work); | 
					
						
							|  |  |  | static DECLARE_WORK(yama_relation_work, yama_relation_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * yama_relation_cleanup - remove invalid entries from the relation list | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void yama_relation_cleanup(struct work_struct *work) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ptrace_relation *relation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&ptracer_relations_lock); | 
					
						
							|  |  |  | 	rcu_read_lock(); | 
					
						
							|  |  |  | 	list_for_each_entry_rcu(relation, &ptracer_relations, node) { | 
					
						
							|  |  |  | 		if (relation->invalid) { | 
					
						
							|  |  |  | 			list_del_rcu(&relation->node); | 
					
						
							|  |  |  | 			kfree_rcu(relation, rcu); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rcu_read_unlock(); | 
					
						
							|  |  |  | 	spin_unlock(&ptracer_relations_lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * yama_ptracer_add - add/replace 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 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Each tracee can have, at most, one tracer registered. Each time this | 
					
						
							|  |  |  |  * is called, the prior registered tracer will be replaced for the tracee. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 if relationship was added, -ve on error. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int yama_ptracer_add(struct task_struct *tracer, | 
					
						
							|  |  |  | 			    struct task_struct *tracee) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	struct ptrace_relation *relation, *added; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	added = kmalloc(sizeof(*added), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!added) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	added->tracee = tracee; | 
					
						
							|  |  |  | 	added->tracer = tracer; | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	added->invalid = false; | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	spin_lock(&ptracer_relations_lock); | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	rcu_read_lock(); | 
					
						
							|  |  |  | 	list_for_each_entry_rcu(relation, &ptracer_relations, node) { | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 		if (relation->invalid) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 		if (relation->tracee == tracee) { | 
					
						
							|  |  |  | 			list_replace_rcu(&relation->node, &added->node); | 
					
						
							|  |  |  | 			kfree_rcu(relation, rcu); | 
					
						
							|  |  |  | 			goto out; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	list_add_rcu(&added->node, &ptracer_relations); | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | out: | 
					
						
							|  |  |  | 	rcu_read_unlock(); | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	spin_unlock(&ptracer_relations_lock); | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	struct ptrace_relation *relation; | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	bool marked = false; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	rcu_read_lock(); | 
					
						
							|  |  |  | 	list_for_each_entry_rcu(relation, &ptracer_relations, node) { | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 		if (relation->invalid) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		if (relation->tracee == tracee || | 
					
						
							| 
									
										
										
										
											2012-02-14 16:48:09 -08:00
										 |  |  | 		    (tracer && relation->tracer == tracer)) { | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 			relation->invalid = true; | 
					
						
							|  |  |  | 			marked = true; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-10-18 14:53:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	rcu_read_unlock(); | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (marked) | 
					
						
							|  |  |  | 		schedule_work(&yama_relation_work); | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * yama_task_free - check for task_pid to remove from exception list | 
					
						
							|  |  |  |  * @task: task being removed | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | void yama_task_free(struct task_struct *task) | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 			   unsigned long arg4, unsigned long arg5) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 	struct task_struct *myself = current; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = cap_task_prctl(option, arg2, arg3, arg4, arg5); | 
					
						
							|  |  |  | 	if (rc != -ENOSYS) | 
					
						
							|  |  |  | 		return rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (option) { | 
					
						
							|  |  |  | 	case PR_SET_PTRACER: | 
					
						
							|  |  |  | 		/* Since a thread can call prctl(), find the group leader
 | 
					
						
							|  |  |  | 		 * before calling _add() or _del() on it, since we want | 
					
						
							|  |  |  | 		 * process-level granularity of control. The tracer group | 
					
						
							|  |  |  | 		 * leader checking is handled later when walking the ancestry | 
					
						
							|  |  |  | 		 * at the time of PTRACE_ATTACH check. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		rcu_read_lock(); | 
					
						
							|  |  |  | 		if (!thread_group_leader(myself)) | 
					
						
							|  |  |  | 			myself = rcu_dereference(myself->group_leader); | 
					
						
							|  |  |  | 		get_task_struct(myself); | 
					
						
							|  |  |  | 		rcu_read_unlock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (arg2 == 0) { | 
					
						
							|  |  |  | 			yama_ptracer_del(NULL, myself); | 
					
						
							|  |  |  | 			rc = 0; | 
					
						
							| 
									
										
										
										
											2012-08-27 11:38:13 -07:00
										 |  |  | 		} else if (arg2 == PR_SET_PTRACER_ANY || (int)arg2 == -1) { | 
					
						
							| 
									
										
										
										
											2012-02-14 16:48:09 -08:00
										 |  |  | 			rc = yama_ptracer_add(NULL, myself); | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		} 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, myself); | 
					
						
							|  |  |  | 				put_task_struct(tracer); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		put_task_struct(myself); | 
					
						
							|  |  |  | 		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(); | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * 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; | 
					
						
							| 
									
										
										
										
											2012-02-14 16:48:09 -08:00
										 |  |  | 	bool found = false; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rcu_read_lock(); | 
					
						
							|  |  |  | 	if (!thread_group_leader(tracee)) | 
					
						
							|  |  |  | 		tracee = rcu_dereference(tracee->group_leader); | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	list_for_each_entry_rcu(relation, &ptracer_relations, node) { | 
					
						
							|  |  |  | 		if (relation->invalid) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		if (relation->tracee == tracee) { | 
					
						
							|  |  |  | 			parent = relation->tracer; | 
					
						
							| 
									
										
										
										
											2012-02-14 16:48:09 -08:00
										 |  |  | 			found = true; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-11-19 15:21:26 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-14 16:48:09 -08:00
										 |  |  | 	if (found && (parent == NULL || task_is_descendant(parent, tracer))) | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		rc = 1; | 
					
						
							|  |  |  | 	rcu_read_unlock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * yama_ptrace_access_check - validate PTRACE_ATTACH calls | 
					
						
							|  |  |  |  * @child: task that current task is attempting to ptrace | 
					
						
							|  |  |  |  * @mode: ptrace attach mode | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 if following the ptrace is allowed, -ve on error. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | int yama_ptrace_access_check(struct task_struct *child, | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 				    unsigned int mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If standard caps disallows it, so does Yama.  We should
 | 
					
						
							|  |  |  | 	 * only tighten restrictions further. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	rc = cap_ptrace_access_check(child, mode); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		return rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* require ptrace target be a child of ptracer on attach */ | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 	if (mode == PTRACE_MODE_ATTACH) { | 
					
						
							|  |  |  | 		switch (ptrace_scope) { | 
					
						
							|  |  |  | 		case YAMA_SCOPE_DISABLED: | 
					
						
							|  |  |  | 			/* No additional restrictions. */ | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case YAMA_SCOPE_RELATIONAL: | 
					
						
							| 
									
										
										
										
											2012-07-26 05:05:21 -07:00
										 |  |  | 			rcu_read_lock(); | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 			if (!task_is_descendant(current, child) && | 
					
						
							|  |  |  | 			    !ptracer_exception_found(current, child) && | 
					
						
							| 
									
										
										
										
											2012-07-26 05:05:21 -07:00
										 |  |  | 			    !ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE)) | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 				rc = -EPERM; | 
					
						
							| 
									
										
										
										
											2012-07-26 05:05:21 -07:00
										 |  |  | 			rcu_read_unlock(); | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		case YAMA_SCOPE_CAPABILITY: | 
					
						
							| 
									
										
										
										
											2012-07-26 05:05:21 -07:00
										 |  |  | 			rcu_read_lock(); | 
					
						
							|  |  |  | 			if (!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE)) | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 				rc = -EPERM; | 
					
						
							| 
									
										
										
										
											2012-07-26 05:05:21 -07:00
										 |  |  | 			rcu_read_unlock(); | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		case YAMA_SCOPE_NO_ATTACH: | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			rc = -EPERM; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (rc) { | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 		printk_ratelimited(KERN_NOTICE | 
					
						
							|  |  |  | 			"ptrace of pid %d was attempted by: %s (pid %d)\n", | 
					
						
							| 
									
										
										
										
											2012-08-15 11:41:55 -07:00
										 |  |  | 			child->pid, current->comm, current->pid); | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-09 19:01:26 -07:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * yama_ptrace_traceme - validate PTRACE_TRACEME calls | 
					
						
							|  |  |  |  * @parent: task that will become the ptracer of the current task | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 if following the ptrace is allowed, -ve on error. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | int yama_ptrace_traceme(struct task_struct *parent) | 
					
						
							| 
									
										
										
										
											2012-08-09 19:01:26 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If standard caps disallows it, so does Yama.  We should
 | 
					
						
							|  |  |  | 	 * only tighten restrictions further. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	rc = cap_ptrace_traceme(parent); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		return rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Only disallow PTRACE_TRACEME on more aggressive settings. */ | 
					
						
							|  |  |  | 	switch (ptrace_scope) { | 
					
						
							|  |  |  | 	case YAMA_SCOPE_CAPABILITY: | 
					
						
							| 
									
										
										
										
											2013-03-21 02:30:41 -07:00
										 |  |  | 		if (!has_ns_capability(parent, current_user_ns(), CAP_SYS_PTRACE)) | 
					
						
							| 
									
										
										
										
											2012-08-09 19:01:26 -07:00
										 |  |  | 			rc = -EPERM; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case YAMA_SCOPE_NO_ATTACH: | 
					
						
							|  |  |  | 		rc = -EPERM; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rc) { | 
					
						
							|  |  |  | 		printk_ratelimited(KERN_NOTICE | 
					
						
							|  |  |  | 			"ptraceme of pid %d was attempted by: %s (pid %d)\n", | 
					
						
							| 
									
										
										
										
											2012-08-15 11:41:55 -07:00
										 |  |  | 			current->pid, parent->comm, parent->pid); | 
					
						
							| 
									
										
										
										
											2012-08-09 19:01:26 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | #ifndef CONFIG_SECURITY_YAMA_STACKED
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | static struct security_operations yama_ops = { | 
					
						
							|  |  |  | 	.name =			"yama", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.ptrace_access_check =	yama_ptrace_access_check, | 
					
						
							| 
									
										
										
										
											2012-08-09 19:01:26 -07:00
										 |  |  | 	.ptrace_traceme =	yama_ptrace_traceme, | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 	.task_prctl =		yama_task_prctl, | 
					
						
							|  |  |  | 	.task_free =		yama_task_free, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SYSCTL
 | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | static int zero; | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | static int max_scope = YAMA_SCOPE_NO_ATTACH; | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct ctl_path yama_sysctl_path[] = { | 
					
						
							|  |  |  | 	{ .procname = "kernel", }, | 
					
						
							|  |  |  | 	{ .procname = "yama", }, | 
					
						
							|  |  |  | 	{ } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ctl_table yama_sysctl_table[] = { | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.procname       = "ptrace_scope", | 
					
						
							|  |  |  | 		.data           = &ptrace_scope, | 
					
						
							|  |  |  | 		.maxlen         = sizeof(int), | 
					
						
							|  |  |  | 		.mode           = 0644, | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 		.proc_handler   = yama_dointvec_minmax, | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 		.extra1         = &zero, | 
					
						
							| 
									
										
										
										
											2012-04-16 11:56:45 -07:00
										 |  |  | 		.extra2         = &max_scope, | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	{ } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | #endif /* CONFIG_SYSCTL */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static __init int yama_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | #ifndef CONFIG_SECURITY_YAMA_STACKED
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 	if (!security_module_enable(&yama_ops)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	printk(KERN_INFO "Yama: becoming mindful.\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | #ifndef CONFIG_SECURITY_YAMA_STACKED
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 	if (register_security(&yama_ops)) | 
					
						
							|  |  |  | 		panic("Yama: kernel registration failed.\n"); | 
					
						
							| 
									
										
										
										
											2012-09-04 13:32:13 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2011-12-21 12:17:04 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #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); |