Imported Upstream version 1.8.4p4
[debian/sudo] / plugins / sudoers / ldap.c
1 /*
2  * Copyright (c) 2003-2011 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * This code is derived from software contributed by Aaron Spangler.
5  *
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.
9  *
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.
17  */
18
19 #include <config.h>
20
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <sys/param.h>
24 #include <sys/stat.h>
25 #include <stdio.h>
26 #ifdef STDC_HEADERS
27 # include <stdlib.h>
28 # include <stddef.h>
29 #else
30 # ifdef HAVE_STDLIB_H
31 #  include <stdlib.h>
32 # endif
33 #endif /* STDC_HEADERS */
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #endif /* HAVE_STRING_H */
37 #ifdef HAVE_STRINGS_H
38 # include <strings.h>
39 #endif /* HAVE_STRINGS_H */
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif /* HAVE_UNISTD_H */
43 #if TIME_WITH_SYS_TIME
44 # include <time.h>
45 #endif
46 #include <ctype.h>
47 #include <pwd.h>
48 #include <grp.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <netdb.h>
52 #ifdef HAVE_LBER_H
53 # include <lber.h>
54 #endif
55 #include <ldap.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>
60 #endif
61 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
62 # ifdef HAVE_SASL_SASL_H
63 #  include <sasl/sasl.h>
64 # else
65 #  include <sasl.h>
66 # endif
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>
73 #  else
74 #   include <gssapi.h>
75 #  endif
76 # endif
77 #endif
78
79 #include "sudoers.h"
80 #include "parse.h"
81 #include "lbuf.h"
82
83 /* Older Netscape LDAP SDKs don't prototype ldapssl_set_strength() */
84 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(HAVE_LDAP_SSL_H) && !defined(HAVE_MPS_LDAP_SSL_H)
85 extern int ldapssl_set_strength(LDAP *ldap, int strength);
86 #endif
87
88 #ifndef LDAP_OPT_SUCCESS
89 # define LDAP_OPT_SUCCESS LDAP_SUCCESS
90 #endif
91
92 #ifndef LDAPS_PORT
93 # define LDAPS_PORT 636
94 #endif
95
96 #if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET)
97 # define LDAP_SASL_QUIET        0
98 #endif
99
100 #ifndef HAVE_LDAP_UNBIND_EXT_S
101 #define ldap_unbind_ext_s(a, b, c)      ldap_unbind_s(a)
102 #endif
103
104 #ifndef HAVE_LDAP_SEARCH_EXT_S
105 # ifdef HAVE_LDAP_SEARCH_ST
106 #  define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k)            \
107         ldap_search_st(a, b, c, d, e, f, i, k)
108 # else
109 #  define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k)            \
110         ldap_search_s(a, b, c, d, e, f, k)
111 # endif
112 #endif
113
114 #define LDAP_FOREACH(var, ld, res)                                      \
115     for ((var) = ldap_first_entry((ld), (res));                         \
116         (var) != NULL;                                                  \
117         (var) = ldap_next_entry((ld), (var)))
118
119 #define DPRINTF(args, level)    if (ldap_conf.debug >= level) warningx args
120
121 #define CONF_BOOL       0
122 #define CONF_INT        1
123 #define CONF_STR        2
124 #define CONF_LIST_STR   4
125 #define CONF_DEREF_VAL  5
126
127 #define SUDO_LDAP_SSL           1
128 #define SUDO_LDAP_STARTTLS      2
129
130 /* The TIMEFILTER_LENGTH includes the filter itself plus the global AND
131    wrapped around the user filter and the time filter when timed entries
132    are used. The length is computed as follows:
133        85       for the filter
134        + 2 * 13 for the now timestamp
135        +      3 for the global AND
136 */
137 #define TIMEFILTER_LENGTH       114    
138
139 /*
140  * The ldap_search structure implements a linked list of ldap and
141  * search result pointers, which allows us to remove them after
142  * all search results have been combined in memory.
143  * XXX - should probably be a tailq since we do appends
144  */
145 struct ldap_search_list {
146     LDAP *ldap;
147     LDAPMessage *searchresult;
148     struct ldap_search_list *next;
149 };
150
151 /*
152  * The ldap_entry_wrapper structure is used to implement sorted result entries.
153  * A double is used for the order to allow for insertion of new entries
154  * without having to renumber everything.
155  * Note: there is no standard floating point type in LDAP.
156  *       As a result, some LDAP servers will only allow an integer.
157  */
158 struct ldap_entry_wrapper {
159     LDAPMessage *entry;
160     double order;
161 };
162
163 /*
164  * The ldap_result structure contains the list of matching searches as
165  * well as an array of all result entries sorted by the sudoOrder attribute.
166  */
167 struct ldap_result {
168     struct ldap_search_list *searches;
169     struct ldap_entry_wrapper *entries;
170     int allocated_entries;
171     int nentries;
172     int user_matches;
173     int host_matches;
174 };
175 #define ALLOCATION_INCREMENT    100
176
177 struct ldap_config_table {
178     const char *conf_str;       /* config file string */
179     short type;                 /* CONF_BOOL, CONF_INT, CONF_STR */
180     short connected;            /* connection-specific value? */
181     int opt_val;                /* LDAP_OPT_* (or -1 for sudo internal) */
182     void *valp;                 /* pointer into ldap_conf */
183 };
184
185 struct ldap_config_list_str {
186     struct ldap_config_list_str *next;
187     char val[1];
188 };
189
190 /* LDAP configuration structure */
191 static struct ldap_config {
192     int port;
193     int version;
194     int debug;
195     int ldap_debug;
196     int tls_checkpeer;
197     int timelimit;
198     int timeout;
199     int bind_timelimit;
200     int use_sasl;
201     int rootuse_sasl;
202     int ssl_mode;
203     int timed;
204     int deref;
205     char *host;
206     struct ldap_config_list_str *uri;
207     char *binddn;
208     char *bindpw;
209     char *rootbinddn;
210     struct ldap_config_list_str *base;
211     char *search_filter;
212     char *ssl;
213     char *tls_cacertfile;
214     char *tls_cacertdir;
215     char *tls_random_file;
216     char *tls_cipher_suite;
217     char *tls_certfile;
218     char *tls_keyfile;
219     char *sasl_auth_id;
220     char *rootsasl_auth_id;
221     char *sasl_secprops;
222     char *krb5_ccname;
223 } ldap_conf;
224
225 static struct ldap_config_table ldap_conf_table[] = {
226     { "sudoers_debug", CONF_INT, false, -1, &ldap_conf.debug },
227     { "host", CONF_STR, false, -1, &ldap_conf.host },
228     { "port", CONF_INT, false, -1, &ldap_conf.port },
229     { "ssl", CONF_STR, false, -1, &ldap_conf.ssl },
230     { "sslpath", CONF_STR, false, -1, &ldap_conf.tls_certfile },
231     { "uri", CONF_LIST_STR, false, -1, &ldap_conf.uri },
232 #ifdef LDAP_OPT_DEBUG_LEVEL
233     { "debug", CONF_INT, false, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
234 #endif
235 #ifdef LDAP_OPT_PROTOCOL_VERSION
236     { "ldap_version", CONF_INT, true, LDAP_OPT_PROTOCOL_VERSION,
237         &ldap_conf.version },
238 #endif
239 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
240     { "tls_checkpeer", CONF_BOOL, false, LDAP_OPT_X_TLS_REQUIRE_CERT,
241         &ldap_conf.tls_checkpeer },
242 #else
243     { "tls_checkpeer", CONF_BOOL, false, -1, &ldap_conf.tls_checkpeer },
244 #endif
245 #ifdef LDAP_OPT_X_TLS_CACERTFILE
246     { "tls_cacertfile", CONF_STR, false, LDAP_OPT_X_TLS_CACERTFILE,
247         &ldap_conf.tls_cacertfile },
248     { "tls_cacert", CONF_STR, false, LDAP_OPT_X_TLS_CACERTFILE,
249         &ldap_conf.tls_cacertfile },
250 #endif
251 #ifdef LDAP_OPT_X_TLS_CACERTDIR
252     { "tls_cacertdir", CONF_STR, false, LDAP_OPT_X_TLS_CACERTDIR,
253         &ldap_conf.tls_cacertdir },
254 #endif
255 #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
256     { "tls_randfile", CONF_STR, false, LDAP_OPT_X_TLS_RANDOM_FILE,
257         &ldap_conf.tls_random_file },
258 #endif
259 #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
260     { "tls_ciphers", CONF_STR, false, LDAP_OPT_X_TLS_CIPHER_SUITE,
261         &ldap_conf.tls_cipher_suite },
262 #endif
263 #ifdef LDAP_OPT_X_TLS_CERTFILE
264     { "tls_cert", CONF_STR, false, LDAP_OPT_X_TLS_CERTFILE,
265         &ldap_conf.tls_certfile },
266 #else
267     { "tls_cert", CONF_STR, false, -1, &ldap_conf.tls_certfile },
268 #endif
269 #ifdef LDAP_OPT_X_TLS_KEYFILE
270     { "tls_key", CONF_STR, false, LDAP_OPT_X_TLS_KEYFILE,
271         &ldap_conf.tls_keyfile },
272 #else
273     { "tls_key", CONF_STR, false, -1, &ldap_conf.tls_keyfile },
274 #endif
275 #ifdef LDAP_OPT_NETWORK_TIMEOUT
276     { "bind_timelimit", CONF_INT, true, -1 /* needs timeval, set manually */,
277         &ldap_conf.bind_timelimit },
278     { "network_timeout", CONF_INT, true, -1 /* needs timeval, set manually */,
279         &ldap_conf.bind_timelimit },
280 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
281     { "bind_timelimit", CONF_INT, true, LDAP_X_OPT_CONNECT_TIMEOUT,
282         &ldap_conf.bind_timelimit },
283     { "network_timeout", CONF_INT, true, LDAP_X_OPT_CONNECT_TIMEOUT,
284         &ldap_conf.bind_timelimit },
285 #endif
286     { "timelimit", CONF_INT, true, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
287 #ifdef LDAP_OPT_TIMEOUT
288     { "timeout", CONF_INT, true, -1 /* needs timeval, set manually */,
289         &ldap_conf.timeout },
290 #endif
291 #ifdef LDAP_OPT_DEREF
292     { "deref", CONF_DEREF_VAL, true, LDAP_OPT_DEREF, &ldap_conf.deref },
293 #endif
294     { "binddn", CONF_STR, false, -1, &ldap_conf.binddn },
295     { "bindpw", CONF_STR, false, -1, &ldap_conf.bindpw },
296     { "rootbinddn", CONF_STR, false, -1, &ldap_conf.rootbinddn },
297     { "sudoers_base", CONF_LIST_STR, false, -1, &ldap_conf.base },
298     { "sudoers_timed", CONF_BOOL, false, -1, &ldap_conf.timed },
299     { "sudoers_search_filter", CONF_STR, false, -1, &ldap_conf.search_filter },
300 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
301     { "use_sasl", CONF_BOOL, false, -1, &ldap_conf.use_sasl },
302     { "sasl_auth_id", CONF_STR, false, -1, &ldap_conf.sasl_auth_id },
303     { "rootuse_sasl", CONF_BOOL, false, -1, &ldap_conf.rootuse_sasl },
304     { "rootsasl_auth_id", CONF_STR, false, -1, &ldap_conf.rootsasl_auth_id },
305 # ifdef LDAP_OPT_X_SASL_SECPROPS
306     { "sasl_secprops", CONF_STR, true, LDAP_OPT_X_SASL_SECPROPS,
307         &ldap_conf.sasl_secprops },
308 # endif
309     { "krb5_ccname", CONF_STR, false, -1, &ldap_conf.krb5_ccname },
310 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
311     { NULL }
312 };
313
314 /* sudo_nss implementation */
315 static int sudo_ldap_open(struct sudo_nss *nss);
316 static int sudo_ldap_close(struct sudo_nss *nss);
317 static int sudo_ldap_parse(struct sudo_nss *nss);
318 static int sudo_ldap_setdefs(struct sudo_nss *nss);
319 static int sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag);
320 static int sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw);
321 static int sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
322     struct lbuf *lbuf);
323 static int sudo_ldap_display_bound_defaults(struct sudo_nss *nss,
324     struct passwd *pw, struct lbuf *lbuf);
325 static int sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
326     struct lbuf *lbuf);
327 static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss,
328     struct passwd *pw);
329
330 /*
331  * LDAP sudo_nss handle.
332  * We store the connection to the LDAP server, the cached ldap_result object
333  * (if any), and the name of the user the query was performed for.
334  * If a new query is launched with sudo_ldap_result_get() that specifies a
335  * different user, the old cached result is freed before the new query is run.
336  */
337 struct sudo_ldap_handle {
338     LDAP *ld;
339     struct ldap_result *result;
340     char *username;
341     struct group_list *grlist;
342 };
343
344 struct sudo_nss sudo_nss_ldap = {
345     &sudo_nss_ldap,
346     NULL,
347     sudo_ldap_open,
348     sudo_ldap_close,
349     sudo_ldap_parse,
350     sudo_ldap_setdefs,
351     sudo_ldap_lookup,
352     sudo_ldap_display_cmnd,
353     sudo_ldap_display_defaults,
354     sudo_ldap_display_bound_defaults,
355     sudo_ldap_display_privs
356 };
357
358 #ifdef HAVE_LDAP_CREATE
359 /*
360  * Rebuild the hosts list and include a specific port for each host.
361  * ldap_create() does not take a default port parameter so we must
362  * append one if we want something other than LDAP_PORT.
363  */
364 static void
365 sudo_ldap_conf_add_ports(void)
366 {
367
368     char *host, *port, defport[13];
369     char hostbuf[LINE_MAX * 2];
370     debug_decl(sudo_ldap_conf_add_ports, SUDO_DEBUG_LDAP)
371
372     hostbuf[0] = '\0';
373     if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
374         errorx(1, _("sudo_ldap_conf_add_ports: port too large"));
375
376     for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
377         if (hostbuf[0] != '\0') {
378             if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
379                 goto toobig;
380         }
381
382         if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
383             goto toobig;
384         /* Append port if there is not one already. */
385         if ((port = strrchr(host, ':')) == NULL ||
386             !isdigit((unsigned char)port[1])) {
387             if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
388                 goto toobig;
389         }
390     }
391
392     efree(ldap_conf.host);
393     ldap_conf.host = estrdup(hostbuf);
394     debug_return;
395
396 toobig:
397     errorx(1, _("sudo_ldap_conf_add_ports: out of space expanding hostbuf"));
398 }
399 #endif
400
401 #ifndef HAVE_LDAP_INITIALIZE
402 /*
403  * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
404  * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
405  * where the trailing slash is optional.
406  */
407 static int
408 sudo_ldap_parse_uri(const struct ldap_config_list_str *uri_list)
409 {
410     char *buf, *uri, *host, *cp, *port;
411     char hostbuf[LINE_MAX];
412     int nldap = 0, nldaps = 0;
413     int rc = -1;
414     debug_decl(sudo_ldap_parse_uri, SUDO_DEBUG_LDAP)
415
416     do {
417         buf = estrdup(uri_list->val);
418         hostbuf[0] = '\0';
419         for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
420             if (strncasecmp(uri, "ldap://", 7) == 0) {
421                 nldap++;
422                 host = uri + 7;
423             } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
424                 nldaps++;
425                 host = uri + 8;
426             } else {
427                 warningx(_("unsupported LDAP uri type: %s"), uri);
428                 goto done;
429             }
430
431             /* trim optional trailing slash */
432             if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
433                 *cp = '\0';
434             }
435
436             if (hostbuf[0] != '\0') {
437                 if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
438                     goto toobig;
439             }
440
441             if (*host == '\0')
442                 host = "localhost";             /* no host specified, use localhost */
443
444             if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
445                 goto toobig;
446
447             /* If using SSL and no port specified, add port 636 */
448             if (nldaps) {
449                 if ((port = strrchr(host, ':')) == NULL ||
450                     !isdigit((unsigned char)port[1]))
451                     if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
452                         goto toobig;
453             }
454         }
455         if (hostbuf[0] == '\0') {
456             warningx(_("invalid uri: %s"), uri_list);
457             goto done;
458         }
459
460         if (nldaps != 0) {
461             if (nldap != 0) {
462                 warningx(_("unable to mix ldap and ldaps URIs"));
463                 goto done;
464             }
465             if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
466                 warningx(_("unable to mix ldaps and starttls"));
467                 goto done;
468             }
469             ldap_conf.ssl_mode = SUDO_LDAP_SSL;
470         }
471
472         efree(ldap_conf.host);
473         ldap_conf.host = estrdup(hostbuf);
474         efree(buf);
475     } while ((uri_list = uri_list->next));
476
477     buf = NULL;
478     rc = 0;
479
480 done:
481     efree(buf);
482     debug_return_int(rc);
483
484 toobig:
485     errorx(1, _("sudo_ldap_parse_uri: out of space building hostbuf"));
486 }
487 #else
488 static char *
489 sudo_ldap_join_uri(struct ldap_config_list_str *uri_list)
490 {
491     struct ldap_config_list_str *uri;
492     size_t len = 0;
493     char *buf, *cp;
494     debug_decl(sudo_ldap_join_uri, SUDO_DEBUG_LDAP)
495
496     /* Usually just a single entry. */
497     if (uri_list->next == NULL)
498         debug_return_str(estrdup(uri_list->val));
499
500     for (uri = uri_list; uri != NULL; uri = uri->next) {
501         len += strlen(uri->val) + 1;
502     }
503     buf = cp = emalloc(len);
504     buf[0] = '\0';
505     for (uri = uri_list; uri != NULL; uri = uri->next) {
506         cp += strlcpy(cp, uri->val, len - (cp - buf));
507         *cp++ = ' ';
508     }
509     cp[-1] = '\0';
510     debug_return_str(buf);
511 }
512 #endif /* HAVE_LDAP_INITIALIZE */
513
514 static int
515 sudo_ldap_init(LDAP **ldp, const char *host, int port)
516 {
517     LDAP *ld = NULL;
518     int rc = LDAP_CONNECT_ERROR;
519     debug_decl(sudo_ldap_init, SUDO_DEBUG_LDAP)
520
521 #ifdef HAVE_LDAPSSL_INIT
522     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
523         DPRINTF(("ldapssl_clientauth_init(%s, %s)",
524             ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
525             ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
526         rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
527             ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
528         /*
529          * Mozilla-derived SDKs have a bug starting with version 5.0
530          * where the path can no longer be a file name and must be a dir.
531          */
532         if (rc != LDAP_SUCCESS) {
533             char *cp;
534             if (ldap_conf.tls_certfile) {
535                 cp = strrchr(ldap_conf.tls_certfile, '/');
536                 if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0)
537                     *cp = '\0';
538             }
539             if (ldap_conf.tls_keyfile) {
540                 cp = strrchr(ldap_conf.tls_keyfile, '/');
541                 if (cp != NULL && strncmp(cp + 1, "key", 3) == 0)
542                     *cp = '\0';
543             }
544             DPRINTF(("ldapssl_clientauth_init(%s, %s)",
545                 ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
546                 ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
547             rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
548                 ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
549             if (rc != LDAP_SUCCESS) {
550                 warningx(_("unable to initialize SSL cert and key db: %s"),
551                     ldapssl_err2string(rc));
552                 goto done;
553             }
554         }
555
556         DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
557         if ((ld = ldapssl_init(host, port, 1)) != NULL)
558             rc = LDAP_SUCCESS;
559     } else
560 #endif
561     {
562 #ifdef HAVE_LDAP_CREATE
563         DPRINTF(("ldap_create()"), 2);
564         if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
565             goto done;
566         DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
567         rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
568 #else
569         DPRINTF(("ldap_init(%s, %d)", host, port), 2);
570         if ((ld = ldap_init(host, port)) != NULL)
571             rc = LDAP_SUCCESS;
572 #endif
573     }
574
575 done:
576     *ldp = ld;
577     debug_return_int(rc);
578 }
579
580 /*
581  * Walk through search results and return true if we have a matching
582  * netgroup, else false.
583  */
584 static bool
585 sudo_ldap_check_user_netgroup(LDAP *ld, LDAPMessage *entry, char *user)
586 {
587     struct berval **bv, **p;
588     char *val;
589     int ret = false;
590     debug_decl(sudo_ldap_check_user_netgroup, SUDO_DEBUG_LDAP)
591
592     if (!entry)
593         debug_return_bool(ret);
594
595     /* get the values from the entry */
596     bv = ldap_get_values_len(ld, entry, "sudoUser");
597     if (bv == NULL)
598         debug_return_bool(ret);
599
600     /* walk through values */
601     for (p = bv; *p != NULL && !ret; p++) {
602         val = (*p)->bv_val;
603         /* match any */
604         if (netgr_matches(val, NULL, NULL, user))
605             ret = true;
606         DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
607             ret ? "MATCH!" : "not"), 2 + ((ret) ? 0 : 1));
608     }
609
610     ldap_value_free_len(bv);    /* cleanup */
611
612     debug_return_bool(ret);
613 }
614
615 /*
616 * Walk through search results and return true if we have a
617 * host match, else false.
618 */
619 static bool
620 sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry)
621 {
622     struct berval **bv, **p;
623     char *val;
624     bool ret = false;
625     debug_decl(sudo_ldap_check_host, SUDO_DEBUG_LDAP)
626
627     if (!entry)
628         debug_return_bool(ret);
629
630     /* get the values from the entry */
631     bv = ldap_get_values_len(ld, entry, "sudoHost");
632     if (bv == NULL)
633         debug_return_bool(ret);
634
635     /* walk through values */
636     for (p = bv; *p != NULL && !ret; p++) {
637         val = (*p)->bv_val;
638         /* match any or address or netgroup or hostname */
639         if (!strcmp(val, "ALL") || addr_matches(val) ||
640             netgr_matches(val, user_host, user_shost, NULL) ||
641             hostname_matches(user_shost, user_host, val))
642             ret = true;
643         DPRINTF(("ldap sudoHost '%s' ... %s", val,
644             ret ? "MATCH!" : "not"), 2);
645     }
646
647     ldap_value_free_len(bv);    /* cleanup */
648
649     debug_return_bool(ret);
650 }
651
652 static int
653 sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry)
654 {
655     struct berval **bv, **p;
656     char *val;
657     bool ret = false;
658     debug_decl(sudo_ldap_check_runas_user, SUDO_DEBUG_LDAP)
659
660     if (!runas_pw)
661         debug_return_bool(UNSPEC);
662
663     /* get the runas user from the entry */
664     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
665     if (bv == NULL)
666         bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
667
668     /*
669      * BUG:
670      * 
671      * if runas is not specified on the command line, the only information
672      * as to which user to run as is in the runas_default option.  We should
673      * check to see if we have the local option present.  Unfortunately we
674      * don't parse these options until after this routine says yes or no.
675      * The query has already returned, so we could peek at the attribute
676      * values here though.
677      * 
678      * For now just require users to always use -u option unless its set
679      * in the global defaults. This behaviour is no different than the global
680      * /etc/sudoers.
681      * 
682      * Sigh - maybe add this feature later
683      */
684
685     /*
686      * If there are no runas entries, match runas_default against
687      * what the user specified on the command line.
688      */
689     if (bv == NULL)
690         debug_return_bool(!strcasecmp(runas_pw->pw_name, def_runas_default));
691
692     /* walk through values returned, looking for a match */
693     for (p = bv; *p != NULL && !ret; p++) {
694         val = (*p)->bv_val;
695         switch (val[0]) {
696         case '+':
697             if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
698                 ret = true;
699             break;
700         case '%':
701             if (usergr_matches(val, runas_pw->pw_name, runas_pw))
702                 ret = true;
703             break;
704         case 'A':
705             if (strcmp(val, "ALL") == 0) {
706                 ret = true;
707                 break;
708             }
709             /* FALLTHROUGH */
710         default:
711             if (strcasecmp(val, runas_pw->pw_name) == 0)
712                 ret = true;
713             break;
714         }
715         DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
716             ret ? "MATCH!" : "not"), 2);
717     }
718
719     ldap_value_free_len(bv);    /* cleanup */
720
721     debug_return_bool(ret);
722 }
723
724 static int
725 sudo_ldap_check_runas_group(LDAP *ld, LDAPMessage *entry)
726 {
727     struct berval **bv, **p;
728     char *val;
729     bool ret = false;
730     debug_decl(sudo_ldap_check_runas_group, SUDO_DEBUG_LDAP)
731
732     /* runas_gr is only set if the user specified the -g flag */
733     if (!runas_gr)
734         debug_return_bool(UNSPEC);
735
736     /* get the values from the entry */
737     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
738     if (bv == NULL)
739         debug_return_bool(ret);
740
741     /* walk through values returned, looking for a match */
742     for (p = bv; *p != NULL && !ret; p++) {
743         val = (*p)->bv_val;
744         if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
745             ret = true;
746         DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
747             ret ? "MATCH!" : "not"), 2);
748     }
749
750     ldap_value_free_len(bv);    /* cleanup */
751
752     debug_return_bool(ret);
753 }
754
755 /*
756  * Walk through search results and return true if we have a runas match,
757  * else false.  RunAs info is optional.
758  */
759 static bool
760 sudo_ldap_check_runas(LDAP *ld, LDAPMessage *entry)
761 {
762     bool ret;
763     debug_decl(sudo_ldap_check_runas, SUDO_DEBUG_LDAP)
764
765     if (!entry)
766         debug_return_bool(false);
767
768     ret = sudo_ldap_check_runas_user(ld, entry) != false &&
769         sudo_ldap_check_runas_group(ld, entry) != false;
770
771     debug_return_bool(ret);
772 }
773
774 /*
775  * Walk through search results and return true if we have a command match,
776  * false if disallowed and UNSPEC if not matched.
777  */
778 static int
779 sudo_ldap_check_command(LDAP *ld, LDAPMessage *entry, int *setenv_implied)
780 {
781     struct berval **bv, **p;
782     char *allowed_cmnd, *allowed_args, *val;
783     bool foundbang;
784     int ret = UNSPEC;
785     debug_decl(sudo_ldap_check_command, SUDO_DEBUG_LDAP)
786
787     if (!entry)
788         debug_return_bool(ret);
789
790     bv = ldap_get_values_len(ld, entry, "sudoCommand");
791     if (bv == NULL)
792         debug_return_bool(ret);
793
794     for (p = bv; *p != NULL && ret != false; p++) {
795         val = (*p)->bv_val;
796         /* Match against ALL ? */
797         if (!strcmp(val, "ALL")) {
798             ret = true;
799             if (setenv_implied != NULL)
800                 *setenv_implied = true;
801             DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
802             continue;
803         }
804
805         /* check for !command */
806         if (*val == '!') {
807             foundbang = true;
808             allowed_cmnd = estrdup(1 + val);    /* !command */
809         } else {
810             foundbang = false;
811             allowed_cmnd = estrdup(val);        /* command */
812         }
813
814         /* split optional args away from command */
815         allowed_args = strchr(allowed_cmnd, ' ');
816         if (allowed_args)
817             *allowed_args++ = '\0';
818
819         /* check the command like normal */
820         if (command_matches(allowed_cmnd, allowed_args)) {
821             /*
822              * If allowed (no bang) set ret but keep on checking.
823              * If disallowed (bang), exit loop.
824              */
825             ret = foundbang ? false : true;
826         }
827         DPRINTF(("ldap sudoCommand '%s' ... %s", val,
828             ret == true ? "MATCH!" : "not"), 2);
829
830         efree(allowed_cmnd);    /* cleanup */
831     }
832
833     ldap_value_free_len(bv);    /* more cleanup */
834
835     debug_return_bool(ret);
836 }
837
838 /*
839  * Search for boolean "option" in sudoOption.
840  * Returns true if found and allowed, false if negated, else UNSPEC.
841  */
842 static int
843 sudo_ldap_check_bool(LDAP *ld, LDAPMessage *entry, char *option)
844 {
845     struct berval **bv, **p;
846     char ch, *var;
847     int ret = UNSPEC;
848     debug_decl(sudo_ldap_check_bool, SUDO_DEBUG_LDAP)
849
850     if (entry == NULL)
851         debug_return_bool(ret);
852
853     bv = ldap_get_values_len(ld, entry, "sudoOption");
854     if (bv == NULL)
855         debug_return_bool(ret);
856
857     /* walk through options */
858     for (p = bv; *p != NULL; p++) {
859         var = (*p)->bv_val;;
860         DPRINTF(("ldap sudoOption: '%s'", var), 2);
861
862         if ((ch = *var) == '!')
863             var++;
864         if (strcmp(var, option) == 0)
865             ret = (ch != '!');
866     }
867
868     ldap_value_free_len(bv);
869
870     debug_return_bool(ret);
871 }
872
873 /*
874  * Read sudoOption and modify the defaults as we go.  This is used once
875  * from the cn=defaults entry and also once when a final sudoRole is matched.
876  */
877 static void
878 sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry)
879 {
880     struct berval **bv, **p;
881     char op, *var, *val;
882     debug_decl(sudo_ldap_parse_options, SUDO_DEBUG_LDAP)
883
884     if (entry == NULL)
885         debug_return;
886
887     bv = ldap_get_values_len(ld, entry, "sudoOption");
888     if (bv == NULL)
889         debug_return;
890
891     /* walk through options */
892     for (p = bv; *p != NULL; p++) {
893         var = estrdup((*p)->bv_val);
894         DPRINTF(("ldap sudoOption: '%s'", var), 2);
895
896         /* check for equals sign past first char */
897         val = strchr(var, '=');
898         if (val > var) {
899             *val++ = '\0';      /* split on = and truncate var */
900             op = *(val - 2);    /* peek for += or -= cases */
901             if (op == '+' || op == '-') {
902                 *(val - 2) = '\0';      /* found, remove extra char */
903                 /* case var+=val or var-=val */
904                 set_default(var, val, (int) op);
905             } else {
906                 /* case var=val */
907                 set_default(var, val, true);
908             }
909         } else if (*var == '!') {
910             /* case !var Boolean False */
911             set_default(var + 1, NULL, false);
912         } else {
913             /* case var Boolean True */
914             set_default(var, NULL, true);
915         }
916         efree(var);
917     }
918
919     ldap_value_free_len(bv);
920
921     debug_return;
922 }
923
924 /*
925  * Build an LDAP timefilter.
926  *
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:
931  *
932  * (&
933  *   (|
934  *      (!(sudoNotAfter=*))
935  *      (sudoNotAfter>__now__)
936  *   )
937  *   (|
938  *      (!(sudoNotBefore=*))
939  *      (sudoNotBefore<__now__)
940  *   )
941  * )
942  *
943  * If either the sudoNotAfter or sudoNotBefore attributes are missing,
944  * no time restriction shall be imposed.
945  */
946 static int
947 sudo_ldap_timefilter(char *buffer, size_t buffersize)
948 {
949     struct tm *tp;
950     time_t now;
951     char timebuffer[16];
952     int bytes = 0;
953     debug_decl(sudo_ldap_timefilter, SUDO_DEBUG_LDAP)
954
955     /* Make sure we have a formatted timestamp for __now__. */
956     time(&now);
957     if ((tp = gmtime(&now)) == NULL) {
958         warning(_("unable to get GMT time"));
959         goto done;
960     }
961
962     /* Format the timestamp according to the RFC. */
963     if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%M%SZ", tp) == 0) {
964         warning(_("unable to format timestamp"));
965         goto done;
966     }
967
968     /* Build filter. */
969     bytes = snprintf(buffer, buffersize, "(&(|(!(sudoNotAfter=*))(sudoNotAfter>=%s))(|(!(sudoNotBefore=*))(sudoNotBefore<=%s)))",
970         timebuffer, timebuffer);
971     if (bytes < 0 || bytes >= buffersize) {
972         warning(_("unable to build time filter"));
973         bytes = 0;
974     }
975
976 done:
977     debug_return_int(bytes);
978 }
979
980 /*
981  * Builds up a filter to search for default settings
982  */
983 static char *
984 sudo_ldap_build_default_filter(void)
985 {
986     char *filt;
987     debug_decl(sudo_ldap_build_default_filter, SUDO_DEBUG_LDAP)
988
989     if (ldap_conf.search_filter)
990         easprintf(&filt, "(&%s(cn=defaults))", ldap_conf.search_filter);
991     else
992         filt = estrdup("cn=defaults");
993     debug_return_str(filt);
994 }
995
996 /*
997  * Determine length of query value after escaping characters
998  * as per RFC 4515.
999  */
1000 static size_t
1001 sudo_ldap_value_len(const char *value)
1002 {
1003     const char *s;
1004     size_t len = 0;
1005
1006     for (s = value; *s != '\0'; s++) {
1007         switch (*s) {
1008         case '\\':
1009         case '(':
1010         case ')':
1011         case '*':
1012             len += 2;
1013             break;
1014         }
1015     }
1016     len += (size_t)(s - value);
1017     return len;
1018 }
1019
1020 /*
1021  * Like strlcat() but escapes characters as per RFC 4515.
1022  */
1023 static size_t
1024 sudo_ldap_value_cat(char *dst, const char *src, size_t size)
1025 {
1026     char *d = dst;
1027     const char *s = src;
1028     size_t n = size;
1029     size_t dlen;
1030
1031     /* Find the end of dst and adjust bytes left but don't go past end */
1032     while (n-- != 0 && *d != '\0')
1033         d++;
1034     dlen = d - dst;
1035     n = size - dlen;
1036
1037     if (n == 0)
1038         return dlen + strlen(s);
1039     while (*s != '\0') {
1040         switch (*s) {
1041         case '\\':
1042             if (n < 3)
1043                 goto done;
1044             *d++ = '\\';
1045             *d++ = '5';
1046             *d++ = 'c';
1047             n -= 3;
1048             break;
1049         case '(':
1050             if (n < 3)
1051                 goto done;
1052             *d++ = '\\';
1053             *d++ = '2';
1054             *d++ = '8';
1055             n -= 3;
1056             break;
1057         case ')':
1058             if (n < 3)
1059                 goto done;
1060             *d++ = '\\';
1061             *d++ = '2';
1062             *d++ = '9';
1063             n -= 3;
1064             break;
1065         case '*':
1066             if (n < 3)
1067                 goto done;
1068             *d++ = '\\';
1069             *d++ = '2';
1070             *d++ = 'a';
1071             n -= 3;
1072             break;
1073         default:
1074             if (n < 1)
1075                 goto done;
1076             *d++ = *s;
1077             n--;
1078             break;
1079         }
1080         s++;
1081     }
1082 done:
1083     *d = '\0';
1084     while (*s != '\0')
1085         s++;
1086     return dlen + (s - src);    /* count does not include NUL */
1087 }
1088
1089 /*
1090  * Builds up a filter to check against LDAP.
1091  */
1092 static char *
1093 sudo_ldap_build_pass1(struct passwd *pw)
1094 {
1095     struct group *grp;
1096     char *buf, timebuffer[TIMEFILTER_LENGTH], gidbuf[MAX_UID_T_LEN];
1097     struct group_list *grlist;
1098     size_t sz = 0;
1099     int i;
1100     debug_decl(sudo_ldap_build_pass1, SUDO_DEBUG_LDAP)
1101
1102     /* Start with LDAP search filter length + 3 */
1103     if (ldap_conf.search_filter)
1104         sz += strlen(ldap_conf.search_filter) + 3;
1105
1106     /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
1107     sz += 29 + sudo_ldap_value_len(pw->pw_name);
1108
1109     /* Add space for primary and supplementary groups and gids */
1110     if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
1111         sz += 12 + sudo_ldap_value_len(grp->gr_name);
1112     }
1113     sz += 13 + MAX_UID_T_LEN;
1114     if ((grlist = get_group_list(pw)) != NULL) {
1115         for (i = 0; i < grlist->ngroups; i++) {
1116             if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
1117                 continue;
1118             sz += 12 + sudo_ldap_value_len(grlist->groups[i]);
1119         }
1120         for (i = 0; i < grlist->ngids; i++) {
1121             if (pw->pw_gid == grlist->gids[i])
1122                 continue;
1123             sz += 13 + MAX_UID_T_LEN;
1124         }
1125     }
1126
1127     /* If timed, add space for time limits. */
1128     if (ldap_conf.timed)
1129         sz += TIMEFILTER_LENGTH;
1130     buf = emalloc(sz);
1131     *buf = '\0';
1132
1133     /*
1134      * If timed or using a search filter, start a global AND clause to
1135      * contain the search filter, search criteria, and time restriction.
1136      */
1137     if (ldap_conf.timed || ldap_conf.search_filter)
1138         (void) strlcpy(buf, "(&", sz);
1139
1140     if (ldap_conf.search_filter)
1141         (void) strlcat(buf, ldap_conf.search_filter, sz);
1142
1143     /* Global OR + sudoUser=user_name filter */
1144     (void) strlcat(buf, "(|(sudoUser=", sz);
1145     (void) sudo_ldap_value_cat(buf, pw->pw_name, sz);
1146     (void) strlcat(buf, ")", sz);
1147
1148     /* Append primary group and gid */
1149     if (grp != NULL) {
1150         (void) strlcat(buf, "(sudoUser=%", sz);
1151         (void) sudo_ldap_value_cat(buf, grp->gr_name, sz);
1152         (void) strlcat(buf, ")", sz);
1153     }
1154     (void) snprintf(gidbuf, sizeof(gidbuf), "%u", (unsigned int)pw->pw_gid);
1155     (void) strlcat(buf, "(sudoUser=%#", sz);
1156     (void) strlcat(buf, gidbuf, sz);
1157     (void) strlcat(buf, ")", sz);
1158
1159     /* Append supplementary groups and gids */
1160     if (grlist != NULL) {
1161         for (i = 0; i < grlist->ngroups; i++) {
1162             if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
1163                 continue;
1164             (void) strlcat(buf, "(sudoUser=%", sz);
1165             (void) sudo_ldap_value_cat(buf, grlist->groups[i], sz);
1166             (void) strlcat(buf, ")", sz);
1167         }
1168         for (i = 0; i < grlist->ngids; i++) {
1169             if (pw->pw_gid == grlist->gids[i])
1170                 continue;
1171             (void) snprintf(gidbuf, sizeof(gidbuf), "%u",
1172                 (unsigned int)grlist->gids[i]);
1173             (void) strlcat(buf, "(sudoUser=%#", sz);
1174             (void) strlcat(buf, gidbuf, sz);
1175             (void) strlcat(buf, ")", sz);
1176         }
1177     }
1178
1179     /* Done with groups. */
1180     if (grlist != NULL)
1181         grlist_delref(grlist);
1182     if (grp != NULL)
1183         gr_delref(grp);
1184
1185     /* Add ALL to list and end the global OR */
1186     if (strlcat(buf, "(sudoUser=ALL)", sz) >= sz)
1187         errorx(1, _("sudo_ldap_build_pass1 allocation mismatch"));
1188
1189     /* Add the time restriction, or simply end the global OR. */
1190     if (ldap_conf.timed) {
1191         strlcat(buf, ")", sz); /* closes the global OR */
1192         sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1193         strlcat(buf, timebuffer, sz);
1194     } else if (ldap_conf.search_filter) {
1195         strlcat(buf, ")", sz); /* closes the global OR */
1196     }
1197     strlcat(buf, ")", sz); /* closes the global OR or the global AND */
1198
1199     debug_return_str(buf);
1200 }
1201
1202 /*
1203  * Builds up a filter to check against netgroup entries in LDAP.
1204  */
1205 static char *
1206 sudo_ldap_build_pass2(void)
1207 {
1208     char *filt, timebuffer[TIMEFILTER_LENGTH];
1209     debug_decl(sudo_ldap_build_pass2, SUDO_DEBUG_LDAP)
1210
1211     if (ldap_conf.timed)
1212         sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
1213
1214     /*
1215      * Match all sudoUsers beginning with a '+'.
1216      * If a search filter or time restriction is specified, 
1217      * those get ANDed in to the expression.
1218      */
1219     easprintf(&filt, "%s%s(sudoUser=+*)%s%s",
1220         (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
1221         ldap_conf.search_filter ? ldap_conf.search_filter : "",
1222         ldap_conf.timed ? timebuffer : "",
1223         (ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
1224
1225     debug_return_str(filt);
1226 }
1227
1228 static void
1229 sudo_ldap_read_secret(const char *path)
1230 {
1231     FILE *fp;
1232     char buf[LINE_MAX], *cp;
1233     debug_decl(sudo_ldap_read_secret, SUDO_DEBUG_LDAP)
1234
1235     if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
1236         if (fgets(buf, sizeof(buf), fp) != NULL) {
1237             if ((cp = strchr(buf, '\n')) != NULL)
1238                 *cp = '\0';
1239             /* copy to bindpw and binddn */
1240             efree(ldap_conf.bindpw);
1241             ldap_conf.bindpw = estrdup(buf);
1242             efree(ldap_conf.binddn);
1243             ldap_conf.binddn = ldap_conf.rootbinddn;
1244             ldap_conf.rootbinddn = NULL;
1245         }
1246         fclose(fp);
1247     }
1248     debug_return;
1249 }
1250
1251 static bool
1252 sudo_ldap_read_config(void)
1253 {
1254     FILE *fp;
1255     char *cp, *keyword, *value;
1256     struct ldap_config_table *cur;
1257     debug_decl(sudo_ldap_read_config, SUDO_DEBUG_LDAP)
1258
1259     /* defaults */
1260     ldap_conf.version = 3;
1261     ldap_conf.port = -1;
1262     ldap_conf.tls_checkpeer = -1;
1263     ldap_conf.timelimit = -1;
1264     ldap_conf.timeout = -1;
1265     ldap_conf.bind_timelimit = -1;
1266     ldap_conf.use_sasl = -1;
1267     ldap_conf.rootuse_sasl = -1;
1268     ldap_conf.deref = -1;
1269
1270     if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
1271         debug_return_bool(false);
1272
1273     while ((cp = sudo_parseln(fp)) != NULL) {
1274         if (*cp == '\0')
1275             continue;           /* skip empty line */
1276
1277         /* split into keyword and value */
1278         keyword = cp;
1279         while (*cp && !isblank((unsigned char) *cp))
1280             cp++;
1281         if (*cp)
1282             *cp++ = '\0';       /* terminate keyword */
1283
1284         /* skip whitespace before value */
1285         while (isblank((unsigned char) *cp))
1286             cp++;
1287         value = cp;
1288
1289         /* Look up keyword in config table. */
1290         for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1291             if (strcasecmp(keyword, cur->conf_str) == 0) {
1292                 switch (cur->type) {
1293                 case CONF_DEREF_VAL:
1294                     if (strcasecmp(value, "searching") == 0)
1295                         *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
1296                     else if (strcasecmp(value, "finding") == 0)
1297                         *(int *)(cur->valp) = LDAP_DEREF_FINDING;
1298                     else if (strcasecmp(value, "always") == 0)
1299                         *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
1300                     else
1301                         *(int *)(cur->valp) = LDAP_DEREF_NEVER;
1302                     break;
1303                 case CONF_BOOL:
1304                     *(int *)(cur->valp) = atobool(value) == true;
1305                     break;
1306                 case CONF_INT:
1307                     *(int *)(cur->valp) = atoi(value);
1308                     break;
1309                 case CONF_STR:
1310                     efree(*(char **)(cur->valp));
1311                     *(char **)(cur->valp) = estrdup(value);
1312                     break;
1313                 case CONF_LIST_STR:
1314                     {
1315                         struct ldap_config_list_str **p;
1316                         size_t len = strlen(value);
1317
1318                         if (len > 0) {
1319                             p = (struct ldap_config_list_str **)cur->valp;
1320                             while (*p != NULL)
1321                                 p = &(*p)->next;
1322                             *p = emalloc(sizeof(struct ldap_config_list_str) + len);
1323                             memcpy((*p)->val, value, len + 1);
1324                             (*p)->next = NULL;
1325                         }
1326                     }
1327                     break;
1328                 }
1329                 break;
1330             }
1331         }
1332     }
1333     fclose(fp);
1334
1335     if (!ldap_conf.host)
1336         ldap_conf.host = estrdup("localhost");
1337
1338     if (ldap_conf.debug > 1) {
1339         sudo_printf(SUDO_CONV_ERROR_MSG, "LDAP Config Summary\n");
1340         sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
1341         if (ldap_conf.uri) {
1342             struct ldap_config_list_str *uri = ldap_conf.uri;
1343
1344             do {
1345                 sudo_printf(SUDO_CONV_ERROR_MSG, "uri              %s\n",
1346                     uri->val);
1347             } while ((uri = uri->next) != NULL);
1348         } else {
1349             sudo_printf(SUDO_CONV_ERROR_MSG, "host             %s\n",
1350                 ldap_conf.host ?  ldap_conf.host : "(NONE)");
1351             sudo_printf(SUDO_CONV_ERROR_MSG, "port             %d\n",
1352                 ldap_conf.port);
1353         }
1354         sudo_printf(SUDO_CONV_ERROR_MSG, "ldap_version     %d\n",
1355             ldap_conf.version);
1356
1357         if (ldap_conf.base) {
1358             struct ldap_config_list_str *base = ldap_conf.base;
1359             do {
1360                 sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base     %s\n",
1361                     base->val);
1362             } while ((base = base->next) != NULL);
1363         } else {
1364             sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base     %s\n",
1365                 "(NONE: LDAP disabled)");
1366         }
1367         if (ldap_conf.search_filter) {
1368             sudo_printf(SUDO_CONV_ERROR_MSG, "search_filter    %s\n",
1369                 ldap_conf.search_filter);
1370         }
1371         sudo_printf(SUDO_CONV_ERROR_MSG, "binddn           %s\n",
1372             ldap_conf.binddn ?  ldap_conf.binddn : "(anonymous)");
1373         sudo_printf(SUDO_CONV_ERROR_MSG, "bindpw           %s\n",
1374             ldap_conf.bindpw ?  ldap_conf.bindpw : "(anonymous)");
1375         if (ldap_conf.bind_timelimit > 0) {
1376             sudo_printf(SUDO_CONV_ERROR_MSG, "bind_timelimit   %d\n",
1377                 ldap_conf.bind_timelimit);
1378         }
1379         if (ldap_conf.timelimit > 0) {
1380             sudo_printf(SUDO_CONV_ERROR_MSG, "timelimit        %d\n",
1381                 ldap_conf.timelimit);
1382         }
1383         if (ldap_conf.deref != -1) {
1384             sudo_printf(SUDO_CONV_ERROR_MSG, "deref            %d\n",
1385                 ldap_conf.deref);
1386         }
1387         sudo_printf(SUDO_CONV_ERROR_MSG, "ssl              %s\n",
1388             ldap_conf.ssl ?  ldap_conf.ssl : "(no)");
1389         if (ldap_conf.tls_checkpeer != -1) {
1390             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_checkpeer    %s\n",
1391                 ldap_conf.tls_checkpeer ?  "(yes)" : "(no)");
1392         }
1393         if (ldap_conf.tls_cacertfile != NULL) {
1394             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertfile   %s\n",
1395                 ldap_conf.tls_cacertfile);
1396         }
1397         if (ldap_conf.tls_cacertdir != NULL) {
1398             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertdir    %s\n",
1399                 ldap_conf.tls_cacertdir);
1400         }
1401         if (ldap_conf.tls_random_file != NULL) {
1402             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_random_file  %s\n",
1403                 ldap_conf.tls_random_file);
1404         }
1405         if (ldap_conf.tls_cipher_suite != NULL) {
1406             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cipher_suite %s\n",
1407                 ldap_conf.tls_cipher_suite);
1408         }
1409         if (ldap_conf.tls_certfile != NULL) {
1410             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_certfile     %s\n",
1411                 ldap_conf.tls_certfile);
1412         }
1413         if (ldap_conf.tls_keyfile != NULL) {
1414             sudo_printf(SUDO_CONV_ERROR_MSG, "tls_keyfile      %s\n",
1415                 ldap_conf.tls_keyfile);
1416         }
1417 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1418         if (ldap_conf.use_sasl != -1) {
1419             sudo_printf(SUDO_CONV_ERROR_MSG, "use_sasl         %s\n",
1420                 ldap_conf.use_sasl ? "yes" : "no");
1421             sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_auth_id     %s\n",
1422                 ldap_conf.sasl_auth_id ?  ldap_conf.sasl_auth_id : "(NONE)");
1423             sudo_printf(SUDO_CONV_ERROR_MSG, "rootuse_sasl     %d\n",
1424                 ldap_conf.rootuse_sasl);
1425             sudo_printf(SUDO_CONV_ERROR_MSG, "rootsasl_auth_id %s\n",
1426                 ldap_conf.rootsasl_auth_id ?  ldap_conf.rootsasl_auth_id : "(NONE)");
1427             sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_secprops    %s\n",
1428                 ldap_conf.sasl_secprops ?  ldap_conf.sasl_secprops : "(NONE)");
1429             sudo_printf(SUDO_CONV_ERROR_MSG, "krb5_ccname      %s\n",
1430                 ldap_conf.krb5_ccname ?  ldap_conf.krb5_ccname : "(NONE)");
1431         }
1432 #endif
1433         sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
1434     }
1435     if (!ldap_conf.base)
1436         debug_return_bool(false);       /* if no base is defined, ignore LDAP */
1437
1438     if (ldap_conf.bind_timelimit > 0)
1439         ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
1440
1441     /*
1442      * Interpret SSL option
1443      */
1444     if (ldap_conf.ssl != NULL) {
1445         if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
1446             ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
1447         else if (atobool(ldap_conf.ssl) == true)
1448             ldap_conf.ssl_mode = SUDO_LDAP_SSL;
1449     }
1450
1451 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
1452     if (ldap_conf.tls_checkpeer != -1) {
1453         ldapssl_set_strength(NULL,
1454             ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
1455     }
1456 #endif
1457
1458 #ifndef HAVE_LDAP_INITIALIZE
1459     /* Convert uri list to host list if no ldap_initialize(). */
1460     if (ldap_conf.uri) {
1461         struct ldap_config_list_str *uri = ldap_conf.uri;
1462         if (sudo_ldap_parse_uri(uri) != 0)
1463             debug_return_bool(false);
1464         do {
1465             ldap_conf.uri = uri->next;
1466             efree(uri);
1467         } while ((uri = ldap_conf.uri));
1468         ldap_conf.port = LDAP_PORT;
1469     }
1470 #endif
1471
1472     if (!ldap_conf.uri) {
1473         /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
1474         if (ldap_conf.port < 0)
1475             ldap_conf.port =
1476                 ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
1477
1478 #ifdef HAVE_LDAP_CREATE
1479         /*
1480          * Cannot specify port directly to ldap_create(), each host must
1481          * include :port to override the default.
1482          */
1483         if (ldap_conf.port != LDAP_PORT)
1484             sudo_ldap_conf_add_ports();
1485 #endif
1486     }
1487
1488     /* If search filter is not parenthesized, make it so. */
1489     if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
1490         size_t len = strlen(ldap_conf.search_filter);
1491         cp = ldap_conf.search_filter;
1492         ldap_conf.search_filter = emalloc(len + 3);
1493         ldap_conf.search_filter[0] = '(';
1494         memcpy(ldap_conf.search_filter + 1, cp, len);
1495         ldap_conf.search_filter[len + 1] = ')';
1496         ldap_conf.search_filter[len + 2] = '\0';
1497         efree(cp);
1498     }
1499
1500     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
1501     if (ldap_conf.rootbinddn)
1502         sudo_ldap_read_secret(_PATH_LDAP_SECRET);
1503
1504 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1505     /*
1506      * Make sure we can open the file specified by krb5_ccname.
1507      */
1508     if (ldap_conf.krb5_ccname != NULL) {
1509         if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
1510             strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
1511             value = ldap_conf.krb5_ccname +
1512                 (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
1513             if ((fp = fopen(value, "r")) != NULL) {
1514                 DPRINTF(("using krb5 credential cache: %s", value), 1);
1515                 fclose(fp);
1516             } else {
1517                 /* Can't open it, just ignore the entry. */
1518                 DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
1519                 efree(ldap_conf.krb5_ccname);
1520                 ldap_conf.krb5_ccname = NULL;
1521             }
1522         }
1523     }
1524 #endif
1525     debug_return_bool(true);
1526 }
1527
1528 /*
1529  * Extract the dn from an entry and return the first rdn from it.
1530  */
1531 static char *
1532 sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
1533 {
1534 #ifdef HAVE_LDAP_STR2DN
1535     char *dn, *rdn = NULL;
1536     LDAPDN tmpDN;
1537     debug_decl(sudo_ldap_get_first_rdn, SUDO_DEBUG_LDAP)
1538
1539     if ((dn = ldap_get_dn(ld, entry)) == NULL)
1540         debug_return_str(NULL);
1541     if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
1542         ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
1543         ldap_dnfree(tmpDN);
1544     }
1545     ldap_memfree(dn);
1546     debug_return_str(rdn);
1547 #else
1548     char *dn, **edn;
1549     debug_decl(sudo_ldap_get_first_rdn, SUDO_DEBUG_LDAP)
1550
1551     if ((dn = ldap_get_dn(ld, entry)) == NULL)
1552         return NULL;
1553     edn = ldap_explode_dn(dn, 1);
1554     ldap_memfree(dn);
1555     debug_return_str(edn ? edn[0] : NULL);
1556 #endif
1557 }
1558
1559 /*
1560  * Fetch and display the global Options.
1561  */
1562 static int
1563 sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
1564     struct lbuf *lbuf)
1565 {
1566     struct berval **bv, **p;
1567     struct timeval tv, *tvp = NULL;
1568     struct ldap_config_list_str *base;
1569     struct sudo_ldap_handle *handle = nss->handle;
1570     LDAP *ld;
1571     LDAPMessage *entry, *result;
1572     char *prefix, *filt;
1573     int rc, count = 0;
1574     debug_decl(sudo_ldap_display_defaults, SUDO_DEBUG_LDAP)
1575
1576     if (handle == NULL || handle->ld == NULL)
1577         goto done;
1578     ld = handle->ld;
1579
1580     filt = sudo_ldap_build_default_filter();
1581     for (base = ldap_conf.base; base != NULL; base = base->next) {
1582         if (ldap_conf.timeout > 0) {
1583             tv.tv_sec = ldap_conf.timeout;
1584             tv.tv_usec = 0;
1585             tvp = &tv;
1586         }
1587         result = NULL;
1588         rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
1589             filt, NULL, 0, NULL, NULL, tvp, 0, &result);
1590         if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
1591             bv = ldap_get_values_len(ld, entry, "sudoOption");
1592             if (bv != NULL) {
1593                 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
1594                     prefix = "    ";
1595                 else
1596                     prefix = ", ";
1597                 for (p = bv; *p != NULL; p++) {
1598                     lbuf_append(lbuf, "%s%s", prefix, (*p)->bv_val);
1599                     prefix = ", ";
1600                     count++;
1601                 }
1602                 ldap_value_free_len(bv);
1603             }
1604         }
1605         if (result)
1606             ldap_msgfree(result);
1607     }
1608     efree(filt);
1609 done:
1610     debug_return_int(count);
1611 }
1612
1613 /*
1614  * STUB
1615  */
1616 static int
1617 sudo_ldap_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
1618     struct lbuf *lbuf)
1619 {
1620     debug_decl(sudo_ldap_display_bound_defaults, SUDO_DEBUG_LDAP)
1621     debug_return_int(0);
1622 }
1623
1624 /*
1625  * Print a record in the short form, ala file sudoers.
1626  */
1627 static int
1628 sudo_ldap_display_entry_short(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
1629 {
1630     struct berval **bv, **p;
1631     int count = 0;
1632     debug_decl(sudo_ldap_display_entry_short, SUDO_DEBUG_LDAP)
1633
1634     lbuf_append(lbuf, "    (");
1635
1636     /* get the RunAsUser Values from the entry */
1637     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1638     if (bv == NULL)
1639         bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1640     if (bv != NULL) {
1641         for (p = bv; *p != NULL; p++) {
1642             lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1643         }
1644         ldap_value_free_len(bv);
1645     } else
1646         lbuf_append(lbuf, "%s", def_runas_default);
1647
1648     /* get the RunAsGroup Values from the entry */
1649     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1650     if (bv != NULL) {
1651         lbuf_append(lbuf, " : ");
1652         for (p = bv; *p != NULL; p++) {
1653             lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1654         }
1655         ldap_value_free_len(bv);
1656     }
1657     lbuf_append(lbuf, ") ");
1658
1659     /* get the Option Values from the entry */
1660     bv = ldap_get_values_len(ld, entry, "sudoOption");
1661     if (bv != NULL) {
1662         for (p = bv; *p != NULL; p++) {
1663             char *cp = (*p)->bv_val;
1664             if (*cp == '!')
1665                 cp++;
1666             if (strcmp(cp, "authenticate") == 0)
1667                 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1668                     "NOPASSWD: " : "PASSWD: ");
1669             else if (strcmp(cp, "noexec") == 0)
1670                 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1671                     "EXEC: " : "NOEXEC: ");
1672             else if (strcmp(cp, "setenv") == 0)
1673                 lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
1674                     "NOSETENV: " : "SETENV: ");
1675         }
1676         ldap_value_free_len(bv);
1677     }
1678
1679     /* get the Command Values from the entry */
1680     bv = ldap_get_values_len(ld, entry, "sudoCommand");
1681     if (bv != NULL) {
1682         for (p = bv; *p != NULL; p++) {
1683             lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1684             count++;
1685         }
1686         ldap_value_free_len(bv);
1687     }
1688     lbuf_append(lbuf, "\n");
1689
1690     debug_return_int(count);
1691 }
1692
1693 /*
1694  * Print a record in the long form.
1695  */
1696 static int
1697 sudo_ldap_display_entry_long(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
1698 {
1699     struct berval **bv, **p;
1700     char *rdn;
1701     int count = 0;
1702     debug_decl(sudo_ldap_display_entry_long, SUDO_DEBUG_LDAP)
1703
1704     /* extract the dn, only show the first rdn */
1705     rdn = sudo_ldap_get_first_rdn(ld, entry);
1706     if (rdn != NULL)
1707         lbuf_append(lbuf, _("\nLDAP Role: %s\n"), rdn);
1708     else
1709         lbuf_append(lbuf, _("\nLDAP Role: UNKNOWN\n"));
1710     if (rdn)
1711         ldap_memfree(rdn);
1712
1713     /* get the RunAsUser Values from the entry */
1714     lbuf_append(lbuf, "    RunAsUsers: ");
1715     bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
1716     if (bv == NULL)
1717         bv = ldap_get_values_len(ld, entry, "sudoRunAs");
1718     if (bv != NULL) {
1719         for (p = bv; *p != NULL; p++) {
1720             lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1721         }
1722         ldap_value_free_len(bv);
1723     } else
1724         lbuf_append(lbuf, "%s", def_runas_default);
1725     lbuf_append(lbuf, "\n");
1726
1727     /* get the RunAsGroup Values from the entry */
1728     bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
1729     if (bv != NULL) {
1730         lbuf_append(lbuf, "    RunAsGroups: ");
1731         for (p = bv; *p != NULL; p++) {
1732             lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1733         }
1734         ldap_value_free_len(bv);
1735         lbuf_append(lbuf, "\n");
1736     }
1737
1738     /* get the Option Values from the entry */
1739     bv = ldap_get_values_len(ld, entry, "sudoOption");
1740     if (bv != NULL) {
1741         lbuf_append(lbuf, "    Options: ");
1742         for (p = bv; *p != NULL; p++) {
1743             lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
1744         }
1745         ldap_value_free_len(bv);
1746         lbuf_append(lbuf, "\n");
1747     }
1748
1749     /*
1750      * Display order attribute if present.  This attribute is single valued,
1751      * so there is no need for a loop.
1752      */
1753     bv = ldap_get_values_len(ld, entry, "sudoOrder");
1754     if (bv != NULL) {
1755         if (*bv != NULL) {
1756             lbuf_append(lbuf, _("    Order: %s\n"), (*bv)->bv_val);
1757         }
1758         ldap_value_free_len(bv);
1759     }
1760
1761     /* Get the command values from the entry. */
1762     bv = ldap_get_values_len(ld, entry, "sudoCommand");
1763     if (bv != NULL) {
1764         lbuf_append(lbuf, _("    Commands:\n"));
1765         for (p = bv; *p != NULL; p++) {
1766             lbuf_append(lbuf, "\t%s\n", (*p)->bv_val);
1767             count++;
1768         }
1769         ldap_value_free_len(bv);
1770     }
1771
1772     debug_return_int(count);
1773 }
1774
1775 /*
1776  * Like sudo_ldap_lookup(), except we just print entries.
1777  */
1778 static int
1779 sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
1780     struct lbuf *lbuf)
1781 {
1782     struct sudo_ldap_handle *handle = nss->handle;
1783     LDAP *ld;
1784     struct ldap_result *lres;
1785     LDAPMessage *entry;
1786     int i, count = 0;
1787     debug_decl(sudo_ldap_display_privs, SUDO_DEBUG_LDAP)
1788
1789     if (handle == NULL || handle->ld == NULL)
1790         goto done;
1791     ld = handle->ld;
1792
1793     DPRINTF(("ldap search for command list"), 1);
1794     lres = sudo_ldap_result_get(nss, pw);
1795
1796     /* Display all matching entries. */
1797     for (i = 0; i < lres->nentries; i++) {
1798         entry = lres->entries[i].entry;
1799         if (long_list)
1800             count += sudo_ldap_display_entry_long(ld, entry, lbuf);
1801         else
1802             count += sudo_ldap_display_entry_short(ld, entry, lbuf);
1803     }
1804
1805 done:
1806     debug_return_int(count);
1807 }
1808
1809 static int
1810 sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
1811 {
1812     struct sudo_ldap_handle *handle = nss->handle;
1813     LDAP *ld;
1814     struct ldap_result *lres;
1815     LDAPMessage *entry;
1816     bool found = false;
1817     int i;
1818     debug_decl(sudo_ldap_display_cmnd, SUDO_DEBUG_LDAP)
1819
1820     if (handle == NULL || handle->ld == NULL)
1821         goto done;
1822     ld = handle->ld;
1823
1824     /*
1825      * The sudo_ldap_result_get() function returns all nodes that match
1826      * the user and the host.
1827      */
1828     DPRINTF(("ldap search for command list"), 1);
1829     lres = sudo_ldap_result_get(nss, pw);
1830     for (i = 0; i < lres->nentries; i++) {
1831         entry = lres->entries[i].entry;
1832         if (sudo_ldap_check_command(ld, entry, NULL) &&
1833             sudo_ldap_check_runas(ld, entry)) {
1834             found = true;
1835             goto done;
1836         }
1837     }
1838
1839 done:
1840     if (found)
1841         printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
1842             user_args ? " " : "", user_args ? user_args : "");
1843    debug_return_bool(!found);
1844 }
1845
1846 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
1847 static int
1848 sudo_ldap_sasl_interact(LDAP *ld, unsigned int flags, void *_auth_id,
1849     void *_interact)
1850 {
1851     char *auth_id = (char *)_auth_id;
1852     sasl_interact_t *interact = (sasl_interact_t *)_interact;
1853     debug_decl(sudo_ldap_sasl_interact, SUDO_DEBUG_LDAP)
1854
1855     for (; interact->id != SASL_CB_LIST_END; interact++) {
1856         if (interact->id != SASL_CB_USER)
1857             debug_return_int(LDAP_PARAM_ERROR);
1858
1859         if (auth_id != NULL)
1860             interact->result = auth_id;
1861         else if (interact->defresult != NULL)
1862             interact->result = interact->defresult;
1863         else
1864             interact->result = "";
1865
1866         interact->len = strlen(interact->result);
1867 #if SASL_VERSION_MAJOR < 2
1868         interact->result = estrdup(interact->result);
1869 #endif /* SASL_VERSION_MAJOR < 2 */
1870     }
1871     debug_return_int(LDAP_SUCCESS);
1872 }
1873 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1874
1875 /*
1876  * Set LDAP options based on the config table.
1877  */
1878 static int
1879 sudo_ldap_set_options(LDAP *ld)
1880 {
1881     struct ldap_config_table *cur;
1882     int rc;
1883     debug_decl(sudo_ldap_set_options, SUDO_DEBUG_LDAP)
1884
1885     /* Set ber options */
1886 #ifdef LBER_OPT_DEBUG_LEVEL
1887     if (ldap_conf.ldap_debug)
1888         ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
1889 #endif
1890
1891     /* Set simple LDAP options */
1892     for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
1893         LDAP *conn;
1894         int ival;
1895         char *sval;
1896
1897         if (cur->opt_val == -1)
1898             continue;
1899
1900         conn = cur->connected ? ld : NULL;
1901         switch (cur->type) {
1902         case CONF_BOOL:
1903         case CONF_INT:
1904             ival = *(int *)(cur->valp);
1905             if (ival >= 0) {
1906                 rc = ldap_set_option(conn, cur->opt_val, &ival);
1907                 if (rc != LDAP_OPT_SUCCESS) {
1908                     warningx("ldap_set_option: %s -> %d: %s",
1909                         cur->conf_str, ival, ldap_err2string(rc));
1910                     debug_return_int(-1);
1911                 }
1912                 DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
1913             }
1914             break;
1915         case CONF_STR:
1916             sval = *(char **)(cur->valp);
1917             if (sval != NULL) {
1918                 rc = ldap_set_option(conn, cur->opt_val, sval);
1919                 if (rc != LDAP_OPT_SUCCESS) {
1920                     warningx("ldap_set_option: %s -> %s: %s",
1921                         cur->conf_str, sval, ldap_err2string(rc));
1922                     debug_return_int(-1);
1923                 }
1924                 DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
1925             }
1926             break;
1927         }
1928     }
1929
1930 #ifdef LDAP_OPT_TIMEOUT
1931     /* Convert timeout to a timeval */
1932     if (ldap_conf.timeout > 0) {
1933         struct timeval tv;
1934         tv.tv_sec = ldap_conf.timeout;
1935         tv.tv_usec = 0;
1936         rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
1937         if (rc != LDAP_OPT_SUCCESS) {
1938             warningx("ldap_set_option(TIMEOUT, %ld): %s",
1939                 (long)tv.tv_sec, ldap_err2string(rc));
1940             debug_return_int(-1);
1941         }
1942         DPRINTF(("ldap_set_option(LDAP_OPT_TIMEOUT, %ld)",
1943             (long)tv.tv_sec), 1);
1944     }
1945 #endif
1946 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1947     /* Convert bind_timelimit to a timeval */
1948     if (ldap_conf.bind_timelimit > 0) {
1949         struct timeval tv;
1950         tv.tv_sec = ldap_conf.bind_timelimit / 1000;
1951         tv.tv_usec = 0;
1952         rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
1953         if (rc != LDAP_OPT_SUCCESS) {
1954             warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
1955                 (long)tv.tv_sec, ldap_err2string(rc));
1956             debug_return_int(-1);
1957         }
1958         DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)",
1959             (long)tv.tv_sec), 1);
1960     }
1961 #endif
1962
1963 #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
1964     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
1965         int val = LDAP_OPT_X_TLS_HARD;
1966         rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
1967         if (rc != LDAP_SUCCESS) {
1968             warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
1969                 ldap_err2string(rc));
1970             debug_return_int(-1);
1971         }
1972         DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)"), 1);
1973     }
1974 #endif
1975     debug_return_int(0);
1976 }
1977
1978 /*
1979  * Create a new sudo_ldap_result structure.
1980  */
1981 static struct ldap_result *
1982 sudo_ldap_result_alloc(void)
1983 {
1984     struct ldap_result *result;
1985     debug_decl(sudo_ldap_result_alloc, SUDO_DEBUG_LDAP)
1986
1987     result = emalloc(sizeof(*result));
1988     result->searches = NULL;
1989     result->nentries = 0;
1990     result->entries = NULL;
1991     result->allocated_entries = 0;
1992     result->user_matches = false;
1993     result->host_matches = false;
1994     debug_return_ptr(result);
1995 }
1996
1997 /*
1998  * Free the ldap result structure
1999  */
2000 static void
2001 sudo_ldap_result_free(struct ldap_result *lres)
2002 {
2003     struct ldap_search_list *s;
2004     debug_decl(sudo_ldap_result_free, SUDO_DEBUG_LDAP)
2005
2006     if (lres != NULL) {
2007         if (lres->nentries) {
2008             efree(lres->entries);
2009             lres->entries = NULL;
2010         }
2011         if (lres->searches) {
2012             while ((s = lres->searches) != NULL) {
2013                 ldap_msgfree(s->searchresult);
2014                 lres->searches = s->next;
2015                 efree(s);
2016             }
2017         }
2018         efree(lres);
2019     }
2020     debug_return;
2021 }
2022
2023 /*
2024  * Add a search result to the ldap_result structure.
2025  */
2026 static struct ldap_search_list *
2027 sudo_ldap_result_add_search(struct ldap_result *lres, LDAP *ldap,
2028     LDAPMessage *searchresult)
2029 {
2030     struct ldap_search_list *s, *news;
2031     debug_decl(sudo_ldap_result_add_search, SUDO_DEBUG_LDAP)
2032
2033     news = emalloc(sizeof(struct ldap_search_list));
2034     news->next = NULL;
2035     news->ldap = ldap;
2036     news->searchresult = searchresult;
2037
2038     /* Add entry to the end of the chain (XXX - tailq instead?). */
2039     if (lres->searches) {
2040         for (s = lres->searches; s->next != NULL; s = s->next)
2041             continue;
2042         s->next = news;
2043     } else {
2044         lres->searches = news;
2045     }
2046     debug_return_ptr(news);
2047 }
2048
2049 /*
2050  * Connect to the LDAP server specified by ld
2051  */
2052 static int
2053 sudo_ldap_bind_s(LDAP *ld)
2054 {
2055     int rc;
2056 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
2057     const char *old_ccname = user_ccname;
2058 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
2059     unsigned int status;
2060 # endif
2061 #endif
2062     debug_decl(sudo_ldap_bind_s, SUDO_DEBUG_LDAP)
2063
2064 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
2065     if (ldap_conf.rootuse_sasl == true ||
2066         (ldap_conf.rootuse_sasl != false && ldap_conf.use_sasl == true)) {
2067         void *auth_id = ldap_conf.rootsasl_auth_id ?
2068             ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
2069
2070         if (ldap_conf.krb5_ccname != NULL) {
2071 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
2072             if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
2073                 != GSS_S_COMPLETE) {
2074                 old_ccname = NULL;
2075                 DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
2076             }
2077 # else
2078             setenv("KRB5CCNAME", ldap_conf.krb5_ccname, true);
2079 # endif
2080         }
2081         rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
2082             NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
2083         if (ldap_conf.krb5_ccname != NULL) {
2084 # ifdef HAVE_GSS_KRB5_CCACHE_NAME
2085             if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
2086                     DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
2087 # else
2088             if (old_ccname != NULL)
2089                 setenv("KRB5CCNAME", old_ccname, true);
2090             else
2091                 unsetenv("KRB5CCNAME");
2092 # endif
2093         }
2094         if (rc != LDAP_SUCCESS) {
2095             warningx("ldap_sasl_interactive_bind_s(): %s",
2096                 ldap_err2string(rc));
2097             debug_return_int(-1);
2098         }
2099         DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
2100     } else
2101 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
2102 #ifdef HAVE_LDAP_SASL_BIND_S
2103     {
2104         struct berval bv;
2105
2106         bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
2107         bv.bv_len = strlen(bv.bv_val);
2108
2109         rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
2110             NULL, NULL, NULL);
2111         if (rc != LDAP_SUCCESS) {
2112             warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
2113             debug_return_int(-1);
2114         }
2115         DPRINTF(("ldap_sasl_bind_s() ok"), 1);
2116     }
2117 #else
2118     {
2119         rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
2120         if (rc != LDAP_SUCCESS) {
2121             warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
2122             debug_return_int(-1);
2123         }
2124         DPRINTF(("ldap_simple_bind_s() ok"), 1);
2125     }
2126 #endif
2127     debug_return_int(0);
2128 }
2129
2130 /*
2131  * Open a connection to the LDAP server.
2132  * Returns 0 on success and non-zero on failure.
2133  */
2134 static int
2135 sudo_ldap_open(struct sudo_nss *nss)
2136 {
2137     LDAP *ld;
2138     int rc;
2139     bool ldapnoinit = false;
2140     struct sudo_ldap_handle *handle;
2141     debug_decl(sudo_ldap_open, SUDO_DEBUG_LDAP)
2142
2143     if (!sudo_ldap_read_config())
2144         debug_return_int(-1);
2145
2146     /* Prevent reading of user ldaprc and system defaults. */
2147     if (getenv("LDAPNOINIT") == NULL) {
2148         ldapnoinit = true;
2149         setenv("LDAPNOINIT", "1", true);
2150     }
2151
2152     /* Connect to LDAP server */
2153 #ifdef HAVE_LDAP_INITIALIZE
2154     if (ldap_conf.uri != NULL) {
2155         char *buf = sudo_ldap_join_uri(ldap_conf.uri);
2156         DPRINTF(("ldap_initialize(ld, %s)", buf), 2);
2157         rc = ldap_initialize(&ld, buf);
2158         efree(buf);
2159     } else
2160 #endif
2161         rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
2162     if (rc != LDAP_SUCCESS) {
2163         warningx(_("unable to initialize LDAP: %s"), ldap_err2string(rc));
2164         debug_return_int(-1);
2165     }
2166
2167     if (ldapnoinit)
2168         unsetenv("LDAPNOINIT");
2169
2170     /* Set LDAP options */
2171     if (sudo_ldap_set_options(ld) < 0)
2172         debug_return_int(-1);
2173
2174     if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
2175 #if defined(HAVE_LDAP_START_TLS_S)
2176         rc = ldap_start_tls_s(ld, NULL, NULL);
2177         if (rc != LDAP_SUCCESS) {
2178             warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
2179             debug_return_int(-1);
2180         }
2181         DPRINTF(("ldap_start_tls_s() ok"), 1);
2182 #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
2183         if (ldap_ssl_client_init(NULL, NULL, 0, &rc) != LDAP_SUCCESS) {
2184             warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
2185             debug_return_int(-1);
2186         }
2187         rc = ldap_start_tls_s_np(ld, NULL);
2188         if (rc != LDAP_SUCCESS) {
2189             warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
2190             debug_return_int(-1);
2191         }
2192         DPRINTF(("ldap_start_tls_s_np() ok"), 1);
2193 #else
2194         warningx(_("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()"));
2195 #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
2196     }
2197
2198     /* Actually connect */
2199     if (sudo_ldap_bind_s(ld) != 0)
2200         debug_return_int(-1);
2201
2202     /* Create a handle container. */
2203     handle = emalloc(sizeof(struct sudo_ldap_handle));
2204     handle->ld = ld;
2205     handle->result = NULL;
2206     handle->username = NULL;
2207     handle->grlist = NULL;
2208     nss->handle = handle;
2209
2210     debug_return_int(0);
2211 }
2212
2213 static int
2214 sudo_ldap_setdefs(struct sudo_nss *nss)
2215 {
2216     struct ldap_config_list_str *base;
2217     struct sudo_ldap_handle *handle = nss->handle;
2218     struct timeval tv, *tvp = NULL;
2219     LDAP *ld;
2220     LDAPMessage *entry, *result;
2221     char *filt;
2222     int rc;
2223     debug_decl(sudo_ldap_setdefs, SUDO_DEBUG_LDAP)
2224
2225     if (handle == NULL || handle->ld == NULL)
2226         debug_return_int(-1);
2227     ld = handle->ld;
2228
2229     filt = sudo_ldap_build_default_filter();
2230     DPRINTF(("Looking for cn=defaults: %s", filt), 1);
2231
2232     for (base = ldap_conf.base; base != NULL; base = base->next) {
2233         if (ldap_conf.timeout > 0) {
2234             tv.tv_sec = ldap_conf.timeout;
2235             tv.tv_usec = 0;
2236             tvp = &tv;
2237         }
2238         result = NULL;
2239         rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
2240             filt, NULL, 0, NULL, NULL, tvp, 0, &result);
2241         if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
2242             DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
2243             sudo_ldap_parse_options(ld, entry);
2244         } else
2245             DPRINTF(("no default options found in %s", base->val), 1);
2246
2247         if (result)
2248             ldap_msgfree(result);
2249     }
2250     efree(filt);
2251
2252     debug_return_int(0);
2253 }
2254
2255 /*
2256  * like sudoers_lookup() - only LDAP style
2257  */
2258 static int
2259 sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
2260 {
2261     struct sudo_ldap_handle *handle = nss->handle;
2262     LDAP *ld;
2263     LDAPMessage *entry;
2264     int i, rc, setenv_implied;
2265     struct ldap_result *lres = NULL;
2266     debug_decl(sudo_ldap_lookup, SUDO_DEBUG_LDAP)
2267
2268     if (handle == NULL || handle->ld == NULL)
2269         debug_return_int(ret);
2270     ld = handle->ld;
2271
2272     /* Fetch list of sudoRole entries that match user and host. */
2273     lres = sudo_ldap_result_get(nss, sudo_user.pw);
2274
2275     /*
2276      * The following queries are only determine whether or not a
2277      * password is required, so the order of the entries doesn't matter.
2278      */
2279     if (pwflag) {
2280         int doauth = UNSPEC;
2281         int matched = UNSPEC;
2282         enum def_tuple pwcheck = 
2283             (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
2284
2285         DPRINTF(("perform search for pwflag %d", pwflag), 1);
2286         for (i = 0; i < lres->nentries; i++) {
2287             entry = lres->entries[i].entry;
2288             if ((pwcheck == any && doauth != false) ||
2289                 (pwcheck == all && doauth == false)) {
2290                 doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
2291             }
2292             /* Only check the command when listing another user. */
2293             if (user_uid == 0 || list_pw == NULL ||
2294                 user_uid == list_pw->pw_uid ||
2295                 sudo_ldap_check_command(ld, entry, NULL)) {
2296                 matched = true;
2297                 break;
2298             }
2299         }
2300         if (matched || user_uid == 0) {
2301             SET(ret, VALIDATE_OK);
2302             CLR(ret, VALIDATE_NOT_OK);
2303             if (def_authenticate) {
2304                 switch (pwcheck) {
2305                     case always:
2306                         SET(ret, FLAG_CHECK_USER);
2307                         break;
2308                     case all:
2309                     case any:
2310                         if (doauth == false)
2311                             def_authenticate = false;
2312                         break;
2313                     case never:
2314                         def_authenticate = false;
2315                         break;
2316                     default:
2317                         break;
2318                 }
2319             }
2320         }
2321         goto done;
2322     }
2323
2324     DPRINTF(("searching LDAP for sudoers entries"), 1);
2325
2326     setenv_implied = false;
2327     for (i = 0; i < lres->nentries; i++) {
2328         entry = lres->entries[i].entry;
2329         if (!sudo_ldap_check_runas(ld, entry))
2330             continue;
2331         rc = sudo_ldap_check_command(ld, entry, &setenv_implied);
2332         if (rc != UNSPEC) {
2333             /* We have a match. */
2334             DPRINTF(("Command %sallowed", rc == true ? "" : "NOT "), 1);
2335             if (rc == true) {
2336                 DPRINTF(("LDAP entry: %p", entry), 1);
2337                 /* Apply entry-specific options. */
2338                 if (setenv_implied)
2339                     def_setenv = true;
2340                 sudo_ldap_parse_options(ld, entry);
2341 #ifdef HAVE_SELINUX
2342                 /* Set role and type if not specified on command line. */
2343                 if (user_role == NULL)
2344                     user_role = def_role;
2345                 if (user_type == NULL)
2346                     user_type = def_type;
2347 #endif /* HAVE_SELINUX */
2348                 SET(ret, VALIDATE_OK);
2349                 CLR(ret, VALIDATE_NOT_OK);
2350             } else {
2351                 SET(ret, VALIDATE_NOT_OK);
2352                 CLR(ret, VALIDATE_OK);
2353             }
2354             break;
2355         }
2356     }
2357
2358 done:
2359     DPRINTF(("done with LDAP searches"), 1);
2360     DPRINTF(("user_matches=%d", lres->user_matches), 1);
2361     DPRINTF(("host_matches=%d", lres->host_matches), 1);
2362
2363     if (!ISSET(ret, VALIDATE_OK)) {
2364         /* No matching entries. */
2365         if (pwflag && list_pw == NULL)
2366             SET(ret, FLAG_NO_CHECK);
2367     }
2368     if (lres->user_matches)
2369         CLR(ret, FLAG_NO_USER);
2370     if (lres->host_matches)
2371         CLR(ret, FLAG_NO_HOST);
2372     DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
2373
2374     debug_return_int(ret);
2375 }
2376
2377 /*
2378  * Comparison function for ldap_entry_wrapper structures, descending order.
2379  */
2380 static int
2381 ldap_entry_compare(const void *a, const void *b)
2382 {
2383     const struct ldap_entry_wrapper *aw = a;
2384     const struct ldap_entry_wrapper *bw = b;
2385     debug_decl(ldap_entry_compare, SUDO_DEBUG_LDAP)
2386
2387     debug_return_int(bw->order < aw->order ? -1 :
2388         (bw->order > aw->order ? 1 : 0));
2389 }
2390
2391 /*
2392  * Find the last entry in the list of searches, usually the
2393  * one currently being used to add entries.
2394  * XXX - use a tailq instead?
2395  */
2396 static struct ldap_search_list *
2397 sudo_ldap_result_last_search(struct ldap_result *lres)
2398 {
2399     struct ldap_search_list *result = lres->searches;
2400     debug_decl(sudo_ldap_result_last_search, SUDO_DEBUG_LDAP)
2401
2402     if (result) {
2403         while (result->next)
2404             result = result->next;
2405     }
2406     debug_return_ptr(result);
2407 }
2408
2409 /*
2410  * Add an entry to the result structure.
2411  */
2412 static struct ldap_entry_wrapper *
2413 sudo_ldap_result_add_entry(struct ldap_result *lres, LDAPMessage *entry)
2414 {
2415     struct ldap_search_list *last;
2416     struct berval **bv;
2417     double order = 0.0;
2418     char *ep;
2419     debug_decl(sudo_ldap_result_add_entry, SUDO_DEBUG_LDAP)
2420
2421     /* Determine whether the entry has the sudoOrder attribute. */
2422     last = sudo_ldap_result_last_search(lres);
2423     bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
2424     if (bv != NULL) {
2425         if (ldap_count_values_len(bv) > 0) {
2426             /* Get the value of this attribute, 0 if not present. */
2427             DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
2428             order = strtod((*bv)->bv_val, &ep);
2429             if (ep == (*bv)->bv_val || *ep != '\0') {
2430                 warningx(_("invalid sudoOrder attribute: %s"), (*bv)->bv_val);
2431                 order = 0.0;
2432             }
2433             DPRINTF(("order attribute: %f", order), 1);
2434         }
2435         ldap_value_free_len(bv);
2436     }
2437
2438     /*
2439      * Enlarge the array of entry wrappers as needed, preallocating blocks
2440      * of 100 entries to save on allocation time.
2441      */
2442     if (++lres->nentries > lres->allocated_entries) {
2443         lres->allocated_entries += ALLOCATION_INCREMENT;
2444         lres->entries = erealloc3(lres->entries, lres->allocated_entries,
2445             sizeof(lres->entries[0]));
2446     }
2447
2448     /* Fill in the new entry and return it. */
2449     lres->entries[lres->nentries - 1].entry = entry;
2450     lres->entries[lres->nentries - 1].order = order;
2451
2452     debug_return_ptr(&lres->entries[lres->nentries - 1]);
2453 }
2454
2455 /*
2456  * Free the ldap result structure in the sudo_nss handle.
2457  */
2458 static void
2459 sudo_ldap_result_free_nss(struct sudo_nss *nss)
2460 {
2461     struct sudo_ldap_handle *handle = nss->handle;
2462     debug_decl(sudo_ldap_result_free_nss, SUDO_DEBUG_LDAP)
2463
2464     if (handle->result != NULL) {
2465         DPRINTF(("removing reusable search result"), 1);
2466         sudo_ldap_result_free(handle->result);
2467         if (handle->username) {
2468             efree(handle->username);
2469             handle->username = NULL;
2470         }
2471         handle->grlist = NULL;
2472         handle->result = NULL;
2473     }
2474     debug_return;
2475 }
2476
2477 /*
2478  * Perform the LDAP query for the user or return a cached query if
2479  * there is one for this user.
2480  */
2481 static struct ldap_result *
2482 sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw)
2483 {
2484     struct sudo_ldap_handle *handle = nss->handle;
2485     struct ldap_config_list_str *base;
2486     struct ldap_result *lres;
2487     struct timeval tv, *tvp = NULL;
2488     LDAPMessage *entry, *result;
2489     LDAP *ld = handle->ld;
2490     int do_netgr, rc;
2491     char *filt;
2492     debug_decl(sudo_ldap_result_get, SUDO_DEBUG_LDAP)
2493
2494     /*
2495      * If we already have a cached result, return it so we don't have to
2496      * have to contact the LDAP server again.
2497      */
2498     if (handle->result) {
2499         if (handle->grlist == user_group_list &&
2500             strcmp(pw->pw_name, handle->username) == 0) {
2501             DPRINTF(("reusing previous result (user %s) with %d entries",
2502                 handle->username, handle->result->nentries), 1);
2503             debug_return_ptr(handle->result);
2504         }
2505         /* User mismatch, cached result cannot be used. */
2506         DPRINTF(("removing result (user %s), new search (user %s)",
2507             handle->username, pw->pw_name), 1);
2508         sudo_ldap_result_free_nss(nss);
2509     }
2510
2511     /*
2512      * Okay - time to search for anything that matches this user
2513      * Lets limit it to only two queries of the LDAP server
2514      *
2515      * The first pass will look by the username, groups, and
2516      * the keyword ALL.  We will then inspect the results that
2517      * came back from the query.  We don't need to inspect the
2518      * sudoUser in this pass since the LDAP server already scanned
2519      * it for us.
2520      *
2521      * The second pass will return all the entries that contain
2522      * user netgroups.  Then we take the netgroups returned and
2523      * try to match them against the username.
2524      *
2525      * Since we have to sort the possible entries before we make a
2526      * decision, we perform the queries and store all of the results in
2527      * an ldap_result object.  The results are then sorted by sudoOrder.
2528      */
2529     lres = sudo_ldap_result_alloc();
2530     for (do_netgr = 0; do_netgr < 2; do_netgr++) {
2531         filt = do_netgr ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
2532         DPRINTF(("ldap search '%s'", filt), 1);
2533         for (base = ldap_conf.base; base != NULL; base = base->next) {
2534             DPRINTF(("searching from base '%s'", base->val), 1);
2535             if (ldap_conf.timeout > 0) {
2536                 tv.tv_sec = ldap_conf.timeout;
2537                 tv.tv_usec = 0;
2538                 tvp = &tv;
2539             }
2540             result = NULL;
2541             rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
2542                 NULL, 0, NULL, NULL, tvp, 0, &result);
2543             if (rc != LDAP_SUCCESS) {
2544                 DPRINTF(("nothing found for '%s'", filt), 1);
2545                 continue;
2546             }
2547             lres->user_matches = true;
2548
2549             /* Add the seach result to list of search results. */
2550             DPRINTF(("adding search result"), 1);
2551             sudo_ldap_result_add_search(lres, ld, result);
2552             LDAP_FOREACH(entry, ld, result) {
2553                 if ((!do_netgr ||
2554                     sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
2555                     sudo_ldap_check_host(ld, entry)) {
2556                     lres->host_matches = true;
2557                     sudo_ldap_result_add_entry(lres, entry);
2558                 }
2559             }
2560             DPRINTF(("result now has %d entries", lres->nentries), 1);
2561         }
2562         efree(filt);
2563     }
2564
2565     /* Sort the entries by the sudoOrder attribute. */
2566     DPRINTF(("sorting remaining %d entries", lres->nentries), 1);
2567     qsort(lres->entries, lres->nentries, sizeof(lres->entries[0]),
2568         ldap_entry_compare);
2569
2570     /* Store everything in the sudo_nss handle. */
2571     handle->result = lres;
2572     handle->username = estrdup(pw->pw_name);
2573     handle->grlist = user_group_list;
2574
2575     debug_return_ptr(lres);
2576 }
2577
2578 /*
2579  * Shut down the LDAP connection.
2580  */
2581 static int
2582 sudo_ldap_close(struct sudo_nss *nss)
2583 {
2584     struct sudo_ldap_handle *handle = nss->handle;
2585     debug_decl(sudo_ldap_close, SUDO_DEBUG_LDAP)
2586
2587     if (handle != NULL) {
2588         /* Free the result before unbinding; it may use the LDAP connection. */
2589         sudo_ldap_result_free_nss(nss);
2590
2591         /* Unbind and close the LDAP connection. */
2592         if (handle->ld != NULL) {
2593             ldap_unbind_ext_s(handle->ld, NULL, NULL);
2594             handle->ld = NULL;
2595         }
2596
2597         /* Free the handle container. */
2598         efree(nss->handle);
2599         nss->handle = NULL;
2600     }
2601     debug_return_int(0);
2602 }
2603
2604 /*
2605  * STUB
2606  */
2607 static int
2608 sudo_ldap_parse(struct sudo_nss *nss)
2609 {
2610     return 0;
2611 }
2612
2613 #if 0
2614 /*
2615  * Create an ldap_result from an LDAP search result.
2616  *
2617  * This function is currently not used anywhere, it is left here as
2618  * an example of how to use the cached searches.
2619  */
2620 static struct ldap_result *
2621 sudo_ldap_result_from_search(LDAP *ldap, LDAPMessage *searchresult)
2622 {
2623     /*
2624      * An ldap_result is built from several search results, which are
2625      * organized in a list. The head of the list is maintained in the
2626      * ldap_result structure, together with the wrappers that point
2627      * to individual entries, this has to be initialized first.
2628      */
2629     struct ldap_result *result = sudo_ldap_result_alloc();
2630
2631     /*
2632      * Build a new list node for the search result, this creates the
2633      * list node.
2634      */
2635     struct ldap_search_list *last = sudo_ldap_result_add_search(result,
2636         ldap, searchresult);
2637
2638     /*
2639      * Now add each entry in the search result to the array of of entries
2640      * in the ldap_result object.
2641      */
2642     LDAPMessage *entry;
2643     LDAP_FOREACH(entry, last->ldap, last->searchresult) {
2644         sudo_ldap_result_add_entry(result, entry);
2645     }
2646     DPRINTF(("sudo_ldap_result_from_search: %d entries found",
2647         result->nentries), 2);
2648     return result;
2649 }
2650 #endif