xref: /aosp_15_r20/system/extras/runconuid/runconuid.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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