LUG Erding

PAM - Pluggable Authentication Modules


Dirk Geschke, LUG-Erding


Letzte Änderung: 19.12.2024

Einleitung

Ich denke, PAM ist jedem schon einmal begegnet und wie die meisten klammert man da eine nähere Betrachtung aus. Dabei ist das einer der wichtigsten Dienste in Linux, kümmert er sich doch unter anderem um die Authentisierung von Benutzern.

Der Grund ist dabei recht einfach: Die Konfiguration ist nicht gerade intuitiv, die verwendeten Begriffe ergeben selten das, was man vermuten mag. Daher lässt man gerne die Finger davon und man vertraut darauf, dass die Distributionen das richtig handhaben.

Interessanterweise finet man in den gängigen Linuxbüchern auch sehr selten etwas zu dem Thema, das beschränkt sich meistens auf die Dateien /etc/passwd, /etc/shadow und /etc/group.

Und da sind wir schon beim eigentlichen Thema.

Historisches

Klassisch meldeten sich Benutzer am System mit dem Benutzernamen und ihrem Passwort an. Das wurde dann mit den Einträgen in /etc/passwd verglichen und wenn das Passwort stimmte, wurde man auf das System gelassen.

In der Datei /etc/passwd stand das gecryptete Passwort. Da diese Datei für jeden Benutzer lesbar sein muss, kam man auf die Idee, das Passwort woanders zu speichern. Dort haben dann nur privilegierte Programme einen Zugriff: /etc/shadow.
Benutzer konnte man in Gruppen organisieren, die Gruppen-ID GID stand in der Datei /etc/passwd und darüber wurde die Gruppe in /etc/group gefunden.

Das funktionierte problemlos und ist heute durchaus noch ein gängiges Prinzip. Wozu braucht man also PAM?

Wozu PAM?

Der Hintergrund ist recht einfach: Früher hat jedes Programm die Authentisierung selber machen können, einfach das Passwort des Users nehmen, mit dem Salt crypten und mit dem Eintrag in /etc/passwd vergleichen.

Durch die Verschiebung des Passwortes nach /etc/shadow geht das nicht mehr so einfach, da braucht der Prozess erweiterte Rechte. Beim Login ist es einfach, der Prozess läuft in aller Regel mit root-Rechten. Bei anderen Anwendungen ist es nicht gegeben, oft nicht gewollt. Das kann über PAM gelöst werden.

Es gibt aber noch mehr Aspekte, wenn man z.B. andere Authentisierungen verwenden will. Man könnte auch gegen einen LDAP-Server authentisieren oder einen zweiten Faktor oder gar Zertifikate verlangen. Das müsste jeder in seine Anwendungen, die eine Authentisierung benötigen, selber einbauen.

Durch PAM wird dafür ein zentraler Mechnanismus bereitgestellt, man überträgt das Ganze der PAM-Bibliothek und sie kümmert sich um alles. Dafür muss lediglich der Quellcode angepasst und auf PAM gesetzt werden, was die meisten Programme mittlerweile auch machen. Zudem kann über das dynamische Laden von Bibliotheken der Funktionsumfang leicht erweitert werden.

Einige Programme, wie zum Beispiel OpenSSH, besitzen auch noch die Option, auf PAM zu verzichten. Jedoch kann auch hier PAM genutzt werden.

Details

Die Komplexität kommt in erste Linie historisch ins Spiel, die dafür verwendeten Begriffe sind irritierend, die Syntax ist nicht gerade offensichtlich.

Am Anfang gab es auch nur eine Konfigurationsdatei: /etc/pam.conf Diese kann auch heute noch verwendet werden, das macht nur niemand. Stattdessen gibt es in /etc/pam.d/ einzelne Konfigurationsdateien mit dem Namen der Anwendung, die PAM nutzen will. Sofern das Verzeichnis existiert, wird die Datei /etc/pam.conf auch ignoriert. Das werde ich jetzt auch machen. Die Syntax ist analog zu den einzelnen Dateien, nur dass hier der Service noch pro Zeile vorangestellt wird. Dieser ist identisch zu dem Namen der Konfigurationsdatei in /etc/pam.d/.

Im Fall des OpenSSH-Daemons ist es /etc/pam.d/sshd, so heißt auch der laufende Prozess. Wenn es für einen Prozess keinen Eintrag gibt, dieser dennoch PAM nutzen will, so gibt es eine spezielle Datei:

   /etc/pam.d/other

