X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Flookups%2Fldap.c;h=f77229ded320ba5b6923f7f916d7d26ee0c0cc89;hp=2a4199c3ac25f2e81efa0a22b0019bc436aea507;hb=deae092e544ecfb3d8a362a260fc00ec01f0883f;hpb=867fcbf59a53d5acbab505bc7670e7d0c29dbc94 diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 2a4199c3a..f77229ded 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/lookups/ldap.c,v 1.15 2009/11/16 19:50:38 nm4 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ /* Many thanks to Stuart Lynne for contributing the original code for this @@ -83,6 +81,7 @@ typedef struct ldap_connection { uschar *password; BOOL bound; int port; + BOOL is_start_tls_called; LDAP *ld; } LDAP_CONNECTION; @@ -281,6 +280,13 @@ if (lcp == NULL) { LDAP *ld; + #ifdef LDAP_OPT_X_TLS_NEWCTX + int am_server = 0; + LDAP *ldsetctx; + #else + LDAP *ldsetctx = NULL; + #endif + /* --------------------------- OpenLDAP ------------------------ */ @@ -366,6 +372,10 @@ if (lcp == NULL) goto RETURN_ERROR; } + #ifdef LDAP_OPT_X_TLS_NEWCTX + ldsetctx = ld; + #endif + /* Set the TCP connect time limit if available. This is something that is in Netscape SDK v4.1; I don't know about other libraries. */ @@ -417,15 +427,43 @@ if (lcp == NULL) if (!ldapi) { int tls_option; + #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT + if (eldap_require_cert != NULL) + { + tls_option = LDAP_OPT_X_TLS_NEVER; + if (Ustrcmp(eldap_require_cert, "hard") == 0) + { + tls_option = LDAP_OPT_X_TLS_HARD; + } + else if (Ustrcmp(eldap_require_cert, "demand") == 0) + { + tls_option = LDAP_OPT_X_TLS_DEMAND; + } + else if (Ustrcmp(eldap_require_cert, "allow") == 0) + { + tls_option = LDAP_OPT_X_TLS_ALLOW; + } + else if (Ustrcmp(eldap_require_cert, "try") == 0) + { + tls_option = LDAP_OPT_X_TLS_TRY; + } + DEBUG(D_lookup) + debug_printf("Require certificate overrides LDAP_OPT_X_TLS option (%d)\n", + tls_option); + } + else + #endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */ if (strncmp(ludp->lud_scheme, "ldaps", 5) == 0) { tls_option = LDAP_OPT_X_TLS_HARD; - DEBUG(D_lookup) debug_printf("LDAP_OPT_X_TLS_HARD set\n"); + DEBUG(D_lookup) + debug_printf("LDAP_OPT_X_TLS_HARD set due to ldaps:// URI\n"); } else { tls_option = LDAP_OPT_X_TLS_TRY; - DEBUG(D_lookup) debug_printf("LDAP_OPT_X_TLS_TRY set\n"); + DEBUG(D_lookup) + debug_printf("LDAP_OPT_X_TLS_TRY set due to ldap:// URI\n"); } ldap_set_option(ld, LDAP_OPT_X_TLS, (void *)&tls_option); } @@ -434,31 +472,31 @@ if (lcp == NULL) #ifdef LDAP_OPT_X_TLS_CACERTFILE if (eldap_ca_cert_file != NULL) { - ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, eldap_ca_cert_file); + ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_CACERTFILE, eldap_ca_cert_file); } #endif #ifdef LDAP_OPT_X_TLS_CACERTDIR if (eldap_ca_cert_dir != NULL) { - ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, eldap_ca_cert_dir); + ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_CACERTDIR, eldap_ca_cert_dir); } #endif #ifdef LDAP_OPT_X_TLS_CERTFILE if (eldap_cert_file != NULL) { - ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, eldap_cert_file); + ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_CERTFILE, eldap_cert_file); } #endif #ifdef LDAP_OPT_X_TLS_KEYFILE if (eldap_cert_key != NULL) { - ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, eldap_cert_key); + ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_KEYFILE, eldap_cert_key); } #endif #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE if (eldap_cipher_suite != NULL) { - ldap_set_option(ld, LDAP_OPT_X_TLS_CIPHER_SUITE, eldap_cipher_suite); + ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_CIPHER_SUITE, eldap_cipher_suite); } #endif #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT @@ -481,7 +519,26 @@ if (lcp == NULL) { cert_option = LDAP_OPT_X_TLS_TRY; } - ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &cert_option); + /* This ldap handle is set at compile time based on client libs. Older + * versions want it to be global and newer versions can force a reload + * of the TLS context (to reload these settings we are changing from the + * default that loaded at instantiation). */ + rc = ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_REQUIRE_CERT, &cert_option); + if (rc) + { + DEBUG(D_lookup) + debug_printf("Unable to set TLS require cert_option(%d) globally: %s\n", + cert_option, ldap_err2string(rc)); + } + } + #endif + #ifdef LDAP_OPT_X_TLS_NEWCTX + rc = ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_NEWCTX, &am_server); + if (rc) + { + DEBUG(D_lookup) + debug_printf("Unable to reload TLS context %d: %s\n", + rc, ldap_err2string(rc)); } #endif @@ -495,6 +552,7 @@ if (lcp == NULL) lcp->port = port; lcp->ld = ld; lcp->next = ldap_connections; + lcp->is_start_tls_called = FALSE; ldap_connections = lcp; } @@ -521,13 +579,26 @@ if (!lcp->bound || { DEBUG(D_lookup) debug_printf("%sbinding with user=%s password=%s\n", (lcp->bound)? "re-" : "", user, password); -#ifdef LDAP_OPT_X_TLS - /* The Oracle LDAP libraries (LDAP_LIB_TYPE=SOLARIS) don't support this: */ - if (eldap_start_tls) + if (eldap_start_tls && !lcp->is_start_tls_called) { - ldap_start_tls_s(lcp->ld, NULL, NULL); - } +#if defined(LDAP_OPT_X_TLS) && !defined(LDAP_LIB_SOLARIS) + /* The Oracle LDAP libraries (LDAP_LIB_TYPE=SOLARIS) don't support this. + * Note: moreover, they appear to now define LDAP_OPT_X_TLS and still not + * export an ldap_start_tls_s symbol. + */ + if ( (rc = ldap_start_tls_s(lcp->ld, NULL, NULL)) != LDAP_SUCCESS) + { + *errmsg = string_sprintf("failed to initiate TLS processing on an " + "LDAP session to server %s%s - ldap_start_tls_s() returned %d:" + " %s", host, porttext, rc, ldap_err2string(rc)); + goto RETURN_ERROR; + } + lcp->is_start_tls_called = TRUE; +#else + DEBUG(D_lookup) + debug_printf("TLS initiation not supported with this Exim and your LDAP library.\n"); #endif + } if ((msgid = ldap_bind(lcp->ld, CS user, CS password, LDAP_AUTH_SIMPLE)) == -1) { @@ -727,10 +798,10 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value); if (values != firstval) - data = string_cat(data, &size, &ptr, US", ", 2); + data = string_cat(data, &size, &ptr, US",", 1); /* For multiple attributes, the data is in quotes. We must escape - internal quotes, backslashes, newlines. */ + internal quotes, backslashes, newlines, and must double commas. */ if (attr_count != 1) { @@ -739,6 +810,8 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == { if (value[j] == '\n') data = string_cat(data, &size, &ptr, US"\\n", 2); + else if (value[j] == ',') + data = string_cat(data, &size, &ptr, US",,", 2); else { if (value[j] == '\"' || value[j] == '\\') @@ -748,9 +821,20 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == } } - /* For single attributes, copy the value verbatim */ + /* For single attributes, just double commas */ + + else + { + int j; + for (j = 0; j < len; j++) + { + if (value[j] == ',') + data = string_cat(data, &size, &ptr, US",,", 2); + else + data = string_cat(data, &size, &ptr, value+j, 1); + } + } - else data = string_cat(data, &size, &ptr, value, len); /* Move on to the next value */ @@ -1049,6 +1133,7 @@ uschar *url = ldap_url; uschar *p; uschar *user = NULL; uschar *password = NULL; +uschar *local_servers = NULL; uschar *server, *list; uschar buffer[512]; @@ -1077,6 +1162,7 @@ while (strncmpic(url, US"ldap", 4) != 0) else if (strncmpic(name, US"TIME=", namelen) == 0) timelimit = Uatoi(value); else if (strncmpic(name, US"CONNECT=", namelen) == 0) tcplimit = Uatoi(value); else if (strncmpic(name, US"NETTIME=", namelen) == 0) tcplimit = Uatoi(value); + else if (strncmpic(name, US"SERVERS=", namelen) == 0) local_servers = value; /* Don't know if all LDAP libraries have LDAP_OPT_DEREF */ @@ -1204,16 +1290,16 @@ if (Ustrncmp(p, "://", 3) != 0) /* No default servers, or URL contains a server name: just one attempt */ -if (eldap_default_servers == NULL || p[3] != '/') +if ((eldap_default_servers == NULL && local_servers == NULL) || p[3] != '/') { return perform_ldap_search(url, NULL, 0, search_type, res, errmsg, &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, referrals); } -/* Loop through the default servers until OK or FAIL */ - -list = eldap_default_servers; +/* Loop through the default servers until OK or FAIL. Use local_servers list + * if defined in the lookup, otherwise use the global default list */ +list = (local_servers == NULL) ? eldap_default_servers : local_servers; while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) { int rc; @@ -1312,7 +1398,8 @@ while ((lcp = ldap_connections) != NULL) { DEBUG(D_lookup) debug_printf("unbind LDAP connection to %s:%d\n", lcp->host, lcp->port); - ldap_unbind(lcp->ld); + if(lcp->bound == TRUE) + ldap_unbind(lcp->ld); ldap_connections = lcp->next; } }