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