From bef58fad8c04cf50831625f9e4e7d826b5cbf6a8 Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmeggins@redhat.com>
Date: Mon, 14 Oct 2013 12:43:51 -0600
Subject: [PATCH] Ticket #47559 hung server - related to sasl and initialize

https://fedorahosted.org/389/ticket/47559
Reviewed by: ???
Branch: master
Fix Description: Use a mutex to protect calls to openldap functions that do
anything with crypto - bind, unbind, start_tls, other calls.
Platforms tested: RHEL6 x86_64
Flag Day: no
Doc impact: no
---
 ldap/servers/slapd/ldaputil.c |   51 +++++++++++++++++++++++++++++++++++++---
 1 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c
index ed3491e..32c05ec 100644
--- a/ldap/servers/slapd/ldaputil.c
+++ b/ldap/servers/slapd/ldaputil.c
@@ -99,10 +99,16 @@
 #if !defined(USE_OPENLDAP)
 #include <ldap_ssl.h>
 #include <ldappr.h>
+#define BIND_LOCK (void)0
+#define BIND_UNLOCK (void)0
 #else
 /* need mutex around ldap_initialize - see https://fedorahosted.org/389/ticket/348 */
 static PRCallOnceType ol_init_callOnce = {0,0};
 static PRLock *ol_init_lock = NULL;
+/* need mutex around ldap_sasl_bind - see https://fedorahosted.org/389/ticket/47599 */
+static PRLock *ol_bind_lock = NULL;
+#define BIND_LOCK PR_Lock(ol_bind_lock)
+#define BIND_UNLOCK PR_Unlock(ol_bind_lock)
 
 static PRStatus
 internal_ol_init_init(void)
@@ -110,12 +116,20 @@ internal_ol_init_init(void)
     PR_ASSERT(NULL == ol_init_lock);
     if ((ol_init_lock = PR_NewLock()) == NULL) {
         PRErrorCode errorCode = PR_GetError();
-        slapi_log_error(SLAPI_LOG_FATAL, "internal_ol_init_init", "PR_NewLock failed %d:%s\n",
+        slapi_log_error(SLAPI_LOG_FATAL, "internal_ol_init_init", "PR_NewLock init_lock failed %d:%s\n",
                         errorCode, slapd_pr_strerror(errorCode));
         return PR_FAILURE;
     }
 
-    return PR_SUCCESS;
+    PR_ASSERT(NULL == ol_bind_lock);
+    if ((ol_bind_lock = PR_NewLock()) == NULL) {
+        PRErrorCode errorCode = PR_GetError();
+        slapi_log_error(SLAPI_LOG_FATAL, "internal_ol_init_init", "PR_NewLock bind_lock failed %d:%s\n",
+                        errorCode, slapd_pr_strerror(errorCode));
+        return PR_FAILURE;
+    }
+
+   return PR_SUCCESS;
 }
 #endif
 
@@ -145,7 +159,16 @@ void
 slapi_ldap_unbind( LDAP *ld )
 {
     if ( ld != NULL ) {
+#if defined(USE_OPENLDAP)
+	if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+		slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_unbind",
+		                "Could not perform internal ol_init init\n");
+		return;
+	}
+#endif
+	BIND_LOCK;
 	ldap_unbind_ext( ld, NULL, NULL );
+	BIND_UNLOCK;
     }
 }
 
@@ -1034,11 +1057,22 @@ slapi_ldap_bind(
     ldap_controls_free(clientctrls);
     ldap_set_option(ld, LDAP_OPT_CLIENT_CONTROLS, NULL);
 
+#if defined(USE_OPENLDAP)
+    if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+        slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_bind",
+           "Could not perform internal ol_init init\n");
+        rc = -1;
+        goto done;
+    }
+#endif
+
     if ((secure > 0) && mech && !strcmp(mech, LDAP_SASL_EXTERNAL)) {
 #if defined(USE_OPENLDAP)
 	/* we already set up a tls context in slapi_ldap_init_ext() - this will
 	   free those old settings and context and create a new one */
+	PR_Lock(ol_bind_lock);
 	rc = setup_ol_tls_conn(ld, 1);
+	PR_Unlock(ol_bind_lock);
 #else
 	/* SSL connections will use the server's security context
 	   and cert for client auth */
@@ -1063,7 +1097,9 @@ slapi_ldap_bind(
     }
 
     if (secure == 2) { /* send start tls */
+	BIND_LOCK;
 	rc = ldap_start_tls_s(ld, NULL /* serverctrls?? */, NULL);
+	BIND_UNLOCK;
 	if (LDAP_SUCCESS != rc) {
 	    slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_bind",
 			    "Error: could not send startTLS request: "
@@ -1085,8 +1121,11 @@ slapi_ldap_bind(
 			"attempting %s bind with id [%s] creds [%s]\n",
 			mech ? mech : "SIMPLE",
 			bindid, creds);
-	if ((rc = ldap_sasl_bind(ld, bindid, mech, &bvcreds, serverctrls,
-	                         NULL /* clientctrls */, &mymsgid))) {
+	BIND_LOCK;
+	rc = ldap_sasl_bind(ld, bindid, mech, &bvcreds, serverctrls,
+	                    NULL /* clientctrls */, &mymsgid);
+	BIND_UNLOCK;
+	if (rc) {
 	    char *myhostname = NULL;
 	    char *copy = NULL;
 	    char *ptr = NULL;
@@ -1142,7 +1181,9 @@ slapi_ldap_bind(
                     /* take the one provided by the caller. It should be the one defined in the protocol */
                     bind_timeout = timeout;
             }
+	    BIND_LOCK;
 	    rc = ldap_result(ld, mymsgid, LDAP_MSG_ALL, bind_timeout, &result);
+	    BIND_UNLOCK;
 	    if (-1 == rc) { /* error */
 		rc = slapi_ldap_get_lderrno(ld, NULL, NULL);
 		slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_bind",
@@ -1206,9 +1247,11 @@ slapi_ldap_bind(
 	    ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX, &max_ssf);
 	}
 #endif
+	BIND_LOCK;
 	rc = slapd_ldap_sasl_interactive_bind(ld, bindid, creds, mech,
 					      serverctrls, returnedctrls,
 					      msgidp);
+	BIND_UNLOCK;
 	if (LDAP_SUCCESS != rc) {
 	    slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_bind",
 			    "Error: could not perform interactive bind for id "
-- 
1.7.1

