2010-03-20 16 views
29

Sto scrivendo del software (in C++, per Linux/Mac OSX) che viene eseguito come utente non privilegiato ma necessita di privilegi di root ad un certo punto (per creare un nuovo dispositivo virtuale).Come ottenere i privilegi di root a livello di programmazione?

L'esecuzione di questo programma come root non è un'opzione (principalmente per problemi di sicurezza) e ho bisogno di conoscere l'identità (uid) dell'utente "reale".

C'è un modo per simulare il comportamento del comando "sudo" (chiedere la password dell'utente) per ottenere temporaneamente i privilegi di root ed eseguire la particolare attività? Se sì, quali funzioni dovrei usare?

Grazie mille per il vostro aiuto!

risposta

12

risposta originale

Si potrebbe considerare l'interruttore setuid sul file eseguibile stesso. Wikipedia ha un article su di esso che mostra anche la differenza tra geteuid() e getuid() in modo abbastanza efficace, il primo è per scoprire chi stai "emulando" e il secondo per chi "sei". Il processo sudo, ad esempio, geteuid dovrebbe restituire 0 (root) e ottenere l'id dell'utente, tuttavia i suoi processi secondari vengono eseguiti come root (è possibile verificarlo con sudo id -u -r).

Non penso che ci sia un modo per accedere facilmente a livello di programmazione - dopotutto, applicando il principio del minimo privilegio, perché dovresti? È pratica comune eseguire solo parti del codice limitate con privilegi elevati. Molti demoni, ecc., Sono anche configurati da sistemi moderni per funzionare come propri utenti con la maggior parte dei privilegi di cui hanno bisogno. È solo per operazioni molto specifiche (montaggio ecc.) Che i privilegi di root sono veramente necessari.

2013 aggiornamento

La mia risposta originale si trova (anche se il mio 2013 auto potrebbe fare un lavoro migliore di esso che il mio 2010 uno), ma se si progetta un'applicazione che richiede l'accesso come root, si consiglia di considerare esattamente quale tipo di accesso root è necessario e considerare l'uso di POSIX Capabilities(man page). Questi sono diversi da capability-based security come implementato in L4 et al. Le funzionalità POSIX consentono alla tua applicazione di ottenere un sottoinsieme dei poteri di root. Ad esempio, CAP_SYS_MODULE consentirà di inserire i moduli del kernel, ma non di fornire altri poteri di root. Questo è in uso nelle distribuzioni, ad es. Fedora has a feature to completely remove setuid binaries con accesso root indiscriminato.

Questo è importante perché, come programmatore, il codice è ovviamente perfetto! Ma le librerie da cui dipendi (sospiro, se solo le avessi scritte tu!) Potrebbero avere delle vulnerabilità in esse. Usando le funzionalità, puoi limitare l'uso di questo exploit e salvare te stesso e la tua azienda da controlli attinenti alla sicurezza. Questo rende tutti più felici.

+0

Sì, il mio approccio non era quello giusto. Impostare il sticky bit è il modo migliore per farlo. Grazie mille. – ereOn

+0

Funziona come un fascino! Grazie;) – ereOn

+2

Quando lo fai, devi essere * molto * attento alla sicurezza, perché la tua app è ora un bersaglio per gli exploit di Escalation of Privilege –

0

Si può provare a lanciare il comando per creare il dispositivo virtuale (incluso sudo) attraverso una shell di sfondo. Chiedere la password degli utenti in una finestra di dialogo personale e inserirla nella shell quando sudo lo richiede. Esistono altre soluzioni come l'utilizzo di gksu, ma non è garantito che queste siano disponibili su ogni macchina.

Non si esegue l'intero programma come root, ma solo la piccola parte di esso che necessita di root. Dovresti generare un processo separato per questo e sudo potrebbe esserti utile.

+0

Grazie per la risposta. Sfortunatamente, il programma non ha sempre bisogno dei privilegi di root. A volte potresti voler semplicemente usarlo in un modo che non ha bisogno di fare quel particolare compito. In tal caso, non è necessario chiedere la password. Ho trovato discussioni che parlano di setuid(). Ho intenzione di indagare su questo. – ereOn

+2

È garantito che sudo sia disponibile su ogni macchina? Ho visto installazioni senza di essa. – ziggystar

+0

No, non è configurato su fedora di default. Inoltre, personalmente blocco gli utenti che non fanno parte del gruppo 'wheel' dall'usare sudo o su come estensione del comportamento tradizionale di unix. –

19

Se sono necessari i privilegi di root ogni volta, la cosa migliore è avviare il programma come root e rilasciarli (in un sottoprocesso) con setuid e setgid. Questo è quello che apache fa quando è necessario associare alla porta ristretta 80.

Se guadagnare i privilegi di root è l'eccezione anziché la regola e il programma viene eseguito in modo interattivo, un altro modo è quello di scrivere un add_interface programma ed eseguire

sudo add_interface args 

e lasciare sudo gestire l'autenticazione per te. Invece di sudo, potresti voler usare un frontend grafico come gksu, gksudo, kdesu o kdesudo. Non proverei a implementare personalmente l'inserimento della password sicura; può essere un problema complicato e probabilmente lascerete buchi di sicurezza spalancati e problemi di funzionalità (supportate i lettori di impronte digitali?).

Un'altra alternativa è polkit, precedentemente denominata PolicyKit.

+0

Aaaah, ho appena postato la mia risposta e questo è venuto fuori - questo è un buon consiglio. +1. –

+0

