While attempting to create a program that reads some configuration before launching programs as a normal user and then as the root user, I noticed this odd behavior. I can't seem to find mention of it anywhere else. Normal filesystems use the effective UID/GID for access checks, but it looks like FUSE seem to check all three of the effective, real, and saved(!!) UID/GID for access. I had initially just dropped the effective uid so that I could recover it later, but this kept me getting permissions errors until I realized what was going on.
Why is this this case? Why does FUSE care about the saved uid/gid?
(I'm aware I can set allow_root
on FUSE and avoid this, that isn't what this question is about)
Example C code to demonstrate:
#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;
}
Output:
$ 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
$