andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone
dc8c34
From 29b29d7c014111ff7624a22a598182079e95eb1a Mon Sep 17 00:00:00 2001
dc8c34
From: Rich Megginson <rmeggins@redhat.com>
dc8c34
Date: Wed, 29 May 2013 18:44:26 -0600
dc8c34
Subject: [PATCH 64/99] Ticket #47375 - flush_ber error sending back start_tls
dc8c34
 response will deadlock
dc8c34
dc8c34
https://fedorahosted.org/389/ticket/47375
dc8c34
Reviewed by: nkinder (Thanks!)
dc8c34
Branch: 389-ds-base-1.2.11
dc8c34
Fix Description: The deadlock is caused by the client (or intermediary network
dc8c34
device) closing the connection while the server is attempting to write to the
dc8c34
client, to send back the start tls "success" response.  The server will lock
dc8c34
the c_mutex to disconnect the connection in this error case.  Since the c_mutex
dc8c34
has already been locked in start_tls(), the server will deadlock.  The polling
dc8c34
thread will also attempt to lock c_mutex, deadlocking it too, and the server
dc8c34
will become completely unresponsive.  The fix for this part is to make sure
dc8c34
never to call send_ldap_result with c_mutex locked.
dc8c34
After the server sends back the "success" response, if the
dc8c34
client immediately issues a TLS session negotiation, the server may not
dc8c34
have yet completely set up the socket for TLS, and will attempt to use the
dc8c34
partially setup socket, which can crash.  The fix for this is to setup the
dc8c34
socket for TLS IO using a Conn_IO_Layer callback, to establish the TLS IO
dc8c34
for the connection before the next read() operation.
dc8c34
Platforms tested: RHEL6 x86_64
dc8c34
Flag Day: no
dc8c34
Doc impact: no
dc8c34
(cherry picked from commit 01f7e5b10a70701b7af790e63cc56d39bc73efa4)
dc8c34
---
dc8c34
 ldap/servers/slapd/start_tls_extop.c | 230 +++++++++++++++++------------------
dc8c34
 1 file changed, 110 insertions(+), 120 deletions(-)
dc8c34
dc8c34
diff --git a/ldap/servers/slapd/start_tls_extop.c b/ldap/servers/slapd/start_tls_extop.c
dc8c34
index dffaeea..1efa657 100644
dc8c34
--- a/ldap/servers/slapd/start_tls_extop.c
dc8c34
+++ b/ldap/servers/slapd/start_tls_extop.c
dc8c34
@@ -72,7 +72,85 @@
dc8c34
 Slapi_PluginDesc exopdesc = { "start_tls_plugin", VENDOR, DS_PACKAGE_VERSION,
dc8c34
 	"Start TLS extended operation plugin" };
dc8c34
 
dc8c34
+static int
dc8c34
+start_tls_io_enable(Connection *c, void *data /* UNUSED */)
dc8c34
+{
dc8c34
+	int secure = 1;
dc8c34
+	PRFileDesc *newsocket;
dc8c34
+	int rv = -1;
dc8c34
+	int ns;
dc8c34
+
dc8c34
+	/* So far we have set up the environment for deploying SSL. It's now time to import the socket
dc8c34
+	 * into SSL and to configure it consequently. */
dc8c34
+
dc8c34
+	if ( slapd_ssl_listener_is_initialized() != 0 ) {
dc8c34
+	       PRFileDesc * ssl_listensocket;
dc8c34
+
dc8c34
+	       ssl_listensocket = get_ssl_listener_fd();
dc8c34
+	       if ( ssl_listensocket == (PRFileDesc *) NULL ) {
dc8c34
+		       slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
dc8c34
+					"SSL listener socket not found.\n" );
dc8c34
+		       goto done;
dc8c34
+	       }
dc8c34
+	       newsocket = slapd_ssl_importFD( ssl_listensocket, c->c_prfd );
dc8c34
+	       if ( newsocket == (PRFileDesc *) NULL ) {
dc8c34
+		       slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
dc8c34
+					"SSL socket import failed.\n" );
dc8c34
+		       goto done;
dc8c34
+	       }
dc8c34
+	} else {
dc8c34
+	       if ( slapd_ssl_init2( &c->c_prfd, 1 ) != 0 ) {
dc8c34
+		       slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
dc8c34
+					"SSL socket import or configuration failed.\n" );
dc8c34
+		       goto done;
dc8c34
+	       }
dc8c34
+	       newsocket = c->c_prfd;
dc8c34
+	}
dc8c34
+
dc8c34
+
dc8c34
+	rv = slapd_ssl_resetHandshake( newsocket, 1 );
dc8c34
+	if ( rv != SECSuccess ) {
dc8c34
+	       slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
dc8c34
+				"Unable to set socket ready for SSL handshake.\n" );
dc8c34
+	       goto done;
dc8c34
+	}
dc8c34
+
dc8c34
+
dc8c34
+	/* From here on, messages will be sent through the SSL layer, so we need to get our
dc8c34
+	 * connection ready. */
dc8c34
+
dc8c34
+	ns = configure_pr_socket( &newsocket, secure, 0 /*never local*/ );
dc8c34
+
dc8c34
+	c->c_flags |= CONN_FLAG_SSL;
dc8c34
+	c->c_flags |= CONN_FLAG_START_TLS;
dc8c34
+	c->c_sd = ns;
dc8c34
+	c->c_prfd = newsocket;
dc8c34
+
dc8c34
+        /* Get the effective key length */
dc8c34
+	SSL_SecurityStatus(c->c_prfd, NULL, NULL, NULL, &(c->c_ssl_ssf), NULL, NULL);
dc8c34
 
