Das Geheimnis von su
« | 25 Oct 2018 | »Während in Windows ein ganzes Gestrüpp an Funktionen herangewachsen ist um
Prozesse mit anderen Rechten zu starten, hat POSIX
bzw. Linux gleich ganz auf eine API
verzichtet. Wer root
ist, darf mit setuid()
und seinen Freunden machen
was er will und sonst hat niemand (kein nicht-privilegierter Account)
was zu melden.
Wie bekommt also ein normaler User eine Funktion mit “erhöhten Rechten” ausgeführt?
Na man ruft das Programm su
oder sudo
auf … und die machen das schon
irgendwie.
Aber wie?
Es beginnt bei einer aus meiner Sicht ganz seltsamen Eigenart der Unix-Dateisysteme. Kurz gesagt, wenn eine Datei das sogenannte “SUID-Bit” gesetzt hat, ist es egal, welcher User das Programm startet, das Programm selbst läuft automatisch mit den Rechten des Eigentümers der Datei.
Also brauchen die Dateien su
und
sudo
einfach nur dem Benutzer root
gehören, dann laufen sie auch immer als root
und können wie oben beschrieben
die setuid()
APIs nutzen um sich selbst auf jeden anderen erdenklichen
Account umzusetzen und dann per exec*()
einfach das Zielprogramm starten,
und das Zielprogramm erbt dann den gewünschten Account.
So weit so gut … für tippende Administratoren. Aber wie bringen wir dieses Feature in eine Programmfunktion, mit der wir per C Code Programme mit anderen Usern starten können?
Natürlich können wir auf mit
fork()
und exec()
aufrufen, aber dann müssen wir ja als erstes das Passwort des
Zielaccounts “eintippen”.
Wer glaubt, wir können uns einfach STDIN krallen und es dort hineinschreiben,
der wird enttäuscht. So unsicher ist Linux (sind Unices) nicht.
su
liest das Passwort vom Terminal direkt und nicht über STDIN.
Wir können aber ein neues virtuelles Terminal erzeugen, und zwar mit
forkpty()
und hier erhalten wir eine Deskriptor, den wir
beschreiben können und so empfängt su
dann unser Passwort.
Wenn alles gut geht, haben wir somit eine Möglichkeit geschaffen, von einem nicht-privilegiertem Prozess aus einen anderen mit mehr Rechten zu starten, den wir teilweise per Ein- und Ausgabe fernsteuern können.
In der Praxis ist es dann schon noch ein bisschen komplizierter.
Wir sollten vor dem Aufruf von su
schon wissen, ob Account und
Passwort korrekt sind, weil wir sonst nicht unterscheiden können,
ob su
mit einem Fehler terminiert hat oder das Zielprogramm.
Die libpam
ist da recht hilfreich.
Und die eine oder andere Adaption der Environment-Variablen nach forkpty()
ist manchmal auch notwendig.
Nachtrag: sudo
habe ich noch nie für solche Zwecke eingesetzt.
Bei der Schaffung einer API analog zu
CreateProcessWithLogon
unter Windows, ist ein Passwort immer erforderlich.
Vielleicht bietet sudo
gepaart mit anderen Bibliotheken heutzutage bessere
Optionen.
Mein Wissen zu forkpty()
und Co stammt jedenfalls aus dem Quellcode zu
kdesudo und hat bisher
gut funktioniert.