clean up changelog and tag confusion in my checked out tree
[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                                             __attribute__((__noreturn__));
56
57 /*
58  * For sudo.c
59  */
60 extern int NewArgc;
61 extern char **NewArgv;
62 extern int user_closefrom;
63 extern char *runas_user;
64 extern char *runas_group;
65
66 /* For getopt(3) */
67 extern char *optarg;
68 extern int optind;
69
70 #ifdef HAVE_BSD_AUTH_H
71 char *login_style;
72 #endif /* HAVE_BSD_AUTH_H */
73
74 /*
75  * Command line argument parsing.
76  * Sets NewArgc and NewArgv which corresponds to the argc/argv we'll use
77  * for the command to be run (if we are running one).
78  */
79 int
80 parse_args(argc, argv)
81     int argc;
82     char **argv;
83 {
84     int mode = 0;               /* what mode is sudo to be run in? */
85     int flags = 0;              /* mode flags */
86     int valid_flags, ch;
87
88     /* First, check to see if we were invoked as "sudoedit". */
89     if (strcmp(getprogname(), "sudoedit") == 0)
90         mode = MODE_EDIT;
91
92     /* Returns true if the last option string was "--" */
93 #define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
94             argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
95
96     /* Returns true if next option is an environment variable */
97 #define is_envar (optind < argc && argv[optind][0] != '/' && \
98             strchr(argv[optind], '=') != NULL)
99
100     /* Flags allowed when running a command */
101     valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
102                   MODE_LOGIN_SHELL|MODE_INVALIDATE|MODE_NONINTERACTIVE|
103                   MODE_PRESERVE_GROUPS|MODE_SHELL;
104     for (;;) {
105         /*
106          * We disable arg permutation for GNU getopt().
107          * Some trickiness is required to allow environment variables
108          * to be interspersed with command line options.
109          */
110         if ((ch = getopt(argc, argv, "+Aa:bC:c:Eeg:HhiKkLlnPp:r:Sst:U:u:Vv")) != -1) {
111             switch (ch) {
112                 case 'A':
113                     SET(tgetpass_flags, TGP_ASKPASS);
114                     break;
115 #ifdef HAVE_BSD_AUTH_H
116                 case 'a':
117                     login_style = optarg;
118                     break;
119 #endif
120                 case 'b':
121                     SET(flags, MODE_BACKGROUND);
122                     break;
123                 case 'C':
124                     if ((user_closefrom = atoi(optarg)) < 3) {
125                         warningx("the argument to -C must be at least 3");
126                         usage(1);
127                     }
128                     break;
129 #ifdef HAVE_LOGIN_CAP_H
130                 case 'c':
131                     login_class = optarg;
132                     def_use_loginclass = TRUE;
133                     break;
134 #endif
135                 case 'E':
136                     SET(flags, MODE_PRESERVE_ENV);
137                     break;
138                 case 'e':
139                     if (mode && mode != MODE_EDIT)
140                         usage_excl(1);
141                     mode = MODE_EDIT;
142                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
143                     break;
144                 case 'g':
145                     runas_group = optarg;
146                     break;
147                 case 'H':
148                     SET(flags, MODE_RESET_HOME);
149                     break;
150                 case 'h':
151                     if (mode && mode != MODE_HELP) {
152                         if (strcmp(getprogname(), "sudoedit") != 0)
153                             usage_excl(1);
154                     }
155                     mode = MODE_HELP;
156                     valid_flags = 0;
157                     break;
158                 case 'i':
159                     SET(flags, MODE_LOGIN_SHELL);
160                     def_env_reset = TRUE;
161                     break;
162                 case 'k':
163                     SET(flags, MODE_INVALIDATE);
164                     break;
165                 case 'K':
166                     if (mode && mode != MODE_KILL)
167                         usage_excl(1);
168                     mode = MODE_KILL;
169                     valid_flags = 0;
170                     break;
171                 case 'L':
172                     if (mode && mode != MODE_LISTDEFS)
173                         usage_excl(1);
174                     mode = MODE_LISTDEFS;
175                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
176                     break;
177                 case 'l':
178                     if (mode) {
179                         if (mode == MODE_LIST)
180                             long_list = 1;
181                         else
182                             usage_excl(1);
183                     }
184                     mode = MODE_LIST;
185                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
186                     break;
187                 case 'n':
188                     SET(flags, MODE_NONINTERACTIVE);
189                     break;
190                 case 'P':
191                     SET(flags, MODE_PRESERVE_GROUPS);
192                     break;
193                 case 'p':
194                     user_prompt = optarg;
195                     def_passprompt_override = TRUE;
196                     break;
197 #ifdef HAVE_SELINUX
198                 case 'r':
199                     user_role = optarg;
200                     break;
201                 case 't':
202                     user_type = optarg;
203                     break;
204 #endif
205                 case 'S':
206                     SET(tgetpass_flags, TGP_STDIN);
207                     break;
208                 case 's':
209                     SET(flags, MODE_SHELL);
210                     break;
211                 case 'U':
212                     if ((list_pw = sudo_getpwnam(optarg)) == NULL)
213                         errorx(1, "unknown user: %s", optarg);
214                     break;
215                 case 'u':
216                     runas_user = optarg;
217                     break;
218                 case 'v':
219                     if (mode && mode != MODE_VALIDATE)
220                         usage_excl(1);
221                     mode = MODE_VALIDATE;
222                     valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
223                     break;
224                 case 'V':
225                     if (mode && mode != MODE_VERSION)
226                         usage_excl(1);
227                     mode = MODE_VERSION;
228                     valid_flags = 0;
229                     break;
230                 default:
231                     usage(1);
232             }
233         } else if (!got_end_of_args && is_envar) {
234             struct list_member *ev;
235
236             /* Store environment variable. */
237             ev = emalloc(sizeof(*ev));
238             ev->value = argv[optind];
239             ev->next = sudo_user.env_vars;
240             sudo_user.env_vars = ev;
241
242             /* Crank optind and resume getopt. */
243             optind++;
244         } else {
245             /* Not an option or an environment variable -- we're done. */
246             break;
247         }
248     }
249
250     NewArgc = argc - optind;
251     NewArgv = argv + optind;
252
253     if (!mode) {
254         /* Defer -k mode setting until we know whether it is a flag or not */
255         if (ISSET(flags, MODE_INVALIDATE) && NewArgc == 0) {
256             mode = MODE_INVALIDATE;     /* -k by itself */
257             CLR(flags, MODE_INVALIDATE);
258             valid_flags = 0;
259         } else {
260             mode = MODE_RUN;            /* running a command */
261         }
262     }
263
264     if (NewArgc > 0 && mode == MODE_LIST)
265         mode = MODE_CHECK;
266
267     if (ISSET(flags, MODE_LOGIN_SHELL)) {
268         if (ISSET(flags, MODE_SHELL)) {
269             warningx("you may not specify both the `-i' and `-s' options");
270             usage(1);
271         }
272         if (ISSET(flags, MODE_PRESERVE_ENV)) {
273             warningx("you may not specify both the `-i' and `-E' options");
274             usage(1);
275         }
276         SET(flags, MODE_SHELL);
277     }
278     if ((flags & valid_flags) != flags)
279         usage(1);
280     if (mode == MODE_EDIT &&
281        (ISSET(flags, MODE_PRESERVE_ENV) || sudo_user.env_vars != NULL)) {
282         if (ISSET(mode, MODE_PRESERVE_ENV))
283             warningx("the `-E' option is not valid in edit mode");
284         if (sudo_user.env_vars != NULL)
285             warningx("you may not specify environment variables in edit mode");
286         usage(1);
287     }
288     if ((runas_user != NULL || runas_group != NULL) &&
289         !ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
290         usage(1);
291     }
292     if (list_pw != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
293         warningx("the `-U' option may only be used with the `-l' option");
294         usage(1);
295     }
296     if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
297         warningx("the `-A' and `-S' options may not be used together");
298         usage(1);
299     }
300     if ((NewArgc == 0 && mode == MODE_EDIT) ||
301         (NewArgc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
302         usage(1);
303     if (NewArgc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL))
304         SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
305
306     return(mode | flags);
307 }
308
309 static int
310 usage_out(buf)
311     const char *buf;
312 {
313     return fputs(buf, stderr);
314 }
315
316 /*
317  * Give usage message and exit.
318  * The actual usage strings are in sudo_usage.h for configure substitution.
319  */
320 void
321 usage(exit_val)
322     int exit_val;
323 {
324     struct lbuf lbuf;
325     char *uvec[6];
326     int i, ulen;
327
328     /*
329      * Use usage vectors appropriate to the progname.
330      */
331     if (strcmp(getprogname(), "sudoedit") == 0) {
332         uvec[0] = SUDO_USAGE5 + 3;
333         uvec[1] = NULL;
334     } else {
335         uvec[0] = SUDO_USAGE1;
336         uvec[1] = SUDO_USAGE2;
337         uvec[2] = SUDO_USAGE3;
338         uvec[3] = SUDO_USAGE4;
339         uvec[4] = SUDO_USAGE5;
340         uvec[5] = NULL;
341     }
342
343     /*
344      * Print usage and wrap lines as needed, depending on the
345      * tty width.
346      */
347     ulen = (int)strlen(getprogname()) + 8;
348     lbuf_init(&lbuf, usage_out, ulen, NULL);
349     for (i = 0; uvec[i] != NULL; i++) {
350         lbuf_append(&lbuf, "usage: ", getprogname(), uvec[i], NULL);
351         lbuf_print(&lbuf);
352     }
353     lbuf_destroy(&lbuf);
354     exit(exit_val);
355 }
356
357 /*
358  * Tell which options are mutually exclusive and exit.
359  */
360 static void
361 usage_excl(exit_val)
362     int exit_val;
363 {
364     warningx("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified");
365     usage(exit_val);
366 }