Comment obtenir par programme des privilèges root?

J’écris un logiciel (en C ++, pour Linux / Mac OSX) qui s’exécute en tant qu’utilisateur non privilégié mais qui a besoin de privilèges root à un moment donné (pour créer un nouveau périphérique virtuel).

Exécuter ce programme en tant que root n’est pas une option (principalement pour des raisons de sécurité) et j’ai besoin de connaître l’identité (uid) de l’utilisateur “réel”.

Existe-t-il un moyen de reproduire le comportement de la commande “sudo” (demander le mot de passe de l’utilisateur) pour obtenir temporairement les privilèges root et exécuter la tâche particulière? Si oui, quelles fonctions utiliserais-je?

Merci beaucoup pour votre aide !

Réponse originale

Vous pourriez envisager le commutateur setuid sur l’exécutable lui-même. Wikipedia contient un article qui montre même la différence entre geteuid() et getuid() , le premier permettant de savoir à qui vous “imitez” et le dernier pour qui vous “êtes”. Le processus sudo, par exemple, geteuid doit renvoyer 0 (racine) et getuid, l’identifiant de votre utilisateur. Toutefois, ses sous-processus sont réellement exécutés en tant que root (vous pouvez le vérifier avec sudo id -u -r ).

Je ne pense pas qu’il y ait un moyen d’obtenir facilement un access root par programme – après tout, en appliquant le principe du moindre privilège, pourquoi en auriez-vous besoin? La pratique courante consiste à n’exécuter que des parties limitées du code avec des privilèges élevés. Un grand nombre de démons, etc., sont également configurés sous des systèmes modernes pour s’exécuter en tant que leur propre utilisateur avec la plupart des privilèges dont ils ont besoin. Ce n’est que pour des opérations très spécifiques (assembly, etc.) que les privilèges root sont vraiment nécessaires.

Mise à jour 2013

Ma réponse initiale est valable (bien que mon profil de 2013 puisse faire un meilleur travail que celui de 2010), mais si vous concevez une application nécessitant un access root, vous voudrez peut-être déterminer exactement quel type d’access root est nécessaire, ainsi que la utilisation des fonctionnalités POSIX (page de manuel) . Celles-ci diffèrent de la sécurité basée sur les capacités telle que mise en œuvre dans L4 et al. Les fonctionnalités POSIX permettent à votre application de se voir atsortingbuer un sous-ensemble des pouvoirs de root. Par exemple, CAP_SYS_MODULE vous permettra d’insérer des modules du kernel, mais ne vous donnera aucun autre pouvoir racine. Ceci est utilisé dans les dissortingbutions, par exemple Fedora possède une fonctionnalité permettant de supprimer complètement les fichiers binarys setuid avec un access root aveugle.

Cela compte car, en tant que programmeur, votre code est évidemment parfait! Mais les bibliothèques dont vous dépendez (soupir, si seulement vous les aviez écrites!) Pourraient avoir des vulnérabilités. En utilisant les capacités, vous pouvez limiter l’utilisation de cet exploit et vous épargner, ainsi que votre entreprise, des contrôles de sécurité. Cela rend tout le monde plus heureux.

Si vous avez besoin de privilèges root à chaque fois, la meilleure chose à faire est de démarrer votre programme en tant que root et de le supprimer (dans un sous-processus) avec setuid et setgid . C’est ce que fait Apache lorsqu’il doit se connecter au port 80 restreint.

Si l’obtention des droits root est l’exception à la place de la règle et que le programme est exécuté de manière interactive, vous pouvez également écrire un programme add_interface et l’exécuter.

 sudo add_interface args 

et laissez sudo gérer l’authentification pour vous. Au lieu de sudo, vous pouvez utiliser une interface graphique telle que gksu, gksudo, kdesu ou kdesudo. Je n’essaierais pas d’implémenter la saisie d’un mot de passe sécurisé moi-même cela peut être un problème épineux et vous allez probablement laisser des failles de sécurité et des problèmes de fonctionnalité (Soutenez-vous les lecteurs d’empreintes digitales?).

Une autre alternative est polkit , précédemment appelé PolicyKit.

Vous ne pouvez pas obtenir les privilèges root, vous devez commencer avec eux et réduire vos privilèges au besoin. La procédure habituelle consiste à installer le programme avec le bit “setuid”: cela lance le programme avec l’ID utilisateur effectif du propriétaire du fichier. Si vous exécutez ls -l sur sudo , vous verrez qu’il est installé de cette façon:

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

Pendant que votre programme s’exécute avec des privilèges root, vous pouvez appeler l’appel système setuid(2) pour changer votre ID utilisateur effectif en utilisateur non privilégié. Je crois (mais je n’ai pas essayé cela) que vous pouvez installer votre programme en tant qu’utilisateur root avec le bit setuid, réduire immédiatement les privilèges, puis restaurer les privilèges nécessaires (il est toutefois possible, une fois que vous aurez abaissé vos privilèges pouvoir le restaurer).

Une meilleure solution consiste à décomposer la partie de votre programme devant s’exécuter en tant que root et à l’installer avec le bit setuid activé. Bien sûr, vous devrez prendre des précautions raisonnables pour que cela ne puisse pas être invoqué en dehors de votre programme principal.

Normalement, ceci est fait en rendant votre binary suid-root.

Une façon de gérer cela afin que les attaques contre votre programme soient difficiles est de minimiser le code qui s’exécute en tant que root, comme ceci:

 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; } } 

Maintenant, le code non privilégié communique avec le code privilégié via le lien fd comlink (qui est une socket connectée). Le code privilégié correspondant utilise stdin / stdout à la fin du lien com.

Le code privilégié doit vérifier la sécurité de chaque opération, mais comme ce code est petit par rapport au code sans privilège, cela devrait être relativement facile.

Vous voudrez peut-être jeter un coup d’œil à ces API:

 setuid, seteuid, setgid, setegid, ... 

Ils sont définis dans l’en-tête dans les systèmes Linux (je ne connais pas grand chose au sujet du MAC, mais vous devriez aussi avoir un en-tête similaire).

Un problème que je peux voir est que le processus doit avoir suffisamment de privilèges pour changer ses identifiants d’utilisateur / de groupe. Sinon, les appels aux fonctions ci-dessus entraîneront une erreur avec errorno réglé sur EPERM .

Je suggérerais que vous exécutiez votre programme en tant qu’utilisateur root que vous seteuid ID utilisateur effectif (en utilisant seteuid ) en un utilisateur défavorisé au tout début. Ensuite, chaque fois que vous devez élever des permissions, seteuid un mot de passe, puis utilisez à nouveau seteuid pour revenir à l’utilisateur root .

Sous OS X, vous pouvez utiliser la fonction AuthorizationExecuteWithPrivileges . La page sur les tâches des services d’autorisation contient des informations détaillées sur cette fonction (et les fonctions connexes).

Voici un peu de code C ++ pour exécuter un programme avec des privilèges d’administrateur:

 static bool execute(const std::ssortingng &program, const std::vector &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 args; for (std::vector::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; } 

Vous pouvez essayer de lancer la commande pour créer le périphérique virtuel (y compris sudo) via un shell en arrière-plan. Demandez le mot de passe de l’utilisateur dans une boîte de dialog de votre choix et dirigez-le vers le shell lorsque sudo le demande. Il existe d’autres solutions telles que l’utilisation de gksu, mais il n’est pas garanti que celles-ci soient disponibles sur toutes les machines.

Vous n’exécutez pas tout votre programme en tant que root, mais seulement la petite partie de celui-ci qui en a besoin. Vous devriez créer un processus distinct pour cela et sudo peut vous aider.