Was ist die sicherste Methode zum programmgesteuerten Schreiben in eine Datei mit Root-Rechten?

35

Eine große Anwendung muss zu einem bestimmten Zeitpunkt eine kleine Anzahl von Schreibvorgängen in eine Datei ausführen, für die Root-Berechtigungen erforderlich sind. Es ist eigentlich keine Datei, sondern eine Hardware-Schnittstelle, die Linux als Datei zur Verfügung stellt.

Um zu vermeiden, dass die gesamte Anwendung Root-Rechte erhält, habe ich ein Bash-Skript geschrieben, das die kritischen Aufgaben ausführt. Das folgende Skript aktiviert beispielsweise Port 17 der Hardwareschnittstelle als Ausgabe:

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

Da dies suidjedoch für Bash-Skripte auf meinem System deaktiviert ist, frage ich mich, wie dies am besten erreicht werden kann.

  1. Verwenden Sie eine hier vorgestellte Problemumgehung

  2. Rufen Sie das Skript mit sudovon der Hauptanwendung aus auf und bearbeiten Sie die Liste der Sudoer entsprechend, um zu vermeiden, dass Sie beim Aufrufen des Skripts ein Kennwort benötigen. Es ist mir ein bisschen unangenehm, Sudo-Berechtigungen zu erteilen echo.

  3. Schreiben Sie einfach ein C-Programm mit fprintfund setzen Sie es auf suid root. Codieren Sie die Zeichenfolgen und Dateinamen fest und stellen Sie sicher, dass sie nur von root bearbeitet werden können. Oder lesen Sie die Zeichenfolgen aus einer Textdatei und vergewissern Sie sich, dass niemand die Datei bearbeiten kann.

  4. Eine andere Lösung, die mir nicht in den Sinn gekommen ist und die sicherer oder einfacher ist als die oben vorgestellten?

vsz
quelle
10
Warum starten Sie Ihr Programm nicht einfach mit Root-Rechten, öffnen die Datei und löschen die Rechte? So macht es jeder Webserver oder so für Sockets. Tatsächlich laufen Sie nicht als Root, und es ist auch kein Helfer erforderlich, der dies tut.
Damon

Antworten:

33

Sie müssen keinen sudoZugriff auf geben echo. Tatsächlich ist das sinnlos, weil z. B. bei sudo echo foo > bardie Umleitung als der ursprüngliche Benutzer erfolgt, nicht als root.

Rufen Sie das kleine Skript mit auf sudo, und NOPASSWD:gewähren Sie den Benutzern, die Zugriff darauf benötigen, NUR Zugriff auf dieses Skript (und andere ähnliche Skripts).

Dies ist immer die beste / sicherste Art zu verwenden sudo. Isolieren Sie die kleine Anzahl von Befehlen, die Root-Berechtigungen benötigen, in ihre eigenen separaten Skripte und erlauben Sie dem nicht vertrauenswürdigen oder teilweise vertrauenswürdigen Benutzer, dieses Skript nur als Root auszuführen.

Die small sudo-able-Skripte sollten entweder keine Argumente (oder Eingaben) vom Benutzer entgegennehmen (dh alle anderen Programme, die sie aufrufen, sollten fest codierte Optionen und Argumente haben) oder sie sollten sorgfältig alle Argumente / Eingaben validieren, die sie benötigen vom Benutzer akzeptieren.

Seien Sie bei der Validierung paranoid - anstatt nach "bekannten schlechten" Dingen zu suchen, die ausgeschlossen werden sollen, lassen Sie nur "bekannte gute" Dinge zu und brechen Sie jede Nichtübereinstimmung oder jeden Fehler oder alles ab, was auch nur annähernd verdächtig ist.

Die Validierung sollte so früh wie möglich im Skript erfolgen (vorzugsweise bevor etwas anderes als root ausgeführt wird).


Ich wirklich hätte dies erwähnt , als ich zum ersten Mal diese Antwort geschrieben, aber wenn Ihr Skript ein Shell - Skript ist es MUSS richtig zitieren alle Variablen. Seien Sie besonders vorsichtig Variablen zu zitieren enthalten Eingabe durch den Benutzer in geliefert irgendeiner Art und Weise, aber nicht davon ausgehen , einige Variablen sicher sind, QUOTE sie alle .

Dazu gehört Umgebungsvariablen möglicherweise durch den Benutzer gesteuert (zB "$PATH", "$HOME", "$USER"etc. Und auf jeden Fall einschließlich "$QUERY_STRING"und "HTTP_USER_AGENT"etc in einem CGI - Skript). Zitieren Sie sie einfach alle. Wenn Sie eine Befehlszeile mit mehreren Argumenten erstellen müssen, verwenden Sie ein Array, um die Argumentliste zu erstellen, und zitieren Sie Folgendes: - "${myarray[@]}".

