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