This is example C source code for a setuid wrapper program. This can be useful if you have a script that you want unprivileged users to be able to run with root permissions. All the usual security disclaimers apply.
/*
* setuid-wrapper.c
* by Nathan Rosenquist
* https://nathanrosenquist.com/setuid-wrapper/
*
* This is a setuid program that simply becomes root and calls the
* given script / executable. You will need to adapt it to your
* particular situation. In particular, you will probably want to change
* the following #define statements:
*
* PROGRAM_NAME this should match whatever you rename this program to
* (e.g. my-program)
*
* CMD_PATH this should point to the full path of your script
* (e.g. /usr/local/bin/my-program.py)
*
* Running programs setuid introduces the possibility of a root compromise.
* Please only use this program if you already know what you are doing, and
* have a concrete plan to keep your system safe. Please make sure you
* understand all of the concepts involved with this program and the
* possible failure modes before using this in a production environment.
* You have been warned!
*
* To compile:
*
* cc -Wall -Werror --pedantic --ansi --static setuid-wrapper.c -o my-program
*
* To install:
*
* sudo cp my-program /usr/local/bin/my-program
* sudo chown root:my-allowed-group /usr/local/bin/my-program
* sudo chmod 4750 /usr/local/bin/my-program
*
* This code is placed in the public domain.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
/* the name of this wrapper program */
#define PROGRAM_NAME "setuid-wrapper"
/* the command to execute */
#define CMD_PATH "/usr/bin/id"
/* exit code */
#define EXIT_FAIL 1
/* environment variable pointer */
extern char **environ;
/* function prototypes */
int setenv(const char *name, const char *value, int overwrite);
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
int execve(const char *filename, char *const argv[], char *const envp[]);
void perror(const char *s);
/* main program */
int main(int argc, char *argv[]) {
/* declare variable to capture system call results */
int result;
/* construct the command that we want to execute */
char *cmd[2];
cmd[0] = CMD_PATH;
cmd[1] = NULL;
/* clear all existing environment variables */
environ = NULL;
/* overwrite the PATH environment variable with something sensible */
result = setenv(
"PATH",
"/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
1
);
if (0 != result) {
perror(PROGRAM_NAME);
return EXIT_FAIL;
}
/* set real and effective user id to root */
result = setreuid(0, 0);
if (0 != result) {
perror(PROGRAM_NAME);
return EXIT_FAIL;
}
/* set real and effective group id to root */
result = setregid(0, 0);
if (0 != result) {
perror(PROGRAM_NAME);
return EXIT_FAIL;
}
/* exec command */
result = execve(cmd[0], cmd, NULL);
if (0 != result) {
perror(PROGRAM_NAME);
return EXIT_FAIL;
}
fprintf(stderr, "%s: could not execute command\n", PROGRAM_NAME);
return EXIT_FAIL;
}