1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2016 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker const char* optstr = "<1u:g:G:c:s";
17*288bf522SAndroid Build Coastguard Worker const char* usage =
18*288bf522SAndroid Build Coastguard Worker R"(usage: runconuid [-s] [-u UID] [-g GID] [-G GROUPS] [-c CONTEXT] COMMAND ARGS
19*288bf522SAndroid Build Coastguard Worker
20*288bf522SAndroid Build Coastguard Worker Run a command in the specified security context, as the specified user,
21*288bf522SAndroid Build Coastguard Worker with the specified group membership.
22*288bf522SAndroid Build Coastguard Worker
23*288bf522SAndroid Build Coastguard Worker -c SELinux context
24*288bf522SAndroid Build Coastguard Worker -g Group ID by name or numeric value
25*288bf522SAndroid Build Coastguard Worker -G List of groups by name or numeric value
26*288bf522SAndroid Build Coastguard Worker -s Set enforcing mode
27*288bf522SAndroid Build Coastguard Worker -u User ID by name or numeric value
28*288bf522SAndroid Build Coastguard Worker )";
29*288bf522SAndroid Build Coastguard Worker
30*288bf522SAndroid Build Coastguard Worker #include <assert.h>
31*288bf522SAndroid Build Coastguard Worker #include <errno.h>
32*288bf522SAndroid Build Coastguard Worker #include <grp.h>
33*288bf522SAndroid Build Coastguard Worker #include <pwd.h>
34*288bf522SAndroid Build Coastguard Worker #include <selinux/selinux.h>
35*288bf522SAndroid Build Coastguard Worker #include <signal.h>
36*288bf522SAndroid Build Coastguard Worker #include <stdio.h>
37*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
38*288bf522SAndroid Build Coastguard Worker #include <string.h>
39*288bf522SAndroid Build Coastguard Worker #include <sys/capability.h>
40*288bf522SAndroid Build Coastguard Worker #include <sys/prctl.h>
41*288bf522SAndroid Build Coastguard Worker #include <sys/ptrace.h>
42*288bf522SAndroid Build Coastguard Worker #include <sys/types.h>
43*288bf522SAndroid Build Coastguard Worker #include <sys/wait.h>
44*288bf522SAndroid Build Coastguard Worker #include <unistd.h>
45*288bf522SAndroid Build Coastguard Worker
46*288bf522SAndroid Build Coastguard Worker static uid_t uid = -1;
47*288bf522SAndroid Build Coastguard Worker static gid_t gid = -1;
48*288bf522SAndroid Build Coastguard Worker static gid_t* groups = nullptr;
49*288bf522SAndroid Build Coastguard Worker static size_t ngroups = 0;
50*288bf522SAndroid Build Coastguard Worker static char* context = nullptr;
51*288bf522SAndroid Build Coastguard Worker static bool setenforce = false;
52*288bf522SAndroid Build Coastguard Worker static char** child_argv = nullptr;
53*288bf522SAndroid Build Coastguard Worker
perror_exit(const char * message)54*288bf522SAndroid Build Coastguard Worker [[noreturn]] void perror_exit(const char* message) {
55*288bf522SAndroid Build Coastguard Worker perror(message);
56*288bf522SAndroid Build Coastguard Worker exit(1);
57*288bf522SAndroid Build Coastguard Worker }
58*288bf522SAndroid Build Coastguard Worker
do_child(void)59*288bf522SAndroid Build Coastguard Worker void do_child(void) {
60*288bf522SAndroid Build Coastguard Worker
61*288bf522SAndroid Build Coastguard Worker if (context && setexeccon(context) < 0) {
62*288bf522SAndroid Build Coastguard Worker perror_exit("Setting context to failed");
63*288bf522SAndroid Build Coastguard Worker }
64*288bf522SAndroid Build Coastguard Worker
65*288bf522SAndroid Build Coastguard Worker // Disregard ambient capability failures, we may just be on a kernel
66*288bf522SAndroid Build Coastguard Worker // that does not support them.
67*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < 64; ++i) {
68*288bf522SAndroid Build Coastguard Worker prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0);
69*288bf522SAndroid Build Coastguard Worker }
70*288bf522SAndroid Build Coastguard Worker
71*288bf522SAndroid Build Coastguard Worker if (ngroups && setgroups(ngroups, groups) < 0) {
72*288bf522SAndroid Build Coastguard Worker perror_exit("Setting supplementary groups failed.");
73*288bf522SAndroid Build Coastguard Worker }
74*288bf522SAndroid Build Coastguard Worker
75*288bf522SAndroid Build Coastguard Worker if (gid != (gid_t) -1 && setresgid(gid, gid, gid) < 0) {
76*288bf522SAndroid Build Coastguard Worker perror_exit("Setting group failed.");
77*288bf522SAndroid Build Coastguard Worker }
78*288bf522SAndroid Build Coastguard Worker
79*288bf522SAndroid Build Coastguard Worker if (uid != (uid_t) -1 && setresuid(uid, uid, uid) < 0) {
80*288bf522SAndroid Build Coastguard Worker perror_exit("Setting user failed.");
81*288bf522SAndroid Build Coastguard Worker }
82*288bf522SAndroid Build Coastguard Worker
83*288bf522SAndroid Build Coastguard Worker ptrace(PTRACE_TRACEME, 0, 0, 0);
84*288bf522SAndroid Build Coastguard Worker raise(SIGSTOP);
85*288bf522SAndroid Build Coastguard Worker execvp(child_argv[0], child_argv);
86*288bf522SAndroid Build Coastguard Worker perror_exit("Failed to execve");
87*288bf522SAndroid Build Coastguard Worker }
88*288bf522SAndroid Build Coastguard Worker
lookup_uid(char * c)89*288bf522SAndroid Build Coastguard Worker uid_t lookup_uid(char* c) {
90*288bf522SAndroid Build Coastguard Worker struct passwd* pw;
91*288bf522SAndroid Build Coastguard Worker uid_t u;
92*288bf522SAndroid Build Coastguard Worker
93*288bf522SAndroid Build Coastguard Worker if (sscanf(c, "%d", &u) == 1) {
94*288bf522SAndroid Build Coastguard Worker return u;
95*288bf522SAndroid Build Coastguard Worker }
96*288bf522SAndroid Build Coastguard Worker
97*288bf522SAndroid Build Coastguard Worker if ((pw = getpwnam(c)) != 0) {
98*288bf522SAndroid Build Coastguard Worker return pw->pw_uid;
99*288bf522SAndroid Build Coastguard Worker }
100*288bf522SAndroid Build Coastguard Worker
101*288bf522SAndroid Build Coastguard Worker perror_exit("Could not resolve user ID by name");
102*288bf522SAndroid Build Coastguard Worker }
103*288bf522SAndroid Build Coastguard Worker
lookup_gid(char * c)104*288bf522SAndroid Build Coastguard Worker gid_t lookup_gid(char* c) {
105*288bf522SAndroid Build Coastguard Worker struct group* gr;
106*288bf522SAndroid Build Coastguard Worker gid_t g;
107*288bf522SAndroid Build Coastguard Worker
108*288bf522SAndroid Build Coastguard Worker if (sscanf(c, "%d", &g) == 1) {
109*288bf522SAndroid Build Coastguard Worker return g;
110*288bf522SAndroid Build Coastguard Worker }
111*288bf522SAndroid Build Coastguard Worker
112*288bf522SAndroid Build Coastguard Worker if ((gr = getgrnam(c)) != 0) {
113*288bf522SAndroid Build Coastguard Worker return gr->gr_gid;
114*288bf522SAndroid Build Coastguard Worker }
115*288bf522SAndroid Build Coastguard Worker
116*288bf522SAndroid Build Coastguard Worker perror_exit("Could not resolve group ID by name");
117*288bf522SAndroid Build Coastguard Worker }
118*288bf522SAndroid Build Coastguard Worker
lookup_groups(char * c)119*288bf522SAndroid Build Coastguard Worker void lookup_groups(char* c) {
120*288bf522SAndroid Build Coastguard Worker char* group;
121*288bf522SAndroid Build Coastguard Worker
122*288bf522SAndroid Build Coastguard Worker // Count the number of groups
123*288bf522SAndroid Build Coastguard Worker for (group = c; *group; group++) {
124*288bf522SAndroid Build Coastguard Worker if (*group == ',') {
125*288bf522SAndroid Build Coastguard Worker ngroups++;
126*288bf522SAndroid Build Coastguard Worker *group = '\0';
127*288bf522SAndroid Build Coastguard Worker }
128*288bf522SAndroid Build Coastguard Worker }
129*288bf522SAndroid Build Coastguard Worker
130*288bf522SAndroid Build Coastguard Worker // The last group is not followed by a comma.
131*288bf522SAndroid Build Coastguard Worker ngroups++;
132*288bf522SAndroid Build Coastguard Worker
133*288bf522SAndroid Build Coastguard Worker // Allocate enough space for all of them
134*288bf522SAndroid Build Coastguard Worker groups = (gid_t*)calloc(ngroups, sizeof(gid_t));
135*288bf522SAndroid Build Coastguard Worker group = c;
136*288bf522SAndroid Build Coastguard Worker
137*288bf522SAndroid Build Coastguard Worker // Fill in the group IDs
138*288bf522SAndroid Build Coastguard Worker for (size_t n = 0; n < ngroups; n++) {
139*288bf522SAndroid Build Coastguard Worker groups[n] = lookup_gid(group);
140*288bf522SAndroid Build Coastguard Worker group += strlen(group) + 1;
141*288bf522SAndroid Build Coastguard Worker }
142*288bf522SAndroid Build Coastguard Worker }
143*288bf522SAndroid Build Coastguard Worker
parse_arguments(int argc,char ** argv)144*288bf522SAndroid Build Coastguard Worker void parse_arguments(int argc, char** argv) {
145*288bf522SAndroid Build Coastguard Worker int c;
146*288bf522SAndroid Build Coastguard Worker
147*288bf522SAndroid Build Coastguard Worker while ((c = getopt(argc, argv, optstr)) != -1) {
148*288bf522SAndroid Build Coastguard Worker switch (c) {
149*288bf522SAndroid Build Coastguard Worker case 'u':
150*288bf522SAndroid Build Coastguard Worker uid = lookup_uid(optarg);
151*288bf522SAndroid Build Coastguard Worker break;
152*288bf522SAndroid Build Coastguard Worker case 'g':
153*288bf522SAndroid Build Coastguard Worker gid = lookup_gid(optarg);
154*288bf522SAndroid Build Coastguard Worker break;
155*288bf522SAndroid Build Coastguard Worker case 'G':
156*288bf522SAndroid Build Coastguard Worker lookup_groups(optarg);
157*288bf522SAndroid Build Coastguard Worker break;
158*288bf522SAndroid Build Coastguard Worker case 's':
159*288bf522SAndroid Build Coastguard Worker setenforce = true;
160*288bf522SAndroid Build Coastguard Worker break;
161*288bf522SAndroid Build Coastguard Worker case 'c':
162*288bf522SAndroid Build Coastguard Worker context = optarg;
163*288bf522SAndroid Build Coastguard Worker break;
164*288bf522SAndroid Build Coastguard Worker default:
165*288bf522SAndroid Build Coastguard Worker perror_exit(usage);
166*288bf522SAndroid Build Coastguard Worker break;
167*288bf522SAndroid Build Coastguard Worker }
168*288bf522SAndroid Build Coastguard Worker }
169*288bf522SAndroid Build Coastguard Worker
170*288bf522SAndroid Build Coastguard Worker child_argv = &argv[optind];
171*288bf522SAndroid Build Coastguard Worker
172*288bf522SAndroid Build Coastguard Worker if (optind == argc) {
173*288bf522SAndroid Build Coastguard Worker perror_exit(usage);
174*288bf522SAndroid Build Coastguard Worker }
175*288bf522SAndroid Build Coastguard Worker }
176*288bf522SAndroid Build Coastguard Worker
main(int argc,char ** argv)177*288bf522SAndroid Build Coastguard Worker int main(int argc, char** argv) {
178*288bf522SAndroid Build Coastguard Worker pid_t child;
179*288bf522SAndroid Build Coastguard Worker
180*288bf522SAndroid Build Coastguard Worker parse_arguments(argc, argv);
181*288bf522SAndroid Build Coastguard Worker child = fork();
182*288bf522SAndroid Build Coastguard Worker
183*288bf522SAndroid Build Coastguard Worker if (child < 0) {
184*288bf522SAndroid Build Coastguard Worker perror_exit("Could not fork.");
185*288bf522SAndroid Build Coastguard Worker }
186*288bf522SAndroid Build Coastguard Worker
187*288bf522SAndroid Build Coastguard Worker if (setenforce && is_selinux_enabled()) {
188*288bf522SAndroid Build Coastguard Worker if (security_setenforce(0) < 0) {
189*288bf522SAndroid Build Coastguard Worker perror("Couldn't set enforcing status to 0");
190*288bf522SAndroid Build Coastguard Worker }
191*288bf522SAndroid Build Coastguard Worker }
192*288bf522SAndroid Build Coastguard Worker
193*288bf522SAndroid Build Coastguard Worker if (child == 0) {
194*288bf522SAndroid Build Coastguard Worker do_child();
195*288bf522SAndroid Build Coastguard Worker }
196*288bf522SAndroid Build Coastguard Worker
197*288bf522SAndroid Build Coastguard Worker if (ptrace(PTRACE_ATTACH, child, 0, 0) < 0) {
198*288bf522SAndroid Build Coastguard Worker int err = errno;
199*288bf522SAndroid Build Coastguard Worker kill(SIGKILL, child);
200*288bf522SAndroid Build Coastguard Worker errno = err;
201*288bf522SAndroid Build Coastguard Worker perror_exit("Could not ptrace child.");
202*288bf522SAndroid Build Coastguard Worker }
203*288bf522SAndroid Build Coastguard Worker
204*288bf522SAndroid Build Coastguard Worker // Wait for the SIGSTOP
205*288bf522SAndroid Build Coastguard Worker int status = 0;
206*288bf522SAndroid Build Coastguard Worker if (-1 == wait(&status)) {
207*288bf522SAndroid Build Coastguard Worker perror_exit("Could not wait for child SIGSTOP");
208*288bf522SAndroid Build Coastguard Worker }
209*288bf522SAndroid Build Coastguard Worker
210*288bf522SAndroid Build Coastguard Worker // Trace all syscalls.
211*288bf522SAndroid Build Coastguard Worker ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
212*288bf522SAndroid Build Coastguard Worker
213*288bf522SAndroid Build Coastguard Worker while (1) {
214*288bf522SAndroid Build Coastguard Worker ptrace(PTRACE_SYSCALL, child, 0, 0);
215*288bf522SAndroid Build Coastguard Worker waitpid(child, &status, 0);
216*288bf522SAndroid Build Coastguard Worker
217*288bf522SAndroid Build Coastguard Worker // Child raises SIGINT after the execve, on the first instruction.
218*288bf522SAndroid Build Coastguard Worker if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
219*288bf522SAndroid Build Coastguard Worker break;
220*288bf522SAndroid Build Coastguard Worker }
221*288bf522SAndroid Build Coastguard Worker
222*288bf522SAndroid Build Coastguard Worker // Child did some other syscall.
223*288bf522SAndroid Build Coastguard Worker if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80) {
224*288bf522SAndroid Build Coastguard Worker continue;
225*288bf522SAndroid Build Coastguard Worker }
226*288bf522SAndroid Build Coastguard Worker
227*288bf522SAndroid Build Coastguard Worker // Child exited.
228*288bf522SAndroid Build Coastguard Worker if (WIFEXITED(status)) {
229*288bf522SAndroid Build Coastguard Worker exit(WEXITSTATUS(status));
230*288bf522SAndroid Build Coastguard Worker }
231*288bf522SAndroid Build Coastguard Worker }
232*288bf522SAndroid Build Coastguard Worker
233*288bf522SAndroid Build Coastguard Worker if (setenforce && is_selinux_enabled()) {
234*288bf522SAndroid Build Coastguard Worker if (security_setenforce(1) < 0) {
235*288bf522SAndroid Build Coastguard Worker perror("Couldn't set enforcing status to 1");
236*288bf522SAndroid Build Coastguard Worker }
237*288bf522SAndroid Build Coastguard Worker }
238*288bf522SAndroid Build Coastguard Worker
239*288bf522SAndroid Build Coastguard Worker ptrace(PTRACE_DETACH, child, 0, 0);
240*288bf522SAndroid Build Coastguard Worker return 0;
241*288bf522SAndroid Build Coastguard Worker }
242