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