TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
Anda perlu menanyakan sistem apakah pengguna memiliki izin menulis. Satu-satunya cara yang dapat diandalkan adalah untuk mengganti gid yang efektif, gid dan suplementasi yang efektif menjadi milik pengguna dan menggunakannyaaccess(W_OK)
panggilan sistem (bahkan yang memiliki beberapa keterbatasan pada beberapa sistem / konfigurasi).
Dan ingatlah bahwa tidak memiliki izin menulis ke file tidak selalu menjamin bahwa Anda tidak dapat mengubah konten file di jalur itu.
Cerita yang lebih panjang
Mari kita pertimbangkan apa yang diperlukan misalnya bagi $ user untuk memiliki akses tulis /foo/file.txt
(dengan asumsi tidak ada /foo
dan /foo/file.txt
merupakan symlink)?
Dia membutuhkan:
- cari akses ke
/
(tidak perlu read
)
- cari akses ke
/foo
(tidak perlu read
)
- akses tulis ke
/foo/file.txt
Anda sudah dapat melihat bahwa pendekatan (seperti @ lcd047 atau @ apaul ) yang memeriksa hanya izin file.txt
tidak akan berfungsi karena mereka bisa mengatakan file.txt
itu dapat ditulisi bahkan jika pengguna tidak memiliki izin pencarian untuk /
atau/foo
.
Dan pendekatan seperti:
sudo -u "$user" find / -writeble
Tidak akan berfungsi karena itu tidak akan melaporkan file dalam direktori pengguna tidak memiliki akses baca ( find
berjalan karena $user
tidak dapat membuat daftar konten mereka) bahkan jika ia dapat menulis kepada mereka.
Jika kita melupakan ACL, sistem file read-only, flag FS (seperti immutable), langkah-langkah keamanan lainnya (apparmor, SELinux, yang bahkan dapat membedakan antara berbagai jenis penulisan) dan hanya fokus pada izin tradisional dan atribut kepemilikan, untuk mendapatkan diberikan (cari atau tulis) izin, itu sudah cukup rumit dan sulit untuk diungkapkan find
.
Anda membutuhkan:
- jika file tersebut milik Anda, Anda memerlukan izin untuk pemiliknya (atau memiliki 0)
- jika file tersebut bukan milik Anda, tetapi grup tersebut adalah salah satu milik Anda, maka Anda memerlukan izin untuk grup tersebut (atau minta 0).
- jika tidak dimiliki oleh Anda, dan tidak di salah satu grup Anda, maka izin lainnya berlaku (kecuali uid Anda adalah 0).
Dalam find
sintaksis, di sini sebagai contoh dengan pengguna uid 1 dan gids 1 dan 2, yaitu:
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
Yang itu memangkas direktori yang pengguna tidak memiliki hak untuk mencari dan untuk jenis file lainnya (symlink dikecualikan karena tidak relevan), memeriksa akses tulis.
Jika Anda juga ingin mempertimbangkan akses tulis ke direktori:
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
Atau untuk arbitrer $user
dan keanggotaan grupnya diambil dari basis data pengguna:
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
(total 3 proses:: id
, sed
dan find
)
Yang terbaik di sini adalah untuk turun pohon sebagai root dan memeriksa izin sebagai pengguna untuk setiap file.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(Itu satu find
proses ditambah satu sudo
dan sh
proses setiap beberapa ribu file, [
dan printf
biasanya dibangun di shell).
Atau dengan perl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 proses secara total: find
, sudo
dan perl
).
Atau dengan zsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(0 total proses, tetapi menyimpan seluruh daftar file dalam memori)
Solusi tersebut bergantung pada access(2)
panggilan sistem. Itu alih-alih mereproduksi algoritma yang digunakan sistem untuk memeriksa izin akses, kami meminta sistem untuk melakukan pemeriksaan dengan algoritma yang sama (yang memperhitungkan izin akun, ACL, bendera yang tidak dapat diubah, sistem file read-only ... ) itu akan digunakan jika Anda mencoba untuk membuka file untuk menulis, jadi terdekat Anda akan mendapatkan solusi yang dapat diandalkan.
Untuk menguji solusi yang diberikan di sini, dengan berbagai kombinasi pengguna, grup, dan izin, Anda dapat melakukan:
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
}
}
}
}
}
}'
Memvariasikan pengguna antara 1 dan 2 dan grup antara 1, 2, dan 3 dan membatasi diri hingga 9 bit yang lebih rendah dari izin karena sudah 9458694 file dibuat. Itu untuk direktori dan sekali lagi untuk file.
Itu menciptakan semua kemungkinan kombinasi u<x>g<y>/<mode1>/u<z>g<w>/<mode2>
. Pengguna dengan uid 1 dan gids 1 dan 2 akan memiliki akses tulis, u2g1/010/u2g3/777
tetapi tidak u1g2/677/u1g1/777
misalnya.
Sekarang, semua solusi itu mencoba mengidentifikasi jalur file yang dapat dibuka oleh pengguna untuk ditulis, itu berbeda dari jalur di mana pengguna mungkin dapat mengubah konten. Untuk menjawab pertanyaan yang lebih umum itu, ada beberapa hal yang harus diperhatikan:
- $ user mungkin tidak memiliki akses tulis ke
/a/b/file
tetapi jika ia memiliki file
(dan memiliki akses pencarian /a/b
, dan sistem file tidak hanya-baca, dan file tidak memiliki bendera yang tidak dapat diubah, dan ia memiliki akses shell ke sistem), maka dia akan dapat mengubah izin file
dan memberikan dirinya akses.
- Hal yang sama jika dia miliki
/a/b
tetapi tidak memiliki akses pencarian untuk itu.
- $ user mungkin tidak memiliki akses ke
/a/b/file
karena ia tidak memiliki akses pencarian ke /a
atau /a/b
, tetapi file itu mungkin memiliki hard link /b/c/file
, misalnya, dalam hal ini ia mungkin dapat memodifikasi konten /a/b/file
dengan membukanya melalui /b/c/file
jalurnya.
- Hal yang sama dengan bind-mounts . Dia mungkin tidak memiliki akses pencarian
/a
, tetapi /a/b
dapat di -mount- in /c
, sehingga dia dapat membuka file
untuk menulis melalui /c/file
jalur lainnya.
- Dia mungkin tidak memiliki izin menulis untuk
/a/b/file
, tetapi jika dia memiliki akses tulis untuk /a/b
dia dapat menghapus atau mengganti nama file
di sana dan menggantinya dengan versinya sendiri. Dia akan mengubah konten file /a/b/file
bahkan jika itu akan menjadi file yang berbeda.
- Hal yang sama jika dia punya akses tulis
/a
(dia bisa mengganti nama /a/b
menjadi /a/c
, membuat /a/b
direktori baru dan baru file
di dalamnya.
Untuk menemukan jalur yang $user
akan dapat dimodifikasi. Untuk mengatasi 1 atau 2, kita tidak bisa lagi mengandalkan access(2)
panggilan sistem. Kami dapat menyesuaikan find -perm
pendekatan kami untuk menganggap akses pencarian ke direktori, atau menulis akses ke file segera setelah Anda pemiliknya:
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
Kita dapat mengatasi 3 dan 4, dengan merekam perangkat dan nomor inode atau semua file $ user memiliki izin menulis dan melaporkan semua path file yang memiliki nomor dev + inode tersebut. Kali ini, kita bisa menggunakan yang lebih andal access(2)
pendekatan berbasis :
Sesuatu seperti:
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 dan 6 pada pandangan pertama diperumit dengan t
sedikit izin. Ketika diterapkan pada direktori, itu adalah bit penghapusan terbatas yang mencegah pengguna (selain pemilik direktori) menghapus atau mengganti nama file yang tidak mereka miliki (walaupun mereka memiliki akses tulis ke direktori).
Misalnya, jika kita kembali ke contoh kita sebelumnya, jika Anda memiliki akses tulis ke /a
, maka Anda harus dapat mengubah nama /a/b
untuk /a/c
, dan kemudian menciptakan sebuah /a/b
direktori dan baru file
di sana. Tetapi jika t
bit sudah diaktifkan /a
dan Anda tidak memiliki /a
, maka Anda hanya dapat melakukannya jika Anda memilikinya /a/b
. Itu memberi:
- Jika Anda memiliki direktori, sesuai 1, Anda dapat memberikan diri Anda akses tulis, dan bit itu tidak berlaku (dan Anda bisa menghapusnya), sehingga Anda dapat menghapus / mengganti nama / membuat ulang file atau direktori apa pun di sana, jadi semua jalur file di bawah ini milik Anda untuk ditulis ulang dengan konten apa pun.
- Jika Anda tidak memilikinya tetapi memiliki akses tulis, maka:
- Entah itu
t
bit tidak disetel, dan Anda berada dalam kasus yang sama seperti di atas (semua jalur file adalah milik Anda).
- atau sudah diatur dan kemudian Anda tidak dapat memodifikasi file yang tidak Anda miliki atau tidak memiliki akses tulis, jadi untuk tujuan kami menemukan jalur file yang dapat Anda modifikasi, itu sama dengan tidak memiliki izin menulis sama sekali.
Jadi kita dapat mengatasi semua 1, 2, 5 dan 6 dengan:
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
Itu dan solusi untuk 3 dan 4 adalah independen, Anda dapat menggabungkan output mereka untuk mendapatkan daftar lengkap:
{
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{$_}++'
Sebagaimana harus jelas jika Anda telah membaca semuanya sejauh ini, sebagian darinya setidaknya hanya berkaitan dengan izin dan kepemilikan, bukan fitur lain yang dapat memberikan atau membatasi akses tulis (hanya baca FS, ACL, bendera tidak berubah, fitur keamanan lainnya ...). Dan ketika kami memprosesnya dalam beberapa tahap, beberapa informasi itu mungkin salah jika file / direktori sedang dibuat / dihapus / diubah namanya atau izin / kepemilikannya dimodifikasi ketika skrip sedang berjalan, seperti pada server file yang sibuk dengan jutaan file .
Catatan portabilitas
Semua kode itu standar (POSIX, Unix for t
bit) kecuali:
-print0
adalah ekstensi GNU sekarang juga didukung oleh beberapa implementasi lainnya. Dengan find
implementasi yang tidak memiliki dukungan untuk itu, Anda dapat menggunakan -exec printf '%s\0' {} +
sebagai gantinya, dan mengganti -exec sh -c 'exec find "$@" -print0' sh {} +
dengan -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
.
perl
bukan perintah yang ditentukan POSIX tetapi tersedia secara luas. Anda perlu perl-5.6.0
atau di atas -Mfiletest=access
.
zsh
bukan perintah yang ditentukan POSIX. Itu zsh
kode di atas harus bekerja dengan zsh-3 (1995) dan di atas.
sudo
bukan perintah yang ditentukan POSIX. Kode harus bekerja dengan versi apa pun selama konfigurasi sistem memungkinkan berjalan perl
sebagai pengguna yang diberikan.