TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
È necessario chiedere al sistema se l'utente dispone dell'autorizzazione di scrittura. L'unico modo affidabile è di cambiare l'effettivo uid, efficace gid e integrazione gid a quello dell'utente e utilizzare la access(W_OK)chiamata di sistema (anche se ha alcune limitazioni su alcuni sistemi / configurazioni).
E tieni presente che non avere il permesso di scrivere su un file non garantisce necessariamente che non puoi modificare il contenuto del file in quel percorso.
La storia più lunga
Consideriamo ciò che serve, ad esempio, per un utente $ per avere accesso in scrittura /foo/file.txt(supponendo che nessuno dei due sia /fooe /foo/file.txtsiano collegamenti simbolici)?
Ha bisogno:
- cerca l' accesso a
/(non è necessario read)
- cerca l' accesso a
/foo(non è necessario read)
- accesso in scrittura a
/foo/file.txt
Puoi già vedere che gli approcci (come @ lcd047 o @ apaul's ) che controllano solo il permesso di file.txtnon funzioneranno perché potrebbero dire che file.txtsono scrivibili anche se l'utente non ha il permesso di ricerca su /o /foo.
E un approccio come:
sudo -u "$user" find / -writeble
Non funzionerà nemmeno perché non segnalerà i file nelle directory in cui l'utente non ha accesso in lettura (in quanto in findesecuzione come $usernon può elencare il loro contenuto) anche se può scrivere a loro.
Se dimentichiamo ACL, file system di sola lettura, flag FS (come immutabili), altre misure di sicurezza (apparmor, SELinux, che può persino distinguere tra diversi tipi di scrittura) e concentrarsi solo su autorizzazioni tradizionali e attributi di proprietà, per ottenere un dato il permesso (cerca o scrivi), è già abbastanza complicato e difficile da esprimere find.
Hai bisogno:
- se il file è di tua proprietà, devi disporre dell'autorizzazione per il proprietario (o hai uid 0)
- se il file non è di tua proprietà, ma il gruppo è uno dei tuoi, allora hai bisogno di tale autorizzazione per il gruppo (o hai uid 0).
- se non appartiene a te e non a nessuno dei tuoi gruppi, si applicano le altre autorizzazioni (a meno che il tuo uid sia 0).
In findsintassi, qui come esempio con un utente di uid 1 e gids 1 e 2, sarebbe:
find / -type d \
\( \
-user 1 \( -perm -u=x -o -prune \) -o \
\( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) -o -type l -o \
-user 1 \( ! -perm -u=w -o -print \) -o \
\( -group 1 -o -group 2 \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
Quello elimina le directory per le quali l'utente non ha il diritto di ricerca e per altri tipi di file (esclusi i collegamenti simbolici in quanto non rilevanti), controlla l'accesso in scrittura.
Se vuoi considerare anche l'accesso in scrittura alle directory:
find / -type d \
\( \
-user 1 \( -perm -u=x -o -prune \) -o \
\( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user 1 \( ! -perm -u=w -o -print \) -o \
\( -group 1 -o -group 2 \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
O per un membro arbitrario $usere il suo gruppo recuperati dal database utente:
groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
\( \
-user "$user" \( -perm -u=x -o -prune \) -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( ! -perm -u=w -o -print \) -o \
\( -group $groups \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
(che è 3 processi in totale: id, sede find)
Il migliore qui sarebbe discendere l'albero come root e controllare le autorizzazioni come utente per ogni file.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(questo è un findprocesso più uno sudoed shelabora ogni poche migliaia di file [e di printfsolito sono integrati nella shell).
O con perl:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 processi complessivamente: find, sudoe perl).
O con zsh:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(0 processo in totale, ma memorizza l'intero elenco di file in memoria)
Tali soluzioni si basano sulla access(2)chiamata di sistema. Cioè invece di riprodurre l'algoritmo che il sistema utilizza per verificare i permessi di accesso, stiamo chiedendo al sistema di fare quel controllo con lo stesso algoritmo (che tiene conto delle autorizzazioni dell'account, ACL, flag immutabili, file system di sola lettura ... ) utilizzeresti se provassi ad aprire il file per la scrittura, quindi è il più vicino che otterrai una soluzione affidabile.
Per testare le soluzioni fornite qui, con le varie combinazioni di utente, gruppo e permessi, puoi fare:
perl -e '
for $u (1,2) {
for $g (1,2,3) {
$d1="u${u}g$g"; mkdir$d1;
for $m (0..511) {
$d2=$d1.sprintf"/%03o",$m; mkdir $d2; chown $u, $g, $d2; chmod $m,$d2;
for $uu (1,2) {
for $gg (1,2,3) {
$d3="$d2/u${uu}g$gg"; mkdir $d3;
for $mm (0..511) {
$f=$d3.sprintf"/%03o",$mm;
open F, ">","$f"; close F;
chown $uu, $gg, $f; chmod $mm, $f
}
}
}
}
}
}'
Variando l'utente tra 1 e 2 e il gruppo tra 1, 2 e 3 e limitandoci ai 9 bit inferiori delle autorizzazioni poiché sono già stati creati 9458694 file. Quello per le directory e poi di nuovo per i file.
Questo crea tutte le possibili combinazioni di u<x>g<y>/<mode1>/u<z>g<w>/<mode2>. L'utente con uid 1 e gids 1 e 2 avrebbe accesso in scrittura u2g1/010/u2g3/777ma non u1g2/677/u1g1/777per esempio.
Ora, tutte quelle soluzioni cercano di identificare i percorsi dei file che l'utente può aprire per la scrittura, che è diverso dai percorsi in cui l'utente può essere in grado di modificare il contenuto. Per rispondere a questa domanda più generica, ci sono diverse cose da prendere in considerazione:
- $ user potrebbe non avere accesso in scrittura
/a/b/filema se possiede file(e ha accesso alla ricerca /a/b, e il file system non è di sola lettura e il file non ha il flag immutabile e ha accesso shell al sistema), quindi sarebbe in grado di modificare le autorizzazioni di filee concedere a se stesso l'accesso.
- Stessa cosa se possiede
/a/bma non ha accesso alla ricerca.
- $ user potrebbe non avere accesso
/a/b/fileperché non ha accesso alla ricerca /ao /a/b, ma quel file potrebbe avere un collegamento reale /b/c/filead esempio, nel qual caso potrebbe essere in grado di modificare il contenuto /a/b/fileaprendolo tramite il suo /b/c/filepercorso.
- Stessa cosa con i bind-mount . Egli non può avere accesso alla ricerca
/a, ma /a/bpuò essere bind-montato in /c, in modo da poter aprire fileper la scrittura attraverso il suo /c/filealtro percorso.
- Potrebbe non avere i permessi di scrittura
/a/b/file, ma se ha accesso in scrittura /a/bpuò rimuoverlo o rinominarlo filee sostituirlo con la sua versione. Modificherebbe il contenuto del file /a/b/fileanche se si tratterebbe di un file diverso.
- Stessa cosa se ha accesso in scrittura
/a(che poteva cambiare titolo /a/ba /a/c, creare una nuova /a/bdirectory e un nuovo filein esso.
Per trovare i percorsi che $usersarebbero in grado di modificare. Per indirizzare 1 o 2, non possiamo più fare affidamento sulla access(2)chiamata di sistema. Potremmo adattare il nostro find -permapproccio per assumere l'accesso alla ricerca nelle directory o accedere in scrittura ai file non appena sei il proprietario:
groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
\( \
-user "$user" -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" -print -o \
\( -group $groups \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
Potremmo indirizzare 3 e 4, registrando il dispositivo e i numeri di inode o tutti i file $ user ha il permesso di scrivere e riporta tutti i percorsi di file che hanno quei numeri di dev + inode. Questa volta, possiamo usare gli access(2)approcci più affidabili :
Qualcosa di simile a:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-w,$_' |
perl -l -0ne '
($w,$p) = /(.)(.*)/;
($dev,$ino) = stat$p or next;
$writable{"$dev,$ino"} = 1 if $w;
push @{$p{"$dev,$ino"}}, $p;
END {
for $i (keys %writable) {
for $p (@{$p{$i}}) {
print $p;
}
}
}'
5 e 6 sono a prima vista complicati dal tbit delle autorizzazioni. Se applicato su directory, questo è il bit di eliminazione limitato che impedisce agli utenti (diversi dal proprietario della directory) di rimuovere o rinominare i file che non possiedono (anche se hanno accesso in scrittura alla directory).
Per esempio, se torniamo al nostro esempio precedente, se si ha accesso in scrittura /a, allora si dovrebbe essere in grado di rinominare /a/ba /a/c, e quindi ricreare una /a/bdirectory e un nuovo filelì. Ma se il tbit è impostato /ae non possiedi /a, puoi farlo solo se lo possiedi /a/b. Questo dà:
- Se possiedi una directory, come da 1, puoi concederti l'accesso in scrittura e il bit t non si applica (e potresti rimuoverlo comunque), quindi puoi eliminare / rinominare / ricreare qualsiasi file o directory lì dentro, quindi tutti i percorsi dei file sottostanti sono tuoi da riscrivere con qualsiasi contenuto.
- Se non lo possiedi ma hai accesso in scrittura, quindi:
- O il
tbit non è impostato e sei nello stesso caso di cui sopra (tutti i percorsi dei file sono tuoi).
- oppure è impostato e quindi non è possibile modificare i file di cui non si è proprietari o ai quali non si ha accesso in scrittura, quindi per il nostro scopo di trovare i percorsi dei file che è possibile modificare, è lo stesso che non si ha affatto il permesso di scrittura.
Quindi possiamo affrontare tutti 1, 2, 5 e 6 con:
find / -type d \
\( \
-user "$user" -prune -exec find {} + -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( -type d -o -print \) -o \
\( -group $groups \) \( ! -perm -g=w -o \
-type d ! -perm -1000 -exec find {} + -o -print \) -o \
! -perm -o=w -o \
-type d ! -perm -1000 -exec find {} + -o \
-print
Questo e la soluzione per 3 e 4 sono indipendenti, puoi unire il loro output per ottenere un elenco completo:
{
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-w,$_' |
perl -0lne '
($w,$p) = /(.)(.*)/;
($dev,$ino) = stat$p or next;
$writable{"$dev,$ino"} = 1 if $w;
push @{$p{"$dev,$ino"}}, $p;
END {
for $i (keys %writable) {
for $p (@{$p{$i}}) {
print $p;
}
}
}'
find / -type d \
\( \
-user "$user" -prune -exec sh -c 'exec find "$@" -print0' sh {} + -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( -type d -o -print0 \) -o \
\( -group $groups \) \( ! -perm -g=w -o \
-type d ! -perm -1000 -exec sh -c 'exec find "$@" -print0' sh {} + -o -print0 \) -o \
! -perm -o=w -o \
-type d ! -perm -1000 -exec sh -c 'exec find "$@" -print0' sh {} + -o \
-print0
} | perl -l -0ne 'print unless $seen{$_}++'
Come dovrebbe essere chiaro se hai letto tutto finora, parte di esso riguarda almeno le autorizzazioni e la proprietà, non le altre funzionalità che possono concedere o limitare l'accesso in scrittura (FS di sola lettura, ACL, flag immutabili, altre funzionalità di sicurezza ...). E mentre lo elaboriamo in più fasi, alcune di queste informazioni potrebbero essere errate se i file / le directory vengono creati / eliminati / rinominati o le loro autorizzazioni / proprietà modificate mentre lo script è in esecuzione, come su un file server occupato con milioni di file .
Note sulla portabilità
Tutto quel codice è standard (POSIX, Unix per tbit) tranne:
-print0è un'estensione GNU ora supportata anche da alcune altre implementazioni. Con findimplementazioni che non ne hanno il supporto, puoi -exec printf '%s\0' {} +invece utilizzare e sostituire -exec sh -c 'exec find "$@" -print0' sh {} +con -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +.
perlnon è un comando specificato da POSIX ma è ampiamente disponibile. Hai bisogno perl-5.6.0o sopra per -Mfiletest=access.
zshnon è un comando specificato da POSIX. Quel zshcodice sopra dovrebbe funzionare con zsh-3 (1995) e successivi.
sudonon è un comando specificato da POSIX. Il codice dovrebbe funzionare con qualsiasi versione purché la configurazione del sistema consenta l'esecuzione perlcome utente specificato.