特定のユーザーがフォルダとそのコンテンツに対する読み取り権限を持っているかどうかを再帰的に確認する方法は?

特定のユーザーがフォルダとそのコンテンツに対する読み取り権限を持っているかどうかを再帰的に確認する方法は?

ユーザーとディレクトリをスクリプトに渡し、ユーザーに読み取り権限を持つそのディレクトリのフォルダ/ファイルのリストを表示させる必要があります。 MSにはこれを行うことができるWindows用AccessChkというツールがありますが、Unix側にも同様のツールはありますか?特定のフォルダまたはファイルに対してこれを行うことができるいくつかのコードが見つかりましたが、ディレクトリに移動するにはこのコードが必要です。

ベストアンサー1

長い話を短く

find "$dir" ! -type l -print0 |
  sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -r'

システムユーザーに読み取り権限があるかどうかを尋ねる必要があります。信頼できる唯一の方法は、有効なuid、有効なgid、および補足のgidをユーザーの有効なuid、有効なgid、および補足のgidに切り替えてシステムコールをaccess(R_OK)使用することです(一部のシステム/構成にはいくつかの制限があるにもかかわらず)。

長いストーリー

$ userが読み取り権限を持つには何が必要かを考えてみましょう/foo/file.txt(どちらもシンボリックリンクではないとします/foo/foo/file.txt

彼は以下が必要です:

  1. 探すアクセス/(不要読む)
  2. 探すアクセス/foo(不要読む)
  3. 読む入力する/foo/file.txt

ユーザーにも検索権限もなくても読むことができるとされているfile.txtため、単に権限を確認するアプローチは機能しないことが既にわかります。file.txt//foo

同様の方法:

sudo -u "$user" find / -readable

また、ユーザーが読み取り可能であるにもかかわらず、ユーザーが読み取り権限を持たない(コンテンツをリストできないためfind実行中)、ディレクトリー内のファイルを報告しないため、機能しません。$user

ACLやその他のセキュリティ対策(衣類、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 \
  \) ! -type d -o -type l -o \
    -user 1 \( ! -perm -u=r -o -print \) -o \
    \( -group 1 -o -group 2 \) \( ! -perm -g=r -o -print \) -o \
    ! -perm -o=r -o -print

それ打つユーザーが検索権限を持たないディレクトリおよび他の種類のファイルへの読み取りアクセス権を確認します(関連のないシンボリックリンクを除く)。

または、ユーザーデータベースから取得された1つ$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=r -o -print \) -o \
    \( -group $groups \) \( ! -perm -g=r -o -print \) -o \
    ! -perm -o=r -o -print

最善の方法は、ルートとしてツリーを下げて、ユーザーとして各ファイルの権限を確認することです。

 find / ! -type l -exec sudo -u "$user" sh -c '
   for file do
     [ -r "$file" ] && printf "%s\n" "$file"
   done' sh {} +

または以下を使用してperl

find / ! -type l -print0 |
  sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -r'

または以下を使用してzsh

files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
  [ -r $f ] && print -r -- $f
}

これらのソリューションはaccess(2)システムコールに依存します。つまり、システムがアクセス権を検証するために使用するアルゴリズムを複製するのではなく、読み取り用にファイルを開こうとしたときと同じアルゴリズム(権限、ACLなどを考慮)を使用してファイルを確認するようにシステムに要求します。 、はい。信頼できるソリューションに最も近いです。

これらのソリューションはすべて、ユーザーが読み取り用に開くことができるファイルパスを識別しようとします。このパスは、ユーザーがコンテンツを読み取ることができるパスとは異なります。このより一般的な質問に答えるために考慮すべきいくつかの点は次のとおりです。

  1. $ userには$ userへの読み取りアクセス権がない可能性がありますが、/a/b/file$ fileuserへの検索アクセス権があり、システムへのシェルアクセス権がある場合は、$ userの権限を変更して自分にアクセス権を付与/a/bできます。file
  2. /a/b検索権限があるが検索権限がない場合も同様です。
  3. $ userには、またはへの検索アクセス権が/a/b/fileないため、アクセス権がない可能性がありますが、ファイルにハードリンクがある可能性があります。この場合、たとえば、そのパスを介してコンテンツを開いてコンテンツを読み取ることができます。/a/a/b/b/c/file/a/b/file/b/c/file
  4. 同じものバンドルのインストール。その人は検索権限を持っていないかも/aしれませんが、/a/bバンドルのインストール、他のパスを介して読みやすいように開くことができ/cます。file/c/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=r -o -print \) -o \
    ! -perm -o=r -o -print

デバイスとinode番号、または$ userが読み取り権限を持つすべてのファイルを記録し、そのdev + inode番号を含むすべてのファイルパスを報告することで、3回と4回の問題を解決できます。今回は、より信頼性の高い access(2)ベースアプローチを使用できます。

それは次のとおりです。

find / ! -type l -print0 |
  sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-r,$_' |
  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;
        }
      }
    }'

両方のソリューションを次にマージします。

{ solution1; solution2
} | perl -l -0ne 'print unless $seen{$_}++'

これまでにすべての内容を読んだ場合、少なくともその一部は権限と所有権のみを扱い、読み取りアクセスを許可または制限できる他の機能(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 {} +
  • perlPOSIX 特定のコマンドではありませんが、広く利用可能です。あなたはperl-5.6.0以上が必要です-Mfiletest=access
  • zshPOSIX 指定コマンドではありません。上記のコードはzshzsh-3(1995)以降で動作します。
  • sudoPOSIX 指定コマンドではありません。コードは、システム構成でperl特定のユーザーとして実行できるようにする限り、すべてのバージョンで動作する必要があります。

おすすめ記事