dc8c34
+	rv = slapd_ssl_handshakeCallback (c->c_prfd, (void *)handle_handshake_done, c);
dc8c34
+
dc8c34
+	if ( rv < 0 ) {
dc8c34
+	       PRErrorCode prerr = PR_GetError();
dc8c34
+	       slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
dc8c34
+			  "SSL_HandshakeCallback() %d " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
dc8c34
+			  rv, prerr, slapd_pr_strerror( prerr ) );
dc8c34
+	}
dc8c34
+
dc8c34
+	if ( config_get_SSLclientAuth() != SLAPD_SSLCLIENTAUTH_OFF ) {
dc8c34
+	       rv = slapd_ssl_badCertHook (c->c_prfd, (void *)handle_bad_certificate, c);
dc8c34
+	       if ( rv < 0 ) {
dc8c34
+		        PRErrorCode prerr = PR_GetError();
dc8c34
+			slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
dc8c34
+					 "SSL_BadCertHook(%i) %i " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
dc8c34
+					 c->c_sd, rv, prerr, slapd_pr_strerror( prerr ) );
dc8c34
+	       }
dc8c34
+	}
dc8c34
+
dc8c34
+done:
dc8c34
+	return rv;
dc8c34
+}
dc8c34
 
dc8c34
 
dc8c34
 /* Start TLS Extended operation plugin function */
dc8c34
@@ -82,13 +160,12 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 
dc8c34
 	char		*oid;
dc8c34
 	Connection      *conn;
dc8c34
-	PRFileDesc      *oldsocket, *newsocket;
dc8c34
-	int             secure;
dc8c34
-	int             ns;
dc8c34
 #ifdef _WIN32
dc8c34
+	PRFileDesc      *oldsocket;
dc8c34
 	int				oldnativesocket;
dc8c34
 #endif
dc8c34
-	int             rv;
dc8c34
+	int             ldaprc = LDAP_SUCCESS;
dc8c34
+	char            *ldapmsg = NULL;
dc8c34
 
dc8c34
 	/* Get the pb ready for sending Start TLS Extended Responses back to the client. 
dc8c34
 	 * The only requirement is to set the LDAP OID of the extended response to the START_TLS_OID. */
dc8c34
@@ -133,23 +210,23 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 
dc8c34
 	conn = pb->pb_conn;
dc8c34
 	PR_Lock( conn->c_mutex );
dc8c34
+	/* cannot call slapi_send_ldap_result with mutex locked - will deadlock if ber_flush returns error */
dc8c34
 #ifndef _WIN32
dc8c34
-	oldsocket = conn->c_prfd;
dc8c34
-	if ( oldsocket == (PRFileDesc *) NULL ) {
dc8c34
-                slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-				 "Connection socket not available.\n" );
dc8c34
-	        slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-					"Connection socket not available.", 0, NULL ); 
dc8c34
+	if ( conn->c_prfd == (PRFileDesc *) NULL ) {
dc8c34
+		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
dc8c34
+		                 "Connection socket not available.\n" );
dc8c34
+		ldaprc = LDAP_UNAVAILABLE;
dc8c34
+		ldapmsg = "Connection socket not available.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}
dc8c34
 #else
dc8c34
 	oldnativesocket = conn->c_sd;
dc8c34
 	oldsocket = PR_ImportTCPSocket(oldnativesocket);
dc8c34
 	if ( oldsocket == (PRFileDesc *) NULL ) {
dc8c34
-                slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-				 "Failed to import NT native socket into NSPR.\n" );
dc8c34
-	        slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-					"Failed to import NT native socket into NSPR.", 0, NULL ); 
dc8c34
+		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
dc8c34
+		                 "Failed to import NT native socket into NSPR.\n" );
dc8c34
+		ldaprc = LDAP_UNAVAILABLE;
dc8c34
+		ldapmsg = "Failed to import NT native socket into NSPR.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}
dc8c34
 #endif
