| /* |
| * Copyright (c) 1996, 1998-2005, 2007-2010 |
| * Todd C. Miller <Todd.Miller@courtesan.com> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Sponsored in part by the Defense Advanced Research Projects |
| * Agency (DARPA) and Air Force Research Laboratory, Air Force |
| * Materiel Command, USAF, under agreement number F39502-99-1-0512. |
| */ |
| |
| #define _SUDO_MAIN |
| |
| #include <config.h> |
| |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <stdio.h> |
| #ifdef STDC_HEADERS |
| # include <stdlib.h> |
| # include <stddef.h> |
| #else |
| # ifdef HAVE_STDLIB_H |
| # include <stdlib.h> |
| # endif |
| #endif /* STDC_HEADERS */ |
| #ifdef HAVE_STRING_H |
| # include <string.h> |
| #endif /* HAVE_STRING_H */ |
| #ifdef HAVE_STRINGS_H |
| # include <strings.h> |
| #endif /* HAVE_STRINGS_H */ |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif /* HAVE_UNISTD_H */ |
| #ifdef HAVE_FNMATCH |
| # include <fnmatch.h> |
| #endif /* HAVE_FNMATCH */ |
| #ifdef HAVE_NETGROUP_H |
| # include <netgroup.h> |
| #endif /* HAVE_NETGROUP_H */ |
| #include <ctype.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| |
| #include "sudo.h" |
| #include "interfaces.h" |
| #include "parse.h" |
| #include <gram.h> |
| |
| #ifndef HAVE_FNMATCH |
| # include "emul/fnmatch.h" |
| #endif /* HAVE_FNMATCH */ |
| |
| /* |
| * Globals |
| */ |
| int Argc, NewArgc; |
| char **Argv, **NewArgv; |
| int num_interfaces; |
| struct interface *interfaces; |
| struct sudo_user sudo_user; |
| struct passwd *list_pw; |
| extern int parse_error; |
| |
| /* For getopt(3) */ |
| extern char *optarg; |
| extern int optind; |
| |
| #if defined(SUDO_DEVEL) && defined(__OpenBSD__) |
| extern char *malloc_options; |
| #endif |
| #ifdef YYDEBUG |
| extern int yydebug; |
| #endif |
| |
| int print_alias __P((void *, void *)); |
| void dump_sudoers __P((void)); |
| void print_defaults __P((void)); |
| void print_privilege __P((struct privilege *)); |
| void print_userspecs __P((void)); |
| void usage __P((void)) __attribute__((__noreturn__)); |
| void set_runasgr __P((char *)); |
| void set_runaspw __P((char *)); |
| |
| extern void setgrfile __P((const char *)); |
| extern void setgrent __P((void)); |
| extern void endgrent __P((void)); |
| extern struct group *getgrent __P((void)); |
| extern struct group *getgrnam __P((const char *)); |
| extern struct group *getgrgid __P((gid_t)); |
| extern void setpwfile __P((const char *)); |
| extern void setpwent __P((void)); |
| extern void endpwent __P((void)); |
| extern struct passwd *getpwent __P((void)); |
| extern struct passwd *getpwnam __P((const char *)); |
| extern struct passwd *getpwuid __P((uid_t)); |
| |
| int |
| main(argc, argv) |
| int argc; |
| char **argv; |
| { |
| struct cmndspec *cs; |
| struct privilege *priv; |
| struct userspec *us; |
| char *p, *grfile, *pwfile, *runas_group, *runas_user; |
| char hbuf[MAXHOSTNAMELEN + 1]; |
| int ch, dflag, rval, matched; |
| |
| #if defined(SUDO_DEVEL) && defined(__OpenBSD__) |
| malloc_options = "AFGJPR"; |
| #endif |
| #ifdef YYDEBUG |
| yydebug = 1; |
| #endif |
| |
| Argv = argv; |
| Argc = argc; |
| |
| dflag = 0; |
| grfile = pwfile = runas_group = runas_user = NULL; |
| while ((ch = getopt(argc, argv, "dg:G:h:p:u:")) != -1) { |
| switch (ch) { |
| case 'd': |
| dflag = 1; |
| break; |
| case 'h': |
| user_host = optarg; |
| break; |
| case 'G': |
| grfile = optarg; |
| break; |
| case 'g': |
| runas_group = optarg; |
| break; |
| case 'p': |
| pwfile = optarg; |
| break; |
| case 'u': |
| runas_user = optarg; |
| break; |
| default: |
| usage(); |
| break; |
| } |
| } |
| argc -= optind; |
| argv += optind; |
| NewArgc = argc; |
| NewArgv = argv; |
| |
| /* Set group/passwd file and init the cache. */ |
| if (grfile) |
| setgrfile(grfile); |
| if (pwfile) |
| setpwfile(pwfile); |
| sudo_setpwent(); |
| sudo_setgrent(); |
| |
| if (argc < 2) { |
| if (!dflag) |
| usage(); |
| if ((sudo_user.pw = sudo_getpwnam("nobody")) == NULL) |
| errorx(1, "no passwd entry for nobody!"); |
| user_cmnd = user_base = "true"; |
| } else { |
| if ((sudo_user.pw = sudo_getpwnam(*argv)) == NULL) |
| errorx(1, "no passwd entry for %s!", *argv); |
| user_cmnd = *++argv; |
| if ((p = strrchr(user_cmnd, '/')) != NULL) |
| user_base = p + 1; |
| else |
| user_base = user_cmnd; |
| NewArgc -= 2; |
| } |
| |
| if (user_host == NULL) { |
| if (gethostname(hbuf, sizeof(hbuf)) != 0) |
| error(1, "gethostname"); |
| hbuf[sizeof(hbuf) - 1] = '\0'; |
| user_host = hbuf; |
| } |
| if ((p = strchr(user_host, '.'))) { |
| *p = '\0'; |
| user_shost = estrdup(user_host); |
| *p = '.'; |
| } else { |
| user_shost = user_host; |
| } |
| |
| /* Fill in user_args from NewArgv. */ |
| if (NewArgc > 1) { |
| char *to, **from; |
| size_t size, n; |
| |
| for (size = 0, from = NewArgv + 1; *from; from++) |
| size += strlen(*from) + 1; |
| |
| user_args = (char *) emalloc(size); |
| for (to = user_args, from = NewArgv + 1; *from; from++) { |
| n = strlcpy(to, *from, size - (to - user_args)); |
| if (n >= size - (to - user_args)) |
| errorx(1, "internal error, init_vars() overflow"); |
| to += n; |
| *to++ = ' '; |
| } |
| *--to = '\0'; |
| } |
| |
| /* Initialize default values. */ |
| init_defaults(); |
| |
| /* Load ip addr/mask for each interface. */ |
| load_interfaces(); |
| |
| /* Allocate space for data structures in the parser. */ |
| init_parser("sudoers", 0); |
| |
| if (yyparse() != 0 || parse_error) |
| (void) fputs("Does not parse", stdout); |
| else |
| (void) fputs("Parses OK", stdout); |
| |
| if (!update_defaults(SETDEF_ALL)) |
| (void) fputs(" (problem with defaults entries)", stdout); |
| puts("."); |
| |
| /* |
| * Set runas passwd/group entries based on command line or sudoers. |
| * Note that if runas_group was specified without runas_user we |
| * defer setting runas_pw so the match routines know to ignore it. |
| */ |
| if (runas_group != NULL) { |
| set_runasgr(runas_group); |
| if (runas_user != NULL) |
| set_runaspw(runas_user); |
| } else |
| set_runaspw(runas_user ? runas_user : def_runas_default); |
| |
| if (dflag) { |
| (void) putchar('\n'); |
| dump_sudoers(); |
| if (argc < 2) |
| exit(0); |
| } |
| |
| /* This loop must match the one in sudoers_lookup() */ |
| printf("\nEntries for user %s:\n", user_name); |
| matched = UNSPEC; |
| tq_foreach_rev(&userspecs, us) { |
| if (userlist_matches(sudo_user.pw, &us->users) != ALLOW) |
| continue; |
| tq_foreach_rev(&us->privileges, priv) { |
| putchar('\n'); |
| print_privilege(priv); /* XXX */ |
| putchar('\n'); |
| if (hostlist_matches(&priv->hostlist) == ALLOW) { |
| puts("\thost matched"); |
| tq_foreach_rev(&priv->cmndlist, cs) { |
| if (runaslist_matches(&cs->runasuserlist, |
| &cs->runasgrouplist) == ALLOW) { |
| puts("\trunas matched"); |
| rval = cmnd_matches(cs->cmnd); |
| if (rval != UNSPEC) |
| matched = rval; |
| printf("\tcmnd %s\n", rval == ALLOW ? "allowed" : |
| rval == DENY ? "denied" : "unmatched"); |
| } |
| } |
| } else |
| puts("\thost unmatched"); |
| } |
| } |
| printf("\nCommand %s\n", matched == ALLOW ? "allowed" : |
| matched == DENY ? "denied" : "unmatched"); |
| |
| exit(0); |
| } |
| |
| void |
| set_runaspw(user) |
| char *user; |
| { |
| if (*user == '#') { |
| if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL) |
| runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0); |
| } else { |
| if ((runas_pw = sudo_getpwnam(user)) == NULL) |
| errorx(1, "unknown user: %s", user); |
| } |
| } |
| |
| void |
| set_runasgr(group) |
| char *group; |
| { |
| if (*group == '#') { |
| if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL) |
| runas_gr = sudo_fakegrnam(group); |
| } else { |
| if ((runas_gr = sudo_getgrnam(group)) == NULL) |
| errorx(1, "unknown group: %s", group); |
| } |
| } |
| |
| void |
| sudo_setspent() |
| { |
| return; |
| } |
| |
| void |
| sudo_endspent() |
| { |
| return; |
| } |
| |
| char * |
| sudo_getepw(pw) |
| const struct passwd *pw; |
| { |
| return (pw->pw_passwd); |
| } |
| |
| void |
| set_fqdn() |
| { |
| return; |
| } |
| |
| FILE * |
| open_sudoers(path, isdir, keepopen) |
| const char *path; |
| int isdir; |
| int *keepopen; |
| { |
| return(fopen(path, "r")); |
| } |
| |
| void |
| init_envtables() |
| { |
| return; |
| } |
| |
| int |
| set_perms(perm) |
| int perm; |
| { |
| return(1); |
| } |
| |
| void |
| cleanup(gotsignal) |
| int gotsignal; |
| { |
| if (!gotsignal) { |
| sudo_endpwent(); |
| sudo_endgrent(); |
| } |
| } |
| |
| void |
| print_member(m) |
| struct member *m; |
| { |
| struct sudo_command *c; |
| |
| if (m->negated) |
| putchar('!'); |
| if (m->name == NULL) |
| fputs("ALL", stdout); |
| else if (m->type != COMMAND) |
| fputs(m->name, stdout); |
| else { |
| c = (struct sudo_command *) m->name; |
| printf("%s%s%s", c->cmnd, c->args ? " " : "", |
| c->args ? c->args : ""); |
| } |
| } |
| |
| void |
| print_defaults() |
| { |
| struct defaults *d; |
| struct member *m; |
| |
| tq_foreach_fwd(&defaults, d) { |
| (void) fputs("Defaults", stdout); |
| switch (d->type) { |
| case DEFAULTS_HOST: |
| putchar('@'); |
| break; |
| case DEFAULTS_USER: |
| putchar(':'); |
| break; |
| case DEFAULTS_RUNAS: |
| putchar('>'); |
| break; |
| case DEFAULTS_CMND: |
| putchar('!'); |
| break; |
| } |
| tq_foreach_fwd(&d->binding, m) { |
| if (m != tq_first(&d->binding)) |
| putchar(','); |
| print_member(m); |
| } |
| printf("\t%s%s", d->op == FALSE ? "!" : "", d->var); |
| if (d->val != NULL) { |
| printf("%c%s", d->op == TRUE ? '=' : d->op, d->val); |
| } |
| putchar('\n'); |
| } |
| } |
| |
| int |
| print_alias(v1, v2) |
| void *v1, *v2; |
| { |
| struct alias *a = (struct alias *)v1; |
| struct member *m; |
| struct sudo_command *c; |
| |
| switch (a->type) { |
| case HOSTALIAS: |
| (void) printf("Host_Alias\t%s = ", a->name); |
| break; |
| case CMNDALIAS: |
| (void) printf("Cmnd_Alias\t%s = ", a->name); |
| break; |
| case USERALIAS: |
| (void) printf("User_Alias\t%s = ", a->name); |
| break; |
| case RUNASALIAS: |
| (void) printf("Runas_Alias\t%s = ", a->name); |
| break; |
| } |
| tq_foreach_fwd(&a->members, m) { |
| if (m != tq_first(&a->members)) |
| fputs(", ", stdout); |
| if (m->type == COMMAND) { |
| c = (struct sudo_command *) m->name; |
| printf("%s%s%s", c->cmnd, c->args ? " " : "", |
| c->args ? c->args : ""); |
| } else |
| fputs(m->name, stdout); |
| } |
| putchar('\n'); |
| return(0); |
| } |
| |
| void |
| print_privilege(priv) |
| struct privilege *priv; |
| { |
| struct cmndspec *cs; |
| struct member *m; |
| struct privilege *p; |
| struct cmndtag tags; |
| |
| for (p = priv; p != NULL; p = p->next) { |
| if (p != priv) |
| fputs(" : ", stdout); |
| tq_foreach_fwd(&p->hostlist, m) { |
| if (m != tq_first(&p->hostlist)) |
| fputs(", ", stdout); |
| print_member(m); |
| } |
| fputs(" = ", stdout); |
| tags.nopasswd = tags.noexec = UNSPEC; |
| tq_foreach_fwd(&p->cmndlist, cs) { |
| if (cs != tq_first(&p->cmndlist)) |
| fputs(", ", stdout); |
| /* XXX - runasgrouplist too */ |
| if (!tq_empty(&cs->runasuserlist)) { |
| fputs("(", stdout); |
| tq_foreach_fwd(&cs->runasuserlist, m) { |
| if (m != tq_first(&cs->runasuserlist)) |
| fputs(", ", stdout); |
| print_member(m); |
| } |
| fputs(") ", stdout); |
| } |
| #ifdef HAVE_SELINUX |
| if (cs->role) |
| printf("ROLE=%s ", cs->role); |
| if (cs->type) |
| printf("TYPE=%s ", cs->type); |
| #endif /* HAVE_SELINUX */ |
| if (cs->tags.nopasswd != UNSPEC && cs->tags.nopasswd != tags.nopasswd) |
| printf("%sPASSWD: ", cs->tags.nopasswd ? "NO" : ""); |
| if (cs->tags.noexec != UNSPEC && cs->tags.noexec != tags.noexec) |
| printf("%sEXEC: ", cs->tags.noexec ? "NO" : ""); |
| print_member(cs->cmnd); |
| memcpy(&tags, &cs->tags, sizeof(tags)); |
| } |
| } |
| } |
| |
| void |
| print_userspecs() |
| { |
| struct member *m; |
| struct userspec *us; |
| |
| tq_foreach_fwd(&userspecs, us) { |
| tq_foreach_fwd(&us->users, m) { |
| if (m != tq_first(&us->users)) |
| fputs(", ", stdout); |
| print_member(m); |
| } |
| putchar('\t'); |
| print_privilege(us->privileges.first); /* XXX */ |
| putchar('\n'); |
| } |
| } |
| |
| void |
| dump_sudoers() |
| { |
| print_defaults(); |
| |
| putchar('\n'); |
| alias_apply(print_alias, NULL); |
| |
| putchar('\n'); |
| print_userspecs(); |
| } |
| |
| void |
| usage() |
| { |
| (void) fprintf(stderr, "usage: %s [-d] [-G grfile] [-g group] [-h host] [-p pwfile] [-u user] <user> <command> [args]\n", getprogname()); |
| exit(1); |
| } |