Quel est le moyen le plus sûr d’écrire par programme dans un fichier avec les privilèges root?

35

Une énorme application doit, à un moment donné, effectuer un petit nombre d'écritures dans un fichier nécessitant des autorisations root. Ce n'est pas vraiment un fichier, mais une interface matérielle exposée à Linux sous forme de fichier.

Pour éviter de donner des privilèges root à l'ensemble de l'application, j'ai écrit un script bash qui effectue les tâches critiques. Par exemple, le script suivant activera le port 17 de l'interface matérielle en sortie:

echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction

Cependant, comme il suidest désactivé pour les scripts bash sur mon système, je me demande quel est le meilleur moyen d'y parvenir.

  1. Utilisez les solutions de contournement présentées ici

  2. Appelez le script sudodepuis l'application principale et modifiez la liste des sudoers en conséquence, pour éviter de demander un mot de passe lors de l'appel du script. Je suis un peu mal à l'aise de donner des privilèges à Sudo echo.

  3. Il suffit d’écrire un programme C, avec fprintf, et de le définir sur suid root. Hardcode les chaînes et les noms de fichiers et assurez-vous que seul root peut le modifier. Ou lisez les chaînes d'un fichier texte, tout en vous assurant que personne ne peut éditer le fichier.

  4. Une autre solution qui ne m'est pas venue à l'esprit et qui est plus sûre ou plus simple que celles présentées ci-dessus?

vsz
la source
10
Pourquoi ne démarrez-vous pas simplement votre programme avec les privilèges root, ouvrez-vous le fichier et supprimez-vous les privilèges? C'est ainsi que tous les serveurs Web ou autres le font pour les sockets. Effectivement, vous ne courez pas en tant que root, aucune aide n'est nécessaire.
Damon

Réponses:

33

Vous n'avez pas besoin de donner sudoaccès à echo. En fait, cela ne sert à rien car, par exemple, avec sudo echo foo > barla redirection, il s’agit de l’utilisateur initial et non de la racine.

Appelez le petit script avec sudo, en autorisant l' NOPASSWD:accès à SEULEMENT ce script (et à tout autre script similaire) par les utilisateurs qui en ont besoin.

C'est toujours le meilleur moyen / le plus sûr sudo. Isolez le petit nombre de commandes nécessitant des privilèges root dans leurs propres scripts distincts et autorisez l'utilisateur non approuvé ou partiellement approuvé à exécuter ce script uniquement en tant qu'utilisateur root.

Le ou les sudoscripts réductibles ne doivent pas accepter les arguments (ou les entrées) de l’utilisateur (c’est-à-dire que les autres programmes qu’il appelle doivent avoir des options et des arguments codés en dur), ou bien valider avec soin les arguments / entrées qu’il doit suivre. accepter de l'utilisateur.

Soyez paranoïaque dans la validation - plutôt que de rechercher les «mauvaises choses connues» à exclure, n'autorisez que les «bonnes choses connues» et annulez toute erreur, toute erreur ou tout ce qui est suspect.

La validation doit avoir lieu le plus tôt possible dans le script (de préférence avant toute autre action en tant que root).


J'aurais vraiment dû mentionner cela lorsque j'ai écrit cette réponse pour la première fois, mais si votre script est un script shell, il DOIT citer correctement toutes les variables. Veillez particulièrement à citer les variables contenant des entrées fournies par l’utilisateur de quelque manière que ce soit, mais ne supposez pas que certaines variables sont sûres, QUOTE THEM ALL .

Cela inclut des variables d'environnement potentiellement contrôlées par l'utilisateur (par exemple "$PATH", "$HOME", "$USER"etc. Et y compris certainement "$QUERY_STRING"et "HTTP_USER_AGENT"etc dans un script CGI). En fait, citez-les tous. Si vous devez construire une ligne de commande avec plusieurs arguments, utilisez un tableau pour construire la liste d'arguments et citez-le - "${myarray[@]}".

Ai-je déjà dit "cite-les tous" assez souvent? Souviens toi. fais le.

