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