L’autenticazione tramite PAM può riuscire quando si esegue il client a mano e fallire con AUTH_FAILED non appena systemd avvia lo stesso profilo. La password non cambia tra queste esecuzioni; ciò che cambia è l’insieme di vincoli attorno al processo che pone la domanda a PAM. PAM risponde tramite un helper la cui capacità di cambiare le proprie credenziali è esattamente ciò che un’unità indurita rimuove. Trovate quell’helper e il confine di cui ha bisogno prima di toccare un elenco di capacità.

unix_chkpwd è il binario che risponde alla domanda di PAM

Con l’autenticazione locale tramite password, pam_unix raramente legge il file hash stesso. Esegue unix_chkpwd, che le distribuzioni installano come setuid root o setgid shadow perché una password possa essere verificata per conto di un processo che non ha nulla a che fare con la lettura di /etc/shadow. Togliete la capacità di ottenere quell’identità e l’helper non può completare il controllo, quindi il modulo restituisce un semplice errore di autenticazione.

Il fallimento allora appare come un problema di credenziali in ogni log che il client può vedere. È la trappola: il client riporta ciò che PAM gli ha detto, e PAM riporta solo che lo stack ha detto no. Eseguire l’helper a mano come root non dimostra nulla neppure esso, perché esercita un percorso di privilegi che il servizio non usa mai. Leggete l’unità che systemd ha effettivamente caricato, poi il log di sistema per il tentativo compiuto. Queste due viste separano un problema di sandbox da un problema di password sbagliata.

Leggete l’unità prima di leggere il log delle password

Un’unità impacchettata può portare un drop-in che non avete scritto, e la configurazione effettiva è il risultato fuso. Stampate l’unità come la vede systemd, insieme alle impostazioni della sandbox che decidono se l’helper può elevarsi. L’ispezione in sola lettura è sufficiente in questa fase.

systemctl cat [email protected]
systemctl show [email protected] -p User -p Group -p CapabilityBoundingSet -p AmbientCapabilities -p NoNewPrivileges
journalctl -u [email protected] -b

Il primo comando mostra da dove vengono l’unità e i suoi drop-in, cosa che conta perché un override aggiunto mesi fa sopravvive alla memoria del perché esiste. Il secondo stampa i valori della sandbox da cui dipendono le due sezioni successive.

Confrontate ciò che vedete con un host funzionante della stessa distribuzione e versione. Una differenza in User, CapabilityBoundingSet o NoNewPrivileges è un indizio concreto; una differenza nei commenti in prosa non lo è. Annotate i valori prima di cambiare qualsiasi cosa, perché un confronto successivo con un bersaglio mobile non dimostra nulla. Se il servizio gira con un account non privilegiato e un insieme bounding vuoto, l’helper non ha quasi nulla con cui lavorare.

Trasformazione delle credenziali, accesso ai file e politica sono tre confini

Iniziate da ciò che accade durante exec. Un binario setuid o un file con capacità di file provoca una trasformazione delle credenziali, quindi l’identità risultante non è una continuazione del chiamante. L’insieme bounding limita ciò che un tale binario può acquisire, e NoNewPrivileges blocca del tutto le acquisizioni di setuid, setgid e capacità di file. Ognuna di queste tre può fermare l’helper lasciando intatte tutte le altre funzioni del servizio.

Poi separate l’aggiramento discrezionale ampio dall’accesso in lettura ristretto. CAP_DAC_OVERRIDE salta i controlli discrezionali quasi ovunque, mentre CAP_DAC_READ_SEARCH copre la lettura e l’attraversamento delle directory con i propri poteri aggiuntivi. Sono strumenti diversi con raggi d’azione diversi, e nessuno dei due è un modo di scrivere lascia che PAM legga il file shadow.

Infine, chiedete se la politica obbligatoria consente l’azione. Una capacità Linux non scavalca SELinux, e possederne una non dimostra nulla su una regola di type enforcement. Trattate un rifiuto di accesso a un file e un rifiuto di capacità come prove diverse, perché nominano correzioni diverse e spesso proprietari diversi. Leggete ogni campo del record invece della sola parola denied. Quale eseguibile ha agito, in quale dominio girava, quale tipo di obiettivo ha toccato, quale classe di oggetto era coinvolta e quale permesso la politica ha rifiutato: ognuno restringe la risposta in modo diverso.

Estraete il rifiuto dal log di audit

Su un host in modalità di applicazione, la traccia di audit è l’unico posto che registra un rifiuto. Interrogate gli eventi recenti e leggeteli rispetto al timestamp dell’accesso fallito, non rispetto all’ora in cui avete iniziato a indagare.

ausearch -m AVC,USER_AVC -ts recent

Un risultato vuoto non è una prova di innocenza. Significa o che l’audit non raccoglie, o che la vostra finestra ha mancato il tentativo, ed entrambe le possibilità vanno escluse prima di escludere la politica.

La guida alla risoluzione dei problemi SELinux di Red Hat mette i controlli di etichette e configurazione prima della generazione delle regole allow, che è anche l’ordine giusto per questo problema. Un helper etichettato male e una lacuna nella politica producono rifiuti simili ma richiedono riparazioni diverse. Generare una regola allow per un errore di etichettatura nasconde il difetto e lascia che il successivo aggiornamento del pacchetto lo reintroduca. Confermate l’etichetta sull’helper installato prima di scrivere qualsiasi politica.

Dimostrate la modifica con un accesso accettato e uno rifiutato

Cambiate solo il confine che avete dimostrato, poi provate entrambi gli esiti. Un account che dovrebbe autenticarsi deve riuscire, e un account con una password sbagliata deve ancora fallire. Una modifica che fa riuscire ogni tentativo ha rimosso l’autenticazione invece di ripararla.

Eseguite un vero tentativo di connessione e leggete il journal subito dopo. La riga che dimostra la correzione è la voce pam_unix(openvpn:auth) per quel tentativo: dovrebbe riportare autenticazione riuscita, e nessun record AVC dovrebbe portare lo stesso secondo.

journalctl -u [email protected] -n 30 --no-pager
ss -tnp | grep openvpn

Un tunnel funzionante ha bisogno anche di una riconnessione, perché le modifiche alla sandbox rompono la seconda connessione più spesso della prima. Controllate il socket stabilito dopo la riconnessione e confermate che la stessa unità lo stia ancora servendo.

Articoli correlati