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 /foo
e /foo/file.txt
siano 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.txt
non funzioneranno perché potrebbero dire che file.txt
sono 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 find
esecuzione come $user
non 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 find
sintassi, 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 $user
e 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
, sed
e 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 find
processo più uno sudo
ed sh
elabora ogni poche migliaia di file [
e di printf
solito 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
, sudo
e 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/777
ma non u1g2/677/u1g1/777
per 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/file
ma 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 file
e concedere a se stesso l'accesso.
- Stessa cosa se possiede
/a/b
ma non ha accesso alla ricerca.
- $ user potrebbe non avere accesso
/a/b/file
perché non ha accesso alla ricerca /a
o /a/b
, ma quel file potrebbe avere un collegamento reale /b/c/file
ad esempio, nel qual caso potrebbe essere in grado di modificare il contenuto /a/b/file
aprendolo tramite il suo /b/c/file
percorso.
- Stessa cosa con i bind-mount . Egli non può avere accesso alla ricerca
/a
, ma /a/b
può essere bind-montato in /c
, in modo da poter aprire file
per la scrittura attraverso il suo /c/file
altro percorso.
- Potrebbe non avere i permessi di scrittura
/a/b/file
, ma se ha accesso in scrittura /a/b
può rimuoverlo o rinominarlo file
e sostituirlo con la sua versione. Modificherebbe il contenuto del file /a/b/file
anche se si tratterebbe di un file diverso.
- Stessa cosa se ha accesso in scrittura
/a
(che poteva cambiare titolo /a/b
a /a/c
, creare una nuova /a/b
directory e un nuovo file
in esso.
Per trovare i percorsi che $user
sarebbero 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 -perm
approccio 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 t
bit 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/b
a /a/c
, e quindi ricreare una /a/b
directory e un nuovo file
lì. Ma se il t
bit è impostato /a
e 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
t
bit 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 t
bit) tranne:
-print0
è un'estensione GNU ora supportata anche da alcune altre implementazioni. Con find
implementazioni 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 {} +
.
perl
non è un comando specificato da POSIX ma è ampiamente disponibile. Hai bisogno perl-5.6.0
o sopra per -Mfiletest=access
.
zsh
non è un comando specificato da POSIX. Quel zsh
codice sopra dovrebbe funzionare con zsh-3 (1995) e successivi.
sudo
non è un comando specificato da POSIX. Il codice dovrebbe funzionare con qualsiasi versione purché la configurazione del sistema consenta l'esecuzione perl
come utente specificato.