From be589f4c607e9ed2ef587b068a2e873df62505f6 Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmeggins@redhat.com>
Date: Tue, 24 Apr 2012 20:09:38 -0600
Subject: [PATCH] Ticket #348 - crash in ldap_initialize with multiple threads

https://fedorahosted.org/389/ticket/348
Resolves: Ticket #348
Bug Description: crash in ldap_initialize with multiple threads
Reviewed by: ???
Branch: master
Fix Description: Protect calls to ldap_initialize() with a mutex to prevent
multiple threads from calling it at the same time.
Platforms tested: RHEL6 x86_64
Flag Day: no
Doc impact: no
---
 ldap/servers/slapd/ldaputil.c                   |   36 +++++++++++++++++++++++
 ldap/servers/slapd/tools/ldclt/ldapfct.c        |   25 ++++++++++++++++
 ldap/servers/slapd/tools/rsearch/addthread.c    |   26 ++++++++++++++++
 ldap/servers/slapd/tools/rsearch/searchthread.c |   32 ++++++++++++++++++++
 4 files changed, 119 insertions(+), 0 deletions(-)

diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c
index 545c703..80ab8cb 100644
--- a/ldap/servers/slapd/ldaputil.c
+++ b/ldap/servers/slapd/ldaputil.c
@@ -99,6 +99,24 @@
 #if !defined(USE_OPENLDAP)
 #include <ldap_ssl.h>
 #include <ldappr.h>
+#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;
+
+static PRStatus
+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",
+                        errorCode, slapd_pr_strerror(errorCode));
+        return PR_FAILURE;
+    }
+
+    return PR_SUCCESS;
+}
 #endif
 
 /* the server depends on the old, deprecated ldap_explode behavior which openldap
@@ -737,7 +755,16 @@ slapi_ldap_init_ext(
 
 #if defined(USE_OPENLDAP)
     if (ldapurl) {
+	if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+	    slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext",
+			    "Could not perform internal ol_init init\n");
+	    rc = -1;
+	    goto done;
+	}
+
+	PR_Lock(ol_init_lock);
 	rc = ldap_initialize(&ld, ldapurl);
+	PR_Unlock(ol_init_lock);
 	if (rc) {
 	    slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext",
 			    "Could not initialize LDAP connection to [%s]: %d:%s\n",
@@ -751,7 +778,16 @@ slapi_ldap_init_ext(
 	} else { /* host port */
 	    makeurl = convert_to_openldap_uri(hostname, port, (secure == 1 ? "ldaps" : "ldap"));
 	}
+	if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+	    slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext",
+			    "Could not perform internal ol_init init\n");
+	    rc = -1;
+	    goto done;
+	}
+
+	PR_Lock(ol_init_lock);
 	rc = ldap_initialize(&ld, makeurl);
+	PR_Unlock(ol_init_lock);
 	if (rc) {
 	    slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext",
 			    "Could not initialize LDAP connection to [%s]: %d:%s\n",
diff --git a/ldap/servers/slapd/tools/ldclt/ldapfct.c b/ldap/servers/slapd/tools/ldclt/ldapfct.c
index 0e8a2fb..3f74fd0 100644
--- a/ldap/servers/slapd/tools/ldclt/ldapfct.c
+++ b/ldap/servers/slapd/tools/ldclt/ldapfct.c
@@ -708,6 +708,23 @@ done:
 
   return rc;
 }
+
+/* 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;
+
+static PRStatus
+internal_ol_init_init(void)
+{
+    PR_ASSERT(NULL == ol_init_lock);
+    if ((ol_init_lock = PR_NewLock()) == NULL) {
+        PRErrorCode errorCode = PR_GetError();
+        printf("internal_ol_init_init PR_NewLock failed %d\n", errorCode);
+        return PR_FAILURE;
+    }
+
+    return PR_SUCCESS;
+}
 #endif /* USE_OPENLDAP */
 
 /* mctx is a global */
@@ -735,12 +752,20 @@ connectToLDAP(thread_context *tttctx, const char *bufBindDN, const char *bufPass
   ldapurl = PR_smprintf("ldap%s://%s:%d/",
 			(mode & SSL) ? "s" : "",
 			mctx.hostname, mctx.port);
+  if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+      printf("Could not perform internal ol_init init\n");
+      goto done;
+  }
+
+  PR_Lock(ol_init_lock);
   if ((ret = ldap_initialize(&ld, ldapurl))) {
+    PR_Unlock(ol_init_lock);
     printf ("ldclt[%d]: T%03d: Cannot ldap_initialize (%s), errno=%d ldaperror=%d:%s\n",
 	    mctx.pid, thrdNum, ldapurl, errno, ret, my_ldap_err2string(ret));
     fflush (stdout);
     goto done;
   }