Habe ich schon oft genug "zitiere sie alle" gesagt? erinnere dich dran. TU es.

cas
quelle
18
Sie haben vergessen zu erwähnen, dass das Skript selbst im Besitz von root sein sollte, mit Berechtigungen von 500.
Wildcard
13
Entfernen Sie zumindest die Schreibrechte, um Himmels willen. Das war wirklich mein Punkt. Der Rest verhärtet sich gerade.
Wildcard
10
Ein sorgfältig geschriebenes C-Programm hat eine kleinere Angriffsfläche als ein Shell-Skript.
user253751
7
Möglicherweise @immibis. Das Schreiben und Debuggen dauert jedoch viel länger als ein Shell-Skript. Und erfordert einen C-Compiler (der auf einigen Produktionsservern verboten ist, um das Sicherheitsrisiko zu verringern, indem Angreifer das Kompilieren von Exploits erschweren). Außerdem ist es weniger wahrscheinlich, dass IMO, ein Shell-Skript, das von einem Anfänger für einen Systemadministrator oder Programmierer auf mittlerer Ebene geschrieben wurde, ausgenutzt werden kann als ein C-Programm, das von jemandem mit ähnlichen Kenntnissen geschrieben wurde - insbesondere, wenn vom Benutzer bereitgestellte Daten akzeptiert und validiert werden müssen.
cas
3
@JonasWielicki - Ich denke, wir sind jetzt wirklich auf der Suche nach Meinungen und nicht nach Fakten. Sie können gültige Argumente für Shell oder C (oder Perl oder Python oder awk usw.) angeben, die mehr oder weniger anfällig für ausnutzbare Fehler sind. Wann dies wirklich der Fall ist, hängt hauptsächlich von den Fähigkeiten und der Liebe zum Detail des Programmierers ab (und von Müdigkeit, Eile, Vorsicht usw.). Tatsache ist jedoch, dass Sprachen auf niedrigeren Ebenen in der Regel mehr Code erfordern, um das zu erreichen, was in höheren Sprachen mit weitaus weniger Codezeilen erreicht werden kann Fehler gemacht werden.
cas
16

Überprüfen Sie den Eigentümer der GPIO-Dateien:

ls -l /sys/class/gpio/

Höchstwahrscheinlich werden Sie feststellen, dass sie der Gruppe gehören gpio:

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

In diesem Fall können Sie einfach Ihren Benutzer zur gpioGruppe hinzufügen , um den Zugriff ohne sudo zu gewähren:

sudo usermod -aG gpio myusername

Danach müssen Sie sich abmelden und erneut anmelden, damit die Änderung wirksam wird.