+1 per PolicyKit. –

7

Non è possibile ottenere i privilegi di root, è necessario iniziare con loro e ridurre i privilegi in base alle esigenze. Il solito modo per farlo è installare il programma con il set bit "setuid": esegue il programma con l'id utente effettivo del proprietario del file. Se si esegue ls -l su sudo, vedrai che è installato in questo modo:

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo 

Mentre il programma è in esecuzione con privilegi di root, è possibile chiamare la chiamata di sistema setuid(2) per cambiare l'userid efficace per alcuni non- utente privilegiato. Credo (ma non l'ho provato) che è possibile installare il programma come root con il bit setuid attivo, ridurre immediatamente i privilegi e quindi ripristinare i privilegi in base alle esigenze (è possibile, tuttavia, che una volta abbassati i privilegi non si essere in grado di ripristinarlo).

Una soluzione migliore è quella di rompere il pezzo del programma che deve essere eseguito come root e installarlo con il bit setuid attivato. Ovviamente, dovrai prendere precauzioni ragionevoli per non poter essere invocato al di fuori del tuo programma principale.

+0

Grazie! Sembra che questa sia la strada da percorrere, anzi. Sto cercando di restituire i risultati;) – ereOn

2

Si potrebbe voler dare un'occhiata a queste API:

setuid, seteuid, setgid, setegid, ... 

Sono definiti nell'intestazione <unistd.h> a sistemi Linux (non so molto di MAC, ma si dovrebbe avere un colpo di testa simile lì pure).

Un problema che posso vedere è che il processo deve disporre di privilegi sufficienti per modificare i propri ID utente/gruppo. In caso contrario, le chiamate alle funzioni sopra riportate comporteranno un errore con errorno impostato su EPERM.

Suggerisco di eseguire il programma come utente root, modificare l'ID utente effettivo (utilizzando seteuid) in un utente non privilegiato all'inizio. Quindi, ogni volta che è necessario elevare le autorizzazioni, richiedere una password, quindi utilizzare nuovamente seteuid per ripristinare l'utente root.

4

Normalmente questo viene fatto rendendo il vostro suid-root binario.

Un modo di gestire questo in modo che gli attacchi contro il vostro programma sono difficili è quello di minimizzare il codice che viene eseguito come root in questo modo:

int privileged_server(int argc, char **argv); 
int unprivileged_client(int argc, char **argv, int comlink); 


int main(int argc, char **argv) { 
    int sockets[2]; 
    pid_t child; 
    socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */ 

    child = fork(); 
    if (child < 0) { 
     perror("fork"); 
     exit(3); 
    } elseif (child == 0) { 
     close(sockets[0]); 
     dup2(sockets[1], 0); 
     close(sockets[1]); 
     dup2(0, 1); 
     dup2(0, 2); /* or not */ 
     _exit(privileged_server(argc, argv)); 
    } else { 
     close(sockets[1]); 
     int rtn; 
     setuid(getuid()); 
     rtn = unprivileged_client(argc, argv, sockets[0]); 
     wait(child); 
     return rtn; 
    } 
} 

Ora i colloqui di codice non privilegiati al codice privilegiato attraverso il comlink fd (che è una presa collegata). Il codice privilegiato corrispondente usa stdin/stdout come fine del comlink.

Il codice privilegiato deve verificare la sicurezza di ogni operazione che deve eseguire ma, poiché questo codice è piccolo rispetto al codice non privilegiato, dovrebbe essere ragionevolmente facile.

+0

il tuo secondo blocco penso tu intenda 'child> 0' –

+0

In realtà credo che dovrebbe essere" else if (child == 0) '- il blocco' else' 'aspetta per il bambino, quindi il bambino deve essere nel mezzo 'else if' block. – user470379

+0

user470379 è corretto. Fisso. – Joshua

2

Su OS X, è possibile utilizzare la funzione AuthorizationExecuteWithPrivileges. La pagina Authorization Services Tasks ha alcune discussioni elaborate su questa (e sulle relative) funzioni.

Ecco un po 'di codice C++ per eseguire un programma con privilegi di amministratore:

static bool execute(const std::string &program, const std::vector<std::string> &arguments) 
{ 
    AuthorizationRef ref; 
    if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) { 
     return false; 
    } 

    AuthorizationItem item = { 
     kAuthorizationRightExecute, 0, 0, 0 
    }; 
    AuthorizationRights rights = { 1, &item }; 
    const AuthorizationFlags flags = kAuthorizationFlagDefaults 
            | kAuthorizationFlagInteractionAllowed 
            | kAuthorizationFlagPreAuthorize 
            | kAuthorizationFlagExtendRights; 

    if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) { 
     AuthorizationFree(ref, kAuthorizationFlagDestroyRights); 
     return false; 
    } 

    std::vector<char*> args; 
    for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { 
     args.push_back(it->c_str()); 
    } 
    args.push_back(0); 

    OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0); 

    AuthorizationFree(ref, kAuthorizationFlagDestroyRights); 
    return status == errAuthorizationSuccess; 
} 
+0

Dove chiamiamo questa funzione? E quale sarebbe il parametro degli argomenti? Ho provato a chiamare all'inizio della funzione principale, ma si è bloccato. gli argomenti erano nulli. Il mio problema è la creazione di un file in/var/log. Ma il permesso è necessario. Come posso affrontare questo problema? – gkhanacer

+0

Sarebbe degno menzionare tutte le librerie di cui questo codice ha bisogno, no? – Konstantin

Problemi correlati