/* * Copyright (c) 1993-1996, 1998-2010 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ #include #include #include #include #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif /* STDC_HEADERS */ #ifdef HAVE_STRING_H # include #endif /* HAVE_STRING_H */ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #include #include #include "sudo.h" #include "lbuf.h" #include /* * Local functions */ static void usage_excl __P((int)) __attribute__((__noreturn__)); /* * For sudo.c */ extern int NewArgc; extern char **NewArgv; extern int user_closefrom; extern char *runas_user; extern char *runas_group; /* For getopt(3) */ extern char *optarg; extern int optind; #ifdef HAVE_BSD_AUTH_H char *login_style; #endif /* HAVE_BSD_AUTH_H */ /* * Command line argument parsing. * Sets NewArgc and NewArgv which corresponds to the argc/argv we'll use * for the command to be run (if we are running one). */ int parse_args(argc, argv) int argc; char **argv; { int mode = 0; /* what mode is sudo to be run in? */ int flags = 0; /* mode flags */ int valid_flags, ch; /* First, check to see if we were invoked as "sudoedit". */ if (strcmp(getprogname(), "sudoedit") == 0) mode = MODE_EDIT; /* Returns true if the last option string was "--" */ #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \ argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0') /* Returns true if next option is an environment variable */ #define is_envar (optind < argc && argv[optind][0] != '/' && \ strchr(argv[optind], '=') != NULL) /* Flags allowed when running a command */ valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME| MODE_LOGIN_SHELL|MODE_INVALIDATE|MODE_NONINTERACTIVE| MODE_PRESERVE_GROUPS|MODE_SHELL; for (;;) { /* * We disable arg permutation for GNU getopt(). * Some trickiness is required to allow environment variables * to be interspersed with command line options. */ if ((ch = getopt(argc, argv, "+Aa:bC:c:Eeg:HhiKkLlnPp:r:Sst:U:u:Vv")) != -1) { switch (ch) { case 'A': SET(tgetpass_flags, TGP_ASKPASS); break; #ifdef HAVE_BSD_AUTH_H case 'a': login_style = optarg; break; #endif case 'b': SET(flags, MODE_BACKGROUND); break; case 'C': if ((user_closefrom = atoi(optarg)) < 3) { warningx("the argument to -C must be at least 3"); usage(1); } break; #ifdef HAVE_LOGIN_CAP_H case 'c': login_class = optarg; def_use_loginclass = TRUE; break; #endif case 'E': SET(flags, MODE_PRESERVE_ENV); break; case 'e': if (mode && mode != MODE_EDIT) usage_excl(1); mode = MODE_EDIT; valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE; break; case 'g': runas_group = optarg; break; case 'H': SET(flags, MODE_RESET_HOME); break; case 'h': if (mode && mode != MODE_HELP) { if (strcmp(getprogname(), "sudoedit") != 0) usage_excl(1); } mode = MODE_HELP; valid_flags = 0; break; case 'i': SET(flags, MODE_LOGIN_SHELL); def_env_reset = TRUE; break; case 'k': SET(flags, MODE_INVALIDATE); break; case 'K': if (mode && mode != MODE_KILL) usage_excl(1); mode = MODE_KILL; valid_flags = 0; break; case 'L': if (mode && mode != MODE_LISTDEFS) usage_excl(1); mode = MODE_LISTDEFS; valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE; break; case 'l': if (mode) { if (mode == MODE_LIST) long_list = 1; else usage_excl(1); } mode = MODE_LIST; valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE; break; case 'n': SET(flags, MODE_NONINTERACTIVE); break; case 'P': SET(flags, MODE_PRESERVE_GROUPS); break; case 'p': user_prompt = optarg; def_passprompt_override = TRUE; break; #ifdef HAVE_SELINUX case 'r': user_role = optarg; break; case 't': user_type = optarg; break; #endif case 'S': SET(tgetpass_flags, TGP_STDIN); break; case 's': SET(flags, MODE_SHELL); break; case 'U': if ((list_pw = sudo_getpwnam(optarg)) == NULL) errorx(1, "unknown user: %s", optarg); break; case 'u': runas_user = optarg; break; case 'v': if (mode && mode != MODE_VALIDATE) usage_excl(1); mode = MODE_VALIDATE; valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE; break; case 'V': if (mode && mode != MODE_VERSION) usage_excl(1); mode = MODE_VERSION; valid_flags = 0; break; default: usage(1); } } else if (!got_end_of_args && is_envar) { struct list_member *ev; /* Store environment variable. */ ev = emalloc(sizeof(*ev)); ev->value = argv[optind]; ev->next = sudo_user.env_vars; sudo_user.env_vars = ev; /* Crank optind and resume getopt. */ optind++; } else { /* Not an option or an environment variable -- we're done. */ break; } } NewArgc = argc - optind; NewArgv = argv + optind; if (!mode) { /* Defer -k mode setting until we know whether it is a flag or not */ if (ISSET(flags, MODE_INVALIDATE) && NewArgc == 0) { mode = MODE_INVALIDATE; /* -k by itself */ CLR(flags, MODE_INVALIDATE); valid_flags = 0; } else { mode = MODE_RUN; /* running a command */ } } if (NewArgc > 0 && mode == MODE_LIST) mode = MODE_CHECK; if (ISSET(flags, MODE_LOGIN_SHELL)) { if (ISSET(flags, MODE_SHELL)) { warningx("you may not specify both the `-i' and `-s' options"); usage(1); } if (ISSET(flags, MODE_PRESERVE_ENV)) { warningx("you may not specify both the `-i' and `-E' options"); usage(1); } SET(flags, MODE_SHELL); } if ((flags & valid_flags) != flags) usage(1); if (mode == MODE_EDIT && (ISSET(flags, MODE_PRESERVE_ENV) || sudo_user.env_vars != NULL)) { if (ISSET(mode, MODE_PRESERVE_ENV)) warningx("the `-E' option is not valid in edit mode"); if (sudo_user.env_vars != NULL) warningx("you may not specify environment variables in edit mode"); usage(1); } if ((runas_user != NULL || runas_group != NULL) && !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) { usage(1); } if (list_pw != NULL && mode != MODE_LIST && mode != MODE_CHECK) { warningx("the `-U' option may only be used with the `-l' option"); usage(1); } if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) { warningx("the `-A' and `-S' options may not be used together"); usage(1); } if ((NewArgc == 0 && mode == MODE_EDIT) || (NewArgc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK))) usage(1); if (NewArgc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL)); return(mode | flags); } static int usage_out(buf) const char *buf; { return fputs(buf, stderr); } /* * Give usage message and exit. * The actual usage strings are in sudo_usage.h for configure substitution. */ void usage(exit_val) int exit_val; { struct lbuf lbuf; char *uvec[6]; int i, ulen; /* * Use usage vectors appropriate to the progname. */ if (strcmp(getprogname(), "sudoedit") == 0) { uvec[0] = SUDO_USAGE5 + 3; uvec[1] = NULL; } else { uvec[0] = SUDO_USAGE1; uvec[1] = SUDO_USAGE2; uvec[2] = SUDO_USAGE3; uvec[3] = SUDO_USAGE4; uvec[4] = SUDO_USAGE5; uvec[5] = NULL; } /* * Print usage and wrap lines as needed, depending on the * tty width. */ ulen = (int)strlen(getprogname()) + 8; lbuf_init(&lbuf, usage_out, ulen, NULL); for (i = 0; uvec[i] != NULL; i++) { lbuf_append(&lbuf, "usage: ", getprogname(), uvec[i], NULL); lbuf_print(&lbuf); } lbuf_destroy(&lbuf); exit(exit_val); } /* * Tell which options are mutually exclusive and exit. */ static void usage_excl(exit_val) int exit_val; { warningx("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified"); usage(exit_val); }