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 */
987