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