From d11cb3d14b1e6bb9dd60ae9e209edf08a406721b Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Wed, 12 Mar 2008 23:42:32 +0000 Subject: [PATCH] Use getopt() to parse the command line. We need to be able to intersperse env variables and options yet still honor "--"" which complicates things slightly. --- sudo.c | 153 +++++++++++++++++++-------------------------------------- 1 file changed, 51 insertions(+), 102 deletions(-) diff --git a/sudo.c b/sudo.c index 776c942f8..4040a13ae 100644 --- a/sudo.c +++ b/sudo.c @@ -157,6 +157,10 @@ static char *runas_user; static char *runas_group; static struct sudo_nss_list *snl; +/* For getopt(3) */ +extern char *optarg; +extern int optind; + int main(argc, argv, envp) int argc; @@ -803,7 +807,9 @@ set_cmnd(sudo_mode) } /* - * Command line argument parsing, can't use getopt(3) due to optional args. + * 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). */ static int parse_args(argc, argv) @@ -812,9 +818,7 @@ parse_args(argc, argv) { int rval = MODE_RUN; /* what mode is sudo to be run in? */ int excl = 0; /* exclusive arg, no others allowed */ - - NewArgv = argv + 1; - NewArgc = argc - 1; + int ch; /* First, check to see if we were invoked as "sudoedit". */ if (strcmp(getprogname(), "sudoedit") == 0) { @@ -823,83 +827,51 @@ parse_args(argc, argv) } else rval = MODE_RUN; - while (NewArgc > 0) { - if (NewArgv[0][0] == '-') { - if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0' && - NewArgv[0][2] != 'l') { - warningx("please use single character options"); - usage(1); - } + /* 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') - switch (NewArgv[0][1]) { + /* Returns true if next option is an environment variable */ +#define is_envar (optind < argc && argv[optind][0] != '/' && \ + strchr(argv[optind], '=') != NULL) + + 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:HhiKkLlPp:r:Sst:Uu:Vv")) != -1) { + switch (ch) { case 'A': SET(tgetpass_flags, TGP_ASKPASS); break; case 'p': - /* Must have an associated prompt. */ - if (NewArgv[1] == NULL) - usage(1); - - user_prompt = NewArgv[1]; + user_prompt = optarg; def_passprompt_override = TRUE; - - NewArgc--; - NewArgv++; break; case 'u': - /* Must have an associated runas user. */ - if (NewArgv[1] == NULL) - usage(1); - - runas_user = NewArgv[1]; - - NewArgc--; - NewArgv++; + runas_user = optarg; break; case 'g': - /* Must have an associated runas group. */ - if (NewArgv[1] == NULL) - usage(1); - - runas_group = NewArgv[1]; - - NewArgc--; - NewArgv++; + runas_group = optarg; break; #ifdef HAVE_BSD_AUTH_H case 'a': - /* Must have an associated authentication style. */ - if (NewArgv[1] == NULL) - usage(1); - - login_style = NewArgv[1]; - - NewArgc--; - NewArgv++; + login_style = optarg; break; #endif #ifdef HAVE_LOGIN_CAP_H case 'c': - /* Must have an associated login class. */ - if (NewArgv[1] == NULL) - usage(1); - - login_class = NewArgv[1]; + login_class = optarg; def_use_loginclass = TRUE; - - NewArgc--; - NewArgv++; break; #endif case 'C': - if (NewArgv[1] == NULL) - usage(1); - if ((user_closefrom = atoi(NewArgv[1])) < 3) { + if ((user_closefrom = atoi(optarg)) < 3) { warningx("the argument to -C must be at least 3"); usage(1); } - NewArgc--; - NewArgv++; break; case 'b': SET(rval, MODE_BACKGROUND); @@ -943,10 +915,10 @@ parse_args(argc, argv) break; case 'l': rval = MODE_LIST; - if (excl && excl != 'l') - usage_excl(1); - if (NewArgv[0][2] == 'l' || excl == 'l') + if (excl == 'l') long_list = 1; + else if (excl) + usage_excl(1); excl = 'l'; break; case 'V': @@ -977,65 +949,42 @@ parse_args(argc, argv) SET(tgetpass_flags, TGP_STDIN); break; case 'U': - /* Must have an associated list user. */ - if (NewArgv[1] == NULL) - usage(1); - if ((list_pw = sudo_getpwnam(NewArgv[1])) == NULL) - errorx(1, "unknown user: %s", NewArgv[1]); - NewArgc--; - NewArgv++; + if ((list_pw = sudo_getpwnam(optarg)) == NULL) + errorx(1, "unknown user: %s", optarg); break; case 'E': SET(rval, MODE_PRESERVE_ENV); break; #ifdef HAVE_SELINUX case 'r': - /* Must have an associated SELinux role. */ - if (NewArgv[1] == NULL) - usage(1); - - user_role = NewArgv[1]; - - NewArgc--; - NewArgv++; + user_role = optarg; break; case 't': - /* Must have an associated SELinux type. */ - if (NewArgv[1] == NULL) - usage(1); - - user_type = NewArgv[1]; - - NewArgc--; - NewArgv++; + user_type = optarg; break; #endif - case '-': - NewArgc--; - NewArgv++; - goto args_done; - case '\0': - warningx("`-' requires an argument"); - usage(1); default: - warningx("illegal option `%s'", NewArgv[0]); usage(1); } - } else if (NewArgv[0][0] != '/' && strchr(NewArgv[0], '=') != NULL) { - /* Could be an environment variable. */ - struct list_member *ev; - ev = emalloc(sizeof(*ev)); - ev->value = NewArgv[0]; - ev->next = sudo_user.env_vars; - sudo_user.env_vars = ev; + } 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 arg */ + /* Not an option or an environment variable -- we're done. */ break; } - NewArgc--; - NewArgv++; } -args_done: + + NewArgc = argc - optind; + NewArgv = argv + optind; if (NewArgc > 0 && rval == MODE_LIST) rval = MODE_CHECK; -- 2.40.0