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