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
120 #define CONF_DEREF_VAL 5
122 #define SUDO_LDAP_SSL 1
123 #define SUDO_LDAP_STARTTLS 2
125 /* The TIMEFILTER_LENGTH includes the filter itself plus the global AND
126 wrapped around the user filter and the time filter when timed entries
127 are used. The length is computed as follows:
129 + 2 * 13 for the now timestamp
130 + 3 for the global AND
132 #define TIMEFILTER_LENGTH 114
135 * The ldap_search structure implements a linked list of ldap and
136 * search result pointers, which allows us to remove them after
137 * all search results have been combined in memory.
138 * XXX - should probably be a tailq since we do appends
140 struct ldap_search_list {
142 LDAPMessage *searchresult;
143 struct ldap_search_list *next;
147 * The ldap_entry_wrapper structure is used to implement sorted result entries.
148 * A double is used for the order to allow for insertion of new entries
149 * without having to renumber everything.
150 * Note: there is no standard floating point type in LDAP.
151 * As a result, some LDAP servers will only allow an integer.
153 struct ldap_entry_wrapper {
159 * The ldap_result structure contains the list of matching searches as
160 * well as an array of all result entries sorted by the sudoOrder attribute.
163 struct ldap_search_list *searches;
164 struct ldap_entry_wrapper *entries;
165 int allocated_entries;
170 #define ALLOCATION_INCREMENT 100
172 struct ldap_config_table {
173 const char *conf_str; /* config file string */
174 short type; /* CONF_BOOL, CONF_INT, CONF_STR */
175 short connected; /* connection-specific value? */
176 int opt_val; /* LDAP_OPT_* (or -1 for sudo internal) */
177 void *valp; /* pointer into ldap_conf */
180 struct ldap_config_list_str {
181 struct ldap_config_list_str *next;
185 /* LDAP configuration structure */
186 static struct ldap_config {
201 struct ldap_config_list_str *uri;
205 struct ldap_config_list_str *base;
208 char *tls_cacertfile;
210 char *tls_random_file;
211 char *tls_cipher_suite;
215 char *rootsasl_auth_id;
220 static struct ldap_config_table ldap_conf_table[] = {
221 { "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug },
222 { "host", CONF_STR, FALSE, -1, &ldap_conf.host },
223 { "port", CONF_INT, FALSE, -1, &ldap_conf.port },
224 { "ssl", CONF_STR, FALSE, -1, &ldap_conf.ssl },
225 { "sslpath", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
226 { "uri", CONF_LIST_STR, FALSE, -1, &ldap_conf.uri },
227 #ifdef LDAP_OPT_DEBUG_LEVEL
228 { "debug", CONF_INT, FALSE, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
230 #ifdef LDAP_OPT_PROTOCOL_VERSION
231 { "ldap_version", CONF_INT, TRUE, LDAP_OPT_PROTOCOL_VERSION,
232 &ldap_conf.version },
234 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
235 { "tls_checkpeer", CONF_BOOL, FALSE, LDAP_OPT_X_TLS_REQUIRE_CERT,
236 &ldap_conf.tls_checkpeer },
238 { "tls_checkpeer", CONF_BOOL, FALSE, -1, &ldap_conf.tls_checkpeer },
240 #ifdef LDAP_OPT_X_TLS_CACERTFILE
241 { "tls_cacertfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
242 &ldap_conf.tls_cacertfile },
243 { "tls_cacert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
244 &ldap_conf.tls_cacertfile },
246 #ifdef LDAP_OPT_X_TLS_CACERTDIR
247 { "tls_cacertdir", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTDIR,
248 &ldap_conf.tls_cacertdir },
250 #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
251 { "tls_randfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_RANDOM_FILE,
252 &ldap_conf.tls_random_file },
254 #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
255 { "tls_ciphers", CONF_STR, FALSE, LDAP_OPT_X_TLS_CIPHER_SUITE,
256 &ldap_conf.tls_cipher_suite },
258 #ifdef LDAP_OPT_X_TLS_CERTFILE
259 { "tls_cert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CERTFILE,
260 &ldap_conf.tls_certfile },
262 { "tls_cert", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
264 #ifdef LDAP_OPT_X_TLS_KEYFILE
265 { "tls_key", CONF_STR, FALSE, LDAP_OPT_X_TLS_KEYFILE,
266 &ldap_conf.tls_keyfile },
268 { "tls_key", CONF_STR, FALSE, -1, &ldap_conf.tls_keyfile },
270 #ifdef LDAP_OPT_NETWORK_TIMEOUT
271 { "bind_timelimit", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
272 &ldap_conf.bind_timelimit },
273 { "network_timeout", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
274 &ldap_conf.bind_timelimit },
275 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
276 { "bind_timelimit", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
277 &ldap_conf.bind_timelimit },
278 { "network_timeout", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
279 &ldap_conf.bind_timelimit },
281 { "timelimit", CONF_INT, TRUE, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
282 #ifdef LDAP_OPT_TIMEOUT
283 { "timeout", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
284 &ldap_conf.timeout },
286 #ifdef LDAP_OPT_DEREF
287 { "deref", CONF_DEREF_VAL, TRUE, LDAP_OPT_DEREF, &ldap_conf.deref },
289 { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn },
290 { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw },
291 { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
292 { "sudoers_base", CONF_LIST_STR, FALSE, -1, &ldap_conf.base },
293 { "sudoers_timed", CONF_BOOL, FALSE, -1, &ldap_conf.timed },
294 { "sudoers_search_filter", CONF_STR, FALSE, -1, &ldap_conf.search_filter },
295 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
296 { "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl },
297 { "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id },
298 { "rootuse_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.rootuse_sasl },
299 { "rootsasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.rootsasl_auth_id },
300 # ifdef LDAP_OPT_X_SASL_SECPROPS
301 { "sasl_secprops", CONF_STR, TRUE, LDAP_OPT_X_SASL_SECPROPS,
302 &ldap_conf.sasl_secprops },
304 { "krb5_ccname", CONF_STR, FALSE, -1, &ldap_conf.krb5_ccname },
305 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
309 /* sudo_nss implementation */
310 static int sudo_ldap_open(struct sudo_nss *nss);
311 static int sudo_ldap_close(struct sudo_nss *nss);
312 static int sudo_ldap_parse(struct sudo_nss *nss);
313 static int sudo_ldap_setdefs(struct sudo_nss *nss);
314 static int sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag);
315 static int sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw);
316 static int sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
318 static int sudo_ldap_display_bound_defaults(struct sudo_nss *nss,
319 struct passwd *pw, struct lbuf *lbuf);
320 static int sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
322 static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss,
326 * LDAP sudo_nss handle.
327 * We store the connection to the LDAP server, the cached ldap_result object
328 * (if any), and the name of the user the query was performed for.
329 * If a new query is launched with sudo_ldap_result_get() that specifies a
330 * different user, the old cached result is freed before the new query is run.
332 struct sudo_ldap_handle {
334 struct ldap_result *result;
336 struct group_list *grlist;
339 struct sudo_nss sudo_nss_ldap = {
347 sudo_ldap_display_cmnd,
348 sudo_ldap_display_defaults,
349 sudo_ldap_display_bound_defaults,
350 sudo_ldap_display_privs
353 #ifdef HAVE_LDAP_CREATE
355 * Rebuild the hosts list and include a specific port for each host.
356 * ldap_create() does not take a default port parameter so we must
357 * append one if we want something other than LDAP_PORT.
360 sudo_ldap_conf_add_ports(void)
363 char *host, *port, defport[13];
364 char hostbuf[LINE_MAX * 2];
367 if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
368 errorx(1, _("sudo_ldap_conf_add_ports: port too large"));
370 for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
371 if (hostbuf[0] != '\0') {
372 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
376 if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
378 /* Append port if there is not one already. */
379 if ((port = strrchr(host, ':')) == NULL ||
380 !isdigit((unsigned char)port[1])) {
381 if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
386 efree(ldap_conf.host);
387 ldap_conf.host = estrdup(hostbuf);
391 errorx(1, _("sudo_ldap_conf_add_ports: out of space expanding hostbuf"));
395 #ifndef HAVE_LDAP_INITIALIZE
397 * For each uri, convert to host:port pairs. For ldaps:// enable SSL
398 * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
399 * where the trailing slash is optional.
402 sudo_ldap_parse_uri(const struct ldap_config_list_str *uri_list)
404 char *buf, *uri, *host, *cp, *port;
405 char hostbuf[LINE_MAX];
406 int nldap = 0, nldaps = 0;
410 buf = estrdup(uri_list->val);
412 for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
413 if (strncasecmp(uri, "ldap://", 7) == 0) {
416 } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
420 warningx(_("unsupported LDAP uri type: %s"), uri);
424 /* trim optional trailing slash */
425 if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
429 if (hostbuf[0] != '\0') {
430 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
435 host = "localhost"; /* no host specified, use localhost */
437 if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
440 /* If using SSL and no port specified, add port 636 */
442 if ((port = strrchr(host, ':')) == NULL ||
443 !isdigit((unsigned char)port[1]))
444 if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
448 if (hostbuf[0] == '\0') {
449 warningx(_("invalid uri: %s"), uri_list);
455 warningx(_("unable to mix ldap and ldaps URIs"));
458 if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
459 warningx(_("unable to mix ldaps and starttls"));
462 ldap_conf.ssl_mode = SUDO_LDAP_SSL;
465 efree(ldap_conf.host);
466 ldap_conf.host = estrdup(hostbuf);
468 } while ((uri_list = uri_list->next));
478 errorx(1, _("sudo_ldap_parse_uri: out of space building hostbuf"));
482 sudo_ldap_join_uri(struct ldap_config_list_str *uri_list)
484 struct ldap_config_list_str *uri;
488 /* Usually just a single entry. */
489 if (uri_list->next == NULL)
490 return estrdup(uri_list->val);
492 for (uri = uri_list; uri != NULL; uri = uri->next) {
493 len += strlen(uri->val) + 1;
495 buf = cp = emalloc(len);
497 for (uri = uri_list; uri != NULL; uri = uri->next) {
498 cp += strlcpy(cp, uri->val, len - (cp - buf));
504 #endif /* HAVE_LDAP_INITIALIZE */
507 sudo_ldap_init(LDAP **ldp, const char *host, int port)
510 int rc = LDAP_CONNECT_ERROR;
512 #ifdef HAVE_LDAPSSL_INIT
513 if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
514 DPRINTF(("ldapssl_clientauth_init(%s, %s)",
515 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
516 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
517 rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
518 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
520 * Mozilla-derived SDKs have a bug starting with version 5.0
521 * where the path can no longer be a file name and must be a dir.
523 if (rc != LDAP_SUCCESS) {
525 if (ldap_conf.tls_certfile) {
526 cp = strrchr(ldap_conf.tls_certfile, '/');
527 if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0)
530 if (ldap_conf.tls_keyfile) {
531 cp = strrchr(ldap_conf.tls_keyfile, '/');
532 if (cp != NULL && strncmp(cp + 1, "key", 3) == 0)
535 DPRINTF(("ldapssl_clientauth_init(%s, %s)",
536 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
537 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
538 rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
539 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
540 if (rc != LDAP_SUCCESS) {
541 warningx(_("unable to initialize SSL cert and key db: %s"),
542 ldapssl_err2string(rc));
547 DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
548 if ((ld = ldapssl_init(host, port, 1)) != NULL)
553 #ifdef HAVE_LDAP_CREATE
554 DPRINTF(("ldap_create()"), 2);
555 if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
557 DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
558 rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
560 DPRINTF(("ldap_init(%s, %d)", host, port), 2);
561 if ((ld = ldap_init(host, port)) != NULL)
572 * Walk through search results and return TRUE if we have a matching
573 * netgroup, else FALSE.
576 sudo_ldap_check_user_netgroup(LDAP *ld, LDAPMessage *entry, char *user)
578 struct berval **bv, **p;
585 /* get the values from the entry */
586 bv = ldap_get_values_len(ld, entry, "sudoUser");
590 /* walk through values */
591 for (p = bv; *p != NULL && !ret; p++) {
594 if (netgr_matches(val, NULL, NULL, user))
596 DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
597 ret ? "MATCH!" : "not"), 2 + ((ret) ? 0 : 1));
600 ldap_value_free_len(bv); /* cleanup */
606 * Walk through search results and return TRUE if we have a
607 * host match, else FALSE.
610 sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry)
612 struct berval **bv, **p;
619 /* get the values from the entry */
620 bv = ldap_get_values_len(ld, entry, "sudoHost");
624 /* walk through values */
625 for (p = bv; *p != NULL && !ret; p++) {
627 /* match any or address or netgroup or hostname */
628 if (!strcmp(val, "ALL") || addr_matches(val) ||
629 netgr_matches(val, user_host, user_shost, NULL) ||
630 hostname_matches(user_shost, user_host, val))
632 DPRINTF(("ldap sudoHost '%s' ... %s", val,
633 ret ? "MATCH!" : "not"), 2);
636 ldap_value_free_len(bv); /* cleanup */
642 sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry)
644 struct berval **bv, **p;
651 /* get the runas user from the entry */
652 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
654 bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
659 * if runas is not specified on the command line, the only information
660 * as to which user to run as is in the runas_default option. We should
661 * check to see if we have the local option present. Unfortunately we
662 * don't parse these options until after this routine says yes or no.
663 * The query has already returned, so we could peek at the attribute
664 * values here though.
666 * For now just require users to always use -u option unless its set
667 * in the global defaults. This behaviour is no different than the global
670 * Sigh - maybe add this feature later
674 * If there are no runas entries, match runas_default against
675 * what the user specified on the command line.
678 return !strcasecmp(runas_pw->pw_name, def_runas_default);
680 /* walk through values returned, looking for a match */
681 for (p = bv; *p != NULL && !ret; p++) {
685 if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
689 if (usergr_matches(val, runas_pw->pw_name, runas_pw))
693 if (strcmp(val, "ALL") == 0) {
699 if (strcasecmp(val, runas_pw->pw_name) == 0)
703 DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
704 ret ? "MATCH!" : "not"), 2);
707 ldap_value_free_len(bv); /* cleanup */
713 sudo_ldap_check_runas_group(LDAP *ld, LDAPMessage *entry)
715 struct berval **bv, **p;
719 /* runas_gr is only set if the user specified the -g flag */
723 /* get the values from the entry */
724 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
728 /* walk through values returned, looking for a match */
729 for (p = bv; *p != NULL && !ret; p++) {
731 if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
733 DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
734 ret ? "MATCH!" : "not"), 2);
737 ldap_value_free_len(bv); /* cleanup */
743 * Walk through search results and return TRUE if we have a runas match,
744 * else FALSE. RunAs info is optional.
747 sudo_ldap_check_runas(LDAP *ld, LDAPMessage *entry)
754 ret = sudo_ldap_check_runas_user(ld, entry) != FALSE &&
755 sudo_ldap_check_runas_group(ld, entry) != FALSE;
761 * Walk through search results and return TRUE if we have a command match,
762 * FALSE if disallowed and UNSPEC if not matched.
765 sudo_ldap_check_command(LDAP *ld, LDAPMessage *entry, int *setenv_implied)
767 struct berval **bv, **p;
768 char *allowed_cmnd, *allowed_args, *val;
769 int foundbang, ret = UNSPEC;
774 bv = ldap_get_values_len(ld, entry, "sudoCommand");
778 for (p = bv; *p != NULL && ret != FALSE; p++) {
780 /* Match against ALL ? */
781 if (!strcmp(val, "ALL")) {
783 if (setenv_implied != NULL)
784 *setenv_implied = TRUE;
785 DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
789 /* check for !command */
792 allowed_cmnd = estrdup(1 + val); /* !command */
795 allowed_cmnd = estrdup(val); /* command */
798 /* split optional args away from command */
799 allowed_args = strchr(allowed_cmnd, ' ');
801 *allowed_args++ = '\0';
803 /* check the command like normal */
804 if (command_matches(allowed_cmnd, allowed_args)) {
806 * If allowed (no bang) set ret but keep on checking.
807 * If disallowed (bang), exit loop.
809 ret = foundbang ? FALSE : TRUE;
811 DPRINTF(("ldap sudoCommand '%s' ... %s", val,
812 ret == TRUE ? "MATCH!" : "not"), 2);
814 efree(allowed_cmnd); /* cleanup */
817 ldap_value_free_len(bv); /* more cleanup */
823 * Search for boolean "option" in sudoOption.
824 * Returns TRUE if found and allowed, FALSE if negated, else UNSPEC.
827 sudo_ldap_check_bool(LDAP *ld, LDAPMessage *entry, char *option)
829 struct berval **bv, **p;
836 bv = ldap_get_values_len(ld, entry, "sudoOption");
840 /* walk through options */
841 for (p = bv; *p != NULL; p++) {
843 DPRINTF(("ldap sudoOption: '%s'", var), 2);
845 if ((ch = *var) == '!')
847 if (strcmp(var, option) == 0)
851 ldap_value_free_len(bv);
857 * Read sudoOption and modify the defaults as we go. This is used once
858 * from the cn=defaults entry and also once when a final sudoRole is matched.
861 sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry)
863 struct berval **bv, **p;
869 bv = ldap_get_values_len(ld, entry, "sudoOption");
873 /* walk through options */
874 for (p = bv; *p != NULL; p++) {
875 var = estrdup((*p)->bv_val);
876 DPRINTF(("ldap sudoOption: '%s'", var), 2);
878 /* check for equals sign past first char */
879 val = strchr(var, '=');
881 *val++ = '\0'; /* split on = and truncate var */
882 op = *(val - 2); /* peek for += or -= cases */
883 if (op == '+' || op == '-') {
884 *(val - 2) = '\0'; /* found, remove extra char */
885 /* case var+=val or var-=val */
886 set_default(var, val, (int) op);
889 set_default(var, val, TRUE);
891 } else if (*var == '!') {
892 /* case !var Boolean False */
893 set_default(var + 1, NULL, FALSE);
895 /* case var Boolean True */
896 set_default(var, NULL, TRUE);
901 ldap_value_free_len(bv);
905 * Build an LDAP timefilter.
907 * Stores a filter in the buffer that makes sure only entries
908 * are selected that have a sudoNotBefore in the past and a
909 * sudoNotAfter in the future, i.e. a filter of the following
910 * structure (spaced out a little more for better readability:
914 * (!(sudoNotAfter=*))
915 * (sudoNotAfter>__now__)
918 * (!(sudoNotBefore=*))
919 * (sudoNotBefore<__now__)
923 * If either the sudoNotAfter or sudoNotBefore attributes are missing,
924 * no time restriction shall be imposed.
927 sudo_ldap_timefilter(char *buffer, size_t buffersize)
934 /* Make sure we have a formatted timestamp for __now__. */
936 if ((tp = gmtime(&now)) == NULL) {
937 warning(_("unable to get GMT time"));
941 /* Format the timestamp according to the RFC. */
942 if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%M%SZ", tp) == 0) {
943 warning(_("unable to format timestamp"));
948 bytes = snprintf(buffer, buffersize, "(&(|(!(sudoNotAfter=*))(sudoNotAfter>=%s))(|(!(sudoNotBefore=*))(sudoNotBefore<=%s)))",
949 timebuffer, timebuffer);
950 if (bytes < 0 || bytes >= buffersize) {
951 warning(_("unable to build time filter"));
960 * Builds up a filter to search for default settings
963 sudo_ldap_build_default_filter()
967 if (ldap_conf.search_filter)
968 easprintf(&filt, "(&%s(cn=defaults))", ldap_conf.search_filter);
970 filt = estrdup("cn=defaults");
975 * Builds up a filter to check against LDAP.
978 sudo_ldap_build_pass1(struct passwd *pw)
981 char *buf, timebuffer[TIMEFILTER_LENGTH];
982 struct group_list *grlist;
986 /* Start with LDAP search filter length + 3 */
987 if (ldap_conf.search_filter)
988 sz += strlen(ldap_conf.search_filter) + 3;
990 /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
991 sz += 29 + strlen(pw->pw_name);
993 /* Add space for primary and supplementary groups */
994 if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
995 sz += 12 + strlen(grp->gr_name);
997 if ((grlist = get_group_list(pw)) != NULL) {
998 for (i = 0; i < grlist->ngroups; i++) {
999 if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
1001 sz += 12 + strlen(grlist->groups[i]);
1005 /* If timed, add space for time limits. */
1006 if (ldap_conf.timed)
1007 sz += TIMEFILTER_LENGTH;
1012 * If timed or using a search filter, start a global AND clause to
1013 * contain the search filter, search criteria, and time restriction.
1015 if (ldap_conf.timed || ldap_conf.search_filter)
1016 (void) strlcpy(buf, "(&", sz);
1018 if (ldap_conf.search_filter)
1019 (void) strlcat(buf, ldap_conf.search_filter, sz);
1021 /* Global OR + sudoUser=user_name filter */
1022 (void) strlcat(buf, "(|(sudoUser=", sz);
1023 (void) strlcat(buf, pw->pw_name, sz);
1024 (void) strlcat(buf, ")", sz);
1026 /* Append primary group */
1028 (void) strlcat(buf, "(sudoUser=%", sz);
1029 (void) strlcat(buf, grp->gr_name, sz);
1030 (void) strlcat(buf, ")", sz);
1033 /* Append supplementary groups */
1034 if (grlist != NULL) {
1035 for (i = 0; i < grlist->ngroups; i++) {
1036 if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
1038 (void) strlcat(buf, "(sudoUser=%", sz);
1039 (void) strlcat(buf, grlist->groups[i], sz);
1040 (void) strlcat(buf, ")", sz);
1044 /* Done with groups. */
1046 grlist_delref(grlist);
1050 /* Add ALL to list and end the global OR */
1051 if (strlcat(buf, "(sudoUser=ALL)", sz) >= sz)
1052 errorx(1, _("sudo_ldap_build_pass1 allocation mismatch"));
1054 /* Add the time restriction, or simply end the global OR. */
1055 if (ldap_conf.timed) {
1056 strlcat(buf, ")", sz); /* closes the global OR */
1057 sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1058 strlcat(buf, timebuffer, sz);
1059 } else if (ldap_conf.search_filter) {
1060 strlcat(buf, ")", sz); /* closes the global OR */
1062 strlcat(buf, ")", sz); /* closes the global OR or the global AND */
1068 * Builds up a filter to check against netgroup entries in LDAP.
1071 sudo_ldap_build_pass2(void)
1073 char *filt, timebuffer[TIMEFILTER_LENGTH];
1075 if (ldap_conf.timed)
1076 sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1079 * Match all sudoUsers beginning with a '+'.
1080 * If a search filter or time restriction is specified,
1081 * those get ANDed in to the expression.
1083 easprintf(&filt, "%s%s(sudoUser=+*)%s%s",
1084 (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
1085 ldap_conf.search_filter ? ldap_conf.search_filter : "",
1086 ldap_conf.timed ? timebuffer : "",
1087 (ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
1093 sudo_ldap_read_secret(const char *path)
1096 char buf[LINE_MAX], *cp;
1098 if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
1099 if (fgets(buf, sizeof(buf), fp) != NULL) {
1100 if ((cp = strchr(buf, '\n')) != NULL)
1102 /* copy to bindpw and binddn */
1103 efree(ldap_conf.bindpw);
1104 ldap_conf.bindpw = estrdup(buf);
1105 efree(ldap_conf.binddn);
1106 ldap_conf.binddn = ldap_conf.rootbinddn;
1107 ldap_conf.rootbinddn = NULL;
1114 sudo_ldap_read_config(void)
1117 char *cp, *keyword, *value;
1118 struct ldap_config_table *cur;
1121 ldap_conf.version = 3;
1122 ldap_conf.port = -1;
1123 ldap_conf.tls_checkpeer = -1;
1124 ldap_conf.timelimit = -1;
1125 ldap_conf.timeout = -1;
1126 ldap_conf.bind_timelimit = -1;
1127 ldap_conf.use_sasl = -1;
1128 ldap_conf.rootuse_sasl = -1;
1129 ldap_conf.deref = -1;
1131 if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
1134 while ((cp = sudo_parseln(fp)) != NULL) {
1136 continue; /* skip empty line */
1138 /* split into keyword and value */
1140 while (*cp && !isblank((unsigned char) *cp))
1143 *cp++ = '\0'; /* terminate keyword */
1145 /* skip whitespace before value */
1146 while (isblank((unsigned char) *cp))
1150 /* Look up keyword in config table. */
1151 for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1152 if (strcasecmp(keyword, cur->conf_str) == 0) {
1153 switch (cur->type) {
1154 case CONF_DEREF_VAL:
1155 if (strcasecmp(value, "searching") == 0)
1156 *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
1157 else if (strcasecmp(value, "finding") == 0)
1158 *(int *)(cur->valp) = LDAP_DEREF_FINDING;
1159 else if (strcasecmp(value, "always") == 0)
1160 *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
1162 *(int *)(cur->valp) = LDAP_DEREF_NEVER;
1165 *(int *)(cur->valp) = atobool(value) == TRUE;
1168 *(int *)(cur->valp) = atoi(value);
1171 efree(*(char **)(cur->valp));
1172 *(char **)(cur->valp) = estrdup(value);
1176 struct ldap_config_list_str **p;
1177 size_t len = strlen(value);
1180 p = (struct ldap_config_list_str **)cur->valp;
1183 *p = emalloc(sizeof(struct ldap_config_list_str) + len);
1184 memcpy((*p)->val, value, len + 1);
1196 if (!ldap_conf.host)
1197 ldap_conf.host = estrdup("localhost");
1199 if (ldap_conf.debug > 1) {
1200 sudo_printf(SUDO_CONV_ERROR_MSG, "LDAP Config Summary\n");
1201 sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
1202 if (ldap_conf.uri) {
1203 struct ldap_config_list_str *uri = ldap_conf.uri;
1206 sudo_printf(SUDO_CONV_ERROR_MSG, "uri %s\n",
1208 } while ((uri = uri->next) != NULL);
1210 sudo_printf(SUDO_CONV_ERROR_MSG, "host %s\n",
1211 ldap_conf.host ? ldap_conf.host : "(NONE)");
1212 sudo_printf(SUDO_CONV_ERROR_MSG, "port %d\n",
1215 sudo_printf(SUDO_CONV_ERROR_MSG, "ldap_version %d\n",
1218 if (ldap_conf.base) {
1219 struct ldap_config_list_str *base = ldap_conf.base;
1221 sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
1223 } while ((base = base->next) != NULL);
1225 sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
1226 "(NONE: LDAP disabled)");
1228 if (ldap_conf.search_filter) {
1229 sudo_printf(SUDO_CONV_ERROR_MSG, "search_filter %s\n",
1230 ldap_conf.search_filter);
1232 sudo_printf(SUDO_CONV_ERROR_MSG, "binddn %s\n",
1233 ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
1234 sudo_printf(SUDO_CONV_ERROR_MSG, "bindpw %s\n",
1235 ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
1236 if (ldap_conf.bind_timelimit > 0) {
1237 sudo_printf(SUDO_CONV_ERROR_MSG, "bind_timelimit %d\n",
1238 ldap_conf.bind_timelimit);
1240 if (ldap_conf.timelimit > 0) {
1241 sudo_printf(SUDO_CONV_ERROR_MSG, "timelimit %d\n",
1242 ldap_conf.timelimit);
1244 if (ldap_conf.deref != -1) {
1245 sudo_printf(SUDO_CONV_ERROR_MSG, "deref %d\n",
1248 sudo_printf(SUDO_CONV_ERROR_MSG, "ssl %s\n",
1249 ldap_conf.ssl ? ldap_conf.ssl : "(no)");
1250 if (ldap_conf.tls_checkpeer != -1) {
1251 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_checkpeer %s\n",
1252 ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
1254 if (ldap_conf.tls_cacertfile != NULL) {
1255 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertfile %s\n",
1256 ldap_conf.tls_cacertfile);
1258 if (ldap_conf.tls_cacertdir != NULL) {
1259 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertdir %s\n",
1260 ldap_conf.tls_cacertdir);
1262 if (ldap_conf.tls_random_file != NULL) {
1263 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_random_file %s\n",
1264 ldap_conf.tls_random_file);
1266 if (ldap_conf.tls_cipher_suite != NULL) {
1267 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cipher_suite %s\n",
1268 ldap_conf.tls_cipher_suite);
1270 if (ldap_conf.tls_certfile != NULL) {
1271 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_certfile %s\n",
1272 ldap_conf.tls_certfile);
1274 if (ldap_conf.tls_keyfile != NULL) {
1275 sudo_printf(SUDO_CONV_ERROR_MSG, "tls_keyfile %s\n",
1276 ldap_conf.tls_keyfile);
1278 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1279 if (ldap_conf.use_sasl != -1) {
1280 sudo_printf(SUDO_CONV_ERROR_MSG, "use_sasl %s\n",
1281 ldap_conf.use_sasl ? "yes" : "no");
1282 sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_auth_id %s\n",
1283 ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
1284 sudo_printf(SUDO_CONV_ERROR_MSG, "rootuse_sasl %d\n",
1285 ldap_conf.rootuse_sasl);
1286 sudo_printf(SUDO_CONV_ERROR_MSG, "rootsasl_auth_id %s\n",
1287 ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
1288 sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_secprops %s\n",
1289 ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
1290 sudo_printf(SUDO_CONV_ERROR_MSG, "krb5_ccname %s\n",
1291 ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
1294 sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
1296 if (!ldap_conf.base)
1297 return FALSE; /* if no base is defined, ignore LDAP */
1299 if (ldap_conf.bind_timelimit > 0)
1300 ldap_conf.bind_timelimit *= 1000; /* convert to ms */
1303 * Interpret SSL option
1305 if (ldap_conf.ssl != NULL) {
1306 if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
1307 ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
1308 else if (atobool(ldap_conf.ssl) == TRUE)
1309 ldap_conf.ssl_mode = SUDO_LDAP_SSL;
1312 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
1313 if (ldap_conf.tls_checkpeer != -1) {
1314 ldapssl_set_strength(NULL,
1315 ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
1319 #ifndef HAVE_LDAP_INITIALIZE
1320 /* Convert uri list to host list if no ldap_initialize(). */
1321 if (ldap_conf.uri) {
1322 struct ldap_config_list_str *uri = ldap_conf.uri;
1323 if (sudo_ldap_parse_uri(uri) != 0)
1326 ldap_conf.uri = uri->next;
1328 } while ((uri = ldap_conf.uri));
1329 ldap_conf.port = LDAP_PORT;
1333 if (!ldap_conf.uri) {
1334 /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
1335 if (ldap_conf.port < 0)
1337 ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
1339 #ifdef HAVE_LDAP_CREATE
1341 * Cannot specify port directly to ldap_create(), each host must
1342 * include :port to override the default.
1344 if (ldap_conf.port != LDAP_PORT)
1345 sudo_ldap_conf_add_ports();
1349 /* If search filter is not parenthesized, make it so. */
1350 if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
1351 size_t len = strlen(ldap_conf.search_filter);
1352 cp = ldap_conf.search_filter;
1353 ldap_conf.search_filter = emalloc(len + 3);
1354 ldap_conf.search_filter[0] = '(';
1355 memcpy(ldap_conf.search_filter + 1, cp, len);
1356 ldap_conf.search_filter[len + 1] = ')';
1357 ldap_conf.search_filter[len + 2] = '\0';
1361 /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
1362 if (ldap_conf.rootbinddn)
1363 sudo_ldap_read_secret(_PATH_LDAP_SECRET);
1365 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1367 * Make sure we can open the file specified by krb5_ccname.
1369 if (ldap_conf.krb5_ccname != NULL) {
1370 if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
1371 strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
1372 value = ldap_conf.krb5_ccname +
1373 (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
1374 if ((fp = fopen(value, "r")) != NULL) {
1375 DPRINTF(("using krb5 credential cache: %s", value), 1);
1378 /* Can't open it, just ignore the entry. */
1379 DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
1380 efree(ldap_conf.krb5_ccname);
1381 ldap_conf.krb5_ccname = NULL;
1390 * Extract the dn from an entry and return the first rdn from it.
1393 sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
1395 #ifdef HAVE_LDAP_STR2DN
1396 char *dn, *rdn = NULL;
1399 if ((dn = ldap_get_dn(ld, entry)) == NULL)
1401 if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
1402 ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
1410 if ((dn = ldap_get_dn(ld, entry)) == NULL)
1412 edn = ldap_explode_dn(dn, 1);
1414 return edn ? edn[0] : NULL;
1419 * Fetch and display the global Options.
1422 sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
1425 struct berval **bv, **p;
1426 struct timeval tv, *tvp = NULL;
1427 struct ldap_config_list_str *base;
1428 struct sudo_ldap_handle *handle = nss->handle;
1430 LDAPMessage *entry, *result;
1431 char *prefix, *filt;
1434 if (handle == NULL || handle->ld == NULL)
1438 filt = sudo_ldap_build_default_filter();
1439 for (base = ldap_conf.base; base != NULL; base = base->next) {
1440 if (ldap_conf.timeout > 0) {
1441 tv.tv_sec = ldap_conf.timeout;
1446 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
1447 filt, NULL, 0, NULL, NULL, tvp, 0, &result);
1448 if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
1449 bv = ldap_get_values_len(ld, entry, "sudoOption");
1451 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
1455 for (p = bv; *p != NULL; p++) {
1456 lbuf_append(lbuf, "%s%s", prefix, (*p)->bv_val);
1460 ldap_value_free_len(bv);
1464 ldap_msgfree(result);
1475 sudo_ldap_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
1482 * Print a record in the short form, ala file sudoers.
1485 sudo_ldap_display_entry_short(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
1487 struct berval **bv, **p;
1490 lbuf_append(lbuf, " (");
1492 /* get the RunAsUser Values from the entry */
1493 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1495 bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1497 for (p = bv; *p != NULL; p++) {
1498 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1500 ldap_value_free_len(bv);
1502 lbuf_append(lbuf, "%s", def_runas_default);
1504 /* get the RunAsGroup Values from the entry */
1505 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1507 lbuf_append(lbuf, " : ");
1508 for (p = bv; *p != NULL; p++) {
1509 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1511 ldap_value_free_len(bv);
1513 lbuf_append(lbuf, ") ");
1515 /* get the Option Values from the entry */
1516 bv = ldap_get_values_len(ld, entry, "sudoOption");
1518 for (p = bv; *p != NULL; p++) {
1519 char *cp = (*p)->bv_val;
1522 if (strcmp(cp, "authenticate") == 0)
1523 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1524 "NOPASSWD: " : "PASSWD: ");
1525 else if (strcmp(cp, "noexec") == 0)
1526 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1527 "EXEC: " : "NOEXEC: ");
1528 else if (strcmp(cp, "setenv") == 0)
1529 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1530 "NOSETENV: " : "SETENV: ");
1532 ldap_value_free_len(bv);
1535 /* get the Command Values from the entry */
1536 bv = ldap_get_values_len(ld, entry, "sudoCommand");
1538 for (p = bv; *p != NULL; p++) {
1539 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1542 ldap_value_free_len(bv);
1544 lbuf_append(lbuf, "\n");
1550 * Print a record in the long form.
1553 sudo_ldap_display_entry_long(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
1555 struct berval **bv, **p;
1559 /* extract the dn, only show the first rdn */
1560 rdn = sudo_ldap_get_first_rdn(ld, entry);
1562 lbuf_append(lbuf, _("\nLDAP Role: %s\n"), rdn);
1564 lbuf_append(lbuf, _("\nLDAP Role: UNKNOWN\n"));
1568 /* get the RunAsUser Values from the entry */
1569 lbuf_append(lbuf, " RunAsUsers: ");
1570 bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1572 bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1574 for (p = bv; *p != NULL; p++) {
1575 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1577 ldap_value_free_len(bv);
1579 lbuf_append(lbuf, "%s", def_runas_default);
1580 lbuf_append(lbuf, "\n");
1582 /* get the RunAsGroup Values from the entry */
1583 bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1585 lbuf_append(lbuf, " RunAsGroups: ");
1586 for (p = bv; *p != NULL; p++) {
1587 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1589 ldap_value_free_len(bv);
1590 lbuf_append(lbuf, "\n");
1593 /* get the Option Values from the entry */
1594 bv = ldap_get_values_len(ld, entry, "sudoOption");
1596 lbuf_append(lbuf, " Options: ");
1597 for (p = bv; *p != NULL; p++) {
1598 lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1600 ldap_value_free_len(bv);
1601 lbuf_append(lbuf, "\n");
1605 * Display order attribute if present. This attribute is single valued,
1606 * so there is no need for a loop.
1608 bv = ldap_get_values_len(ld, entry, "sudoOrder");
1611 lbuf_append(lbuf, _(" Order: %s\n"), (*bv)->bv_val);
1613 ldap_value_free_len(bv);
1616 /* Get the command values from the entry. */
1617 bv = ldap_get_values_len(ld, entry, "sudoCommand");
1619 lbuf_append(lbuf, _(" Commands:\n"));
1620 for (p = bv; *p != NULL; p++) {
1621 lbuf_append(lbuf, "\t%s\n", (*p)->bv_val);
1624 ldap_value_free_len(bv);
1631 * Like sudo_ldap_lookup(), except we just print entries.
1634 sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
1637 struct sudo_ldap_handle *handle = nss->handle;
1639 struct ldap_result *lres;
1643 if (handle == NULL || handle->ld == NULL)
1647 DPRINTF(("ldap search for command list"), 1);
1648 lres = sudo_ldap_result_get(nss, pw);
1650 /* Display all matching entries. */
1651 for (i = 0; i < lres->nentries; i++) {
1652 entry = lres->entries[i].entry;
1654 count += sudo_ldap_display_entry_long(ld, entry, lbuf);
1656 count += sudo_ldap_display_entry_short(ld, entry, lbuf);
1664 sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
1666 struct sudo_ldap_handle *handle = nss->handle;
1668 struct ldap_result *lres;
1670 int i, found = FALSE;
1672 if (handle == NULL || handle->ld == NULL)
1677 * The sudo_ldap_result_get() function returns all nodes that match
1678 * the user and the host.
1680 DPRINTF(("ldap search for command list"), 1);
1681 lres = sudo_ldap_result_get(nss, pw);
1682 for (i = 0; i < lres->nentries; i++) {
1683 entry = lres->entries[i].entry;
1684 if (sudo_ldap_check_command(ld, entry, NULL) &&
1685 sudo_ldap_check_runas(ld, entry)) {
1693 printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
1694 user_args ? " " : "", user_args ? user_args : "");
1698 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1700 sudo_ldap_sasl_interact(LDAP *ld, unsigned int flags, void *_auth_id,
1703 char *auth_id = (char *)_auth_id;
1704 sasl_interact_t *interact = (sasl_interact_t *)_interact;
1706 for (; interact->id != SASL_CB_LIST_END; interact++) {
1707 if (interact->id != SASL_CB_USER)
1708 return LDAP_PARAM_ERROR;
1710 if (auth_id != NULL)
1711 interact->result = auth_id;
1712 else if (interact->defresult != NULL)
1713 interact->result = interact->defresult;
1715 interact->result = "";
1717 interact->len = strlen(interact->result);
1718 #if SASL_VERSION_MAJOR < 2
1719 interact->result = estrdup(interact->result);
1720 #endif /* SASL_VERSION_MAJOR < 2 */
1722 return LDAP_SUCCESS;
1724 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1727 * Set LDAP options based on the config table.
1730 sudo_ldap_set_options(LDAP *ld)
1732 struct ldap_config_table *cur;
1735 /* Set ber options */
1736 #ifdef LBER_OPT_DEBUG_LEVEL
1737 if (ldap_conf.ldap_debug)
1738 ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
1741 /* Set simple LDAP options */
1742 for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1747 if (cur->opt_val == -1)
1750 conn = cur->connected ? ld : NULL;
1751 switch (cur->type) {
1754 ival = *(int *)(cur->valp);
1756 rc = ldap_set_option(conn, cur->opt_val, &ival);
1757 if (rc != LDAP_OPT_SUCCESS) {
1758 warningx("ldap_set_option: %s -> %d: %s",
1759 cur->conf_str, ival, ldap_err2string(rc));
1762 DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
1766 sval = *(char **)(cur->valp);
1768 rc = ldap_set_option(conn, cur->opt_val, sval);
1769 if (rc != LDAP_OPT_SUCCESS) {
1770 warningx("ldap_set_option: %s -> %s: %s",
1771 cur->conf_str, sval, ldap_err2string(rc));
1774 DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
1780 #ifdef LDAP_OPT_TIMEOUT
1781 /* Convert timeout to a timeval */
1782 if (ldap_conf.timeout > 0) {
1784 tv.tv_sec = ldap_conf.timeout;
1786 rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
1787 if (rc != LDAP_OPT_SUCCESS) {
1788 warningx("ldap_set_option(TIMEOUT, %ld): %s",
1789 (long)tv.tv_sec, ldap_err2string(rc));
1792 DPRINTF(("ldap_set_option(LDAP_OPT_TIMEOUT, %ld)",
1793 (long)tv.tv_sec), 1);
1796 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1797 /* Convert bind_timelimit to a timeval */
1798 if (ldap_conf.bind_timelimit > 0) {
1800 tv.tv_sec = ldap_conf.bind_timelimit / 1000;
1802 rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
1803 if (rc != LDAP_OPT_SUCCESS) {
1804 warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
1805 (long)tv.tv_sec, ldap_err2string(rc));
1808 DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)",
1809 (long)tv.tv_sec), 1);
1813 #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
1814 if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
1815 int val = LDAP_OPT_X_TLS_HARD;
1816 rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
1817 if (rc != LDAP_SUCCESS) {
1818 warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
1819 ldap_err2string(rc));
1822 DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)"), 1);
1829 * Create a new sudo_ldap_result structure.
1831 static struct ldap_result *
1832 sudo_ldap_result_alloc(void)
1834 struct ldap_result *result;
1836 result = emalloc(sizeof(*result));
1837 result->searches = NULL;
1838 result->nentries = 0;
1839 result->entries = NULL;
1840 result->allocated_entries = 0;
1841 result->user_matches = FALSE;
1842 result->host_matches = FALSE;
1847 * Free the ldap result structure
1850 sudo_ldap_result_free(struct ldap_result *lres)
1852 struct ldap_search_list *s;
1855 if (lres->nentries) {
1856 efree(lres->entries);
1857 lres->entries = NULL;
1859 if (lres->searches) {
1860 while ((s = lres->searches) != NULL) {
1861 ldap_msgfree(s->searchresult);
1862 lres->searches = s->next;
1871 * Add a search result to the ldap_result structure.
1873 static struct ldap_search_list *
1874 sudo_ldap_result_add_search(struct ldap_result *lres, LDAP *ldap,
1875 LDAPMessage *searchresult)
1877 struct ldap_search_list *s, *news;
1879 news = emalloc(sizeof(struct ldap_search_list));
1882 news->searchresult = searchresult;
1884 /* Add entry to the end of the chain (XXX - tailq instead?). */
1885 if (lres->searches) {
1886 for (s = lres->searches; s->next != NULL; s = s->next)
1890 lres->searches = news;
1896 * Connect to the LDAP server specified by ld
1899 sudo_ldap_bind_s(LDAP *ld)
1902 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1903 const char *old_ccname = user_ccname;
1904 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1905 unsigned int status;
1909 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1910 if (ldap_conf.rootuse_sasl == TRUE ||
1911 (ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE)) {
1912 void *auth_id = ldap_conf.rootsasl_auth_id ?
1913 ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
1915 if (ldap_conf.krb5_ccname != NULL) {
1916 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1917 if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
1918 != GSS_S_COMPLETE) {
1920 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
1923 setenv("KRB5CCNAME", ldap_conf.krb5_ccname, TRUE);
1926 rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
1927 NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
1928 if (ldap_conf.krb5_ccname != NULL) {
1929 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
1930 if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
1931 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
1933 if (old_ccname != NULL)
1934 setenv("KRB5CCNAME", old_ccname, TRUE);
1936 unsetenv("KRB5CCNAME");
1939 if (rc != LDAP_SUCCESS) {
1940 warningx("ldap_sasl_interactive_bind_s(): %s",
1941 ldap_err2string(rc));
1944 DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
1946 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1947 #ifdef HAVE_LDAP_SASL_BIND_S
1951 bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
1952 bv.bv_len = strlen(bv.bv_val);
1954 rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
1956 if (rc != LDAP_SUCCESS) {
1957 warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
1960 DPRINTF(("ldap_sasl_bind_s() ok"), 1);
1964 rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
1965 if (rc != LDAP_SUCCESS) {
1966 warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
1969 DPRINTF(("ldap_simple_bind_s() ok"), 1);
1976 * Open a connection to the LDAP server.
1977 * Returns 0 on success and non-zero on failure.
1980 sudo_ldap_open(struct sudo_nss *nss)
1983 int rc, ldapnoinit = FALSE;
1984 struct sudo_ldap_handle *handle;
1986 if (!sudo_ldap_read_config())
1989 /* Prevent reading of user ldaprc and system defaults. */
1990 if (getenv("LDAPNOINIT") == NULL) {
1992 setenv("LDAPNOINIT", "1", TRUE);
1995 /* Connect to LDAP server */
1996 #ifdef HAVE_LDAP_INITIALIZE
1997 if (ldap_conf.uri != NULL) {
1998 char *buf = sudo_ldap_join_uri(ldap_conf.uri);
1999 DPRINTF(("ldap_initialize(ld, %s)", buf), 2);
2000 rc = ldap_initialize(&ld, buf);
2004 rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
2005 if (rc != LDAP_SUCCESS) {
2006 warningx(_("unable to initialize LDAP: %s"), ldap_err2string(rc));
2011 unsetenv("LDAPNOINIT");
2013 /* Set LDAP options */
2014 if (sudo_ldap_set_options(ld) < 0)
2017 if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
2018 #if defined(HAVE_LDAP_START_TLS_S)
2019 rc = ldap_start_tls_s(ld, NULL, NULL);
2020 if (rc != LDAP_SUCCESS) {
2021 warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
2024 DPRINTF(("ldap_start_tls_s() ok"), 1);
2025 #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
2026 if (ldap_ssl_client_init(NULL, NULL, 0, &rc) != LDAP_SUCCESS) {
2027 warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
2030 rc = ldap_start_tls_s_np(ld, NULL);
2031 if (rc != LDAP_SUCCESS) {
2032 warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
2035 DPRINTF(("ldap_start_tls_s_np() ok"), 1);
2037 warningx(_("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()"));
2038 #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
2041 /* Actually connect */
2042 if (sudo_ldap_bind_s(ld) != 0)
2045 /* Create a handle container. */
2046 handle = emalloc(sizeof(struct sudo_ldap_handle));
2048 handle->result = NULL;
2049 handle->username = NULL;
2050 handle->grlist = NULL;
2051 nss->handle = handle;
2057 sudo_ldap_setdefs(struct sudo_nss *nss)
2059 struct ldap_config_list_str *base;
2060 struct sudo_ldap_handle *handle = nss->handle;
2061 struct timeval tv, *tvp = NULL;
2063 LDAPMessage *entry, *result;
2067 if (handle == NULL || handle->ld == NULL)
2071 filt = sudo_ldap_build_default_filter();
2072 DPRINTF(("Looking for cn=defaults: %s", filt), 1);
2074 for (base = ldap_conf.base; base != NULL; base = base->next) {
2075 if (ldap_conf.timeout > 0) {
2076 tv.tv_sec = ldap_conf.timeout;
2081 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
2082 filt, NULL, 0, NULL, NULL, tvp, 0, &result);
2083 if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
2084 DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
2085 sudo_ldap_parse_options(ld, entry);
2087 DPRINTF(("no default options found in %s", base->val), 1);
2090 ldap_msgfree(result);
2098 * like sudoers_lookup() - only LDAP style
2101 sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
2103 struct sudo_ldap_handle *handle = nss->handle;
2106 int i, rc, setenv_implied;
2107 struct ldap_result *lres = NULL;
2109 if (handle == NULL || handle->ld == NULL)
2113 /* Fetch list of sudoRole entries that match user and host. */
2114 lres = sudo_ldap_result_get(nss, sudo_user.pw);
2117 * The following queries are only determine whether or not a
2118 * password is required, so the order of the entries doesn't matter.
2121 int doauth = UNSPEC;
2122 int matched = UNSPEC;
2123 enum def_tuple pwcheck =
2124 (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
2126 DPRINTF(("perform search for pwflag %d", pwflag), 1);
2127 for (i = 0; i < lres->nentries; i++) {
2128 entry = lres->entries[i].entry;
2129 if ((pwcheck == any && doauth != FALSE) ||
2130 (pwcheck == all && doauth == FALSE)) {
2131 doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
2133 /* Only check the command when listing another user. */
2134 if (user_uid == 0 || list_pw == NULL ||
2135 user_uid == list_pw->pw_uid ||
2136 sudo_ldap_check_command(ld, entry, NULL)) {
2141 if (matched || user_uid == 0) {
2142 SET(ret, VALIDATE_OK);
2143 CLR(ret, VALIDATE_NOT_OK);
2144 if (def_authenticate) {
2147 SET(ret, FLAG_CHECK_USER);
2151 if (doauth == FALSE)
2152 def_authenticate = FALSE;
2155 def_authenticate = FALSE;
2165 DPRINTF(("searching LDAP for sudoers entries"), 1);
2167 setenv_implied = FALSE;
2168 for (i = 0; i < lres->nentries; i++) {
2169 entry = lres->entries[i].entry;
2170 if (!sudo_ldap_check_runas(ld, entry))
2172 rc = sudo_ldap_check_command(ld, entry, &setenv_implied);
2174 /* We have a match. */
2175 DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
2177 DPRINTF(("LDAP entry: %p", entry), 1);
2178 /* Apply entry-specific options. */
2181 sudo_ldap_parse_options(ld, entry);
2183 /* Set role and type if not specified on command line. */
2184 if (user_role == NULL)
2185 user_role = def_role;
2186 if (user_type == NULL)
2187 user_type = def_type;
2188 #endif /* HAVE_SELINUX */
2189 SET(ret, VALIDATE_OK);
2190 CLR(ret, VALIDATE_NOT_OK);
2192 SET(ret, VALIDATE_NOT_OK);
2193 CLR(ret, VALIDATE_OK);
2200 DPRINTF(("done with LDAP searches"), 1);
2201 DPRINTF(("user_matches=%d", lres->user_matches), 1);
2202 DPRINTF(("host_matches=%d", lres->host_matches), 1);
2204 if (!ISSET(ret, VALIDATE_OK)) {
2205 /* No matching entries. */
2206 if (pwflag && list_pw == NULL)
2207 SET(ret, FLAG_NO_CHECK);
2209 if (lres->user_matches)
2210 CLR(ret, FLAG_NO_USER);
2211 if (lres->host_matches)
2212 CLR(ret, FLAG_NO_HOST);
2213 DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
2219 * Comparison function for ldap_entry_wrapper structures, descending order.
2222 ldap_entry_compare(const void *a, const void *b)
2224 const struct ldap_entry_wrapper *aw = a;
2225 const struct ldap_entry_wrapper *bw = b;
2227 return bw->order < aw->order ? -1 :
2228 (bw->order > aw->order ? 1 : 0);
2232 * Find the last entry in the list of searches, usually the
2233 * one currently being used to add entries.
2234 * XXX - use a tailq instead?
2236 static struct ldap_search_list *
2237 sudo_ldap_result_last_search(struct ldap_result *lres)
2239 struct ldap_search_list *result = lres->searches;
2242 while (result->next)
2243 result = result->next;
2249 * Add an entry to the result structure.
2251 static struct ldap_entry_wrapper *
2252 sudo_ldap_result_add_entry(struct ldap_result *lres, LDAPMessage *entry)
2254 struct ldap_search_list *last;
2259 /* Determine whether the entry has the sudoOrder attribute. */
2260 last = sudo_ldap_result_last_search(lres);
2261 bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
2263 if (ldap_count_values_len(bv) > 0) {
2264 /* Get the value of this attribute, 0 if not present. */
2265 DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
2266 order = strtod((*bv)->bv_val, &ep);
2267 if (ep == (*bv)->bv_val || *ep != '\0') {
2268 warningx(_("invalid sudoOrder attribute: %s"), (*bv)->bv_val);
2271 DPRINTF(("order attribute: %f", order), 1);
2273 ldap_value_free_len(bv);
2277 * Enlarge the array of entry wrappers as needed, preallocating blocks
2278 * of 100 entries to save on allocation time.
2280 if (++lres->nentries > lres->allocated_entries) {
2281 lres->allocated_entries += ALLOCATION_INCREMENT;
2282 lres->entries = erealloc3(lres->entries, lres->allocated_entries,
2283 sizeof(lres->entries[0]));
2286 /* Fill in the new entry and return it. */
2287 lres->entries[lres->nentries - 1].entry = entry;
2288 lres->entries[lres->nentries - 1].order = order;
2290 return &lres->entries[lres->nentries - 1];
2294 * Free the ldap result structure in the sudo_nss handle.
2297 sudo_ldap_result_free_nss(struct sudo_nss *nss)
2299 struct sudo_ldap_handle *handle = nss->handle;
2301 if (handle->result != NULL) {
2302 DPRINTF(("removing reusable search result"), 1);
2303 sudo_ldap_result_free(handle->result);
2304 if (handle->username) {
2305 efree(handle->username);
2306 handle->username = NULL;
2308 handle->grlist = NULL;
2309 handle->result = NULL;
2314 * Perform the LDAP query for the user or return a cached query if
2315 * there is one for this user.
2317 static struct ldap_result *
2318 sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw)
2320 struct sudo_ldap_handle *handle = nss->handle;
2321 struct ldap_config_list_str *base;
2322 struct ldap_result *lres;
2323 struct timeval tv, *tvp = NULL;
2324 LDAPMessage *entry, *result;
2325 LDAP *ld = handle->ld;
2330 * If we already have a cached result, return it so we don't have to
2331 * have to contact the LDAP server again.
2333 if (handle->result) {
2334 if (handle->grlist == user_group_list &&
2335 strcmp(pw->pw_name, handle->username) == 0) {
2336 DPRINTF(("reusing previous result (user %s) with %d entries",
2337 handle->username, handle->result->nentries), 1);
2338 return handle->result;
2340 /* User mismatch, cached result cannot be used. */
2341 DPRINTF(("removing result (user %s), new search (user %s)",
2342 handle->username, pw->pw_name), 1);
2343 sudo_ldap_result_free_nss(nss);
2347 * Okay - time to search for anything that matches this user
2348 * Lets limit it to only two queries of the LDAP server
2350 * The first pass will look by the username, groups, and
2351 * the keyword ALL. We will then inspect the results that
2352 * came back from the query. We don't need to inspect the
2353 * sudoUser in this pass since the LDAP server already scanned
2356 * The second pass will return all the entries that contain
2357 * user netgroups. Then we take the netgroups returned and
2358 * try to match them against the username.
2360 * Since we have to sort the possible entries before we make a
2361 * decision, we perform the queries and store all of the results in
2362 * an ldap_result object. The results are then sorted by sudoOrder.
2364 lres = sudo_ldap_result_alloc();
2365 for (do_netgr = 0; do_netgr < 2; do_netgr++) {
2366 filt = do_netgr ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
2367 DPRINTF(("ldap search '%s'", filt), 1);
2368 for (base = ldap_conf.base; base != NULL; base = base->next) {
2369 DPRINTF(("searching from base '%s'", base->val), 1);
2370 if (ldap_conf.timeout > 0) {
2371 tv.tv_sec = ldap_conf.timeout;
2376 rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
2377 NULL, 0, NULL, NULL, tvp, 0, &result);
2378 if (rc != LDAP_SUCCESS) {
2379 DPRINTF(("nothing found for '%s'", filt), 1);
2382 lres->user_matches = TRUE;
2384 /* Add the seach result to list of search results. */
2385 DPRINTF(("adding search result"), 1);
2386 sudo_ldap_result_add_search(lres, ld, result);
2387 LDAP_FOREACH(entry, ld, result) {
2389 sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
2390 sudo_ldap_check_host(ld, entry)) {
2391 lres->host_matches = TRUE;
2392 sudo_ldap_result_add_entry(lres, entry);
2395 DPRINTF(("result now has %d entries", lres->nentries), 1);
2400 /* Sort the entries by the sudoOrder attribute. */
2401 DPRINTF(("sorting remaining %d entries", lres->nentries), 1);
2402 qsort(lres->entries, lres->nentries, sizeof(lres->entries[0]),
2403 ldap_entry_compare);
2405 /* Store everything in the sudo_nss handle. */
2406 handle->result = lres;
2407 handle->username = estrdup(pw->pw_name);
2408 handle->grlist = user_group_list;
2414 * Shut down the LDAP connection.
2417 sudo_ldap_close(struct sudo_nss *nss)
2419 struct sudo_ldap_handle *handle = nss->handle;
2421 if (handle != NULL) {
2422 /* Free the result before unbinding; it may use the LDAP connection. */
2423 sudo_ldap_result_free_nss(nss);
2425 /* Unbind and close the LDAP connection. */
2426 if (handle->ld != NULL) {
2427 ldap_unbind_ext_s(handle->ld, NULL, NULL);
2431 /* Free the handle container. */
2442 sudo_ldap_parse(struct sudo_nss *nss)
2449 * Create an ldap_result from an LDAP search result.
2451 * This function is currently not used anywhere, it is left here as
2452 * an example of how to use the cached searches.
2454 static struct ldap_result *
2455 sudo_ldap_result_from_search(LDAP *ldap, LDAPMessage *searchresult)
2458 * An ldap_result is built from several search results, which are
2459 * organized in a list. The head of the list is maintained in the
2460 * ldap_result structure, together with the wrappers that point
2461 * to individual entries, this has to be initialized first.
2463 struct ldap_result *result = sudo_ldap_result_alloc();
2466 * Build a new list node for the search result, this creates the
2469 struct ldap_search_list *last = sudo_ldap_result_add_search(result,
2470 ldap, searchresult);
2473 * Now add each entry in the search result to the array of of entries
2474 * in the ldap_result object.
2477 LDAP_FOREACH(entry, last->ldap, last->searchresult) {
2478 sudo_ldap_result_add_entry(result, entry);
2480 DPRINTF(("sudo_ldap_result_from_search: %d entries found",
2481 result->nentries), 2);