TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
Sisteme kullanıcının yazma izni olup olmadığını sormanız gerekir. Tek güvenilir yol, etkin kullanıcı kimliği, etkili gid ve ek kılavuzları kullanıcınınkiyle değiştirmek ve access(W_OK)
sistem çağrısını kullanmaktır (bazı sistemlerde / yapılandırmalarda bazı sınırlamalar olsa bile).
Ayrıca, bir dosyaya yazma iznine sahip olmamanın, o yoldaki dosyanın içeriğini değiştiremeyeceğinizi garanti etmediğini unutmayın.
Daha uzun hikaye
Örneğin, $ kullanıcısı için yazma erişimine sahip olmanın ne anlama geldiğini düşünelim /foo/file.txt
( sembolik bağlantıların hiçbirinin /foo
ve /foo/file.txt
sembolik olmadığını varsayarak )?
Onun ihtiyacı var:
- arama erişimi
/
(gerek yok read
)
- arama erişimi
/foo
(gerek yok read
)
- yazma erişimi
/foo/file.txt
Zaten görebilirsiniz yaklaşımlar (gibi @ lcd047 en ya @ apaul en ) sadece iznini kontrol etmenizi file.txt
derler çünkü olmaz işin file.txt
kullanıcı arama iznine sahip olmasa bile yazılabilir olduğunu /
veya /foo
.
Ve şöyle bir yaklaşım:
sudo -u "$user" find / -writeble
Kullanıcılara okuma erişimi olmayan dizinlerde ( içeriği listelemediği gibi find
çalıştığı gibi) $user
dosyaları onlara yazabilse bile bildirmeyeceği için çalışmaz .
ACL'leri, salt okunur dosya sistemlerini, FS bayraklarını (değiştirilemez gibi), diğer güvenlik önlemlerini (farklı yazı türlerini bile ayırt edebilen apparmor, SELinux) unutursak ve yalnızca geleneksel izin ve sahiplik özelliklerine odaklanırsak, (arama veya yazma) izni verildiğinde, bu zaten oldukça karmaşık ve ifade edilmesi zor find
.
Gerekenler:
- dosyanın sahibi sizseniz, dosya sahibi için bu izne ihtiyacınız vardır (veya uid 0'a sahip olmalısınız)
- dosya size ait değilse, ancak grup sizinkilerden biriyse, grup için bu izne ihtiyacınız vardır (veya uid 0 değerine sahip olmalısınız).
- size ait değilse ve gruplarınızdan hiçbirinde yoksa, diğer izinler uygulanır (kullanıcı adınız 0 değilse).
Olarak find
söz dizimi, burada kimliği 1 ve Gids 1 ve 2'nin bir müşteri bir örnek olarak, bu şöyle olacaktır:
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
Bu bir kuru erik kullanıcı için ve diğer dosya türleri (bunlar alakalı değiliz olarak dışlanan symlinks), yazma erişimi için kontroller için doğru aramak var olmadığını dizinleri.
Ayrıca dizinlere yazma erişimini de dikkate almak istiyorsanız:
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
Veya $user
kullanıcı veritabanından alınan rasgele ve grup üyeliği için:
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
(: bu toplam 3 işlemleri olduğunu id
, sed
ve find
)
Burada en iyisi, ağacı kök olarak alçaltmak ve her dosya için kullanıcı olarak izinleri kontrol etmektir.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(bu bir find
işlem artı birdir sudo
ve sh
her birkaç bin dosyada bir işlem görür [
ve printf
genellikle kabukta oluşturulur).
Veya perl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 toplam süreçleri: find
, sudo
ve perl
).
Veya zsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(Toplamda 0 işlem, ancak tüm dosya listesini bellekte saklar)
Bu çözümler access(2)
sistem çağrısına dayanır . Bu, sistemin erişim iznini kontrol etmek için kullandığı algoritmayı yeniden üretmek yerine, sistemden bu kontrolü aynı algoritma ile yapmasını istiyoruz (izinleri, ACL'leri, değişmez bayrakları, salt okunur dosya sistemlerini dikkate alıyor ... ) kullanmak için dosyayı yazmak için açmaya çalışacaksınız, bu yüzden güvenilir bir çözüme en yakın olacaksınız.
Burada verilen çözümleri, çeşitli kullanıcı, grup ve izin kombinasyonlarıyla test etmek için şunları yapabilirsiniz:
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
}
}
}
}
}
}'
Kullanıcı 1 ile 2 arasında değişir ve grup 1, 2 ve 3 arasında değişir ve kendimizi zaten 9458694 dosya oluşturulduğu için izinlerin alt 9 bitiyle sınırlandırırız. Bu dizinler için ve sonra tekrar dosyalar için.
Bu, tüm olası kombinasyonlarını oluşturur u<x>g<y>/<mode1>/u<z>g<w>/<mode2>
. Uid 1 ve gids 1 ve 2'ye sahip olan kullanıcı yazma erişimine sahip olabilir, u2g1/010/u2g3/777
ancak böyle olmaz u1g2/677/u1g1/777
.
Şimdi, tüm bu çözümler kullanıcının yazmak için açabileceği dosyaların yollarını belirlemeye çalışır, bu kullanıcının içeriği değiştirebileceği yollardan farklıdır. Bu daha genel soruyu cevaplamak için dikkate alınması gereken birkaç şey vardır:
- $ kullanıcısının yazma erişimi olmayabilir,
/a/b/file
ancak sahibi varsa file
(ve arama erişimi varsa /a/b
ve dosya sistemi salt okunur değilse ve dosya değişmez bayrağa sahip değilse ve sisteme kabuk erişimi varsa), o zaman izinlerini değiştirebilir file
ve kendisine erişim izni verebilir .
- Sahip olduğu
/a/b
ancak arama erişimi yoksa aynı şey .
- $ kullanıcısı,
/a/b/file
e'ye arama erişimi olmadığı için erişemeyebilir /a
veya /a/b
bu dosyanın örneğin bir sabit bağlantısı olabilir /b/c/file
, bu durumda içeriğini yolunu /a/b/file
açarak değiştirebilir /b/c/file
.
- Bağ bağları ile aynı şey . O arama erişimi olmayabilir
/a
, ama /a/b
olabilir bağlama monte içinde /c
o açabileceğini yüzden, file
onun vasıtasıyla yazmak için /c/file
başka yolu.
- Yazma izni olmayabilir
/a/b/file
, ancak yazma erişimi varsa , oradan /a/b
kaldırabilir veya yeniden adlandırabilir file
ve kendi sürümüyle değiştirebilir. /a/b/file
Farklı bir dosya olsa bile dosyanın içeriğini değiştirirdi .
- O yazma erişimi var ise aynı şey
/a
o adlandırmak olabilir ( /a/b
hiç /a/c
bir yeni oluşturmak /a/b
dizin ve yeni file
İçinde.
$user
Değiştirilebilecek yolları bulmak için . 1 veya 2 adresini ele almak için access(2)
artık sistem çağrısına güvenemeyiz . find -perm
Dizinlere arama erişimi olduğunu varsaymak için yaklaşımımızı ayarlayabilir veya dosya sahibi olur olmaz dosyalara erişim yazabiliriz:
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
Cihaz ve inode numaralarını veya $ user'ın yazma iznine sahip olduğu tüm dosyaları kaydedip dev + inode numaralarına sahip tüm dosya yollarını kaydederek 3 ve 4'ü ele alabiliriz. Bu kez, daha güvenilir access(2)
tabanlı yaklaşımları kullanabiliriz:
Gibi bir şey:
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 ve 6 ilk bakışta t
izinlerin biti nedeniyle karmaşıktır . Dizinlere uygulandığında, kullanıcıların (dizinin sahibinden başkaları hariç) sahip olmadıkları dosyaları (dizine yazma erişimine sahip olsalar bile) kaldırmasını veya yeniden adlandırmasını engelleyen kısıtlı silme bitidir.
Örneğin, önceki örneğimize dönersek, yazma erişiminiz varsa /a
, yeniden adlandırabilmeniz ve ardından bir dizini ve yeni bir dizini yeniden /a/b
oluşturabilmeniz gerekir . Ancak bit açıksa ve sahip değilseniz, yalnızca sahipseniz yapabilirsiniz . Bu şunu verir:/a/c
/a/b
file
t
/a
/a
/a/b
- Bir dizininiz varsa, 1'e göre, kendinize yazma erişimi verebilirsiniz ve t biti geçerli değildir (ve yine de kaldırabilirsiniz), böylece içindeki herhangi bir dosyayı veya dizini silebilir / yeniden adlandırabilir / yeniden oluşturabilirsiniz. oradaki tüm dosya yolları, herhangi bir içerikle yeniden yazılmaya hazırdır.
- Sahip değilseniz ancak yazma erişiminiz varsa:
- Ya
t
biraz ayarlayın ve yukarıdaki ile aynı durumda olduğunu değil (tüm dosya yolları senin olsun).
- veya ayarlanmışsa ve sahip olmadığınız veya yazma erişimine sahip olmadığınız dosyaları değiştiremezsiniz, bu nedenle değiştirebileceğiniz dosya yollarını bulma amacımız için yazma iznine sahip olmamakla aynıdır.
Böylece 1, 2, 5 ve 6'nın tümünü aşağıdakilerle ele alabiliriz:
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
Bu ve 3 ve 4 için çözüm bağımsızdır, tam bir liste almak için çıktılarını birleştirebilirsiniz:
{
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{$_}++'
Şimdiye kadar her şeyi okuduysanız anlaşılacağı gibi, bir kısmı en azından yalnızca izinler ve sahiplikle ilgilidir, yazma erişimi verebilecek veya kısıtlayabilecek diğer özellikler (salt okunur FS, ACL'ler, değişmez bayrak, diğer güvenlik özellikleri) ...). Ve birkaç aşamada işlediğimizden, dosyalar / dizinler oluşturuluyorsa / siliniyorsa / yeniden adlandırılıyorsa veya bu komut dosyası çalışırken izinleri / sahipliği değiştirilirse, milyonlarca dosyaya sahip yoğun bir dosya sunucusunda olduğu gibi, bu bilgilerin bazıları yanlış olabilir. .
Taşınabilirlik notları
t
Şunlar hariç tüm bu kod standarttır (POSIX, Unix for bit):
-print0
artık birkaç uygulama tarafından da desteklenen bir GNU oluşumudur. Bunun find
için destek bulunmayan uygulamalarda -exec printf '%s\0' {} +
bunun yerine kullanılabilir ve -exec sh -c 'exec find "$@" -print0' sh {} +
onunla değiştirebilirsiniz -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
.
perl
POSIX tarafından belirtilen bir komut değildir, ancak yaygın olarak kullanılabilir. İçin perl-5.6.0
veya daha fazlasına ihtiyacınız var -Mfiletest=access
.
zsh
POSIX tarafından belirtilen bir komut değildir. Bu zsh
kod, yukarıda zsh-3 (1995), yukarıda birlikte çalışması gerekir.
sudo
POSIX tarafından belirtilen bir komut değildir. Sistem yapılandırması perl
verilen kullanıcı olarak çalışmasına izin verdiği sürece kodun herhangi bir sürümle çalışması gerekir .