저장된 UID/GID를 보기 위한 FUSE 파일 시스템?

저장된 UID/GID를 보기 위한 FUSE 파일 시스템?

일반 사용자로 프로그램을 시작한 다음 루트로 프로그램을 시작하기 전에 일부 구성을 읽는 프로그램을 만들려고 시도하는 동안 이 이상한 동작을 발견했습니다. 다른 곳에서는 언급을 찾을 수 없는 것 같습니다. 일반 파일 시스템은 액세스 확인을 위해 유효한 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;
}

관련 정보