나는 뿌리입니다. 루트가 아닌 사용자가 일부 파일(수천 개)에 대한 쓰기 액세스 권한을 갖고 있는지 알고 싶습니다. 프로세스 생성을 피하면서 효율적으로 수행하는 방법은 무엇입니까?
답변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
).
그는 다음이 필요합니다:
- 찾다액세스
/
(필수는 아님read
) - 찾다액세스
/foo
(필수는 아님read
) - 쓰다입력하다
/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
, 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, gid 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
- 같은 것번들 설치. 그 사람 은 검색 권한이 없을 수도
/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
장치 및 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/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
특정 사용자로 실행을 허용하는 한 모든 버전에서 작동해야 합니다.
답변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
경로명만약에 사실이다경로명다음에 정의된 대로 쓰기 권한이 부여될 파일에 대한 기존 디렉토리 항목을 확인합니다.파일 읽기, 쓰기 및 생성. 거짓이면경로명해결이 불가능하거나,경로명파일에 대한 쓰기 권한을 부여하지 않는 파일의 기존 디렉터리 항목으로 확인됩니다.
다들 확인하고 있어쉬운 접근.