+ DPRINTF(("searching LDAP for sudoers entries"), 1);
+
+ 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. */
+ DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
+ matched = TRUE;
+ if (rc == TRUE) {
+ DPRINTF(("LDAP entry: %p", entry), 1);
+ /* Apply entry-specific options. */
+ if (setenv_implied)
+ def_setenv = TRUE;
+ 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_OK);
+ CLR(ret, VALIDATE_NOT_OK);
+ } else {
+ SET(ret, VALIDATE_NOT_OK);
+ CLR(ret, VALIDATE_OK);
+ }
+ break;
+ }
+ }
+
+done:
+ DPRINTF(("done with LDAP searches"), 1);
+ DPRINTF(("user_matches=%d", lres->user_matches), 1);
+ DPRINTF(("host_matches=%d", lres->host_matches), 1);
+
+ if (!ISSET(ret, VALIDATE_OK)) {
+ /* No matching entries. */
+ if (pwflag && list_pw == NULL)
+ SET(ret, FLAG_NO_CHECK);
+ }
+ if (lres->user_matches)
+ CLR(ret, FLAG_NO_USER);
+ if (lres->host_matches)
+ CLR(ret, FLAG_NO_HOST);
+ DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
+
+ return ret;
+}
+
+/*
+ * Comparison function for ldap_entry_wrapper structures, descending order.
+ */
+static int
+ldap_entry_compare(a, b)
+ const void *a;
+ const void *b;
+{
+ const struct ldap_entry_wrapper *aw = a;
+ const struct ldap_entry_wrapper *bw = b;
+
+ return bw->order < aw->order ? -1 :
+ (bw->order > aw->order ? 1 : 0);
+}
+
+/*
+ * Find the last entry in the list of searches, usually the
+ * one currently being used to add entries.
+ * XXX - use a tailq instead?
+ */
+static struct ldap_search_list *
+sudo_ldap_result_last_search(lres)
+ struct ldap_result *lres;
+{
+ struct ldap_search_list *result = lres->searches;
+
+ if (result) {
+ while (result->next)
+ result = result->next;
+ }
+ return result;
+}
+
+/*
+ * Add an entry to the result structure.
+ */
+static struct ldap_entry_wrapper *
+sudo_ldap_result_add_entry(lres, entry)
+ struct ldap_result *lres;
+ LDAPMessage *entry;
+{
+ struct ldap_search_list *last;
+ struct berval **bv;
+ double order = 0.0;
+ char *ep;
+
+ /* Determine whether the entry has the sudoOrder attribute. */
+ last = sudo_ldap_result_last_search(lres);
+ bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
+ if (bv != NULL) {
+ if (ldap_count_values_len(bv) > 0) {
+ /* Get the value of this attribute, 0 if not present. */
+ DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
+ order = strtod((*bv)->bv_val, &ep);
+ if (ep == (*bv)->bv_val || *ep != '\0') {
+ warningx("invalid sudoOrder attribute: %s", (*bv)->bv_val);
+ order = 0.0;
+ }
+ DPRINTF(("order attribute: %f", order), 1);
+ }
+ ldap_value_free_len(bv);
+ }
+
+ /*
+ * Enlarge the array of entry wrappers as needed, preallocating blocks
+ * of 100 entries to save on allocation time.
+ */
+ if (++lres->nentries > lres->allocated_entries) {
+ lres->allocated_entries += ALLOCATION_INCREMENT;
+ lres->entries = erealloc3(lres->entries, lres->allocated_entries,
+ sizeof(lres->entries[0]));
+ }
+
+ /* Fill in the new entry and return it. */
+ lres->entries[lres->nentries - 1].entry = entry;
+ lres->entries[lres->nentries - 1].order = order;
+
+ return &lres->entries[lres->nentries - 1];
+}
+
+/*
+ * Free the ldap result structure in the sudo_nss handle.
+ */
+static void
+sudo_ldap_result_free_nss(nss)
+ struct sudo_nss *nss;
+{
+ struct sudo_ldap_handle *handle = nss->handle;
+
+ if (handle->result != NULL) {
+ DPRINTF(("removing reusable search result"), 1);
+ sudo_ldap_result_free(handle->result);
+ if (handle->username) {
+ efree(handle->username);
+ handle->username = NULL;
+ }
+ handle->groups = NULL;
+ handle->result = NULL;
+ }
+}
+
+/*
+ * Perform the LDAP query for the user or return a cached query if
+ * there is one for this user.
+ */
+static struct ldap_result *
+sudo_ldap_result_get(nss, pw)
+ struct sudo_nss *nss;
+ struct passwd *pw;
+{
+ struct sudo_ldap_handle *handle = nss->handle;
+ struct ldap_config_list_str *base;
+ struct ldap_result *lres;
+ struct timeval tv, *tvp = NULL;
+ LDAPMessage *entry, *result;
+ LDAP *ld = handle->ld;
+ int do_netgr, rc;
+ char *filt;
+
+ /*
+ * 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->groups == user_groups &&
+ strcmp(pw->pw_name, handle->username) == 0) {
+ DPRINTF(("reusing previous result (user %s) with %d entries",
+ handle->username, handle->result->nentries), 1);
+ return handle->result;
+ }
+ /* User mismatch, cached result cannot be used. */
+ DPRINTF(("removing result (user %s), new search (user %s)",
+ handle->username, pw->pw_name), 1);
+ sudo_ldap_result_free_nss(nss);
+ }
+