2 * Copyright (c) 1993-1996, 1998-2011 Todd C. Miller <Todd.Miller@courtesan.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 * Sponsored in part by the Defense Advanced Research Projects
17 * Agency (DARPA) and Air Force Research Laboratory, Air Force
18 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
23 #include <sys/types.h>
24 #include <sys/param.h>
34 #endif /* STDC_HEADERS */
37 #endif /* HAVE_STRING_H */
40 #endif /* HAVE_STRINGS_H */
43 #endif /* HAVE_UNISTD_H */
48 #include <sudo_usage.h>
61 static void help(void) __attribute__((__noreturn__));
62 static void usage_excl(int);
65 * Mapping of command line flags to name/value settings.
67 static struct sudo_settings {
71 #define ARG_BSDAUTH_TYPE 0
73 #define ARG_LOGIN_CLASS 1
75 #define ARG_DEBUG_LEVEL 2
77 #define ARG_PRESERVE_ENVIRONMENT 3
78 { "preserve_environment" },
79 #define ARG_RUNAS_GROUP 4
81 #define ARG_SET_HOME 5
83 #define ARG_USER_SHELL 6
85 #define ARG_LOGIN_SHELL 7
87 #define ARG_IGNORE_TICKET 8
91 #define ARG_SELINUX_ROLE 10
93 #define ARG_SELINUX_TYPE 11
95 #define ARG_RUNAS_USER 12
97 #define ARG_PROGNAME 13
99 #define ARG_IMPLIED_SHELL 14
101 #define ARG_PRESERVE_GROUPS 15
102 { "preserve_groups" },
103 #define ARG_NONINTERACTIVE 16
104 { "noninteractive" },
105 #define ARG_SUDOEDIT 17
107 #define ARG_CLOSEFROM 18
109 #define ARG_NET_ADDRS 19
111 #define NUM_SETTINGS 20
116 * Command line argument parsing.
117 * Sets nargc and nargv which corresponds to the argc/argv we'll use
118 * for the command to be run (if we are running one).
121 parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
124 int mode = 0; /* what mode is sudo to be run in? */
125 int flags = 0; /* mode flags */
128 char *cp, **env_add, **settings;
132 env_add = emalloc2(env_size, sizeof(char *));
134 /* Pass progname to plugin so it can call setprogname() */
135 sudo_settings[ARG_PROGNAME].value = getprogname();
137 /* First, check to see if we were invoked as "sudoedit". */
138 if (strcmp(getprogname(), "sudoedit") == 0) {
140 sudo_settings[ARG_SUDOEDIT].value = "true";
143 /* Load local IP addresses and masks. */
144 if (get_net_ifs(&cp) > 0)
145 sudo_settings[ARG_NET_ADDRS].value = cp;
147 /* Returns true if the last option string was "--" */
148 #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
149 argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
151 /* Returns true if next option is an environment variable */
152 #define is_envar (optind < argc && argv[optind][0] != '/' && \
153 strchr(argv[optind], '=') != NULL)
155 /* Flags allowed when running a command */
156 valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
157 MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL;
158 /* XXX - should fill in settings at the end to avoid dupes */
161 * We disable arg permutation for GNU getopt().
162 * Some trickiness is required to allow environment variables
163 * to be interspersed with command line options.
165 if ((ch = getopt(argc, argv, "+Aa:bC:c:D:Eeg:HhiKklnPp:r:Sst:U:u:Vv")) != -1) {
168 SET(tgetpass_flags, TGP_ASKPASS);
170 #ifdef HAVE_BSD_AUTH_H
172 sudo_settings[ARG_BSDAUTH_TYPE].value = optarg;
176 SET(flags, MODE_BACKGROUND);
179 if (atoi(optarg) < 3) {
180 warningx("the argument to -C must be a number greater than or equal to 3");
183 sudo_settings[ARG_CLOSEFROM].value = optarg;
185 #ifdef HAVE_LOGIN_CAP_H
187 sudo_settings[ARG_LOGIN_CLASS].value = optarg;
191 if ((debug_level = atoi(optarg)) < 1 || debug_level > 9) {
192 warningx("the argument to -D must be between 1 and 9 inclusive");
195 sudo_settings[ARG_DEBUG_LEVEL].value = optarg;
198 sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true";
201 if (mode && mode != MODE_EDIT)
204 sudo_settings[ARG_SUDOEDIT].value = "true";
205 valid_flags = MODE_NONINTERACTIVE;
208 runas_group = optarg;
209 sudo_settings[ARG_RUNAS_GROUP].value = optarg;
212 sudo_settings[ARG_SET_HOME].value = "true";
215 if (mode && mode != MODE_HELP) {
216 if (strcmp(getprogname(), "sudoedit") != 0)
223 sudo_settings[ARG_LOGIN_SHELL].value = "true";
224 SET(flags, MODE_LOGIN_SHELL);
227 sudo_settings[ARG_IGNORE_TICKET].value = "true";
230 sudo_settings[ARG_IGNORE_TICKET].value = "true";
231 if (mode && mode != MODE_KILL)
238 if (mode == MODE_LIST)
239 SET(flags, MODE_LONG_LIST);
244 valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
247 SET(flags, MODE_NONINTERACTIVE);
248 sudo_settings[ARG_NONINTERACTIVE].value = "true";
251 sudo_settings[ARG_PRESERVE_GROUPS].value = "true";
254 sudo_settings[ARG_PROMPT].value = optarg;
258 sudo_settings[ARG_SELINUX_ROLE].value = optarg;
261 sudo_settings[ARG_SELINUX_TYPE].value = optarg;
265 SET(tgetpass_flags, TGP_STDIN);
268 sudo_settings[ARG_USER_SHELL].value = "true";
269 SET(flags, MODE_SHELL);
272 if ((getpwnam(optarg)) == NULL)
273 errorx(1, "unknown user: %s", optarg);
278 sudo_settings[ARG_RUNAS_USER].value = optarg;
281 if (mode && mode != MODE_VALIDATE)
283 mode = MODE_VALIDATE;
284 valid_flags = MODE_NONINTERACTIVE;
287 if (mode && mode != MODE_VERSION)
295 } else if (!got_end_of_args && is_envar) {
296 if (nenv == env_size - 2) {
298 env_add = erealloc3(env_add, env_size, sizeof(char *));
300 env_add[nenv++] = argv[optind];
302 /* Crank optind and resume getopt. */
305 /* Not an option or an environment variable -- we're done. */
309 env_add[nenv] = NULL;
315 /* Defer -k mode setting until we know whether it is a flag or not */
316 if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) {
318 mode = MODE_INVALIDATE; /* -k by itself */
319 sudo_settings[ARG_IGNORE_TICKET].value = NULL;
324 mode = MODE_RUN; /* running a command */
327 if (argc > 0 && mode == MODE_LIST)
330 if (ISSET(flags, MODE_LOGIN_SHELL)) {
331 if (ISSET(flags, MODE_SHELL)) {
332 warningx("you may not specify both the `-i' and `-s' options");
335 if (ISSET(flags, MODE_PRESERVE_ENV)) {
336 warningx("you may not specify both the `-i' and `-E' options");
339 SET(flags, MODE_SHELL);
341 if ((flags & valid_flags) != flags)
343 if (mode == MODE_EDIT &&
344 (ISSET(flags, MODE_PRESERVE_ENV) || env_add[0] != NULL)) {
345 if (ISSET(mode, MODE_PRESERVE_ENV))
346 warningx("the `-E' option is not valid in edit mode");
347 if (env_add[0] != NULL)
348 warningx("you may not specify environment variables in edit mode");
351 if ((runas_user != NULL || runas_group != NULL) &&
352 !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
355 if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
356 warningx("the `-U' option may only be used with the `-l' option");
359 if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
360 warningx("the `-A' and `-S' options may not be used together");
363 if ((argc == 0 && mode == MODE_EDIT) ||
364 (argc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
366 if (argc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) {
367 SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
368 sudo_settings[ARG_IMPLIED_SHELL].value = "true";
371 if (mode == MODE_HELP)
375 * For shell mode we need to rewrite argv
377 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
384 av = emalloc2(ac + 1, sizeof(char *));
385 memcpy(av + 1, argv, argc * sizeof(char *));
387 /* shell -c "command" */
388 char *src, *dst, *end;
389 size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
390 strlen(argv[argc - 1]) + 1;
392 av = emalloc2(ac + 1, sizeof(char *));
394 av[2] = dst = emalloc(cmnd_size);
396 for (end = src + cmnd_size - 1; src < end; src++, dst++)
397 *dst = *src == '\0' ? ' ' : *src;
400 av[0] = (char *)user_details.shell; /* plugin may override shell */
408 * Format setting_pairs into settings array.
410 settings = emalloc2(NUM_SETTINGS + 1, sizeof(char *));
411 for (i = 0, j = 0; i < NUM_SETTINGS; i++) {
412 if (sudo_settings[i].value) {
413 sudo_debug(9, "settings: %s=%s", sudo_settings[i].name,
414 sudo_settings[i].value);
415 settings[j] = fmt_string(sudo_settings[i].name,
416 sudo_settings[i].value);
417 if (settings[j] == NULL)
418 errorx(1, "unable to allocate memory");
424 if (mode == MODE_EDIT) {
425 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
426 /* Must have the command in argv[0]. */
429 argv[0] = "sudoedit";
431 errorx(1, "sudoedit is not supported on this platform");
435 *settingsp = settings;
443 usage_err(const char *buf)
445 return fputs(buf, stderr);
449 usage_out(const char *buf)
451 return fputs(buf, stdout);
455 * Give usage message and exit.
456 * The actual usage strings are in sudo_usage.h for configure substitution.
466 * Use usage vectors appropriate to the progname.
468 if (strcmp(getprogname(), "sudoedit") == 0) {
469 uvec[0] = SUDO_USAGE5 + 3;
472 uvec[0] = SUDO_USAGE1;
473 uvec[1] = SUDO_USAGE2;
474 uvec[2] = SUDO_USAGE3;
475 uvec[3] = SUDO_USAGE4;
476 uvec[4] = SUDO_USAGE5;
481 * Print usage and wrap lines as needed, depending on the
484 ulen = (int)strlen(getprogname()) + 8;
485 lbuf_init(&lbuf, fatal ? usage_err : usage_out, ulen, NULL,
486 user_details.ts_cols);
487 for (i = 0; uvec[i] != NULL; i++) {
488 lbuf_append(&lbuf, "usage: ", getprogname(), uvec[i], NULL);
497 * Tell which options are mutually exclusive and exit.
500 usage_excl(int fatal)
502 warningx("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified");
511 const char *pname = getprogname();
513 lbuf_init(&lbuf, usage_out, indent, NULL, user_details.ts_cols);
514 if (strcmp(pname, "sudoedit") == 0)
515 lbuf_append(&lbuf, pname, " - edit files as another user\n\n", NULL);
517 lbuf_append(&lbuf, pname, " - execute a command as another user\n\n", NULL);
522 lbuf_append(&lbuf, "\nOptions:\n", NULL);
523 #ifdef HAVE_BSD_AUTH_H
525 " -A use helper program for password prompting\n", NULL);
528 " -a type use specified BSD authentication type\n", NULL);
530 " -b run command in the background\n", NULL);
532 " -C fd close all file descriptors >= fd\n", NULL);
533 #ifdef HAVE_LOGIN_CAP_H
535 " -c class run command with specified login class\n", NULL);
538 " -E preserve user environment when executing command\n",
541 " -e edit files instead of running a command\n", NULL);
543 " -g group execute command as the specified group\n", NULL);
545 " -H set HOME variable to target user's home dir.\n",
548 " -h display help message and exit\n", NULL);
550 " -i [command] run a login shell as target user\n", NULL);
552 " -K remove timestamp file completely\n", NULL);
554 " -k invalidate timestamp file\n", NULL);
556 " -l[l] command list user's available commands\n", NULL);
558 " -n non-interactive mode, will not prompt user\n", NULL);
560 " -P preserve group vector instead of setting to target's\n",
563 " -p prompt use specified password prompt\n", NULL);
566 " -r role create SELinux security context with specified role\n",
570 " -S read password from standard input\n", NULL);
572 " -s [command] run a shell as target user\n", NULL);
575 " -t type create SELinux security context with specified role\n",
579 " -U user when listing, list specified user's privileges\n",
582 " -u user run command (or edit file) as specified user\n", NULL);
584 " -V display version information and exit\n", NULL);
586 " -v update user's timestamp without running a command\n",
589 " -- stop processing command line arguments\n", NULL);