| /* |
| * Copyright (c) 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. |
| */ |
| |
| #include <config.h> |
| |
| #include <sys/types.h> |
| #include <sys/param.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 */ |
| #include <pwd.h> |
| #include <grp.h> |
| #include <ctype.h> |
| |
| #include "sudo.h" |
| #include "lbuf.h" |
| |
| extern struct sudo_nss sudo_nss_file; |
| #ifdef HAVE_LDAP |
| extern struct sudo_nss sudo_nss_ldap; |
| #endif |
| |
| #if defined(HAVE_LDAP) && defined(_PATH_NSSWITCH_CONF) |
| /* |
| * Read in /etc/nsswitch.conf |
| * Returns a tail queue of matches. |
| */ |
| struct sudo_nss_list * |
| sudo_read_nss() |
| { |
| FILE *fp; |
| char *cp; |
| int saw_files = FALSE; |
| int saw_ldap = FALSE; |
| int got_match = FALSE; |
| static struct sudo_nss_list snl; |
| |
| if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL) |
| goto nomatch; |
| |
| while ((cp = sudo_parseln(fp)) != NULL) { |
| /* Skip blank or comment lines */ |
| if (*cp == '\0') |
| continue; |
| |
| /* Look for a line starting with "sudoers:" */ |
| if (strncasecmp(cp, "sudoers:", 8) != 0) |
| continue; |
| |
| /* Parse line */ |
| for ((cp = strtok(cp + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) { |
| if (strcasecmp(cp, "files") == 0 && !saw_files) { |
| tq_append(&snl, &sudo_nss_file); |
| got_match = TRUE; |
| } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) { |
| tq_append(&snl, &sudo_nss_ldap); |
| got_match = TRUE; |
| } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) { |
| /* NOTFOUND affects the most recent entry */ |
| tq_last(&snl)->ret_if_notfound = TRUE; |
| got_match = FALSE; |
| } else |
| got_match = FALSE; |
| } |
| /* Only parse the first "sudoers:" line */ |
| break; |
| } |
| fclose(fp); |
| |
| nomatch: |
| /* Default to files only if no matches */ |
| if (tq_empty(&snl)) |
| tq_append(&snl, &sudo_nss_file); |
| |
| return(&snl); |
| } |
| |
| #else /* HAVE_LDAP && _PATH_NSSWITCH_CONF */ |
| |
| # if defined(HAVE_LDAP) && defined(_PATH_NETSVC_CONF) |
| |
| /* |
| * Read in /etc/netsvc.conf (like nsswitch.conf on AIX) |
| * Returns a tail queue of matches. |
| */ |
| struct sudo_nss_list * |
| sudo_read_nss() |
| { |
| FILE *fp; |
| char *cp, *ep; |
| int saw_files = FALSE; |
| int saw_ldap = FALSE; |
| int got_match = FALSE; |
| static struct sudo_nss_list snl; |
| |
| if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL) |
| goto nomatch; |
| |
| while ((cp = sudo_parseln(fp)) != NULL) { |
| /* Skip blank or comment lines */ |
| if (*cp == '\0') |
| continue; |
| |
| /* Look for a line starting with "sudoers = " */ |
| if (strncasecmp(cp, "sudoers", 7) != 0) |
| continue; |
| cp += 7; |
| while (isspace((unsigned char)*cp)) |
| cp++; |
| if (*cp++ != '=') |
| continue; |
| |
| /* Parse line */ |
| for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) { |
| /* Trim leading whitespace. */ |
| while (isspace((unsigned char)*cp)) |
| cp++; |
| |
| if (!saw_files && strncasecmp(cp, "files", 5) == 0 && |
| (isspace((unsigned char)cp[5]) || cp[5] == '\0')) { |
| tq_append(&snl, &sudo_nss_file); |
| got_match = TRUE; |
| ep = &cp[5]; |
| } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 && |
| (isspace((unsigned char)cp[4]) || cp[4] == '\0')) { |
| tq_append(&snl, &sudo_nss_ldap); |
| got_match = TRUE; |
| ep = &cp[4]; |
| } else { |
| got_match = FALSE; |
| } |
| |
| /* check for = auth qualifier */ |
| if (got_match && *ep) { |
| cp = ep; |
| while (isspace((unsigned char)*cp) || *cp == '=') |
| cp++; |
| if (strncasecmp(cp, "auth", 4) == 0 && |
| (isspace((unsigned char)cp[4]) || cp[4] == '\0')) { |
| tq_last(&snl)->ret_if_found = TRUE; |
| } |
| } |
| } |
| /* Only parse the first "sudoers" line */ |
| break; |
| } |
| fclose(fp); |
| |
| nomatch: |
| /* Default to files only if no matches */ |
| if (tq_empty(&snl)) |
| tq_append(&snl, &sudo_nss_file); |
| |
| return(&snl); |
| } |
| |
| # else /* !_PATH_NETSVC_CONF && !_PATH_NSSWITCH_CONF */ |
| |
| /* |
| * Non-nsswitch.conf version with hard-coded order. |
| */ |
| struct sudo_nss_list * |
| sudo_read_nss() |
| { |
| static struct sudo_nss_list snl; |
| |
| # ifdef HAVE_LDAP |
| tq_append(&snl, &sudo_nss_ldap); |
| # endif |
| tq_append(&snl, &sudo_nss_file); |
| |
| return(&snl); |
| } |
| |
| # endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */ |
| |
| #endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */ |
| |
| /* Reset user_groups based on passwd entry. */ |
| static void |
| reset_groups(pw) |
| struct passwd *pw; |
| { |
| #if defined(HAVE_INITGROUPS) && defined(HAVE_GETGROUPS) |
| if (pw != sudo_user.pw) { |
| # ifdef HAVE_SETAUTHDB |
| aix_setauthdb(pw->pw_name); |
| # endif |
| (void) initgroups(pw->pw_name, pw->pw_gid); |
| efree(user_groups); |
| user_groups = NULL; |
| if ((user_ngroups = getgroups(0, NULL)) > 0) { |
| user_groups = emalloc2(user_ngroups, sizeof(GETGROUPS_T)); |
| if (getgroups(user_ngroups, user_groups) < 0) |
| log_error(USE_ERRNO|MSG_ONLY, "can't get group vector"); |
| } |
| # ifdef HAVE_SETAUTHDB |
| aix_restoreauthdb(); |
| # endif |
| } |
| #endif /* HAVE_INITGROUPS && HAVE_GETGROUPS */ |
| } |
| |
| static int |
| output(buf) |
| const char *buf; |
| { |
| return fputs(buf, stdout); |
| } |
| |
| /* |
| * Print out privileges for the specified user. |
| * We only get here if the user is allowed to run something on this host. |
| */ |
| void |
| display_privs(snl, pw) |
| struct sudo_nss_list *snl; |
| struct passwd *pw; |
| { |
| struct sudo_nss *nss; |
| struct lbuf lbuf; |
| int count; |
| |
| /* Reset group vector so group matching works correctly. */ |
| reset_groups(pw); |
| |
| lbuf_init(&lbuf, output, 4, NULL); |
| |
| /* Display defaults from all sources. */ |
| lbuf_append(&lbuf, "Matching Defaults entries for ", pw->pw_name, |
| " on this host:\n", NULL); |
| count = 0; |
| tq_foreach_fwd(snl, nss) { |
| count += nss->display_defaults(nss, pw, &lbuf); |
| } |
| if (count) { |
| lbuf_append(&lbuf, "\n\n", NULL); |
| lbuf_print(&lbuf); |
| } |
| |
| /* Display Runas and Cmnd-specific defaults from all sources. */ |
| lbuf.len = 0; |
| lbuf_append(&lbuf, "Runas and Command-specific defaults for ", pw->pw_name, |
| ":\n", NULL); |
| count = 0; |
| tq_foreach_fwd(snl, nss) { |
| count += nss->display_bound_defaults(nss, pw, &lbuf); |
| } |
| if (count) { |
| lbuf_append(&lbuf, "\n\n", NULL); |
| lbuf_print(&lbuf); |
| } |
| |
| /* Display privileges from all sources. */ |
| lbuf.len = 0; |
| lbuf_append(&lbuf, "User ", pw->pw_name, |
| " may run the following commands on this host:\n", NULL); |
| count = 0; |
| tq_foreach_fwd(snl, nss) { |
| count += nss->display_privs(nss, pw, &lbuf); |
| } |
| if (count) { |
| lbuf_print(&lbuf); |
| } |
| |
| lbuf_destroy(&lbuf); |
| } |
| |
| /* |
| * Check user_cmnd against sudoers and print the matching entry if the |
| * command is allowed. |
| */ |
| int |
| display_cmnd(snl, pw) |
| struct sudo_nss_list *snl; |
| struct passwd *pw; |
| { |
| struct sudo_nss *nss; |
| |
| /* Reset group vector so group matching works correctly. */ |
| reset_groups(pw); |
| |
| tq_foreach_fwd(snl, nss) { |
| if (nss->display_cmnd(nss, pw) == 0) |
| return(0); |
| } |
| return(1); |
| } |