Diese greift für alle anderen Programme, die keine eigene Konfiguration mitbringen. Der Aufbau ist noch recht einfach, jede Zeile besteht aus vier Bereichen:

  1. type: der Funktionstyp der Regel
  2. control: was soll bei Erfolg oder Misserfolg des Moduls passieren
  3. module: welches PAM-Modul soll verwendet werden
  4. options: Optionen für das Modul, diese sind modulspezifisch
Um schon einmal ein wenig Farbe ins Spiel zu bringen, es gibt vier Typen:

Wer nun meint, das klingt alles etwas seltsam: Das kann man noch steigern!

Das ist dann das zweite Feld ist relevant für das Ergebnis des Moduls und wie damit verfahren wird. Ein Erfolg oder Misserfolg müssen nicht unbedingt bedeuten, dass der Zugang abgelehnt wird. Die Benennung ist hier historisch und es geht natürlich auch komplexer. Das ist das, wo fast jeder mit dem Kopf schütteln mag:

Das sind die vier klassischen Werte, es gibt noch zwei weitere:

Der substack klingt ein wenig irrigiertend, das ist jedoch wohl mehr aus historischen Gründen definiert. Auf meinen Systemen wird es nicht verwendet.

Und um die Komplexität noch zu steigern, gibt es noch eine Form:

   [value1=action1 value2=action2 ...]

Dabei bezieht sich value auf den Rückgabewert des Moduls und action definiert, was dann getan werden soll. Es ist also keine Zuordnung, das ist angesichts des Gleichheitszeichens etwas irritierend.

Die Liste der möglichen values ist lang und steht in der Datei:

   /usr/include/security/_pam_types.h

Die meistgenutzten sind success und default, wobei letzteres für alles andere steht, was vorher nicht explizit gelistet wurde.

Als Aktionen kommen diese Möglichkeiten in Betracht:

Über diese komplexen Aktionen lassen sich die einfachen von oben auch definieren, das macht es vielleicht etwas deutlicher:

Das new_authtok_reqd bedeutet laut _pam_types.h:

   New authentication token required. This is normally returned if the
   machine security policies require that the password should be changed
   because the password is NULL or it has aged 

Das nächste ist nun das eigentliche Modul, welche es auf dem lokalen System gibt, kann man über das Kommando

   man -s 8 -k pam_

anzeigen lassen.

Damit sind wir auch beim letzten Feld, das sind die Optionen für das Modul, die für jedes Modul anders sein können. Diese sind dann in der manual page zu finden.

Um es ein wenig mit Farbe zu füllen, das hier ist aus meiner Datei common-auth, sie wird fast überall per

   @include common-auth

eingebunden. Warum das @-Zeichen am Anfang seht, weiß ich jedoch nicht. Sie sieht bei mir so aus:

  auth    [success=1 default=ignore]      pam_unix.so nullok
  auth    requisite                       pam_deny.so
  auth    required                        pam_permit.so

Der erste Eintrag verwendet pam_unix.so mit der Option nullok. Das ist die klassische Unix-Passwortabfrage. Das nullok erlaubt auch ein leeres Passwort. Wenn dieses Modul erfolgreich war, also ein success liefert. dann wird die nächste Zeile übersprungen. Diese würde den Zugang komplett ablehnen:

  auth    requisite                       pam_deny.so

Bei Erfolg landen wir also bei:

  auth    required                        pam_permit.so

Das erlaubt dann den Zugang zum System.

Eine Besonderheit bei der Syntax gibt es noch: Wenn eine Zeile mit einem Minus-Zeichen beginnt, so wird diese einfach ignoriert, falls das angegebene Modul gar nicht installiert ist. Das erleichtert es eine passende PAM-Datei zu erstellen.

Nach einer klassischen Passwortauthentisierung könnte man bei einem Fehler auch z.B. noch ein pam_ldap.so aufrufen und über die Option try_first_pass dasselbe Passwort für die LDAP-Authentisierung nutzen, wie für pam_unix.so. So braucht der Benutzer das Passwort nicht erneut eingeben.

Fazit

Im Grunde ist PAM gar nicht so schwierig, die Syntax ist jedoch nicht gerade leicht zu interpretieren und die Vielzahl der Optionen bei dem Modulen macht es auch nicht einfacher. Glücklicherweise sind diese alle in den man-Pages gelistet und erklärt.

Besonders schwierig ist es, zu verstehen, wie die Ergebnisse der einzelnen Module verarbeitet werden, welches relevant ist und welches doch nur optional oder bei Bedarf ingoriert wird. Dafür ist dann die man-Page von pam.conf eine sehr gute Quelle für diese Informationen.


Dirk Geschke, dirk@lug-erding.de