Rechte und Pflichten im Dateisystem

So wie sich Sommer und Winter abwechseln, so kämpften Microsoft und die OpenSource Programmierer jahrelang um “das Beste” Berechtigungskonzept.

DOS und Windows 3.x und 9x waren rechtelos und damit den Unix-Derivaten schwer unterlegen. Mit Windows NT 3 und NT 4 legte man ACLs (Access Control Lists) über FAT32 und taufte es NTFS, was flexibler als die alten Posix-Rechte war, die nur zwischen einem Eigentümer (Owner), einer Gruppe (Group) und allem anderen (Other) unterschied.

Etwas später zogen Linux und BSD nach unten bauten einen erweiterten POSIX Entwurf ein, der diese Zugriffslisten (ACLs) auch auf ihrer Seite möglich machte.

Und letztendlich versuchte man in beiden Welten durch diverse Tricks die begehrten “Administrativen Privilegien” stückweise den Anwendungsprogrammen bereitzustellen, ohne dass man gleich alle Macht dem Standard-Benutzer-Account vor die Füße warf.

Um niemanden zu beleidigen sagen wir deshalb: Es steht nun unentschieden.

Da das GATE Projekt auf mehreren Platformen lauffähig sein soll, spielen Dateirechte tatsächlich eine wichtige Rolle. Und da Windows und Linux hier unterschiedlich agieren, ist eine “Normierung” per Code aus meiner Sicht wünschenswert.

Denn unabhängig von den nativen Möglichkeiten der Berechtigung, verhalten sich die Standard-C-APIs auf beidenen Plattformen rechtetechnisch leider sehr verschieden.

Windows

Wer per fopen() oder std::fstream eine neue Datei (zum Beschreiben) anlegt, erhält die Rechte, die das Eltern-Verzeichnis definiert oder gleich einen Fehler bzw. eine Exception, weil das Elternverzeichnis dem angemeldeten Benutzer das Erstellen nicht erlaubt.

Das ist eigentlich sehr vernünftig und wir sprechen hier von einer Rechte-Vererbung. Ein Administrator legt die Rechtevorgabe für ein Verzeichnis fest und Dateien und Unterordner erhalten automatisch die Rechte, genau so wie der Admin es will.

Will man nun aber für einzelne Dateien aus diesem Schema ausbrechen, um z.B. Schlüsselwerte oder “geheime” Daten so zu speichern, damit kein anderer Benutzer sie lesen kann, wird es unter Windows kompliziert:

  1. Man braucht die SID (Security IDs) der Accounts und Gruppen, die berechtig sein sollen.
  2. Dann folgen InitializeSecurityDescriptor() und InitializeAcl().
  3. Mit AddAccessAllowedAce() werden die SIDs mit Rechte-Flags kombiniert in die neue ACL hinzugefügt
  4. Schließlich pflanzt SetSecurityDescriptorDacl() die ACL in einen Security-Descriptor
  5. Und am Ende wird der Descriptor beim Aufruf von CreateFile() in einer SECURITY_ATTRIBUTES Struktur mitübergeben und so auf die neu erstellte Datei angewendet.

Linux

Linux “denkt” hier ganz anders. Jede neue Datei braucht die Angabe von mode-Flags, die festlegen welche Rechte der Eigentümer (der Account unter dem der Prozess läuft), die Gruppe (die primäre Gruppe des Prozess-Accounts) und alle anderen Accounts haben sollen.

fopen() stellt uns diese Schnittstelle nicht bereit, sondern setzt immer die Rechte 0666 ein, was bedeutet, dass alle die neue Datei lesen und beschreiben (aber nicht ausführen) dürfen.

Verkompliziert wird dass dann noch durch den globalen Wert von umask, der entweder auf den Wert 0775 oder 0755 gesetzt ist und die Schreib-Bits für ‘Alle’ (und für die Gruppe) wegkürzt.

Für individuelle Berechtigung ist die POSIX API mit open() angenehm einfach, doch wenn man (wie unter Windows) ein Vererbungsmodell aufbauen möchte, wird es wiederum kompliziert. Hierzu muss man

  1. Die Rechte des Elternverzeichnisses auslesen
  2. Die Datei erstellen
  3. Die Rechte per fchmod() setzen (weil man auf umask nicht vertrauen darf)
  4. Eigentümer und Gruppe per fchown() auf die Werte des Elternverzeichnisses setzen.

Die GATE Implementierung

Wer sich nun fragt, warum man unter Linux/POSIX das Windows-Vererbungsschema umsetzen sollte, dem kann ich die einfache Antwort: “Services” bzw. “Daemons” geben.

Da eine Bibliothek grundsätzlich unabhängig ist, also nicht weiß, von wem sie genutzt wird, ergibt sich beim Zusammenspiel von Benutzeranwendungen und Diensten, das Problem, dass wenn Dienste unter Linux in Verzeichnisse der Benutzer schreiben, die Nutzer diese Daten nicht mehr lesen können, weil Dienste alles mit root (oder anderen hohen) Rechten anlegen würden.

Wenn man aber die Rechte “ererben” lässt, besteht dieses Problem nicht.

Umgekehrt ist es kein Problem wenn eine neue Datei von Benutzer A die Rechte eines Verzeichnisses von Nutzer B erbt, denn wenn das Anlegen klappt, dann war die Berechtigung schon so ausgelegt, das Nutzer A im B-Verzeichnis lesen UND schreiben darf.

Und daher ist Vererbung der Standard bei den GATE-File-APIs, jedoch können über zusätzliche Flags “Alle” und “Gruppen” explizit ausgeschlossen werden, womit man konkret angeben kann, dass spezielle Dateien eben nicht von anderen gelesen und beschrieben werden dürfen.

Und diese Optionen lassen sich auf beiden Plattformen implementieren.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!