TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
Musisz zapytać system, czy użytkownik ma uprawnienia do zapisu. Jedynym niezawodnym sposobem jest przełączenie efektywnego identyfikatora UID, efektywnego identyfikatora GID i suplementów na identyfikator użytkownika i użycieaccess(W_OK)
wywołania systemowego (nawet jeśli ma to pewne ograniczenia w niektórych systemach / konfiguracjach).
I pamiętaj, że brak uprawnień do zapisu do pliku niekoniecznie gwarantuje, że nie możesz modyfikować zawartości pliku w tej ścieżce.
Dłuższa historia
Zastanówmy się, czego potrzeba na przykład, aby użytkownik $ miał dostęp do zapisu /foo/file.txt
(zakładając, że żaden z /foo
i/foo/file.txt
ma dowiązań symbolicznych)?
On potrzebuje:
- dostęp do wyszukiwania do
/
(nie ma potrzebyread
)
- dostęp do wyszukiwania do
/foo
(nie ma potrzebyread
)
- dostęp do zapisu do
/foo/file.txt
Widać już, że podejścia (takie jak @ lcd047 lub @ apaul ), które sprawdzają, że tylko pozwolenie file.txt
nie działa, ponieważ mogliby powiedzieć, że file.txt
jest zapisywalne, nawet jeśli użytkownik nie ma uprawnień do wyszukiwania /
lub/foo
.
I takie podejście jak:
sudo -u "$user" find / -writeble
Nie zadziała również, ponieważ nie zgłosi plików w katalogach, użytkownik nie ma dostępu do odczytu (tak find
działający, jak $user
nie może wyświetlić ich zawartości), nawet jeśli może do nich pisać.
Jeśli zapomnimy o listach ACL, systemach plików tylko do odczytu, flagach FS (takich jak niezmienne), innych środkach bezpieczeństwa (apparmor, SELinux, które potrafią nawet rozróżnić różne typy zapisu) i skupimy się tylko na tradycyjnych atrybutach uprawnień i własności, aby uzyskać z uprawnieniami (wyszukiwanie lub zapis), jest to już dość skomplikowane i trudne do wyrażenia find
.
Potrzebujesz:
- jeśli plik należy do Ciebie, potrzebujesz tego uprawnienia dla właściciela (lub masz identyfikator użytkownika 0)
- jeśli plik nie jest twoją własnością, ale grupa należy do ciebie, musisz mieć to uprawnienie dla grupy (lub mieć identyfikator użytkownika 0).
- jeśli nie jest twoją własnością i nie należy do żadnej z twoich grup, obowiązują inne uprawnienia (chyba że twój identyfikator użytkownika wynosi 0).
W find
składni, tutaj jako przykład z użytkownikiem UID 1 oraz GID 1 i 2, byłoby to:
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
Ten ściąga katalogi, których użytkownik nie ma prawa do wyszukiwania i do innych typów plików (wykluczone dowiązania symboliczne, ponieważ nie są istotne), sprawdza dostęp do zapisu.
Jeśli chcesz również rozważyć dostęp do zapisu do katalogów:
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
Lub dla arbitralnego $user
i jego członkostwa w grupie pobranych z bazy danych użytkowników:
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
(to jest 3 procesy w sumie: id
, sed
a find
)
Najlepiej byłoby zejść z drzewa jako root i sprawdzić uprawnienia jako użytkownik dla każdego pliku.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(to jeden find
proces plus jeden sudo
i sh
przetwarzaj co kilka tysięcy plików [
i printf
zwykle są one wbudowane w powłokę).
Lub z perl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 procesy łącznie: find
, sudo
a perl
).
Lub z zsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(W sumie 0 proces, ale przechowuje całą listę plików w pamięci)
Te rozwiązania opierają się na access(2)
wywołaniu systemowym. Oznacza to, że zamiast odtwarzać algorytm używany przez system do sprawdzania uprawnień dostępu, prosimy system o sprawdzenie tego samego algorytmu (który uwzględnia uprawnienia, listy ACL, niezmienne flagi, systemy plików tylko do odczytu ... ) przydałby się, gdybyś spróbował otworzyć plik do zapisu, więc jest to najbliższe niezawodne rozwiązanie.
Aby przetestować podane tutaj rozwiązania z różnymi kombinacjami użytkownika, grupy i uprawnień, możesz:
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
}
}
}
}
}
}'
Zmieniający się użytkownik między 1 a 2 i grupa między 1, 2 i 3 i ograniczający się do niższych 9 bitów uprawnień, ponieważ są to już utworzone pliki 9458694. Dotyczy to katalogów, a następnie plików.
To tworzy wszystkie możliwe kombinacje u<x>g<y>/<mode1>/u<z>g<w>/<mode2>
. Użytkownik z identyfikatorami UID 1 i GID 1 i 2 miałby dostęp do zapisu, u2g1/010/u2g3/777
ale nieu1g2/677/u1g1/777
na przykład nie.
Teraz wszystkie te rozwiązania próbują zidentyfikować ścieżki plików, które użytkownik może otworzyć do zapisu, co różni się od ścieżek, w których użytkownik może modyfikować zawartość. Aby odpowiedzieć na to bardziej ogólne pytanie, należy wziąć pod uwagę kilka rzeczy:
- $ użytkownik może nie mieć dostępu do zapisu,
/a/b/file
ale jeśli jest właścicielem file
(i ma dostęp do wyszukiwania /a/b
, a system plików nie jest tylko do odczytu, a plik nie ma niezmiennej flagi, a on ma dostęp do powłoki z systemu), wtedy będzie mógł zmienić uprawnienia file
i udzielić sobie dostępu.
- To samo, jeśli jest właścicielem,
/a/b
ale nie ma dostępu do wyszukiwania.
- $ użytkownik może nie mieć dostępu,
/a/b/file
ponieważ nie ma dostępu do wyszukiwania /a
lub /a/b
, ale plik ten może mieć na /b/c/file
przykład twardy link , w którym to przypadku może być w stanie zmodyfikować zawartość /a/b/file
, otwierając go poprzez /b/c/file
ścieżkę.
- To samo dotyczy mocowań opraw . On nie może mieć dostęp do wyszukiwarki
/a
, ale /a/b
może wiązać montowane w /c
, więc on może otworzyć file
na piśmie za pośrednictwem swojej /c/file
drugiej ścieżce.
- Może nie mieć uprawnień do zapisu
/a/b/file
, ale jeśli ma dostęp do zapisu /a/b
, może tam usunąć lub zmienić nazwę file
i zastąpić go własną wersją. Zmieniłby zawartość pliku, /a/b/file
nawet jeśli byłby to inny plik.
- To samo, jeśli on ma dostęp do zapisu
/a
(mógł zmienić nazwę /a/b
, aby /a/c
utworzyć nowy /a/b
katalog, a nowy file
w nim.
Aby znaleźć ścieżki, $user
które można modyfikować. W przypadku adresu 1 lub 2 nie możemy już polegać na access(2)
wywołaniu systemowym. Możemy dostosować nasze find -perm
podejście, aby zakładać dostęp do wyszukiwania do katalogów lub zapisywać dostęp do plików, gdy tylko będziesz właścicielem:
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
Możemy adresować 3 i 4, rejestrując numery urządzeń i i-węzłów lub wszystkie pliki $ użytkownik ma uprawnienia do zapisu i zgłasza wszystkie ścieżki plików, które mają te numery dev i i-węzłów. Tym razem możemy skorzystać z bardziej niezawodnych access(2)
podejścia:
Coś jak:
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 i 6 są na pierwszy rzut oka skomplikowane przez t
odrobinę uprawnień. W przypadku katalogów jest to bit z ograniczonym usuwaniem, który uniemożliwia użytkownikom (innym niż właściciel katalogu) usuwanie lub zmianę nazw plików, których nie są właścicielami (nawet jeśli mają dostęp do zapisu w katalogu).
Na przykład, jeśli wrócimy do naszego wcześniejszego przykładu, jeśli masz dostęp do zapisu /a
, powinieneś być w stanie zmienić nazwę /a/b
na /a/c
, a następnie ponownie utworzyć /a/b
katalog i nowy file
w nim. Ale jeśli t
bit jest włączony, /a
a ty nie jesteś właścicielem /a
, możesz to zrobić tylko wtedy, gdy jesteś właścicielem /a/b
. To daje:
- Jeśli posiadasz katalog, zgodnie z 1, możesz przyznać sobie prawo do zapisu, a bit t nie ma zastosowania (i możesz go i tak usunąć), więc możesz usunąć / zmienić nazwę / odtworzyć dowolny plik lub katalog, więc wszystkie znajdujące się tam ścieżki plików można przepisać dowolną zawartością.
- Jeśli nie jesteś właścicielem, ale masz dostęp do zapisu, to:
- Albo
t
bit nie jest ustawiony, a ty jesteś w takim samym przypadku jak powyżej (wszystkie ścieżki do plików są twoje).
- lub jest ustawiony, a następnie nie możesz modyfikować plików, których nie jesteś właścicielem lub nie masz dostępu do zapisu, więc dla naszego celu znalezienia ścieżek plików, które możesz zmodyfikować, to tak samo, jak w ogóle nie mieć uprawnień do zapisu.
Możemy więc rozwiązać wszystkie 1, 2, 5 i 6 za pomocą:
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
To i rozwiązanie dla 3 i 4 są niezależne, możesz scalić ich wyniki, aby uzyskać pełną listę:
{
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{$_}++'
Jak powinno być jasne, jeśli przeczytałeś wszystko do tej pory, jego część przynajmniej dotyczy uprawnień i własności, a nie innych funkcji, które mogą przyznawać lub ograniczać dostęp do zapisu (FS tylko do odczytu, listy ACL, niezmienna flaga, inne funkcje bezpieczeństwa ...) A ponieważ przetwarzamy je na kilku etapach, niektóre z tych informacji mogą być niepoprawne, jeśli pliki / katalogi są tworzone / usuwane / zmieniane nazwy lub ich uprawnienia / własność modyfikowane podczas działania tego skryptu, na przykład na zajętym serwerze plików z milionami plików .
Uwagi na temat przenośności
Cały ten kod jest standardowy (POSIX, Unix dla t
bitów), z wyjątkiem:
-print0
jest rozszerzeniem GNU obsługiwanym teraz także przez kilka innych implementacji. Z find
wdrożeń, które nie mają poparcia dla niego, można użyć -exec printf '%s\0' {} +
zamiast tego, i wymienić -exec sh -c 'exec find "$@" -print0' sh {} +
z -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
.
perl
nie jest poleceniem określonym w POSIX, ale jest powszechnie dostępne. Potrzebujesz perl-5.6.0
lub więcej -Mfiletest=access
.
zsh
nie jest poleceniem określonym przez POSIX. Powyższy zsh
kod powinien działać z zsh-3 (1995) i nowszymi.
sudo
nie jest poleceniem określonym przez POSIX. Kod powinien działać z dowolną wersją, o ile konfiguracja systemu pozwala na działanie perl
jako dany użytkownik.