开发者

Linux effective, real, saved, filesystem UID manipulation in pseudocode

I have spent some time on a little code in Groovy to make Linux UID manipulation somewhat less confusing to myself and because the man pages are messy. The result is a TestCase which is meant to show what happens under the hood of the setuid, seteuid, setfsuid, setreuid and setresuid calls. Details about returned error codes (or not) were not considered.

The question basically is: did I miss something?

/**
 * This is some Groovy code to explain Linux privilege handling
 * The manipulable data structure is "PermVector", and it is manipulated through a
 * TestCase.
 */

class PermissionTestCase extends GroovyTestCase {

  class PermVector {

    int ruid  // real UID; affects the permissions for sending signals 
    int euid  // effective UID; affects file creation and access
    int suid  // saved UID
    int fsuid // filesystem UID; access control to the file system for NFS in Linux

    /**
     * The permission vector of a process that is created from a parent process
     * having the given parent_euid, with its executable file having the given
     * exe_suid_bit and being owned by the given exe_uid  
     */

    PermVector(Map params) {
        ruid  = params.parent_euid // is this right??
        euid  = params.parent_euid
        suid  = params.exe_suid_bit ? params.exe_uid : params.parent_euid
        fsuid = params.parent_euid // is this right??
    }

    /**
     * What does it mean for a process to be "privileged"?
     */

    def isPrivileged() {
        return euid == 0
    }

    /**
     * Helper
     */

    private def euid_part(int new_euid) {
        if (isPrivileged() || 
            (new_euid == ruid || new_euid == euid || new_euid == suid)) {
            return new_euid
        }
        else {
            throw new IllegalStateException("Nixed euid ${euid} to ${new_euid}")
        }
    }

    /**
     * Helper
     */

    private def ruid_part(int new_ruid) {
        if (isPrivileged() || (new_ruid == ruid || new_ruid == euid)) {
            return new_ruid
        }
        else {
            throw new IllegalStateException("Nixed ruid ${ruid} to ${new_ruid}")
        }
    }

    /**
     * Helper
     */

    private def suid_part(int new_suid) {
        if (isPrivileged() || 
            (new_suid == ruid || new_suid == euid || new_suid == suid)) {
            return new_suid
        }
        else {
            throw new IllegalStateException("Nixed suid ${suid} to ${new_suid}")
        }
    }

    /**
     * Helper
     */

    private def ruid_part_for_setresuid(int new_ruid) {
        if (isPrivileged() || 
            (new_ruid == ruid || new_ruid == euid || new_ruid == suid)) {
            return new_ruid
        }
        else {
            throw new IllegalStateException("Nixed ruid ${ruid} to ${new_ruid}")
        }
    }

    /**
     * Behaviour of SETREUID(2)
     */

    def setreuid(int new_ruid, int new_euid) {
        int next_euid = euid_part(new_euid)
        int next_ruid = ruid_part(new_ruid)
        if (next_euid != euid || next_ruid != ruid) {
            suid = next_euid
        }
        euid = next_euid
        ruid = next_ruid
        fsuid = next_euid
    }

    /**
     * Behaviour of SETEUID(2)
     */

    def seteuid(int new_euid) {
        if (isPrivileged()) {
            euid = new_euid
            fsuid = new_euid
        }
        else {
            if (new_euid == ruid || new_euid == euid || new_euid == suid) {
                euid = new_euid
                fsuid = new_euid
                // glibc 2.1  and  later do not change the suid!
            }
            else {
                throw new IllegalStateException("Nixed euid ${euid} to ${new_euid}")
            }
        }
    }

    /**
     * Behaviour of SETUID(2)
     */

    def setuid(int new_euid) {
        if (isPrivileged()) {
            euid = new_euid
            ruid = new_euid
            suid = new_euid
            fsuid = new_euid
        }
        else {
            if (new_euid == ruid || new_euid == suid) {
                euid = new_euid
                fsuid = new_euid
            }
            else {
                throw new IllegalStateException("Nixed euid ${euid} to ${new_euid}")
            }
        }
    }

