proot icon indicating copy to clipboard operation
proot copied to clipboard

Preserve file ownership after setgid/setuid or fork

Open LHLaurini opened this issue 3 years ago • 3 comments

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
--------

LHLaurini avatar May 04 '21 17:05 LHLaurini

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

oxr463 avatar Jun 02 '21 22:06 oxr463

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?

LHLaurini avatar Jun 02 '21 22:06 LHLaurini

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.

oxr463 avatar Jun 02 '21 22:06 oxr463