Imported Upstream version 1.7.6p1
[debian/sudo] / parse_args.c
1 /*
2  * Copyright (c) 1993-1996, 1998-2010 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 <pwd.h>
45 #include <grp.h>
46
47 #include "sudo.h"
48 #include "lbuf.h"
49 #include <sudo_usage.h>
50
51 /*
52  * Local functions
53  */
54 static void usage_excl                  __P((int));
55
56 /*
57  * For sudo.c
58  */
59 extern int NewArgc;
60 extern char **NewArgv;
61 extern int user_closefrom;
62 extern char *runas_user;
63 extern char *runas_group;
64
65 /* For getopt(3) */
66 extern char *optarg;
67 extern int optind;
68
69 #ifdef HAVE_BSD_AUTH_H
70 char *login_style;
71 #endif /* HAVE_BSD_AUTH_H */
72
73 /*
74  * Command line argument parsing.
75  * Sets NewArgc and NewArgv which corresponds to the argc/argv we'll use
76  * for the command to be run (if we are running one).
77  */
78 int
79 parse_args(argc, argv)
80     int argc;
81     char **argv;
82 {
83     int mode = 0;               /* what mode is sudo to be run in? */
84     int flags = 0;              /* mode flags */
85     int valid_flags, ch;
86
87     /* First, check to see if we were invoked as "sudoedit". */
88     if (strcmp(getprogname(), "sudoedit") == 0)
89         mode = MODE_EDIT;
90
91     /* Returns true if the last option string was "--" */
92 #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
93             argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
94
95     /* Returns true if next option is an environment variable */
96 #define is_envar (optind < argc && argv[optind][0] != '/' && \
97             strchr(argv[optind], '=') != NULL)
98
99     /* Flags allowed when running a command */
100     valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
101                   MODE_LOGIN_SHELL|MODE_INVALIDATE|MODE_NONINTERACTIVE|
102                   MODE_PRESERVE_GROUPS|MODE_SHELL;
103     for (;;) {
104         /*
105          * We disable arg permutation for GNU getopt().
106          * Some trickiness is required to allow environment variables
107          * to be interspersed with command line options.
108          */
109         if ((ch = getopt(argc, argv, "+Aa:bC:c:Eeg:HhiKkLlnPp:r:Sst:U:u:Vv")) != -1) {
110             switch (ch) {
111                 case 'A':
112                     SET(tgetpass_flags, TGP_ASKPASS);
113                     break;
114 #ifdef HAVE_BSD_AUTH_H
115                 case 'a':
116                     login_style = optarg;
117                     break;
118 #endif
119                 case 'b':
120                     SET(flags, MODE_BACKGROUND);
121                     break;
122                 case 'C':
123                     if ((user_closefrom = atoi(optarg)) < 3) {
124                         warningx("the argument to -C must be a number greater than or equal to 3");
125                         usage(1);
126                     }
127                     break;
128 #ifdef HAVE_LOGIN_CAP_H
129                 case 'c':
130                     login_class = optarg;
131                     def_use_loginclass = TRUE;
132                     break;
133 #endif
134                 case 'E':
135                     SET(flags, MODE_PRESERVE_ENV);
136                     break;
137                 case 'e':
138                     if (mode && mode != MODE_EDIT)
139                         usage_excl(1);
140                     mode = MODE_EDIT;
141                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
142                     break;
143                 case 'g':
144                     runas_group = optarg;
145                     break;
146                 case 'H':
147                     SET(flags, MODE_RESET_HOME);
148                     break;
149                 case 'h':
150                     if (mode && mode != MODE_HELP) {
151                         if (strcmp(getprogname(), "sudoedit") != 0)
152                             usage_excl(1);
153                     }
154                     mode = MODE_HELP;
155                     valid_flags = 0;
156                     break;
157                 case 'i':
158                     SET(flags, MODE_LOGIN_SHELL);
159                     def_env_reset = TRUE;
160                     break;
161                 case 'k':
162                     SET(flags, MODE_INVALIDATE);
163                     break;
164                 case 'K':
165                     if (mode && mode != MODE_KILL)
166                         usage_excl(1);
167                     mode = MODE_KILL;
168                     valid_flags = 0;
169                     break;
170                 case 'L':
171                     if (mode && mode != MODE_LISTDEFS)
172                         usage_excl(1);
173                     mode = MODE_LISTDEFS;
174                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
175                     break;
176                 case 'l':
177                     if (mode) {
178                         if (mode == MODE_LIST)
179                             long_list = 1;
180                         else
181                             usage_excl(1);
182                     }
183                     mode = MODE_LIST;
184                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
185                     break;
186                 case 'n':
187                     SET(flags, MODE_NONINTERACTIVE);
188                     break;
189                 case 'P':
190                     SET(flags, MODE_PRESERVE_GROUPS);
191                     break;
192                 case 'p':
193                     user_prompt = optarg;
194                     def_passprompt_override = TRUE;
195                     break;
196 #ifdef HAVE_SELINUX
197                 case 'r':
198                     user_role = optarg;
199                     break;
200                 case 't':
201                     user_type = optarg;
202                     break;
203 #endif
204                 case 'S':
205                     SET(tgetpass_flags, TGP_STDIN);
206                     break;
207                 case 's':
208                     SET(flags, MODE_SHELL);
209                     break;
210                 case 'U':
211                     if ((list_pw = sudo_getpwnam(optarg)) == NULL)
212                         errorx(1, "unknown user: %s", optarg);
213                     break;
214                 case 'u':
215                     runas_user = optarg;
216                     break;
217                 case 'v':
218                     if (mode && mode != MODE_VALIDATE)
219                         usage_excl(1);
220                     mode = MODE_VALIDATE;
221                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
222                     break;
223                 case 'V':
224                     if (mode && mode != MODE_VERSION)
225                         usage_excl(1);
226                     mode = MODE_VERSION;
227                     valid_flags = 0;
228                     break;
229                 default:
230                     usage(1);
231             }
232         } else if (!got_end_of_args && is_envar) {
233             struct list_member *ev;
234
235             /* Store environment variable. */
236             ev = emalloc(sizeof(*ev));
237             ev->value = argv[optind];
238             ev->next = sudo_user.env_vars;
239             sudo_user.env_vars = ev;
240
241             /* Crank optind and resume getopt. */
242             optind++;
243         } else {
244             /* Not an option or an environment variable -- we're done. */
245             break;
246         }
247     }
248
249     NewArgc = argc - optind;
250     NewArgv = argv + optind;
251
252     if (!mode) {
253         /* Defer -k mode setting until we know whether it is a flag or not */
254         if (ISSET(flags, MODE_INVALIDATE) && NewArgc == 0) {
255             mode = MODE_INVALIDATE;     /* -k by itself */
256             CLR(flags, MODE_INVALIDATE);
257             valid_flags = 0;
258         } else {
259             mode = MODE_RUN;            /* running a command */
260         }
261     }
262
263     if (NewArgc > 0 && mode == MODE_LIST)
264         mode = MODE_CHECK;
265
266     if (ISSET(flags, MODE_LOGIN_SHELL)) {
267         if (ISSET(flags, MODE_SHELL)) {
268             warningx("you may not specify both the `-i' and `-s' options");
269             usage(1);
270         }
271         if (ISSET(flags, MODE_PRESERVE_ENV)) {
272             warningx("you may not specify both the `-i' and `-E' options");
273             usage(1);
274         }
275         SET(flags, MODE_SHELL);
276     }
277     if ((flags & valid_flags) != flags)
278         usage(1);
279     if (mode == MODE_EDIT &&
280        (ISSET(flags, MODE_PRESERVE_ENV) || sudo_user.env_vars != NULL)) {
281         if (ISSET(mode, MODE_PRESERVE_ENV))
282             warningx("the `-E' option is not valid in edit mode");
283         if (sudo_user.env_vars != NULL)
284             warningx("you may not specify environment variables in edit mode");
285         usage(1);
286     }
287     if ((runas_user != NULL || runas_group != NULL) &&
288         !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
289         usage(1);
290     }
291     if (list_pw != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
292         warningx("the `-U' option may only be used with the `-l' option");
293         usage(1);
294     }
295     if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
296         warningx("the `-A' and `-S' options may not be used together");
297         usage(1);
298     }
299     if ((NewArgc == 0 && mode == MODE_EDIT) ||
300         (NewArgc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
301         usage(1);
302     if (NewArgc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL))
303         SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
304
305     return mode | flags;
306 }
307
308 static int
309 usage_err(buf)
310     const char *buf;
311 {
312     return fputs(buf, stderr);
313 }
314
315 static int
316 usage_out(buf)
317     const char *buf;
318 {
319     return fputs(buf, stdout);
320 }
321
322 /*
323  * Give usage message and exit.
324  * The actual usage strings are in sudo_usage.h for configure substitution.
325  */
326 void
327 usage(fatal)
328     int fatal;
329 {
330     struct lbuf lbuf;
331     char *uvec[6];
332     int i, ulen;
333
334     /*
335      * Use usage vectors appropriate to the progname.
336      */
337     if (strcmp(getprogname(), "sudoedit") == 0) {
338         uvec[0] = SUDO_USAGE5 + 3;
339         uvec[1] = NULL;
340     } else {
341         uvec[0] = SUDO_USAGE1;
342         uvec[1] = SUDO_USAGE2;
343         uvec[2] = SUDO_USAGE3;
344         uvec[3] = SUDO_USAGE4;
345         uvec[4] = SUDO_USAGE5;
346         uvec[5] = NULL;
347     }
348
349     /*
350      * Print usage and wrap lines as needed, depending on the
351      * tty width.
352      */
353     ulen = (int)strlen(getprogname()) + 8;
354     lbuf_init(&lbuf, fatal ? usage_err : usage_out, ulen, NULL);
355     for (i = 0; uvec[i] != NULL; i++) {
356         lbuf_append(&lbuf, "usage: ", getprogname(), uvec[i], NULL);
357         lbuf_print(&lbuf);
358     }
359     lbuf_destroy(&lbuf);
360     if (fatal)
361         exit(1);
362 }
363
364 /*
365  * Tell which options are mutually exclusive and exit.
366  */
367 static void
368 usage_excl(fatal)
369     int fatal;
370 {
371     warningx("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified");
372     usage(fatal);
373 }
374
375 void
376 help()
377 {
378     struct lbuf lbuf;
379     int indent = 16;
380     const char *pname = getprogname();
381
382     lbuf_init(&lbuf, usage_out, indent, NULL);
383     if (strcmp(pname, "sudoedit") == 0)
384         lbuf_append(&lbuf, pname,  " - edit files as another user\n\n", NULL);
385     else
386         lbuf_append(&lbuf, pname,  " - execute a command as another user\n\n", NULL);
387     lbuf_print(&lbuf);
388
389     usage(0);
390
391     lbuf_append(&lbuf, "\nOptions:\n", NULL);
392 #ifdef HAVE_BSD_AUTH_H
393     lbuf_append(&lbuf,
394         "  -A            use helper program for password prompting\n", NULL);
395 #endif
396     lbuf_append(&lbuf,
397         "  -a type       use specified BSD authentication type\n", NULL);
398     lbuf_append(&lbuf,
399         "  -b            run command in the background\n", NULL);
400     lbuf_append(&lbuf,
401         "  -C fd         close all file descriptors >= fd\n", NULL);
402 #ifdef HAVE_LOGIN_CAP_H
403     lbuf_append(&lbuf,
404         "  -c class      run command with specified login class\n", NULL);
405 #endif
406     lbuf_append(&lbuf,
407         "  -E            preserve user environment when executing command\n",
408         NULL);
409     lbuf_append(&lbuf,
410         "  -e            edit files instead of running a command\n", NULL);
411     lbuf_append(&lbuf,
412         "  -g group      execute command as the specified group\n", NULL);
413     lbuf_append(&lbuf,
414         "  -H            set HOME variable to target user's home dir.\n",
415         NULL);
416     lbuf_append(&lbuf,
417         "  -h            display help message and exit\n", NULL);
418     lbuf_append(&lbuf,
419         "  -i [command]  run a login shell as target user\n", NULL);
420     lbuf_append(&lbuf,
421         "  -K            remove timestamp file completely\n", NULL);
422     lbuf_append(&lbuf,
423         "  -k            invalidate timestamp file\n", NULL);
424     lbuf_append(&lbuf,
425         "  -L            list supported sudoers Defaults values\n", NULL);
426     lbuf_append(&lbuf,
427         "  -l[l] command list user's available commands\n", NULL);
428     lbuf_append(&lbuf,
429         "  -n            non-interactive mode, will not prompt user\n", NULL);
430     lbuf_append(&lbuf,
431         "  -P            preserve group vector instead of setting to target's\n",
432         NULL);
433     lbuf_append(&lbuf,
434         "  -p prompt     use specified password prompt\n", NULL);
435 #ifdef HAVE_SELINUX
436     lbuf_append(&lbuf,
437         "  -r role       create SELinux security context with specified role\n",
438         NULL);
439 #endif
440     lbuf_append(&lbuf,
441         "  -S            read password from standard input\n", NULL);
442     lbuf_append(&lbuf,
443         "  -s [command]  run a shell as target user\n", NULL);
444 #ifdef HAVE_SELINUX
445     lbuf_append(&lbuf,
446         "  -t type       create SELinux security context with specified role\n",
447         NULL);
448 #endif
449     lbuf_append(&lbuf,
450         "  -U user       when listing, list specified user's privileges\n",
451         NULL);
452     lbuf_append(&lbuf,
453         "  -u user       run command (or edit file) as specified user\n", NULL);
454     lbuf_append(&lbuf,
455         "  -V            display version information and exit\n", NULL);
456     lbuf_append(&lbuf,
457         "  -v            update user's timestamp without running a command\n",
458         NULL);
459     lbuf_append(&lbuf,
460         "  --            stop processing command line arguments\n", NULL);
461     lbuf_print(&lbuf);
462     lbuf_destroy(&lbuf);
463     exit(0);
464 }