1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
/*
* $Id: sucap.c,v 1.1.1.1 1999/04/17 22:16:31 morgan Exp $
*
* This was written by Finn Arne Gangstad <finnag@guardian.no>
*
* This is a program that is intended to exec a subsequent program.
* The purpose of this 'sucap' wrapper is to change uid but keep all
* privileges. All environment variables are inherited.
*/
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#undef _POSIX_SOURCE
#include <sys/capability.h>
#include <pwd.h>
#define __USE_BSD
#include <grp.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
static void usage(void)
{
fprintf(stderr,
"usage: sucap <user> <group> <command-path> [command-args...]\n\n"
" This program is a wrapper that change UID but not privileges of a\n"
" program to be executed.\n"
" Note, this wrapper is intended to assist in overcoming a lack of support\n"
" for filesystem capability attributes and should be used to launch other\n"
" files. This program should _NOT_ be made setuid-0.\n\n"
"[Copyright (c) 1998 Finn Arne Gangstad <finnag@guardian.no>]\n");
exit(1);
}
static void
wait_on_fd(int fd)
{
/* Wait until some data is available on a file descriptor, or until
* end of file or an error is detected */
char buf[1];
while (read(fd, buf, sizeof(buf)) == -1 && errno == EINTR) {
/* empty loop */
}
}
int main(int argc, char **argv)
{
cap_t old_caps;
uid_t uid;
pid_t pid, parent_pid;
gid_t gid;
int pipe_fds[2];
/* this program should not be made setuid-0 */
if (getuid() && !geteuid()) {
usage();
}
/* check that we have at least 3 arguments */
if (argc < 4) {
usage();
}
/* Convert username to uid */
{
struct passwd *pw = getpwnam(argv[1]);
if (!pw) {
fprintf(stderr, "sucap: No such user: %s\n", argv[1]);
exit(1);
}
uid = pw->pw_uid;
}
/* Convert groupname to gid */
{
struct group *gr = getgrnam(argv[2]);
if (!gr) {
fprintf(stderr, "sucap: No such group: %s\n", argv[2]);
exit(1);
}
gid = gr->gr_gid;
}
/* set process group to current pid */
if (setpgid(0, getpid())) {
perror("sucap: Failed to set process group");
exit(1);
}
if (pipe(pipe_fds)) {
perror("sucap: pipe() failed");
exit(1);
}
parent_pid = getpid();
old_caps = cap_init();
if (capgetp(0, old_caps)) {
perror("sucap: capgetp");
exit(1);
}
{
ssize_t x;
printf("Caps: %s\n", cap_to_text(old_caps, &x));
}
/* fork off a child to do the hard work */
fflush(NULL);
pid = fork();
if (pid == -1) {
perror("sucap: fork failed");
exit(1);
}
/* 1. mother process sets gid and uid
* 2. child process sets capabilities of mother process
* 3. mother process execs whatever is to be executed
*/
if (pid) {
/* Mother process. */
close(pipe_fds[0]);
/* Get rid of any supplemental groups */
if (!getuid() && setgroups(0, 0)) {
perror("sucap: setgroups failed");
exit(1);
}
/* Set gid and uid (this probably clears capabilities) */
setregid(gid, gid);
setreuid(uid, uid);
{
ssize_t x;
cap_t cap = cap_init();
capgetp(0, cap);
printf("Caps: %s\n", cap_to_text(cap, &x));
}
printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid());
/* Signal child that we want our privileges updated */
close(pipe_fds[1]); /* Child hangs in blocking read */
/* Wait for child process to set our privileges */
{
int status = 0;
if (wait(&status) == -1) {
perror("sucap: wait failed");
}
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
fprintf(stderr, "sucap: child did not exit cleanly.\n");
exit(1);
}
}
{
ssize_t x;
cap_t cap = cap_init();
capgetp(0, cap);
printf("Caps: %s\n", cap_to_text(cap, &x));
}
/* printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); */
/* exec the program indicated by args 2 ... */
execvp(argv[3], argv+3);
/* if we fall through to here, our exec failed -- announce the fact */
fprintf(stderr, "Unable to execute command: %s\n", strerror(errno));
usage();
} else {
/* Child process */
close(pipe_fds[1]);
/* Wait for mother process to setuid */
wait_on_fd(pipe_fds[0]);
/* Set privileges on mother process */
if (capsetp(parent_pid, old_caps)) {
perror("sucaps: capsetp");
_exit(1);
}
/* exit to signal mother process that we are ready */
_exit(0);
}
return 0;
}
|