aboutsummaryrefslogtreecommitdiffstats
path: root/security/commoncap.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-12-31 10:15:42 -0500
committerJames Morris <jmorris@namei.org>2009-01-04 19:17:04 -0500
commit14eaddc967b16017d4a1a24d2be6c28ecbe06ed8 (patch)
treece10216d592f0fa89ae02c4e4e9e9497010e7714 /security/commoncap.c
parent5c8c40be4b5a2944483bfc1a45d6c3fa02551af3 (diff)
CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #2]
Fix a regression in cap_capable() due to: commit 5ff7711e635b32f0a1e558227d030c7e45b4a465 Author: David Howells <dhowells@redhat.com> Date: Wed Dec 31 02:52:28 2008 +0000 CRED: Differentiate objective and effective subjective credentials on a task The problem is that the above patch allows a process to have two sets of credentials, and for the most part uses the subjective credentials when accessing current's creds. There is, however, one exception: cap_capable(), and thus capable(), uses the real/objective credentials of the target task, whether or not it is the current task. Ordinarily this doesn't matter, since usually the two cred pointers in current point to the same set of creds. However, sys_faccessat() makes use of this facility to override the credentials of the calling process to make its test, without affecting the creds as seen from other processes. One of the things sys_faccessat() does is to make an adjustment to the effective capabilities mask, which cap_capable(), as it stands, then ignores. The affected capability check is in generic_permission(): if (!(mask & MAY_EXEC) || execute_ok(inode)) if (capable(CAP_DAC_OVERRIDE)) return 0; This change splits capable() from has_capability() down into the commoncap and SELinux code. The capable() security op now only deals with the current process, and uses the current process's subjective creds. A new security op - task_capable() - is introduced that can check any task's objective creds. strictly the capable() security op is superfluous with the presence of the task_capable() op, however it should be faster to call the capable() op since two fewer arguments need be passed down through the various layers. This can be tested by compiling the following program from the XFS testsuite: /* * t_access_root.c - trivial test program to show permission bug. * * Written by Michael Kerrisk - copyright ownership not pursued. * Sourced from: http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-10/6030.html */ #include <limits.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #define UID 500 #define GID 100 #define PERM 0 #define TESTPATH "/tmp/t_access" static void errExit(char *msg) { perror(msg); exit(EXIT_FAILURE); } /* errExit */ static void accessTest(char *file, int mask, char *mstr) { printf("access(%s, %s) returns %d\n", file, mstr, access(file, mask)); } /* accessTest */ int main(int argc, char *argv[]) { int fd, perm, uid, gid; char *testpath; char cmd[PATH_MAX + 20]; testpath = (argc > 1) ? argv[1] : TESTPATH; perm = (argc > 2) ? strtoul(argv[2], NULL, 8) : PERM; uid = (argc > 3) ? atoi(argv[3]) : UID; gid = (argc > 4) ? atoi(argv[4]) : GID; unlink(testpath); fd = open(testpath, O_RDWR | O_CREAT, 0); if (fd == -1) errExit("open"); if (fchown(fd, uid, gid) == -1) errExit("fchown"); if (fchmod(fd, perm) == -1) errExit("fchmod"); close(fd); snprintf(cmd, sizeof(cmd), "ls -l %s", testpath); system(cmd); if (seteuid(uid) == -1) errExit("seteuid"); accessTest(testpath, 0, "0"); accessTest(testpath, R_OK, "R_OK"); accessTest(testpath, W_OK, "W_OK"); accessTest(testpath, X_OK, "X_OK"); accessTest(testpath, R_OK | W_OK, "R_OK | W_OK"); accessTest(testpath, R_OK | X_OK, "R_OK | X_OK"); accessTest(testpath, W_OK | X_OK, "W_OK | X_OK"); accessTest(testpath, R_OK | W_OK | X_OK, "R_OK | W_OK | X_OK"); exit(EXIT_SUCCESS); } /* main */ This can be run against an Ext3 filesystem as well as against an XFS filesystem. If successful, it will show: [root@andromeda src]# ./t_access_root /tmp/xxx 0 4043 4043 ---------- 1 dhowells dhowells 0 2008-12-31 03:00 /tmp/xxx access(/tmp/xxx, 0) returns 0 access(/tmp/xxx, R_OK) returns 0 access(/tmp/xxx, W_OK) returns 0 access(/tmp/xxx, X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK) returns 0 access(/tmp/xxx, R_OK | X_OK) returns -1 access(/tmp/xxx, W_OK | X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK | X_OK) returns -1 If unsuccessful, it will show: [root@andromeda src]# ./t_access_root /tmp/xxx 0 4043 4043 ---------- 1 dhowells dhowells 0 2008-12-31 02:56 /tmp/xxx access(/tmp/xxx, 0) returns 0 access(/tmp/xxx, R_OK) returns -1 access(/tmp/xxx, W_OK) returns -1 access(/tmp/xxx, X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK) returns -1 access(/tmp/xxx, R_OK | X_OK) returns -1 access(/tmp/xxx, W_OK | X_OK) returns -1 access(/tmp/xxx, R_OK | W_OK | X_OK) returns -1 I've also tested the fix with the SELinux and syscalls LTP testsuites. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/commoncap.c')
-rw-r--r--security/commoncap.c42
1 files changed, 29 insertions, 13 deletions
diff --git a/security/commoncap.c b/security/commoncap.c
index 79713545cd63..7f0b2a68717d 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -43,28 +43,44 @@ int cap_netlink_recv(struct sk_buff *skb, int cap)
43EXPORT_SYMBOL(cap_netlink_recv); 43EXPORT_SYMBOL(cap_netlink_recv);
44 44
45/** 45/**
46 * cap_capable - Determine whether a task has a particular effective capability 46 * cap_capable - Determine whether current has a particular effective capability
47 * @tsk: The task to query
48 * @cap: The capability to check for 47 * @cap: The capability to check for
49 * @audit: Whether to write an audit message or not 48 * @audit: Whether to write an audit message or not
50 * 49 *
51 * Determine whether the nominated task has the specified capability amongst 50 * Determine whether the nominated task has the specified capability amongst
52 * its effective set, returning 0 if it does, -ve if it does not. 51 * its effective set, returning 0 if it does, -ve if it does not. Note that
52 * this uses current's subjective/effective credentials.
53 * 53 *
54 * NOTE WELL: cap_capable() cannot be used like the kernel's capable() 54 * NOTE WELL: cap_capable() cannot be used like the kernel's capable()
55 * function. That is, it has the reverse semantics: cap_capable() returns 0 55 * function. That is, it has the reverse semantics: cap_capable() returns 0
56 * when a task has a capability, but the kernel's capable() returns 1 for this 56 * when a task has a capability, but the kernel's capable() returns 1 for this
57 * case. 57 * case.
58 */ 58 */
59int cap_capable(struct task_struct *tsk, int cap, int audit) 59int cap_capable(int cap, int audit)
60{ 60{
61 __u32 cap_raised; 61 return cap_raised(current_cap(), cap) ? 0 : -EPERM;
62}
62 63
63 /* Derived from include/linux/sched.h:capable. */ 64/**
64 rcu_read_lock(); 65 * cap_has_capability - Determine whether a task has a particular effective capability
65 cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap); 66 * @tsk: The task to query
66 rcu_read_unlock(); 67 * @cred: The credentials to use
67 return cap_raised ? 0 : -EPERM; 68 * @cap: The capability to check for
69 * @audit: Whether to write an audit message or not
70 *
71 * Determine whether the nominated task has the specified capability amongst
72 * its effective set, returning 0 if it does, -ve if it does not. Note that
73 * this uses the task's objective/real credentials.
74 *
75 * NOTE WELL: cap_has_capability() cannot be used like the kernel's
76 * has_capability() function. That is, it has the reverse semantics:
77 * cap_has_capability() returns 0 when a task has a capability, but the
78 * kernel's has_capability() returns 1 for this case.
79 */
80int cap_task_capable(struct task_struct *tsk, const struct cred *cred, int cap,
81 int audit)
82{
83 return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
68} 84}
69 85
70/** 86/**
@@ -160,7 +176,7 @@ static inline int cap_inh_is_capped(void)
160 /* they are so limited unless the current task has the CAP_SETPCAP 176 /* they are so limited unless the current task has the CAP_SETPCAP
161 * capability 177 * capability
162 */ 178 */
163 if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) 179 if (cap_capable(CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
164 return 0; 180 return 0;
165#endif 181#endif
166 return 1; 182 return 1;
@@ -869,7 +885,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
869 & (new->securebits ^ arg2)) /*[1]*/ 885 & (new->securebits ^ arg2)) /*[1]*/
870 || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ 886 || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
871 || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ 887 || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
872 || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ 888 || (cap_capable(CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/
873 /* 889 /*
874 * [1] no changing of bits that are locked 890 * [1] no changing of bits that are locked
875 * [2] no unlocking of locks 891 * [2] no unlocking of locks
@@ -950,7 +966,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
950{ 966{
951 int cap_sys_admin = 0; 967 int cap_sys_admin = 0;
952 968
953 if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) 969 if (cap_capable(CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0)
954 cap_sys_admin = 1; 970 cap_sys_admin = 1;
955 return __vm_enough_memory(mm, pages, cap_sys_admin); 971 return __vm_enough_memory(mm, pages, cap_sys_admin);
956} 972}