Postfix Security Advisory - 14. August 2008

Wietse Venema


Inhaltsverzeichnis

1. Postfix-local-privilege-Eskalation über hartgelinkte Symlinks
2. Welche Konfigurationen (nicht) betroffen sind
3. Lösung
4. Gegenmaßnahmen
A. Vorgehen, um herauszufinden, ob ein System betroffen ist
B. Vorgehen zum Schützen der Mailbox-Dateien
C. Quellcode-Patch

Zusammenfassung

Die Dateisystem-Semantik der Solaris- und Linux-Dateisysteme hat sich geändert. Dadurch ist eine der Grundannahmen für das Postfix-Mailsystem nicht mehr korrekt.

Postfix ist auf Systemen mit standardkonformen Verhalten (POSIX, X/Open) nicht betroffen, wie z.B. *BSD, AIX, Mac OS, HP-UX und alte Sun/Linux Systeme. Korrektur und Workarounds bei anderen Systemen sind einfach.

[Anmerkung]Anmerkung

Es gibt Bestrebungen, das vom Standard abweichende Verhalten (es wird im Anschluss weiter unten beschrieben) jetzt zu einem eigenem Standard zu erheben. Laut Protokoll eines „X/Open Standards Meeting“ vom Juni 2008 gibt es dort einen Vorschlag für einen neuen Syscall (llink).

1. Postfix-local-privilege-Eskalation über hartgelinkte Symlinks

Sebastian Krahmer von SuSE hat ein Problem gefunden, das durch Erhöhung von Rechten (privilege escalation) verursacht wird. Auf manchen Systemen kann ein Angreifer auf diesem Weg einen root gehörenden Symlink durch einen Hardlink z.B. nach /var/mail oder /var/spool/mail legen und so Postfix dazu bringen, E-Mail an bestehende Dateien anzuhängen. Dies ist auf jenen Systemen mit dem spezifischen, vom Standard abweichenden Verhalten möglich.

Symlinks (symbolische Links) stellen Aliase für UNIX-Pfadnamen zur Verfügung. Sie wurden 1983 mit 4.2BSD UNIX eingeführt und mit der Zeit von anderen Systemen übernommen. Hardlinks sind älter und stellen den primären Mechanismus zum Zugriff auf Dateisystemobjekte dar.

Auf manchen UNIX-Systemen hat sich die link(symlink, newpath)-Operation mit der Zeit verändert: Anstatt dem Symlink rekursiv zu folgen und dann einen Hardlink auf die so gefundene Datei zeigen zu lassen, wird nun der Hardlink direkt auf den Softlink gesetzt.

Dieses Verhalten weicht z.B. von den POSIX.1-2001- und X/Open XPG4v2-Standards ab, und es ist auf aktuellen Solaris-, IRIX- und Linux-Systemen das voreingestellte Verhalten. Auf Systemen, die auf die beschriebene Weise vom Standard abweichen, kann Postfix verwundbar sein - je nachdem wie Postfix konfiguriert wurde.

Postfix gestattet einen Symlink in Besitz von root als Ziel für lokale Mailzustellung, damit eine E-Mail z.B. nach /dev/null zugestellt werden kann (/dev/null ist ein Symlink unter Solaris).

2. Welche Konfigurationen (nicht) betroffen sind

Eine Konfiguration gilt als betroffen, wenn ein Angreifer mit lokalem Systemzugriff Postfix dazu bringen kann, E-Mail an eine bereits bestehende Datei eines anderen Benutzers anzuhängen. Anhang A, Vorgehen, um herauszufinden, ob ein System betroffen ist beschreibt eine Prozedur mit deren Hilfe bestimmt werden kann, ob ein System betroffen ist.

Die folgenden Konfigurationen sind NICHT betroffen: Postfix auf FreeBSD 7.0, OpenBSD 4.3, NetBSD 4.0, Mac OS X 10.5, AIX 5.3, HP-UX 11.11, Solaris 1.x, Linux mit Kernel Version 1.2.13 und andere Systeme mit dem standardkonformen Verhalten für die Erzeugung von Hardlinks.

