123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- /*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- const char* optstr = "<1u:g:G:c:s";
- const char* usage =
- R"(usage: runconuid [-s] [-u UID] [-g GID] [-G GROUPS] [-c CONTEXT] COMMAND ARGS
- Run a command in the specified security context, as the specified user,
- with the specified group membership.
- -c SELinux context
- -g Group ID by name or numeric value
- -G List of groups by name or numeric value
- -s Set enforcing mode
- -u User ID by name or numeric value
- )";
- #include <assert.h>
- #include <errno.h>
- #include <grp.h>
- #include <pwd.h>
- #include <selinux/selinux.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/capability.h>
- #include <sys/prctl.h>
- #include <sys/ptrace.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- static uid_t uid = -1;
- static gid_t gid = -1;
- static gid_t* groups = nullptr;
- static size_t ngroups = 0;
- static char* context = nullptr;
- static bool setenforce = false;
- static char** child_argv = nullptr;
- [[noreturn]] void perror_exit(const char* message) {
- perror(message);
- exit(1);
- }
- void do_child(void) {
- if (context && setexeccon(context) < 0) {
- perror_exit("Setting context to failed");
- }
- // Disregard ambient capability failures, we may just be on a kernel
- // that does not support them.
- for (int i = 0; i < 64; ++i) {
- prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0);
- }
- if (ngroups && setgroups(ngroups, groups) < 0) {
- perror_exit("Setting supplementary groups failed.");
- }
- if (gid != (gid_t) -1 && setresgid(gid, gid, gid) < 0) {
- perror_exit("Setting group failed.");
- }
- if (uid != (uid_t) -1 && setresuid(uid, uid, uid) < 0) {
- perror_exit("Setting user failed.");
- }
- ptrace(PTRACE_TRACEME, 0, 0, 0);
- raise(SIGSTOP);
- execvp(child_argv[0], child_argv);
- perror_exit("Failed to execve");
- }
- uid_t lookup_uid(char* c) {
- struct passwd* pw;
- uid_t u;
- if (sscanf(c, "%d", &u) == 1) {
- return u;
- }
- if ((pw = getpwnam(c)) != 0) {
- return pw->pw_uid;
- }
- perror_exit("Could not resolve user ID by name");
- }
- gid_t lookup_gid(char* c) {
- struct group* gr;
- gid_t g;
- if (sscanf(c, "%d", &g) == 1) {
- return g;
- }
- if ((gr = getgrnam(c)) != 0) {
- return gr->gr_gid;
- }
- perror_exit("Could not resolve group ID by name");
- }
- void lookup_groups(char* c) {
- char* group;
- // Count the number of groups
- for (group = c; *group; group++) {
- if (*group == ',') {
- ngroups++;
- *group = '\0';
- }
- }
- // The last group is not followed by a comma.
- ngroups++;
- // Allocate enough space for all of them
- groups = (gid_t*)calloc(ngroups, sizeof(gid_t));
- group = c;
- // Fill in the group IDs
- for (size_t n = 0; n < ngroups; n++) {
- groups[n] = lookup_gid(group);
- group += strlen(group) + 1;
- }
- }
- void parse_arguments(int argc, char** argv) {
- int c;
- while ((c = getopt(argc, argv, optstr)) != -1) {
- switch (c) {
- case 'u':
- uid = lookup_uid(optarg);
- break;
- case 'g':
- gid = lookup_gid(optarg);
- break;
- case 'G':
- lookup_groups(optarg);
- break;
- case 's':
- setenforce = true;
- break;
- case 'c':
- context = optarg;
- break;
- default:
- perror_exit(usage);
- break;
- }
- }
- child_argv = &argv[optind];
- if (optind == argc) {
- perror_exit(usage);
- }
- }
- int main(int argc, char** argv) {
- pid_t child;
- parse_arguments(argc, argv);
- child = fork();
- if (child < 0) {
- perror_exit("Could not fork.");
- }
- if (setenforce && is_selinux_enabled()) {
- if (security_setenforce(0) < 0) {
- perror("Couldn't set enforcing status to 0");
- }
- }
- if (child == 0) {
- do_child();
- }
- if (ptrace(PTRACE_ATTACH, child, 0, 0) < 0) {
- int err = errno;
- kill(SIGKILL, child);
- errno = err;
- perror_exit("Could not ptrace child.");
- }
- // Wait for the SIGSTOP
- int status = 0;
- if (-1 == wait(&status)) {
- perror_exit("Could not wait for child SIGSTOP");
- }
- // Trace all syscalls.
- ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
- while (1) {
- ptrace(PTRACE_SYSCALL, child, 0, 0);
- waitpid(child, &status, 0);
- // Child raises SIGINT after the execve, on the first instruction.
- if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
- break;
- }
- // Child did some other syscall.
- if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80) {
- continue;
- }
- // Child exited.
- if (WIFEXITED(status)) {
- exit(WEXITSTATUS(status));
- }
- }
- if (setenforce && is_selinux_enabled()) {
- if (security_setenforce(1) < 0) {
- perror("Couldn't set enforcing status to 1");
- }
- }
- ptrace(PTRACE_DETACH, child, 0, 0);
- return 0;
- }
|