From: Todd C. Miller Date: Mon, 14 May 2018 15:05:03 +0000 (-0600) Subject: Simplify the nss interface such that each sudoers provider fills X-Git-Tag: SUDO_1_8_24^2~87 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f9be3a48a221560671bd3dc6425f3aac348329b4;p=sudo Simplify the nss interface such that each sudoers provider fills in a per-nss list of userspecs and defaults instead of using separate lookup and list functions. This makes it possible to have a single implementation of the code for sudoers lookup and listing. --- diff --git a/MANIFEST b/MANIFEST index 5fe9eaa3e..88a6d9b20 100644 --- a/MANIFEST +++ b/MANIFEST @@ -278,6 +278,7 @@ plugins/sudoers/digestname.c plugins/sudoers/editor.c plugins/sudoers/env.c plugins/sudoers/env_pattern.c +plugins/sudoers/file.c plugins/sudoers/filedigest.c plugins/sudoers/filedigest_gcrypt.c plugins/sudoers/filedigest_openssl.c diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 15ca1f9e7..030e3cb81 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -154,11 +154,11 @@ LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo digestname.lo \ timestr.lo toke.lo toke_util.lo SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo \ - env_pattern.lo find_path.lo fmtsudoers.lo gc.lo goodpath.lo \ - group_plugin.lo interfaces.lo iolog.lo iolog_path.lo locale.lo \ - logging.lo logwrap.lo mkdir_parents.lo parse.lo policy.lo \ - prompt.lo set_perms.lo starttime.lo sudo_nss.lo sudoers.lo \ - timestamp.lo @SUDOERS_OBJS@ + env_pattern.lo file.lo find_path.lo fmtsudoers.lo gc.lo \ + goodpath.lo group_plugin.lo interfaces.lo iolog.lo \ + iolog_path.lo locale.lo logging.lo logwrap.lo mkdir_parents.lo \ + parse.lo policy.lo prompt.lo set_perms.lo starttime.lo \ + sudo_nss.lo sudoers.lo timestamp.lo @SUDOERS_OBJS@ VISUDO_OBJS = editor.o find_path.o goodpath.o locale.o stubs.o sudo_printf.o visudo.o @@ -1111,6 +1111,15 @@ pam.lo: $(authdir)/pam.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ $(top_builddir)/pathnames.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(authdir)/pam.c +file.lo: $(srcdir)/file.c $(devdir)/def_data.h $(devdir)/gram.h \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ + $(incdir)/sudo_gettext.h $(incdir)/sudo_lbuf.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/file.c parse.lo: $(srcdir)/parse.c $(devdir)/def_data.h $(devdir)/gram.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index bc5c1fc78..d4715f72f 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -725,11 +725,11 @@ default_binding_matches(struct defaults *d, int what) } /* - * Update the defaults based on what was set by sudoers. + * Update the global defaults based on the given defaults list. * Pass in an OR'd list of which default types to update. */ bool -update_defaults(int what, bool quiet) +update_defaults(struct defaults_list *defs, int what, bool quiet) { struct defaults *d; bool ret = true; @@ -741,7 +741,7 @@ update_defaults(int what, bool quiet) /* * First apply Defaults values marked as early. */ - TAILQ_FOREACH(d, &defaults, entries) { + TAILQ_FOREACH(d, defs, entries) { struct early_default *early = is_early_default(d->var); if (early == NULL) continue; @@ -763,7 +763,7 @@ update_defaults(int what, bool quiet) /* * Then set the rest of the defaults. */ - TAILQ_FOREACH(d, &defaults, entries) { + TAILQ_FOREACH(d, defs, entries) { /* Skip Defaults marked as early, we already did them. */ if (is_early_default(d->var)) continue; diff --git a/plugins/sudoers/defaults.h b/plugins/sudoers/defaults.h index c0016e294..41582acce 100644 --- a/plugins/sudoers/defaults.h +++ b/plugins/sudoers/defaults.h @@ -122,13 +122,14 @@ struct early_default { /* * Prototypes */ +struct defaults_list; void dump_default(void); bool init_defaults(void); struct early_default *is_early_default(const char *name); bool run_early_defaults(void); bool set_early_default(const char *var, const char *val, int op, const char *file, int lineno, bool quiet, struct early_default *early); bool set_default(const char *var, const char *val, int op, const char *file, int lineno, bool quiet); -bool update_defaults(int what, bool quiet); +bool update_defaults(struct defaults_list *defs, int what, bool quiet); bool check_defaults(bool quiet); extern struct sudo_defs_types sudo_defs_table[]; diff --git a/plugins/sudoers/file.c b/plugins/sudoers/file.c new file mode 100644 index 000000000..85a0163d6 --- /dev/null +++ b/plugins/sudoers/file.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2004-2005, 2007-2018 Todd C. Miller + * + * 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. + */ + +#include + +#include +#include +#include +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#include +#include +#include +#include +#include + +#include "sudoers.h" +#include "parse.h" +#include "sudo_lbuf.h" +#include + +/* + * Local prototypes. + */ +static int sudo_file_close(struct sudo_nss *); +static int sudo_file_open(struct sudo_nss *); +static int sudo_file_parse(struct sudo_nss *); +static int sudo_file_query(struct sudo_nss *, struct passwd *pw); +static int sudo_file_getdefs(struct sudo_nss *); + +/* sudo_nss implementation */ +struct sudo_nss sudo_nss_file = { + { NULL, NULL }, + sudo_file_open, + sudo_file_close, + sudo_file_parse, + sudo_file_query, + sudo_file_getdefs +}; + +static int +sudo_file_open(struct sudo_nss *nss) +{ + debug_decl(sudo_file_open, SUDOERS_DEBUG_NSS) + + if (def_ignore_local_sudoers) + debug_return_int(-1); + nss->handle = open_sudoers(sudoers_file, false, NULL); + debug_return_int(nss->handle ? 0 : -1); +} + +static int +sudo_file_close(struct sudo_nss *nss) +{ + struct member_list *prev_binding = NULL; + struct defaults *def; + struct userspec *us; + debug_decl(sudo_file_close, SUDOERS_DEBUG_NSS) + + if (nss->handle != NULL) { + fclose(nss->handle); + nss->handle = NULL; + sudoersin = NULL; + + /* XXX - do in main module? */ + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + while ((def = TAILQ_FIRST(&nss->defaults)) != NULL) { + TAILQ_REMOVE(&nss->defaults, def, entries); + free_default(def, &prev_binding); + } + } + + debug_return_int(0); +} + +/* + * Parse the specified sudoers file. + */ +static int +sudo_file_parse(struct sudo_nss *nss) +{ + debug_decl(sudo_file_close, SUDOERS_DEBUG_NSS) + + if (nss->handle == NULL) + debug_return_int(-1); + + sudoersin = nss->handle; + if (sudoersparse() != 0 || parse_error) { + if (errorlineno != -1) { + log_warningx(SLOG_SEND_MAIL, N_("parse error in %s near line %d"), + errorfile, errorlineno); + } else { + log_warningx(SLOG_SEND_MAIL, N_("parse error in %s"), errorfile); + } + debug_return_int(-1); + } + + /* Move parsed userspecs and defaults to nss structure. */ + TAILQ_CONCAT(&nss->userspecs, &userspecs, entries); + TAILQ_CONCAT(&nss->defaults, &defaults, entries); + + debug_return_int(0); +} + +/* + * No need for explicit queries for sudoers file, we have it all in memory. + */ +static int +sudo_file_query(struct sudo_nss *nss, struct passwd *pw) +{ + debug_decl(sudo_file_query, SUDOERS_DEBUG_NSS) + debug_return_int(0); +} + +/* + * No need to get defaults for sudoers file, the parse function handled it. + */ +static int +sudo_file_getdefs(struct sudo_nss *nss) +{ + debug_decl(sudo_file_getdefs, SUDOERS_DEBUG_NSS) + debug_return_int(0); +} diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c index 26617329c..912b6db52 100644 --- a/plugins/sudoers/ldap.c +++ b/plugins/sudoers/ldap.c @@ -142,31 +142,20 @@ STAILQ_HEAD(ldap_netgroup_list, ldap_netgroup); static int sudo_ldap_open(struct sudo_nss *nss); static int sudo_ldap_close(struct sudo_nss *nss); static int sudo_ldap_parse(struct sudo_nss *nss); -static int sudo_ldap_setdefs(struct sudo_nss *nss); -static int sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag); -static int sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw); -static int sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf); -static int sudo_ldap_display_bound_defaults(struct sudo_nss *nss, - struct passwd *pw, struct sudo_lbuf *lbuf); -static int sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf); +static int sudo_ldap_query(struct sudo_nss *nss, struct passwd *pw); +static int sudo_ldap_getdefs(struct sudo_nss *nss); static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw); static char *sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry); /* * LDAP sudo_nss handle. - * We store the connection to the LDAP server, the cached ldap_result object - * (if any), and the name of the user the query was performed for. - * If a new query is launched with sudo_ldap_result_get() that specifies a - * different user, the old cached result is freed before the new query is run. + * We store the connection to the LDAP server and the passwd struct of the + * user the last query was performed for. */ struct sudo_ldap_handle { LDAP *ld; - struct ldap_result *result; - const char *username; - struct gid_list *gidlist; + struct passwd *pw; }; struct sudo_nss sudo_nss_ldap = { @@ -174,12 +163,8 @@ struct sudo_nss sudo_nss_ldap = { sudo_ldap_open, sudo_ldap_close, sudo_ldap_parse, - sudo_ldap_setdefs, - sudo_ldap_lookup, - sudo_ldap_display_cmnd, - sudo_ldap_display_defaults, - sudo_ldap_display_bound_defaults, - sudo_ldap_display_privs + sudo_ldap_query, + sudo_ldap_getdefs }; #ifdef HAVE_LDAP_INITIALIZE @@ -397,321 +382,60 @@ sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry, struct passwd *pw) debug_return_bool(matched == true); } -static int -sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry, int *group_matched) -{ - struct berval **bv, **p; - char *val; - bool ret = false; - debug_decl(sudo_ldap_check_runas_user, SUDOERS_DEBUG_LDAP) - - /* get the runas user from the entry */ - bv = ldap_get_values_len(ld, entry, "sudoRunAsUser"); - if (bv == NULL) - bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */ - if (bv == NULL) { - DPRINTF2("sudoRunAsUser: no result."); - if (*group_matched == UNSPEC) { - /* We haven't check for sudoRunAsGroup yet, check now. */ - bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup"); - if (bv != NULL) { - *group_matched = false; - ldap_value_free_len(bv); - } - } - if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED)) - debug_return_int(UNSPEC); - switch (*group_matched) { - case UNSPEC: - /* - * No runas user or group entries. Match runas_default - * against what the user specified on the command line. - */ - ret = userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw); - break; - case true: - /* - * No runas user entries but have a matching runas group entry. - * If trying to run as the invoking user, allow it. - */ - if (userpw_matches(user_name, runas_pw->pw_name, runas_pw)) - ret = true; - break; - } - debug_return_int(ret); - } - - /* - * BUG: - * - * if runas is not specified on the command line, the only information - * as to which user to run as is in the runas_default option. We should - * check to see if we have the local option present. Unfortunately we - * don't parse these options until after this routine says yes or no. - * The query has already returned, so we could peek at the attribute - * values here though. - * - * For now just require users to always use -u option unless its set - * in the global defaults. This behaviour is no different than the global - * /etc/sudoers. - * - * Sigh - maybe add this feature later - */ - - /* walk through values returned, looking for a match */ - for (p = bv; *p != NULL && !ret; p++) { - val = (*p)->bv_val; - switch (val[0]) { - case '+': - if (netgr_matches(val, def_netgroup_tuple ? user_runhost : NULL, - def_netgroup_tuple ? user_srunhost : NULL, runas_pw->pw_name)) - ret = true; - break; - case '%': - if (usergr_matches(val, runas_pw->pw_name, runas_pw)) - ret = true; - break; - case '\0': - /* Empty RunAsUser means run as the invoking user. */ - if (ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) && - userpw_matches(user_name, runas_pw->pw_name, runas_pw)) - ret = true; - break; - case 'A': - if (strcmp(val, "ALL") == 0) { - ret = true; - break; - } - /* FALLTHROUGH */ - default: - if (userpw_matches(val, runas_pw->pw_name, runas_pw)) - ret = true; - break; - } - DPRINTF2("ldap sudoRunAsUser '%s' ... %s", val, ret ? "MATCH!" : "not"); - } - - ldap_value_free_len(bv); /* cleanup */ - - debug_return_int(ret); -} - -static int -sudo_ldap_check_runas_group(LDAP *ld, LDAPMessage *entry) -{ - struct berval **bv, **p; - char *val; - bool ret = false; - debug_decl(sudo_ldap_check_runas_group, SUDOERS_DEBUG_LDAP) - - /* get the values from the entry */ - bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup"); - if (bv == NULL) { - DPRINTF2("sudoRunAsGroup: no result."); - if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED)) { - if (runas_pw->pw_gid == runas_gr->gr_gid) - ret = true; /* runas group matches passwd db */ - } - debug_return_int(ret); - } - - /* walk through values returned, looking for a match */ - for (p = bv; *p != NULL && !ret; p++) { - val = (*p)->bv_val; - if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr)) - ret = true; - DPRINTF2("ldap sudoRunAsGroup '%s' ... %s", - val, ret ? "MATCH!" : "not"); - } - - ldap_value_free_len(bv); /* cleanup */ - - debug_return_int(ret); -} - /* - * Walk through search results and return true if we have a runas match, - * else false. RunAs info is optional. + * Read sudoOption and fill in the defaults list. + * This is used to parse the cn=defaults entry. */ static bool -sudo_ldap_check_runas(LDAP *ld, LDAPMessage *entry) +sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry, struct defaults_list *defs) { - int user_matched = UNSPEC; - int group_matched = UNSPEC; - debug_decl(sudo_ldap_check_runas, SUDOERS_DEBUG_LDAP) - - if (!entry) - debug_return_bool(false); - - if (ISSET(sudo_user.flags, RUNAS_GROUP_SPECIFIED)) - group_matched = sudo_ldap_check_runas_group(ld, entry); - user_matched = sudo_ldap_check_runas_user(ld, entry, &group_matched); - - debug_return_bool(group_matched != false && user_matched != false); -} - -/* - * Walk through search results and return true if we have a command match, - * false if disallowed and UNSPEC if not matched. - */ -static int -sudo_ldap_check_command(LDAP *ld, LDAPMessage *entry, int *setenv_implied) -{ - struct sudo_digest digest, *allowed_digest = NULL; struct berval **bv, **p; - char *allowed_cmnd, *allowed_args, *val; - int ret = UNSPEC; - bool negated; - debug_decl(sudo_ldap_check_command, SUDOERS_DEBUG_LDAP) - - if (!entry) - debug_return_int(ret); - - bv = ldap_get_values_len(ld, entry, "sudoCommand"); - if (bv == NULL) - debug_return_int(ret); - - for (p = bv; *p != NULL && ret != false; p++) { - val = (*p)->bv_val; - - /* Match against ALL ? */ - if (strcmp(val, "ALL") == 0) { - ret = true; - if (setenv_implied != NULL) - *setenv_implied = true; - DPRINTF2("ldap sudoCommand '%s' ... MATCH!", val); - continue; - } - - /* check for sha-2 digest */ - allowed_digest = sudo_ldap_extract_digest(&val, &digest); - - /* check for !command */ - allowed_cmnd = val; - negated = sudo_ldap_is_negated(&allowed_cmnd); - - /* split optional args away from command */ - allowed_args = strchr(allowed_cmnd, ' '); - if (allowed_args) - *allowed_args++ = '\0'; - - /* check the command like normal */ - if (command_matches(allowed_cmnd, allowed_args, allowed_digest)) { - /* - * If allowed (no bang) set ret but keep on checking. - * If disallowed (bang), exit loop. - */ - ret = negated ? false : true; - } - if (allowed_args != NULL) - allowed_args[-1] = ' '; /* restore val */ - - DPRINTF2("ldap sudoCommand '%s' ... %s", - val, ret == true ? "MATCH!" : "not"); - - if (allowed_digest != NULL) - free(allowed_digest->digest_str); - } - - ldap_value_free_len(bv); /* more cleanup */ - - debug_return_int(ret); -} - -/* - * Search for boolean "option" in sudoOption. - * Returns true if found and allowed, false if negated, else UNSPEC. - */ -static int -sudo_ldap_check_bool(LDAP *ld, LDAPMessage *entry, char *option) -{ - struct berval **bv, **p; - char *var; - bool negated; - int ret = UNSPEC; - debug_decl(sudo_ldap_check_bool, SUDOERS_DEBUG_LDAP) - - if (entry == NULL) - debug_return_int(ret); - - bv = ldap_get_values_len(ld, entry, "sudoOption"); - if (bv == NULL) - debug_return_int(ret); - - /* walk through options */ - for (p = bv; *p != NULL; p++) { - var = (*p)->bv_val; - DPRINTF2("ldap sudoOption: '%s'", var); - - negated = sudo_ldap_is_negated(&var); - if (strcmp(var, option) == 0) - ret = negated ? false : true; - } - - ldap_value_free_len(bv); - - debug_return_int(ret); -} - -/* - * Read sudoOption and modify the defaults as we go. This is used once - * from the cn=defaults entry and also once when a final sudoRole is matched. - */ -static bool -sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry) -{ - struct berval **bv, **p; - char *cn, *var, *val, *source = NULL; + char *cn, *cp, *source = NULL; bool ret = false; - int op; debug_decl(sudo_ldap_parse_options, SUDOERS_DEBUG_LDAP) bv = ldap_get_values_len(ld, entry, "sudoOption"); if (bv == NULL) debug_return_bool(true); - /* get the entry's dn for option error reporting */ + /* Use sudoRole in place of file name in defaults. */ cn = sudo_ldap_get_first_rdn(ld, entry); - if (cn != NULL) { - if (asprintf(&source, "sudoRole %s", cn) == -1) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - source = NULL; - goto done; - } + if (asprintf(&cp, "sudoRole %s", cn ? cn : "UNKNOWN") == -1) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + goto done; + } + if ((source = rcstr_dup(cp)) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + free(cp); + goto done; } - /* walk through options, early ones first */ + /* Walk through options, appending to defs. */ for (p = bv; *p != NULL; p++) { - struct early_default *early; - char *copy; + char *copy, *var, *val; + int op; - /* Avoid modifying bv as we need to use it again below. */ - if ((copy = strdup((*p)->bv_val)) == NULL) { + copy = strdup((*p)->bv_val); /* XXX - should not need to copy */ + if (copy == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + free(copy); goto done; } + op = sudo_ldap_parse_option(copy, &var, &val); - early = is_early_default(var); - if (early != NULL) { - set_early_default(var, val, op, - source ? source : "sudoRole UNKNOWN", 0, false, early); + if (!sudo_ldap_add_default(var, val, op, source, defs)) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + free(copy); + goto done; } + free(copy); } - run_early_defaults(); - /* walk through options again, skipping early ones */ - for (p = bv; *p != NULL; p++) { - op = sudo_ldap_parse_option((*p)->bv_val, &var, &val); - if (is_early_default(var) == NULL) { - set_default(var, val, op, - source ? source : "sudoRole UNKNOWN", 0, false); - } - } ret = true; done: - free(source); + rcstr_delref(source); if (cn) ldap_memfree(cn); ldap_value_free_len(bv); @@ -1416,81 +1140,6 @@ sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry) #endif } -/* - * Fetch and display the global Options. - */ -static int -sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - struct berval **bv, **p; - struct timeval tv, *tvp = NULL; - struct ldap_config_str *base; - struct sudo_ldap_handle *handle = nss->handle; - LDAP *ld; - LDAPMessage *entry, *result; - char *prefix, *filt; - int rc, count = 0; - debug_decl(sudo_ldap_display_defaults, SUDOERS_DEBUG_LDAP) - - if (handle == NULL || handle->ld == NULL) - goto done; - ld = handle->ld; - - filt = sudo_ldap_build_default_filter(); - if (filt == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - count = -1; - goto done; - } - STAILQ_FOREACH(base, &ldap_conf.base, entries) { - if (ldap_conf.timeout > 0) { - tv.tv_sec = ldap_conf.timeout; - tv.tv_usec = 0; - tvp = &tv; - } - result = NULL; - rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, - filt, NULL, 0, NULL, NULL, tvp, 0, &result); - if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { - bv = ldap_get_values_len(ld, entry, "sudoOption"); - if (bv != NULL) { - if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1])) - prefix = " "; - else - prefix = ", "; - for (p = bv; *p != NULL; p++) { - struct defaults d; - - sudo_lbuf_append(lbuf, "%s", prefix); - d.op = sudo_ldap_parse_option((*p)->bv_val, &d.var, &d.val); - sudoers_format_default(lbuf, &d); - prefix = ", "; - count++; - } - ldap_value_free_len(bv); - } - } - ldap_msgfree(result); - } - free(filt); -done: - if (sudo_lbuf_error(lbuf)) - debug_return_int(-1); - debug_return_int(count); -} - -/* - * STUB - */ -static int -sudo_ldap_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - debug_decl(sudo_ldap_display_bound_defaults, SUDOERS_DEBUG_LDAP) - debug_return_int(0); -} - static char * berval_iter(void **vp) { @@ -1500,19 +1149,15 @@ berval_iter(void **vp) return *bv ? (*bv)->bv_val : NULL; } -static struct userspec_list * -ldap_to_sudoers(LDAP *ld, struct ldap_result *lres) +static bool +ldap_to_sudoers(LDAP *ld, struct ldap_result *lres, + struct userspec_list *ldap_userspecs) { - struct userspec_list *ldap_userspecs; struct userspec *us; struct member *m; unsigned int i; debug_decl(ldap_to_sudoers, SUDOERS_DEBUG_LDAP) - if ((ldap_userspecs = calloc(1, sizeof(*ldap_userspecs))) == NULL) - goto oom; - TAILQ_INIT(ldap_userspecs); - /* We only have a single userspec */ if ((us = calloc(1, sizeof(*us))) == NULL) goto oom; @@ -1586,103 +1231,13 @@ ldap_to_sudoers(LDAP *ld, struct ldap_result *lres) oom: sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - if (ldap_userspecs != NULL) { - while ((us = TAILQ_FIRST(ldap_userspecs)) != NULL) { - TAILQ_REMOVE(ldap_userspecs, us, entries); - free_userspec(us); - } - free(ldap_userspecs); + while ((us = TAILQ_FIRST(ldap_userspecs)) != NULL) { + TAILQ_REMOVE(ldap_userspecs, us, entries); + free_userspec(us); } debug_return_ptr(NULL); } -/* - * Like sudo_ldap_lookup(), except we just print entries. - */ -static int -sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - struct sudo_ldap_handle *handle = nss->handle; - struct userspec_list *ldap_userspecs = NULL; - struct ldap_result *lres; - LDAP *ld; - int ret = 0; - debug_decl(sudo_ldap_display_privs, SUDOERS_DEBUG_LDAP) - - if (handle == NULL || handle->ld == NULL) - goto done; - ld = handle->ld; - - DPRINTF1("ldap search for command list"); - lres = sudo_ldap_result_get(nss, pw); - if (lres == NULL) - goto done; - - /* Convert to sudoers parse tree. */ - if ((ldap_userspecs = ldap_to_sudoers(ld, lres)) == NULL) { - ret = -1; - goto done; - } - - /* Call common display code. */ - ret = sudo_display_userspecs(ldap_userspecs, pw, lbuf); - -done: - if (ldap_userspecs != NULL) { - struct userspec *us; - while ((us = TAILQ_FIRST(ldap_userspecs)) != NULL) { - TAILQ_REMOVE(ldap_userspecs, us, entries); - free_userspec(us); - } - free(ldap_userspecs); - } - if (sudo_lbuf_error(lbuf)) - debug_return_int(-1); - debug_return_int(ret); -} - -static int -sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw) -{ - struct sudo_ldap_handle *handle = nss->handle; - LDAP *ld; - struct ldap_result *lres; - LDAPMessage *entry; - bool found = false; - unsigned int i; - debug_decl(sudo_ldap_display_cmnd, SUDOERS_DEBUG_LDAP) - - if (handle == NULL || handle->ld == NULL) - goto done; - ld = handle->ld; - - /* - * The sudo_ldap_result_get() function returns all nodes that match - * the user and the host. - */ - DPRINTF1("ldap search for command list"); - lres = sudo_ldap_result_get(nss, pw); - if (lres == NULL) - goto done; - for (i = 0; i < lres->nentries; i++) { - entry = lres->entries[i].entry; - if (!sudo_ldap_check_runas(ld, entry)) - continue; - if (sudo_ldap_check_command(ld, entry, NULL) == true) { - found = true; - goto done; - } - } - -done: - if (found) - sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n", - safe_cmnd ? safe_cmnd : user_cmnd, - user_args ? " " : "", user_args ? user_args : ""); - debug_return_int(!found); -} - #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S static unsigned int (*sudo_gss_krb5_ccache_name)(unsigned int *minor_status, const char *name, const char **old_name); @@ -2087,21 +1642,29 @@ done: } static int -sudo_ldap_setdefs(struct sudo_nss *nss) +sudo_ldap_getdefs(struct sudo_nss *nss) { struct ldap_config_str *base; struct sudo_ldap_handle *handle = nss->handle; + struct member_list *prev_binding = NULL; struct timeval tv, *tvp = NULL; + struct defaults *def; LDAP *ld; LDAPMessage *entry, *result = NULL; char *filt; - int ret; - debug_decl(sudo_ldap_setdefs, SUDOERS_DEBUG_LDAP) + int rc, ret = -1; + debug_decl(sudo_ldap_getdefs, SUDOERS_DEBUG_LDAP) if (handle == NULL || handle->ld == NULL) debug_return_int(-1); ld = handle->ld; + /* Free old defaults, if any. */ + while ((def = TAILQ_FIRST(&nss->defaults)) != NULL) { + TAILQ_REMOVE(&nss->defaults, def, entries); + free_default(def, &prev_binding); + } + filt = sudo_ldap_build_default_filter(); if (filt == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); @@ -2117,14 +1680,12 @@ sudo_ldap_setdefs(struct sudo_nss *nss) } ldap_msgfree(result); result = NULL; - ret = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, + rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt, NULL, 0, NULL, NULL, tvp, 0, &result); - if (ret == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { + if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { DPRINTF1("found:%s", ldap_get_dn(ld, entry)); - if (!sudo_ldap_parse_options(ld, entry)) { - ret = -1; + if (!sudo_ldap_parse_options(ld, entry, &nss->defaults)) goto done; - } } else { DPRINTF1("no default options found in %s", base->val); } @@ -2138,132 +1699,6 @@ done: debug_return_int(ret); } -/* - * like sudoers_lookup() - only LDAP style - */ -static int -sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag) -{ - struct sudo_ldap_handle *handle = nss->handle; - LDAP *ld; - LDAPMessage *entry; - int rc, setenv_implied; - unsigned int i; - struct ldap_result *lres = NULL; - debug_decl(sudo_ldap_lookup, SUDOERS_DEBUG_LDAP) - - if (handle == NULL || handle->ld == NULL) - debug_return_int(ret); - ld = handle->ld; - - /* Fetch list of sudoRole entries that match user and host. */ - lres = sudo_ldap_result_get(nss, sudo_user.pw); - if (lres == NULL) - debug_return_int(ret); - - /* - * The following queries only determine whether or not a password - * is required, so the order of the entries doesn't matter. - */ - if (pwflag) { - int doauth = UNSPEC; - int matched = UNSPEC; - enum def_tuple pwcheck = - (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; - - DPRINTF1("perform search for pwflag %d", pwflag); - for (i = 0; i < lres->nentries; i++) { - entry = lres->entries[i].entry; - if ((pwcheck == any && doauth != false) || - (pwcheck == all && doauth != true)) { - doauth = !!sudo_ldap_check_bool(ld, entry, "authenticate"); - } - if (matched == true) - continue; - /* Only check the command when listing another user. */ - if (user_uid == 0 || list_pw == NULL || - user_uid == list_pw->pw_uid || - sudo_ldap_check_command(ld, entry, NULL) == true) { - matched = true; - } - } - if (matched == true || user_uid == 0) { - SET(ret, VALIDATE_SUCCESS); - CLR(ret, VALIDATE_FAILURE); - switch (pwcheck) { - case always: - SET(ret, FLAG_CHECK_USER); - break; - case all: - case any: - if (doauth == false) - SET(ret, FLAG_NOPASSWD); - else - CLR(ret, FLAG_NOPASSWD); - break; - default: - break; - } - } - goto done; - } - - DPRINTF1("searching LDAP for sudoers entries"); - - setenv_implied = false; - for (i = 0; i < lres->nentries; i++) { - entry = lres->entries[i].entry; - if (!sudo_ldap_check_runas(ld, entry)) - continue; - rc = sudo_ldap_check_command(ld, entry, &setenv_implied); - if (rc != UNSPEC) { - /* We have a match. */ - DPRINTF1("Command %sallowed", rc == true ? "" : "NOT "); - if (rc == true) { - DPRINTF1("LDAP entry: %p", entry); - /* Apply entry-specific options. */ - if (setenv_implied) - def_setenv = true; - if (sudo_ldap_parse_options(ld, entry)) { -#ifdef HAVE_SELINUX - /* Set role and type if not specified on command line. */ - if (user_role == NULL) - user_role = def_role; - if (user_type == NULL) - user_type = def_type; -#endif /* HAVE_SELINUX */ - SET(ret, VALIDATE_SUCCESS); - CLR(ret, VALIDATE_FAILURE); - } else { - SET(ret, VALIDATE_ERROR); - } - } else { - SET(ret, VALIDATE_FAILURE); - CLR(ret, VALIDATE_SUCCESS); - } - break; - } - } - -done: - DPRINTF1("done with LDAP searches"); - DPRINTF1("user_matches=%s", lres->user_matches ? "true" : "false"); - DPRINTF1("host_matches=%s", lres->host_matches ? "true" : "false"); - - if (!ISSET(ret, VALIDATE_SUCCESS)) { - /* No matching entries. */ - if (pwflag && list_pw == NULL) - SET(ret, FLAG_NO_CHECK); - } - if (pwflag || lres->user_matches) - CLR(ret, FLAG_NO_USER); - if (pwflag || lres->host_matches) - CLR(ret, FLAG_NO_HOST); - DPRINTF1("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret); - - debug_return_int(ret); -} - /* * Comparison function for ldap_entry_wrapper structures, descending order. */ @@ -2344,27 +1779,36 @@ sudo_ldap_result_add_entry(struct ldap_result *lres, LDAPMessage *entry) } /* - * Free the ldap result structure in the sudo_nss handle. + * Free the cached results in the sudo_nss handle, if present. */ static void sudo_ldap_result_free_nss(struct sudo_nss *nss) { struct sudo_ldap_handle *handle = nss->handle; + struct member_list *prev_binding = NULL; + struct defaults *def; + struct userspec *us; debug_decl(sudo_ldap_result_free_nss, SUDOERS_DEBUG_LDAP) - if (handle->result != NULL) { - DPRINTF1("removing reusable search result"); - sudo_ldap_result_free(handle->result); - handle->username = NULL; - handle->gidlist = NULL; - handle->result = NULL; + if (handle->pw != NULL) + sudo_pw_delref(handle->pw); + + /* XXX - do in main module? */ + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + while ((def = TAILQ_FIRST(&nss->defaults)) != NULL) { + TAILQ_REMOVE(&nss->defaults, def, entries); + free_default(def, &prev_binding); } + debug_return; } /* - * Perform the LDAP query for the user or return a cached query if - * there is one for this user. + * Perform the LDAP query for the user. The caller is responsible for + * freeing the result with sudo_ldap_result_free(). */ static struct ldap_result * sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw) @@ -2379,23 +1823,6 @@ sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw) char *filt; debug_decl(sudo_ldap_result_get, SUDOERS_DEBUG_LDAP) - /* - * If we already have a cached result, return it so we don't have to - * have to contact the LDAP server again. - */ - if (handle->result) { - if (handle->gidlist == user_gid_list && - strcmp(pw->pw_name, handle->username) == 0) { - DPRINTF1("reusing previous result (user %s) with %d entries", - handle->username, handle->result->nentries); - debug_return_ptr(handle->result); - } - /* User mismatch, cached result cannot be used. */ - DPRINTF1("removing result (user %s), new search (user %s)", - handle->username, pw->pw_name); - sudo_ldap_result_free_nss(nss); - } - /* * Okay - time to search for anything that matches this user * Lets limit it to only two queries of the LDAP server @@ -2483,13 +1910,6 @@ sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw) ldap_entry_compare); } - /* Store everything in the sudo_nss handle. */ - /* XXX - store pw and take a reference to it. */ - /* XXX - take refs for gidlist and grlist */ - handle->result = lres; - handle->username = pw->pw_name; - handle->gidlist = user_gid_list; - debug_return_ptr(lres); } @@ -2503,7 +1923,7 @@ sudo_ldap_close(struct sudo_nss *nss) debug_decl(sudo_ldap_close, SUDOERS_DEBUG_LDAP) if (handle != NULL) { - /* Free the result before unbinding; it may use the LDAP connection. */ + /* Free the cached result. */ sudo_ldap_result_free_nss(nss); /* Unbind and close the LDAP connection. */ @@ -2519,6 +1939,65 @@ sudo_ldap_close(struct sudo_nss *nss) debug_return_int(0); } +/* + * Perform LDAP query for user and host and convert to sudoers + * parse tree. + */ +static int +sudo_ldap_query(struct sudo_nss *nss, struct passwd *pw) +{ + struct sudo_ldap_handle *handle = nss->handle; + struct ldap_result *lres = NULL; + struct userspec *us; + int ret = 0; + LDAP *ld; + debug_decl(sudo_ldap_query, SUDOERS_DEBUG_LDAP) + + if (handle == NULL || handle->ld == NULL) + goto done; + ld = handle->ld; + + /* Use cached result if it matches pw. */ + if (handle->pw != NULL) { + if (pw == handle->pw) + goto done; + sudo_pw_delref(handle->pw); + handle->pw = NULL; + } + + /* Free old userspecs, if any. */ + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + + DPRINTF1("%s: ldap search user %s, host %s", __func__, pw->pw_name, + user_runhost); + if ((lres = sudo_ldap_result_get(nss, pw)) == NULL) + goto done; + + /* Convert to sudoers parse tree. */ + if (!ldap_to_sudoers(ld, lres, &nss->userspecs)) { + ret = -1; + goto done; + } + + /* Stash a ref to the passwd struct in the handle. */ + sudo_pw_addref(pw); + handle->pw = pw; + +done: + /* Cleanup. */ + sudo_ldap_result_free(lres); + if (ret == -1) { + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + } + debug_return_int(ret); +} + /* * STUB */ diff --git a/plugins/sudoers/ldap_util.c b/plugins/sudoers/ldap_util.c index aa468a33a..74fb11d78 100644 --- a/plugins/sudoers/ldap_util.c +++ b/plugins/sudoers/ldap_util.c @@ -241,6 +241,39 @@ oom: debug_return_ptr(NULL); } +bool +sudo_ldap_add_default(const char *var, const char *val, int op, + char *source, struct defaults_list *defs) +{ + struct defaults *def; + debug_decl(sudo_ldap_add_default, SUDOERS_DEBUG_LDAP) + + if ((def = calloc(1, sizeof(*def))) == NULL) + goto oom; + + def->type = DEFAULTS; + def->op = op; + if ((def->var = strdup(var)) == NULL) { + goto oom; + } + if (val != NULL) { + if ((def->val = strdup(val)) == NULL) + goto oom; + } + def->file = source; + rcstr_addref(source); + TAILQ_INSERT_TAIL(defs, def, entries); + debug_return_bool(true); + +oom: + if (def != NULL) { + free(def->var); + free(def->val); + free(def); + } + debug_return_bool(false); +} + /* * Convert an LDAP sudoRole to a sudoers privilege. * Pass in struct berval ** for LDAP or char *** for SSSD. @@ -384,7 +417,15 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, /* Parse sudoOptions. */ if (opts != NULL) { - char *opt; + char *opt, *source = NULL; + + if (store_options) { + /* Use sudoRole in place of file name in defaults. */ + size_t slen = sizeof("sudoRole") + strlen(priv->ldap_role); + if ((source = rcstr_alloc(slen)) == NULL) + goto oom; + snprintf(source, slen, "sudoRole %s", priv->ldap_role); + } while ((opt = iter(&opts)) != NULL) { char *var, *val; @@ -419,23 +460,10 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, } #endif /* HAVE_PRIV_SET */ } else if (store_options) { - struct defaults *def = calloc(1, sizeof(*def)); - if (def == NULL) + if (!sudo_ldap_add_default(var, val, op, source, + &priv->defaults)) { goto oom; - def->type = DEFAULTS; - def->op = op; - if ((def->var = strdup(var)) == NULL) { - free(def); - goto oom; - } - if (val != NULL) { - if ((def->val = strdup(val)) == NULL) { - free(def->var); - free(def); - goto oom; - } } - TAILQ_INSERT_TAIL(&priv->defaults, def, entries); } else { /* Convert to tags. */ bool handled = true; @@ -474,6 +502,7 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, } } } + rcstr_delref(source); } /* So we can inherit previous values. */ diff --git a/plugins/sudoers/parse.c b/plugins/sudoers/parse.c index 587c64b8c..b3ae3b807 100644 --- a/plugins/sudoers/parse.c +++ b/plugins/sudoers/parse.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #ifdef HAVE_STRING_H @@ -38,147 +39,57 @@ #include /* - * Local prototypes. + * Look up the user in the sudoers prase tree for pseudo-commands like + * list, verify and kill. */ -static int display_bound_defaults(int deftype, struct sudo_lbuf *lbuf); -static int sudo_file_close(struct sudo_nss *); -static int sudo_file_display_bound_defaults(struct sudo_nss *, struct passwd *, struct sudo_lbuf *); -static int sudo_file_display_cmnd(struct sudo_nss *, struct passwd *); -static int sudo_file_display_defaults(struct sudo_nss *, struct passwd *, struct sudo_lbuf *); -static int sudo_file_display_privs(struct sudo_nss *, struct passwd *, struct sudo_lbuf *); -static int sudo_file_lookup(struct sudo_nss *, int, int); -static int sudo_file_open(struct sudo_nss *); -static int sudo_file_parse(struct sudo_nss *); -static int sudo_file_setdefs(struct sudo_nss *); - -/* sudo_nss implementation */ -struct sudo_nss sudo_nss_file = { - { NULL, NULL }, - sudo_file_open, - sudo_file_close, - sudo_file_parse, - sudo_file_setdefs, - sudo_file_lookup, - sudo_file_display_cmnd, - sudo_file_display_defaults, - sudo_file_display_bound_defaults, - sudo_file_display_privs -}; - -int -sudo_file_open(struct sudo_nss *nss) -{ - debug_decl(sudo_file_open, SUDOERS_DEBUG_NSS) - - if (def_ignore_local_sudoers) - debug_return_int(-1); - nss->handle = open_sudoers(sudoers_file, false, NULL); - debug_return_int(nss->handle ? 0 : -1); -} - -int -sudo_file_close(struct sudo_nss *nss) -{ - debug_decl(sudo_file_close, SUDOERS_DEBUG_NSS) - - /* Free parser data structures and close sudoers file. */ - init_parser(NULL, false); - if (nss->handle != NULL) { - fclose(nss->handle); - nss->handle = NULL; - sudoersin = NULL; - } - debug_return_int(0); -} - -/* - * Parse the specified sudoers file. - */ -int -sudo_file_parse(struct sudo_nss *nss) -{ - debug_decl(sudo_file_close, SUDOERS_DEBUG_NSS) - - if (nss->handle == NULL) - debug_return_int(-1); - - init_parser(sudoers_file, false); - sudoersin = nss->handle; - if (sudoersparse() != 0 || parse_error) { - if (errorlineno != -1) { - log_warningx(SLOG_SEND_MAIL, N_("parse error in %s near line %d"), - errorfile, errorlineno); - } else { - log_warningx(SLOG_SEND_MAIL, N_("parse error in %s"), errorfile); - } - debug_return_int(-1); - } - debug_return_int(0); -} - -/* - * Wrapper around update_defaults() for nsswitch code. - */ -int -sudo_file_setdefs(struct sudo_nss *nss) -{ - debug_decl(sudo_file_setdefs, SUDOERS_DEBUG_NSS) - - if (nss->handle == NULL) - debug_return_int(-1); - - if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, - false)) - debug_return_int(-1); - debug_return_int(0); -} - -/* - * Look up the user in the parsed sudoers file and check to see if they are - * allowed to run the specified command on this host as the target user. - */ -int -sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag) +static int +sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, + int validated, int pwflag) { - int match, host_match, runas_match, cmnd_match, timeout; + int match; + struct sudo_nss *nss; struct cmndspec *cs; - struct cmndtag *tags = NULL; struct privilege *priv; struct userspec *us; - struct member *matching_user; - time_t now; - debug_decl(sudo_file_lookup, SUDOERS_DEBUG_NSS) - - if (nss->handle == NULL) - debug_return_int(validated); - - /* - * Only check the actual command if pwflag is not set. - * It is set for the "validate", "list" and "kill" pseudo-commands. - * Always check the host and user. - */ - if (pwflag) { - int nopass; - enum def_tuple pwcheck; - - pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; - nopass = (pwcheck == all) ? true : false; - - if (list_pw == NULL) - SET(validated, FLAG_NO_CHECK); - CLR(validated, FLAG_NO_USER); - CLR(validated, FLAG_NO_HOST); - match = DENY; - TAILQ_FOREACH(us, &userspecs, entries) { - if (userlist_matches(sudo_user.pw, &us->users) != ALLOW) + struct defaults *def; + int nopass; + enum def_tuple pwcheck; + debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER) + + pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; + nopass = (pwcheck == all) ? true : false; + + if (list_pw == NULL) + SET(validated, FLAG_NO_CHECK); + CLR(validated, FLAG_NO_USER); + CLR(validated, FLAG_NO_HOST); + match = DENY; + TAILQ_FOREACH(nss, snl, entries) { + if (nss->query(nss, pw) == -1) { + /* The query function should have printed an error message. */ + SET(validated, VALIDATE_ERROR); + break; + } + TAILQ_FOREACH(us, &nss->userspecs, entries) { + if (userlist_matches(pw, &us->users) != ALLOW) continue; TAILQ_FOREACH(priv, &us->privileges, entries) { - if (hostlist_matches(sudo_user.pw, &priv->hostlist) != ALLOW) + int priv_nopass = UNSPEC; + + if (hostlist_matches(pw, &priv->hostlist) != ALLOW) continue; + TAILQ_FOREACH(def, &priv->defaults, entries) { + if (strcmp(def->var, "authenticate") == 0) + priv_nopass = def->op; + } TAILQ_FOREACH(cs, &priv->cmndlist, entries) { - if ((pwcheck == any && cs->tags.nopasswd == true) || - (pwcheck == all && cs->tags.nopasswd != true)) - nopass = cs->tags.nopasswd; + if (pwcheck == any) { + if (cs->tags.nopasswd == true || priv_nopass == true) + nopass = true; + } else if (pwcheck == all) { + if (cs->tags.nopasswd != true && priv_nopass != true) + nopass = false; + } if (match == ALLOW) continue; /* Only check the command when listing another user. */ @@ -189,34 +100,38 @@ sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag) } } } - if (match == ALLOW || user_uid == 0) { - /* User has an entry for this host. */ - SET(validated, VALIDATE_SUCCESS); - } else if (match == DENY) - SET(validated, VALIDATE_FAILURE); - if (pwcheck == always && def_authenticate) - SET(validated, FLAG_CHECK_USER); - else if (nopass == true) - SET(validated, FLAG_NOPASSWD); - else - CLR(validated, FLAG_NOPASSWD); - debug_return_int(validated); } + if (match == ALLOW || user_uid == 0) { + /* User has an entry for this host. */ + SET(validated, VALIDATE_SUCCESS); + } else if (match == DENY) + SET(validated, VALIDATE_FAILURE); + if (pwcheck == always && def_authenticate) + SET(validated, FLAG_CHECK_USER); + else if (nopass == true) + def_authenticate = false; + debug_return_int(validated); +} - /* Need to be runas user while stat'ing things. */ - if (!set_perms(PERM_RUNAS)) - debug_return_int(validated); +static int +sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw, int *validated, + struct cmndspec **matching_cs, struct defaults_list **defs, time_t now) +{ + int host_match, runas_match, cmnd_match; + struct cmndspec *cs; + struct privilege *priv; + struct userspec *us; + struct member *matching_user; + debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER) - time(&now); - match = UNSPEC; - TAILQ_FOREACH_REVERSE(us, &userspecs, userspec_list, entries) { - if (userlist_matches(sudo_user.pw, &us->users) != ALLOW) + TAILQ_FOREACH_REVERSE(us, &nss->userspecs, userspec_list, entries) { + if (userlist_matches(pw, &us->users) != ALLOW) continue; - CLR(validated, FLAG_NO_USER); + CLR(*validated, FLAG_NO_USER); TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) { - host_match = hostlist_matches(sudo_user.pw, &priv->hostlist); + host_match = hostlist_matches(pw, &priv->hostlist); if (host_match == ALLOW) - CLR(validated, FLAG_NO_HOST); + CLR(*validated, FLAG_NO_HOST); else continue; TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) { @@ -234,63 +149,6 @@ sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag) if (runas_match == ALLOW) { cmnd_match = cmnd_matches(cs->cmnd); if (cmnd_match != UNSPEC) { - match = cmnd_match; - tags = &cs->tags; - timeout = cs->timeout; -#ifdef HAVE_SELINUX - /* Set role and type if not specified on command line. */ - if (user_role == NULL) { - if (cs->role != NULL) { - user_role = strdup(cs->role); - if (user_role == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - SET(validated, VALIDATE_ERROR); - goto done; - } - } else { - user_role = def_role; - } - } - if (user_type == NULL) { - if (cs->type != NULL) { - user_type = strdup(cs->type); - if (user_type == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - SET(validated, VALIDATE_ERROR); - goto done; - } - } else { - user_type = def_type; - } - } -#endif /* HAVE_SELINUX */ -#ifdef HAVE_PRIV_SET - /* Set Solaris privilege sets */ - if (runas_privs == NULL) { - if (cs->privs != NULL) { - runas_privs = strdup(cs->privs); - if (runas_privs == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - SET(validated, VALIDATE_ERROR); - goto done; - } - } else { - runas_privs = def_privs; - } - } - if (runas_limitprivs == NULL) { - if (cs->limitprivs != NULL) { - runas_limitprivs = strdup(cs->limitprivs); - if (runas_limitprivs == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - SET(validated, VALIDATE_ERROR); - goto done; - } - } else { - runas_limitprivs = def_limitprivs; - } - } -#endif /* HAVE_PRIV_SET */ /* * If user is running command as himself, * set runas_pw = sudo_user.pw. @@ -301,75 +159,174 @@ sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag) sudo_pw_addref(sudo_user.pw); runas_pw = sudo_user.pw; } - goto matched2; + *matching_cs = cs; + *defs = &priv->defaults; + debug_return_int(cmnd_match); } } } } } - matched2: - if (match == ALLOW) { - SET(validated, VALIDATE_SUCCESS); - CLR(validated, VALIDATE_FAILURE); - if (timeout > 0) - def_command_timeout = timeout; - if (tags != NULL) { - if (tags->nopasswd != UNSPEC) - def_authenticate = !tags->nopasswd; - if (tags->noexec != UNSPEC) - def_noexec = tags->noexec; - if (tags->setenv != UNSPEC) - def_setenv = tags->setenv; - if (tags->log_input != UNSPEC) - def_log_input = tags->log_input; - if (tags->log_output != UNSPEC) - def_log_output = tags->log_output; - if (tags->send_mail != UNSPEC) { - if (tags->send_mail) { - def_mail_all_cmnds = true; - } else { - def_mail_all_cmnds = false; - def_mail_always = false; - def_mail_no_perms = false; + debug_return_int(UNSPEC); +} + +/* + * Apply cmndspec-specific settngs including SELinux role/type, + * Solaris privs, and command tags. + */ +static bool +apply_cmndspec(struct cmndspec *cs) +{ + debug_decl(apply_cmndspec, SUDOERS_DEBUG_PARSER) + + if (cs != NULL) { +#ifdef HAVE_SELINUX + /* Set role and type if not specified on command line. */ + if (user_role == NULL) { + if (cs->role != NULL) { + user_role = strdup(cs->role); + if (user_role == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + debug_return_bool(false); } + } else { + user_role = def_role; } - if (tags->follow != UNSPEC) - def_sudoedit_follow = tags->follow; } - } else if (match == DENY) { - SET(validated, VALIDATE_FAILURE); - CLR(validated, VALIDATE_SUCCESS); - if (tags != NULL) { - if (tags->nopasswd != UNSPEC) - def_authenticate = !tags->nopasswd; - if (tags->send_mail != UNSPEC) { - if (tags->send_mail) { - def_mail_all_cmnds = true; - } else { - def_mail_all_cmnds = false; - def_mail_always = false; - def_mail_no_perms = false; + if (user_type == NULL) { + if (cs->type != NULL) { + user_type = strdup(cs->type); + if (user_type == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + debug_return_bool(false); } + } else { + user_type = def_type; } } +#endif /* HAVE_SELINUX */ +#ifdef HAVE_PRIV_SET + /* Set Solaris privilege sets */ + if (runas_privs == NULL) { + if (cs->privs != NULL) { + runas_privs = strdup(cs->privs); + if (runas_privs == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + debug_return_bool(false); + } + } else { + runas_privs = def_privs; + } + } + if (runas_limitprivs == NULL) { + if (cs->limitprivs != NULL) { + runas_limitprivs = strdup(cs->limitprivs); + if (runas_limitprivs == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + debug_return_bool(false); + } + } else { + runas_limitprivs = def_limitprivs; + } + } +#endif /* HAVE_PRIV_SET */ + if (cs->timeout > 0) + def_command_timeout = cs->timeout; + if (cs->tags.nopasswd != UNSPEC) + def_authenticate = !cs->tags.nopasswd; + if (cs->tags.noexec != UNSPEC) + def_noexec = cs->tags.noexec; + if (cs->tags.setenv != UNSPEC) + def_setenv = cs->tags.setenv; + if (cs->tags.log_input != UNSPEC) + def_log_input = cs->tags.log_input; + if (cs->tags.log_output != UNSPEC) + def_log_output = cs->tags.log_output; + if (cs->tags.send_mail != UNSPEC) { + if (cs->tags.send_mail) { + def_mail_all_cmnds = true; + } else { + def_mail_all_cmnds = false; + def_mail_always = false; + def_mail_no_perms = false; + } + } + if (cs->tags.follow != UNSPEC) + def_sudoedit_follow = cs->tags.follow; + } + + debug_return_bool(true); +} + +/* + * Look up the user in the sudoers prase tree and check to see if they are + * allowed to run the specified command on this host as the target user. + */ +int +sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated, + int pwflag) +{ + struct defaults_list *defs = NULL; + struct cmndspec *cs = NULL; + struct sudo_nss *nss; + int m, match = UNSPEC; + time_t now; + debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER) + + /* + * Special case checking the "validate", "list" and "kill" pseudo-commands. + */ + if (pwflag) + debug_return_int(sudoers_lookup_pseudo(snl, pw, validated, pwflag)); + + /* Need to be runas user while stat'ing things. */ + if (!set_perms(PERM_RUNAS)) + debug_return_int(validated); + + /* Query each sudoers source and check the user. */ + time(&now); + TAILQ_FOREACH(nss, snl, entries) { + if (nss->query(nss, pw) == -1) { + /* The query function should have printed an error message. */ + SET(validated, VALIDATE_ERROR); + break; + } + + m = sudoers_lookup_check(nss, pw, &validated, &cs, &defs, now); + if (m != UNSPEC) + match = m; + + if (!sudo_nss_can_continue(nss, m)) + break; + } + if (defs != NULL) + update_defaults(defs, SETDEF_GENERIC, false); + if (match != UNSPEC) { + if (!apply_cmndspec(cs)) + SET(validated, VALIDATE_ERROR); + else if (match == ALLOW) + SET(validated, VALIDATE_SUCCESS); + else + SET(validated, VALIDATE_FAILURE); } -#if defined(HAVE_SELINUX) || defined(HAVE_PRIV_SET) -done: -#endif if (!restore_perms()) SET(validated, VALIDATE_ERROR); debug_return_int(validated); } static int -sudo_file_display_priv_short(struct passwd *pw, struct userspec *us, +display_priv_short(struct passwd *pw, struct userspec *us, struct sudo_lbuf *lbuf) { struct cmndspec *cs, *prev_cs; struct member *m; struct privilege *priv; int nfound = 0; - debug_decl(sudo_file_display_priv_short, SUDOERS_DEBUG_NSS) + debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER) TAILQ_FOREACH(priv, &us->privileges, entries) { if (hostlist_matches(pw, &priv->hostlist) != ALLOW) @@ -421,39 +378,41 @@ sudo_file_display_priv_short(struct passwd *pw, struct userspec *us, static bool new_long_entry(struct cmndspec *cs, struct cmndspec *prev_cs) { + debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER) + if (prev_cs == NULL) - return true; + debug_return_bool(true); if (RUNAS_CHANGED(cs, prev_cs) || TAGS_CHANGED(prev_cs->tags, cs->tags)) - return true; + debug_return_bool(true); #ifdef HAVE_PRIV_SET if (cs->privs && (!prev_cs->privs || strcmp(cs->privs, prev_cs->privs) != 0)) - return true; + debug_return_bool(true); if (cs->limitprivs && (!prev_cs->limitprivs || strcmp(cs->limitprivs, prev_cs->limitprivs) != 0)) - return true; + debug_return_bool(true); #endif /* HAVE_PRIV_SET */ #ifdef HAVE_SELINUX if (cs->role && (!prev_cs->role || strcmp(cs->role, prev_cs->role) != 0)) - return true; + debug_return_bool(true); if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0)) - return true; + debug_return_bool(true); #endif /* HAVE_SELINUX */ if (cs->timeout != prev_cs->timeout) - return true; + debug_return_bool(true); if (cs->notbefore != prev_cs->notbefore) - return true; + debug_return_bool(true); if (cs->notafter != prev_cs->notafter) - return true; - return false; + debug_return_bool(true); + debug_return_bool(false); } static int -sudo_file_display_priv_long(struct passwd *pw, struct userspec *us, +display_priv_long(struct passwd *pw, struct userspec *us, struct sudo_lbuf *lbuf) { struct cmndspec *cs, *prev_cs; struct privilege *priv; int nfound = 0, olen; - debug_decl(sudo_file_display_priv_long, SUDOERS_DEBUG_NSS) + debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER) TAILQ_FOREACH(priv, &us->privileges, entries) { if (hostlist_matches(pw, &priv->hostlist) != ALLOW) @@ -559,65 +518,46 @@ sudo_file_display_priv_long(struct passwd *pw, struct userspec *us, debug_return_int(nfound); } -/* XXX - better naming */ -int +static int sudo_display_userspecs(struct userspec_list *usl, struct passwd *pw, struct sudo_lbuf *lbuf) { struct userspec *us; int nfound = 0; - debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_NSS) + debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER) TAILQ_FOREACH(us, usl, entries) { if (userlist_matches(pw, &us->users) != ALLOW) continue; if (long_list) - nfound += sudo_file_display_priv_long(pw, us, lbuf); + nfound += display_priv_long(pw, us, lbuf); else - nfound += sudo_file_display_priv_short(pw, us, lbuf); + nfound += display_priv_short(pw, us, lbuf); } if (sudo_lbuf_error(lbuf)) debug_return_int(-1); debug_return_int(nfound); } -/* - * Returns the number of matching privileges or -1 on error. - */ -static int -sudo_file_display_privs(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - debug_decl(sudo_file_display_priv, SUDOERS_DEBUG_NSS) - - if (nss->handle == NULL) - debug_return_int(0); - - debug_return_int(sudo_display_userspecs(&userspecs, pw, lbuf)); -} - /* * Display matching Defaults entries for the given user on this host. */ -int -sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw, +static int +display_defaults(struct defaults_list *defs, struct passwd *pw, struct sudo_lbuf *lbuf) { struct defaults *d; char *prefix; int nfound = 0; - debug_decl(sudo_file_display_defaults, SUDOERS_DEBUG_NSS) - - if (nss->handle == NULL) - goto done; + debug_decl(display_defaults, SUDOERS_DEBUG_PARSER) if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1])) prefix = " "; else prefix = ", "; - TAILQ_FOREACH(d, &defaults, entries) { + TAILQ_FOREACH(d, defs, entries) { switch (d->type) { case DEFAULTS_HOST: if (hostlist_matches(pw, d->binding) != ALLOW) @@ -636,26 +576,6 @@ sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw, prefix = ", "; nfound++; } - if (sudo_lbuf_error(lbuf)) - debug_return_int(-1); -done: - debug_return_int(nfound); -} - -/* - * Display Defaults entries that are per-runas or per-command - */ -int -sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - int nfound = 0; - debug_decl(sudo_file_display_bound_defaults, SUDOERS_DEBUG_NSS) - - /* XXX - should only print ones that match what the user can do. */ - nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf); - nfound += display_bound_defaults(DEFAULTS_CMND, lbuf); - if (sudo_lbuf_error(lbuf)) debug_return_int(-1); debug_return_int(nfound); @@ -665,14 +585,15 @@ sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw, * Display Defaults entries of the given type. */ static int -display_bound_defaults(int deftype, struct sudo_lbuf *lbuf) +display_bound_defaults_by_type(struct defaults_list *defs, int deftype, + struct sudo_lbuf *lbuf) { struct defaults *d; struct member_list *binding = NULL; struct member *m; char *dsep; int atype, nfound = 0; - debug_decl(display_bound_defaults, SUDOERS_DEBUG_NSS) + debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER) switch (deftype) { case DEFAULTS_HOST: @@ -694,7 +615,7 @@ display_bound_defaults(int deftype, struct sudo_lbuf *lbuf) default: debug_return_int(-1); } - TAILQ_FOREACH(d, &defaults, entries) { + TAILQ_FOREACH(d, defs, entries) { if (d->type != deftype) continue; @@ -721,51 +642,208 @@ display_bound_defaults(int deftype, struct sudo_lbuf *lbuf) } /* - * Returns 0 if the command is allowed, 1 if not or -1 on error. + * Display Defaults entries that are per-runas or per-command + */ +static int +display_bound_defaults(struct defaults_list *defs, struct passwd *pw, + struct sudo_lbuf *lbuf) +{ + int nfound = 0; + debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER) + + /* XXX - should only print ones that match what the user can do. */ + nfound += display_bound_defaults_by_type(defs, DEFAULTS_RUNAS, lbuf); + nfound += display_bound_defaults_by_type(defs, DEFAULTS_CMND, lbuf); + + if (sudo_lbuf_error(lbuf)) + debug_return_int(-1); + debug_return_int(nfound); +} + +static int +output(const char *buf) +{ + struct sudo_conv_message msg; + struct sudo_conv_reply repl; + debug_decl(output, SUDOERS_DEBUG_NSS) + + /* Call conversation function */ + memset(&msg, 0, sizeof(msg)); + msg.msg_type = SUDO_CONV_INFO_MSG; + msg.msg = buf; + memset(&repl, 0, sizeof(repl)); + if (sudo_conv(1, &msg, &repl, NULL) == -1) + debug_return_int(0); + debug_return_int(strlen(buf)); +} + +/* + * Print out privileges for the specified user. + * Returns true on success or -1 on error. */ int -sudo_file_display_cmnd(struct sudo_nss *nss, struct passwd *pw) +display_privs(struct sudo_nss_list *snl, struct passwd *pw) { + struct sudo_nss *nss; + struct sudo_lbuf def_buf, priv_buf; + struct stat sb; + int cols, count, olen, n; + debug_decl(display_privs, SUDOERS_DEBUG_PARSER) + + /* Query all sudoers sources first. */ + TAILQ_FOREACH(nss, snl, entries) { + if (nss->query(nss, pw) == -1) { + /* The query function should have printed an error message. */ + debug_return_int(-1); + } + } + + cols = sudo_user.cols; + if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode)) + cols = 0; + sudo_lbuf_init(&def_buf, output, 4, NULL, cols); + sudo_lbuf_init(&priv_buf, output, 8, NULL, cols); + + sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"), + pw->pw_name, user_srunhost); + count = 0; + TAILQ_FOREACH(nss, snl, entries) { + n = display_defaults(&nss->defaults, pw, &def_buf); + if (n == -1) + goto bad; + count += n; + } + if (count != 0) { + sudo_lbuf_append(&def_buf, "\n\n"); + } else { + /* Undo Defaults header. */ + def_buf.len = 0; + } + + /* Display Runas and Cmnd-specific defaults. */ + olen = def_buf.len; + sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"), + pw->pw_name); + count = 0; + TAILQ_FOREACH(nss, snl, entries) { + n = display_bound_defaults(&nss->defaults, pw, &def_buf); + if (n == -1) + goto bad; + count += n; + } + if (count != 0) { + sudo_lbuf_append(&def_buf, "\n\n"); + } else { + /* Undo Defaults header. */ + def_buf.len = olen; + } + + /* Display privileges from all sources. */ + sudo_lbuf_append(&priv_buf, + _("User %s may run the following commands on %s:\n"), + pw->pw_name, user_srunhost); + count = 0; + TAILQ_FOREACH(nss, snl, entries) { + n = sudo_display_userspecs(&nss->userspecs, pw, &priv_buf); + if (n == -1) + goto bad; + count += n; + } + if (count == 0) { + def_buf.len = 0; + priv_buf.len = 0; + sudo_lbuf_append(&priv_buf, + _("User %s is not allowed to run sudo on %s.\n"), + pw->pw_name, user_shost); + } + if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf)) + goto bad; + + sudo_lbuf_print(&def_buf); + sudo_lbuf_print(&priv_buf); + + sudo_lbuf_destroy(&def_buf); + sudo_lbuf_destroy(&priv_buf); + + debug_return_int(true); +bad: + sudo_lbuf_destroy(&def_buf); + sudo_lbuf_destroy(&priv_buf); + + debug_return_int(-1); +} + +static int +display_cmnd_check(struct sudo_nss *nss, struct passwd *pw, time_t now) +{ + int host_match, runas_match, cmnd_match; struct cmndspec *cs; - struct member *match; struct privilege *priv; struct userspec *us; - int ret = 1; - int host_match, runas_match, cmnd_match; - debug_decl(sudo_file_display_cmnd, SUDOERS_DEBUG_NSS) - - if (nss->handle == NULL) - goto done; + debug_decl(display_cmnd_check, SUDOERS_DEBUG_PARSER) - match = NULL; - TAILQ_FOREACH_REVERSE(us, &userspecs, userspec_list, entries) { + TAILQ_FOREACH_REVERSE(us, &nss->userspecs, userspec_list, entries) { if (userlist_matches(pw, &us->users) != ALLOW) continue; - TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) { host_match = hostlist_matches(pw, &priv->hostlist); if (host_match != ALLOW) continue; TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) { + if (cs->notbefore != UNSPEC) { + if (now < cs->notbefore) + continue; + } + if (cs->notafter != UNSPEC) { + if (now > cs->notafter) + continue; + } runas_match = runaslist_matches(cs->runasuserlist, cs->runasgrouplist, NULL, NULL); if (runas_match == ALLOW) { cmnd_match = cmnd_matches(cs->cmnd); - if (cmnd_match != UNSPEC) { - if (cmnd_match == ALLOW) - match = cs->cmnd; - goto matched; - } + if (cmnd_match != UNSPEC) + debug_return_int(cmnd_match); } } } } - matched: - if (match != NULL && !match->negated) { + debug_return_int(UNSPEC); +} + +/* + * Check user_cmnd against sudoers and print the matching entry if the + * command is allowed. + * Returns true if the command is allowed, false if not or -1 on error. + */ +int +display_cmnd(struct sudo_nss_list *snl, struct passwd *pw) +{ + struct sudo_nss *nss; + int m, match = UNSPEC; + int ret = false; + time_t now; + debug_decl(display_cmnd, SUDOERS_DEBUG_PARSER) + + /* Iterate over each source, checking for the command. */ + time(&now); + TAILQ_FOREACH(nss, snl, entries) { + if (nss->query(nss, pw) == -1) { + /* The query function should have printed an error message. */ + debug_return_int(-1); + } + + m = display_cmnd_check(nss, pw, now); + if (m != UNSPEC) + match = m; + + if (!sudo_nss_can_continue(nss, m)) + break; + } + if (match == ALLOW) { const int len = sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n", safe_cmnd, user_args ? " " : "", user_args ? user_args : ""); - ret = len < 0 ? -1 : 0; + ret = len < 0 ? -1 : true; } -done: debug_return_int(ret); } diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index a24df094b..94fb6c369 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -318,10 +318,13 @@ unsigned char *sudo_filedigest(int fd, const char *file, int digest_type, size_t const char *digest_type_to_name(int digest_type); /* parse.c */ -struct sudo_lbuf; -int sudo_display_userspecs(struct userspec_list *usl, struct passwd *pw, struct sudo_lbuf *lbuf); +struct sudo_nss_list; +int sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated, int pwflag); +int display_privs(struct sudo_nss_list *snl, struct passwd *pw); +int display_cmnd(struct sudo_nss_list *snl, struct passwd *pw); /* fmtsudoers.c */ +struct sudo_lbuf; bool sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct cmndspec *cs, struct cmndspec *prev_cs, bool expand_aliases); bool sudoers_format_default(struct sudo_lbuf *lbuf, struct defaults *d); bool sudoers_format_default_line(struct sudo_lbuf *lbuf, struct defaults *d, struct defaults **next, bool expand_aliases); diff --git a/plugins/sudoers/sssd.c b/plugins/sudoers/sssd.c index bcd70b623..5cd3d3042 100644 --- a/plugins/sudoers/sssd.c +++ b/plugins/sudoers/sssd.c @@ -94,24 +94,19 @@ struct sudo_sss_handle { static int sudo_sss_open(struct sudo_nss *nss); static int sudo_sss_close(struct sudo_nss *nss); static int sudo_sss_parse(struct sudo_nss *nss); +static int sudo_sss_query(struct sudo_nss *nss, struct passwd *pw); static bool sudo_sss_parse_options(struct sudo_sss_handle *handle, - struct sss_sudo_rule *rule); -static int sudo_sss_setdefs(struct sudo_nss *nss); -static int sudo_sss_lookup(struct sudo_nss *nss, int ret, int pwflag); -static int sudo_sss_display_cmnd(struct sudo_nss *nss, struct passwd *pw); -static int sudo_sss_display_defaults(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf); - -static int sudo_sss_display_bound_defaults(struct sudo_nss *nss, - struct passwd *pw, struct sudo_lbuf *lbuf); - -static int sudo_sss_display_privs(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *sudo_lbuf); + struct sss_sudo_rule *rule, + struct defaults_list *defs); +static int sudo_sss_getdefs(struct sudo_nss *nss); static struct sss_sudo_result *sudo_sss_result_get(struct sudo_nss *nss, - struct passwd *pw, - uint32_t *state); + struct passwd *pw); + +static bool sss_to_sudoers(struct sudo_sss_handle *handle, + struct sss_sudo_result *sss_result, + struct userspec_list *sss_userspecs); static void sudo_sss_attrfree(struct sss_sudo_attr *attr) @@ -208,9 +203,7 @@ sudo_sss_rulecpy(struct sss_sudo_rule *dst, const struct sss_sudo_rule *src) #define SUDO_SSS_FILTER_INCLUDE 0 #define SUDO_SSS_FILTER_EXCLUDE 1 -#define SUDO_SSS_STATE_HOSTMATCH 0x01 -#define SUDO_SSS_STATE_USERMATCH 0x02 - +/* XXX - insted of filtering result, include user and host in sudoers parse tree */ static struct sss_sudo_result * sudo_sss_filter_result(struct sudo_sss_handle *handle, struct sss_sudo_result *in_res, @@ -367,12 +360,8 @@ struct sudo_nss sudo_nss_sss = { sudo_sss_open, sudo_sss_close, sudo_sss_parse, - sudo_sss_setdefs, - sudo_sss_lookup, - sudo_sss_display_cmnd, - sudo_sss_display_defaults, - sudo_sss_display_bound_defaults, - sudo_sss_display_privs + sudo_sss_query, + sudo_sss_getdefs }; /* sudo_nss implementation */ @@ -447,7 +436,6 @@ sudo_sss_open(struct sudo_nss *nss) debug_return_int(EFAULT); } - handle->pw = sudo_user.pw; nss->handle = handle; /* @@ -470,21 +458,97 @@ sudo_sss_open(struct sudo_nss *nss) static int sudo_sss_close(struct sudo_nss *nss) { + struct member_list *prev_binding = NULL; + struct defaults *def; + struct userspec *us; struct sudo_sss_handle *handle; debug_decl(sudo_sss_close, SUDOERS_DEBUG_SSSD); if (nss && nss->handle) { handle = nss->handle; sudo_dso_unload(handle->ssslib); + if (handle->pw != NULL) + sudo_pw_delref(handle->pw); free(handle->ipa_host); if (handle->ipa_host != handle->ipa_shost) free(handle->ipa_shost); free(handle); nss->handle = NULL; + + /* XXX - do in main module? */ + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + while ((def = TAILQ_FIRST(&nss->defaults)) != NULL) { + TAILQ_REMOVE(&nss->defaults, def, entries); + free_default(def, &prev_binding); + } } debug_return_int(0); } +/* + * Perform query for user and host and convert to sudoers parse tree. + */ +static int +sudo_sss_query(struct sudo_nss *nss, struct passwd *pw) +{ + struct sudo_sss_handle *handle = nss->handle; + struct sss_sudo_result *sss_result = NULL; + struct userspec *us; + int ret = 0; + debug_decl(sudo_sss_query, SUDOERS_DEBUG_SSSD); + + /* Use cached result if it matches pw. */ + if (handle->pw != NULL) { + if (pw == handle->pw) + goto done; + sudo_pw_delref(handle->pw); + handle->pw = NULL; + } + + /* Free old userspecs, if any. */ + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + + /* Fetch list of sudoRole entries that match user and host. */ + sss_result = sudo_sss_result_get(nss, pw); + + sudo_debug_printf(SUDO_DEBUG_DIAG, + "searching SSSD/LDAP for sudoers entries for user %s, host %s", + pw->pw_name, user_runhost); + + if (sss_result == NULL) + goto done; + + /* Convert to sudoers parse tree. */ + if (!sss_to_sudoers(handle, sss_result, &nss->userspecs)) { + ret = -1; + goto done; + } + + /* Stash a ref to the passwd struct in the handle. */ + sudo_pw_addref(pw); + handle->pw = pw; + +done: + /* Cleanup */ + handle->fn_free_result(sss_result); + if (ret == -1) { + while ((us = TAILQ_FIRST(&nss->userspecs)) != NULL) { + TAILQ_REMOVE(&nss->userspecs, us, entries); + free_userspec(us); + } + } + + sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches"); + + debug_return_int(ret); +} + // ok static int sudo_sss_parse(struct sudo_nss *nss) @@ -494,23 +558,32 @@ sudo_sss_parse(struct sudo_nss *nss) } static int -sudo_sss_setdefs(struct sudo_nss *nss) +sudo_sss_getdefs(struct sudo_nss *nss) { struct sudo_sss_handle *handle = nss->handle; struct sss_sudo_result *sss_result = NULL; struct sss_sudo_rule *sss_rule; + struct member_list *prev_binding = NULL; + struct defaults *def; uint32_t sss_error; unsigned int i; - debug_decl(sudo_sss_setdefs, SUDOERS_DEBUG_SSSD); + debug_decl(sudo_sss_getdefs, SUDOERS_DEBUG_SSSD); if (handle == NULL) debug_return_int(-1); + /* Free old defaults, if any. */ + while ((def = TAILQ_FIRST(&nss->defaults)) != NULL) { + TAILQ_REMOVE(&nss->defaults, def, entries); + free_default(def, &prev_binding); + } + sudo_debug_printf(SUDO_DEBUG_DIAG, "Looking for cn=defaults"); - if (handle->fn_send_recv_defaults(handle->pw->pw_uid, handle->pw->pw_name, - &sss_error, &handle->domainname, - &sss_result) != 0) { + /* NOTE: these are global defaults, user ID and name are not used. */ + if (handle->fn_send_recv_defaults(sudo_user.pw->pw_uid, + sudo_user.pw->pw_name, &sss_error, + &handle->domainname, &sss_result) != 0) { sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_send_recv_defaults: != 0, sss_error=%u", sss_error); debug_return_int(-1); @@ -529,7 +602,7 @@ sudo_sss_setdefs(struct sudo_nss *nss) sudo_debug_printf(SUDO_DEBUG_DIAG, "Parsing cn=defaults, %d/%d", i, sss_result->num_rules); sss_rule = sss_result->rules + i; - if (!sudo_sss_parse_options(handle, sss_rule)) + if (!sudo_sss_parse_options(handle, sss_rule, &nss->defaults)) goto bad; } @@ -541,217 +614,6 @@ bad: debug_return_int(-1); } -static int -sudo_sss_checkpw(struct sudo_nss *nss, struct passwd *pw) -{ - struct sudo_sss_handle *handle = nss->handle; - debug_decl(sudo_sss_checkpw, SUDOERS_DEBUG_SSSD); - - if (pw->pw_name != handle->pw->pw_name || - pw->pw_uid != handle->pw->pw_uid) { - sudo_debug_printf(SUDO_DEBUG_DIAG, - "Requested name or uid don't match the initial once, reinitializing..."); - handle->pw = pw; - - if (sudo_sss_setdefs(nss) != 0) - debug_return_int(-1); - } - - debug_return_int(0); -} - -static int -sudo_sss_check_runas_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *sss_rule, int *group_matched) -{ - const char *host = handle->ipa_host ? handle->ipa_host : user_runhost; - const char *shost = handle->ipa_shost ? handle->ipa_shost : user_srunhost; - char *val, **val_array = NULL; - int ret = false, i; - debug_decl(sudo_sss_check_runas_user, SUDOERS_DEBUG_SSSD); - - /* get the runas user from the entry */ - i = handle->fn_get_values(sss_rule, "sudoRunAsUser", &val_array); - if (i == ENOENT) { - sudo_debug_printf(SUDO_DEBUG_INFO, - "sudoRunAsUser: no result, trying sudoRunAs"); - i = handle->fn_get_values(sss_rule, "sudoRunAs", &val_array); - } - switch (i) { - case 0: - break; - case ENOENT: - sudo_debug_printf(SUDO_DEBUG_INFO, "sudoRunAsUser: no result."); - if (*group_matched == UNSPEC) { - /* We haven't check for sudoRunAsGroup yet, check now. */ - i = handle->fn_get_values(sss_rule, "sudoRunAsGroup", &val_array); - if (i == 0) { - *group_matched = false; - handle->fn_free_values(val_array); - } - } - if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED)) - debug_return_int(UNSPEC); - switch (*group_matched) { - case UNSPEC: - /* - * No runas user or group entries. Match runas_default - * against what the user specified on the command line. - */ - sudo_debug_printf(SUDO_DEBUG_INFO, "Matching against runas_default"); - ret = userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw); - break; - case true: - /* - * No runas user entries but have a matching runas group entry. - * If trying to run as the invoking user, allow it. - */ - sudo_debug_printf(SUDO_DEBUG_INFO, "Matching against user_name"); - if (userpw_matches(user_name, runas_pw->pw_name, runas_pw)) - ret = true; - break; - } - debug_return_int(ret); - default: - sudo_debug_printf(SUDO_DEBUG_INFO, - "handle->fn_get_values(sudoRunAsUser): %d", i); - debug_return_int(false); - } - - /* - * BUG: - * - * if runas is not specified on the command line, the only information - * as to which user to run as is in the runas_default option. We should - * check to see if we have the local option present. Unfortunately we - * don't parse these options until after this routine says yes or no. - * The query has already returned, so we could peek at the attribute - * values here though. - * - * For now just require users to always use -u option unless its set - * in the global defaults. This behaviour is no different than the global - * /etc/sudoers. - * - * Sigh - maybe add this feature later - */ - - /* walk through values returned, looking for a match */ - for (i = 0; val_array[i] != NULL && !ret; ++i) { - val = val_array[i]; - - sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); - - switch (val[0]) { - case '+': - sudo_debug_printf(SUDO_DEBUG_DEBUG, "netgr_"); - if (netgr_matches(val, def_netgroup_tuple ? host : NULL, - def_netgroup_tuple ? shost : NULL, runas_pw->pw_name)) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match"); - ret = true; - } - break; - case '%': - sudo_debug_printf(SUDO_DEBUG_DEBUG, "usergr_"); - if (usergr_matches(val, runas_pw->pw_name, runas_pw)) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match"); - ret = true; - } - break; - case '\0': - /* Empty RunAsUser means run as the invoking user. */ - if (ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) && - userpw_matches(user_name, runas_pw->pw_name, runas_pw)) - ret = true; - break; - case 'A': - if (strcmp(val, "ALL") == 0) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, "ALL => match"); - ret = true; - break; - } - /* FALLTHROUGH */ - sudo_debug_printf(SUDO_DEBUG_DEBUG, "FALLTHROUGH"); - default: - if (userpw_matches(val, runas_pw->pw_name, runas_pw)) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, - "%s == %s (pw_name) => match", val, runas_pw->pw_name); - ret = true; - } - break; - } - - sudo_debug_printf(SUDO_DEBUG_INFO, - "sssd/ldap sudoRunAsUser '%s' ... %s", val, ret ? "MATCH!" : "not"); - } - - handle->fn_free_values(val_array); /* cleanup */ - - debug_return_int(ret); -} - -static int -sudo_sss_check_runas_group(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) -{ - char **val_array = NULL; - char *val; - int ret = false, i; - debug_decl(sudo_sss_check_runas_group, SUDOERS_DEBUG_SSSD); - - /* get the values from the entry */ - i = handle->fn_get_values(rule, "sudoRunAsGroup", &val_array); - switch (i) { - case 0: - break; - case ENOENT: - sudo_debug_printf(SUDO_DEBUG_INFO, "sudoRunAsGroup: no result."); - if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED)) { - if (runas_pw->pw_gid == runas_gr->gr_gid) - ret = true; /* runas group matches passwd db */ - } - debug_return_int(ret); - default: - sudo_debug_printf(SUDO_DEBUG_INFO, - "handle->fn_get_values(sudoRunAsGroup): %d", i); - debug_return_int(false); - } - - /* walk through values returned, looking for a match */ - for (i = 0; val_array[i] != NULL; ++i) { - val = val_array[i]; - sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); - - if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr)) - ret = true; - - sudo_debug_printf(SUDO_DEBUG_INFO, - "sssd/ldap sudoRunAsGroup '%s' ... %s", val, ret ? "MATCH!" : "not"); - } - - handle->fn_free_values(val_array); - - debug_return_int(ret); -} - -/* - * Walk through search results and return true if we have a runas match, - * else false. RunAs info is optional. - */ -static bool -sudo_sss_check_runas(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) -{ - int user_matched = UNSPEC; - int group_matched = UNSPEC; - debug_decl(sudo_sss_check_runas, SUDOERS_DEBUG_SSSD); - - if (rule == NULL) - debug_return_bool(false); - - if (ISSET(sudo_user.flags, RUNAS_GROUP_SPECIFIED)) - group_matched = sudo_sss_check_runas_group(handle, rule); - user_matched = sudo_sss_check_runas_user(handle, rule, &group_matched); - - debug_return_bool(group_matched != false && user_matched != false); -} - static bool sudo_sss_check_host(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) { @@ -867,6 +729,7 @@ sudo_sss_check_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) debug_return_bool(ret); } +/* XXX - insted of filtering result, include user and host in sudoers parse tree */ static int sudo_sss_result_filterp(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule, void *unused) @@ -882,23 +745,20 @@ sudo_sss_result_filterp(struct sudo_sss_handle *handle, } static struct sss_sudo_result * -sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw, uint32_t *state) +sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw) { struct sudo_sss_handle *handle = nss->handle; struct sss_sudo_result *u_sss_result, *f_sss_result; uint32_t sss_error = 0, ret; debug_decl(sudo_sss_result_get, SUDOERS_DEBUG_SSSD); - if (sudo_sss_checkpw(nss, pw) != 0) - debug_return_ptr(NULL); - - sudo_debug_printf(SUDO_DEBUG_DIAG, " username=%s", handle->pw->pw_name); + sudo_debug_printf(SUDO_DEBUG_DIAG, " username=%s", pw->pw_name); sudo_debug_printf(SUDO_DEBUG_DIAG, "domainname=%s", handle->domainname ? handle->domainname : "NULL"); u_sss_result = f_sss_result = NULL; - ret = handle->fn_send_recv(handle->pw->pw_uid, handle->pw->pw_name, + ret = handle->fn_send_recv(pw->pw_uid, pw->pw_name, handle->domainname, &sss_error, &u_sss_result); switch (ret) { @@ -906,10 +766,6 @@ sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw, uint32_t *state) switch (sss_error) { case 0: if (u_sss_result != NULL) { - if (state != NULL) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, "state |= USERMATCH"); - *state |= SUDO_SSS_STATE_USERMATCH; - } sudo_debug_printf(SUDO_DEBUG_INFO, "Received %u rule(s)", u_sss_result->num_rules); } else { @@ -932,16 +788,11 @@ sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw, uint32_t *state) debug_return_ptr(NULL); } + /* XXX - insted of filtering result, include user and host in sudoers parse tree */ f_sss_result = sudo_sss_filter_result(handle, u_sss_result, sudo_sss_result_filterp, SUDO_SSS_FILTER_INCLUDE, NULL); if (f_sss_result != NULL) { - if (f_sss_result->num_rules > 0) { - if (state != NULL) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, "state |= HOSTMATCH"); - *state |= SUDO_SSS_STATE_HOSTMATCH; - } - } sudo_debug_printf(SUDO_DEBUG_DEBUG, "u_sss_result=(%p, %u) => f_sss_result=(%p, %u)", u_sss_result, u_sss_result->num_rules, f_sss_result, f_sss_result->num_rules); @@ -956,132 +807,11 @@ sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw, uint32_t *state) debug_return_ptr(f_sss_result); } -/* - * Search for boolean "option" in sudoOption. - * Returns true if found and allowed, false if negated, else UNSPEC. - */ -static int -sudo_sss_check_bool(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule, - char *option) -{ - char *var, **val_array = NULL; - int i, ret = UNSPEC; - bool negated; - debug_decl(sudo_sss_check_bool, SUDOERS_DEBUG_SSSD); - - if (rule == NULL) - debug_return_int(ret); - - switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { - case 0: - break; - case ENOENT: - sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); - debug_return_int(ret); - default: - sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0"); - debug_return_int(ret); - } - - /* walk through options */ - for (i = 0; val_array[i] != NULL; ++i) { - var = val_array[i]; - sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoOption: '%s'", var); - - negated = sudo_ldap_is_negated(&var); - if (strcmp(var, option) == 0) - ret = negated ? false : true; - } - - handle->fn_free_values(val_array); - - debug_return_int(ret); -} - -/* - * Walk through search results and return true if we have a command match, - * false if disallowed and UNSPEC if not matched. - */ -static int -sudo_sss_check_command(struct sudo_sss_handle *handle, - struct sss_sudo_rule *rule, int *setenv_implied) -{ - char **val_array = NULL, *val; - char *allowed_cmnd, *allowed_args; - int ret = UNSPEC; - bool negated; - unsigned int i; - struct sudo_digest digest, *allowed_digest = NULL; - debug_decl(sudo_sss_check_command, SUDOERS_DEBUG_SSSD); - - if (rule == NULL) - debug_return_int(ret); - - switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) { - case 0: - break; - case ENOENT: - sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); - debug_return_int(ret); - default: - sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0"); - debug_return_int(ret); - } - - for (i = 0; val_array[i] != NULL && ret != false; ++i) { - val = val_array[i]; - - sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); - - /* Match against ALL ? */ - if (strcmp(val, "ALL") == 0) { - ret = true; - if (setenv_implied != NULL) - *setenv_implied = true; - sudo_debug_printf(SUDO_DEBUG_INFO, - "sssd/ldap sudoCommand '%s' ... MATCH!", val); - continue; - } - - /* check for sha-2 digest */ - allowed_digest = sudo_ldap_extract_digest(&val, &digest); - - /* check for !command */ - allowed_cmnd = val; - negated = sudo_ldap_is_negated(&allowed_cmnd); - - /* split optional args away from command */ - allowed_args = strchr(allowed_cmnd, ' '); - if (allowed_args) - *allowed_args++ = '\0'; - - /* check the command like normal */ - if (command_matches(allowed_cmnd, allowed_args, allowed_digest)) { - /* - * If allowed (no bang) set ret but keep on checking. - * If disallowed (bang), exit loop. - */ - ret = negated ? false : true; - } - if (allowed_args != NULL) - allowed_args[-1] = ' '; /* restore val */ - - sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoCommand '%s' ... %s", - val, ret == true ? "MATCH!" : "not"); - if (allowed_digest != NULL) - free(allowed_digest->digest_str); - } - - handle->fn_free_values(val_array); /* more cleanup */ - - debug_return_int(ret); -} - static bool -sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) +sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule, struct defaults_list *defs) { - int i, op; - char *copy, *var, *val, *source = NULL; + int i; + char *source = NULL; bool ret = false; char **val_array = NULL; char **cn_array = NULL; @@ -1101,312 +831,52 @@ sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rul debug_return_bool(false); } - /* get the entry's cn for option error reporting */ + /* Use sudoRole in place of file name in defaults. */ if (handle->fn_get_values(rule, "cn", &cn_array) == 0) { if (cn_array[0] != NULL) { - if (asprintf(&source, "sudoRole %s", cn_array[0]) == -1) { - sudo_warnx(U_("%s: %s"), __func__, - U_("unable to allocate memory")); - source = NULL; - goto done; - } + char *cp; + if (asprintf(&cp, "sudoRole %s", cn_array[0]) == -1) + goto oom; + source = rcstr_dup(cp); + free(cp); + if (source == NULL) + goto oom; } handle->fn_free_values(cn_array); cn_array = NULL; } - - /* walk through options, early ones first */ - for (i = 0; val_array[i] != NULL; i++) { - struct early_default *early; - - if ((copy = strdup(val_array[i])) == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - goto done; - } - op = sudo_ldap_parse_option(copy, &var, &val); - early = is_early_default(var); - if (early != NULL) { - set_early_default(var, val, op, - source ? source : "sudoRole UNKNOWN", 0, false, early); - } - free(copy); + if (source == NULL) { + if ((source = rcstr_dup("sudoRole UNKNOWN")) == NULL) + goto oom; } - run_early_defaults(); - /* walk through options again, skipping early ones */ + /* Walk through options, appending to defs. */ for (i = 0; val_array[i] != NULL; i++) { - if ((copy = strdup(val_array[i])) == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - goto done; - } + char *copy, *var, *val; + int op; + + /* XXX - should not need to copy */ + if ((copy = strdup(val_array[i])) == NULL) + goto oom; op = sudo_ldap_parse_option(copy, &var, &val); - if (is_early_default(var) == NULL) { - set_default(var, val, op, - source ? source : "sudoRole UNKNOWN", 0, false); + if (!sudo_ldap_add_default(var, val, op, source, defs)) { + free(copy); + goto oom; } free(copy); } ret = true; + goto done; + +oom: + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); done: - free(source); + rcstr_delref(source); handle->fn_free_values(val_array); debug_return_bool(ret); } -static int -sudo_sss_lookup(struct sudo_nss *nss, int ret, int pwflag) -{ - int rc, setenv_implied; - - struct sudo_sss_handle *handle = nss->handle; - struct sss_sudo_result *sss_result = NULL; - struct sss_sudo_rule *rule; - uint32_t i, state = 0; - debug_decl(sudo_sss_lookup, SUDOERS_DEBUG_SSSD); - - /* Fetch list of sudoRole entries that match user and host. */ - sss_result = sudo_sss_result_get(nss, sudo_user.pw, &state); - - /* - * The following queries are only determine whether or not a - * password is required, so the order of the entries doesn't matter. - */ - if (pwflag) { - int doauth = UNSPEC; - int matched = UNSPEC; - enum def_tuple pwcheck = - (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; - - sudo_debug_printf(SUDO_DEBUG_INFO, "perform search for pwflag %d", pwflag); - if (sss_result != NULL) { - for (i = 0; i < sss_result->num_rules; i++) { - rule = sss_result->rules + i; - if ((pwcheck == any && doauth != false) || - (pwcheck == all && doauth != true)) { - doauth = !!sudo_sss_check_bool(handle, rule, "authenticate"); - } - if (matched == true) - continue; - /* Only check the command when listing another user. */ - if (user_uid == 0 || list_pw == NULL || - user_uid == list_pw->pw_uid || - sudo_sss_check_command(handle, rule, NULL) == true) { - matched = true; - } - } - } - if (matched == true || user_uid == 0) { - SET(ret, VALIDATE_SUCCESS); - CLR(ret, VALIDATE_FAILURE); - switch (pwcheck) { - case always: - SET(ret, FLAG_CHECK_USER); - break; - case all: - case any: - if (doauth == false) - SET(ret, FLAG_NOPASSWD); - else - CLR(ret, FLAG_NOPASSWD); - break; - default: - break; - } - } - goto done; - } - - sudo_debug_printf(SUDO_DEBUG_DIAG, - "searching SSSD/LDAP for sudoers entries"); - - setenv_implied = false; - if (sss_result != NULL) { - for (i = 0; i < sss_result->num_rules; i++) { - rule = sss_result->rules + i; - if (!sudo_sss_check_runas(handle, rule)) - continue; - rc = sudo_sss_check_command(handle, rule, &setenv_implied); - if (rc != UNSPEC) { - /* We have a match. */ - sudo_debug_printf(SUDO_DEBUG_DIAG, "Command %sallowed", - rc == true ? "" : "NOT "); - if (rc == true) { - sudo_debug_printf(SUDO_DEBUG_DEBUG, "SSSD rule: %p", rule); - /* Apply entry-specific options. */ - if (setenv_implied) - def_setenv = true; - if (sudo_sss_parse_options(handle, rule)) { -#ifdef HAVE_SELINUX - /* Set role/type if not specified on command line. */ - if (user_role == NULL) - user_role = def_role; - if (user_type == NULL) - user_type = def_type; -#endif /* HAVE_SELINUX */ - SET(ret, VALIDATE_SUCCESS); - CLR(ret, VALIDATE_FAILURE); - } else { - SET(ret, VALIDATE_ERROR); - } - } else { - SET(ret, VALIDATE_FAILURE); - CLR(ret, VALIDATE_SUCCESS); - } - break; - } - } - } -done: - handle->fn_free_result(sss_result); - - sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches"); - - if (!ISSET(ret, VALIDATE_SUCCESS)) { - /* No matching entries. */ - if (pwflag && list_pw == NULL) - SET(ret, FLAG_NO_CHECK); - } - - if (pwflag || ISSET(state, SUDO_SSS_STATE_USERMATCH)) - CLR(ret, FLAG_NO_USER); - if (pwflag || ISSET(state, SUDO_SSS_STATE_HOSTMATCH)) - CLR(ret, FLAG_NO_HOST); - - sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_sss_lookup(%d)=0x%02x", - pwflag, ret); - - debug_return_int(ret); -} - -static int -sudo_sss_display_cmnd(struct sudo_nss *nss, struct passwd *pw) -{ - struct sudo_sss_handle *handle = nss->handle; - struct sss_sudo_result *sss_result = NULL; - struct sss_sudo_rule *rule; - unsigned int i; - bool found = false; - debug_decl(sudo_sss_display_cmnd, SUDOERS_DEBUG_SSSD); - - if (handle == NULL) - debug_return_int(-1); - if (sudo_sss_checkpw(nss, pw) != 0) - debug_return_int(-1); - - /* - * The sudo_sss_result_get() function returns all nodes that match - * the user and the host. - */ - sudo_debug_printf(SUDO_DEBUG_DIAG, "sssd/ldap search for command list"); - sss_result = sudo_sss_result_get(nss, pw, NULL); - - if (sss_result == NULL) - goto done; - - for (i = 0; i < sss_result->num_rules; i++) { - rule = sss_result->rules + i; - if (!sudo_sss_check_runas(handle, rule)) - continue; - if (sudo_sss_check_command(handle, rule, NULL) == true) { - found = true; - goto done; - } - } - -done: - if (found) - sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n", - safe_cmnd ? safe_cmnd : user_cmnd, - user_args ? " " : "", user_args ? user_args : ""); - - handle->fn_free_result(sss_result); - - debug_return_int(!found); -} - -static int -sudo_sss_display_defaults(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - struct sudo_sss_handle *handle = nss->handle; - struct sss_sudo_rule *rule; - struct sss_sudo_result *sss_result = NULL; - uint32_t sss_error = 0; - char *prefix, **val_array = NULL; - unsigned int i, j; - int count = 0; - debug_decl(sudo_sss_display_defaults, SUDOERS_DEBUG_SSSD); - - if (handle == NULL) - goto done; - - if (handle->fn_send_recv_defaults(pw->pw_uid, pw->pw_name, - &sss_error, &handle->domainname, - &sss_result) != 0) { - sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_send_recv_defaults: !=0, sss_error=%u", sss_error); - goto done; - } - - if (sss_error == ENOENT) { - sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD."); - goto done; - } else if(sss_error != 0) { - sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error); - goto done; - } - - handle->pw = pw; - - for (i = 0; i < sss_result->num_rules; ++i) { - rule = sss_result->rules + i; - - switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { - case 0: - break; - case ENOENT: - sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); - continue; - default: - sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0"); - continue; - } - - if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1])) - prefix = " "; - else - prefix = ", "; - - for (j = 0; val_array[j] != NULL; ++j) { - struct defaults d; - - sudo_lbuf_append(lbuf, "%s", prefix); - d.op = sudo_ldap_parse_option(val_array[j], &d.var, &d.val); - sudoers_format_default(lbuf, &d); - prefix = ", "; - count++; - } - - handle->fn_free_values(val_array); - val_array = NULL; - } - - handle->fn_free_result(sss_result); -done: - if (sudo_lbuf_error(lbuf)) - debug_return_int(-1); - debug_return_int(count); -} - -// ok -static int -sudo_sss_display_bound_defaults(struct sudo_nss *nss, - struct passwd *pw, struct sudo_lbuf *lbuf) -{ - debug_decl(sudo_sss_display_bound_defaults, SUDOERS_DEBUG_SSSD); - debug_return_int(0); -} - static char * val_array_iter(void **vp) { @@ -1417,19 +887,14 @@ val_array_iter(void **vp) return *val_array; } -static struct userspec_list * -sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_result) +static bool +sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_result, struct userspec_list *sss_userspecs) { - struct userspec_list *sss_userspecs; struct userspec *us; struct member *m; unsigned int i; debug_decl(sss_to_sudoers, SUDOERS_DEBUG_SSSD) - if ((sss_userspecs = calloc(1, sizeof(*sss_userspecs))) == NULL) - goto oom; - TAILQ_INIT(sss_userspecs); - /* We only have a single userspec */ if ((us = calloc(1, sizeof(*us))) == NULL) goto oom; @@ -1439,6 +904,7 @@ sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_resul TAILQ_INSERT_TAIL(sss_userspecs, us, entries); /* The user has already matched, use ALL as wildcard. */ + /* XXX - remove filtering and include sudoUser and host in userspec */ if ((m = calloc(1, sizeof(*m))) == NULL) goto oom; m->type = ALL; @@ -1501,64 +967,14 @@ sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_resul TAILQ_INSERT_TAIL(&us->privileges, priv, entries); } - debug_return_ptr(sss_userspecs); + debug_return_bool(true); oom: sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - if (sss_userspecs != NULL) { - while ((us = TAILQ_FIRST(sss_userspecs)) != NULL) { - TAILQ_REMOVE(sss_userspecs, us, entries); - free_userspec(us); - } - free(sss_userspecs); - } - debug_return_ptr(NULL); -} - -static int -sudo_sss_display_privs(struct sudo_nss *nss, struct passwd *pw, - struct sudo_lbuf *lbuf) -{ - struct sudo_sss_handle *handle = nss->handle; - struct userspec_list *sss_userspecs = NULL; - struct sss_sudo_result *sss_result = NULL; - int ret = 0; - debug_decl(sudo_sss_display_privs, SUDOERS_DEBUG_SSSD); - - if (handle == NULL) - debug_return_int(-1); - if (sudo_sss_checkpw(nss, pw) != 0) - debug_return_int(-1); - - sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap search for command list"); - - sss_result = sudo_sss_result_get(nss, pw, NULL); - - if (sss_result == NULL) - debug_return_int(ret); - - /* Convert to sudoers parse tree. */ - if ((sss_userspecs = sss_to_sudoers(handle, sss_result)) == NULL) { - ret = -1; - goto done; - } - - /* Call common display code. */ - ret = sudo_display_userspecs(sss_userspecs, pw, lbuf); - -done: - /* Cleanup */ - handle->fn_free_result(sss_result); - if (sss_userspecs != NULL) { - struct userspec *us; - while ((us = TAILQ_FIRST(sss_userspecs)) != NULL) { - TAILQ_REMOVE(sss_userspecs, us, entries); - free_userspec(us); - } - free(sss_userspecs); + while ((us = TAILQ_FIRST(sss_userspecs)) != NULL) { + TAILQ_REMOVE(sss_userspecs, us, entries); + free_userspec(us); } - if (sudo_lbuf_error(lbuf)) - debug_return_int(-1); - debug_return_int(ret); + debug_return_bool(false); } #endif /* HAVE_SSSD */ diff --git a/plugins/sudoers/sudo_ldap.h b/plugins/sudoers/sudo_ldap.h index aa4a2bde6..38639341e 100644 --- a/plugins/sudoers/sudo_ldap.h +++ b/plugins/sudoers/sudo_ldap.h @@ -20,8 +20,9 @@ /* Iterators used by sudo_ldap_role_to_priv() to handle bervar ** or char ** */ typedef char * (*sudo_ldap_iter_t)(void **); -/* ldap_common.c */ +/* ldap_util.c */ bool sudo_ldap_is_negated(char **valp); +bool sudo_ldap_add_default(const char *var, const char *val, int op, char *source, struct defaults_list *defs); int sudo_ldap_parse_option(char *optstr, char **varp, char **valp); struct privilege *sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, void *runasgroups, void *cmnds, void *opts, const char *notbefore, const char *notafter, bool warnings, bool store_options, sudo_ldap_iter_t iter); struct sudo_digest *sudo_ldap_extract_digest(char **cmnd, struct sudo_digest *digest); diff --git a/plugins/sudoers/sudo_nss.c b/plugins/sudoers/sudo_nss.c index 355d12ca8..426434cb8 100644 --- a/plugins/sudoers/sudo_nss.c +++ b/plugins/sudoers/sudo_nss.c @@ -43,6 +43,18 @@ extern struct sudo_nss sudo_nss_ldap; extern struct sudo_nss sudo_nss_sss; #endif +static void +sudo_nss_insert(struct sudo_nss_list *snl, struct sudo_nss *nss) +{ + debug_decl(sudo_nss_insert, SUDOERS_DEBUG_NSS) + + TAILQ_INIT(&nss->userspecs); + TAILQ_INIT(&nss->defaults); + TAILQ_INSERT_TAIL(snl, nss, entries); + + debug_return; +} + /* Make sure we have not already inserted the nss entry. */ #define SUDO_NSS_CHECK_UNUSED(nss, tag) \ if (nss.entries.tqe_next != NULL || nss.entries.tqe_prev != NULL) { \ @@ -91,18 +103,18 @@ sudo_read_nss(void) for ((cp = strtok_r(line + 8, " \t", &last)); cp != NULL; (cp = strtok_r(NULL, " \t", &last))) { if (strcasecmp(cp, "files") == 0 && !saw_files) { SUDO_NSS_CHECK_UNUSED(sudo_nss_file, "files"); - TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries); + sudo_nss_insert(&snl, &sudo_nss_file); got_match = saw_files = true; #ifdef HAVE_LDAP } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) { SUDO_NSS_CHECK_UNUSED(sudo_nss_ldap, "ldap"); - TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries); + sudo_nss_insert(&snl, &sudo_nss_ldap); got_match = saw_ldap = true; #endif #ifdef HAVE_SSSD } else if (strcasecmp(cp, "sss") == 0 && !saw_sss) { SUDO_NSS_CHECK_UNUSED(sudo_nss_sss, "sss"); - TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries); + sudo_nss_insert(&snl, &sudo_nss_sss); got_match = saw_sss = true; #endif } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) { @@ -125,7 +137,7 @@ sudo_read_nss(void) nomatch: /* Default to files only if no matches */ if (TAILQ_EMPTY(&snl)) - TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries); + sudo_nss_insert(&snl, &sudo_nss_file); debug_return_ptr(&snl); } @@ -178,20 +190,20 @@ sudo_read_nss(void) if (!saw_files && strncasecmp(cp, "files", 5) == 0 && (isspace((unsigned char)cp[5]) || cp[5] == '\0')) { - TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries); + sudo_nss_insert(&snl, &sudo_nss_file); got_match = saw_files = true; ep = &cp[5]; #ifdef HAVE_LDAP } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 && (isspace((unsigned char)cp[4]) || cp[4] == '\0')) { - TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries); + sudo_nss_insert(&snl, &sudo_nss_ldap); got_match = saw_ldap = true; ep = &cp[4]; #endif #ifdef HAVE_SSSD } else if (!saw_sss && strncasecmp(cp, "sss", 3) == 0 && (isspace((unsigned char)cp[3]) || cp[3] == '\0')) { - TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries); + sudo_nss_insert(&snl, &sudo_nss_sss); got_match = saw_sss = true; ep = &cp[3]; #endif @@ -218,7 +230,7 @@ sudo_read_nss(void) nomatch: /* Default to files only if no matches */ if (TAILQ_EMPTY(&snl)) - TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries); + sudo_nss_insert(&snl, &sudo_nss_file); debug_return_ptr(&snl); } @@ -235,12 +247,12 @@ sudo_read_nss(void) debug_decl(sudo_read_nss, SUDOERS_DEBUG_NSS) # ifdef HAVE_SSSD - TAILQ_INSERT_TAIL(&snl, &sudo_nss_sss, entries); + sudo_nss_insert(&snl, &sudo_nss_sss); # endif # ifdef HAVE_LDAP - TAILQ_INSERT_TAIL(&snl, &sudo_nss_ldap, entries); + sudo_nss_insert(&snl, &sudo_nss_ldap); # endif - TAILQ_INSERT_TAIL(&snl, &sudo_nss_file, entries); + sudo_nss_insert(&snl, &sudo_nss_file); debug_return_ptr(&snl); } @@ -249,132 +261,18 @@ sudo_read_nss(void) #endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */ -static int -output(const char *buf) +bool +sudo_nss_can_continue(struct sudo_nss *nss, int match) { - struct sudo_conv_message msg; - struct sudo_conv_reply repl; - debug_decl(output, SUDOERS_DEBUG_NSS) - - /* Call conversation function */ - memset(&msg, 0, sizeof(msg)); - msg.msg_type = SUDO_CONV_INFO_MSG; - msg.msg = buf; - memset(&repl, 0, sizeof(repl)); - if (sudo_conv(1, &msg, &repl, NULL) == -1) - debug_return_int(0); - debug_return_int(strlen(buf)); -} - -/* - * Print out privileges for the specified user. - * Returns true on success or -1 on error. - */ -int -display_privs(struct sudo_nss_list *snl, struct passwd *pw) -{ - struct sudo_nss *nss; - struct sudo_lbuf defs, privs; - struct stat sb; - int cols, count, olen; - debug_decl(display_privs, SUDOERS_DEBUG_NSS) - - cols = sudo_user.cols; - if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode)) - cols = 0; - sudo_lbuf_init(&defs, output, 4, NULL, cols); - sudo_lbuf_init(&privs, output, 8, NULL, cols); - - /* Display defaults from all sources. */ - sudo_lbuf_append(&defs, _("Matching Defaults entries for %s on %s:\n"), - pw->pw_name, user_srunhost); - count = 0; - TAILQ_FOREACH(nss, snl, entries) { - const int n = nss->display_defaults(nss, pw, &defs); - if (n == -1) - goto bad; - count += n; - } - if (count) { - sudo_lbuf_append(&defs, "\n\n"); - } else { - /* Undo Defaults header. */ - defs.len = 0; - } - - /* Display Runas and Cmnd-specific defaults from all sources. */ - olen = defs.len; - sudo_lbuf_append(&defs, _("Runas and Command-specific defaults for %s:\n"), - pw->pw_name); - count = 0; - TAILQ_FOREACH(nss, snl, entries) { - const int n = nss->display_bound_defaults(nss, pw, &defs); - if (n == -1) - goto bad; - count += n; - } - if (count) { - sudo_lbuf_append(&defs, "\n\n"); - } else { - /* Undo Defaults header. */ - defs.len = olen; - } + debug_decl(sudo_nss_should_continue, SUDOERS_DEBUG_NSS) - /* Display privileges from all sources. */ - sudo_lbuf_append(&privs, - _("User %s may run the following commands on %s:\n"), - pw->pw_name, user_srunhost); - count = 0; - TAILQ_FOREACH(nss, snl, entries) { - const int n = nss->display_privs(nss, pw, &privs); - if (n == -1) - goto bad; - count += n; - } - if (count == 0) { - defs.len = 0; - privs.len = 0; - sudo_lbuf_append(&privs, - _("User %s is not allowed to run sudo on %s.\n"), - pw->pw_name, user_shost); - } - if (sudo_lbuf_error(&defs) || sudo_lbuf_error(&privs)) - goto bad; + /* Handle [NOTFOUND=return] */ + if (nss->ret_if_notfound && match == UNSPEC) + debug_return_bool(false); - sudo_lbuf_print(&defs); - sudo_lbuf_print(&privs); + /* Handle [SUCCESS=return] */ + if (nss->ret_if_found && match != UNSPEC) + debug_return_bool(false); - sudo_lbuf_destroy(&defs); - sudo_lbuf_destroy(&privs); - - debug_return_int(true); -bad: - sudo_lbuf_destroy(&defs); - sudo_lbuf_destroy(&privs); - - debug_return_int(-1); -} - -/* - * Check user_cmnd against sudoers and print the matching entry if the - * command is allowed. - * Returns true if the command is allowed, false if not or -1 on error. - */ -int -display_cmnd(struct sudo_nss_list *snl, struct passwd *pw) -{ - struct sudo_nss *nss; - debug_decl(display_cmnd, SUDOERS_DEBUG_NSS) - - /* XXX - display_cmnd return value is backwards */ - /* XXX - doesn't handle commands allowed by one backend denied by another. */ - TAILQ_FOREACH(nss, snl, entries) { - switch (nss->display_cmnd(nss, pw)) { - case 0: - debug_return_int(true); - case -1: - debug_return_int(-1); - } - } - debug_return_int(false); + debug_return_bool(true); } diff --git a/plugins/sudoers/sudo_nss.h b/plugins/sudoers/sudo_nss.h index 8569c9ba6..20909f577 100644 --- a/plugins/sudoers/sudo_nss.h +++ b/plugins/sudoers/sudo_nss.h @@ -25,19 +25,18 @@ struct sudo_nss { int (*open)(struct sudo_nss *nss); int (*close)(struct sudo_nss *nss); int (*parse)(struct sudo_nss *nss); - int (*setdefs)(struct sudo_nss *nss); - int (*lookup)(struct sudo_nss *nss, int, int); - int (*display_cmnd)(struct sudo_nss *nss, struct passwd *); - int (*display_defaults)(struct sudo_nss *nss, struct passwd *, struct sudo_lbuf *); - int (*display_bound_defaults)(struct sudo_nss *nss, struct passwd *, struct sudo_lbuf *); - int (*display_privs)(struct sudo_nss *nss, struct passwd *, struct sudo_lbuf *); + int (*query)(struct sudo_nss *nss, struct passwd *pw); + int (*getdefs)(struct sudo_nss *nss); void *handle; - short ret_if_found; - short ret_if_notfound; + bool ret_if_found; + bool ret_if_notfound; + struct defaults_list defaults; + struct userspec_list userspecs; }; TAILQ_HEAD(sudo_nss_list, sudo_nss); struct sudo_nss_list *sudo_read_nss(void); +bool sudo_nss_can_continue(struct sudo_nss *nss, int match); #endif /* SUDOERS_NSS_H */ diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 16ef7e1ae..29ce411f5 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993-1996, 1998-2017 Todd C. Miller + * Copyright (c) 1993-1996, 1998-2018 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -59,6 +59,7 @@ #include #include "sudoers.h" +#include "parse.h" #include "auth/sudo_auth.h" #ifndef HAVE_GETADDRINFO @@ -189,11 +190,13 @@ sudoers_policy_init(void *info, char * const envp[]) */ sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); sudo_warn_set_locale_func(sudoers_warn_setlocale); + init_parser(sudoers_file, false); TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { if (nss->open(nss) == 0 && nss->parse(nss) == 0) { sources++; - if (nss->setdefs(nss) != 0) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, + if (nss->getdefs(nss) != 0 || !update_defaults(&nss->defaults, + SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { + log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("problem with defaults entries")); } } else { @@ -228,7 +231,6 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], char *iolog_path = NULL; mode_t cmnd_umask = ACCESSPERMS; struct sudo_nss *nss; - int nopass = -1; int cmnd_status = -1, oldlocale, validated; int ret = -1; debug_decl(sudoers_policy_main, SUDOERS_DEBUG_PLUGIN) @@ -307,54 +309,12 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], * Check sudoers sources, using the locale specified in sudoers. */ sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); - validated = FLAG_NO_USER | FLAG_NO_HOST; - TAILQ_FOREACH(nss, snl, entries) { - validated = nss->lookup(nss, validated, pwflag); - - /* - * The NOPASSWD tag needs special handling among all sources - * in -l or -v mode. - */ - if (pwflag) { - enum def_tuple pwcheck = - (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; - switch (pwcheck) { - case all: - if (!ISSET(validated, FLAG_NOPASSWD)) - nopass = false; - else if (nopass == -1) - nopass = true; - break; - case any: - if (ISSET(validated, FLAG_NOPASSWD)) - nopass = true; - break; - case never: - nopass = true; - break; - case always: - nopass = false; - break; - default: - break; - } - } - - if (ISSET(validated, VALIDATE_ERROR)) { - /* The lookup function should have printed an error. */ - goto done; - } else if (ISSET(validated, VALIDATE_SUCCESS)) { - /* Handle [SUCCESS=return] */ - if (nss->ret_if_found) - break; - } else { - /* Handle [NOTFOUND=return] */ - if (nss->ret_if_notfound) - break; - } + validated = sudoers_lookup(snl, sudo_user.pw, FLAG_NO_USER | FLAG_NO_HOST, + pwflag); + if (ISSET(validated, VALIDATE_ERROR)) { + /* The lookup function should have printed an error. */ + goto done; } - if (pwflag && nopass == true) - def_authenticate = false; /* Restore user's locale. */ sudoers_setlocale(oldlocale, NULL); @@ -546,6 +506,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], } if (def_group_plugin) group_plugin_unload(); + init_parser(NULL, false); if (ISSET(sudo_mode, (MODE_VALIDATE|MODE_CHECK|MODE_LIST))) { /* ret already set appropriately */ @@ -801,8 +762,9 @@ init_vars(char * const envp[]) static int set_cmnd(void) { - int ret = FOUND; + struct sudo_nss *nss; char *path = user_path; + int ret = FOUND; debug_decl(set_cmnd, SUDOERS_DEBUG_PLUGIN) /* Allocate user_stat for find_path() and match functions. */ @@ -890,9 +852,11 @@ set_cmnd(void) else user_base = user_cmnd; - if (!update_defaults(SETDEF_CMND, false)) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, - N_("problem with defaults entries")); + TAILQ_FOREACH(nss, snl, entries) { + if (!update_defaults(&nss->defaults, SETDEF_CMND, false)) { + log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, + N_("problem with defaults entries")); + } } debug_return_int(ret); diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index e0f3a3959..7b6f74fb6 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -137,7 +137,6 @@ struct sudo_user { #define FLAG_NO_CHECK 0x080 #define FLAG_NON_INTERACTIVE 0x100 #define FLAG_BAD_PASSWORD 0x200 -#define FLAG_NOPASSWD 0x400 /* * find_path()/set_cmnd() return values @@ -300,10 +299,6 @@ void dump_auth_methods(void); /* getspwuid.c */ char *sudo_getepw(const struct passwd *); -/* sudo_nss.c */ -int display_privs(struct sudo_nss_list *, struct passwd *); -int display_cmnd(struct sudo_nss_list *, struct passwd *); - /* pwutil.c */ typedef struct cache_item * (*sudo_make_pwitem_t)(uid_t uid, const char *user); typedef struct cache_item * (*sudo_make_gritem_t)(gid_t gid, const char *group); diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c index 7f5469149..556f4ae69 100644 --- a/plugins/sudoers/testsudoers.c +++ b/plugins/sudoers/testsudoers.c @@ -285,7 +285,7 @@ main(int argc, char *argv[]) (void) fputs("Parses OK", stdout); } - if (!update_defaults(SETDEF_ALL, false)) + if (!update_defaults(&defaults, SETDEF_ALL, false)) (void) fputs(" (problem with defaults entries)", stdout); puts("."); diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c index a9963e727..dd9c03d67 100644 --- a/plugins/sudoers/visudo.c +++ b/plugins/sudoers/visudo.c @@ -246,7 +246,8 @@ main(int argc, char *argv[]) init_parser(sudoers_file, quiet); sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); (void) sudoersparse(); - (void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, quiet); + (void) update_defaults(&defaults, SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, + quiet); sudoers_setlocale(oldlocale, NULL); editor = get_editor(&editor_argc, &editor_argv); @@ -601,7 +602,8 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv, } fclose(sudoersin); if (!parse_error) { - (void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, true); + (void) update_defaults(&defaults, + SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, true); check_defaults_and_aliases(strict, quiet); } sudoers_setlocale(oldlocale, NULL); @@ -918,7 +920,8 @@ check_syntax(const char *sudoers_file, bool quiet, bool strict, bool oldperms) sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } if (!parse_error) { - (void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, true); + (void) update_defaults(&defaults, + SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, true); check_defaults_and_aliases(strict, quiet); } sudoers_setlocale(oldlocale, NULL);