Annotation of embedaddon/sudo/src/parse_args.c, revision 1.1.1.2
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)) {
381: char **av;
382: int ac;
383:
384: if (argc == 0) {
385: /* just the shell */
386: ac = argc + 1;
387: av = emalloc2(ac + 1, sizeof(char *));
388: memcpy(av + 1, argv, argc * sizeof(char *));
389: } else {
390: /* shell -c "command" */
391: char *cmnd, *src, *dst;
392: size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
393: strlen(argv[argc - 1]) + 1;
394:
395: cmnd = dst = emalloc2(cmnd_size, 2);
396: for (av = argv; *av != NULL; av++) {
397: for (src = *av; *src != '\0'; src++) {
398: /* quote potential meta characters */
399: if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-')
400: *dst++ = '\\';
401: *dst++ = *src;
402: }
403: *dst++ = ' ';
404: }
405: if (cmnd != dst)
406: dst--; /* replace last space with a NUL */
407: *dst = '\0';
408:
409: ac = 3;
410: av = emalloc2(ac + 1, sizeof(char *));
411: av[1] = "-c";
412: av[2] = cmnd;
413: }
414: av[0] = (char *)user_details.shell; /* plugin may override shell */
415: av[ac] = NULL;
416:
417: argv = av;
418: argc = ac;
419: }
420:
421: /*
422: * Format setting_pairs into settings array.
423: */
424: settings = emalloc2(NUM_SETTINGS + 1, sizeof(char *));
425: for (i = 0, j = 0; i < NUM_SETTINGS; i++) {
426: if (sudo_settings[i].value) {
1.1.1.2 ! misho 427: sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s=%s",
! 428: sudo_settings[i].name, sudo_settings[i].value);
1.1 misho 429: settings[j] = fmt_string(sudo_settings[i].name,
430: sudo_settings[i].value);
431: if (settings[j] == NULL)
432: errorx(1, _("unable to allocate memory"));
433: j++;
434: }
435: }
436: settings[j] = NULL;
437:
438: if (mode == MODE_EDIT) {
439: #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
440: /* Must have the command in argv[0]. */
441: argc++;
442: argv--;
443: argv[0] = "sudoedit";
444: #else
445: errorx(1, _("sudoedit is not supported on this platform"));
446: #endif
447: }
448:
449: *settingsp = settings;
450: *env_addp = env_add;
451: *nargc = argc;
452: *nargv = argv;
1.1.1.2 ! misho 453: debug_return_int(mode | flags);
1.1 misho 454: }
455:
456: static int
457: usage_err(const char *buf)
458: {
459: return fputs(buf, stderr);
460: }
461:
462: static int
463: usage_out(const char *buf)
464: {
465: return fputs(buf, stdout);
466: }
467:
468: /*
469: * Give usage message and exit.
470: * The actual usage strings are in sudo_usage.h for configure substitution.
471: */
472: void
473: usage(int fatal)
474: {
475: struct lbuf lbuf;
476: char *uvec[6];
477: int i, ulen;
478:
479: /*
480: * Use usage vectors appropriate to the progname.
481: */
482: if (strcmp(getprogname(), "sudoedit") == 0) {
483: uvec[0] = SUDO_USAGE5 + 3;
484: uvec[1] = NULL;
485: } else {
486: uvec[0] = SUDO_USAGE1;
487: uvec[1] = SUDO_USAGE2;
488: uvec[2] = SUDO_USAGE3;
489: uvec[3] = SUDO_USAGE4;
490: uvec[4] = SUDO_USAGE5;
491: uvec[5] = NULL;
492: }
493:
494: /*
495: * Print usage and wrap lines as needed, depending on the
496: * tty width.
497: */
498: ulen = (int)strlen(getprogname()) + 8;
499: lbuf_init(&lbuf, fatal ? usage_err : usage_out, ulen, NULL,
500: user_details.ts_cols);
501: for (i = 0; uvec[i] != NULL; i++) {
502: lbuf_append(&lbuf, "usage: %s%s", getprogname(), uvec[i]);
503: lbuf_print(&lbuf);
504: }
505: lbuf_destroy(&lbuf);
506: if (fatal)
507: exit(1);
508: }
509:
510: /*
511: * Tell which options are mutually exclusive and exit.
512: */
513: static void
514: usage_excl(int fatal)
515: {
1.1.1.2 ! misho 516: debug_decl(usage_excl, SUDO_DEBUG_ARGS)
! 517:
1.1 misho 518: warningx(_("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified"));
519: usage(fatal);
520: }
521:
522: static void
523: help(void)
524: {
525: struct lbuf lbuf;
526: int indent = 16;
527: const char *pname = getprogname();
1.1.1.2 ! misho 528: debug_decl(help, SUDO_DEBUG_ARGS)
1.1 misho 529:
530: lbuf_init(&lbuf, usage_out, indent, NULL, user_details.ts_cols);
531: if (strcmp(pname, "sudoedit") == 0)
532: lbuf_append(&lbuf, _("%s - edit files as another user\n\n"), pname);
533: else
534: lbuf_append(&lbuf, _("%s - execute a command as another user\n\n"), pname);
535: lbuf_print(&lbuf);
536:
537: usage(0);
538:
539: lbuf_append(&lbuf, _("\nOptions:\n"));
540: #ifdef HAVE_BSD_AUTH_H
541: lbuf_append(&lbuf, " -A %s",
542: _("use helper program for password prompting\n"));
543: #endif
544: lbuf_append(&lbuf, " -a type %s",
545: _("use specified BSD authentication type\n"));
546: lbuf_append(&lbuf, " -b %s",
547: _("run command in the background\n"));
548: lbuf_append(&lbuf, " -C fd %s",
549: _("close all file descriptors >= fd\n"));
550: #ifdef HAVE_LOGIN_CAP_H
551: lbuf_append(&lbuf, " -c class %s",
552: _("run command with specified login class\n"));
553: #endif
554: lbuf_append(&lbuf, " -E %s",
555: _("preserve user environment when executing command\n"));
556: lbuf_append(&lbuf, " -e %s",
557: _("edit files instead of running a command\n"));
558: lbuf_append(&lbuf, " -g group %s",
559: _("execute command as the specified group\n"));
560: lbuf_append(&lbuf, " -H %s",
561: _("set HOME variable to target user's home dir.\n"));
562: lbuf_append(&lbuf, " -h %s",
563: _("display help message and exit\n"));
564: lbuf_append(&lbuf, " -i [command] %s",
565: _("run a login shell as target user\n"));
566: lbuf_append(&lbuf, " -K %s",
567: _("remove timestamp file completely\n"));
568: lbuf_append(&lbuf, " -k %s",
569: _("invalidate timestamp file\n"));
570: lbuf_append(&lbuf, " -l[l] command %s",
571: _("list user's available commands\n"));
572: lbuf_append(&lbuf, " -n %s",
573: _("non-interactive mode, will not prompt user\n"));
574: lbuf_append(&lbuf, " -P %s",
575: _("preserve group vector instead of setting to target's\n"));
576: lbuf_append(&lbuf, " -p prompt %s",
577: _("use specified password prompt\n"));
578: #ifdef HAVE_SELINUX
579: lbuf_append(&lbuf, " -r role %s",
580: _("create SELinux security context with specified role\n"));
581: #endif
582: lbuf_append(&lbuf, " -S %s",
583: _("read password from standard input\n"));
584: lbuf_append(&lbuf,
585: " -s [command] %s", _("run a shell as target user\n"));
586: #ifdef HAVE_SELINUX
587: lbuf_append(&lbuf, " -t type %s",
588: _("create SELinux security context with specified role\n"));
589: #endif
590: lbuf_append(&lbuf, " -U user %s",
591: _("when listing, list specified user's privileges\n"));
592: lbuf_append(&lbuf, " -u user %s",
593: _("run command (or edit file) as specified user\n"));
594: lbuf_append(&lbuf, " -V %s",
595: _("display version information and exit\n"));
596: lbuf_append(&lbuf, " -v %s",
597: _("update user's timestamp without running a command\n"));
598: lbuf_append(&lbuf, " -- %s",
599: _("stop processing command line arguments\n"));
600: lbuf_print(&lbuf);
601: lbuf_destroy(&lbuf);
1.1.1.2 ! misho 602: sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 0);
1.1 misho 603: exit(0);
604: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>