TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
Sie müssen das System fragen, ob der Benutzer über Schreibrechte verfügt. Die einzig zuverlässige Möglichkeit besteht darin, die effektive Benutzer-ID, die effektive Benutzer-ID und die ergänzende Benutzer-ID auf die des Benutzers umzustellen und den access(W_OK)Systemaufruf zu verwenden (auch wenn dies bei einigen Systemen / Konfigurationen Einschränkungen aufweist).
Beachten Sie außerdem, dass das Fehlen einer Schreibberechtigung für eine Datei nicht unbedingt garantiert, dass Sie den Inhalt der Datei unter diesem Pfad nicht ändern können.
Die längere Geschichte
Lassen Sie uns überlegen , was es zum Beispiel für einen $ Benutzer nimmt Schreibzugriff zu haben , um /foo/file.txt(unter der Annahme keiner /foound /foo/file.txtSymlinks sind)?
Er braucht:
- Suche nach Zugang zu
/(keine Notwendigkeit für read)
- Suche nach Zugang zu
/foo(keine Notwendigkeit für read)
- Schreibzugriff auf
/foo/file.txt
Sie können bereits sehen, dass Ansätze (wie @ lcd047 oder @ apaul ), die nur die Berechtigung von überprüfen file.txt, nicht funktionieren, weil sie sagen könnten, dass sie file.txtbeschreibbar sind, selbst wenn der Benutzer keine Suchberechtigung für /oder hat /foo.
Und ein Ansatz wie:
sudo -u "$user" find / -writeble
Funktioniert auch nicht, da keine Dateien in Verzeichnissen gemeldet werden, auf die der Benutzer keinen Lesezugriff hat (da findausgeführt wird, weil der $userInhalt nicht aufgelistet werden kann), selbst wenn er darauf schreiben kann.
Vergessen wir ACLs, schreibgeschützte Dateisysteme, FS-Flags (wie unveränderlich), andere Sicherheitsmaßnahmen (Apparmor, SELinux, die sogar zwischen verschiedenen Schreibweisen unterscheiden können) und konzentrieren wir uns nur auf traditionelle Berechtigungs- und Eigentumsattribute, um eine zu erhalten mit der erlaubnis (suchen oder schreiben) ist das schon ziemlich kompliziert und schwer auszudrücken find.
Du brauchst:
- Wenn sich die Datei in Ihrem Besitz befindet, benötigen Sie diese Berechtigung für den Eigentümer (oder haben die UID 0).
- Wenn sich die Datei nicht in Ihrem Besitz befindet, die Gruppe jedoch eine Ihrer ist, benötigen Sie diese Berechtigung für die Gruppe (oder haben die UID 0).
- Wenn es Ihnen nicht gehört und keiner Ihrer Gruppen angehört, gelten die anderen Berechtigungen (es sei denn, Ihre UID ist 0).
In der findSyntax, hier als Beispiel mit einem Benutzer von UID 1 und GID 1 und 2, wäre das:
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
Dass man Pflaumen die Verzeichnisse der Benutzer suchen haben , nicht direkt für und für andere Arten von Dateien (Symlinks ausgeschlossen , da sie nicht relevant sind), Schecks für den Schreibzugriff.
Wenn Sie auch den Schreibzugriff auf Verzeichnisse in Betracht ziehen möchten:
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
Oder für eine beliebige $userund deren Gruppenmitgliedschaft, die aus der Benutzerdatenbank abgerufen wird:
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
(das ist 3 - Prozesse insgesamt: id, sedund find)
Am besten ist es, wenn Sie den Baum als root absteigen und die Berechtigungen als Benutzer für jede Datei prüfen.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(Das ist ein findProzess plus eins sudound shverarbeitet alle paar tausend Dateien [und printfwird normalerweise in der Shell erstellt.)
Oder mit perl:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 Prozesse insgesamt: find, sudound perl).
Oder mit zsh:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(0 Prozess insgesamt, speichert aber die gesamte Dateiliste im Speicher)
Diese Lösungen basieren auf dem access(2)Systemaufruf. Das heißt, anstatt den Algorithmus zu reproduzieren, den das System verwendet, um nach Zugriffsberechtigungen zu suchen, bitten wir das System, diese Überprüfung mit demselben Algorithmus durchzuführen (der Berechtigungen, ACLs, unveränderliche Flags, schreibgeschützte Dateisysteme berücksichtigt ...). ) Wenn Sie versuchen würden, die Datei zum Schreiben zu öffnen, ist dies die zuverlässigste Lösung.
Um die hier angegebenen Lösungen mit den verschiedenen Kombinationen von Benutzern, Gruppen und Berechtigungen zu testen, können Sie Folgendes tun:
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
}
}
}
}
}
}'
Variieren Sie den Benutzer zwischen 1 und 2 und die Gruppe zwischen 1, 2 und 3 und beschränken Sie sich auf die unteren 9 Bits der Berechtigungen, da bereits 9458694 Dateien erstellt wurden. Das für Verzeichnisse und dann nochmal für Dateien.
Das schafft alle möglichen Kombinationen von u<x>g<y>/<mode1>/u<z>g<w>/<mode2>. Der Benutzer mit Benutzer-ID 1 und Benutzer-ID 1 und Benutzer-ID 2 hätte beispielsweise Schreibzugriff auf, u2g1/010/u2g3/777jedoch nicht u1g2/677/u1g1/777auf.
Alle diese Lösungen versuchen nun, die Pfade der Dateien zu identifizieren, die der Benutzer möglicherweise zum Schreiben öffnet. Dies unterscheidet sich von den Pfaden, unter denen der Benutzer möglicherweise den Inhalt ändern kann. Um diese allgemeinere Frage zu beantworten, gibt es mehrere Dinge zu berücksichtigen:
- $ user hat möglicherweise keinen Schreibzugriff auf,
/a/b/fileaber wenn er Eigentümer ist file(und Suchzugriff auf /a/bhat und das Dateisystem nicht schreibgeschützt ist und die Datei nicht das unveränderliche Flag hat und Shell-Zugriff auf das System hat), dann könnte er die Berechtigungen des ändern fileund sich selbst Zugriff gewähren.
- Dasselbe, wenn er es besitzt,
/a/baber keinen Suchzugriff darauf hat.
- $ user hat möglicherweise keinen Zugriff auf,
/a/b/fileweil er keinen Suchzugriff auf /aoder /a/bhat. Diese Datei verfügt jedoch möglicherweise über einen festen Link /b/c/file. In diesem Fall kann er den Inhalt möglicherweise ändern, /a/b/fileindem er ihn über seinen /b/c/filePfad öffnet .
- Gleiches gilt für Bindemounts . Er kann nicht suchen den Zugang haben
/a, sondern /a/bkann sein bind montierte in /c, so dass er öffnen konnte filefür das Schreiben über seinen /c/fileanderen Weg.
- Er hat möglicherweise keine Schreibrechte für
/a/b/file, aber wenn er Schreibrechte hat, /a/bkann er diese entfernen oder umbenennen fileund durch seine eigene Version ersetzen. Er würde den Inhalt der Datei ändern, /a/b/fileselbst wenn dies eine andere Datei wäre.
- Das Gleiche gilt , wenn er Schreibzugriff auf bekam ist
/a(er umbenennen könnte /a/bzu /a/cerstellen Sie ein neues /a/bVerzeichnis und ein neues filedrin.
Um die Pfade zu finden, die geändert $userwerden könnten. Für Adresse 1 oder 2 können wir uns nicht mehr auf den access(2)Systemaufruf verlassen . Wir könnten unseren find -permAnsatz anpassen , um den Suchzugriff auf Verzeichnisse oder den Schreibzugriff auf Dateien zu übernehmen, sobald Sie der Eigentümer sind:
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
Wir könnten die Adressen 3 und 4 ansprechen, indem wir die Geräte- und Inode-Nummern oder alle Dateien aufzeichnen, für die der Benutzer Schreibrechte besitzt, und alle Dateipfade mit diesen dev + -Inode-Nummern melden. Dieses Mal können wir die zuverlässigeren access(2)Ansätze verwenden:
So etwas wie:
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 und 6 werden auf den ersten Blick durch das tbisschen der Berechtigungen kompliziert . Bei der Anwendung auf Verzeichnisse ist dies das eingeschränkte Löschbit, das verhindert, dass Benutzer (außer dem Eigentümer des Verzeichnisses) die Dateien entfernen oder umbenennen, deren Eigentümer sie nicht sind (obwohl sie Schreibzugriff auf das Verzeichnis haben).
Zum Beispiel, wenn wir zurück zu unserem früheren Beispiel gehen, wenn Sie Schreibzugriff haben /a, dann sollten Sie in der Lage sein , umbenennen /a/bzu /a/c, und dann ein neu - /a/bVerzeichnis und ein neu filedrin. Aber wenn das tBit gesetzt ist /aund Sie es nicht besitzen /a, können Sie es nur tun, wenn Sie es besitzen /a/b. Das gibt:
- Wenn Sie ein Verzeichnis gemäß Nummer 1 besitzen, können Sie sich selbst Schreibzugriff gewähren, und das t-Bit wird nicht angewendet (und Sie können es trotzdem entfernen), sodass Sie alle Dateien oder Verzeichnisse dort löschen / umbenennen / neu erstellen können Alle Dateipfade, die sich dort befinden, können mit allen Inhalten überschrieben werden.
- Wenn Sie es nicht besitzen, aber Schreibzugriff haben, dann:
- Entweder ist das
tBit nicht gesetzt, und Sie sind im selben Fall wie oben (alle Dateipfade gehören Ihnen).
- oder es ist festgelegt und dann können Sie die Dateien nicht ändern, die Sie nicht besitzen oder auf die Sie keinen Schreibzugriff haben. Um die Dateipfade zu ermitteln, die Sie ändern können, müssen Sie also überhaupt keine Schreibberechtigung besitzen.
So können wir alle von 1, 2, 5 und 6 ansprechen mit:
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
Das und die Lösung für 3 und 4 sind unabhängig, Sie können ihre Ausgabe zusammenführen, um eine vollständige Liste zu erhalten:
{
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{$_}++'
Wie klar sein sollte, wenn Sie bisher alles gelesen haben, befasst sich ein Teil davon zumindest nur mit Berechtigungen und Eigentumsrechten, nicht aber mit den anderen Funktionen, die möglicherweise Schreibzugriff gewähren oder einschränken (schreibgeschütztes FS, ACLs, unveränderliches Flag, andere Sicherheitsfunktionen) ...). Und da wir es in mehreren Schritten verarbeiten, können einige dieser Informationen falsch sein, wenn die Dateien / Verzeichnisse erstellt / gelöscht / umbenannt werden oder ihre Berechtigungen / Eigentümer geändert werden, während das Skript ausgeführt wird, wie auf einem ausgelasteten Dateiserver mit Millionen von Dateien .
Hinweise zur Portabilität
Der gesamte Code ist Standard (POSIX, Unix für tBit), außer:
-print0ist eine GNU-Erweiterung, die jetzt auch von einigen anderen Implementierungen unterstützt wird. Mit findImplementierungen , die Unterstützung für sie fehlt, können Sie -exec printf '%s\0' {} +stattdessen und ersetzen -exec sh -c 'exec find "$@" -print0' sh {} +mit -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +.
perlist kein POSIX-spezifizierter Befehl, ist aber weit verbreitet. Sie benötigen perl-5.6.0oder über -Mfiletest=access.
zshist kein POSIX-angegebener Befehl. Der zshobige Code sollte mit zsh-3 (1995) und höher funktionieren.
sudoist kein POSIX-angegebener Befehl. Der Code sollte mit jeder Version funktionieren, sofern die Systemkonfiguration die Ausführung perlals angegebener Benutzer zulässt .