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