diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c index dfc518fad..7c5529867 100644 --- a/plugins/sudoers/cvtsudoers.c +++ b/plugins/sudoers/cvtsudoers.c @@ -134,7 +134,7 @@ main(int argc, char *argv[]) textdomain("sudoers"); /* Initialize early, before any "goto done". */ - init_parse_tree(&merged_tree, NULL, NULL); + init_parse_tree(&merged_tree, NULL, NULL, NULL); /* Read debug and plugin sections of sudo.conf. */ if (sudo_conf_read(NULL, SUDO_CONF_DEBUG|SUDO_CONF_PLUGINS) == -1) @@ -390,7 +390,7 @@ main(int argc, char *argv[]) parse_tree = malloc(sizeof(*parse_tree)); if (parse_tree == NULL) sudo_fatalx("%s", U_("unable to allocate memory")); - init_parse_tree(parse_tree, lhost, shost); + init_parse_tree(parse_tree, lhost, shost, NULL); TAILQ_INSERT_TAIL(&parse_trees, parse_tree, entries); /* Setup defaults data structures. */ diff --git a/plugins/sudoers/file.c b/plugins/sudoers/file.c index 3ccf14136..889a58e13 100644 --- a/plugins/sudoers/file.c +++ b/plugins/sudoers/file.c @@ -74,7 +74,7 @@ sudo_file_open(struct sudo_nss *nss) if (handle != NULL) { handle->fp = open_sudoers(sudoers_file, false, NULL); if (handle->fp != NULL) { - init_parse_tree(&handle->parse_tree, NULL, NULL); + init_parse_tree(&handle->parse_tree, NULL, NULL, nss); } else { free(handle); handle = NULL; diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c index e35aca25a..04f4f6983 100644 --- a/plugins/sudoers/gram.c +++ b/plugins/sudoers/gram.c @@ -3921,13 +3921,15 @@ free_userspec(struct userspec *us) * Takes ownership of lhost and shost. */ void -init_parse_tree(struct sudoers_parse_tree *parse_tree, char *lhost, char *shost) +init_parse_tree(struct sudoers_parse_tree *parse_tree, char *lhost, char *shost, + struct sudo_nss *nss) { TAILQ_INIT(&parse_tree->userspecs); TAILQ_INIT(&parse_tree->defaults); parse_tree->aliases = NULL; parse_tree->shost = shost; parse_tree->lhost = lhost; + parse_tree->nss = nss; } /* diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index 21e9fb615..da7960440 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -1738,13 +1738,15 @@ free_userspec(struct userspec *us) * Takes ownership of lhost and shost. */ void -init_parse_tree(struct sudoers_parse_tree *parse_tree, char *lhost, char *shost) +init_parse_tree(struct sudoers_parse_tree *parse_tree, char *lhost, char *shost, + struct sudo_nss *nss) { TAILQ_INIT(&parse_tree->userspecs); TAILQ_INIT(&parse_tree->defaults); parse_tree->aliases = NULL; parse_tree->shost = shost; parse_tree->lhost = lhost; + parse_tree->nss = nss; } /* diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c index 90aa35eae..616a2a69a 100644 --- a/plugins/sudoers/ldap.c +++ b/plugins/sudoers/ldap.c @@ -348,7 +348,9 @@ sudo_ldap_check_non_unix_group(LDAP *ld, LDAPMessage *entry, struct passwd *pw) negated = true; } if (*val == '+') { - if (netgr_matches(val, def_netgroup_tuple ? user_runhost : NULL, + /* Custom innetgr() function not used here. */ + if (netgr_matches(NULL, val, + def_netgroup_tuple ? user_runhost : NULL, def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name)) ret = true; DPRINTF2("ldap sudoUser netgroup '%s%s' ... %s", @@ -1766,7 +1768,7 @@ sudo_ldap_open(struct sudo_nss *nss) } handle->ld = ld; /* handle->pw = NULL; */ - init_parse_tree(&handle->parse_tree, NULL, NULL); + init_parse_tree(&handle->parse_tree, NULL, NULL, nss); nss->handle = handle; done: diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c index 445bdb248..40b7ae614 100644 --- a/plugins/sudoers/match.c +++ b/plugins/sudoers/match.c @@ -79,7 +79,7 @@ user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, matched = !m->negated; break; case NETGROUP: - if (netgr_matches(m->name, + if (netgr_matches(parse_tree->nss, m->name, def_netgroup_tuple ? lhost : NULL, def_netgroup_tuple ? shost : NULL, pw->pw_name)) matched = !m->negated; @@ -174,7 +174,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree, user_matched = !m->negated; break; case NETGROUP: - if (netgr_matches(m->name, + if (netgr_matches(parse_tree->nss, m->name, def_netgroup_tuple ? lhost : NULL, def_netgroup_tuple ? shost : NULL, runas_pw->pw_name)) @@ -332,7 +332,7 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, matched = !m->negated; break; case NETGROUP: - if (netgr_matches(m->name, lhost, shost, + if (netgr_matches(parse_tree->nss, m->name, lhost, shost, def_netgroup_tuple ? pw->pw_name : NULL)) matched = !m->negated; break; @@ -670,7 +670,8 @@ sudo_getdomainname(void) * in which case that argument is not checked... */ bool -netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user) +netgr_matches(struct sudo_nss *nss, const char *netgr, + const char *lhost, const char *shost, const char *user) { #ifdef HAVE_INNETGR const char *domain; @@ -683,7 +684,6 @@ netgr_matches(const char *netgr, const char *lhost, const char *shost, const cha debug_return_bool(false); } -#ifdef HAVE_INNETGR /* make sure we have a valid netgroup, sudo style */ if (*netgr++ != '+') { sudo_debug_printf(SUDO_DEBUG_DIAG, "netgroup %s has no leading '+'", @@ -694,16 +694,42 @@ netgr_matches(const char *netgr, const char *lhost, const char *shost, const cha /* get the domain name (if any) */ domain = sudo_getdomainname(); - if (innetgr(netgr, lhost, user, domain)) - rc = true; - else if (lhost != shost && innetgr(netgr, shost, user, domain)) - rc = true; + /* Use nss-specific innetgr() function if available. */ + if (nss != NULL && nss->innetgr != NULL) { + switch (nss->innetgr(nss, netgr, lhost, user, domain)) { + case 0: + if (lhost != shost) { + if (nss->innetgr(nss, netgr, shost, user, domain) == 1) + rc = true; + } + goto done; + case 1: + rc = true; + goto done; + default: + /* Not supported, use system innetgr(3). */ + break; + } + } +#ifdef HAVE_INNETGR + /* Use system innetgr() function. */ + if (innetgr(netgr, lhost, user, domain) == 1) { + rc = true; + } else if (lhost != shost) { + if (innetgr(netgr, shost, user, domain) == 1) + rc = true; + } +#else + sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO, + "%s: no system netgroup support", __func__); +#endif /* HAVE_INNETGR */ + +done: sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, "netgroup %s matches (%s|%s, %s, %s): %s", netgr, lhost ? lhost : "", shost ? shost : "", user ? user : "", domain ? domain : "", rc ? "true" : "false"); -#endif /* HAVE_INNETGR */ debug_return_bool(rc); } diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 521f121c8..1ef950db0 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -302,12 +302,14 @@ struct defaults { /* * Parsed sudoers policy. */ +struct sudo_nss; struct sudoers_parse_tree { TAILQ_ENTRY(sudoers_parse_tree) entries; struct userspec_list userspecs; struct defaults_list defaults; struct rbtree *aliases; char *shost, *lhost; + struct sudo_nss *nss; }; /* @@ -380,7 +382,7 @@ void free_userspec(struct userspec *us); void free_userspecs(struct userspec_list *usl); void free_default(struct defaults *def); void free_defaults(struct defaults_list *defs); -void init_parse_tree(struct sudoers_parse_tree *parse_tree, char *lhost, char *shost); +void init_parse_tree(struct sudoers_parse_tree *parse_tree, char *lhost, char *shost, struct sudo_nss *nss); void free_parse_tree(struct sudoers_parse_tree *parse_tree); void reparent_parse_tree(struct sudoers_parse_tree *new_tree); bool parser_leak_add(enum parser_leak_types type, void *v); @@ -401,7 +403,7 @@ struct group; struct passwd; bool group_matches(const char *sudoers_group, const struct group *gr); bool hostname_matches(const char *shost, const char *lhost, const char *pattern); -bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user); +bool netgr_matches(struct sudo_nss *nss, const char *netgr, const char *lhost, const char *shost, const char *user); bool usergr_matches(const char *group, const char *user, const struct passwd *pw); bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw); int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot, struct cmnd_info *info); diff --git a/plugins/sudoers/regress/fuzz/fuzz_sudoers.c b/plugins/sudoers/regress/fuzz/fuzz_sudoers.c index 15dc72a21..aaa737696 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_sudoers.c +++ b/plugins/sudoers/regress/fuzz/fuzz_sudoers.c @@ -304,7 +304,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } /* Only one sudoers source, the sudoers file itself. */ - init_parse_tree(&parse_tree, NULL, NULL); + init_parse_tree(&parse_tree, NULL, NULL, NULL); memset(&sudo_nss_fuzz, 0, sizeof(sudo_nss_fuzz)); sudo_nss_fuzz.parse_tree = &parse_tree; sudo_nss_fuzz.query = sudo_fuzz_query; diff --git a/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c b/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c index f3a28a402..941ce94aa 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c +++ b/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c @@ -139,7 +139,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) /* Initialize defaults and parse LDIF-format sudoers. */ init_defaults(); - init_parse_tree(&parse_tree, NULL, NULL); + init_parse_tree(&parse_tree, NULL, NULL, NULL); sudoers_parse_ldif(&parse_tree, fp, "ou=SUDOers,dc=sudo,dc=ws", true); /* Cleanup. */ diff --git a/plugins/sudoers/sssd.c b/plugins/sudoers/sssd.c index d4c70b92a..1b0aa5512 100644 --- a/plugins/sudoers/sssd.c +++ b/plugins/sudoers/sssd.c @@ -203,7 +203,7 @@ sudo_sss_check_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) switch (*val) { case '+': /* Netgroup spec found, check membership. */ - if (netgr_matches(val, def_netgroup_tuple ? host : NULL, + if (netgr_matches(NULL, val, def_netgroup_tuple ? host : NULL, def_netgroup_tuple ? shost : NULL, handle->pw->pw_name)) { ret = true; } @@ -638,7 +638,8 @@ sudo_sss_open(struct sudo_nss *nss) } /* The "parse tree" contains userspecs, defaults, aliases and hostnames. */ - init_parse_tree(&handle->parse_tree, handle->ipa_host, handle->ipa_shost); + init_parse_tree(&handle->parse_tree, handle->ipa_host, handle->ipa_shost, + nss); nss->handle = handle; sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle); diff --git a/plugins/sudoers/sudo_nss.h b/plugins/sudoers/sudo_nss.h index a7487cb68..2c195979b 100644 --- a/plugins/sudoers/sudo_nss.h +++ b/plugins/sudoers/sudo_nss.h @@ -33,6 +33,8 @@ struct sudo_nss { struct sudoers_parse_tree *(*parse)(struct sudo_nss *nss); int (*query)(struct sudo_nss *nss, struct passwd *pw); int (*getdefs)(struct sudo_nss *nss); + int (*innetgr)(struct sudo_nss *nss, const char *netgr, const char *host, + const char *user, const char *domain); void *handle; struct sudoers_parse_tree *parse_tree; bool ret_if_found;