su.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. su.c
  5. Abstract:
  6. This module implements the su command, which is used to execute commands
  7. as another user.
  8. Author:
  9. Evan Green 12-Mar-2015
  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 <syslog.h>
  23. #include <unistd.h>
  24. #include "../swlib.h"
  25. #include "lutil.h"
  26. //
  27. // ---------------------------------------------------------------- Definitions
  28. //
  29. #define SU_VERSION_MAJOR 1
  30. #define SU_VERSION_MINOR 0
  31. #define SU_USAGE \
  32. "usage: su [options] [username]\n" \
  33. "The su utility is used to become another user during a login session. \n" \
  34. "With no username specified, su defaults to becoming the superuser. \n" \
  35. "Options are:\n" \
  36. " -c, --command=cmd -- Specify a command that will be invoked by the \n" \
  37. " shell using its -c argument format. The executed program will \n" \
  38. " have no controlling terminal, and so it cannot be used to \n" \
  39. " execute interactive programs.\n" \
  40. " -, -l, --login -- Provide an environment similar to what the user \n" \
  41. " would expect if he or she had logged in directly.\n" \
  42. " -s, --shell=shell -- Specifies the shell to be invoked. If this is \n" \
  43. " not specified, $SHELL will be invoked, or the shell from the \n" \
  44. " user's account, or /bin/sh.\n" \
  45. " -m, --preserve-environment -- Preserve the current environment, \n" \
  46. " except for PATH and IFS.\n" \
  47. " --help -- Displays this help text and exits.\n" \
  48. " --version -- Displays the application version and exits.\n"
  49. #define SU_OPTIONS_STRING "c:ls:mHV"
  50. //
  51. // Define application options.
  52. //
  53. //
  54. // Set this option to act as a login environment.
  55. //
  56. #define SU_OPTION_LOGIN 0x00000001
  57. //
  58. // Set this option to preserve the environment variables.
  59. //
  60. #define SU_OPTION_PRESERVE_ENVIRONMENT 0x00000002
  61. //
  62. // ------------------------------------------------------ Data Type Definitions
  63. //
  64. //
  65. // ----------------------------------------------- Internal Function Prototypes
  66. //
  67. BOOL
  68. SuIsRestrictedShell (
  69. PSTR Shell
  70. );
  71. //
  72. // -------------------------------------------------------------------- Globals
  73. //
  74. struct option SuLongOptions[] = {
  75. {"command", required_argument, 0, 'c'},
  76. {"login", no_argument, 0, 'l'},
  77. {"shell", required_argument, 0, 's'},
  78. {"preserve-environment", no_argument, 0, 'm'},
  79. {"help", no_argument, 0, 'H'},
  80. {"version", no_argument, 0, 'V'},
  81. {NULL, 0, 0, 0},
  82. };
  83. //
  84. // ------------------------------------------------------------------ Functions
  85. //
  86. INT
  87. SuMain (
  88. INT ArgumentCount,
  89. CHAR **Arguments
  90. )
  91. /*++
  92. Routine Description:
  93. This routine is the main entry point for the su utility.
  94. Arguments:
  95. ArgumentCount - Supplies the number of command line arguments the program
  96. was invoked with.
  97. Arguments - Supplies a tokenized array of command line arguments.
  98. Return Value:
  99. Returns an integer exit code. 0 for success, nonzero otherwise.
  100. --*/
  101. {
  102. ULONG ArgumentIndex;
  103. PSTR Command;
  104. uid_t CurrentUserId;
  105. PSTR CurrentUserName;
  106. BOOL Login;
  107. BOOL LogOpen;
  108. INT Option;
  109. ULONG Options;
  110. ULONG SetupFlags;
  111. PSTR Shell;
  112. int Status;
  113. struct passwd *User;
  114. PSTR UserName;
  115. Command = NULL;
  116. CurrentUserName = NULL;
  117. Options = 0;
  118. Shell = NULL;
  119. User = NULL;
  120. UserName = NULL;
  121. openlog("su", 0, LOG_AUTH);
  122. LogOpen = TRUE;
  123. //
  124. // Process the control arguments.
  125. //
  126. while (TRUE) {
  127. Option = getopt_long(ArgumentCount,
  128. Arguments,
  129. SU_OPTIONS_STRING,
  130. SuLongOptions,
  131. NULL);
  132. if (Option == -1) {
  133. break;
  134. }
  135. if ((Option == '?') || (Option == ':')) {
  136. Status = 1;
  137. goto MainEnd;
  138. }
  139. switch (Option) {
  140. case 'c':
  141. Command = optarg;
  142. break;
  143. case 'l':
  144. Options |= SU_OPTION_LOGIN;
  145. break;
  146. case 'm':
  147. Options |= SU_OPTION_PRESERVE_ENVIRONMENT;
  148. break;
  149. case 's':
  150. Shell = optarg;
  151. break;
  152. case 'V':
  153. SwPrintVersion(SU_VERSION_MAJOR, SU_VERSION_MINOR);
  154. return 1;
  155. case 'H':
  156. printf(SU_USAGE);
  157. return 1;
  158. default:
  159. assert(FALSE);
  160. Status = 1;
  161. goto MainEnd;
  162. }
  163. }
  164. ArgumentIndex = optind;
  165. if (ArgumentIndex > ArgumentCount) {
  166. ArgumentIndex = ArgumentCount;
  167. }
  168. if ((ArgumentIndex < ArgumentCount) &&
  169. (strcmp(Arguments[ArgumentIndex], "-") == 0)) {
  170. Options |= SU_OPTION_LOGIN;
  171. ArgumentIndex += 1;
  172. }
  173. //
  174. // Get the current user name.
  175. //
  176. CurrentUserId = getuid();
  177. User = getpwuid(CurrentUserId);
  178. if (User == NULL) {
  179. SwPrintError(0, NULL, "Failed to get current user name");
  180. Status = 1;
  181. goto MainEnd;
  182. }
  183. CurrentUserName = strdup(User->pw_name);
  184. if (CurrentUserName == NULL) {
  185. Status = ENOMEM;
  186. goto MainEnd;
  187. }
  188. //
  189. // Get the user entry to act as, either specified on the command line or
  190. // assumed to be 0 (root).
  191. //
  192. if (ArgumentIndex < ArgumentCount) {
  193. UserName = Arguments[ArgumentIndex];
  194. ArgumentIndex += 1;
  195. User = getpwnam(UserName);
  196. } else {
  197. User = getpwuid(0);
  198. }
  199. if (User == NULL) {
  200. SwPrintError(0, UserName, "Failed to find user");
  201. Status = 1;
  202. goto MainEnd;
  203. }
  204. if ((CurrentUserId == 0) || (SwGetAndCheckPassword(User, NULL) == 0)) {
  205. syslog(LOG_NOTICE,
  206. "%c %s %s:%s",
  207. '+',
  208. ttyname(STDIN_FILENO),
  209. CurrentUserName,
  210. User->pw_name);
  211. } else {
  212. syslog(LOG_NOTICE,
  213. "%c %s %s:%s",
  214. '-',
  215. ttyname(STDIN_FILENO),
  216. CurrentUserName,
  217. User->pw_name);
  218. sleep(LOGIN_FAIL_DELAY);
  219. SwPrintError(0, NULL, "Incorrect password");
  220. Status = 1;
  221. goto MainEnd;
  222. }
  223. closelog();
  224. LogOpen = FALSE;
  225. //
  226. // If a shell wasn't provided but the caller wants to preserve the
  227. // environment, get the shell from the environment.
  228. //
  229. if ((Shell == NULL) && ((Options & SU_OPTION_PRESERVE_ENVIRONMENT) != 0)) {
  230. Shell = getenv("SHELL");
  231. }
  232. //
  233. // If the accounts main shell is not a user shell, this is probably a
  234. // special account. Don't override its shell.
  235. //
  236. if ((Shell != NULL) && (CurrentUserId != 0) &&
  237. (User->pw_shell != NULL) &&
  238. (SuIsRestrictedShell(User->pw_shell) != FALSE)) {
  239. SwPrintError(0, NULL, "Using restricted shell");
  240. Shell = NULL;
  241. }
  242. if ((Shell == NULL) || (*Shell == '\0')) {
  243. Shell = User->pw_shell;
  244. }
  245. Status = SwBecomeUser(User);
  246. if (Status != 0) {
  247. goto MainEnd;
  248. }
  249. SetupFlags = 0;
  250. Login = FALSE;
  251. if ((Options & SU_OPTION_LOGIN) != 0) {
  252. SetupFlags |= SETUP_USER_ENVIRONMENT_CLEAR_ENVIRONMENT;
  253. Login = TRUE;
  254. } else {
  255. SetupFlags |= SETUP_USER_ENVIRONMENT_NO_DIRECTORY;
  256. }
  257. if ((Options & SU_OPTION_PRESERVE_ENVIRONMENT) == 0) {
  258. SetupFlags |= SETUP_USER_ENVIRONMENT_CHANGE_ENVIRONMENT;
  259. }
  260. SwSetupUserEnvironment(User, Shell, SetupFlags);
  261. SwExecuteShell(Shell, Login, Command, Arguments + ArgumentIndex);
  262. Status = 1;
  263. MainEnd:
  264. if (LogOpen != FALSE) {
  265. closelog();
  266. }
  267. if (CurrentUserName != NULL) {
  268. free(CurrentUserName);
  269. }
  270. return Status;
  271. }
  272. //
  273. // --------------------------------------------------------- Internal Functions
  274. //
  275. BOOL
  276. SuIsRestrictedShell (
  277. PSTR Shell
  278. )
  279. /*++
  280. Routine Description:
  281. This routine determines if the given shell is a restricted shell, meaning
  282. a shell not in the user shell list (as determined by getusershell).
  283. Arguments:
  284. Shell - Supplies a pointer to the path to the shell in question.
  285. Return Value:
  286. TRUE if the shell is restricted (not on the user shell list).
  287. FALSE if the shell is a user shell.
  288. --*/
  289. {
  290. PSTR Line;
  291. BOOL Restricted;
  292. Restricted = TRUE;
  293. setusershell();
  294. while (TRUE) {
  295. Line = getusershell();
  296. if (Line == NULL) {
  297. break;
  298. }
  299. if ((*Line != '#') && (strcmp(Line, Shell) == 0)) {
  300. Restricted = FALSE;
  301. break;
  302. }
  303. }
  304. endusershell();
  305. return Restricted;
  306. }