Zum Inhalt springen

Linux/SELinux/Praxis/3: Unterschied zwischen den Versionen

Aus Foxwiki
Die Seite wurde neu angelegt: „ Kategorie:Linux/SELinux/Praxis
 
Keine Bearbeitungszusammenfassung
Zeile 2: Zeile 2:


[[Kategorie:Linux/SELinux/Praxis]]
[[Kategorie:Linux/SELinux/Praxis]]
= SCHRITT 3.1: Der Prozess als SELinux-Subjekt =
Bis zu diesem Punkt wurden Dateien und Verzeichnisse betrachtet. In der Terminologie von SELinux sind all diese Elemente '''Objekte (Objects)'''. Sie sind passiv. Eine Datei kann sich nicht selbst lesen oder löschen.
Im klassischen Linux hängen die Rechte eines Prozesses davon ab, unter welcher Benutzerkennung er läuft (UID/GID). In SELinux erhält ein '''Prozess beim Start seinen eigenen Sicherheitskontext'''. Dieser Kontext bestimmt '''vollständig''', was der Prozess im System tun darf – unabhängig davon, ob es sich um einen Root-Prozess oder um einen gewöhnlichen Benutzerprozess handelt.
* Um den Kontext laufender Prozesse anzuzeigen, wird bei <code>ps</code> die Option <code>-Z</code> verwendet:
<code>ps -eZ | grep sshd</code>
Ausgabe:
<code># ps -Z
LABEL                              PID TTY          TIME CMD
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 90819 pts/1 00:00:00 sudo
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 90820 pts/1 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 91782 pts/1 00:00:00 ps</code>
== Prinzipien der SELinux-Arbeitsweise bei Prozessen ==
Die Grundlage von SELinux ist der Mechanismus der '''zwingenden Zugriffskontrolle auf Basis von Typen''' – '''Type Enforcement (TE)'''. In diesem Modell wird jedem Element im System ein bestimmter Bezeichner zugewiesen, der als Typ (type) bezeichnet wird.
* Bei passiven Entitäten (Dateien, Verzeichnisse, Sockets) wird diese Kennzeichnung einfach als '''Typ''' bezeichnet (file context / type). Bei einer Datei beschreibt das Feld <code>type</code> den '''Objekttyp'''.
* Bei aktiven Entitäten (Prozessen) wird dieselbe Kennzeichnung historisch und konzeptionell als '''Domäne''' (domain) bezeichnet. Beim Prozess beschreibt das Feld <code>type</code> die '''Domäne des Subjekts'''.
Wenn ein Prozess versucht, eine Aktion auszuführen, bewertet SELinux die Anfrage anhand der Gesamtheit der Attribute von Subjekt und Objekt. Die klassischen UNIX-/DAC-Prüfungen erfolgen dabei '''zuerst''': Wenn DAC die Operation bereits verweigert (zum Beispiel weil dem Benutzer die herkömmlichen Leserechte fehlen), kommt SELinux gar nicht mehr zum Zug, und es kann auch keinen AVC-Eintrag geben.
Wenn keine DAC-Hindernisse vorliegen, vergleicht SELinux den Prozesskontext und den Objektkontext mit der geladenen Policy. Die Grundlogik lautet '''default deny''': Zugriff wird nur dann erlaubt, wenn eine Regel existiert, die die benötigte Interaktion explizit zulässt.
=== Algorithmus der Zugriffsprüfung nach DAC im Type Enforcement: ===
# '''Identifikation des Subjekts:''' Es wird die '''Domäne''' des anfragenden Prozesses bestimmt (zum Beispiel ein Webserver in der Domäne <code>httpd_t</code>).
# '''Identifikation des Objekts:''' Es wird der '''Typ der Zielressource''' bestimmt (zum Beispiel eine Datei mit dem Typ <code>httpd_sys_content_t</code>).
# '''Bestimmung von Klasse und Aktion:''' Es werden die '''Objektklasse''' (<code>file</code>) und die '''angeforderte Aktion''' (<code>read</code>) festgelegt.
# '''Prüfung der Policy (Regelsuche):''' Der Kernel greift auf den '''Access Vector Cache (AVC)''' oder auf die '''geladene Policy''' zu, um nach einer erlaubenden Regel zu suchen.
= SCHRITT 3.2: Policy und Zugriffsregeln. Das Werkzeug <code>sesearch</code> =
Wie bereits besprochen, wird im TE-Modell ein Prozess als Subjekt betrachtet, das in einer bestimmten '''Domäne''' arbeitet, während ein Objekt als Entität mit einem bestimmten '''Typ''' gilt. Für SELinux ist dies die zentrale Grundlage der Entscheidungsfindung: Die Policy legt fest, '''welche Domänen auf welche Typen''' zugreifen dürfen, auf Objekte welcher Klassen und mit welchen Aktionen (permissions).
Eine TE-Regel hat die folgende Form:
<code>allow source_type target_type:object_class { permission };</code>
* <code>source_type</code>
** Dies ist der Typ des Subjekts. Bei einem Prozess ist das seine '''Domäne'''. Wenn ein Prozess in <code>httpd_t</code>, <code>sshd_t</code> oder <code>passwd_t</code> läuft, steht genau dieser Wert auf der linken Seite der TE-Prüfung.
* <code>target_type</code>
** Dies ist der '''Typ des Objekts''', mit dem das Subjekt interagiert: zum Beispiel <code>httpd_sys_content_t</code>, <code>tmp_t</code>, <code>shadow_t</code>, <code>user_home_t</code>. Derselbe Prozess kann mit unterschiedlichen Objekten arbeiten, und die Prüfung wird für jedes Paar aus Domäne und Typ separat durchgeführt.
* <code>object_class</code>
** SELinux gruppiert Objekte in Klassen.
** Bei einer Datei und einem Verzeichnis können sich die Berechtigungen beispielsweise unterscheiden. Daneben existieren auch Klassen wie <code>filesystem</code>, <code>tcp_socket</code>, <code>netif</code>, <code>node</code> und weitere. Jede Klasse besitzt ihre eigene Menge möglicher Permissions.
* <code>permission</code>
** Dies ist die konkrete Aktion, die erlaubt werden muss: <code>read</code>, <code>write</code>, <code>open</code>, <code>search</code>, <code>getattr</code>, <code>create</code>, <code>append</code>, <code>rename</code> usw.
----
== Zugriff gewähren ==
Der Ablauf zur Gewährung eines Zugriffs auf ein Objekt sieht grundsätzlich wie folgt aus:
* einen bereits vorhandenen '''geeigneten Typ''' für das Objekt auswählen
* ein '''Boolean''' aktivieren*, das die benötigten konditionalen Regeln einschaltet
* oder die Policy erweitern bzw. ändern, falls die Standard-Policy eine solche Interaktion nicht vorsieht (weil die erforderlichen Typen oder Booleans nicht existieren)
<nowiki>*</nowiki> '''Boolean''' ist ein logischer Schalter für bereits in der Policy vorhandene Regelsätze. Ausführlicher wird dieses Konzept in den folgenden Lektionen zu SELinux Booleans behandelt.
----
== Arbeit mit Regeln mithilfe von <code>sesearch</code> ==
<code>sesearch</code> ist ein Werkzeug aus dem SETools-Paket zur Suche nach Regeln in der SELinux-Policy.
=== Verwendung ===
<code>sesearch -A -s <SOURCE_TYPE> -t <TARGET_TYPE> -c <CLASS> -p <PERM></code>
{| class="wikitable"
!'''Parameter'''
!'''Bedeutung'''
|-
|<code>-A</code>
|<code>allow</code>- und <code>allowxperm</code>-Regeln suchen
|-
|<code>-s</code> / <code>--source</code>
|source type oder source attribute*
|-
|<code>-t</code> / <code>--target</code>
|target type oder target attribute*
|-
|<code>-c</code> / <code>--class</code>
|Objektklasse
|-
|<code>-p</code> / <code>--perm</code>
|permission oder Liste von permissions
|}
<nowiki>*</nowiki> ''Type Attributes. Nicht mit Dateiattributen verwechseln.''
* Das folgende Beispiel kann direkt im Terminal ausgeführt werden:
<code>sesearch -A -s unconfined_t -t tmp_t -c file</code>
Ausgabe:
<code># sesearch -A -s unconfined_t -t tmp_t -c file
allow files_unconfined_type file_type:file execmod; [ allow_execmod ]:True
allow files_unconfined_type file_type:file { append create execute execute_no_trans getattr ioctl link lock map mounton open quotaon read relabelfrom relabelto rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write };</code>
Hier ist das Ergebnis einer Suche nach erlaubenden Regeln (<code>allow</code>) für ein Subjekt in der Domäne <code>unconfined_t</code> beim Zugriff auf Objekte des Typs <code>tmp_t</code> zu sehen, eingeschränkt auf die Objektklasse <code>file</code>.
* '''Type Attributes.''' Statt konkreter Typen erscheinen hier <code>files_unconfined_type</code> und <code>file_type</code>. Dies sind '''Type Attributes''' – also Gruppen, in denen jeweils viele Typen zusammengefasst sind.
* '''Die erste Zeile''' enthält eine separate Regel für <code>execmod</code>, die mit Speicherschutz zusammenhängt; darauf wird an dieser Stelle noch nicht näher eingegangen. Der Teil "<code>[ allow_execmod ]:True</code>" bedeutet, dass diese Regel nur aktiv ist, wenn das Boolean <code>allow_execmod</code> eingeschaltet ist.
* '''Die zweite Zeile''' enthält eine erlaubende Regel für alle Typen, die zum '''Type Attribute''' <code>files_unconfined_type</code> gehören, und umfasst einen großen Satz von Operationen auf '''Dateien''' (also Objekten mit dem Attribut <code>file_type</code> und der Klasse <code>file</code>).
** Die Aktionen stehen in geschweiften Klammern: <code>{ append create execute ... write }</code>
* Daraus folgt, dass ein Prozess in der Domäne <code>unconfined_t</code> seine Rechte über das Attribut <code>files_unconfined_type</code> erhält. Das bedeutet, dass jeder Prozess, der unter einem „unconfined“-Benutzerkontext läuft, praktisch umfassende Kontrolle über temporäre Dateien besitzt, da <code>tmp_t</code> zum globalen Attribut <code>file_type</code> gehört.
Wichtig ist, dass der Doppelpunkt hier '''nicht''' „Teil eines Typnamens“ bedeutet. Er trennt '''target''' und '''object_class'''.
=== Optionen ===
Wichtige <code>sesearch</code>-Optionen zusätzlich zu den oben gezeigten:
==== <code>--allow</code>, <code>--dontaudit</code>, <code>--auditallow</code> ====
* Auswahl des Typs der zu suchenden Regeln, ähnlich wie <code>-A</code>
* <code>--allow</code> – sucht nur nach <code>allow</code>
* <code>--auditallow</code> – Regeln, die vorschreiben, '''erlaubte Aktionen zu protokollieren'''
* <code>--dontaudit</code> – Regeln zur '''Unterdrückung der Protokollierung von Zugriffverweigerungen'''
==== <code>-p</code>, <code>--perm</code> ====
<code>sesearch -A -s httpd_t -c file -p read</code>
* Standardmäßig sucht <code>-p</code> nach Regeln, die '''mindestens eine''' der angegebenen Permissions enthalten.
* Für strengere Abfragen (am Beispiel <code>read,open</code>):
** <code>-ep read,open</code> – findet nur Regeln, bei denen die Menge der Permissions '''genau''' der angegebenen Menge entspricht
** <code>-Sp read,open</code> – findet Regeln, deren Permission-Menge eine '''Obermenge''' der angegebenen Menge ist. Das ist besonders nützlich, um Regeln zu finden, die die benötigten Rechte vollständig abdecken.
==== <code>-b</code>, <code>--bool</code> ====
<code>sesearch -A -b httpd_read_user_content</code>
* Sucht nach '''conditional rules''', die an ein Boolean gebunden sind.
** Conditional rules werden im Abschnitt zu SELinux Booleans im Detail behandelt; hier genügt der Hinweis, dass es sich um bedingte Regeln im Sinne von <code>if / else</code> handelt.
* Eine solche Anfrage ist nützlich, wenn bekannt ist, dass ein bestimmtes Verhalten von einem SELinux-Boolean gesteuert wird und sichtbar werden soll, welche Regeln dadurch beeinflusst werden.
==== <code>-ds</code> und <code>-dt</code> ====
Sehr wichtige Optionen für eine präzise Analyse:
* <code>-ds</code> – verlangt, dass die Source '''explizit''' angegeben ist
* <code>-dt</code> – verlangt, dass das Target '''explizit''' angegeben ist
Wie in den obigen Beispielen zu sehen ist, kann man ohne <code>-ds</code> / <code>-dt</code> Regeln erhalten, die über ein Attribut ermittelt wurden. Diese Optionen unterbinden die implizite Suche über Attribute.
==== <code>-rs</code>, <code>-rt</code>, <code>-rc</code>, <code>-rd</code>, <code>-rb</code> ====
<code>sesearch -A -rs '.*httpd.*' -c file</code>
* Diese Optionen aktivieren '''Regexp-Matching''' für source, target, class, default beziehungsweise Boolean.
----
==== Praktische Aufgabe ====
Es soll geprüft werden, ob der Prozess <code>sshd</code> Dateien im Home-Verzeichnis eines Benutzers lesen darf.
* Zuerst werden die Kontexte (Typen) der Dateien im Home-Verzeichnis angezeigt:
<code># ls -Z1
unconfined_u:object_r:user_home_t:s0 other
    system_u:object_r:user_home_t:s0 testfile</code>
