aboutsummaryrefslogtreecommitdiffstats
path: root/security/commoncap.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-01-06 17:27:01 -0500
committerJames Morris <jmorris@namei.org>2009-01-06 17:38:48 -0500
commit3699c53c485bf0168e6500d0ed18bf931584dd7c (patch)
treeeee63a8ddbdb0665bc6a4a053a2405ca7a5b867f /security/commoncap.c
parent29881c4502ba05f46bc12ae8053d4e08d7e2615c (diff)
CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #3]
Fix a regression in cap_capable() due to: commit 3b11a1decef07c19443d24ae926982bc8ec9f4c0 Author: David Howells <dhowells@redhat.com> Date: Fri Nov 14 10:39:26 2008 +1100 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 passes the set of credentials to be tested down into the commoncap and SELinux code. The security functions called by capable() and has_capability() select the appropriate set of credentials from the process being checked. 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> Tested-by: J. Bruce Fields <bfields@citi.umich.edu> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/commoncap.c')
-rw-r--r--security/commoncap.c29
1 files changed, 14 insertions, 15 deletions
diff --git a/security/commoncap.c b/security/commoncap.c
index 79713545cd63..f0e671dcfff0 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -45,26 +45,22 @@ EXPORT_SYMBOL(cap_netlink_recv);
45/** 45/**
46 * cap_capable - Determine whether a task has a particular effective capability 46 * cap_capable - Determine whether a task has a particular effective capability
47 * @tsk: The task to query 47 * @tsk: The task to query
48 * @cred: The credentials to use
48 * @cap: The capability to check for 49 * @cap: The capability to check for
49 * @audit: Whether to write an audit message or not 50 * @audit: Whether to write an audit message or not
50 * 51 *
51 * Determine whether the nominated task has the specified capability amongst 52 * Determine whether the nominated task has the specified capability amongst
52 * its effective set, returning 0 if it does, -ve if it does not. 53 * its effective set, returning 0 if it does, -ve if it does not.
53 * 54 *
54 * NOTE WELL: cap_capable() cannot be used like the kernel's capable() 55 * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable()
55 * function. That is, it has the reverse semantics: cap_capable() returns 0 56 * and has_capability() functions. That is, it has the reverse semantics:
56 * when a task has a capability, but the kernel's capable() returns 1 for this 57 * cap_has_capability() returns 0 when a task has a capability, but the
57 * case. 58 * kernel's capable() and has_capability() returns 1 for this case.
58 */ 59 */
59int cap_capable(struct task_struct *tsk, int cap, int audit) 60int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap,
61 int audit)
60{ 62{
61 __u32 cap_raised; 63 return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
62
63 /* Derived from include/linux/sched.h:capable. */
64 rcu_read_lock();
65 cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap);
66 rcu_read_unlock();
67 return cap_raised ? 0 : -EPERM;
68} 64}
69 65
70/** 66/**
@@ -160,7 +156,8 @@ static inline int cap_inh_is_capped(void)
160 /* they are so limited unless the current task has the CAP_SETPCAP 156 /* they are so limited unless the current task has the CAP_SETPCAP
161 * capability 157 * capability
162 */ 158 */
163 if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) 159 if (cap_capable(current, current_cred(), CAP_SETPCAP,
160 SECURITY_CAP_AUDIT) == 0)
164 return 0; 161 return 0;
165#endif 162#endif
166 return 1; 163 return 1;
@@ -869,7 +866,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
869 & (new->securebits ^ arg2)) /*[1]*/ 866 & (new->securebits ^ arg2)) /*[1]*/
870 || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ 867 || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
871 || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ 868 || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
872 || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ 869 || (cap_capable(current, current_cred(), CAP_SETPCAP,
870 SECURITY_CAP_AUDIT) != 0) /*[4]*/
873 /* 871 /*
874 * [1] no changing of bits that are locked 872 * [1] no changing of bits that are locked
875 * [2] no unlocking of locks 873 * [2] no unlocking of locks
@@ -950,7 +948,8 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
950{ 948{
951 int cap_sys_admin = 0; 949 int cap_sys_admin = 0;
952 950
953 if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) 951 if (cap_capable(current, current_cred(), CAP_SYS_ADMIN,
952 SECURITY_CAP_NOAUDIT) == 0)
954 cap_sys_admin = 1; 953 cap_sys_admin = 1;
955 return __vm_enough_memory(mm, pages, cap_sys_admin); 954 return __vm_enough_memory(mm, pages, cap_sys_admin);
956} 955}