최소한의 프로세스 생성으로 사용자가 쓸 수 있는 파일을 효율적으로 찾습니다.

최소한의 프로세스 생성으로 사용자가 쓸 수 있는 파일을 효율적으로 찾습니다.

나는 뿌리입니다. 루트가 아닌 사용자가 일부 파일(수천 개)에 대한 쓰기 액세스 권한을 갖고 있는지 알고 싶습니다. 프로세스 생성을 피하면서 효율적으로 수행하는 방법은 무엇입니까?

답변1

긴 이야기 짧게

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

사용자에게 쓰기 권한이 있는지 시스템에 물어봐야 합니다. 신뢰할 수 있는 유일한 방법은 유효 uid, 유효 gid 및 보충 gid를 사용자의 유효 uid, 유효 gid 및 보충 gid로 전환하고 시스템 access(W_OK)호출을 사용하는 것입니다(일부 시스템/구성에는 일부 제한이 있음에도 불구하고).

파일에 대한 쓰기 권한이 없다고 해서 반드시 해당 경로에 있는 파일의 내용을 수정할 수 없다는 보장은 없습니다.

더 긴 이야기

$user가 쓰기 권한을 가지려면 무엇이 필요한지 생각해 봅시다 /foo/file.txt(둘 다 심볼릭 링크도 아니고 심볼릭 링크 /foo도 아니라고 가정 /foo/file.txt).

그는 다음이 필요합니다:

  1. 찾다액세스 /(필수는 아님 read)
  2. 찾다액세스 /foo(필수는 아님 read)
  3. 쓰다입력하다/foo/file.txt

이 접근 방식은 이미 볼 수 있습니다(예:@lcd047님또는@aPaul의) 사용자에게 검색 권한이 없더라도 쓰기가 가능 file.txt하기 때문에 권한을 확인하는 것만으로는 작동하지 않습니다 .file.txt//foo

유사한 방법:

sudo -u "$user" find / -writeble

또한 사용자가 해당 파일에 쓸 수 있더라도 읽기 액세스 권한이 없는( 내용을 나열할 수 없기 때문에 find실행 중 ) 디렉터리의 파일을 보고하지 않기 때문에 작동하지 않습니다.$user

ACL, 읽기 전용 파일 시스템, FS 플래그(예: 불변), 기타 보안 조치(의류, 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

저것치다사용자에게 검색 권한이 없는 디렉토리 및 기타 유형의 파일에 대한 쓰기 액세스 권한을 확인하십시오(관련이 없는 심볼릭 링크 제외).

디렉토리에 대한 쓰기 액세스도 고려하려면 다음을 수행하십시오.

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, sedfind)

가장 좋은 방법은 루트로 트리를 내려가고 사용자로 각 파일의 권한을 확인하는 것입니다.

 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, sudoperl).

또는 다음을 사용하여 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, gid 1, 2를 가진 사용자에게는 쓰기 권한이 있지만 u2g1/010/u2g3/777쓰기 u1g2/677/u1g1/777권한은 없습니다.

이제 이러한 모든 솔루션은 사용자가 쓰기 위해 열 수 있는 파일 경로를 식별하려고 시도합니다. 이 경로는 사용자가 콘텐츠를 수정할 수 있는 경로와 다릅니다. 이 보다 일반적인 질문에 답하기 위해 고려해야 할 몇 가지 사항은 다음과 같습니다.

  1. $user는 쓰기 권한이 없을 수 있지만, /a/b/file그럴 경우 file(시스템에 대한 검색 권한이 있고 /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
  5. 쓰기 권한이 없을 수도 있지만 /a/b/file쓰기 권한이 있는 경우 내용을 /a/b삭제하거나 이름을 바꾸고 file자신의 버전으로 바꿀 수 있습니다. /a/b/file다른 파일이더라도 파일의 내용을 변경합니다 .
  6. 쓰기 액세스 권한이 있는 경우에도 마찬가지입니다 /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

장치 및 inode 번호 또는 $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/bfilet/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)입니다.

  • -print0GNU 확장이며 이제 여러 다른 구현에서도 지원됩니다. 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특정 사용자로 실행을 허용하는 한 모든 버전에서 작동해야 합니다.

답변2

어쩌면 다음과 같은 것일 수도 있습니다.

#! /bin/bash

