summaryrefslogtreecommitdiff
path: root/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/xpcom/src/nsLDAPSecurityGlue.cpp')
-rw-r--r--ldap/xpcom/src/nsLDAPSecurityGlue.cpp342
1 files changed, 342 insertions, 0 deletions
diff --git a/ldap/xpcom/src/nsLDAPSecurityGlue.cpp b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
new file mode 100644
index 0000000000..c39723a72a
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
@@ -0,0 +1,342 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Only build this code if PSM is being built also
+//
+#include "nsLDAPInternal.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsNetCID.h"
+#include "nsISocketProvider.h"
+#include "nsISSLSocketControl.h"
+#include "nsString.h"
+#include "nsMemory.h"
+#include "plstr.h"
+#include "ldap.h"
+#include "ldappr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+// LDAP per-session data structure.
+//
+typedef struct {
+ char *hostname;
+ LDAP_X_EXTIOF_CLOSE_CALLBACK *realClose;
+ LDAP_X_EXTIOF_CONNECT_CALLBACK *realConnect;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *realDisposeHandle;
+} nsLDAPSSLSessionClosure;
+
+// LDAP per-socket data structure.
+//
+typedef struct {
+ nsLDAPSSLSessionClosure *sessionClosure; /* session info */
+} nsLDAPSSLSocketClosure;
+
+// free the per-socket data structure as necessary
+//
+static void
+nsLDAPSSLFreeSocketClosure(nsLDAPSSLSocketClosure **aClosure)
+{
+ if (aClosure && *aClosure) {
+ free(*aClosure);
+ *aClosure = nullptr;
+ }
+}
+
+// Replacement close() function, which cleans up local stuff associated
+// with this socket, and then calls the real close function.
+//
+extern "C" int LDAP_CALLBACK
+nsLDAPSSLClose(int s, struct lextiof_socket_private *socketarg)
+{
+ PRLDAPSocketInfo socketInfo;
+ nsLDAPSSLSocketClosure *socketClosure;
+ nsLDAPSSLSessionClosure *sessionClosure;
+
+ // get the socketInfo associated with this socket
+ //
+ memset(&socketInfo, 0, sizeof(socketInfo));
+ socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if (prldap_get_socket_info(s, socketarg, &socketInfo) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLClose(): prldap_get_socket_info() failed\n");
+ return -1;
+ }
+
+ // save off the session closure data in an automatic, since we're going to
+ // need to call through it
+ //
+ socketClosure = reinterpret_cast<nsLDAPSSLSocketClosure *>
+ (socketInfo.soinfo_appdata);
+ sessionClosure = socketClosure->sessionClosure;
+
+ // free the socket closure data
+ //
+ nsLDAPSSLFreeSocketClosure(
+ reinterpret_cast<nsLDAPSSLSocketClosure **>
+ (&socketInfo.soinfo_appdata));
+
+ // call the real close function
+ //
+ return (*(sessionClosure->realClose))(s, socketarg);
+}
+
+// Replacement connection function. Calls the real connect function,
+//
+extern "C" int LDAP_CALLBACK
+nsLDAPSSLConnect(const char *hostlist, int defport, int timeout,
+ unsigned long options,
+ struct lextiof_session_private *sessionarg,
+ struct lextiof_socket_private **socketargp )
+{
+ PRLDAPSocketInfo socketInfo;
+ PRLDAPSessionInfo sessionInfo;
+ nsLDAPSSLSocketClosure *socketClosure = nullptr;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ int intfd = -1;
+ nsCOMPtr <nsISupports> securityInfo;
+ nsCOMPtr <nsISocketProvider> tlsSocketProvider;
+ nsCOMPtr <nsISSLSocketControl> sslSocketControl;
+ nsresult rv;
+
+ // Ensure secure option is set. Also, clear secure bit in options
+ // the we pass to the standard connect() function (since it doesn't know
+ // how to handle the secure option).
+ //
+ NS_ASSERTION(options & LDAP_X_EXTIOF_OPT_SECURE,
+ "nsLDAPSSLConnect(): called for non-secure connection");
+ options &= ~LDAP_X_EXTIOF_OPT_SECURE;
+
+ // Retrieve session info. so we can store a pointer to our session info.
+ // in our socket info. later.
+ //
+ memset(&sessionInfo, 0, sizeof(sessionInfo));
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if (prldap_get_session_info(nullptr, sessionarg, &sessionInfo)
+ != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get session info");
+ return -1;
+ }
+ sessionClosure = reinterpret_cast<nsLDAPSSLSessionClosure *>
+ (sessionInfo.seinfo_appdata);
+
+ // Call the real connect() callback to make the TCP connection. If it
+ // succeeds, *socketargp is set.
+ //
+ intfd = (*(sessionClosure->realConnect))(hostlist, defport, timeout,
+ options, sessionarg, socketargp);
+ if ( intfd < 0 ) {
+ PR_LOG(gLDAPLogModule, PR_LOG_DEBUG,
+ ("nsLDAPSSLConnect(): standard connect() function returned %d",
+ intfd));
+ return intfd;
+ }
+
+ // Retrieve socket info from the newly created socket so that we
+ // have the PRFileDesc onto which we will be layering SSL.
+ //
+ memset(&socketInfo, 0, sizeof(socketInfo));
+ socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if (prldap_get_socket_info(intfd, *socketargp, &socketInfo)
+ != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get socket info");
+ goto close_socket_and_exit_with_error;
+ }
+
+ // Allocate a structure to hold our socket-specific data.
+ //
+ socketClosure = static_cast<nsLDAPSSLSocketClosure *>(moz_xmalloc(
+ sizeof(nsLDAPSSLSocketClosure)));
+ if (!socketClosure) {
+ NS_WARNING("nsLDAPSSLConnect(): unable to allocate socket closure");
+ goto close_socket_and_exit_with_error;
+ }
+ memset(socketClosure, 0, sizeof(nsLDAPSSLSocketClosure));
+ socketClosure->sessionClosure = sessionClosure;
+
+ // Add the NSPR layer for SSL provided by PSM to this socket.
+ //
+ tlsSocketProvider = do_GetService(NS_STARTTLSSOCKETPROVIDER_CONTRACTID,
+ &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get socket provider service");
+ goto close_socket_and_exit_with_error;
+ }
+ // XXXdmose: Note that hostlist can be a list of hosts (in the
+ // current XPCOM SDK code, it will always be a list of IP
+ // addresses). Because of this, we need to use
+ // sessionClosure->hostname which was passed in separately to tell
+ // AddToSocket what to match the name in the certificate against.
+ // What exactly happen will happen when this is used with some IP
+ // address in the list other than the first one is not entirely
+ // clear, and I suspect it may depend on the format of the name in
+ // the certificate. Need to investigate.
+ //
+ rv = tlsSocketProvider->AddToSocket(PR_AF_INET,
+ sessionClosure->hostname,
+ defport,
+ nullptr,
+ mozilla::NeckoOriginAttributes(),
+ 0,
+ socketInfo.soinfo_prfd,
+ getter_AddRefs(securityInfo));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to add SSL layer to socket");
+ goto close_socket_and_exit_with_error;
+ }
+
+ // If possible we want to avoid using SSLv2, as this can confuse
+ // some directory servers (notably the netscape 4.1 ds). The only
+ // way that PSM provides for us to do this is to use a socket that can
+ // be used for the STARTTLS protocol, because the STARTTLS protocol disallows
+ // the use of SSLv2.
+ // (Thanks to Brian Ryner for helping figure this out).
+ //
+ sslSocketControl = do_QueryInterface(securityInfo, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("nsLDAPSSLConnect(): unable to QI to nsISSLSocketControl");
+ } else {
+ rv = sslSocketControl->StartTLS();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("nsLDAPSSLConnect(): StartTLS failed");
+ }
+ }
+
+ // Attach our closure to the socketInfo.
+ //
+ socketInfo.soinfo_appdata = reinterpret_cast<prldap_socket_private *>
+ (socketClosure);
+ if (prldap_set_socket_info(intfd, *socketargp, &socketInfo)
+ != LDAP_SUCCESS ) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to set socket info");
+ }
+ return intfd; // success
+
+close_socket_and_exit_with_error:
+ if (socketInfo.soinfo_prfd) {
+ PR_Close(socketInfo.soinfo_prfd);
+ }
+ if (socketClosure) {
+ nsLDAPSSLFreeSocketClosure(&socketClosure);
+ }
+ if ( intfd >= 0 && *socketargp ) {
+ (*(sessionClosure->realClose))(intfd, *socketargp);
+ }
+ return -1;
+
+}
+
+// Free data associated with this session (LDAP *) as necessary.
+//
+static void
+nsLDAPSSLFreeSessionClosure(nsLDAPSSLSessionClosure **aSessionClosure)
+{
+ if (aSessionClosure && *aSessionClosure) {
+
+ // free the hostname
+ //
+ if ( (*aSessionClosure)->hostname ) {
+ PL_strfree((*aSessionClosure)->hostname);
+ (*aSessionClosure)->hostname = nullptr;
+ }
+
+ // free the structure itself
+ //
+ free(*aSessionClosure);
+ *aSessionClosure = nullptr;
+ }
+}
+
+// Replacement session handle disposal code. First cleans up our local
+// stuff, then calls the original session handle disposal function.
+//
+extern "C" void LDAP_CALLBACK
+nsLDAPSSLDisposeHandle(LDAP *ld, struct lextiof_session_private *sessionarg)
+{
+ PRLDAPSessionInfo sessionInfo;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
+
+ memset(&sessionInfo, 0, sizeof(sessionInfo));
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if (prldap_get_session_info(ld, nullptr, &sessionInfo) == LDAP_SUCCESS) {
+ sessionClosure = reinterpret_cast<nsLDAPSSLSessionClosure *>
+ (sessionInfo.seinfo_appdata);
+ disposehdl_fn = sessionClosure->realDisposeHandle;
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ (*disposehdl_fn)(ld, sessionarg);
+ }
+}
+
+// Installs appropriate routines and data for making this connection
+// handle SSL. The aHostName is ultimately passed to PSM and is used to
+// validate certificates.
+//
+nsresult
+nsLDAPInstallSSL( LDAP *ld, const char *aHostName)
+{
+ struct ldap_x_ext_io_fns iofns;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ PRLDAPSessionInfo sessionInfo;
+
+ // Allocate our own session information.
+ //
+ sessionClosure = static_cast<nsLDAPSSLSessionClosure *>(moz_xmalloc(
+ sizeof(nsLDAPSSLSessionClosure)));
+ if (!sessionClosure) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ memset(sessionClosure, 0, sizeof(nsLDAPSSLSessionClosure));
+
+ // Override a few functions, saving a pointer to the original function
+ // in each case so we can call it from our SSL savvy functions.
+ //
+ memset(&iofns, 0, sizeof(iofns));
+ iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
+ if (ldap_get_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ static_cast<void *>(&iofns)) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): unexpected error getting"
+ " LDAP_X_OPT_EXTIO_FN_PTRS");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Make a copy of the hostname to pass to AddToSocket later
+ //
+ sessionClosure->hostname = PL_strdup(aHostName);
+ if (!sessionClosure->hostname) {
+ NS_ERROR("nsLDAPInstallSSL(): PL_strdup failed\n");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Override functions
+ //
+ sessionClosure->realClose = iofns.lextiof_close;
+ iofns.lextiof_close = nsLDAPSSLClose;
+ sessionClosure->realConnect = iofns.lextiof_connect;
+ iofns.lextiof_connect = nsLDAPSSLConnect;
+ sessionClosure->realDisposeHandle = iofns.lextiof_disposehandle;
+ iofns.lextiof_disposehandle = nsLDAPSSLDisposeHandle;
+
+ if (ldap_set_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ static_cast<void *>(&iofns)) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): error setting LDAP_X_OPT_EXTIO_FN_PTRS");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Store session info. for later retrieval.
+ //
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ sessionInfo.seinfo_appdata = reinterpret_cast<prldap_session_private *>
+ (sessionClosure);
+ if (prldap_set_session_info(ld, nullptr, &sessionInfo) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): error setting prldap session info");
+ free(sessionClosure);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}