Basically, you will depend on the expert(s) assessing the program security having a deep knowledge of what the programs that are allowed elevated rights can do.
The above sentence includes knowledge of the libraries used by the program and the system you are using (Linux? *BSD?).
Going down to a full source-code audit may not be needed. For a well-documented program, a full read of the documentation may be enough to find unsecure options.
It will also depend on the time and expertise you have available.
As is often the case, proving that a program is unsafe will be generally simple to show (once found) but proving that it is safe would be very hard.
The main problematic points would be:
- Features unintended for your use case. From an option to view in shell to a config file that now allows embedded LUA.
- Vulnerabilities in the program. A buffer overflow in an interactive program is generally not critical, but when run as root it becomes a path to privilege elevation.
- Usage of many or too-complex libraries. E.g. a graphical program. You would want to reject these right away.
You will want to allow those users to run with sudo programs as simple and 'closed' as possible. If the program allows multiple options, provide them so they are fixed, if there is a configuration file, make it use one not configurable by the user, etc.
I would recommend allowing a limited shell script or short program instead of the full-fledged program.
For example, for the stated problem of ‘untrusted user needs to read absolutely any file with less’ (or emacs, vi…) I might use:
/**
* This program when run by sudo allows reading as root an arbitrary file via less(1)
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int sudo_var(const char* varname) {
char *value, *endptr;
int number;
value = getenv(varname);
if (!value) {
fprintf(stderr, "Environment doesn't contain %s!\n", varname);
exit(5);
}
errno = 0;
number = strtol(value, &endptr, 10);
if (*endptr != '\0') {
fprintf(stderr, "Bad environment variable %s!\n", varname);
exit(5);
}
if (errno || number <= 0) {
fprintf(stderr, "Bad value for environment variable %s!\n", varname);
exit(5);
}
return number;
}
int main(int argc, char **argv, char **envp) {
gid_t gid;
uid_t uid;
int fd, err;
struct stat stat_variable;
char *less_argv[] = {"/usr/bin/less", NULL};
if (argc < 2) {
fprintf(stderr, "Provide the file to read\n");
return 1;
}
fd = open(argv[1], O_RDONLY | O_NOCTTY);
if (fd == -1) {
perror("Error opening file");
return 2;
}
err = fstat(fd, &stat_variable);
if (err) {
perror("stat error");
return 3;
}
if (!S_ISREG(stat_variable.st_mode)) {
fprintf(stderr, "Not a regular file\n");
return 3;
}
err = dup2(fd, STDIN_FILENO);
if (err) {
perror("dup2 failed");
return 4;
}
gid = sudo_var("SUDO_GID");
uid = sudo_var("SUDO_UID");
err = setgid(gid);
if (err) {
perror("Error setting group id");
return 6;
}
err = setuid(uid);
if (err) {
perror("Error setting user id");
return 7;
}
execve(less_argv[0], less_argv, envp);
perror("exec failed");
return 8;
}
cat
being safe.sudo cat /etc/shadow