Die Benutzerdateien haben den Typ <code>user_home_t</code>; dieser Wert wird festgehalten.
* Danach wird der Typ des Prozesses <code>sshd</code> betrachtet:
<code># ps -eZ | grep sshd
system_u:system_r:sshd_t:s0        592 ?        00:00:00 sshd</code>
Der Prozess <code>sshd</code> hat den Typ <code>sshd_t</code>.
* Anschließend erfolgt die Suche nach einer Regel in der Policy-Datenbank:
<code>sesearch -A -s sshd_t -t user_home_t -c file -p read</code>
...Hm, nichts gefunden! Müsste <code>sshd</code> nicht Zugriff auf das Verzeichnis <code>~/.ssh</code> haben? Dann sollte der Typ des versteckten Verzeichnisses <code>.ssh</code> geprüft werden.
* Prüfung:
<code># ls -dZ ~/.ssh
unconfined_u:object_r:ssh_home_t:s0 /root/.ssh</code>
'''Der Dienst''' <code>sshd</code> '''benötigt für seine Arbeit keinen Zugriff auf beliebige Benutzerdateien, sondern nur auf das Verzeichnis''' <code>.ssh</code>.
* Daher wird das Vorhandensein einer passenden Regel in der Policy geprüft:
<code>sesearch -A -s sshd_t -t ssh_home_t -c file -p read</code>
Ausgabe:
<code>allow ssh_server ssh_home_t:file { getattr ioctl lock open read };</code>
Damit lässt sich sicher feststellen, dass <code>sshd</code> auf Dateien im Typ <code>ssh_home_t</code> zugreifen darf, und zwar mit folgenden Rechten:
* Lesen von Attributen (<code>getattr</code>) – der Prozess kann Metadaten der Datei abfragen, etwa Größe, Eigentümer, Berechtigungen oder Änderungszeit
* Steuerung von I/O-Parametern (<code>ioctl</code>) – wird häufig von Bibliotheken für die korrekte Arbeit mit Datenströmen verwendet
* Setzen von Sperren (<code>lock</code>) – <code>sshd</code> kann Dateisperren setzen; das ist wichtig, um gleichzeitige Schreibzugriffe mehrerer Prozesse zu verhindern
* Öffnen der Datei (<code>open</code>) – selbst wenn ein Leserecht vorhanden ist, kann der Kernel ohne explizites <code>open</code> dem Prozess keinen Dateideskriptor für die weitere Arbeit bereitstellen
= SCHRITT 3.3: Domain Transition =
Nach der Analyse von <code>allow</code> wird sichtbar, dass SELinux zwei unterschiedliche Fragen beantwortet:
# Was darf ein bereits laufender Prozess tun?
# Welche Domäne soll dieser Prozess beim Start eines Programms überhaupt erhalten?
Die zweite Frage beantwortet der Mechanismus des Übergangs von einer Domäne in eine andere ('''domain transition''').
Ein Domain Transition ist ein streng kontrolliertes Ereignis, das in der Regel beim Start eines neuen Programms stattfindet.
== Regeln für den Übergang ==
Ein Domänenwechsel ist fast immer an den Moment gebunden, in dem der aktuelle Prozess die Ausführung einer neuen Binärdatei über den Systemaufruf <code>execve()</code> auslöst.
In diesem kritischen Moment analysiert SELinux drei Komponenten:
# '''Den Kontext des aktuellen Prozesses''' (Source Domain)
# '''Den Kontext der ausführbaren Datei''' (File Context / Entrypoint)
# '''Die Policy-Regeln''', die den Übergang in eine neue Domäne erlauben (Target Domain)
'''Die Policy-Regel sieht wie folgt aus:'''
<code>type_transition source_domain executable_type:process target_domain;</code>
* Beispiel:
<code>type_transition init_t crond_exec_t:process crond_t;</code>
* Wenn ein Prozess in der Domäne <code>init_t</code> eine Datei vom Typ <code>crond_exec_t</code> ausführt, soll der neue Prozess in die Domäne <code>crond_t</code> wechseln.
----
== Voraussetzungen für den Übergang in eine neue Domäne ==
Damit der Übergang tatsächlich stattfindet, verlangt SELinux nicht nur eine einzige Regel, sondern zusätzlich mindestens drei logische Bedingungen.
* Die '''Quell-Domäne''' muss das Recht <code>transition</code> in die Ziel-Domäne besitzen.
* Die '''ausführbare Datei''' muss aus der Quell-Domäne heraus '''ausgeführt werden dürfen'''.
* Diese '''Datei''' muss für die Ziel-Domäne als <code>entrypoint</code> '''zugelassen sein'''.
'''Erforderliche Regeln (drei erlaubende Regeln plus die eigentliche Transition-Regel):'''
<code>allow source_domain target_domain:process transition;
allow source_domain executable_type:file { execute };
allow target_domain executable_type:file entrypoint;
type_transition source_domain executable_type:process target_domain;</code>
== Entrypoint ==
Damit eine Datei einen Prozess in eine neue Domäne überführen kann, muss sie einen speziellen Typ besitzen, der in der Policy als '''Entrypoint''' für die Ziel-Domäne definiert ist.
=== Reales Beispiel ===
* Die Datei <code>/usr/bin/passwd</code> besitzt den Typ <code>passwd_exec_t</code>.
* Genau dieser Typ ist in der Policy als <code>entrypoint</code> für die Domäne <code>passwd_t</code> erlaubt.
* Deshalb führt die Ausführung von <code>passwd</code> dazu, dass der Prozess in <code>passwd_t</code> wechselt.
* Für die Domäne <code>passwd_t</code> existiert dann bereits die Berechtigung, mit Dateien des Typs <code>shadow_t</code> zu arbeiten, zum Beispiel mit <code>/etc/shadow</code>.
Der <code>entrypoint</code>-Mechanismus schützt das System vor dem Fall, dass ein Angreifer versucht, ein eigenes schädliches Skript zu starten, um sich darüber Zugang zu einer geschützten Domäne zu verschaffen.
----Domain Transition dient der '''Isolation von Privilegien'''. Ein Prozess erhält nicht im Voraus alle denkbaren Rechte. Stattdessen gelangt er nur über eine streng definierte ausführbare Datei in eine spezialisierte Domäne.
----
== Einschränkung von Übergängen ==
Die Begrenzung unerwünschter Domain Transitions basiert auf den folgenden Prinzipien:
# Harte Adressierung der Regeln
Betrachtet wird die folgende Regel:
<code>type_transition unconfined_t mysvc_exec_t:process mysvc_t;</code>
* In diesem Fall gilt die Transition '''nur für die Ausführung von Dateien des Typs''' <code>mysvc_exec_t</code> '''aus der Domäne''' <code>unconfined_t</code>.
# Die Ziel-Domäne definiert selbst über <code>entrypoint</code>, über welche Dateien sie überhaupt betreten werden darf.
#* Fremde Binärdateien können ohne eine passende <code>entrypoint</code>-Regel keinen nicht autorisierten Zugang erhalten.
# Der Übergang muss eindeutig sein.
#* Die SELinux-Policy erlaubt keine zwei unterschiedlichen Ziel-Domänen für dieselbe Kombination aus <code>source domain + executable type + process</code>.
#* Ein Policy-Kompilierungsfehler verhindert die Definition zweier verschiedener Ziel-Domänen für denselben Fall.
== Anzeige von <code>type_transition</code>-Regeln ==
'''Zur Anzeige von Transition-Regeln wird die bereits bekannte''' <code>sesearch</code>'''-Option''' <code>-T</code> '''verwendet.'''
* Beispiel:
<code>sesearch -T -t passwd_exec_t -c process</code>
Ausgabe:
<code># sesearch -T -t passwd_exec_t -c process
type_transition accountsd_t passwd_exec_t:process passwd_t;
type_transition auditadm_t passwd_exec_t:process passwd_t;
type_transition guest_t passwd_exec_t:process passwd_t;
type_transition secadm_t passwd_exec_t:process passwd_t;
type_transition smbd_t passwd_exec_t:process passwd_t; [ samba_domain_controller ]:True
type_transition staff_t passwd_exec_t:process passwd_t;
type_transition sysadm_t passwd_exec_t:process passwd_t;
type_transition user_t passwd_exec_t:process passwd_t;
type_transition xguest_t passwd_exec_t:process passwd_t;</code>
Wenn ein Prozess aus der Domäne <code>user_t</code> (gewöhnlicher Benutzer), <code>staff_t</code> oder sogar <code>guest_t</code> eine Datei mit dem Typ <code>passwd_exec_t</code> ausführt, '''muss der Kernel versuchen, den Kontext des neuen Prozesses auf''' <code>passwd_t</code> '''umzuschalten'''.
* Zusätzlich kann geprüft werden, ob die Transition nach <code>passwd_t</code> selbst erlaubt ist:
<code>sesearch -A -t passwd_t -c process -p transition</code>
Ausgabe:
<code># sesearch -A -t passwd_t -c process -p transition
allow accountsd_t passwd_t:process transition;
allow auditadm_t passwd_t:process transition;
allow guest_t passwd_t:process transition;
allow passwd_t passwd_t:process { dyntransition fork getattr getcap getpgid getrlimit getsched getsession noatsecure rlimitinh setcap setfscreate setkeycreate setpgid setrlimit setsched setsockcreate share sigchld siginh sigkill signal signull sigstop transition };
allow secadm_t passwd_t:process transition;
allow smbd_t passwd_t:process transition; [ samba_domain_controller ]:True
allow staff_t passwd_t:process transition;
allow sysadm_t passwd_t:process transition;
allow user_t passwd_t:process transition;
allow xguest_t passwd_t:process transition;</code>
Die zweite <code>sesearch -A</code>-Ausgabe bestätigt, dass all diese Domänen (Source Domains) eine '''offizielle Berechtigung für diesen Übergang''' besitzen.
* Abschließend wird noch das <code>entrypoint</code>-Recht geprüft:
<code>sesearch -A -s passwd_t -t passwd_exec_t -c file -p entrypoint</code>
Ausgabe:
<code># sesearch -A -s passwd_t -t passwd_exec_t -c file -p entrypoint
allow passwd_t passwd_exec_t:file { entrypoint execute getattr ioctl lock map open read };</code>
Die Logik dieser Regel ist die folgende: Der automatische Übergang in die Domäne <code>passwd_t</code> ist nur dann gültig, wenn er über eine ausführbare Datei des Typs <code>passwd_exec_t</code> ausgelöst wurde, die für diese Domäne als <code>entrypoint</code> definiert ist.
Zusätzlich sind die Standardrechte für die Programmausführung sichtbar (<code>execute</code>, <code>read</code>, <code>open</code>). Dadurch kann die Domäne <code>passwd_t</code> ihren eigenen Programmcode korrekt lesen und ausführen, nachdem die Kontrolle an sie übergeben wurde.
----
== Wann kein Übergang stattfindet ==
Nicht jeder Programmstart führt zu einem Domänenwechsel. Wenn für das Paar '''Quell-Domäne + Typ der ausführbaren Datei''' in der Policy keine passende <code>type_transition</code>-Regel existiert, bleibt der Prozess in der Regel '''in seiner bisherigen Domäne'''.
'''Das ist jedoch nur dann möglich, wenn die Policy die Ausführung der Datei ohne Domänenwechsel erlaubt, also über die Permission''' <code>execute_no_trans</code>.
Mit anderen Worten gibt es bei <code>execve()</code> zwei mögliche Varianten:
# '''Übergang in eine neue Domäne'''
#* wenn eine <code>type_transition</code>-Regel existiert
#* und alle notwendigen Bedingungen erfüllt sind (<code>transition</code>, <code>entrypoint</code>, Berechtigung zur Ausführung der Datei)
# '''Ausführung ohne Übergang'''
#* wenn kein Übergang definiert ist
#* die aktuelle Domäne aber <code>execute_no_trans</code> für diesen Dateityp besitzt
Wenn die Policy hingegen '''weder eine Transition noch eine Ausführung ohne Übergang erlaubt''', wird der Programmstart verweigert.
= SCHRITT 3.4: Type Attributes und <code>seinfo</code> =
In SELinux arbeitet die Policy nur selten direkt mit einzelnen Typen. Stattdessen wird die Abstraktion der '''Type Attributes''' verwendet – also logische Gruppen von Typen, die nach funktionalen Kriterien zusammengefasst sind.
Zur Anzeige von Informationen über Attribute und die darin enthaltenen Typen dient das Werkzeug <code>seinfo</code>.
* Es wird geprüft, welche Typen das Attribut <code>httpdcontent</code> enthält:
<code>seinfo -a httpdcontent -x</code>
Ausgabe:
<code># seinfo -a httpdcontent -x
Type Attributes: 1
    attribute httpdcontent;
        httpd_apcupsd_cgi_content_t
        httpd_apcupsd_cgi_ra_content_t
        httpd_apcupsd_cgi_rw_content_t
        httpd_awstats_content_t
        httpd_awstats_ra_content_t
        ...
        httpd_user_ra_content_t
        httpd_user_rw_content_t
        httpd_webalizer_content_t
        httpd_webalizer_ra_content_t
        httpd_webalizer_rw_content_t</code>
