6.2 Die Datei /etc/shadow
Wie bereits erwähnt, speicherten ältere Linux-/UNIX-Versionen die Passwörter noch direkt in der Datei /etc/passwd im zweiten Feld. Hatte ein Anwender Zugriff auf diese Datei, wurde häufig ein so genannter Wörterbuchangriff gemacht, um dieses Passwort zu entschlüsseln. Hierzu wurde dazumal das Kommando crypt zum Entschlüsseln verwendet. Hatte der Anwender dabei ein einfaches Passwort aus Buchstaben verwendet, war es in der Tat möglich, das Passwort zu knacken. Hierauf soll aber nicht näher eingegangen werden.
Fakt ist, dass eben eine Angriffsfläche vorhanden war, die mit der Passwortdatei /etc/shadow beseitigt wurde. Da der Eigentümer und die Gruppe dieser Datei root ist, ist diese auch nicht mehr zum Lesen zu öffnen. Der Eintrag in die Datei erfolgt nach demselben Schema wie schon in der Datei /etc/passwd. Ein Eintrag steht jeweils in einer Zeile, und die einzelnen Felder wiederum werden durch einen Doppelpunkt getrennt. Die Syntax:
Name:Pass:DOC:MinTag:MaxTag:Warn:DeaktivIn:DeaktivSeit:Frei
|
Name – Der Benutzername, der im ersten Feld steht, verknüpft diesen Eintrag mit einem gleichnamigen Eintrag in der Datei /etc/passwd. Somit sollten für alle Einträge in der Datei /etc/passwd auch Einträge in der Datei /etc/shadow existieren. |
|
Pass – Hier steht das verschlüsselte Passwort des Benutzers. Befindet sich hier ein »*« oder »!«, bedeutet dies, dass kein Passwort eingetragen ist – was aus Gründen der Datensicherheit vermieden werden sollte. |
|
DOC (Day of last change) – Hier steht der Tag, an dem das Datum seit dem 1.1.1970 verändert wurde. Dieser Tag (eine Integer-Zahl) wird zur Überprüfung der Lebensdauer von Passwörtern verwendet. |
|
MinTag – Der frühestmögliche Zeitpunkt (in Tagen), wann der Benutzer sein Passwort ändern kann. |
|
MaxTag – Der spätmöglichste Termin (in Tagen), wann der Benutzer sein Passwort ändern muss. |
|
Warn – Hier wird eingetragen, wie viele Tage vor Ablauf des Passwortes (siehe »MaxTage«) der Benutzer eine Warnung bei der Anmeldung auf dem Bildschirm angezeigt bekommt. |
|
Deaktiv – Hier wird eingetragen, wie viele Tag das Passwort nach Ablauf von »MaxTage« noch gülig ist. Danach kann sich der Benutzer nicht mehr anmelden. |
|
DeaktivSeit – Hier findet man die Anzahl von Tagen, seit ein Passwort gesperrt wurde (deaktiv ist). |
|
Frei – Ein freies Feld für eventuell spätere Erweiterungen. Hat jetzt noch keine Bedeutung. |
Hierzu ein einfaches Beispiel aus meiner /etc/shadow-Datei:
john:$1$oLLnoOfp$MQch.ycZRIzUZ0JYnaXRB/:12952:0:99999:7:::
Der Benutzer heißt hier »john«, das verschlüsselte Passwort lautet »$1$oLLnoOfp$MQch.ycZRIzUZ0JYnaXRB/«, das Passwort wurde am 12952. Tag nach 1.1.1970 das letzte Mal verändert und kann frühestens in 0 Tagen (also heute) und muss spätestens in 99999 Tagen (ca. 274 Jahren) geändert werden – in dem Fall also immer. 14 Tage (sofern Sie diesen Tag überleben) vor Ablauf des Passwortes soll eine Warnung ausgegeben werden. Die anderen Werte wurden vom Administrator nicht definiert und sind leer.
6.2.1 Die Datei /etc/shadow auswerten
Die einzelnen Felder der Datei /etc/shadow sind in der Struktur struct spwd enthalten und in der Headerdatei <shadow.h> definiert.
Tabelle 6.2
Die Struktur spwd in der Headerdatei <shadow.h>
Strukturvariable
|
Bedeutung
|
char *sp_namp
|
Benutzername (Loginname)
|
char *sp_pwdp
|
Verschlüsseltes Passwort
|
long int sp_lstchg
|
Datum der letzten Änderung (seit dem 1.1.1970)
|
long int sp_min
|
Nächste Änderung frühestens möglich in sp_min Tagen
|
long int sp_max
|
Spätestens muss eine Änderung gemacht werden in sp_max Tagen.
|
long int sp_warn
|
Anzahl Tage, die der Benutzer gewarnt werden soll, bevor das Passwort abläuft.
|
long int sp_inact
|
Anzahl nach sp_max, wenn der Account deaktiviert wird
|
long int sp_expire
|
Anzahl Tage nach dem 1.1.1970, seit der Account deaktiviert bzw. gesperrt wurde
|
unsigned long int sp_flag
|
Wird noch nicht verwendet.
|
6.2.2 getspent, setspent und endspent – komplette Abfrage von
/etc/shadow
Wie auch schon bei der Datei /etc/passwd stehen Ihnen bei der Datei /etc/shadow entsprechende Funktionen zur Verfügung, wo Sie alle Daten in /etc/shadow erfragen können.
#include <shadow.h>
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
Die Funktion getspent() liefert einen Zeiger auf die Struktur spwd zurück, die den Inhalt einer Zeile in /etc/shadow beinhaltet. Auch hier wird beim ersten Aufruf immer der erste Eintrag der Datei /etc/shadow zurückgeliefert. Jeder weitere Aufruf von getspent() holt sich immer die nächste Zeile der Datei /etc/shadow. Nach jedem Aufruf von getspent() wird der alte Inhalt in der Struktur von neuem überschrieben. Der Rückgabewert ist bei einem Fehler oder dem Ende der Datei /etc/shadow NULL.
Wollen Sie sichergehen, dass getspent() wirklich vom Anfang der Datei /etc/shadow beginnt, müssen Sie die Funktion setspent() aufrufen. Hiermit wird der Lesezeiger auf den Anfang der Datei /etc/shadow gesetzt.
Wenn Sie mit getspent() fertig sind, können Sie mit endspent() die Datei /etc/shadow wieder schließen. Dies garantiert Ihnen bei einem erneuten Aufruf von getspent(), dass /etc/shadow wieder neu geöffnet wird und sich der Dateizeiger am Anfang dieser Datei befindet.
Hierzu ein einfaches Beispiel von getspent() und endspent(). Sie geben über der Kommandozeile einen entsprechenden Benutzer ein, wovon Sie die Passwortinformationen aus der /etc/shadow-Datei ermitteln können. Da /etc/shadow eine Datei ist, die nur vom Benutzer und der Gruppe root gelesen werden kann, benötigen Sie für dieses Programm auch die entsprechende Superuser-Rechte.
/* read_shadow.c */
/* Programm benötigt Superuser-Rechte */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <shadow.h>
#include <string.h>
#include <time.h>
struct spwd *getshadow(const char *loginname) {
struct spwd *shadow_ptr;
while( shadow_ptr=getspent() ) {
if( strcmp(shadow_ptr->sp_namp, loginname) == 0) {
endspent();
return shadow_ptr;
}
}
endspent();
return NULL;
}
int main(int argc, char *argv[]) {
struct spwd *shadow_ptr;
if( argc != 2 ) {
fprintf(stderr, "Usage: %s Name\n", argv[0]);
exit (EXIT_FAILURE);
}
shadow_ptr=getshadow(argv[1]);
if(shadow_ptr == NULL) {
printf("Konnte nichts über %s ermitteln\n", argv[1]);
exit (EXIT_FAILURE);
}
printf("Folgendes wurde zu %s ermittelt: \n", argv[1]);
printf("Benutzername : %s\n",
shadow_ptr->sp_namp);
printf("Passwort (verschlüsselt) : %d\n",
shadow_ptr->sp_pwdp);
printf("Tag der letzten Änderung : %li Tage\n",
shadow_ptr->sp_lstchg);
printf("Nächste Änderung möglich : %li Tage\n",
shadow_ptr->sp_min);
printf("Nächste Änderung fällig : %li Tage\n",
shadow_ptr->sp_max);
printf("Warnung, wenn Passwort fällig %li Tage zuvor!\n",
shadow_ptr->sp_warn);
printf("Konto nach %li Tagen vor Ablauf der Frist sperren!\n",
shadow_ptr->sp_inact);
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
you@host > sudo ./read_shadow tot
Passwort:*******
Folgendes wurde zu tot ermittelt:
Benutzername : tot
Passwort (verschlüsselt) : 134520844
Tag der letzten Änderung : 12928 Tage
Nächste Änderung möglich : 0 Tage
Nächste Änderung fällig : 99999 Tage
Warnung, wenn Passwort fällig 7 Tage zuvor!
Konto nach -1 Tagen vor Ablauf der Frist sperren!
Eine Ausgabe von -1 wie in der letzten Zeile zeigt an, dass dieser Wert nicht verwendet wird.
Natürlich gibt es auch eine entsprechende Funktion wie getpwnam() bei /etc/passwd für /etc/shadow mit getspnam(). Die Syntax:
#include <shadow.h>
struct spwd *getspnam(const char *name);
Die Funktion getspnam() gibt ebenfalls einen Zeiger auf eine Struktur spwd zurück, die den Inhalt einer Zeile in /etc/shadow mit dem Benutzernamen name enthält. Ansonsten bei einem Fehler oder wenn der Name nicht gefunden werden konnte, gibt diese Funktion NULL zurück. Hierzu das Beispiel zu getspnam():
/* read_shadow2.c */
/* Programm benötigt Superuser-Rechte */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <shadow.h>
#include <string.h>
#include <time.h>
int main(int argc, char *argv[]) {
struct spwd *shadow_ptr;
if( argc != 2 ) {
fprintf(stderr, "Usage: %s Name\n", argv[0]);
exit (EXIT_FAILURE);
}
shadow_ptr=getspnam(argv[1]);
if(shadow_ptr == NULL) {
printf("Konnte nichts über %s ermitteln\n", argv[1]);
exit (EXIT_FAILURE);
}
printf("Folgendes wurde zu %s ermittelt: \n", argv[1]);
printf("Benutzername : %s\n",
shadow_ptr->sp_namp);
printf("Passwort (verschlüsselt) : %d\n",
shadow_ptr->sp_pwdp);
printf("Tag der letzten Änderung : %li Tage\n",
shadow_ptr->sp_lstchg);
printf("Nächste Änderung möglich : %li Tage\n",
shadow_ptr->sp_min);
printf("Nächste Änderung fällig : %li Tage\n",
shadow_ptr->sp_max);
printf("Warnung, wenn Passwort fällig %li Tage zuvor!\n",
shadow_ptr->sp_warn);
printf("Konto nach %li Tagen vor Ablauf der Frist sperren!\n",
shadow_ptr->sp_inact);
return EXIT_SUCCESS;
}
Bei der Ausführung entspricht das Programme exakt dem von »read_shadow.c«. Natürlich benötigen Sie auch hierzu wieder die Superuser-Rechte, um die Datei /etc/shadow lesen zu können.
Hinweis Neben den hier vorgestellten Funktionen bietet die <shadow.h >-API noch einige Funktionen mehr an. Ein Aufruf von z. B. »man getspent« bringt die weiteren Routinen zum Vorschein.
|
|