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 __P((struct sudo_nss *nss));
306 static int sudo_ldap_close __P((struct sudo_nss *nss));
307 static int sudo_ldap_parse __P((struct sudo_nss *nss));
308 static int sudo_ldap_setdefs __P((struct sudo_nss *nss));
309 static int sudo_ldap_lookup __P((struct sudo_nss *nss, int ret, int pwflag));
310 static int sudo_ldap_display_cmnd __P((struct sudo_nss *nss,
312 static int sudo_ldap_display_defaults __P((struct sudo_nss *nss,
313 struct passwd *pw, struct lbuf *lbuf));
314 static int sudo_ldap_display_bound_defaults __P((struct sudo_nss *nss,
315 struct passwd *pw, struct lbuf *lbuf));
316 static int sudo_ldap_display_privs __P((struct sudo_nss *nss,
317 struct passwd *pw, struct lbuf *lbuf));
318 static struct ldap_result *sudo_ldap_result_get __P((struct sudo_nss *nss,
322 * LDAP sudo_nss handle.
323 * We store the connection to the LDAP server, the cached ldap_result object
324 * (if any), and the name of the user the query was performed for.
325 * If a new query is launched with sudo_ldap_result_get() that specifies a
326 * different user, the old cached result is freed before the new query is run.
328 struct sudo_ldap_handle {
330 struct ldap_result *result;
335 struct sudo_nss sudo_nss_ldap = {
343 sudo_ldap_display_cmnd,
344 sudo_ldap_display_defaults,
345 sudo_ldap_display_bound_defaults,
346 sudo_ldap_display_privs
349 #ifdef HAVE_LDAP_CREATE
351 * Rebuild the hosts list and include a specific port for each host.
352 * ldap_create() does not take a default port parameter so we must
353 * append one if we want something other than LDAP_PORT.
356 sudo_ldap_conf_add_ports()
359 char *host, *port, defport[13];
360 char hostbuf[LINE_MAX * 2];
363 if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
364 errorx(1, "sudo_ldap_conf_add_ports: port too large");
366 for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
367 if (hostbuf[0] != '\0') {
368 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
372 if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
374 /* Append port if there is not one already. */
375 if ((port = strrchr(host, ':')) == NULL ||
376 !isdigit((unsigned char)port[1])) {
377 if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
382 efree(ldap_conf.host);
383 ldap_conf.host = estrdup(hostbuf);
387 errorx(1, "sudo_ldap_conf_add_ports: out of space expanding hostbuf");
391 #ifndef HAVE_LDAP_INITIALIZE
393 * For each uri, convert to host:port pairs. For ldaps:// enable SSL
394 * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
395 * where the trailing slash is optional.
398 sudo_ldap_parse_uri(uri_list)
399 const struct ldap_config_list_str *uri_list;
401 char *buf, *uri, *host, *cp, *port;
402 char hostbuf[LINE_MAX];
403 int nldap = 0, nldaps = 0;
407 buf = estrdup(uri_list->val);
409 for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
410 if (strncasecmp(uri, "ldap://", 7) == 0) {
413 } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
417 warningx("unsupported LDAP uri type: %s", uri);
421 /* trim optional trailing slash */
422 if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
426 if (hostbuf[0] != '\0') {
427 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
432 host = "localhost"; /* no host specified, use localhost */
434 if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
437 /* If using SSL and no port specified, add port 636 */
439 if ((port = strrchr(host, ':')) == NULL ||
440 !isdigit((unsigned char)port[1]))
441 if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
445 if (hostbuf[0] == '\0') {
446 warningx("invalid uri: %s", uri_list);
452 warningx("cannot mix ldap and ldaps URIs");
455 if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
456 warningx("cannot mix ldaps and starttls");
459 ldap_conf.ssl_mode = SUDO_LDAP_SSL;
462 efree(ldap_conf.host);
463 ldap_conf.host = estrdup(hostbuf);
465 } while ((uri_list = uri_list->next));
475 errorx(1, "sudo_ldap_parse_uri: out of space building hostbuf");
479 sudo_ldap_join_uri(uri_list)
480 struct ldap_config_list_str *uri_list;
482 struct ldap_config_list_str *uri;
486 /* Usually just a single entry. */
487 if (uri_list->next == NULL)
488 return estrdup(uri_list->val);
490 for (uri = uri_list; uri != NULL; uri = uri->next) {
491 len += strlen(uri->val) + 1;
493 buf = cp = emalloc(len);
495 for (uri = uri_list; uri != NULL; uri = uri->next) {
496 cp += strlcpy(cp, uri->val, len - (cp - buf));
502 #endif /* HAVE_LDAP_INITIALIZE */
505 sudo_ldap_init(ldp, host, port)
511 int rc = LDAP_CONNECT_ERROR;
513 #ifdef HAVE_LDAPSSL_INIT
514 if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
515 DPRINTF(("ldapssl_clientauth_init(%s, %s)",
516 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
517 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
518 rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
519 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
521 * Mozilla-derived SDKs have a bug starting with version 5.0
522 * where the path can no longer be a file name and must be a dir.
524 if (rc != LDAP_SUCCESS) {
526 if (ldap_conf.tls_certfile) {
527 cp = strrchr(ldap_conf.tls_certfile, '/');
528 if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0)
531 if (ldap_conf.tls_keyfile) {
532 cp = strrchr(ldap_conf.tls_keyfile, '/');
533 if (cp != NULL && strncmp(cp + 1, "key", 3) == 0)
536 DPRINTF(("ldapssl_clientauth_init(%s, %s)",
537 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
538 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
539 rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
540 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
541 if (rc != LDAP_SUCCESS) {
542 warningx("unable to initialize SSL cert and key db: %s",
543 ldapssl_err2string(rc));
548 DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
549 if ((ld = ldapssl_init(host, port, 1)) != NULL)
554 #ifdef HAVE_LDAP_CREATE
555 DPRINTF(("ldap_create()"), 2);
556 if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
558 DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
559 rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
561 DPRINTF(("ldap_init(%s, %d)", host, port), 2);
562 if ((ld = ldap_init(host, port)) != NULL)
573 * Walk through search results and return TRUE if we have a matching
574 * netgroup, else FALSE.
577 sudo_ldap_check_user_netgroup(ld, entry, user)
582 struct berval **bv, **p;
589 /* get the values from the entry */
590 bv = ldap_get_values_len(ld, entry, "sudoUser");
594 /* walk through values */
595 for (p = bv; *p != NULL && !ret; p++) {
598 if (netgr_matches(val, NULL, NULL, user))
600 DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
601 ret ? "MATCH!" : "not"), 2 + ((ret) ? 0 : 1));
604 ldap_value_free_len(bv); /* cleanup */
610 * Walk through search results and return TRUE if we have a
611 * host match, else FALSE.
614 sudo_ldap_check_host(ld, entry)
618 struct berval **bv, **p;
625 /* get the values from the entry */
626 bv = ldap_get_values_len(ld, entry, "sudoHost");
630 /* walk through values */
631 for (p = bv; *p != NULL && !ret; p++) {
633 /* match any or address or netgroup or hostname */
634 if (!strcmp(val, "ALL") || addr_matches(val) ||
635 netgr_matches(val, user_host, user_shost, NULL) ||
636 hostname_matches(user_shost, user_host, val))
638 DPRINTF(("ldap sudoHost '%s' ... %s", val,
639 ret ? "MATCH!" : "not"), 2);
642 ldap_value_free_len(bv); /* cleanup */
648 sudo_ldap_check_runas_user(ld, entry)
652 struct berval **bv, **p;
659 /* get the runas user from the entry */
660 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
662 bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
667 * if runas is not specified on the command line, the only information
668 * as to which user to run as is in the runas_default option. We should
669 * check to see if we have the local option present. Unfortunately we
670 * don't parse these options until after this routine says yes or no.
671 * The query has already returned, so we could peek at the attribute
672 * values here though.
674 * For now just require users to always use -u option unless its set
675 * in the global defaults. This behaviour is no different than the global
678 * Sigh - maybe add this feature later
682 * If there are no runas entries, match runas_default against
683 * what the user specified on the command line.
686 return !strcasecmp(runas_pw->pw_name, def_runas_default);
688 /* walk through values returned, looking for a match */
689 for (p = bv; *p != NULL && !ret; p++) {
693 if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
697 if (usergr_matches(val, runas_pw->pw_name, runas_pw))
701 if (strcmp(val, "ALL") == 0) {
707 if (strcasecmp(val, runas_pw->pw_name) == 0)
711 DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
712 ret ? "MATCH!" : "not"), 2);
715 ldap_value_free_len(bv); /* cleanup */
721 sudo_ldap_check_runas_group(ld, entry)
725 struct berval **bv, **p;
729 /* runas_gr is only set if the user specified the -g flag */
733 /* get the values from the entry */
734 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
738 /* walk through values returned, looking for a match */
739 for (p = bv; *p != NULL && !ret; p++) {
741 if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
743 DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
744 ret ? "MATCH!" : "not"), 2);
747 ldap_value_free_len(bv); /* cleanup */
753 * Walk through search results and return TRUE if we have a runas match,
754 * else FALSE. RunAs info is optional.
757 sudo_ldap_check_runas(ld, entry)
766 ret = sudo_ldap_check_runas_user(ld, entry) != FALSE &&
767 sudo_ldap_check_runas_group(ld, entry) != FALSE;
773 * Walk through search results and return TRUE if we have a command match,
774 * FALSE if disallowed and UNSPEC if not matched.
777 sudo_ldap_check_command(ld, entry, setenv_implied)
782 struct berval **bv, **p;
783 char *allowed_cmnd, *allowed_args, *val;
784 int foundbang, ret = UNSPEC;
789 bv = ldap_get_values_len(ld, entry, "sudoCommand");
793 for (p = bv; *p != NULL && ret != FALSE; p++) {
795 /* Match against ALL ? */
796 if (!strcmp(val, "ALL")) {
798 if (setenv_implied != NULL)
799 *setenv_implied = TRUE;
800 DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
804 /* check for !command */
807 allowed_cmnd = estrdup(1 + val); /* !command */
810 allowed_cmnd = estrdup(val); /* command */
813 /* split optional args away from command */
814 allowed_args = strchr(allowed_cmnd, ' ');
816 *allowed_args++ = '\0';
818 /* check the command like normal */
819 if (command_matches(allowed_cmnd, allowed_args)) {
821 * If allowed (no bang) set ret but keep on checking.
822 * If disallowed (bang), exit loop.
824 ret = foundbang ? FALSE : TRUE;
826 DPRINTF(("ldap sudoCommand '%s' ... %s", val,
827 ret == TRUE ? "MATCH!" : "not"), 2);
829 efree(allowed_cmnd); /* cleanup */
832 ldap_value_free_len(bv); /* more cleanup */
838 * Search for boolean "option" in sudoOption.
839 * Returns TRUE if found and allowed, FALSE if negated, else UNSPEC.
842 sudo_ldap_check_bool(ld, entry, option)
847 struct berval **bv, **p;
854 bv = ldap_get_values_len(ld, entry, "sudoOption");
858 /* walk through options */
859 for (p = bv; *p != NULL; p++) {
861 DPRINTF(("ldap sudoOption: '%s'", var), 2);
863 if ((ch = *var) == '!')
865 if (strcmp(var, option) == 0)
869 ldap_value_free_len(bv);
875 * Read sudoOption and modify the defaults as we go. This is used once
876 * from the cn=defaults entry and also once when a final sudoRole is matched.
879 sudo_ldap_parse_options(ld, entry)
883 struct berval **bv, **p;
889 bv = ldap_get_values_len(ld, entry, "sudoOption");
893 /* walk through options */
894 for (p = bv; *p != NULL; p++) {
895 var = estrdup((*p)->bv_val);
896 DPRINTF(("ldap sudoOption: '%s'", var), 2);
898 /* check for equals sign past first char */
899 val = strchr(var, '=');
901 *val++ = '\0'; /* split on = and truncate var */
902 op = *(val - 2); /* peek for += or -= cases */
903 if (op == '+' || op == '-') {
904 *(val - 2) = '\0'; /* found, remove extra char */
905 /* case var+=val or var-=val */
906 set_default(var, val, (int) op);
909 set_default(var, val, TRUE);
911 } else if (*var == '!') {
912 /* case !var Boolean False */
913 set_default(var + 1, NULL, FALSE);
915 /* case var Boolean True */
916 set_default(var, NULL, TRUE);
921 ldap_value_free_len(bv);
925 * Build an LDAP timefilter.
927 * Stores a filter in the buffer that makes sure only entries
928 * are selected that have a sudoNotBefore in the past and a
929 * sudoNotAfter in the future, i.e. a filter of the following
930 * structure (spaced out a little more for better readability:
934 * (!(sudoNotAfter=*))
935 * (sudoNotAfter>__now__)
938 * (!(sudoNotBefore=*))
939 * (sudoNotBefore<__now__)
943 * If either the sudoNotAfter or sudoNotBefore attributes are missing,
944 * no time restriction shall be imposed.
947 sudo_ldap_timefilter(buffer, buffersize)
956 /* Make sure we have a formatted timestamp for __now__. */
958 if ((tp = gmtime(&now)) == NULL) {
959 warning("unable to get GMT");
963 /* Format the timestamp according to the RFC. */
964 if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%MZ", tp) == 0) {
965 warning("unable to format timestamp");
970 bytes = snprintf(buffer, buffersize, "(&(|(!(sudoNotAfter=*))(sudoNotAfter>=%s))(|(!(sudoNotBefore=*))(sudoNotBefore<=%s)))",
971 timebuffer, timebuffer);
972 if (bytes < 0 || bytes >= buffersize) {
973 warning("unable to build time filter");
982 * Builds up a filter to search for default settings
985 sudo_ldap_build_default_filter()
989 if (ldap_conf.search_filter)
990 easprintf(&filt, "(&%s(cn=defaults))", ldap_conf.search_filter);
992 filt = estrdup("cn=defaults");
997 * Builds up a filter to check against LDAP.
1000 sudo_ldap_build_pass1(pw)
1004 char *buf, timebuffer[TIMEFILTER_LENGTH];
1008 /* Start with LDAP search filter length + 3 */
1009 if (ldap_conf.search_filter)
1010 sz += strlen(ldap_conf.search_filter) + 3;
1012 /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
1013 sz += 29 + strlen(pw->pw_name);
1015 /* Add space for groups */
1016 if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
1017 sz += 12 + strlen(grp->gr_name); /* primary group */
1020 for (i = 0; i < user_ngroups; i++) {
1021 if (user_groups[i] == pw->pw_gid)
1023 if ((grp = sudo_getgrgid(user_groups[i])) != NULL) {
1024 sz += 12 + strlen(grp->gr_name); /* supplementary group */
1029 /* If timed, add space for time limits. */
1030 if (ldap_conf.timed)
1031 sz += TIMEFILTER_LENGTH;
1036 * If timed or using a search filter, start a global AND clause to
1037 * contain the search filter, search criteria, and time restriction.
1039 if (ldap_conf.timed || ldap_conf.search_filter)
1040 (void) strlcpy(buf, "(&", sz);
1042 if (ldap_conf.search_filter)
1043 (void) strlcat(buf, ldap_conf.search_filter, sz);
1045 /* Global OR + sudoUser=user_name filter */
1046 (void) strlcat(buf, "(|(sudoUser=", sz);
1047 (void) strlcat(buf, pw->pw_name, sz);
1048 (void) strlcat(buf, ")", sz);
1050 /* Append primary group */
1051 if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
1052 (void) strlcat(buf, "(sudoUser=%", sz);
1053 (void) strlcat(buf, grp->gr_name, sz);
1054 (void) strlcat(buf, ")", sz);
1058 /* Append supplementary groups */
1059 for (i = 0; i < user_ngroups; i++) {
1060 if (user_groups[i] == pw->pw_gid)
1062 if ((grp = sudo_getgrgid(user_groups[i])) != NULL) {
1063 (void) strlcat(buf, "(sudoUser=%", sz);
1064 (void) strlcat(buf, grp->gr_name, sz);
1065 (void) strlcat(buf, ")", sz);
1070 /* Add ALL to list and end the global OR */
1071 if (strlcat(buf, "(sudoUser=ALL)", sz) >= sz)
1072 errorx(1, "sudo_ldap_build_pass1 allocation mismatch");
1074 /* Add the time restriction, or simply end the global OR. */
1075 if (ldap_conf.timed) {
1076 strlcat(buf, ")", sz); /* closes the global OR */
1077 sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1078 strlcat(buf, timebuffer, sz);
1079 } else if (ldap_conf.search_filter) {
1080 strlcat(buf, ")", sz); /* closes the global OR */
1082 strlcat(buf, ")", sz); /* closes the global OR or the global AND */
1088 * Builds up a filter to check against netgroup entries in LDAP.
1091 sudo_ldap_build_pass2()
1093 char *filt, timebuffer[TIMEFILTER_LENGTH];
1095 if (ldap_conf.timed)
1096 sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1099 * Match all sudoUsers beginning with a '+'.
1100 * If a search filter or time restriction is specified,
1101 * those get ANDed in to the expression.
1103 easprintf(&filt, "%s%s(sudoUser=+*)%s%s",
1104 (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
1105 ldap_conf.search_filter ? ldap_conf.search_filter : "",
1106 ldap_conf.timed ? timebuffer : "",
1107 (ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
1113 * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
1122 if (strcasecmp(s, "yes") == 0)
1127 if (strcasecmp(s, "true") == 0)
1132 if (strcasecmp(s, "on") == 0)
1134 if (strcasecmp(s, "off") == 0)
1139 if (strcasecmp(s, "no") == 0)
1144 if (strcasecmp(s, "false") == 0)
1152 sudo_ldap_read_secret(path)
1156 char buf[LINE_MAX], *cp;
1158 if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
1159 if (fgets(buf, sizeof(buf), fp) != NULL) {
1160 if ((cp = strchr(buf, '\n')) != NULL)
1162 /* copy to bindpw and binddn */
1163 efree(ldap_conf.bindpw);
1164 ldap_conf.bindpw = estrdup(buf);
1165 efree(ldap_conf.binddn);
1166 ldap_conf.binddn = ldap_conf.rootbinddn;
1167 ldap_conf.rootbinddn = NULL;
1174 sudo_ldap_read_config()
1177 char *cp, *keyword, *value;
1178 struct ldap_config_table *cur;
1181 ldap_conf.version = 3;
1182 ldap_conf.port = -1;
1183 ldap_conf.tls_checkpeer = -1;
1184 ldap_conf.timelimit = -1;
1185 ldap_conf.timeout = -1;
1186 ldap_conf.bind_timelimit = -1;
1187 ldap_conf.use_sasl = -1;
1188 ldap_conf.rootuse_sasl = -1;
1190 if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
1193 while ((cp = sudo_parseln(fp)) != NULL) {
1195 continue; /* skip empty line */
1197 /* split into keyword and value */
1199 while (*cp && !isblank((unsigned char) *cp))
1202 *cp++ = '\0'; /* terminate keyword */
1204 /* skip whitespace before value */
1205 while (isblank((unsigned char) *cp))
1209 /* Look up keyword in config table. */
1210 for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1211 if (strcasecmp(keyword, cur->conf_str) == 0) {
1212 switch (cur->type) {
1214 *(int *)(cur->valp) = _atobool(value);
1217 *(int *)(cur->valp) = atoi(value);
1220 efree(*(char **)(cur->valp));
1221 *(char **)(cur->valp) = estrdup(value);
1225 struct ldap_config_list_str **p;
1226 size_t len = strlen(value);
1229 p = (struct ldap_config_list_str **)cur->valp;
1232 *p = emalloc(sizeof(struct ldap_config_list_str) + len);
1233 memcpy((*p)->val, value, len + 1);
1245 if (!ldap_conf.host)
1246 ldap_conf.host = estrdup("localhost");
1248 if (ldap_conf.debug > 1) {
1249 fprintf(stderr, "LDAP Config Summary\n");
1250 fprintf(stderr, "===================\n");
1251 if (ldap_conf.uri) {
1252 struct ldap_config_list_str *uri = ldap_conf.uri;
1255 fprintf(stderr, "uri %s\n", uri->val);
1256 } while ((uri = uri->next) != NULL);
1258 fprintf(stderr, "host %s\n", ldap_conf.host ?
1259 ldap_conf.host : "(NONE)");
1260 fprintf(stderr, "port %d\n", ldap_conf.port);
1262 fprintf(stderr, "ldap_version %d\n", ldap_conf.version);
1263 if (ldap_conf.base) {
1264 struct ldap_config_list_str *base = ldap_conf.base;
1267 fprintf(stderr, "sudoers_base %s\n", base->val);
1268 } while ((base = base->next) != NULL);
1270 fprintf(stderr, "sudoers_base %s\n",
1271 "(NONE) <---Sudo will ignore ldap)");
1273 if (ldap_conf.search_filter)
1274 fprintf(stderr, "search_filter %s\n", ldap_conf.search_filter);
1275 fprintf(stderr, "binddn %s\n", ldap_conf.binddn ?
1276 ldap_conf.binddn : "(anonymous)");
1277 fprintf(stderr, "bindpw %s\n", ldap_conf.bindpw ?
1278 ldap_conf.bindpw : "(anonymous)");
1279 if (ldap_conf.bind_timelimit > 0)
1280 fprintf(stderr, "bind_timelimit %d\n", ldap_conf.bind_timelimit);
1281 if (ldap_conf.timelimit > 0)
1282 fprintf(stderr, "timelimit %d\n", ldap_conf.timelimit);
1283 if (ldap_conf.timeout > 0)
1284 fprintf(stderr, "timeout %d\n", ldap_conf.timeout);
1285 fprintf(stderr, "ssl %s\n", ldap_conf.ssl ?
1286 ldap_conf.ssl : "(no)");
1287 if (ldap_conf.tls_checkpeer != -1)
1288 fprintf(stderr, "tls_checkpeer %s\n", ldap_conf.tls_checkpeer ?
1290 if (ldap_conf.tls_cacertfile != NULL)
1291 fprintf(stderr, "tls_cacertfile %s\n", ldap_conf.tls_cacertfile);
1292 if (ldap_conf.tls_cacertdir != NULL)
1293 fprintf(stderr, "tls_cacertdir %s\n", ldap_conf.tls_cacertdir);
1294 if (ldap_conf.tls_random_file != NULL)
1295 fprintf(stderr, "tls_random_file %s\n", ldap_conf.tls_random_file);
1296 if (ldap_conf.tls_cipher_suite != NULL)
1297 fprintf(stderr, "tls_cipher_suite %s\n", ldap_conf.tls_cipher_suite);
1298 if (ldap_conf.tls_certfile != NULL)
1299 fprintf(stderr, "tls_certfile %s\n", ldap_conf.tls_certfile);
1300 if (ldap_conf.tls_keyfile != NULL)
1301 fprintf(stderr, "tls_keyfile %s\n", ldap_conf.tls_keyfile);
1302 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1303 if (ldap_conf.use_sasl != -1) {
1304 fprintf(stderr, "use_sasl %s\n",
1305 ldap_conf.use_sasl ? "yes" : "no");
1306 fprintf(stderr, "sasl_auth_id %s\n", ldap_conf.sasl_auth_id ?
1307 ldap_conf.sasl_auth_id : "(NONE)");
1308 fprintf(stderr, "rootuse_sasl %d\n", ldap_conf.rootuse_sasl);
1309 fprintf(stderr, "rootsasl_auth_id %s\n", ldap_conf.rootsasl_auth_id ?
1310 ldap_conf.rootsasl_auth_id : "(NONE)");
1311 fprintf(stderr, "sasl_secprops %s\n", ldap_conf.sasl_secprops ?
1312 ldap_conf.sasl_secprops : "(NONE)");
1313 fprintf(stderr, "krb5_ccname %s\n", ldap_conf.krb5_ccname ?
1314 ldap_conf.krb5_ccname : "(NONE)");
1317 fprintf(stderr, "===================\n");
1319 if (!ldap_conf.base)
1320 return FALSE; /* if no base is defined, ignore LDAP */
1322 if (ldap_conf.bind_timelimit > 0)
1323 ldap_conf.bind_timelimit *= 1000; /* convert to ms */
1326 * Interpret SSL option
1328 if (ldap_conf.ssl != NULL) {
1329 if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
1330 ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
1331 else if (_atobool(ldap_conf.ssl))
1332 ldap_conf.ssl_mode = SUDO_LDAP_SSL;
1335 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
1336 if (ldap_conf.tls_checkpeer != -1) {
1337 ldapssl_set_strength(NULL,
1338 ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
1342 #ifndef HAVE_LDAP_INITIALIZE
1343 /* Convert uri list to host list if no ldap_initialize(). */
1344 if (ldap_conf.uri) {
1345 struct ldap_config_list_str *uri = ldap_conf.uri;
1346 if (sudo_ldap_parse_uri(uri) != 0)
1349 ldap_conf.uri = uri->next;
1351 } while ((uri = ldap_conf.uri));
1352 ldap_conf.port = LDAP_PORT;
1356 if (!ldap_conf.uri) {
1357 /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
1358 if (ldap_conf.port < 0)
1360 ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
1362 #ifdef HAVE_LDAP_CREATE
1364 * Cannot specify port directly to ldap_create(), each host must
1365 * include :port to override the default.
1367 if (ldap_conf.port != LDAP_PORT)
1368 sudo_ldap_conf_add_ports();
1372 /* If search filter is not parenthesized, make it so. */
1373 if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
1374 size_t len = strlen(ldap_conf.search_filter);
1375 cp = ldap_conf.search_filter;
1376 ldap_conf.search_filter = emalloc(len + 3);
1377 ldap_conf.search_filter[0] = '(';
1378 memcpy(ldap_conf.search_filter + 1, cp, len);
1379 ldap_conf.search_filter[len + 1] = ')';
1380 ldap_conf.search_filter[len + 2] = '\0';
1384 /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
1385 if (ldap_conf.rootbinddn)
1386 sudo_ldap_read_secret(_PATH_LDAP_SECRET);
1388 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1390 * Make sure we can open the file specified by krb5_ccname.
1392 if (ldap_conf.krb5_ccname != NULL) {
1393 if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
1394 strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
1395 value = ldap_conf.krb5_ccname +
1396 (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
1397 if ((fp = fopen(value, "r")) != NULL) {
1398 DPRINTF(("using krb5 credential cache: %s", value), 1);
1401 /* Can't open it, just ignore the entry. */
1402 DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
1403 efree(ldap_conf.krb5_ccname);
1404 ldap_conf.krb5_ccname = NULL;
1413 * Extract the dn from an entry and return the first rdn from it.
1416 sudo_ldap_get_first_rdn(ld, entry)
1420 #ifdef HAVE_LDAP_STR2DN
1421 char *dn, *rdn = NULL;
1424 if ((dn = ldap_get_dn(ld, entry)) == NULL)
1426 if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
1427 ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
1435 if ((dn = ldap_get_dn(ld, entry)) == NULL)
1437 edn = ldap_explode_dn(dn, 1);
1439 return edn ? edn[0] : NULL;
1444 * Fetch and display the global Options.
1447 sudo_ldap_display_defaults(nss, pw, lbuf)
1448 struct sudo_nss *nss;
1452 struct berval **bv, **p;
1453 struct timeval tv, *tvp = NULL;
1454 struct ldap_config_list_str *base;
1455 struct sudo_ldap_handle *handle = nss->handle;
1457 LDAPMessage *entry, *result;
1458 char *prefix, *filt;
1461 if (handle == NULL || handle->ld == NULL)
1465 filt = sudo_ldap_build_default_filter();
1466 for (base = ldap_conf.base; base != NULL; base = base->next) {
1467 if (ldap_conf.timeout > 0) {
1468 tv.tv_sec = ldap_conf.timeout;
1473 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
1474 filt, NULL, 0, NULL, NULL, tvp, 0, &result);
1475 if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
1476 bv = ldap_get_values_len(ld, entry, "sudoOption");
1478 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
1482 for (p = bv; *p != NULL; p++) {
1483 lbuf_append(lbuf, prefix, (*p)->bv_val, NULL);
1487 ldap_value_free_len(bv);
1491 ldap_msgfree(result);
1502 sudo_ldap_display_bound_defaults(nss, pw, lbuf)
1503 struct sudo_nss *nss;
1511 * Print a record in the short form, ala file sudoers.
1514 sudo_ldap_display_entry_short(ld, entry, lbuf)
1519 struct berval **bv, **p;
1522 lbuf_append(lbuf, " (", NULL);
1524 /* get the RunAsUser Values from the entry */
1525 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1527 bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1529 for (p = bv; *p != NULL; p++) {
1531 lbuf_append(lbuf, ", ", NULL);
1532 lbuf_append(lbuf, (*p)->bv_val, NULL);
1534 ldap_value_free_len(bv);
1536 lbuf_append(lbuf, def_runas_default, NULL);
1538 /* get the RunAsGroup Values from the entry */
1539 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1541 lbuf_append(lbuf, " : ", NULL);
1542 for (p = bv; *p != NULL; p++) {
1544 lbuf_append(lbuf, ", ", NULL);
1545 lbuf_append(lbuf, (*p)->bv_val, NULL);
1547 ldap_value_free_len(bv);
1549 lbuf_append(lbuf, ") ", NULL);
1551 /* get the Option Values from the entry */
1552 bv = ldap_get_values_len(ld, entry, "sudoOption");
1556 for (p = bv; *p != NULL; p++) {
1561 if (strcmp(cp, "authenticate") == 0)
1562 tag = (*p)->bv_val[0] == '!' ?
1563 "NOPASSWD: " : "PASSWD: ";
1564 else if (strcmp(cp, "noexec") == 0)
1565 tag = (*p)->bv_val[0] == '!' ?
1566 "EXEC: " : "NOEXEC: ";
1567 else if (strcmp(cp, "setenv") == 0)
1568 tag = (*p)->bv_val[0] == '!' ?
1569 "NOSETENV: " : "SETENV: ";
1571 lbuf_append(lbuf, tag, NULL);
1573 ldap_value_free_len(bv);
1576 /* get the Command Values from the entry */
1577 bv = ldap_get_values_len(ld, entry, "sudoCommand");
1579 for (p = bv; *p != NULL; p++) {
1581 lbuf_append(lbuf, ", ", NULL);
1582 lbuf_append(lbuf, (*p)->bv_val, NULL);
1585 ldap_value_free_len(bv);
1587 lbuf_append(lbuf, "\n", NULL);
1593 * Print a record in the long form.
1596 sudo_ldap_display_entry_long(ld, entry, lbuf)
1601 struct berval **bv, **p;
1605 /* extract the dn, only show the first rdn */
1606 rdn = sudo_ldap_get_first_rdn(ld, entry);
1607 lbuf_append(lbuf, "\nLDAP Role: ", rdn ? rdn : "UNKNOWN", "\n", NULL);
1611 /* get the RunAsUser Values from the entry */
1612 lbuf_append(lbuf, " RunAsUsers: ", NULL);
1613 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1615 bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1617 for (p = bv; *p != NULL; p++) {
1619 lbuf_append(lbuf, ", ", NULL);
1620 lbuf_append(lbuf, (*p)->bv_val, NULL);
1622 ldap_value_free_len(bv);
1624 lbuf_append(lbuf, def_runas_default, NULL);
1625 lbuf_append(lbuf, "\n", NULL);
1627 /* get the RunAsGroup Values from the entry */
1628 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1630 lbuf_append(lbuf, " RunAsGroups: ", NULL);
1631 for (p = bv; *p != NULL; p++) {
1633 lbuf_append(lbuf, ", ", NULL);
1634 lbuf_append(lbuf, (*p)->bv_val, NULL);
1636 ldap_value_free_len(bv);
1637 lbuf_append(lbuf, "\n", NULL);
1640 /* get the Option Values from the entry */
1641 bv = ldap_get_values_len(ld, entry, "sudoOption");
1643 lbuf_append(lbuf, " Options: ", NULL);
1644 for (p = bv; *p != NULL; p++) {
1646 lbuf_append(lbuf, ", ", NULL);
1647 lbuf_append(lbuf, (*p)->bv_val, NULL);
1649 ldap_value_free_len(bv);
1650 lbuf_append(lbuf, "\n", NULL);
1654 * Display order attribute if present. This attribute is single valued,
1655 * so there is no need for a loop.
1657 bv = ldap_get_values_len(ld, entry, "sudoOrder");
1660 lbuf_append(lbuf, " Order: ", (*bv)->bv_val, "\n", NULL);
1662 ldap_value_free_len(bv);
1665 /* Get the command values from the entry. */
1666 bv = ldap_get_values_len(ld, entry, "sudoCommand");
1668 lbuf_append(lbuf, " Commands:\n", NULL);
1669 for (p = bv; *p != NULL; p++) {
1670 lbuf_append(lbuf, "\t", (*p)->bv_val, "\n", NULL);
1673 ldap_value_free_len(bv);
1680 * Like sudo_ldap_lookup(), except we just print entries.
1683 sudo_ldap_display_privs(nss, pw, lbuf)
1684 struct sudo_nss *nss;
1688 struct sudo_ldap_handle *handle = nss->handle;
1690 struct ldap_result *lres;
1694 if (handle == NULL || handle->ld == NULL)
1698 DPRINTF(("ldap search for command list"), 1);
1699 lres = sudo_ldap_result_get(nss, pw);
1701 /* Display all matching entries. */
1702 for (i = 0; i < lres->nentries; i++) {
1703 entry = lres->entries[i].entry;
1705 count += sudo_ldap_display_entry_long(ld, entry, lbuf);
1707 count += sudo_ldap_display_entry_short(ld, entry, lbuf);
1715 sudo_ldap_display_cmnd(nss, pw)
1716 struct sudo_nss *nss;
1719 struct sudo_ldap_handle *handle = nss->handle;
1721 struct ldap_result *lres;
1723 int i, found = FALSE;
1725 if (handle == NULL || handle->ld == NULL)
1730 * The sudo_ldap_result_get() function returns all nodes that match
1731 * the user and the host.
1733 DPRINTF(("ldap search for command list"), 1);
1734 lres = sudo_ldap_result_get(nss, pw);
1735 for (i = 0; i < lres->nentries; i++) {
1736 entry = lres->entries[i].entry;
1737 if (sudo_ldap_check_command(ld, entry, NULL) &&
1738 sudo_ldap_check_runas(ld, entry)) {
1746 printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
1747 user_args ? " " : "", user_args ? user_args : "");
1751 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1753 sudo_ldap_sasl_interact(ld, flags, _auth_id, _interact)
1759 char *auth_id = (char *)_auth_id;
1760 sasl_interact_t *interact = (sasl_interact_t *)_interact;
1762 for (; interact->id != SASL_CB_LIST_END; interact++) {
1763 if (interact->id != SASL_CB_USER)
1764 return LDAP_PARAM_ERROR;
1766 if (auth_id != NULL)
1767 interact->result = auth_id;
1768 else if (interact->defresult != NULL)
1769 interact->result = interact->defresult;
1771 interact->result = "";
1773 interact->len = strlen(interact->result);
1774 #if SASL_VERSION_MAJOR < 2
1775 interact->result = estrdup(interact->result);
1776 #endif /* SASL_VERSION_MAJOR < 2 */
1778 return LDAP_SUCCESS;
1780 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1783 * Set LDAP options based on the config table.
1786 sudo_ldap_set_options(ld)
1789 struct ldap_config_table *cur;
1792 /* Set ber options */
1793 #ifdef LBER_OPT_DEBUG_LEVEL
1794 if (ldap_conf.ldap_debug)
1795 ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
1798 /* Set simple LDAP options */
1799 for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1804 if (cur->opt_val == -1)
1807 conn = cur->connected ? ld : NULL;
1808 switch (cur->type) {
1811 ival = *(int *)(cur->valp);
1813 rc = ldap_set_option(conn, cur->opt_val, &ival);
1814 if (rc != LDAP_OPT_SUCCESS) {
1815 warningx("ldap_set_option: %s -> %d: %s",
1816 cur->conf_str, ival, ldap_err2string(rc));
1819 DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
1823 sval = *(char **)(cur->valp);
1825 rc = ldap_set_option(conn, cur->opt_val, sval);
1826 if (rc != LDAP_OPT_SUCCESS) {
1827 warningx("ldap_set_option: %s -> %s: %s",
1828 cur->conf_str, sval, ldap_err2string(rc));
1831 DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
1837 #ifdef LDAP_OPT_TIMEOUT
1838 /* Convert timeout to a timeval */
1839 if (ldap_conf.timeout > 0) {
1841 tv.tv_sec = ldap_conf.timeout;
1843 rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
1844 if (rc != LDAP_OPT_SUCCESS) {
1845 warningx("ldap_set_option(TIMEOUT, %ld): %s",
1846 (long)tv.tv_sec, ldap_err2string(rc));
1849 DPRINTF(("ldap_set_option(LDAP_OPT_TIMEOUT, %ld)",
1850 (long)tv.tv_sec), 1);
1853 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1854 /* Convert bind_timelimit to a timeval */
1855 if (ldap_conf.bind_timelimit > 0) {
1857 tv.tv_sec = ldap_conf.bind_timelimit / 1000;
1859 rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
1860 if (rc != LDAP_OPT_SUCCESS) {
1861 warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
1862 (long)tv.tv_sec, ldap_err2string(rc));
1865 DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)",
1866 (long)tv.tv_sec), 1);
1870 #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
1871 if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
1872 int val = LDAP_OPT_X_TLS_HARD;
1873 rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
1874 if (rc != LDAP_SUCCESS) {
1875 warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
1876 ldap_err2string(rc));
1879 DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)"), 1);
1886 * Create a new sudo_ldap_result structure.
1888 static struct ldap_result *
1889 sudo_ldap_result_alloc()
1891 struct ldap_result *result;
1893 result = emalloc(sizeof(*result));
1894 result->searches = NULL;
1895 result->nentries = 0;
1896 result->entries = NULL;
1897 result->allocated_entries = 0;
1898 result->user_matches = FALSE;
1899 result->host_matches = FALSE;
1904 * Free the ldap result structure
1907 sudo_ldap_result_free(lres)
1908 struct ldap_result *lres;
1910 struct ldap_search_list *s;
1913 if (lres->nentries) {
1914 efree(lres->entries);
1915 lres->entries = NULL;
1917 if (lres->searches) {
1918 while ((s = lres->searches) != NULL) {
1919 ldap_msgfree(s->searchresult);
1920 lres->searches = s->next;
1929 * Add a search result to the ldap_result structure.
1931 static struct ldap_search_list *
1932 sudo_ldap_result_add_search(lres, ldap, searchresult)
1933 struct ldap_result *lres;
1935 LDAPMessage *searchresult;
1937 struct ldap_search_list *s, *news;
1939 news = emalloc(sizeof(struct ldap_search_list));
1942 news->searchresult = searchresult;
1944 /* Add entry to the end of the chain (XXX - tailq instead?). */
1945 if (lres->searches) {
1946 for (s = lres->searches; s->next != NULL; s = s->next)
1950 lres->searches = news;
1956 * Connect to the LDAP server specified by ld
1959 sudo_ldap_bind_s(ld)
1963 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1964 const char *old_ccname = user_ccname;
1965 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1966 unsigned int status;
1970 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1971 if (ldap_conf.rootuse_sasl == TRUE ||
1972 (ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE)) {
1973 void *auth_id = ldap_conf.rootsasl_auth_id ?
1974 ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
1976 if (ldap_conf.krb5_ccname != NULL) {
1977 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1978 if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
1979 != GSS_S_COMPLETE) {
1981 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
1984 setenv("KRB5CCNAME", ldap_conf.krb5_ccname, TRUE);
1987 rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
1988 NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
1989 if (ldap_conf.krb5_ccname != NULL) {
1990 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1991 if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
1992 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
1994 if (old_ccname != NULL)
1995 setenv("KRB5CCNAME", old_ccname, TRUE);
1997 unsetenv("KRB5CCNAME");
2000 if (rc != LDAP_SUCCESS) {
2001 warningx("ldap_sasl_interactive_bind_s(): %s", ldap_err2string(rc));
2004 DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
2006 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
2007 #ifdef HAVE_LDAP_SASL_BIND_S
2011 bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
2012 bv.bv_len = strlen(bv.bv_val);
2014 rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
2016 if (rc != LDAP_SUCCESS) {
2017 warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
2020 DPRINTF(("ldap_sasl_bind_s() ok"), 1);
2024 rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
2025 if (rc != LDAP_SUCCESS) {
2026 warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
2029 DPRINTF(("ldap_simple_bind_s() ok"), 1);
2036 * Open a connection to the LDAP server.
2037 * Returns 0 on success and non-zero on failure.
2041 struct sudo_nss *nss;
2044 int rc, ldapnoinit = FALSE;
2045 struct sudo_ldap_handle *handle;
2047 if (!sudo_ldap_read_config())
2050 /* Prevent reading of user ldaprc and system defaults. */
2051 if (getenv("LDAPNOINIT") == NULL) {
2053 setenv("LDAPNOINIT", "1", TRUE);
2056 /* Connect to LDAP server */
2057 #ifdef HAVE_LDAP_INITIALIZE
2058 if (ldap_conf.uri != NULL) {
2059 char *buf = sudo_ldap_join_uri(ldap_conf.uri);
2060 DPRINTF(("ldap_initialize(ld, %s)", buf), 2);
2061 rc = ldap_initialize(&ld, buf);
2065 rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
2066 if (rc != LDAP_SUCCESS) {
2067 warningx("unable to initialize LDAP: %s", ldap_err2string(rc));
2072 unsetenv("LDAPNOINIT");
2074 /* Set LDAP options */
2075 if (sudo_ldap_set_options(ld) < 0)
2078 if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
2079 #if defined(HAVE_LDAP_START_TLS_S)
2080 rc = ldap_start_tls_s(ld, NULL, NULL);
2081 if (rc != LDAP_SUCCESS) {
2082 warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
2085 DPRINTF(("ldap_start_tls_s() ok"), 1);
2086 #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
2087 if (ldap_ssl_client_init(NULL, NULL, 0, &rc) != LDAP_SUCCESS) {
2088 warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
2091 rc = ldap_start_tls_s_np(ld, NULL);
2092 if (rc != LDAP_SUCCESS) {
2093 warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
2096 DPRINTF(("ldap_start_tls_s_np() ok"), 1);
2098 warningx("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()");
2099 #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
2102 /* Actually connect */
2103 if (sudo_ldap_bind_s(ld) != 0)
2106 /* Create a handle container. */
2107 handle = emalloc(sizeof(struct sudo_ldap_handle));
2109 handle->result = NULL;
2110 handle->username = NULL;
2111 handle->groups = NULL;
2112 nss->handle = handle;
2118 sudo_ldap_setdefs(nss)
2119 struct sudo_nss *nss;
2121 struct ldap_config_list_str *base;
2122 struct sudo_ldap_handle *handle = nss->handle;
2123 struct timeval tv, *tvp = NULL;
2125 LDAPMessage *entry, *result;
2129 if (handle == NULL || handle->ld == NULL)
2133 filt = sudo_ldap_build_default_filter();
2134 DPRINTF(("Looking for cn=defaults: %s", filt), 1);
2136 for (base = ldap_conf.base; base != NULL; base = base->next) {
2137 if (ldap_conf.timeout > 0) {
2138 tv.tv_sec = ldap_conf.timeout;
2143 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
2144 filt, NULL, 0, NULL, NULL, NULL, 0, &result);
2145 if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
2146 DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
2147 sudo_ldap_parse_options(ld, entry);
2149 DPRINTF(("no default options found in %s", base->val), 1);
2152 ldap_msgfree(result);
2160 * like sudoers_lookup() - only LDAP style
2163 sudo_ldap_lookup(nss, ret, pwflag)
2164 struct sudo_nss *nss;
2168 struct sudo_ldap_handle *handle = nss->handle;
2171 int i, rc, setenv_implied, matched = UNSPEC;
2172 struct ldap_result *lres = NULL;
2174 if (handle == NULL || handle->ld == NULL)
2178 /* Fetch list of sudoRole entries that match user and host. */
2179 lres = sudo_ldap_result_get(nss, sudo_user.pw);
2182 * The following queries are only determine whether or not a
2183 * password is required, so the order of the entries doesn't matter.
2186 DPRINTF(("perform search for pwflag %d", pwflag), 1);
2187 int doauth = UNSPEC;
2188 enum def_tupple pwcheck =
2189 (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
2191 for (i = 0; i < lres->nentries; i++) {
2192 entry = lres->entries[i].entry;
2193 if ((pwcheck == any && doauth != FALSE) ||
2194 (pwcheck == all && doauth == FALSE)) {
2195 doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
2197 /* Only check the command when listing another user. */
2198 if (user_uid == 0 || list_pw == NULL ||
2199 user_uid == list_pw->pw_uid ||
2200 sudo_ldap_check_command(ld, entry, NULL)) {
2205 if (matched || user_uid == 0) {
2206 SET(ret, VALIDATE_OK);
2207 CLR(ret, VALIDATE_NOT_OK);
2208 if (def_authenticate) {
2211 SET(ret, FLAG_CHECK_USER);
2215 if (doauth == FALSE)
2216 def_authenticate = FALSE;
2219 def_authenticate = FALSE;
2229 DPRINTF(("searching LDAP for sudoers entries"), 1);
2231 setenv_implied = FALSE;
2232 for (i = 0; i < lres->nentries; i++) {
2233 entry = lres->entries[i].entry;
2234 if (!sudo_ldap_check_runas(ld, entry))
2236 rc = sudo_ldap_check_command(ld, entry, &setenv_implied);
2238 /* We have a match. */
2239 DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
2242 DPRINTF(("LDAP entry: %p", entry), 1);
2243 /* Apply entry-specific options. */
2246 sudo_ldap_parse_options(ld, entry);
2248 /* Set role and type if not specified on command line. */
2249 if (user_role == NULL)
2250 user_role = def_role;
2251 if (user_type == NULL)
2252 user_type = def_type;
2253 #endif /* HAVE_SELINUX */
2254 SET(ret, VALIDATE_OK);
2255 CLR(ret, VALIDATE_NOT_OK);
2257 SET(ret, VALIDATE_NOT_OK);
2258 CLR(ret, VALIDATE_OK);
2265 DPRINTF(("done with LDAP searches"), 1);
2266 DPRINTF(("user_matches=%d", lres->user_matches), 1);
2267 DPRINTF(("host_matches=%d", lres->host_matches), 1);
2269 if (!ISSET(ret, VALIDATE_OK)) {
2270 /* No matching entries. */
2271 if (pwflag && list_pw == NULL)
2272 SET(ret, FLAG_NO_CHECK);
2274 if (lres->user_matches)
2275 CLR(ret, FLAG_NO_USER);
2276 if (lres->host_matches)
2277 CLR(ret, FLAG_NO_HOST);
2278 DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
2284 * Comparison function for ldap_entry_wrapper structures, descending order.
2287 ldap_entry_compare(a, b)
2291 const struct ldap_entry_wrapper *aw = a;
2292 const struct ldap_entry_wrapper *bw = b;
2294 return bw->order < aw->order ? -1 :
2295 (bw->order > aw->order ? 1 : 0);
2299 * Find the last entry in the list of searches, usually the
2300 * one currently being used to add entries.
2301 * XXX - use a tailq instead?
2303 static struct ldap_search_list *
2304 sudo_ldap_result_last_search(lres)
2305 struct ldap_result *lres;
2307 struct ldap_search_list *result = lres->searches;
2310 while (result->next)
2311 result = result->next;
2317 * Add an entry to the result structure.
2319 static struct ldap_entry_wrapper *
2320 sudo_ldap_result_add_entry(lres, entry)
2321 struct ldap_result *lres;
2324 struct ldap_search_list *last;
2329 /* Determine whether the entry has the sudoOrder attribute. */
2330 last = sudo_ldap_result_last_search(lres);
2331 bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
2333 if (ldap_count_values_len(bv) > 0) {
2334 /* Get the value of this attribute, 0 if not present. */
2335 DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
2336 order = strtod((*bv)->bv_val, &ep);
2337 if (ep == (*bv)->bv_val || *ep != '\0') {
2338 warningx("invalid sudoOrder attribute: %s", (*bv)->bv_val);
2341 DPRINTF(("order attribute: %f", order), 1);
2343 ldap_value_free_len(bv);
2347 * Enlarge the array of entry wrappers as needed, preallocating blocks
2348 * of 100 entries to save on allocation time.
2350 if (++lres->nentries > lres->allocated_entries) {
2351 lres->allocated_entries += ALLOCATION_INCREMENT;
2352 lres->entries = erealloc3(lres->entries, lres->allocated_entries,
2353 sizeof(lres->entries[0]));
2356 /* Fill in the new entry and return it. */
2357 lres->entries[lres->nentries - 1].entry = entry;
2358 lres->entries[lres->nentries - 1].order = order;
2360 return &lres->entries[lres->nentries - 1];
2364 * Free the ldap result structure in the sudo_nss handle.
2367 sudo_ldap_result_free_nss(nss)
2368 struct sudo_nss *nss;
2370 struct sudo_ldap_handle *handle = nss->handle;
2372 if (handle->result != NULL) {
2373 DPRINTF(("removing reusable search result"), 1);
2374 sudo_ldap_result_free(handle->result);
2375 if (handle->username) {
2376 efree(handle->username);
2377 handle->username = NULL;
2379 handle->groups = NULL;
2380 handle->result = NULL;
2385 * Perform the LDAP query for the user or return a cached query if
2386 * there is one for this user.
2388 static struct ldap_result *
2389 sudo_ldap_result_get(nss, pw)
2390 struct sudo_nss *nss;
2393 struct sudo_ldap_handle *handle = nss->handle;
2394 struct ldap_config_list_str *base;
2395 struct ldap_result *lres;
2396 struct timeval tv, *tvp = NULL;
2397 LDAPMessage *entry, *result;
2398 LDAP *ld = handle->ld;
2403 * If we already have a cached result, return it so we don't have to
2404 * have to contact the LDAP server again.
2406 if (handle->result) {
2407 if (handle->groups == user_groups &&
2408 strcmp(pw->pw_name, handle->username) == 0) {
2409 DPRINTF(("reusing previous result (user %s) with %d entries",
2410 handle->username, handle->result->nentries), 1);
2411 return handle->result;
2413 /* User mismatch, cached result cannot be used. */
2414 DPRINTF(("removing result (user %s), new search (user %s)",
2415 handle->username, pw->pw_name), 1);
2416 sudo_ldap_result_free_nss(nss);
2420 * Okay - time to search for anything that matches this user
2421 * Lets limit it to only two queries of the LDAP server
2423 * The first pass will look by the username, groups, and
2424 * the keyword ALL. We will then inspect the results that
2425 * came back from the query. We don't need to inspect the
2426 * sudoUser in this pass since the LDAP server already scanned
2429 * The second pass will return all the entries that contain
2430 * user netgroups. Then we take the netgroups returned and
2431 * try to match them against the username.
2433 * Since we have to sort the possible entries before we make a
2434 * decision, we perform the queries and store all of the results in
2435 * an ldap_result object. The results are then sorted by sudoOrder.
2437 lres = sudo_ldap_result_alloc();
2438 for (do_netgr = 0; do_netgr < 2; do_netgr++) {
2439 filt = do_netgr ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
2440 DPRINTF(("ldap search '%s'", filt), 1);
2441 for (base = ldap_conf.base; base != NULL; base = base->next) {
2442 DPRINTF(("searching from base '%s'", base->val), 1);
2443 if (ldap_conf.timeout > 0) {
2444 tv.tv_sec = ldap_conf.timeout;
2449 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
2450 NULL, 0, NULL, NULL, NULL, 0, &result);
2451 if (rc != LDAP_SUCCESS) {
2452 DPRINTF(("nothing found for '%s'", filt), 1);
2455 lres->user_matches = TRUE;
2457 /* Add the seach result to list of search results. */
2458 DPRINTF(("adding search result"), 1);
2459 sudo_ldap_result_add_search(lres, ld, result);
2460 LDAP_FOREACH(entry, ld, result) {
2462 sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
2463 sudo_ldap_check_host(ld, entry)) {
2464 lres->host_matches = TRUE;
2465 sudo_ldap_result_add_entry(lres, entry);
2468 DPRINTF(("result now has %d entries", lres->nentries), 1);
2473 /* Sort the entries by the sudoOrder attribute. */
2474 DPRINTF(("sorting remaining %d entries", lres->nentries), 1);
2475 qsort(lres->entries, lres->nentries, sizeof(lres->entries[0]),
2476 ldap_entry_compare);
2478 /* Store everything in the sudo_nss handle. */
2479 handle->result = lres;
2480 handle->username = estrdup(pw->pw_name);
2481 handle->groups = user_groups;
2487 * Shut down the LDAP connection.
2490 sudo_ldap_close(nss)
2491 struct sudo_nss *nss;
2493 struct sudo_ldap_handle *handle = nss->handle;
2495 if (handle != NULL) {
2496 /* Free the result before unbinding; it may use the LDAP connection. */
2497 sudo_ldap_result_free_nss(nss);
2499 /* Unbind and close the LDAP connection. */
2500 if (handle->ld != NULL) {
2501 ldap_unbind_ext_s(handle->ld, NULL, NULL);
2505 /* Free the handle container. */
2516 sudo_ldap_parse(nss)
2517 struct sudo_nss *nss;
2524 * Create an ldap_result from an LDAP search result.
2526 * This function is currently not used anywhere, it is left here as
2527 * an example of how to use the cached searches.
2529 static struct ldap_result *
2530 sudo_ldap_result_from_search(ldap, searchresult)
2532 LDAPMessage *searchresult;
2535 * An ldap_result is built from several search results, which are
2536 * organized in a list. The head of the list is maintained in the
2537 * ldap_result structure, together with the wrappers that point
2538 * to individual entries, this has to be initialized first.
2540 struct ldap_result *result = sudo_ldap_result_alloc();
2543 * Build a new list node for the search result, this creates the
2546 struct ldap_search_list *last = sudo_ldap_result_add_search(result,
2547 ldap, searchresult);
2550 * Now add each entry in the search result to the array of of entries
2551 * in the ldap_result object.
2554 LDAP_FOREACH(entry, last->ldap, last->searchresult) {
2555 sudo_ldap_result_add_entry(result, entry);
2557 DPRINTF(("sudo_ldap_result_from_search: %d entries found",
2558 result->nentries), 2);