From: <svn...@op...> - 2010-01-10 18:27:51
|
Author: scriptor Date: Sun Jan 10 19:27:36 2010 New Revision: 5996 URL: http://www.opensync.org/changeset/5996 Log: I have added support for SSL/TLS when libldap60 from the Mozilla LDAP C SDK is used. In this case certain SSL/TLS related steps have to be taken from inside the LDAP plugin, whereas this is not necessary when the plugin is linked against libldap from openldap. When the LDAP server to be used is slapd from openldap, then it is better 1. to link the LDAP plugin against libldap from openldap and 2. to use the openssl utilities ( http://www.openssl.org/ ) for the configuration. Whereas, when is the LDAP server is ns-slapd (= 389 Directory Server = Fedora Directory Server), then it is better 1. to link the LDAP plugin against libldap60 from the Mozilla LDAP C SDK and 2. to use certutil etc. from the "Network Security Services (NSS)" ("nss-tools" package under fedora/redhat) for configuration purposes. At least, as far as SSL/TLS is concerned, it seems, that it is not possible, to cross this line by intermixing the different libraries and certificates/keys. With the NSS libraries talking to an openldap LDAP server, protocol errors occur. Whereas with the openldap libraries talking to ns-slapd (389-ds = fedora-ds), the protocol seems to be ok, but the configuration is difficult. The openssl certificates do not seem to be accepted by the NSS side ("Bad certificate"). However, my last word on this has not been spoken, yet. Added: plugins/ldap-sync/src/ldap_ssl.c Modified: plugins/ldap-sync/src/ldap-sync plugins/ldap-sync/src/ldap_connect.c plugins/ldap-sync/src/ldap_plugin.c plugins/ldap-sync/src/ldap_plugin.h plugins/ldap-sync/src/ldap_sasl.c Modified: plugins/ldap-sync/src/ldap-sync ============================================================================== --- plugins/ldap-sync/src/ldap-sync Sun Jan 10 19:26:47 2010 (r5995) +++ plugins/ldap-sync/src/ldap-sync Sun Jan 10 19:27:36 2010 (r5996) @@ -33,6 +33,36 @@ </AdvancedOption> <AdvancedOption> + <Name>mozldap_tls_certdb</Name> + <Type>string</Type> + <Value></Value> + </AdvancedOption> + + <AdvancedOption> + <Name>mozldap_tls_certnickname</Name> + <Type>string</Type> + <Value></Value> + </AdvancedOption> + + <AdvancedOption> + <Name>mozldap_tls_keydb</Name> + <Type>string</Type> + <Value></Value> + </AdvancedOption> + + <AdvancedOption> + <Name>mozldap_tls_keynickname</Name> + <Type>string</Type> + <Value></Value> + </AdvancedOption> + + <AdvancedOption> + <Name>mozldap_tls_keypassword</Name> + <Type>string</Type> + <Value></Value> + </AdvancedOption> + + <AdvancedOption> <!-- Anonymous bind --> <Name>anonymous</Name> <Type>string</Type> Modified: plugins/ldap-sync/src/ldap_connect.c ============================================================================== --- plugins/ldap-sync/src/ldap_connect.c Sun Jan 10 19:26:47 2010 (r5995) +++ plugins/ldap-sync/src/ldap_connect.c Sun Jan 10 19:27:36 2010 (r5996) @@ -147,7 +147,17 @@ ldap_initialize(&(sinkenv->ld), sinkenv->servername); #else # ifdef USE_MOZLDAP - sinkenv->ld = ldap_init(sinkenv->servername, sinkenv->serverport); + if (sinkenv->encryption == TRUE) { + if (!ldap_plugin_prepare_mozldap_ssl(ctx, sinkenv, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ldap_plugin_prepare_mozldap_ssl() has failed.\n", __FILE__, __LINE__); + } + + goto error; + } + } else { + sinkenv->ld = ldap_init(sinkenv->servername, sinkenv->serverport); + } // if (sinkenv->encryption == TRUE) # else sinkenv->ld = NULL; # endif @@ -158,15 +168,10 @@ goto error; } - } else { - - if (sinkenv->encryption == FALSE) { - sinkenv->url = g_strdup_printf("%s://%s:%i", sinkenv->protocol, sinkenv->servername, sinkenv->serverport); - - } else { - sinkenv->url = g_strdup_printf("%s://%s:%i", sinkenv->protocol, sinkenv->servername, sinkenv->serverport); - } + + } else { // if (ldap_is_ldap_url) + sinkenv->url = g_strdup_printf("%s://%s:%i", sinkenv->protocol, sinkenv->servername, sinkenv->serverport); osync_trace(TRACE_INTERNAL, "%s:%i: INFO: url = \"%s\"\n", __FILE__, __LINE__, sinkenv->url); @@ -175,17 +180,28 @@ ldap_initialize(&(sinkenv->ld), sinkenv->url); #else # ifdef USE_MOZLDAP - sinkenv->ld = ldap_init(sinkenv->servername, sinkenv->serverport); + if (sinkenv->encryption == TRUE) { + + if (!ldap_plugin_prepare_mozldap_ssl(ctx, sinkenv, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ldap_plugin_check_ldap_schema_support() has failed.\n", __FILE__, __LINE__); + } + + goto error; + } + } else { + sinkenv->ld = ldap_init(sinkenv->servername, sinkenv->serverport); + } # else sinkenv->ld = NULL; -#endif +# endif #endif if (sinkenv->ld == NULL ) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "Could not connect to \"%s\"", sinkenv->url); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: Could not connect to \"%s\"", __FILE__, __LINE__, sinkenv->url); goto error; } } @@ -195,11 +211,9 @@ error: - if (!osync_error_is_set(error)) osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); - osync_context_report_osyncwarning(ctx, *error); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } @@ -474,21 +488,16 @@ // Set LDAP version on created connection - ldap_plugin_set_ldap_protocol(ctx, info, sinkenv, &error); + ldap_plugin_set_ldap_protocol(ctx, sinkenv, &error); - // Set encryption on the LDAP connection, if requested, - // provided that the protocol chosen does NOT contain this - // information, yet; i.e., it MUST NOT be equal to "ldaps": if (sinkenv->encryption) { - if (strcmp(sinkenv->protocol, "ldaps")) { - if (!ldap_plugin_encrypt_connection(ctx, sinkenv, &error)) { - if (!osync_error_is_set(&error)) { - osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: Could not start encryption.\n", __FILE__, __LINE__); - } - - goto error; + if (!ldap_plugin_setup_encryption(ctx, sinkenv, &error)) { + if (!osync_error_is_set(&error)) { + osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ldap_plugin_setup_encryption() has failed.", __FILE__, __LINE__); } + + goto error; } } @@ -505,6 +514,9 @@ + // The hash values for the opensync database are based on the + // entryCSN attribute. If it cannot be found we will fall back to + // the modifyTimestamp attribute. if (!ldap_plugin_check_for_entryCSN(ctx, sinkenv, &error)) { if (!osync_error_is_set(&error)) { osync_error_set(&error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_check_for entryCSN() has failed.", __FILE__, __LINE__); @@ -700,11 +712,11 @@ * * @returns TRUE on success, FALSE on error */ -osync_bool ldap_plugin_set_ldap_protocol (OSyncContext *ctx, OSyncPluginInfo *info, sink_environment *sinkenv, OSyncError **error) +osync_bool ldap_plugin_set_ldap_protocol (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) { int *ldap_protocol_version = NULL; - osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, (void *)ctx, (void *)info, (void *)sinkenv, (void *)error); + osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)error); if (ctx == NULL) { @@ -712,12 +724,6 @@ goto error; } - - if (info == NULL) { - osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: info = NULL. Returning.\n", __FILE__, __LINE__); - goto error; - } - if (sinkenv == NULL) { osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); goto error; @@ -772,65 +778,6 @@ -/** - * @brief Encrypts the connection to the LDAP server by using TLS/SSL. - * - * @param ctx The libopensync context. - * @param sinkenv The object type specific environment. - * @param error The libopensync error pointer. - * - * @returns TRUE on success, FALSE in case of any error. - */ - -osync_bool ldap_plugin_encrypt_connection (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) -{ - int ldap_errno = 0; - - - osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)error); - - - if (ctx == NULL) { - osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ctx = NULL. Returning.\n", __FILE__, __LINE__); - goto error; - } - - - if (sinkenv == NULL) { - osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); - goto error; - } - - - ldap_errno = ldap_start_tls_s(sinkenv->ld, NULL, NULL); - if (ldap_errno != LDAP_SUCCESS) { - char *ldap_error = ldap_plugin_report_ldap_error(sinkenv, __FILE__, __LINE__, ldap_errno); - - - if (ldap_error == NULL) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore.\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername); - } else { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore: \"%s\"\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, ldap_error); - } - - goto error; - } - - - osync_trace(TRACE_EXIT, "%s", __func__); - return TRUE; - - -error: - if (!osync_error_is_set(error)) - osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); - - osync_context_report_osyncerror(ctx, *error); - osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); - return FALSE; -} - - /** @@ -962,6 +909,30 @@ tmp_authzid = g_strdup(authzid); tmp_realm = g_strdup(realm); + + if (tmp_authmech == NULL) { + osync_trace(TRACE_ERROR, "%s:%i: WARNING: tmp_authmech = NULL. Setting it to \"SIMPLE\".", __FILE__, __LINE__); + tmp_authmech = g_strdup("SIMPLE"); + + if (tmp_authmech == NULL) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: tmp_authmech is still NULL. Giving up.", __FILE__, __LINE__); + rv = FALSE; + goto out1; + } + } + + if (tmp_authmech[0] == 0) { + osync_trace(TRACE_ERROR, "%s:%i: WARNING: tmp_authmech is a empty string. Setting it to \"SIMPLE\".", __FILE__, __LINE__); + g_free(tmp_authmech); + + tmp_authmech = g_strdup("SIMPLE"); + + if (tmp_authmech == NULL) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: tmp_authmech is still NULL. Giving up.", __FILE__, __LINE__); + rv = FALSE; + goto out1; + } + } // Load default parameters into a libldap specific struct defaults = ldap_plugin_lutil_sasl_defaults(sinkenv->ld, tmp_authmech, tmp_realm, tmp_authcid, passwd.bv_val, tmp_authzid); @@ -973,8 +944,61 @@ // Try and bind with the LDAP server +#ifdef USE_MOZLDAP + // Currently this is called in both cases: STARTTLS and LDAP over SSL. + + if (!strcmp(tmp_authmech, "EXTERNAL")) { +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and EXTERNAL: Right before ldap_plugin_setup_mozldap_ssl_authentication()\n", __FILE__, __LINE__); +#endif + if (!ldap_plugin_setup_mozldap_ssl_authentication(ctx, sinkenv, error)) { + if (!osync_error_is_set(error)) + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_plugin_setup_mozldap_ssl_authentication() has failed.\n", __FILE__, __LINE__); + + rv = FALSE; + goto out1; + } + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and EXTERNAL: Right before ldap_sasl_bind_s\n", __FILE__, __LINE__); +#endif + + ldap_errno = ldap_sasl_bind_s(sinkenv->ld, NULL, LDAP_SASL_EXTERNAL, NULL, NULL, NULL, NULL); + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and EXTERNAL: Right after ldap_sasl_bind_s\n", __FILE__, __LINE__); +#endif + + } else { +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP: Right before ldap_sasl_interactive_bind_s\n", __FILE__, __LINE__); +#endif + + ldap_errno = ldap_sasl_interactive_bind_s(sinkenv->ld, NULL, tmp_authmech, NULL, NULL, sasl_flags, (LDAP_SASL_INTERACT_PROC *)ldap_plugin_lutil_sasl_interact, (void *)defaults); + + } + + + + +#else + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_OPENLDAP: Right before ldap_sasl_interactive_bind_s\n", __FILE__, __LINE__); +#endif + ldap_errno = ldap_sasl_interactive_bind_s(sinkenv->ld, NULL, tmp_authmech, NULL, NULL, sasl_flags, (LDAP_SASL_INTERACT_PROC *)ldap_plugin_lutil_sasl_interact, (void *)defaults); + // ldap_errno = ldap_sasl_bind_s(sinkenv->ld, NULL, tmp_authmech, NULL, NULL, NULL, NULL); + +#endif + + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: ldap_errno = %i\n", __FILE__, __LINE__, ldap_errno); +#endif + + if (ldap_errno != LDAP_SUCCESS) { char *ldap_error = ldap_plugin_report_ldap_error(sinkenv, __FILE__, __LINE__, ldap_errno); @@ -1032,7 +1056,9 @@ } - if (passwd.bv_len == 0) { + // Give hint about empty password only for authentication mechanisms + // other than EXTERNAL + if (passwd.bv_len == 0 && strcmp(tmp_authmech, "EXTERNAL")) { if (ldap_error == NULL) { osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind with \"%s\" as \"%s\" with an empty password, using \"%s\" as authentication mechanism. Maybe the LDAP server does not allow an anonymous bind.", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, sinkenv->authcid, tmp_authmech); } else { @@ -1139,7 +1165,6 @@ if (!osync_error_is_set(error)) osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); - osync_context_report_osyncwarning(ctx, *error); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); } @@ -1171,8 +1196,9 @@ { LDAPMessage *res = NULL, *res2 = NULL, *res3 = NULL, *res4 = NULL; int i = 0; - struct berval **ber = NULL, **ber2 = NULL; - char *search_base = NULL; + struct berval **ber = NULL, **ber2 = NULL; + BerElement *ber3 = NULL; + char *search_base = NULL, *attr = NULL; osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)ldap_schema, (void *)result, (void *)error); @@ -1209,7 +1235,12 @@ // Get the relevant search_base. It may differ from server to server. - if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, "", "(objectClass=*)", LDAP_SCOPE_BASE, OPERATIONAL_ATTRIBUTES, sinkenv, TRUE, &res3, error)) { +#ifdef USE_MOZLDAP + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, "", "(objectClass=*)", LDAP_SCOPE_BASE, FEDORADSsubschemaSubentry, sinkenv, TRUE, &res3, error)) +#else + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, "", "(objectClass=*)", LDAP_SCOPE_BASE, OPERATIONAL_ATTRIBUTES, sinkenv, TRUE, &res3, error)) +#endif + { if (!osync_error_is_set(error)) { osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); } @@ -1228,31 +1259,87 @@ } - res4 = ldap_first_entry(sinkenv->ld, res3); - if (res4) { - ber2 = ldap_get_values_len(sinkenv->ld, res4, "subschemaSubentry"); - if (ber2) { - while (ber2[i]) { - if (ber2[i]->bv_val) { - search_base = g_strdup(ber2[i]->bv_val); - break; + for (res4 = ldap_first_entry(sinkenv->ld, res3); + res4 != NULL; + res4 = ldap_next_entry(sinkenv->ld, res3)) + { + if (res4) { +#ifdef DEBUG_subschema_discovery + char *dn = ldap_get_dn(sinkenv->ld, res4); + + + if (dn) { + ldap_plugin_printf("%s:%i: dn = \"%s\"\n", __FILE__, __LINE__, dn); + ldap_memfree(dn); + dn = NULL; + } +#endif + + for (attr = ldap_first_attribute(sinkenv->ld, res4, &ber3); + attr != NULL; + attr = ldap_next_attribute(sinkenv->ld, res4, ber3) + ) + { +#ifdef DEBUG_subschema_discovery + ldap_plugin_printf("%s:%i: %s: ", __FILE__, __LINE__, attr ? attr : "NULL."); +#endif + + if (strcmp(attr, "subschemaSubentry")) { +#ifdef DEBUG_subschema_discovery + ldap_plugin_printf("\n"); +#endif + continue; } - i++; - } - } - } + ber2 = ldap_get_values_len(sinkenv->ld, res4, attr); + if (ber2) { + i = 0; + while (ber2[i]) { + if (ber2[i]->bv_val) { +#ifdef DEBUG_subschema_discovery + ldap_plugin_printf("%s ", ber2[i]->bv_val); +#endif + search_base = g_strdup(ber2[i]->bv_val); + break; + } + i++; + } // while (ber2[i]) + +#ifdef DEBUG_subschema_discovery + ldap_plugin_printf("\n-------------------------\n"); +#endif + if (search_base != NULL && search_base[0] != 0) { + break; + } + } else { + ldap_plugin_printf("%s:%i: WARNING: ber2 = NULL.", __FILE__, __LINE__); + } + + ldap_memfree(attr); + } + + if (search_base != NULL && search_base[0] != 0) { + break; + } + } + } - // We haven't found the real search_base. So let's assume, it is - // "cn=Subschema". + + // We haven't found the real search_base, yet. So let's assume, it is + // "cn=Subschema" or "cn=schema" for ns-slapd. if (search_base == NULL) { - osync_trace(TRACE_INTERNAL, "%s:%i: WARNING: search_base = NULL. Setting it to \"cn=Subschema\".", __FILE__, __LINE__); +#ifdef USE_MOZLDAP + ldap_plugin_printf("%s:%i: WARNING: search_base = NULL. Setting it to \"cn=schema\".", __FILE__, __LINE__); + search_base = g_strdup("cn=schema"); +#else + ldap_plugin_printf("%s:%i: WARNING: search_base = NULL. Setting it to \"cn=Subschema\".", __FILE__, __LINE__); search_base = g_strdup("cn=Subschema"); +#endif } @@ -1277,8 +1364,10 @@ res2 = ldap_first_entry(sinkenv->ld, res); if (!res2) { /* No objectClass entries found */ - if (res) + if (res) { ldap_msgfree(res); + res = NULL; + } // Look for all the LDAP schemata (as used by ns-slapd from the // fedora directory server): @@ -1331,6 +1420,9 @@ if (res) ldap_msgfree(res); + if (res3) + ldap_msgfree(res3); + if (search_base) g_free(search_base); @@ -1346,6 +1438,9 @@ if (res) ldap_msgfree(res); + if (res3) + ldap_msgfree(res3); + if (search_base) g_free(search_base); @@ -1573,6 +1668,9 @@ const char *scope_str = NULL; const char *userattributes[2]; userattributes[1] = NULL; + char *msg = NULL; + int rv = 0; + if (results == NULL) { @@ -1588,8 +1686,12 @@ // openldap version userattributes[0] = LDAP_ALL_USER_ATTRIBUTES; #else +# ifdef LDAP_ALL_USER_ATTRS // mozldap version + userattributes[0] = LDAP_ALL_USER_ATTRS; +# else userattributes[0] = NULL; +# endif #endif break; @@ -1611,21 +1713,27 @@ userattributes[0] = "modifyTimestamp"; break; + case FEDORADSsubschemaSubentry: + userattributes[0] = "subschemaSubentry"; + break; + default: #ifdef LDAP_ALL_USER_ATTRIBUTES // openldap version userattributes[0] = LDAP_ALL_USER_ATTRIBUTES; #else - // xxx jl: TODO ??????????????? + +# ifdef LDAP_ALL_USER_ATTRS // mozldap version + userattributes[0] = LDAP_ALL_USER_ATTRS; +# else userattributes[0] = NULL; +# endif + #endif }; - char *msg = NULL; - int rv = 0; - if (ldap_handle == NULL) { osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_handle = NULL.\n", __FILE__, __LINE__); Modified: plugins/ldap-sync/src/ldap_plugin.c ============================================================================== --- plugins/ldap-sync/src/ldap_plugin.c Sun Jan 10 19:26:47 2010 (r5995) +++ plugins/ldap-sync/src/ldap_plugin.c Sun Jan 10 19:27:36 2010 (r5996) @@ -666,6 +666,10 @@ sinkenv->authcid = NULL; sinkenv->authzid = NULL; sinkenv->realm = NULL; + sinkenv->mozldap_tls_certdb = NULL; + sinkenv->mozldap_tls_certnickname = NULL; + sinkenv->mozldap_tls_keydb = NULL; + sinkenv->mozldap_tls_keynickname = NULL; // also known as "token name" sinkenv->searchbase = NULL; sinkenv->searchfilter = NULL; sinkenv->storebase = NULL; @@ -739,6 +743,21 @@ if (!strcmp(name, "realm")) sinkenv->realm = g_strdup(val); + if (!strcmp(name, "mozldap_tls_certdb")) + sinkenv->mozldap_tls_certdb = g_strdup(val); + + if (!strcmp(name, "mozldap_tls_certnickname")) + sinkenv->mozldap_tls_certnickname = g_strdup(val); + + if (!strcmp(name, "mozldap_tls_keydb")) + sinkenv->mozldap_tls_keydb = g_strdup(val); + + if (!strcmp(name, "mozldap_tls_keynickname")) + sinkenv->mozldap_tls_keynickname = g_strdup(val); + + if (!strcmp(name, "mozldap_tls_keypassword")) + sinkenv->mozldap_tls_keypassword = g_strdup(val); + if (!strcmp(name, "anonymous")) { #ifdef DEBUG_auth ldap_plugin_printf("%s:%i: Previous setting of anonymous was: %i", __FILE__, __LINE__, sinkenv->anonymous); Modified: plugins/ldap-sync/src/ldap_plugin.h ============================================================================== --- plugins/ldap-sync/src/ldap_plugin.h Sun Jan 10 19:26:47 2010 (r5995) +++ plugins/ldap-sync/src/ldap_plugin.h Sun Jan 10 19:27:36 2010 (r5996) @@ -161,6 +161,8 @@ ///< ldap_format_do_detect_plain_as_ldap_format(). #define DEBUG_auth 1 +#define DEBUG_ssl 1 +#define DEBUG_subschema_discovery 1 #define DEBUG_configuration 1 #define DEBUG_detection 1 #define DEBUG_ldapdata_from_server 1 @@ -180,6 +182,8 @@ #undef DEBUG_auth +#undef DEBUG_ssl +#undef DEBUG_subschema_discovery #undef DEBUG_configuration #undef DEBUG_detection #undef DEBUG_ldapdata_from_server @@ -234,6 +238,7 @@ ///< LDAP_ALL_USER_ATTRIBUTES and ///< LDAP_ALL_OPERATIONAL_ATTRIBUTES. ///< This is sloppy. +#define FEDORADSsubschemaSubentry 5 ///< dto. @@ -394,6 +399,24 @@ ///< as one person, act as a different person. char *realm; ///< Name of a group of users; independend of the ///< domain name + char *mozldap_tls_certdb; ///< The TLS certificate database. For example: + ///< /home/user1/.netscape/cert8.db + ///< The NSS certificate and key database are + ///< part or the Network Security Services + ///< (= NSS), which is a set of libraries + ///< for SSL/TLS, as used by the Mozilla LDAP + ///< C SDK. + ///< + ///< Cf. http://www.mozilla.org/projects/security/pki/nss/ + ///< + char *mozldap_tls_certnickname; ///< The TLS certificate nick-name. For example: + ///< ldap_user + char *mozldap_tls_keydb; ///< The TLS key database. For example: + ///< /home/user1/.netscape/key3.db + char *mozldap_tls_keynickname; ///< Also known as "token name". For example: + ///< NSS Certificate DB + char *mozldap_tls_keypassword; ///< The password for the TLS key. For example: + ///< secret char *bindpwd; ///< Bind password char *searchbase; ///< Base DN for any searches char *searchfilter; ///< Search filter @@ -490,7 +513,6 @@ void ldap_plugin_disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata); osync_bool ldap_plugin_do_connect (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); osync_bool ldap_plugin_do_disconnect (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); -osync_bool ldap_plugin_encrypt_connection (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); void ldap_plugin_free_ldap_entry (ldap_entry *entry); void ldap_plugin_free_ldap_entries (GList *entrylist); void ldap_plugin_free_ldapmods_list(GList *ldapmods); @@ -506,7 +528,7 @@ char *ldap_plugin_hash_append(OSyncContext *ctx, char *hash, const sink_environment *sinkenv, const ldap_entry *entry, OSyncError **error); osync_bool ldap_plugin_makebind(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); osync_bool ldap_plugin_rewrite_ldap_entry(ldap_entry *entry, GList *modifications, OSyncError **error); -osync_bool ldap_plugin_set_ldap_protocol (OSyncContext *ctx, OSyncPluginInfo *info, sink_environment *sinkenv, OSyncError **error); +osync_bool ldap_plugin_set_ldap_protocol (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); @@ -573,5 +595,19 @@ #endif + + +// ldap_ssl.c: +osync_bool ldap_plugin_setup_encryption(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); +#ifdef USE_MOZLDAP +osync_bool ldap_plugin_setup_mozldap_ssl_authentication(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); +#endif +osync_bool ldap_plugin_starttls (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); +#ifdef USE_MOZLDAP +osync_bool ldap_plugin_prepare_mozldap_ssl(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); +#endif + + + #endif // #ifndef _LDAP_PLUGIN_H Modified: plugins/ldap-sync/src/ldap_sasl.c ============================================================================== --- plugins/ldap-sync/src/ldap_sasl.c Sun Jan 10 19:26:47 2010 (r5995) +++ plugins/ldap-sync/src/ldap_sasl.c Sun Jan 10 19:27:36 2010 (r5996) @@ -329,7 +329,7 @@ /** * @brief This function was intended to check which SASL authentication - * mechanism are supported by the LDAP server, i.e. by libldap. + * mechanism are supported by the LDAP server. * However, this question can better be answered by command line * tools: * Added: plugins/ldap-sync/src/ldap_ssl.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ plugins/ldap-sync/src/ldap_ssl.c Sun Jan 10 19:27:36 2010 (r5996) @@ -0,0 +1,482 @@ +/* + * ldap-sync - A plugin for the opensync framework + * + * $Id$ + * Copyright (C) 2009 Juergen Leising <jle...@us...> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + + + +#include "ldap_plugin.h" + + + +/** + * @defgroup ssl Functions for setting up encryption in form of SSL/TLS. + * + * @brief While in libldap from openldap the SSL/TLS related work is mostly done by libldap itself, libldap60 from the Mozilla LDAP C SDK needs some preparation. + * + * LDAP and SSL/TLS work together in two forms: + * + * 1) LDAP over SSL/TLS: "ldaps://" (typically port 636 on the server side) + * + * 2) SSL/TLS inside of LDAP: "ldap://": STARTTLS (typicalls the usual port 389 on the server side) + * + * @ingroup ldap_sync + * + * @{ + */ + + + + +/** + * @brief This function decides whether or not to call STARTTLS. + * This function is used both with libldap from openldap and with + * libldap60 from the Mozilla LDAP C SDK. + * + * @param ctx The libopensync context. + * @param sinkenv The object type specific environment + * @param error The libopensync error pointer + * + * @returns TRUE on success, FALSE in case of any errors + + * + * + */ + +osync_bool +ldap_plugin_setup_encryption(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) +{ + osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)error); + + + if (ctx == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ctx = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (sinkenv == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + // Set encryption on the LDAP connection, if requested, + // provided that the protocol chosen does NOT contain this + // information, yet; i.e., it MUST NOT be equal to "ldaps": + + if (strcmp(sinkenv->protocol, "ldaps") && (strcmp(sinkenv->protocol, "LDAPS"))) { + // Then invoke STARTTLS, i.e. SSL/TLS inside of LDAP + if (!ldap_plugin_starttls(ctx, sinkenv, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: Could not start encryption.\n", __FILE__, __LINE__); + } + + goto error; + } + } + // otherwise it is already done by the LDAP library: LDAP over SSL/TLS. + // At least this is true for the libldap from openldap. + + + osync_trace(TRACE_EXIT, "%s", __func__); + return TRUE; + +error: + if (!osync_error_is_set(error)) + osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); + + osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); + + return FALSE; +} + + + + + + + + + + +/** + * @brief This function is called only, when the plugin is linked against + * libldap60 from the Mozilla LDAP C SDK. It performs the + * initial setups with regard to SSL/TSL, which are necessary for + * this library (as opposed to libldap from openldap). + * Please note: + * 1) Encryption is one thing, + * 2) Authentication based on the contents of an SSL/TLS certificate + * is a different story. 1) does not necessarily lead to 2). + * See also ldap_plugin_setup_mozldap_ssl_authentication() + * + * + * @param ctx The libopensync context. + * @param sinkenv The object type specific environment + * @param error The libopensync error pointer + * + * @returns TRUE on success, FALSE in case of any errors + */ + + +#ifdef USE_MOZLDAP +osync_bool +ldap_plugin_prepare_mozldap_ssl(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) +{ + char *certdb = NULL; + struct stat stat_buffer; + + + osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)error); + + + if (ctx == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ctx = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + if (sinkenv == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + if (sinkenv->servername == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv->servername = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (sinkenv->protocol == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv->protocol = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + if (sinkenv->mozldap_tls_certdb) { + certdb = g_strdup(sinkenv->mozldap_tls_certdb); + } else { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv->mozldap_tls_certdb = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + if (certdb == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: certdb = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + + errno = 0; + if (stat(certdb, &stat_buffer)) { + osync_trace(TRACE_ERROR, "%s:%i: WARNING: \"%s\" could not be found: \"%s\"\n", __FILE__, __LINE__, certdb, strerror(errno)); + } + + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and encryption: Right before ldapssl_client_init().\n", __FILE__, __LINE__); +#endif + + // Initialize the client + if (ldapssl_client_init(certdb, NULL) < 0) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldapssl_clientauth_init() has failed. certdb = \"%s\"", __FILE__, __LINE__, certdb); + goto error; + } + + + if (certdb) { + g_free(certdb); + certdb = NULL; + } + + + + + // Get a handle to an LDAP connection + if (!strcmp(sinkenv->protocol, "ldaps") || (!strcmp(sinkenv->protocol, "LDAPS"))) { +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and encryption: Right before ldapssl_init().\n", __FILE__, __LINE__); +#endif + + // For LDAP over SSL/TLS: + sinkenv->ld = ldapssl_init(sinkenv->servername, sinkenv->serverport, LDAPSSL_AUTH_CERT); + // Be careful: Setting LDAPSSL_AUTH_WEAK here has led to hangs with my tests. + // I don't know why. + // Cf. /usr/include/mozldap/ldap_ssl.h + } else { +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and encryption: Right before ldap_init().\n", __FILE__, __LINE__); +#endif + + // For SSL/TLS inside LDAP, also known as STARTTLS: + sinkenv->ld = ldap_init(sinkenv->servername, sinkenv->serverport); + } + + + osync_trace(TRACE_EXIT, "%s", __func__); + return TRUE; + + +error: + if (certdb) { + g_free(certdb); + certdb = NULL; + } + + if (!osync_error_is_set(error)) + osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); + + osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); + return FALSE; +} +#endif + + + + + + + +/** + * @brief This function is intended for libldap60 from the Mozilla LDAP C SDK, + * only. It performs authentication related steps in the course of + * setting up the SSL/TLS layer. Please note: + * 1) Encryption is one thing, + * 2) Authentication based on the contents of an SSL/TLS certificate + * is a different story. 1) does not necessarily lead to 2). + * See also ldap_plugin_prepare_mozldap_ssl() + * + * @param ctx The libopensync context. + * @param sinkenv The object type specific environment. + * @param error The libopensync error pointer. + * + * @returns TRUE on success, FALSE in case of any error. + */ + +#ifdef USE_MOZLDAP +osync_bool ldap_plugin_setup_mozldap_ssl_authentication(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) +{ + int ldap_errno = 0; + char *ldap_error = NULL; + char *keynickname = NULL; + char *keypasswd = NULL; + char *certnickname = NULL; + + + osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)error); + + + if (ctx == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ctx = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + if (sinkenv == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (sinkenv->ld == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv->ld = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + + // The authentication is based on the SSL/TLS certificate only with + // the authentication mechanism "EXTERNAL". + if (!strcmp(sinkenv->authmech, "EXTERNAL")) { + + if (sinkenv->mozldap_tls_keynickname) { + keynickname = g_strdup(sinkenv->mozldap_tls_keynickname); + osync_trace(TRACE_INTERNAL, "%s:%i: keynickname = \"%s\"", __FILE__, __LINE__, keynickname); + } else { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: WARNING: sinkenv->mozldap_tls_keynickname = NULL.", __FILE__, __LINE__); + goto error; + } + + + if (sinkenv->mozldap_tls_keypassword) { + keypasswd = g_strdup(sinkenv->mozldap_tls_keypassword); + osync_trace(TRACE_SENSITIVE, "keypassword = \"%s\"", keypasswd); + } else { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: WARNING: sinkenv->mozldap_tls_keypassword = NULL.", __FILE__, __LINE__); + goto error; + } + + + if (sinkenv->mozldap_tls_certnickname) { + certnickname = g_strdup(sinkenv->mozldap_tls_certnickname); + osync_trace(TRACE_INTERNAL, "%s:%i: certnickname = \"%s\"", __FILE__, __LINE__, certnickname); + } else { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: WARNING: sinkenv->mozldap_tls_certnickname = NULL.", __FILE__, __LINE__); + goto error; + } + + + + if (keynickname == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: keynickname = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (keypasswd == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: keypasswd = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (certnickname == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: certnickname = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: USE_MOZLDAP and EXTERNAL: Right before ldapssl_enable_clientauth()\n", __FILE__, __LINE__); +#endif + + ldap_errno = ldapssl_enable_clientauth(sinkenv->ld, keynickname, keypasswd, certnickname); + + if (keynickname) { + g_free(keynickname); + keynickname = NULL; + } + + if (keypasswd) { + g_free(keypasswd); + keypasswd = NULL; + } + + if (certnickname) { + g_free(certnickname); + certnickname = NULL; + } + + if (ldap_errno != LDAP_SUCCESS) { + ldap_error = ldap_plugin_report_ldap_error(sinkenv, __FILE__, __LINE__, ldap_errno); + + + if (ldap_error == NULL) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldapssl_enable_clientauth() has failed. Could not establish a connection to the LDAP server \"%s\", therefore.\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername); + } else { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldapssl_enable_clientauth() has failed. Could not establish a connection to the LDAP server \"%s\", therefore: \"%s\"\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, ldap_error); + } + + goto error; + } + } // if (!strcmp(sinkenv->authmech, "EXTERNAL")) + + + + + osync_trace(TRACE_EXIT, "%s", __func__); + return TRUE; + + +error: + if (!osync_error_is_set(error)) + osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); + + osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); + return FALSE; +} +#endif + + + + + + + +/** + * @brief Encrypts the connection to the LDAP server in form of + * STARTTLS, i.e. SSL/TLS inside of LDAP. + * + * @param ctx The libopensync context. + * @param sinkenv The object type specific environment. + * @param error The libopensync error pointer. + * + * @returns TRUE on success, FALSE in case of any error. + */ + +osync_bool ldap_plugin_starttls (OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) +{ + int ldap_errno = 0; + char *ldap_error = NULL; + + + osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, (void *)ctx, (void *)sinkenv, (void *)error); + + + if (ctx == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ctx = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (sinkenv == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (sinkenv->ld == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv->ld = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + +#ifdef DEBUG_ssl + ldap_plugin_printf("%s:%i: Right before ldap_start_tls_s()\n", __FILE__, __LINE__); +#endif + + ldap_errno = ldap_start_tls_s(sinkenv->ld, NULL, NULL); + + if (ldap_errno != LDAP_SUCCESS) { + ldap_error = ldap_plugin_report_ldap_error(sinkenv, __FILE__, __LINE__, ldap_errno); + + if (ldap_error == NULL) { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore.\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername); + } else { + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore: \"%s\"\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, ldap_error); + } + + + goto error; + } + + + osync_trace(TRACE_EXIT, "%s", __func__); + return TRUE; + + +error: + if (!osync_error_is_set(error)) + osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); + + osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); + return FALSE; +} + + + + + +/* @} */ |