[Achtung]Achtung

Sie können aber DENNOCH betroffen sein, wenn sie Dateisysteme mit anderen Maschinen teilen, deren Benutzer Hardlinks auf Symlinks erzeugen können.

Ebenso sind folgende Konfigurationen nicht betroffen:

  • Maildir-Zustellung durch Postfixs local oder virtual delivery agent

  • Mailzustellung durch externe, nicht zu Postfix gehörende Programme (z.B. cyrus-deliver, dovecot-deliver, procmail, maildrop, usw.)

  • Mailbox-Zustellung (mbox-Format) mit dem Postfix virtual delivery agent wenn die Verzeichnisse oberhalb der Mailboxen der zugehörigen Gruppe oder Anderen kein Schreibrecht einräumen.

Folgenden Konfigurationen sind betroffen: Linux mit Kernel Version >= 2.0, Solaris >= 2.0, OpenSolaris 11-2008.5, IRIX 6.5 und andere Systeme, auf denen User Hardlinks auf Symlinks erzeugen können:

  • Mailbox-Zustellung (mbox-Format) mit dem Postfix local delivery agent

  • Mailbox-Zustellung (mbox-Format) mit dem Postfix virtual delivery agent, wenn die Verzeichnisse oberhalb der Mailboxen der zugehörigen Gruppe oder Anderen das Schreiben gestatten.

3. Lösung

Wenn Ihr System betroffen ist, erneuern Sie entweder Ihre Postfix-Installation oder applizieren Sie den Patch aus Anhang C, Quellcode-Patch, oder ergreifen Sie eine der Gegenmaßnahmen aus dem folgenden Abschnitt Abschnitt 4, „Gegenmaßnahmen“.

Fehlerbereinigte Versionen werden auf http://www.postfix.org für die Postfix-Versionen 2.3, 2.4, 2.5 und 2.6 bereitgestellt. Individuelle Distributoren geben Updates entsprechend ihrer Supportpolicy heraus.

4. Gegenmaßnahmen

Jede der folgenden Gegenmaßnahmen verhindert die Erhöhung der Rechte durch Postfix über hardgelinkte Symlinks:

  1. Schützen Sie mbox-Mailboxen (Maildir-Mailboxen sind nicht betroffen). Das Skript aus Anhang B, Vorgehen zum Schützen der Mailbox-Dateien stellt sicher, dass das mail-spool-Verzeichnis des Systems dem Benutzer root gehört, dass das sticky-Bit gesetzt ist und dass jedes UNIX-Systemkonto über eine mbox-Mailbox verfügt. Es hat auch Vorschläge für die Zustellung in mbox-Mailboxen durch den Postfix virtual delivery agent (auch hier gilt: Maildir-Mailboxen sind nicht betroffen).

  2. Verbieten Sie Nicht-root Benutzern das Erstellen von Hardlinks auf Objekte anderer User. Dieses Verhalten ist auf einigen Systemen konfigurierbar.

A. Vorgehen, um herauszufinden, ob ein System betroffen ist

Wie bereits im Abschnitt Abschnitt 2, „Welche Konfigurationen (nicht) betroffen sind“ erwähnt, sind Maildir-Mailboxen die durch den Postfix local oder virtual delivery agent verwaltet werden nicht betroffen. Nicht betroffen sind ebenfalls Zustellprogramme, die nicht zu Postfix gehören, sowie mbox-Mailboxen, die mit dem Postfix virtual delivery agent verwaltet werden, wenn die Verzeichnisse oberhalb der Mailboxen ihren zugehörigen Gruppen oder Anderen keine Schreibrechte einräumen.

Um herauszufinden, ob ein System betroffen ist, führen Sie bitte die folgenden Befehle nacheinander und nicht als Benutzer root auf einem lokalen Dateisystem aus:

