Search code examples
pythoncsetuidexecve

Executing a Python command with a setuid/setgid wrapper


I have the following Python script that I'd like to execute with the permissions afforded by the setuid/setgid bit:

#!/usr/bin/env python3
from mypackage.cli import main as cli_main
cli_main()

However: I would like execute the command directly from a C wrapper without an intermediate Python script file.

I've tried doing this using execve as follows:

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

const char *ENV = "/usr/bin/env";
const char *ENV_ARGS[] = { "python3", "-c", "from mypackage.cli import main as cli_main; cli_main()" };
const int NUM_ENV_ARGS = 3;

int main(int argc, char *argv[], char *envp[]) {
    int total_num_args = (argc - 1) + NUM_ENV_ARGS + 1;

    // Create an array of strings to hold the final arg list.
    // No need to free the malloc'ed memory as it will be freed if execve succeeds,
    // or when the program exits if execve fails.
    char **final_args = (char **)malloc(total_num_args * sizeof(char *));
    if (final_args == NULL) {
        return 1;
    }

    // Copy the invocation and the arguments to this program into the final arg list.
    memcpy(final_args, ENV_ARGS, NUM_ENV_ARGS * sizeof(char *));
    memcpy(final_args + NUM_ENV_ARGS, argv + 1, (argc - 1) * sizeof(char *));
    final_args[total_num_args - 1] = NULL;

    return execve(ENV, final_args, envp);
}

But I get the following error when I run the compiled program as ./mycli foo bar:

python3: illegal option -- c
usage: env [-iv] [-P utilpath] [-S string] [-u name]
           [name=value ...] [utility [argument ...]]

How can I accomplish this?


Solution

  • You are constructing your argument array incorrectly. It should correspond exactly to the exec'ed program's argv array, including that the 0th element designates the name of the program, which in this case would conventionally be "env" or "/usr/bin/env". Because you skip the "env", env is interpreting "python3" as its own name, as evidenced by the error message. That message comes from env, not from Python.