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