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
75static PRStatus local_SSLPLCY_Install(void);
76
77/*
78 * This little tricky guy keeps us from initializing twice
79 */
80static int		inited = 0;
81#ifdef _SOLARIS_SDK
82mutex_t			inited_mutex = DEFAULTMUTEX;
83#else
84static mutex_t		inited_mutex = DEFAULTMUTEX;
85#endif	/* _SOLARIS_SDK */
86#if 0	/* UNNEEDED BY LIBLDAP */
87static char  tokDes[34] = "Internal (Software) Database     ";
88static 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
99static int
100splitpath(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
157static PRStatus local_SSLPLCY_Install(void)
158{
159	return NSS_SetDomesticPolicy() ? PR_FAILURE : PR_SUCCESS;
160}
161
162
163
164static void
165ldapssl_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 */
185static void *
186ldapssl_malloc( size_t size )
187{
188    void	*p;
189
190    p = malloc( size );
191    return p;
192}
193
194
195static void *
196ldapssl_calloc( int nelem, size_t elsize )
197{
198    void	*p;
199
200    p = calloc( nelem, elsize );
201    return p;
202}
203
204
205static char *
206ldapssl_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
219static void
220ldapssl_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 */
242static int
243update_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 */
267static int
268reset_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
279static char *
280buildDBName(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
316char *
317GetCertDBName(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 */
340static char *
341GetDBName(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 */
406int
407LDAP_CALL
408ldapssl_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).  */
510int
511LDAP_CALL
512ldapssl_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 */
582void set_using_pkcs_functions( int val );
583
584int
585LDAP_CALL
586ldapssl_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 */
704int
705LDAP_CALL
706ldapssl_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 */
729int
730LDAP_CALL
731ldapssl_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 */
746static 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 */
774static 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
821ssl_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
865int
866LDAP_CALL
867ldapssl_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
942free_msg_and_return:
943    ldap_msgfree( res );
944    return resultCode;
945}
946
947#endif /* NET_SSL */
948