Manipulation de UID sous Linux par un système de fichiers sous forme de pseudocode

J’ai passé du temps sur un petit code dans Groovy pour que la manipulation de Linux UID soit un peu moins déroutante et parce que les pages de manuel sont en désordre. Le résultat est un TestCase destiné à montrer ce qui se passe sous le capot des appels setuid, seteuid, setfsuid, setreuid et setresuid. Les détails sur les codes d’erreur renvoyés (ou non) n’ont pas été pris en compte.

La question est fondamentalement: ai-je manqué quelque chose?

/** * 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 */ Ssortingng toSsortingng() { 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) } } 

Pour la différence entre UID réel et effectif, lisez par exemple ceci : c’est le concept clé des UID UNIX. Vous pouvez par exemple avoir un processus avec le jeu de bits UID défini, et si l’utilisateur l’exécute normalement, il aura l’UID effectif de root (par exemple), mais le véritable UID sera toujours son UID. Après avoir compris cet exemple, il deviendra plus clair …