Sommerzeit, Jobwechselzeit! Wer den Sprung ins kalte Wasser wagen und einen neuen lukrativen Job an Land ziehen möchte, muss im Einstellungsgespräch wissen, was es mit dem Sticky-Bit unter Unix auf sich hat.
Eine beliebte Frage bei Einstellungsgesprächen ist die nach dem sogenannten Sticky-Bit. Ich persönlich stelle diese Frage nie, wenn ich einen Kandidaten durchleuchte, denn ich denke, dass wer die Antwort weiß, sich höchstens dadurch auszeichnet, dass er sich auf das Gespräch vorbereitet hat. Ein Beweis für Fachkompetenz lässt sich daraus nicht ableiten, aber viele große Firmen im Silicon-Valley haben die Frage im Katalog, und als Spieler in der A-Klasse der Softwareindustrie muss man selbstverständlich auch auf unnütze Fragen eine Antwort wissen.
Deshalb nehmen wir uns diesmal im Perl-Snapshot dem Thema an: Wozu ist das Sticky-Bit da? Wie immer wenn es um knifflige Unix-Fragen geht, hilft das Buch von Michael Kerrisk ([2]) weiter. Dieser Wälzer der Superlative mit mehr als 1500 Seiten ist als Papierbuch nur von durchtrainierten Bodybuildern lesbar, aber im elektronischen Kindle-Format leicht überall mitzuführen. Und wer denkt, er wüsste schon alles über die Feinheiten der Programmierung unter Unix, dem haut es bei der Lektüre dieses Jahrhundertwerks regelmäßig den Schalter raus: Selbst graubärtige Unix-Gurus fördern aus den erstaunlichen Tiefen des Schinkens immer wieder verblüffende Tatsachen hervor.
Wer hätte zum Beispiel gewusst, dass ein User auf einem Unix-System
eine Datei aus einem Verzeichnis löschen kann, obwohl er keine Schreibrechte
an der Datei hat? Richtig, erforderlich sind nur Schreib- und
Ausführungsrechte am Verzeichnis, in dem die Datei liegt. Listing 1
legt zu Demonstrationszwecken ein neues Verzeichnis test
im aktuellen
Verzeichnis an und gibt ihm mit dem oktalen Wert 0333
die Rechte -wx-wx-wx
.
Der anschließend mit blurt()
aus dem CPAN-Modul Sysadm::Install
erzeugten Datei abc
weist die chmod
-Anweisung
in Zeile 9 mit 0444
die Rechte -r--r--r--
zu. Alle User dürfen sie
also lesen, aber nicht beschreiben. Dennoch läuft die unlink
-Anweisung
in Zeile 14 ohne Meckern durch, und auch ein
rm -f test/abc
auf der Kommandozeile löscht die Datei ohne Rückfrage
oder Fehler. Des Rätsels Lösung: Schreibrechte an einer Datei unter
Unix erlauben das Modifizieren des Inhalts einer Datei, Löschrechte hingegen
regelt das Verzeichnis, in dem die Datei liegt, nicht die Datei selber.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use Sysadm::Install qw( blurt ); 04 use File::Path qw( rmtree ); 05 06 mkdir "test"; 07 blurt "data", "test/abc"; 08 chmod 0333, "test"; 09 chmod 0444, "test/abc"; 10 11 # d-wx-wx-wx test 12 # -r--r--r-- test/abc 13 14 unlink "test/abc" 15 or die "unlink failed: $!"; 16 17 rmtree "test";
Als weitere interessante Feinheit der Unix-Programmierung sticht bei
Listing 1 ins Auge,
dass das Verzeichnis test
allen Usern
mit -wx-wx-wx
zwar Schreib- und
Ausführungsrechte gewährt, aber keine Leserechte. Gemäß diesen
Vorschriften kann das Skript das Verzeichnis also durchqueren und
schreibend darauf zugreifen, die in ihm enthaltenen Dateien
aber nicht auflisten. Nachdem das Skript aber weiß, dass die Datei
test/abc
darin liegt, darf es diese auch löschen, denn Schreibrechte
liegen vor. Gehört die Datei einem anderen User, ändert sich
nichts daran, dass Unix sie rücksichtslos löscht, wenn das Verzeichnis,
in dem sie liegt, seine Zustimmung gegeben hat. Das führt naturgemäß zu
Problemen, für die die Unix-Väter in grauer Vorzeit eine recht
zusammengeschusterte Lösung fanden, aber davon später.
Was bedeutet das x
, wenn ein Verzeichnis die Rechte -wx-wx-wx
anzeigt?
Es regelt das Wegerecht zum Durchsteigen des Verzeichnisses. Egal ob ein
Kommando auf den Inhalt einer Datei sub/file
schreiben oder lesend
zugreift oder mit
sub/sub1/...
in ein im Verzeichnis sub
liegendes Unterverzeichnis
sub1
hinabsteigt, benötigt es Ausführungsrechte am Verzeichnis sub
.
Wie Listing 2 zeigt, heißt das aber keineswegs, dass ein Kommando, das
auf das Verzeichnis top/sub/sub1
zugreift, Ausführungsrechte an allen
Pfadkomponenten top
, sub
und sub1
benötigt. Hat ein
Skript zum Beispiel sein aktuelles Verzeichnis vor dem Einrichten
der Zugangssperre
mit chdir()
auf
top/sub/sub2
gesetzt, kann es mit der relativen Pfadangabe ../sub1
sehr wohl auch dann auf sub1
zugreifen, auch wenn dann später
top
die x
-Rechte wie in Zeile 21 in Listing 2 entzogen
werden (Abbildung 1).
Abbildung 1: Gewährt das Verzeichnis top kein Wegerecht, schlägt der Zugriff auf sub1 über den absoluten Pfad zwar fehl, doch der relative Pfad von sub2 nach sub1 ist passierbar. |
Listing 2 zeigt eine mit Test::More implementierte Testsuite zum
Verifizieren dieser Tatsache.
Die Funktion throws_ok()
in Zeile 23 stammt aus dem CPAN-Modul
Test::Exception, fängt auftretende Fehler ab und erlaubt es, diese
mittels regulären Ausdrücken mit in einer Testsuite erwarteten zu
vergleichen. Erwartungsgemäß tritt beim Zugriff über den absoluten
Pfad mit "Permission denied" ein Fehler auf, während eine relative
Pfadangabe wie in Zeile 27 zum Erfolg führt:
$ ./dir-traverse ok 1 - abs path fails ok 2 - rel path ok 1..2
Wer übrigens denkt,
dass der Trick mit dem relativen Pfad auch dann funktioniert,
falls nicht top
sondern top/sub
das Wegerecht storniert, irrt
sich: Wenn ein Skript sich in top/sub/sub2
befindet und
mittels ../sub1
auf top/sub/sub1
zugreift, durchschreitet es
top/sub
und benötigt deshalb dort das Wegerecht.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use Test::More; 04 use Test::Exception; 05 use File::Temp qw( tempdir ); 06 use Sysadm::Install qw( blurt slurp 07 cd cdback ); 08 09 my $ROOT_DIR = tempdir( CLEANUP => 1 ); 10 11 # Read via a relative path while the abso- 12 # lute path doesn't have traversal rights. 13 dirmk( "top", 0700 ); 14 dirmk( "top/sub", 0700 ); 15 dirmk( "top/sub/sub1", 0700 ); 16 dirmk( "top/sub/sub2", 0700 ); 17 18 blurt "content", 19 "$ROOT_DIR/top/sub/sub1/file"; 20 cd "$ROOT_DIR/top/sub/sub2"; 21 chmod 0600, "$ROOT_DIR/top"; 22 23 throws_ok 24 { slurp( "$ROOT_DIR/top/sub/sub1/file" ) } 25 qr/Permission denied/, "abs path fails"; 26 27 is slurp( "../sub1/file" ), 28 "content", "rel path ok"; 29 30 cdback; 31 done_testing(); 32 33 ########################################### 34 sub dirmk { 35 ########################################### 36 my( $dir, $perm ) = @_; 37 38 my $path = "$ROOT_DIR/$dir"; 39 mkdir $path or die "$!"; 40 chmod $perm, $path or die "$!"; 41 }
Das eingangs erwähnte Kuriosum, dass ein User eine Datei löschen darf,
auch wenn die Datei selbst ihm keine Schreibrechte gewährt, führt zu
Problemen, falls sich mehrere User ein Verzeichnis teilen. Legen zum
Beispiel die User A und B die Dateien file-a
und file-b
in /tmp
ab, sollte es A verwehrt bleiben, file-b
zu löschen.
Doch wie soll das gehen, falls nicht die Datei selbst, sondern das
enthaltende Verzeichnis den Löschzugriff regelt? Damit jeder User
auf /tmp
zugreifen darf, sind dessen Rechte auf rwxrwxrwx
eingestellt.
Dies eröffnet allerdings auch beliebigen Usern Löschrechte an allen
dort eingestellten Dateien, und deshalb behalfen sich die Unix-Väter
nach der Geburt des Dinosauriers
mit einem Trick: Ein spezielles 'sticky' Bit, das mit
chmod +t /tmp
gesetzt wird und daran zu erkennen ist, dass das ls
-Kommando
die Verzeichnisrechte folgendermaßen anzeigt:
$ ls -ld /tmp drwxrwxrwt 7 root root 4096 Apr 13 00:17 /tmp
Statt einem abschließenden "rwx" steht nun ein "rwt" in der
Zugriffslatte, und Unix stellt deswegen die
Zugriffsregeln in diesem Verzeichnis auf den Kopf. Auf einmal darf
nun nicht mehr jeder x-beliebige User Einträge löschen, sondern nur noch der
Eigentümer des jeweiligen Eintrags. Und genau deswegen ist auf jedem
Unix-System im Verzeichnis /tmp
das Sticky-Bit gesetzt. Es verhindert,
dass User sich gegenseitig ihre temporären Datein weglöschen. Schreib-
und Leserechte
am Verzeichnis sind nach wie vor für Alle vorhanden, also sollten
temporäre Dateien ihre Zugriffsrechte entsprechend beschränken.
Bonuspunkte erhält der Kandidat beim Einstellungsgespräch übrigens noch,
falls er erklären kann, wann Unix das Sticky-Bit eines Verzeichnisses mit
t
und wann mit T
anzeigt. Mit dem abschließenden t
in rwt
verdeckt der Hack nämlich mit dem Sticky-Bit die Ausführungsrechte
der Allgemeinheit am Verzeichnis. Es lässt sich nicht mehr sagen, ob
vor dem Setzen des Sticky-Bits rw-
oder rwx
vorlag. Deshalb
zeigt Unix rwt
an, falls rwx
vorliegt und rwT
, falls die
Ausführungsrechte fehlen, also vorher rw-
dort stand.
Und wer auch noch weiß, dass nicht nur Verzeichnisse, sondern auch Dateien ein Sticky-Bit führen können, dieses aber etwas völlig anderes regelt, beweist, dass er die Vorbereitung ernst genommen und [2] studiert hat. Der Kandidat hat 100 Punkte und kommt in die nächste Runde!
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2015/06/Perl
Michael Kerrisk, "The Linux Programming Interface: A Linux and UNIX System Programming Handbook", No Starch Press, 2010