dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libldap5 / sources / ldap / ssldap / ldapsinit.c
blob8ba27c2d48f1d4e0774bb1e4e7388406209278d0
1 /*
2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * The contents of this file are subject to the Netscape Public
8 * License Version 1.1 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.mozilla.org/NPL/
12 * Software distributed under the License is distributed on an "AS
13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is Netscape
21 * Communications Corporation. Portions created by Netscape are
22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23 * Rights Reserved.
25 * Contributor(s):
29 * ldapsinit.c
32 #if defined(NET_SSL)
34 #if defined( _WINDOWS )
35 #include <windows.h>
36 #endif
38 /* XXX:mhein The following is a workaround for the redefinition of */
39 /* const problem on OSF. Fix to be provided by NSS */
40 /* This is a pretty benign workaround for us which */
41 /* should not cause problems in the future even if */
42 /* we forget to take it out :-) */
44 #ifdef OSF1V4D
45 #ifndef __STDC__
46 # define __STDC__
47 #endif /* __STDC__ */
48 #endif /* OSF1V4D */
50 #include <errno.h>
51 #include <nspr.h>
52 #include <cert.h>
53 #include <key.h>
54 #include <ssl.h>
55 #include <sslproto.h>
56 #include <sslerr.h>
57 #include <prnetdb.h>
59 #include <ldap.h>
61 #include <ldappr.h>
62 #include <pk11func.h>
64 #ifdef _SOLARIS_SDK
65 #include "solaris-int.h"
66 #include <libintl.h>
67 #include <syslog.h>
68 #include <nsswitch.h>
69 #include <synch.h>
70 #include <nss_dbdefs.h>
71 #include <netinet/in.h>
73 #define HOST_BUF_SIZE 2048
75 #ifndef INADDR_NONE
76 #define INADDR_NONE (-1)
77 #endif
79 extern int
80 str2hostent(const char *instr, int lenstr, void *ent, char *buffer,
81 int buflen);
83 extern int
84 str2hostent6(const char *instr, int lenstr, void *ent, char *buffer,
85 int buflen);
87 extern LDAPHostEnt *
88 _ns_gethostbyaddr(LDAP *ld, const char *addr, int length, int type,
89 LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
90 void *extradata);
92 static char *host_service = NULL;
94 static DEFINE_NSS_DB_ROOT(db_root_hosts);
95 static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
96 #endif
99 * Data structure to hold the standard NSPR I/O function pointers set by
100 * libprldap. We save them in our session data structure so we can call
101 * them from our own I/O functions (we add functionality to support SSL
102 * while using libprldap's functions as much as possible).
104 typedef struct ldapssl_std_functions {
105 LDAP_X_EXTIOF_CLOSE_CALLBACK *lssf_close_fn;
106 LDAP_X_EXTIOF_CONNECT_CALLBACK *lssf_connect_fn;
107 LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *lssf_disposehdl_fn;
108 } LDAPSSLStdFunctions;
113 * LDAP session data structure.
115 typedef struct ldapssl_session_info {
116 int lssei_using_pcks_fns;
117 int lssei_ssl_strength;
118 char *lssei_certnickname;
119 char *lssei_keypasswd;
120 LDAPSSLStdFunctions lssei_std_functions;
121 CERTCertDBHandle *lssei_certdbh;
122 #ifdef _SOLARIS_SDK
124 * This is a hack.
125 * ld is used so that we can use libldap's gethostbyaddr
126 * resolver. This is needed to prevent recursion with libsldap.
128 LDAP *ld;
129 #endif /* _SOLARIS_SDK */
130 } LDAPSSLSessionInfo;
134 * LDAP socket data structure.
136 typedef struct ldapssl_socket_info {
137 LDAPSSLSessionInfo *soi_sessioninfo; /* session info */
138 } LDAPSSLSocketInfo;
142 * XXXceb This is a hack until the new IO functions are done.
143 * this function MUST be called before ldap_enable_clienauth.
144 * right now, this function is called in ldapssl_pkcs_init();
147 static int using_pkcs_functions = 0;
149 void set_using_pkcs_functions( int val )
151 using_pkcs_functions = val;
156 * Utility functions:
158 static void ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp );
159 static void ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp );
163 * SSL Stuff
166 static int ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd,
167 PRBool checkSig, PRBool isServer);
170 * client auth stuff
172 static int get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
173 CERTDistNames *caNames, CERTCertificate **pRetCert,
174 SECKEYPrivateKey **pRetKey );
175 static int get_keyandcert( LDAPSSLSessionInfo *ssip,
176 CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
177 char **errmsgp );
178 static int check_clientauth_nicknames_and_passwd( LDAP *ld,
179 LDAPSSLSessionInfo *ssip );
180 static char *get_keypassword( PK11SlotInfo *slot, PRBool retry,
181 void *sessionarg );
184 * Static variables.
186 #ifdef _SOLARIS_SDK
187 static int default_ssl_strength = LDAPSSL_AUTH_CNCHECK;
188 #else
189 static int default_ssl_strength = LDAPSSL_AUTH_CERT;
190 #endif
193 * Like ldap_init(), except also install I/O routines from libsec so we
194 * can support SSL. If defsecure is non-zero, SSL is enabled for the
195 * default connection as well.
197 LDAP *
198 LDAP_CALL
199 ldapssl_init( const char *defhost, int defport, int defsecure )
201 LDAP *ld;
204 #ifndef LDAP_SSLIO_HOOKS
205 return( NULL );
206 #else
207 if (0 ==defport)
208 defport = LDAPS_PORT;
210 if (( ld = ldap_init( defhost, defport )) == NULL ) {
211 return( NULL );
214 if ( ldapssl_install_routines( ld ) < 0 || ldap_set_option( ld,
215 LDAP_OPT_SSL, defsecure ? LDAP_OPT_ON : LDAP_OPT_OFF ) != 0 ) {
216 PR_SetError( PR_UNKNOWN_ERROR, EINVAL ); /* XXXmcs: just a guess! */
217 ldap_unbind( ld );
218 return( NULL );
221 return( ld );
222 #endif
226 static int
227 ldapssl_close(int s, struct lextiof_socket_private *socketarg)
229 PRLDAPSocketInfo soi;
230 LDAPSSLSocketInfo *ssoip;
231 LDAPSSLSessionInfo *sseip;
233 memset( &soi, 0, sizeof(soi));
234 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
235 if ( prldap_get_socket_info( s, socketarg, &soi ) != LDAP_SUCCESS ) {
236 return( -1 );
239 ssoip = (LDAPSSLSocketInfo *)soi.soinfo_appdata;
240 sseip = ssoip->soi_sessioninfo;
242 ldapssl_free_socket_info( (LDAPSSLSocketInfo **)&soi.soinfo_appdata );
244 return( (*(sseip->lssei_std_functions.lssf_close_fn))( s, socketarg ));
247 static int
248 do_ldapssl_connect(const char *hostlist, int defport, int timeout,
249 unsigned long options, struct lextiof_session_private *sessionarg,
250 struct lextiof_socket_private **socketargp, int clientauth )
252 int intfd = -1;
253 PRBool secure;
254 PRLDAPSessionInfo sei;
255 PRLDAPSocketInfo soi;
256 LDAPSSLSocketInfo *ssoip = NULL;
257 LDAPSSLSessionInfo *sseip;
258 PRFileDesc *sslfd = NULL;
259 #ifdef _SOLARIS_SDK
260 int port;
261 int parse_err;
262 char *host = NULL;
263 char *name;
264 struct ldap_x_hostlist_status
265 *status = NULL;
266 in_addr_t addr_ipv4;
267 in6_addr_t addr_ipv6;
268 char *host_buf;
269 LDAPHostEnt *hent;
270 LDAPHostEnt host_ent;
271 int stat;
272 int type;
273 #endif /* _SOLARIS_SDK */
276 * Determine if secure option is set. Also, clear secure bit in options
277 * the we pass to the standard connect() function (since it doesn't know
278 * how to handle the secure option).
280 if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) {
281 secure = PR_TRUE;
282 options &= ~LDAP_X_EXTIOF_OPT_SECURE;
283 } else {
284 secure = PR_FALSE;
288 * Retrieve session info. so we can store a pointer to our session info.
289 * in our socket info. later.
291 memset( &sei, 0, sizeof(sei));
292 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
293 if ( prldap_get_session_info( NULL, sessionarg, &sei ) != LDAP_SUCCESS ) {
294 return( -1 );
296 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
299 * Call the standard connect() callback to make the TCP connection.
300 * If it succeeds, *socketargp is set.
303 intfd = (*(sseip->lssei_std_functions.lssf_connect_fn))( hostlist, defport,
304 timeout, options, sessionarg, socketargp
305 #ifdef _SOLARIS_SDK
306 , &host );
307 #else
309 #endif /* _SOLARIS_SDK */
311 if ( intfd < 0 ) {
312 return( intfd );
315 #ifdef _SOLARIS_SDK
317 * Determine if the "host name" is an ip address. If so,
318 * we must look up the actual host name corresponding to
319 * it.
321 if ( NULL == host ) {
322 goto close_socket_and_exit_with_error;
324 type = AF_UNSPEC;
325 if (strlen(host) < INET6_ADDRSTRLEN &&
326 inet_pton(AF_INET6, host, &addr_ipv6) == 1) {
327 type = AF_INET6;
328 } else if (strlen(host) < INET_ADDRSTRLEN &&
329 inet_pton(AF_INET, host, &addr_ipv4) == 1) {
330 type = AF_INET;
332 if (type == AF_INET || type == AF_INET6) {
333 host_buf = malloc(HOST_BUF_SIZE);
334 if (host_buf == NULL) {
335 /* will free host in close_socket_and_exit_with_error */
336 goto close_socket_and_exit_with_error;
339 /* Call ldap layer's gethostbyaddr resolver */
340 hent = _ns_gethostbyaddr(sseip->ld, host, strlen(host), type,
341 &host_ent, host_buf, HOST_BUF_SIZE, &stat, NULL);
343 /* If we are unable to lookup the host addr, we fail! */
344 if (hent == NULL) {
345 syslog(LOG_WARNING,
346 "libldap: do_ldapssl_connect: "
347 "Unable to resolve '%s'", host);
348 free(host_buf);
349 /* will free host in close_socket_and_exit_with_error */
350 goto close_socket_and_exit_with_error;
352 /* We support only the primary host name */
353 else {
354 if (hent->ldaphe_name != NULL)
355 name = strdup(hent->ldaphe_name);
356 free(host_buf);
357 if (name == NULL)
358 goto close_socket_and_exit_with_error;
359 else
360 ldap_memfree(host); host = NULL;
361 host = name;
364 #endif /* _SOLARIS_SDK */
367 * Retrieve socket info. so we have the PRFileDesc.
369 memset( &soi, 0, sizeof(soi));
370 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
371 if ( prldap_get_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) {
372 goto close_socket_and_exit_with_error;
376 * Allocate a structure to hold our socket-specific data.
378 if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
379 goto close_socket_and_exit_with_error;
381 ssoip->soi_sessioninfo = sseip;
384 * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
386 if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
387 goto close_socket_and_exit_with_error;
390 if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess ||
391 SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure )
392 != SECSuccess || ( secure && SSL_ResetHandshake( sslfd,
393 PR_FALSE ) != SECSuccess )) {
394 goto close_socket_and_exit_with_error;
398 * Let the standard NSPR to LDAP layer know about the new socket and
399 * our own socket-specific data.
401 soi.soinfo_prfd = sslfd;
402 soi.soinfo_appdata = (void *)ssoip;
403 if ( prldap_set_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) {
404 goto close_socket_and_exit_with_error;
407 #ifdef _SOLARIS_SDK
409 * Set hostname which will be retrieved (depending on ssl strength) when
410 * using client or server auth.
412 if (SSL_SetURL(sslfd, host) != SECSuccess)
413 goto close_socket_and_exit_with_error;
414 ldap_memfree(host);
415 host = NULL;
416 #endif /* _SOLARIS_SDK */
418 sslfd = NULL; /* so we don't close the socket twice upon error */
421 * Install certificate hook function.
423 SSL_AuthCertificateHook( soi.soinfo_prfd,
424 (SSLAuthCertificate)ldapssl_AuthCertificate,
425 (void *)sseip);
427 if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
428 get_clientauth_data, clientauth ? sseip : NULL ) != 0 ) {
429 goto close_socket_and_exit_with_error;
432 return( intfd ); /* success */
434 close_socket_and_exit_with_error:
435 #ifdef _SOLARIS_SDK
436 if ( NULL != host ) ldap_memfree(host);
437 #endif /* _SOLARIS_SDK */
438 if ( NULL != sslfd ) {
439 PR_Close( sslfd );
441 if ( NULL != ssoip ) {
442 ldapssl_free_socket_info( &ssoip );
444 if ( intfd >= 0 && NULL != *socketargp ) {
445 (*(sseip->lssei_std_functions.lssf_close_fn))( intfd, *socketargp );
447 return( -1 );
451 static int
452 ldapssl_connect(const char *hostlist, int defport, int timeout,
453 unsigned long options, struct lextiof_session_private *sessionarg,
454 struct lextiof_socket_private **socketargp )
456 return( do_ldapssl_connect( hostlist, defport, timeout, options,
457 sessionarg, socketargp, 0 ));
461 static int
462 ldapssl_clientauth_connect(const char *hostlist, int defport, int timeout,
463 unsigned long options, struct lextiof_session_private *sessionarg,
464 struct lextiof_socket_private **socketargp )
466 return( do_ldapssl_connect( hostlist, defport, timeout, options,
467 sessionarg, socketargp, 1 ));
471 static void
472 ldapssl_disposehandle(LDAP *ld, struct lextiof_session_private *sessionarg)
474 PRLDAPSessionInfo sei;
475 LDAPSSLSessionInfo *sseip;
476 LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
478 memset( &sei, 0, sizeof( sei ));
479 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
480 if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS ) {
481 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
482 disposehdl_fn = sseip->lssei_std_functions.lssf_disposehdl_fn;
483 ldapssl_free_session_info( &sseip );
484 (*disposehdl_fn)( ld, sessionarg );
490 * Install I/O routines from libsec and NSPR into libldap to allow libldap
491 * to do SSL.
493 * We rely on libprldap to provide most of the functions, and then we override
494 * a few of them to support SSL.
497 LDAP_CALL
498 ldapssl_install_routines( LDAP *ld )
500 #ifndef LDAP_SSLIO_HOOKS
501 ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
502 return( -1 );
503 #else
504 struct ldap_x_ext_io_fns iofns;
505 LDAPSSLSessionInfo *ssip;
506 PRLDAPSessionInfo sei;
509 * This is done within ldap_init() and
510 * ldap_init() is called from ldapssl_init()
512 #ifndef _SOLARIS_SDK
513 if ( prldap_install_routines(
515 1 /* shared -- we have to assume it is */ )
516 != LDAP_SUCCESS ) {
517 return( -1 );
519 #endif /*_SOLARIS_SDK*/
522 * Allocate our own session information.
524 if ( NULL == ( ssip = (LDAPSSLSessionInfo *)PR_Calloc( 1,
525 sizeof( LDAPSSLSessionInfo )))) {
526 ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
527 return( -1 );
530 * Initialize session info.
531 * XXX: it would be nice to be able to set these on a per-session basis:
532 * lssei_using_pcks_fns
533 * lssei_certdbh
535 ssip->lssei_ssl_strength = default_ssl_strength;
536 ssip->lssei_using_pcks_fns = using_pkcs_functions;
537 ssip->lssei_certdbh = CERT_GetDefaultCertDB();
538 #ifdef _SOLARIS_SDK
540 * This is part of a hack to allow the ssl portion of the
541 * library to call the ldap library gethostbyaddr resolver.
543 ssip->ld = ld;
544 #endif /* _SOLARIS_SDK */
547 * override a few functions, saving a pointer to the standard function
548 * in each case so we can call it from our SSL savvy functions.
550 memset( &iofns, 0, sizeof(iofns));
551 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
552 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
553 ldapssl_free_session_info( &ssip );
554 return( -1 );
557 /* override socket, connect, and ioctl */
558 ssip->lssei_std_functions.lssf_connect_fn = iofns.lextiof_connect;
559 iofns.lextiof_connect = ldapssl_connect;
560 ssip->lssei_std_functions.lssf_close_fn = iofns.lextiof_close;
561 iofns.lextiof_close = ldapssl_close;
562 ssip->lssei_std_functions.lssf_disposehdl_fn = iofns.lextiof_disposehandle;
563 iofns.lextiof_disposehandle = ldapssl_disposehandle;
565 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
566 ldapssl_free_session_info( &ssip );
567 return( -1 );
571 * Store session info. for later retrieval.
573 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
574 sei.seinfo_appdata = (void *)ssip;
575 if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
576 return( -1 );
579 return( 0 );
580 #endif
585 * Set the SSL strength for an existing SSL-enabled LDAP session handle.
587 * See the description of ldapssl_serverauth_init() above for valid
588 * sslstrength values. If ld is NULL, the default for new LDAP session
589 * handles is set.
591 * Returns 0 if all goes well and -1 if an error occurs.
594 LDAP_CALL
595 ldapssl_set_strength( LDAP *ld, int sslstrength )
597 int rc = 0; /* assume success */
599 if ( sslstrength != LDAPSSL_AUTH_WEAK &&
600 sslstrength != LDAPSSL_AUTH_CERT &&
601 sslstrength != LDAPSSL_AUTH_CNCHECK ) {
602 rc = -1;
603 } else {
604 if ( NULL == ld ) { /* set default strength */
605 default_ssl_strength = sslstrength;
606 } else { /* set session-specific strength */
607 PRLDAPSessionInfo sei;
608 LDAPSSLSessionInfo *sseip;
610 memset( &sei, 0, sizeof( sei ));
611 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
612 if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS )
614 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
615 sseip->lssei_ssl_strength = sslstrength;
616 } else {
617 rc = -1;
622 return( rc );
626 LDAP_CALL
627 ldapssl_enable_clientauth( LDAP *ld, char *keynickname,
628 char *keypasswd, char *certnickname )
630 #ifndef LDAP_SSLIO_HOOKS
631 ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
632 return( -1 );
633 #else
634 struct ldap_x_ext_io_fns iofns;
635 LDAPSSLSessionInfo *ssip;
636 PRLDAPSessionInfo sei;
639 * Check parameters
641 if ( certnickname == NULL || keypasswd == NULL ) {
642 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
643 return( -1 );
647 * Update session info. data structure.
649 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
650 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
651 return( -1 );
653 ssip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
654 if ( NULL == ssip ) {
655 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
656 return( -1 );
658 ssip->lssei_certnickname = PL_strdup( certnickname );
659 ssip->lssei_keypasswd = PL_strdup( keypasswd );
661 if ( NULL == ssip->lssei_certnickname || NULL == ssip->lssei_keypasswd ) {
662 ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
663 return( -1 );
666 if ( check_clientauth_nicknames_and_passwd( ld, ssip ) != 0 ) {
667 return( -1 );
671 * replace standard SSL CONNECT function with client auth aware one
673 memset( &iofns, 0, sizeof(iofns));
674 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
675 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
676 != 0 ) {
677 return( -1 );
680 if ( iofns.lextiof_connect != ldapssl_connect ) {
681 /* standard SSL setup has not done */
682 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
683 return( -1 );
686 iofns.lextiof_connect = ldapssl_clientauth_connect;
688 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
689 != 0 ) {
690 return( -1 );
693 return( 0 );
694 #endif
698 static void
699 ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp )
701 if ( NULL != ssipp && NULL != *ssipp ) {
702 if ( NULL != (*ssipp)->lssei_certnickname ) {
703 PL_strfree( (*ssipp)->lssei_certnickname );
704 (*ssipp)->lssei_certnickname = NULL;
706 if ( NULL != (*ssipp)->lssei_keypasswd ) {
707 PL_strfree( (*ssipp)->lssei_keypasswd );
708 (*ssipp)->lssei_keypasswd = NULL;
710 PR_Free( *ssipp );
711 *ssipp = NULL;
716 static void
717 ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp )
719 if ( NULL != soipp && NULL != *soipp ) {
720 PR_Free( *soipp );
721 *soipp = NULL;
726 /* this function provides cert authentication. This is called during
727 * the SSL_Handshake process. Once the cert has been retrieved from
728 * the server, the it is checked, using VerifyCertNow(), then
729 * the cn is checked against the host name, set with SSL_SetURL()
732 static int
733 ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, PRBool checkSig,
734 PRBool isServer)
736 SECStatus rv = SECFailure;
737 LDAPSSLSessionInfo *sseip;
738 CERTCertificate *cert;
739 SECCertUsage certUsage;
740 char *hostname = NULL;
742 if (!sessionarg || !socket)
743 return rv;
745 sseip = (LDAPSSLSessionInfo *)sessionarg;
747 if (LDAPSSL_AUTH_WEAK == sseip->lssei_ssl_strength ) { /* no check */
748 return SECSuccess;
751 if ( isServer ) {
752 certUsage = certUsageSSLClient;
753 } else {
754 certUsage = certUsageSSLServer;
756 cert = SSL_PeerCertificate( fd );
758 rv = CERT_VerifyCertNow(sseip->lssei_certdbh, cert, checkSig,
759 certUsage, NULL);
761 if ( rv != SECSuccess || isServer )
762 return rv;
764 if ( LDAPSSL_AUTH_CNCHECK == sseip->lssei_ssl_strength )
766 /* cert is OK. This is the client side of an SSL connection.
767 * Now check the name field in the cert against the desired hostname.
768 * NB: This is our only defense against Man-In-The-Middle (MITM)
769 * attacks!
772 hostname = SSL_RevealURL( fd );
774 if (hostname && hostname[0]) {
775 rv = CERT_VerifyCertName(cert, hostname);
776 } else {
777 rv = SECFailure;
779 if (hostname)
780 PORT_Free(hostname);
781 if (rv != SECSuccess)
782 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
785 return((int)rv);
790 * called during SSL client auth. when server wants our cert and key.
791 * return 0 if we succeeded and set *pRetCert and *pRetKey, -1 otherwise.
792 * if -1 is returned SSL will proceed without sending a cert.
795 static int
796 get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
797 CERTDistNames *caNames, CERTCertificate **pRetCert,
798 SECKEYPrivateKey **pRetKey )
801 LDAPSSLSessionInfo *ssip;
803 if (( ssip = (LDAPSSLSessionInfo *)sessionarg ) == NULL ) {
804 return( -1 ); /* client auth. not enabled */
807 return( get_keyandcert( ssip, pRetCert, pRetKey, NULL ));
810 static int
811 get_keyandcert( LDAPSSLSessionInfo *ssip,
812 CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
813 char **errmsgp )
815 CERTCertificate *cert;
816 SECKEYPrivateKey *key;
818 if (( cert = PK11_FindCertFromNickname( ssip->lssei_certnickname, NULL ))
819 == NULL ) {
820 if ( errmsgp != NULL ) {
821 *errmsgp = dgettext(TEXT_DOMAIN, "unable to find certificate");
823 return( -1 );
827 PK11_SetPasswordFunc( get_keypassword );
832 if (( key = PK11_FindKeyByAnyCert( cert, (void *)ssip )) == NULL ) {
833 CERT_DestroyCertificate( cert );
834 if ( errmsgp != NULL ) {
835 *errmsgp = dgettext(TEXT_DOMAIN, "bad key or key password");
837 return( -1 );
840 *pRetCert = cert;
841 *pRetKey = key;
842 return( 0 );
847 * This function returns the password to NSS.
848 * This function is enable through PK11_SetPasswordFunc
849 * only if pkcs functions are not being used.
852 static char *
853 get_keypassword( PK11SlotInfo *slot, PRBool retry, void *sessionarg )
855 LDAPSSLSessionInfo *ssip;
857 if ( retry)
858 return (NULL);
860 ssip = (LDAPSSLSessionInfo *)sessionarg;
861 if ( NULL == ssip ) {
862 return( NULL );
865 return( ssip->lssei_keypasswd );
870 * performs some basic checks on clientauth cert and key/password
872 * XXXmcs: could perform additional checks... see servers/slapd/ssl.c
873 * 1) check expiration
874 * 2) check that public key in cert matches private key
875 * see ns/netsite/ldap/servers/slapd/ssl.c:slapd_ssl_init() for example code.
877 static int
878 check_clientauth_nicknames_and_passwd( LDAP *ld, LDAPSSLSessionInfo *ssip )
880 char *errmsg = NULL;
881 CERTCertificate *cert = NULL;
882 SECKEYPrivateKey *key = NULL;
883 int rv;
885 rv = get_keyandcert( ssip, &cert, &key, &errmsg );
887 if ( rv != 0 ) {
888 if ( errmsg != NULL ) {
889 errmsg = strdup( errmsg );
891 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg );
892 return( -1 );
895 if ( cert != NULL ) {
896 CERT_DestroyCertificate( cert );
898 if ( key != NULL ) {
899 SECKEY_DestroyPrivateKey( key );
901 return( 0 );
905 #if 0 /* NOT_NEEDED_IN_LIBLDAP */
906 /* there are patches and kludges. this is both. force some linkers to
907 * link this stuff in
909 int stubs_o_stuff( void )
911 PRExplodedTime exploded;
912 PLArenaPool pool;
914 const char *name ="t";
915 PRUint32 size = 0, align = 0;
917 PR_ImplodeTime( &exploded );
918 PL_InitArenaPool( &pool, name, size, align);
919 PR_Cleanup();
920 PR_fprintf((PRFileDesc*)stderr, "Bad IDEA!!");
922 return 0;
925 #endif /* NOT_NEEDED_IN_LIBLDAP */
929 * Import the file descriptor corresponding to the socket of an already
930 * open LDAP connection into SSL, and update the socket and session
931 * information accordingly.
933 int ldapssl_import_fd ( LDAP *ld, int secure )
935 PRLDAPSessionInfo sei;
936 PRLDAPSocketInfo soi;
937 LDAPSSLSocketInfo *ssoip = NULL;
938 LDAPSSLSessionInfo *sseip;
939 PRFileDesc *sslfd = NULL;
943 * Retrieve session info. so we can store a pointer to our session info.
944 * in our socket info. later.
946 memset( &sei, 0, sizeof(sei));
947 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
948 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
949 return( -1 );
951 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
955 * Retrieve socket info. so we have the PRFileDesc.
957 memset( &soi, 0, sizeof(soi));
958 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
959 if ( prldap_get_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
960 return( -1 );
964 * Allocate a structure to hold our socket-specific data.
966 if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
967 goto reset_socket_and_exit_with_error;
969 ssoip->soi_sessioninfo = sseip;
972 * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
974 if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
975 goto reset_socket_and_exit_with_error;
978 if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess ||
979 SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure )
980 != SECSuccess || ( secure && SSL_ResetHandshake( sslfd,
981 PR_FALSE ) != SECSuccess )) {
982 goto reset_socket_and_exit_with_error;
986 * Let the standard NSPR to LDAP layer know about the new socket and
987 * our own socket-specific data.
989 soi.soinfo_prfd = sslfd;
990 soi.soinfo_appdata = (void *)ssoip;
991 if ( prldap_set_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
992 goto reset_socket_and_exit_with_error;
996 * Install certificate hook function.
998 if ( SSL_AuthCertificateHook( soi.soinfo_prfd,
999 (SSLAuthCertificate)ldapssl_AuthCertificate,
1000 (void *)CERT_GetDefaultCertDB()) != 0 ) {
1001 goto reset_socket_and_exit_with_error;
1004 if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
1005 get_clientauth_data, sseip->lssei_certnickname ? sseip : NULL )
1006 != 0 ) {
1007 goto reset_socket_and_exit_with_error;
1010 return 0;
1012 reset_socket_and_exit_with_error:
1013 if ( NULL != sslfd ) {
1015 * "Unimport" the socket from SSL, i.e. get rid of the upper layer of
1016 * the file descriptor stack, which represents SSL.
1018 soi.soinfo_prfd = sslfd;
1019 sslfd = PR_PopIOLayer( soi.soinfo_prfd, PR_TOP_IO_LAYER );
1020 sslfd->dtor( sslfd );
1022 if ( NULL != ssoip ) {
1023 ldapssl_free_socket_info( &ssoip );
1024 soi.soinfo_appdata = NULL;
1026 prldap_set_default_socket_info( ld, &soi );
1028 return( -1 );
1033 * Reset an LDAP session from SSL to a non-secure status.
1034 * Basically, this function undoes the work done by ldapssl_install_routines.
1036 int ldapssl_reset_to_nonsecure ( LDAP *ld )
1038 PRLDAPSessionInfo sei;
1039 LDAPSSLSessionInfo *sseip;
1041 struct ldap_x_ext_io_fns iofns;
1042 int rc = 0;
1045 * Retrieve session info.
1047 memset( &sei, 0, sizeof(sei));
1048 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
1049 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
1050 return( -1 );
1052 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
1054 if ( sseip != NULL ) {
1056 * Reset the standard extended io functions.
1058 memset( &iofns, 0, sizeof(iofns));
1059 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
1060 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
1061 < 0) {
1062 rc = -1;
1063 goto free_session_info;
1066 /* reset socket, connect, and ioctl */
1067 iofns.lextiof_connect = sseip->lssei_std_functions.lssf_connect_fn;
1068 iofns.lextiof_close = sseip->lssei_std_functions.lssf_close_fn;
1069 iofns.lextiof_disposehandle =
1070 sseip->lssei_std_functions.lssf_disposehdl_fn;
1072 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
1073 < 0) {
1074 rc = -1;
1075 goto free_session_info;
1078 free_session_info:
1079 ldapssl_free_session_info( &sseip );
1080 sei.seinfo_appdata = NULL;
1081 if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
1082 rc = -1;
1084 } /* if ( sseip && *sseip ) */
1086 if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_OFF ) < 0 ) {
1087 return (-1);
1090 return rc;
1094 #ifdef _SOLARIS_SDK
1095 static void
1096 _nss_initf_ipnodes(nss_db_params_t *p)
1098 static char *no_service = "";
1100 p->name = NSS_DBNAM_IPNODES;
1101 p->flags |= NSS_USE_DEFAULT_CONFIG;
1102 p->default_config = host_service == NULL ? no_service : host_service;
1105 static void
1106 _nss_initf_hosts(nss_db_params_t *p)
1108 static char *no_service = "";
1110 p->name = NSS_DBNAM_HOSTS;
1111 p->flags |= NSS_USE_DEFAULT_CONFIG;
1112 p->default_config = host_service == NULL ? no_service : host_service;
1115 static struct hostent *
1116 _switch_gethostbyaddr_r(const char *addr, int len, int type,
1117 struct hostent *result, char *buffer, int buflen,
1118 int *h_errnop)
1120 nss_XbyY_args_t arg;
1121 nss_status_t res;
1122 int (*str2ent)();
1123 void (*nss_initf)();
1124 nss_db_root_t *nss_db_root;
1126 if (AF_INET == type) {
1127 str2ent = str2hostent;
1128 nss_initf = _nss_initf_hosts;
1129 nss_db_root = &db_root_hosts;
1130 } else if (AF_INET6 == type) {
1131 str2ent = str2hostent6;
1132 nss_initf = _nss_initf_ipnodes;
1133 nss_db_root = &db_root_ipnodes;
1134 } else {
1135 return NULL;
1138 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
1140 arg.key.hostaddr.addr = addr;
1141 arg.key.hostaddr.len = len;
1142 arg.key.hostaddr.type = type;
1143 arg.stayopen = 0;
1145 res = nss_search(nss_db_root, nss_initf,
1146 NSS_DBOP_HOSTS_BYADDR, &arg);
1147 arg.status = res;
1148 *h_errnop = arg.h_errno;
1149 return (struct hostent *)NSS_XbyY_FINI(&arg);
1153 * ns_gethostbyaddr is used to be a substitute gethostbyaddr for
1154 * libldap when ssl will need to determine the fully qualified
1155 * host name from an address when it is unsafe to use the normal
1156 * nameservice functions.
1158 * Note that the ldap name service resolver calls this with the address as
1159 * a character string - which we must convert into address form.
1162 /*ARGSUSED*/
1163 static LDAPHostEnt *
1164 ns_gethostbyaddr(const char *addr, int len, int type,
1165 LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
1166 void *extradata)
1168 LDAPHostEnt *ldap_hent;
1169 int h_errno;
1170 struct hostent h_ent;
1171 struct hostent *h_e = NULL;
1172 struct in_addr a;
1173 struct in6_addr a6;
1174 int inet_error; /* error returned by inet_pton */
1177 if (addr == NULL || result == NULL || buffer == NULL ||
1178 (type != AF_INET && type != AF_INET6))
1179 return (NULL);
1182 (void) memset(&h_ent, 0, sizeof (h_ent));
1184 if (AF_INET == type) {
1185 if (inet_pton(type, addr, &a.s_addr) == 1) {
1186 h_e = _switch_gethostbyaddr_r((char *)&a,
1187 sizeof (a.s_addr), type, &h_ent,
1188 buffer, buflen, &h_errno);
1190 } else if (AF_INET6 == type) {
1191 if (inet_pton(type, addr, &a6.s6_addr) == 1) {
1192 h_e = _switch_gethostbyaddr_r((char *)&a6,
1193 sizeof (a6.s6_addr), type, &h_ent,
1194 buffer, buflen, &h_errno);
1198 if (h_e == NULL) {
1199 ldap_hent = NULL;
1200 } else {
1201 (void) memset(result, 0, sizeof (LDAPHostEnt));
1202 ldap_hent = result;
1203 result->ldaphe_name = h_e->h_name;
1204 result->ldaphe_aliases = h_e->h_aliases;
1205 result->ldaphe_addrtype = h_e->h_addrtype;
1206 result->ldaphe_length = h_e->h_length;
1207 result->ldaphe_addr_list = h_e->h_addr_list;
1209 return (ldap_hent);
1213 * ldapssl_install_gethostbyaddr attempts to prevent recursion in
1214 * gethostbyaddr calls when an ip address is given to ssl. This ip address
1215 * must be resolved to a host name.
1217 * For example, libsldap cannot use LDAP to resolve this address to a
1218 * name because of recursion. The caller is instructing libldap to skip
1219 * the specified name service when resolving addresses for the specified
1220 * ldap connection.
1222 * Currently only ldap and dns name services always return fully qualified
1223 * names. The other name services (files, nis, and nisplus) will returned
1224 * fully qualified names if the host names are stored as fully qualified names
1225 * in these name services.
1227 * Note:
1229 * Since host_service applies to all connections, calling
1230 * ldapssl_install_gethostbyaddr with different name services to
1231 * skip will lead to unpredictable results.
1233 * Returns:
1234 * 0 if success
1235 * -1 if failure
1239 ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip)
1241 enum __nsw_parse_err pserr;
1242 struct __nsw_switchconfig *conf;
1243 struct __nsw_lookup *lkp;
1244 struct ldap_dns_fns dns_fns;
1245 char *name_list = NULL;
1246 char *tmp;
1247 const char *name;
1248 int len;
1249 boolean_t got_skip = B_FALSE;
1252 * db_root_hosts.lock mutex is used to ensure that the name list
1253 * is not in use by the name service switch while we are updating
1254 * the host_service
1257 (void) mutex_lock(&db_root_hosts.lock);
1258 conf = __nsw_getconfig("hosts", &pserr);
1259 if (conf == NULL) {
1260 (void) mutex_unlock(&db_root_hosts.lock);
1261 return (0);
1264 /* check for ldap and count other backends */
1265 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
1266 name = lkp->service_name;
1267 if (strcmp(name, skip) == 0) {
1268 got_skip = B_TRUE;
1269 continue;
1271 if (name_list == NULL)
1272 name_list = strdup(name);
1273 else {
1274 len = strlen(name_list);
1275 tmp = realloc(name_list, len + strlen(name) + 2);
1276 if (tmp == NULL) {
1277 free(name_list);
1278 name_list = NULL;
1279 } else {
1280 name_list = tmp;
1281 name_list[len++] = ' ';
1282 (void) strcpy(name_list+len, name);
1285 if (name_list == NULL) { /* alloc error */
1286 (void) mutex_unlock(&db_root_hosts.lock);
1287 __nsw_freeconfig(conf);
1288 return (-1);
1291 __nsw_freeconfig(conf);
1292 if (!got_skip) {
1294 * Since skip name service not used for hosts, we do not need
1295 * to install our private address resolution function
1297 (void) mutex_unlock(&db_root_hosts.lock);
1298 free(name_list);
1299 return (0);
1301 free(host_service);
1302 host_service = name_list;
1303 (void) mutex_unlock(&db_root_hosts.lock);
1305 if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
1306 return (-1);
1307 dns_fns.lddnsfn_gethostbyaddr = ns_gethostbyaddr;
1308 if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
1309 return (-1);
1310 return (0);
1312 #endif /* _SOLARIS_SDK */
1313 #endif /* NET_SSL */