aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Smalley <sds@tycho.nsa.gov>2008-05-19 08:32:49 -0400
committerJames Morris <jmorris@namei.org>2008-07-14 01:01:47 -0400
commit006ebb40d3d65338bd74abb03b945f8d60e362bd (patch)
treec548c678b54b307e1fb9acf94676fb7bfd849501
parentfeb2a5b82d87fbdc01c00b7e9413e4b5f4c1f0c1 (diff)
Security: split proc ptrace checking into read vs. attach
Enable security modules to distinguish reading of process state via proc from full ptrace access by renaming ptrace_may_attach to ptrace_may_access and adding a mode argument indicating whether only read access or full attach access is requested. This allows security modules to permit access to reading process state without granting full ptrace access. The base DAC/capability checking remains unchanged. Read access to /proc/pid/mem continues to apply a full ptrace attach check since check_mem_permission() already requires the current task to already be ptracing the target. The other ptrace checks within proc for elements like environ, maps, and fds are changed to pass the read mode instead of attach. In the SELinux case, we model such reading of process state as a reading of a proc file labeled with the target process' label. This enables SELinux policy to permit such reading of process state without permitting control or manipulation of the target process, as there are a number of cases where programs probe for such information via proc but do not need to be able to control the target (e.g. procps, lsof, PolicyKit, ConsoleKit). At present we have to choose between allowing full ptrace in policy (more permissive than required/desired) or breaking functionality (or in some cases just silencing the denials via dontaudit rules but this can hide genuine attacks). This version of the patch incorporates comments from Casey Schaufler (change/replace existing ptrace_may_attach interface, pass access mode), and Chris Wright (provide greater consistency in the checking). Note that like their predecessors __ptrace_may_attach and ptrace_may_attach, the __ptrace_may_access and ptrace_may_access interfaces use different return value conventions from each other (0 or -errno vs. 1 or 0). I retained this difference to avoid any changes to the caller logic but made the difference clearer by changing the latter interface to return a bool rather than an int and by adding a comment about it to ptrace.h for any future callers. Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Acked-by: Chris Wright <chrisw@sous-sol.org> Signed-off-by: James Morris <jmorris@namei.org>
-rw-r--r--fs/proc/base.c9
-rw-r--r--fs/proc/task_mmu.c6
-rw-r--r--fs/proc/task_nommu.c2
-rw-r--r--include/linux/ptrace.h8
-rw-r--r--include/linux/security.h16
-rw-r--r--kernel/ptrace.c15
-rw-r--r--security/commoncap.c3
-rw-r--r--security/dummy.c3
-rw-r--r--security/security.c5
-rw-r--r--security/selinux/hooks.c13
-rw-r--r--security/smack/smack_lsm.c5
11 files changed, 55 insertions, 30 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 3b455371e7ff..58c3e6a8e15e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -233,7 +233,7 @@ static int check_mem_permission(struct task_struct *task)
233 */ 233 */
234 if (task->parent == current && (task->ptrace & PT_PTRACED) && 234 if (task->parent == current && (task->ptrace & PT_PTRACED) &&
235 task_is_stopped_or_traced(task) && 235 task_is_stopped_or_traced(task) &&
236 ptrace_may_attach(task)) 236 ptrace_may_access(task, PTRACE_MODE_ATTACH))
237 return 0; 237 return 0;
238 238
239 /* 239 /*
@@ -251,7 +251,8 @@ struct mm_struct *mm_for_maps(struct task_struct *task)
251 task_lock(task); 251 task_lock(task);
252 if (task->mm != mm) 252 if (task->mm != mm)
253 goto out; 253 goto out;
254 if (task->mm != current->mm && __ptrace_may_attach(task) < 0) 254 if (task->mm != current->mm &&
255 __ptrace_may_access(task, PTRACE_MODE_READ) < 0)
255 goto out; 256 goto out;
256 task_unlock(task); 257 task_unlock(task);
257 return mm; 258 return mm;
@@ -518,7 +519,7 @@ static int proc_fd_access_allowed(struct inode *inode)
518 */ 519 */
519 task = get_proc_task(inode); 520 task = get_proc_task(inode);
520 if (task) { 521 if (task) {
521 allowed = ptrace_may_attach(task); 522 allowed = ptrace_may_access(task, PTRACE_MODE_READ);
522 put_task_struct(task); 523 put_task_struct(task);
523 } 524 }
524 return allowed; 525 return allowed;
@@ -904,7 +905,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
904 if (!task) 905 if (!task)
905 goto out_no_task; 906 goto out_no_task;
906 907
907 if (!ptrace_may_attach(task)) 908 if (!ptrace_may_access(task, PTRACE_MODE_READ))
908 goto out; 909 goto out;
909 910
910 ret = -ENOMEM; 911 ret = -ENOMEM;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index c492449f3b45..164bd9f9ede3 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -210,7 +210,7 @@ static int show_map(struct seq_file *m, void *v)
210 dev_t dev = 0; 210 dev_t dev = 0;
211 int len; 211 int len;
212 212
213 if (maps_protect && !ptrace_may_attach(task)) 213 if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
214 return -EACCES; 214 return -EACCES;
215 215
216 if (file) { 216 if (file) {
@@ -646,7 +646,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
646 goto out; 646 goto out;
647 647
648 ret = -EACCES; 648 ret = -EACCES;
649 if (!ptrace_may_attach(task)) 649 if (!ptrace_may_access(task, PTRACE_MODE_READ))
650 goto out_task; 650 goto out_task;
651 651
652 ret = -EINVAL; 652 ret = -EINVAL;
@@ -747,7 +747,7 @@ static int show_numa_map_checked(struct seq_file *m, void *v)
747 struct proc_maps_private *priv = m->private; 747 struct proc_maps_private *priv = m->private;
748 struct task_struct *task = priv->task; 748 struct task_struct *task = priv->task;
749 749
750 if (maps_protect && !ptrace_may_attach(task)) 750 if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
751 return -EACCES; 751 return -EACCES;
752 752
753 return show_numa_map(m, v); 753 return show_numa_map(m, v);
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 4b4f9cc2f186..5d84e7121df8 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -113,7 +113,7 @@ static int show_map(struct seq_file *m, void *_vml)
113 struct proc_maps_private *priv = m->private; 113 struct proc_maps_private *priv = m->private;
114 struct task_struct *task = priv->task; 114 struct task_struct *task = priv->task;
115 115
116 if (maps_protect && !ptrace_may_attach(task)) 116 if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
117 return -EACCES; 117 return -EACCES;
118 118
119 return nommu_vma_show(m, vml->vma); 119 return nommu_vma_show(m, vml->vma);
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index f98501ba557e..c6f5f9dd0cee 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -95,8 +95,12 @@ extern void __ptrace_link(struct task_struct *child,
95 struct task_struct *new_parent); 95 struct task_struct *new_parent);
96extern void __ptrace_unlink(struct task_struct *child); 96extern void __ptrace_unlink(struct task_struct *child);
97extern void ptrace_untrace(struct task_struct *child); 97extern void ptrace_untrace(struct task_struct *child);
98extern int ptrace_may_attach(struct task_struct *task); 98#define PTRACE_MODE_READ 1
99extern int __ptrace_may_attach(struct task_struct *task); 99#define PTRACE_MODE_ATTACH 2
100/* Returns 0 on success, -errno on denial. */
101extern int __ptrace_may_access(struct task_struct *task, unsigned int mode);
102/* Returns true on success, false on denial. */
103extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
100 104
101static inline int ptrace_reparented(struct task_struct *child) 105static inline int ptrace_reparented(struct task_struct *child)
102{ 106{
diff --git a/include/linux/security.h b/include/linux/security.h
index 50737c70e78e..62bd80cb7f87 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -46,7 +46,8 @@ struct audit_krule;
46 */ 46 */
47extern int cap_capable(struct task_struct *tsk, int cap); 47extern int cap_capable(struct task_struct *tsk, int cap);
48extern int cap_settime(struct timespec *ts, struct timezone *tz); 48extern int cap_settime(struct timespec *ts, struct timezone *tz);
49extern int cap_ptrace(struct task_struct *parent, struct task_struct *child); 49extern int cap_ptrace(struct task_struct *parent, struct task_struct *child,
50 unsigned int mode);
50extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); 51extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
51extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); 52extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
52extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); 53extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
@@ -1170,6 +1171,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
1170 * attributes would be changed by the execve. 1171 * attributes would be changed by the execve.
1171 * @parent contains the task_struct structure for parent process. 1172 * @parent contains the task_struct structure for parent process.
1172 * @child contains the task_struct structure for child process. 1173 * @child contains the task_struct structure for child process.
1174 * @mode contains the PTRACE_MODE flags indicating the form of access.
1173 * Return 0 if permission is granted. 1175 * Return 0 if permission is granted.
1174 * @capget: 1176 * @capget:
1175 * Get the @effective, @inheritable, and @permitted capability sets for 1177 * Get the @effective, @inheritable, and @permitted capability sets for
@@ -1295,7 +1297,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
1295struct security_operations { 1297struct security_operations {
1296 char name[SECURITY_NAME_MAX + 1]; 1298 char name[SECURITY_NAME_MAX + 1];
1297 1299
1298 int (*ptrace) (struct task_struct *parent, struct task_struct *child); 1300 int (*ptrace) (struct task_struct *parent, struct task_struct *child,
1301 unsigned int mode);
1299 int (*capget) (struct task_struct *target, 1302 int (*capget) (struct task_struct *target,
1300 kernel_cap_t *effective, 1303 kernel_cap_t *effective,
1301 kernel_cap_t *inheritable, kernel_cap_t *permitted); 1304 kernel_cap_t *inheritable, kernel_cap_t *permitted);
@@ -1573,7 +1576,8 @@ extern struct dentry *securityfs_create_dir(const char *name, struct dentry *par
1573extern void securityfs_remove(struct dentry *dentry); 1576extern void securityfs_remove(struct dentry *dentry);
1574 1577
1575/* Security operations */ 1578/* Security operations */
1576int security_ptrace(struct task_struct *parent, struct task_struct *child); 1579int security_ptrace(struct task_struct *parent, struct task_struct *child,
1580 unsigned int mode);
1577int security_capget(struct task_struct *target, 1581int security_capget(struct task_struct *target,
1578 kernel_cap_t *effective, 1582 kernel_cap_t *effective,
1579 kernel_cap_t *inheritable, 1583 kernel_cap_t *inheritable,
@@ -1755,9 +1759,11 @@ static inline int security_init(void)
1755 return 0; 1759 return 0;
1756} 1760}
1757 1761
1758static inline int security_ptrace(struct task_struct *parent, struct task_struct *child) 1762static inline int security_ptrace(struct task_struct *parent,
1763 struct task_struct *child,
1764 unsigned int mode)
1759{ 1765{
1760 return cap_ptrace(parent, child); 1766 return cap_ptrace(parent, child, mode);
1761} 1767}
1762 1768
1763static inline int security_capget(struct task_struct *target, 1769static inline int security_capget(struct task_struct *target,
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 6c19e94fd0a5..e337390fce01 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -121,7 +121,7 @@ int ptrace_check_attach(struct task_struct *child, int kill)
121 return ret; 121 return ret;
122} 122}
123 123
124int __ptrace_may_attach(struct task_struct *task) 124int __ptrace_may_access(struct task_struct *task, unsigned int mode)
125{ 125{
126 /* May we inspect the given task? 126 /* May we inspect the given task?
127 * This check is used both for attaching with ptrace 127 * This check is used both for attaching with ptrace
@@ -148,16 +148,16 @@ int __ptrace_may_attach(struct task_struct *task)
148 if (!dumpable && !capable(CAP_SYS_PTRACE)) 148 if (!dumpable && !capable(CAP_SYS_PTRACE))
149 return -EPERM; 149 return -EPERM;
150 150
151 return security_ptrace(current, task); 151 return security_ptrace(current, task, mode);
152} 152}
153 153
154int ptrace_may_attach(struct task_struct *task) 154bool ptrace_may_access(struct task_struct *task, unsigned int mode)
155{ 155{
156 int err; 156 int err;
157 task_lock(task); 157 task_lock(task);
158 err = __ptrace_may_attach(task); 158 err = __ptrace_may_access(task, mode);
159 task_unlock(task); 159 task_unlock(task);
160 return !err; 160 return (!err ? true : false);
161} 161}
162 162
163int ptrace_attach(struct task_struct *task) 163int ptrace_attach(struct task_struct *task)
@@ -195,7 +195,7 @@ repeat:
195 /* the same process cannot be attached many times */ 195 /* the same process cannot be attached many times */
196 if (task->ptrace & PT_PTRACED) 196 if (task->ptrace & PT_PTRACED)
197 goto bad; 197 goto bad;
198 retval = __ptrace_may_attach(task); 198 retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
199 if (retval) 199 if (retval)
200 goto bad; 200 goto bad;
201 201
@@ -494,7 +494,8 @@ int ptrace_traceme(void)
494 */ 494 */
495 task_lock(current); 495 task_lock(current);
496 if (!(current->ptrace & PT_PTRACED)) { 496 if (!(current->ptrace & PT_PTRACED)) {
497 ret = security_ptrace(current->parent, current); 497 ret = security_ptrace(current->parent, current,
498 PTRACE_MODE_ATTACH);
498 /* 499 /*
499 * Set the ptrace bit in the process ptrace flags. 500 * Set the ptrace bit in the process ptrace flags.
500 */ 501 */
diff --git a/security/commoncap.c b/security/commoncap.c
index 33d343308413..0b6537a3672d 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -63,7 +63,8 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
63 return 0; 63 return 0;
64} 64}
65 65
66int cap_ptrace (struct task_struct *parent, struct task_struct *child) 66int cap_ptrace (struct task_struct *parent, struct task_struct *child,
67 unsigned int mode)
67{ 68{
68 /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ 69 /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
69 if (!cap_issubset(child->cap_permitted, parent->cap_permitted) && 70 if (!cap_issubset(child->cap_permitted, parent->cap_permitted) &&
diff --git a/security/dummy.c b/security/dummy.c
index b8916883b77f..1db712d99dc7 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -30,7 +30,8 @@
30#include <linux/prctl.h> 30#include <linux/prctl.h>
31#include <linux/securebits.h> 31#include <linux/securebits.h>
32 32
33static int dummy_ptrace (struct task_struct *parent, struct task_struct *child) 33static int dummy_ptrace (struct task_struct *parent, struct task_struct *child,
34 unsigned int mode)
34{ 35{
35 return 0; 36 return 0;
36} 37}
diff --git a/security/security.c b/security/security.c
index 59838a99b80e..c4507ce2a5a0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -161,9 +161,10 @@ int mod_reg_security(const char *name, struct security_operations *ops)
161 161
162/* Security operations */ 162/* Security operations */
163 163
164int security_ptrace(struct task_struct *parent, struct task_struct *child) 164int security_ptrace(struct task_struct *parent, struct task_struct *child,
165 unsigned int mode)
165{ 166{
166 return security_ops->ptrace(parent, child); 167 return security_ops->ptrace(parent, child, mode);
167} 168}
168 169
169int security_capget(struct task_struct *target, 170int security_capget(struct task_struct *target,
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index eca70f42e678..4be156334b22 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1686,14 +1686,23 @@ static inline u32 file_to_av(struct file *file)
1686 1686
1687/* Hook functions begin here. */ 1687/* Hook functions begin here. */
1688 1688
1689static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) 1689static int selinux_ptrace(struct task_struct *parent,
1690 struct task_struct *child,
1691 unsigned int mode)
1690{ 1692{
1691 int rc; 1693 int rc;
1692 1694
1693 rc = secondary_ops->ptrace(parent, child); 1695 rc = secondary_ops->ptrace(parent, child, mode);
1694 if (rc) 1696 if (rc)
1695 return rc; 1697 return rc;
1696 1698
1699 if (mode == PTRACE_MODE_READ) {
1700 struct task_security_struct *tsec = parent->security;
1701 struct task_security_struct *csec = child->security;
1702 return avc_has_perm(tsec->sid, csec->sid,
1703 SECCLASS_FILE, FILE__READ, NULL);
1704 }
1705
1697 return task_has_perm(parent, child, PROCESS__PTRACE); 1706 return task_has_perm(parent, child, PROCESS__PTRACE);
1698} 1707}
1699 1708
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 4a09293efa00..3c7150b3493d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -95,11 +95,12 @@ struct inode_smack *new_inode_smack(char *smack)
95 * 95 *
96 * Do the capability checks, and require read and write. 96 * Do the capability checks, and require read and write.
97 */ 97 */
98static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp) 98static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp,
99 unsigned int mode)
99{ 100{
100 int rc; 101 int rc;
101 102
102 rc = cap_ptrace(ptp, ctp); 103 rc = cap_ptrace(ptp, ctp, mode);
103 if (rc != 0) 104 if (rc != 0)
104 return rc; 105 return rc;
105 106