jpa
quelle
Es funktioniert nicht In der Tat ist der Gruppenbesitzer von allem in /sys/class/gpio/gpio, aber selbst nachdem ich mich zu dieser Gruppe hinzugefügt habe, erhalte ich immer noch "Erlaubnis verweigert", wenn ich versuche, etwas dort zu schreiben.
vsz
1
Das Problem ist, dass die Dateien /sys/class/gpio/nur Symlinks sind, /sys/devices/platform/soc/<some temporary name>/gpioauf die sowohl der Eigentümer als auch die Gruppe als Root verweisen.
vsz
4
@vsz Hast du es versucht chgrp gpio /sys/devices/platform/soc/*/gpio? Vielleicht könnte so etwas in eine Startdatei eingefügt werden.
JPA
ja, aber so einfach ist das nicht Da sie immer anders generiert werden, musste ich etwas wiechgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
vsz
7

Eine Lösung hierfür (besonders auf dem Linux-Desktop, aber auch in anderen Fällen anwendbar) ist die Verwendung von D-Bus , um einen kleinen Dienst zu aktivieren, der als Root und Polkit für die Autorisierung ausgeführt wird. Dafür wurde das Polkit im Grunde genommen entwickelt . aus seiner einleitenden Dokumentation :

polkit bietet eine Berechtigungs-API, die von privilegierten Programmen ("MECHANISMS") verwendet werden soll und Dienste für nichtprivilegierte Programme ("CLIENTS") anbietet. Auf der Handbuchseite zum Polkit finden Sie Informationen zur Systemarchitektur und zum Gesamtüberblick.

Anstatt Ihr Hilfsprogramm auszuführen, würde das große, nicht privilegierte Programm eine Anforderung auf dem Bus senden. Ihr Helfer kann entweder als Daemon ausgeführt werden, der beim Systemstart gestartet wurde, oder er kann nach Bedarf von systemd aktiviert werden . Dann würde dieser Helfer ein Polkit verwenden, um zu überprüfen, ob die Anforderung von einem autorisierten Ort stammt. (In diesem Fall können Sie auch einen anderen fest codierten Authentifizierungs- / Autorisierungsmechanismus verwenden, wenn dies zu viel des Guten ist.)

Ich habe einen guten Basisartikel über die Kommunikation über D-Bus gefunden , und obwohl ich ihn noch nicht getestet habe, scheint dies ein grundlegendes Beispiel für das Hinzufügen eines Polkits zum Mix zu sein .

Bei diesem Ansatz muss nichts als setuid markiert werden.

mattdm
quelle
5

Eine Möglichkeit, dies zu tun, besteht darin, ein in C geschriebenes setuid-root-Programm zu erstellen, das nur das tut, was benötigt wird, und nicht mehr. In Ihrem Fall muss keine Benutzereingabe berücksichtigt werden.

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

Es gibt keine Möglichkeit, dies durch Umgebungsvariablen oder etwas anderes zu untergraben, da nur ein paar Systemaufrufe erforderlich sind.

Nachteil: Sie sollten den Rückgabewert jedes Systemaufrufs überprüfen.

Nach oben: Fehlerprüfung ist ganz einfach: Wenn überhaupt ein Fehler vorliegt, einfach perrorund aussteigen: Beenden mit einem Status ungleich Null. Wenn ein Fehler auftritt, untersuchen Sie ihn mit strace. Sie brauchen dieses Programm nicht, um wirklich nette Fehlermeldungen zu geben.

Peter Cordes
quelle
2
Ich könnte versucht sein, beide Dateien zu öffnen, bevor ich etwas schreibe, um mich vor dem Fehlen der zweiten zu schützen. Und ich könnte dieses Programm über ausführen, sudodamit es nicht selbst gesetzt werden muss. Übrigens ist es <fcntl.h>für open().
Jonathan Leffler
3

Bless tee anstelle von Echo für sudo ist eine gängige Methode, um Situationen zu begegnen, in denen Sie die Dauerwellen begrenzen müssen. Die Umleitung zu / dev / null soll verhindern, dass die Ausgabe ausläuft - das T-Stück macht, was Sie wollen.

echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null
Miles Gillham
quelle
5
Wenn Sie die teeAusführung mit zulassen , lassen sudoSie zu, dass JEDE Datei überschrieben oder angehängt wird (einschließlich beispielsweise /etc/passwd- fügen Sie einfach ein neues Konto mit der UID = 0 hinzu). Sie können auch zulassen, dass alle Befehle mit ausgeführt werden sudo(BTW /etc/sudoersgehört in den Satz von 'ANY-Dateien', kann also mit überschrieben werden sudo tee)
cas
2
Anscheinend können Sie die Dateien, in die geschrieben werden teekann, einschränken , wie in dieser Antwort auf einer separaten Frage beschrieben. Lesen Sie auch die Kommentare, da einige Benutzer Probleme mit der verwendeten ursprünglichen Syntax hatten und Vorschläge zur Behebung dieses Problems machen.
Alex
1
@alex, ja, das kannst du mit jedem Befehl in sudo machen - beschränke die zulässigen Argumente. Die Konfiguration kann sehr lang und kompliziert werden, wenn Sie zulassen möchten, dass teeoder was auch immer mit vielen Dateien gearbeitet wird sudo.
cas
0

Sie können ein Skript erstellen, das Ihre gewünschte Aufgabe ausführt. Erstellen Sie dann ein neues Benutzerkonto, das sich nur mit einem von OpenSSH verwendeten Schlüssel anmelden kann.

Hinweis: Jeder kann dieses Skript ausführen, wenn er über die Schlüsseldatei verfügt. Stellen Sie daher sicher, dass die OpenSSH-Schlüsseldatei von niemandem gelesen werden kann, der die Ausführung der Aufgabe verhindern soll.

Geben Sie in der OpenSSH-Konfiguration (in der Datei authorized_keys) vor der Angabe des Schlüssels einen Befehl (gefolgt von einem Leerzeichen) an, wie in diesem "Beispiel" beschrieben:

command="myscript" keydata optionalComment

Diese Konfigurationsoption kann den OpenSSH-Schlüssel darauf beschränken, nur einen bestimmten Befehl auszuführen. Jetzt haben Sie sudo, um die Berechtigungen zu erteilen, aber die OpenSSH-Konfiguration ist der Teil der Lösung, der wirklich verwendet wird, um die Möglichkeiten dieses Benutzers einzuschränken / einzuschränken, damit der Benutzer keine anderen Befehle ausführt. Dies hat auch nichts mit einer "sudo" -Konfigurationsdatei zu tun. Wenn Sie also OpenBSD verwenden oder wenn OpenBSDs neues "doas" ("do as") populärer wird (mit zukünftigen Versionen des von Ihnen verwendeten Betriebssystems) , Sie müssen sich nicht mit viel Komplexität in der sudo-Konfiguration herumschlagen.

TOOGAM
quelle