cas
la source
18
Vous avez oublié de mentionner que le script lui-même doit appartenir à root, avec les autorisations 500.
Wildcard
13
Supprimez au moins les autorisations d'écriture, pour l'amour de Dieu. C'était vraiment ce que je voulais dire. Le reste ne fait que durcir.
Wildcard
10
Un programme C soigneusement écrit aura une surface d’attaque plus petite qu’un script shell.
user253751
7
@immibis, éventuellement. Mais l'écriture et le débogage prendront beaucoup plus de temps qu'un script shell. Et nécessite l’utilisation d’un compilateur C (interdit sur certains serveurs de production afin de réduire les risques pour la sécurité en rendant plus difficile la tâche des attaquants lors de la compilation des exploits). En outre, un script shell écrit par un administrateur système ou programmateur de niveau débutant à un niveau intermédiaire est moins susceptible d'être exploitable qu'un programme C écrit par une personne ayant des compétences similaires, en particulier s'il doit accepter et valider les données fournies par l'utilisateur.
cas
3
@JonasWielicki - Je pense que nous sommes maintenant bel et bien dans le royaume de l'opinion plutôt que dans les faits. Vous pouvez définir des arguments valides pour que shell ou C (ou perl ou python ou awk, etc.) soit plus ou moins sujet à des erreurs exploitables. Quand, en réalité, cela dépend principalement des compétences du programmeur et de son souci du détail (et de la fatigue, de la hâte, de la prudence, etc.). Il est un fait, cependant, que les langages de bas niveau ont tendance à demander plus de code pour écrire ce qui peut être fait avec beaucoup moins de lignes de code dans les langages de niveau supérieur ... et chaque CdC est une autre opportunité pour erreur à faire.
cas
16

Vérifiez le propriétaire sur les fichiers gpio:

ls -l /sys/class/gpio/

Très probablement, vous découvrirez qu'ils appartiennent à un groupe gpio:

-rwxrwx--- 1 root     gpio     4096 Mar  8 10:50 export
...

Dans ce cas, vous pouvez simplement ajouter votre utilisateur au gpiogroupe pour accorder l'accès sans sudo:

sudo usermod -aG gpio myusername

Vous devrez vous déconnecter puis vous reconnecter pour que les modifications prennent effet.

