xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/ssldap/ldapsinit.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * ldapsinit.c
32  */
33 
34 #if defined(NET_SSL)
35 
36 #if defined( _WINDOWS )
37 #include <windows.h>
38 #endif
39 
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 :-) */
45 
46 #ifdef OSF1V4D
47 #ifndef __STDC__
48 #  define __STDC__
49 #endif /* __STDC__ */
50 #endif /* OSF1V4D */
51 
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>
60 
61 #include <ldap.h>
62 
63 #include <ldappr.h>
64 #include <pk11func.h>
65 
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>
74 
75 #define	HOST_BUF_SIZE	2048
76 
77 #ifndef INADDR_NONE
78 #define	INADDR_NONE (-1)
79 #endif
80 
81 extern int
82 str2hostent(const char *instr, int lenstr, void *ent, char *buffer,
83 		int buflen);
84 
85 extern int
86 str2hostent6(const char *instr, int lenstr, void *ent, char *buffer,
87 		int buflen);
88 
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);
93 
94 static char *host_service = NULL;
95 
96 static DEFINE_NSS_DB_ROOT(db_root_hosts);
97 static DEFINE_NSS_DB_ROOT(db_root_ipnodes);
98 #endif
99 
100 /*
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).
105  */
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;
111 
112 
113 
114 /*
115  * LDAP session data structure.
116  */
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
125 	/*
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.
129 	 */
130     LDAP		*ld;
131 #endif	/* _SOLARIS_SDK */
132 } LDAPSSLSessionInfo;
133 
134 
135 /*
136  * LDAP socket data structure.
137  */
138 typedef struct ldapssl_socket_info {
139     LDAPSSLSessionInfo	*soi_sessioninfo;	/* session info */
140 } LDAPSSLSocketInfo;
141 
142 
143 /*
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();
147  */
148 
149 static int using_pkcs_functions = 0;
150 
151 void set_using_pkcs_functions( int val )
152 {
153     using_pkcs_functions = val;
154 }
155 
156 
157 /*
158  * Utility functions:
159  */
160 static void ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp );
161 static void ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp );
162 
163 
164 /*
165  *  SSL Stuff
166  */
167 
168 static int ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd,
169         PRBool checkSig, PRBool isServer);
170 
171 /*
172  * client auth stuff
173  */
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 );
184 
185 /*
186  * Static variables.
187  */
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
193 
194 /*
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.
198  */
199 LDAP *
200 LDAP_CALL
201 ldapssl_init( const char *defhost, int defport, int defsecure )
202 {
203     LDAP	*ld;
204 
205 
206 #ifndef LDAP_SSLIO_HOOKS
207     return( NULL );
208 #else
209     if (0 ==defport)
210 	defport = LDAPS_PORT;
211 
212     if (( ld = ldap_init( defhost, defport )) == NULL ) {
213 	return( NULL );
214     }
215 
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 );
221     }
222 
223     return( ld );
224 #endif
225 }
226 
227 
228 static int
229 ldapssl_close(int s, struct lextiof_socket_private *socketarg)
230 {
231     PRLDAPSocketInfo	soi;
232     LDAPSSLSocketInfo	*ssoip;
233     LDAPSSLSessionInfo	*sseip;
234 
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 );
239     }
240 
241     ssoip = (LDAPSSLSocketInfo *)soi.soinfo_appdata;
242     sseip = ssoip->soi_sessioninfo;
243 
244     ldapssl_free_socket_info( (LDAPSSLSocketInfo **)&soi.soinfo_appdata );
245 
246     return( (*(sseip->lssei_std_functions.lssf_close_fn))( s, socketarg ));
247 }
248 
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 )
253 {
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 */
276 
277     /*
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).
281      */
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;
287     }
288 
289     /*
290      * Retrieve session info. so we can store a pointer to our session info.
291      * in our socket info. later.
292      */
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 );
297     }
298     sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
299 
300     /*
301      * Call the standard connect() callback to make the TCP connection.
302      * If it succeeds, *socketargp is set.
303      */
304 
305     intfd = (*(sseip->lssei_std_functions.lssf_connect_fn))( hostlist, defport,
306 		timeout, options, sessionarg, socketargp
307 #ifdef _SOLARIS_SDK
308 		, &host );
309 #else
310 		);
311 #endif	/* _SOLARIS_SDK */
312 
313     if ( intfd < 0 ) {
314 	return( intfd );
315     }
316 
317 #ifdef _SOLARIS_SDK
318         /*
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.
322          */
323 	if ( NULL == host ) {
324 		goto close_socket_and_exit_with_error;
325 	}
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;
333         }
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;
339 		}
340 
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);
344 
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;
353                 }
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;
364 		}
365         }
366 #endif	/* _SOLARIS_SDK */
367 
368     /*
369      * Retrieve socket info. so we have the PRFileDesc.
370      */
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;
375     }
376 
377     /*
378      * Allocate a structure to hold our socket-specific data.
379      */
380     if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
381 	goto close_socket_and_exit_with_error;
382     }
383     ssoip->soi_sessioninfo = sseip;
384 
385     /*
386      * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
387      */
388     if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
389 	goto close_socket_and_exit_with_error;
390     }
391 
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;
397     }
398 
399     /*
400      * Let the standard NSPR to LDAP layer know about the new socket and
401      * our own socket-specific data.
402      */
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;
407     }
408 
409 #ifdef _SOLARIS_SDK
410     /*
411      * Set hostname which will be retrieved (depending on ssl strength) when
412      * using client or server auth.
413      */
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 */
419 
420     sslfd = NULL;	/* so we don't close the socket twice upon error */
421 
422     /*
423      * Install certificate hook function.
424      */
425     SSL_AuthCertificateHook( soi.soinfo_prfd,
426 			     (SSLAuthCertificate)ldapssl_AuthCertificate,
427                              (void *)sseip);
428 
429     if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd,
430 		get_clientauth_data, clientauth ? sseip : NULL ) != 0 ) {
431 	goto close_socket_and_exit_with_error;
432     }
433 
434     return( intfd );	/* success */
435 
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 );
442     }
443     if ( NULL != ssoip ) {
444 	ldapssl_free_socket_info( &ssoip );
445     }
446     if ( intfd >= 0 && NULL != *socketargp ) {
447 	(*(sseip->lssei_std_functions.lssf_close_fn))( intfd, *socketargp );
448     }
449     return( -1 );
450 }
451 
452 
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 )
457 {
458     return( do_ldapssl_connect( hostlist, defport, timeout, options,
459 		sessionarg, socketargp, 0 ));
460 }
461 
462 
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 )
467 {
468     return( do_ldapssl_connect( hostlist, defport, timeout, options,
469 		sessionarg, socketargp, 1 ));
470 }
471 
472 
473 static void
474 ldapssl_disposehandle(LDAP *ld, struct lextiof_session_private *sessionarg)
475 {
476     PRLDAPSessionInfo				sei;
477     LDAPSSLSessionInfo				*sseip;
478     LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK	*disposehdl_fn;
479 
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 );
487     }
488 }
489 
490 
491 /*
492  * Install I/O routines from libsec and NSPR into libldap to allow libldap
493  * to do SSL.
494  *
495  * We rely on libprldap to provide most of the functions, and then we override
496  * a few of them to support SSL.
497  */
498 int
499 LDAP_CALL
500 ldapssl_install_routines( LDAP *ld )
501 {
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;
509 
510 /*
511  * This is done within ldap_init() and
512  * ldap_init() is called from ldapssl_init()
513  */
514 #ifndef _SOLARIS_SDK
515     if ( prldap_install_routines(
516 		ld,
517 		1 /* shared -- we have to assume it is */ )
518 		!= LDAP_SUCCESS ) {
519 	return( -1 );
520     }
521 #endif /*_SOLARIS_SDK*/
522 
523     /*
524      * Allocate our own session information.
525      */
526     if ( NULL == ( ssip = (LDAPSSLSessionInfo *)PR_Calloc( 1,
527 		sizeof( LDAPSSLSessionInfo )))) {
528 	ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
529 	return( -1 );
530     }
531     /*
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
536      */
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
541     /*
542      * This is part of a hack to allow the ssl portion of the
543      * library to call the ldap library gethostbyaddr resolver.
544      */
545     ssip->ld = ld;
546 #endif	/* _SOLARIS_SDK */
547 
548     /*
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.
551      */
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 );
557     }
558 
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;
566 
567     if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
568 	ldapssl_free_session_info( &ssip );
569 	return( -1 );
570     }
571 
572     /*
573      * Store session info. for later retrieval.
574      */
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 );
579     }
580 
581     return( 0 );
582 #endif
583 }
584 
585 
586 /*
587  * Set the SSL strength for an existing SSL-enabled LDAP session handle.
588  *
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.
592  *
593  * Returns 0 if all goes well and -1 if an error occurs.
594  */
595 int
596 LDAP_CALL
597 ldapssl_set_strength( LDAP *ld, int sslstrength )
598 {
599     int                 rc = 0; /* assume success */
600 
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;
611 
612             memset( &sei, 0, sizeof( sei ));
613             sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
614             if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS )
615 {
616                 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
617                 sseip->lssei_ssl_strength = sslstrength;
618             } else {
619                 rc = -1;
620             }
621         }
622     }
623 
624     return( rc );
625 }
626 
627 int
628 LDAP_CALL
629 ldapssl_enable_clientauth( LDAP *ld, char *keynickname,
630         char *keypasswd, char *certnickname )
631 {
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;
639 
640     /*
641      * Check parameters
642      */
643     if ( certnickname == NULL || keypasswd == NULL ) {
644 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
645 	return( -1 );
646     }
647 
648     /*
649      * Update session info. data structure.
650      */
651     sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
652     if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) {
653 	return( -1 );
654     }
655     ssip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
656     if ( NULL == ssip ) {
657 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
658 	return( -1 );
659     }
660     ssip->lssei_certnickname = PL_strdup( certnickname );
661     ssip->lssei_keypasswd = PL_strdup( keypasswd );
662 
663     if ( NULL == ssip->lssei_certnickname || NULL == ssip->lssei_keypasswd ) {
664 	ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
665 	return( -1 );
666     }
667 
668     if ( check_clientauth_nicknames_and_passwd( ld, ssip ) != 0 ) {
669 	return( -1 );
670     }
671 
672     /*
673      * replace standard SSL CONNECT function with client auth aware one
674      */
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 );
680     }
681 
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 );
686     }
687 
688     iofns.lextiof_connect = ldapssl_clientauth_connect;
689 
690     if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
691 		!= 0 ) {
692 	return( -1 );
693     }
694 
695     return( 0 );
696 #endif
697 }
698 
699 
700 static void
701 ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp )
702 {
703     if ( NULL != ssipp && NULL != *ssipp ) {
704 	if ( NULL != (*ssipp)->lssei_certnickname ) {
705 	    PL_strfree( (*ssipp)->lssei_certnickname );
706 	    (*ssipp)->lssei_certnickname = NULL;
707 	}
708 	if ( NULL != (*ssipp)->lssei_keypasswd ) {
709 	    PL_strfree( (*ssipp)->lssei_keypasswd );
710 	    (*ssipp)->lssei_keypasswd = NULL;
711 	}
712 	PR_Free( *ssipp );
713 	*ssipp = NULL;
714     }
715 }
716 
717 
718 static void
719 ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp )
720 {
721     if ( NULL != soipp && NULL != *soipp ) {
722 	PR_Free( *soipp );
723 	*soipp = NULL;
724     }
725 }
726 
727 
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()
732  */
733 
734 static int
735 ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, PRBool checkSig,
736 	PRBool isServer)
737 {
738     SECStatus		rv = SECFailure;
739     LDAPSSLSessionInfo	*sseip;
740     CERTCertificate	*cert;
741     SECCertUsage	certUsage;
742     char		*hostname = (char *)0;
743 
744     if (!sessionarg || !socket)
745 	return rv;
746 
747     sseip = (LDAPSSLSessionInfo *)sessionarg;
748 
749     if (LDAPSSL_AUTH_WEAK == sseip->lssei_ssl_strength ) { /* no check */
750         return SECSuccess;
751     }
752 
753     if ( isServer ) {
754 	certUsage = certUsageSSLClient;
755     } else {
756 	certUsage = certUsageSSLServer;
757     }
758     cert = SSL_PeerCertificate( fd );
759 
760     rv = CERT_VerifyCertNow(sseip->lssei_certdbh, cert, checkSig,
761 			certUsage, NULL);
762 
763     if ( rv != SECSuccess || isServer )
764 	return rv;
765 
766     if ( LDAPSSL_AUTH_CNCHECK == sseip->lssei_ssl_strength )
767       {
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!
772 	 */
773 
774 	hostname = SSL_RevealURL( fd );
775 
776 	if (hostname && hostname[0]) {
777 	  rv = CERT_VerifyCertName(cert, hostname);
778 	} else  {
779 	  rv = SECFailure;
780      	}
781 	if (hostname)
782 		PORT_Free(hostname);
783 	if (rv != SECSuccess)
784 	  PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
785       }
786 
787     return((int)rv);
788 }
789 
790 
791 /*
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.
795  */
796 
797 static int
798 get_clientauth_data( void *sessionarg, PRFileDesc *prfd,
799         CERTDistNames *caNames,  CERTCertificate **pRetCert,
800         SECKEYPrivateKey **pRetKey )
801 
802 {
803     LDAPSSLSessionInfo	*ssip;
804 
805     if (( ssip = (LDAPSSLSessionInfo *)sessionarg ) == NULL ) {
806 	return( -1 );       /* client auth. not enabled */
807     }
808 
809     return( get_keyandcert( ssip, pRetCert, pRetKey, NULL ));
810 }
811 
812 static int
813 get_keyandcert( LDAPSSLSessionInfo *ssip,
814 	CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey,
815 	char **errmsgp )
816 {
817     CERTCertificate	*cert;
818     SECKEYPrivateKey	*key;
819 
820     if (( cert = PK11_FindCertFromNickname( ssip->lssei_certnickname, NULL ))
821 		== NULL ) {
822 	if ( errmsgp != NULL ) {
823 	    *errmsgp = dgettext(TEXT_DOMAIN, "unable to find certificate");
824 	}
825 	return( -1 );
826     }
827 
828     {
829 	PK11_SetPasswordFunc( get_keypassword );
830     }
831 
832 
833 
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");
838 	}
839 	return( -1 );
840     }
841 
842     *pRetCert = cert;
843     *pRetKey = key;
844     return( 0 );
845 }
846 
847 
848 /*
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.
852  */
853 
854 static char *
855 get_keypassword( PK11SlotInfo *slot, PRBool retry, void *sessionarg )
856 {
857     LDAPSSLSessionInfo	*ssip;
858 
859     if ( retry)
860       return (NULL);
861 
862     ssip = (LDAPSSLSessionInfo *)sessionarg;
863     if ( NULL == ssip ) {
864 	return( NULL );
865     }
866 
867     return( ssip->lssei_keypasswd );
868 }
869 
870 
871 /*
872  * performs some basic checks on clientauth cert and key/password
873  *
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.
878  */
879 static int
880 check_clientauth_nicknames_and_passwd( LDAP *ld, LDAPSSLSessionInfo *ssip )
881 {
882     char		*errmsg = NULL;
883     CERTCertificate	*cert = NULL;
884     SECKEYPrivateKey	*key = NULL;
885     int rv;
886 
887     rv = get_keyandcert( ssip, &cert, &key, &errmsg );
888 
889     if ( rv != 0 ) {
890     	if ( errmsg != NULL ) {
891 	    errmsg = strdup( errmsg );
892 	}
893 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg );
894 	return( -1 );
895     }
896 
897     if ( cert != NULL ) {
898 	CERT_DestroyCertificate( cert );
899     }
900     if ( key != NULL ) {
901 	SECKEY_DestroyPrivateKey( key );
902     }
903     return( 0 );
904 }
905 
906 
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
910  */
911 int stubs_o_stuff( void )
912 {
913     PRExplodedTime exploded;
914     PLArenaPool pool;
915 
916     const char *name ="t";
917     PRUint32 size = 0, align = 0;
918 
919     PR_ImplodeTime( &exploded );
920     PL_InitArenaPool( &pool, name, size, align);
921     PR_Cleanup();
922     PR_fprintf((PRFileDesc*)stderr, "Bad IDEA!!");
923 
924     return 0;
925 
926 }
927 #endif	/* NOT_NEEDED_IN_LIBLDAP */
928 
929 
930 /*
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.
934  */
935 int ldapssl_import_fd ( LDAP *ld, int secure )
936 {
937     PRLDAPSessionInfo   sei;
938     PRLDAPSocketInfo    soi;
939     LDAPSSLSocketInfo   *ssoip = NULL;
940     LDAPSSLSessionInfo  *sseip;
941     PRFileDesc          *sslfd = NULL;
942 
943 
944     /*
945      * Retrieve session info. so we can store a pointer to our session info.
946      * in our socket info. later.
947      */
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 );
952     }
953     sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
954 
955 
956     /*
957      * Retrieve socket info. so we have the PRFileDesc.
958      */
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 );
963     }
964 
965     /*
966      * Allocate a structure to hold our socket-specific data.
967      */
968     if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) {
969         goto reset_socket_and_exit_with_error;
970     }
971     ssoip->soi_sessioninfo = sseip;
972 
973     /*
974      * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL.
975      */
976     if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) {
977         goto reset_socket_and_exit_with_error;
978     }
979 
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;
985     }
986 
987     /*
988      * Let the standard NSPR to LDAP layer know about the new socket and
989      * our own socket-specific data.
990      */
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;
995     }
996 
997     /*
998      * Install certificate hook function.
999      */
1000     if ( SSL_AuthCertificateHook( soi.soinfo_prfd,
1001                                   (SSLAuthCertificate)ldapssl_AuthCertificate,
1002                                   (void *)CERT_GetDefaultCertDB()) != 0 ) {
1003         goto reset_socket_and_exit_with_error;
1004     }
1005 
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;
1010     }
1011 
1012     return 0;
1013 
1014  reset_socket_and_exit_with_error:
1015     if ( NULL != sslfd ) {
1016         /*
1017          * "Unimport" the socket from SSL, i.e. get rid of the upper layer of
1018          * the file descriptor stack, which represents SSL.
1019          */
1020         soi.soinfo_prfd = sslfd;
1021         sslfd = PR_PopIOLayer( soi.soinfo_prfd, PR_TOP_IO_LAYER );
1022         sslfd->dtor( sslfd );
1023     }
1024     if ( NULL != ssoip ) {
1025         ldapssl_free_socket_info( &ssoip );
1026         soi.soinfo_appdata = NULL;
1027     }
1028     prldap_set_default_socket_info( ld, &soi );
1029 
1030     return( -1 );
1031 }
1032 
1033 
1034 /*
1035  * Reset an LDAP session from SSL to a non-secure status.
1036  * Basically, this function undoes the work done by ldapssl_install_routines.
1037  */
1038 int ldapssl_reset_to_nonsecure ( LDAP *ld )
1039 {
1040     PRLDAPSessionInfo   sei;
1041     LDAPSSLSessionInfo  *sseip;
1042 
1043     struct ldap_x_ext_io_fns    iofns;
1044     int rc = 0;
1045 
1046     /*
1047      * Retrieve session info.
1048      */
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 );
1053     }
1054     sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata;
1055 
1056     if ( sseip != NULL ) {
1057         /*
1058          * Reset the standard extended io functions.
1059          */
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;
1066         }
1067 
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;
1073 
1074         if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns )
1075 		< 0) {
1076             rc = -1;
1077             goto free_session_info;
1078         }
1079 
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;
1085         }
1086     } /* if ( sseip && *sseip ) */
1087 
1088     if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_OFF ) < 0 ) {
1089         return (-1);
1090     }
1091 
1092     return rc;
1093 }
1094 
1095 
1096 #ifdef _SOLARIS_SDK
1097 static void
1098 _nss_initf_ipnodes(nss_db_params_t *p)
1099 {
1100 	static char *no_service = "";
1101 
1102 	p->name = NSS_DBNAM_IPNODES;
1103         p->flags |= NSS_USE_DEFAULT_CONFIG;
1104         p->default_config = host_service == NULL ? no_service : host_service;
1105 }
1106 
1107 static void
1108 _nss_initf_hosts(nss_db_params_t *p)
1109 {
1110 	static char *no_service = "";
1111 
1112 	p->name = NSS_DBNAM_HOSTS;
1113         p->flags |= NSS_USE_DEFAULT_CONFIG;
1114         p->default_config = host_service == NULL ? no_service : host_service;
1115 }
1116 
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)
1121 {
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;
1127 
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;
1138 	}
1139 
1140 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
1141 
1142         arg.key.hostaddr.addr	= addr;
1143         arg.key.hostaddr.len	= len;
1144         arg.key.hostaddr.type	= type;
1145         arg.stayopen		= 0;
1146 
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);
1152 }
1153 
1154 /*
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.
1159  *
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.
1162  */
1163 
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)
1169 {
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 */
1177 
1178 
1179 	if (addr == NULL || result == NULL || buffer == NULL ||
1180 			(type != AF_INET && type != AF_INET6))
1181 		return (NULL);
1182 
1183 
1184 	(void) memset(&h_ent, 0, sizeof (h_ent));
1185 
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);
1191 		}
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);
1197 		}
1198 	}
1199 
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;
1210 	}
1211 	return (ldap_hent);
1212 }
1213 
1214 /*
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.
1218  *
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.
1223  *
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.
1228  *
1229  * Note:
1230  *
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.
1234  *
1235  * Returns:
1236  *	0	if success
1237  *	-1	if failure
1238  */
1239 
1240 int
1241 ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip)
1242 {
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;
1252 
1253 	/*
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
1257 	 */
1258 
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);
1264 	}
1265 
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;
1272 		}
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);
1285 			}
1286 		}
1287 		if (name_list == NULL) {	/* alloc error */
1288 			(void) mutex_unlock(&db_root_hosts.lock);
1289 			__nsw_freeconfig(conf);
1290 			return (-1);
1291 		}
1292 	}
1293 	__nsw_freeconfig(conf);
1294 	if (!got_skip) {
1295 		/*
1296 		 * Since skip name service not used for hosts, we do not need
1297 		 * to install our private address resolution function
1298 		 */
1299 		(void) mutex_unlock(&db_root_hosts.lock);
1300 		if (name_list != NULL)
1301 			free(name_list);
1302 		return (0);
1303 	}
1304 	if (host_service != NULL)
1305 		free(host_service);
1306 	host_service = name_list;
1307 	(void) mutex_unlock(&db_root_hosts.lock);
1308 
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);
1315 }
1316 #endif	/* _SOLARIS_SDK */
1317 #endif /* NET_SSL */
1318