summaryrefslogtreecommitdiff
path: root/usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c')
-rw-r--r--usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c1317
1 files changed, 1317 insertions, 0 deletions
diff --git a/usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c b/usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c
new file mode 100644
index 0000000000..795b727ff2
--- /dev/null
+++ b/usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c
@@ -0,0 +1,1317 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * The contents of this file are subject to the Netscape 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/NPL/
+ *
+ * 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 Netscape are
+ * Copyright (C) 1998-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * ldapsinit.c
+ */
+
+#if defined(NET_SSL)
+
+#if defined( _WINDOWS )
+#include <windows.h>
+#endif
+
+/* XXX:mhein The following is a workaround for the redefinition of */
+/* const problem on OSF. Fix to be provided by NSS */
+/* This is a pretty benign workaround for us which */
+/* should not cause problems in the future even if */
+/* we forget to take it out :-) */
+
+#ifdef OSF1V4D
+#ifndef __STDC__
+# define __STDC__
+#endif /* __STDC__ */
+#endif /* OSF1V4D */
+
+#include <errno.h>
+#include <nspr.h>
+#include <cert.h>
+#include <key.h>
+#include <ssl.h>
+#include <sslproto.h>
+#include <sslerr.h>
+#include <prnetdb.h>
+
+#include <ldap.h>
+
+#include <ldappr.h>
+#include <pk11func.h>
+
+#ifdef _SOLARIS_SDK
+#include "solaris-int.h"
+#include <libintl.h>
+#include <syslog.h>
+#include <nsswitch.h>
+#include <synch.h>
+#include <nss_dbdefs.h>
+#include <netinet/in.h>
+
+#define HOST_BUF_SIZE 2048
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1)
+#endif
+
+extern int
+str2hostent(const char *instr, int lenstr, void *ent, char *buffer,
+ int buflen);
+
+extern int
+str2hostent6(const char *instr, int lenstr, void *ent, char *buffer,
+ int buflen);
+
+extern LDAPHostEnt *
+_ns_gethostbyaddr(LDAP *ld, const char *addr, int length, int type,
+ LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
+ void *extradata);
+
+static char *host_service = NULL;
+
+static DEFINE_NSS_DB_ROOT(db_root_hosts);
+static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
+#endif
+
+/*
+ * Data structure to hold the standard NSPR I/O function pointers set by
+ * libprldap. We save them in our session data structure so we can call
+ * them from our own I/O functions (we add functionality to support SSL
+ * while using libprldap's functions as much as possible).
+ */
+typedef struct ldapssl_std_functions {
+ LDAP_X_EXTIOF_CLOSE_CALLBACK *lssf_close_fn;
+ LDAP_X_EXTIOF_CONNECT_CALLBACK *lssf_connect_fn;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *lssf_disposehdl_fn;
+} LDAPSSLStdFunctions;
+
+
+
+/*
+ * LDAP session data structure.
+ */
+typedef struct ldapssl_session_info {
+ int lssei_using_pcks_fns;
+ int lssei_ssl_strength;
+ char *lssei_certnickname;
+ char *lssei_keypasswd;
+ LDAPSSLStdFunctions lssei_std_functions;
+ CERTCertDBHandle *lssei_certdbh;
+#ifdef _SOLARIS_SDK
+ /*
+ * This is a hack.
+ * ld is used so that we can use libldap's gethostbyaddr
+ * resolver. This is needed to prevent recursion with libsldap.
+ */
+ LDAP *ld;
+#endif /* _SOLARIS_SDK */
+} LDAPSSLSessionInfo;
+
+
+/*
+ * LDAP socket data structure.
+ */
+typedef struct ldapssl_socket_info {
+ LDAPSSLSessionInfo *soi_sessioninfo; /* session info */
+} LDAPSSLSocketInfo;
+
+
+/*
+ * XXXceb This is a hack until the new IO functions are done.
+ * this function MUST be called before ldap_enable_clienauth.
+ * right now, this function is called in ldapssl_pkcs_init();
+ */
+
+static int using_pkcs_functions = 0;
+
+void set_using_pkcs_functions( int val )
+{
+ using_pkcs_functions = val;
+}
+
+
+/*
+ * Utility functions:
+ */
+static void ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp );
+static void ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp );
+
+
+/*
+ * SSL Stuff
+ */
+
+static int ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd,
+ PRBool checkSig, PRBool isServer);
+
+/*
+ * client auth stuff
+ */
+static int get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
+ CERTDistNames *caNames, CERTCertificate **pRetCert,
+ SECKEYPrivateKey **pRetKey );
+static int get_keyandcert( LDAPSSLSessionInfo *ssip,
+ CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
+ char **errmsgp );
+static int check_clientauth_nicknames_and_passwd( LDAP *ld,
+ LDAPSSLSessionInfo *ssip );
+static char *get_keypassword( PK11SlotInfo *slot, PRBool retry,
+ void *sessionarg );
+
+/*
+ * Static variables.
+ */
+#ifdef _SOLARIS_SDK
+static int default_ssl_strength = LDAPSSL_AUTH_CNCHECK;
+#else
+static int default_ssl_strength = LDAPSSL_AUTH_CERT;
+#endif
+
+/*
+ * Like ldap_init(), except also install I/O routines from libsec so we
+ * can support SSL. If defsecure is non-zero, SSL is enabled for the
+ * default connection as well.
+ */
+LDAP *
+LDAP_CALL
+ldapssl_init( const char *defhost, int defport, int defsecure )
+{
+ LDAP *ld;
+
+
+#ifndef LDAP_SSLIO_HOOKS
+ return( NULL );
+#else
+ if (0 ==defport)
+ defport = LDAPS_PORT;
+
+ if (( ld = ldap_init( defhost, defport )) == NULL ) {
+ return( NULL );
+ }
+
+ if ( ldapssl_install_routines( ld ) < 0 || ldap_set_option( ld,
+ LDAP_OPT_SSL, defsecure ? LDAP_OPT_ON : LDAP_OPT_OFF ) != 0 ) {
+ PR_SetError( PR_UNKNOWN_ERROR, EINVAL ); /* XXXmcs: just a guess! */
+ ldap_unbind( ld );
+ return( NULL );
+ }
+
+ return( ld );
+#endif
+}
+
+
+static int
+ldapssl_close(int s, struct lextiof_socket_private *socketarg)
+{
+ PRLDAPSocketInfo soi;
+ LDAPSSLSocketInfo *ssoip;
+ LDAPSSLSessionInfo *sseip;
+
+ memset( &soi, 0, sizeof(soi));
+ soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if ( prldap_get_socket_info( s, socketarg, &soi ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+
+ ssoip = (LDAPSSLSocketInfo *)soi.soinfo_appdata;
+ sseip = ssoip->soi_sessioninfo;
+
+ ldapssl_free_socket_info( (LDAPSSLSocketInfo **)&soi.soinfo_appdata );
+
+ return( (*(sseip->lssei_std_functions.lssf_close_fn))( s, socketarg ));
+}
+
+static int
+do_ldapssl_connect(const char *hostlist, int defport, int timeout,
+ unsigned long options, struct lextiof_session_private *sessionarg,
+ struct lextiof_socket_private **socketargp, int clientauth )
+{
+ int intfd = -1;
+ PRBool secure;
+ PRLDAPSessionInfo sei;
+ PRLDAPSocketInfo soi;
+ LDAPSSLSocketInfo *ssoip = NULL;
+ LDAPSSLSessionInfo *sseip;
+ PRFileDesc *sslfd = NULL;
+#ifdef _SOLARIS_SDK
+ int port;
+ int parse_err;
+ char *host = NULL;
+ char *name;
+ struct ldap_x_hostlist_status
+ *status = NULL;
+ in_addr_t addr_ipv4;
+ in6_addr_t addr_ipv6;
+ char *host_buf;
+ LDAPHostEnt *hent;
+ LDAPHostEnt host_ent;
+ int stat;
+ int type;
+#endif /* _SOLARIS_SDK */
+
+ /*
+ * Determine if 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).
+ */
+ if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) {
+ secure = PR_TRUE;
+ options &= ~LDAP_X_EXTIOF_OPT_SECURE;
+ } else {
+ secure = PR_FALSE;
+ }
+
+ /*
+ * Retrieve session info. so we can store a pointer to our session info.
+ * in our socket info. later.
+ */
+ memset( &sei, 0, sizeof(sei));
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if ( prldap_get_session_info( NULL, sessionarg, &sei ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+ sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
+
+ /*
+ * Call the standard connect() callback to make the TCP connection.
+ * If it succeeds, *socketargp is set.
+ */
+
+ intfd = (*(sseip->lssei_std_functions.lssf_connect_fn))( hostlist, defport,
+ timeout, options, sessionarg, socketargp
+#ifdef _SOLARIS_SDK
+ , &host );
+#else
+ );
+#endif /* _SOLARIS_SDK */
+
+ if ( intfd < 0 ) {
+ return( intfd );
+ }
+
+#ifdef _SOLARIS_SDK
+ /*
+ * Determine if the "host name" is an ip address. If so,
+ * we must look up the actual host name corresponding to
+ * it.
+ */
+ if ( NULL == host ) {
+ goto close_socket_and_exit_with_error;
+ }
+ type = AF_UNSPEC;
+ if (strlen(host) < INET6_ADDRSTRLEN &&
+ inet_pton(AF_INET6, host, &addr_ipv6) == 1) {
+ type = AF_INET6;
+ } else if (strlen(host) < INET_ADDRSTRLEN &&
+ inet_pton(AF_INET, host, &addr_ipv4) == 1) {
+ type = AF_INET;
+ }
+ if (type == AF_INET || type == AF_INET6) {
+ host_buf = malloc(HOST_BUF_SIZE);
+ if (host_buf == NULL) {
+ /* will free host in close_socket_and_exit_with_error */
+ goto close_socket_and_exit_with_error;
+ }
+
+ /* Call ldap layer's gethostbyaddr resolver */
+ hent = _ns_gethostbyaddr(sseip->ld, host, strlen(host), type,
+ &host_ent, host_buf, HOST_BUF_SIZE, &stat, NULL);
+
+ /* If we are unable to lookup the host addr, we fail! */
+ if (hent == NULL) {
+ syslog(LOG_WARNING,
+ "libldap: do_ldapssl_connect: "
+ "Unable to resolve '%s'", host);
+ free(host_buf);
+ /* will free host in close_socket_and_exit_with_error */
+ goto close_socket_and_exit_with_error;
+ }
+ /* We support only the primary host name */
+ else {
+ if (hent->ldaphe_name != NULL)
+ name = strdup(hent->ldaphe_name);
+ free(host_buf);
+ if (name == NULL)
+ goto close_socket_and_exit_with_error;
+ else
+ ldap_memfree(host); host = NULL;
+ host = name;
+ }
+ }
+#endif /* _SOLARIS_SDK */
+
+ /*
+ * Retrieve socket info. so we have the PRFileDesc.
+ */
+ memset( &soi, 0, sizeof(soi));
+ soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if ( prldap_get_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) {
+ goto close_socket_and_exit_with_error;
+ }
+
+ /*
+ * Allocate a structure to hold our socket-specific data.
+ */
+ if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
+ goto close_socket_and_exit_with_error;
+ }
+ ssoip->soi_sessioninfo = sseip;
+
+ /*
+ * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
+ */
+ if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
+ goto close_socket_and_exit_with_error;
+ }
+
+ if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess ||
+ SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure )
+ != SECSuccess || ( secure && SSL_ResetHandshake( sslfd,
+ PR_FALSE ) != SECSuccess )) {
+ goto close_socket_and_exit_with_error;
+ }
+
+ /*
+ * Let the standard NSPR to LDAP layer know about the new socket and
+ * our own socket-specific data.
+ */
+ soi.soinfo_prfd = sslfd;
+ soi.soinfo_appdata = (void *)ssoip;
+ if ( prldap_set_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) {
+ goto close_socket_and_exit_with_error;
+ }
+
+#ifdef _SOLARIS_SDK
+ /*
+ * Set hostname which will be retrieved (depending on ssl strength) when
+ * using client or server auth.
+ */
+ if (SSL_SetURL(sslfd, host) != SECSuccess)
+ goto close_socket_and_exit_with_error;
+ ldap_memfree(host);
+ host = NULL;
+#endif /* _SOLARIS_SDK */
+
+ sslfd = NULL; /* so we don't close the socket twice upon error */
+
+ /*
+ * Install certificate hook function.
+ */
+ SSL_AuthCertificateHook( soi.soinfo_prfd,
+ (SSLAuthCertificate)ldapssl_AuthCertificate,
+ (void *)sseip);
+
+ if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
+ get_clientauth_data, clientauth ? sseip : NULL ) != 0 ) {
+ goto close_socket_and_exit_with_error;
+ }
+
+ return( intfd ); /* success */
+
+close_socket_and_exit_with_error:
+#ifdef _SOLARIS_SDK
+ if ( NULL != host ) ldap_memfree(host);
+#endif /* _SOLARIS_SDK */
+ if ( NULL != sslfd ) {
+ PR_Close( sslfd );
+ }
+ if ( NULL != ssoip ) {
+ ldapssl_free_socket_info( &ssoip );
+ }
+ if ( intfd >= 0 && NULL != *socketargp ) {
+ (*(sseip->lssei_std_functions.lssf_close_fn))( intfd, *socketargp );
+ }
+ return( -1 );
+}
+
+
+static int
+ldapssl_connect(const char *hostlist, int defport, int timeout,
+ unsigned long options, struct lextiof_session_private *sessionarg,
+ struct lextiof_socket_private **socketargp )
+{
+ return( do_ldapssl_connect( hostlist, defport, timeout, options,
+ sessionarg, socketargp, 0 ));
+}
+
+
+static int
+ldapssl_clientauth_connect(const char *hostlist, int defport, int timeout,
+ unsigned long options, struct lextiof_session_private *sessionarg,
+ struct lextiof_socket_private **socketargp )
+{
+ return( do_ldapssl_connect( hostlist, defport, timeout, options,
+ sessionarg, socketargp, 1 ));
+}
+
+
+static void
+ldapssl_disposehandle(LDAP *ld, struct lextiof_session_private *sessionarg)
+{
+ PRLDAPSessionInfo sei;
+ LDAPSSLSessionInfo *sseip;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
+
+ memset( &sei, 0, sizeof( sei ));
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS ) {
+ sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
+ disposehdl_fn = sseip->lssei_std_functions.lssf_disposehdl_fn;
+ ldapssl_free_session_info( &sseip );
+ (*disposehdl_fn)( ld, sessionarg );
+ }
+}
+
+
+/*
+ * Install I/O routines from libsec and NSPR into libldap to allow libldap
+ * to do SSL.
+ *
+ * We rely on libprldap to provide most of the functions, and then we override
+ * a few of them to support SSL.
+ */
+int
+LDAP_CALL
+ldapssl_install_routines( LDAP *ld )
+{
+#ifndef LDAP_SSLIO_HOOKS
+ ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
+ return( -1 );
+#else
+ struct ldap_x_ext_io_fns iofns;
+ LDAPSSLSessionInfo *ssip;
+ PRLDAPSessionInfo sei;
+
+/*
+ * This is done within ldap_init() and
+ * ldap_init() is called from ldapssl_init()
+ */
+#ifndef _SOLARIS_SDK
+ if ( prldap_install_routines(
+ ld,
+ 1 /* shared -- we have to assume it is */ )
+ != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+#endif /*_SOLARIS_SDK*/
+
+ /*
+ * Allocate our own session information.
+ */
+ if ( NULL == ( ssip = (LDAPSSLSessionInfo *)PR_Calloc( 1,
+ sizeof( LDAPSSLSessionInfo )))) {
+ ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
+ return( -1 );
+ }
+ /*
+ * Initialize session info.
+ * XXX: it would be nice to be able to set these on a per-session basis:
+ * lssei_using_pcks_fns
+ * lssei_certdbh
+ */
+ ssip->lssei_ssl_strength = default_ssl_strength;
+ ssip->lssei_using_pcks_fns = using_pkcs_functions;
+ ssip->lssei_certdbh = CERT_GetDefaultCertDB();
+#ifdef _SOLARIS_SDK
+ /*
+ * This is part of a hack to allow the ssl portion of the
+ * library to call the ldap library gethostbyaddr resolver.
+ */
+ ssip->ld = ld;
+#endif /* _SOLARIS_SDK */
+
+ /*
+ * override a few functions, saving a pointer to the standard 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, (void *)&iofns ) < 0 ) {
+ ldapssl_free_session_info( &ssip );
+ return( -1 );
+ }
+
+ /* override socket, connect, and ioctl */
+ ssip->lssei_std_functions.lssf_connect_fn = iofns.lextiof_connect;
+ iofns.lextiof_connect = ldapssl_connect;
+ ssip->lssei_std_functions.lssf_close_fn = iofns.lextiof_close;
+ iofns.lextiof_close = ldapssl_close;
+ ssip->lssei_std_functions.lssf_disposehdl_fn = iofns.lextiof_disposehandle;
+ iofns.lextiof_disposehandle = ldapssl_disposehandle;
+
+ if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
+ ldapssl_free_session_info( &ssip );
+ return( -1 );
+ }
+
+ /*
+ * Store session info. for later retrieval.
+ */
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ sei.seinfo_appdata = (void *)ssip;
+ if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+
+ return( 0 );
+#endif
+}
+
+
+/*
+ * Set the SSL strength for an existing SSL-enabled LDAP session handle.
+ *
+ * See the description of ldapssl_serverauth_init() above for valid
+ * sslstrength values. If ld is NULL, the default for new LDAP session
+ * handles is set.
+ *
+ * Returns 0 if all goes well and -1 if an error occurs.
+ */
+int
+LDAP_CALL
+ldapssl_set_strength( LDAP *ld, int sslstrength )
+{
+ int rc = 0; /* assume success */
+
+ if ( sslstrength != LDAPSSL_AUTH_WEAK &&
+ sslstrength != LDAPSSL_AUTH_CERT &&
+ sslstrength != LDAPSSL_AUTH_CNCHECK ) {
+ rc = -1;
+ } else {
+ if ( NULL == ld ) { /* set default strength */
+ default_ssl_strength = sslstrength;
+ } else { /* set session-specific strength */
+ PRLDAPSessionInfo sei;
+ LDAPSSLSessionInfo *sseip;
+
+ memset( &sei, 0, sizeof( sei ));
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS )
+{
+ sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
+ sseip->lssei_ssl_strength = sslstrength;
+ } else {
+ rc = -1;
+ }
+ }
+ }
+
+ return( rc );
+}
+
+int
+LDAP_CALL
+ldapssl_enable_clientauth( LDAP *ld, char *keynickname,
+ char *keypasswd, char *certnickname )
+{
+#ifndef LDAP_SSLIO_HOOKS
+ ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
+ return( -1 );
+#else
+ struct ldap_x_ext_io_fns iofns;
+ LDAPSSLSessionInfo *ssip;
+ PRLDAPSessionInfo sei;
+
+ /*
+ * Check parameters
+ */
+ if ( certnickname == NULL || keypasswd == NULL ) {
+ ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
+ return( -1 );
+ }
+
+ /*
+ * Update session info. data structure.
+ */
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+ ssip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
+ if ( NULL == ssip ) {
+ ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
+ return( -1 );
+ }
+ ssip->lssei_certnickname = PL_strdup( certnickname );
+ ssip->lssei_keypasswd = PL_strdup( keypasswd );
+
+ if ( NULL == ssip->lssei_certnickname || NULL == ssip->lssei_keypasswd ) {
+ ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
+ return( -1 );
+ }
+
+ if ( check_clientauth_nicknames_and_passwd( ld, ssip ) != 0 ) {
+ return( -1 );
+ }
+
+ /*
+ * replace standard SSL CONNECT function with client auth aware one
+ */
+ memset( &iofns, 0, sizeof(iofns));
+ iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
+ if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
+ != 0 ) {
+ return( -1 );
+ }
+
+ if ( iofns.lextiof_connect != ldapssl_connect ) {
+ /* standard SSL setup has not done */
+ ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
+ return( -1 );
+ }
+
+ iofns.lextiof_connect = ldapssl_clientauth_connect;
+
+ if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
+ != 0 ) {
+ return( -1 );
+ }
+
+ return( 0 );
+#endif
+}
+
+
+static void
+ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp )
+{
+ if ( NULL != ssipp && NULL != *ssipp ) {
+ if ( NULL != (*ssipp)->lssei_certnickname ) {
+ PL_strfree( (*ssipp)->lssei_certnickname );
+ (*ssipp)->lssei_certnickname = NULL;
+ }
+ if ( NULL != (*ssipp)->lssei_keypasswd ) {
+ PL_strfree( (*ssipp)->lssei_keypasswd );
+ (*ssipp)->lssei_keypasswd = NULL;
+ }
+ PR_Free( *ssipp );
+ *ssipp = NULL;
+ }
+}
+
+
+static void
+ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp )
+{
+ if ( NULL != soipp && NULL != *soipp ) {
+ PR_Free( *soipp );
+ *soipp = NULL;
+ }
+}
+
+
+/* this function provides cert authentication. This is called during
+ * the SSL_Handshake process. Once the cert has been retrieved from
+ * the server, the it is checked, using VerifyCertNow(), then
+ * the cn is checked against the host name, set with SSL_SetURL()
+ */
+
+static int
+ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, PRBool checkSig,
+ PRBool isServer)
+{
+ SECStatus rv = SECFailure;
+ LDAPSSLSessionInfo *sseip;
+ CERTCertificate *cert;
+ SECCertUsage certUsage;
+ char *hostname = (char *)0;
+
+ if (!sessionarg || !socket)
+ return rv;
+
+ sseip = (LDAPSSLSessionInfo *)sessionarg;
+
+ if (LDAPSSL_AUTH_WEAK == sseip->lssei_ssl_strength ) { /* no check */
+ return SECSuccess;
+ }
+
+ if ( isServer ) {
+ certUsage = certUsageSSLClient;
+ } else {
+ certUsage = certUsageSSLServer;
+ }
+ cert = SSL_PeerCertificate( fd );
+
+ rv = CERT_VerifyCertNow(sseip->lssei_certdbh, cert, checkSig,
+ certUsage, NULL);
+
+ if ( rv != SECSuccess || isServer )
+ return rv;
+
+ if ( LDAPSSL_AUTH_CNCHECK == sseip->lssei_ssl_strength )
+ {
+ /* cert is OK. This is the client side of an SSL connection.
+ * Now check the name field in the cert against the desired hostname.
+ * NB: This is our only defense against Man-In-The-Middle (MITM)
+ * attacks!
+ */
+
+ hostname = SSL_RevealURL( fd );
+
+ if (hostname && hostname[0]) {
+ rv = CERT_VerifyCertName(cert, hostname);
+ } else {
+ rv = SECFailure;
+ }
+ if (hostname)
+ PORT_Free(hostname);
+ if (rv != SECSuccess)
+ PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+ }
+
+ return((int)rv);
+}
+
+
+/*
+ * called during SSL client auth. when server wants our cert and key.
+ * return 0 if we succeeded and set *pRetCert and *pRetKey, -1 otherwise.
+ * if -1 is returned SSL will proceed without sending a cert.
+ */
+
+static int
+get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
+ CERTDistNames *caNames, CERTCertificate **pRetCert,
+ SECKEYPrivateKey **pRetKey )
+
+{
+ LDAPSSLSessionInfo *ssip;
+
+ if (( ssip = (LDAPSSLSessionInfo *)sessionarg ) == NULL ) {
+ return( -1 ); /* client auth. not enabled */
+ }
+
+ return( get_keyandcert( ssip, pRetCert, pRetKey, NULL ));
+}
+
+static int
+get_keyandcert( LDAPSSLSessionInfo *ssip,
+ CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
+ char **errmsgp )
+{
+ CERTCertificate *cert;
+ SECKEYPrivateKey *key;
+
+ if (( cert = PK11_FindCertFromNickname( ssip->lssei_certnickname, NULL ))
+ == NULL ) {
+ if ( errmsgp != NULL ) {
+ *errmsgp = dgettext(TEXT_DOMAIN, "unable to find certificate");
+ }
+ return( -1 );
+ }
+
+ {
+ PK11_SetPasswordFunc( get_keypassword );
+ }
+
+
+
+ if (( key = PK11_FindKeyByAnyCert( cert, (void *)ssip )) == NULL ) {
+ CERT_DestroyCertificate( cert );
+ if ( errmsgp != NULL ) {
+ *errmsgp = dgettext(TEXT_DOMAIN, "bad key or key password");
+ }
+ return( -1 );
+ }
+
+ *pRetCert = cert;
+ *pRetKey = key;
+ return( 0 );
+}
+
+
+/*
+ * This function returns the password to NSS.
+ * This function is enable through PK11_SetPasswordFunc
+ * only if pkcs functions are not being used.
+ */
+
+static char *
+get_keypassword( PK11SlotInfo *slot, PRBool retry, void *sessionarg )
+{
+ LDAPSSLSessionInfo *ssip;
+
+ if ( retry)
+ return (NULL);
+
+ ssip = (LDAPSSLSessionInfo *)sessionarg;
+ if ( NULL == ssip ) {
+ return( NULL );
+ }
+
+ return( ssip->lssei_keypasswd );
+}
+
+
+/*
+ * performs some basic checks on clientauth cert and key/password
+ *
+ * XXXmcs: could perform additional checks... see servers/slapd/ssl.c
+ * 1) check expiration
+ * 2) check that public key in cert matches private key
+ * see ns/netsite/ldap/servers/slapd/ssl.c:slapd_ssl_init() for example code.
+ */
+static int
+check_clientauth_nicknames_and_passwd( LDAP *ld, LDAPSSLSessionInfo *ssip )
+{
+ char *errmsg = NULL;
+ CERTCertificate *cert = NULL;
+ SECKEYPrivateKey *key = NULL;
+ int rv;
+
+ rv = get_keyandcert( ssip, &cert, &key, &errmsg );
+
+ if ( rv != 0 ) {
+ if ( errmsg != NULL ) {
+ errmsg = strdup( errmsg );
+ }
+ ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg );
+ return( -1 );
+ }
+
+ if ( cert != NULL ) {
+ CERT_DestroyCertificate( cert );
+ }
+ if ( key != NULL ) {
+ SECKEY_DestroyPrivateKey( key );
+ }
+ return( 0 );
+}
+
+
+#if 0 /* NOT_NEEDED_IN_LIBLDAP */
+/* there are patches and kludges. this is both. force some linkers to
+ * link this stuff in
+ */
+int stubs_o_stuff( void )
+{
+ PRExplodedTime exploded;
+ PLArenaPool pool;
+
+ const char *name ="t";
+ PRUint32 size = 0, align = 0;
+
+ PR_ImplodeTime( &exploded );
+ PL_InitArenaPool( &pool, name, size, align);
+ PR_Cleanup();
+ PR_fprintf((PRFileDesc*)stderr, "Bad IDEA!!");
+
+ return 0;
+
+}
+#endif /* NOT_NEEDED_IN_LIBLDAP */
+
+
+/*
+ * Import the file descriptor corresponding to the socket of an already
+ * open LDAP connection into SSL, and update the socket and session
+ * information accordingly.
+ */
+int ldapssl_import_fd ( LDAP *ld, int secure )
+{
+ PRLDAPSessionInfo sei;
+ PRLDAPSocketInfo soi;
+ LDAPSSLSocketInfo *ssoip = NULL;
+ LDAPSSLSessionInfo *sseip;
+ PRFileDesc *sslfd = NULL;
+
+
+ /*
+ * Retrieve session info. so we can store a pointer to our session info.
+ * in our socket info. later.
+ */
+ memset( &sei, 0, sizeof(sei));
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+ sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
+
+
+ /*
+ * Retrieve socket info. so we have the PRFileDesc.
+ */
+ memset( &soi, 0, sizeof(soi));
+ soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if ( prldap_get_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+
+ /*
+ * Allocate a structure to hold our socket-specific data.
+ */
+ if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
+ goto reset_socket_and_exit_with_error;
+ }
+ ssoip->soi_sessioninfo = sseip;
+
+ /*
+ * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
+ */
+ if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
+ goto reset_socket_and_exit_with_error;
+ }
+
+ if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess ||
+ SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure )
+ != SECSuccess || ( secure && SSL_ResetHandshake( sslfd,
+ PR_FALSE ) != SECSuccess )) {
+ goto reset_socket_and_exit_with_error;
+ }
+
+ /*
+ * Let the standard NSPR to LDAP layer know about the new socket and
+ * our own socket-specific data.
+ */
+ soi.soinfo_prfd = sslfd;
+ soi.soinfo_appdata = (void *)ssoip;
+ if ( prldap_set_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
+ goto reset_socket_and_exit_with_error;
+ }
+
+ /*
+ * Install certificate hook function.
+ */
+ if ( SSL_AuthCertificateHook( soi.soinfo_prfd,
+ (SSLAuthCertificate)ldapssl_AuthCertificate,
+ (void *)CERT_GetDefaultCertDB()) != 0 ) {
+ goto reset_socket_and_exit_with_error;
+ }
+
+ if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
+ get_clientauth_data, sseip->lssei_certnickname ? sseip : NULL )
+ != 0 ) {
+ goto reset_socket_and_exit_with_error;
+ }
+
+ return 0;
+
+ reset_socket_and_exit_with_error:
+ if ( NULL != sslfd ) {
+ /*
+ * "Unimport" the socket from SSL, i.e. get rid of the upper layer of
+ * the file descriptor stack, which represents SSL.
+ */
+ soi.soinfo_prfd = sslfd;
+ sslfd = PR_PopIOLayer( soi.soinfo_prfd, PR_TOP_IO_LAYER );
+ sslfd->dtor( sslfd );
+ }
+ if ( NULL != ssoip ) {
+ ldapssl_free_socket_info( &ssoip );
+ soi.soinfo_appdata = NULL;
+ }
+ prldap_set_default_socket_info( ld, &soi );
+
+ return( -1 );
+}
+
+
+/*
+ * Reset an LDAP session from SSL to a non-secure status.
+ * Basically, this function undoes the work done by ldapssl_install_routines.
+ */
+int ldapssl_reset_to_nonsecure ( LDAP *ld )
+{
+ PRLDAPSessionInfo sei;
+ LDAPSSLSessionInfo *sseip;
+
+ struct ldap_x_ext_io_fns iofns;
+ int rc = 0;
+
+ /*
+ * Retrieve session info.
+ */
+ memset( &sei, 0, sizeof(sei));
+ sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+ sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
+
+ if ( sseip != NULL ) {
+ /*
+ * Reset the standard extended io 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, (void *)&iofns )
+ < 0) {
+ rc = -1;
+ goto free_session_info;
+ }
+
+ /* reset socket, connect, and ioctl */
+ iofns.lextiof_connect = sseip->lssei_std_functions.lssf_connect_fn;
+ iofns.lextiof_close = sseip->lssei_std_functions.lssf_close_fn;
+ iofns.lextiof_disposehandle =
+ sseip->lssei_std_functions.lssf_disposehdl_fn;
+
+ if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
+ < 0) {
+ rc = -1;
+ goto free_session_info;
+ }
+
+free_session_info:
+ ldapssl_free_session_info( &sseip );
+ sei.seinfo_appdata = NULL;
+ if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
+ rc = -1;
+ }
+ } /* if ( sseip && *sseip ) */
+
+ if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_OFF ) < 0 ) {
+ return (-1);
+ }
+
+ return rc;
+}
+
+
+#ifdef _SOLARIS_SDK
+static void
+_nss_initf_ipnodes(nss_db_params_t *p)
+{
+ static char *no_service = "";
+
+ p->name = NSS_DBNAM_IPNODES;
+ p->flags |= NSS_USE_DEFAULT_CONFIG;
+ p->default_config = host_service == NULL ? no_service : host_service;
+}
+
+static void
+_nss_initf_hosts(nss_db_params_t *p)
+{
+ static char *no_service = "";
+
+ p->name = NSS_DBNAM_HOSTS;
+ p->flags |= NSS_USE_DEFAULT_CONFIG;
+ p->default_config = host_service == NULL ? no_service : host_service;
+}
+
+static struct hostent *
+_switch_gethostbyaddr_r(const char *addr, int len, int type,
+ struct hostent *result, char *buffer, int buflen,
+ int *h_errnop)
+{
+ nss_XbyY_args_t arg;
+ nss_status_t res;
+ int (*str2ent)();
+ void (*nss_initf)();
+ nss_db_root_t *nss_db_root;
+
+ if (AF_INET == type) {
+ str2ent = str2hostent;
+ nss_initf = _nss_initf_hosts;
+ nss_db_root = &db_root_hosts;
+ } else if (AF_INET6 == type) {
+ str2ent = str2hostent6;
+ nss_initf = _nss_initf_ipnodes;
+ nss_db_root = &db_root_ipnodes;
+ } else {
+ return NULL;
+ }
+
+ NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
+
+ arg.key.hostaddr.addr = addr;
+ arg.key.hostaddr.len = len;
+ arg.key.hostaddr.type = type;
+ arg.stayopen = 0;
+
+ res = nss_search(nss_db_root, nss_initf,
+ NSS_DBOP_HOSTS_BYADDR, &arg);
+ arg.status = res;
+ *h_errnop = arg.h_errno;
+ return (struct hostent *)NSS_XbyY_FINI(&arg);
+}
+
+/*
+ * ns_gethostbyaddr is used to be a substitute gethostbyaddr for
+ * libldap when ssl will need to determine the fully qualified
+ * host name from an address when it is unsafe to use the normal
+ * nameservice functions.
+ *
+ * Note that the ldap name service resolver calls this with the address as
+ * a character string - which we must convert into address form.
+ */
+
+/*ARGSUSED*/
+static LDAPHostEnt *
+ns_gethostbyaddr(const char *addr, int len, int type,
+ LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
+ void *extradata)
+{
+ LDAPHostEnt *ldap_hent;
+ int h_errno;
+ struct hostent h_ent;
+ struct hostent *h_e = NULL;
+ struct in_addr a;
+ struct in6_addr a6;
+ int inet_error; /* error returned by inet_pton */
+
+
+ if (addr == NULL || result == NULL || buffer == NULL ||
+ (type != AF_INET && type != AF_INET6))
+ return (NULL);
+
+
+ (void) memset(&h_ent, 0, sizeof (h_ent));
+
+ if (AF_INET == type) {
+ if (inet_pton(type, addr, &a.s_addr) == 1) {
+ h_e = _switch_gethostbyaddr_r((char *)&a,
+ sizeof (a.s_addr), type, &h_ent,
+ buffer, buflen, &h_errno);
+ }
+ } else if (AF_INET6 == type) {
+ if (inet_pton(type, addr, &a6.s6_addr) == 1) {
+ h_e = _switch_gethostbyaddr_r((char *)&a6,
+ sizeof (a6.s6_addr), type, &h_ent,
+ buffer, buflen, &h_errno);
+ }
+ }
+
+ if (h_e == NULL) {
+ ldap_hent = NULL;
+ } else {
+ (void) memset(result, 0, sizeof (LDAPHostEnt));
+ ldap_hent = result;
+ result->ldaphe_name = h_e->h_name;
+ result->ldaphe_aliases = h_e->h_aliases;
+ result->ldaphe_addrtype = h_e->h_addrtype;
+ result->ldaphe_length = h_e->h_length;
+ result->ldaphe_addr_list = h_e->h_addr_list;
+ }
+ return (ldap_hent);
+}
+
+/*
+ * ldapssl_install_gethostbyaddr attempts to prevent recursion in
+ * gethostbyaddr calls when an ip address is given to ssl. This ip address
+ * must be resolved to a host name.
+ *
+ * For example, libsldap cannot use LDAP to resolve this address to a
+ * name because of recursion. The caller is instructing libldap to skip
+ * the specified name service when resolving addresses for the specified
+ * ldap connection.
+ *
+ * Currently only ldap and dns name services always return fully qualified
+ * names. The other name services (files, nis, and nisplus) will returned
+ * fully qualified names if the host names are stored as fully qualified names
+ * in these name services.
+ *
+ * Note:
+ *
+ * Since host_service applies to all connections, calling
+ * ldapssl_install_gethostbyaddr with different name services to
+ * skip will lead to unpredictable results.
+ *
+ * Returns:
+ * 0 if success
+ * -1 if failure
+ */
+
+int
+ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip)
+{
+ enum __nsw_parse_err pserr;
+ struct __nsw_switchconfig *conf;
+ struct __nsw_lookup *lkp;
+ struct ldap_dns_fns dns_fns;
+ char *name_list = NULL;
+ char *tmp;
+ const char *name;
+ int len;
+ boolean_t got_skip = B_FALSE;
+
+ /*
+ * db_root_hosts.lock mutex is used to ensure that the name list
+ * is not in use by the name service switch while we are updating
+ * the host_service
+ */
+
+ (void) mutex_lock(&db_root_hosts.lock);
+ conf = __nsw_getconfig("hosts", &pserr);
+ if (conf == NULL) {
+ (void) mutex_unlock(&db_root_hosts.lock);
+ return (0);
+ }
+
+ /* check for ldap and count other backends */
+ for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
+ name = lkp->service_name;
+ if (strcmp(name, skip) == 0) {
+ got_skip = B_TRUE;
+ continue;
+ }
+ if (name_list == NULL)
+ name_list = strdup(name);
+ else {
+ len = strlen(name_list);
+ tmp = realloc(name_list, len + strlen(name) + 2);
+ if (tmp == NULL) {
+ free(name_list);
+ name_list = NULL;
+ } else {
+ name_list = tmp;
+ name_list[len++] = ' ';
+ (void) strcpy(name_list+len, name);
+ }
+ }
+ if (name_list == NULL) { /* alloc error */
+ (void) mutex_unlock(&db_root_hosts.lock);
+ __nsw_freeconfig(conf);
+ return (-1);
+ }
+ }
+ __nsw_freeconfig(conf);
+ if (!got_skip) {
+ /*
+ * Since skip name service not used for hosts, we do not need
+ * to install our private address resolution function
+ */
+ (void) mutex_unlock(&db_root_hosts.lock);
+ if (name_list != NULL)
+ free(name_list);
+ return (0);
+ }
+ if (host_service != NULL)
+ free(host_service);
+ host_service = name_list;
+ (void) mutex_unlock(&db_root_hosts.lock);
+
+ if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
+ return (-1);
+ dns_fns.lddnsfn_gethostbyaddr = ns_gethostbyaddr;
+ if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
+ return (-1);
+ return (0);
+}
+#endif /* _SOLARIS_SDK */
+#endif /* NET_SSL */