TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
ユーザーに書き込み権限があるかどうかをシステムに尋ねる必要があります。唯一の信頼できる方法は、有効なuid、有効なgid、および補足のgidをユーザーのgidに切り替え、access(W_OK)
システムコールを使用することです(システム/構成によっては制限があります)。
また、ファイルへの書き込み権限を持たないからといって、必ずしもそのパスでファイルの内容を変更できないとは限らないことを覚えておいてください。
長い話
のは、それはへの書き込みアクセス持っている$ユーザーのためのインスタンスのにかかるかを考えてみましょう/foo/file.txt
(のどれを想定していない/foo
と/foo/file.txt
シンボリックリンクされているが)?
彼が必要とします:
- への検索アクセス
/
(の必要なしread
)
- への検索アクセス
/foo
(の必要なしread
)
- への書き込みアクセス
/foo/file.txt
あなたは既に見ることができるのアプローチ(のような@ lcd047のか@ apaulさん)の権限だけをチェックしているfile.txt
彼らが言う可能性があるためではないでしょう仕事file.txt
ユーザが検索権限を持っていない場合でも書き込み可能です/
か/foo
。
そして、次のようなアプローチ:
sudo -u "$user" find / -writeble
ユーザーが読み取りアクセス権を持っていないディレクトリ内のファイルを報告しないため(コンテンツを一覧表示できないためfind
実行し$user
ている場合)、ユーザーがファイルに書き込みを行っても動作しません。
ACL、読み取り専用ファイルシステム、FSフラグ(不変など)、その他のセキュリティ対策(さまざまなタイプの書き込みを区別することもできるapparmor、SELinux)を忘れて、従来の許可属性と所有権属性のみに焦点を当てて、 (検索または書き込み)許可が与えられている場合、それはすでにかなり複雑であり、で表現するのは困難find
です。
必要なもの:
- ファイルが所有者である場合、所有者にその許可が必要です(またはuid 0を持っています)
- ファイルの所有者が自分ではないが、グループが自分のものである場合は、グループにそのアクセス許可が必要です(またはuid 0が必要です)。
- あなたが所有しておらず、どのグループにも属していない場合、他の許可が適用されます(uidが0でない場合)。
find
構文、ここでUID 1とGID 1及び2のユーザとの一例として、それは次のようになります。
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
1つのそれプルーンユーザーが権利のためにと他の種類のファイル(彼らは関係ありませんねとして除外シンボリックリンク)、書き込みアクセスのためのチェックのために検索していないことをディレクトリ。
ディレクトリへの書き込みアクセスも検討する場合:
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
または$user
、ユーザーデータベースから取得した任意のグループメンバーシップの場合:
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
(これは合計で3つのプロセスです:id
、sed
およびfind
)
ここでの最良の方法は、ルートとしてツリーを降りて、各ファイルのユーザーとして権限を確認することです。
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(それは一つだfind
プロセスプラスワンsudo
とsh
ごとに数千のファイルを処理し、[
そしてprintf
通常はシェルに組み込まれています)。
またはperl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(合計3プロセス:find
、sudo
およびperl
)。
またはzsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(合計で0プロセスですが、ファイルリスト全体をメモリに保存します)
これらのソリューションは、access(2)
システムコールに依存しています。これは、システムがアクセス許可を確認するために使用するアルゴリズムを再現する代わりに、システムに同じアルゴリズム(許可、ACL、不変フラグ、読み取り専用ファイルシステムを考慮に入れる)でその確認を行うように依頼しています。 )書き込み用にファイルを開こうとすると、信頼できるソリューションに最も近いものが使用されます。
ユーザー、グループ、アクセス許可のさまざまな組み合わせを使用して、ここで示されたソリューションをテストするには、次のようにします。
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
}
}
}
}
}
}'
1から2の間でユーザーを変更し、1、2、3の間でグループを作成し、既に9458694のファイルが作成されているため、アクセス許可の下位9ビットに制限します。ディレクトリの場合は、ファイルの場合も同様です。
これにより、可能なすべての組み合わせが作成されますu<x>g<y>/<mode1>/u<z>g<w>/<mode2>
。uid 1とgids 1および2を持つユーザーは書き込みアクセス権を持ちますu2g1/010/u2g3/777
がu1g2/677/u1g1/777
、たとえばアクセス権はありません。
現在、これらのソリューションはすべて、ユーザーが書き込みのために開く可能性のあるファイルのパスを特定しようとします。これは、ユーザーがコンテンツを変更できるパスとは異なります。このより一般的な質問に答えるために、考慮すべきことがいくつかあります。
- $ userは書き込みアクセス権を持たないかもしれませんが
/a/b/file
、所有している場合file
(および、検索アクセス権を/a/b
持ち、ファイルシステムが読み取り専用ではなく、ファイルに不変フラグがなく、システムへのシェルアクセスがある場合)、その後、彼はの許可を変更し、file
自分にアクセスを許可することができます。
- 彼が所有
/a/b
しているが検索アクセス権を持っていない場合も同じです。
- $ userは
/a/b/file
、/a
またはへの検索アクセス権がないためアクセスできませんが/a/b
、そのファイルには/b/c/file
、たとえば、ハードリンクが含まれている場合があり、その場合/a/b/file
、/b/c/file
パスを介してファイルを開くことでコンテンツを変更できる場合があります。
- bind-mountsでも同じです。彼はへの検索アクセス権を持っていない可能性
/a
が/a/b
ありますが、にバインドマウントされている可能性がある/c
ためfile
、/c/file
他のパスを介して書き込み用に開くことができます。
- 彼には書き込み権限がない場合がありますが、書き込み権限
/a/b/file
がある場合は、/a/b
削除または名前を変更file
して自分のバージョンに置き換えることができます。彼は別のファイルで/a/b/file
あっても、ファイルの内容を変更します。
- 彼が書き込みアクセス権を持っている場合も同じです
/a
(名前/a/b
を/a/c
に変更して、新しい/a/b
ディレクトリとその中に新しいディレクトリを作成できます)file
。
$user
変更できるパスを見つけるため。1または2に対処するには、access(2)
システムコールに依存できなくなります。find -perm
ディレクトリへの検索アクセスを想定するようにアプローチを調整するか、所有者になったらすぐにファイルへの書き込みアクセスを行うことができます。
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
デバイスとiノード番号、または$ userがそれらのdev + inode番号を持つすべてのファイルパスへの書き込み権限を持ち、報告するすべてのファイルを記録することにより、3と4に対処できます。今回は、より信頼性の高いaccess(2)
ベースのアプローチを使用できます 。
何かのようなもの:
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と6はt
、許可のビットによって一見複雑です。ディレクトリに適用された場合、それはユーザー(ディレクトリの所有者以外)が(ディレクトリへの書き込みアクセス権を持っているとしても)所有していないファイルを削除または名前変更できないようにする制限付き削除ビットです。
たとえば、前の例に戻って、への書き込みアクセス権がある/a
場合、名前/a/b
をに変更し/a/c
、/a/b
ディレクトリとfile
そこに新しいディレクトリを再作成できるはずです。しかし、t
ビットがオンに設定され/a
ていて、所有していない場合は、所有/a
している場合にのみ実行できます/a/b
。それは与える:
- 1のようにディレクトリを所有している場合は、自分に書き込みアクセスを許可できますが、tビットは適用されません(とにかく削除できます)。そのため、ファイルやディレクトリを削除/名前変更/再作成できますそこにあるすべてのファイルパスは、任意のコンテンツで書き換える必要があります。
- 所有していないが、書き込みアクセス権がある場合:
- いずれかの
t
ビットがセットされ、あなたが上記と同じ場合にしていません(すべてのファイル・パスは、あなたです)。
- または、設定されているので、所有していないファイルや書き込みアクセス権のないファイルを変更することはできません。したがって、変更可能なファイルパスを見つけるという目的では、書き込み許可をまったく持たないのと同じです。
したがって、1、2、5、および6のすべてに以下を使用して対処できます。
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
それと3と4のソリューションは独立しているため、それらの出力をマージして完全なリストを取得できます。
{
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{$_}++'
これまでにすべてを読んだ場合は明らかなように、その一部は少なくともアクセス許可と所有権のみを扱い、書き込みアクセスを許可または制限する可能性のある他の機能(読み取り専用FS、ACL、不変フラグ、その他のセキュリティ機能) ...)。そして数段階で処理するため、数百万のファイルがあるビジーなファイルサーバーのように、スクリプトの実行中にファイル/ディレクトリが作成/削除/名前変更されたり、権限/所有権が変更されると、その情報の一部が間違っている可能性があります。
移植性に関する注意
以下を除くすべてのコードは標準です(POSIX、t
ビットはUnix )。
-print0
は、現在いくつかの他の実装でもサポートされているGNU拡張機能です。find
それをサポートしていない実装では、使用することができます-exec printf '%s\0' {} +
代わりに、と交換してください-exec sh -c 'exec find "$@" -print0' sh {} +
と-exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
。
perl
POSIX指定のコマンドではありませんが、広く利用可能です。perl-5.6.0
以上が必要です-Mfiletest=access
。
zsh
POSIX指定のコマンドではありません。そのzsh
コードは、上記のzsh-3(1995)および上記で動作しなければなりません。
sudo
POSIX指定のコマンドではありません。システム構成で指定さperl
れたユーザーとして実行できる限り、コードはどのバージョンでも動作するはずです。