mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-11 01:37:31 +09:00
342 lines
12 KiB
C++
342 lines
12 KiB
C++
/* 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;
|
|
}
|