Skip to content
  • David Howells's avatar
    CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #3] · 3699c53c
    David Howells authored
    Fix a regression in cap_capable() due to:
    
    	commit 3b11a1de
    	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: default avatarDavid Howells <dhowells@redhat.com>
    Tested-by: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
    Acked-by: default avatarSerge Hallyn <serue@us.ibm.com>
    Signed-off-by: default avatarJames Morris <jmorris@namei.org>
    3699c53c