在尝试创建一个在以普通用户身份启动程序然后以 root 用户身份启动程序之前读取某些配置的程序时,我注意到了这种奇怪的行为。我似乎在其他地方找不到提及它。普通文件系统使用有效 UID/GID 进行访问检查,但看起来 FUSE 似乎检查所有三个有效、真实和已保存(!!) UID/GID 进行访问。我最初只是删除了有效的 uid,以便稍后恢复它,但这让我不断收到权限错误,直到我意识到发生了什么。
为什么会出现这样的情况呢?为什么FUSE关心保存的uid/gid?
(我知道我可以设置allow_root
FUSE 并避免这种情况,这不是这个问题的目的)
用于演示的示例 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;
}