Back to index

lightning-sunbird  0.9+nobinonly
nsLDAPSecurityGlue.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Mark Smith <mcs@netscape.com>
00024  *   Michael Hein <mhein@sun.com>
00025  *   Dan Mosedale <dmose@netscape.com>
00026  *   Seth Spitzer <sspitzer@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 // Only build this code if PSM is being built also
00043 //
00044 #ifdef MOZ_PSM
00045 
00046 #include "nsLDAPInternal.h"
00047 #include "nsCOMPtr.h"
00048 #include "nsIServiceManager.h"
00049 #include "nsIInterfaceRequestor.h"
00050 #include "nsNetCID.h"
00051 #include "nsISocketProvider.h"
00052 #include "nsISSLSocketControl.h"
00053 #include "nsMemory.h"
00054 #include "plstr.h"
00055 #include "ldap.h"
00056 #include "ldappr.h"
00057 
00058 // LDAP per-session data structure.
00059 //
00060 typedef struct {
00061     char *hostname;
00062     LDAP_X_EXTIOF_CLOSE_CALLBACK *realClose;
00063     LDAP_X_EXTIOF_CONNECT_CALLBACK *realConnect;
00064     LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *realDisposeHandle;
00065 } nsLDAPSSLSessionClosure;
00066 
00067 // LDAP per-socket data structure.
00068 //
00069 typedef struct {
00070     nsLDAPSSLSessionClosure *sessionClosure; /* session info */
00071 } nsLDAPSSLSocketClosure;
00072 
00073 // free the per-socket data structure as necessary
00074 //
00075 static void
00076 nsLDAPSSLFreeSocketClosure(nsLDAPSSLSocketClosure **aClosure)
00077 {
00078     if (aClosure && *aClosure) {
00079        nsMemory::Free(*aClosure);
00080        *aClosure = nsnull;
00081     }
00082 }
00083 
00084 // Replacement close() function, which cleans up local stuff associated
00085 // with this socket, and then calls the real close function.
00086 //
00087 extern "C" int LDAP_CALLBACK
00088 nsLDAPSSLClose(int s, struct lextiof_socket_private *socketarg)
00089 {
00090     PRLDAPSocketInfo socketInfo;
00091     nsLDAPSSLSocketClosure *socketClosure;
00092     nsLDAPSSLSessionClosure *sessionClosure;
00093 
00094     // get the socketInfo associated with this socket
00095     //
00096     memset(&socketInfo, 0, sizeof(socketInfo));
00097     socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
00098     if (prldap_get_socket_info(s, socketarg, &socketInfo) != LDAP_SUCCESS) {
00099        NS_ERROR("nsLDAPSSLClose(): prldap_get_socket_info() failed\n");
00100        return -1;
00101     }
00102 
00103     // save off the session closure data in an automatic, since we're going to
00104     // need to call through it
00105     //
00106     socketClosure = NS_REINTERPRET_CAST(nsLDAPSSLSocketClosure *,
00107                                    socketInfo.soinfo_appdata);
00108     sessionClosure = socketClosure->sessionClosure;
00109 
00110     // free the socket closure data
00111     //
00112     nsLDAPSSLFreeSocketClosure(
00113        NS_REINTERPRET_CAST(nsLDAPSSLSocketClosure **,
00114                          &socketInfo.soinfo_appdata));
00115 
00116     // call the real close function
00117     //
00118     return (*(sessionClosure->realClose))(s, socketarg);
00119 }
00120 
00121 // Replacement connection function.  Calls the real connect function,
00122 // 
00123 extern "C" int LDAP_CALLBACK
00124 nsLDAPSSLConnect(const char *hostlist, int defport, int timeout,
00125                unsigned long options, 
00126                struct lextiof_session_private *sessionarg,
00127                struct lextiof_socket_private **socketargp )
00128 {
00129     PRLDAPSocketInfo socketInfo;
00130     PRLDAPSessionInfo sessionInfo;
00131     nsLDAPSSLSocketClosure *socketClosure = nsnull;
00132     nsLDAPSSLSessionClosure *sessionClosure;
00133     int       intfd = -1;
00134     nsCOMPtr <nsISupports> securityInfo;
00135     nsCOMPtr <nsISocketProvider> tlsSocketProvider;
00136     nsCOMPtr <nsISSLSocketControl> sslSocketControl;
00137     nsresult rv;
00138 
00139     // Ensure secure option is set.  Also, clear secure bit in options
00140     // the we pass to the standard connect() function (since it doesn't know
00141     // how to handle the secure option).
00142     //
00143     NS_ASSERTION(options & LDAP_X_EXTIOF_OPT_SECURE, 
00144                "nsLDAPSSLConnect(): called for non-secure connection");
00145     options &= ~LDAP_X_EXTIOF_OPT_SECURE;
00146 
00147     // Retrieve session info. so we can store a pointer to our session info.
00148     // in our socket info. later.
00149     //
00150     memset(&sessionInfo, 0, sizeof(sessionInfo));
00151     sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
00152     if (prldap_get_session_info(nsnull, sessionarg, &sessionInfo) 
00153        != LDAP_SUCCESS) {
00154        NS_ERROR("nsLDAPSSLConnect(): unable to get session info");
00155         return -1;
00156     }
00157     sessionClosure = NS_REINTERPRET_CAST(nsLDAPSSLSessionClosure *,
00158                                     sessionInfo.seinfo_appdata);
00159     
00160     // Call the real connect() callback to make the TCP connection.  If it
00161     // succeeds, *socketargp is set.
00162     //
00163     intfd = (*(sessionClosure->realConnect))(hostlist, defport, timeout, 
00164                                         options, sessionarg, socketargp);
00165     if ( intfd < 0 ) {
00166        PR_LOG(gLDAPLogModule, PR_LOG_DEBUG,
00167               ("nsLDAPSSLConnect(): standard connect() function returned %d",
00168               intfd));
00169         return intfd;
00170     }
00171 
00172     // Retrieve socket info from the newly created socket so that we
00173     // have the PRFileDesc onto which we will be layering SSL.
00174     //
00175     memset(&socketInfo, 0, sizeof(socketInfo));
00176     socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
00177     if (prldap_get_socket_info(intfd, *socketargp, &socketInfo)
00178        != LDAP_SUCCESS)  {
00179        NS_ERROR("nsLDAPSSLConnect(): unable to get socket info");
00180         goto close_socket_and_exit_with_error;
00181     }
00182 
00183     // Allocate a structure to hold our socket-specific data.
00184     //
00185     socketClosure = NS_STATIC_CAST(nsLDAPSSLSocketClosure *, 
00186                                nsMemory::Alloc(
00187                                    sizeof(nsLDAPSSLSocketClosure)));
00188     if (!socketClosure) {
00189        NS_WARNING("nsLDAPSSLConnect(): unable to allocate socket closure");
00190        goto close_socket_and_exit_with_error;
00191     }
00192     memset(socketClosure, 0, sizeof(nsLDAPSSLSocketClosure));
00193     socketClosure->sessionClosure = sessionClosure;
00194 
00195     // Add the NSPR layer for SSL provided by PSM to this socket. 
00196     //
00197     tlsSocketProvider = do_GetService(NS_STARTTLSSOCKETPROVIDER_CONTRACTID, 
00198                                   &rv);
00199     if (NS_FAILED(rv)) {
00200        NS_ERROR("nsLDAPSSLConnect(): unable to get socket provider service");
00201         goto close_socket_and_exit_with_error;
00202     }
00203     // XXXdmose: Note that hostlist can be a list of hosts (in the
00204     // current XPCOM SDK code, it will always be a list of IP
00205     // addresses).  Because of this, we need to use
00206     // sessionClosure->hostname which was passed in separately to tell
00207     // AddToSocket what to match the name in the certificate against.
00208     // What exactly happen will happen when this is used with some IP
00209     // address in the list other than the first one is not entirely
00210     // clear, and I suspect it may depend on the format of the name in
00211     // the certificate.  Need to investigate.
00212     //
00213     rv = tlsSocketProvider->AddToSocket(PR_AF_INET,
00214                                         sessionClosure->hostname, defport,
00215                                         nsnull, 0, 0, socketInfo.soinfo_prfd,
00216                                         getter_AddRefs(securityInfo));
00217     if (NS_FAILED(rv)) {
00218        NS_ERROR("nsLDAPSSLConnect(): unable to add SSL layer to socket");
00219         goto close_socket_and_exit_with_error;
00220     }
00221 
00222     // If possible we want to avoid using SSLv2, as this can confuse
00223     // some directory servers (notably the netscape 4.1 ds).  The only
00224     // way that PSM provides for us to do this is to use a socket that can
00225     // be used for the STARTTLS protocol, because the STARTTLS protocol disallows
00226     // the use of SSLv2.
00227     // (Thanks to Brian Ryner for helping figure this out).
00228     //
00229     sslSocketControl = do_QueryInterface(securityInfo, &rv);
00230     if (NS_FAILED(rv)) {
00231         NS_WARNING("nsLDAPSSLConnect(): unable to QI to nsISSLSocketControl");
00232     } else {
00233        rv = sslSocketControl->StartTLS();
00234        if (NS_FAILED(rv)) {
00235            NS_WARNING("nsLDAPSSLConnect(): StartTLS failed");
00236        }
00237     }
00238 
00239     // Attach our closure to the socketInfo.
00240     //
00241     socketInfo.soinfo_appdata = NS_REINTERPRET_CAST(prldap_socket_private *,
00242                                               socketClosure);
00243     if (prldap_set_socket_info(intfd, *socketargp, &socketInfo)
00244        != LDAP_SUCCESS ) {
00245        NS_ERROR("nsLDAPSSLConnect(): unable to set socket info");
00246     }
00247     return intfd;    // success
00248 
00249 close_socket_and_exit_with_error:
00250     if (socketInfo.soinfo_prfd) {
00251        PR_Close(socketInfo.soinfo_prfd);
00252     }
00253     if (socketClosure) {
00254         nsLDAPSSLFreeSocketClosure(&socketClosure);
00255     }
00256     if ( intfd >= 0 && *socketargp ) {
00257         (*(sessionClosure->realClose))(intfd, *socketargp);
00258     }
00259     return -1;
00260 
00261 }
00262 
00263 // Free data associated with this session (LDAP *) as necessary.
00264 //
00265 static void
00266 nsLDAPSSLFreeSessionClosure(nsLDAPSSLSessionClosure **aSessionClosure)
00267 {
00268     if (aSessionClosure && *aSessionClosure) {
00269 
00270        // free the hostname
00271        //
00272        if ( (*aSessionClosure)->hostname ) {
00273            PL_strfree((*aSessionClosure)->hostname);
00274            (*aSessionClosure)->hostname = nsnull;
00275        }
00276 
00277        // free the structure itself
00278        //
00279        nsMemory::Free(*aSessionClosure);
00280        *aSessionClosure = nsnull;
00281     }
00282 }
00283 
00284 // Replacement session handle disposal code.  First cleans up our local
00285 // stuff, then calls the original session handle disposal function.
00286 //
00287 extern "C" void LDAP_CALLBACK
00288 nsLDAPSSLDisposeHandle(LDAP *ld, struct lextiof_session_private *sessionarg)
00289 {
00290     PRLDAPSessionInfo sessionInfo;
00291     nsLDAPSSLSessionClosure *sessionClosure;
00292     LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
00293 
00294     memset(&sessionInfo, 0, sizeof(sessionInfo));
00295     sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
00296     if (prldap_get_session_info(ld, nsnull, &sessionInfo) == LDAP_SUCCESS) {
00297        sessionClosure = NS_REINTERPRET_CAST(nsLDAPSSLSessionClosure *,
00298                                         sessionInfo.seinfo_appdata);
00299        disposehdl_fn = sessionClosure->realDisposeHandle;
00300        nsLDAPSSLFreeSessionClosure(&sessionClosure);
00301        (*disposehdl_fn)(ld, sessionarg);
00302     } 
00303 }
00304 
00305 // Installs appropriate routines and data for making this connection
00306 // handle SSL.  The aHostName is ultimately passed to PSM and is used to
00307 // validate certificates.
00308 //
00309 nsresult
00310 nsLDAPInstallSSL( LDAP *ld, const char *aHostName)
00311 {
00312     struct ldap_x_ext_io_fns iofns;
00313     nsLDAPSSLSessionClosure *sessionClosure;
00314     PRLDAPSessionInfo sessionInfo;
00315 
00316     // Allocate our own session information.
00317     //
00318     sessionClosure = NS_STATIC_CAST(nsLDAPSSLSessionClosure *, 
00319                                 nsMemory::Alloc(
00320                                    sizeof(nsLDAPSSLSessionClosure)));
00321     if (!sessionClosure) {
00322        return NS_ERROR_OUT_OF_MEMORY;
00323     }
00324     memset(sessionClosure, 0, sizeof(nsLDAPSSLSessionClosure));
00325 
00326     // Override a few functions, saving a pointer to the original function
00327     // in each case so we can call it from our SSL savvy functions.
00328     //
00329     memset(&iofns, 0, sizeof(iofns));
00330     iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
00331     if (ldap_get_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS, 
00332                      NS_STATIC_CAST(void *, &iofns)) != LDAP_SUCCESS) {
00333        NS_ERROR("nsLDAPInstallSSL(): unexpected error getting"
00334                " LDAP_X_OPT_EXTIO_FN_PTRS");
00335        nsLDAPSSLFreeSessionClosure(&sessionClosure);
00336         return NS_ERROR_UNEXPECTED;
00337     }
00338 
00339     // Make a copy of the hostname to pass to AddToSocket later
00340     //
00341     sessionClosure->hostname = PL_strdup(aHostName);
00342     if (!sessionClosure->hostname) {
00343        NS_ERROR("nsLDAPInstallSSL(): PL_strdup failed\n");
00344        nsLDAPSSLFreeSessionClosure(&sessionClosure);
00345        return NS_ERROR_OUT_OF_MEMORY;
00346     }
00347 
00348     // Override functions
00349     //
00350     sessionClosure->realClose = iofns.lextiof_close;
00351     iofns.lextiof_close = nsLDAPSSLClose;
00352     sessionClosure->realConnect = iofns.lextiof_connect;
00353     iofns.lextiof_connect = nsLDAPSSLConnect;
00354     sessionClosure->realDisposeHandle = iofns.lextiof_disposehandle;
00355     iofns.lextiof_disposehandle = nsLDAPSSLDisposeHandle;
00356 
00357     if (ldap_set_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS, 
00358                      NS_STATIC_CAST(void *, &iofns)) != LDAP_SUCCESS) {
00359        NS_ERROR("nsLDAPInstallSSL(): error setting LDAP_X_OPT_EXTIO_FN_PTRS");
00360        nsLDAPSSLFreeSessionClosure(&sessionClosure);
00361         return NS_ERROR_FAILURE;
00362     }
00363 
00364     // Store session info. for later retrieval.
00365     //
00366     sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
00367     sessionInfo.seinfo_appdata = NS_REINTERPRET_CAST(prldap_session_private *,
00368                                                sessionClosure);
00369     if (prldap_set_session_info(ld, nsnull, &sessionInfo) != LDAP_SUCCESS) {
00370        NS_ERROR("nsLDAPInstallSSL(): error setting prldap session info");
00371        nsMemory::Free(sessionClosure);
00372        return NS_ERROR_UNEXPECTED;
00373     }
00374 
00375     return NS_OK;
00376 }
00377 
00378 #endif