dc8c34
@@ -160,8 +237,8 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 				1 /* check for ops where result not yet sent */ )) {
dc8c34
 		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
 				 "Other operations are still pending on the connection.\n" );
dc8c34
-		slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, 
dc8c34
-					"Other operations are still pending on the connection.", 0, NULL );
dc8c34
+		ldaprc = LDAP_OPERATIONS_ERROR;
dc8c34
+		ldapmsg = "Other operations are still pending on the connection.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}
dc8c34
 
dc8c34
@@ -171,8 +248,8 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 	        /* slapi_send_ldap_result( pb, LDAP_REFERRAL, NULL, msg, 0, url ); */
dc8c34
 		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
 				 "SSL not supported by this server.\n" );
dc8c34
-		slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, 
dc8c34
-					"SSL not supported by this server.", 0, NULL );
dc8c34
+		ldaprc = LDAP_PROTOCOL_ERROR;
dc8c34
+		ldapmsg = "SSL not supported by this server.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}
dc8c34
 
dc8c34
@@ -180,16 +257,16 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 	if ( conn->c_flags & CONN_FLAG_SSL ) {
dc8c34
 		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
 				 "SSL connection already established.\n" );
dc8c34
-		slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, 
dc8c34
-					"SSL connection already established.", 0, NULL );
dc8c34
+		ldaprc = LDAP_OPERATIONS_ERROR;
dc8c34
+		ldapmsg = "SSL connection already established.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}
dc8c34
 
dc8c34
 	if ( conn->c_flags & CONN_FLAG_SASL_CONTINUE ) {
dc8c34
 		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
 				 "SASL multi-stage bind in progress.\n" );
dc8c34
-		slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, 
dc8c34
-					"SASL multi-stage bind in progress.", 0, NULL );
dc8c34
+		ldaprc = LDAP_OPERATIONS_ERROR;
dc8c34
+		ldapmsg = "SASL multi-stage bind in progress.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}
dc8c34
 
dc8c34
@@ -197,8 +274,8 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 	if ( conn->c_flags & CONN_FLAG_CLOSING ) {
dc8c34
 		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
 				 "Connection being closed at this moment.\n" );
dc8c34
-		slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-					"Connection being closed at this moment.", 0, NULL );
dc8c34
+		ldaprc = LDAP_UNAVAILABLE;
dc8c34
+		ldapmsg = "Connection being closed at this moment.";
dc8c34
 		goto unlock_and_return;
dc8c34
 	}	
dc8c34
 
dc8c34
@@ -208,110 +285,23 @@ start_tls( Slapi_PBlock *pb )
dc8c34
 	 * So, we may as well try initialising SSL. */
dc8c34
 
dc8c34
 	if ( slapd_security_library_is_initialized() == 0 ) {	  
dc8c34
-               slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-				"NSS libraries not initialised.\n" );
dc8c34
-	       slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-				       "NSS libraries not initialised.", 0, NULL );
dc8c34
-	       goto unlock_and_return;
dc8c34
+		slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
dc8c34
+		                 "NSS libraries not initialised.\n" );
dc8c34
+		ldaprc = LDAP_UNAVAILABLE;
dc8c34
+		ldapmsg = "NSS libraries not initialised.";
dc8c34
+		goto unlock_and_return;
dc8c34
 	}	
dc8c34
 
dc8c34
 
dc8c34
+        /* Enable TLS I/O on the connection */
dc8c34
+        connection_set_io_layer_cb(conn, start_tls_io_enable, NULL, NULL);
dc8c34
 
dc8c34
 	/* Since no specific argument for denying the Start TLS request has been found, 
dc8c34
 	 * we send a success response back to the client. */
