8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libldap5 / sources / ldap / ssldap / ldapsinit.c
blob795b727ff209dac54c944d73520b0c42ee32a64a
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):
28 #pragma ident "%Z%%M% %I% %E% SMI"
31 * ldapsinit.c
34 #if defined(NET_SSL)
36 #if defined( _WINDOWS )
37 #include <windows.h>
38 #endif
40 /* XXX:mhein The following is a workaround for the redefinition of */
41 /* const problem on OSF. Fix to be provided by NSS */
42 /* This is a pretty benign workaround for us which */
43 /* should not cause problems in the future even if */
44 /* we forget to take it out :-) */
46 #ifdef OSF1V4D
47 #ifndef __STDC__
48 # define __STDC__
49 #endif /* __STDC__ */
50 #endif /* OSF1V4D */
52 #include <errno.h>
53 #include <nspr.h>
54 #include <cert.h>
55 #include <key.h>
56 #include <ssl.h>
57 #include <sslproto.h>
58 #include <sslerr.h>
59 #include <prnetdb.h>
61 #include <ldap.h>
63 #include <ldappr.h>
64 #include <pk11func.h>
66 #ifdef _SOLARIS_SDK
67 #include "solaris-int.h"
68 #include <libintl.h>
69 #include <syslog.h>
70 #include <nsswitch.h>
71 #include <synch.h>
72 #include <nss_dbdefs.h>
73 #include <netinet/in.h>
75 #define HOST_BUF_SIZE 2048
77 #ifndef INADDR_NONE
78 #define INADDR_NONE (-1)
79 #endif
81 extern int
82 str2hostent(const char *instr, int lenstr, void *ent, char *buffer,
83 int buflen);
85 extern int
86 str2hostent6(const char *instr, int lenstr, void *ent, char *buffer,
87 int buflen);
89 extern LDAPHostEnt *
90 _ns_gethostbyaddr(LDAP *ld, const char *addr, int length, int type,
91 LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
92 void *extradata);
94 static char *host_service = NULL;
96 static DEFINE_NSS_DB_ROOT(db_root_hosts);
97 static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
98 #endif
101 * Data structure to hold the standard NSPR I/O function pointers set by
102 * libprldap. We save them in our session data structure so we can call
103 * them from our own I/O functions (we add functionality to support SSL
104 * while using libprldap's functions as much as possible).
106 typedef struct ldapssl_std_functions {
107 LDAP_X_EXTIOF_CLOSE_CALLBACK *lssf_close_fn;
108 LDAP_X_EXTIOF_CONNECT_CALLBACK *lssf_connect_fn;
109 LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *lssf_disposehdl_fn;
110 } LDAPSSLStdFunctions;
115 * LDAP session data structure.
117 typedef struct ldapssl_session_info {
118 int lssei_using_pcks_fns;
119 int lssei_ssl_strength;
120 char *lssei_certnickname;
121 char *lssei_keypasswd;
122 LDAPSSLStdFunctions lssei_std_functions;
123 CERTCertDBHandle *lssei_certdbh;
124 #ifdef _SOLARIS_SDK
126 * This is a hack.
127 * ld is used so that we can use libldap's gethostbyaddr
128 * resolver. This is needed to prevent recursion with libsldap.
130 LDAP *ld;
131 #endif /* _SOLARIS_SDK */
132 } LDAPSSLSessionInfo;
136 * LDAP socket data structure.
138 typedef struct ldapssl_socket_info {
139 LDAPSSLSessionInfo *soi_sessioninfo; /* session info */
140 } LDAPSSLSocketInfo;
144 * XXXceb This is a hack until the new IO functions are done.
145 * this function MUST be called before ldap_enable_clienauth.
146 * right now, this function is called in ldapssl_pkcs_init();
149 static int using_pkcs_functions = 0;
151 void set_using_pkcs_functions( int val )
153 using_pkcs_functions = val;
158 * Utility functions:
160 static void ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp );
161 static void ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp );
165 * SSL Stuff
168 static int ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd,
169 PRBool checkSig, PRBool isServer);
172 * client auth stuff
174 static int get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
175 CERTDistNames *caNames, CERTCertificate **pRetCert,
176 SECKEYPrivateKey **pRetKey );
177 static int get_keyandcert( LDAPSSLSessionInfo *ssip,
178 CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
179 char **errmsgp );
180 static int check_clientauth_nicknames_and_passwd( LDAP *ld,
181 LDAPSSLSessionInfo *ssip );
182 static char *get_keypassword( PK11SlotInfo *slot, PRBool retry,
183 void *sessionarg );
186 * Static variables.
188 #ifdef _SOLARIS_SDK
189 static int default_ssl_strength = LDAPSSL_AUTH_CNCHECK;
190 #else
191 static int default_ssl_strength = LDAPSSL_AUTH_CERT;
192 #endif
195 * Like ldap_init(), except also install I/O routines from libsec so we
196 * can support SSL. If defsecure is non-zero, SSL is enabled for the
197 * default connection as well.
199 LDAP *
200 LDAP_CALL
201 ldapssl_init( const char *defhost, int defport, int defsecure )
203 LDAP *ld;
206 #ifndef LDAP_SSLIO_HOOKS
207 return( NULL );
208 #else
209 if (0 ==defport)
210 defport = LDAPS_PORT;
212 if (( ld = ldap_init( defhost, defport )) == NULL ) {
213 return( NULL );
216 if ( ldapssl_install_routines( ld ) < 0 || ldap_set_option( ld,
217 LDAP_OPT_SSL, defsecure ? LDAP_OPT_ON : LDAP_OPT_OFF ) != 0 ) {
218 PR_SetError( PR_UNKNOWN_ERROR, EINVAL ); /* XXXmcs: just a guess! */
219 ldap_unbind( ld );
220 return( NULL );
223 return( ld );
224 #endif
228 static int
229 ldapssl_close(int s, struct lextiof_socket_private *socketarg)
231 PRLDAPSocketInfo soi;
232 LDAPSSLSocketInfo *ssoip;
233 LDAPSSLSessionInfo *sseip;
235 memset( &soi, 0, sizeof(soi));
236 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
237 if ( prldap_get_socket_info( s, socketarg, &soi ) != LDAP_SUCCESS ) {
238 return( -1 );
241 ssoip = (LDAPSSLSocketInfo *)soi.soinfo_appdata;
242 sseip = ssoip->soi_sessioninfo;
244 ldapssl_free_socket_info( (LDAPSSLSocketInfo **)&soi.soinfo_appdata );
246 return( (*(sseip->lssei_std_functions.lssf_close_fn))( s, socketarg ));
249 static int
250 do_ldapssl_connect(const char *hostlist, int defport, int timeout,
251 unsigned long options, struct lextiof_session_private *sessionarg,
252 struct lextiof_socket_private **socketargp, int clientauth )
254 int intfd = -1;
255 PRBool secure;
256 PRLDAPSessionInfo sei;
257 PRLDAPSocketInfo soi;
258 LDAPSSLSocketInfo *ssoip = NULL;
259 LDAPSSLSessionInfo *sseip;
260 PRFileDesc *sslfd = NULL;
261 #ifdef _SOLARIS_SDK
262 int port;
263 int parse_err;
264 char *host = NULL;
265 char *name;
266 struct ldap_x_hostlist_status
267 *status = NULL;
268 in_addr_t addr_ipv4;
269 in6_addr_t addr_ipv6;
270 char *host_buf;
271 LDAPHostEnt *hent;
272 LDAPHostEnt host_ent;
273 int stat;
274 int type;
275 #endif /* _SOLARIS_SDK */
278 * Determine if secure option is set. Also, clear secure bit in options
279 * the we pass to the standard connect() function (since it doesn't know
280 * how to handle the secure option).
282 if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) {
283 secure = PR_TRUE;
284 options &= ~LDAP_X_EXTIOF_OPT_SECURE;
285 } else {
286 secure = PR_FALSE;
290 * Retrieve session info. so we can store a pointer to our session info.
291 * in our socket info. later.
293 memset( &sei, 0, sizeof(sei));
294 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
295 if ( prldap_get_session_info( NULL, sessionarg, &sei ) != LDAP_SUCCESS ) {
296 return( -1 );
298 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
301 * Call the standard connect() callback to make the TCP connection.
302 * If it succeeds, *socketargp is set.
305 intfd = (*(sseip->lssei_std_functions.lssf_connect_fn))( hostlist, defport,
306 timeout, options, sessionarg, socketargp
307 #ifdef _SOLARIS_SDK
308 , &host );
309 #else
311 #endif /* _SOLARIS_SDK */
313 if ( intfd < 0 ) {
314 return( intfd );
317 #ifdef _SOLARIS_SDK
319 * Determine if the "host name" is an ip address. If so,
320 * we must look up the actual host name corresponding to
321 * it.
323 if ( NULL == host ) {
324 goto close_socket_and_exit_with_error;
326 type = AF_UNSPEC;
327 if (strlen(host) < INET6_ADDRSTRLEN &&
328 inet_pton(AF_INET6, host, &addr_ipv6) == 1) {
329 type = AF_INET6;
330 } else if (strlen(host) < INET_ADDRSTRLEN &&
331 inet_pton(AF_INET, host, &addr_ipv4) == 1) {
332 type = AF_INET;
334 if (type == AF_INET || type == AF_INET6) {
335 host_buf = malloc(HOST_BUF_SIZE);
336 if (host_buf == NULL) {
337 /* will free host in close_socket_and_exit_with_error */
338 goto close_socket_and_exit_with_error;
341 /* Call ldap layer's gethostbyaddr resolver */
342 hent = _ns_gethostbyaddr(sseip->ld, host, strlen(host), type,
343 &host_ent, host_buf, HOST_BUF_SIZE, &stat, NULL);
345 /* If we are unable to lookup the host addr, we fail! */
346 if (hent == NULL) {
347 syslog(LOG_WARNING,
348 "libldap: do_ldapssl_connect: "
349 "Unable to resolve '%s'", host);
350 free(host_buf);
351 /* will free host in close_socket_and_exit_with_error */
352 goto close_socket_and_exit_with_error;
354 /* We support only the primary host name */
355 else {
356 if (hent->ldaphe_name != NULL)
357 name = strdup(hent->ldaphe_name);
358 free(host_buf);
359 if (name == NULL)
360 goto close_socket_and_exit_with_error;
361 else
362 ldap_memfree(host); host = NULL;
363 host = name;
366 #endif /* _SOLARIS_SDK */
369 * Retrieve socket info. so we have the PRFileDesc.
371 memset( &soi, 0, sizeof(soi));
372 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
373 if ( prldap_get_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) {
374 goto close_socket_and_exit_with_error;
378 * Allocate a structure to hold our socket-specific data.
380 if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
381 goto close_socket_and_exit_with_error;
383 ssoip->soi_sessioninfo = sseip;
386 * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
388 if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
389 goto close_socket_and_exit_with_error;
392 if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess ||
393 SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure )
394 != SECSuccess || ( secure && SSL_ResetHandshake( sslfd,
395 PR_FALSE ) != SECSuccess )) {
396 goto close_socket_and_exit_with_error;
400 * Let the standard NSPR to LDAP layer know about the new socket and
401 * our own socket-specific data.
403 soi.soinfo_prfd = sslfd;
404 soi.soinfo_appdata = (void *)ssoip;
405 if ( prldap_set_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) {
406 goto close_socket_and_exit_with_error;
409 #ifdef _SOLARIS_SDK
411 * Set hostname which will be retrieved (depending on ssl strength) when
412 * using client or server auth.
414 if (SSL_SetURL(sslfd, host) != SECSuccess)
415 goto close_socket_and_exit_with_error;
416 ldap_memfree(host);
417 host = NULL;
418 #endif /* _SOLARIS_SDK */
420 sslfd = NULL; /* so we don't close the socket twice upon error */
423 * Install certificate hook function.
425 SSL_AuthCertificateHook( soi.soinfo_prfd,
426 (SSLAuthCertificate)ldapssl_AuthCertificate,
427 (void *)sseip);
429 if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
430 get_clientauth_data, clientauth ? sseip : NULL ) != 0 ) {
431 goto close_socket_and_exit_with_error;
434 return( intfd ); /* success */
436 close_socket_and_exit_with_error:
437 #ifdef _SOLARIS_SDK
438 if ( NULL != host ) ldap_memfree(host);
439 #endif /* _SOLARIS_SDK */
440 if ( NULL != sslfd ) {
441 PR_Close( sslfd );
443 if ( NULL != ssoip ) {
444 ldapssl_free_socket_info( &ssoip );
446 if ( intfd >= 0 && NULL != *socketargp ) {
447 (*(sseip->lssei_std_functions.lssf_close_fn))( intfd, *socketargp );
449 return( -1 );
453 static int
454 ldapssl_connect(const char *hostlist, int defport, int timeout,
455 unsigned long options, struct lextiof_session_private *sessionarg,
456 struct lextiof_socket_private **socketargp )
458 return( do_ldapssl_connect( hostlist, defport, timeout, options,
459 sessionarg, socketargp, 0 ));
463 static int
464 ldapssl_clientauth_connect(const char *hostlist, int defport, int timeout,
465 unsigned long options, struct lextiof_session_private *sessionarg,
466 struct lextiof_socket_private **socketargp )
468 return( do_ldapssl_connect( hostlist, defport, timeout, options,
469 sessionarg, socketargp, 1 ));
473 static void
474 ldapssl_disposehandle(LDAP *ld, struct lextiof_session_private *sessionarg)
476 PRLDAPSessionInfo sei;
477 LDAPSSLSessionInfo *sseip;
478 LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
480 memset( &sei, 0, sizeof( sei ));
481 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
482 if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS ) {
483 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
484 disposehdl_fn = sseip->lssei_std_functions.lssf_disposehdl_fn;
485 ldapssl_free_session_info( &sseip );
486 (*disposehdl_fn)( ld, sessionarg );
492 * Install I/O routines from libsec and NSPR into libldap to allow libldap
493 * to do SSL.
495 * We rely on libprldap to provide most of the functions, and then we override
496 * a few of them to support SSL.
499 LDAP_CALL
500 ldapssl_install_routines( LDAP *ld )
502 #ifndef LDAP_SSLIO_HOOKS
503 ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
504 return( -1 );
505 #else
506 struct ldap_x_ext_io_fns iofns;
507 LDAPSSLSessionInfo *ssip;
508 PRLDAPSessionInfo sei;
511 * This is done within ldap_init() and
512 * ldap_init() is called from ldapssl_init()
514 #ifndef _SOLARIS_SDK
515 if ( prldap_install_routines(
517 1 /* shared -- we have to assume it is */ )
518 != LDAP_SUCCESS ) {
519 return( -1 );
521 #endif /*_SOLARIS_SDK*/
524 * Allocate our own session information.
526 if ( NULL == ( ssip = (LDAPSSLSessionInfo *)PR_Calloc( 1,
527 sizeof( LDAPSSLSessionInfo )))) {
528 ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
529 return( -1 );
532 * Initialize session info.
533 * XXX: it would be nice to be able to set these on a per-session basis:
534 * lssei_using_pcks_fns
535 * lssei_certdbh
537 ssip->lssei_ssl_strength = default_ssl_strength;
538 ssip->lssei_using_pcks_fns = using_pkcs_functions;
539 ssip->lssei_certdbh = CERT_GetDefaultCertDB();
540 #ifdef _SOLARIS_SDK
542 * This is part of a hack to allow the ssl portion of the
543 * library to call the ldap library gethostbyaddr resolver.
545 ssip->ld = ld;
546 #endif /* _SOLARIS_SDK */
549 * override a few functions, saving a pointer to the standard function
550 * in each case so we can call it from our SSL savvy functions.
552 memset( &iofns, 0, sizeof(iofns));
553 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
554 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
555 ldapssl_free_session_info( &ssip );
556 return( -1 );
559 /* override socket, connect, and ioctl */
560 ssip->lssei_std_functions.lssf_connect_fn = iofns.lextiof_connect;
561 iofns.lextiof_connect = ldapssl_connect;
562 ssip->lssei_std_functions.lssf_close_fn = iofns.lextiof_close;
563 iofns.lextiof_close = ldapssl_close;
564 ssip->lssei_std_functions.lssf_disposehdl_fn = iofns.lextiof_disposehandle;
565 iofns.lextiof_disposehandle = ldapssl_disposehandle;
567 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
568 ldapssl_free_session_info( &ssip );
569 return( -1 );
573 * Store session info. for later retrieval.
575 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
576 sei.seinfo_appdata = (void *)ssip;
577 if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
578 return( -1 );
581 return( 0 );
582 #endif
587 * Set the SSL strength for an existing SSL-enabled LDAP session handle.
589 * See the description of ldapssl_serverauth_init() above for valid
590 * sslstrength values. If ld is NULL, the default for new LDAP session
591 * handles is set.
593 * Returns 0 if all goes well and -1 if an error occurs.
596 LDAP_CALL
597 ldapssl_set_strength( LDAP *ld, int sslstrength )
599 int rc = 0; /* assume success */
601 if ( sslstrength != LDAPSSL_AUTH_WEAK &&
602 sslstrength != LDAPSSL_AUTH_CERT &&
603 sslstrength != LDAPSSL_AUTH_CNCHECK ) {
604 rc = -1;
605 } else {
606 if ( NULL == ld ) { /* set default strength */
607 default_ssl_strength = sslstrength;
608 } else { /* set session-specific strength */
609 PRLDAPSessionInfo sei;
610 LDAPSSLSessionInfo *sseip;
612 memset( &sei, 0, sizeof( sei ));
613 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
614 if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS )
616 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
617 sseip->lssei_ssl_strength = sslstrength;
618 } else {
619 rc = -1;
624 return( rc );
628 LDAP_CALL
629 ldapssl_enable_clientauth( LDAP *ld, char *keynickname,
630 char *keypasswd, char *certnickname )
632 #ifndef LDAP_SSLIO_HOOKS
633 ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
634 return( -1 );
635 #else
636 struct ldap_x_ext_io_fns iofns;
637 LDAPSSLSessionInfo *ssip;
638 PRLDAPSessionInfo sei;
641 * Check parameters
643 if ( certnickname == NULL || keypasswd == NULL ) {
644 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
645 return( -1 );
649 * Update session info. data structure.
651 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
652 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
653 return( -1 );
655 ssip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
656 if ( NULL == ssip ) {
657 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
658 return( -1 );
660 ssip->lssei_certnickname = PL_strdup( certnickname );
661 ssip->lssei_keypasswd = PL_strdup( keypasswd );
663 if ( NULL == ssip->lssei_certnickname || NULL == ssip->lssei_keypasswd ) {
664 ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
665 return( -1 );
668 if ( check_clientauth_nicknames_and_passwd( ld, ssip ) != 0 ) {
669 return( -1 );
673 * replace standard SSL CONNECT function with client auth aware one
675 memset( &iofns, 0, sizeof(iofns));
676 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
677 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
678 != 0 ) {
679 return( -1 );
682 if ( iofns.lextiof_connect != ldapssl_connect ) {
683 /* standard SSL setup has not done */
684 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
685 return( -1 );
688 iofns.lextiof_connect = ldapssl_clientauth_connect;
690 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
691 != 0 ) {
692 return( -1 );
695 return( 0 );
696 #endif
700 static void
701 ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp )
703 if ( NULL != ssipp && NULL != *ssipp ) {
704 if ( NULL != (*ssipp)->lssei_certnickname ) {
705 PL_strfree( (*ssipp)->lssei_certnickname );
706 (*ssipp)->lssei_certnickname = NULL;
708 if ( NULL != (*ssipp)->lssei_keypasswd ) {
709 PL_strfree( (*ssipp)->lssei_keypasswd );
710 (*ssipp)->lssei_keypasswd = NULL;
712 PR_Free( *ssipp );
713 *ssipp = NULL;
718 static void
719 ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp )
721 if ( NULL != soipp && NULL != *soipp ) {
722 PR_Free( *soipp );
723 *soipp = NULL;
728 /* this function provides cert authentication. This is called during
729 * the SSL_Handshake process. Once the cert has been retrieved from
730 * the server, the it is checked, using VerifyCertNow(), then
731 * the cn is checked against the host name, set with SSL_SetURL()
734 static int
735 ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, PRBool checkSig,
736 PRBool isServer)
738 SECStatus rv = SECFailure;
739 LDAPSSLSessionInfo *sseip;
740 CERTCertificate *cert;
741 SECCertUsage certUsage;
742 char *hostname = (char *)0;
744 if (!sessionarg || !socket)
745 return rv;
747 sseip = (LDAPSSLSessionInfo *)sessionarg;
749 if (LDAPSSL_AUTH_WEAK == sseip->lssei_ssl_strength ) { /* no check */
750 return SECSuccess;
753 if ( isServer ) {
754 certUsage = certUsageSSLClient;
755 } else {
756 certUsage = certUsageSSLServer;
758 cert = SSL_PeerCertificate( fd );
760 rv = CERT_VerifyCertNow(sseip->lssei_certdbh, cert, checkSig,
761 certUsage, NULL);
763 if ( rv != SECSuccess || isServer )
764 return rv;
766 if ( LDAPSSL_AUTH_CNCHECK == sseip->lssei_ssl_strength )
768 /* cert is OK. This is the client side of an SSL connection.
769 * Now check the name field in the cert against the desired hostname.
770 * NB: This is our only defense against Man-In-The-Middle (MITM)
771 * attacks!
774 hostname = SSL_RevealURL( fd );
776 if (hostname && hostname[0]) {
777 rv = CERT_VerifyCertName(cert, hostname);
778 } else {
779 rv = SECFailure;
781 if (hostname)
782 PORT_Free(hostname);
783 if (rv != SECSuccess)
784 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
787 return((int)rv);
792 * called during SSL client auth. when server wants our cert and key.
793 * return 0 if we succeeded and set *pRetCert and *pRetKey, -1 otherwise.
794 * if -1 is returned SSL will proceed without sending a cert.
797 static int
798 get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
799 CERTDistNames *caNames, CERTCertificate **pRetCert,
800 SECKEYPrivateKey **pRetKey )
803 LDAPSSLSessionInfo *ssip;
805 if (( ssip = (LDAPSSLSessionInfo *)sessionarg ) == NULL ) {
806 return( -1 ); /* client auth. not enabled */
809 return( get_keyandcert( ssip, pRetCert, pRetKey, NULL ));
812 static int
813 get_keyandcert( LDAPSSLSessionInfo *ssip,
814 CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
815 char **errmsgp )
817 CERTCertificate *cert;
818 SECKEYPrivateKey *key;
820 if (( cert = PK11_FindCertFromNickname( ssip->lssei_certnickname, NULL ))
821 == NULL ) {
822 if ( errmsgp != NULL ) {
823 *errmsgp = dgettext(TEXT_DOMAIN, "unable to find certificate");
825 return( -1 );
829 PK11_SetPasswordFunc( get_keypassword );
834 if (( key = PK11_FindKeyByAnyCert( cert, (void *)ssip )) == NULL ) {
835 CERT_DestroyCertificate( cert );
836 if ( errmsgp != NULL ) {
837 *errmsgp = dgettext(TEXT_DOMAIN, "bad key or key password");
839 return( -1 );
842 *pRetCert = cert;
843 *pRetKey = key;
844 return( 0 );
849 * This function returns the password to NSS.
850 * This function is enable through PK11_SetPasswordFunc
851 * only if pkcs functions are not being used.
854 static char *
855 get_keypassword( PK11SlotInfo *slot, PRBool retry, void *sessionarg )
857 LDAPSSLSessionInfo *ssip;
859 if ( retry)
860 return (NULL);
862 ssip = (LDAPSSLSessionInfo *)sessionarg;
863 if ( NULL == ssip ) {
864 return( NULL );
867 return( ssip->lssei_keypasswd );
872 * performs some basic checks on clientauth cert and key/password
874 * XXXmcs: could perform additional checks... see servers/slapd/ssl.c
875 * 1) check expiration
876 * 2) check that public key in cert matches private key
877 * see ns/netsite/ldap/servers/slapd/ssl.c:slapd_ssl_init() for example code.
879 static int
880 check_clientauth_nicknames_and_passwd( LDAP *ld, LDAPSSLSessionInfo *ssip )
882 char *errmsg = NULL;
883 CERTCertificate *cert = NULL;
884 SECKEYPrivateKey *key = NULL;
885 int rv;
887 rv = get_keyandcert( ssip, &cert, &key, &errmsg );
889 if ( rv != 0 ) {
890 if ( errmsg != NULL ) {
891 errmsg = strdup( errmsg );
893 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg );
894 return( -1 );
897 if ( cert != NULL ) {
898 CERT_DestroyCertificate( cert );
900 if ( key != NULL ) {
901 SECKEY_DestroyPrivateKey( key );
903 return( 0 );
907 #if 0 /* NOT_NEEDED_IN_LIBLDAP */
908 /* there are patches and kludges. this is both. force some linkers to
909 * link this stuff in
911 int stubs_o_stuff( void )
913 PRExplodedTime exploded;
914 PLArenaPool pool;
916 const char *name ="t";
917 PRUint32 size = 0, align = 0;
919 PR_ImplodeTime( &exploded );
920 PL_InitArenaPool( &pool, name, size, align);
921 PR_Cleanup();
922 PR_fprintf((PRFileDesc*)stderr, "Bad IDEA!!");
924 return 0;
927 #endif /* NOT_NEEDED_IN_LIBLDAP */
931 * Import the file descriptor corresponding to the socket of an already
932 * open LDAP connection into SSL, and update the socket and session
933 * information accordingly.
935 int ldapssl_import_fd ( LDAP *ld, int secure )
937 PRLDAPSessionInfo sei;
938 PRLDAPSocketInfo soi;
939 LDAPSSLSocketInfo *ssoip = NULL;
940 LDAPSSLSessionInfo *sseip;
941 PRFileDesc *sslfd = NULL;
945 * Retrieve session info. so we can store a pointer to our session info.
946 * in our socket info. later.
948 memset( &sei, 0, sizeof(sei));
949 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
950 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
951 return( -1 );
953 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
957 * Retrieve socket info. so we have the PRFileDesc.
959 memset( &soi, 0, sizeof(soi));
960 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
961 if ( prldap_get_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
962 return( -1 );
966 * Allocate a structure to hold our socket-specific data.
968 if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
969 goto reset_socket_and_exit_with_error;
971 ssoip->soi_sessioninfo = sseip;
974 * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
976 if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
977 goto reset_socket_and_exit_with_error;
980 if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess ||
981 SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure )
982 != SECSuccess || ( secure && SSL_ResetHandshake( sslfd,
983 PR_FALSE ) != SECSuccess )) {
984 goto reset_socket_and_exit_with_error;
988 * Let the standard NSPR to LDAP layer know about the new socket and
989 * our own socket-specific data.
991 soi.soinfo_prfd = sslfd;
992 soi.soinfo_appdata = (void *)ssoip;
993 if ( prldap_set_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
994 goto reset_socket_and_exit_with_error;
998 * Install certificate hook function.
1000 if ( SSL_AuthCertificateHook( soi.soinfo_prfd,
1001 (SSLAuthCertificate)ldapssl_AuthCertificate,
1002 (void *)CERT_GetDefaultCertDB()) != 0 ) {
1003 goto reset_socket_and_exit_with_error;
1006 if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
1007 get_clientauth_data, sseip->lssei_certnickname ? sseip : NULL )
1008 != 0 ) {
1009 goto reset_socket_and_exit_with_error;
1012 return 0;
1014 reset_socket_and_exit_with_error:
1015 if ( NULL != sslfd ) {
1017 * "Unimport" the socket from SSL, i.e. get rid of the upper layer of
1018 * the file descriptor stack, which represents SSL.
1020 soi.soinfo_prfd = sslfd;
1021 sslfd = PR_PopIOLayer( soi.soinfo_prfd, PR_TOP_IO_LAYER );
1022 sslfd->dtor( sslfd );
1024 if ( NULL != ssoip ) {
1025 ldapssl_free_socket_info( &ssoip );
1026 soi.soinfo_appdata = NULL;
1028 prldap_set_default_socket_info( ld, &soi );
1030 return( -1 );
1035 * Reset an LDAP session from SSL to a non-secure status.
1036 * Basically, this function undoes the work done by ldapssl_install_routines.
1038 int ldapssl_reset_to_nonsecure ( LDAP *ld )
1040 PRLDAPSessionInfo sei;
1041 LDAPSSLSessionInfo *sseip;
1043 struct ldap_x_ext_io_fns iofns;
1044 int rc = 0;
1047 * Retrieve session info.
1049 memset( &sei, 0, sizeof(sei));
1050 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
1051 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
1052 return( -1 );
1054 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
1056 if ( sseip != NULL ) {
1058 * Reset the standard extended io functions.
1060 memset( &iofns, 0, sizeof(iofns));
1061 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
1062 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
1063 < 0) {
1064 rc = -1;
1065 goto free_session_info;
1068 /* reset socket, connect, and ioctl */
1069 iofns.lextiof_connect = sseip->lssei_std_functions.lssf_connect_fn;
1070 iofns.lextiof_close = sseip->lssei_std_functions.lssf_close_fn;
1071 iofns.lextiof_disposehandle =
1072 sseip->lssei_std_functions.lssf_disposehdl_fn;
1074 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
1075 < 0) {
1076 rc = -1;
1077 goto free_session_info;
1080 free_session_info:
1081 ldapssl_free_session_info( &sseip );
1082 sei.seinfo_appdata = NULL;
1083 if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
1084 rc = -1;
1086 } /* if ( sseip && *sseip ) */
1088 if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_OFF ) < 0 ) {
1089 return (-1);
1092 return rc;
1096 #ifdef _SOLARIS_SDK
1097 static void
1098 _nss_initf_ipnodes(nss_db_params_t *p)
1100 static char *no_service = "";
1102 p->name = NSS_DBNAM_IPNODES;
1103 p->flags |= NSS_USE_DEFAULT_CONFIG;
1104 p->default_config = host_service == NULL ? no_service : host_service;
1107 static void
1108 _nss_initf_hosts(nss_db_params_t *p)
1110 static char *no_service = "";
1112 p->name = NSS_DBNAM_HOSTS;
1113 p->flags |= NSS_USE_DEFAULT_CONFIG;
1114 p->default_config = host_service == NULL ? no_service : host_service;
1117 static struct hostent *
1118 _switch_gethostbyaddr_r(const char *addr, int len, int type,
1119 struct hostent *result, char *buffer, int buflen,
1120 int *h_errnop)
1122 nss_XbyY_args_t arg;
1123 nss_status_t res;
1124 int (*str2ent)();
1125 void (*nss_initf)();
1126 nss_db_root_t *nss_db_root;
1128 if (AF_INET == type) {
1129 str2ent = str2hostent;
1130 nss_initf = _nss_initf_hosts;
1131 nss_db_root = &db_root_hosts;
1132 } else if (AF_INET6 == type) {
1133 str2ent = str2hostent6;
1134 nss_initf = _nss_initf_ipnodes;
1135 nss_db_root = &db_root_ipnodes;
1136 } else {
1137 return NULL;
1140 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
1142 arg.key.hostaddr.addr = addr;
1143 arg.key.hostaddr.len = len;
1144 arg.key.hostaddr.type = type;
1145 arg.stayopen = 0;
1147 res = nss_search(nss_db_root, nss_initf,
1148 NSS_DBOP_HOSTS_BYADDR, &arg);
1149 arg.status = res;
1150 *h_errnop = arg.h_errno;
1151 return (struct hostent *)NSS_XbyY_FINI(&arg);
1155 * ns_gethostbyaddr is used to be a substitute gethostbyaddr for
1156 * libldap when ssl will need to determine the fully qualified
1157 * host name from an address when it is unsafe to use the normal
1158 * nameservice functions.
1160 * Note that the ldap name service resolver calls this with the address as
1161 * a character string - which we must convert into address form.
1164 /*ARGSUSED*/
1165 static LDAPHostEnt *
1166 ns_gethostbyaddr(const char *addr, int len, int type,
1167 LDAPHostEnt *result, char *buffer, int buflen, int *statusp,
1168 void *extradata)
1170 LDAPHostEnt *ldap_hent;
1171 int h_errno;
1172 struct hostent h_ent;
1173 struct hostent *h_e = NULL;
1174 struct in_addr a;
1175 struct in6_addr a6;
1176 int inet_error; /* error returned by inet_pton */
1179 if (addr == NULL || result == NULL || buffer == NULL ||
1180 (type != AF_INET && type != AF_INET6))
1181 return (NULL);
1184 (void) memset(&h_ent, 0, sizeof (h_ent));
1186 if (AF_INET == type) {
1187 if (inet_pton(type, addr, &a.s_addr) == 1) {
1188 h_e = _switch_gethostbyaddr_r((char *)&a,
1189 sizeof (a.s_addr), type, &h_ent,
1190 buffer, buflen, &h_errno);
1192 } else if (AF_INET6 == type) {
1193 if (inet_pton(type, addr, &a6.s6_addr) == 1) {
1194 h_e = _switch_gethostbyaddr_r((char *)&a6,
1195 sizeof (a6.s6_addr), type, &h_ent,
1196 buffer, buflen, &h_errno);
1200 if (h_e == NULL) {
1201 ldap_hent = NULL;
1202 } else {
1203 (void) memset(result, 0, sizeof (LDAPHostEnt));
1204 ldap_hent = result;
1205 result->ldaphe_name = h_e->h_name;
1206 result->ldaphe_aliases = h_e->h_aliases;
1207 result->ldaphe_addrtype = h_e->h_addrtype;
1208 result->ldaphe_length = h_e->h_length;
1209 result->ldaphe_addr_list = h_e->h_addr_list;
1211 return (ldap_hent);
1215 * ldapssl_install_gethostbyaddr attempts to prevent recursion in
1216 * gethostbyaddr calls when an ip address is given to ssl. This ip address
1217 * must be resolved to a host name.
1219 * For example, libsldap cannot use LDAP to resolve this address to a
1220 * name because of recursion. The caller is instructing libldap to skip
1221 * the specified name service when resolving addresses for the specified
1222 * ldap connection.
1224 * Currently only ldap and dns name services always return fully qualified
1225 * names. The other name services (files, nis, and nisplus) will returned
1226 * fully qualified names if the host names are stored as fully qualified names
1227 * in these name services.
1229 * Note:
1231 * Since host_service applies to all connections, calling
1232 * ldapssl_install_gethostbyaddr with different name services to
1233 * skip will lead to unpredictable results.
1235 * Returns:
1236 * 0 if success
1237 * -1 if failure
1241 ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip)
1243 enum __nsw_parse_err pserr;
1244 struct __nsw_switchconfig *conf;
1245 struct __nsw_lookup *lkp;
1246 struct ldap_dns_fns dns_fns;
1247 char *name_list = NULL;
1248 char *tmp;
1249 const char *name;
1250 int len;
1251 boolean_t got_skip = B_FALSE;
1254 * db_root_hosts.lock mutex is used to ensure that the name list
1255 * is not in use by the name service switch while we are updating
1256 * the host_service
1259 (void) mutex_lock(&db_root_hosts.lock);
1260 conf = __nsw_getconfig("hosts", &pserr);
1261 if (conf == NULL) {
1262 (void) mutex_unlock(&db_root_hosts.lock);
1263 return (0);
1266 /* check for ldap and count other backends */
1267 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
1268 name = lkp->service_name;
1269 if (strcmp(name, skip) == 0) {
1270 got_skip = B_TRUE;
1271 continue;
1273 if (name_list == NULL)
1274 name_list = strdup(name);
1275 else {
1276 len = strlen(name_list);
1277 tmp = realloc(name_list, len + strlen(name) + 2);
1278 if (tmp == NULL) {
1279 free(name_list);
1280 name_list = NULL;
1281 } else {
1282 name_list = tmp;
1283 name_list[len++] = ' ';
1284 (void) strcpy(name_list+len, name);
1287 if (name_list == NULL) { /* alloc error */
1288 (void) mutex_unlock(&db_root_hosts.lock);
1289 __nsw_freeconfig(conf);
1290 return (-1);
1293 __nsw_freeconfig(conf);
1294 if (!got_skip) {
1296 * Since skip name service not used for hosts, we do not need
1297 * to install our private address resolution function
1299 (void) mutex_unlock(&db_root_hosts.lock);
1300 if (name_list != NULL)
1301 free(name_list);
1302 return (0);
1304 if (host_service != NULL)
1305 free(host_service);
1306 host_service = name_list;
1307 (void) mutex_unlock(&db_root_hosts.lock);
1309 if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
1310 return (-1);
1311 dns_fns.lddnsfn_gethostbyaddr = ns_gethostbyaddr;
1312 if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
1313 return (-1);
1314 return (0);
1316 #endif /* _SOLARIS_SDK */
1317 #endif /* NET_SSL */