writable()
{
    local uid="$1"
    local gids="$2"
    local ids
    local perms

    ids=($( stat -L -c '%u %g %a' -- "$3" ))
    perms="0${ids[2]}"

    if [[ ${ids[0]} -eq $uid ]]; then
        return $(( ( perms & 0200 ) == 0 ))
    elif [[ $gids =~ (^|[[:space:]])"${ids[1]}"($|[[:space:]]) ]]; then
        return $(( ( perms & 020 ) == 0 ))
    else
        return $(( ( perms & 2 ) == 0 ))
    fi
}

user=foo
uid="$( id -u "$user" )"
gids="$( id -G "$user" )"

while IFS= read -r f; do
    writable "$uid" "$gids" "$f" && printf '%s writable\n' "$f"
done

위의 명령은 각 파일에 대해 외부 프로그램, 즉 stat(1).

노트:이것은 bash(1)Linux의 가상적인 화신 입니다 stat(1).

노트 2:이 접근 방식의 과거, 현재, 미래 및 잠재적인 위험과 한계에 대해 알아보려면 아래 Stéphane Chazelas의 리뷰를 읽어보세요.

답변3

옵션을 명령과 결합하여 find지정된 모드와 소유자가 있는 파일을 찾을 수 있습니다. 예를 들어:

$ find / \( -group staff -o -group users \) -and -perm -g+w

위 명령은 "staff" 또는 "users" 그룹에 속하고 해당 그룹에 대한 쓰기 권한을 가진 모든 항목을 나열합니다.

또한 사용자가 소유한 항목과 누구나 쓸 수 있는 파일도 확인해야 합니다.

$ find / \( -user yourusername -or \
             \(  \( -group staff -o -group users \) -and -perm -g+w \
             \) -or \
            -perm -o+w \
         \)

그러나 이 명령은 확장 ACL이 있는 항목과 일치하지 않습니다. 이 방법으로 su쓰기 가능한 모든 항목을 찾을 수 있습니다.

# su - yourusername
$ find / -writable

답변4

넌 할 수있어...

find / ! -type d -exec tee -a {} + </dev/null

...사용자가 액세스한 모든 파일 목록할 수 없다stderr에 작성된 대로 작성합니다...

"tee: cannot access %s\n", <pathname>" 

...또는 유사합니다.

이 접근 방식에서 발생할 수 있는 문제에 대한 설명은 아래 설명을 참조하고, 이 접근 방식이 작동하는 이유에 대한 설명은 아래를 참조하세요. 그러나 보다 합리적으로, 아마도오직 find다음과 같은 일반 파일:

find / -type f -exec tee -a {} + </dev/null

즉, tee시도할 때 오류를 인쇄합니다.open()두 플래그 중 하나가 포함된 파일...

O_WRONLY

쓰기 전용입니다.

O_RDWR

읽기와 쓰기에 열려있습니다. 이 플래그가 FIFO에 적용되면 결과는 정의되지 않습니다.

...그리고 만남...

[EACCES]

경로 접두사의 구성 요소에 대한 검색 권한이 거부되었거나, 파일이 존재하고 oflag로 지정된 권한이 거부되었거나, 파일이 존재하지 않고 생성될 파일의 ​​상위 디렉터리에 대한 쓰기 권한이 거부되었거나, O_TRUNC가 지정되었습니다. 권한이 거부되었습니다.

...지정된 대로여기:

tee유틸리티는 표준 입력을 표준 출력으로 복사하여 0개 이상의 파일을 복사해야 합니다. tee 유틸리티는 출력을 버퍼링해서는 안 됩니다.

이 옵션을 지정하지 않으면 -a출력 파일을 작성해야 합니다(참조:파일 읽기, 쓰기 및 생성)...

...POSIX.1-2008에는 다음과 동일한 기능이 필요합니다.O_APPEND...

같은 방법으로 확인해야 하기 때문에test -w하다...

-w 경로명

만약에 사실이다경로명다음에 정의된 대로 쓰기 권한이 부여될 파일에 대한 기존 디렉토리 항목을 확인합니다.파일 읽기, 쓰기 및 생성. 거짓이면경로명해결이 불가능하거나,경로명파일에 대한 쓰기 권한을 부여하지 않는 파일의 기존 디렉터리 항목으로 확인됩니다.

다들 확인하고 있어쉬운 접근.

관련 정보