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         return NSS_SetDomesticPolicy() ? PR_FAILURE : PR_SUCCESS;
 160 }
 161 
 162 
 163 
 164 static void
 165 ldapssl_basic_init( void )
 166 {
 167 #ifndef _SOLARIS_SDK
 168         /*
 169          * NSPR is initialized in .init on SOLARIS
 170          */
 171     /* PR_Init() must to be called before everything else... */
 172     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
 173 #endif
 174 
 175     PR_SetConcurrency( 4 );     /* work around for NSPR 3.x I/O hangs */
 176 }
 177 
 178 
 179 
 180 /*
 181  * Cover  functions for malloc(), calloc(), strdup() and free() that are
 182  * compatible with the NSS libraries (they seem to use the C runtime
 183  * library malloc/free so these functions are quite simple right now).
 184  */
 185 static void *
 186 ldapssl_malloc( size_t size )
 187 {
 188     void        *p;
 189 
 190     p = malloc( size );
 191     return p;
 192 }
 193 
 194 
 195 static void *
 196 ldapssl_calloc( int nelem, size_t elsize )
 197 {
 198     void        *p;
 199 
 200     p = calloc( nelem, elsize );
 201     return p;
 202 }
 203 
 204 
 205 static char *
 206 ldapssl_strdup( const char *s )
 207 {
 208     char        *scopy;
 209 
 210     if ( NULL == s ) {
 211         scopy = NULL;
 212     } else {
 213         scopy = strdup( s );
 214     }
 215     return scopy;
 216 }
 217 
 218 
 219 static void
 220 ldapssl_free( void **pp )
 221 {
 222     if ( NULL != pp && NULL != *pp ) {
 223         free( (void *)*pp );
 224         *pp = NULL;
 225     }
 226 }
 227 
 228 
 229 #ifdef _SOLARIS_SDK
 230 /*
 231  * Disable strict fork detection of NSS library to allow safe fork of
 232  * consumers. Otherwise NSS will not work after fork because it was not
 233  * deinitialized before fork and there is no safe way how to do it after fork.
 234  *
 235  * Return values:
 236  *     1 - DISABLED was already set, no modification to environment
 237  *     0 - successfully modified environment, old value saved to enval if there
 238  *         was some
 239  *    -1 - setenv or strdup failed, the environment was left unchanged
 240  *
 241  */
 242 static int
 243 update_nss_strict_fork_env(char **enval)
 244 {
 245         char *temps = getenv("NSS_STRICT_NOFORK");
 246         if (temps == NULL) {
 247                 *enval = NULL;
 248         } else if (strncmp(temps, "DISABLED", 9) == 0) {
 249                 /* Do not need to set as DISABLED, it is already set. */
 250                 *enval = NULL;
 251                 return (1);
 252         } else {
 253                 if ((*enval = ldapssl_strdup(temps)) == NULL)
 254                         return (-1);
 255         }
 256         return (setenv("NSS_STRICT_NOFORK", "DISABLED", 1));
 257 }
 258 
 259 /*
 260  * Reset environment variable NSS_STRICT_NOFORK to value before
 261  * update_nss_strict_fork_env() call or remove it from environment if it did
 262  * not exist.
 263  * NSS_STRICT_NOFORK=DISABLED is needed only during NSS initialization to
 264  * disable activation of atfork handler in NSS which is invalidating
 265  * initialization in child process after fork.
 266  */
 267 static int
 268 reset_nss_strict_fork_env(char *enval)
 269 {
 270         if (enval != NULL) {
 271                 return (setenv("NSS_STRICT_NOFORK", enval, 1));
 272         } else {
 273                 return (unsetenv("NSS_STRICT_NOFORK"));
 274         }
 275 }
 276 #endif
 277 
 278 
 279 static char *
 280 buildDBName(const char *basename, const char *dbname)
 281 {
 282         char            *result;
 283         PRUint32        len, pathlen, addslash;
 284 
 285         if (basename)
 286         {
 287             if (( len = PL_strlen( basename )) > 3
 288                 && PL_strcasecmp( ".db", basename + len - 3 ) == 0 ) {
 289                 return (ldapssl_strdup(basename));
 290             }
 291             
 292             pathlen = len;
 293             len = pathlen + PL_strlen(dbname) + 1;
 294             addslash = ( pathlen > 0 &&
 295                 (( *(basename + pathlen - 1) != FILE_PATHSEP ) || 
 296                 ( *(basename + pathlen - 1) != '\\'  )));
 297 
 298             if ( addslash ) {
 299                 ++len;
 300             }
 301             if (( result = ldapssl_malloc( len )) != NULL ) {
 302                 PL_strcpy( result, basename );
 303                 if ( addslash ) {
 304                     *(result+pathlen) = FILE_PATHSEP;  /* replaces '\0' */
 305                     ++pathlen;
 306                 }
 307                 PL_strcpy(result+pathlen, dbname);
 308             }
 309             
 310         }
 311 
 312 
 313         return result;
 314 }
 315 
 316 char *
 317 GetCertDBName(void *alias, int dbVersion)
 318 {
 319     char                *source;
 320     char dbname[128];
 321     
 322     source = (char *)alias;
 323     
 324     if (!source)
 325     {
 326         source = "";
 327     }
 328     
 329     sprintf(dbname, "cert%d.db",dbVersion);
 330     return(buildDBName(source, dbname));
 331 
 332 
 333 }
 334 
 335 /*
 336  * return database name by appending "dbname" to "path".
 337  * this code doesn't need to be terribly efficient (not called often).
 338  */
 339 /* XXXceb this is the old function.  To be removed eventually */
 340 static char *
 341 GetDBName(const char *dbname, const char *path)
 342 {
 343     char                *result;
 344     PRUint32    len, pathlen;
 345     int         addslash;
 346     
 347     if ( dbname == NULL ) {
 348         dbname = "";
 349     }
 350     
 351     if ((path == NULL) || (*path == 0)) {
 352         result = ldapssl_strdup(dbname);
 353     } else {
 354         pathlen = PL_strlen(path);
 355         len = pathlen + PL_strlen(dbname) + 1;
 356         addslash = ( path[pathlen - 1] != '/' );
 357         if ( addslash ) {
 358             ++len;
 359         }
 360         if (( result = ldapssl_malloc( len )) != NULL ) {
 361             PL_strcpy( result, path );
 362             if ( addslash ) {
 363                 *(result+pathlen) = '/';  /* replaces '\0' */
 364                 ++pathlen;
 365             }
 366             PL_strcpy(result+pathlen, dbname);
 367         }
 368     }
 369     
 370     return result;
 371 }
 372 
 373 /*
 374  * Initialize ns/security so it can be used for SSL client authentication.
 375  * It is safe to call this more than once.
 376  *
 377  * If needkeydb == 0, no key database is opened and SSL server authentication
 378  * is supported but not client authentication.
 379  *
 380  * If "certdbpath" is NULL or "", the default cert. db is used (typically
 381  * ~/.netscape/cert7.db).
 382  *
 383  * If "certdbpath" ends with ".db" (case-insensitive compare), then
 384  * it is assumed to be a full path to the cert. db file; otherwise,
 385  * it is assumed to be a directory that contains a file called
 386  * "cert7.db" or "cert.db".
 387  *
 388  * If certdbhandle is non-NULL, it is assumed to be a pointer to a
 389  * SECCertDBHandle structure.  It is fine to pass NULL since this
 390  * routine will allocate one for you (CERT_GetDefaultDB() can be
 391  * used to retrieve the cert db handle).
 392  *
 393  * If "keydbpath" is NULL or "", the default key db is used (typically
 394  * ~/.netscape/key3.db).
 395  *
 396  * If "keydbpath" ends with ".db" (case-insensitive compare), then
 397  * it is assumed to be a full path to the key db file; otherwise,
 398  * it is assumed to be a directory that contains a file called
 399  * "key3.db" 
 400  *
 401  * If certdbhandle is non-NULL< it is assumed to be a pointed to a
 402  * SECKEYKeyDBHandle structure.  It is fine to pass NULL since this
 403  * routine will allocate one for you (SECKEY_GetDefaultDB() can be
 404  * used to retrieve the cert db handle).
 405  */
 406 int
 407 LDAP_CALL
 408 ldapssl_clientauth_init( const char *certdbpath, void *certdbhandle, 
 409     const int needkeydb, const char *keydbpath, void *keydbhandle )
 410 
 411 {
 412     int rc;
 413 #ifdef _SOLARIS_SDK
 414     char *enval;
 415     int rcenv = 0;
 416 #endif
 417      
 418     /*
 419      *     LDAPDebug(LDAP_DEBUG_TRACE, "ldapssl_clientauth_init\n",0 ,0 ,0);
 420      */
 421 
 422     mutex_lock(&inited_mutex);
 423     if ( inited ) {
 424         mutex_unlock(&inited_mutex);
 425         return( 0 );
 426     }
 427 
 428     ldapssl_basic_init();
 429 
 430 #ifdef _SOLARIS_SDK
 431     if ((rcenv = update_nss_strict_fork_env(&enval)) == -1) {
 432         mutex_unlock(&inited_mutex);
 433         return (-1);
 434     }
 435 #endif
 436 
 437     /* Open the certificate database */
 438     rc = NSS_Init(certdbpath);
 439 #ifdef _SOLARIS_SDK
 440     /* Error from NSS_Init() more important! */
 441     if ((rcenv != 1) && (reset_nss_strict_fork_env(enval) != 0) && (rc == 0)) {
 442         ldapssl_free(&enval);
 443         mutex_unlock(&inited_mutex);
 444         return (-1);
 445     }
 446     ldapssl_free(&enval);
 447 #endif
 448     if (rc != 0) {
 449         if ((rc = PR_GetError()) >= 0)
 450             rc = -1;
 451         mutex_unlock(&inited_mutex);
 452         return (rc);
 453     }
 454 
 455     if (SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE)
 456             || SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE)) {
 457         if (( rc = PR_GetError()) >= 0 ) {
 458             rc = -1;
 459         }
 460         mutex_unlock(&inited_mutex);
 461         return( rc );
 462     }
 463 
 464 
 465 
 466     if (local_SSLPLCY_Install() == PR_FAILURE) {
 467       mutex_unlock(&inited_mutex);
 468       return( -1 );
 469     }
 470 
 471     inited = 1;
 472     mutex_unlock(&inited_mutex);
 473 
 474     return( 0 );
 475 
 476 }
 477 
 478 /*
 479  * Initialize ns/security so it can be used for SSL client authentication.
 480  * It is safe to call this more than once.
 481  *
 482  * If needkeydb == 0, no key database is opened and SSL server authentication
 483  * is supported but not client authentication.
 484  *
 485  * If "certdbpath" is NULL or "", the default cert. db is used (typically
 486  * ~/.netscape/cert7.db).
 487  *
 488  * If "certdbpath" ends with ".db" (case-insensitive compare), then
 489  * it is assumed to be a full path to the cert. db file; otherwise,
 490  * it is assumed to be a directory that contains a file called
 491  * "cert7.db" or "cert.db".
 492  *
 493  * If certdbhandle is non-NULL, it is assumed to be a pointer to a
 494  * SECCertDBHandle structure.  It is fine to pass NULL since this
 495  * routine will allocate one for you (CERT_GetDefaultDB() can be
 496  * used to retrieve the cert db handle).
 497  *
 498  * If "keydbpath" is NULL or "", the default key db is used (typically
 499  * ~/.netscape/key3.db).
 500  *
 501  * If "keydbpath" ends with ".db" (case-insensitive compare), then
 502  * it is assumed to be a full path to the key db file; otherwise,
 503  * it is assumed to be a directory that contains a file called
 504  * "key3.db" 
 505  *
 506  * If certdbhandle is non-NULL< it is assumed to be a pointed to a
 507  * SECKEYKeyDBHandle structure.  It is fine to pass NULL since this
 508  * routine will allocate one for you (SECKEY_GetDefaultDB() can be
 509  * used to retrieve the cert db handle).  */
 510 int
 511 LDAP_CALL
 512 ldapssl_advclientauth_init( 
 513     const char *certdbpath, void *certdbhandle, 
 514     const int needkeydb, const char *keydbpath, void *keydbhandle,  
 515     const int needsecmoddb, const char *secmoddbpath,
 516     const int sslstrength )
 517 {
 518     int rc;
 519 #ifdef _SOLARIS_SDK
 520     char *enval;
 521     int rcenv = 0;
 522 #endif
 523 
 524     mutex_lock(&inited_mutex);
 525     if ( inited ) {
 526         mutex_unlock(&inited_mutex);
 527         return( 0 );
 528     }
 529 
 530     /*
 531      *    LDAPDebug(LDAP_DEBUG_TRACE, "ldapssl_advclientauth_init\n",0 ,0 ,0);
 532      */
 533 
 534     ldapssl_basic_init();
 535 
 536 #ifdef _SOLARIS_SDK
 537     if ((rcenv = update_nss_strict_fork_env(&enval)) == -1) {
 538         mutex_unlock(&inited_mutex);
 539         return (-1);
 540     }
 541 #endif
 542 
 543     rc = NSS_Init(certdbpath);
 544 #ifdef _SOLARIS_SDK
 545     /* Error from NSS_Init() more important! */
 546     if ((rcenv != 1) && (reset_nss_strict_fork_env(enval) != 0) && (rc == 0)) {
 547         ldapssl_free(&enval);
 548         mutex_unlock(&inited_mutex);
 549         return (-1);
 550     }
 551     ldapssl_free(&enval);
 552 #endif
 553     if (rc != 0) {
 554         if ((rc = PR_GetError()) >= 0)
 555             rc = -1;
 556         mutex_unlock(&inited_mutex);
 557         return (rc);
 558     }
 559 
 560     if (local_SSLPLCY_Install() == PR_FAILURE) {
 561       mutex_unlock(&inited_mutex);
 562       return( -1 );
 563     }
 564 
 565     inited = 1;
 566     mutex_unlock(&inited_mutex);
 567 
 568     return( ldapssl_set_strength( NULL, sslstrength));
 569 
 570 }
 571 
 572 
 573 /*
 574  * Initialize ns/security so it can be used for SSL client authentication.
 575  * It is safe to call this more than once.
 576   */
 577 
 578 /* 
 579  * XXXceb  This is a hack until the new IO functions are done.
 580  * this function lives in ldapsinit.c
 581  */
 582 void set_using_pkcs_functions( int val );
 583 
 584 int
 585 LDAP_CALL
 586 ldapssl_pkcs_init( const struct ldapssl_pkcs_fns *pfns )
 587 {
 588 
 589     char                *certdbName, *s, *keydbpath;
 590     char                *certdbPrefix, *keydbPrefix;
 591     char                *confDir, *keydbName;
 592     static char         *secmodname =  "secmod.db";
 593     int                 rc;
 594 #ifdef _SOLARIS_SDK
 595     char *enval;
 596     int rcenv = 0;
 597 #endif
 598     
 599     mutex_lock(&inited_mutex);
 600     if ( inited ) {
 601         mutex_unlock(&inited_mutex);
 602         return( 0 );
 603     }
 604 /* 
 605  * XXXceb  This is a hack until the new IO functions are done.
 606  * this function MUST be called before ldap_enable_clienauth.
 607  * 
 608  */
 609     set_using_pkcs_functions( 1 );
 610     
 611     /*
 612      *    LDAPDebug(LDAP_DEBUG_TRACE, "ldapssl_pkcs_init\n",0 ,0 ,0);
 613      */
 614 
 615 
 616     ldapssl_basic_init();
 617 
 618     pfns->pkcs_getcertpath( NULL, &s);
 619     confDir = ldapssl_strdup( s );
 620     certdbPrefix = ldapssl_strdup( s );
 621     certdbName = ldapssl_strdup( s );
 622     *certdbPrefix = 0;
 623     splitpath(s, confDir, certdbPrefix, certdbName);
 624 
 625     pfns->pkcs_getkeypath( NULL, &s);
 626     keydbpath = ldapssl_strdup( s );
 627     keydbPrefix = ldapssl_strdup( s );
 628     keydbName = ldapssl_strdup( s );
 629     *keydbPrefix = 0;
 630     splitpath(s, keydbpath, keydbPrefix, keydbName);
 631 
 632 
 633     /* verify confDir == keydbpath and adjust as necessary */
 634     ldapssl_free((void **)&certdbName);
 635     ldapssl_free((void **)&keydbName);
 636     ldapssl_free((void **)&keydbpath);
 637 
 638 #ifdef _SOLARIS_SDK
 639     if ((rcenv = update_nss_strict_fork_env(&enval)) == -1) {
 640         mutex_unlock(&inited_mutex);
 641         return (-1);
 642     }
 643 #endif
 644 
 645     rc = NSS_Initialize(confDir,certdbPrefix,keydbPrefix,secmodname,
 646                 NSS_INIT_READONLY);
 647 
 648     ldapssl_free((void **)&certdbPrefix);
 649     ldapssl_free((void **)&keydbPrefix);
 650     ldapssl_free((void **)&confDir);
 651 
 652 #ifdef _SOLARIS_SDK
 653     /* Error from NSS_Initialize() more important! */
 654     if ((rcenv != 1) && (reset_nss_strict_fork_env(enval) != 0) && (rc == 0)) {
 655         ldapssl_free(&enval);
 656         mutex_unlock(&inited_mutex);
 657         return (-1);
 658     }
 659     ldapssl_free(&enval);
 660 #endif
 661     
 662     if (rc != 0) {
 663         if ((rc = PR_GetError()) >= 0)
 664             rc = -1;
 665         mutex_unlock(&inited_mutex);
 666         return (rc);
 667     }
 668 
 669 
 670 #if 0   /* UNNEEDED BY LIBLDAP */
 671     /* this is odd */
 672     PK11_ConfigurePKCS11(NULL, NULL, tokDes, ptokDes, NULL, NULL, NULL, NULL, 0, 0 );
 673 #endif  /* UNNEEDED BY LIBLDAP */
 674 
 675     if (SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE)
 676         || SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE)) {
 677         if (( rc = PR_GetError()) >= 0 ) {
 678             rc = -1;
 679         }
 680         
 681         mutex_unlock(&inited_mutex);
 682         return( rc );
 683     }
 684     
 685     if (local_SSLPLCY_Install() == PR_FAILURE) {
 686       mutex_unlock(&inited_mutex);
 687       return( -1 );
 688     }
 689 
 690     inited = 1;
 691 
 692     if ( certdbName != NULL ) {
 693         ldapssl_free((void **) &certdbName );
 694     }
 695     
 696     return( ldapssl_set_strength( NULL, LDAPSSL_AUTH_CNCHECK));
 697 }
 698 
 699 
 700 /*
 701  * ldapssl_client_init() is a server-authentication only version of
 702  * ldapssl_clientauth_init().
 703  */
 704 int
 705 LDAP_CALL
 706 ldapssl_client_init(const char* certdbpath, void *certdbhandle )
 707 {
 708     return( ldapssl_clientauth_init( certdbpath, certdbhandle,
 709             0, NULL, NULL ));
 710 }
 711 /*
 712  * ldapssl_serverauth_init() is a server-authentication only version of
 713  * ldapssl_clientauth_init().  This function allows the sslstrength
 714  * to be passed in.  The sslstrength can take one of the following
 715  * values:
 716  *      LDAPSSL_AUTH_WEAK: indicate that you accept the server's
 717  *                         certificate without checking the CA who
 718  *                         issued the certificate
 719  *      LDAPSSL_AUTH_CERT: indicates that you accept the server's
 720  *                         certificate only if you trust the CA who
 721  *                         issued the certificate
 722  *      LDAPSSL_AUTH_CNCHECK:
 723                            indicates that you accept the server's
 724  *                         certificate only if you trust the CA who
 725  *                         issued the certificate and if the value
 726  *                         of the cn attribute in the DNS hostname
 727  *                         of the server
 728  */
 729 int
 730 LDAP_CALL
 731 ldapssl_serverauth_init(const char* certdbpath,
 732                      void *certdbhandle,
 733                      const int sslstrength )
 734 {
 735     if ( ldapssl_set_strength( NULL, sslstrength ) != 0) {
 736         return ( -1 );
 737     }
 738 
 739     return( ldapssl_clientauth_init( certdbpath, certdbhandle,
 740             0, NULL, NULL ));
 741 }
 742 
 743 /*
 744  * Function that makes an asynchronous Start TLS extended operation request.
 745  */
 746 static int ldapssl_tls_start(LDAP *ld, int *msgidp)
 747 {
 748     int version, rc;
 749     BerValue extreq_data;
 750 
 751     /* Start TLS extended operation requires an absent "requestValue" field. */
 752 
 753     extreq_data.bv_val = NULL;
 754     extreq_data.bv_len = 0;
 755 
 756     /* Make sure version is set to LDAPv3 for extended operations to be
 757        supported. */
 758 
 759     version = LDAP_VERSION3;
 760     ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
 761 
 762     /* Send the Start TLS request (OID: 1.3.6.1.4.1.1466.20037) */
 763     rc = ldap_extended_operation( ld, START_TLS_OID, &extreq_data,
 764               NULL, NULL, msgidp );
 765    
 766     return rc;
 767 }
 768 
 769 
 770 /*
 771  * Function that enables SSL on an already open non-secured LDAP connection.
 772  * (i.e. the connection is henceforth secured)
 773  */
 774 static int ldapssl_enableSSL_on_open_connection(LDAP *ld, int defsecure,
 775         char *certdbpath, char *keydbpath)
 776 {
 777     PRLDAPSocketInfo  soi;
 778 
 779 
 780     if ( ldapssl_clientauth_init( certdbpath, NULL, 1, keydbpath, NULL ) < 0 ) {
 781         goto ssl_setup_failure;
 782     }
 783    
 784     /*
 785      * Retrieve socket info. so we have the PRFileDesc.
 786      */
 787     memset( &soi, 0, sizeof(soi));
 788     soi.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
 789     if ( prldap_get_default_socket_info( ld, &soi ) < 0 ) {
 790         goto ssl_setup_failure;
 791     }
 792 
 793     if ( ldapssl_install_routines( ld ) < 0 ) {
 794         goto ssl_setup_failure;
 795     }
 796 
 797 
 798     if (soi.soinfo_prfd == NULL) {
 799         int sd;
 800         ldap_get_option( ld, LDAP_OPT_DESC, &sd );
 801         soi.soinfo_prfd = (PRFileDesc *) PR_ImportTCPSocket( sd );
 802     }
 803     /* set the socket information back into the connection handle,
 804      * because ldapssl_install_routines() resets the socket_arg info in the
 805      * socket buffer. */
 806     if ( prldap_set_default_socket_info( ld, &soi ) != LDAP_SUCCESS ) {
 807       goto ssl_setup_failure;
 808     }
 809 
 810     if ( ldap_set_option( ld, LDAP_OPT_SSL,
 811         defsecure ? LDAP_OPT_ON : LDAP_OPT_OFF ) < 0 ) {
 812         goto ssl_setup_failure;
 813     }
 814   
 815     if ( ldapssl_import_fd( ld, defsecure ) < 0 ) {
 816         goto ssl_setup_failure;
 817     }
 818 
 819     return 0;
 820 
 821 ssl_setup_failure:
 822     ldapssl_reset_to_nonsecure( ld );
 823 
 824     /* we should here warn the server that we switch back to a non-secure
 825        connection */
 826 
 827     return( -1 );
 828 }
 829 
 830 
 831 /*
 832  * ldapssl_tls_start_s() performs a synchronous Start TLS extended operation
 833  * request.
 834  *
 835  * The function returns the result code of the extended operation response
 836  * sent by the server.
 837  *
 838  * In case of a successfull response (LDAP_SUCCESS returned), by the time
 839  * this function returns the LDAP session designed by ld will have been
 840  * secured, i.e. the connection will have been imported into SSL.
 841  *
 842  * Should the Start TLS request be rejected by the server, the result code
 843  * returned will be one of the following:
 844  *    LDAP_OPERATIONS_ERROR,
 845  *    LDAP_PROTOCOL_ERROR,
 846  *    LDAP_REFERRAL,
 847  *    LDAP_UNAVAILABLE.
 848  *
 849  * Any other error code returned will be due to a failure in the course
 850  * of operations done on the client side.
 851  *
 852  * "certdbpath" and "keydbpath" should contain the path to the client's
 853  * certificate and key databases respectively. Either the path to the
 854  * directory containing "default name" databases (i.e. cert7.db and key3.db)
 855  * can be specified or the actual filenames can be included.
 856  * If any of these parameters is NULL, the function will assume the database
 857  * is the same used by Netscape Communicator, which is usually under
 858  * ~/.netsca /)
 859  *
 860  * "referralsp" is a pointer to a list of referrals the server might
 861  * eventually send back with an LDAP_REFERRAL result code.
 862  *
 863  */
 864 
 865 int
 866 LDAP_CALL
 867 ldapssl_tls_start_s(LDAP *ld,int defsecure, char *certdbpath, char *keydbpath,
 868         char ***referralsp)
 869 {
 870     int             rc, resultCode, msgid;
 871     char            *extresp_oid;
 872     BerValue        *extresp_data;
 873     LDAPMessage     *res;
 874 
 875     rc = ldapssl_tls_start( ld, &msgid );
 876     if ( rc != LDAP_SUCCESS ) {
 877          return rc;
 878     }
 879 
 880     rc = ldap_result( ld, msgid, 1, (struct timeval *) NULL, &res );
 881     if ( rc != LDAP_RES_EXTENDED ) {
 882 
 883       /* the first response received must be an extended response to an
 884        Start TLS request */
 885 
 886          ldap_msgfree( res );
 887          return( -1 );
 888 
 889     }
 890 
 891     rc = ldap_parse_extended_result( ld, res, &extresp_oid, &extresp_data, 0 );
 892 
 893     if ( rc != LDAP_SUCCESS ) {
 894          ldap_msgfree( res );
 895          return rc;
 896     }
 897 
 898     if ( strcasecmp( extresp_oid, START_TLS_OID ) != 0 ) {
 899 
 900          /* the extended response received doesn't correspond to the
 901           Start TLS request */
 902 
 903          ldap_msgfree( res );
 904          return -1;
 905     }
 906 
 907     resultCode = ldap_get_lderrno( ld, NULL, NULL );
 908 
 909     /* Analyze the server's response */
 910     switch (resultCode) {
 911     case LDAP_REFERRAL:
 912       {
 913       rc = ldap_parse_result( ld, res, NULL, NULL, NULL, referralsp, NULL, 0 );
 914       if ( rc != LDAP_SUCCESS ) {
 915           ldap_msgfree( res );
 916           return rc;
 917       }
 918     }
 919     case LDAP_OPERATIONS_ERROR:
 920 
 921     case LDAP_PROTOCOL_ERROR:
 922 
 923     case LDAP_UNAVAILABLE:
 924         goto free_msg_and_return;
 925     case LDAP_SUCCESS:
 926       {
 927       /*
 928        * If extended response successfull, get connection ready for
 929        * communicating with the server over SSL/TLS.
 930        */
 931 
 932       if ( ldapssl_enableSSL_on_open_connection( ld, defsecure,
 933                                          certdbpath, keydbpath ) < 0 ) {
 934           resultCode = -1;
 935       }
 936 
 937     } /* case LDAP_SUCCESS */
 938     default:
 939         goto free_msg_and_return;
 940     } /* switch */
 941 
 942 free_msg_and_return:
 943     ldap_msgfree( res );
 944     return resultCode;
 945 }
 946 
 947 #endif /* NET_SSL */