1 /*
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (c) 2003-2020 Todd C. Miller <Todd.Miller@sudo.ws>
5  *
6  * This code is derived from software contributed by Aaron Spangler.
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 /*
22  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
23  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
24  */
25 
26 #include <config.h>
27 
28 #include <sys/time.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_STRINGS_H
33 # include <strings.h>
34 #endif /* HAVE_STRINGS_H */
35 #include <unistd.h>
36 #include <ctype.h>
37 #include <fcntl.h>
38 #ifdef HAVE_LBER_H
39 # include <lber.h>
40 #endif
41 #include <ldap.h>
42 #if defined(HAVE_LDAP_SSL_H)
43 # include <ldap_ssl.h>
44 #elif defined(HAVE_MPS_LDAP_SSL_H)
45 # include <mps/ldap_ssl.h>
46 #endif
47 
48 #include "sudoers.h"
49 #include "sudo_lbuf.h"
50 #include "sudo_ldap.h"
51 #include "sudo_ldap_conf.h"
52 
53 /* Older Netscape LDAP SDKs don't prototype ldapssl_set_strength() */
54 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(HAVE_LDAP_SSL_H) && !defined(HAVE_MPS_LDAP_SSL_H)
55 extern int ldapssl_set_strength(LDAP *ldap, int strength);
56 #endif
57 
58 #if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
59 # define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
60 #endif
61 
62 #ifndef LDAP_OPT_SUCCESS
63 # define LDAP_OPT_SUCCESS LDAP_SUCCESS
64 #endif
65 
66 #ifndef LDAPS_PORT
67 # define LDAPS_PORT 636
68 #endif
69 
70 /* Default search filter. */
71 #define DEFAULT_SEARCH_FILTER	"(objectClass=sudoRole)"
72 
73 /* Default netgroup search filter. */
74 #define DEFAULT_NETGROUP_SEARCH_FILTER	"(objectClass=nisNetgroup)"
75 
76 /* LDAP configuration structure */
77 struct ldap_config ldap_conf;
78 
79 static struct ldap_config_table ldap_conf_global[] = {
80     { "sudoers_debug", CONF_INT, -1, &ldap_conf.debug },
81     { "host", CONF_STR, -1, &ldap_conf.host },
82     { "port", CONF_INT, -1, &ldap_conf.port },
83     { "ssl", CONF_STR, -1, &ldap_conf.ssl },
84     { "sslpath", CONF_STR, -1, &ldap_conf.tls_certfile },
85     { "uri", CONF_LIST_STR, -1, &ldap_conf.uri },
86 #ifdef LDAP_OPT_DEBUG_LEVEL
87     { "debug", CONF_INT, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
88 #endif
89 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
90     { "tls_checkpeer", CONF_BOOL, LDAP_OPT_X_TLS_REQUIRE_CERT,
91 	&ldap_conf.tls_checkpeer },
92     { "tls_reqcert", CONF_REQCERT_VAL, LDAP_OPT_X_TLS_REQUIRE_CERT,
93 	&ldap_conf.tls_reqcert },
94 #else
95     { "tls_checkpeer", CONF_BOOL, -1, &ldap_conf.tls_checkpeer },
96 #endif
97 #ifdef LDAP_OPT_X_TLS_CACERTFILE
98     { "tls_cacertfile", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
99 	&ldap_conf.tls_cacertfile },
100     { "tls_cacert", CONF_STR, LDAP_OPT_X_TLS_CACERTFILE,
101 	&ldap_conf.tls_cacertfile },
102 #endif
103 #ifdef LDAP_OPT_X_TLS_CACERTDIR
104     { "tls_cacertdir", CONF_STR, LDAP_OPT_X_TLS_CACERTDIR,
105 	&ldap_conf.tls_cacertdir },
106 #endif
107 #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
108     { "tls_randfile", CONF_STR, LDAP_OPT_X_TLS_RANDOM_FILE,
109 	&ldap_conf.tls_random_file },
110 #endif
111 #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
112     { "tls_ciphers", CONF_STR, LDAP_OPT_X_TLS_CIPHER_SUITE,
113 	&ldap_conf.tls_cipher_suite },
114 #elif defined(LDAP_OPT_SSL_CIPHER)
115     { "tls_ciphers", CONF_STR, LDAP_OPT_SSL_CIPHER,
116 	&ldap_conf.tls_cipher_suite },
117 #endif
118 #ifdef LDAP_OPT_X_TLS_CERTFILE
119     { "tls_cert", CONF_STR, LDAP_OPT_X_TLS_CERTFILE,
120 	&ldap_conf.tls_certfile },
121 #else
122     { "tls_cert", CONF_STR, -1, &ldap_conf.tls_certfile },
123 #endif
124 #ifdef LDAP_OPT_X_TLS_KEYFILE
125     { "tls_key", CONF_STR, LDAP_OPT_X_TLS_KEYFILE,
126 	&ldap_conf.tls_keyfile },
127 #else
128     { "tls_key", CONF_STR, -1, &ldap_conf.tls_keyfile },
129 #endif
130 #ifdef HAVE_LDAP_SSL_CLIENT_INIT
131     { "tls_keypw", CONF_STR, -1, &ldap_conf.tls_keypw },
132 #endif
133     { "binddn", CONF_STR, -1, &ldap_conf.binddn },
134     { "bindpw", CONF_STR, -1, &ldap_conf.bindpw },
135     { "rootbinddn", CONF_STR, -1, &ldap_conf.rootbinddn },
136     { "sudoers_base", CONF_LIST_STR, -1, &ldap_conf.base },
137     { "sudoers_timed", CONF_BOOL, -1, &ldap_conf.timed },
138     { "sudoers_search_filter", CONF_STR, -1, &ldap_conf.search_filter },
139     { "netgroup_base", CONF_LIST_STR, -1, &ldap_conf.netgroup_base },
140     { "netgroup_search_filter", CONF_STR, -1, &ldap_conf.netgroup_search_filter },
141 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
142     { "use_sasl", CONF_BOOL, -1, &ldap_conf.use_sasl },
143     { "sasl_mech", CONF_STR, -1, &ldap_conf.sasl_mech },
144     { "sasl_auth_id", CONF_STR, -1, &ldap_conf.sasl_auth_id },
145     { "rootuse_sasl", CONF_BOOL, -1, &ldap_conf.rootuse_sasl },
146     { "rootsasl_auth_id", CONF_STR, -1, &ldap_conf.rootsasl_auth_id },
147     { "krb5_ccname", CONF_STR, -1, &ldap_conf.krb5_ccname },
148 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
149     { NULL }
150 };
151 
152 static struct ldap_config_table ldap_conf_conn[] = {
153 #ifdef LDAP_OPT_PROTOCOL_VERSION
154     { "ldap_version", CONF_INT, LDAP_OPT_PROTOCOL_VERSION,
155 	&ldap_conf.version },
156 #endif
157 #ifdef LDAP_OPT_NETWORK_TIMEOUT
158     { "bind_timelimit", CONF_INT, -1 /* needs timeval, set manually */,
159 	&ldap_conf.bind_timelimit },
160     { "network_timeout", CONF_INT, -1 /* needs timeval, set manually */,
161 	&ldap_conf.bind_timelimit },
162 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
163     { "bind_timelimit", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
164 	&ldap_conf.bind_timelimit },
165     { "network_timeout", CONF_INT, LDAP_X_OPT_CONNECT_TIMEOUT,
166 	&ldap_conf.bind_timelimit },
167 #endif
168     { "timelimit", CONF_INT, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
169 #ifdef LDAP_OPT_TIMEOUT
170     { "timeout", CONF_INT, -1 /* needs timeval, set manually */,
171 	&ldap_conf.timeout },
172 #endif
173 #ifdef LDAP_OPT_DEREF
174     { "deref", CONF_DEREF_VAL, LDAP_OPT_DEREF, &ldap_conf.deref },
175 #endif
176 #ifdef LDAP_OPT_X_SASL_SECPROPS
177     { "sasl_secprops", CONF_STR, LDAP_OPT_X_SASL_SECPROPS,
178 	&ldap_conf.sasl_secprops },
179 #endif
180     { NULL }
181 };
182 
183 #ifdef HAVE_LDAP_CREATE
184 /*
185  * Rebuild the hosts list and include a specific port for each host.
186  * ldap_create() does not take a default port parameter so we must
187  * append one if we want something other than LDAP_PORT.
188  */
189 static bool
sudo_ldap_conf_add_ports(void)190 sudo_ldap_conf_add_ports(void)
191 {
192     char *host, *last, *port, defport[13];
193     char hostbuf[LINE_MAX * 2];
194     int len;
195     debug_decl(sudo_ldap_conf_add_ports, SUDOERS_DEBUG_LDAP);
196 
197     hostbuf[0] = '\0';
198     len = snprintf(defport, sizeof(defport), ":%d", ldap_conf.port);
199     if (len < 0 || len >= ssizeof(defport)) {
200 	sudo_warnx(U_("%s: port too large"), __func__);
201 	debug_return_bool(false);
202     }
203 
204     for ((host = strtok_r(ldap_conf.host, " \t", &last)); host; (host = strtok_r(NULL, " \t", &last))) {
205 	if (hostbuf[0] != '\0')
206 	    CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
207 	CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
208 
209 	/* Append port if there is not one already. */
210 	if ((port = strrchr(host, ':')) == NULL ||
211 	    !isdigit((unsigned char)port[1])) {
212 	    CHECK_STRLCAT(hostbuf, defport, sizeof(hostbuf));
213 	}
214     }
215 
216     free(ldap_conf.host);
217     if ((ldap_conf.host = strdup(hostbuf)) == NULL)
218 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
219     debug_return_bool(ldap_conf.host != NULL);
220 
221 overflow:
222     sudo_warnx(U_("internal error, %s overflow"), __func__);
223     debug_return_bool(false);
224 }
225 #endif
226 
227 #ifndef HAVE_LDAP_INITIALIZE
228 /*
229  * For each uri, convert to host:port pairs.  For ldaps:// enable SSL
230  * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
231  * where the trailing slash is optional.
232  * Returns LDAP_SUCCESS on success, else non-zero.
233  */
234 static int
sudo_ldap_parse_uri(const struct ldap_config_str_list * uri_list)235 sudo_ldap_parse_uri(const struct ldap_config_str_list *uri_list)
236 {
237     const struct ldap_config_str *entry;
238     char *buf, hostbuf[LINE_MAX];
239     int nldap = 0, nldaps = 0;
240     int ret = -1;
241     debug_decl(sudo_ldap_parse_uri, SUDOERS_DEBUG_LDAP);
242 
243     hostbuf[0] = '\0';
244     STAILQ_FOREACH(entry, uri_list, entries) {
245 	char *cp, *host, *last, *port, *uri;
246 
247 	buf = strdup(entry->val);
248 	if (buf == NULL) {
249 	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
250 	    goto done;
251 	}
252 	for ((uri = strtok_r(buf, " \t", &last)); uri != NULL; (uri = strtok_r(NULL, " \t", &last))) {
253 	    if (strncasecmp(uri, "ldap://", 7) == 0) {
254 		nldap++;
255 		host = uri + 7;
256 	    } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
257 		nldaps++;
258 		host = uri + 8;
259 	    } else {
260 		sudo_warnx(U_("unsupported LDAP uri type: %s"), uri);
261 		goto done;
262 	    }
263 
264 	    /* trim optional trailing slash */
265 	    if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
266 		*cp = '\0';
267 	    }
268 
269 	    if (hostbuf[0] != '\0')
270 		CHECK_STRLCAT(hostbuf, " ", sizeof(hostbuf));
271 
272 	    if (*host == '\0')
273 		host = "localhost";		/* no host specified, use localhost */
274 
275 	    CHECK_STRLCAT(hostbuf, host, sizeof(hostbuf));
276 
277 	    /* If using SSL and no port specified, add port 636 */
278 	    if (nldaps) {
279 		if ((port = strrchr(host, ':')) == NULL ||
280 		    !isdigit((unsigned char)port[1]))
281 		    CHECK_STRLCAT(hostbuf, ":636", sizeof(hostbuf));
282 	    }
283 	}
284 
285 	if (nldaps != 0) {
286 	    if (nldap != 0) {
287 		sudo_warnx("%s", U_("unable to mix ldap and ldaps URIs"));
288 		goto done;
289 	    }
290 	    if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS)
291 		sudo_warnx("%s", U_("starttls not supported when using ldaps"));
292 	    ldap_conf.ssl_mode = SUDO_LDAP_SSL;
293 	}
294 	free(buf);
295     }
296     buf = NULL;
297 
298     /* Store parsed URI(s) in host for ldap_create() or ldap_init(). */
299     free(ldap_conf.host);
300     if ((ldap_conf.host = strdup(hostbuf)) == NULL) {
301 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
302 	goto done;
303     }
304 
305     ret = LDAP_SUCCESS;
306 
307 done:
308     free(buf);
309     debug_return_int(ret);
310 
311 overflow:
312     sudo_warnx(U_("internal error, %s overflow"), __func__);
313     free(buf);
314     debug_return_int(-1);
315 }
316 #endif /* HAVE_LDAP_INITIALIZE */
317 
318 /*
319  * Decode a secret if it is base64 encoded, else return NULL.
320  */
321 static char *
sudo_ldap_decode_secret(const char * secret)322 sudo_ldap_decode_secret(const char *secret)
323 {
324     unsigned char *result = NULL;
325     size_t len, reslen;
326     debug_decl(sudo_ldap_decode_secret, SUDOERS_DEBUG_LDAP);
327 
328     if (strncasecmp(secret, "base64:", sizeof("base64:") - 1) == 0) {
329 	/*
330 	 * Decode a base64 secret.  The decoded length is 3/4 the encoded
331 	 * length but padding may be missing so round up to a multiple of 4.
332 	 */
333 	secret += sizeof("base64:") - 1;
334 	reslen = ((strlen(secret) + 3) / 4 * 3);
335 	result = malloc(reslen + 1);
336 	if (result == NULL) {
337 	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
338 	} else {
339 	    len = base64_decode(secret, result, reslen);
340 	    if (len == (size_t)-1) {
341 		free(result);
342 		result = NULL;
343 	    } else {
344 		result[len] = '\0';
345 	    }
346 	}
347     }
348     debug_return_str((char *)result);
349 }
350 
351 static void
sudo_ldap_read_secret(const char * path)352 sudo_ldap_read_secret(const char *path)
353 {
354     FILE *fp;
355     char *line = NULL;
356     size_t linesize = 0;
357     ssize_t len;
358     debug_decl(sudo_ldap_read_secret, SUDOERS_DEBUG_LDAP);
359 
360     if ((fp = fopen(path_ldap_secret, "r")) != NULL) {
361 	len = getdelim(&line, &linesize, '\n', fp);
362 	if (len != -1) {
363 	    /* trim newline */
364 	    while (len > 0 && line[len - 1] == '\n')
365 		line[--len] = '\0';
366 	    /* copy to bindpw and binddn */
367 	    free(ldap_conf.bindpw);
368 	    ldap_conf.bindpw = sudo_ldap_decode_secret(line);
369 	    if (ldap_conf.bindpw == NULL) {
370 		/* not base64 encoded, use directly */
371 		ldap_conf.bindpw = line;
372 		line = NULL;
373 	    }
374 	    free(ldap_conf.binddn);
375 	    ldap_conf.binddn = ldap_conf.rootbinddn;
376 	    ldap_conf.rootbinddn = NULL;
377 	}
378 	fclose(fp);
379 	free(line);
380     }
381     debug_return;
382 }
383 
384 /*
385  * Look up keyword in config tables.
386  * Returns true if found, else false.
387  */
388 static bool
sudo_ldap_parse_keyword(const char * keyword,const char * value,struct ldap_config_table * table)389 sudo_ldap_parse_keyword(const char *keyword, const char *value,
390     struct ldap_config_table *table)
391 {
392     struct ldap_config_table *cur;
393     const char *errstr;
394     debug_decl(sudo_ldap_parse_keyword, SUDOERS_DEBUG_LDAP);
395 
396     /* Look up keyword in config tables */
397     for (cur = table; cur->conf_str != NULL; cur++) {
398 	if (strcasecmp(keyword, cur->conf_str) == 0) {
399 	    switch (cur->type) {
400 	    case CONF_DEREF_VAL:
401 		if (strcasecmp(value, "searching") == 0)
402 		    *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
403 		else if (strcasecmp(value, "finding") == 0)
404 		    *(int *)(cur->valp) = LDAP_DEREF_FINDING;
405 		else if (strcasecmp(value, "always") == 0)
406 		    *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
407 		else
408 		    *(int *)(cur->valp) = LDAP_DEREF_NEVER;
409 		break;
410 	    case CONF_REQCERT_VAL:
411 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
412 		if (strcasecmp(value, "never") == 0)
413 		    *(int *)(cur->valp) = LDAP_OPT_X_TLS_NEVER;
414 		else if (strcasecmp(value, "allow") == 0)
415 		    *(int *)(cur->valp) = LDAP_OPT_X_TLS_ALLOW;
416 		else if (strcasecmp(value, "try") == 0)
417 		    *(int *)(cur->valp) = LDAP_OPT_X_TLS_TRY;
418 		else if (strcasecmp(value, "hard") == 0)
419 		    *(int *)(cur->valp) = LDAP_OPT_X_TLS_HARD;
420 		else if (strcasecmp(value, "demand") == 0)
421 		    *(int *)(cur->valp) = LDAP_OPT_X_TLS_DEMAND;
422 #endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
423 		break;
424 	    case CONF_BOOL:
425 		*(int *)(cur->valp) = sudo_strtobool(value) == true;
426 		break;
427 	    case CONF_INT:
428 		*(int *)(cur->valp) = sudo_strtonum(value, INT_MIN, INT_MAX,
429 		    &errstr);
430 		if (errstr != NULL) {
431 		    sudo_warnx(U_("%s: %s: %s: %s"),
432 			path_ldap_conf, keyword, value, U_(errstr));
433 		}
434 		break;
435 	    case CONF_STR:
436 		{
437 		    char *cp = NULL;
438 
439 		    free(*(char **)(cur->valp));
440 		    if (*value && (cp = strdup(value)) == NULL) {
441 			sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
442 			debug_return_bool(false);
443 		    }
444 		    *(char **)(cur->valp) = cp;
445 		    break;
446 		}
447 	    case CONF_LIST_STR:
448 		{
449 		    struct ldap_config_str_list *head;
450 		    struct ldap_config_str *str;
451 		    size_t len = strlen(value);
452 
453 		    if (len > 0) {
454 			head = (struct ldap_config_str_list *)cur->valp;
455 			if ((str = malloc(sizeof(*str) + len)) == NULL) {
456 			    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
457 			    debug_return_bool(false);
458 			}
459 			memcpy(str->val, value, len + 1);
460 			STAILQ_INSERT_TAIL(head, str, entries);
461 		    }
462 		}
463 		break;
464 	    }
465 	    debug_return_bool(true);
466 	}
467     }
468     debug_return_bool(false);
469 }
470 
471 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
472 const char *
sudo_krb5_ccname_path(const char * old_ccname)473 sudo_krb5_ccname_path(const char *old_ccname)
474 {
475     const char *ccname = old_ccname;
476     debug_decl(sudo_krb5_ccname_path, SUDOERS_DEBUG_LDAP);
477 
478     /* Strip off leading FILE: or WRFILE: prefix. */
479     switch (ccname[0]) {
480 	case 'F':
481 	case 'f':
482 	    if (strncasecmp(ccname, "FILE:", 5) == 0)
483 		ccname += 5;
484 	    break;
485 	case 'W':
486 	case 'w':
487 	    if (strncasecmp(ccname, "WRFILE:", 7) == 0)
488 		ccname += 7;
489 	    break;
490     }
491     sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
492 	"ccache %s -> %s", old_ccname, ccname);
493 
494     /* Credential cache must be a fully-qualified path name. */
495     debug_return_const_str(*ccname == '/' ? ccname : NULL);
496 }
497 
498 static bool
sudo_check_krb5_ccname(const char * ccname)499 sudo_check_krb5_ccname(const char *ccname)
500 {
501     int fd = -1;
502     const char *ccname_path;
503     debug_decl(sudo_check_krb5_ccname, SUDOERS_DEBUG_LDAP);
504 
505     /* Strip off prefix to get path name. */
506     ccname_path = sudo_krb5_ccname_path(ccname);
507     if (ccname_path == NULL) {
508 	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
509 	    "unsupported krb5 credential cache path: %s", ccname);
510 	debug_return_bool(false);
511     }
512     /* Make sure credential cache is fully-qualified and exists. */
513     fd = open(ccname_path, O_RDONLY|O_NONBLOCK, 0);
514     if (fd == -1) {
515 	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
516 	    "unable to open krb5 credential cache: %s", ccname_path);
517 	debug_return_bool(false);
518     }
519     close(fd);
520     sudo_debug_printf(SUDO_DEBUG_INFO,
521 	"using krb5 credential cache: %s", ccname_path);
522     debug_return_bool(true);
523 }
524 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
525 
526 bool
sudo_ldap_read_config(void)527 sudo_ldap_read_config(void)
528 {
529     char *cp, *keyword, *value, *line = NULL;
530     struct ldap_config_str *conf_str;
531     size_t linesize = 0;
532     FILE *fp;
533     debug_decl(sudo_ldap_read_config, SUDOERS_DEBUG_LDAP);
534 
535     /* defaults */
536     ldap_conf.version = 3;
537     ldap_conf.port = -1;
538     ldap_conf.tls_checkpeer = -1;
539     ldap_conf.tls_reqcert = -1;
540     ldap_conf.timelimit = -1;
541     ldap_conf.timeout = -1;
542     ldap_conf.bind_timelimit = -1;
543     ldap_conf.use_sasl = -1;
544     ldap_conf.rootuse_sasl = -1;
545     ldap_conf.deref = -1;
546     ldap_conf.search_filter = strdup(DEFAULT_SEARCH_FILTER);
547     ldap_conf.netgroup_search_filter = strdup(DEFAULT_NETGROUP_SEARCH_FILTER);
548     STAILQ_INIT(&ldap_conf.uri);
549     STAILQ_INIT(&ldap_conf.base);
550     STAILQ_INIT(&ldap_conf.netgroup_base);
551 
552     if (ldap_conf.search_filter == NULL || ldap_conf.netgroup_search_filter == NULL) {
553 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
554 	debug_return_bool(false);
555     }
556 
557     if ((fp = fopen(path_ldap_conf, "r")) == NULL)
558 	debug_return_bool(false);
559 
560     while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_COMM_BOL|PARSELN_CONT_IGN) != -1) {
561 	if (*line == '\0')
562 	    continue;		/* skip empty line */
563 
564 	/* split into keyword and value */
565 	keyword = cp = line;
566 	while (*cp && !isblank((unsigned char) *cp))
567 	    cp++;
568 	if (*cp)
569 	    *cp++ = '\0';	/* terminate keyword */
570 
571 	/* skip whitespace before value */
572 	while (isblank((unsigned char) *cp))
573 	    cp++;
574 	value = cp;
575 
576 	/* Look up keyword in config tables */
577 	if (!sudo_ldap_parse_keyword(keyword, value, ldap_conf_global))
578 	    sudo_ldap_parse_keyword(keyword, value, ldap_conf_conn);
579     }
580     free(line);
581     fclose(fp);
582 
583     if (!ldap_conf.host) {
584 	ldap_conf.host = strdup("localhost");
585 	if (ldap_conf.host == NULL) {
586 	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
587 	    debug_return_bool(false);
588 	}
589     }
590 
591     DPRINTF1("LDAP Config Summary");
592     DPRINTF1("===================");
593     if (!STAILQ_EMPTY(&ldap_conf.uri)) {
594 	STAILQ_FOREACH(conf_str, &ldap_conf.uri, entries) {
595 	    DPRINTF1("uri              %s", conf_str->val);
596 	}
597     } else {
598 	DPRINTF1("host             %s",
599 	    ldap_conf.host ? ldap_conf.host : "(NONE)");
600 	DPRINTF1("port             %d", ldap_conf.port);
601     }
602     DPRINTF1("ldap_version     %d", ldap_conf.version);
603 
604     if (!STAILQ_EMPTY(&ldap_conf.base)) {
605 	STAILQ_FOREACH(conf_str, &ldap_conf.base, entries) {
606 	    DPRINTF1("sudoers_base     %s", conf_str->val);
607 	}
608     } else {
609 	DPRINTF1("sudoers_base     %s", "(NONE: LDAP disabled)");
610     }
611     if (ldap_conf.search_filter) {
612 	DPRINTF1("search_filter    %s", ldap_conf.search_filter);
613     }
614     if (!STAILQ_EMPTY(&ldap_conf.netgroup_base)) {
615 	STAILQ_FOREACH(conf_str, &ldap_conf.netgroup_base, entries) {
616 	    DPRINTF1("netgroup_base    %s", conf_str->val);
617 	}
618     } else {
619 	DPRINTF1("netgroup_base %s", "(NONE: will use nsswitch)");
620     }
621     if (ldap_conf.netgroup_search_filter) {
622         DPRINTF1("netgroup_search_filter %s", ldap_conf.netgroup_search_filter);
623     }
624     DPRINTF1("binddn           %s",
625 	ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
626     DPRINTF1("bindpw           %s",
627 	ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
628     if (ldap_conf.bind_timelimit > 0) {
629 	DPRINTF1("bind_timelimit   %d", ldap_conf.bind_timelimit);
630     }
631     if (ldap_conf.timelimit > 0) {
632 	DPRINTF1("timelimit        %d", ldap_conf.timelimit);
633     }
634     if (ldap_conf.deref != -1) {
635 	DPRINTF1("deref            %d", ldap_conf.deref);
636     }
637     DPRINTF1("ssl              %s", ldap_conf.ssl ? ldap_conf.ssl : "(no)");
638     if (ldap_conf.tls_checkpeer != -1) {
639 	DPRINTF1("tls_checkpeer    %s",
640 	    ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
641     }
642 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
643     if (ldap_conf.tls_reqcert != -1) {
644 	DPRINTF1("tls_reqcert    %s",
645 	    ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_NEVER ? "hard" :
646 	    ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_ALLOW ? "allow" :
647 	    ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_TRY ? "try" :
648 	    ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_HARD ? "hard" :
649 	    ldap_conf.tls_reqcert == LDAP_OPT_X_TLS_DEMAND ? "demand" :
650 	    "unknown");
651     }
652 #endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
653     if (ldap_conf.tls_cacertfile != NULL) {
654 	DPRINTF1("tls_cacertfile   %s", ldap_conf.tls_cacertfile);
655     }
656     if (ldap_conf.tls_cacertdir != NULL) {
657 	DPRINTF1("tls_cacertdir    %s", ldap_conf.tls_cacertdir);
658     }
659     if (ldap_conf.tls_random_file != NULL) {
660 	DPRINTF1("tls_random_file  %s", ldap_conf.tls_random_file);
661     }
662     if (ldap_conf.tls_cipher_suite != NULL) {
663 	DPRINTF1("tls_cipher_suite %s", ldap_conf.tls_cipher_suite);
664     }
665     if (ldap_conf.tls_certfile != NULL) {
666 	DPRINTF1("tls_certfile     %s", ldap_conf.tls_certfile);
667     }
668     if (ldap_conf.tls_keyfile != NULL) {
669 	DPRINTF1("tls_keyfile      %s", ldap_conf.tls_keyfile);
670     }
671 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
672     if (ldap_conf.use_sasl != -1) {
673 	if (ldap_conf.sasl_mech == NULL) {
674 	    /* Default mechanism is GSSAPI. */
675 	    ldap_conf.sasl_mech = strdup("GSSAPI");
676 	    if (ldap_conf.sasl_mech == NULL) {
677 		sudo_warnx(U_("%s: %s"), __func__,
678 		    U_("unable to allocate memory"));
679 		debug_return_bool(false);
680 	    }
681 	}
682 	DPRINTF1("use_sasl         %s", ldap_conf.use_sasl ? "yes" : "no");
683 	DPRINTF1("sasl_mech        %s", ldap_conf.sasl_mech);
684 	DPRINTF1("sasl_auth_id     %s",
685 	    ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
686 	DPRINTF1("rootuse_sasl     %d",
687 	    ldap_conf.rootuse_sasl);
688 	DPRINTF1("rootsasl_auth_id %s",
689 	    ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
690 	DPRINTF1("sasl_secprops    %s",
691 	    ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
692 	DPRINTF1("krb5_ccname      %s",
693 	    ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
694     }
695 #endif
696     DPRINTF1("===================");
697 
698     if (STAILQ_EMPTY(&ldap_conf.base))
699 	debug_return_bool(false);	/* if no base is defined, ignore LDAP */
700 
701     if (ldap_conf.bind_timelimit > 0)
702 	ldap_conf.bind_timelimit *= 1000;	/* convert to ms */
703 
704     /*
705      * Interpret SSL option
706      */
707     if (ldap_conf.ssl != NULL) {
708 	if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
709 	    ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
710 	else if (sudo_strtobool(ldap_conf.ssl) == true)
711 	    ldap_conf.ssl_mode = SUDO_LDAP_SSL;
712     }
713 
714 #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
715     if (ldap_conf.tls_checkpeer != -1) {
716 	ldapssl_set_strength(NULL,
717 	    ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
718     }
719 #endif
720 
721 #ifndef HAVE_LDAP_INITIALIZE
722     /* Convert uri list to host list if no ldap_initialize(). */
723     if (!STAILQ_EMPTY(&ldap_conf.uri)) {
724 	struct ldap_config_str *uri;
725 
726 	if (sudo_ldap_parse_uri(&ldap_conf.uri) != LDAP_SUCCESS)
727 	    debug_return_bool(false);
728 	while ((uri = STAILQ_FIRST(&ldap_conf.uri)) != NULL) {
729 	    STAILQ_REMOVE_HEAD(&ldap_conf.uri, entries);
730 	    free(uri);
731 	}
732 	ldap_conf.port = LDAP_PORT;
733     }
734 #endif
735 
736     if (STAILQ_EMPTY(&ldap_conf.uri)) {
737 	/* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
738 	if (ldap_conf.port < 0)
739 	    ldap_conf.port =
740 		ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
741 
742 #ifdef HAVE_LDAP_CREATE
743 	/*
744 	 * Cannot specify port directly to ldap_create(), each host must
745 	 * include :port to override the default.
746 	 */
747 	if (ldap_conf.port != LDAP_PORT) {
748 	    if (!sudo_ldap_conf_add_ports())
749 		debug_return_bool(false);
750 	}
751 #endif
752     }
753 
754     /* If search filter is not parenthesized, make it so. */
755     if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
756 	size_t len = strlen(ldap_conf.search_filter);
757 	cp = ldap_conf.search_filter;
758 	ldap_conf.search_filter = malloc(len + 3);
759 	if (ldap_conf.search_filter == NULL) {
760 	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
761 	    debug_return_bool(false);
762 	}
763 	ldap_conf.search_filter[0] = '(';
764 	memcpy(ldap_conf.search_filter + 1, cp, len);
765 	ldap_conf.search_filter[len + 1] = ')';
766 	ldap_conf.search_filter[len + 2] = '\0';
767 	free(cp);
768     }
769 
770 
771     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
772     if (ldap_conf.rootbinddn) {
773 	sudo_ldap_read_secret(path_ldap_secret);
774     } else if (ldap_conf.bindpw) {
775 	cp = sudo_ldap_decode_secret(ldap_conf.bindpw);
776 	if (cp != NULL) {
777 	    free(ldap_conf.bindpw);
778 	    ldap_conf.bindpw = cp;
779 	}
780     }
781 
782     if (ldap_conf.tls_keypw) {
783 	cp = sudo_ldap_decode_secret(ldap_conf.tls_keypw);
784 	if (cp != NULL) {
785 	    free(ldap_conf.tls_keypw);
786 	    ldap_conf.tls_keypw = cp;
787 	}
788     }
789 
790 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
791     /*
792      * Make sure we can open the file specified by krb5_ccname.
793      */
794     if (ldap_conf.krb5_ccname != NULL) {
795 	if (!sudo_check_krb5_ccname(ldap_conf.krb5_ccname))
796 	    ldap_conf.krb5_ccname = NULL;
797     }
798 #endif
799 
800     debug_return_bool(true);
801 }
802 
803 /*
804  * Set LDAP options from the specified options table
805  * Returns LDAP_SUCCESS on success, else non-zero.
806  */
807 static int
sudo_ldap_set_options_table(LDAP * ld,struct ldap_config_table * table)808 sudo_ldap_set_options_table(LDAP *ld, struct ldap_config_table *table)
809 {
810     struct ldap_config_table *cur;
811     int ival, rc, errors = 0;
812     char *sval;
813     debug_decl(sudo_ldap_set_options_table, SUDOERS_DEBUG_LDAP);
814 
815     for (cur = table; cur->conf_str != NULL; cur++) {
816 	if (cur->opt_val == -1)
817 	    continue;
818 
819 	switch (cur->type) {
820 	case CONF_BOOL:
821 	case CONF_INT:
822 	    ival = *(int *)(cur->valp);
823 	    if (ival >= 0) {
824 		DPRINTF1("ldap_set_option: %s -> %d", cur->conf_str, ival);
825 		rc = ldap_set_option(ld, cur->opt_val, &ival);
826 		if (rc != LDAP_OPT_SUCCESS) {
827 		    sudo_warnx("ldap_set_option: %s -> %d: %s",
828 			cur->conf_str, ival, ldap_err2string(rc));
829 		    errors++;
830 		}
831 	    }
832 	    break;
833 	case CONF_STR:
834 	    sval = *(char **)(cur->valp);
835 	    if (sval != NULL) {
836 		DPRINTF1("ldap_set_option: %s -> %s", cur->conf_str, sval);
837 		rc = ldap_set_option(ld, cur->opt_val, sval);
838 		if (rc != LDAP_OPT_SUCCESS) {
839 		    sudo_warnx("ldap_set_option: %s -> %s: %s",
840 			cur->conf_str, sval, ldap_err2string(rc));
841 		    errors++;
842 		}
843 	    }
844 	    break;
845 	}
846     }
847     debug_return_int(errors ? -1 : LDAP_SUCCESS);
848 }
849 
850 /*
851  * Set LDAP options based on the global config table.
852  * Returns LDAP_SUCCESS on success, else non-zero.
853  */
854 int
sudo_ldap_set_options_global(void)855 sudo_ldap_set_options_global(void)
856 {
857     int ret;
858     debug_decl(sudo_ldap_set_options_global, SUDOERS_DEBUG_LDAP);
859 
860     /* Set ber options */
861 #ifdef LBER_OPT_DEBUG_LEVEL
862     if (ldap_conf.ldap_debug)
863 	ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
864 #endif
865 
866     /* Parse global LDAP options table. */
867     ret = sudo_ldap_set_options_table(NULL, ldap_conf_global);
868     debug_return_int(ret);
869 }
870 
871 /*
872  * Set LDAP options based on the per-connection config table.
873  * Returns LDAP_SUCCESS on success, else non-zero.
874  */
875 int
sudo_ldap_set_options_conn(LDAP * ld)876 sudo_ldap_set_options_conn(LDAP *ld)
877 {
878     int rc;
879     debug_decl(sudo_ldap_set_options_conn, SUDOERS_DEBUG_LDAP);
880 
881     /* Parse per-connection LDAP options table. */
882     rc = sudo_ldap_set_options_table(ld, ldap_conf_conn);
883     if (rc == -1)
884 	debug_return_int(-1);
885 
886 #ifdef LDAP_OPT_TIMEOUT
887     /* Convert timeout to a timeval */
888     if (ldap_conf.timeout > 0) {
889 	struct timeval tv;
890 	tv.tv_sec = ldap_conf.timeout;
891 	tv.tv_usec = 0;
892 	DPRINTF1("ldap_set_option(LDAP_OPT_TIMEOUT, %d)", ldap_conf.timeout);
893 	rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
894 	if (rc != LDAP_OPT_SUCCESS) {
895 	    sudo_warnx("ldap_set_option(TIMEOUT, %d): %s",
896 		ldap_conf.timeout, ldap_err2string(rc));
897 	}
898     }
899 #endif
900 #ifdef LDAP_OPT_NETWORK_TIMEOUT
901     /* Convert bind_timelimit to a timeval */
902     if (ldap_conf.bind_timelimit > 0) {
903 	struct timeval tv;
904 	tv.tv_sec = ldap_conf.bind_timelimit / 1000;
905 	tv.tv_usec = 0;
906 	DPRINTF1("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %d)",
907 	    ldap_conf.bind_timelimit / 1000);
908 	rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
909 # if !defined(LDAP_OPT_CONNECT_TIMEOUT) || LDAP_VENDOR_VERSION != 510
910 	/* Tivoli Directory Server 6.3 libs always return a (bogus) error. */
911 	if (rc != LDAP_OPT_SUCCESS) {
912 	    sudo_warnx("ldap_set_option(NETWORK_TIMEOUT, %d): %s",
913 		ldap_conf.bind_timelimit / 1000, ldap_err2string(rc));
914 	}
915 # endif
916     }
917 #endif
918 
919 #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
920     if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
921 	int val = LDAP_OPT_X_TLS_HARD;
922 	DPRINTF1("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)");
923 	rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
924 	if (rc != LDAP_SUCCESS) {
925 	    sudo_warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
926 		ldap_err2string(rc));
927 	    debug_return_int(-1);
928 	}
929     }
930 #endif
931     debug_return_int(LDAP_SUCCESS);
932 }
933