X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Flookups%2Fldap.c;h=5c1ea0b569f22fd9630a08490c11ee777c18ae93;hp=043135e039127e687d8de8ce38a43ddf20248b3c;hb=e74376d84aa63876c9a3b240513b8f38920733b7;hpb=d38f823269ffb0e25cbf2c9b6204f61e149169a7 diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 043135e03..5c1ea0b56 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.5 2004/12/21 12:00:59 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2004 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* Many thanks to Stuart Lynne for contributing the original code for this @@ -15,23 +13,14 @@ researching how to handle the different kinds of error. */ #include "../exim.h" #include "lf_functions.h" -#include "ldap.h" - - -/* We can't just compile this code and allow the library mechanism to omit the -functions if they are not wanted, because we need to have the LDAP headers -available for compiling. Therefore, compile these functions only if LOOKUP_LDAP -is defined. However, some compilers don't like compiling empty modules, so keep -them happy with a dummy when skipping the rest. Make it reference itself to -stop picky compilers complaining that it is unused, and put in a dummy argument -to stop even pickier compilers complaining about infinite loops. */ -#ifndef LOOKUP_LDAP -static void dummy(int x) { dummy(x-1); } -#else +/* Include LDAP headers. The code below uses some "old" LDAP interfaces that +are deprecated in OpenLDAP. I don't know their status in other LDAP +implementations. LDAP_DEPRECATED causes their prototypes to be defined in +ldap.h. */ -/* Include LDAP headers */ +#define LDAP_DEPRECATED 1 #include #include @@ -132,6 +121,7 @@ Arguments: tcplimit max time for network activity, e.g. connect, or 0 for OS default deference the dereference option, which is one of LDAP_DEREF_{NEVER,SEARCHING,FINDING,ALWAYS} + referrals the referral option, which is LDAP_OPT_ON or LDAP_OPT_OFF Returns: OK or FAIL or DEFER FAIL is given only if a lookup was performed successfully, but @@ -141,7 +131,7 @@ Returns: OK or FAIL or DEFER static int perform_ldap_search(uschar *ldap_url, uschar *server, int s_port, int search_type, uschar **res, uschar **errmsg, BOOL *defer_break, uschar *user, uschar *password, - int sizelimit, int timelimit, int tcplimit, int dereference) + int sizelimit, int timelimit, int tcplimit, int dereference, void *referrals) { LDAPURLDesc *ludp = NULL; LDAPMessage *result = NULL; @@ -395,7 +385,7 @@ if (lcp == NULL) #ifdef LDAP_OPT_NETWORK_TIMEOUT if (tcplimit > 0) ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, (void *)timeoutptr); - #endif + #endif /* I could not get TLS to work until I set the version to 3. That version seems to be the default nowadays. The RFC is dated 1997, so I would hope @@ -439,6 +429,60 @@ if (lcp == NULL) } #endif /* LDAP_OPT_X_TLS */ + #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); + } + #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); + } + #endif + #ifdef LDAP_OPT_X_TLS_CERTFILE + if (eldap_cert_file != NULL) + { + ldap_set_option(ld, 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); + } + #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); + } + #endif + #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT + if (eldap_require_cert != NULL) + { + int cert_option = LDAP_OPT_X_TLS_NEVER; + if (Ustrcmp(eldap_require_cert, "hard") == 0) + { + cert_option = LDAP_OPT_X_TLS_HARD; + } + else if (Ustrcmp(eldap_require_cert, "demand") == 0) + { + cert_option = LDAP_OPT_X_TLS_DEMAND; + } + else if (Ustrcmp(eldap_require_cert, "allow") == 0) + { + cert_option = LDAP_OPT_X_TLS_ALLOW; + } + else if (Ustrcmp(eldap_require_cert, "try") == 0) + { + cert_option = LDAP_OPT_X_TLS_TRY; + } + ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &cert_option); + } + #endif + /* Now add this connection to the chain of cached connections */ lcp = store_get(sizeof(LDAP_CONNECTION)); @@ -475,6 +519,18 @@ 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 ( (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; + } + } +#endif if ((msgid = ldap_bind(lcp->ld, CS user, CS password, LDAP_AUTH_SIMPLE)) == -1) { @@ -486,7 +542,7 @@ if (!lcp->bound || if ((rc = ldap_result( lcp->ld, msgid, 1, timeoutptr, &result )) <= 0) { *errmsg = string_sprintf("failed to bind the LDAP connection to server " - "%s%s - LDAP error: %s", host, porttext, + "%s%s - LDAP error: %s", host, porttext, rc == -1 ? "result retrieval failed" : "timeout" ); result = NULL; goto RETURN_ERROR; @@ -551,6 +607,14 @@ an LDAP library without LDAP_OPT_DEREF. */ ldap_set_option(lcp->ld, LDAP_OPT_DEREF, (void *)&dereference); #endif +/* Similarly for the referral setting; should the library follow referrals that +the LDAP server returns? The conditional is just in case someone uses a library +without it. */ + +#if defined(LDAP_OPT_REFERRALS) +ldap_set_option(lcp->ld, LDAP_OPT_REFERRALS, referrals); +#endif + /* Start the search on the server. */ DEBUG(D_lookup) debug_printf("Start search\n"); @@ -560,7 +624,16 @@ msgid = ldap_search(lcp->ld, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter, if (msgid == -1) { - *errmsg = string_sprintf("ldap search initiation failed"); + #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2 + int err; + ldap_get_option(lcp->ld, LDAP_OPT_ERROR_NUMBER, &err); + *errmsg = string_sprintf("ldap_search failed: %d, %s", err, + ldap_err2string(err)); + + #else + *errmsg = string_sprintf("ldap_search failed"); + #endif + goto RETURN_ERROR; } @@ -657,10 +730,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) { @@ -669,6 +742,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] == '\\') @@ -678,9 +753,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 */ @@ -779,10 +865,16 @@ if (rc == -1 || result == NULL) } /* A return code that isn't -1 doesn't necessarily mean there were no problems -with the search. The message must be an LDAP_RES_SEARCH_RESULT or -LDAP_RES_SEARCH_REFERENCE or else it's something we can't handle. */ - -if (rc != LDAP_RES_SEARCH_RESULT && rc != LDAP_RES_SEARCH_REFERENCE) +with the search. The message must be an LDAP_RES_SEARCH_RESULT or +LDAP_RES_SEARCH_REFERENCE or else it's something we can't handle. Some versions +of LDAP do not define LDAP_RES_SEARCH_REFERENCE (LDAP v1 is one, it seems). So +we don't provide that functionality when we can't. :-) */ + +if (rc != LDAP_RES_SEARCH_RESULT +#ifdef LDAP_RES_SEARCH_REFERENCE + && rc != LDAP_RES_SEARCH_REFERENCE +#endif + ) { *errmsg = string_sprintf("ldap_result returned unexpected code %d", rc); goto RETURN_ERROR; @@ -793,12 +885,15 @@ We need to parse the message to find out exactly what's happened. */ #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2 ldap_rc = rc; - ldap_parse_rc = ldap_parse_result(lcp->ld, result, &rc, CSS &matched, + ldap_parse_rc = ldap_parse_result(lcp->ld, result, &rc, CSS &matched, CSS &error2, NULL, NULL, 0); DEBUG(D_lookup) debug_printf("ldap_parse_result: %d\n", ldap_parse_rc); - if (ldap_parse_rc < 0 && - (ldap_parse_rc != LDAP_NO_RESULTS_RETURNED || - ldap_rc != LDAP_RES_SEARCH_REFERENCE)) + if (ldap_parse_rc < 0 && + (ldap_parse_rc != LDAP_NO_RESULTS_RETURNED + #ifdef LDAP_RES_SEARCH_REFERENCE + || ldap_rc != LDAP_RES_SEARCH_REFERENCE + #endif + )) { *errmsg = string_sprintf("ldap_parse_result failed %d", ldap_parse_rc); goto RETURN_ERROR; @@ -821,18 +916,27 @@ We need to parse the message to find out exactly what's happened. */ (1) If we get LDAP_SIZELIMIT_EXCEEDED, just carry on, to return the truncated result list. - (2) The range of errors defined by LDAP_NAME_ERROR generally mean "that + (2) If we get LDAP_RES_SEARCH_REFERENCE, also just carry on. This was a + submitted patch that is reported to "do the right thing" with Solaris + LDAP libraries. (The problem it addresses apparently does not occur with + Open LDAP.) + + (3) The range of errors defined by LDAP_NAME_ERROR generally mean "that object does not, or cannot, exist in the database". For those cases we fail the lookup. - (3) All other non-successes here are treated as some kind of problem with + (4) All other non-successes here are treated as some kind of problem with the lookup, so return DEFER (which is the default in error_yield). */ DEBUG(D_lookup) debug_printf("ldap_parse_result yielded %d: %s\n", rc, ldap_err2string(rc)); -if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED) +if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED + #ifdef LDAP_RES_SEARCH_REFERENCE + && rc != LDAP_RES_SEARCH_REFERENCE + #endif + ) { *errmsg = string_sprintf("LDAP search failed - error %d: %s%s%s%s%s", rc, @@ -954,8 +1058,9 @@ BOOL defer_break = FALSE; int timelimit = LDAP_NO_LIMIT; int sizelimit = LDAP_NO_LIMIT; int tcplimit = 0; -int dereference = LDAP_DEREF_NEVER; int sep = 0; +int dereference = LDAP_DEREF_NEVER; +void* referrals = LDAP_OPT_ON; uschar *url = ldap_url; uschar *p; uschar *user = NULL; @@ -1009,7 +1114,29 @@ while (strncmpic(url, US"ldap", 4) != 0) DEBUG(D_lookup) debug_printf("%s\n", *errmsg); return DEFER; } + #endif + #ifdef LDAP_OPT_REFERRALS + else if (strncmpic(name, US"REFERRALS=", namelen) == 0) + { + if (strcmpic(value, US"follow") == 0) referrals = LDAP_OPT_ON; + else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF; + else + { + *errmsg = string_sprintf("LDAP option REFERRALS is not \"follow\" " + "or \"nofollow\""); + DEBUG(D_lookup) debug_printf("%s\n", *errmsg); + return DEFER; + } + } + #else + else if (strncmpic(name, US"REFERRALS=", namelen) == 0) + { + *errmsg = string_sprintf("LDAP_OP_REFERRALS not defined in this LDAP " + "library - cannot use \"referrals\""); + DEBUG(D_lookup) debug_printf("%s\n", *errmsg); + return DEFER; + } #endif else @@ -1058,8 +1185,8 @@ if (user != NULL) DEBUG(D_lookup) debug_printf("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d " - "dereference=%d\n", user, password, sizelimit, timelimit, tcplimit, - dereference); + "dereference=%d referrals=%s\n", user, password, sizelimit, timelimit, + tcplimit, dereference, (referrals == LDAP_OPT_ON)? "on" : "off"); /* If the request is just to check authentication, some credentials must be given. The password must not be empty because LDAP binds with an empty @@ -1096,7 +1223,8 @@ if (Ustrncmp(p, "://", 3) != 0) if (eldap_default_servers == NULL || p[3] != '/') { return perform_ldap_search(url, NULL, 0, search_type, res, errmsg, - &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference); + &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, + referrals); } /* Loop through the default servers until OK or FAIL */ @@ -1113,7 +1241,8 @@ while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL port = Uatoi(colon+1); } rc = perform_ldap_search(url, server, port, search_type, res, errmsg, - &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference); + &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, + referrals); if (rc != DEFER || defer_break) return rc; } @@ -1130,7 +1259,7 @@ return DEFER; are handled by a common function, with a flag to differentiate between them. The handle and filename arguments are not used. */ -int +static int eldap_find(void *handle, uschar *filename, uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { @@ -1139,7 +1268,7 @@ do_cache = do_cache; return(control_ldap_search(ldap_url, SEARCH_LDAP_SINGLE, result, errmsg)); } -int +static int eldapm_find(void *handle, uschar *filename, uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { @@ -1148,7 +1277,7 @@ do_cache = do_cache; return(control_ldap_search(ldap_url, SEARCH_LDAP_MULTIPLE, result, errmsg)); } -int +static int eldapdn_find(void *handle, uschar *filename, uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { @@ -1174,7 +1303,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_AUTH, result, errmsg)); /* See local README for interface description. */ -void * +static void * eldap_open(uschar *filename, uschar **errmsg) { return (void *)(1); /* Just return something non-null */ @@ -1189,7 +1318,7 @@ return (void *)(1); /* Just return something non-null */ /* See local README for interface description. Make sure that eldap_dn does not refer to reclaimed or worse, freed store */ -void +static void eldap_tidy(void) { LDAP_CONNECTION *lcp = NULL; @@ -1285,7 +1414,7 @@ quote_ldap_dn, respectively. */ -uschar * +static uschar * eldap_quote(uschar *s, uschar *opt) { register int c; @@ -1404,6 +1533,66 @@ else return quoted; } -#endif /* LOOKUP_LDAP */ + + +/************************************************* +* Version reporting entry point * +*************************************************/ + +/* See local README for interface description. */ + +#include "../version.h" + +void +ldap_version_report(FILE *f) +{ +#ifdef DYNLOOKUP +fprintf(f, "Library version: LDAP: Exim version %s\n", EXIM_VERSION_STR); +#endif +} + + +static lookup_info ldap_lookup_info = { + US"ldap", /* lookup name */ + lookup_querystyle, /* query-style lookup */ + eldap_open, /* open function */ + NULL, /* check function */ + eldap_find, /* find function */ + NULL, /* no close function */ + eldap_tidy, /* tidy function */ + eldap_quote, /* quoting function */ + ldap_version_report /* version reporting */ +}; + +static lookup_info ldapdn_lookup_info = { + US"ldapdn", /* lookup name */ + lookup_querystyle, /* query-style lookup */ + eldap_open, /* sic */ /* open function */ + NULL, /* check function */ + eldapdn_find, /* find function */ + NULL, /* no close function */ + eldap_tidy, /* sic */ /* tidy function */ + eldap_quote, /* sic */ /* quoting function */ + NULL /* no version reporting (redundant) */ +}; + +static lookup_info ldapm_lookup_info = { + US"ldapm", /* lookup name */ + lookup_querystyle, /* query-style lookup */ + eldap_open, /* sic */ /* open function */ + NULL, /* check function */ + eldapm_find, /* find function */ + NULL, /* no close function */ + eldap_tidy, /* sic */ /* tidy function */ + eldap_quote, /* sic */ /* quoting function */ + NULL /* no version reporting (redundant) */ +}; + +#ifdef DYNLOOKUP +#define ldap_lookup_module_info _lookup_module_info +#endif + +static lookup_info *_lookup_list[] = { &ldap_lookup_info, &ldapdn_lookup_info, &ldapm_lookup_info }; +lookup_module_info ldap_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 3 }; /* End of lookups/ldap.c */