일반 사용자로 프로그램을 시작한 다음 루트로 프로그램을 시작하기 전에 일부 구성을 읽는 프로그램을 만들려고 시도하는 동안 이 이상한 동작을 발견했습니다. 다른 곳에서는 언급을 찾을 수 없는 것 같습니다. 일반 파일 시스템은 액세스 확인을 위해 유효한 UID/GID를 사용하지만 FUSE는 액세스를 위해 유효하고 실제이며 저장된(!!) UID/GID 3개를 모두 확인하는 것처럼 보입니다. 처음에는 나중에 복원할 수 있도록 유효한 uid를 삭제했지만 무슨 일이 일어나고 있는지 깨달을 때까지 권한 오류가 계속 발생했습니다.
왜 그런 상황이 있습니까? FUSE가 저장된 uid/gid에 관심을 갖는 이유는 무엇입니까?
(FUSE를 설정 allow_root
하고 이를 피할 수 있다는 것을 알고 있습니다. 이는 이 질문의 목적이 아닙니다.)
데모용 샘플 C 코드:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define measure() getresuid(&ruid, &euid, &suid); getresgid(&rgid, &egid, &sgid); printf("UID: %4d, %4d, %4d. GID: %4d, %4d, %4d \t\t", ruid, euid, suid, rgid, egid, sgid); fflush(stdout)
#define set(r,e,s) if (setresuid(0,0,0 ) != 0) return 1; if (setresgid(r,e,s ) != 0) return 1; if (setresuid(r, e, s) != 0) return 1;
#define attempt(r,e,s) set(r,e,s); measure(); test(argv[1])
void test(char* arg)
{
struct stat sb;
if (stat(arg, &sb) == -1)
perror("fail");
else
printf("Success\n");
}
int main(int argc, char *argv[])
{
uid_t ruid, euid, suid; gid_t rgid, egid, sgid;
measure();
printf("\n\n");
attempt(1000,0,0); // Expect: Fail. Actual: Fail
attempt(0, 1000,0); // Expect: ok. Actual: Fail
attempt(0, 0, 1000); // Expect: Fail. Actual: Fail
attempt(1000,1000,0); // Expect: ok. Actual: Fail
attempt(1000,0,1000); // Expect: Fail. Actual: Fail
attempt(0,1000,1000); // Expect: ok. Actual: Fail
attempt(1000,1000,1000); // Expect: ok. Actual: ok
return 0;
}
산출:
$ sshfs some-other-machine:/ /tmp/testit # I think any FUSE filesystem should "work"
$ gcc test.c -o test
$ sudo ./test /tmp/testit
UID: 0, 0, 0. GID: 0, 0, 0
UID: 1000, 0, 0. GID: 1000, 0, 0 fail: Permission denied
UID: 0, 1000, 0. GID: 0, 1000, 0 fail: Permission denied
UID: 0, 0, 1000. GID: 0, 0, 1000 fail: Permission denied
UID: 1000, 1000, 0. GID: 1000, 1000, 0 fail: Permission denied
UID: 1000, 0, 1000. GID: 1000, 0, 1000 fail: Permission denied
UID: 0, 1000, 1000. GID: 0, 1000, 1000 fail: Permission denied
UID: 1000, 1000, 1000. GID: 1000, 1000, 1000 Success
$
답변1
알다시피 allow_root
/ allow_other
옵션이 없으면 다른 프로세스는 파일 시스템에 액세스할 수 없습니다. 이는 파일 시스템을 보호하기 위한 것이 아니라 다른 프로세스를 보호하기 위한 것입니다. 따라서 접근 과정에서 다른 신원에 대한 힌트가 조금이라도 있으면 접근이 허용되지 않습니다.
다음은 이 동작과 관련된 커널의 코드입니다( fs/fuse/dir.c
).
/*
* Calling into a user-controlled filesystem gives the filesystem
* daemon ptrace-like capabilities over the current process. This
* means, that the filesystem daemon is able to record the exact
* filesystem operations performed, and can also control the behavior
* of the requester process in otherwise impossible ways. For example
* it can delay the operation for arbitrary length of time allowing
* DoS against the requester.
*
* For this reason only those processes can call into the filesystem,
* for which the owner of the mount has ptrace privilege. This
* excludes processes started by other users, suid or sgid processes.
*/
int fuse_allow_current_process(struct fuse_conn *fc)
{
const struct cred *cred;
if (fc->allow_other)
return current_in_userns(fc->user_ns);
cred = current_cred();
if (uid_eq(cred->euid, fc->user_id) &&
uid_eq(cred->suid, fc->user_id) &&
uid_eq(cred->uid, fc->user_id) &&
gid_eq(cred->egid, fc->group_id) &&
gid_eq(cred->sgid, fc->group_id) &&
gid_eq(cred->gid, fc->group_id))
return 1;
return 0;
}