Example #1
0
SECStatus 
SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey,
	      PRUint32 protocolmask, PRUint16 *ciphersuites, int nsuites,
              PRBool *pcanbypass, void *pwArg)
{
#ifdef NO_PKCS11_BYPASS
    if (!pcanbypass) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    *pcanbypass = PR_FALSE;
    return SECSuccess;
#else
    SECStatus	      rv;
    int		      i;
    PRUint16	      suite;
    PK11SymKey *      pms = NULL;
    SECKEYPublicKey * srvPubkey = NULL;
    KeyType	      privKeytype;
    PK11SlotInfo *    slot = NULL;
    SECItem           param;
    CK_VERSION 	      version;
    CK_MECHANISM_TYPE mechanism_array[2];
    SECItem           enc_pms = {siBuffer, NULL, 0};
    PRBool	      isTLS = PR_FALSE;
    SSLCipherSuiteInfo csdef;
    PRBool	      testrsa = PR_FALSE;
    PRBool	      testrsa_export = PR_FALSE;
    PRBool	      testecdh = PR_FALSE;
    PRBool	      testecdhe = PR_FALSE;
#ifdef NSS_ENABLE_ECC
    SECKEYECParams ecParams = { siBuffer, NULL, 0 };
#endif

    if (!cert || !srvPrivkey || !ciphersuites || !pcanbypass) {
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    
    srvPubkey = CERT_ExtractPublicKey(cert);
    if (!srvPubkey)
        return SECFailure;
	
    *pcanbypass = PR_TRUE;
    rv = SECFailure;
    
    
    for (i=0; i < nsuites && (suite = *ciphersuites++) != 0; i++) {
	
	if (SSL_GetCipherSuiteInfo(suite, &csdef, sizeof(csdef)) != SECSuccess
	    || SSL_IS_SSL2_CIPHER(suite) )
	    continue;
	switch (csdef.keaType) {
	case ssl_kea_rsa:
	    switch (csdef.cipherSuite) {
	    case TLS_RSA_EXPORT1024_WITH_RC4_56_SHA:
	    case TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA:
	    case SSL_RSA_EXPORT_WITH_RC4_40_MD5:
	    case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
		testrsa_export = PR_TRUE;
	    }
	    if (!testrsa_export)
		testrsa = PR_TRUE;
	    break;
	case ssl_kea_ecdh:
	    if (strcmp(csdef.keaTypeName, "ECDHE") == 0) 
		testecdhe = PR_TRUE;
	    else
		testecdh = PR_TRUE;
	    break;
	case ssl_kea_dh:
	    
	default:
	    continue;
	}
    }
    
    privKeytype = SECKEY_GetPrivateKeyType(srvPrivkey);
    protocolmask &= SSL_CBP_SSL3|SSL_CBP_TLS1_0;
    while (protocolmask) {
	if (protocolmask & SSL_CBP_SSL3) {
	    isTLS = PR_FALSE;
	    protocolmask ^= SSL_CBP_SSL3;
	} else {
	    isTLS = PR_TRUE;
	    protocolmask ^= SSL_CBP_TLS1_0;
	}

	if (privKeytype == rsaKey && testrsa_export) {
	    if (PK11_GetPrivateModulusLen(srvPrivkey) > EXPORT_RSA_KEY_LENGTH) {
		*pcanbypass = PR_FALSE;
		rv = SECSuccess;
		break;
	    } else
		testrsa = PR_TRUE;
	}
	for (; privKeytype == rsaKey && testrsa; ) {
	    
	    unsigned char     rsaPmsBuf[SSL3_RSA_PMS_LENGTH];
	    unsigned int      outLen = 0;
	    CK_MECHANISM_TYPE target;
	    SECStatus	      irv;
	    
	    mechanism_array[0] = CKM_SSL3_PRE_MASTER_KEY_GEN;
	    mechanism_array[1] = CKM_RSA_PKCS;

	    slot = PK11_GetBestSlotMultiple(mechanism_array, 2, pwArg);
	    if (slot == NULL) {
		PORT_SetError(SSL_ERROR_TOKEN_SLOT_NOT_FOUND);
		break;
	    }

	    
	    version.major = 3 ;
	    version.minor = 0 ;
	    param.data = (unsigned char *)&version;
	    param.len  = sizeof version;
	    pms = PK11_KeyGen(slot, CKM_SSL3_PRE_MASTER_KEY_GEN, &param, 0, pwArg);
	    PK11_FreeSlot(slot);
	    if (!pms)
		break;
	    
	    enc_pms.len  = SECKEY_PublicKeyStrength(srvPubkey);
	    enc_pms.data = (unsigned char*)PORT_Alloc(enc_pms.len);
	    if (enc_pms.data == NULL) {
	        PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
	        break;
	    }
	    irv = PK11_PubWrapSymKey(CKM_RSA_PKCS, srvPubkey, pms, &enc_pms);
	    if (irv != SECSuccess) 
		break;
	    PK11_FreeSymKey(pms);
	    pms = NULL;
	    
	    rv = PK11_PrivDecryptPKCS1(srvPrivkey, rsaPmsBuf, &outLen,
				       sizeof rsaPmsBuf,
				       (unsigned char *)enc_pms.data,
				       enc_pms.len);
	    
	    if (rv == SECSuccess) {
		*pcanbypass = PR_TRUE;
		break;
	    }
	    
	    target = isTLS ? CKM_TLS_MASTER_KEY_DERIVE
			: CKM_SSL3_MASTER_KEY_DERIVE;
	    pms = PK11_PubUnwrapSymKey(srvPrivkey, &enc_pms,
				       target, CKA_DERIVE, 0);
	    rv = ssl_canExtractMS(pms, isTLS, PR_FALSE, pcanbypass);
	    if (rv == SECSuccess && *pcanbypass == PR_FALSE)
		goto done;
	    break;
	}

	if (enc_pms.data != NULL) {
	    SECITEM_FreeItem(&enc_pms, PR_FALSE);
        }
#ifdef NSS_ENABLE_ECC
	for (; (privKeytype == ecKey && ( testecdh || testecdhe)) ||
	       (privKeytype == rsaKey && testecdhe); ) {
	    CK_MECHANISM_TYPE target;
	    SECKEYPublicKey  *keapub = NULL;
	    SECKEYPrivateKey *keapriv;
	    SECKEYPublicKey  *cpub = NULL; 
	    SECKEYPrivateKey *cpriv = NULL;
	    SECKEYECParams   *pecParams = NULL;

	    if (privKeytype == ecKey && testecdhe) {
		
		pecParams = &srvPubkey->u.ec.DEREncodedParams;
	    } else if (privKeytype == rsaKey && testecdhe) {
		
		ECName       ec_curve;
		int		 serverKeyStrengthInBits;
		int		 signatureKeyStrength;
		int		 requiredECCbits;

		
		requiredECCbits = PK11_GetPrivateModulusLen(srvPrivkey);
		if (requiredECCbits < 0)
		    break;
		requiredECCbits *= BPB;
		serverKeyStrengthInBits = srvPubkey->u.rsa.modulus.len;
		if (srvPubkey->u.rsa.modulus.data[0] == 0) {
		    serverKeyStrengthInBits--;
		}
		
		serverKeyStrengthInBits *= BPB;

		signatureKeyStrength =
		    SSL_RSASTRENGTH_TO_ECSTRENGTH(serverKeyStrengthInBits);

		if ( requiredECCbits > signatureKeyStrength ) 
		     requiredECCbits = signatureKeyStrength;

		ec_curve =
		    ssl3_GetCurveWithECKeyStrength(
					ssl3_GetSupportedECCurveMask(NULL),
				  	requiredECCbits);
		rv = ssl3_ECName2Params(NULL, ec_curve, &ecParams);
		if (rv == SECFailure) {
		    break;
		}
		pecParams = &ecParams;
	    }

	    if (testecdhe) {
		
		keapriv = SECKEY_CreateECPrivateKey(pecParams, &keapub, NULL); 
		if (!keapriv || !keapub) {
		    if (keapriv)
			SECKEY_DestroyPrivateKey(keapriv);
		    if (keapub)
			SECKEY_DestroyPublicKey(keapub);
		    PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
		    rv = SECFailure;
		    break;
		}
	    } else {
		
		keapub = srvPubkey;
		keapriv = srvPrivkey;
		pecParams = &srvPubkey->u.ec.DEREncodedParams;
	    }

	    
	    
	    cpriv = SECKEY_CreateECPrivateKey(pecParams, &cpub, NULL);
	    if (!cpriv || !cpub) {
		if (testecdhe) {
		    SECKEY_DestroyPrivateKey(keapriv);
		    SECKEY_DestroyPublicKey(keapub);
		}
		PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
		rv = SECFailure;
		break;
	    }
	    
	    
	    target = isTLS ? CKM_TLS_MASTER_KEY_DERIVE_DH
			   : CKM_SSL3_MASTER_KEY_DERIVE_DH;
	    pms = PK11_PubDeriveWithKDF(keapriv, cpub, PR_FALSE, NULL, NULL,
				    CKM_ECDH1_DERIVE,
				    target,
				    CKA_DERIVE, 0, CKD_NULL, NULL, NULL);
	    rv = ssl_canExtractMS(pms, isTLS, PR_TRUE, pcanbypass);
	    SECKEY_DestroyPrivateKey(cpriv);
	    SECKEY_DestroyPublicKey(cpub);
	    if (testecdhe) {
		SECKEY_DestroyPrivateKey(keapriv);
		SECKEY_DestroyPublicKey(keapub);
	    }
	    if (rv == SECSuccess && *pcanbypass == PR_FALSE)
		goto done;
	    break;
	}
	
	if (ecParams.data != NULL) {
	    PORT_Free(ecParams.data);
	    ecParams.data = NULL;
	}
#endif 
	if (pms)
	    PK11_FreeSymKey(pms);
    }

    
    rv = SECSuccess;
    
  done:
    if (pms)
	PK11_FreeSymKey(pms);

    if (enc_pms.data != NULL) {
    	SECITEM_FreeItem(&enc_pms, PR_FALSE);
    }
#ifdef NSS_ENABLE_ECC
    if (ecParams.data != NULL) {
        PORT_Free(ecParams.data);
        ecParams.data = NULL;
    }
#endif 

    if (srvPubkey) {
    	SECKEY_DestroyPublicKey(srvPubkey);
	srvPubkey = NULL;
    }


    return rv;
#endif 
}
Example #2
0
SECStatus 
SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey,
	      PRUint32 protocolmask, PRUint16 *ciphersuites, int nsuites,
              PRBool *pcanbypass, void *pwArg)
{   SECStatus	      rv;
    int		      i;
    PRUint16	      suite;
    PK11SymKey *      pms = NULL;
    SECKEYPublicKey * srvPubkey = NULL;
    KeyType	      privKeytype;
    PK11SlotInfo *    slot = NULL;
    SECItem           param;
    CK_VERSION 	      version;
    CK_MECHANISM_TYPE mechanism_array[2];
    SECItem           enc_pms = {siBuffer, NULL, 0};
    PRBool	      isTLS = PR_FALSE;
    SSLCipherSuiteInfo csdef;
    PRBool	      testrsa = PR_FALSE;
    PRBool	      testrsa_export = PR_FALSE;
    PRBool	      testecdh = PR_FALSE;
    PRBool	      testecdhe = PR_FALSE;
#ifdef NSS_ENABLE_ECC
    SECKEYECParams ecParams = { siBuffer, NULL, 0 };
#endif

    if (!cert || !srvPrivkey || !ciphersuites || !pcanbypass) {
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    
    srvPubkey = CERT_ExtractPublicKey(cert);
    if (!srvPubkey)
        return SECFailure;
	
    *pcanbypass = PR_TRUE;
    rv = SECFailure;
    
    /* determine which KEAs to test */
    /* 0 (SSL_NULL_WITH_NULL_NULL) is used as a list terminator because
     * SSL3 and TLS specs forbid negotiating that cipher suite number.
     */
    for (i=0; i < nsuites && (suite = *ciphersuites++) != 0; i++) {
	/* skip SSL2 cipher suites and ones NSS doesn't support */
	if (SSL_GetCipherSuiteInfo(suite, &csdef, sizeof(csdef)) != SECSuccess
	    || SSL_IS_SSL2_CIPHER(suite) )
	    continue;
	switch (csdef.keaType) {
	case ssl_kea_rsa:
	    switch (csdef.cipherSuite) {
	    case TLS_RSA_EXPORT1024_WITH_RC4_56_SHA:
	    case TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA:
	    case SSL_RSA_EXPORT_WITH_RC4_40_MD5:
	    case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
		testrsa_export = PR_TRUE;
	    }
	    if (!testrsa_export)
		testrsa = PR_TRUE;
	    break;
	case ssl_kea_ecdh:
	    if (strcmp(csdef.keaTypeName, "ECDHE") == 0) /* ephemeral? */
		testecdhe = PR_TRUE;
	    else
		testecdh = PR_TRUE;
	    break;
	case ssl_kea_dh:
	    /* this is actually DHE */
	default:
	    continue;
	}
    }
    
    /* For each protocol try to derive and extract an MS.
     * Failure of function any function except MS extract means
     * continue with the next cipher test. Stop testing when the list is
     * exhausted or when the first MS extract--not derive--fails.
     */
    privKeytype = SECKEY_GetPrivateKeyType(srvPrivkey);
    protocolmask &= SSL_CBP_SSL3|SSL_CBP_TLS1_0;
    while (protocolmask) {
	if (protocolmask & SSL_CBP_SSL3) {
	    isTLS = PR_FALSE;
	    protocolmask ^= SSL_CBP_SSL3;
	} else {
	    isTLS = PR_TRUE;
	    protocolmask ^= SSL_CBP_TLS1_0;
	}

	if (privKeytype == rsaKey && testrsa_export) {
	    if (PK11_GetPrivateModulusLen(srvPrivkey) > EXPORT_RSA_KEY_LENGTH) {
		*pcanbypass = PR_FALSE;
		rv = SECSuccess;
		break;
	    } else
		testrsa = PR_TRUE;
	}
	for (; privKeytype == rsaKey && testrsa; ) {
	    /* TLS_RSA */
	    unsigned char     rsaPmsBuf[SSL3_RSA_PMS_LENGTH];
	    unsigned int      outLen = 0;
	    CK_MECHANISM_TYPE target;
	    SECStatus	      irv;
	    
	    mechanism_array[0] = CKM_SSL3_PRE_MASTER_KEY_GEN;
	    mechanism_array[1] = CKM_RSA_PKCS;

	    slot = PK11_GetBestSlotMultiple(mechanism_array, 2, pwArg);
	    if (slot == NULL) {
		PORT_SetError(SSL_ERROR_TOKEN_SLOT_NOT_FOUND);
		break;
	    }

	    /* Generate the pre-master secret ...  (client side) */
	    version.major = 3 /*MSB(clientHelloVersion)*/;
	    version.minor = 0 /*LSB(clientHelloVersion)*/;
	    param.data = (unsigned char *)&version;
	    param.len  = sizeof version;
	    pms = PK11_KeyGen(slot, CKM_SSL3_PRE_MASTER_KEY_GEN, &param, 0, pwArg);
	    PK11_FreeSlot(slot);
	    if (!pms)
		break;
	    /* now wrap it */
	    enc_pms.len  = SECKEY_PublicKeyStrength(srvPubkey);
	    enc_pms.data = (unsigned char*)PORT_Alloc(enc_pms.len);
	    if (enc_pms.data == NULL) {
	        PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
	        break;
	    }
	    irv = PK11_PubWrapSymKey(CKM_RSA_PKCS, srvPubkey, pms, &enc_pms);
	    if (irv != SECSuccess) 
		break;
	    PK11_FreeSymKey(pms);
	    pms = NULL;
	    /* now do the server side--check the triple bypass first */
	    rv = PK11_PrivDecryptPKCS1(srvPrivkey, rsaPmsBuf, &outLen,
				       sizeof rsaPmsBuf,
				       (unsigned char *)enc_pms.data,
				       enc_pms.len);
	    /* if decrypt worked we're done with the RSA test */
	    if (rv == SECSuccess) {
		*pcanbypass = PR_TRUE;
		break;
	    }
	    /* check for fallback to double bypass */
	    target = isTLS ? CKM_TLS_MASTER_KEY_DERIVE
			: CKM_SSL3_MASTER_KEY_DERIVE;
	    pms = PK11_PubUnwrapSymKey(srvPrivkey, &enc_pms,
				       target, CKA_DERIVE, 0);
	    rv = ssl_canExtractMS(pms, isTLS, PR_FALSE, pcanbypass);
	    if (rv == SECSuccess && *pcanbypass == PR_FALSE)
		goto done;
	    break;
	}

	/* Check for NULL to avoid double free. 
	 * SECItem_FreeItem sets data NULL in secitem.c#265 
	 */
	if (enc_pms.data != NULL) {
	    SECITEM_FreeItem(&enc_pms, PR_FALSE);
        }
#ifdef NSS_ENABLE_ECC
	for (; (privKeytype == ecKey && ( testecdh || testecdhe)) ||
	       (privKeytype == rsaKey && testecdhe); ) {
	    CK_MECHANISM_TYPE target;
	    SECKEYPublicKey  *keapub = NULL;
	    SECKEYPrivateKey *keapriv;
	    SECKEYPublicKey  *cpub = NULL; /* client's ephemeral ECDH keys */
	    SECKEYPrivateKey *cpriv = NULL;
	    SECKEYECParams   *pecParams = NULL;

	    if (privKeytype == ecKey && testecdhe) {
		/* TLS_ECDHE_ECDSA */
		pecParams = &srvPubkey->u.ec.DEREncodedParams;
	    } else if (privKeytype == rsaKey && testecdhe) {
		/* TLS_ECDHE_RSA */
		ECName       ec_curve;
		int		 serverKeyStrengthInBits;
		int		 signatureKeyStrength;
		int		 requiredECCbits;

		/* find a curve of equivalent strength to the RSA key's */
		requiredECCbits = PK11_GetPrivateModulusLen(srvPrivkey);
		if (requiredECCbits < 0)
		    break;
		requiredECCbits *= BPB;
		serverKeyStrengthInBits = srvPubkey->u.rsa.modulus.len;
		if (srvPubkey->u.rsa.modulus.data[0] == 0) {
		    serverKeyStrengthInBits--;
		}
		/* convert to strength in bits */
		serverKeyStrengthInBits *= BPB;

		signatureKeyStrength =
		    SSL_RSASTRENGTH_TO_ECSTRENGTH(serverKeyStrengthInBits);

		if ( requiredECCbits > signatureKeyStrength ) 
		     requiredECCbits = signatureKeyStrength;

		ec_curve =
		    ssl3_GetCurveWithECKeyStrength(SSL3_SUPPORTED_CURVES_MASK,
						   requiredECCbits);
		rv = ssl3_ECName2Params(NULL, ec_curve, &ecParams);
		if (rv == SECFailure) {
		    break;
		}
		pecParams = &ecParams;
	    }

	    if (testecdhe) {
		/* generate server's ephemeral keys */
		keapriv = SECKEY_CreateECPrivateKey(pecParams, &keapub, NULL); 
		if (!keapriv || !keapub) {
		    if (keapriv)
			SECKEY_DestroyPrivateKey(keapriv);
		    if (keapub)
			SECKEY_DestroyPublicKey(keapub);
		    PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
		    rv = SECFailure;
		    break;
		}
	    } else {
		/* TLS_ECDH_ECDSA */
		keapub = srvPubkey;
		keapriv = srvPrivkey;
		pecParams = &srvPubkey->u.ec.DEREncodedParams;
	    }

	    /* perform client side ops */
	    /* generate a pair of ephemeral keys using server's parms */
	    cpriv = SECKEY_CreateECPrivateKey(pecParams, &cpub, NULL);
	    if (!cpriv || !cpub) {
		if (testecdhe) {
		    SECKEY_DestroyPrivateKey(keapriv);
		    SECKEY_DestroyPublicKey(keapub);
		}
		PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
		rv = SECFailure;
		break;
	    }
	    /* now do the server side */
	    /* determine the PMS using client's public value */
	    target = isTLS ? CKM_TLS_MASTER_KEY_DERIVE_DH
			   : CKM_SSL3_MASTER_KEY_DERIVE_DH;
	    pms = PK11_PubDeriveWithKDF(keapriv, cpub, PR_FALSE, NULL, NULL,
				    CKM_ECDH1_DERIVE,
				    target,
				    CKA_DERIVE, 0, CKD_NULL, NULL, NULL);
	    rv = ssl_canExtractMS(pms, isTLS, PR_TRUE, pcanbypass);
	    SECKEY_DestroyPrivateKey(cpriv);
	    SECKEY_DestroyPublicKey(cpub);
	    if (testecdhe) {
		SECKEY_DestroyPrivateKey(keapriv);
		SECKEY_DestroyPublicKey(keapub);
	    }
	    if (rv == SECSuccess && *pcanbypass == PR_FALSE)
		goto done;
	    break;
	}
	/* Check for NULL to avoid double free. */
	if (ecParams.data != NULL) {
	    PORT_Free(ecParams.data);
	    ecParams.data = NULL;
	}
#endif /* NSS_ENABLE_ECC */
	if (pms)
	    PK11_FreeSymKey(pms);
    }

    /* *pcanbypass has been set */
    rv = SECSuccess;
    
  done:
    if (pms)
	PK11_FreeSymKey(pms);

    /* Check for NULL to avoid double free. 
     * SECItem_FreeItem sets data NULL in secitem.c#265 
     */
    if (enc_pms.data != NULL) {
    	SECITEM_FreeItem(&enc_pms, PR_FALSE);
    }
#ifdef NSS_ENABLE_ECC
    if (ecParams.data != NULL) {
        PORT_Free(ecParams.data);
        ecParams.data = NULL;
    }
#endif /* NSS_ENABLE_ECC */

    if (srvPubkey) {
    	SECKEY_DestroyPublicKey(srvPubkey);
	srvPubkey = NULL;
    }


    return rv;
}
Example #3
0
gint
camel_init (const gchar *configdir,
            gboolean nss_init)
{
	CamelCertDB *certdb;
	gchar *path;

	if (initialised)
		return 0;

	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");

	camel_debug_init ();

	if (nss_init) {
		static gchar v2_enabled = -1, weak_ciphers = -1;
		gchar *nss_configdir = NULL;
		gchar *nss_sql_configdir = NULL;
		SECStatus status = SECFailure;

#if NSS_VMAJOR < 3 || (NSS_VMAJOR == 3 && NSS_VMINOR < 14)
		/* NSS pre-3.14 has most of the ciphers disabled, thus enable
		 * weak ciphers, if it's compiled against such */
		weak_ciphers = 1;
#endif

		/* check camel-tcp-stream-ssl.c for the same "CAMEL_SSL_V2_ENABLE" */
		if (v2_enabled == -1)
			v2_enabled = g_strcmp0 (g_getenv ("CAMEL_SSL_V2_ENABLE"), "1") == 0 ? 1 : 0;
		if (weak_ciphers == -1)
			weak_ciphers = g_strcmp0 (g_getenv ("CAMEL_SSL_WEAK_CIPHERS"), "1") == 0 ? 1 : 0;

		if (nss_initlock == NULL) {
			PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 10);
			nss_initlock = PR_NewLock ();
		}
		PR_Lock (nss_initlock);

		if (NSS_IsInitialized ())
			goto skip_nss_init;

#ifndef G_OS_WIN32
		nss_configdir = g_strdup (configdir);
#else
		nss_configdir = g_win32_locale_filename_from_utf8 (configdir);
#endif

		if (nss_has_system_db ()) {
			nss_sql_configdir = g_strdup ("sql:" NSS_SYSTEM_DB );
		} else {
			/* On Windows, we use the Evolution configdir. On other
			 * operating systems we use ~/.pki/nssdb/, which is where
			 * the user-specific part of the "shared system db" is
			 * stored and is what Chrome uses too.
			 *
			 * We have to create the configdir if it does not exist,
			 * to prevent camel from bailing out on first run. */
#ifdef G_OS_WIN32
			g_mkdir_with_parents (configdir, 0700);
			nss_sql_configdir = g_strconcat (
				"sql:", nss_configdir, NULL);
#else
			gchar *user_nss_dir = g_build_filename (
				g_get_home_dir (), ".pki/nssdb", NULL );
			if (g_mkdir_with_parents (user_nss_dir, 0700))
				g_warning (
					"Failed to create SQL "
					"database directory %s: %s\n",
					user_nss_dir, strerror (errno));

			nss_sql_configdir = g_strconcat (
				"sql:", user_nss_dir, NULL);
			g_free (user_nss_dir);
#endif
		}

#if NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && NSS_VMINOR >= 12)
		/* See: https://wiki.mozilla.org/NSS_Shared_DB,
		 * particularly "Mode 3A".  Note that the target
		 * directory MUST EXIST. */
		status = NSS_InitWithMerge (
			nss_sql_configdir,	/* dest dir */
			"", "",			/* new DB name prefixes */
			SECMOD_DB,		/* secmod name */
			nss_configdir,		/* old DB dir */
			"", "",			/* old DB name prefixes */
			nss_configdir,		/* unique ID for old DB */
			"Evolution S/MIME",	/* UI name for old DB */
			0);			/* flags */

		if (status == SECFailure) {
			g_warning (
				"Failed to initialize NSS SQL database in %s: NSS error %d",
				nss_sql_configdir, PORT_GetError ());
			/* Fall back to opening the old DBM database */
		}
#endif
		/* Support old versions of libnss, pre-sqlite support. */
		if (status == SECFailure)
			status = NSS_InitReadWrite (nss_configdir);
		if (status == SECFailure) {
			/* Fall back to using volatile dbs? */
			status = NSS_NoDB_Init (nss_configdir);
			if (status == SECFailure) {
				g_free (nss_configdir);
				g_free (nss_sql_configdir);
				g_warning ("Failed to initialize NSS");
				PR_Unlock (nss_initlock);
				return -1;
			}
		}

		nss_initialized = TRUE;
skip_nss_init:

		NSS_SetDomesticPolicy ();

		if (weak_ciphers) {
			PRUint16 indx;

			/* enable SSL3/TLS cipher-suites */
			for (indx = 0; indx < SSL_NumImplementedCiphers; indx++) {
				if (!SSL_IS_SSL2_CIPHER (SSL_ImplementedCiphers[indx]) &&
				    SSL_ImplementedCiphers[indx] != SSL_RSA_WITH_NULL_SHA &&
				    SSL_ImplementedCiphers[indx] != SSL_RSA_WITH_NULL_MD5)
					SSL_CipherPrefSetDefault (SSL_ImplementedCiphers[indx], PR_TRUE);
			}
		}

		SSL_OptionSetDefault (SSL_ENABLE_SSL2, v2_enabled ? PR_TRUE : PR_FALSE);
		SSL_OptionSetDefault (SSL_V2_COMPATIBLE_HELLO, PR_FALSE);
		SSL_OptionSetDefault (SSL_ENABLE_SSL3, PR_TRUE);
		SSL_OptionSetDefault (SSL_ENABLE_TLS, PR_TRUE);

		PR_Unlock (nss_initlock);

		g_free (nss_configdir);
		g_free (nss_sql_configdir);
	}

	path = g_strdup_printf ("%s/camel-cert.db", configdir);
	certdb = camel_certdb_new ();
	camel_certdb_set_filename (certdb, path);
	g_free (path);

	/* if we fail to load, who cares? it'll just be a volatile certdb */
	camel_certdb_load (certdb);

	/* set this certdb as the default db */
	camel_certdb_set_default (certdb);

	g_object_unref (certdb);

	initialised = TRUE;

	return 0;
}