1 /*
   2  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 
   5 /*
   6  * The contents of this file are subject to the Netscape Public
   7  * License Version 1.1 (the "License"); you may not use this file
   8  * except in compliance with the License. You may obtain a copy of
   9  * the License at http://www.mozilla.org/NPL/
  10  *
  11  * Software distributed under the License is distributed on an "AS
  12  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  13  * implied. See the License for the specific language governing
  14  * rights and limitations under the License.
  15  *
  16  * The Original Code is Mozilla Communicator client code, released
  17  * March 31, 1998.
  18  *
  19  * The Initial Developer of the Original Code is Netscape
  20  * Communications Corporation. Portions created by Netscape are
  21  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
  22  * Rights Reserved.
  23  *
  24  * Contributor(s):
  25  */
  26 
  27 /*
  28  * clientinit.c
  29  */
  30 
  31 #if defined(NET_SSL)
  32 
  33 
  34 #if defined( _WINDOWS )
  35 #include <windows.h>
  36 #include "proto-ntutil.h"
  37 #endif
  38 
  39 #include <nspr.h>
  40 #include <plstr.h>
  41 #include <synch.h>
  42 #include <cert.h>
  43 #include <key.h>
  44 #include <ssl.h>
  45 #include <sslproto.h>
  46 #include <ldap.h>
  47 #include <ldappr.h>
  48 #include <solaris-int.h>
  49 
  50 
  51 #include <nss.h>
  52 
  53 /* XXX:mhein The following is a workaround for the redefinition of */
  54 /*           const problem on OSF.  Fix to be provided by NSS */
  55 /*           This is a pretty benign workaround for us which */
  56 /*           should not cause problems in the future even if */
  57 /*           we forget to take it out :-) */
  58 
  59 #ifdef OSF1V4D
  60 #ifndef __STDC__
  61 #  define __STDC__
  62 #endif /* __STDC__ */
  63 #endif /* OSF1V4D */
  64 
  65 #ifndef FILE_PATHSEP
  66 #define FILE_PATHSEP '/'
  67 #endif
  68 
  69 /*
  70  * StartTls()
  71  */
  72 
  73 #define START_TLS_OID "1.3.6.1.4.1.1466.20037"
  74 
  75 static PRStatus local_SSLPLCY_Install(void);
  76 
  77 /*
  78  * This little tricky guy keeps us from initializing twice 
  79  */
  80 static int              inited = 0;
  81 #ifdef _SOLARIS_SDK
  82 mutex_t                 inited_mutex = DEFAULTMUTEX;
  83 #else
  84 static mutex_t          inited_mutex = DEFAULTMUTEX;
  85 #endif  /* _SOLARIS_SDK */
  86 #if 0   /* UNNEEDED BY LIBLDAP */
  87 static char  tokDes[34] = "Internal (Software) Database     ";
  88 static char ptokDes[34] = "Internal (Software) Token        ";
  89 #endif  /* UNNEEDED BY LIBLDAP */
  90 
  91 
  92 /* IN:                                       */
  93 /* string:      /u/mhein/.netscape/mykey3.db */
  94 /* OUT:                                      */
  95 /* dir:         /u/mhein/.netscape/          */
  96 /* prefix:      my                           */
  97 /* key:         key3.db                      */
  98 
  99 static int
 100 splitpath(char *string, char *dir, char *prefix, char *key) {
 101         char *k;
 102         char *s;
 103         char *d = string;
 104         char *l;
 105         int  len = 0;
 106 
 107 
 108         if (string == NULL)
 109                 return (-1);
 110 
 111         /* goto the end of the string, and walk backwards until */
 112         /* you get to the first pathseparator */
 113         len = PL_strlen(string);
 114         l = string + len - 1;
 115         while (l != string && *l != '/' && *l != '\\')
 116                         l--;
 117         /* search for the .db */
 118         if ((k = PL_strstr(l, ".db")) != NULL) {
 119                 /* now we are sitting on . of .db */
 120 
 121                 /* move backward to the first 'c' or 'k' */
 122                 /* indicating cert or key */
 123                 while (k != l && *k != 'c' && *k != 'k')
 124                         k--;
 125 
 126                 /* move backwards to the first path separator */
 127                 if (k != d && k > d)
 128                         s = k - 1;
 129                 while (s != d && *s != '/' && *s != '\\')
 130                         s--;
 131 
 132                 /* if we are sitting on top of a path */
 133                 /* separator there is no prefix */
 134                 if (s + 1 == k) {
 135                         /* we know there is no prefix */
 136                         prefix = '\0';
 137                         PL_strcpy(key, k);
 138                         *k = '\0';
 139                         PL_strcpy(dir, d);
 140                 } else {
 141                         /* grab the prefix */
 142                         PL_strcpy(key, k);
 143                         *k = '\0';
 144                         PL_strcpy(prefix, ++s);
 145                         *s = '\0';
 146                         PL_strcpy(dir, d);
 147                 }
 148         } else {
 149                 /* neither *key[0-9].db nor *cert[0=9].db found */
 150                 return (-1);
 151         }
 152 
 153         return (0);
 154 }
 155 
 156 
 157 static PRStatus local_SSLPLCY_Install(void)
 158 {
 159         SECStatus s;
 160 
 161 #ifdef NS_DOMESTIC
 162         s = NSS_SetDomesticPolicy(); 
 163 #elif NS_EXPORT
 164         s = NSS_SetExportPolicy(); 
 165 #else
 166         s = PR_FAILURE;
 167 #endif
 168         return s?PR_FAILURE:PR_SUCCESS;
 169 }
 170 
 171 
 172 
 173 static void
 174 ldapssl_basic_init( void )
 175 {
 176 #ifndef _SOLARIS_SDK
 177         /*
 178          * NSPR is initialized in .init on SOLARIS
 179          */
 180     /* PR_Init() must to be called before everything else... */
 181     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
 182 #endif
 183 
 184     PR_SetConcurrency( 4 );     /* work around for NSPR 3.x I/O hangs */
 185 }
 186 
 187 
 188 
 189 /*
 190  * Cover  functions for malloc(), calloc(), strdup() and free() that are
 191  * compatible with the NSS libraries (they seem to use the C runtime
 192  * library malloc/free so these functions are quite simple right now).
 193  */
 194 static void *
 195 ldapssl_malloc( size_t size )
 196 {
 197     void        *p;
 198 
 199     p = malloc( size );
 200     return p;
 201 }
 202 
 203 
 204 static void *
 205 ldapssl_calloc( int nelem, size_t elsize )
 206 {
 207     void        *p;
 208 
 209     p = calloc( nelem, elsize );
 210     return p;
 211 }
 212 
 213 
 214 static char *
 215 ldapssl_strdup( const char *s )
 216 {
 217     char        *scopy;
 218 
 219     if ( NULL == s ) {
 220         scopy = NULL;
 221     } else {
 222         scopy = strdup( s );
 223     }
 224     return scopy;
 225 }
 226 
 227 
 228 static void
 229 ldapssl_free( void **pp )
 230 {
 231     if ( NULL != pp && NULL != *pp ) {
 232         free( (void *)*pp );
 233         *pp = NULL;
 234     }
 235 }
 236 
 237 
 238 #ifdef _SOLARIS_SDK
 239 /*
 240  * Disable strict fork detection of NSS library to allow safe fork of
 241  * consumers. Otherwise NSS will not work after fork because it was not
 242  * deinitialized before fork and there is no safe way how to do it after fork.
 243  *
 244  * Return values:
 245  *     1 - DISABLED was already set, no modification to environment
 246  *     0 - successfully modified environment, old value saved to enval if there
 247  *         was some
 248  *    -1 - setenv or strdup failed, the environment was left unchanged
 249  *
 250  */
 251 static int
 252 update_nss_strict_fork_env(char **enval)
 253 {
 254         char *temps = getenv("NSS_STRICT_NOFORK");
 255         if (temps == NULL) {
 256                 *enval = NULL;
 257         } else if (strncmp(temps, "DISABLED", 9) == 0) {
 258                 /* Do not need to set as DISABLED, it is already set. */
 259                 *enval = NULL;
 260                 return (1);
 261         } else {
 262                 if ((*enval = ldapssl_strdup(temps)) == NULL)
 263                         return (-1);
 264         }
 265         return (setenv("NSS_STRICT_NOFORK", "DISABLED", 1));
 266 }
 267 
 268 /*
 269  * Reset environment variable NSS_STRICT_NOFORK to value before
 270  * update_nss_strict_fork_env() call or remove it from environment if it did
 271  * not exist.
 272  * NSS_STRICT_NOFORK=DISABLED is needed only during NSS initialization to
 273  * disable activation of atfork handler in NSS which is invalidating
 274  * initialization in child process after fork.
 275  */
 276 static int
 277 reset_nss_strict_fork_env(char *enval)
 278 {
 279         if (enval != NULL) {
 280                 return (setenv("NSS_STRICT_NOFORK", enval, 1));
 281         } else {
 282                 return (unsetenv("NSS_STRICT_NOFORK"));
 283         }
 284 }
 285 #endif
 286 
 287 
 288 static char *
 289 buildDBName(const char *basename, const char *dbname)
 290 {
 291         char            *result;
 292         PRUint32        len, pathlen, addslash;
 293 
 294         if (basename)
 295         {
 296             if (( len = PL_strlen( basename )) > 3
 297                 && PL_strcasecmp( ".db", basename + len - 3 ) == 0 ) {
 298                 return (ldapssl_strdup(basename));
 299             }
 300             
 301             pathlen = len;
 302             len = pathlen + PL_strlen(dbname) + 1;
 303             addslash = ( pathlen > 0 &&
 304                 (( *(basename + pathlen - 1) != FILE_PATHSEP ) || 
 305                 ( *(basename + pathlen - 1) != '\\'  )));
 306 
 307             if ( addslash ) {
 308                 ++len;
 309             }
 310             if (( result = ldapssl_malloc( len )) != NULL ) {
 311                 PL_strcpy( result, basename );
 312                 if ( addslash ) {
 313                     *(result+pathlen) = FILE_PATHSEP;  /* replaces '\0' */
 314                     ++pathlen;
 315                 }
 316                 PL_strcpy(result+pathlen, dbname);
 317             }
 318             
 319         }
 320 
 321 
 322         return result;
 323 }
 324 
 325 char *
 326 GetCertDBName(void *alias, int dbVersion)
 327 {
 328     char                *source;
 329     char dbname[128];
 330     
 331     source = (char *)alias;
 332     
 333     if (!source)
 334     {
 335         source = "";
 336     }
 337     
 338     sprintf(dbname, "cert%d.db",dbVersion);
 339     return(buildDBName(source, dbname));
 340 
 341 
 342 }
 343 
 344 /*
 345  * return database name by appending "dbname" to "path".
 346  * this code doesn't need to be terribly efficient (not called often).
 347  */
 348 /* XXXceb this is the old function.  To be removed eventually */
 349 static char *
 350 GetDBName(const char *dbname, const char *path)
 351 {
 352     char                *result;
 353     PRUint32    len, pathlen;
 354     int         addslash;
 355     
 356     if ( dbname == NULL ) {
 357         dbname = "";
 358     }
 359     
 360     if ((path == NULL) || (*path == 0)) {
 361         result = ldapssl_strdup(dbname);
 362     } else {
 363         pathlen = PL_strlen(path);
 364         len = pathlen + PL_strlen(dbname) + 1;
 365         addslash = ( path[pathlen - 1] != '/' );
 366         if ( addslash ) {
 367             ++len;
 368         }
 369         if (( result = ldapssl_malloc( len )) != NULL ) {
 370             PL_strcpy( result, path );
 371             if ( addslash ) {
 372                 *(result+pathlen) = '/';  /* replaces '\0' */
 373                 ++pathlen;
 374             }
 375             PL_strcpy(result+pathlen, dbname);
 376         }
 377     }
 378     
 379     return result;
 380 }
 381 
 382 /*
 383  * Initialize ns/security so it can be used for SSL client authentication.
 384  * It is safe to call this more than once.
 385  *
 386  * If needkeydb == 0, no key database is opened and SSL server authentication
 387  * is supported but not client authentication.
 388  *
 389  * If "certdbpath" is NULL or "", the default cert. db is used (typically
 390  * ~/.netscape/cert7.db).
 391  *
 392  * If "certdbpath" ends with ".db" (case-insensitive compare), then
 393  * it is assumed to be a full path to the cert. db file; otherwise,
 394  * it is assumed to be a directory that contains a file called
 395  * "cert7.db" or "cert.db".
 396  *
 397  * If certdbhandle is non-NULL, it is assumed to be a pointer to a
 398  * SECCertDBHandle structure.  It is fine to pass NULL since this
 399  * routine will allocate one for you (CERT_GetDefaultDB() can be
 400  * used to retrieve the cert db handle).
 401  *
 402  * If "keydbpath" is NULL or "", the default key db is used (typically
 403  * ~/.netscape/key3.db).
 404  *
 405  * If "keydbpath" ends with ".db" (case-insensitive compare), then
 406  * it is assumed to be a full path to the key db file; otherwise,
 407  * it is assumed to be a directory that contains a file called
 408  * "key3.db" 
 409  *
 410  * If certdbhandle is non-NULL< it is assumed to be a pointed to a
 411  * SECKEYKeyDBHandle structure.  It is fine to pass NULL since this
 412  * routine will allocate one for you (SECKEY_GetDefaultDB() can be
 413  * used to retrieve the cert db handle).
 414  */
 415 int
 416 LDAP_CALL
 417 ldapssl_clientauth_init( const char *certdbpath, void *certdbhandle, 
 418     const int needkeydb, const char *keydbpath, void *keydbhandle )
 419 
 420 {
 421     int rc;
 422 #ifdef _SOLARIS_SDK
 423     char *enval;
 424     int rcenv = 0;
 425 #endif
 426      
 427     /*
 428      *     LDAPDebug(LDAP_DEBUG_TRACE, "ldapssl_clientauth_init\n",0 ,0 ,0);
 429      */
 430 
 431     mutex_lock(&inited_mutex);
 432     if ( inited ) {
 433         mutex_unlock(&inited_mutex);
 434         return( 0 );
 435     }
 436 
 437     ldapssl_basic_init();
 438 
 439 #ifdef _SOLARIS_SDK
 440     if ((rcenv = update_nss_strict_fork_env(&enval)) == -1) {
 441         mutex_unlock(&inited_mutex);
 442         return (-1);
 443     }
 444 #endif
 445 
 446     /* Open the certificate database */
 447     rc = NSS_Init(certdbpath);
 448 #ifdef _SOLARIS_SDK
 449     /* Error from NSS_Init() more important! */
 450     if ((rcenv != 1) && (reset_nss_strict_fork_env(enval) != 0) && (rc == 0)) {
 451         ldapssl_free(&enval);
 452         mutex_unlock(&inited_mutex);
 453         return (-1);
 454     }
 455     ldapssl_free(&enval);
 456 #endif
 457     if (rc != 0) {
 458         if ((rc = PR_GetError()) >= 0)
 459             rc = -1;
 460         mutex_unlock(&inited_mutex);
 461         return (rc);
 462     }
 463 
 464     if (SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE)
 465             || SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE)) {
 466         if (( rc = PR_GetError()) >= 0 ) {
 467             rc = -1;
 468         }
 469         mutex_unlock(&inited_mutex);
 470         return( rc );
 471     }
 472 
 473 
 474 
 475 #if defined(NS_DOMESTIC)
 476     if (local_SSLPLCY_Install() == PR_FAILURE) {
 477       mutex_unlock(&inited_mutex);
 478       return( -1 );
 479     }
 480 #elif(NS_EXPORT)
 481     if (local_SSLPLCY_Install() == PR_FAILURE) {
 482       mutex_unlock(&inited_mutex);
 483       return( -1 );
 484     }
 485 #else
 486     mutex_unlock(&inited_mutex);
 487     return( -1 );
 488 #endif
 489 
 490     inited = 1;
 491     mutex_unlock(&inited_mutex);
 492 
 493     return( 0 );
 494 
 495 }
 496 
 497 /*
 498  * Initialize ns/security so it can be used for SSL client authentication.
 499  * It is safe to call this more than once.
 500  *
 501  * If needkeydb == 0, no key database is opened and SSL server authentication
 502  * is supported but not client authentication.
 503  *
 504  * If "certdbpath" is NULL or "", the default cert. db is used (typically
 505  * ~/.netscape/cert7.db).
 506  *
 507  * If "certdbpath" ends with ".db" (case-insensitive compare), then
 508  * it is assumed to be a full path to the cert. db file; otherwise,
 509  * it is assumed to be a directory that contains a file called
 510  * "cert7.db" or "cert.db".
 511  *
 512  * If certdbhandle is non-NULL, it is assumed to be a pointer to a
 513  * SECCertDBHandle structure.  It is fine to pass NULL since this
 514  * routine will allocate one for you (CERT_GetDefaultDB() can be
 515  * used to retrieve the cert db handle).
 516  *
 517  * If "keydbpath" is NULL or "", the default key db is used (typically
 518  * ~/.netscape/key3.db).
 519  *
 520  * If "keydbpath" ends with ".db" (case-insensitive compare), then
 521  * it is assumed to be a full path to the key db file; otherwise,
 522  * it is assumed to be a directory that contains a file called
 523  * "key3.db" 
 524  *
 525  * If certdbhandle is non-NULL< it is assumed to be a pointed to a
 526  * SECKEYKeyDBHandle structure.  It is fine to pass NULL since this
 527  * routine will allocate one for you (SECKEY_GetDefaultDB() can be
 528  * used to retrieve the cert db handle).  */
 529 int
 530 LDAP_CALL
 531 ldapssl_advclientauth_init( 
 532     const char *certdbpath, void *certdbhandle, 
 533     const int needkeydb, const char *keydbpath, void *keydbhandle,  
 534     const int needsecmoddb, const char *secmoddbpath,
 535     const int sslstrength )
 536 {
 537     int rc;
 538 #ifdef _SOLARIS_SDK
 539     char *enval;
 540     int rcenv = 0;
 541 #endif
 542 
 543     mutex_lock(&inited_mutex);
 544     if ( inited ) {
 545         mutex_unlock(&inited_mutex);
 546         return( 0 );
 547     }
 548 
 549     /*
 550      *    LDAPDebug(LDAP_DEBUG_TRACE, "ldapssl_advclientauth_init\n",0 ,0 ,0);
 551      */
 552 
 553     ldapssl_basic_init();
 554 
 555 #ifdef _SOLARIS_SDK
 556     if ((rcenv = update_nss_strict_fork_env(&enval)) == -1) {
 557         mutex_unlock(&inited_mutex);
 558         return (-1);
 559     }
 560 #endif
 561 
 562     rc = NSS_Init(certdbpath);
 563 #ifdef _SOLARIS_SDK
 564     /* Error from NSS_Init() more important! */
 565     if ((rcenv != 1) && (reset_nss_strict_fork_env(enval) != 0) && (rc == 0)) {
 566         ldapssl_free(&enval);
 567         mutex_unlock(&inited_mutex);
 568         return (-1);
 569     }
 570     ldapssl_free(&enval);
 571 #endif
 572     if (rc != 0) {
 573         if ((rc = PR_GetError()) >= 0)
 574             rc = -1;
 575         mutex_unlock(&inited_mutex);
 576         return (rc);
 577     }
 578 
 579 #if defined(NS_DOMESTIC)
 580     if (local_SSLPLCY_Install() == PR_FAILURE) {
 581       mutex_unlock(&inited_mutex);
 582       return( -1 );
 583     }
 584 #elif(NS_EXPORT)
 585     if (local_SSLPLCY_Install() == PR_FAILURE) {
 586       mutex_unlock(&inited_mutex);
 587       return( -1 );
 588     }
 589 #else
 590     mutex_unlock(&inited_mutex);
 591     return( -1 );
 592 #endif
 593 
 594     inited = 1;
 595     mutex_unlock(&inited_mutex);
 596 
 597     return( ldapssl_set_strength( NULL, sslstrength));
 598 
 599 }
 600 
 601 
 602 /*
 603  * Initialize ns/security so it can be used for SSL client authentication.
 604  * It is safe to call this more than once.
 605   */
 606 
 607 /* 
 608  * XXXceb  This is a hack until the new IO functions are done.
 609  * this function lives in ldapsinit.c
 610  */
 611 void set_using_pkcs_functions( int val );
 612 
 613 int
 614 LDAP_CALL
 615 ldapssl_pkcs_init( const struct ldapssl_pkcs_fns *pfns )
 616 {
 617 
 618     char                *certdbName, *s, *keydbpath;
 619     char                *certdbPrefix, *keydbPrefix;
 620     char                *confDir, *keydbName;
 621     static char         *secmodname =  "secmod.db";
 622     int                 rc;
 623 #ifdef _SOLARIS_SDK
 624     char *enval;
 625     int rcenv = 0;
 626 #endif
 627     
 628     mutex_lock(&inited_mutex);
 629     if ( inited ) {
 630         mutex_unlock(&inited_mutex);
 631         return( 0 );
 632     }
 633 /* 
 634  * XXXceb  This is a hack until the new IO functions are done.
 635  * this function MUST be called before ldap_enable_clienauth.
 636  * 
 637  */
 638     set_using_pkcs_functions( 1 );
 639     
 640     /*
 641      *    LDAPDebug(LDAP_DEBUG_TRACE, "ldapssl_pkcs_init\n",0 ,0 ,0);
 642      */
 643 
 644 
 645     ldapssl_basic_init();
 646 
 647     pfns->pkcs_getcertpath( NULL, &s);
 648     confDir = ldapssl_strdup( s );
 649     certdbPrefix = ldapssl_strdup( s );
 650     certdbName = ldapssl_strdup( s );
 651     *certdbPrefix = 0;
 652     splitpath(s, confDir, certdbPrefix, certdbName);
 653 
 654     pfns->pkcs_getkeypath( NULL, &s);
 655     keydbpath = ldapssl_strdup( s );
 656     keydbPrefix = ldapssl_strdup( s );
 657     keydbName = ldapssl_strdup( s );
 658     *keydbPrefix = 0;
 659     splitpath(s, keydbpath, keydbPrefix, keydbName);
 660 
 661 
 662     /* verify confDir == keydbpath and adjust as necessary */
 663     ldapssl_free((void **)&certdbName);
 664     ldapssl_free((void **)&keydbName);
 665     ldapssl_free((void **)&keydbpath);
 666 
 667 #ifdef _SOLARIS_SDK
 668     if ((rcenv = update_nss_strict_fork_env(&enval)) == -1) {
 669         mutex_unlock(&inited_mutex);
 670         return (-1);
 671     }
 672 #endif
 673 
 674     rc = NSS_Initialize(confDir,certdbPrefix,keydbPrefix,secmodname,
 675                 NSS_INIT_READONLY);
 676 
 677     ldapssl_free((void **)&certdbPrefix);
 678     ldapssl_free((void **)&keydbPrefix);
 679     ldapssl_free((void **)&confDir);
 680 
 681 #ifdef _SOLARIS_SDK
 682     /* Error from NSS_Initialize() more important! */
 683     if ((rcenv != 1) && (reset_nss_strict_fork_env(enval) != 0) && (rc == 0)) {
 684         ldapssl_free(&enval);
 685         mutex_unlock(&inited_mutex);
 686         return (-1);
 687     }
 688     ldapssl_free(&enval);
 689 #endif
 690     
 691     if (rc != 0) {
 692         if ((rc = PR_GetError()) >= 0)
 693             rc = -1;
 694         mutex_unlock(&inited_mutex);
 695         return (rc);
 696     }
 697 
 698 
 699 #if 0   /* UNNEEDED BY LIBLDAP */
 700     /* this is odd */
 701     PK11_ConfigurePKCS11(NULL, NULL, tokDes, ptokDes, NULL, NULL, NULL, NULL, 0, 0 );
 702 #endif  /* UNNEEDED BY LIBLDAP */
 703 
 704     if (SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE)
 705         || SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE)) {
 706         if (( rc = PR_GetError()) >= 0 ) {
 707             rc = -1;
 708         }
 709         
 710         mutex_unlock(&inited_mutex);
 711         return( rc );
 712     }
 713     
 714 #if defined(NS_DOMESTIC)
 715     if (local_SSLPLCY_Install() == PR_FAILURE) {
 716       mutex_unlock(&inited_mutex);
 717       return( -1 );
 718     }
 719 #elif(NS_EXPORT)
 720     if (local_SSLPLCY_Install() == PR_FAILURE) {
 721       mutex_unlock(&inited_mutex);
 722       return( -1 );
 723     }
 724 #else
 725     mutex_unlock(&inited_mutex);
 726     return( -1 );
 727 #endif
 728 
 729     inited = 1;
 730 
 731     if ( certdbName != NULL ) {
 732         ldapssl_free((void **) &certdbName );
 733     }
 734     
 735     return( ldapssl_set_strength( NULL, LDAPSSL_AUTH_CNCHECK));
 736 }
 737 
 738 
 739 /*
 740  * ldapssl_client_init() is a server-authentication only version of
 741  * ldapssl_clientauth_init().
 742  */
 743 int
 744 LDAP_CALL
 745 ldapssl_client_init(const char* certdbpath, void *certdbhandle )
 746 {
 747     return( ldapssl_clientauth_init( certdbpath, certdbhandle,
 748             0, NULL, NULL ));
 749 }
 750 /*
 751  * ldapssl_serverauth_init() is a server-authentication only version of
 752  * ldapssl_clientauth_init().  This function allows the sslstrength
 753  * to be passed in.  The sslstrength can take one of the following
 754  * values:
 755  *      LDAPSSL_AUTH_WEAK: indicate that you accept the server's
 756  *                         certificate without checking the CA who
 757  *                         issued the certificate
 758  *      LDAPSSL_AUTH_CERT: indicates that you accept the server's
 759  *                         certificate only if you trust the CA who
 760  *                         issued the certificate
 761  *      LDAPSSL_AUTH_CNCHECK:
 762                            indicates that you accept the server's
 763  *                         certificate only if you trust the CA who
 764  *                         issued the certificate and if the value
 765  *                         of the cn attribute in the DNS hostname
 766  *                         of the server
 767  */
 768 int
 769 LDAP_CALL
 770 ldapssl_serverauth_init(const char* certdbpath,
 771                      void *certdbhandle,
 772                      const int sslstrength )
 773 {
 774     if ( ldapssl_set_strength( NULL, sslstrength ) != 0) {
 775         return ( -1 );
 776     }
 777 
 778     return( ldapssl_clientauth_init( certdbpath, certdbhandle,
 779             0, NULL, NULL ));
 780 }
 781 
 782 /*
 783  * Function that makes an asynchronous Start TLS extended operation request.
 784  */
 785 static int ldapssl_tls_start(LDAP *ld, int *msgidp)
 786 {
 787     int version, rc;
 788     BerValue extreq_data;
 789 
 790     /* Start TLS extended operation requires an absent "requestValue" field. */
 791 
 792     extreq_data.bv_val = NULL;
 793     extreq_data.bv_len = 0;
 794 
 795     /* Make sure version is set to LDAPv3 for extended operations to be
 796        supported. */
 797 
 798     version = LDAP_VERSION3;
 799     ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
 800 
 801     /* Send the Start TLS request (OID: 1.3.6.1.4.1.1466.20037) */
 802     rc = ldap_extended_operation( ld, START_TLS_OID, &extreq_data,
 803               NULL, NULL, msgidp );
 804    
 805     return rc;
 806 }
 807 
 808 
 809 /*
 810  * Function that enables SSL on an already open non-secured LDAP connection.
 811  * (i.e. the connection is henceforth secured)
 812  */
 813 static int ldapssl_enableSSL_on_open_connection(LDAP *ld, int defsecure,
 814         char *certdbpath, char *keydbpath)
 815 {
 816     PRLDAPSocketInfo  soi;
 817 
 818 
 819     if ( ldapssl_clientauth_init( certdbpath, NULL, 1, keydbpath, NULL ) < 0 ) {
 820         goto ssl_setup_failure;
 821     }
 822    
 823     /*
 824      * Retrieve socket info. so we have the PRFileDesc.
 825      */
 826     memset( &soi, 0, sizeof(soi));
 827     soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
 828     if ( prldap_get_default_socket_info( ld, &soi ) < 0 ) {
 829         goto ssl_setup_failure;
 830     }
 831 
 832     if ( ldapssl_install_routines( ld ) < 0 ) {
 833         goto ssl_setup_failure;
 834     }
 835 
 836 
 837     if (soi.soinfo_prfd == NULL) {
 838         int sd;
 839         ldap_get_option( ld, LDAP_OPT_DESC, &sd );
 840         soi.soinfo_prfd = (PRFileDesc *) PR_ImportTCPSocket( sd );
 841     }
 842     /* set the socket information back into the connection handle,
 843      * because ldapssl_install_routines() resets the socket_arg info in the
 844      * socket buffer. */
 845     if ( prldap_set_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
 846       goto ssl_setup_failure;
 847     }
 848 
 849     if ( ldap_set_option( ld, LDAP_OPT_SSL,
 850         defsecure ? LDAP_OPT_ON : LDAP_OPT_OFF ) < 0 ) {
 851         goto ssl_setup_failure;
 852     }
 853   
 854     if ( ldapssl_import_fd( ld, defsecure ) < 0 ) {
 855         goto ssl_setup_failure;
 856     }
 857 
 858     return 0;
 859 
 860 ssl_setup_failure:
 861     ldapssl_reset_to_nonsecure( ld );
 862 
 863     /* we should here warn the server that we switch back to a non-secure
 864        connection */
 865 
 866     return( -1 );
 867 }
 868 
 869 
 870 /*
 871  * ldapssl_tls_start_s() performs a synchronous Start TLS extended operation
 872  * request.
 873  *
 874  * The function returns the result code of the extended operation response
 875  * sent by the server.
 876  *
 877  * In case of a successfull response (LDAP_SUCCESS returned), by the time
 878  * this function returns the LDAP session designed by ld will have been
 879  * secured, i.e. the connection will have been imported into SSL.
 880  *
 881  * Should the Start TLS request be rejected by the server, the result code
 882  * returned will be one of the following:
 883  *    LDAP_OPERATIONS_ERROR,
 884  *    LDAP_PROTOCOL_ERROR,
 885  *    LDAP_REFERRAL,
 886  *    LDAP_UNAVAILABLE.
 887  *
 888  * Any other error code returned will be due to a failure in the course
 889  * of operations done on the client side.
 890  *
 891  * "certdbpath" and "keydbpath" should contain the path to the client's
 892  * certificate and key databases respectively. Either the path to the
 893  * directory containing "default name" databases (i.e. cert7.db and key3.db)
 894  * can be specified or the actual filenames can be included.
 895  * If any of these parameters is NULL, the function will assume the database
 896  * is the same used by Netscape Communicator, which is usually under
 897  * ~/.netsca /)
 898  *
 899  * "referralsp" is a pointer to a list of referrals the server might
 900  * eventually send back with an LDAP_REFERRAL result code.
 901  *
 902  */
 903 
 904 int
 905 LDAP_CALL
 906 ldapssl_tls_start_s(LDAP *ld,int defsecure, char *certdbpath, char *keydbpath,
 907         char ***referralsp)
 908 {
 909     int             rc, resultCode, msgid;
 910     char            *extresp_oid;
 911     BerValue        *extresp_data;
 912     LDAPMessage     *res;
 913 
 914     rc = ldapssl_tls_start( ld, &msgid );
 915     if ( rc != LDAP_SUCCESS ) {
 916          return rc;
 917     }
 918 
 919     rc = ldap_result( ld, msgid, 1, (struct timeval *) NULL, &res );
 920     if ( rc != LDAP_RES_EXTENDED ) {
 921 
 922       /* the first response received must be an extended response to an
 923        Start TLS request */
 924 
 925          ldap_msgfree( res );
 926          return( -1 );
 927 
 928     }
 929 
 930     rc = ldap_parse_extended_result( ld, res, &extresp_oid, &extresp_data, 0 );
 931 
 932     if ( rc != LDAP_SUCCESS ) {
 933          ldap_msgfree( res );
 934          return rc;
 935     }
 936 
 937     if ( strcasecmp( extresp_oid, START_TLS_OID ) != 0 ) {
 938 
 939          /* the extended response received doesn't correspond to the
 940           Start TLS request */
 941 
 942          ldap_msgfree( res );
 943          return -1;
 944     }
 945 
 946     resultCode = ldap_get_lderrno( ld, NULL, NULL );
 947 
 948     /* Analyze the server's response */
 949     switch (resultCode) {
 950     case LDAP_REFERRAL:
 951       {
 952       rc = ldap_parse_result( ld, res, NULL, NULL, NULL, referralsp, NULL, 0 );
 953       if ( rc != LDAP_SUCCESS ) {
 954           ldap_msgfree( res );
 955           return rc;
 956       }
 957     }
 958     case LDAP_OPERATIONS_ERROR:
 959 
 960     case LDAP_PROTOCOL_ERROR:
 961 
 962     case LDAP_UNAVAILABLE:
 963         goto free_msg_and_return;
 964     case LDAP_SUCCESS:
 965       {
 966       /*
 967        * If extended response successfull, get connection ready for
 968        * communicating with the server over SSL/TLS.
 969        */
 970 
 971       if ( ldapssl_enableSSL_on_open_connection( ld, defsecure,
 972                                          certdbpath, keydbpath ) < 0 ) {
 973           resultCode = -1;
 974       }
 975 
 976     } /* case LDAP_SUCCESS */
 977     default:
 978         goto free_msg_and_return;
 979     } /* switch */
 980 
 981 free_msg_and_return:
 982     ldap_msgfree( res );
 983     return resultCode;
 984 }
 985 
 986 #endif /* NET_SSL */