2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-31 06:15:37 +00:00

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.
This commit is contained in:
Todd C. Miller
2018-05-14 09:05:03 -06:00
parent 71e98d9493
commit f9be3a48a2
17 changed files with 980 additions and 1959 deletions

View File

@@ -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)
{
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)
sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry, struct defaults_list *defs)
{
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
*/