$ PATH=/bin:/usr/bin:$PATH
$ mkdir test
$ cd test
$ touch src
$ ln -s src dst1
$ ln dst1 dst2
$ ls -l
[Anmerkung]Anmerkung

Damit der Test gültig ist, müssen alle Befehle ohne Fehler ausgeführt werden.

Das System ist NICHT betroffen, wenn die Ausgabe von ls -l einen Symlink (dst1 -> src) und zwei Dateien (dst2, src) wie in Beispiel A.1, „Ein nicht betroffenes System“ zeigt.

Beispiel A.1. Ein nicht betroffenes System

$ ls -l
lrwxr-xr-x  1 user users 3 Mmm dd hh:mm dst1 -> src
-rw-r--r--  2 user users 0 Mmm dd hh:mm dst2
-rw-r--r--  2 user users 0 Mmm dd hh:mm src

[Achtung]Achtung

Dennoch können auch diese Systeme betroffen sein, wenn sie Dateisysteme mit Maschinen teilen, auf denen die Benutzer Hardlinks auf Symlinks erzeugen können.

Das System ist betroffen, wenn die Ausgabe von ls -l zwei Symlinks und eine Datei zeigt (siehe: Beispiel A.2, „Ein betroffenes System“) und folgende Postfix-Konfigurationen gegeben sind:

  • Zustellung in eine mbox-Mailbox mit dem Postfix local delivery agent

  • Zustellung in eine Maildir-Mailbox mit dem Postfix virtual delivery agent und die Verzeichnisse oberhalb der Mailbox gestatten ihrer zugehörigen Gruppe oder Anderen das Schreiben.

Beispiel A.2. Ein betroffenes System

$ ls -l
lrwxrwxrwx  2 user users 3 Mmm dd hh:mm dst1 -> src
lrwxrwxrwx  2 user users 3 Mmm dd hh:mm dst2 -> src
-rw-r--r--  1 user users 0 Mmm dd hh:mm src

B. Vorgehen zum Schützen der Mailbox-Dateien

Dieser Abschnitt beschreibt eine der Gegenmaßnahmen (siehe Abschnitt 4, „Gegenmaßnahmen“), die das Problem eliminieren, ohne dass ein Update von Postfix notwendig ist.

Das unten angeführte Perl-Skript härtet Systeme mit mbox-Mailboxen, die den Postfix local delivery agent verwenden. Es stellt sicher, dass das mail-spool-Verzeichnis root gehört, dass das sticky-Bit gesetzt ist und das jedes UNIX-Systemkonto über eine mbox-Mailbox verfügt. Das Skript geht davon aus, dass die Mailbox-Dateien unterhalb von /var/mail abgelegt sind.

Eine ähnliche Vorgehensweise ist notwendig für Systeme, die eine Mailbox Zustellung mit dem virtual delivery agent benutzen, wenn die Verzeichnisse oberhalb der Mailboxen Gruppen- oder andere Schreibrechte haben. Ein automatisiertes Skript ist hier leider nicht möglich, weil die Postfix-Konfigurationen hierfür zu stark variieren.

#!/usr/bin/perl

# fix-mailspool - Make sure the mailspool directory is root-owned
# and sticky, and that every UNIX account has a mailbox file.

use Fcntl;

$debug = 0;

# Follow compatibility symlink.
$mailspool="/var/mail/";

chown(0, -1, $mailspool)
    || die("can't set root ownership for $mailspool: $!\n");

chmod((stat($mailspool))[2] | 01000, $mailspool)
    || die("can't set sticky bit for $mailspool: $!\n");

