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 /foove /foo/file.txtsembolik 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.txtderler çünkü olmaz işin file.txtkullanı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) $userdosyaları 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 findsö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 $userkullanı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, sedve 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 findişlem artı birdir sudove shher birkaç bin dosyada bir işlem görür [ve printfgenellikle 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, sudove 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/777ancak 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/fileancak sahibi varsa file(ve arama erişimi varsa /a/bve 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 fileve kendisine erişim izni verebilir .
- Sahip olduğu
/a/bancak arama erişimi yoksa aynı şey .
- $ kullanıcısı,
/a/b/filee'ye arama erişimi olmadığı için erişemeyebilir /aveya /a/bbu dosyanın örneğin bir sabit bağlantısı olabilir /b/c/file, bu durumda içeriğini yolunu /a/b/fileaçarak değiştirebilir /b/c/file.
- Bağ bağları ile aynı şey . O arama erişimi olmayabilir
/a, ama /a/bolabilir bağlama monte içinde /co açabileceğini yüzden, fileonun vasıtasıyla yazmak için /c/filebaşka yolu.
- Yazma izni olmayabilir
/a/b/file, ancak yazma erişimi varsa , oradan /a/bkaldırabilir veya yeniden adlandırabilir fileve kendi sürümüyle değiştirebilir. /a/b/fileFarklı bir dosya olsa bile dosyanın içeriğini değiştirirdi .
- O yazma erişimi var ise aynı şey
/ao adlandırmak olabilir ( /a/bhiç /a/cbir yeni oluşturmak /a/bdizin ve yeni fileİçinde.
$userDeğ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 -permDizinlere 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 tizinlerin 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/boluşturabilmeniz gerekir . Ancak bit açıksa ve sahip değilseniz, yalnızca sahipseniz yapabilirsiniz . Bu şunu verir:/a/c/a/bfilet/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
tbiraz 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):
-print0artık birkaç uygulama tarafından da desteklenen bir GNU oluşumudur. Bunun findiç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 {} +.
perlPOSIX tarafından belirtilen bir komut değildir, ancak yaygın olarak kullanılabilir. İçin perl-5.6.0veya daha fazlasına ihtiyacınız var -Mfiletest=access.
zshPOSIX tarafından belirtilen bir komut değildir. Bu zshkod, yukarıda zsh-3 (1995), yukarıda birlikte çalışması gerekir.
sudoPOSIX tarafından belirtilen bir komut değildir. Sistem yapılandırması perlverilen kullanıcı olarak çalışmasına izin verdiği sürece kodun herhangi bir sürümle çalışması gerekir .