1 /* 2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * The contents of this file are subject to the Netscape Public 8 * License Version 1.1 (the "License"); you may not use this file 9 * except in compliance with the License. You may obtain a copy of 10 * the License at http://www.mozilla.org/NPL/ 11 * 12 * Software distributed under the License is distributed on an "AS 13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 14 * implied. See the License for the specific language governing 15 * rights and limitations under the License. 16 * 17 * The Original Code is Mozilla Communicator client code, released 18 * March 31, 1998. 19 * 20 * The Initial Developer of the Original Code is Netscape 21 * Communications Corporation. Portions created by Netscape are 22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All 23 * Rights Reserved. 24 * 25 * Contributor(s): 26 */ 27 28 /* 29 * ldapsinit.c 30 */ 31 32 #if defined(NET_SSL) 33 34 #if defined( _WINDOWS ) 35 #include <windows.h> 36 #endif 37 38 /* XXX:mhein The following is a workaround for the redefinition of */ 39 /* const problem on OSF. Fix to be provided by NSS */ 40 /* This is a pretty benign workaround for us which */ 41 /* should not cause problems in the future even if */ 42 /* we forget to take it out :-) */ 43 44 #ifdef OSF1V4D 45 #ifndef __STDC__ 46 # define __STDC__ 47 #endif /* __STDC__ */ 48 #endif /* OSF1V4D */ 49 50 #include <errno.h> 51 #include <nspr.h> 52 #include <cert.h> 53 #include <key.h> 54 #include <ssl.h> 55 #include <sslproto.h> 56 #include <sslerr.h> 57 #include <prnetdb.h> 58 59 #include <ldap.h> 60 61 #include <ldappr.h> 62 #include <pk11func.h> 63 64 #ifdef _SOLARIS_SDK 65 #include "solaris-int.h" 66 #include <libintl.h> 67 #include <syslog.h> 68 #include <nsswitch.h> 69 #include <synch.h> 70 #include <nss_dbdefs.h> 71 #include <netinet/in.h> 72 73 #define HOST_BUF_SIZE 2048 74 75 #ifndef INADDR_NONE 76 #define INADDR_NONE (-1) 77 #endif 78 79 extern int 80 str2hostent(const char *instr, int lenstr, void *ent, char *buffer, 81 int buflen); 82 83 extern int 84 str2hostent6(const char *instr, int lenstr, void *ent, char *buffer, 85 int buflen); 86 87 extern LDAPHostEnt * 88 _ns_gethostbyaddr(LDAP *ld, const char *addr, int length, int type, 89 LDAPHostEnt *result, char *buffer, int buflen, int *statusp, 90 void *extradata); 91 92 static char *host_service = NULL; 93 94 static DEFINE_NSS_DB_ROOT(db_root_hosts); 95 static DEFINE_NSS_DB_ROOT(db_root_ipnodes); 96 #endif 97 98 /* 99 * Data structure to hold the standard NSPR I/O function pointers set by 100 * libprldap. We save them in our session data structure so we can call 101 * them from our own I/O functions (we add functionality to support SSL 102 * while using libprldap's functions as much as possible). 103 */ 104 typedef struct ldapssl_std_functions { 105 LDAP_X_EXTIOF_CLOSE_CALLBACK *lssf_close_fn; 106 LDAP_X_EXTIOF_CONNECT_CALLBACK *lssf_connect_fn; 107 LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *lssf_disposehdl_fn; 108 } LDAPSSLStdFunctions; 109 110 111 112 /* 113 * LDAP session data structure. 114 */ 115 typedef struct ldapssl_session_info { 116 int lssei_using_pcks_fns; 117 int lssei_ssl_strength; 118 char *lssei_certnickname; 119 char *lssei_keypasswd; 120 LDAPSSLStdFunctions lssei_std_functions; 121 CERTCertDBHandle *lssei_certdbh; 122 #ifdef _SOLARIS_SDK 123 /* 124 * This is a hack. 125 * ld is used so that we can use libldap's gethostbyaddr 126 * resolver. This is needed to prevent recursion with libsldap. 127 */ 128 LDAP *ld; 129 #endif /* _SOLARIS_SDK */ 130 } LDAPSSLSessionInfo; 131 132 133 /* 134 * LDAP socket data structure. 135 */ 136 typedef struct ldapssl_socket_info { 137 LDAPSSLSessionInfo *soi_sessioninfo; /* session info */ 138 } LDAPSSLSocketInfo; 139 140 141 /* 142 * XXXceb This is a hack until the new IO functions are done. 143 * this function MUST be called before ldap_enable_clienauth. 144 * right now, this function is called in ldapssl_pkcs_init(); 145 */ 146 147 static int using_pkcs_functions = 0; 148 149 void set_using_pkcs_functions( int val ) 150 { 151 using_pkcs_functions = val; 152 } 153 154 155 /* 156 * Utility functions: 157 */ 158 static void ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp ); 159 static void ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp ); 160 161 162 /* 163 * SSL Stuff 164 */ 165 166 static int ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, 167 PRBool checkSig, PRBool isServer); 168 169 /* 170 * client auth stuff 171 */ 172 static int get_clientauth_data( void *sessionarg, PRFileDesc *prfd, 173 CERTDistNames *caNames, CERTCertificate **pRetCert, 174 SECKEYPrivateKey **pRetKey ); 175 static int get_keyandcert( LDAPSSLSessionInfo *ssip, 176 CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey, 177 char **errmsgp ); 178 static int check_clientauth_nicknames_and_passwd( LDAP *ld, 179 LDAPSSLSessionInfo *ssip ); 180 static char *get_keypassword( PK11SlotInfo *slot, PRBool retry, 181 void *sessionarg ); 182 183 /* 184 * Static variables. 185 */ 186 #ifdef _SOLARIS_SDK 187 static int default_ssl_strength = LDAPSSL_AUTH_CNCHECK; 188 #else 189 static int default_ssl_strength = LDAPSSL_AUTH_CERT; 190 #endif 191 192 /* 193 * Like ldap_init(), except also install I/O routines from libsec so we 194 * can support SSL. If defsecure is non-zero, SSL is enabled for the 195 * default connection as well. 196 */ 197 LDAP * 198 LDAP_CALL 199 ldapssl_init( const char *defhost, int defport, int defsecure ) 200 { 201 LDAP *ld; 202 203 204 #ifndef LDAP_SSLIO_HOOKS 205 return( NULL ); 206 #else 207 if (0 ==defport) 208 defport = LDAPS_PORT; 209 210 if (( ld = ldap_init( defhost, defport )) == NULL ) { 211 return( NULL ); 212 } 213 214 if ( ldapssl_install_routines( ld ) < 0 || ldap_set_option( ld, 215 LDAP_OPT_SSL, defsecure ? LDAP_OPT_ON : LDAP_OPT_OFF ) != 0 ) { 216 PR_SetError( PR_UNKNOWN_ERROR, EINVAL ); /* XXXmcs: just a guess! */ 217 ldap_unbind( ld ); 218 return( NULL ); 219 } 220 221 return( ld ); 222 #endif 223 } 224 225 226 static int 227 ldapssl_close(int s, struct lextiof_socket_private *socketarg) 228 { 229 PRLDAPSocketInfo soi; 230 LDAPSSLSocketInfo *ssoip; 231 LDAPSSLSessionInfo *sseip; 232 233 memset( &soi, 0, sizeof(soi)); 234 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE; 235 if ( prldap_get_socket_info( s, socketarg, &soi ) != LDAP_SUCCESS ) { 236 return( -1 ); 237 } 238 239 ssoip = (LDAPSSLSocketInfo *)soi.soinfo_appdata; 240 sseip = ssoip->soi_sessioninfo; 241 242 ldapssl_free_socket_info( (LDAPSSLSocketInfo **)&soi.soinfo_appdata ); 243 244 return( (*(sseip->lssei_std_functions.lssf_close_fn))( s, socketarg )); 245 } 246 247 static int 248 do_ldapssl_connect(const char *hostlist, int defport, int timeout, 249 unsigned long options, struct lextiof_session_private *sessionarg, 250 struct lextiof_socket_private **socketargp, int clientauth ) 251 { 252 int intfd = -1; 253 PRBool secure; 254 PRLDAPSessionInfo sei; 255 PRLDAPSocketInfo soi; 256 LDAPSSLSocketInfo *ssoip = NULL; 257 LDAPSSLSessionInfo *sseip; 258 PRFileDesc *sslfd = NULL; 259 #ifdef _SOLARIS_SDK 260 int port; 261 int parse_err; 262 char *host = NULL; 263 char *name; 264 struct ldap_x_hostlist_status 265 *status = NULL; 266 in_addr_t addr_ipv4; 267 in6_addr_t addr_ipv6; 268 char *host_buf; 269 LDAPHostEnt *hent; 270 LDAPHostEnt host_ent; 271 int stat; 272 int type; 273 #endif /* _SOLARIS_SDK */ 274 275 /* 276 * Determine if secure option is set. Also, clear secure bit in options 277 * the we pass to the standard connect() function (since it doesn't know 278 * how to handle the secure option). 279 */ 280 if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) { 281 secure = PR_TRUE; 282 options &= ~LDAP_X_EXTIOF_OPT_SECURE; 283 } else { 284 secure = PR_FALSE; 285 } 286 287 /* 288 * Retrieve session info. so we can store a pointer to our session info. 289 * in our socket info. later. 290 */ 291 memset( &sei, 0, sizeof(sei)); 292 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 293 if ( prldap_get_session_info( NULL, sessionarg, &sei ) != LDAP_SUCCESS ) { 294 return( -1 ); 295 } 296 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata; 297 298 /* 299 * Call the standard connect() callback to make the TCP connection. 300 * If it succeeds, *socketargp is set. 301 */ 302 303 intfd = (*(sseip->lssei_std_functions.lssf_connect_fn))( hostlist, defport, 304 timeout, options, sessionarg, socketargp 305 #ifdef _SOLARIS_SDK 306 , &host ); 307 #else 308 ); 309 #endif /* _SOLARIS_SDK */ 310 311 if ( intfd < 0 ) { 312 return( intfd ); 313 } 314 315 #ifdef _SOLARIS_SDK 316 /* 317 * Determine if the "host name" is an ip address. If so, 318 * we must look up the actual host name corresponding to 319 * it. 320 */ 321 if ( NULL == host ) { 322 goto close_socket_and_exit_with_error; 323 } 324 type = AF_UNSPEC; 325 if (strlen(host) < INET6_ADDRSTRLEN && 326 inet_pton(AF_INET6, host, &addr_ipv6) == 1) { 327 type = AF_INET6; 328 } else if (strlen(host) < INET_ADDRSTRLEN && 329 inet_pton(AF_INET, host, &addr_ipv4) == 1) { 330 type = AF_INET; 331 } 332 if (type == AF_INET || type == AF_INET6) { 333 host_buf = malloc(HOST_BUF_SIZE); 334 if (host_buf == NULL) { 335 /* will free host in close_socket_and_exit_with_error */ 336 goto close_socket_and_exit_with_error; 337 } 338 339 /* Call ldap layer's gethostbyaddr resolver */ 340 hent = _ns_gethostbyaddr(sseip->ld, host, strlen(host), type, 341 &host_ent, host_buf, HOST_BUF_SIZE, &stat, NULL); 342 343 /* If we are unable to lookup the host addr, we fail! */ 344 if (hent == NULL) { 345 syslog(LOG_WARNING, 346 "libldap: do_ldapssl_connect: " 347 "Unable to resolve '%s'", host); 348 free(host_buf); 349 /* will free host in close_socket_and_exit_with_error */ 350 goto close_socket_and_exit_with_error; 351 } 352 /* We support only the primary host name */ 353 else { 354 if (hent->ldaphe_name != NULL) 355 name = strdup(hent->ldaphe_name); 356 free(host_buf); 357 if (name == NULL) 358 goto close_socket_and_exit_with_error; 359 else 360 ldap_memfree(host); host = NULL; 361 host = name; 362 } 363 } 364 #endif /* _SOLARIS_SDK */ 365 366 /* 367 * Retrieve socket info. so we have the PRFileDesc. 368 */ 369 memset( &soi, 0, sizeof(soi)); 370 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE; 371 if ( prldap_get_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) { 372 goto close_socket_and_exit_with_error; 373 } 374 375 /* 376 * Allocate a structure to hold our socket-specific data. 377 */ 378 if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) { 379 goto close_socket_and_exit_with_error; 380 } 381 ssoip->soi_sessioninfo = sseip; 382 383 /* 384 * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL. 385 */ 386 if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) { 387 goto close_socket_and_exit_with_error; 388 } 389 390 if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess || 391 SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure ) 392 != SECSuccess || ( secure && SSL_ResetHandshake( sslfd, 393 PR_FALSE ) != SECSuccess )) { 394 goto close_socket_and_exit_with_error; 395 } 396 397 /* 398 * Let the standard NSPR to LDAP layer know about the new socket and 399 * our own socket-specific data. 400 */ 401 soi.soinfo_prfd = sslfd; 402 soi.soinfo_appdata = (void *)ssoip; 403 if ( prldap_set_socket_info( intfd, *socketargp, &soi ) != LDAP_SUCCESS ) { 404 goto close_socket_and_exit_with_error; 405 } 406 407 #ifdef _SOLARIS_SDK 408 /* 409 * Set hostname which will be retrieved (depending on ssl strength) when 410 * using client or server auth. 411 */ 412 if (SSL_SetURL(sslfd, host) != SECSuccess) 413 goto close_socket_and_exit_with_error; 414 ldap_memfree(host); 415 host = NULL; 416 #endif /* _SOLARIS_SDK */ 417 418 sslfd = NULL; /* so we don't close the socket twice upon error */ 419 420 /* 421 * Install certificate hook function. 422 */ 423 SSL_AuthCertificateHook( soi.soinfo_prfd, 424 (SSLAuthCertificate)ldapssl_AuthCertificate, 425 (void *)sseip); 426 427 if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd, 428 get_clientauth_data, clientauth ? sseip : NULL ) != 0 ) { 429 goto close_socket_and_exit_with_error; 430 } 431 432 return( intfd ); /* success */ 433 434 close_socket_and_exit_with_error: 435 #ifdef _SOLARIS_SDK 436 if ( NULL != host ) ldap_memfree(host); 437 #endif /* _SOLARIS_SDK */ 438 if ( NULL != sslfd ) { 439 PR_Close( sslfd ); 440 } 441 if ( NULL != ssoip ) { 442 ldapssl_free_socket_info( &ssoip ); 443 } 444 if ( intfd >= 0 && NULL != *socketargp ) { 445 (*(sseip->lssei_std_functions.lssf_close_fn))( intfd, *socketargp ); 446 } 447 return( -1 ); 448 } 449 450 451 static int 452 ldapssl_connect(const char *hostlist, int defport, int timeout, 453 unsigned long options, struct lextiof_session_private *sessionarg, 454 struct lextiof_socket_private **socketargp ) 455 { 456 return( do_ldapssl_connect( hostlist, defport, timeout, options, 457 sessionarg, socketargp, 0 )); 458 } 459 460 461 static int 462 ldapssl_clientauth_connect(const char *hostlist, int defport, int timeout, 463 unsigned long options, struct lextiof_session_private *sessionarg, 464 struct lextiof_socket_private **socketargp ) 465 { 466 return( do_ldapssl_connect( hostlist, defport, timeout, options, 467 sessionarg, socketargp, 1 )); 468 } 469 470 471 static void 472 ldapssl_disposehandle(LDAP *ld, struct lextiof_session_private *sessionarg) 473 { 474 PRLDAPSessionInfo sei; 475 LDAPSSLSessionInfo *sseip; 476 LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn; 477 478 memset( &sei, 0, sizeof( sei )); 479 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 480 if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS ) { 481 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata; 482 disposehdl_fn = sseip->lssei_std_functions.lssf_disposehdl_fn; 483 ldapssl_free_session_info( &sseip ); 484 (*disposehdl_fn)( ld, sessionarg ); 485 } 486 } 487 488 489 /* 490 * Install I/O routines from libsec and NSPR into libldap to allow libldap 491 * to do SSL. 492 * 493 * We rely on libprldap to provide most of the functions, and then we override 494 * a few of them to support SSL. 495 */ 496 int 497 LDAP_CALL 498 ldapssl_install_routines( LDAP *ld ) 499 { 500 #ifndef LDAP_SSLIO_HOOKS 501 ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); 502 return( -1 ); 503 #else 504 struct ldap_x_ext_io_fns iofns; 505 LDAPSSLSessionInfo *ssip; 506 PRLDAPSessionInfo sei; 507 508 /* 509 * This is done within ldap_init() and 510 * ldap_init() is called from ldapssl_init() 511 */ 512 #ifndef _SOLARIS_SDK 513 if ( prldap_install_routines( 514 ld, 515 1 /* shared -- we have to assume it is */ ) 516 != LDAP_SUCCESS ) { 517 return( -1 ); 518 } 519 #endif /*_SOLARIS_SDK*/ 520 521 /* 522 * Allocate our own session information. 523 */ 524 if ( NULL == ( ssip = (LDAPSSLSessionInfo *)PR_Calloc( 1, 525 sizeof( LDAPSSLSessionInfo )))) { 526 ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL ); 527 return( -1 ); 528 } 529 /* 530 * Initialize session info. 531 * XXX: it would be nice to be able to set these on a per-session basis: 532 * lssei_using_pcks_fns 533 * lssei_certdbh 534 */ 535 ssip->lssei_ssl_strength = default_ssl_strength; 536 ssip->lssei_using_pcks_fns = using_pkcs_functions; 537 ssip->lssei_certdbh = CERT_GetDefaultCertDB(); 538 #ifdef _SOLARIS_SDK 539 /* 540 * This is part of a hack to allow the ssl portion of the 541 * library to call the ldap library gethostbyaddr resolver. 542 */ 543 ssip->ld = ld; 544 #endif /* _SOLARIS_SDK */ 545 546 /* 547 * override a few functions, saving a pointer to the standard function 548 * in each case so we can call it from our SSL savvy functions. 549 */ 550 memset( &iofns, 0, sizeof(iofns)); 551 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; 552 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) { 553 ldapssl_free_session_info( &ssip ); 554 return( -1 ); 555 } 556 557 /* override socket, connect, and ioctl */ 558 ssip->lssei_std_functions.lssf_connect_fn = iofns.lextiof_connect; 559 iofns.lextiof_connect = ldapssl_connect; 560 ssip->lssei_std_functions.lssf_close_fn = iofns.lextiof_close; 561 iofns.lextiof_close = ldapssl_close; 562 ssip->lssei_std_functions.lssf_disposehdl_fn = iofns.lextiof_disposehandle; 563 iofns.lextiof_disposehandle = ldapssl_disposehandle; 564 565 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) { 566 ldapssl_free_session_info( &ssip ); 567 return( -1 ); 568 } 569 570 /* 571 * Store session info. for later retrieval. 572 */ 573 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 574 sei.seinfo_appdata = (void *)ssip; 575 if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) { 576 return( -1 ); 577 } 578 579 return( 0 ); 580 #endif 581 } 582 583 584 /* 585 * Set the SSL strength for an existing SSL-enabled LDAP session handle. 586 * 587 * See the description of ldapssl_serverauth_init() above for valid 588 * sslstrength values. If ld is NULL, the default for new LDAP session 589 * handles is set. 590 * 591 * Returns 0 if all goes well and -1 if an error occurs. 592 */ 593 int 594 LDAP_CALL 595 ldapssl_set_strength( LDAP *ld, int sslstrength ) 596 { 597 int rc = 0; /* assume success */ 598 599 if ( sslstrength != LDAPSSL_AUTH_WEAK && 600 sslstrength != LDAPSSL_AUTH_CERT && 601 sslstrength != LDAPSSL_AUTH_CNCHECK ) { 602 rc = -1; 603 } else { 604 if ( NULL == ld ) { /* set default strength */ 605 default_ssl_strength = sslstrength; 606 } else { /* set session-specific strength */ 607 PRLDAPSessionInfo sei; 608 LDAPSSLSessionInfo *sseip; 609 610 memset( &sei, 0, sizeof( sei )); 611 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 612 if ( prldap_get_session_info( ld, NULL, &sei ) == LDAP_SUCCESS ) 613 { 614 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata; 615 sseip->lssei_ssl_strength = sslstrength; 616 } else { 617 rc = -1; 618 } 619 } 620 } 621 622 return( rc ); 623 } 624 625 int 626 LDAP_CALL 627 ldapssl_enable_clientauth( LDAP *ld, char *keynickname, 628 char *keypasswd, char *certnickname ) 629 { 630 #ifndef LDAP_SSLIO_HOOKS 631 ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); 632 return( -1 ); 633 #else 634 struct ldap_x_ext_io_fns iofns; 635 LDAPSSLSessionInfo *ssip; 636 PRLDAPSessionInfo sei; 637 638 /* 639 * Check parameters 640 */ 641 if ( certnickname == NULL || keypasswd == NULL ) { 642 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); 643 return( -1 ); 644 } 645 646 /* 647 * Update session info. data structure. 648 */ 649 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 650 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) { 651 return( -1 ); 652 } 653 ssip = (LDAPSSLSessionInfo *)sei.seinfo_appdata; 654 if ( NULL == ssip ) { 655 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); 656 return( -1 ); 657 } 658 ssip->lssei_certnickname = PL_strdup( certnickname ); 659 ssip->lssei_keypasswd = PL_strdup( keypasswd ); 660 661 if ( NULL == ssip->lssei_certnickname || NULL == ssip->lssei_keypasswd ) { 662 ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL ); 663 return( -1 ); 664 } 665 666 if ( check_clientauth_nicknames_and_passwd( ld, ssip ) != 0 ) { 667 return( -1 ); 668 } 669 670 /* 671 * replace standard SSL CONNECT function with client auth aware one 672 */ 673 memset( &iofns, 0, sizeof(iofns)); 674 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; 675 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) 676 != 0 ) { 677 return( -1 ); 678 } 679 680 if ( iofns.lextiof_connect != ldapssl_connect ) { 681 /* standard SSL setup has not done */ 682 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); 683 return( -1 ); 684 } 685 686 iofns.lextiof_connect = ldapssl_clientauth_connect; 687 688 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) 689 != 0 ) { 690 return( -1 ); 691 } 692 693 return( 0 ); 694 #endif 695 } 696 697 698 static void 699 ldapssl_free_session_info( LDAPSSLSessionInfo **ssipp ) 700 { 701 if ( NULL != ssipp && NULL != *ssipp ) { 702 if ( NULL != (*ssipp)->lssei_certnickname ) { 703 PL_strfree( (*ssipp)->lssei_certnickname ); 704 (*ssipp)->lssei_certnickname = NULL; 705 } 706 if ( NULL != (*ssipp)->lssei_keypasswd ) { 707 PL_strfree( (*ssipp)->lssei_keypasswd ); 708 (*ssipp)->lssei_keypasswd = NULL; 709 } 710 PR_Free( *ssipp ); 711 *ssipp = NULL; 712 } 713 } 714 715 716 static void 717 ldapssl_free_socket_info( LDAPSSLSocketInfo **soipp ) 718 { 719 if ( NULL != soipp && NULL != *soipp ) { 720 PR_Free( *soipp ); 721 *soipp = NULL; 722 } 723 } 724 725 726 /* this function provides cert authentication. This is called during 727 * the SSL_Handshake process. Once the cert has been retrieved from 728 * the server, the it is checked, using VerifyCertNow(), then 729 * the cn is checked against the host name, set with SSL_SetURL() 730 */ 731 732 static int 733 ldapssl_AuthCertificate(void *sessionarg, PRFileDesc *fd, PRBool checkSig, 734 PRBool isServer) 735 { 736 SECStatus rv = SECFailure; 737 LDAPSSLSessionInfo *sseip; 738 CERTCertificate *cert; 739 SECCertUsage certUsage; 740 char *hostname = (char *)0; 741 742 if (!sessionarg || !socket) 743 return rv; 744 745 sseip = (LDAPSSLSessionInfo *)sessionarg; 746 747 if (LDAPSSL_AUTH_WEAK == sseip->lssei_ssl_strength ) { /* no check */ 748 return SECSuccess; 749 } 750 751 if ( isServer ) { 752 certUsage = certUsageSSLClient; 753 } else { 754 certUsage = certUsageSSLServer; 755 } 756 cert = SSL_PeerCertificate( fd ); 757 758 rv = CERT_VerifyCertNow(sseip->lssei_certdbh, cert, checkSig, 759 certUsage, NULL); 760 761 if ( rv != SECSuccess || isServer ) 762 return rv; 763 764 if ( LDAPSSL_AUTH_CNCHECK == sseip->lssei_ssl_strength ) 765 { 766 /* cert is OK. This is the client side of an SSL connection. 767 * Now check the name field in the cert against the desired hostname. 768 * NB: This is our only defense against Man-In-The-Middle (MITM) 769 * attacks! 770 */ 771 772 hostname = SSL_RevealURL( fd ); 773 774 if (hostname && hostname[0]) { 775 rv = CERT_VerifyCertName(cert, hostname); 776 } else { 777 rv = SECFailure; 778 } 779 if (hostname) 780 PORT_Free(hostname); 781 if (rv != SECSuccess) 782 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); 783 } 784 785 return((int)rv); 786 } 787 788 789 /* 790 * called during SSL client auth. when server wants our cert and key. 791 * return 0 if we succeeded and set *pRetCert and *pRetKey, -1 otherwise. 792 * if -1 is returned SSL will proceed without sending a cert. 793 */ 794 795 static int 796 get_clientauth_data( void *sessionarg, PRFileDesc *prfd, 797 CERTDistNames *caNames, CERTCertificate **pRetCert, 798 SECKEYPrivateKey **pRetKey ) 799 800 { 801 LDAPSSLSessionInfo *ssip; 802 803 if (( ssip = (LDAPSSLSessionInfo *)sessionarg ) == NULL ) { 804 return( -1 ); /* client auth. not enabled */ 805 } 806 807 return( get_keyandcert( ssip, pRetCert, pRetKey, NULL )); 808 } 809 810 static int 811 get_keyandcert( LDAPSSLSessionInfo *ssip, 812 CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey, 813 char **errmsgp ) 814 { 815 CERTCertificate *cert; 816 SECKEYPrivateKey *key; 817 818 if (( cert = PK11_FindCertFromNickname( ssip->lssei_certnickname, NULL )) 819 == NULL ) { 820 if ( errmsgp != NULL ) { 821 *errmsgp = dgettext(TEXT_DOMAIN, "unable to find certificate"); 822 } 823 return( -1 ); 824 } 825 826 { 827 PK11_SetPasswordFunc( get_keypassword ); 828 } 829 830 831 832 if (( key = PK11_FindKeyByAnyCert( cert, (void *)ssip )) == NULL ) { 833 CERT_DestroyCertificate( cert ); 834 if ( errmsgp != NULL ) { 835 *errmsgp = dgettext(TEXT_DOMAIN, "bad key or key password"); 836 } 837 return( -1 ); 838 } 839 840 *pRetCert = cert; 841 *pRetKey = key; 842 return( 0 ); 843 } 844 845 846 /* 847 * This function returns the password to NSS. 848 * This function is enable through PK11_SetPasswordFunc 849 * only if pkcs functions are not being used. 850 */ 851 852 static char * 853 get_keypassword( PK11SlotInfo *slot, PRBool retry, void *sessionarg ) 854 { 855 LDAPSSLSessionInfo *ssip; 856 857 if ( retry) 858 return (NULL); 859 860 ssip = (LDAPSSLSessionInfo *)sessionarg; 861 if ( NULL == ssip ) { 862 return( NULL ); 863 } 864 865 return( ssip->lssei_keypasswd ); 866 } 867 868 869 /* 870 * performs some basic checks on clientauth cert and key/password 871 * 872 * XXXmcs: could perform additional checks... see servers/slapd/ssl.c 873 * 1) check expiration 874 * 2) check that public key in cert matches private key 875 * see ns/netsite/ldap/servers/slapd/ssl.c:slapd_ssl_init() for example code. 876 */ 877 static int 878 check_clientauth_nicknames_and_passwd( LDAP *ld, LDAPSSLSessionInfo *ssip ) 879 { 880 char *errmsg = NULL; 881 CERTCertificate *cert = NULL; 882 SECKEYPrivateKey *key = NULL; 883 int rv; 884 885 rv = get_keyandcert( ssip, &cert, &key, &errmsg ); 886 887 if ( rv != 0 ) { 888 if ( errmsg != NULL ) { 889 errmsg = strdup( errmsg ); 890 } 891 ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, errmsg ); 892 return( -1 ); 893 } 894 895 if ( cert != NULL ) { 896 CERT_DestroyCertificate( cert ); 897 } 898 if ( key != NULL ) { 899 SECKEY_DestroyPrivateKey( key ); 900 } 901 return( 0 ); 902 } 903 904 905 #if 0 /* NOT_NEEDED_IN_LIBLDAP */ 906 /* there are patches and kludges. this is both. force some linkers to 907 * link this stuff in 908 */ 909 int stubs_o_stuff( void ) 910 { 911 PRExplodedTime exploded; 912 PLArenaPool pool; 913 914 const char *name ="t"; 915 PRUint32 size = 0, align = 0; 916 917 PR_ImplodeTime( &exploded ); 918 PL_InitArenaPool( &pool, name, size, align); 919 PR_Cleanup(); 920 PR_fprintf((PRFileDesc*)stderr, "Bad IDEA!!"); 921 922 return 0; 923 924 } 925 #endif /* NOT_NEEDED_IN_LIBLDAP */ 926 927 928 /* 929 * Import the file descriptor corresponding to the socket of an already 930 * open LDAP connection into SSL, and update the socket and session 931 * information accordingly. 932 */ 933 int ldapssl_import_fd ( LDAP *ld, int secure ) 934 { 935 PRLDAPSessionInfo sei; 936 PRLDAPSocketInfo soi; 937 LDAPSSLSocketInfo *ssoip = NULL; 938 LDAPSSLSessionInfo *sseip; 939 PRFileDesc *sslfd = NULL; 940 941 942 /* 943 * Retrieve session info. so we can store a pointer to our session info. 944 * in our socket info. later. 945 */ 946 memset( &sei, 0, sizeof(sei)); 947 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 948 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) { 949 return( -1 ); 950 } 951 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata; 952 953 954 /* 955 * Retrieve socket info. so we have the PRFileDesc. 956 */ 957 memset( &soi, 0, sizeof(soi)); 958 soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE; 959 if ( prldap_get_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) { 960 return( -1 ); 961 } 962 963 /* 964 * Allocate a structure to hold our socket-specific data. 965 */ 966 if ( NULL == ( ssoip = PR_Calloc( 1, sizeof( LDAPSSLSocketInfo )))) { 967 goto reset_socket_and_exit_with_error; 968 } 969 ssoip->soi_sessioninfo = sseip; 970 971 /* 972 * Add SSL layer and let the standard NSPR to LDAP layer and enable SSL. 973 */ 974 if (( sslfd = SSL_ImportFD( NULL, soi.soinfo_prfd )) == NULL ) { 975 goto reset_socket_and_exit_with_error; 976 } 977 978 if ( SSL_OptionSet( sslfd, SSL_SECURITY, secure ) != SECSuccess || 979 SSL_OptionSet( sslfd, SSL_HANDSHAKE_AS_CLIENT, secure ) 980 != SECSuccess || ( secure && SSL_ResetHandshake( sslfd, 981 PR_FALSE ) != SECSuccess )) { 982 goto reset_socket_and_exit_with_error; 983 } 984 985 /* 986 * Let the standard NSPR to LDAP layer know about the new socket and 987 * our own socket-specific data. 988 */ 989 soi.soinfo_prfd = sslfd; 990 soi.soinfo_appdata = (void *)ssoip; 991 if ( prldap_set_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) { 992 goto reset_socket_and_exit_with_error; 993 } 994 995 /* 996 * Install certificate hook function. 997 */ 998 if ( SSL_AuthCertificateHook( soi.soinfo_prfd, 999 (SSLAuthCertificate)ldapssl_AuthCertificate, 1000 (void *)CERT_GetDefaultCertDB()) != 0 ) { 1001 goto reset_socket_and_exit_with_error; 1002 } 1003 1004 if ( SSL_GetClientAuthDataHook( soi.soinfo_prfd, 1005 get_clientauth_data, sseip->lssei_certnickname ? sseip : NULL ) 1006 != 0 ) { 1007 goto reset_socket_and_exit_with_error; 1008 } 1009 1010 return 0; 1011 1012 reset_socket_and_exit_with_error: 1013 if ( NULL != sslfd ) { 1014 /* 1015 * "Unimport" the socket from SSL, i.e. get rid of the upper layer of 1016 * the file descriptor stack, which represents SSL. 1017 */ 1018 soi.soinfo_prfd = sslfd; 1019 sslfd = PR_PopIOLayer( soi.soinfo_prfd, PR_TOP_IO_LAYER ); 1020 sslfd->dtor( sslfd ); 1021 } 1022 if ( NULL != ssoip ) { 1023 ldapssl_free_socket_info( &ssoip ); 1024 soi.soinfo_appdata = NULL; 1025 } 1026 prldap_set_default_socket_info( ld, &soi ); 1027 1028 return( -1 ); 1029 } 1030 1031 1032 /* 1033 * Reset an LDAP session from SSL to a non-secure status. 1034 * Basically, this function undoes the work done by ldapssl_install_routines. 1035 */ 1036 int ldapssl_reset_to_nonsecure ( LDAP *ld ) 1037 { 1038 PRLDAPSessionInfo sei; 1039 LDAPSSLSessionInfo *sseip; 1040 1041 struct ldap_x_ext_io_fns iofns; 1042 int rc = 0; 1043 1044 /* 1045 * Retrieve session info. 1046 */ 1047 memset( &sei, 0, sizeof(sei)); 1048 sei.seinfo_size = PRLDAP_SESSIONINFO_SIZE; 1049 if ( prldap_get_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) { 1050 return( -1 ); 1051 } 1052 sseip = (LDAPSSLSessionInfo *)sei.seinfo_appdata; 1053 1054 if ( sseip != NULL ) { 1055 /* 1056 * Reset the standard extended io functions. 1057 */ 1058 memset( &iofns, 0, sizeof(iofns)); 1059 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; 1060 if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) 1061 < 0) { 1062 rc = -1; 1063 goto free_session_info; 1064 } 1065 1066 /* reset socket, connect, and ioctl */ 1067 iofns.lextiof_connect = sseip->lssei_std_functions.lssf_connect_fn; 1068 iofns.lextiof_close = sseip->lssei_std_functions.lssf_close_fn; 1069 iofns.lextiof_disposehandle = 1070 sseip->lssei_std_functions.lssf_disposehdl_fn; 1071 1072 if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) 1073 < 0) { 1074 rc = -1; 1075 goto free_session_info; 1076 } 1077 1078 free_session_info: 1079 ldapssl_free_session_info( &sseip ); 1080 sei.seinfo_appdata = NULL; 1081 if ( prldap_set_session_info( ld, NULL, &sei ) != LDAP_SUCCESS ) { 1082 rc = -1; 1083 } 1084 } /* if ( sseip && *sseip ) */ 1085 1086 if ( ldap_set_option( ld, LDAP_OPT_SSL, LDAP_OPT_OFF ) < 0 ) { 1087 return (-1); 1088 } 1089 1090 return rc; 1091 } 1092 1093 1094 #ifdef _SOLARIS_SDK 1095 static void 1096 _nss_initf_ipnodes(nss_db_params_t *p) 1097 { 1098 static char *no_service = ""; 1099 1100 p->name = NSS_DBNAM_IPNODES; 1101 p->flags |= NSS_USE_DEFAULT_CONFIG; 1102 p->default_config = host_service == NULL ? no_service : host_service; 1103 } 1104 1105 static void 1106 _nss_initf_hosts(nss_db_params_t *p) 1107 { 1108 static char *no_service = ""; 1109 1110 p->name = NSS_DBNAM_HOSTS; 1111 p->flags |= NSS_USE_DEFAULT_CONFIG; 1112 p->default_config = host_service == NULL ? no_service : host_service; 1113 } 1114 1115 static struct hostent * 1116 _switch_gethostbyaddr_r(const char *addr, int len, int type, 1117 struct hostent *result, char *buffer, int buflen, 1118 int *h_errnop) 1119 { 1120 nss_XbyY_args_t arg; 1121 nss_status_t res; 1122 int (*str2ent)(); 1123 void (*nss_initf)(); 1124 nss_db_root_t *nss_db_root; 1125 1126 if (AF_INET == type) { 1127 str2ent = str2hostent; 1128 nss_initf = _nss_initf_hosts; 1129 nss_db_root = &db_root_hosts; 1130 } else if (AF_INET6 == type) { 1131 str2ent = str2hostent6; 1132 nss_initf = _nss_initf_ipnodes; 1133 nss_db_root = &db_root_ipnodes; 1134 } else { 1135 return NULL; 1136 } 1137 1138 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent); 1139 1140 arg.key.hostaddr.addr = addr; 1141 arg.key.hostaddr.len = len; 1142 arg.key.hostaddr.type = type; 1143 arg.stayopen = 0; 1144 1145 res = nss_search(nss_db_root, nss_initf, 1146 NSS_DBOP_HOSTS_BYADDR, &arg); 1147 arg.status = res; 1148 *h_errnop = arg.h_errno; 1149 return (struct hostent *)NSS_XbyY_FINI(&arg); 1150 } 1151 1152 /* 1153 * ns_gethostbyaddr is used to be a substitute gethostbyaddr for 1154 * libldap when ssl will need to determine the fully qualified 1155 * host name from an address when it is unsafe to use the normal 1156 * nameservice functions. 1157 * 1158 * Note that the ldap name service resolver calls this with the address as 1159 * a character string - which we must convert into address form. 1160 */ 1161 1162 /*ARGSUSED*/ 1163 static LDAPHostEnt * 1164 ns_gethostbyaddr(const char *addr, int len, int type, 1165 LDAPHostEnt *result, char *buffer, int buflen, int *statusp, 1166 void *extradata) 1167 { 1168 LDAPHostEnt *ldap_hent; 1169 int h_errno; 1170 struct hostent h_ent; 1171 struct hostent *h_e = NULL; 1172 struct in_addr a; 1173 struct in6_addr a6; 1174 int inet_error; /* error returned by inet_pton */ 1175 1176 1177 if (addr == NULL || result == NULL || buffer == NULL || 1178 (type != AF_INET && type != AF_INET6)) 1179 return (NULL); 1180 1181 1182 (void) memset(&h_ent, 0, sizeof (h_ent)); 1183 1184 if (AF_INET == type) { 1185 if (inet_pton(type, addr, &a.s_addr) == 1) { 1186 h_e = _switch_gethostbyaddr_r((char *)&a, 1187 sizeof (a.s_addr), type, &h_ent, 1188 buffer, buflen, &h_errno); 1189 } 1190 } else if (AF_INET6 == type) { 1191 if (inet_pton(type, addr, &a6.s6_addr) == 1) { 1192 h_e = _switch_gethostbyaddr_r((char *)&a6, 1193 sizeof (a6.s6_addr), type, &h_ent, 1194 buffer, buflen, &h_errno); 1195 } 1196 } 1197 1198 if (h_e == NULL) { 1199 ldap_hent = NULL; 1200 } else { 1201 (void) memset(result, 0, sizeof (LDAPHostEnt)); 1202 ldap_hent = result; 1203 result->ldaphe_name = h_e->h_name; 1204 result->ldaphe_aliases = h_e->h_aliases; 1205 result->ldaphe_addrtype = h_e->h_addrtype; 1206 result->ldaphe_length = h_e->h_length; 1207 result->ldaphe_addr_list = h_e->h_addr_list; 1208 } 1209 return (ldap_hent); 1210 } 1211 1212 /* 1213 * ldapssl_install_gethostbyaddr attempts to prevent recursion in 1214 * gethostbyaddr calls when an ip address is given to ssl. This ip address 1215 * must be resolved to a host name. 1216 * 1217 * For example, libsldap cannot use LDAP to resolve this address to a 1218 * name because of recursion. The caller is instructing libldap to skip 1219 * the specified name service when resolving addresses for the specified 1220 * ldap connection. 1221 * 1222 * Currently only ldap and dns name services always return fully qualified 1223 * names. The other name services (files, nis, and nisplus) will returned 1224 * fully qualified names if the host names are stored as fully qualified names 1225 * in these name services. 1226 * 1227 * Note: 1228 * 1229 * Since host_service applies to all connections, calling 1230 * ldapssl_install_gethostbyaddr with different name services to 1231 * skip will lead to unpredictable results. 1232 * 1233 * Returns: 1234 * 0 if success 1235 * -1 if failure 1236 */ 1237 1238 int 1239 ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip) 1240 { 1241 enum __nsw_parse_err pserr; 1242 struct __nsw_switchconfig *conf; 1243 struct __nsw_lookup *lkp; 1244 struct ldap_dns_fns dns_fns; 1245 char *name_list = NULL; 1246 char *tmp; 1247 const char *name; 1248 int len; 1249 boolean_t got_skip = B_FALSE; 1250 1251 /* 1252 * db_root_hosts.lock mutex is used to ensure that the name list 1253 * is not in use by the name service switch while we are updating 1254 * the host_service 1255 */ 1256 1257 (void) mutex_lock(&db_root_hosts.lock); 1258 conf = __nsw_getconfig("hosts", &pserr); 1259 if (conf == NULL) { 1260 (void) mutex_unlock(&db_root_hosts.lock); 1261 return (0); 1262 } 1263 1264 /* check for ldap and count other backends */ 1265 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { 1266 name = lkp->service_name; 1267 if (strcmp(name, skip) == 0) { 1268 got_skip = B_TRUE; 1269 continue; 1270 } 1271 if (name_list == NULL) 1272 name_list = strdup(name); 1273 else { 1274 len = strlen(name_list); 1275 tmp = realloc(name_list, len + strlen(name) + 2); 1276 if (tmp == NULL) { 1277 free(name_list); 1278 name_list = NULL; 1279 } else { 1280 name_list = tmp; 1281 name_list[len++] = ' '; 1282 (void) strcpy(name_list+len, name); 1283 } 1284 } 1285 if (name_list == NULL) { /* alloc error */ 1286 (void) mutex_unlock(&db_root_hosts.lock); 1287 __nsw_freeconfig(conf); 1288 return (-1); 1289 } 1290 } 1291 __nsw_freeconfig(conf); 1292 if (!got_skip) { 1293 /* 1294 * Since skip name service not used for hosts, we do not need 1295 * to install our private address resolution function 1296 */ 1297 (void) mutex_unlock(&db_root_hosts.lock); 1298 if (name_list != NULL) 1299 free(name_list); 1300 return (0); 1301 } 1302 if (host_service != NULL) 1303 free(host_service); 1304 host_service = name_list; 1305 (void) mutex_unlock(&db_root_hosts.lock); 1306 1307 if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0) 1308 return (-1); 1309 dns_fns.lddnsfn_gethostbyaddr = ns_gethostbyaddr; 1310 if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0) 1311 return (-1); 1312 return (0); 1313 } 1314 #endif /* _SOLARIS_SDK */ 1315 #endif /* NET_SSL */ 1316