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