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