proot
proot copied to clipboard
Preserve file ownership after setgid/setuid or fork
Expected Behavior
File ownership should be preserved after calls to setuid
and/or fork
. Such behavior is often required for some test suites to run properly.
Actual Behavior
Files created by proot always belong to the current gid:uid.
Steps to Reproduce the Problem
I wrote a simple program in C to show the issue.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
void writeFile(const char* filename)
{
FILE* file = fopen(filename, "w");
if (!file) {
fprintf(stderr, "fopen failed\n");
exit(1);
}
fputs("This is a file\n", file);
fclose(file);
}
void printOwner(const char* file, bool parent)
{
struct stat stat;
if (lstat(file, &stat) < 0) {
fprintf(stderr, "lstat failed\n");
exit(1);
}
printf("%s sees '%s' with owner %d:%d\n", parent ? "Parent" : "Child", file, stat.st_gid, stat.st_uid);
}
void testFork(bool parentIsUser, bool childIsUser)
{
const char* const parentFilename = "file created by parent";
const char* const childFilenameBeforeSetuid = "file created by child before setuid";
const char* const childFilename = "file created by child";
pid_t pid;
if (parentIsUser) {
setgid(1000);
setuid(1000);
}
writeFile(parentFilename);
printOwner(parentFilename, true);
pid = fork();
if (pid == 0) {
if (childIsUser) {
writeFile(childFilenameBeforeSetuid);
printOwner(childFilenameBeforeSetuid, false);
setgid(1000);
setuid(1000);
printOwner(childFilenameBeforeSetuid, false);
}
printOwner(parentFilename, false);
writeFile(childFilename);
printOwner(childFilename, false);
exit(0);
} else {
waitpid(pid, NULL, 0);
}
if (childIsUser) {
printOwner(childFilenameBeforeSetuid, true);
unlink(childFilenameBeforeSetuid);
}
printOwner(childFilename, true);
unlink(parentFilename);
unlink(childFilename);
}
void test(bool parentIsUser, bool childIsUser)
{
struct stat stat;
pid_t pid;
pid = fork();
if (pid == 0) {
testFork(parentIsUser, childIsUser);
exit(0);
} else {
waitpid(pid, NULL, 0);
}
}
int main()
{
puts("--------\nParent is root, child is root\n--------");
test(false, false);
puts("--------\nParent is root, child is user\n--------");
test(false, true);
puts("--------\nParent is user, child is user\n--------");
test(true, true);
puts("--------");
}
Specifications
- Proot/Care version: Proot v5.2.0-alpha-8c0ccf7d
- Kernel version: 5.11.16-arch1-1
- Host distribution: Arch Linux
- Guest distribution: None
Command Output
Running with sudo
, the result is as expected.
$ sudo ./test
--------
Parent is root, child is root
--------
Parent sees 'file created by parent' with owner 0:0
Child sees 'file created by parent' with owner 0:0
Child sees 'file created by child' with owner 0:0
Parent sees 'file created by child' with owner 0:0
--------
Parent is root, child is user
--------
Parent sees 'file created by parent' with owner 0:0
Child sees 'file created by child before setuid' with owner 0:0
Child sees 'file created by child before setuid' with owner 0:0
Child sees 'file created by parent' with owner 0:0
Child sees 'file created by child' with owner 1000:1000
Parent sees 'file created by child before setuid' with owner 0:0
Parent sees 'file created by child' with owner 1000:1000
--------
Parent is user, child is user
--------
Parent sees 'file created by parent' with owner 1000:1000
Child sees 'file created by child before setuid' with owner 1000:1000
Child sees 'file created by child before setuid' with owner 1000:1000
Child sees 'file created by parent' with owner 1000:1000
Child sees 'file created by child' with owner 1000:1000
Parent sees 'file created by child before setuid' with owner 1000:1000
Parent sees 'file created by child' with owner 1000:1000
--------
Running with proot -0
, ownership changes every time gid:uid changes.
$ proot -0 ./test
--------
Parent is root, child is root
--------
Parent sees 'file created by parent' with owner 0:0
Child sees 'file created by parent' with owner 0:0
Child sees 'file created by child' with owner 0:0
Parent sees 'file created by child' with owner 0:0
--------
Parent is root, child is user
--------
Parent sees 'file created by parent' with owner 0:0
Child sees 'file created by child before setuid' with owner 0:0
Child sees 'file created by child before setuid' with owner 1000:1000
Child sees 'file created by parent' with owner 1000:1000
Child sees 'file created by child' with owner 1000:1000
Parent sees 'file created by child before setuid' with owner 0:0
Parent sees 'file created by child' with owner 0:0
--------
Parent is user, child is user
--------
Parent sees 'file created by parent' with owner 1000:1000
Child sees 'file created by child before setuid' with owner 1000:1000
Child sees 'file created by child before setuid' with owner 1000:1000
Child sees 'file created by parent' with owner 1000:1000
Child sees 'file created by child' with owner 1000:1000
Parent sees 'file created by child before setuid' with owner 1000:1000
Parent sees 'file created by child' with owner 1000:1000
--------
I found this in the roadmap:
Explain why PRoot does not work with setuid programs.
Source: https://github.com/proot-me/proot/blob/master/doc/proot/roadmap.rst#documentation
I guess a setuid program is a program that has the setuid flag set, not a program that uses the setuid
syscall. Is that wrong?
I guess a setuid program is a program that has the setuid flag set, not a program that uses the
setuid
syscall. Is that wrong?
To be honest, I'm not sure.