2010-07-28 22 views
45

Possiedo un daemon che viene avviato come root (in modo che possa eseguire il binding alle porte basse). Dopo l'inizializzazione mi piacerebbe moltissimo lasciarlo cadere i privilegi di root per ragioni di sicurezza.Eliminazione dei privilegi di root

Qualcuno può indicarmi un noto come corretto codice in C che lo farà?

Ho letto le pagine man, ho esaminato varie implementazioni di questo in diverse applicazioni, e sono tutte diverse, e alcune sono davvero complesse. Questo è un codice relativo alla sicurezza e non voglio reinventare gli stessi errori che stanno facendo gli altri. Quello che sto cercando è una buona pratica, una buona conoscenza, una funzione di libreria portatile che posso usare sapendo che andrà bene. Esiste una cosa del genere?

Per riferimento: sto iniziando come root; Ho bisogno di cambiare per correre con un diverso uid e gid; Ho bisogno di avere i gruppi supplementari impostati correttamente; Non ho bisogno di tornare ai privilegi di root in seguito.

+5

Questo varia un po 'tra gli unix - ce ne sono alcuni in particolare? Se hai bisogno di una soluzione "portatile", sarà complicato, e ti conviene afferrare per es. la funzione permanently_set_uid() di OpenSSH - in uidswap.c file – nos

risposta

43

Per eliminare tutti i privilegi (utente e gruppo), è necessario rilasciare il gruppo prima che l'utente. Dato che userid e groupid contiene gli ID degli utenti e il gruppo che si desidera scendere a, e assumendo che gli ID efficaci sono anche radici, questo viene realizzato chiamando setuid() e setgid():

if (getuid() == 0) { 
    /* process is running as root, drop privileges */ 
    if (setgid(groupid) != 0) 
     fatal("setgid: Unable to drop group privileges: %s", strerror(errno)); 
    if (setuid(userid) != 0) 
     fatal("setuid: Unable to drop user privileges: %S", strerror(errno)); 
} 

Se siete paranoici , puoi provare a riavere i tuoi privilegi di root, cosa che dovrebbe fallire. Se non manca, si salvataggio:

if (setuid(0) != -1) 
    fatal("ERROR: Managed to regain root privileges?"); 

Inoltre, se siete ancora paranoico, si può decidere di seteuid() e setegid() troppo, ma non dovrebbe essere necessario, dal momento che setuid() e setgid() già impostato tutti gli ID se il processo è di proprietà di root.

L'elenco di gruppo supplementare è un problema, perché non esiste alcuna funzione POSIX per impostare gruppi supplementari (c'è getgroups(), ma nessun setgroup()). C'è un'estensione BSD e Linux setgroups() che puoi usare, questo riguarda te.

Si dovrebbe anche chdir("/") o in qualsiasi altra directory, in modo che il processo non rimanga in una directory di proprietà della radice.

Poiché la tua domanda riguarda Unix in generale, questo è l'approccio molto generale. Nota che in Linux questo non è più l'approccio preferito. Nelle attuali versioni di Linux, è necessario impostare CAP_NET_BIND_SERVICE capability sull'eseguibile ed eseguirlo come utente normale. Non è necessario l'accesso alla root.

+1

Anche tu dovresti impostare il gid - e questo potrebbe variare leggermente tra gli unix per quanto riguarda setuid/setgid in realtà mette in disordine anche gli id ​​reali e quelli salvati – nos

+1

Questo ha davvero bisogno di essere una soluzione portatile, quindi niente Linux la capacità è permessa, temo. E ho provato il semplice approccio con setuid() e setgid(); non imposta correttamente i gruppi (e se non si chiama setgroups(), apparentemente si può ancora essere membri di alcuni dei gruppi di root!). –

+0

@nos Grazie. Espanso per coprire i gruppi. Se il processo è di proprietà di root (come indicato dall'OP) o se è setuid-root, quindi setuid() e setgid() hanno già impostato tutti gli ID (reali, effettivi e salvati). Questo è nelle specifiche. Altrimenti, l'implementazione non sarebbe conforme a POSIX. – Juliano

1

Questo era quello che potevo fare best:

#define _GNU_SOURCE // for secure_getenv() 


int drop_root_privileges(void) { // returns 0 on success and -1 on failure 
    gid_t gid; 
    uid_t uid; 

    // no need to "drop" the privileges that you don't have in the first place! 
    if (getuid() != 0) { 
     return 0; 
    } 

    // when your program is invoked with sudo, getuid() will return 0 and you 
    // won't be able to drop your privileges 
    if ((uid = getuid()) == 0) { 
     const char *sudo_uid = secure_getenv("SUDO_UID"); 
     if (sudo_uid == NULL) { 
      printf("environment variable `SUDO_UID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     uid = (uid_t) strtoll(sudo_uid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_UID` to integer"); 
      return -1; 
     } 
    } 

    // again, in case your program is invoked using sudo 
    if ((gid = getgid()) == 0) { 
     const char *sudo_gid = secure_getenv("SUDO_GID"); 
     if (sudo_gid == NULL) { 
      printf("environment variable `SUDO_GID` not found\n"); 
      return -1; 
     } 
     errno = 0; 
     gid = (gid_t) strtoll(sudo_gid, NULL, 10); 
     if (errno != 0) { 
      perror("under-/over-flow in converting `SUDO_GID` to integer"); 
      return -1; 
     } 
    } 

    if (setgid(gid) != 0) { 
     perror("setgid"); 
     return -1; 
    } 
    if (setuid(uid) != 0) { 
     perror("setgid"); 
     return -1;  
    } 

    // change your directory to somewhere else, just in case if you are in a 
    // root-owned one (e.g. /root) 
    if (chdir("/") != 0) { 
     perror("chdir"); 
     return -1; 
    } 

    // check if we successfully dropped the root privileges 
    if (setuid(0) == 0 || seteuid(0) == 0) { 
     printf("could not drop root privileges!\n"); 
     return -1; 
    } 

    return 0; 
} 
Problemi correlati