jpa
la source
Ça ne marche pas En effet, le propriétaire du groupe dans tout /sys/class/gpio/est gpio, mais même après m'être ajouté à ce groupe, je reçois toujours une "permission refusée" chaque fois que j'essaye d'écrire quoi que ce soit là.
vsz
1
Le problème est que les fichiers ne /sys/class/gpio/sont en fait que des liens symboliques vers /sys/devices/platform/soc/<some temporary name>/gpiole propriétaire et le groupe sont root.
vsz
4
@vsz Avez-vous essayé chgrp gpio /sys/devices/platform/soc/*/gpio? Peut-être que quelque chose comme ça pourrait être mis dans un fichier de démarrage.
jpa
oui, mais ce n'est pas si simple. Comme ils sont toujours générés de manière différente, j'ai dû utiliser quelque chose du genrechgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
vsz le
7

Une solution à cela (utilisée en particulier sur le bureau Linux mais également applicable dans d'autres cas) consiste à utiliser D-Bus pour activer un petit service fonctionnant en tant que root et polkit pour effectuer l'autorisation. C'est fondamentalement ce pour quoi polkit a été conçu ; de sa documentation d'introduction :

polkit fournit une API d'autorisation destinée à être utilisée par des programmes privilégiés («MÉCANISMES») offrant des services aux programmes sans privilèges («CLIENTS»). Voir la page de manuel de polkit pour l'architecture du système et une vue d'ensemble.

Plutôt que d'exécuter votre programme d'assistance, le gros programme sans privilèges enverrait une demande sur le bus. Votre assistant peut soit être exécuté en tant que démon démarré au démarrage du système, soit, mieux, être activé à la demande de systemd . Ensuite, cette personne utilisera polkit pour vérifier que la demande provient d'un lieu autorisé. (Ou, dans ce cas, si cela vous semble excessif, vous pouvez utiliser un autre mécanisme d'authentification / autorisation codé en dur.)

J'ai trouvé un bon article de base sur la communication via D-Bus et, même si je ne l'ai pas encore testé, il semble que ce soit un exemple élémentaire de l'ajout de polkit au mélange .

Dans cette approche, rien ne doit être marqué setuid.

mattdm
la source
5

Une façon de faire est de créer un programme setuid-root écrit en C qui ne fait que ce qui est nécessaire et rien de plus. Dans votre cas, il n’est pas nécessaire de consulter les entrées de l’utilisateur.

#include <unistd.h>
#include <string.h>
#include <stdio.h>  // for perror(3)
// #include ...  more stuff for open(2)

static void write_str_to_file(const char*fn, const char*str) {
    int fd = open(fn, O_WRONLY)
    if (-1 == fd) {
        perror("opening device file");  // make this a CPP macro instead of function so you can use string concat to get the filename into the error msg
        exit(1);
    }
    int err = write(fd, str, strlen(str));
    // ... error check
    err = close(fd);
    // ... error check
}

int main(int argc, char**argv) {
    write_string_to_file("/sys/class/gpio/export", "17");
    write_string_to_file("/sys/class/gpio/gpio17/direction", "out");
    return 0;
}

Il n'y a aucun moyen de subvertir cela dans des variables d'environnement ou quoi que ce soit, car il ne fait que quelques appels système.

Inconvénient: vous devriez vérifier la valeur de retour de chaque appel système.

Upside: la vérification des erreurs est vraiment facile: s'il y a une erreur, il suffit de perrorsortir de la machine: sortie avec un statut non nul. En cas d'erreur, recherchez avec strace. Vous n'avez pas besoin de ce programme lui-même pour donner des messages d'erreur vraiment sympas.

Peter Cordes
la source
2
Je pourrais être tenté d’ouvrir les deux fichiers avant d’écrire quoi que ce soit pour me protéger de l’absence du second. Et je pourrais exécuter ce programme via sudoafin qu'il n'ait pas besoin d'être lui-même défini. Incidemment, c'est <fcntl.h>pour open().
Jonathan Leffler
3

Bénédiction de tee au lieu d’écho pour sudo est une façon courante d’aborder une situation dans laquelle il est nécessaire de limiter les permanentes de racine. La redirection vers / dev / null consiste à empêcher toute sortie de sortie: le tee fait ce que vous voulez.

echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null
Miles Gillham
la source
5
Si vous autorisez teel'exécution avec sudo, vous autorisez le remplacement ou l' /etc/passwdajout de n'importe quel fichier (y compris, par exemple, un simple compte uid = 0). Vous pouvez également autoriser l'exécution de toutes les commandes sudo(BTW /etc/sudoersappartient à l'ensemble "Tous les fichiers" et peut donc être écrasé avec sudo tee)
cas
2
Apparemment, vous pouvez limiter les fichiers sur lesquels vous teepouvez écrire, comme décrit dans cette réponse à une question distincte. Assurez-vous simplement de lire les commentaires également, car certaines personnes ont eu des problèmes avec la syntaxe originale utilisée et suggèrent des solutions à ce problème.
Alex
1
@alex, oui, vous pouvez le faire avec n'importe quelle commande dans sudo - restreindre les arguments autorisés. La configuration peut être très longue et compliquée si vous voulez autoriser teeou ne pas utiliser beaucoup de fichiers avec sudo.
cas
0

Vous pouvez créer un script qui exécute la tâche souhaitée. Créez ensuite un nouveau compte utilisateur qui ne peut se connecter qu’en fournissant une clé utilisée par OpenSSH.

Remarque: tout le monde pourra exécuter ce script s'il possède le fichier de clé. Assurez-vous donc que le fichier de clé OpenSSH n'est pas lisible par quiconque que vous souhaitez empêcher de réaliser cette tâche.

Dans la configuration OpenSSH (dans le fichier allowed_keys), avant de spécifier la clé, spécifiez une commande (suivie d'un espace), comme décrit dans cet exemple de texte:

command="myscript" keydata optionalComment

Cette option de configuration peut limiter cette clé OpenSSH à l'exécution d'une commande spécifique uniquement. Maintenant, vous avez les autorisations sudo, mais la configuration OpenSSH est la partie de la solution réellement utilisée pour restreindre ce que cet utilisateur est capable de faire, de sorte qu'il n'exécute pas d'autres commandes. Cela n’a pas non plus un fichier de configuration "sudo" aussi compliqué, donc si vous utilisez OpenBSD ou si le nouveau "doas" ("do as") d’OpenBSD commence à devenir plus populaire (avec les futures versions du système d’exploitation que vous utilisez) , vous n’aurez pas besoin de beaucoup de difficultés dans la configuration de sudo.

TOOGAM
la source