Mozilla/mozilla/directory/xpcom/base/src/nsLDAPSecurityGlue.cpp
bugzilla%standard8.demon.co.uk c312d8ec5b Bug 136773 Enable ldap logging on optimized builds. r/sr=dmose
git-svn-id: svn://10.0.0.236/trunk@187168 18797224-902f-48f8-a5cc-f745e15eee43
2006-01-09 17:19:57 +00:00

379 lines
13 KiB
C++

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Smith <mcs@netscape.com>
* Michael Hein <mhein@sun.com>
* Dan Mosedale <dmose@netscape.com>
* Seth Spitzer <sspitzer@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Only build this code if PSM is being built also
//
#ifdef MOZ_PSM
#include "nsLDAPInternal.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsIInterfaceRequestor.h"
#include "nsNetCID.h"
#include "nsISocketProvider.h"
#include "nsISSLSocketControl.h"
#include "nsMemory.h"
#include "plstr.h"
#include "ldap.h"
#include "ldappr.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) {
nsMemory::Free(*aClosure);
*aClosure = nsnull;
}
}
// 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 = NS_REINTERPRET_CAST(nsLDAPSSLSocketClosure *,
socketInfo.soinfo_appdata);
sessionClosure = socketClosure->sessionClosure;
// free the socket closure data
//
nsLDAPSSLFreeSocketClosure(
NS_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 = nsnull;
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(nsnull, sessionarg, &sessionInfo)
!= LDAP_SUCCESS) {
NS_ERROR("nsLDAPSSLConnect(): unable to get session info");
return -1;
}
sessionClosure = NS_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 = NS_STATIC_CAST(nsLDAPSSLSocketClosure *,
nsMemory::Alloc(
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,
nsnull, 0, 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 = NS_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 = nsnull;
}
// free the structure itself
//
nsMemory::Free(*aSessionClosure);
*aSessionClosure = nsnull;
}
}
// 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, nsnull, &sessionInfo) == LDAP_SUCCESS) {
sessionClosure = NS_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 = NS_STATIC_CAST(nsLDAPSSLSessionClosure *,
nsMemory::Alloc(
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,
NS_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,
NS_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 = NS_REINTERPRET_CAST(prldap_session_private *,
sessionClosure);
if (prldap_set_session_info(ld, nsnull, &sessionInfo) != LDAP_SUCCESS) {
NS_ERROR("nsLDAPInstallSSL(): error setting prldap session info");
nsMemory::Free(sessionClosure);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
#endif