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