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
set_using_pkcs_functions(int val)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
ldapssl_init(const char * defhost,int defport,int defsecure)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
ldapssl_close(int s,struct lextiof_socket_private * socketarg)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
do_ldapssl_connect(const char * hostlist,int defport,int timeout,unsigned long options,struct lextiof_session_private * sessionarg,struct lextiof_socket_private ** socketargp,int clientauth)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
ldapssl_connect(const char * hostlist,int defport,int timeout,unsigned long options,struct lextiof_session_private * sessionarg,struct lextiof_socket_private ** socketargp)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
ldapssl_clientauth_connect(const char * hostlist,int defport,int timeout,unsigned long options,struct lextiof_session_private * sessionarg,struct lextiof_socket_private ** socketargp)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
ldapssl_disposehandle(LDAP * ld,struct lextiof_session_private * sessionarg)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
ldapssl_install_routines(LDAP * ld)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
ldapssl_set_strength(LDAP * ld,int sslstrength)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
ldapssl_enable_clientauth(LDAP * ld,char * keynickname,char * keypasswd,char * certnickname)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
ldapssl_free_session_info(LDAPSSLSessionInfo ** ssipp)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
ldapssl_free_socket_info(LDAPSSLSocketInfo ** soipp)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
ldapssl_AuthCertificate(void * sessionarg,PRFileDesc * fd,PRBool checkSig,PRBool isServer)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
get_clientauth_data(void * sessionarg,PRFileDesc * prfd,CERTDistNames * caNames,CERTCertificate ** pRetCert,SECKEYPrivateKey ** pRetKey)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
get_keyandcert(LDAPSSLSessionInfo * ssip,CERTCertificate ** pRetCert,SECKEYPrivateKey ** pRetKey,char ** errmsgp)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 *
get_keypassword(PK11SlotInfo * slot,PRBool retry,void * sessionarg)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
check_clientauth_nicknames_and_passwd(LDAP * ld,LDAPSSLSessionInfo * ssip)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 */
ldapssl_import_fd(LDAP * ld,int secure)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 */
ldapssl_reset_to_nonsecure(LDAP * ld)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
_nss_initf_ipnodes(nss_db_params_t * p)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
_nss_initf_hosts(nss_db_params_t * p)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 *
_switch_gethostbyaddr_r(const char * addr,int len,int type,struct hostent * result,char * buffer,int buflen,int * h_errnop)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 *
ns_gethostbyaddr(const char * addr,int len,int type,LDAPHostEnt * result,char * buffer,int buflen,int * statusp,void * extradata)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
ldapssl_install_gethostbyaddr(LDAP * ld,const char * skip)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