xref: /titanic_51/usr/src/lib/libldap5/sources/ldap/ssldap/clientinit.c (revision 96aa4f28c45f26c5cb5f5eb8bf23cb7fc6991d8f)
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 */
948