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