dc8c34
-	
dc8c34
-	slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-			 "Start TLS request accepted.Server willing to negotiate SSL.\n" );
dc8c34
-	slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, 
dc8c34
-				"Start TLS request accepted.Server willing to negotiate SSL.", 0, NULL );	
dc8c34
-	
dc8c34
-
dc8c34
-	/* So far we have set up the environment for deploying SSL. It's now time to import the socket
dc8c34
-	 * into SSL and to configure it consequently. */
dc8c34
-  
dc8c34
-	if ( slapd_ssl_listener_is_initialized() != 0 ) {
dc8c34
-	       PRFileDesc * ssl_listensocket;
dc8c34
-
dc8c34
-	       ssl_listensocket = get_ssl_listener_fd(); 
dc8c34
-	       if ( ssl_listensocket == (PRFileDesc *) NULL ) {
dc8c34
-		       slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-					"SSL listener socket not found.\n" );
dc8c34
-		       slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-					       "SSL listener socket not found.", 0, NULL );
dc8c34
-		       goto unlock_and_return;
dc8c34
-	       }
dc8c34
-	       newsocket = slapd_ssl_importFD( ssl_listensocket, oldsocket );
dc8c34
-	       if ( newsocket == (PRFileDesc *) NULL ) {
dc8c34
-		       slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-					"SSL socket import failed.\n" );
dc8c34
-		       slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-					       "SSL socket import failed.", 0, NULL );
dc8c34
-		       goto unlock_and_return;
dc8c34
-	       }
dc8c34
-	} else {
dc8c34
-	       if ( slapd_ssl_init2( &oldsocket, 1 ) != 0 ) {
dc8c34
-		       slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-					"SSL socket import or configuration failed.\n" );
dc8c34
-		       slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-					       "SSL socket import or configuration failed.", 0, NULL );
dc8c34
-		       goto unlock_and_return;
dc8c34
-	       }
dc8c34
-	       newsocket = oldsocket; 
dc8c34
-	}
dc8c34
-
dc8c34
-
dc8c34
-	rv = slapd_ssl_resetHandshake( newsocket, 1 );
dc8c34
-	if ( rv != SECSuccess ) {
dc8c34
-	       slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-				"Unable to set socket ready for SSL handshake.\n" );
dc8c34
-	       slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL, 
dc8c34
-				       "Unable to set socket ready for SSL handshake.", 0, NULL );
dc8c34
-	       goto unlock_and_return;
dc8c34
-	}
dc8c34
-	
dc8c34
-
dc8c34
-
dc8c34
-	/* From here on, messages will be sent through the SSL layer, so we need to get our 
dc8c34
-	 * connection ready. */
dc8c34
-
dc8c34
-	secure = 1;
dc8c34
-	ns = configure_pr_socket( &newsocket, secure, 0 /*never local*/ );
dc8c34
-
dc8c34
-	conn->c_flags |= CONN_FLAG_SSL;
dc8c34
-	conn->c_flags |= CONN_FLAG_START_TLS;
dc8c34
-	conn->c_sd = ns;
dc8c34
-	conn->c_prfd = newsocket;
dc8c34
-
dc8c34
-        /* Get the effective key length */
dc8c34
-	SSL_SecurityStatus(conn->c_prfd, NULL, NULL, NULL, &(conn->c_ssl_ssf), NULL, NULL);
dc8c34
-	
dc8c34
-	rv = slapd_ssl_handshakeCallback (conn->c_prfd, (void *)handle_handshake_done, conn);
dc8c34
-
dc8c34
-	if ( rv < 0 ) {
dc8c34
-	       PRErrorCode prerr = PR_GetError();
dc8c34
-	       slapi_log_error( SLAPI_LOG_FATAL, "start_tls", 
dc8c34
-			  "SSL_HandshakeCallback() %d " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
dc8c34
-			  rv, prerr, slapd_pr_strerror( prerr ) );
dc8c34
-	}
dc8c34
-	
dc8c34
-	if ( config_get_SSLclientAuth() != SLAPD_SSLCLIENTAUTH_OFF ) {
dc8c34
-	       rv = slapd_ssl_badCertHook (conn->c_prfd, (void *)handle_bad_certificate, conn);
dc8c34
-	       if ( rv < 0 ) {
dc8c34
-		        PRErrorCode prerr = PR_GetError();
dc8c34
-			slapi_log_error( SLAPI_LOG_FATAL, "start_tls", 
dc8c34
-					 "SSL_BadCertHook(%i) %i " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
dc8c34
-					 conn->c_sd, rv, prerr, slapd_pr_strerror( prerr ) );
dc8c34
-	       }
dc8c34
-	}
dc8c34
-	
dc8c34
-	
dc8c34
-	/* Once agreed in starting TLS, the handshake must be carried out. */
dc8c34
-	
dc8c34
-	slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls", 
dc8c34
-			 "Starting SSL Handshake.\n" );
dc8c34
-
dc8c34
+        ldapmsg = "Start TLS request accepted.Server willing to negotiate SSL.";
dc8c34
  unlock_and_return:
dc8c34
 	PR_Unlock( conn->c_mutex );
dc8c34
+	slapi_send_ldap_result( pb, ldaprc, NULL, ldapmsg, 0, NULL );
dc8c34
 
dc8c34
 	return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );	
dc8c34
 
dc8c34
-- 
dc8c34
1.8.1.4
dc8c34