Dank der Type Attributes kann eine einzige Regel formuliert werden, anstatt für jeden einzelnen Typ eine separate Regel definieren zu müssen.
Damit wirken Type Attributes wie ein '''Mechanismus der „Vererbung“ von Regeln'''.
== Effective Permissions ==
Aus dem bisher Gesagten wird deutlich, dass die Rechte einer Domäne nicht nur von direkten <code>allow</code>-Regeln abhängen, sondern auch von Regeln, die Typen indirekt über Attribute erhalten.
* Bei der Analyse solcher geerbten Regeln hilft das bereits bekannte Werkzeug <code>sesearch</code>. In der Standardverwendung löst es Attribute auf. Das lässt sich mit dem folgenden Befehl nachvollziehen:
<code>sesearch -A -s httpd_t -t httpd_sys_content_t -c file -p read</code>
* Nun derselbe Aufruf mit Parametern, die eine Auflösung über Attribute unterbinden:
<code>sesearch -A -s httpd_t -t httpd_sys_content_t -c file -p read -ds -dt</code>
== <code>seinfo</code> ==
<code>seinfo</code> ist ein Werkzeug zur '''Inventarisierung und strukturellen Analyse einer SELinux-Policy'''. Während <code>sesearch</code> die Frage beantwortet, „gibt es eine solche Regel?“, beantwortet <code>seinfo</code> die Frage: „aus welchen Entitäten besteht die geladene Policy?“
Die Syntax sieht wie folgt aus:
<code>seinfo [OPTIONS] [EXPRESSION] [POLICY]</code>
=== Zentrale Einsatzgebiete von <code>seinfo</code>: ===
* Prüfung, ob ein Typ, Attribut, eine Rolle, ein Benutzer oder ein Boolean existiert
* Suche nach den Attributen, zu denen ein bestimmter Typ bzw. eine bestimmte Domäne gehört
* Ermittlung, welche Typen in einem bestimmten Attribut enthalten sind
* Anzeige, welche Rollen und Benutzer in der Policy überhaupt definiert sind
* Überblick über die Struktur der Policy insgesamt
----
=== Optionen ===
==== Aufruf ohne Optionen ====
<code>seinfo</code>
* Liefert eine Zusammenfassung. Angezeigt werden die Anzahl von Typen, Attributen, Rollen, Benutzern, Booleans und weiteren Policy-Objekten.
==== <code>-x</code>, <code>--expand</code> ====
<code>seinfo -t sshd_t -x
seinfo -a file_type -x</code>
* Die wichtigste Option
* Aktiviert eine '''erweiterte Ausgabe'''
* Bei Typen bedeutet dies in der Regel, dass die Mitgliedschaft in Attributen sichtbar wird.
* Bei Attributen wird die Liste der enthaltenen Mitglieder ausgegeben.
==== <code>-t [TYPE]</code>, <code>--type [TYPE]</code> ====
<code>seinfo -t httpd_t -x</code>
* Fordert Informationen zu einem Typ an, also insbesondere, in welchen Attributen dieser Typ enthalten ist.
==== <code>-a [ATTR]</code>, <code>--attribute [ATTR]</code> ====
<code>seinfo -a daemon -x</code>
* Fordert Informationen zu einem Attribut an, also welche Typen diesem Attribut zugeordnet sind.
==== <code>-r [ROLE]</code>, <code>--role [ROLE]</code> ====
* Abfrage einer Rolle
==== <code>-u [USER]</code>, <code>--user [USER]</code> ====
* Abfrage eines Benutzers
==== <code>-b [BOOL]</code>, <code>--bool [BOOL]</code> ====
* Abfrage eines Booleans
==== <code>POLICY</code> als Argument ====
Es kann nicht nur die aktuell aktive Policy analysiert werden, sondern auch eine konkrete Policy-Datei.
<code>seinfo -t httpd_t -x /etc/selinux/targeted/policy/policy.*</code>
= Schritt 3.5: SELinux Booleans =
In den vorherigen Etappen wurde deutlich, dass die Rechte von Domänen fest in der Policy definiert sind. Doch was ist zu tun, wenn das Standardverhalten eines Dienstes leicht angepasst werden soll? Zum Beispiel dann, wenn dem Apache-Webserver erlaubt werden soll, sich mit einer entfernten Datenbank zu verbinden oder E-Mails zu versenden.
Dafür ein eigenes Policy-Modul von Grund auf zu schreiben, ist oft zu aufwendig und für Standardfälle unnötig. Für genau solche typischen Szenarien stellen die SELinux-Entwickler '''Booleans''' bereit.
'''Booleans''' sind in die Policy integrierte Schalter (on/off), mit denen bestimmte Regelblöcke ('''Conditional Rules''') innerhalb der bereits kompilierten Policy aktiviert oder deaktiviert werden.
Ein und derselbe Prozess in <code>httpd_t</code>, <code>smbd_t</code> oder einer anderen Service-Domäne kann nach dem Umschalten eines Boolean zusätzlichen Zugriff auf Objekttypen erhalten, die in der Policy bereits vorgesehen sind.
----Regeln, die mit <code>booleans</code> arbeiten, besitzen einen Zusatzblock in eckigen Klammern:
<code>allow httpd_t user_home_t:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True</code>
* <code>[ httpd_read_user_content ]:True</code> bedeutet, dass sich diese Regel im <code>if</code>-Zweig befindet. Sie ist '''nur dann aktiv''', wenn der Schalter <code>httpd_read_user_content</code> auf '''ON''' steht.
* <code>[ httpd_read_user_content ]:False</code> bedeutet, dass sich diese Regel im <code>else</code>-Zweig befindet. Sie ist '''nur dann aktiv''', wenn der Schalter <code>httpd_read_user_content</code> auf '''OFF''' steht.
----
== Werkzeuge ==
=== <code>getsebool</code> ===
Wird verwendet, um den '''aktuellen Zustand eines Boolean''' anzuzeigen.
* Alle aktuellen Booleans anzeigen:
<code>getsebool -a</code>
* Einen einzelnen Boolean anzeigen:
<code>getsebool httpd_use_nfs</code>
=== <code>setsebool</code> ===
Wird verwendet, um den '''Zustand eines Boolean zu ändern'''.
* Boolean einschalten (temporär):
<code>setsebool ftpd_anon_write on</code>
* Boolean einschalten (persistent):
<code>setsebool -P ftpd_anon_write on</code>
* Wenn mehrere Booleans gesetzt werden, müssen sie in der Form <code>parameter=wert</code> angegeben werden:
<code>setsebool ftpd_anon_write=on ftpd_use_passive_mode=on</code>
=== <code>semanage boolean</code> ===
Wird verwendet, um '''Boolean-Einstellungen als Teil der Policy-Konfiguration''' zu verwalten. Außerdem lassen sich damit eine menschenlesbare Beschreibung jedes Schalters sowie dessen Standardwert anzeigen.
* Alle Booleans mit Beschreibung und Werten anzeigen (aktuell und Standard):
<code>semanage boolean -l</code>
* Beschreibung eines bestimmten Boolean suchen:
<code>semanage boolean -l | grep httpd</code>
* Zustand eines Boolean ändern (funktional ähnlich zu <code>setsebool -P</code>):
<code>semanage boolean -m --on ftpd_anon_write</code>
== Praktisches Beispiel ==
=== Aufgabe ===
Es soll erreicht werden, dass die Web-Anfrage mit <code>curl</code> eine Datei lesen kann, die als auf NFS liegend gelabelt ist – und zwar ausschließlich mithilfe des Boolean-Mechanismus.
=== Lösung ===
* Vorbereitung der Umgebung:
<code>mkdir -p /var/www/html/nfs_test
echo "Hello from NFS" > /var/www/html/nfs_test/index.html
chcon -R -t nfs_t /var/www/html/nfs_test
setenforce 1 # Enforcing-Modus aktivieren</code>
* Versuch, die Seite aus dem NFS-Verzeichnis mit <code>curl</code> abzurufen:
<code>curl -I <nowiki>http://localhost/nfs_test/index.html</nowiki></code>
Ergebnis:
<code># curl -I <nowiki>http://localhost/nfs_test/index.html</nowiki>
HTTP/1.1 403 Forbidden
...</code>
* Relevante Rechte und zugehörige Booleans prüfen:
<code>semanage boolean -l | grep httpd
semanage boolean -l | grep nfs
sesearch -A -s httpd_t -t nfs_t -c file -p read</code>
Mithilfe von <code>sesearch</code> lässt sich der Lösungsweg bestimmen:
<code># sesearch -A -s httpd_t -t nfs_t -c file -p read
allow httpd_t nfs_t:file { execute execute_no_trans getattr ioctl map open read }; [ ( httpd_builtin_scripting && use_nfs_home_dirs && httpd_enable_homedirs ) ]:True
allow httpd_t nfs_t:file { execute execute_no_trans getattr ioctl map open read }; [ httpd_builtin_scripting && httpd_use_nfs ]:True
allow httpd_t nfs_t:file { execute getattr ioctl map open read }; [ httpd_use_nfs && httpd_enable_cgi ]:True
allow httpd_t nfs_t:file { getattr ioctl lock open read }; [ use_nfs_home_dirs && httpd_enable_homedirs ]:True</code>
Es ist zu erkennen, dass in der Liste unter anderem <code>httpd_use_nfs</code> vorkommt, dieses Boolean allein jedoch nicht ausreicht, damit eine passende Regel aktiv wird. Deshalb wird entschieden, <code>httpd_builtin_scripting</code> als zweiten Aktivierungsfaktor zu verwenden.
* Den aktuellen Zustand von <code>httpd_use_nfs</code> und <code>httpd_builtin_scripting</code> prüfen:
<code>getsebool httpd_use_nfs httpd_builtin_scripting</code>
* Beide Booleans testweise temporär aktivieren:
<code>setsebool httpd_use_nfs=on httpd_builtin_scripting=on</code>
* Erneute Prüfung:
<code>curl -I <nowiki>http://localhost/nfs_test/index.html</nowiki></code>
Ergebnis:
<code>HTTP/1.1 200 OK
...</code>
'''Erfolgreich!'''
* Wenn das Verhalten den Erwartungen entspricht, können die Änderungen persistent gespeichert werden:
<code>setsebool -P httpd_use_nfs=on httpd_builtin_scripting=on</code>
= SCHRITT 3.6: Einschränkungen oberhalb von <code>allow</code> =
In den vorherigen Schritten konnte der Eindruck entstehen, dass das Vorhandensein einer <code>allow</code>-Regel automatisch zu einem erfolgreichen Zugriff führt. In SELinux können jedoch oberhalb von <code>allow</code> zusätzliche Mechanismen greifen.
Nach der normalen TE-Prüfung kann der Kernel weitere Einschränkungen anwenden: <code>constrain</code> / <code>validatetrans</code>, spezielle Prüfungen für ausführbaren Speicher, globale <code>policycap</code>-Schalter sowie die Logik von <code>no_new_privs</code>. Deshalb tritt in der praktischen Diagnose häufig die Situation auf: '''Eine''' <code>allow</code>'''-Regel ist vorhanden, die Operation wird aber trotzdem verweigert.'''
== Constraints (Einschränkungen) ==
Ein <code>constraint</code> ist eine zusätzliche Bedingung, die '''zusätzlich zu einer bereits gefundenen''' <code>allow</code>'''-Regel''' erfüllt sein muss. Ein <code>constraint</code> gewährt keine neuen Rechte, sondern schränkt den Geltungsbereich einer bereits vorhandenen <code>allow</code>-Regel weiter ein.
<code>constrain { object_class_list } { permissions } ( expression );</code>
=== Beispiel ===
<code>allow unconfined_t ext_gateway_t:process transition;
constrain process transition ( r1 == r2 );</code>
* Die Transition-Regel besagt, dass der Übergang grundsätzlich erlaubt ist.
* Das <code>constraint</code> fügt eine weitere zwingende Bedingung hinzu: Der Übergang ist nur zulässig, wenn die Rolle der Quelle mit der Rolle des Ergebnisses übereinstimmt.
----<code>validatetrans</code> '''(validate transition):''' Eine spezielle Form von Einschränkungen, die nur bei <code>relabel</code>-Operationen greift. Dabei werden gleichzeitig drei Parameter ausgewertet: der alte Dateikontext, der neue Dateikontext und der Kontext des Prozesses, der die Änderung vornimmt.
<code>validatetrans</code> schützt das System davor, dass ein kompromittierter Prozess mit dem Recht <code>relabel</code> eine normale Textdatei in eine ausführbare Binärdatei (<code>bin_t</code>) oder in eine Passwortdatei (<code>shadow_t</code>) umwandelt. Für solche Aktionen wird in der Regel das Vorhandensein eines speziellen Attributs verlangt, etwa <code>can_change_system_roles</code>.
<code>validatetrans { class_id } ( expression );</code>
----
=== Anzeige ===
Das Werkzeug <code>seinfo</code> kann sowohl normale <code>constraints</code> als auch <code>validatetrans</code>-Regeln anzeigen.
<code>seinfo --constrain process
seinfo --validatetrans file</code>
----
== Memory Protection (Speicherschutz) ==
SELinux ist tief in das Speicherverwaltungssubsystem des Kernels integriert, um vor der Ausnutzung von Schwachstellen wie etwa Buffer Overflows zu schützen. Selbst wenn ein Prozess eine Datei ausführen darf, kann SELinux bestimmte Speicheroperationen trotzdem verbieten.
* Für die Klasse <code>process</code> definiert SELinux unter anderem die folgenden Permissions:
** <code>execmem</code> – erlaubt es, eine anonyme oder privat eingeblendete writable memory mapping als ausführbar zu markieren
** <code>execstack</code> – erlaubt es, den Haupt-Stack des Prozesses als ausführbar zu markieren
** <code>execheap</code> – erlaubt es, den Heap-Speicher des Prozesses als ausführbar zu markieren
* Für die Klasse <code>file</code> existiert die Permission <code>execmod</code>, die typischerweise mit der Ausführung einer im Speicher modifizierten Dateiabbildung zusammenhängt.
* Zusätzlich gibt es die Klasse <code>memprotect</code> mit der Permission <code>mmap_zero</code>, die Versuche kontrolliert, per <code>mmap()</code> in den niedrigen Adressraum zu mappen.
Damit kontrolliert SELinux nicht nur den Zugriff auf Dateien oder das Recht auf einen Domain Transition, sondern auch die '''Sicherheit des Ausführungsmodells von Code im Speicher'''.
Deshalb kann ein Programm zwar berechtigt sein, eine Bibliothek zu lesen und sogar zu starten, dennoch aber einen Denial erhalten – etwa wegen des Versuchs, ein writable+executable Mapping zu erzeugen, Code vom Stack auszuführen, einen ausführbaren Heap zu verwenden oder eine modifizierte Dateimapping zu laden.
=== Anzeige ===
Zur Analyse von Problemen mit Speicherschutzmechanismen wie <code>execmem</code> werden in erster Linie die zugehörigen Regeln geprüft; zusätzlich kann auch ein relevantes Boolean abgefragt werden:
<code>sesearch -A -s DOMAIN_T -c process -p execmem,execstack,execheap
sesearch -A -s DOMAIN_T -t FILE_TYPE_T -c file -p execmod
getsebool -a | grep execmem</code>
----
== Policy capabilities ==
<code>Policy capabilities</code> (<code>policycap</code>) sind '''globale Fähigkeiten der geladenen Policy''', also eingebaute Schalter für bestimmte Betriebsmodi von SELinux. Sie ermöglichen es der Policy, beim Hinzukommen neuer Kernel-Funktionen abwärtskompatibel zu bleiben.
=== Beispiele ===
* <code>network_peer_controls</code>: Aktiviert moderne Prüfungen für Netzwerkpakete.
* <code>open_perms</code>: Aktiviert die Prüfung der Permission <code>open</code> für Dateien; früher wurden nur <code>read</code> und <code>write</code> geprüft.
* <code>always_check_network</code>: Erzwingt, dass SELinux Netzwerk-Labels und netzwerkbezogene SELinux-Prüfungen immer als aktiv behandelt, auch wenn die entsprechenden Netzwerkmechanismen aktuell nicht explizit konfiguriert sind.
=== Anzeige ===
<code>seinfo --polcap -x</code>
----
== No New Privileges (NNP) ==
Das Flag <code>no_new_privs</code> (<code>NNP</code>) ist ein Mechanismus des Linux-Kernels, der seit Kernel-Version 3.5 existiert. Jeder Prozess kann dieses Bit setzen. Danach wird es über <code>fork</code>, <code>clone</code> und <code>execve</code> vererbt und kann nicht mehr zurückgesetzt werden.
Für SELinux ist das besonders wichtig, weil auch ein Domain Transition bei <code>execve()</code> als Privilegienänderung betrachtet werden kann. Im aktuellen SELinux-Modell sind bei aktivierter Policy Capability <code>nnp_nosuid_transition</code> für bestimmte Domain Transitions zusätzliche Permissions erforderlich:
* <code>process2:nnp_transition</code> – wenn der Thread mit <code>no_new_privs</code> läuft
* <code>process2:nosuid_transition</code> – wenn der Übergang von einem mit <code>nosuid</code> gemounteten Dateisystem ausgeht
** Wenn die Capability <code>nnp_nosuid_transition</code> deaktiviert ist, werden solche Übergänge üblicherweise durch '''bounded transitions''' eingeschränkt; darauf wird etwas später noch eingegangen.
----
=== <code>typebounds</code> ===
Ein '''bounded transition''' ist ein Domain Transition, bei dem '''die neue Domäne als eingeschränkte Version der alten Domäne gilt''' und keine Privilegien über die der Ausgangsdomäne hinaus erhält. Das bedeutet: Der Kernel erlaubt einen Domain Transition bei aktivem '''NNP''', wenn in der Policy explizit definiert ist, dass '''die neue Domäne in ihren Rechten nicht weiter reicht als die alte'''.
Dies wird über die Regel <code>typebounds</code> festgelegt.
<code>typebounds SRC_T DST_T;</code>
Wenn eine solche Regel existiert, versteht der Kernel, dass ein Prozess beim Wechsel nach <code>DST_T</code> keine Rechte erhält, die er in <code>SRC_T</code> nicht bereits hatte, und erlaubt die Operation deshalb auch mit gesetztem <code>no_new_privs</code>-Flag.
Prüfung, ob <code>typebounds</code> vorhanden ist:
<code>seinfo --typebounds -x</code>
----
=== Beispiel ===
Wenn in der Policy die folgende Regel vorhanden ist:
<code>allow SRC_T DST_T:process transition;</code>
* dann ist für einen Prozess mit <code>NNP</code> zusätzlich die folgende Regel erforderlich:
<code>allow SRC_T DST_T:process2 nnp_transition;</code>
=== Anzeige ===
* Standard-Domain-Transition prüfen:
<code>sesearch -A -s SRC_T -t DST_T -c process -p transition</code>
* Erlaubnis für den Übergang mit NNP prüfen:
<code>sesearch -A -s SRC_T -t DST_T -c process2 -p nnp_transition</code>
* Erlaubnis für den Übergang von einem <code>nosuid</code>-gemounteten Dateisystem prüfen:
<code>sesearch -A -s SRC_T -t DST_T -c process2 -p nosuid_transition</code>
----
== <code>audit2why</code> ==
An dieser Stelle ist es sinnvoll, auch das Werkzeug <code>audit2why</code> zu erwähnen, das bei der Analyse komplexer Situationen und von AVC-Denials hilfreich sein kann.
<code>audit2why</code> liest Meldungen aus dem <code>audit.log</code> und versucht, '''die Ursache einer Verweigerung in menschenlesbarer Form zu erklären''', anstatt nur rohe AVC-Einträge anzuzeigen.
Die praktische Verwendung beschränkt sich im Wesentlichen auf die folgenden Varianten:
* Alle AVC-Meldungen anzeigen:
<code>audit2why -a</code>
* Einen benutzerdefinierten Satz von AVCs über <code>ausearch</code> an <code>audit2why</code> weitergeben:
<code>ausearch -m AVC,USER_AVC,SELINUX_ERR -ts recent -i | audit2why
ausearch -m AVC,USER_AVC,SELINUX_ERR -c httpd -i | audit2why</code>
<code>audit2why</code> kann Zeit sparen oder zumindest einen Hinweis darauf geben, in welche Richtung bei der Problemlösung weiter analysiert werden sollte.
= SCHRITT 3.7: Dateierzeugung durch Prozesse =
Standardmäßig gilt in SELinux eine einfache Vererbungsregel: '''Eine neu erzeugte Datei erhält den Typ des Verzeichnisses, in dem sie erstellt wird'''. Wenn zum Beispiel ein Webserver (<code>httpd_t</code>) eine temporäre Datei in <code>/tmp</code> (<code>tmp_t</code>) anlegt, erhält diese Datei standardmäßig ebenfalls den Typ <code>tmp_t</code>. Das ist jedoch oft weder sicher noch praktikabel, weil auch andere Prozesse Zugriff auf <code>tmp_t</code> haben können. Wie lässt sich erreichen, dass der Webserver seine temporären Dateien automatisch mit dem passenden Typ versieht, etwa <code>httpd_tmp_t</code>?
Das manuelle Setzen eines Labels mit <code>chcon</code> skaliert aus drei Gründen schlecht:
* <code>chcon</code> löst das Problem nur für ein bereits existierendes Objekt, nicht aber für die nächste Datei, die der Dienst eine Sekunde später erzeugt
* Das gesetzte Label ist temporär und kann durch <code>restorecon</code> oder ein vollständiges Relabeling wieder überschrieben werden
* Die Logik des Labelings wird aus der Policy in manuelle Administratoraktionen verlagert, was fast immer zu Konfigurationsdrift und schwer diagnostizierbaren Fehlern führt
== File Transitions ==
SELinux löst dieses Problem elegant durch automatische Kernel-seitige Labelvergabe im Moment der Dateierzeugung ('''policy-based labeling'''). Die Grundidee ist, dass die Policy nicht eine bereits existierende Datei steuert, sondern das Ergebnis der Erzeugungsoperation.
Dafür wird eine Regel vom Typ '''File Type Transition''' verwendet. Sie sagt dem Kernel: ''Wenn ein Prozess in Domäne X ein Objekt der Klasse Y in einem Verzeichnis des Typs Z erstellt, muss diesem Objekt automatisch der Typ W zugewiesen werden.''
<code>type_transition <domain> <parent_dir_type>:<class> <new_type>;</code>
=== Beispiel ===
<code>type_transition httpd_t tmp_t:file httpd_tmp_t;</code>
''Wenn der Webserver (''<code>httpd_t</code>'') in einem Verzeichnis vom Typ'' <code>tmp_t</code> ''eine Datei (''<code>file</code>'') erzeugt, muss dieser Datei unmittelbar bei der Erzeugung das Label'' <code>httpd_tmp_t</code> ''zugewiesen werden.''
Wie zu sehen ist, beginnt die Regel ebenso mit <code>type_transition</code> wie eine Domain-Transition-Regel. Der wesentliche syntaktische Unterschied besteht jedoch darin, dass hier '''explizit eine Objektklasse wie''' <code>file</code> '''angegeben wird''' und nicht die Prozessklasse.
'''Wichtig:''' <code>type_transition</code> legt nur das Label des neuen Objekts fest. Die Regel vergibt '''keine''' Rechte wie <code>create</code>, <code>add_name</code>, <code>write</code> usw.
=== Einschränkungen ===
Ein gewöhnlicher File Transition ist dann sinnvoll, wenn alle Objekte einer bestimmten Klasse, die in einem bestimmten Container erzeugt werden, denselben Typ erhalten sollen.
Wenn ein Dienst in seinem Runtime-Verzeichnis beispielsweise immer Hilfsdateien mit derselben Funktion erzeugt, ist kein Dateiname erforderlich: Domäne des Prozesses, Verzeichnistyp und Objektklasse reichen aus.
Das Problem beginnt dann, wenn '''derselbe Prozess''' im '''selben Verzeichnis''' mehrere unterschiedliche Objekte erzeugt, die verschiedene SELinux-Typen erhalten sollen.
== Named File Transition ==
Ein <code>named file transition</code> ist derselbe <code>type_transition</code>, jedoch mit einer zusätzlichen Bedingung: '''Der Parameter''' <code>basename</code> '''des neu erzeugten Objekts muss mit dem angegebenen Namen übereinstimmen.'''
<code>type_transition <domain> <parent_dir_type>:<class> <new_type> "<basename>";</code>
Ein named transition vergleicht '''nicht den Pfad''', sondern ausschließlich den '''Basename'''.
* Der Vergleich erfolgt als exakte Übereinstimmung. '''Der Name muss Zeichen für Zeichen identisch sein'''
* Reguläre Ausdrücke und Wildcard-Symbole werden hier nicht unterstützt
* Wie beim normalen File Transition betrifft der Mechanismus nur die '''initiale Erzeugung''' eines Objekts und korrigiert keine bereits existierenden falsch gelabelten Dateien
=== Beispiel ===
<code>type_transition unconfined_t admin_home_t:dir ssh_home_t ".ssh";</code>
''Wenn ein Prozess vom Typ'' <code>unconfined_t</code> ''innerhalb eines Verzeichnisses vom Typ'' <code>admin_home_t</code> ''ein Verzeichnis mit dem Namen'' <code>.ssh</code> ''anlegt, erhält das neue Verzeichnis den Typ'' <code>ssh_home_t</code>''.''
=== Wichtiger Hinweis ===
<nowiki><u>Wenn ein Label </nowiki><code>restorecon</code> oder <code>setfiles</code> überstehen soll, muss es zusätzlich auf Ebene der <code>file_contexts</code> abgebildet sein.<nowiki></u></nowiki>
== Arbeit mit File Transitions ==
* Hinsichtlich des Geltungsbereichs ist wichtig, dass <code>file transition</code>-Regeln nicht nur für <code>file</code> und <code>dir</code> gelten, sondern auch für andere Objektklassen, etwa <code>sock_file</code>, <code>fifo_file</code>, <code>chr_file</code> oder <code>blk_file</code>
* Für die Suche nach Regeln und die Diagnose von Problemen mit File Transitions ist <code>sesearch</code> mit der Option <code>-T</code> besonders nützlich:
<code>sesearch -T -s <source_domain> -t <parent_dir_type></code>
* Beispiel einer Prüfung:
<code># sesearch -T -s user_t -t tmp_t
type_transition user_t tmp_t:dir user_tmp_t;
type_transition user_t tmp_t:fifo_file user_tmp_t;
type_transition user_t tmp_t:file user_tmp_t;
type_transition user_t tmp_t:lnk_file user_tmp_t;
type_transition user_t tmp_t:sock_file user_tmp_t;</code>
Interpretation der Ausgabe: ''Wenn ein Prozess in der Domäne'' <code>user_t</code> ''in einem Verzeichnis vom Typ'' <code>tmp_t</code> ''ein Objekt erzeugt – etwa eine Datei, ein Verzeichnis, einen Link oder ein Socket –, dann soll diesem Objekt der Typ'' <code>user_tmp_t</code> ''zugewiesen werden.''
= SCHRITT 3.8: <code>restorecond</code>, der Hintergrund-Korrektor für Labels =
In dieser Lektion wird der Dienst <code>restorecond</code> behandelt – ein Hintergrundmechanismus zur automatischen Wiederherstellung von Labels. In modernen Linux-Distributionen wird dieser Daemon in der Regel nicht standardmäßig installiert, und viele seiner Aufgaben werden heute effizient direkt im Kernel über File-Transition-Regeln sowie durch eingebaute Mechanismen von systemd gelöst. Dennoch ist das Verständnis von <code>restorecond</code> notwendig: Das Werkzeug taucht weiterhin in bestimmten Spezialfällen auf, und seine Funktionsweise ist für korrektes Troubleshooting von zentraler Bedeutung.
== <code>restorecond</code> ==
<code>restorecond</code> (Restore Context Daemon) ist ein Hintergrundprozess (Dienst), der bestimmte Dateien und Verzeichnisse in Echtzeit überwacht, und zwar mithilfe der Kernel-Subsystems <code>inotify</code>.
Wenn in einem überwachten Verzeichnis eine neue Datei erscheint oder ein Objekt in ein überwachten Verzeichnis verschoben wird, prüft <code>restorecond</code> sofort dessen Label. Wenn das aktuelle Label nicht mit dem in der SELinux-Referenzdatenbank (<code>file_contexts</code>) hinterlegten Label übereinstimmt, setzt der Daemon automatisch das korrekte Label zurück.
<code>restorecond</code> überwacht nicht das gesamte Dateisystem – das würde die Server-Performance massiv beeinträchtigen. Stattdessen beobachtet der Dienst nur die Pfade, die explizit in seinen Konfigurationsdateien eingetragen sind:
* <code>/etc/selinux/restorecond.conf</code> – die systemweite Konfigurationsdatei. Hier werden typischerweise für das System kritische Pfade eingetragen, in denen Benutzer oder Skripte häufig Dateien mit falschen Labels erzeugen. ''(Beispiele:'' <code>/etc/resolv.conf</code>'','' <code>/root/*</code>'','' <code>/var/run/*</code>'')''
* <code>/etc/selinux/restorecond_user.conf</code> – die benutzerspezifische Konfiguration. Sie wird verwendet, um Dateien in Home-Verzeichnissen zu überwachen, zum Beispiel SSH-Schlüssel (<code>~/.ssh/*</code>) oder Dateien unter <code>~/.public_html/</code>, damit Webserver oder SSH-Daemonen nicht durch fehlerhafte Labels den Zugriff verlieren.
Für die Wiederherstellung der Labels verwendet <code>restorecond</code> denselben Mechanismus wie <code>restorecon</code>.
== Manuelles Setzen von Labels und <code>restorecond</code> ==
Das manuelle Setzen eines Labels auf einem überwachten Pfad kann in mindestens drei Situationen instabil sein:
* '''Der Daemon''' <code>restorecond</code> '''wird gestartet oder liest seine Konfiguration erneut ein'''
* '''Die Datei wird unter demselben Namen neu erzeugt'''
* '''Die Datei wird atomar ersetzt''' – etwa per Umbenennung oder Verschieben (<code>mv</code>) in ein überwachtes Verzeichnis. Viele Programme aktualisieren Dateien nicht durch direktes Überschreiben, sondern nach dem Muster „temporäre Datei erzeugen und anschließend per move über die alte Datei legen“. In einem solchen Fall wird die Label-Wiederherstellung durch <code>restorecond</code> ausgelöst.
Dadurch kann das Verhalten auftreten, dass ein Administrator ein Label mit <code>chcon</code> setzt, das Label aber kurze Zeit später wieder auf den ursprünglichen Zustand zurückgesetzt wird. In einer solchen Situation sollte zuerst geprüft werden, ob der Dienst <code>restorecond</code> im System aktiv ist.
== Funktionsweise ==
* Zunächst liest <code>restorecond</code> seine Konfiguration und bestimmt für jeden Eintrag:
** '''das Verzeichnis''', das überwacht werden soll
** '''den Dateinamen oder das Basename-Muster''', mit dem eingehende Ereignisse verglichen werden
* Danach setzt <code>restorecond</code> einen <code>inotify</code>-Watch '''auf das Verzeichnis'''
----Wichtig ist, dass <code>restorecond</code> '''nicht an der Kernel-Logik der Label-Vergabe im Moment der Dateierzeugung beteiligt ist'''. Zuerst wird das Objekt erzeugt, danach trifft ein <code>inotify</code>-Ereignis ein, und erst anschließend ruft der Daemon für den konkreten Pfad den <code>restorecon</code>-Mechanismus auf. Genau das ist der grundlegende '''Unterschied zu''' <code>file transition</code>''', das bereits im Kernel während der Erzeugung eines Objekts greift'''.
----'''Pfadvergleich'''
Der Vergleich erfolgt anhand der '''letzten Pfadkomponente''', weil Verzeichnis und Basename getrennt verarbeitet werden. Das bedeutet, dass Muster mit <code>*</code> '''nur in der letzten Komponente''' funktionieren:
<code>/root/.ssh/*
~/public_html/*</code>
== Anzeige ==
* Status des Dienstes über systemd prüfen:
<code>systemctl status restorecond</code>
* Vorhandensein des Prozesses und seine SELinux-Domäne prüfen:
<code>ps -eZ | grep restorecond_t</code>
* <code>restorecond</code>-Konfiguration anzeigen:
<code>cat /etc/selinux/restorecond.conf</code>

Version vom 27. März 2026, 09:11 Uhr


SCHRITT 3.1: Der Prozess als SELinux-Subjekt

Bis zu diesem Punkt wurden Dateien und Verzeichnisse betrachtet. In der Terminologie von SELinux sind all diese Elemente Objekte (Objects). Sie sind passiv. Eine Datei kann sich nicht selbst lesen oder löschen.

Im klassischen Linux hängen die Rechte eines Prozesses davon ab, unter welcher Benutzerkennung er läuft (UID/GID). In SELinux erhält ein Prozess beim Start seinen eigenen Sicherheitskontext. Dieser Kontext bestimmt vollständig, was der Prozess im System tun darf – unabhängig davon, ob es sich um einen Root-Prozess oder um einen gewöhnlichen Benutzerprozess handelt.

  • Um den Kontext laufender Prozesse anzuzeigen, wird bei ps die Option -Z verwendet:
ps -eZ | grep sshd

Ausgabe:

# ps -Z
LABEL                               PID TTY          TIME CMD
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 90819 pts/1 00:00:00 sudo
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 90820 pts/1 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 91782 pts/1 00:00:00 ps

Prinzipien der SELinux-Arbeitsweise bei Prozessen

Die Grundlage von SELinux ist der Mechanismus der zwingenden Zugriffskontrolle auf Basis von TypenType Enforcement (TE). In diesem Modell wird jedem Element im System ein bestimmter Bezeichner zugewiesen, der als Typ (type) bezeichnet wird.

  • Bei passiven Entitäten (Dateien, Verzeichnisse, Sockets) wird diese Kennzeichnung einfach als Typ bezeichnet (file context / type). Bei einer Datei beschreibt das Feld type den Objekttyp.
  • Bei aktiven Entitäten (Prozessen) wird dieselbe Kennzeichnung historisch und konzeptionell als Domäne (domain) bezeichnet. Beim Prozess beschreibt das Feld type die Domäne des Subjekts.

Wenn ein Prozess versucht, eine Aktion auszuführen, bewertet SELinux die Anfrage anhand der Gesamtheit der Attribute von Subjekt und Objekt. Die klassischen UNIX-/DAC-Prüfungen erfolgen dabei zuerst: Wenn DAC die Operation bereits verweigert (zum Beispiel weil dem Benutzer die herkömmlichen Leserechte fehlen), kommt SELinux gar nicht mehr zum Zug, und es kann auch keinen AVC-Eintrag geben.

Wenn keine DAC-Hindernisse vorliegen, vergleicht SELinux den Prozesskontext und den Objektkontext mit der geladenen Policy. Die Grundlogik lautet default deny: Zugriff wird nur dann erlaubt, wenn eine Regel existiert, die die benötigte Interaktion explizit zulässt.

Algorithmus der Zugriffsprüfung nach DAC im Type Enforcement:

  1. Identifikation des Subjekts: Es wird die Domäne des anfragenden Prozesses bestimmt (zum Beispiel ein Webserver in der Domäne httpd_t).
  2. Identifikation des Objekts: Es wird der Typ der Zielressource bestimmt (zum Beispiel eine Datei mit dem Typ httpd_sys_content_t).
  3. Bestimmung von Klasse und Aktion: Es werden die Objektklasse (file) und die angeforderte Aktion (read) festgelegt.
  4. Prüfung der Policy (Regelsuche): Der Kernel greift auf den Access Vector Cache (AVC) oder auf die geladene Policy zu, um nach einer erlaubenden Regel zu suchen.

SCHRITT 3.2: Policy und Zugriffsregeln. Das Werkzeug sesearch

Wie bereits besprochen, wird im TE-Modell ein Prozess als Subjekt betrachtet, das in einer bestimmten Domäne arbeitet, während ein Objekt als Entität mit einem bestimmten Typ gilt. Für SELinux ist dies die zentrale Grundlage der Entscheidungsfindung: Die Policy legt fest, welche Domänen auf welche Typen zugreifen dürfen, auf Objekte welcher Klassen und mit welchen Aktionen (permissions).

Eine TE-Regel hat die folgende Form:

allow source_type target_type:object_class { permission };
  • source_type
    • Dies ist der Typ des Subjekts. Bei einem Prozess ist das seine Domäne. Wenn ein Prozess in httpd_t, sshd_t oder passwd_t läuft, steht genau dieser Wert auf der linken Seite der TE-Prüfung.
  • target_type
    • Dies ist der Typ des Objekts, mit dem das Subjekt interagiert: zum Beispiel httpd_sys_content_t, tmp_t, shadow_t, user_home_t. Derselbe Prozess kann mit unterschiedlichen Objekten arbeiten, und die Prüfung wird für jedes Paar aus Domäne und Typ separat durchgeführt.
  • object_class
    • SELinux gruppiert Objekte in Klassen.
    • Bei einer Datei und einem Verzeichnis können sich die Berechtigungen beispielsweise unterscheiden. Daneben existieren auch Klassen wie filesystem, tcp_socket, netif, node und weitere. Jede Klasse besitzt ihre eigene Menge möglicher Permissions.
  • permission
    • Dies ist die konkrete Aktion, die erlaubt werden muss: read, write, open, search, getattr, create, append, rename usw.

Zugriff gewähren

Der Ablauf zur Gewährung eines Zugriffs auf ein Objekt sieht grundsätzlich wie folgt aus:

  • einen bereits vorhandenen geeigneten Typ für das Objekt auswählen
  • ein Boolean aktivieren*, das die benötigten konditionalen Regeln einschaltet
  • oder die Policy erweitern bzw. ändern, falls die Standard-Policy eine solche Interaktion nicht vorsieht (weil die erforderlichen Typen oder Booleans nicht existieren)

* Boolean ist ein logischer Schalter für bereits in der Policy vorhandene Regelsätze. Ausführlicher wird dieses Konzept in den folgenden Lektionen zu SELinux Booleans behandelt.


Arbeit mit Regeln mithilfe von sesearch

sesearch ist ein Werkzeug aus dem SETools-Paket zur Suche nach Regeln in der SELinux-Policy.

Verwendung

sesearch -A -s <SOURCE_TYPE> -t <TARGET_TYPE> -c <CLASS> -p <PERM>
Parameter Bedeutung
-A allow- und allowxperm-Regeln suchen
-s / --source source type oder source attribute*
-t / --target target type oder target attribute*
-c / --class Objektklasse
-p / --perm permission oder Liste von permissions

* Type Attributes. Nicht mit Dateiattributen verwechseln.

  • Das folgende Beispiel kann direkt im Terminal ausgeführt werden:
sesearch -A -s unconfined_t -t tmp_t -c file

Ausgabe:

# sesearch -A -s unconfined_t -t tmp_t -c file
allow files_unconfined_type file_type:file execmod; [ allow_execmod ]:True
allow files_unconfined_type file_type:file { append create execute execute_no_trans getattr ioctl link lock map mounton open quotaon read relabelfrom relabelto rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write };

Hier ist das Ergebnis einer Suche nach erlaubenden Regeln (allow) für ein Subjekt in der Domäne unconfined_t beim Zugriff auf Objekte des Typs tmp_t zu sehen, eingeschränkt auf die Objektklasse file.

  • Type Attributes. Statt konkreter Typen erscheinen hier files_unconfined_type und file_type. Dies sind Type Attributes – also Gruppen, in denen jeweils viele Typen zusammengefasst sind.
  • Die erste Zeile enthält eine separate Regel für execmod, die mit Speicherschutz zusammenhängt; darauf wird an dieser Stelle noch nicht näher eingegangen. Der Teil "[ allow_execmod ]:True" bedeutet, dass diese Regel nur aktiv ist, wenn das Boolean allow_execmod eingeschaltet ist.
  • Die zweite Zeile enthält eine erlaubende Regel für alle Typen, die zum Type Attribute files_unconfined_type gehören, und umfasst einen großen Satz von Operationen auf Dateien (also Objekten mit dem Attribut file_type und der Klasse file).
    • Die Aktionen stehen in geschweiften Klammern: { append create execute ... write }
  • Daraus folgt, dass ein Prozess in der Domäne unconfined_t seine Rechte über das Attribut files_unconfined_type erhält. Das bedeutet, dass jeder Prozess, der unter einem „unconfined“-Benutzerkontext läuft, praktisch umfassende Kontrolle über temporäre Dateien besitzt, da tmp_t zum globalen Attribut file_type gehört.

Wichtig ist, dass der Doppelpunkt hier nicht „Teil eines Typnamens“ bedeutet. Er trennt target und object_class.

Optionen

Wichtige sesearch-Optionen zusätzlich zu den oben gezeigten:

--allow, --dontaudit, --auditallow

  • Auswahl des Typs der zu suchenden Regeln, ähnlich wie -A
  • --allow – sucht nur nach allow
  • --auditallow – Regeln, die vorschreiben, erlaubte Aktionen zu protokollieren
  • --dontaudit – Regeln zur Unterdrückung der Protokollierung von Zugriffverweigerungen

-p, --perm

sesearch -A -s httpd_t -c file -p read
  • Standardmäßig sucht -p nach Regeln, die mindestens eine der angegebenen Permissions enthalten.
  • Für strengere Abfragen (am Beispiel read,open):
    • -ep read,open – findet nur Regeln, bei denen die Menge der Permissions genau der angegebenen Menge entspricht
    • -Sp read,open – findet Regeln, deren Permission-Menge eine Obermenge der angegebenen Menge ist. Das ist besonders nützlich, um Regeln zu finden, die die benötigten Rechte vollständig abdecken.

-b, --bool

sesearch -A -b httpd_read_user_content
  • Sucht nach conditional rules, die an ein Boolean gebunden sind.
    • Conditional rules werden im Abschnitt zu SELinux Booleans im Detail behandelt; hier genügt der Hinweis, dass es sich um bedingte Regeln im Sinne von if / else handelt.
  • Eine solche Anfrage ist nützlich, wenn bekannt ist, dass ein bestimmtes Verhalten von einem SELinux-Boolean gesteuert wird und sichtbar werden soll, welche Regeln dadurch beeinflusst werden.

-ds und -dt

Sehr wichtige Optionen für eine präzise Analyse:

  • -ds – verlangt, dass die Source explizit angegeben ist
  • -dt – verlangt, dass das Target explizit angegeben ist

Wie in den obigen Beispielen zu sehen ist, kann man ohne -ds / -dt Regeln erhalten, die über ein Attribut ermittelt wurden. Diese Optionen unterbinden die implizite Suche über Attribute.

-rs, -rt, -rc, -rd, -rb

sesearch -A -rs '.*httpd.*' -c file
  • Diese Optionen aktivieren Regexp-Matching für source, target, class, default beziehungsweise Boolean.

Praktische Aufgabe

Es soll geprüft werden, ob der Prozess sshd Dateien im Home-Verzeichnis eines Benutzers lesen darf.

  • Zuerst werden die Kontexte (Typen) der Dateien im Home-Verzeichnis angezeigt:
# ls -Z1
unconfined_u:object_r:user_home_t:s0 other
    system_u:object_r:user_home_t:s0 testfile

Die Benutzerdateien haben den Typ user_home_t; dieser Wert wird festgehalten.

  • Danach wird der Typ des Prozesses sshd betrachtet:
# ps -eZ | grep sshd
system_u:system_r:sshd_t:s0         592 ?        00:00:00 sshd

Der Prozess sshd hat den Typ sshd_t.

  • Anschließend erfolgt die Suche nach einer Regel in der Policy-Datenbank:
sesearch -A -s sshd_t -t user_home_t -c file -p read

...Hm, nichts gefunden! Müsste sshd nicht Zugriff auf das Verzeichnis ~/.ssh haben? Dann sollte der Typ des versteckten Verzeichnisses .ssh geprüft werden.

  • Prüfung:
# ls -dZ ~/.ssh
unconfined_u:object_r:ssh_home_t:s0 /root/.ssh

Der Dienst sshd benötigt für seine Arbeit keinen Zugriff auf beliebige Benutzerdateien, sondern nur auf das Verzeichnis .ssh.

  • Daher wird das Vorhandensein einer passenden Regel in der Policy geprüft:
sesearch -A -s sshd_t -t ssh_home_t -c file -p read

Ausgabe:

allow ssh_server ssh_home_t:file { getattr ioctl lock open read };

Damit lässt sich sicher feststellen, dass sshd auf Dateien im Typ ssh_home_t zugreifen darf, und zwar mit folgenden Rechten:

  • Lesen von Attributen (getattr) – der Prozess kann Metadaten der Datei abfragen, etwa Größe, Eigentümer, Berechtigungen oder Änderungszeit
  • Steuerung von I/O-Parametern (ioctl) – wird häufig von Bibliotheken für die korrekte Arbeit mit Datenströmen verwendet
  • Setzen von Sperren (lock) – sshd kann Dateisperren setzen; das ist wichtig, um gleichzeitige Schreibzugriffe mehrerer Prozesse zu verhindern
  • Öffnen der Datei (open) – selbst wenn ein Leserecht vorhanden ist, kann der Kernel ohne explizites open dem Prozess keinen Dateideskriptor für die weitere Arbeit bereitstellen

SCHRITT 3.3: Domain Transition

Nach der Analyse von allow wird sichtbar, dass SELinux zwei unterschiedliche Fragen beantwortet:

  1. Was darf ein bereits laufender Prozess tun?
  2. Welche Domäne soll dieser Prozess beim Start eines Programms überhaupt erhalten?

Die zweite Frage beantwortet der Mechanismus des Übergangs von einer Domäne in eine andere (domain transition).

Ein Domain Transition ist ein streng kontrolliertes Ereignis, das in der Regel beim Start eines neuen Programms stattfindet.

Regeln für den Übergang

Ein Domänenwechsel ist fast immer an den Moment gebunden, in dem der aktuelle Prozess die Ausführung einer neuen Binärdatei über den Systemaufruf execve() auslöst.

In diesem kritischen Moment analysiert SELinux drei Komponenten:

  1. Den Kontext des aktuellen Prozesses (Source Domain)
  2. Den Kontext der ausführbaren Datei (File Context / Entrypoint)
  3. Die Policy-Regeln, die den Übergang in eine neue Domäne erlauben (Target Domain)

Die Policy-Regel sieht wie folgt aus:

type_transition source_domain executable_type:process target_domain;
  • Beispiel:
type_transition init_t crond_exec_t:process crond_t;
  • Wenn ein Prozess in der Domäne init_t eine Datei vom Typ crond_exec_t ausführt, soll der neue Prozess in die Domäne crond_t wechseln.

Voraussetzungen für den Übergang in eine neue Domäne

Damit der Übergang tatsächlich stattfindet, verlangt SELinux nicht nur eine einzige Regel, sondern zusätzlich mindestens drei logische Bedingungen.

  • Die Quell-Domäne muss das Recht transition in die Ziel-Domäne besitzen.
  • Die ausführbare Datei muss aus der Quell-Domäne heraus ausgeführt werden dürfen.
  • Diese Datei muss für die Ziel-Domäne als entrypoint zugelassen sein.

Erforderliche Regeln (drei erlaubende Regeln plus die eigentliche Transition-Regel):

allow source_domain target_domain:process transition;
allow source_domain executable_type:file { execute };
allow target_domain executable_type:file entrypoint;
type_transition source_domain executable_type:process target_domain;

Entrypoint

Damit eine Datei einen Prozess in eine neue Domäne überführen kann, muss sie einen speziellen Typ besitzen, der in der Policy als Entrypoint für die Ziel-Domäne definiert ist.

Reales Beispiel

  • Die Datei /usr/bin/passwd besitzt den Typ passwd_exec_t.
  • Genau dieser Typ ist in der Policy als entrypoint für die Domäne passwd_t erlaubt.
  • Deshalb führt die Ausführung von passwd dazu, dass der Prozess in passwd_t wechselt.
  • Für die Domäne passwd_t existiert dann bereits die Berechtigung, mit Dateien des Typs shadow_t zu arbeiten, zum Beispiel mit /etc/shadow.

Der entrypoint-Mechanismus schützt das System vor dem Fall, dass ein Angreifer versucht, ein eigenes schädliches Skript zu starten, um sich darüber Zugang zu einer geschützten Domäne zu verschaffen.


Domain Transition dient der Isolation von Privilegien. Ein Prozess erhält nicht im Voraus alle denkbaren Rechte. Stattdessen gelangt er nur über eine streng definierte ausführbare Datei in eine spezialisierte Domäne.


Einschränkung von Übergängen

Die Begrenzung unerwünschter Domain Transitions basiert auf den folgenden Prinzipien:

  1. Harte Adressierung der Regeln

Betrachtet wird die folgende Regel:

type_transition unconfined_t mysvc_exec_t:process mysvc_t;
  • In diesem Fall gilt die Transition nur für die Ausführung von Dateien des Typs mysvc_exec_t aus der Domäne unconfined_t.
  1. Die Ziel-Domäne definiert selbst über entrypoint, über welche Dateien sie überhaupt betreten werden darf.
    • Fremde Binärdateien können ohne eine passende entrypoint-Regel keinen nicht autorisierten Zugang erhalten.
  2. Der Übergang muss eindeutig sein.
    • Die SELinux-Policy erlaubt keine zwei unterschiedlichen Ziel-Domänen für dieselbe Kombination aus source domain + executable type + process.
    • Ein Policy-Kompilierungsfehler verhindert die Definition zweier verschiedener Ziel-Domänen für denselben Fall.

Anzeige von type_transition-Regeln

Zur Anzeige von Transition-Regeln wird die bereits bekannte sesearch-Option -T verwendet.

  • Beispiel:
sesearch -T -t passwd_exec_t -c process

Ausgabe:

# sesearch -T -t passwd_exec_t -c process
type_transition accountsd_t passwd_exec_t:process passwd_t;
type_transition auditadm_t passwd_exec_t:process passwd_t;
type_transition guest_t passwd_exec_t:process passwd_t;
type_transition secadm_t passwd_exec_t:process passwd_t;
type_transition smbd_t passwd_exec_t:process passwd_t; [ samba_domain_controller ]:True
type_transition staff_t passwd_exec_t:process passwd_t;
type_transition sysadm_t passwd_exec_t:process passwd_t;
type_transition user_t passwd_exec_t:process passwd_t;
type_transition xguest_t passwd_exec_t:process passwd_t;

Wenn ein Prozess aus der Domäne user_t (gewöhnlicher Benutzer), staff_t oder sogar guest_t eine Datei mit dem Typ passwd_exec_t ausführt, muss der Kernel versuchen, den Kontext des neuen Prozesses auf passwd_t umzuschalten.

  • Zusätzlich kann geprüft werden, ob die Transition nach passwd_t selbst erlaubt ist:
sesearch -A -t passwd_t -c process -p transition

Ausgabe:

# sesearch -A -t passwd_t -c process -p transition
allow accountsd_t passwd_t:process transition;
allow auditadm_t passwd_t:process transition;
allow guest_t passwd_t:process transition;
allow passwd_t passwd_t:process { dyntransition fork getattr getcap getpgid getrlimit getsched getsession noatsecure rlimitinh setcap setfscreate setkeycreate setpgid setrlimit setsched setsockcreate share sigchld siginh sigkill signal signull sigstop transition };
allow secadm_t passwd_t:process transition;
allow smbd_t passwd_t:process transition; [ samba_domain_controller ]:True
allow staff_t passwd_t:process transition;
allow sysadm_t passwd_t:process transition;
allow user_t passwd_t:process transition;
allow xguest_t passwd_t:process transition;

Die zweite sesearch -A-Ausgabe bestätigt, dass all diese Domänen (Source Domains) eine offizielle Berechtigung für diesen Übergang besitzen.

  • Abschließend wird noch das entrypoint-Recht geprüft:
sesearch -A -s passwd_t -t passwd_exec_t -c file -p entrypoint

Ausgabe:

# sesearch -A -s passwd_t -t passwd_exec_t -c file -p entrypoint
allow passwd_t passwd_exec_t:file { entrypoint execute getattr ioctl lock map open read };

Die Logik dieser Regel ist die folgende: Der automatische Übergang in die Domäne passwd_t ist nur dann gültig, wenn er über eine ausführbare Datei des Typs passwd_exec_t ausgelöst wurde, die für diese Domäne als entrypoint definiert ist.

Zusätzlich sind die Standardrechte für die Programmausführung sichtbar (execute, read, open). Dadurch kann die Domäne passwd_t ihren eigenen Programmcode korrekt lesen und ausführen, nachdem die Kontrolle an sie übergeben wurde.


Wann kein Übergang stattfindet

Nicht jeder Programmstart führt zu einem Domänenwechsel. Wenn für das Paar Quell-Domäne + Typ der ausführbaren Datei in der Policy keine passende type_transition-Regel existiert, bleibt der Prozess in der Regel in seiner bisherigen Domäne.

Das ist jedoch nur dann möglich, wenn die Policy die Ausführung der Datei ohne Domänenwechsel erlaubt, also über die Permission execute_no_trans.

Mit anderen Worten gibt es bei execve() zwei mögliche Varianten:

  1. Übergang in eine neue Domäne
    • wenn eine type_transition-Regel existiert
    • und alle notwendigen Bedingungen erfüllt sind (transition, entrypoint, Berechtigung zur Ausführung der Datei)
  2. Ausführung ohne Übergang
    • wenn kein Übergang definiert ist
    • die aktuelle Domäne aber execute_no_trans für diesen Dateityp besitzt

Wenn die Policy hingegen weder eine Transition noch eine Ausführung ohne Übergang erlaubt, wird der Programmstart verweigert.

SCHRITT 3.4: Type Attributes und seinfo

In SELinux arbeitet die Policy nur selten direkt mit einzelnen Typen. Stattdessen wird die Abstraktion der Type Attributes verwendet – also logische Gruppen von Typen, die nach funktionalen Kriterien zusammengefasst sind.

Zur Anzeige von Informationen über Attribute und die darin enthaltenen Typen dient das Werkzeug seinfo.

  • Es wird geprüft, welche Typen das Attribut httpdcontent enthält:
seinfo -a httpdcontent -x

Ausgabe:

# seinfo -a httpdcontent -x

Type Attributes: 1
   attribute httpdcontent;
        httpd_apcupsd_cgi_content_t
        httpd_apcupsd_cgi_ra_content_t
        httpd_apcupsd_cgi_rw_content_t
        httpd_awstats_content_t
        httpd_awstats_ra_content_t
        ...
        httpd_user_ra_content_t
        httpd_user_rw_content_t
        httpd_webalizer_content_t
        httpd_webalizer_ra_content_t
        httpd_webalizer_rw_content_t

Dank der Type Attributes kann eine einzige Regel formuliert werden, anstatt für jeden einzelnen Typ eine separate Regel definieren zu müssen.

Damit wirken Type Attributes wie ein Mechanismus der „Vererbung“ von Regeln.

Effective Permissions

Aus dem bisher Gesagten wird deutlich, dass die Rechte einer Domäne nicht nur von direkten allow-Regeln abhängen, sondern auch von Regeln, die Typen indirekt über Attribute erhalten.

  • Bei der Analyse solcher geerbten Regeln hilft das bereits bekannte Werkzeug sesearch. In der Standardverwendung löst es Attribute auf. Das lässt sich mit dem folgenden Befehl nachvollziehen:
sesearch -A -s httpd_t -t httpd_sys_content_t -c file -p read
  • Nun derselbe Aufruf mit Parametern, die eine Auflösung über Attribute unterbinden:
sesearch -A -s httpd_t -t httpd_sys_content_t -c file -p read -ds -dt

seinfo

seinfo ist ein Werkzeug zur Inventarisierung und strukturellen Analyse einer SELinux-Policy. Während sesearch die Frage beantwortet, „gibt es eine solche Regel?“, beantwortet seinfo die Frage: „aus welchen Entitäten besteht die geladene Policy?“

Die Syntax sieht wie folgt aus:

seinfo [OPTIONS] [EXPRESSION] [POLICY]

Zentrale Einsatzgebiete von seinfo:

  • Prüfung, ob ein Typ, Attribut, eine Rolle, ein Benutzer oder ein Boolean existiert
  • Suche nach den Attributen, zu denen ein bestimmter Typ bzw. eine bestimmte Domäne gehört
  • Ermittlung, welche Typen in einem bestimmten Attribut enthalten sind
  • Anzeige, welche Rollen und Benutzer in der Policy überhaupt definiert sind
  • Überblick über die Struktur der Policy insgesamt

Optionen

Aufruf ohne Optionen

seinfo
  • Liefert eine Zusammenfassung. Angezeigt werden die Anzahl von Typen, Attributen, Rollen, Benutzern, Booleans und weiteren Policy-Objekten.

-x, --expand

seinfo -t sshd_t -x
seinfo -a file_type -x
  • Die wichtigste Option
  • Aktiviert eine erweiterte Ausgabe
  • Bei Typen bedeutet dies in der Regel, dass die Mitgliedschaft in Attributen sichtbar wird.
  • Bei Attributen wird die Liste der enthaltenen Mitglieder ausgegeben.

-t [TYPE], --type [TYPE]

seinfo -t httpd_t -x
  • Fordert Informationen zu einem Typ an, also insbesondere, in welchen Attributen dieser Typ enthalten ist.

-a [ATTR], --attribute [ATTR]

seinfo -a daemon -x
  • Fordert Informationen zu einem Attribut an, also welche Typen diesem Attribut zugeordnet sind.

-r [ROLE], --role [ROLE]

  • Abfrage einer Rolle

-u [USER], --user [USER]

  • Abfrage eines Benutzers

-b [BOOL], --bool [BOOL]

  • Abfrage eines Booleans

POLICY als Argument

Es kann nicht nur die aktuell aktive Policy analysiert werden, sondern auch eine konkrete Policy-Datei.

seinfo -t httpd_t -x /etc/selinux/targeted/policy/policy.*

Schritt 3.5: SELinux Booleans

In den vorherigen Etappen wurde deutlich, dass die Rechte von Domänen fest in der Policy definiert sind. Doch was ist zu tun, wenn das Standardverhalten eines Dienstes leicht angepasst werden soll? Zum Beispiel dann, wenn dem Apache-Webserver erlaubt werden soll, sich mit einer entfernten Datenbank zu verbinden oder E-Mails zu versenden.

Dafür ein eigenes Policy-Modul von Grund auf zu schreiben, ist oft zu aufwendig und für Standardfälle unnötig. Für genau solche typischen Szenarien stellen die SELinux-Entwickler Booleans bereit.

Booleans sind in die Policy integrierte Schalter (on/off), mit denen bestimmte Regelblöcke (Conditional Rules) innerhalb der bereits kompilierten Policy aktiviert oder deaktiviert werden.

Ein und derselbe Prozess in httpd_t, smbd_t oder einer anderen Service-Domäne kann nach dem Umschalten eines Boolean zusätzlichen Zugriff auf Objekttypen erhalten, die in der Policy bereits vorgesehen sind.


Regeln, die mit booleans arbeiten, besitzen einen Zusatzblock in eckigen Klammern:

allow httpd_t user_home_t:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True
  • [ httpd_read_user_content ]:True bedeutet, dass sich diese Regel im if-Zweig befindet. Sie ist nur dann aktiv, wenn der Schalter httpd_read_user_content auf ON steht.
  • [ httpd_read_user_content ]:False bedeutet, dass sich diese Regel im else-Zweig befindet. Sie ist nur dann aktiv, wenn der Schalter httpd_read_user_content auf OFF steht.

Werkzeuge

getsebool

Wird verwendet, um den aktuellen Zustand eines Boolean anzuzeigen.

  • Alle aktuellen Booleans anzeigen:
getsebool -a
  • Einen einzelnen Boolean anzeigen:
getsebool httpd_use_nfs

setsebool

Wird verwendet, um den Zustand eines Boolean zu ändern.

  • Boolean einschalten (temporär):
setsebool ftpd_anon_write on
  • Boolean einschalten (persistent):
setsebool -P ftpd_anon_write on
  • Wenn mehrere Booleans gesetzt werden, müssen sie in der Form parameter=wert angegeben werden:
setsebool ftpd_anon_write=on ftpd_use_passive_mode=on

semanage boolean

Wird verwendet, um Boolean-Einstellungen als Teil der Policy-Konfiguration zu verwalten. Außerdem lassen sich damit eine menschenlesbare Beschreibung jedes Schalters sowie dessen Standardwert anzeigen.

  • Alle Booleans mit Beschreibung und Werten anzeigen (aktuell und Standard):
semanage boolean -l
  • Beschreibung eines bestimmten Boolean suchen:
semanage boolean -l | grep httpd
  • Zustand eines Boolean ändern (funktional ähnlich zu setsebool -P):
semanage boolean -m --on ftpd_anon_write

Praktisches Beispiel

Aufgabe

Es soll erreicht werden, dass die Web-Anfrage mit curl eine Datei lesen kann, die als auf NFS liegend gelabelt ist – und zwar ausschließlich mithilfe des Boolean-Mechanismus.

Lösung

  • Vorbereitung der Umgebung:
mkdir -p /var/www/html/nfs_test
echo "Hello from NFS" > /var/www/html/nfs_test/index.html
chcon -R -t nfs_t /var/www/html/nfs_test
setenforce 1 # Enforcing-Modus aktivieren
  • Versuch, die Seite aus dem NFS-Verzeichnis mit curl abzurufen:
curl -I http://localhost/nfs_test/index.html

Ergebnis:

# curl -I http://localhost/nfs_test/index.html
HTTP/1.1 403 Forbidden
...
  • Relevante Rechte und zugehörige Booleans prüfen:
semanage boolean -l | grep httpd
semanage boolean -l | grep nfs
sesearch -A -s httpd_t -t nfs_t -c file -p read

Mithilfe von sesearch lässt sich der Lösungsweg bestimmen:

# sesearch -A -s httpd_t -t nfs_t -c file -p read
allow httpd_t nfs_t:file { execute execute_no_trans getattr ioctl map open read }; [ ( httpd_builtin_scripting && use_nfs_home_dirs && httpd_enable_homedirs ) ]:True
allow httpd_t nfs_t:file { execute execute_no_trans getattr ioctl map open read }; [ httpd_builtin_scripting && httpd_use_nfs ]:True
allow httpd_t nfs_t:file { execute getattr ioctl map open read }; [ httpd_use_nfs && httpd_enable_cgi ]:True
allow httpd_t nfs_t:file { getattr ioctl lock open read }; [ use_nfs_home_dirs && httpd_enable_homedirs ]:True

Es ist zu erkennen, dass in der Liste unter anderem httpd_use_nfs vorkommt, dieses Boolean allein jedoch nicht ausreicht, damit eine passende Regel aktiv wird. Deshalb wird entschieden, httpd_builtin_scripting als zweiten Aktivierungsfaktor zu verwenden.

  • Den aktuellen Zustand von httpd_use_nfs und httpd_builtin_scripting prüfen:
getsebool httpd_use_nfs httpd_builtin_scripting
  • Beide Booleans testweise temporär aktivieren:
setsebool httpd_use_nfs=on httpd_builtin_scripting=on
  • Erneute Prüfung:
curl -I http://localhost/nfs_test/index.html

Ergebnis:

HTTP/1.1 200 OK
...

Erfolgreich!

  • Wenn das Verhalten den Erwartungen entspricht, können die Änderungen persistent gespeichert werden:
setsebool -P httpd_use_nfs=on httpd_builtin_scripting=on

SCHRITT 3.6: Einschränkungen oberhalb von allow

In den vorherigen Schritten konnte der Eindruck entstehen, dass das Vorhandensein einer allow-Regel automatisch zu einem erfolgreichen Zugriff führt. In SELinux können jedoch oberhalb von allow zusätzliche Mechanismen greifen.

Nach der normalen TE-Prüfung kann der Kernel weitere Einschränkungen anwenden: constrain / validatetrans, spezielle Prüfungen für ausführbaren Speicher, globale policycap-Schalter sowie die Logik von no_new_privs. Deshalb tritt in der praktischen Diagnose häufig die Situation auf: Eine allow-Regel ist vorhanden, die Operation wird aber trotzdem verweigert.

Constraints (Einschränkungen)

Ein constraint ist eine zusätzliche Bedingung, die zusätzlich zu einer bereits gefundenen allow-Regel erfüllt sein muss. Ein constraint gewährt keine neuen Rechte, sondern schränkt den Geltungsbereich einer bereits vorhandenen allow-Regel weiter ein.

constrain { object_class_list } { permissions } ( expression );

Beispiel

allow unconfined_t ext_gateway_t:process transition;
constrain process transition ( r1 == r2 );
  • Die Transition-Regel besagt, dass der Übergang grundsätzlich erlaubt ist.
  • Das constraint fügt eine weitere zwingende Bedingung hinzu: Der Übergang ist nur zulässig, wenn die Rolle der Quelle mit der Rolle des Ergebnisses übereinstimmt.

validatetrans (validate transition): Eine spezielle Form von Einschränkungen, die nur bei relabel-Operationen greift. Dabei werden gleichzeitig drei Parameter ausgewertet: der alte Dateikontext, der neue Dateikontext und der Kontext des Prozesses, der die Änderung vornimmt.

validatetrans schützt das System davor, dass ein kompromittierter Prozess mit dem Recht relabel eine normale Textdatei in eine ausführbare Binärdatei (bin_t) oder in eine Passwortdatei (shadow_t) umwandelt. Für solche Aktionen wird in der Regel das Vorhandensein eines speziellen Attributs verlangt, etwa can_change_system_roles.

validatetrans { class_id } ( expression );

Anzeige

Das Werkzeug seinfo kann sowohl normale constraints als auch validatetrans-Regeln anzeigen.

seinfo --constrain process
seinfo --validatetrans file

Memory Protection (Speicherschutz)

SELinux ist tief in das Speicherverwaltungssubsystem des Kernels integriert, um vor der Ausnutzung von Schwachstellen wie etwa Buffer Overflows zu schützen. Selbst wenn ein Prozess eine Datei ausführen darf, kann SELinux bestimmte Speicheroperationen trotzdem verbieten.

  • Für die Klasse process definiert SELinux unter anderem die folgenden Permissions:
    • execmem – erlaubt es, eine anonyme oder privat eingeblendete writable memory mapping als ausführbar zu markieren
    • execstack – erlaubt es, den Haupt-Stack des Prozesses als ausführbar zu markieren
    • execheap – erlaubt es, den Heap-Speicher des Prozesses als ausführbar zu markieren
  • Für die Klasse file existiert die Permission execmod, die typischerweise mit der Ausführung einer im Speicher modifizierten Dateiabbildung zusammenhängt.
  • Zusätzlich gibt es die Klasse memprotect mit der Permission mmap_zero, die Versuche kontrolliert, per mmap() in den niedrigen Adressraum zu mappen.

Damit kontrolliert SELinux nicht nur den Zugriff auf Dateien oder das Recht auf einen Domain Transition, sondern auch die Sicherheit des Ausführungsmodells von Code im Speicher.

Deshalb kann ein Programm zwar berechtigt sein, eine Bibliothek zu lesen und sogar zu starten, dennoch aber einen Denial erhalten – etwa wegen des Versuchs, ein writable+executable Mapping zu erzeugen, Code vom Stack auszuführen, einen ausführbaren Heap zu verwenden oder eine modifizierte Dateimapping zu laden.

Anzeige

Zur Analyse von Problemen mit Speicherschutzmechanismen wie execmem werden in erster Linie die zugehörigen Regeln geprüft; zusätzlich kann auch ein relevantes Boolean abgefragt werden:

sesearch -A -s DOMAIN_T -c process -p execmem,execstack,execheap
sesearch -A -s DOMAIN_T -t FILE_TYPE_T -c file -p execmod
getsebool -a | grep execmem

Policy capabilities

Policy capabilities (policycap) sind globale Fähigkeiten der geladenen Policy, also eingebaute Schalter für bestimmte Betriebsmodi von SELinux. Sie ermöglichen es der Policy, beim Hinzukommen neuer Kernel-Funktionen abwärtskompatibel zu bleiben.

Beispiele

  • network_peer_controls: Aktiviert moderne Prüfungen für Netzwerkpakete.
  • open_perms: Aktiviert die Prüfung der Permission open für Dateien; früher wurden nur read und write geprüft.
  • always_check_network: Erzwingt, dass SELinux Netzwerk-Labels und netzwerkbezogene SELinux-Prüfungen immer als aktiv behandelt, auch wenn die entsprechenden Netzwerkmechanismen aktuell nicht explizit konfiguriert sind.

Anzeige

seinfo --polcap -x

No New Privileges (NNP)

Das Flag no_new_privs (NNP) ist ein Mechanismus des Linux-Kernels, der seit Kernel-Version 3.5 existiert. Jeder Prozess kann dieses Bit setzen. Danach wird es über fork, clone und execve vererbt und kann nicht mehr zurückgesetzt werden.

Für SELinux ist das besonders wichtig, weil auch ein Domain Transition bei execve() als Privilegienänderung betrachtet werden kann. Im aktuellen SELinux-Modell sind bei aktivierter Policy Capability nnp_nosuid_transition für bestimmte Domain Transitions zusätzliche Permissions erforderlich:

  • process2:nnp_transition – wenn der Thread mit no_new_privs läuft
  • process2:nosuid_transition – wenn der Übergang von einem mit nosuid gemounteten Dateisystem ausgeht
    • Wenn die Capability nnp_nosuid_transition deaktiviert ist, werden solche Übergänge üblicherweise durch bounded transitions eingeschränkt; darauf wird etwas später noch eingegangen.

typebounds

Ein bounded transition ist ein Domain Transition, bei dem die neue Domäne als eingeschränkte Version der alten Domäne gilt und keine Privilegien über die der Ausgangsdomäne hinaus erhält. Das bedeutet: Der Kernel erlaubt einen Domain Transition bei aktivem NNP, wenn in der Policy explizit definiert ist, dass die neue Domäne in ihren Rechten nicht weiter reicht als die alte.

Dies wird über die Regel typebounds festgelegt.

typebounds SRC_T DST_T;

Wenn eine solche Regel existiert, versteht der Kernel, dass ein Prozess beim Wechsel nach DST_T keine Rechte erhält, die er in SRC_T nicht bereits hatte, und erlaubt die Operation deshalb auch mit gesetztem no_new_privs-Flag.

Prüfung, ob typebounds vorhanden ist:

seinfo --typebounds -x

Beispiel

Wenn in der Policy die folgende Regel vorhanden ist:

allow SRC_T DST_T:process transition;
  • dann ist für einen Prozess mit NNP zusätzlich die folgende Regel erforderlich:
allow SRC_T DST_T:process2 nnp_transition;

Anzeige

  • Standard-Domain-Transition prüfen:
sesearch -A -s SRC_T -t DST_T -c process -p transition
  • Erlaubnis für den Übergang mit NNP prüfen:
sesearch -A -s SRC_T -t DST_T -c process2 -p nnp_transition
  • Erlaubnis für den Übergang von einem nosuid-gemounteten Dateisystem prüfen:
sesearch -A -s SRC_T -t DST_T -c process2 -p nosuid_transition

audit2why

An dieser Stelle ist es sinnvoll, auch das Werkzeug audit2why zu erwähnen, das bei der Analyse komplexer Situationen und von AVC-Denials hilfreich sein kann.

audit2why liest Meldungen aus dem audit.log und versucht, die Ursache einer Verweigerung in menschenlesbarer Form zu erklären, anstatt nur rohe AVC-Einträge anzuzeigen.

Die praktische Verwendung beschränkt sich im Wesentlichen auf die folgenden Varianten:

  • Alle AVC-Meldungen anzeigen:
audit2why -a
  • Einen benutzerdefinierten Satz von AVCs über ausearch an audit2why weitergeben:
ausearch -m AVC,USER_AVC,SELINUX_ERR -ts recent -i | audit2why
ausearch -m AVC,USER_AVC,SELINUX_ERR -c httpd -i | audit2why

audit2why kann Zeit sparen oder zumindest einen Hinweis darauf geben, in welche Richtung bei der Problemlösung weiter analysiert werden sollte.

SCHRITT 3.7: Dateierzeugung durch Prozesse

Standardmäßig gilt in SELinux eine einfache Vererbungsregel: Eine neu erzeugte Datei erhält den Typ des Verzeichnisses, in dem sie erstellt wird. Wenn zum Beispiel ein Webserver (httpd_t) eine temporäre Datei in /tmp (tmp_t) anlegt, erhält diese Datei standardmäßig ebenfalls den Typ tmp_t. Das ist jedoch oft weder sicher noch praktikabel, weil auch andere Prozesse Zugriff auf tmp_t haben können. Wie lässt sich erreichen, dass der Webserver seine temporären Dateien automatisch mit dem passenden Typ versieht, etwa httpd_tmp_t?

Das manuelle Setzen eines Labels mit chcon skaliert aus drei Gründen schlecht:

  • chcon löst das Problem nur für ein bereits existierendes Objekt, nicht aber für die nächste Datei, die der Dienst eine Sekunde später erzeugt
  • Das gesetzte Label ist temporär und kann durch restorecon oder ein vollständiges Relabeling wieder überschrieben werden
  • Die Logik des Labelings wird aus der Policy in manuelle Administratoraktionen verlagert, was fast immer zu Konfigurationsdrift und schwer diagnostizierbaren Fehlern führt

File Transitions

SELinux löst dieses Problem elegant durch automatische Kernel-seitige Labelvergabe im Moment der Dateierzeugung (policy-based labeling). Die Grundidee ist, dass die Policy nicht eine bereits existierende Datei steuert, sondern das Ergebnis der Erzeugungsoperation.

Dafür wird eine Regel vom Typ File Type Transition verwendet. Sie sagt dem Kernel: Wenn ein Prozess in Domäne X ein Objekt der Klasse Y in einem Verzeichnis des Typs Z erstellt, muss diesem Objekt automatisch der Typ W zugewiesen werden.

type_transition <domain> <parent_dir_type>:<class> <new_type>;

Beispiel

type_transition httpd_t tmp_t:file httpd_tmp_t;

Wenn der Webserver (httpd_t) in einem Verzeichnis vom Typ tmp_t eine Datei (file) erzeugt, muss dieser Datei unmittelbar bei der Erzeugung das Label httpd_tmp_t zugewiesen werden.

Wie zu sehen ist, beginnt die Regel ebenso mit type_transition wie eine Domain-Transition-Regel. Der wesentliche syntaktische Unterschied besteht jedoch darin, dass hier explizit eine Objektklasse wie file angegeben wird und nicht die Prozessklasse.

Wichtig: type_transition legt nur das Label des neuen Objekts fest. Die Regel vergibt keine Rechte wie create, add_name, write usw.

Einschränkungen

Ein gewöhnlicher File Transition ist dann sinnvoll, wenn alle Objekte einer bestimmten Klasse, die in einem bestimmten Container erzeugt werden, denselben Typ erhalten sollen.

Wenn ein Dienst in seinem Runtime-Verzeichnis beispielsweise immer Hilfsdateien mit derselben Funktion erzeugt, ist kein Dateiname erforderlich: Domäne des Prozesses, Verzeichnistyp und Objektklasse reichen aus.

Das Problem beginnt dann, wenn derselbe Prozess im selben Verzeichnis mehrere unterschiedliche Objekte erzeugt, die verschiedene SELinux-Typen erhalten sollen.

Named File Transition

Ein named file transition ist derselbe type_transition, jedoch mit einer zusätzlichen Bedingung: Der Parameter basename des neu erzeugten Objekts muss mit dem angegebenen Namen übereinstimmen.

type_transition <domain> <parent_dir_type>:<class> <new_type> "<basename>";

Ein named transition vergleicht nicht den Pfad, sondern ausschließlich den Basename.

  • Der Vergleich erfolgt als exakte Übereinstimmung. Der Name muss Zeichen für Zeichen identisch sein
  • Reguläre Ausdrücke und Wildcard-Symbole werden hier nicht unterstützt
  • Wie beim normalen File Transition betrifft der Mechanismus nur die initiale Erzeugung eines Objekts und korrigiert keine bereits existierenden falsch gelabelten Dateien

Beispiel

type_transition unconfined_t admin_home_t:dir ssh_home_t ".ssh";

Wenn ein Prozess vom Typ unconfined_t innerhalb eines Verzeichnisses vom Typ admin_home_t ein Verzeichnis mit dem Namen .ssh anlegt, erhält das neue Verzeichnis den Typ ssh_home_t.

Wichtiger Hinweis

<u>Wenn ein Label restorecon oder setfiles überstehen soll, muss es zusätzlich auf Ebene der file_contexts abgebildet sein.</u>

Arbeit mit File Transitions

  • Hinsichtlich des Geltungsbereichs ist wichtig, dass file transition-Regeln nicht nur für file und dir gelten, sondern auch für andere Objektklassen, etwa sock_file, fifo_file, chr_file oder blk_file
  • Für die Suche nach Regeln und die Diagnose von Problemen mit File Transitions ist sesearch mit der Option -T besonders nützlich:
sesearch -T -s <source_domain> -t <parent_dir_type>
  • Beispiel einer Prüfung:
# sesearch -T -s user_t -t tmp_t
type_transition user_t tmp_t:dir user_tmp_t;
type_transition user_t tmp_t:fifo_file user_tmp_t;
type_transition user_t tmp_t:file user_tmp_t;
type_transition user_t tmp_t:lnk_file user_tmp_t;
type_transition user_t tmp_t:sock_file user_tmp_t;

Interpretation der Ausgabe: Wenn ein Prozess in der Domäne user_t in einem Verzeichnis vom Typ tmp_t ein Objekt erzeugt – etwa eine Datei, ein Verzeichnis, einen Link oder ein Socket –, dann soll diesem Objekt der Typ user_tmp_t zugewiesen werden.

SCHRITT 3.8: restorecond, der Hintergrund-Korrektor für Labels

In dieser Lektion wird der Dienst restorecond behandelt – ein Hintergrundmechanismus zur automatischen Wiederherstellung von Labels. In modernen Linux-Distributionen wird dieser Daemon in der Regel nicht standardmäßig installiert, und viele seiner Aufgaben werden heute effizient direkt im Kernel über File-Transition-Regeln sowie durch eingebaute Mechanismen von systemd gelöst. Dennoch ist das Verständnis von restorecond notwendig: Das Werkzeug taucht weiterhin in bestimmten Spezialfällen auf, und seine Funktionsweise ist für korrektes Troubleshooting von zentraler Bedeutung.

restorecond

restorecond (Restore Context Daemon) ist ein Hintergrundprozess (Dienst), der bestimmte Dateien und Verzeichnisse in Echtzeit überwacht, und zwar mithilfe der Kernel-Subsystems inotify.

Wenn in einem überwachten Verzeichnis eine neue Datei erscheint oder ein Objekt in ein überwachten Verzeichnis verschoben wird, prüft restorecond sofort dessen Label. Wenn das aktuelle Label nicht mit dem in der SELinux-Referenzdatenbank (file_contexts) hinterlegten Label übereinstimmt, setzt der Daemon automatisch das korrekte Label zurück.

restorecond überwacht nicht das gesamte Dateisystem – das würde die Server-Performance massiv beeinträchtigen. Stattdessen beobachtet der Dienst nur die Pfade, die explizit in seinen Konfigurationsdateien eingetragen sind:

  • /etc/selinux/restorecond.conf – die systemweite Konfigurationsdatei. Hier werden typischerweise für das System kritische Pfade eingetragen, in denen Benutzer oder Skripte häufig Dateien mit falschen Labels erzeugen. (Beispiele: /etc/resolv.conf, /root/*, /var/run/*)
  • /etc/selinux/restorecond_user.conf – die benutzerspezifische Konfiguration. Sie wird verwendet, um Dateien in Home-Verzeichnissen zu überwachen, zum Beispiel SSH-Schlüssel (~/.ssh/*) oder Dateien unter ~/.public_html/, damit Webserver oder SSH-Daemonen nicht durch fehlerhafte Labels den Zugriff verlieren.

Für die Wiederherstellung der Labels verwendet restorecond denselben Mechanismus wie restorecon.

Manuelles Setzen von Labels und restorecond

Das manuelle Setzen eines Labels auf einem überwachten Pfad kann in mindestens drei Situationen instabil sein:

  • Der Daemon restorecond wird gestartet oder liest seine Konfiguration erneut ein
  • Die Datei wird unter demselben Namen neu erzeugt
  • Die Datei wird atomar ersetzt – etwa per Umbenennung oder Verschieben (mv) in ein überwachtes Verzeichnis. Viele Programme aktualisieren Dateien nicht durch direktes Überschreiben, sondern nach dem Muster „temporäre Datei erzeugen und anschließend per move über die alte Datei legen“. In einem solchen Fall wird die Label-Wiederherstellung durch restorecond ausgelöst.

Dadurch kann das Verhalten auftreten, dass ein Administrator ein Label mit chcon setzt, das Label aber kurze Zeit später wieder auf den ursprünglichen Zustand zurückgesetzt wird. In einer solchen Situation sollte zuerst geprüft werden, ob der Dienst restorecond im System aktiv ist.

Funktionsweise

  • Zunächst liest restorecond seine Konfiguration und bestimmt für jeden Eintrag:
    • das Verzeichnis, das überwacht werden soll
    • den Dateinamen oder das Basename-Muster, mit dem eingehende Ereignisse verglichen werden
  • Danach setzt restorecond einen inotify-Watch auf das Verzeichnis

Wichtig ist, dass restorecond nicht an der Kernel-Logik der Label-Vergabe im Moment der Dateierzeugung beteiligt ist. Zuerst wird das Objekt erzeugt, danach trifft ein inotify-Ereignis ein, und erst anschließend ruft der Daemon für den konkreten Pfad den restorecon-Mechanismus auf. Genau das ist der grundlegende Unterschied zu file transition, das bereits im Kernel während der Erzeugung eines Objekts greift.


Pfadvergleich

Der Vergleich erfolgt anhand der letzten Pfadkomponente, weil Verzeichnis und Basename getrennt verarbeitet werden. Das bedeutet, dass Muster mit * nur in der letzten Komponente funktionieren:

/root/.ssh/*
~/public_html/*

Anzeige

  • Status des Dienstes über systemd prüfen:
systemctl status restorecond
  • Vorhandensein des Prozesses und seine SELinux-Domäne prüfen:
ps -eZ | grep restorecond_t
  • restorecond-Konfiguration anzeigen:
cat /etc/selinux/restorecond.conf