Annotation of embedaddon/sudo/src/parse_args.c, revision 1.1.1.3
1.1 misho 1: /*
1.1.1.2 misho 2: * Copyright (c) 1993-1996, 1998-2012 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 misho 3: *
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.
7: *
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.
15: *
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.
19: */
20:
21: #include <config.h>
22:
23: #include <sys/types.h>
24: #include <sys/param.h>
25:
26: #include <stdio.h>
27: #ifdef STDC_HEADERS
28: # include <stdlib.h>
29: # include <stddef.h>
30: #else
31: # ifdef HAVE_STDLIB_H
32: # include <stdlib.h>
33: # endif
34: #endif /* STDC_HEADERS */
35: #ifdef HAVE_STRING_H
36: # include <string.h>
37: #endif /* HAVE_STRING_H */
38: #ifdef HAVE_STRINGS_H
39: # include <strings.h>
40: #endif /* HAVE_STRINGS_H */
41: #ifdef HAVE_UNISTD_H
42: # include <unistd.h>
43: #endif /* HAVE_UNISTD_H */
44: #include <ctype.h>
45: #include <grp.h>
46: #include <pwd.h>
47:
48: #include <sudo_usage.h>
49: #include "sudo.h"
50: #include "lbuf.h"
51:
52: /* For getopt(3) */
53: extern char *optarg;
54: extern int optind;
55:
56: int tgetpass_flags;
57:
58: /*
59: * Local functions.
60: */
61: static void help(void) __attribute__((__noreturn__));
62: static void usage_excl(int);
63:
64: /*
65: * Mapping of command line flags to name/value settings.
66: */
67: static struct sudo_settings {
68: const char *name;
69: const char *value;
70: } sudo_settings[] = {
71: #define ARG_BSDAUTH_TYPE 0
72: { "bsdauth_type" },
73: #define ARG_LOGIN_CLASS 1
74: { "login_class" },
1.1.1.2 misho 75: #define ARG_DEBUG_FLAGS 2
76: { "debug_flags" },
1.1 misho 77: #define ARG_PRESERVE_ENVIRONMENT 3
78: { "preserve_environment" },
79: #define ARG_RUNAS_GROUP 4
80: { "runas_group" },
81: #define ARG_SET_HOME 5
82: { "set_home" },
83: #define ARG_USER_SHELL 6
84: { "run_shell" },
85: #define ARG_LOGIN_SHELL 7
86: { "login_shell" },
87: #define ARG_IGNORE_TICKET 8
88: { "ignore_ticket" },
89: #define ARG_PROMPT 9
90: { "prompt" },
91: #define ARG_SELINUX_ROLE 10
92: { "selinux_role" },
93: #define ARG_SELINUX_TYPE 11
94: { "selinux_type" },
95: #define ARG_RUNAS_USER 12
96: { "runas_user" },
97: #define ARG_PROGNAME 13
98: { "progname" },
99: #define ARG_IMPLIED_SHELL 14
100: { "implied_shell" },
101: #define ARG_PRESERVE_GROUPS 15
102: { "preserve_groups" },
103: #define ARG_NONINTERACTIVE 16
104: { "noninteractive" },
105: #define ARG_SUDOEDIT 17
106: { "sudoedit" },
107: #define ARG_CLOSEFROM 18
108: { "closefrom" },
109: #define ARG_NET_ADDRS 19
110: { "network_addrs" },
111: #define NUM_SETTINGS 20
112: { NULL }
113: };
114:
115: /*
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).
119: */
120: int
121: parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
122: char ***env_addp)
123: {
124: int mode = 0; /* what mode is sudo to be run in? */
125: int flags = 0; /* mode flags */
126: int valid_flags, ch;
127: int i, j;
128: char *cp, **env_add, **settings;
1.1.1.2 misho 129: const char *debug_flags;
1.1 misho 130: int nenv = 0;
131: int env_size = 32;
1.1.1.2 misho 132: debug_decl(parse_args, SUDO_DEBUG_ARGS)
1.1 misho 133:
134: env_add = emalloc2(env_size, sizeof(char *));
135:
136: /* Pass progname to plugin so it can call setprogname() */
137: sudo_settings[ARG_PROGNAME].value = getprogname();
138:
139: /* First, check to see if we were invoked as "sudoedit". */
140: if (strcmp(getprogname(), "sudoedit") == 0) {
141: mode = MODE_EDIT;
142: sudo_settings[ARG_SUDOEDIT].value = "true";
143: }
144:
145: /* Load local IP addresses and masks. */
146: if (get_net_ifs(&cp) > 0)
147: sudo_settings[ARG_NET_ADDRS].value = cp;
148:
1.1.1.2 misho 149: /* Set debug file and flags from sudo.conf. */
150: debug_flags = sudo_conf_debug_flags();
151: if (debug_flags != NULL)
152: sudo_settings[ARG_DEBUG_FLAGS].value = debug_flags;
153:
1.1 misho 154: /* Returns true if the last option string was "--" */
155: #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
156: argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
157:
158: /* Returns true if next option is an environment variable */
159: #define is_envar (optind < argc && argv[optind][0] != '/' && \
160: strchr(argv[optind], '=') != NULL)
161:
162: /* Flags allowed when running a command */
163: valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
164: MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL;
165: /* XXX - should fill in settings at the end to avoid dupes */
166: for (;;) {
167: /*
168: * We disable arg permutation for GNU getopt().
169: * Some trickiness is required to allow environment variables
170: * to be interspersed with command line options.
171: */
172: if ((ch = getopt(argc, argv, "+Aa:bC:c:D:Eeg:HhiKklnPp:r:Sst:U:u:Vv")) != -1) {
173: switch (ch) {
174: case 'A':
175: SET(tgetpass_flags, TGP_ASKPASS);
176: break;
177: #ifdef HAVE_BSD_AUTH_H
178: case 'a':
179: sudo_settings[ARG_BSDAUTH_TYPE].value = optarg;
180: break;
181: #endif
182: case 'b':
183: SET(flags, MODE_BACKGROUND);
184: break;
185: case 'C':
186: if (atoi(optarg) < 3) {
187: warningx(_("the argument to -C must be a number greater than or equal to 3"));
188: usage(1);
189: }
190: sudo_settings[ARG_CLOSEFROM].value = optarg;
191: break;
192: #ifdef HAVE_LOGIN_CAP_H
193: case 'c':
194: sudo_settings[ARG_LOGIN_CLASS].value = optarg;
195: break;
196: #endif
197: case 'D':
1.1.1.2 misho 198: /* Ignored for backwards compatibility. */
1.1 misho 199: break;
200: case 'E':
201: sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true";
202: break;
203: case 'e':
204: if (mode && mode != MODE_EDIT)
205: usage_excl(1);
206: mode = MODE_EDIT;
207: sudo_settings[ARG_SUDOEDIT].value = "true";
208: valid_flags = MODE_NONINTERACTIVE;
209: break;
210: case 'g':
211: runas_group = optarg;
212: sudo_settings[ARG_RUNAS_GROUP].value = optarg;
213: break;
214: case 'H':
215: sudo_settings[ARG_SET_HOME].value = "true";
216: break;
217: case 'h':
218: if (mode && mode != MODE_HELP) {
219: if (strcmp(getprogname(), "sudoedit") != 0)
220: usage_excl(1);
221: }
222: mode = MODE_HELP;
223: valid_flags = 0;
224: break;
225: case 'i':
226: sudo_settings[ARG_LOGIN_SHELL].value = "true";
227: SET(flags, MODE_LOGIN_SHELL);
228: break;
229: case 'k':
230: sudo_settings[ARG_IGNORE_TICKET].value = "true";
231: break;
232: case 'K':
233: sudo_settings[ARG_IGNORE_TICKET].value = "true";
234: if (mode && mode != MODE_KILL)
235: usage_excl(1);
236: mode = MODE_KILL;
237: valid_flags = 0;
238: break;
239: case 'l':
240: if (mode) {
241: if (mode == MODE_LIST)
242: SET(flags, MODE_LONG_LIST);
243: else
244: usage_excl(1);
245: }
246: mode = MODE_LIST;
247: valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
248: break;
249: case 'n':
250: SET(flags, MODE_NONINTERACTIVE);
251: sudo_settings[ARG_NONINTERACTIVE].value = "true";
252: break;
253: case 'P':
254: sudo_settings[ARG_PRESERVE_GROUPS].value = "true";
255: break;
256: case 'p':
257: sudo_settings[ARG_PROMPT].value = optarg;
258: break;
259: #ifdef HAVE_SELINUX
260: case 'r':
261: sudo_settings[ARG_SELINUX_ROLE].value = optarg;
262: break;
263: case 't':
264: sudo_settings[ARG_SELINUX_TYPE].value = optarg;
265: break;
266: #endif
267: case 'S':
268: SET(tgetpass_flags, TGP_STDIN);
269: break;
270: case 's':
271: sudo_settings[ARG_USER_SHELL].value = "true";
272: SET(flags, MODE_SHELL);
273: break;
274: case 'U':
275: if ((getpwnam(optarg)) == NULL)
276: errorx(1, _("unknown user: %s"), optarg);
277: list_user = optarg;
278: break;
279: case 'u':
280: runas_user = optarg;
281: sudo_settings[ARG_RUNAS_USER].value = optarg;
282: break;
283: case 'v':
284: if (mode && mode != MODE_VALIDATE)
285: usage_excl(1);
286: mode = MODE_VALIDATE;
287: valid_flags = MODE_NONINTERACTIVE;
288: break;
289: case 'V':
290: if (mode && mode != MODE_VERSION)
291: usage_excl(1);
292: mode = MODE_VERSION;
293: valid_flags = 0;
294: break;
295: default:
296: usage(1);
297: }
298: } else if (!got_end_of_args && is_envar) {
299: if (nenv == env_size - 2) {
300: env_size *= 2;
301: env_add = erealloc3(env_add, env_size, sizeof(char *));
302: }
303: env_add[nenv++] = argv[optind];
304:
305: /* Crank optind and resume getopt. */
306: optind++;
307: } else {
308: /* Not an option or an environment variable -- we're done. */
309: break;
310: }
311: }
312: env_add[nenv] = NULL;
313:
314: argc -= optind;
315: argv += optind;
316:
317: if (!mode) {
318: /* Defer -k mode setting until we know whether it is a flag or not */
319: if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) {
1.1.1.2 misho 320: if (argc == 0 && !(flags & (MODE_SHELL|MODE_LOGIN_SHELL))) {
1.1 misho 321: mode = MODE_INVALIDATE; /* -k by itself */
322: sudo_settings[ARG_IGNORE_TICKET].value = NULL;
323: valid_flags = 0;
324: }
325: }
326: if (!mode)
327: mode = MODE_RUN; /* running a command */
328: }
329:
330: if (argc > 0 && mode == MODE_LIST)
331: mode = MODE_CHECK;
332:
333: if (ISSET(flags, MODE_LOGIN_SHELL)) {
334: if (ISSET(flags, MODE_SHELL)) {
335: warningx(_("you may not specify both the `-i' and `-s' options"));
336: usage(1);
337: }
338: if (ISSET(flags, MODE_PRESERVE_ENV)) {
339: warningx(_("you may not specify both the `-i' and `-E' options"));
340: usage(1);
341: }
342: SET(flags, MODE_SHELL);
343: }
344: if ((flags & valid_flags) != flags)
345: usage(1);
346: if (mode == MODE_EDIT &&
347: (ISSET(flags, MODE_PRESERVE_ENV) || env_add[0] != NULL)) {
348: if (ISSET(mode, MODE_PRESERVE_ENV))
349: warningx(_("the `-E' option is not valid in edit mode"));
350: if (env_add[0] != NULL)
351: warningx(_("you may not specify environment variables in edit mode"));
352: usage(1);
353: }
354: if ((runas_user != NULL || runas_group != NULL) &&
355: !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
356: usage(1);
357: }
358: if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
359: warningx(_("the `-U' option may only be used with the `-l' option"));
360: usage(1);
361: }
362: if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
363: warningx(_("the `-A' and `-S' options may not be used together"));
364: usage(1);
365: }
366: if ((argc == 0 && mode == MODE_EDIT) ||
367: (argc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
368: usage(1);
369: if (argc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) {
370: SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
371: sudo_settings[ARG_IMPLIED_SHELL].value = "true";
372: }
373:
374: if (mode == MODE_HELP)
375: help();
376:
377: /*
378: * For shell mode we need to rewrite argv
379: */
380: if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
1.1.1.3 ! misho 381: char **av, *cmnd = NULL;
! 382: int ac = 1;
1.1 misho 383:
1.1.1.3 ! misho 384: if (argc != 0) {
1.1 misho 385: /* shell -c "command" */
1.1.1.3 ! misho 386: char *src, *dst;
1.1 misho 387: size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
388: strlen(argv[argc - 1]) + 1;
389:
390: cmnd = dst = emalloc2(cmnd_size, 2);
391: for (av = argv; *av != NULL; av++) {
392: for (src = *av; *src != '\0'; src++) {
393: /* quote potential meta characters */
394: if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-')
395: *dst++ = '\\';
396: *dst++ = *src;
397: }
398: *dst++ = ' ';
399: }
400: if (cmnd != dst)
401: dst--; /* replace last space with a NUL */
402: *dst = '\0';
403:
1.1.1.3 ! misho 404: ac += 2; /* -c cmnd */
! 405: }
! 406:
! 407: av = emalloc2(ac + 1, sizeof(char *));
! 408: av[0] = (char *)user_details.shell; /* plugin may override shell */
! 409: if (cmnd != NULL) {
1.1 misho 410: av[1] = "-c";
411: av[2] = cmnd;
412: }
413: av[ac] = NULL;
414:
415: argv = av;
416: argc = ac;
417: }
418:
419: /*
420: * Format setting_pairs into settings array.
421: */
422: settings = emalloc2(NUM_SETTINGS + 1, sizeof(char *));
423: for (i = 0, j = 0; i < NUM_SETTINGS; i++) {
424: if (sudo_settings[i].value) {
1.1.1.2 misho 425: sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s=%s",
426: sudo_settings[i].name, sudo_settings[i].value);
1.1 misho 427: settings[j] = fmt_string(sudo_settings[i].name,
428: sudo_settings[i].value);
429: if (settings[j] == NULL)
430: errorx(1, _("unable to allocate memory"));
431: j++;
432: }
433: }
434: settings[j] = NULL;
435:
436: if (mode == MODE_EDIT) {
437: #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
438: /* Must have the command in argv[0]. */
439: argc++;
440: argv--;
441: argv[0] = "sudoedit";
442: #else
443: errorx(1, _("sudoedit is not supported on this platform"));
444: #endif
445: }
446:
447: *settingsp = settings;
448: *env_addp = env_add;
449: *nargc = argc;
450: *nargv = argv;
1.1.1.2 misho 451: debug_return_int(mode | flags);
1.1 misho 452: }
453:
454: static int
455: usage_err(const char *buf)
456: {
457: return fputs(buf, stderr);
458: }
459:
460: static int
461: usage_out(const char *buf)
462: {
463: return fputs(buf, stdout);
464: }
465:
466: /*
467: * Give usage message and exit.
468: * The actual usage strings are in sudo_usage.h for configure substitution.
469: */
470: void
471: usage(int fatal)
472: {
473: struct lbuf lbuf;
474: char *uvec[6];
475: int i, ulen;
476:
477: /*
478: * Use usage vectors appropriate to the progname.
479: */
480: if (strcmp(getprogname(), "sudoedit") == 0) {
481: uvec[0] = SUDO_USAGE5 + 3;
482: uvec[1] = NULL;
483: } else {
484: uvec[0] = SUDO_USAGE1;
485: uvec[1] = SUDO_USAGE2;
486: uvec[2] = SUDO_USAGE3;
487: uvec[3] = SUDO_USAGE4;
488: uvec[4] = SUDO_USAGE5;
489: uvec[5] = NULL;
490: }
491:
492: /*
493: * Print usage and wrap lines as needed, depending on the
494: * tty width.
495: */
496: ulen = (int)strlen(getprogname()) + 8;
497: lbuf_init(&lbuf, fatal ? usage_err : usage_out, ulen, NULL,
498: user_details.ts_cols);
499: for (i = 0; uvec[i] != NULL; i++) {
500: lbuf_append(&lbuf, "usage: %s%s", getprogname(), uvec[i]);
501: lbuf_print(&lbuf);
502: }
503: lbuf_destroy(&lbuf);
504: if (fatal)
505: exit(1);
506: }
507:
508: /*
509: * Tell which options are mutually exclusive and exit.
510: */
511: static void
512: usage_excl(int fatal)
513: {
1.1.1.2 misho 514: debug_decl(usage_excl, SUDO_DEBUG_ARGS)
515:
1.1 misho 516: warningx(_("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified"));
517: usage(fatal);
518: }
519:
520: static void
521: help(void)
522: {
523: struct lbuf lbuf;
524: int indent = 16;
525: const char *pname = getprogname();
1.1.1.2 misho 526: debug_decl(help, SUDO_DEBUG_ARGS)
1.1 misho 527:
528: lbuf_init(&lbuf, usage_out, indent, NULL, user_details.ts_cols);
529: if (strcmp(pname, "sudoedit") == 0)
530: lbuf_append(&lbuf, _("%s - edit files as another user\n\n"), pname);
531: else
532: lbuf_append(&lbuf, _("%s - execute a command as another user\n\n"), pname);
533: lbuf_print(&lbuf);
534:
535: usage(0);
536:
537: lbuf_append(&lbuf, _("\nOptions:\n"));
538: #ifdef HAVE_BSD_AUTH_H
539: lbuf_append(&lbuf, " -A %s",
540: _("use helper program for password prompting\n"));
541: #endif
542: lbuf_append(&lbuf, " -a type %s",
543: _("use specified BSD authentication type\n"));
544: lbuf_append(&lbuf, " -b %s",
545: _("run command in the background\n"));
546: lbuf_append(&lbuf, " -C fd %s",
547: _("close all file descriptors >= fd\n"));
548: #ifdef HAVE_LOGIN_CAP_H
549: lbuf_append(&lbuf, " -c class %s",
550: _("run command with specified login class\n"));
551: #endif
552: lbuf_append(&lbuf, " -E %s",
553: _("preserve user environment when executing command\n"));
554: lbuf_append(&lbuf, " -e %s",
555: _("edit files instead of running a command\n"));
556: lbuf_append(&lbuf, " -g group %s",
557: _("execute command as the specified group\n"));
558: lbuf_append(&lbuf, " -H %s",
559: _("set HOME variable to target user's home dir.\n"));
560: lbuf_append(&lbuf, " -h %s",
561: _("display help message and exit\n"));
562: lbuf_append(&lbuf, " -i [command] %s",
563: _("run a login shell as target user\n"));
564: lbuf_append(&lbuf, " -K %s",
565: _("remove timestamp file completely\n"));
566: lbuf_append(&lbuf, " -k %s",
567: _("invalidate timestamp file\n"));
568: lbuf_append(&lbuf, " -l[l] command %s",
569: _("list user's available commands\n"));
570: lbuf_append(&lbuf, " -n %s",
571: _("non-interactive mode, will not prompt user\n"));
572: lbuf_append(&lbuf, " -P %s",
573: _("preserve group vector instead of setting to target's\n"));
574: lbuf_append(&lbuf, " -p prompt %s",
575: _("use specified password prompt\n"));
576: #ifdef HAVE_SELINUX
577: lbuf_append(&lbuf, " -r role %s",
578: _("create SELinux security context with specified role\n"));
579: #endif
580: lbuf_append(&lbuf, " -S %s",
581: _("read password from standard input\n"));
582: lbuf_append(&lbuf,
583: " -s [command] %s", _("run a shell as target user\n"));
584: #ifdef HAVE_SELINUX
585: lbuf_append(&lbuf, " -t type %s",
586: _("create SELinux security context with specified role\n"));
587: #endif
588: lbuf_append(&lbuf, " -U user %s",
589: _("when listing, list specified user's privileges\n"));
590: lbuf_append(&lbuf, " -u user %s",
591: _("run command (or edit file) as specified user\n"));
592: lbuf_append(&lbuf, " -V %s",
593: _("display version information and exit\n"));
594: lbuf_append(&lbuf, " -v %s",
595: _("update user's timestamp without running a command\n"));
596: lbuf_append(&lbuf, " -- %s",
597: _("stop processing command line arguments\n"));
598: lbuf_print(&lbuf);
599: lbuf_destroy(&lbuf);
1.1.1.2 misho 600: sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 0);
1.1 misho 601: exit(0);
602: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>