web-dev-qa-db-fra.com

Quel est le moyen le plus sûr d'écrire par programmation dans un fichier avec des privilèges root?

Une énorme application nécessite, à un moment donné, d'effectuer un petit nombre d'écritures dans un fichier qui nécessite des autorisations racines. Ce n'est pas vraiment un fichier mais une interface matérielle exposée à Linux en tant que fichier.

Pour éviter de donner des privilèges root à toute 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 comme sortie:

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

Cependant, comme suid est désactivé pour les scripts Bash sur mon système, je me demande quelle est la meilleure façon de y parvenir.

  1. Utiliser une solution de contournement présentée ici

  2. Appelez le script avec Sudo à partir de l'application principale et modifiez la liste sudoers en conséquence, pour éviter de nécessiter un mot de passe lors de l'appelant le script. Je suis un peu mal à l'aise pour donner des privilèges sudo à echo.

  3. Il suffit d'écrire un programme C, avec fprintf et réglez-le sur la racine de Suid. Code du câble des cordes et des noms de fichiers et assurez-vous que seule la racine peut la modifier. Ou lisez les chaînes d'un fichier texte, en vous assurant que personne ne peut modifier le fichier.

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

35
vsz

Vous n'avez pas besoin de donner Sudo accès à echo. En fait, c'est inutile parce que, par exemple. Avec Sudo echo foo > bar, la redirection est effectuée en tant qu'utilisateur d'origine, pas en tant que root.

Appelez le petit script avec Sudo, permettant à NOPASSWD: Accès à ce script (et à tout autre script similaire) par les utilisateurs qui ont besoin d'y accéder.

Ceci est toujours le moyen le mieux/le plus sûr d'utiliser Sudo. Isolez le petit nombre de commandes nécessitant des privilèges root dans leur propre script distinct et permettent à l'utilisateur non fi fiducié ou partiellement fiducié de ne pas exécuter que le script en tant que root.

Le petit Sudo- capable de script (s) ne doit pas ne pas prendre arguments (ou une entrée) de l'utilisateur (c'est-à-dire tout autre programme que les appels informatiques devraient avoir des options et des arguments codés par le disque), soit très prudent valider les arguments/Entrée qu'il doit accepter de l'utilisateur.

Soyez paranoïaque dans la validation - plutôt que de rechercher des choses "mauvaises" connues "à exclure, n'autorisez que des choses" bien connues "et avorter sur n'importe quelle inadéquation ou erreur ou quoi que ce soit, même si vous êtes suspectement à distance.

La validation devrait survenir dès que possible au début du script (de préférence avant qu'elle ne le fait rien autre en tant que root).


I Vraiment aurait dû mentionner cela lorsque j'ai écrit cette réponse, mais si votre script est un script shell, il DOIT Devis correctement - tous variables. Soyez particulièrement prudent pour citer les variables contenant des entrées fournies par l'utilisateur en n'importe quel Way, mais ne supposez pas que certaines variables sont en sécurité, les citez tous.

Qui inclut des variables d'environnement potentiellement contrôlées par l'utilisateur (par exemple, "$PATH", "$HOME", "$USER", Etc., incluant "$QUERY_STRING" Et "HTTP_USER_AGENT" dans un script CGI). En fait, il suffit de les citer tous. Si vous devez construire une ligne de commande avec plusieurs arguments, utilisez un tableau pour créer la liste Args et indiquer que - "${myarray[@]}".

Ai-je dit "les citer tous" assez souvent? Souviens toi. fais-le.

33
cas

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

ls -l /sys/class/gpio/

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

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

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

Sudo usermod -aG gpio myusername

Vous aurez besoin de vous déconnecter et de vous connecter après cela pour que le changement prend effet.

16
jpa

Une solution pour cela (utilisée en particulier sur le bureau Linux mais aussi applicable dans d'autres cas) consiste à utiliser D-Bus pour activer un petit service en tant que root et POLKIT à faire l'autorisation. Ceci est fondamentalement ce que POLKIT était conçu pour; de son Documentation d'introduction :

polkit fournit une API d'autorisation destinée à être utilisée par des programmes privilégiés ("mécanismes") offrant un service à des programmes non privilégiés ("clients"). Voir la page Manuelle Polkit pour l'architecture système et la grande image.

Plutôt que d'exécuter votre programme d'assistrations, le grand programme non privilégié enverrait une demande dans le bus. Votre assistant pourrait être exécutée comme un démon démarré à la démarrage du système ou, mieux, être activé selon les besoins par SystemD . Ensuite, cette assistante utiliserait Polkit pour vérifier que la demande provient d'un lieu agréé. (Ou, dans ce cas, si cela ressemble à une overkill, vous pouvez utiliser un autre mécanisme d'authentification/autorisation codée dur.)

J'ai trouvé un bon article de base sur la communication via D-bus , et pendant que je ne l'ai pas testé, Ceci semble être un exemple de base d'ajouter Polkit au mélange .

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

7
mattdm

Une façon de faire cela consiste à faire un programme de racine setuidir rédigé 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 regarder une entrée utilisateur du tout.

#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 l'interversion par les variables d'environnement ou quoi que ce soit, car tout ce qu'il fait, c'est faire quelques appels système.

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

UPSE: La vérification des erreurs est vraiment facile: s'il y a une erreur du tout, juste perror et cautionnement: sortie avec statut non nul. S'il y a une erreur, enquêtez avec strace. Vous n'avez pas besoin de ce programme lui-même pour donner de très beaux messages d'erreur.

5
Peter Cordes

Bénis Tee au lieu d'Echo pour Sudo est un moyen courant d'approcher une situation où vous devez limiter les permanentes racines. La redirection à/dev/null est d'arrêter toute sortie qui fuit - 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
3
Miles Gillham

Vous pouvez créer un script qui effectue votre tâche souhaitée. Ensuite, créez un nouveau compte utilisateur qui ne peut se connecter que en fournissant une clé utilisée par OpenSSH.

Remarque: Tout le monde sera en mesure d'exécuter ce script s'ils disposent du fichier de clé, assurez-vous donc que le fichier de clé OpenSSH n'est lisible par personne que vous souhaitez empêcher d'effectuer la tâche.

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

command="myscript" keydata optionalComment

Cette option de configuration peut limiter la touche OpenSSH pour uniquement exécuter une commande spécifique. Vous avez maintenant sudo accordant les autorisations, mais la configuration OpenSSH est la pièce de la solution qui est vraiment utilisée pour limiter/limiter ce que cet utilisateur est capable de faire, de sorte que l'utilisateur n'exécute pas d'autres commandes. Cela n'a pas non plus aussi compliqué d'un fichier de configuration "sudo", donc si vous utilisez OPENBSD ou si les nouvelles "DOAS" de OpenBSD ("faire comme") commencent à devenir plus populaires (avec les futures versions de tout système d'exploitation que vous utilisez) Vous n'avez pas besoin d'être mis au défi de la complexité de la configuration sudo.

0
TOOGAM