+  PR_Unlock(ol_init_lock);
   PR_smprintf_free(ldapurl);
   ldapurl = NULL;
   if (mode & SSL) {
diff --git a/ldap/servers/slapd/tools/rsearch/addthread.c b/ldap/servers/slapd/tools/rsearch/addthread.c
index f01ba18..a3e5568 100644
--- a/ldap/servers/slapd/tools/rsearch/addthread.c
+++ b/ldap/servers/slapd/tools/rsearch/addthread.c
@@ -176,6 +176,25 @@ static void at_disconnect(AddThread *at)
 }
 #endif
 
+#if defined(USE_OPENLDAP)
+/* 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;
+
+static PRStatus
+internal_ol_init_init(void)
+{
+    PR_ASSERT(NULL == ol_init_lock);
+    if ((ol_init_lock = PR_NewLock()) == NULL) {
+        PRErrorCode errorCode = PR_GetError();
+        fprintf(stderr, "internal_ol_init_init PR_NewLock failed %d\n", errorCode);
+        return PR_FAILURE;
+    }
+
+    return PR_SUCCESS;
+}
+#endif
+
 static void at_bind(AddThread *at)
 {
     int ret;
@@ -185,7 +204,14 @@ static void at_bind(AddThread *at)
 
     at->ld = NULL;
     ldapurl = PR_smprintf("ldap://%s:%d", hostname, port);
+    if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+        fprintf(stderr, "Could not perform internal ol_init init\n");
+        return;
+    }
+
+    PR_Lock(ol_init_lock);
     ret = ldap_initialize(&at->ld, ldapurl);
+    PR_Unlock(ol_init_lock);
     PR_smprintf_free(ldapurl);
     ldapurl = NULL;
 	if (ret) {
diff --git a/ldap/servers/slapd/tools/rsearch/searchthread.c b/ldap/servers/slapd/tools/rsearch/searchthread.c
index 9e2b0d6..8a74d58 100644
--- a/ldap/servers/slapd/tools/rsearch/searchthread.c
+++ b/ldap/servers/slapd/tools/rsearch/searchthread.c
@@ -180,6 +180,24 @@ static int st_bind_core(SearchThread *st, LDAP **ld, char *dn, char *pw)
 	return 1;
 }
 
+#if defined(USE_OPENLDAP)
+/* 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;
+
+static PRStatus
+internal_ol_init_init(void)
+{
+    PR_ASSERT(NULL == ol_init_lock);
+    if ((ol_init_lock = PR_NewLock()) == NULL) {
+        PRErrorCode errorCode = PR_GetError();
+        fprintf(stderr, "internal_ol_init_init PR_NewLock failed %d\n", errorCode);
+        return PR_FAILURE;
+    }
+
+    return PR_SUCCESS;
+}
+#endif
 static int st_bind(SearchThread *st)
 {
     if (!st->ld) {
@@ -189,7 +207,14 @@ static int st_bind(SearchThread *st)
 
         st->ld = NULL;
         ldapurl = PR_smprintf("ldap://%s:%d", hostname, port);
+        if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+            fprintf(stderr, "Could not perform internal ol_init init\n");
+            return 0;
+        }
+
+        PR_Lock(ol_init_lock);
         ret = ldap_initialize(&st->ld, ldapurl);
+        PR_Unlock(ol_init_lock);
         PR_smprintf_free(ldapurl);
         ldapurl = NULL;
         if (ret) {
@@ -212,7 +237,14 @@ static int st_bind(SearchThread *st)
 
         st->ld2 = NULL;
         ldapurl = PR_smprintf("ldap://%s:%d", hostname, port);
+        if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
+            fprintf(stderr, "Could not perform internal ol_init init\n");
+            return 0;
+        }
+
+        PR_Lock(ol_init_lock);
         ret = ldap_initialize(&st->ld2, ldapurl);
+        PR_Unlock(ol_init_lock);
         PR_smprintf_free(ldapurl);
         ldapurl = NULL;
         if (ret) {
-- 
1.7.1

