runconuid.cpp 5.2 KB

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