Imported Upstream version 1.7.6p1
[debian/sudo] / testsudoers.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2010
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23
24 #define _SUDO_MAIN
25
26 #include <config.h>
27
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/socket.h>
32 #include <stdio.h>
33 #ifdef STDC_HEADERS
34 # include <stdlib.h>
35 # include <stddef.h>
36 #else
37 # ifdef HAVE_STDLIB_H
38 #  include <stdlib.h>
39 # endif
40 #endif /* STDC_HEADERS */
41 #ifdef HAVE_STRING_H
42 # include <string.h>
43 #endif /* HAVE_STRING_H */
44 #ifdef HAVE_STRINGS_H
45 # include <strings.h>
46 #endif /* HAVE_STRINGS_H */
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif /* HAVE_UNISTD_H */
50 #ifdef HAVE_FNMATCH
51 # include <fnmatch.h>
52 #endif /* HAVE_FNMATCH */
53 #ifdef HAVE_NETGROUP_H
54 # include <netgroup.h>
55 #endif /* HAVE_NETGROUP_H */
56 #include <ctype.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 #include <netdb.h>
62
63 #include "tsgetgrpw.h"
64 #include "sudo.h"
65 #include "interfaces.h"
66 #include "parse.h"
67 #include <gram.h>
68
69 #ifndef HAVE_FNMATCH
70 # include "emul/fnmatch.h"
71 #endif /* HAVE_FNMATCH */
72
73 /*
74  * Globals
75  */
76 int  Argc, NewArgc;
77 char **Argv, **NewArgv;
78 int num_interfaces;
79 struct interface *interfaces;
80 struct sudo_user sudo_user;
81 struct passwd *list_pw;
82 extern int parse_error;
83
84 /* For getopt(3) */
85 extern char *optarg;
86 extern int optind;
87
88 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
89 extern char *malloc_options;
90 #endif
91 #ifdef YYDEBUG
92 extern int yydebug;
93 #endif
94
95 int  print_alias __P((void *, void *));
96 void dump_sudoers __P((void));
97 void print_defaults __P((void));
98 void print_privilege __P((struct privilege *));
99 void print_userspecs __P((void));
100 void usage __P((void)) __attribute__((__noreturn__));
101 void set_runasgr __P((char *));
102 void set_runaspw __P((char *));
103
104 int
105 main(argc, argv)
106     int argc;
107     char **argv;
108 {
109     struct cmndspec *cs;
110     struct privilege *priv;
111     struct userspec *us;
112     char *p, *grfile, *pwfile, *runas_group, *runas_user;
113     char hbuf[MAXHOSTNAMELEN + 1];
114     int match, host_match, runas_match, cmnd_match;
115     int ch, dflag;
116
117 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
118     malloc_options = "AFGJPR";
119 #endif
120 #ifdef YYDEBUG
121     yydebug = 1;
122 #endif
123
124     Argv = argv;
125     Argc = argc;
126
127     dflag = 0;
128     grfile = pwfile = runas_group = runas_user = NULL;
129     while ((ch = getopt(argc, argv, "dg:G:h:p:u:")) != -1) {
130         switch (ch) {
131             case 'd':
132                 dflag = 1;
133                 break;
134             case 'h':
135                 user_host = optarg;
136                 break;
137             case 'G':
138                 grfile = optarg;
139                 break;
140             case 'g':
141                 runas_group = optarg;
142                 break;
143             case 'p':
144                 pwfile = optarg;
145                 break;
146             case 'u':
147                 runas_user = optarg;
148                 break;
149             default:
150                 usage();
151                 break;
152         }
153     }
154     argc -= optind;
155     argv += optind;
156     NewArgc = argc;
157     NewArgv = argv;
158
159     /* Set group/passwd file and init the cache. */
160     if (grfile)
161         setgrfile(grfile);
162     if (pwfile)
163         setpwfile(pwfile);
164     sudo_setpwent();
165     sudo_setgrent();
166
167     if (argc < 2) {
168         if (!dflag)
169             usage();
170         if ((sudo_user.pw = sudo_getpwnam("root")) == NULL)
171             errorx(1, "no passwd entry for root!");
172         user_cmnd = user_base = "true";
173     } else {
174         if ((sudo_user.pw = sudo_getpwnam(*argv)) == NULL)
175             errorx(1, "no passwd entry for %s!", *argv);
176         user_cmnd = *++argv;
177         if ((p = strrchr(user_cmnd, '/')) != NULL)
178             user_base = p + 1;
179         else
180             user_base = user_cmnd;
181         NewArgc -= 2;
182     }
183
184     if (user_host == NULL) {
185         if (gethostname(hbuf, sizeof(hbuf)) != 0)
186             error(1, "gethostname");
187         hbuf[sizeof(hbuf) - 1] = '\0';
188         user_host = hbuf;
189     }
190     if ((p = strchr(user_host, '.'))) {
191         *p = '\0';
192         user_shost = estrdup(user_host);
193         *p = '.';
194     } else {
195         user_shost = user_host;
196     }
197
198     /* Fill in user_args from NewArgv. */
199     if (NewArgc > 0) {
200         char *to, **from;
201         size_t size, n;
202
203         for (size = 0, from = NewArgv + 1; *from; from++)
204             size += strlen(*from) + 1;
205
206         user_args = (char *) emalloc(size);
207         for (to = user_args, from = NewArgv + 1; *from; from++) {
208             n = strlcpy(to, *from, size - (to - user_args));
209             if (n >= size - (to - user_args))
210                     errorx(1, "internal error, init_vars() overflow");
211             to += n;
212             *to++ = ' ';
213         }
214         *--to = '\0';
215     }
216
217     /* Initialize default values. */
218     init_defaults();
219
220     /* Load ip addr/mask for each interface. */
221     load_interfaces();
222
223     /* Allocate space for data structures in the parser. */
224     init_parser("sudoers", 0);
225
226     if (yyparse() != 0 || parse_error) {
227         parse_error = TRUE;
228         (void) fputs("Does not parse", stdout);
229     } else {
230         (void) fputs("Parses OK", stdout);
231     }
232
233     if (!update_defaults(SETDEF_ALL))
234         (void) fputs(" (problem with defaults entries)", stdout);
235     puts(".");
236
237     /*
238      * Set runas passwd/group entries based on command line or sudoers.
239      * Note that if runas_group was specified without runas_user we
240      * defer setting runas_pw so the match routines know to ignore it.
241      */
242     if (runas_group != NULL) {
243         set_runasgr(runas_group);
244         if (runas_user != NULL)
245             set_runaspw(runas_user);
246     } else
247         set_runaspw(runas_user ? runas_user : def_runas_default);
248
249     if (dflag) {
250         (void) putchar('\n');
251         dump_sudoers();
252         if (argc < 2)
253             exit(parse_error ? 1 : 0);
254     }
255
256     /* This loop must match the one in sudo_file_lookup() */
257     printf("\nEntries for user %s:\n", user_name);
258     match = UNSPEC;
259     tq_foreach_rev(&userspecs, us) {
260         if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
261             continue;
262         tq_foreach_rev(&us->privileges, priv) {
263             putchar('\n');
264             print_privilege(priv); /* XXX */
265             putchar('\n');
266             host_match = hostlist_matches(&priv->hostlist);
267             if (host_match == ALLOW) {
268                 puts("\thost  matched");
269                 tq_foreach_rev(&priv->cmndlist, cs) {
270                     runas_match = runaslist_matches(&cs->runasuserlist,
271                         &cs->runasgrouplist);
272                     if (runas_match == ALLOW) {
273                         puts("\trunas matched");
274                         cmnd_match = cmnd_matches(cs->cmnd);
275                         if (cmnd_match != UNSPEC)
276                             match = cmnd_match;
277                         printf("\tcmnd  %s\n", match == ALLOW ? "allowed" :
278                             match == DENY ? "denied" : "unmatched");
279                     }
280                 }
281             } else
282                 puts("\thost  unmatched");
283         }
284     }
285     printf("\nCommand %s\n", match == ALLOW ? "allowed" :
286         match == DENY ? "denied" : "unmatched");
287
288     /*
289      * Exit codes:
290      *  0 - parsed OK and command matched.
291      *  1 - parse error
292      *  2 - command not matched
293      *  3 - command denied
294      */
295     if (parse_error)
296         exit(1);
297     exit(match == ALLOW ? 0 : match + 3);
298 }
299
300 void
301 set_runaspw(user)
302     char *user;
303 {
304     if (*user == '#') {
305         if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL)
306             runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0);
307     } else {
308         if ((runas_pw = sudo_getpwnam(user)) == NULL)
309             errorx(1, "unknown user: %s", user);
310     }
311 }
312
313 void
314 set_runasgr(group)
315     char *group;
316 {
317     if (*group == '#') {
318         if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL)
319             runas_gr = sudo_fakegrnam(group);
320     } else {
321         if ((runas_gr = sudo_getgrnam(group)) == NULL)
322             errorx(1, "unknown group: %s", group);
323     }
324 }
325
326 void
327 sudo_setspent()
328 {
329     return;
330 }
331
332 void
333 sudo_endspent()
334 {
335     return;
336 }
337
338 char *
339 sudo_getepw(pw)
340     const struct passwd *pw;
341 {
342     return pw->pw_passwd;
343 }
344
345 void
346 set_fqdn()
347 {
348     return;
349 }
350
351 FILE *
352 open_sudoers(path, isdir, keepopen)
353     const char *path;
354     int isdir;
355     int *keepopen;
356 {
357     return fopen(path, "r");
358 }
359
360 void
361 init_envtables()
362 {
363     return;
364 }
365
366 int
367 set_perms(perm)
368     int perm;
369 {
370     return 1;
371 }
372
373 void
374 cleanup(gotsignal)
375     int gotsignal;
376 {
377     if (!gotsignal) {
378         sudo_endpwent();
379         sudo_endgrent();
380     }
381 }
382
383 void
384 print_member(m)    
385     struct member *m;
386 {
387     struct sudo_command *c;
388
389     if (m->negated)
390         putchar('!');
391     if (m->name == NULL)
392         fputs("ALL", stdout);
393     else if (m->type != COMMAND)
394         fputs(m->name, stdout);
395     else {
396         c = (struct sudo_command *) m->name;
397         printf("%s%s%s", c->cmnd, c->args ? " " : "",
398             c->args ? c->args : "");
399     }
400 }
401
402 void
403 print_defaults()
404 {
405     struct defaults *d;
406     struct member *m;
407
408     tq_foreach_fwd(&defaults, d) {
409         (void) fputs("Defaults", stdout);
410         switch (d->type) {
411             case DEFAULTS_HOST:
412                 putchar('@');
413                 break;
414             case DEFAULTS_USER:
415                 putchar(':');
416                 break;
417             case DEFAULTS_RUNAS:
418                 putchar('>');
419                 break;
420             case DEFAULTS_CMND:
421                 putchar('!');
422                 break;
423         }
424         tq_foreach_fwd(&d->binding, m) {
425             if (m != tq_first(&d->binding))
426                 putchar(',');
427             print_member(m);
428         }
429         printf("\t%s%s", d->op == FALSE ? "!" : "", d->var);
430         if (d->val != NULL) {
431             printf("%c%s", d->op == TRUE ? '=' : d->op, d->val);
432         }
433         putchar('\n');
434     }
435 }
436
437 int
438 print_alias(v1, v2)
439     void *v1, *v2;
440 {
441     struct alias *a = (struct alias *)v1;
442     struct member *m;
443     struct sudo_command *c;
444
445     switch (a->type) {
446         case HOSTALIAS:
447             (void) printf("Host_Alias\t%s = ", a->name);
448             break;
449         case CMNDALIAS:
450             (void) printf("Cmnd_Alias\t%s = ", a->name);
451             break;
452         case USERALIAS:
453             (void) printf("User_Alias\t%s = ", a->name);
454             break;
455         case RUNASALIAS:
456             (void) printf("Runas_Alias\t%s = ", a->name);
457             break;
458     }
459     tq_foreach_fwd(&a->members, m) {
460         if (m != tq_first(&a->members))
461             fputs(", ", stdout);
462         if (m->type == COMMAND) {
463             c = (struct sudo_command *) m->name;
464             printf("%s%s%s", c->cmnd, c->args ? " " : "",
465                 c->args ? c->args : "");
466         } else if (m->type == ALL) {
467             fputs("ALL", stdout);
468         } else {
469             fputs(m->name, stdout);
470         }
471     }
472     putchar('\n');
473     return 0;
474 }
475
476 void
477 print_privilege(priv)
478     struct privilege *priv;
479 {
480     struct cmndspec *cs;
481     struct member *m;
482     struct privilege *p;
483     struct cmndtag tags;
484
485     for (p = priv; p != NULL; p = p->next) {
486         if (p != priv)
487             fputs(" : ", stdout);
488         tq_foreach_fwd(&p->hostlist, m) {
489             if (m != tq_first(&p->hostlist))
490                 fputs(", ", stdout);
491             print_member(m);
492         }
493         fputs(" = ", stdout);
494         tags.nopasswd = tags.noexec = UNSPEC;
495         tq_foreach_fwd(&p->cmndlist, cs) {
496             if (cs != tq_first(&p->cmndlist))
497                 fputs(", ", stdout);
498             if (!tq_empty(&cs->runasuserlist) || !tq_empty(&cs->runasgrouplist)) {
499                 fputs("(", stdout);
500                 if (!tq_empty(&cs->runasuserlist)) {
501                     tq_foreach_fwd(&cs->runasuserlist, m) {
502                         if (m != tq_first(&cs->runasuserlist))
503                             fputs(", ", stdout);
504                         print_member(m);
505                     }  
506                 } else if (tq_empty(&cs->runasgrouplist)) {
507                     fputs(def_runas_default, stdout);
508                 } else {
509                     fputs(sudo_user.pw->pw_name, stdout);
510                 }
511                 if (!tq_empty(&cs->runasgrouplist)) {
512                     fputs(" : ", stdout);
513                     tq_foreach_fwd(&cs->runasgrouplist, m) {
514                         if (m != tq_first(&cs->runasgrouplist))
515                             fputs(", ", stdout);
516                         print_member(m);
517                     }
518                 }
519                 fputs(") ", stdout);
520             }
521 #ifdef HAVE_SELINUX
522             if (cs->role)
523                 printf("ROLE=%s ", cs->role);
524             if (cs->type)
525                 printf("TYPE=%s ", cs->type);
526 #endif /* HAVE_SELINUX */
527             if (cs->tags.nopasswd != UNSPEC && cs->tags.nopasswd != tags.nopasswd)
528                 printf("%sPASSWD: ", cs->tags.nopasswd ? "NO" : "");
529             if (cs->tags.noexec != UNSPEC && cs->tags.noexec != tags.noexec)
530                 printf("%sEXEC: ", cs->tags.noexec ? "NO" : "");
531             print_member(cs->cmnd);
532             memcpy(&tags, &cs->tags, sizeof(tags));
533         }
534     }
535 }
536
537 void
538 print_userspecs()
539 {
540     struct member *m;
541     struct userspec *us;
542
543     tq_foreach_fwd(&userspecs, us) {
544         tq_foreach_fwd(&us->users, m) {
545             if (m != tq_first(&us->users))
546                 fputs(", ", stdout);
547             print_member(m);
548         }
549         putchar('\t');
550         print_privilege(us->privileges.first); /* XXX */
551         putchar('\n');
552     }
553 }
554
555 void
556 dump_sudoers()
557 {
558     print_defaults();
559
560     putchar('\n');
561     alias_apply(print_alias, NULL);
562
563     putchar('\n');
564     print_userspecs();
565 }
566
567 void
568 usage()
569 {
570     (void) fprintf(stderr, "usage: %s [-d] [-G grfile] [-g group] [-h host] [-p pwfile] [-u user] <user> <command> [args]\n", getprogname());
571     exit(1);
572 }