Add per-source innetgr function pointer and use it in netgr_matches().

This will be used to implement LDAP-specific netgroup lookups when
netgroup_base is set in ldap.conf.
This commit is contained in:
Todd C. Miller
2023-03-08 13:44:22 -07:00
parent d2582c2cdb
commit 0aad96bba1
11 changed files with 60 additions and 23 deletions

View File

@@ -134,7 +134,7 @@ main(int argc, char *argv[])
textdomain("sudoers"); textdomain("sudoers");
/* Initialize early, before any "goto done". */ /* 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. */ /* Read debug and plugin sections of sudo.conf. */
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG|SUDO_CONF_PLUGINS) == -1) 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)); parse_tree = malloc(sizeof(*parse_tree));
if (parse_tree == NULL) if (parse_tree == NULL)
sudo_fatalx("%s", U_("unable to allocate memory")); 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); TAILQ_INSERT_TAIL(&parse_trees, parse_tree, entries);
/* Setup defaults data structures. */ /* Setup defaults data structures. */

View File

@@ -74,7 +74,7 @@ sudo_file_open(struct sudo_nss *nss)
if (handle != NULL) { if (handle != NULL) {
handle->fp = open_sudoers(sudoers_file, false, NULL); handle->fp = open_sudoers(sudoers_file, false, NULL);
if (handle->fp != NULL) { if (handle->fp != NULL) {
init_parse_tree(&handle->parse_tree, NULL, NULL); init_parse_tree(&handle->parse_tree, NULL, NULL, nss);
} else { } else {
free(handle); free(handle);
handle = NULL; handle = NULL;

View File

@@ -3921,13 +3921,15 @@ free_userspec(struct userspec *us)
* Takes ownership of lhost and shost. * Takes ownership of lhost and shost.
*/ */
void 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->userspecs);
TAILQ_INIT(&parse_tree->defaults); TAILQ_INIT(&parse_tree->defaults);
parse_tree->aliases = NULL; parse_tree->aliases = NULL;
parse_tree->shost = shost; parse_tree->shost = shost;
parse_tree->lhost = lhost; parse_tree->lhost = lhost;
parse_tree->nss = nss;
} }
/* /*

View File

@@ -1738,13 +1738,15 @@ free_userspec(struct userspec *us)
* Takes ownership of lhost and shost. * Takes ownership of lhost and shost.
*/ */
void 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->userspecs);
TAILQ_INIT(&parse_tree->defaults); TAILQ_INIT(&parse_tree->defaults);
parse_tree->aliases = NULL; parse_tree->aliases = NULL;
parse_tree->shost = shost; parse_tree->shost = shost;
parse_tree->lhost = lhost; parse_tree->lhost = lhost;
parse_tree->nss = nss;
} }
/* /*

View File

@@ -348,7 +348,9 @@ sudo_ldap_check_non_unix_group(LDAP *ld, LDAPMessage *entry, struct passwd *pw)
negated = true; negated = true;
} }
if (*val == '+') { 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)) def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name))
ret = true; ret = true;
DPRINTF2("ldap sudoUser netgroup '%s%s' ... %s", DPRINTF2("ldap sudoUser netgroup '%s%s' ... %s",
@@ -1766,7 +1768,7 @@ sudo_ldap_open(struct sudo_nss *nss)
} }
handle->ld = ld; handle->ld = ld;
/* handle->pw = NULL; */ /* handle->pw = NULL; */
init_parse_tree(&handle->parse_tree, NULL, NULL); init_parse_tree(&handle->parse_tree, NULL, NULL, nss);
nss->handle = handle; nss->handle = handle;
done: done:

View File

