Ich habe eine Serveranwendung, die unter einem Systemkonto ausgeführt wird, weil sie zu jedem beliebigen Zeitpunkt Anfragen im Auftrag eines Benutzers auf dem System verarbeitet. Diese Anfragen bestehen aus Anweisungen zum Manipulieren des Dateisystems.
Hier ist der Haken: Das Programm muss die Berechtigungen des jeweiligen Benutzers berücksichtigen, wenn er die Aktionen ausführt. Beispiel: joe
sollte /home/larry
nicht ändern können, wenn die Berechtigungen 755
sind.
Momentan ist meine Strategie das
Ist das klug? Gibt es einen einfacheren Weg, dies zu tun?
Zuerst dachte ich daran, mehrere Instanzen der App unter den Benutzerkonten laufen zu lassen - aber das ist keine Option, weil dann nur eine der Instanzen auf einen bestimmten TCP-Port zugreifen kann.
Sieh dir Samba an, um ein Beispiel dafür zu bekommen. Der Samba-Daemon läuft als root, aber verzweigt und übernimmt so schnell wie möglich die Anmeldeinformationen eines normalen Benutzers.
Unix-Systeme verfügen über zwei getrennte Anmeldeinformationen: die echten Benutzer- / Gruppen-IDs und die effektiven Benutzer- / Gruppen-IDs. Das reale Set identifiziert, wer Sie tatsächlich sind, und das effektive Set definiert, worauf Sie zugreifen können. Sie können die effektive uid / gid nach Belieben ändern, wenn Sie root sind - einschließlich eines normalen Benutzers und wieder zurück -, da Ihre echten Benutzer- / Gruppen-IDs während des Übergangs root bleiben. Eine andere Möglichkeit, dies in einem einzigen Prozess zu tun, besteht darin, seteuid/gid
zu verwenden, um die Berechtigungen verschiedener Benutzer nach Bedarf vor- und zurückzuschalten. Wenn Ihr Server-Daemon als root läuft oder CAP_SETUID
hat, ist dies erlaubt.
Beachten Sie jedoch, dass, wenn Sie die effektive uid / gid nach Belieben wechseln können und Ihre Anwendung unterlaufen ist, diese Subversion beispielsweise die effektive uid / gid zurück auf 0 setzen könnte und Sie eine ernsthafte Sicherheitslücke haben könnten . Aus diesem Grund ist es ratsam, alle Privilegien so schnell wie möglich dauerhaft zu löschen, einschließlich Ihres echten Benutzers uid / gid.
Aus diesem Grund ist es normal und sicherer, wenn ein einzelner abhörender Socket als root ausgeführt wird, dann abzweigt und sowohl die echten als auch die effektiven Benutzer-IDs durch Aufruf von setuid
ändert. Dann kann es nicht zurück wechseln. Ihr gegabelter Prozess hat den Socket, der accept()
ed ist, da es sich um eine Verzweigung handelt. Jeder Prozess schließt nur die Dateideskriptoren, die er nicht benötigt; Die Sockets bleiben lebendig, da sie von den Dateideskriptoren in den entgegengesetzten Prozessen referenziert werden.
Sie könnten auch versuchen, die Berechtigungen zu erzwingen, indem Sie sie einzeln untersuchen, aber ich hoffe, es ist offensichtlich, dass dies potentiell fehleranfällig ist, viele Randfälle hat und eher schief geht (zB wird es nicht funktionieren mit POSIX-ACLs, es sei denn, Sie implementieren dies auch speziell).
Sie haben also drei Möglichkeiten:
setgid()
/ setuid()
an den von Ihnen gewünschten Benutzer. Wenn eine Kommunikation erforderlich ist, verwenden Sie pipe(2)
oder socketpair(2)
, bevor Sie fork. seteuid()
/ setegid()
nach Bedarf (weniger sicher: es ist wahrscheinlicher, dass Ihr Server versehentlich beschädigt wird). Wenn Sie mit dem Daemon kommunizieren müssen, ist es zwar schwieriger, einen Socket oder eine Pipe zu verwenden, aber die erste Option ist wirklich der richtige sichere Weg. Sehen Sie sich an, wie SSH die Rechtetrennung vornimmt . Sie können auch überlegen, ob Sie Ihre Architektur so ändern können, dass der Prozess statt irgendeiner Kommunikation nur etwas Speicherplatz oder Speicherplatz freigibt.
Sie erwähnen, dass Sie in Betracht gezogen haben, für jeden Benutzer einen separaten Prozess auszuführen, aber einen einzigen empfangenden TCP-Port benötigen. Du kannst das immer noch tun. Lassen Sie einen Master-Daemon den TCP-Port abhören und Anfragen an jeden Benutzer-Daemon senden und kommunizieren Sie wie gewünscht (z. B. über Unix-Domain-Sockets). Dies wäre tatsächlich fast dasselbe wie ein Forking-Master-Daemon; Ich denke, Letzteres würde sich leichter umsetzen lassen.
Weitere Informationen: Die Hilfeseite credentials(7)
. Beachten Sie auch, dass Linux über das Dateisystem uid / gids verfügt. Dies ist fast dasselbe wie effektive UID / GIDs, außer für andere Dinge wie das Senden von Signalen. Wenn Ihre Benutzer keinen Shell-Zugriff haben und keinen beliebigen Code ausführen können, müssen Sie sich keine Gedanken über den Unterschied machen.
Ich hätte meinen Server fork () und sofort setuid(uid)
, um root-Privilegien aufzugeben. Dann würde jede Dateimanipulation im Namen des Benutzers sein, der Sie geworden sind. Da du ein Kind des Servers bist, hättest du immer noch den accept () Ed Child-Socket, den die Anfrage (und ich nehme eine Antwort) weitermachen würde. Dies erfordert (offensichtlich) root-Rechte für den Daemon.
Das Übergeben von Dateideskriptoren zwischen Prozessen erscheint in diesem Fall unnötig kompliziert, da das Kind bereits den Deskriptor "request" hat.
Lassen Sie einen Server auf dem previlegierten Server-Port laufen und erstellen Sie untergeordnete Prozesse für Benutzer, die sich beim System anmelden. Die untergeordneten Prozesse sollten Privilegien löschen und die Identität des angemeldeten Benutzers annehmen. Jetzt können die Kinder keinen Schaden mehr anrichten.
Tags und Links unix privileges file-manipulation