2 * Copyright (c) 2003-2011 Todd C. Miller <Todd.Miller@courtesan.com>
4 * This code is derived from software contributed by Aaron Spangler.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
23 #include <sys/param.h>
33 #endif /* STDC_HEADERS */
36 #endif /* HAVE_STRING_H */
39 #endif /* HAVE_STRINGS_H */
42 #endif /* HAVE_UNISTD_H */
43 #if TIME_WITH_SYS_TIME
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
56 #if defined(HAVE_LDAP_SSL_H)
57 # include <ldap_ssl.h>
58 #elif defined(HAVE_MPS_LDAP_SSL_H)
59 # include <mps/ldap_ssl.h>
61 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
62 # ifdef HAVE_SASL_SASL_H
63 # include <sasl/sasl.h>
67 # if HAVE_GSS_KRB5_CCACHE_NAME
68 # if defined(HAVE_GSSAPI_GSSAPI_KRB5_H)
69 # include <gssapi/gssapi.h>
70 # include <gssapi/gssapi_krb5.h>
71 # elif defined(HAVE_GSSAPI_GSSAPI_H)
72 # include <gssapi/gssapi.h>
83 #ifndef LDAP_OPT_SUCCESS
84 # define LDAP_OPT_SUCCESS LDAP_SUCCESS
88 # define LDAPS_PORT 636
91 #if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET)
92 # define LDAP_SASL_QUIET 0
95 #ifndef HAVE_LDAP_UNBIND_EXT_S
96 #define ldap_unbind_ext_s(a, b, c) ldap_unbind_s(a)
99 #ifndef HAVE_LDAP_SEARCH_EXT_S
100 # ifdef HAVE_LDAP_SEARCH_ST
101 # define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k) \
102 ldap_search_st(a, b, c, d, e, f, i, k)
104 # define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k) \
105 ldap_search_s(a, b, c, d, e, f, k)
109 #define LDAP_FOREACH(var, ld, res) \
110 for ((var) = ldap_first_entry((ld), (res)); \
112 (var) = ldap_next_entry((ld), (var)))
114 #define DPRINTF(args, level) if (ldap_conf.debug >= level) warningx args
119 #define CONF_LIST_STR 4
121 #define SUDO_LDAP_SSL 1
122 #define SUDO_LDAP_STARTTLS 2
124 /* The TIMEFILTER_LENGTH includes the filter itself plus the global AND
125 wrapped around the user filter and the time filter when timed entries
126 are used. The length is computed as follows:
128 + 2 * 13 for the now timestamp
129 + 3 for the global AND
131 #define TIMEFILTER_LENGTH 114
134 * The ldap_search structure implements a linked list of ldap and
135 * search result pointers, which allows us to remove them after
136 * all search results have been combined in memory.
137 * XXX - should probably be a tailq since we do appends
139 struct ldap_search_list {
141 LDAPMessage *searchresult;
142 struct ldap_search_list *next;
146 * The ldap_entry_wrapper structure is used to implement sorted result entries.
147 * A double is used for the order to allow for insertion of new entries
148 * without having to renumber everything.
149 * Note: there is no standard floating point type in LDAP.
150 * As a result, some LDAP servers will only allow an integer.
152 struct ldap_entry_wrapper {
158 * The ldap_result structure contains the list of matching searches as
159 * well as an array of all result entries sorted by the sudoOrder attribute.
162 struct ldap_search_list *searches;
163 struct ldap_entry_wrapper *entries;
164 int allocated_entries;
169 #define ALLOCATION_INCREMENT 100
171 struct ldap_config_table {
172 const char *conf_str; /* config file string */
173 short type; /* CONF_BOOL, CONF_INT, CONF_STR */
174 short connected; /* connection-specific value? */
175 int opt_val; /* LDAP_OPT_* (or -1 for sudo internal) */
176 void *valp; /* pointer into ldap_conf */
179 struct ldap_config_list_str {
180 struct ldap_config_list_str *next;
184 /* LDAP configuration structure */
185 static struct ldap_config {
199 struct ldap_config_list_str *uri;
203 struct ldap_config_list_str *base;
206 char *tls_cacertfile;
208 char *tls_random_file;
209 char *tls_cipher_suite;
213 char *rootsasl_auth_id;
218 static struct ldap_config_table ldap_conf_table[] = {
219 { "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug },
220 { "host", CONF_STR, FALSE, -1, &ldap_conf.host },
221 { "port", CONF_INT, FALSE, -1, &ldap_conf.port },
222 { "ssl", CONF_STR, FALSE, -1, &ldap_conf.ssl },
223 { "sslpath", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
224 { "uri", CONF_LIST_STR, FALSE, -1, &ldap_conf.uri },
225 #ifdef LDAP_OPT_DEBUG_LEVEL
226 { "debug", CONF_INT, FALSE, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
228 #ifdef LDAP_OPT_PROTOCOL_VERSION
229 { "ldap_version", CONF_INT, TRUE, LDAP_OPT_PROTOCOL_VERSION,
230 &ldap_conf.version },
232 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
233 { "tls_checkpeer", CONF_BOOL, FALSE, LDAP_OPT_X_TLS_REQUIRE_CERT,
234 &ldap_conf.tls_checkpeer },
236 { "tls_checkpeer", CONF_BOOL, FALSE, -1, &ldap_conf.tls_checkpeer },
238 #ifdef LDAP_OPT_X_TLS_CACERTFILE
239 { "tls_cacertfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
240 &ldap_conf.tls_cacertfile },
241 { "tls_cacert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
242 &ldap_conf.tls_cacertfile },
244 #ifdef LDAP_OPT_X_TLS_CACERTDIR
245 { "tls_cacertdir", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTDIR,
246 &ldap_conf.tls_cacertdir },
248 #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
249 { "tls_randfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_RANDOM_FILE,
250 &ldap_conf.tls_random_file },
252 #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
253 { "tls_ciphers", CONF_STR, FALSE, LDAP_OPT_X_TLS_CIPHER_SUITE,
254 &ldap_conf.tls_cipher_suite },
256 #ifdef LDAP_OPT_X_TLS_CERTFILE
257 { "tls_cert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CERTFILE,
258 &ldap_conf.tls_certfile },
260 { "tls_cert", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
262 #ifdef LDAP_OPT_X_TLS_KEYFILE
263 { "tls_key", CONF_STR, FALSE, LDAP_OPT_X_TLS_KEYFILE,
264 &ldap_conf.tls_keyfile },
266 { "tls_key", CONF_STR, FALSE, -1, &ldap_conf.tls_keyfile },
268 #ifdef LDAP_OPT_NETWORK_TIMEOUT
269 { "bind_timelimit", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
270 &ldap_conf.bind_timelimit },
271 { "network_timeout", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
272 &ldap_conf.bind_timelimit },
273 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
274 { "bind_timelimit", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
275 &ldap_conf.bind_timelimit },
276 { "network_timeout", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
277 &ldap_conf.bind_timelimit },
279 { "timelimit", CONF_INT, TRUE, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
280 #ifdef LDAP_OPT_TIMEOUT
281 { "timeout", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
282 &ldap_conf.timeout },
284 { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn },
285 { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw },
286 { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
287 { "sudoers_base", CONF_LIST_STR, FALSE, -1, &ldap_conf.base },
288 { "sudoers_timed", CONF_BOOL, FALSE, -1, &ldap_conf.timed },
289 { "sudoers_search_filter", CONF_STR, FALSE, -1, &ldap_conf.search_filter },
290 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
291 { "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl },
292 { "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id },
293 { "rootuse_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.rootuse_sasl },
294 { "rootsasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.rootsasl_auth_id },
295 # ifdef LDAP_OPT_X_SASL_SECPROPS
296 { "sasl_secprops", CONF_STR, TRUE, LDAP_OPT_X_SASL_SECPROPS,
297 &ldap_conf.sasl_secprops },
299 { "krb5_ccname", CONF_STR, FALSE, -1, &ldap_conf.krb5_ccname },
300 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
304 /* sudo_nss implementation */
305 static int sudo_ldap_open(struct sudo_nss *nss);
306 static int sudo_ldap_close(struct sudo_nss *nss);
307 static int sudo_ldap_parse(struct sudo_nss *nss);
308 static int sudo_ldap_setdefs(struct sudo_nss *nss);
309 static int sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag);
310 static int sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw);
311 static int sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
313 static int sudo_ldap_display_bound_defaults(struct sudo_nss *nss,
314 struct passwd *pw, struct lbuf *lbuf);
315 static int sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
317 static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss,
321 * LDAP sudo_nss handle.
322 * We store the connection to the LDAP server, the cached ldap_result object
323 * (if any), and the name of the user the query was performed for.
324 * If a new query is launched with sudo_ldap_result_get() that specifies a
325 * different user, the old cached result is freed before the new query is run.
327 struct sudo_ldap_handle {
329 struct ldap_result *result;
331 struct group_list *grlist;
334 struct sudo_nss sudo_nss_ldap = {
342 sudo_ldap_display_cmnd,
343 sudo_ldap_display_defaults,
344 sudo_ldap_display_bound_defaults,
345 sudo_ldap_display_privs
348 #ifdef HAVE_LDAP_CREATE
350 * Rebuild the hosts list and include a specific port for each host.
351 * ldap_create() does not take a default port parameter so we must
352 * append one if we want something other than LDAP_PORT.
355 sudo_ldap_conf_add_ports(void)
358 char *host, *port, defport[13];
359 char hostbuf[LINE_MAX * 2];
362 if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
363 errorx(1, _("sudo_ldap_conf_add_ports: port too large"));
365 for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
366 if (hostbuf[0] != '\0') {
367 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
371 if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
373 /* Append port if there is not one already. */
374 if ((port = strrchr(host, ':')) == NULL ||
375 !isdigit((unsigned char)port[1])) {
376 if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
381 efree(ldap_conf.host);
382 ldap_conf.host = estrdup(hostbuf);
386 errorx(1, _("sudo_ldap_conf_add_ports: out of space expanding hostbuf"));
390 #ifndef HAVE_LDAP_INITIALIZE
392 * For each uri, convert to host:port pairs. For ldaps:// enable SSL
393 * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
394 * where the trailing slash is optional.
397 sudo_ldap_parse_uri(const struct ldap_config_list_str *uri_list)
399 char *buf, *uri, *host, *cp, *port;
400 char hostbuf[LINE_MAX];
401 int nldap = 0, nldaps = 0;
405 buf = estrdup(uri_list->val);
407 for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
408 if (strncasecmp(uri, "ldap://", 7) == 0) {
411 } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
415 warningx(_("unsupported LDAP uri type: %s"), uri);
419 /* trim optional trailing slash */
420 if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
424 if (hostbuf[0] != '\0') {
425 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
430 host = "localhost"; /* no host specified, use localhost */
432 if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
435 /* If using SSL and no port specified, add port 636 */
437 if ((port = strrchr(host, ':')) == NULL ||
438 !isdigit((unsigned char)port[1]))
439 if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
443 if (hostbuf[0] == '\0') {
444 warningx(_("invalid uri: %s"), uri_list);
450 warningx(_("unable to mix ldap and ldaps URIs"));
453 if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
454 warningx(_("unable to mix ldaps and starttls"));
457 ldap_conf.ssl_mode = SUDO_LDAP_SSL;
460 efree(ldap_conf.host);
461 ldap_conf.host = estrdup(hostbuf);
463 } while ((uri_list = uri_list->next));
473 errorx(1, _("sudo_ldap_parse_uri: out of space building hostbuf"));
477 sudo_ldap_join_uri(struct ldap_config_list_str *uri_list)
479 struct ldap_config_list_str *uri;
483 /* Usually just a single entry. */
484 if (uri_list->next == NULL)
485 return estrdup(uri_list->val);
487 for (uri = uri_list; uri != NULL; uri = uri->next) {
488 len += strlen(uri->val) + 1;
490 buf = cp = emalloc(len);
492 for (uri = uri_list; uri != NULL; uri = uri->next) {
493 cp += strlcpy(cp, uri->val, len - (cp - buf));
499 #endif /* HAVE_LDAP_INITIALIZE */
502 sudo_ldap_init(LDAP **ldp, const char *host, int port)
505 int rc = LDAP_CONNECT_ERROR;
507 #ifdef HAVE_LDAPSSL_INIT
508 if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
509 DPRINTF(("ldapssl_clientauth_init(%s, %s)",
510 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
511 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
512 rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
513 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
515 * Mozilla-derived SDKs have a bug starting with version 5.0
516 * where the path can no longer be a file name and must be a dir.
518 if (rc != LDAP_SUCCESS) {
520 if (ldap_conf.tls_certfile) {
521 cp = strrchr(ldap_conf.tls_certfile, '/');
522 if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0)
525 if (ldap_conf.tls_keyfile) {
526 cp = strrchr(ldap_conf.tls_keyfile, '/');
527 if (cp != NULL && strncmp(cp + 1, "key", 3) == 0)
530 DPRINTF(("ldapssl_clientauth_init(%s, %s)",
531 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
532 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
533 rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
534 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
535 if (rc != LDAP_SUCCESS) {
536 warningx(_("unable to initialize SSL cert and key db: %s"),
537 ldapssl_err2string(rc));
542 DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
543 if ((ld = ldapssl_init(host, port, 1)) != NULL)
548 #ifdef HAVE_LDAP_CREATE
549 DPRINTF(("ldap_create()"), 2);
550 if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
552 DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
553 rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
555 DPRINTF(("ldap_init(%s, %d)", host, port), 2);
556 if ((ld = ldap_init(host, port)) != NULL)
567 * Walk through search results and return TRUE if we have a matching
568 * netgroup, else FALSE.
571 sudo_ldap_check_user_netgroup(LDAP *ld, LDAPMessage *entry, char *user)
573 struct berval **bv, **p;
580 /* get the values from the entry */
581 bv = ldap_get_values_len(ld, entry, "sudoUser");
585 /* walk through values */
586 for (p = bv; *p != NULL && !ret; p++) {
589 if (netgr_matches(val, NULL, NULL, user))
591 DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
592 ret ? "MATCH!" : "not"), 2 + ((ret) ? 0 : 1));
595 ldap_value_free_len(bv); /* cleanup */
601 * Walk through search results and return TRUE if we have a
602 * host match, else FALSE.
605 sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry)
607 struct berval **bv, **p;
614 /* get the values from the entry */
615 bv = ldap_get_values_len(ld, entry, "sudoHost");
619 /* walk through values */
620 for (p = bv; *p != NULL && !ret; p++) {
622 /* match any or address or netgroup or hostname */
623 if (!strcmp(val, "ALL") || addr_matches(val) ||
624 netgr_matches(val, user_host, user_shost, NULL) ||
625 hostname_matches(user_shost, user_host, val))
627 DPRINTF(("ldap sudoHost '%s' ... %s", val,
628 ret ? "MATCH!" : "not"), 2);
631 ldap_value_free_len(bv); /* cleanup */
637 sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry)
639 struct berval **bv, **p;
646 /* get the runas user from the entry */
647 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
649 bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
654 * if runas is not specified on the command line, the only information
655 * as to which user to run as is in the runas_default option. We should
656 * check to see if we have the local option present. Unfortunately we
657 * don't parse these options until after this routine says yes or no.
658 * The query has already returned, so we could peek at the attribute
659 * values here though.
661 * For now just require users to always use -u option unless its set
662 * in the global defaults. This behaviour is no different than the global
665 * Sigh - maybe add this feature later
669 * If there are no runas entries, match runas_default against
670 * what the user specified on the command line.
673 return !strcasecmp(runas_pw->pw_name, def_runas_default);
675 /* walk through values returned, looking for a match */
676 for (p = bv; *p != NULL && !ret; p++) {
680 if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
684 if (usergr_matches(val, runas_pw->pw_name, runas_pw))
688 if (strcmp(val, "ALL") == 0) {
694 if (strcasecmp(val, runas_pw->pw_name) == 0)
698 DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
699 ret ? "MATCH!" : "not"), 2);
702 ldap_value_free_len(bv); /* cleanup */
708 sudo_ldap_check_runas_group(LDAP *ld, LDAPMessage *entry)
710 struct berval **bv, **p;
714 /* runas_gr is only set if the user specified the -g flag */
718 /* get the values from the entry */
719 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
723 /* walk through values returned, looking for a match */
724 for (p = bv; *p != NULL && !ret; p++) {
726 if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
728 DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
729 ret ? "MATCH!" : "not"), 2);
732 ldap_value_free_len(bv); /* cleanup */
738 * Walk through search results and return TRUE if we have a runas match,
739 * else FALSE. RunAs info is optional.
742 sudo_ldap_check_runas(LDAP *ld, LDAPMessage *entry)
749 ret = sudo_ldap_check_runas_user(ld, entry) != FALSE &&
750 sudo_ldap_check_runas_group(ld, entry) != FALSE;
756 * Walk through search results and return TRUE if we have a command match,
757 * FALSE if disallowed and UNSPEC if not matched.
760 sudo_ldap_check_command(LDAP *ld, LDAPMessage *entry, int *setenv_implied)
762 struct berval **bv, **p;
763 char *allowed_cmnd, *allowed_args, *val;
764 int foundbang, ret = UNSPEC;
769 bv = ldap_get_values_len(ld, entry, "sudoCommand");
773 for (p = bv; *p != NULL && ret != FALSE; p++) {
775 /* Match against ALL ? */
776 if (!strcmp(val, "ALL")) {
778 if (setenv_implied != NULL)
779 *setenv_implied = TRUE;
780 DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
784 /* check for !command */
787 allowed_cmnd = estrdup(1 + val); /* !command */
790 allowed_cmnd = estrdup(val); /* command */
793 /* split optional args away from command */
794 allowed_args = strchr(allowed_cmnd, ' ');
796 *allowed_args++ = '\0';
798 /* check the command like normal */
799 if (command_matches(allowed_cmnd, allowed_args)) {
801 * If allowed (no bang) set ret but keep on checking.
802 * If disallowed (bang), exit loop.
804 ret = foundbang ? FALSE : TRUE;
806 DPRINTF(("ldap sudoCommand '%s' ... %s", val,
807 ret == TRUE ? "MATCH!" : "not"), 2);
809 efree(allowed_cmnd); /* cleanup */
812 ldap_value_free_len(bv); /* more cleanup */
818 * Search for boolean "option" in sudoOption.
819 * Returns TRUE if found and allowed, FALSE if negated, else UNSPEC.
822 sudo_ldap_check_bool(LDAP *ld, LDAPMessage *entry, char *option)
824 struct berval **bv, **p;
831 bv = ldap_get_values_len(ld, entry, "sudoOption");
835 /* walk through options */
836 for (p = bv; *p != NULL; p++) {
838 DPRINTF(("ldap sudoOption: '%s'", var), 2);
840 if ((ch = *var) == '!')
842 if (strcmp(var, option) == 0)
846 ldap_value_free_len(bv);
852 * Read sudoOption and modify the defaults as we go. This is used once
853 * from the cn=defaults entry and also once when a final sudoRole is matched.
856 sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry)
858 struct berval **bv, **p;
864 bv = ldap_get_values_len(ld, entry, "sudoOption");
868 /* walk through options */
869 for (p = bv; *p != NULL; p++) {
870 var = estrdup((*p)->bv_val);
871 DPRINTF(("ldap sudoOption: '%s'", var), 2);
873 /* check for equals sign past first char */
874 val = strchr(var, '=');
876 *val++ = '\0'; /* split on = and truncate var */
877 op = *(val - 2); /* peek for += or -= cases */
878 if (op == '+' || op == '-') {
879 *(val - 2) = '\0'; /* found, remove extra char */
880 /* case var+=val or var-=val */
881 set_default(var, val, (int) op);
884 set_default(var, val, TRUE);
886 } else if (*var == '!') {
887 /* case !var Boolean False */
888 set_default(var + 1, NULL, FALSE);
890 /* case var Boolean True */
891 set_default(var, NULL, TRUE);
896 ldap_value_free_len(bv);
900 * Build an LDAP timefilter.
902 * Stores a filter in the buffer that makes sure only entries
903 * are selected that have a sudoNotBefore in the past and a
904 * sudoNotAfter in the future, i.e. a filter of the following
905 * structure (spaced out a little more for better readability:
909 * (!(sudoNotAfter=*))
910 * (sudoNotAfter>__now__)
913 * (!(sudoNotBefore=*))
914 * (sudoNotBefore<__now__)
918 * If either the sudoNotAfter or sudoNotBefore attributes are missing,
919 * no time restriction shall be imposed.
922 sudo_ldap_timefilter(char *buffer, size_t buffersize)
929 /* Make sure we have a formatted timestamp for __now__. */
931 if ((tp = gmtime(&now)) == NULL) {
932 warning(_("unable to get GMT time"));
936 /* Format the timestamp according to the RFC. */
937 if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%M%SZ", tp) == 0) {
938 warning(_("unable to format timestamp"));
943 bytes = snprintf(buffer, buffersize, "(&(|(!(sudoNotAfter=*))(sudoNotAfter>=%s))(|(!(sudoNotBefore=*))(sudoNotBefore<=%s)))",
944 timebuffer, timebuffer);
945 if (bytes < 0 || bytes >= buffersize) {
946 warning(_("unable to build time filter"));
955 * Builds up a filter to search for default settings
958 sudo_ldap_build_default_filter()
962 if (ldap_conf.search_filter)
963 easprintf(&filt, "(&%s(cn=defaults))", ldap_conf.search_filter);
965 filt = estrdup("cn=defaults");
970 * Builds up a filter to check against LDAP.
973 sudo_ldap_build_pass1(struct passwd *pw)
976 char *buf, timebuffer[TIMEFILTER_LENGTH];
977 struct group_list *grlist;
981 /* Start with LDAP search filter length + 3 */
982 if (ldap_conf.search_filter)
983 sz += strlen(ldap_conf.search_filter) + 3;
985 /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
986 sz += 29 + strlen(pw->pw_name);
988 /* Add space for primary and supplementary groups */
989 if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
990 sz += 12 + strlen(grp->gr_name);
992 if ((grlist = get_group_list(pw)) != NULL) {
993 for (i = 0; i < grlist->ngroups; i++) {
994 if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
996 sz += 12 + strlen(grlist->groups[i]);
1000 /* If timed, add space for time limits. */
1001 if (ldap_conf.timed)
1002 sz += TIMEFILTER_LENGTH;
1007 * If timed or using a search filter, start a global AND clause to
1008 * contain the search filter, search criteria, and time restriction.
1010 if (ldap_conf.timed || ldap_conf.search_filter)
1011 (void) strlcpy(buf, "(&", sz);
1013 if (ldap_conf.search_filter)
1014 (void) strlcat(buf, ldap_conf.search_filter, sz);
1016 /* Global OR + sudoUser=user_name filter */
1017 (void) strlcat(buf, "(|(sudoUser=", sz);
1018 (void) strlcat(buf, pw->pw_name, sz);
1019 (void) strlcat(buf, ")", sz);
1021 /* Append primary group */
1023 (void) strlcat(buf, "(sudoUser=%", sz);
1024 (void) strlcat(buf, grp->gr_name, sz);
1025 (void) strlcat(buf, ")", sz);
1028 /* Append supplementary groups */
1029 if (grlist != NULL) {
1030 for (i = 0; i < grlist->ngroups; i++) {
1031 if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
1033 (void) strlcat(buf, "(sudoUser=%", sz);
1034 (void) strlcat(buf, grlist->groups[i], sz);
1035 (void) strlcat(buf, ")", sz);
1039 /* Done with groups. */
1041 grlist_delref(grlist);
1045 /* Add ALL to list and end the global OR */
1046 if (strlcat(buf, "(sudoUser=ALL)", sz) >= sz)
1047 errorx(1, _("sudo_ldap_build_pass1 allocation mismatch"));
1049 /* Add the time restriction, or simply end the global OR. */
1050 if (ldap_conf.timed) {
1051 strlcat(buf, ")", sz); /* closes the global OR */
1052 sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1053 strlcat(buf, timebuffer, sz);
1054 } else if (ldap_conf.search_filter) {
1055 strlcat(buf, ")", sz); /* closes the global OR */
1057 strlcat(buf, ")", sz); /* closes the global OR or the global AND */
1063 * Builds up a filter to check against netgroup entries in LDAP.
1066 sudo_ldap_build_pass2(void)
1068 char *filt, timebuffer[TIMEFILTER_LENGTH];
1070 if (ldap_conf.timed)
1071 sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1074 * Match all sudoUsers beginning with a '+'.
1075 * If a search filter or time restriction is specified,
1076 * those get ANDed in to the expression.
1078 easprintf(&filt, "%s%s(sudoUser=+*)%s%s",
1079 (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
1080 ldap_conf.search_filter ? ldap_conf.search_filter : "",
1081 ldap_conf.timed ? timebuffer : "",
1082 (ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
1088 sudo_ldap_read_secret(const char *path)
1091 char buf[LINE_MAX], *cp;
1093 if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
1094 if (fgets(buf, sizeof(buf), fp) != NULL) {
1095 if ((cp = strchr(buf, '\n')) != NULL)
1097 /* copy to bindpw and binddn */
1098 efree(ldap_conf.bindpw);
1099 ldap_conf.bindpw = estrdup(buf);
1100 efree(ldap_conf.binddn);
1101 ldap_conf.binddn = ldap_conf.rootbinddn;
1102 ldap_conf.rootbinddn = NULL;
1109 sudo_ldap_read_config(void)
1112 char *cp, *keyword, *value;
1113 struct ldap_config_table *cur;
1116 ldap_conf.version = 3;
1117 ldap_conf.port = -1;
1118 ldap_conf.tls_checkpeer = -1;
1119 ldap_conf.timelimit = -1;
1120 ldap_conf.timeout = -1;
1121 ldap_conf.bind_timelimit = -1;
1122 ldap_conf.use_sasl = -1;
1123 ldap_conf.rootuse_sasl = -1;
1125 if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
1128 while ((cp = sudo_parseln(fp)) != NULL) {
1130 continue; /* skip empty line */
1132 /* split into keyword and value */
1134 while (*cp && !isblank((unsigned char) *cp))
1137 *cp++ = '\0'; /* terminate keyword */
1139 /* skip whitespace before value */
1140 while (isblank((unsigned char) *cp))
1144 /* Look up keyword in config table. */
1145 for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1146 if (strcasecmp(keyword, cur->conf_str) == 0) {
1147 switch (cur->type) {
1149 *(int *)(cur->valp) = atobool(value) == TRUE;
1152 *(int *)(cur->valp) = atoi(value);
1155 efree(*(char **)(cur->valp));
1156 *(char **)(cur->valp) = estrdup(value);
1160 struct ldap_config_list_str **p;
1161 size_t len = strlen(value);
1164 p = (struct ldap_config_list_str **)cur->valp;
1167 *p = emalloc(sizeof(struct ldap_config_list_str) + len);
1168 memcpy((*p)->val, value, len + 1);
1180 if (!ldap_conf.host)
1181 ldap_conf.host = estrdup("localhost");
1183 if (ldap_conf.debug > 1) {
1184 sudo_printf(SUDO_CONV_ERROR_MSG, "LDAP Config Summary\n");
1185 sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
1186 if (ldap_conf.uri) {
1187 struct ldap_config_list_str *uri = ldap_conf.uri;
1190 sudo_printf(SUDO_CONV_ERROR_MSG, "uri %s\n",
1192 } while ((uri = uri->next) != NULL);
1194 sudo_printf(SUDO_CONV_ERROR_MSG, "host %s\n",
1195 ldap_conf.host ? ldap_conf.host : "(NONE)");
1196 sudo_printf(SUDO_CONV_ERROR_MSG, "port %d\n",
1199 sudo_printf(SUDO_CONV_ERROR_MSG, "ldap_version %d\n",
1202 if (ldap_conf.base) {
1203 struct ldap_config_list_str *base = ldap_conf.base;
1205 sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
1207 } while ((base = base->next) != NULL);
1209 sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
1210 "(NONE: LDAP disabled)");
1212 if (ldap_conf.search_filter) {
1213 sudo_printf(SUDO_CONV_ERROR_MSG, "search_filter %s\n",
1214 ldap_conf.search_filter);
1216 sudo_printf(SUDO_CONV_ERROR_MSG, "binddn %s\n",
1217 ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
1218 sudo_printf(SUDO_CONV_ERROR_MSG, "bindpw %s\n",
1219 ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
1220 if (ldap_conf.bind_timelimit > 0) {
1221 sudo_printf(SUDO_CONV_ERROR_MSG, "bind_timelimit %d\n",
1222 ldap_conf.bind_timelimit);
1224 if (ldap_conf.timelimit > 0) {
1225 sudo_printf(SUDO_CONV_ERROR_MSG, "timelimit %d\n",
1226 ldap_conf.timelimit);
1228 sudo_printf(SUDO_CONV_ERROR_MSG, "ssl %s\n",
1229 ldap_conf.ssl ? ldap_conf.ssl : "(no)");
1230 if (ldap_conf.tls_checkpeer != -1) {
1231 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_checkpeer %s\n",
1232 ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
1234 if (ldap_conf.tls_cacertfile != NULL) {
1235 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertfile %s\n",
1236 ldap_conf.tls_cacertfile);
1238 if (ldap_conf.tls_cacertdir != NULL) {
1239 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertdir %s\n",
1240 ldap_conf.tls_cacertdir);
1242 if (ldap_conf.tls_random_file != NULL) {
1243 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_random_file %s\n",
1244 ldap_conf.tls_random_file);
1246 if (ldap_conf.tls_cipher_suite != NULL) {
1247 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cipher_suite %s\n",
1248 ldap_conf.tls_cipher_suite);
1250 if (ldap_conf.tls_certfile != NULL) {
1251 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_certfile %s\n",
1252 ldap_conf.tls_certfile);
1254 if (ldap_conf.tls_keyfile != NULL) {
1255 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_keyfile %s\n",
1256 ldap_conf.tls_keyfile);
1258 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1259 if (ldap_conf.use_sasl != -1) {
1260 sudo_printf(SUDO_CONV_ERROR_MSG, "use_sasl %s\n",
1261 ldap_conf.use_sasl ? "yes" : "no");
1262 sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_auth_id %s\n",
1263 ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
1264 sudo_printf(SUDO_CONV_ERROR_MSG, "rootuse_sasl %d\n",
1265 ldap_conf.rootuse_sasl);
1266 sudo_printf(SUDO_CONV_ERROR_MSG, "rootsasl_auth_id %s\n",
1267 ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
1268 sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_secprops %s\n",
1269 ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
1270 sudo_printf(SUDO_CONV_ERROR_MSG, "krb5_ccname %s\n",
1271 ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
1274 sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
1276 if (!ldap_conf.base)
1277 return FALSE; /* if no base is defined, ignore LDAP */
1279 if (ldap_conf.bind_timelimit > 0)
1280 ldap_conf.bind_timelimit *= 1000; /* convert to ms */
1283 * Interpret SSL option
1285 if (ldap_conf.ssl != NULL) {
1286 if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
1287 ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
1288 else if (atobool(ldap_conf.ssl) == TRUE)
1289 ldap_conf.ssl_mode = SUDO_LDAP_SSL;
1292 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
1293 if (ldap_conf.tls_checkpeer != -1) {
1294 ldapssl_set_strength(NULL,
1295 ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
1299 #ifndef HAVE_LDAP_INITIALIZE
1300 /* Convert uri list to host list if no ldap_initialize(). */
1301 if (ldap_conf.uri) {
1302 struct ldap_config_list_str *uri = ldap_conf.uri;
1303 if (sudo_ldap_parse_uri(uri) != 0)
1306 ldap_conf.uri = uri->next;
1308 } while ((uri = ldap_conf.uri));
1309 ldap_conf.port = LDAP_PORT;
1313 if (!ldap_conf.uri) {
1314 /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
1315 if (ldap_conf.port < 0)
1317 ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
1319 #ifdef HAVE_LDAP_CREATE
1321 * Cannot specify port directly to ldap_create(), each host must
1322 * include :port to override the default.
1324 if (ldap_conf.port != LDAP_PORT)
1325 sudo_ldap_conf_add_ports();
1329 /* If search filter is not parenthesized, make it so. */
1330 if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
1331 size_t len = strlen(ldap_conf.search_filter);
1332 cp = ldap_conf.search_filter;
1333 ldap_conf.search_filter = emalloc(len + 3);
1334 ldap_conf.search_filter[0] = '(';
1335 memcpy(ldap_conf.search_filter + 1, cp, len);
1336 ldap_conf.search_filter[len + 1] = ')';
1337 ldap_conf.search_filter[len + 2] = '\0';
1341 /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
1342 if (ldap_conf.rootbinddn)
1343 sudo_ldap_read_secret(_PATH_LDAP_SECRET);
1345 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1347 * Make sure we can open the file specified by krb5_ccname.
1349 if (ldap_conf.krb5_ccname != NULL) {
1350 if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
1351 strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
1352 value = ldap_conf.krb5_ccname +
1353 (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
1354 if ((fp = fopen(value, "r")) != NULL) {
1355 DPRINTF(("using krb5 credential cache: %s", value), 1);
1358 /* Can't open it, just ignore the entry. */
1359 DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
1360 efree(ldap_conf.krb5_ccname);
1361 ldap_conf.krb5_ccname = NULL;
1370 * Extract the dn from an entry and return the first rdn from it.
1373 sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
1375 #ifdef HAVE_LDAP_STR2DN
1376 char *dn, *rdn = NULL;
1379 if ((dn = ldap_get_dn(ld, entry)) == NULL)
1381 if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
1382 ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
1390 if ((dn = ldap_get_dn(ld, entry)) == NULL)
1392 edn = ldap_explode_dn(dn, 1);
1394 return edn ? edn[0] : NULL;
1399 * Fetch and display the global Options.
1402 sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
1405 struct berval **bv, **p;
1406 struct timeval tv, *tvp = NULL;
1407 struct ldap_config_list_str *base;
1408 struct sudo_ldap_handle *handle = nss->handle;
1410 LDAPMessage *entry, *result;
1411 char *prefix, *filt;
1414 if (handle == NULL || handle->ld == NULL)
1418 filt = sudo_ldap_build_default_filter();
1419 for (base = ldap_conf.base; base != NULL; base = base->next) {
1420 if (ldap_conf.timeout > 0) {
1421 tv.tv_sec = ldap_conf.timeout;
1426 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
1427 filt, NULL, 0, NULL, NULL, tvp, 0, &result);
1428 if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
1429 bv = ldap_get_values_len(ld, entry, "sudoOption");
1431 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
1435 for (p = bv; *p != NULL; p++) {
1436 lbuf_append(lbuf, "%s%s", prefix, (*p)->bv_val);
1440 ldap_value_free_len(bv);
1444 ldap_msgfree(result);
1455 sudo_ldap_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
1462 * Print a record in the short form, ala file sudoers.
1465 sudo_ldap_display_entry_short(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
1467 struct berval **bv, **p;
1470 lbuf_append(lbuf, " (");
1472 /* get the RunAsUser Values from the entry */
1473 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1475 bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1477 for (p = bv; *p != NULL; p++) {
1478 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1480 ldap_value_free_len(bv);
1482 lbuf_append(lbuf, "%s", def_runas_default);
1484 /* get the RunAsGroup Values from the entry */
1485 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1487 lbuf_append(lbuf, " : ");
1488 for (p = bv; *p != NULL; p++) {
1489 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1491 ldap_value_free_len(bv);
1493 lbuf_append(lbuf, ") ");
1495 /* get the Option Values from the entry */
1496 bv = ldap_get_values_len(ld, entry, "sudoOption");
1498 for (p = bv; *p != NULL; p++) {
1499 char *cp = (*p)->bv_val;
1502 if (strcmp(cp, "authenticate") == 0)
1503 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1504 "NOPASSWD: " : "PASSWD: ");
1505 else if (strcmp(cp, "noexec") == 0)
1506 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1507 "EXEC: " : "NOEXEC: ");
1508 else if (strcmp(cp, "setenv") == 0)
1509 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1510 "NOSETENV: " : "SETENV: ");
1512 ldap_value_free_len(bv);
1515 /* get the Command Values from the entry */
1516 bv = ldap_get_values_len(ld, entry, "sudoCommand");
1518 for (p = bv; *p != NULL; p++) {
1519 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1522 ldap_value_free_len(bv);
1524 lbuf_append(lbuf, "\n");
1530 * Print a record in the long form.
1533 sudo_ldap_display_entry_long(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
1535 struct berval **bv, **p;
1539 /* extract the dn, only show the first rdn */
1540 rdn = sudo_ldap_get_first_rdn(ld, entry);
1542 lbuf_append(lbuf, _("\nLDAP Role: %s\n"), rdn);
1544 lbuf_append(lbuf, _("\nLDAP Role: UNKNOWN\n"));
1548 /* get the RunAsUser Values from the entry */
1549 lbuf_append(lbuf, " RunAsUsers: ");
1550 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1552 bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1554 for (p = bv; *p != NULL; p++) {
1555 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1557 ldap_value_free_len(bv);
1559 lbuf_append(lbuf, "%s", def_runas_default);
1560 lbuf_append(lbuf, "\n");
1562 /* get the RunAsGroup Values from the entry */
1563 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1565 lbuf_append(lbuf, " RunAsGroups: ");
1566 for (p = bv; *p != NULL; p++) {
1567 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1569 ldap_value_free_len(bv);
1570 lbuf_append(lbuf, "\n");
1573 /* get the Option Values from the entry */
1574 bv = ldap_get_values_len(ld, entry, "sudoOption");
1576 lbuf_append(lbuf, " Options: ");
1577 for (p = bv; *p != NULL; p++) {
1578 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1580 ldap_value_free_len(bv);
1581 lbuf_append(lbuf, "\n");
1585 * Display order attribute if present. This attribute is single valued,
1586 * so there is no need for a loop.
1588 bv = ldap_get_values_len(ld, entry, "sudoOrder");
1591 lbuf_append(lbuf, _(" Order: %s\n"), (*bv)->bv_val);
1593 ldap_value_free_len(bv);
1596 /* Get the command values from the entry. */
1597 bv = ldap_get_values_len(ld, entry, "sudoCommand");
1599 lbuf_append(lbuf, _(" Commands:\n"));
1600 for (p = bv; *p != NULL; p++) {
1601 lbuf_append(lbuf, "\t%s\n", (*p)->bv_val);
1604 ldap_value_free_len(bv);
1611 * Like sudo_ldap_lookup(), except we just print entries.
1614 sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
1617 struct sudo_ldap_handle *handle = nss->handle;
1619 struct ldap_result *lres;
1623 if (handle == NULL || handle->ld == NULL)
1627 DPRINTF(("ldap search for command list"), 1);
1628 lres = sudo_ldap_result_get(nss, pw);
1630 /* Display all matching entries. */
1631 for (i = 0; i < lres->nentries; i++) {
1632 entry = lres->entries[i].entry;
1634 count += sudo_ldap_display_entry_long(ld, entry, lbuf);
1636 count += sudo_ldap_display_entry_short(ld, entry, lbuf);
1644 sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
1646 struct sudo_ldap_handle *handle = nss->handle;
1648 struct ldap_result *lres;
1650 int i, found = FALSE;
1652 if (handle == NULL || handle->ld == NULL)
1657 * The sudo_ldap_result_get() function returns all nodes that match
1658 * the user and the host.
1660 DPRINTF(("ldap search for command list"), 1);
1661 lres = sudo_ldap_result_get(nss, pw);
1662 for (i = 0; i < lres->nentries; i++) {
1663 entry = lres->entries[i].entry;
1664 if (sudo_ldap_check_command(ld, entry, NULL) &&
1665 sudo_ldap_check_runas(ld, entry)) {
1673 printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
1674 user_args ? " " : "", user_args ? user_args : "");
1678 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1680 sudo_ldap_sasl_interact(LDAP *ld, unsigned int flags, void *_auth_id,
1683 char *auth_id = (char *)_auth_id;
1684 sasl_interact_t *interact = (sasl_interact_t *)_interact;
1686 for (; interact->id != SASL_CB_LIST_END; interact++) {
1687 if (interact->id != SASL_CB_USER)
1688 return LDAP_PARAM_ERROR;
1690 if (auth_id != NULL)
1691 interact->result = auth_id;
1692 else if (interact->defresult != NULL)
1693 interact->result = interact->defresult;
1695 interact->result = "";
1697 interact->len = strlen(interact->result);
1698 #if SASL_VERSION_MAJOR < 2
1699 interact->result = estrdup(interact->result);
1700 #endif /* SASL_VERSION_MAJOR < 2 */
1702 return LDAP_SUCCESS;
1704 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1707 * Set LDAP options based on the config table.
1710 sudo_ldap_set_options(LDAP *ld)
1712 struct ldap_config_table *cur;
1715 /* Set ber options */
1716 #ifdef LBER_OPT_DEBUG_LEVEL
1717 if (ldap_conf.ldap_debug)
1718 ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
1721 /* Set simple LDAP options */
1722 for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1727 if (cur->opt_val == -1)
1730 conn = cur->connected ? ld : NULL;
1731 switch (cur->type) {
1734 ival = *(int *)(cur->valp);
1736 rc = ldap_set_option(conn, cur->opt_val, &ival);
1737 if (rc != LDAP_OPT_SUCCESS) {
1738 warningx("ldap_set_option: %s -> %d: %s",
1739 cur->conf_str, ival, ldap_err2string(rc));
1742 DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
1746 sval = *(char **)(cur->valp);
1748 rc = ldap_set_option(conn, cur->opt_val, sval);
1749 if (rc != LDAP_OPT_SUCCESS) {
1750 warningx("ldap_set_option: %s -> %s: %s",
1751 cur->conf_str, sval, ldap_err2string(rc));
1754 DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
1760 #ifdef LDAP_OPT_TIMEOUT
1761 /* Convert timeout to a timeval */
1762 if (ldap_conf.timeout > 0) {
1764 tv.tv_sec = ldap_conf.timeout;
1766 rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
1767 if (rc != LDAP_OPT_SUCCESS) {
1768 warningx("ldap_set_option(TIMEOUT, %ld): %s",
1769 (long)tv.tv_sec, ldap_err2string(rc));
1772 DPRINTF(("ldap_set_option(LDAP_OPT_TIMEOUT, %ld)",
1773 (long)tv.tv_sec), 1);
1776 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1777 /* Convert bind_timelimit to a timeval */
1778 if (ldap_conf.bind_timelimit > 0) {
1780 tv.tv_sec = ldap_conf.bind_timelimit / 1000;
1782 rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
1783 if (rc != LDAP_OPT_SUCCESS) {
1784 warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
1785 (long)tv.tv_sec, ldap_err2string(rc));
1788 DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)",
1789 (long)tv.tv_sec), 1);
1793 #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
1794 if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
1795 int val = LDAP_OPT_X_TLS_HARD;
1796 rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
1797 if (rc != LDAP_SUCCESS) {
1798 warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
1799 ldap_err2string(rc));
1802 DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)"), 1);
1809 * Create a new sudo_ldap_result structure.
1811 static struct ldap_result *
1812 sudo_ldap_result_alloc(void)
1814 struct ldap_result *result;
1816 result = emalloc(sizeof(*result));
1817 result->searches = NULL;
1818 result->nentries = 0;
1819 result->entries = NULL;
1820 result->allocated_entries = 0;
1821 result->user_matches = FALSE;
1822 result->host_matches = FALSE;
1827 * Free the ldap result structure
1830 sudo_ldap_result_free(struct ldap_result *lres)
1832 struct ldap_search_list *s;
1835 if (lres->nentries) {
1836 efree(lres->entries);
1837 lres->entries = NULL;
1839 if (lres->searches) {
1840 while ((s = lres->searches) != NULL) {
1841 ldap_msgfree(s->searchresult);
1842 lres->searches = s->next;
1851 * Add a search result to the ldap_result structure.
1853 static struct ldap_search_list *
1854 sudo_ldap_result_add_search(struct ldap_result *lres, LDAP *ldap,
1855 LDAPMessage *searchresult)
1857 struct ldap_search_list *s, *news;
1859 news = emalloc(sizeof(struct ldap_search_list));
1862 news->searchresult = searchresult;
1864 /* Add entry to the end of the chain (XXX - tailq instead?). */
1865 if (lres->searches) {
1866 for (s = lres->searches; s->next != NULL; s = s->next)
1870 lres->searches = news;
1876 * Connect to the LDAP server specified by ld
1879 sudo_ldap_bind_s(LDAP *ld)
1882 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1883 const char *old_ccname = user_ccname;
1884 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1885 unsigned int status;
1889 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1890 if (ldap_conf.rootuse_sasl == TRUE ||
1891 (ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE)) {
1892 void *auth_id = ldap_conf.rootsasl_auth_id ?
1893 ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
1895 if (ldap_conf.krb5_ccname != NULL) {
1896 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1897 if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
1898 != GSS_S_COMPLETE) {
1900 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
1903 setenv("KRB5CCNAME", ldap_conf.krb5_ccname, TRUE);
1906 rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
1907 NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
1908 if (ldap_conf.krb5_ccname != NULL) {
1909 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1910 if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
1911 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
1913 if (old_ccname != NULL)
1914 setenv("KRB5CCNAME", old_ccname, TRUE);
1916 unsetenv("KRB5CCNAME");
1919 if (rc != LDAP_SUCCESS) {
1920 warningx("ldap_sasl_interactive_bind_s(): %s",
1921 ldap_err2string(rc));
1924 DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
1926 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1927 #ifdef HAVE_LDAP_SASL_BIND_S
1931 bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
1932 bv.bv_len = strlen(bv.bv_val);
1934 rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
1936 if (rc != LDAP_SUCCESS) {
1937 warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
1940 DPRINTF(("ldap_sasl_bind_s() ok"), 1);
1944 rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
1945 if (rc != LDAP_SUCCESS) {
1946 warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
1949 DPRINTF(("ldap_simple_bind_s() ok"), 1);
1956 * Open a connection to the LDAP server.
1957 * Returns 0 on success and non-zero on failure.
1960 sudo_ldap_open(struct sudo_nss *nss)
1963 int rc, ldapnoinit = FALSE;
1964 struct sudo_ldap_handle *handle;
1966 if (!sudo_ldap_read_config())
1969 /* Prevent reading of user ldaprc and system defaults. */
1970 if (getenv("LDAPNOINIT") == NULL) {
1972 setenv("LDAPNOINIT", "1", TRUE);
1975 /* Connect to LDAP server */
1976 #ifdef HAVE_LDAP_INITIALIZE
1977 if (ldap_conf.uri != NULL) {
1978 char *buf = sudo_ldap_join_uri(ldap_conf.uri);
1979 DPRINTF(("ldap_initialize(ld, %s)", buf), 2);
1980 rc = ldap_initialize(&ld, buf);
1984 rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
1985 if (rc != LDAP_SUCCESS) {
1986 warningx(_("unable to initialize LDAP: %s"), ldap_err2string(rc));
1991 unsetenv("LDAPNOINIT");
1993 /* Set LDAP options */
1994 if (sudo_ldap_set_options(ld) < 0)
1997 if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
1998 #if defined(HAVE_LDAP_START_TLS_S)
1999 rc = ldap_start_tls_s(ld, NULL, NULL);
2000 if (rc != LDAP_SUCCESS) {
2001 warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
2004 DPRINTF(("ldap_start_tls_s() ok"), 1);
2005 #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
2006 if (ldap_ssl_client_init(NULL, NULL, 0, &rc) != LDAP_SUCCESS) {
2007 warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
2010 rc = ldap_start_tls_s_np(ld, NULL);
2011 if (rc != LDAP_SUCCESS) {
2012 warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
2015 DPRINTF(("ldap_start_tls_s_np() ok"), 1);
2017 warningx(_("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()"));
2018 #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
2021 /* Actually connect */
2022 if (sudo_ldap_bind_s(ld) != 0)
2025 /* Create a handle container. */
2026 handle = emalloc(sizeof(struct sudo_ldap_handle));
2028 handle->result = NULL;
2029 handle->username = NULL;
2030 handle->grlist = NULL;
2031 nss->handle = handle;
2037 sudo_ldap_setdefs(struct sudo_nss *nss)
2039 struct ldap_config_list_str *base;
2040 struct sudo_ldap_handle *handle = nss->handle;
2041 struct timeval tv, *tvp = NULL;
2043 LDAPMessage *entry, *result;
2047 if (handle == NULL || handle->ld == NULL)
2051 filt = sudo_ldap_build_default_filter();
2052 DPRINTF(("Looking for cn=defaults: %s", filt), 1);
2054 for (base = ldap_conf.base; base != NULL; base = base->next) {
2055 if (ldap_conf.timeout > 0) {
2056 tv.tv_sec = ldap_conf.timeout;
2061 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
2062 filt, NULL, 0, NULL, NULL, tvp, 0, &result);
2063 if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
2064 DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
2065 sudo_ldap_parse_options(ld, entry);
2067 DPRINTF(("no default options found in %s", base->val), 1);
2070 ldap_msgfree(result);
2078 * like sudoers_lookup() - only LDAP style
2081 sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
2083 struct sudo_ldap_handle *handle = nss->handle;
2086 int i, rc, setenv_implied;
2087 struct ldap_result *lres = NULL;
2089 if (handle == NULL || handle->ld == NULL)
2093 /* Fetch list of sudoRole entries that match user and host. */
2094 lres = sudo_ldap_result_get(nss, sudo_user.pw);
2097 * The following queries are only determine whether or not a
2098 * password is required, so the order of the entries doesn't matter.
2101 int doauth = UNSPEC;
2102 int matched = UNSPEC;
2103 enum def_tuple pwcheck =
2104 (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
2106 DPRINTF(("perform search for pwflag %d", pwflag), 1);
2107 for (i = 0; i < lres->nentries; i++) {
2108 entry = lres->entries[i].entry;
2109 if ((pwcheck == any && doauth != FALSE) ||
2110 (pwcheck == all && doauth == FALSE)) {
2111 doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
2113 /* Only check the command when listing another user. */
2114 if (user_uid == 0 || list_pw == NULL ||
2115 user_uid == list_pw->pw_uid ||
2116 sudo_ldap_check_command(ld, entry, NULL)) {
2121 if (matched || user_uid == 0) {
2122 SET(ret, VALIDATE_OK);
2123 CLR(ret, VALIDATE_NOT_OK);
2124 if (def_authenticate) {
2127 SET(ret, FLAG_CHECK_USER);
2131 if (doauth == FALSE)
2132 def_authenticate = FALSE;
2135 def_authenticate = FALSE;
2145 DPRINTF(("searching LDAP for sudoers entries"), 1);
2147 setenv_implied = FALSE;
2148 for (i = 0; i < lres->nentries; i++) {
2149 entry = lres->entries[i].entry;
2150 if (!sudo_ldap_check_runas(ld, entry))
2152 rc = sudo_ldap_check_command(ld, entry, &setenv_implied);
2154 /* We have a match. */
2155 DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
2157 DPRINTF(("LDAP entry: %p", entry), 1);
2158 /* Apply entry-specific options. */
2161 sudo_ldap_parse_options(ld, entry);
2163 /* Set role and type if not specified on command line. */
2164 if (user_role == NULL)
2165 user_role = def_role;
2166 if (user_type == NULL)
2167 user_type = def_type;
2168 #endif /* HAVE_SELINUX */
2169 SET(ret, VALIDATE_OK);
2170 CLR(ret, VALIDATE_NOT_OK);
2172 SET(ret, VALIDATE_NOT_OK);
2173 CLR(ret, VALIDATE_OK);
2180 DPRINTF(("done with LDAP searches"), 1);
2181 DPRINTF(("user_matches=%d", lres->user_matches), 1);
2182 DPRINTF(("host_matches=%d", lres->host_matches), 1);
2184 if (!ISSET(ret, VALIDATE_OK)) {
2185 /* No matching entries. */
2186 if (pwflag && list_pw == NULL)
2187 SET(ret, FLAG_NO_CHECK);
2189 if (lres->user_matches)
2190 CLR(ret, FLAG_NO_USER);
2191 if (lres->host_matches)
2192 CLR(ret, FLAG_NO_HOST);
2193 DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
2199 * Comparison function for ldap_entry_wrapper structures, descending order.
2202 ldap_entry_compare(const void *a, const void *b)
2204 const struct ldap_entry_wrapper *aw = a;
2205 const struct ldap_entry_wrapper *bw = b;
2207 return bw->order < aw->order ? -1 :
2208 (bw->order > aw->order ? 1 : 0);
2212 * Find the last entry in the list of searches, usually the
2213 * one currently being used to add entries.
2214 * XXX - use a tailq instead?
2216 static struct ldap_search_list *
2217 sudo_ldap_result_last_search(struct ldap_result *lres)
2219 struct ldap_search_list *result = lres->searches;
2222 while (result->next)
2223 result = result->next;
2229 * Add an entry to the result structure.
2231 static struct ldap_entry_wrapper *
2232 sudo_ldap_result_add_entry(struct ldap_result *lres, LDAPMessage *entry)
2234 struct ldap_search_list *last;
2239 /* Determine whether the entry has the sudoOrder attribute. */
2240 last = sudo_ldap_result_last_search(lres);
2241 bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
2243 if (ldap_count_values_len(bv) > 0) {
2244 /* Get the value of this attribute, 0 if not present. */
2245 DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
2246 order = strtod((*bv)->bv_val, &ep);
2247 if (ep == (*bv)->bv_val || *ep != '\0') {
2248 warningx(_("invalid sudoOrder attribute: %s"), (*bv)->bv_val);
2251 DPRINTF(("order attribute: %f", order), 1);
2253 ldap_value_free_len(bv);
2257 * Enlarge the array of entry wrappers as needed, preallocating blocks
2258 * of 100 entries to save on allocation time.
2260 if (++lres->nentries > lres->allocated_entries) {
2261 lres->allocated_entries += ALLOCATION_INCREMENT;
2262 lres->entries = erealloc3(lres->entries, lres->allocated_entries,
2263 sizeof(lres->entries[0]));
2266 /* Fill in the new entry and return it. */
2267 lres->entries[lres->nentries - 1].entry = entry;
2268 lres->entries[lres->nentries - 1].order = order;
2270 return &lres->entries[lres->nentries - 1];
2274 * Free the ldap result structure in the sudo_nss handle.
2277 sudo_ldap_result_free_nss(struct sudo_nss *nss)
2279 struct sudo_ldap_handle *handle = nss->handle;
2281 if (handle->result != NULL) {
2282 DPRINTF(("removing reusable search result"), 1);
2283 sudo_ldap_result_free(handle->result);
2284 if (handle->username) {
2285 efree(handle->username);
2286 handle->username = NULL;
2288 handle->grlist = NULL;
2289 handle->result = NULL;
2294 * Perform the LDAP query for the user or return a cached query if
2295 * there is one for this user.
2297 static struct ldap_result *
2298 sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw)
2300 struct sudo_ldap_handle *handle = nss->handle;
2301 struct ldap_config_list_str *base;
2302 struct ldap_result *lres;
2303 struct timeval tv, *tvp = NULL;
2304 LDAPMessage *entry, *result;
2305 LDAP *ld = handle->ld;
2310 * If we already have a cached result, return it so we don't have to
2311 * have to contact the LDAP server again.
2313 if (handle->result) {
2314 if (handle->grlist == user_group_list &&
2315 strcmp(pw->pw_name, handle->username) == 0) {
2316 DPRINTF(("reusing previous result (user %s) with %d entries",
2317 handle->username, handle->result->nentries), 1);
2318 return handle->result;
2320 /* User mismatch, cached result cannot be used. */
2321 DPRINTF(("removing result (user %s), new search (user %s)",
2322 handle->username, pw->pw_name), 1);
2323 sudo_ldap_result_free_nss(nss);
2327 * Okay - time to search for anything that matches this user
2328 * Lets limit it to only two queries of the LDAP server
2330 * The first pass will look by the username, groups, and
2331 * the keyword ALL. We will then inspect the results that
2332 * came back from the query. We don't need to inspect the
2333 * sudoUser in this pass since the LDAP server already scanned
2336 * The second pass will return all the entries that contain
2337 * user netgroups. Then we take the netgroups returned and
2338 * try to match them against the username.
2340 * Since we have to sort the possible entries before we make a
2341 * decision, we perform the queries and store all of the results in
2342 * an ldap_result object. The results are then sorted by sudoOrder.
2344 lres = sudo_ldap_result_alloc();
2345 for (do_netgr = 0; do_netgr < 2; do_netgr++) {
2346 filt = do_netgr ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
2347 DPRINTF(("ldap search '%s'", filt), 1);
2348 for (base = ldap_conf.base; base != NULL; base = base->next) {
2349 DPRINTF(("searching from base '%s'", base->val), 1);
2350 if (ldap_conf.timeout > 0) {
2351 tv.tv_sec = ldap_conf.timeout;
2356 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
2357 NULL, 0, NULL, NULL, tvp, 0, &result);
2358 if (rc != LDAP_SUCCESS) {
2359 DPRINTF(("nothing found for '%s'", filt), 1);
2362 lres->user_matches = TRUE;
2364 /* Add the seach result to list of search results. */
2365 DPRINTF(("adding search result"), 1);
2366 sudo_ldap_result_add_search(lres, ld, result);
2367 LDAP_FOREACH(entry, ld, result) {
2369 sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
2370 sudo_ldap_check_host(ld, entry)) {
2371 lres->host_matches = TRUE;
2372 sudo_ldap_result_add_entry(lres, entry);
2375 DPRINTF(("result now has %d entries", lres->nentries), 1);
2380 /* Sort the entries by the sudoOrder attribute. */
2381 DPRINTF(("sorting remaining %d entries", lres->nentries), 1);
2382 qsort(lres->entries, lres->nentries, sizeof(lres->entries[0]),
2383 ldap_entry_compare);
2385 /* Store everything in the sudo_nss handle. */
2386 handle->result = lres;
2387 handle->username = estrdup(pw->pw_name);
2388 handle->grlist = user_group_list;
2394 * Shut down the LDAP connection.
2397 sudo_ldap_close(struct sudo_nss *nss)
2399 struct sudo_ldap_handle *handle = nss->handle;
2401 if (handle != NULL) {
2402 /* Free the result before unbinding; it may use the LDAP connection. */
2403 sudo_ldap_result_free_nss(nss);
2405 /* Unbind and close the LDAP connection. */
2406 if (handle->ld != NULL) {
2407 ldap_unbind_ext_s(handle->ld, NULL, NULL);
2411 /* Free the handle container. */
2422 sudo_ldap_parse(struct sudo_nss *nss)
2429 * Create an ldap_result from an LDAP search result.
2431 * This function is currently not used anywhere, it is left here as
2432 * an example of how to use the cached searches.
2434 static struct ldap_result *
2435 sudo_ldap_result_from_search(LDAP *ldap, LDAPMessage *searchresult)
2438 * An ldap_result is built from several search results, which are
2439 * organized in a list. The head of the list is maintained in the
2440 * ldap_result structure, together with the wrappers that point
2441 * to individual entries, this has to be initialized first.
2443 struct ldap_result *result = sudo_ldap_result_alloc();
2446 * Build a new list node for the search result, this creates the
2449 struct ldap_search_list *last = sudo_ldap_result_add_search(result,
2450 ldap, searchresult);
2453 * Now add each entry in the search result to the array of of entries
2454 * in the ldap_result object.
2457 LDAP_FOREACH(entry, last->ldap, last->searchresult) {
2458 sudo_ldap_result_add_entry(result, entry);
2460 DPRINTF(("sudo_ldap_result_from_search: %d entries found",
2461 result->nentries), 2);