#include <arpa/inet.h>
#include <netdb.h>
#include <err.h>
-#include <errno.h>
#ifdef HAVE_LBER_H
# include <lber.h>
#endif
# define LDAP_OPT_SUCCESS LDAP_SUCCESS
#endif
+#define LDAP_FOREACH(var, ld, res) \
+ for ((var) = ldap_first_entry((ld), (res)); \
+ (var) != NULL; \
+ (var) = ldap_next_entry((ld), (var)))
+
#define DPRINTF(args, level) if (ldap_conf.debug >= level) warnx args
/* ldap configuration structure */
* netgroup, else FALSE.
*/
int
-sudo_ldap_check_user_netgroup(ld, entry)
+sudo_ldap_check_user_netgroup(ld, entry, user)
LDAP *ld;
LDAPMessage *entry;
+ char *user;
{
char **v = NULL, **p = NULL;
int ret = FALSE;
/* walk through values */
for (p = v; p && *p && !ret; p++) {
/* match any */
- if (netgr_matches(*p, NULL, NULL, user_name))
+ if (netgr_matches(*p, NULL, NULL, user))
ret = TRUE;
DPRINTF(("ldap sudoUser netgroup '%s' ... %s", *p,
ret ? "MATCH!" : "not"), 2);
*/
/*
- * If there are no runas entries, then match the runas_default with whats
- * on the command line
+ * If there are no runas entries, then match the runas_default with
+ * what's on the command line.
*/
if (!v)
ret = !strcasecmp(*user_runas, def_runas_default);
/*
- * What about the case where exactly one runas is specified in the config
- * and the user forgets the -u option, should we switch it?
+ * What about the case where exactly one runas is specified in the
+ * config and the user forgets the -u option, should we switch it?
* Probably not...
*/
v = ldap_get_values(ld, entry, "sudoCommand");
- /* get_first_entry */
for (p = v; p && *p && ret >= 0; p++) {
/* Match against ALL ? */
if (!strcasecmp(*p, "ALL")) {
return(ret > 0);
}
+/*
+ * Search for boolean "option" in sudoOption.
+ * Returns TRUE if found and allowed, FALSE if negated, else UNSPEC.
+ */
+int
+sudo_ldap_check_bool(ld, entry, option)
+ LDAP *ld;
+ LDAPMessage *entry;
+ char *option;
+{
+ char ch, *var, **v, **p;
+ int ret = UNSPEC;
+
+ if (entry == NULL)
+ return(UNSPEC);
+
+ /* walk through options */
+ v = ldap_get_values(ld, entry, "sudoOption");
+ for (p = v; p && *p; p++) {
+ var = *p;
+ DPRINTF(("ldap sudoOption: '%s'", var), 2);
+
+ if ((ch = *var) == '!')
+ var++;
+ if (strcmp(var, option) == 0)
+ ret = (ch != '!');
+ }
+
+ if (v)
+ ldap_value_free(v);
+
+ return(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.
LDAP *ld;
LDAPMessage *entry;
{
- char op, *var, *val, **v = NULL, **p = NULL;
+ char op, *var, *val, **v, **p;
- if (!entry)
+ if (entry == NULL)
return;
v = ldap_get_values(ld, entry, "sudoOption");
* builds together a filter to check against ldap
*/
char *
-sudo_ldap_build_pass1()
+sudo_ldap_build_pass1(pw)
+ struct passwd *pw;
{
struct group *grp;
size_t sz;
/* build filter sudoUser=user_name */
ncat(&b, &sz, "(sudoUser=");
- ncat(&b, &sz, user_name);
+ ncat(&b, &sz, pw->pw_name);
ncat(&b, &sz, ")");
/* Append primary group */
- grp = sudo_getgrgid(getgid());
+ grp = sudo_getgrgid(pw->pw_gid);
if (grp != NULL) {
ncat(&b, &sz, "(sudoUser=%");
- ncat(&b, &sz, grp -> gr_name);
+ ncat(&b, &sz, grp->gr_name);
ncat(&b, &sz, ")");
}
for (i = 0; i < user_ngroups; i++) {
if ((grp = sudo_getgrgid(user_groups[i])) != NULL) {
ncat(&b, &sz, "(sudoUser=%");
- ncat(&b, &sz, grp -> gr_name);
+ ncat(&b, &sz, grp->gr_name);
ncat(&b, &sz, ")");
}
}
}
/*
- * like perl's join(sep,@ARGS)
+ * Like sudo_ldap_check(), except we just print entries.
*/
-char *
- _ldap_join_values(sep, v)
- char *sep;
- char **v;
+void
+sudo_ldap_display_privs(ldv, pw)
+ VOID *ldv;
+ struct passwd *pw;
{
- char *b = NULL, **p = NULL;
- size_t sz = 0;
+ LDAP *ld = (LDAP *) ldv;
+ LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
+ char *filt; /* used to parse attributes */
+ char *dn, **edn, **v, **p;
+ int rc, do_netgr;
- /* paste values together */
- for (p = v; p && *p; p++) {
- if (p != v && sep != NULL)
- ncat(&b, &sz, sep); /* append seperator */
- ncat(&b, &sz, *p); /* append value */
+ /*
+ * First, get (and display) the global Options.
+ */
+ rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_ONELEVEL,
+ "cn=defaults", NULL, 0, &result);
+ if (rc == 0 && (entry = ldap_first_entry(ld, result))) {
+ v = ldap_get_values(ld, entry, "sudoOption");
+ if (v != NULL) {
+ fputs("Global options:\n ", stdout);
+ for (p = v; *p != NULL; p++) {
+ if (p != v)
+ fputs("\n ", stdout);
+ fputs(*p, stdout);
+ }
+ putchar('\n');
+ ldap_value_free(v);
+ }
}
+ if (result)
+ ldap_msgfree(result);
- /* sanity check */
- if (b[0] == '\0') {
- /* something went wrong, put something here */
- ncat(&b, &sz, "(empty list)"); /* append value */
+ /*
+ * Okay - time to search for anything that matches this user
+ * Lets limit it to only two queries of the LDAP server
+ *
+ * The first pass will look by the username, groups, and
+ * the keyword ALL. We will then inspect the results that
+ * came back from the query. We don't need to inspect the
+ * sudoUser in this pass since the LDAP server already scanned
+ * it for us.
+ *
+ * The second pass will return all the entries that contain
+ * user netgroups. Then we take the netgroups returned and
+ * try to match them against the username.
+ */
+ for (do_netgr = 0; do_netgr < 2; do_netgr++) {
+ filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
+ DPRINTF(("ldap search '%s'", filt), 1);
+ rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_ONELEVEL, filt,
+ NULL, 0, &result);
+ efree(filt);
+ if (rc != 0)
+ continue; /* no entries for this pass */
+
+ /* print each matching entry */
+ LDAP_FOREACH(entry, ld, result) {
+ if ((!do_netgr ||
+ sudo_ldap_check_user_netgroup(ld, entry, pw->pw_passwd)) &&
+ sudo_ldap_check_host(ld, entry)) {
+
+ /* collect the dn, only show the rdn */
+ dn = ldap_get_dn(ld, entry);
+ edn = dn ? ldap_explode_dn(dn, 1) : NULL;
+ printf("\nLDAP Role: %s\n", (edn && *edn) ? *edn : "UNKNOWN");
+ if (dn)
+ ldap_memfree(dn);
+ if (edn)
+ ldap_value_free(edn);
+
+ /* get the Option Values from the entry */
+ v = ldap_get_values(ld, entry, "sudoOption");
+ if (v != NULL) {
+ fputs(" Options:\n ", stdout);
+ for (p = v; *p != NULL; p++) {
+ if (p != v)
+ fputs("\n ", stdout);
+ fputs(*p, stdout);
+ }
+ putchar('\n');
+ ldap_value_free(v);
+ }
+
+ /* get the RunAs Values from the entry */
+ v = ldap_get_values(ld, entry, "sudoRunAs");
+ if (v != NULL) {
+ printf(" RunAs: (");
+ for (p = v; *p != NULL; p++) {
+ if (p != v)
+ fputs(", ", stdout);
+ fputs(*p, stdout);
+ }
+ puts(")");
+ ldap_value_free(v);
+ }
+
+ /* get the Command Values from the entry */
+ v = ldap_get_values(ld, entry, "sudoCommand");
+ if (v != NULL) {
+ fputs(" Commands:\n ", stdout);
+ for (p = v; *p != NULL; p++) {
+ if (p != v)
+ fputs("\n ", stdout);
+ fputs(*p, stdout);
+ }
+ putchar('\n');
+ ldap_value_free(v);
+ } else {
+ puts(" Commands: NONE");
+ }
+ }
+ }
+ ldap_msgfree(result);
}
-
- return(b);
}
-char *sudo_ldap_cm_list = NULL;
-size_t sudo_ldap_cm_list_size;
-
-#define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
-/*
- * Walks through search result and returns TRUE if we have a
- * command match
- */
int
-sudo_ldap_add_match(ld, entry, pwflag)
- LDAP *ld;
- LDAPMessage *entry;
- int pwflag;
+sudo_ldap_display_cmnd(ldv, pw)
+ VOID *ldv;
+ struct passwd *pw;
{
- char *dn, **edn, **v = NULL;
-
- /* if we are not collecting matches, then don't save them */
- if (pwflag != I_LISTPW)
- return(TRUE);
-
- /* collect the dn, only show the rdn */
- dn = ldap_get_dn(ld, entry);
- edn = dn ? ldap_explode_dn(dn, 1) : NULL;
- SAVE_LIST("\nLDAP Role: ");
- SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
- SAVE_LIST("\n");
- if (dn)
- ldap_memfree(dn);
- if (edn)
- ldap_value_free(edn);
-
- /* get the Runas Values from the entry */
- v = ldap_get_values(ld, entry, "sudoRunAs");
- if (v && *v) {
- SAVE_LIST(" RunAs: (");
- SAVE_LIST(_ldap_join_values(", ", v));
- SAVE_LIST(")\n");
- }
- if (v)
- ldap_value_free(v);
+ LDAP *ld = (LDAP *) ldv;
+ LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
+ char *filt; /* used to parse attributes */
+ int rc, found, do_netgr; /* temp/final return values */
- /* get the Command Values from the entry */
- v = ldap_get_values(ld, entry, "sudoCommand");
- if (v && *v) {
- SAVE_LIST(" Commands:\n ");
- SAVE_LIST(_ldap_join_values("\n ", v));
- SAVE_LIST("\n");
- } else {
- SAVE_LIST(" Commands: NONE\n");
- }
- if (v)
- ldap_value_free(v);
+ /*
+ * Okay - time to search for anything that matches this user
+ * Lets limit it to only two queries of the LDAP server
+ *
+ * The first pass will look by the username, groups, and
+ * the keyword ALL. We will then inspect the results that
+ * came back from the query. We don't need to inspect the
+ * sudoUser in this pass since the LDAP server already scanned
+ * it for us.
+ *
+ * The second pass will return all the entries that contain
+ * user netgroups. Then we take the netgroups returned and
+ * try to match them against the username.
+ */
+ for (found = FALSE, do_netgr = 0; !found && do_netgr < 2; do_netgr++) {
+ filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
+ DPRINTF(("ldap search '%s'", filt), 1);
+ rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_ONELEVEL, filt,
+ NULL, 0, &result);
+ efree(filt);
+ if (rc != 0)
+ continue; /* no entries for this pass */
- return(FALSE); /* Don't stop at the first match */
-}
-#undef SAVE_LIST
+ LDAP_FOREACH(entry, ld, result) {
+ if ((!do_netgr ||
+ sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
+ sudo_ldap_check_host(ld, entry) &&
+ sudo_ldap_check_command(ld, entry) &&
+ sudo_ldap_check_runas(ld, entry)) {
-void
-sudo_ldap_display_privs()
-{
- if (sudo_ldap_cm_list != NULL)
- printf("%s", sudo_ldap_cm_list);
+ found = TRUE;
+ break;
+ }
+ }
+ if (result)
+ ldap_msgfree(result);
+ result = NULL;
+ }
+
+ if (found)
+ printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
+ user_args ? user_args : "");
+ return(!found);
}
/*
optname, ldap_conf.val, rc, ldap_err2string(rc)); \
return(NULL) ; \
} \
- } \
+ }
/* like above, but assumes val is in int */
#define SET_OPTI(opt,optname,val) \
fprintf(stderr,"ldap_set_option(LDAP_OPT_%s,0x%02x)=%d: %s\n", \
optname, ldap_conf.val, rc, ldap_err2string(rc)); \
return(NULL) ; \
- } \
+ }
/* attempt to setup ssl options */
#ifdef LDAP_OPT_X_TLS_CACERTFILE
} else
#endif /* HAVE_LDAP_INITIALIZE */
if (ldap_conf.host) {
-
DPRINTF(("ldap_init(%s,%d)", ldap_conf.host, ldap_conf.port), 2);
-
if ((ld = ldap_init(ldap_conf.host, ldap_conf.port)) == NULL) {
- fprintf(stderr, "ldap_init(): errno=%d : %s\n",
- errno, strerror(errno));
+ warn("ldap_init()");
return(NULL);
}
}
-#ifdef LDAP_OPT_PROTOCOL_VERSION
+#ifdef LDAP_OPT_PROTOCOL_VERSION
/* Set the LDAP Protocol version */
SET_OPTI(LDAP_OPT_PROTOCOL_VERSION, "PROTOCOL_VERSION", version);
-
#endif /* LDAP_OPT_PROTOCOL_VERSION */
#ifdef HAVE_LDAP_START_TLS_S
rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_ONELEVEL,
"cn=defaults", NULL, 0, &result);
- if (!rc && (entry = ldap_first_entry(ld, result))) {
+ if (rc == 0 && (entry = ldap_first_entry(ld, result))) {
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
sudo_ldap_parse_options(ld, entry);
} else
LDAP *ld = (LDAP *) v;
LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
char *filt; /* used to parse attributes */
- int rc = FALSE, ret = FALSE, do_netgr; /* temp/final return values */
+ int do_netgr, rc, ret; /* temp/final return values */
int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */
+ struct passwd *pw = list_pw ? list_pw : sudo_user.pw;
+
+ if (pwflag) {
+ int doauth = UNSPEC;
+ enum def_tupple pwcheck =
+ (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
+
+ for (ret = 0, do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
+ filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
+ rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_ONELEVEL, filt,
+ NULL, 0, &result);
+ efree(filt);
+ if (rc != 0)
+ continue;
+
+ LDAP_FOREACH(entry, ld, result) {
+ /* only verify netgroup matches in pass 2 */
+ if (do_netgr && !sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name))
+ continue;
+
+ ldap_user_matches = TRUE;
+ if (sudo_ldap_check_host(ld, entry)) {
+ ldap_host_matches = TRUE;
+ if ((pwcheck == any && doauth != FALSE) ||
+ (pwcheck == all && doauth == FALSE))
+ doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
+ /* 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)) {
+ ret = 1;
+ break; /* end foreach */
+ }
+ }
+ }
+ ldap_msgfree(result);
+ }
+ if (ret || user_uid == 0) {
+ ret = VALIDATE_OK;
+ if (def_authenticate) {
+ switch (pwcheck) {
+ case always:
+ SET(ret, FLAG_CHECK_USER);
+ break;
+ case all:
+ /*
+ * If we are not ignoring local sudoers we
+ * can't make a decision yet. (XXX)
+ */
+ if (!def_ignore_local_sudoers && doauth == FALSE)
+ def_authenticate = FALSE;
+ break;
+ case any:
+ if (doauth == FALSE)
+ def_authenticate = FALSE;
+ break;
+ case never:
+ def_authenticate = FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ goto done;
+ }
/*
* Okay - time to search for anything that matches this user
* user netgroups. Then we take the netgroups returned and
* try to match them against the username.
*/
-
- for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
- filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1();
+ for (ret = 0, do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
+ filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
DPRINTF(("ldap search '%s'", filt), 1);
rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_ONELEVEL, filt,
NULL, 0, &result);
efree(filt);
/* parse each entry returned from this most recent search */
- entry = rc ? NULL : ldap_first_entry(ld, result);
- while (entry != NULL) {
- DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
- if (
- /* first verify user netgroup matches - only if in pass 2 */
- (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) &&
- /* remember that user matched */
- (ldap_user_matches = -1) &&
- /* verify host match */
- sudo_ldap_check_host(ld, entry) &&
- /* remember that host matched */
- (ldap_host_matches = -1) &&
- /* add matches for listing later */
- sudo_ldap_add_match(ld, entry, pwflag) &&
- /* verify command match */
- sudo_ldap_check_command(ld, entry) &&
- /* verify runas match */
- sudo_ldap_check_runas(ld, entry)
- ) {
- /* We have a match! */
- DPRINTF(("Perfect Matched!"), 1);
- /* pick up any options */
- sudo_ldap_parse_options(ld, entry);
- /* make sure we don't reenter loop */
- ret = VALIDATE_OK;
- /* break from inside for loop */
- break;
+ if (rc == 0) {
+ LDAP_FOREACH(entry, ld, result) {
+ DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
+ if (
+ /* first verify user netgroup matches - only if in pass 2 */
+ (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
+ /* remember that user matched */
+ (ldap_user_matches = TRUE) &&
+ /* verify host match */
+ sudo_ldap_check_host(ld, entry) &&
+ /* remember that host matched */
+ (ldap_host_matches = TRUE) &&
+ /* verify command match */
+ sudo_ldap_check_command(ld, entry) &&
+ /* verify runas match */
+ sudo_ldap_check_runas(ld, entry)
+ ) {
+ /* We have a match! */
+ DPRINTF(("Perfect Match!"), 1);
+ /* pick up any options */
+ sudo_ldap_parse_options(ld, entry);
+ /* make sure we don't reenter loop */
+ ret = VALIDATE_OK;
+ /* break from inside for loop */
+ break;
+ }
}
- entry = ldap_next_entry(ld, entry);
- }
- if (result)
ldap_msgfree(result);
- result = NULL;
+ }
}
+done:
DPRINTF(("user_matches=%d", ldap_user_matches), 1);
DPRINTF(("host_matches=%d", ldap_host_matches), 1);
- /* Check for special case for -v, -k, -l options */
- if (pwflag && ldap_user_matches && ldap_host_matches) {
- /*
- * Handle verifypw & listpw
- *
- * To be extra paranoid, since we haven't read any NOPASSWD options
- * in /etc/sudoers yet, but we have to make the decission now, lets
- * assume the worst and prefer to prompt for password unless the setting
- * is "never". (example verifypw=never or listpw=never)
- *
- */
- ret = VALIDATE_OK;
- if (pwflag != -1) {
- switch (sudo_defs_table[pwflag].sd_un.tuple) {
- case never:
- def_authenticate = FALSE;
- break;
- case always:
- if (def_authenticate)
- SET(ret, FLAG_CHECK_USER);
- break;
- default:
- break;
- }
- }
- }
if (!ISSET(ret, VALIDATE_OK)) {
/* we do not have a match */
ret = VALIDATE_NOT_OK;
- if (pwflag)
+ if (pwflag && list_pw == NULL)
SET(ret, FLAG_NO_CHECK);
else if (!ldap_user_matches)
SET(ret, FLAG_NO_USER);
sudo_ldap_close(v)
VOID *v;
{
- if (v)
+ if (v != NULL)
ldap_unbind_s((LDAP *) v);
}