xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c (revision 201ceb75ab95f9bf1f42ea1dc9ab363b43ba47cf)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
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/
11  *
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.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
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.
24  *
25  * Contributor(s):
26  */
27 
28 /*
29  * ldapsinit.c
30  */
31 
32 #if defined(NET_SSL)
33 
34 #if defined( _WINDOWS )
35 #include <windows.h>
36 #endif
37 
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 :-) */
43 
44 #ifdef OSF1V4D
45 #ifndef __STDC__
46 #  define __STDC__
47 #endif /* __STDC__ */
48 #endif /* OSF1V4D */
49 
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>
58 
59 #include <ldap.h>
60 
61 #include <ldappr.h>
62 #include <pk11func.h>
63 
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>
72 
73 #define	HOST_BUF_SIZE	2048
74 
75 #ifndef INADDR_NONE
76 #define	INADDR_NONE (-1)
77 #endif
78 
79 extern int
80 str2hostent(const char *instr, int lenstr, void *ent, char *buffer,
81 		int buflen);
82 
83 extern int
84 str2hostent6(const char *instr, int lenstr, void *ent, char *buffer,
85 		int buflen);
86 
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);
91 
92 static char *host_service = NULL;
93 
94 static DEFINE_NSS_DB_ROOT(db_root_hosts);
95 static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
96 #endif
97 
98 /*
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).
103  */
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;
109 
110 
111 
112 /*
113  * LDAP session data structure.
114  */
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
123 	/*
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.
127 	 */
128     LDAP		*ld;
129 #endif	/* _SOLARIS_SDK */
130 } LDAPSSLSessionInfo;
131 
132 
133 /*
134  * LDAP socket data structure.
135  */
136 typedef struct ldapssl_socket_info {
137     LDAPSSLSessionInfo	*soi_sessioninfo;	/* session info */
138 } LDAPSSLSocketInfo;
139 
140 
141 /*
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();
145  */
146 
147 static int using_pkcs_functions = 0;
148 
149 void set_using_pkcs_functions( int val )
150 {
151     using_pkcs_functions = val;
152 }
153 
154 
155 /*
156  * Utility functions:
157  */
158 static void ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp );
159 static void ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp );
160 
161 
162 /*
163  *  SSL Stuff
164  */
165 
166 static int ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd,
167         PRBool checkSig, PRBool isServer);
168 
169 /*
170  * client auth stuff
171  */
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 );
182 
183 /*
184  * Static variables.
185  */
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
191 
192 /*
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.
196  */
197 LDAP *
198 LDAP_CALL
199 ldapssl_init( const char *defhost, int defport, int defsecure )
200 {
201     LDAP	*ld;
202 
203 
204 #ifndef LDAP_SSLIO_HOOKS
205     return( NULL );
206 #else
207     if (0 ==defport)
208 	defport = LDAPS_PORT;
209 
210     if (( ld = ldap_init( defhost, defport )) == NULL ) {
211 	return( NULL );
212     }
213 
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 );
219     }
220 
221     return( ld );
222 #endif
223 }
224 
225 
226 static int
227 ldapssl_close(int s, struct lextiof_socket_private *socketarg)
228 {
229     PRLDAPSocketInfo	soi;
230     LDAPSSLSocketInfo	*ssoip;
231     LDAPSSLSessionInfo	*sseip;
232 
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 );
237     }
238 
239     ssoip = (LDAPSSLSocketInfo *)soi.soinfo_appdata;
240     sseip = ssoip->soi_sessioninfo;
241 
242     ldapssl_free_socket_info( (LDAPSSLSocketInfo **)&soi.soinfo_appdata );
243 
244     return( (*(sseip->lssei_std_functions.lssf_close_fn))( s, socketarg ));
245 }
246 
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 )
251 {
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 */
274 
275     /*
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).
279      */
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;
285     }
286 
287     /*
288      * Retrieve session info. so we can store a pointer to our session info.
289      * in our socket info. later.
290      */
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 );
295     }
296     sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
297 
298     /*
299      * Call the standard connect() callback to make the TCP connection.
300      * If it succeeds, *socketargp is set.
301      */
302 
303     intfd = (*(sseip->lssei_std_functions.lssf_connect_fn))( hostlist, defport,
304 		timeout, options, sessionarg, socketargp
305 #ifdef _SOLARIS_SDK
306 		, &host );
307 #else
308 		);
309 #endif	/* _SOLARIS_SDK */
310 
311     if ( intfd < 0 ) {
312 	return( intfd );
313     }
314 
315 #ifdef _SOLARIS_SDK
316         /*
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.
320          */
321 	if ( NULL == host ) {
322 		goto close_socket_and_exit_with_error;
323 	}
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;
331         }
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;
337 		}
338 
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);
342 
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;
351                 }
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;
362 		}
363         }
364 #endif	/* _SOLARIS_SDK */
365 
366     /*
367      * Retrieve socket info. so we have the PRFileDesc.
368      */
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;
373     }
374 
375     /*
376      * Allocate a structure to hold our socket-specific data.
377      */
378     if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
379 	goto close_socket_and_exit_with_error;
380     }
381     ssoip->soi_sessioninfo = sseip;
382 
383     /*
384      * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
385      */
386     if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
387 	goto close_socket_and_exit_with_error;
388     }
389 
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;
395     }
396 
397     /*
398      * Let the standard NSPR to LDAP layer know about the new socket and
399      * our own socket-specific data.
400      */
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;
405     }
406 
407 #ifdef _SOLARIS_SDK
408     /*
409      * Set hostname which will be retrieved (depending on ssl strength) when
410      * using client or server auth.
411      */
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 */
417 
418     sslfd = NULL;	/* so we don't close the socket twice upon error */
419 
420     /*
421      * Install certificate hook function.
422      */
423     SSL_AuthCertificateHook( soi.soinfo_prfd,
424 			     (SSLAuthCertificate)ldapssl_AuthCertificate,
425                              (void *)sseip);
426 
427     if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
428 		get_clientauth_data, clientauth ? sseip : NULL ) != 0 ) {
429 	goto close_socket_and_exit_with_error;
430     }
431 
432     return( intfd );	/* success */
433 
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 );
440     }
441     if ( NULL != ssoip ) {
442 	ldapssl_free_socket_info( &ssoip );
443     }
444     if ( intfd >= 0 && NULL != *socketargp ) {
445 	(*(sseip->lssei_std_functions.lssf_close_fn))( intfd, *socketargp );
446     }
447     return( -1 );
448 }
449 
450 
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 )
455 {
456     return( do_ldapssl_connect( hostlist, defport, timeout, options,
457 		sessionarg, socketargp, 0 ));
458 }
459 
460 
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 )
465 {
466     return( do_ldapssl_connect( hostlist, defport, timeout, options,
467 		sessionarg, socketargp, 1 ));
468 }
469 
470 
471 static void
472 ldapssl_disposehandle(LDAP *ld, struct lextiof_session_private *sessionarg)
473 {
474     PRLDAPSessionInfo				sei;
475     LDAPSSLSessionInfo				*sseip;
476     LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK	*disposehdl_fn;
477 
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 );
485     }
486 }
487 
488 
489 /*
490  * Install I/O routines from libsec and NSPR into libldap to allow libldap
491  * to do SSL.
492  *
493  * We rely on libprldap to provide most of the functions, and then we override
494  * a few of them to support SSL.
495  */
496 int
497 LDAP_CALL
498 ldapssl_install_routines( LDAP *ld )
499 {
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;
507 
508 /*
509  * This is done within ldap_init() and
510  * ldap_init() is called from ldapssl_init()
511  */
512 #ifndef _SOLARIS_SDK
513     if ( prldap_install_routines(
514 		ld,
515 		1 /* shared -- we have to assume it is */ )
516 		!= LDAP_SUCCESS ) {
517 	return( -1 );
518     }
519 #endif /*_SOLARIS_SDK*/
520 
521     /*
522      * Allocate our own session information.
523      */
524     if ( NULL == ( ssip = (LDAPSSLSessionInfo *)PR_Calloc( 1,
525 		sizeof( LDAPSSLSessionInfo )))) {
526 	ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
527 	return( -1 );
528     }
529     /*
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
534      */
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
539     /*
540      * This is part of a hack to allow the ssl portion of the
541      * library to call the ldap library gethostbyaddr resolver.
542      */
543     ssip->ld = ld;
544 #endif	/* _SOLARIS_SDK */
545 
546     /*
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.
549      */
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 );
555     }
556 
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;
564 
565     if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
566 	ldapssl_free_session_info( &ssip );
567 	return( -1 );
568     }
569 
570     /*
571      * Store session info. for later retrieval.
572      */
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 );
577     }
578 
579     return( 0 );
580 #endif
581 }
582 
583 
584 /*
585  * Set the SSL strength for an existing SSL-enabled LDAP session handle.
586  *
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.
590  *
591  * Returns 0 if all goes well and -1 if an error occurs.
592  */
593 int
594 LDAP_CALL
595 ldapssl_set_strength( LDAP *ld, int sslstrength )
596 {
597     int                 rc = 0; /* assume success */
598 
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;
609 
610             memset( &sei, 0, sizeof( sei ));
611             sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
612             if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS )
613 {
614                 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
615                 sseip->lssei_ssl_strength = sslstrength;
616             } else {
617                 rc = -1;
618             }
619         }
620     }
621 
622     return( rc );
623 }
624 
625 int
626 LDAP_CALL
627 ldapssl_enable_clientauth( LDAP *ld, char *keynickname,
628         char *keypasswd, char *certnickname )
629 {
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;
637 
638     /*
639      * Check parameters
640      */
641     if ( certnickname == NULL || keypasswd == NULL ) {
642 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
643 	return( -1 );
644     }
645 
646     /*
647      * Update session info. data structure.
648      */
649     sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
650     if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
651 	return( -1 );
652     }
653     ssip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
654     if ( NULL == ssip ) {
655 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
656 	return( -1 );
657     }
658     ssip->lssei_certnickname = PL_strdup( certnickname );
659     ssip->lssei_keypasswd = PL_strdup( keypasswd );
660 
661     if ( NULL == ssip->lssei_certnickname || NULL == ssip->lssei_keypasswd ) {
662 	ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
663 	return( -1 );
664     }
665 
666     if ( check_clientauth_nicknames_and_passwd( ld, ssip ) != 0 ) {
667 	return( -1 );
668     }
669 
670     /*
671      * replace standard SSL CONNECT function with client auth aware one
672      */
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 );
678     }
679 
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 );
684     }
685 
686     iofns.lextiof_connect = ldapssl_clientauth_connect;
687 
688     if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
689 		!= 0 ) {
690 	return( -1 );
691     }
692 
693     return( 0 );
694 #endif
695 }
696 
697 
698 static void
699 ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp )
700 {
701     if ( NULL != ssipp && NULL != *ssipp ) {
702 	if ( NULL != (*ssipp)->lssei_certnickname ) {
703 	    PL_strfree( (*ssipp)->lssei_certnickname );
704 	    (*ssipp)->lssei_certnickname = NULL;
705 	}
706 	if ( NULL != (*ssipp)->lssei_keypasswd ) {
707 	    PL_strfree( (*ssipp)->lssei_keypasswd );
708 	    (*ssipp)->lssei_keypasswd = NULL;
709 	}
710 	PR_Free( *ssipp );
711 	*ssipp = NULL;
712     }
713 }
714 
715 
716 static void
717 ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp )
718 {
719     if ( NULL != soipp && NULL != *soipp ) {
720 	PR_Free( *soipp );
721 	*soipp = NULL;
722     }
723 }
724 
725 
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()
730  */
731 
732 static int
733 ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, PRBool checkSig,
734 	PRBool isServer)
735 {
736     SECStatus		rv = SECFailure;
737     LDAPSSLSessionInfo	*sseip;
738     CERTCertificate	*cert;
739     SECCertUsage	certUsage;
740     char		*hostname = (char *)0;
741 
742     if (!sessionarg || !socket)
743 	return rv;
744 
745     sseip = (LDAPSSLSessionInfo *)sessionarg;
746 
747     if (LDAPSSL_AUTH_WEAK == sseip->lssei_ssl_strength ) { /* no check */
748         return SECSuccess;
749     }
750 
751     if ( isServer ) {
752 	certUsage = certUsageSSLClient;
753     } else {
754 	certUsage = certUsageSSLServer;
755     }
756     cert = SSL_PeerCertificate( fd );
757 
758     rv = CERT_VerifyCertNow(sseip->lssei_certdbh, cert, checkSig,
759 			certUsage, NULL);
760 
761     if ( rv != SECSuccess || isServer )
762 	return rv;
763 
764     if ( LDAPSSL_AUTH_CNCHECK == sseip->lssei_ssl_strength )
765       {
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!
770 	 */
771 
772 	hostname = SSL_RevealURL( fd );
773 
774 	if (hostname && hostname[0]) {
775 	  rv = CERT_VerifyCertName(cert, hostname);
776 	} else  {
777 	  rv = SECFailure;
778      	}
779 	if (hostname)
780 		PORT_Free(hostname);
781 	if (rv != SECSuccess)
782 	  PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
783       }
784 
785     return((int)rv);
786 }
787 
788 
789 /*
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.
793  */
794 
795 static int
796 get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
797         CERTDistNames *caNames,  CERTCertificate **pRetCert,
798         SECKEYPrivateKey **pRetKey )
799 
800 {
801     LDAPSSLSessionInfo	*ssip;
802 
803     if (( ssip = (LDAPSSLSessionInfo *)sessionarg ) == NULL ) {
804 	return( -1 );       /* client auth. not enabled */
805     }
806 
807     return( get_keyandcert( ssip, pRetCert, pRetKey, NULL ));
808 }
809 
810 static int
811 get_keyandcert( LDAPSSLSessionInfo *ssip,
812 	CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
813 	char **errmsgp )
814 {
815     CERTCertificate	*cert;
816     SECKEYPrivateKey	*key;
817 
818     if (( cert = PK11_FindCertFromNickname( ssip->lssei_certnickname, NULL ))
819 		== NULL ) {
820 	if ( errmsgp != NULL ) {
821 	    *errmsgp = dgettext(TEXT_DOMAIN, "unable to find certificate");
822 	}
823 	return( -1 );
824     }
825 
826     {
827 	PK11_SetPasswordFunc( get_keypassword );
828     }
829 
830 
831 
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");
836 	}
837 	return( -1 );
838     }
839 
840     *pRetCert = cert;
841     *pRetKey = key;
842     return( 0 );
843 }
844 
845 
846 /*
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.
850  */
851 
852 static char *
853 get_keypassword( PK11SlotInfo *slot, PRBool retry, void *sessionarg )
854 {
855     LDAPSSLSessionInfo	*ssip;
856 
857     if ( retry)
858       return (NULL);
859 
860     ssip = (LDAPSSLSessionInfo *)sessionarg;
861     if ( NULL == ssip ) {
862 	return( NULL );
863     }
864 
865     return( ssip->lssei_keypasswd );
866 }
867 
868 
869 /*
870  * performs some basic checks on clientauth cert and key/password
871  *
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.
876  */
877 static int
878 check_clientauth_nicknames_and_passwd( LDAP *ld, LDAPSSLSessionInfo *ssip )
879 {
880     char		*errmsg = NULL;
881     CERTCertificate	*cert = NULL;
882     SECKEYPrivateKey	*key = NULL;
883     int rv;
884 
885     rv = get_keyandcert( ssip, &cert, &key, &errmsg );
886 
887     if ( rv != 0 ) {
888     	if ( errmsg != NULL ) {
889 	    errmsg = strdup( errmsg );
890 	}
891 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg );
892 	return( -1 );
893     }
894 
895     if ( cert != NULL ) {
896 	CERT_DestroyCertificate( cert );
897     }
898     if ( key != NULL ) {
899 	SECKEY_DestroyPrivateKey( key );
900     }
901     return( 0 );
902 }
903 
904 
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
908  */
909 int stubs_o_stuff( void )
910 {
911     PRExplodedTime exploded;
912     PLArenaPool pool;
913 
914     const char *name ="t";
915     PRUint32 size = 0, align = 0;
916 
917     PR_ImplodeTime( &exploded );
918     PL_InitArenaPool( &pool, name, size, align);
919     PR_Cleanup();
920     PR_fprintf((PRFileDesc*)stderr, "Bad IDEA!!");
921 
922     return 0;
923 
924 }
925 #endif	/* NOT_NEEDED_IN_LIBLDAP */
926 
927 
928 /*
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.
932  */
933 int ldapssl_import_fd ( LDAP *ld, int secure )
934 {
935     PRLDAPSessionInfo   sei;
936     PRLDAPSocketInfo    soi;
937     LDAPSSLSocketInfo   *ssoip = NULL;
938     LDAPSSLSessionInfo  *sseip;
939     PRFileDesc          *sslfd = NULL;
940 
941 
942     /*
943      * Retrieve session info. so we can store a pointer to our session info.
944      * in our socket info. later.
945      */
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 );
950     }
951     sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
952 
953 
954     /*
955      * Retrieve socket info. so we have the PRFileDesc.
956      */
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 );
961     }
962 
963     /*
964      * Allocate a structure to hold our socket-specific data.
965      */
966     if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
967         goto reset_socket_and_exit_with_error;
968     }
969     ssoip->soi_sessioninfo = sseip;
970 
971     /*
972      * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
973      */
974     if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
975         goto reset_socket_and_exit_with_error;
976     }
977 
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;
983     }
984 
985     /*
986      * Let the standard NSPR to LDAP layer know about the new socket and
987      * our own socket-specific data.
988      */
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;
993     }
994 
995     /*
996      * Install certificate hook function.
997      */
998     if ( SSL_AuthCertificateHook( soi.soinfo_prfd,
999                                   (SSLAuthCertificate)ldapssl_AuthCertificate,
1000                                   (void *)CERT_GetDefaultCertDB()) != 0 ) {
1001         goto reset_socket_and_exit_with_error;
1002     }
1003 
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;
1008     }
1009 
1010     return 0;
1011 
1012  reset_socket_and_exit_with_error:
1013     if ( NULL != sslfd ) {
1014         /*
1015          * "Unimport" the socket from SSL, i.e. get rid of the upper layer of
1016          * the file descriptor stack, which represents SSL.
1017          */
1018         soi.soinfo_prfd = sslfd;
1019         sslfd = PR_PopIOLayer( soi.soinfo_prfd, PR_TOP_IO_LAYER );
1020         sslfd->dtor( sslfd );
1021     }
1022     if ( NULL != ssoip ) {
1023         ldapssl_free_socket_info( &ssoip );
1024         soi.soinfo_appdata = NULL;
1025     }
1026     prldap_set_default_socket_info( ld, &soi );
1027 
1028     return( -1 );
1029 }
1030 
1031 
1032 /*
1033  * Reset an LDAP session from SSL to a non-secure status.
1034  * Basically, this function undoes the work done by ldapssl_install_routines.
1035  */
1036 int ldapssl_reset_to_nonsecure ( LDAP *ld )
1037 {
1038     PRLDAPSessionInfo   sei;
1039     LDAPSSLSessionInfo  *sseip;
1040 
1041     struct ldap_x_ext_io_fns    iofns;
1042     int rc = 0;
1043 
1044     /*
1045      * Retrieve session info.
1046      */
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 );
1051     }
1052     sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
1053 
1054     if ( sseip != NULL ) {
1055         /*
1056          * Reset the standard extended io functions.
1057          */
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;
1064         }
1065 
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;
1071 
1072         if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
1073 		< 0) {
1074             rc = -1;
1075             goto free_session_info;
1076         }
1077 
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;
1083         }
1084     } /* if ( sseip && *sseip ) */
1085 
1086     if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_OFF ) < 0 ) {
1087         return (-1);
1088     }
1089 
1090     return rc;
1091 }
1092 
1093 
1094 #ifdef _SOLARIS_SDK
1095 static void
1096 _nss_initf_ipnodes(nss_db_params_t *p)
1097 {
1098 	static char *no_service = "";
1099 
1100 	p->name = NSS_DBNAM_IPNODES;
1101         p->flags |= NSS_USE_DEFAULT_CONFIG;
1102         p->default_config = host_service == NULL ? no_service : host_service;
1103 }
1104 
1105 static void
1106 _nss_initf_hosts(nss_db_params_t *p)
1107 {
1108 	static char *no_service = "";
1109 
1110 	p->name = NSS_DBNAM_HOSTS;
1111         p->flags |= NSS_USE_DEFAULT_CONFIG;
1112         p->default_config = host_service == NULL ? no_service : host_service;
1113 }
1114 
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)
1119 {
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;
1125 
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;
1136 	}
1137 
1138 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
1139 
1140         arg.key.hostaddr.addr	= addr;
1141         arg.key.hostaddr.len	= len;
1142         arg.key.hostaddr.type	= type;
1143         arg.stayopen		= 0;
1144 
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);
1150 }
1151 
1152 /*
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.
1157  *
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.
1160  */
1161 
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)
1167 {
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 */
1175 
1176 
1177 	if (addr == NULL || result == NULL || buffer == NULL ||
1178 			(type != AF_INET && type != AF_INET6))
1179 		return (NULL);
1180 
1181 
1182 	(void) memset(&h_ent, 0, sizeof (h_ent));
1183 
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);
1189 		}
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);
1195 		}
1196 	}
1197 
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;
1208 	}
1209 	return (ldap_hent);
1210 }
1211 
1212 /*
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.
1216  *
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.
1221  *
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.
1226  *
1227  * Note:
1228  *
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.
1232  *
1233  * Returns:
1234  *	0	if success
1235  *	-1	if failure
1236  */
1237 
1238 int
1239 ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip)
1240 {
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;
1250 
1251 	/*
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
1255 	 */
1256 
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);
1262 	}
1263 
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;
1270 		}
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);
1283 			}
1284 		}
1285 		if (name_list == NULL) {	/* alloc error */
1286 			(void) mutex_unlock(&db_root_hosts.lock);
1287 			__nsw_freeconfig(conf);
1288 			return (-1);
1289 		}
1290 	}
1291 	__nsw_freeconfig(conf);
1292 	if (!got_skip) {
1293 		/*
1294 		 * Since skip name service not used for hosts, we do not need
1295 		 * to install our private address resolution function
1296 		 */
1297 		(void) mutex_unlock(&db_root_hosts.lock);
1298 		if (name_list != NULL)
1299 			free(name_list);
1300 		return (0);
1301 	}
1302 	if (host_service != NULL)
1303 		free(host_service);
1304 	host_service = name_list;
1305 	(void) mutex_unlock(&db_root_hosts.lock);
1306 
1307 	if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
1308 		return (-1);
1309 	dns_fns.lddnsfn_gethostbyaddr = ns_gethostbyaddr;
1310 	if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0)
1311 		return (-1);
1312 	return (0);
1313 }
1314 #endif	/* _SOLARIS_SDK */
1315 #endif /* NET_SSL */
1316