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 */
76int  Argc, NewArgc;
77char **Argv, **NewArgv;
78int num_interfaces;
79struct interface *interfaces;
80struct sudo_user sudo_user;
81struct passwd *list_pw;
82extern int parse_error;
83
84/* For getopt(3) */
85extern char *optarg;
86extern int optind;
87
88#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
89extern char *malloc_options;
90#endif
91#ifdef YYDEBUG
92extern int yydebug;
93#endif
94
95int  print_alias __P((void *, void *));
96void dump_sudoers __P((void));
97void print_defaults __P((void));
98void print_privilege __P((struct privilege *));
99void print_userspecs __P((void));
100void usage __P((void)) __attribute__((__noreturn__));
101void set_runasgr __P((char *));
102void set_runaspw __P((char *));
103
104int
105main(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:P: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		runas_group = optarg;
139		break;
140	    case 'P':
141		grfile = 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
300void
301set_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
313void
314set_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
326void
327sudo_setspent()
328{
329    return;
330}
331
332void
333sudo_endspent()
334{
335    return;
336}
337
338char *
339sudo_getepw(pw)
340    const struct passwd *pw;
341{
342    return pw->pw_passwd;
343}
344
345void
346set_fqdn()
347{
348    return;
349}
350
351FILE *
352open_sudoers(path, isdir, keepopen)
353    const char *path;
354    int isdir;
355    int *keepopen;
356{
357    return fopen(path, "r");
358}
359
360void
361init_envtables()
362{
363    return;
364}
365
366int
367set_perms(perm)
368    int perm;
369{
370    return 1;
371}
372
373void
374cleanup(gotsignal)
375    int gotsignal;
376{
377    if (!gotsignal) {
378	sudo_endpwent();
379	sudo_endgrent();
380    }
381}
382
383void
384print_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
402void
403print_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
437int
438print_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
476void
477print_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
537void
538print_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
555void
556dump_sudoers()
557{
558    print_defaults();
559
560    putchar('\n');
561    alias_apply(print_alias, NULL);
562
563    putchar('\n');
564    print_userspecs();
565}
566
567void
568usage()
569{
570    (void) fprintf(stderr, "usage: %s [-d] [-g group] [-h host] [-P grfile] [-p pwfile] [-u user] <user> <command> [args]\n", getprogname());
571    exit(1);
572}
573