@@ -79,7 +79,7 @@ user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
matched = !m->negated; matched = !m->negated;
break; break;
case NETGROUP: case NETGROUP:
if (netgr_matches(m->name, if (netgr_matches(parse_tree->nss, m->name,
def_netgroup_tuple ? lhost : NULL, def_netgroup_tuple ? lhost : NULL,
def_netgroup_tuple ? shost : NULL, pw->pw_name)) def_netgroup_tuple ? shost : NULL, pw->pw_name))
matched = !m->negated; matched = !m->negated;
@@ -174,7 +174,7 @@ runaslist_matches(struct sudoers_parse_tree *parse_tree,
user_matched = !m->negated; user_matched = !m->negated;
break; break;
case NETGROUP: case NETGROUP:
if (netgr_matches(m->name, if (netgr_matches(parse_tree->nss, m->name,
def_netgroup_tuple ? lhost : NULL, def_netgroup_tuple ? lhost : NULL,
def_netgroup_tuple ? shost : NULL, def_netgroup_tuple ? shost : NULL,
runas_pw->pw_name)) runas_pw->pw_name))
@@ -332,7 +332,7 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
matched = !m->negated; matched = !m->negated;
break; break;
case NETGROUP: 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)) def_netgroup_tuple ? pw->pw_name : NULL))
matched = !m->negated; matched = !m->negated;
break; break;
@@ -670,7 +670,8 @@ sudo_getdomainname(void)
* in which case that argument is not checked... * in which case that argument is not checked...
*/ */
bool 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 #ifdef HAVE_INNETGR
const char *domain; const char *domain;
@@ -683,7 +684,6 @@ netgr_matches(const char *netgr, const char *lhost, const char *shost, const cha
debug_return_bool(false); debug_return_bool(false);
} }
#ifdef HAVE_INNETGR
/* make sure we have a valid netgroup, sudo style */ /* make sure we have a valid netgroup, sudo style */
if (*netgr++ != '+') { if (*netgr++ != '+') {
sudo_debug_printf(SUDO_DEBUG_DIAG, "netgroup %s has no leading '+'", 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) */ /* get the domain name (if any) */
domain = sudo_getdomainname(); domain = sudo_getdomainname();
if (innetgr(netgr, lhost, user, domain)) /* Use nss-specific innetgr() function if available. */
rc = true; if (nss != NULL && nss->innetgr != NULL) {
else if (lhost != shost && innetgr(netgr, shost, user, domain)) switch (nss->innetgr(nss, netgr, lhost, user, domain)) {
rc = true; 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, sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"netgroup %s matches (%s|%s, %s, %s): %s", netgr, lhost ? lhost : "", "netgroup %s matches (%s|%s, %s, %s): %s", netgr, lhost ? lhost : "",
shost ? shost : "", user ? user : "", domain ? domain : "", shost ? shost : "", user ? user : "", domain ? domain : "",
rc ? "true" : "false"); rc ? "true" : "false");
#endif /* HAVE_INNETGR */
debug_return_bool(rc); debug_return_bool(rc);
} }

View File

@@ -302,12 +302,14 @@ struct defaults {
/* /*
* Parsed sudoers policy. * Parsed sudoers policy.
*/ */
struct sudo_nss;
struct sudoers_parse_tree { struct sudoers_parse_tree {
TAILQ_ENTRY(sudoers_parse_tree) entries; TAILQ_ENTRY(sudoers_parse_tree) entries;
struct userspec_list userspecs; struct userspec_list userspecs;
struct defaults_list defaults; struct defaults_list defaults;
struct rbtree *aliases; struct rbtree *aliases;
char *shost, *lhost; 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_userspecs(struct userspec_list *usl);
void free_default(struct defaults *def); void free_default(struct defaults *def);
void free_defaults(struct defaults_list *defs); 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 free_parse_tree(struct sudoers_parse_tree *parse_tree);
void reparent_parse_tree(struct sudoers_parse_tree *new_tree); void reparent_parse_tree(struct sudoers_parse_tree *new_tree);
bool parser_leak_add(enum parser_leak_types type, void *v); bool parser_leak_add(enum parser_leak_types type, void *v);
@@ -401,7 +403,7 @@ struct group;
struct passwd; struct passwd;
bool group_matches(const char *sudoers_group, const struct group *gr); bool group_matches(const char *sudoers_group, const struct group *gr);
bool hostname_matches(const char *shost, const char *lhost, const char *pattern); 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 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); 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); int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot, struct cmnd_info *info);

View File

@@ -304,7 +304,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
} }
/* Only one sudoers source, the sudoers file itself. */ /* 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)); memset(&sudo_nss_fuzz, 0, sizeof(sudo_nss_fuzz));
sudo_nss_fuzz.parse_tree = &parse_tree; sudo_nss_fuzz.parse_tree = &parse_tree;
sudo_nss_fuzz.query = sudo_fuzz_query; sudo_nss_fuzz.query = sudo_fuzz_query;

View File

@@ -139,7 +139,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
/* Initialize defaults and parse LDIF-format sudoers. */ /* Initialize defaults and parse LDIF-format sudoers. */
init_defaults(); 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); sudoers_parse_ldif(&parse_tree, fp, "ou=SUDOers,dc=sudo,dc=ws", true);
/* Cleanup. */ /* Cleanup. */

View File

@@ -203,7 +203,7 @@ sudo_sss_check_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule)
switch (*val) { switch (*val) {
case '+': case '+':
/* Netgroup spec found, check membership. */ /* 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)) { def_netgroup_tuple ? shost : NULL, handle->pw->pw_name)) {
ret = true; ret = true;
} }
@@ -638,7 +638,8 @@ sudo_sss_open(struct sudo_nss *nss)
} }
/* The "parse tree" contains userspecs, defaults, aliases and hostnames. */ /* 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; nss->handle = handle;
sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle); sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle);

View File

@@ -33,6 +33,8 @@ struct sudo_nss {
struct sudoers_parse_tree *(*parse)(struct sudo_nss *nss); struct sudoers_parse_tree *(*parse)(struct sudo_nss *nss);
int (*query)(struct sudo_nss *nss, struct passwd *pw); int (*query)(struct sudo_nss *nss, struct passwd *pw);
int (*getdefs)(struct sudo_nss *nss); 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; void *handle;
struct sudoers_parse_tree *parse_tree; struct sudoers_parse_tree *parse_tree;
bool ret_if_found; bool ret_if_found;