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