web-dev-qa-db-fra.com

Comment obtenir par programme les privilèges root?

J'écris des logiciels (en C++, pour Linux/Mac OSX) qui fonctionnent en tant qu'utilisateur non privilégié mais qui ont besoin de privilèges root à un moment donné (pour créer un nouveau périphérique virtuel).

L'exécution de ce programme en tant que root n'est pas une option (principalement pour des problèmes de sécurité) et j'ai besoin de connaître l'identité (uid) du "vrai" utilisateur.

Existe-t-il un moyen d'imiter le comportement de la commande "Sudo" (demander le mot de passe utilisateur) pour obtenir temporairement les privilèges root et effectuer la tâche particulière? Si oui, quelles fonctions utiliserais-je?

Merci beaucoup pour votre aide !

33
ereOn

Réponse originale

Vous pourriez envisager le commutateur setuid sur l'exécutable lui-même. Wikipedia a un article dessus qui vous montre même la différence entre geteuid() et getuid() assez efficacement, le premier étant pour savoir qui vous "émulez" "et ce dernier pour qui vous" êtes ". Le processus Sudo, par exemple, geteuid devrait retourner 0 (root) et getuid l'identifiant de votre utilisateur, cependant, ses sous-processus fonctionnent vraiment en tant que root (vous pouvez le vérifier avec Sudo id -u -r).

Je ne pense pas qu'il existe un moyen d'obtenir facilement un accès root par programmation - après tout, en appliquant le principe du moindre privilège, pourquoi en auriez-vous besoin? La pratique courante consiste à exécuter uniquement des parties limitées de code avec des privilèges élevés. De nombreux démons, etc. sont également configurés sous des systèmes modernes pour fonctionner 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 (montage, etc.) que les privilèges root sont vraiment nécessaires.

Mise à jour 2013

Ma réponse d'origine est valable (bien que mon auto de 2013 puisse faire mieux que celui de 2010), mais si vous concevez une application qui nécessite un accès root, vous voudrez peut-être considérer exactement quel type d'accès root est nécessaire et considérer le utilisation de POSIX Capabilities(man page) . Celles-ci sont différentes de sécurité basée sur les capacités comme implémenté dans L4 et al. Les capacités POSIX permettent d'accorder à votre application un sous-ensemble des pouvoirs de root. Par exemple CAP_SYS_MODULE vous permettra d'insérer des modules du noyau, mais ne vous donnera aucun autre pouvoir root. Ceci est utilisé dans les distributions, par exemple Fedora a une fonctionnalité pour supprimer complètement les binaires setuid avec un accès root aveugle.

C'est important 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 contenir des vulnérabilités. En utilisant des capacités, vous pouvez limiter l'utilisation de cet exploit et vous épargner, ainsi que votre entreprise, des contrôles liés à la sécurité. Cela rend tout le monde plus heureux.

15
user257111

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 les supprimer (dans un sous-processus) avec setuid et setgid . C'est ce que fait Apache lorsqu'il doit se connecter au port restreint 80.

Si l'obtention des droits root est l'exception au lieu de la règle et que le programme est exécuté de manière interactive, une autre façon consiste à écrire un programme add_interface et à exécuter

Sudo add_interface args

et laissez Sudo gérer l'authentification pour vous. Au lieu de Sudo, vous souhaiterez peut-être utiliser une interface graphique comme gksu, gksudo, kdesu ou kdesudo. Je n'essaierais pas d'implémenter la saisie de mot de passe sécurisée moi-même; cela peut être un problème délicat et vous laisserez probablement des trous de sécurité béants et des problèmes de fonctionnalité (supportez-vous les lecteurs d'empreintes digitales?).

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

20
phihag

Vous ne pouvez pas obtenir de privilèges root, vous devez commencer avec eux et réduire vos privilèges au besoin. La façon habituelle de procéder consiste à installer le programme avec le bit "setuid" défini: cela exécute 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 les privilèges root, vous pouvez appeler l'appel système setuid(2) pour changer votre ID utilisateur effectif en un utilisateur non privilégié. Je crois (mais je n'ai pas essayé) que vous pouvez installer votre programme en tant que root avec le bit setuid, réduire immédiatement les privilèges, puis restaurer les privilèges si nécessaire (il est possible, cependant, qu'une fois que vous réduisez vos privilèges, vous ne le ferez pas pouvoir le restaurer).

Une meilleure solution consiste à décomposer la partie de votre programme qui doit s'exécuter en tant que root et à l'installer avec le bit setuid activé. Vous devrez, bien sûr, prendre des précautions raisonnables pour qu'il ne puisse pas être invoqué en dehors de votre programme maître.

7
kdgregory

Normalement, cela se fait en créant votre suid-root binaire.

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é parle au code privilégié via le comlink fd (qui est un socket connecté). Le code privilégié correspondant utilise stdin/stdout comme fin du comlink.

Le code privilégié doit vérifier la sécurité de chaque opération qu'il doit effectuer, mais comme ce code est petit par rapport au code non privilégié, cela devrait être relativement facile.

4
Joshua

Sous OS X, vous pouvez utiliser la fonction AuthorizationExecuteWithPrivileges . La page sur Tâches des services d'autorisation contient une discussion détaillée de cette fonction (et des fonctions connexes).

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

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;
}
2
Frerich Raabe

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

setuid, seteuid, setgid, setegid, ...

Ils sont définis dans l'en-tête <unistd.h> dans les systèmes Linux (je ne connais pas grand-chose à MAC, mais vous devriez aussi avoir un en-tête similaire).

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

Je vous suggère d'exécuter votre programme en tant qu'utilisateur root, de changer l'ID utilisateur effectif (en utilisant seteuid) en un utilisateur défavorisé au tout début. Ensuite, chaque fois que vous devez augmenter les autorisations, demandez un mot de passe, puis utilisez à nouveau seteuid pour revenir à l'utilisateur root.

2
themoondothshine

Vous pouvez essayer de lancer la commande pour créer le périphérique virtuel (y compris Sudo) via un shell d'arrière-plan. Demandez le mot de passe des utilisateurs dans une boîte de dialogue et transférez-le dans le Shell lorsque Sudo le demande. Il existe d'autres solutions comme l'utilisation de gksu, mais celles-ci ne sont pas garanties d'être disponibles sur chaque machine.

Vous n'exécutez pas l'intégralité de votre programme en tant que root, mais seulement la petite partie de celui-ci qui a besoin de root. Vous devez générer un processus distinct pour cela et Sudo peut vous être utile.

0
Gerco Dries