chroot.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. chroot.c
  5. Abstract:
  6. This module implements the chroot utility, which runs a command line
  7. jailed to a specific region of the file system.
  8. Author:
  9. Evan Green 22-Oct-2014
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <getopt.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define CHROOT_VERSION_MAJOR 1
  28. #define CHROOT_VERSION_MINOR 0
  29. #define CHROOT_USAGE \
  30. "usage: chroot [options] new_root [command [arguments]]\n" \
  31. " chroot options\n" \
  32. "The chroot utility runs the given command or interactive shell jailed \n" \
  33. "to a specific region of the file system. If no command is given, \n" \
  34. "${SHELL} -i is run (with a default of /bin/sh). Options are\n" \
  35. " -e, --escape -- Attempt to escape the current root.\n" \
  36. " -u, --userspec=user:group -- Specifies the user and group (ID or \n" \
  37. " name) to change to before executing the command.\n" \
  38. " -G, --groups=groups -- Specifies the supplementary groups the \n" \
  39. " process will become a member of before executing the command.\n" \
  40. " --help -- Show this help text and exit.\n" \
  41. " --version -- Print the application version information and exit.\n"
  42. #define CHROOT_OPTIONS_STRING "eu:G:h"
  43. #define CHROOT_SHELL_VARIABLE "SHELL"
  44. #define CHROOT_DEFAULT_SHELL "/bin/sh"
  45. //
  46. // Set this option to attempt to escape the current chroot.
  47. //
  48. #define CHROOT_OPTION_ESCAPE 0x00000001
  49. //
  50. // ------------------------------------------------------ Data Type Definitions
  51. //
  52. //
  53. // ----------------------------------------------- Internal Function Prototypes
  54. //
  55. //
  56. // -------------------------------------------------------------------- Globals
  57. //
  58. struct option ChrootLongOptions[] = {
  59. {"escape", no_argument, 0, 'e'},
  60. {"userspec", required_argument, 0, 'u'},
  61. {"groups", required_argument, 0, 'g'},
  62. {"help", no_argument, 0, 'h'},
  63. {"version", no_argument, 0, 'V'},
  64. {NULL, 0, 0, 0},
  65. };
  66. //
  67. // ------------------------------------------------------------------ Functions
  68. //
  69. INT
  70. ChrootMain (
  71. INT ArgumentCount,
  72. CHAR **Arguments
  73. )
  74. /*++
  75. Routine Description:
  76. This routine is the main entry point for the chroot utility, which changes
  77. the root directory and runs a command.
  78. Arguments:
  79. ArgumentCount - Supplies the number of command line arguments the program
  80. was invoked with.
  81. Arguments - Supplies a tokenized array of command line arguments.
  82. Return Value:
  83. Returns an integer exit code. 0 for success, nonzero otherwise.
  84. --*/
  85. {
  86. PSTR Argument;
  87. ULONG ArgumentIndex;
  88. id_t Group;
  89. size_t GroupCount;
  90. gid_t *Groups;
  91. gid_t NewGroup;
  92. uid_t NewUser;
  93. INT Option;
  94. ULONG Options;
  95. PSTR ShellArguments[3];
  96. int Status;
  97. uid_t User;
  98. Groups = NULL;
  99. GroupCount = 0;
  100. Options = 0;
  101. User = SwGetRealUserId();
  102. Group = SwGetRealGroupId();
  103. NewUser = User;
  104. NewGroup = Group;
  105. //
  106. // Process the control arguments.
  107. //
  108. while (TRUE) {
  109. Option = getopt_long(ArgumentCount,
  110. Arguments,
  111. CHROOT_OPTIONS_STRING,
  112. ChrootLongOptions,
  113. NULL);
  114. if (Option == -1) {
  115. break;
  116. }
  117. if ((Option == '?') || (Option == ':')) {
  118. Status = 1;
  119. goto MainEnd;
  120. }
  121. switch (Option) {
  122. case 'e':
  123. Options |= CHROOT_OPTION_ESCAPE;
  124. break;
  125. case 'u':
  126. Status = SwParseUserAndGroupString(optarg, &NewUser, &NewGroup);
  127. if (Status != 0) {
  128. if (Status == EINVAL) {
  129. SwPrintError(0, optarg, "Invalid user/group string");
  130. } else {
  131. SwPrintError(Status, optarg, "Invalid user/group string");
  132. }
  133. goto MainEnd;
  134. }
  135. if (NewUser == (uid_t)-1) {
  136. NewUser = User;
  137. }
  138. if (NewGroup == (gid_t)-1) {
  139. NewGroup = Group;
  140. }
  141. break;
  142. case 'g':
  143. Status = SwParseGroupList(optarg, &Groups, &GroupCount);
  144. if (Status != 0) {
  145. SwPrintError(Status, optarg, "Invalid group list");
  146. goto MainEnd;
  147. }
  148. break;
  149. case 'V':
  150. SwPrintVersion(CHROOT_VERSION_MAJOR, CHROOT_VERSION_MINOR);
  151. return 1;
  152. case 'h':
  153. printf(CHROOT_USAGE);
  154. return 1;
  155. default:
  156. assert(FALSE);
  157. Status = 1;
  158. goto MainEnd;
  159. }
  160. }
  161. ArgumentIndex = optind;
  162. if (ArgumentIndex > ArgumentCount) {
  163. ArgumentIndex = ArgumentCount;
  164. }
  165. //
  166. // If the user wants to escape, try to leave the root by passing in NULL.
  167. //
  168. if ((Options & CHROOT_OPTION_ESCAPE) != 0) {
  169. Status = SwChroot(NULL);
  170. if (Status != 0) {
  171. SwPrintError(Status, NULL, "Failed to change root");
  172. goto MainEnd;
  173. }
  174. //
  175. // Change the root. Change the current directory too.
  176. //
  177. } else {
  178. if (ArgumentIndex == ArgumentCount) {
  179. SwPrintError(0, NULL, "New root directory expected");
  180. Status = EINVAL;
  181. goto MainEnd;
  182. }
  183. Argument = Arguments[ArgumentIndex];
  184. ArgumentIndex += 1;
  185. Status = chdir(Argument);
  186. if (Status != 0) {
  187. Status = errno;
  188. SwPrintError(Status, Argument, "Failed to change directory");
  189. goto MainEnd;
  190. }
  191. Status = SwChroot(".");
  192. if (Status != 0) {
  193. SwPrintError(Status, Argument, "Failed to change root");
  194. goto MainEnd;
  195. }
  196. }
  197. //
  198. // Change the user, group, and groups if needed.
  199. //
  200. if (Groups != NULL) {
  201. Status = SwSetGroups(GroupCount, Groups);
  202. if (Status != 0) {
  203. SwPrintError(Status, NULL, "Failed to set supplementary groups");
  204. goto MainEnd;
  205. }
  206. }
  207. if (NewGroup != Group) {
  208. Status = SwSetRealGroupId(NewGroup);
  209. if (Status != 0) {
  210. SwPrintError(Status, NULL, "Failed to set group ID");
  211. goto MainEnd;
  212. }
  213. }
  214. if (NewUser != User) {
  215. Status = SwSetRealUserId(NewUser);
  216. if (Status != 0) {
  217. SwPrintError(Status, NULL, "Failed to set user ID");
  218. goto MainEnd;
  219. }
  220. }
  221. if (ArgumentIndex == ArgumentCount) {
  222. ShellArguments[0] = getenv("SHELL");
  223. if (ShellArguments[0] == NULL) {
  224. ShellArguments[0] = CHROOT_DEFAULT_SHELL;
  225. }
  226. ShellArguments[1] = "-i";
  227. ShellArguments[2] = NULL;
  228. Argument = ShellArguments[0];
  229. Status = SwExec(ShellArguments[0], ShellArguments, 2);
  230. } else {
  231. Argument = Arguments[ArgumentIndex];
  232. Status = SwExec(Arguments[ArgumentIndex],
  233. &(Arguments[ArgumentIndex]),
  234. ArgumentCount - ArgumentIndex);
  235. }
  236. SwPrintError(Status, Argument, "Failed to execute");
  237. MainEnd:
  238. if (Groups != NULL) {
  239. free(Groups);
  240. }
  241. return Status;
  242. }
  243. //
  244. // --------------------------------------------------------- Internal Functions
  245. //