2 * Copyright (c) 1993-1996, 1998-2013 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>
33 #endif /* STDC_HEADERS */
36 #endif /* HAVE_STRING_H */
39 #endif /* HAVE_STRINGS_H */
42 #endif /* HAVE_UNISTD_H */
47 #include "sudo_usage.h"
60 static void help(void) __attribute__((__noreturn__));
61 static void usage_excl(int);
64 * Mapping of command line flags to name/value settings.
66 static struct sudo_settings {
70 #define ARG_BSDAUTH_TYPE 0
72 #define ARG_LOGIN_CLASS 1
74 #define ARG_DEBUG_FLAGS 2
76 #define ARG_PRESERVE_ENVIRONMENT 3
77 { "preserve_environment" },
78 #define ARG_RUNAS_GROUP 4
80 #define ARG_SET_HOME 5
82 #define ARG_USER_SHELL 6
84 #define ARG_LOGIN_SHELL 7
86 #define ARG_IGNORE_TICKET 8
90 #define ARG_SELINUX_ROLE 10
92 #define ARG_SELINUX_TYPE 11
94 #define ARG_RUNAS_USER 12
96 #define ARG_PROGNAME 13
98 #define ARG_IMPLIED_SHELL 14
100 #define ARG_PRESERVE_GROUPS 15
101 { "preserve_groups" },
102 #define ARG_NONINTERACTIVE 16
103 { "noninteractive" },
104 #define ARG_SUDOEDIT 17
106 #define ARG_CLOSEFROM 18
108 #define ARG_NET_ADDRS 19
110 #define ARG_MAX_GROUPS 20
112 #define ARG_PLUGIN_DIR 21
114 #define NUM_SETTINGS 22
119 * Command line argument parsing.
120 * Sets nargc and nargv which corresponds to the argc/argv we'll use
121 * for the command to be run (if we are running one).
124 parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
127 int mode = 0; /* what mode is sudo to be run in? */
128 int flags = 0; /* mode flags */
131 char *cp, **env_add, **settings;
132 const char *debug_flags;
135 debug_decl(parse_args, SUDO_DEBUG_ARGS)
137 env_add = emalloc2(env_size, sizeof(char *));
139 /* Pass progname to plugin so it can call setprogname() */
140 sudo_settings[ARG_PROGNAME].value = getprogname();
142 /* First, check to see if we were invoked as "sudoedit". */
143 if (strcmp(getprogname(), "sudoedit") == 0) {
145 sudo_settings[ARG_SUDOEDIT].value = "true";
148 /* Load local IP addresses and masks. */
149 if (get_net_ifs(&cp) > 0)
150 sudo_settings[ARG_NET_ADDRS].value = cp;
152 /* Set debug file and flags from sudo.conf. */
153 debug_flags = sudo_conf_debug_flags();
154 if (debug_flags != NULL)
155 sudo_settings[ARG_DEBUG_FLAGS].value = debug_flags;
157 /* Set max_groups from sudo.conf. */
158 i = sudo_conf_max_groups();
160 easprintf(&cp, "%d", i);
161 sudo_settings[ARG_MAX_GROUPS].value = cp;
164 /* Returns true if the last option string was "--" */
165 #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
166 argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
168 /* Returns true if next option is an environment variable */
169 #define is_envar (optind < argc && argv[optind][0] != '/' && \
170 strchr(argv[optind], '=') != NULL)
172 /* Flags allowed when running a command */
173 valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
174 MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL;
175 /* XXX - should fill in settings at the end to avoid dupes */
178 * We disable arg permutation for GNU getopt().
179 * Some trickiness is required to allow environment variables
180 * to be interspersed with command line options.
182 if ((ch = getopt(argc, argv, "+Aa:bC:c:D:Eeg:HhiKklnPp:r:Sst:U:u:Vv")) != -1) {
185 SET(tgetpass_flags, TGP_ASKPASS);
187 #ifdef HAVE_BSD_AUTH_H
189 sudo_settings[ARG_BSDAUTH_TYPE].value = optarg;
193 SET(flags, MODE_BACKGROUND);
196 if (atoi(optarg) < 3) {
197 warningx(_("the argument to -C must be a number greater than or equal to 3"));
200 sudo_settings[ARG_CLOSEFROM].value = optarg;
202 #ifdef HAVE_LOGIN_CAP_H
204 sudo_settings[ARG_LOGIN_CLASS].value = optarg;
208 /* Ignored for backwards compatibility. */
211 sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true";
214 if (mode && mode != MODE_EDIT)
217 sudo_settings[ARG_SUDOEDIT].value = "true";
218 valid_flags = MODE_NONINTERACTIVE;
221 runas_group = optarg;
222 sudo_settings[ARG_RUNAS_GROUP].value = optarg;
225 sudo_settings[ARG_SET_HOME].value = "true";
228 if (mode && mode != MODE_HELP) {
229 if (strcmp(getprogname(), "sudoedit") != 0)
236 sudo_settings[ARG_LOGIN_SHELL].value = "true";
237 SET(flags, MODE_LOGIN_SHELL);
240 sudo_settings[ARG_IGNORE_TICKET].value = "true";
243 sudo_settings[ARG_IGNORE_TICKET].value = "true";
244 if (mode && mode != MODE_KILL)
251 if (mode == MODE_LIST)
252 SET(flags, MODE_LONG_LIST);
257 valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
260 SET(flags, MODE_NONINTERACTIVE);
261 sudo_settings[ARG_NONINTERACTIVE].value = "true";
264 sudo_settings[ARG_PRESERVE_GROUPS].value = "true";
267 sudo_settings[ARG_PROMPT].value = optarg;
271 sudo_settings[ARG_SELINUX_ROLE].value = optarg;
274 sudo_settings[ARG_SELINUX_TYPE].value = optarg;
278 SET(tgetpass_flags, TGP_STDIN);
281 sudo_settings[ARG_USER_SHELL].value = "true";
282 SET(flags, MODE_SHELL);
285 if ((getpwnam(optarg)) == NULL)
286 fatalx(_("unknown user: %s"), optarg);
291 sudo_settings[ARG_RUNAS_USER].value = optarg;
294 if (mode && mode != MODE_VALIDATE)
296 mode = MODE_VALIDATE;
297 valid_flags = MODE_NONINTERACTIVE;
300 if (mode && mode != MODE_VERSION)
308 } else if (!got_end_of_args && is_envar) {
309 if (nenv == env_size - 2) {
311 env_add = erealloc3(env_add, env_size, sizeof(char *));
313 env_add[nenv++] = argv[optind];
315 /* Crank optind and resume getopt. */
318 /* Not an option or an environment variable -- we're done. */
322 env_add[nenv] = NULL;
328 /* Defer -k mode setting until we know whether it is a flag or not */
329 if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) {
330 if (argc == 0 && !(flags & (MODE_SHELL|MODE_LOGIN_SHELL))) {
331 mode = MODE_INVALIDATE; /* -k by itself */
332 sudo_settings[ARG_IGNORE_TICKET].value = NULL;
337 mode = MODE_RUN; /* running a command */
340 if (argc > 0 && mode == MODE_LIST)
343 if (ISSET(flags, MODE_LOGIN_SHELL)) {
344 if (ISSET(flags, MODE_SHELL)) {
345 warningx(_("you may not specify both the `-i' and `-s' options"));
348 if (ISSET(flags, MODE_PRESERVE_ENV)) {
349 warningx(_("you may not specify both the `-i' and `-E' options"));
352 SET(flags, MODE_SHELL);
354 if ((flags & valid_flags) != flags)
356 if (mode == MODE_EDIT &&
357 (ISSET(flags, MODE_PRESERVE_ENV) || env_add[0] != NULL)) {
358 if (ISSET(mode, MODE_PRESERVE_ENV))
359 warningx(_("the `-E' option is not valid in edit mode"));
360 if (env_add[0] != NULL)
361 warningx(_("you may not specify environment variables in edit mode"));
364 if ((runas_user != NULL || runas_group != NULL) &&
365 !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
368 if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
369 warningx(_("the `-U' option may only be used with the `-l' option"));
372 if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
373 warningx(_("the `-A' and `-S' options may not be used together"));
376 if ((argc == 0 && mode == MODE_EDIT) ||
377 (argc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
379 if (argc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) {
380 SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
381 sudo_settings[ARG_IMPLIED_SHELL].value = "true";
384 if (mode == MODE_HELP)
388 * For shell mode we need to rewrite argv
390 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
391 char **av, *cmnd = NULL;
395 /* shell -c "command" */
397 size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
398 strlen(argv[argc - 1]) + 1;
400 cmnd = dst = emalloc2(cmnd_size, 2);
401 for (av = argv; *av != NULL; av++) {
402 for (src = *av; *src != '\0'; src++) {
403 /* quote potential meta characters */
404 if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-')
411 dst--; /* replace last space with a NUL */
414 ac += 2; /* -c cmnd */
417 av = emalloc2(ac + 1, sizeof(char *));
418 av[0] = (char *)user_details.shell; /* plugin may override shell */
430 * Format setting_pairs into settings array.
432 #ifdef _PATH_SUDO_PLUGIN_DIR
433 sudo_settings[ARG_PLUGIN_DIR].value = _PATH_SUDO_PLUGIN_DIR;
435 settings = emalloc2(NUM_SETTINGS + 1, sizeof(char *));
436 for (i = 0, j = 0; i < NUM_SETTINGS; i++) {
437 if (sudo_settings[i].value) {
438 sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s=%s",
439 sudo_settings[i].name, sudo_settings[i].value);
440 settings[j] = fmt_string(sudo_settings[i].name,
441 sudo_settings[i].value);
442 if (settings[j] == NULL)
449 if (mode == MODE_EDIT) {
450 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
451 /* Must have the command in argv[0]. */
454 argv[0] = "sudoedit";
456 fatalx(_("sudoedit is not supported on this platform"));
460 *settingsp = settings;
464 debug_return_int(mode | flags);
468 usage_err(const char *buf)
470 return fputs(buf, stderr);
474 usage_out(const char *buf)
476 return fputs(buf, stdout);
480 * Give usage message and exit.
481 * The actual usage strings are in sudo_usage.h for configure substitution.
491 * Use usage vectors appropriate to the progname.
493 if (strcmp(getprogname(), "sudoedit") == 0) {
494 uvec[0] = SUDO_USAGE5 + 3;
497 uvec[0] = SUDO_USAGE1;
498 uvec[1] = SUDO_USAGE2;
499 uvec[2] = SUDO_USAGE3;
500 uvec[3] = SUDO_USAGE4;
501 uvec[4] = SUDO_USAGE5;
506 * Print usage and wrap lines as needed, depending on the
509 ulen = (int)strlen(getprogname()) + 8;
510 lbuf_init(&lbuf, fatal ? usage_err : usage_out, ulen, NULL,
511 user_details.ts_cols);
512 for (i = 0; uvec[i] != NULL; i++) {
513 lbuf_append(&lbuf, "usage: %s%s", getprogname(), uvec[i]);
522 * Tell which options are mutually exclusive and exit.
525 usage_excl(int fatal)
527 debug_decl(usage_excl, SUDO_DEBUG_ARGS)
529 warningx(_("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified"));
538 const char *pname = getprogname();
539 debug_decl(help, SUDO_DEBUG_ARGS)
541 lbuf_init(&lbuf, usage_out, indent, NULL, user_details.ts_cols);
542 if (strcmp(pname, "sudoedit") == 0)
543 lbuf_append(&lbuf, _("%s - edit files as another user\n\n"), pname);
545 lbuf_append(&lbuf, _("%s - execute a command as another user\n\n"), pname);
550 lbuf_append(&lbuf, _("\nOptions:\n"));
551 lbuf_append(&lbuf, " -A %s",
552 _("use helper program for password prompting\n"));
553 #ifdef HAVE_BSD_AUTH_H
554 lbuf_append(&lbuf, " -a type %s",
555 _("use specified BSD authentication type\n"));
557 lbuf_append(&lbuf, " -b %s",
558 _("run command in the background\n"));
559 lbuf_append(&lbuf, " -C fd %s",
560 _("close all file descriptors >= fd\n"));
561 #ifdef HAVE_LOGIN_CAP_H
562 lbuf_append(&lbuf, " -c class %s",
563 _("run command with specified login class\n"));
565 lbuf_append(&lbuf, " -E %s",
566 _("preserve user environment when executing command\n"));
567 lbuf_append(&lbuf, " -e %s",
568 _("edit files instead of running a command\n"));
569 lbuf_append(&lbuf, " -g group %s",
570 _("execute command as the specified group\n"));
571 lbuf_append(&lbuf, " -H %s",
572 _("set HOME variable to target user's home dir.\n"));
573 lbuf_append(&lbuf, " -h %s",
574 _("display help message and exit\n"));
575 lbuf_append(&lbuf, " -i [command] %s",
576 _("run a login shell as target user\n"));
577 lbuf_append(&lbuf, " -K %s",
578 _("remove timestamp file completely\n"));
579 lbuf_append(&lbuf, " -k %s",
580 _("invalidate timestamp file\n"));
581 lbuf_append(&lbuf, " -l[l] command %s",
582 _("list user's available commands\n"));
583 lbuf_append(&lbuf, " -n %s",
584 _("non-interactive mode, will not prompt user\n"));
585 lbuf_append(&lbuf, " -P %s",
586 _("preserve group vector instead of setting to target's\n"));
587 lbuf_append(&lbuf, " -p prompt %s",
588 _("use specified password prompt\n"));
590 lbuf_append(&lbuf, " -r role %s",
591 _("create SELinux security context with specified role\n"));
593 lbuf_append(&lbuf, " -S %s",
594 _("read password from standard input\n"));
596 " -s [command] %s", _("run a shell as target user\n"));
598 lbuf_append(&lbuf, " -t type %s",
599 _("create SELinux security context with specified role\n"));
601 lbuf_append(&lbuf, " -U user %s",
602 _("when listing, list specified user's privileges\n"));
603 lbuf_append(&lbuf, " -u user %s",
604 _("run command (or edit file) as specified user\n"));
605 lbuf_append(&lbuf, " -V %s",
606 _("display version information and exit\n"));
607 lbuf_append(&lbuf, " -v %s",
608 _("update user's timestamp without running a command\n"));
609 lbuf_append(&lbuf, " -- %s",
610 _("stop processing command line arguments\n"));
613 sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 0);