    /**
     * Behaviour of SETFSUID(2)
     */

    def setfsuid(int new_fsuid) {
        if (isPrivileged()) {
            fsuid = new_fsuid
        }
        else {
            if (new_fsuid == ruid || new_fsuid == euid || 
                new_fsuid == suid || new_fsuid == fsuid) {
                fsuid = new_fsuid
            }
            else {
                throw new IllegalStateException("Nixed fsuid ${fsuid} to ${new_fsuid}")
            }
        }
    }

    /**
     * Behaviour of SETRESUID(2)
     */

    def setresuid(int new_ruid, int new_euid, int new_suid) {
        int next_ruid = new_ruid==-1 ? ruid : ruid_part_for_setresuid(new_ruid)
        int next_euid = new_euid==-1 ? euid : euid_part(new_euid)
        int next_suid = new_suid==-1 ? suid : suid_part(new_suid)
        ruid = next_ruid
        euid = next_euid
        suid = next_suid
        fsuid = next_euid
    }

    /**
     * Printing
     */

    String toString() {
        return "[ruid:${ruid}, euid:${euid}, suid:${suid}, fsuid:${fsuid}]"
    }
}


/**
 * Use case: drop privileges for good
 */

void testDropPrivilegesFromRoot() {
    PermVector pv = 
         new PermVector(parent_euid : 0, exe_suid_bit : false, exe_uid : 500)        
    System.out << "Dropping privileges from ${pv} using setuid(1000) .... "
    pv.setuid(1000)
    System.out << "now at ${pv}\n"
    assertEquals(1000, pv.ruid)
    assertEquals(1000, pv.euid)
    assertEquals(1000, pv.suid)
    assertEquals(1000, pv.fsuid)

}

/**
 * Use case: elevate privileges, do some work, then drop privileges again
 */

void testElevatePrivilegesTemporarily() {
    PermVector pv = 
       new PermVector(parent_euid : 500, exe_suid_bit : true, exe_uid : 0)
    System.out << "Elevating privileges from ${pv} using setreuid(500,0) .... "
    pv.setreuid(500,0)        
    System.out << "now at ${pv}, doing privileged work .... "
    assertEquals(500, pv.ruid)
    assertEquals(0, pv.euid)
    assertEquals(0, pv.suid)
    assertEquals(0, pv.fsuid)
    System.out << "dropping back .... "        
    pv.setuid(500)
    System.out << "now at ${pv}\n"
    assertEquals(500, pv.ruid)
    assertEquals(500, pv.euid)
    assertEquals(500, pv.suid)
    assertEquals(500, pv.fsuid)

}

/**
 * Use case: drop privileges, do some work, then elevate privileges again
 */

void testDropPrivilegesTemporarily() {
    PermVector pv = 
       new PermVector(parent_euid : 0, exe_suid_bit : false, exe_uid : 500)
    System.out << "Dropping privileges from ${pv} using setreuid(0,500) .... "
    pv.setreuid(0,开发者_如何转开发 500)
    System.out << "now at ${pv} ... doing unprivileged work safely .... "
    assertEquals(0, pv.ruid)
    assertEquals(500, pv.euid)
    assertEquals(500, pv.suid)
    assertEquals(500, pv.fsuid)
    System.out << "elevating .... "
    pv.setuid(0)
    System.out << "back at ${pv}\n"
    assertEquals(0, pv.ruid)
    assertEquals(0, pv.euid)
    assertEquals(500, pv.suid)
    assertEquals(0, pv.fsuid)

  }
}


For the difference between real and effective UID, read for example this - this is the key concept of UNIX UIDs. You can e.g. have a process with set UID bit set, and if normal user runs it, he will have the effective UID of root (for example), but the real UID will still be his UID. After you understand this example it will become more clear...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