while(($name, $passwd, $uid, $gid, $quota,
    $comment, $gcos, $dir, $shell) = getpwent()) {
    print "user $name\n" if $debug;
    $mailbox = ($mailspool . $name);
    if (! -e $mailbox) {
        print "create $mailbox\n" if $debug;
        if (!sysopen(MAILBOX, $mailbox, (O_CREAT | O_RDWR | O_EXCL), 0600)) {
            warn("can't create $mailbox: $!\n");
        } else {
            # XXX fchown() is not portable.
            chown($uid, $gid, $mailbox) || warn("chown $mailbox: $!\n");
            close(MAILBOX);
        }
    } elsif (! -f $mailbox) {
        warn("$mailbox is not a regular file\n");
    } elsif ((stat($mailbox))[4] != $uid) {
        warn("$mailbox is not owned by $name\n");
    }
}

C. Quellcode-Patch

Dieser Patch ist für Postfix 2.0 und spätere Versionen. Er ist minimal-invasiv, um nur das Problem zu beheben. Künftige Postfix-Versionen können möglicherweise andere Wege gehen, um das Problem zu vermeiden.

Die Lösung wird durch folgende Änderung erreicht: Wenn das Ziel einer E-Mail-Zustellung ein Symlink ist, dann darf das Verzeichnis oberhalb des Symlinks nach dem Patch nur noch von root schreibbar sein (zusätzlich zu der bereits existierenden Beschränkung, dass der Symlink root gehören muss).

[Wichtig]Wichtig

Diese Änderung wird bisher einwandfrei funktionierende Konfigurationen unbrauchbar machen, wenn E-Mail an einen Symlink zugestellt werden soll, der sich unterhalb eines Verzeichnisses mit wenig restriktiven Rechten befindet!

*** src/util/safe_open.c.orig   Sun Jun  4 19:04:49 2006
--- src/util/safe_open.c        Mon Aug  4 16:47:18 2008
***************
*** 83,88 ****
--- 83,89 ----
  #include <msg.h>
  #include <vstream.h>
  #include <vstring.h>
+ #include <stringops.h>
  #include <safe_open.h>

  /* safe_open_exist - open existing file */
***************
*** 138,150 ****
       * for symlinks owned by root. NEVER, NEVER, make exceptions for symlinks
       * owned by a non-root user. This would open a security hole when
       * delivering mail to a world-writable mailbox directory.
       */
      else if (lstat(path, &lstat_st) < 0) {
        vstring_sprintf(why, "file status changed unexpectedly: %m");
        errno = EPERM;
      } else if (S_ISLNK(lstat_st.st_mode)) {
!       if (lstat_st.st_uid == 0)
!           return (fp);
        vstring_sprintf(why, "file is a symbolic link");
        errno = EPERM;
      } else if (fstat_st->st_dev != lstat_st.st_dev
--- 139,167 ----
       * for symlinks owned by root. NEVER, NEVER, make exceptions for symlinks
       * owned by a non-root user. This would open a security hole when
       * delivering mail to a world-writable mailbox directory.
+      *
+      * Sebastian Krahmer of SuSE brought to my attention that some systems have
+      * changed their semantics of link(symlink, newpath), such that the
+      * result is a hardlink to the symlink. For this reason, we now also
+      * require that the symlink's parent directory is writable only by root.
       */
      else if (lstat(path, &lstat_st) < 0) {
        vstring_sprintf(why, "file status changed unexpectedly: %m");
        errno = EPERM;
      } else if (S_ISLNK(lstat_st.st_mode)) {
!       if (lstat_st.st_uid == 0) {
!           VSTRING *parent_buf = vstring_alloc(100);
!           const char *parent_path = sane_dirname(parent_buf, path);
!           struct stat parent_st;
!           int     parent_ok;
!
!           parent_ok = (stat(parent_path, &parent_st) == 0     /* not lstat */
!                        && parent_st.st_uid == 0
!                        && (parent_st.st_mode & (S_IWGRP | S_IWOTH)) == 0);
!           vstring_free(parent_buf);
!           if (parent_ok)
!               return (fp);
!       }
        vstring_sprintf(why, "file is a symbolic link");
        errno = EPERM;
      } else if (fstat_st->st_dev != lstat_st.st_dev