summaryrefslogtreecommitdiff
path: root/nss/lib/ssl/sslcert.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-01-04 14:24:24 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-01-04 14:24:24 +0000
commitdc1565216a5d20ae0d75872151523252309a1292 (patch)
treed57454ba9a40386552179eddf60d28bd1e8f3d54 /nss/lib/ssl/sslcert.c
parent26c046fbc57d53136b4fb3b5e0d18298318125d4 (diff)
downloadnss-dc1565216a5d20ae0d75872151523252309a1292.tar.gz
nss-3.28.1nss-3.28.1
Diffstat (limited to 'nss/lib/ssl/sslcert.c')
-rw-r--r--nss/lib/ssl/sslcert.c992
1 files changed, 992 insertions, 0 deletions
diff --git a/nss/lib/ssl/sslcert.c b/nss/lib/ssl/sslcert.c
new file mode 100644
index 0000000..ea52455
--- /dev/null
+++ b/nss/lib/ssl/sslcert.c
@@ -0,0 +1,992 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * SSL server certificate configuration functions.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ssl.h"
+#include "sslimpl.h"
+#include "secoid.h" /* for SECOID_GetAlgorithmTag */
+#include "pk11func.h" /* for PK11_ReferenceSlot */
+#include "nss.h" /* for NSS_RegisterShutdown */
+#include "prinit.h" /* for PR_CallOnceWithArg */
+
+static const PRCallOnceType pristineCallOnce;
+static PRCallOnceType setupServerCAListOnce;
+
+static SECStatus
+serverCAListShutdown(void *appData, void *nssData)
+{
+ PORT_Assert(ssl3_server_ca_list);
+ if (ssl3_server_ca_list) {
+ CERT_FreeDistNames(ssl3_server_ca_list);
+ ssl3_server_ca_list = NULL;
+ }
+ setupServerCAListOnce = pristineCallOnce;
+ return SECSuccess;
+}
+
+static PRStatus
+serverCAListSetup(void *arg)
+{
+ CERTCertDBHandle *dbHandle = (CERTCertDBHandle *)arg;
+ SECStatus rv = NSS_RegisterShutdown(serverCAListShutdown, NULL);
+ PORT_Assert(SECSuccess == rv);
+ if (SECSuccess == rv) {
+ ssl3_server_ca_list = CERT_GetSSLCACerts(dbHandle);
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+
+sslServerCert *
+ssl_NewServerCert(const sslServerCertType *certType)
+{
+ sslServerCert *sc = PORT_ZNew(sslServerCert);
+ if (!sc) {
+ return NULL;
+ }
+ memcpy(&sc->certType, certType, sizeof(sc->certType));
+ sc->serverCert = NULL;
+ sc->serverCertChain = NULL;
+ sc->certStatusArray = NULL;
+ sc->signedCertTimestamps.len = 0;
+ return sc;
+}
+
+sslServerCert *
+ssl_CopyServerCert(const sslServerCert *oc)
+{
+ sslServerCert *sc;
+
+ sc = ssl_NewServerCert(&oc->certType);
+ if (!sc) {
+ return NULL;
+ }
+
+ if (oc->serverCert && oc->serverCertChain) {
+ sc->serverCert = CERT_DupCertificate(oc->serverCert);
+ if (!sc->serverCert)
+ goto loser;
+ sc->serverCertChain = CERT_DupCertList(oc->serverCertChain);
+ if (!sc->serverCertChain)
+ goto loser;
+ } else {
+ sc->serverCert = NULL;
+ sc->serverCertChain = NULL;
+ }
+
+ if (oc->serverKeyPair) {
+ sc->serverKeyPair = ssl_GetKeyPairRef(oc->serverKeyPair);
+ if (!sc->serverKeyPair)
+ goto loser;
+ } else {
+ sc->serverKeyPair = NULL;
+ }
+ sc->serverKeyBits = oc->serverKeyBits;
+
+ if (oc->certStatusArray) {
+ sc->certStatusArray = SECITEM_DupArray(NULL, oc->certStatusArray);
+ if (!sc->certStatusArray)
+ goto loser;
+ } else {
+ sc->certStatusArray = NULL;
+ }
+
+ if (SECITEM_CopyItem(NULL, &sc->signedCertTimestamps,
+ &oc->signedCertTimestamps) != SECSuccess)
+ goto loser;
+ return sc;
+loser:
+ ssl_FreeServerCert(sc);
+ return NULL;
+}
+
+void
+ssl_FreeServerCert(sslServerCert *sc)
+{
+ if (!sc) {
+ return;
+ }
+
+ if (sc->serverCert) {
+ CERT_DestroyCertificate(sc->serverCert);
+ }
+ if (sc->serverCertChain) {
+ CERT_DestroyCertificateList(sc->serverCertChain);
+ }
+ if (sc->serverKeyPair) {
+ ssl_FreeKeyPair(sc->serverKeyPair);
+ }
+ if (sc->certStatusArray) {
+ SECITEM_FreeArray(sc->certStatusArray, PR_TRUE);
+ }
+ if (sc->signedCertTimestamps.len) {
+ SECITEM_FreeItem(&sc->signedCertTimestamps, PR_FALSE);
+ }
+ PORT_ZFree(sc, sizeof(*sc));
+}
+
+sslServerCert *
+ssl_FindServerCert(const sslSocket *ss,
+ const sslServerCertType *certType)
+{
+ PRCList *cursor;
+
+ for (cursor = PR_NEXT_LINK(&ss->serverCerts);
+ cursor != &ss->serverCerts;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslServerCert *cert = (sslServerCert *)cursor;
+ if (cert->certType.authType != certType->authType) {
+ continue;
+ }
+ switch (cert->certType.authType) {
+ case ssl_auth_ecdsa:
+ case ssl_auth_ecdh_rsa:
+ case ssl_auth_ecdh_ecdsa:
+ /* Note: For deprecated APIs, we need to be able to find and
+ match a slot with any named curve. */
+ if (certType->namedCurve &&
+ cert->certType.namedCurve != certType->namedCurve) {
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ return cert;
+ }
+ return NULL;
+}
+
+sslServerCert *
+ssl_FindServerCertByAuthType(const sslSocket *ss, SSLAuthType authType)
+{
+ sslServerCertType certType;
+ certType.authType = authType;
+ /* Setting the named curve to NULL ensures that all EC certificates
+ * are matched when searching for this slot. */
+ certType.namedCurve = NULL;
+ return ssl_FindServerCert(ss, &certType);
+}
+
+SECStatus
+ssl_OneTimeCertSetup(sslSocket *ss, const sslServerCert *sc)
+{
+ if (PR_SUCCESS != PR_CallOnceWithArg(&setupServerCAListOnce,
+ &serverCAListSetup,
+ (void *)(ss->dbHandle))) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* Determine which slot a certificate fits into. SSLAuthType is known, but
+ * extra information needs to be worked out from the cert and key. */
+static void
+ssl_PopulateCertType(sslServerCertType *certType, SSLAuthType authType,
+ CERTCertificate *cert, sslKeyPair *keyPair)
+{
+ certType->authType = authType;
+ switch (authType) {
+ case ssl_auth_ecdsa:
+ case ssl_auth_ecdh_rsa:
+ case ssl_auth_ecdh_ecdsa:
+ certType->namedCurve = ssl_ECPubKey2NamedGroup(keyPair->pubKey);
+ break;
+ default:
+ break;
+ }
+}
+
+static SECStatus
+ssl_PopulateServerCert(sslServerCert *sc, CERTCertificate *cert,
+ const CERTCertificateList *certChain)
+{
+ if (sc->serverCert) {
+ CERT_DestroyCertificate(sc->serverCert);
+ }
+ if (sc->serverCertChain) {
+ CERT_DestroyCertificateList(sc->serverCertChain);
+ }
+
+ if (!cert) {
+ sc->serverCert = NULL;
+ sc->serverCertChain = NULL;
+ return SECSuccess;
+ }
+
+ sc->serverCert = CERT_DupCertificate(cert);
+ if (certChain) {
+ sc->serverCertChain = CERT_DupCertList(certChain);
+ } else {
+ sc->serverCertChain =
+ CERT_CertChainFromCert(sc->serverCert, certUsageSSLServer,
+ PR_TRUE);
+ }
+ return sc->serverCertChain ? SECSuccess : SECFailure;
+}
+
+static SECStatus
+ssl_PopulateKeyPair(sslServerCert *sc, sslKeyPair *keyPair)
+{
+ /* Copy over the key pair. */
+ if (sc->serverKeyPair) {
+ ssl_FreeKeyPair(sc->serverKeyPair);
+ }
+ if (keyPair) {
+ /* Get the size of the cert's public key, and remember it. */
+ sc->serverKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->pubKey);
+ if (sc->serverKeyBits == 0) {
+ return SECFailure;
+ }
+
+ SECKEY_CacheStaticFlags(keyPair->privKey);
+ sc->serverKeyPair = ssl_GetKeyPairRef(keyPair);
+ } else {
+ sc->serverKeyPair = NULL;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+ssl_PopulateOCSPResponses(sslServerCert *sc,
+ const SECItemArray *stapledOCSPResponses)
+{
+ if (sc->certStatusArray) {
+ SECITEM_FreeArray(sc->certStatusArray, PR_TRUE);
+ }
+ if (stapledOCSPResponses) {
+ sc->certStatusArray = SECITEM_DupArray(NULL, stapledOCSPResponses);
+ return sc->certStatusArray ? SECSuccess : SECFailure;
+ } else {
+ sc->certStatusArray = NULL;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+ssl_PopulateSignedCertTimestamps(sslServerCert *sc,
+ const SECItem *signedCertTimestamps)
+{
+ if (sc->signedCertTimestamps.len) {
+ SECITEM_FreeItem(&sc->signedCertTimestamps, PR_FALSE);
+ }
+ if (signedCertTimestamps && signedCertTimestamps->len) {
+ return SECITEM_CopyItem(NULL, &sc->signedCertTimestamps,
+ signedCertTimestamps);
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+ssl_ConfigCert(sslSocket *ss, CERTCertificate *cert,
+ sslKeyPair *keyPair, const SSLExtraServerCertData *data)
+{
+ sslServerCert *oldsc;
+ sslServerCertType certType;
+ SECStatus rv;
+ sslServerCert *sc = NULL;
+ int error_code = SEC_ERROR_NO_MEMORY;
+
+ PORT_Assert(cert);
+ PORT_Assert(keyPair);
+ PORT_Assert(data);
+ PORT_Assert(data->authType != ssl_auth_null);
+
+ if (!cert || !keyPair || !data || data->authType == ssl_auth_null) {
+ error_code = SEC_ERROR_INVALID_ARGS;
+ goto loser;
+ }
+
+ ssl_PopulateCertType(&certType, data->authType, cert, keyPair);
+
+ /* Delete any existing certificate that matches this one, since we can only
+ * use one certificate of a given type. */
+ oldsc = ssl_FindServerCert(ss, &certType);
+ if (oldsc) {
+ PR_REMOVE_LINK(&oldsc->link);
+ ssl_FreeServerCert(oldsc);
+ }
+ sc = ssl_NewServerCert(&certType);
+ if (!sc) {
+ goto loser;
+ }
+
+ rv = ssl_PopulateServerCert(sc, cert, data->certChain);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = ssl_PopulateKeyPair(sc, keyPair);
+ if (rv != SECSuccess) {
+ error_code = SEC_ERROR_INVALID_ARGS;
+ goto loser;
+ }
+ rv = ssl_PopulateOCSPResponses(sc, data->stapledOCSPResponses);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = ssl_PopulateSignedCertTimestamps(sc, data->signedCertTimestamps);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ PR_APPEND_LINK(&sc->link, &ss->serverCerts);
+
+ /* This one-time setup depends on having the certificate in place. */
+ rv = ssl_OneTimeCertSetup(ss, sc);
+ if (rv != SECSuccess) {
+ PR_REMOVE_LINK(&sc->link);
+ error_code = PORT_GetError();
+ goto loser;
+ }
+ return SECSuccess;
+
+loser:
+ if (sc) {
+ ssl_FreeServerCert(sc);
+ }
+ /* This is the only way any of the calls above can fail, except the one time
+ * setup, which doesn't land here. */
+ PORT_SetError(error_code);
+ return SECFailure;
+}
+
+static SSLAuthType
+ssl_GetEcdhAuthType(CERTCertificate *cert)
+{
+ SECOidTag sigTag = SECOID_GetAlgorithmTag(&cert->signature);
+ switch (sigTag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
+ case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
+ return ssl_auth_ecdh_rsa;
+ case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
+ case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST:
+ case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST:
+ return ssl_auth_ecdh_ecdsa;
+ default:
+ return ssl_auth_null;
+ }
+}
+
+/* This function examines the key usages of the given RSA-PKCS1 certificate
+ * and configures one or multiple server certificates based on that data.
+ *
+ * If the data argument contains an authType value other than ssl_auth_null,
+ * then only that slot will be used. If that choice is invalid,
+ * then this will fail. */
+static SECStatus
+ssl_ConfigRsaPkcs1CertByUsage(sslSocket *ss, CERTCertificate *cert,
+ sslKeyPair *keyPair,
+ SSLExtraServerCertData *data)
+{
+ SECStatus rv = SECFailure;
+
+ PRBool ku_sig = (PRBool)(cert->keyUsage & KU_DIGITAL_SIGNATURE);
+ PRBool ku_enc = (PRBool)(cert->keyUsage & KU_KEY_ENCIPHERMENT);
+
+ if ((data->authType == ssl_auth_rsa_sign && ku_sig) ||
+ (data->authType == ssl_auth_rsa_pss && ku_sig) ||
+ (data->authType == ssl_auth_rsa_decrypt && ku_enc)) {
+ return ssl_ConfigCert(ss, cert, keyPair, data);
+ }
+
+ if (data->authType != ssl_auth_null || !(ku_sig || ku_enc)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (ku_sig) {
+ data->authType = ssl_auth_rsa_sign;
+ rv = ssl_ConfigCert(ss, cert, keyPair, data);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ /* This certificate is RSA, assume that it's also PSS. */
+ data->authType = ssl_auth_rsa_pss;
+ rv = ssl_ConfigCert(ss, cert, keyPair, data);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+
+ if (ku_enc) {
+ /* If ku_sig=true we configure signature and encryption slots with the
+ * same cert. This is bad form, but there are enough dual-usage RSA
+ * certs that we can't really break by limiting this to one type. */
+ data->authType = ssl_auth_rsa_decrypt;
+ rv = ssl_ConfigCert(ss, cert, keyPair, data);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+
+ return rv;
+}
+
+/* This function examines the type of certificate and its key usage and
+ * configures a certificate based on that information. For some certificates
+ * this can mean that multiple server certificates are configured.
+ *
+ * If the data argument contains an authType value other than ssl_auth_null,
+ * then only that slot will be used. If that choice is invalid,
+ * then this will fail. */
+static SECStatus
+ssl_ConfigCertByUsage(sslSocket *ss, CERTCertificate *cert,
+ sslKeyPair *keyPair, const SSLExtraServerCertData *data)
+{
+ SECStatus rv = SECFailure;
+ SSLExtraServerCertData arg;
+ SECOidTag tag;
+
+ PORT_Assert(data);
+ /* Take a (shallow) copy so that we can play with it */
+ memcpy(&arg, data, sizeof(arg));
+
+ tag = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm);
+ switch (tag) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ return ssl_ConfigRsaPkcs1CertByUsage(ss, cert, keyPair, &arg);
+
+ case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
+ if (cert->keyUsage & KU_DIGITAL_SIGNATURE) {
+ arg.authType = ssl_auth_rsa_pss;
+ }
+ break;
+
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ if (cert->keyUsage & KU_DIGITAL_SIGNATURE) {
+ arg.authType = ssl_auth_dsa;
+ }
+ break;
+
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ if (cert->keyUsage & KU_KEY_ENCIPHERMENT) {
+ if ((cert->keyUsage & KU_DIGITAL_SIGNATURE) &&
+ arg.authType == ssl_auth_null) {
+ /* See above regarding bad practice. */
+ arg.authType = ssl_auth_ecdsa;
+ rv = ssl_ConfigCert(ss, cert, keyPair, &arg);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+
+ arg.authType = ssl_GetEcdhAuthType(cert);
+ } else if (cert->keyUsage & KU_DIGITAL_SIGNATURE) {
+ arg.authType = ssl_auth_ecdsa;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check that we successfully picked an authType */
+ if (arg.authType == ssl_auth_null) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* |data->authType| has to either agree or be ssl_auth_null. */
+ if (data && data->authType != ssl_auth_null &&
+ data->authType != arg.authType) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return ssl_ConfigCert(ss, cert, keyPair, &arg);
+}
+
+/* This function adopts pubKey and destroys it if things go wrong. */
+static sslKeyPair *
+ssl_MakeKeyPairForCert(SECKEYPrivateKey *key, SECKEYPublicKey *pubKey)
+{
+ sslKeyPair *keyPair = NULL;
+ SECKEYPrivateKey *privKeyCopy = NULL;
+ PK11SlotInfo *bestSlot;
+
+ if (key->pkcs11Slot) {
+ bestSlot = PK11_ReferenceSlot(key->pkcs11Slot);
+ if (bestSlot) {
+ privKeyCopy = PK11_CopyTokenPrivKeyToSessionPrivKey(bestSlot, key);
+ PK11_FreeSlot(bestSlot);
+ }
+ }
+ if (!privKeyCopy) {
+ CK_MECHANISM_TYPE keyMech = PK11_MapSignKeyType(key->keyType);
+ /* XXX Maybe should be bestSlotMultiple? */
+ bestSlot = PK11_GetBestSlot(keyMech, NULL /* wincx */);
+ if (bestSlot) {
+ privKeyCopy = PK11_CopyTokenPrivKeyToSessionPrivKey(bestSlot, key);
+ PK11_FreeSlot(bestSlot);
+ }
+ }
+ if (!privKeyCopy) {
+ privKeyCopy = SECKEY_CopyPrivateKey(key);
+ }
+ if (privKeyCopy) {
+ keyPair = ssl_NewKeyPair(privKeyCopy, pubKey);
+ }
+ if (!keyPair) {
+ if (privKeyCopy) {
+ SECKEY_DestroyPrivateKey(privKeyCopy);
+ }
+ /* We adopted the public key, so we're responsible. */
+ if (pubKey) {
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ }
+ return keyPair;
+}
+
+/* Configure a certificate and private key.
+ *
+ * This function examines the certificate and key to determine which slot (or
+ * slots) to place the information in. As long as certificates are different
+ * (based on having different values of sslServerCertType), then this function
+ * can be called multiple times and the certificates will all be remembered.
+ */
+SECStatus
+SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert,
+ SECKEYPrivateKey *key,
+ const SSLExtraServerCertData *data, unsigned int data_len)
+{
+ sslSocket *ss;
+ SECKEYPublicKey *pubKey;
+ sslKeyPair *keyPair;
+ SECStatus rv;
+ SSLExtraServerCertData dataCopy = {
+ ssl_auth_null, NULL, NULL, NULL
+ };
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ if (!cert || !key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (data) {
+ if (data_len > sizeof(dataCopy)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ PORT_Memcpy(&dataCopy, data, data_len);
+ }
+
+ pubKey = CERT_ExtractPublicKey(cert);
+ if (!pubKey) {
+ return SECFailure;
+ }
+
+ keyPair = ssl_MakeKeyPairForCert(key, pubKey);
+ if (!keyPair) {
+ /* pubKey is adopted by ssl_MakeKeyPairForCert() */
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ rv = ssl_ConfigCertByUsage(ss, cert, keyPair, &dataCopy);
+ ssl_FreeKeyPair(keyPair);
+ return rv;
+}
+
+/*******************************************************************/
+/* Deprecated functions.
+ *
+ * The remainder of this file contains deprecated functions for server
+ * certificate configuration. These configure certificates incorrectly, but in
+ * a way that allows old code to continue working without change. All these
+ * functions create certificate slots based on SSLKEAType values. Some values
+ * of SSLKEAType cause multiple certificates to be configured.
+ */
+
+SECStatus
+SSL_ConfigSecureServer(PRFileDesc *fd, CERTCertificate *cert,
+ SECKEYPrivateKey *key, SSLKEAType kea)
+{
+ return SSL_ConfigSecureServerWithCertChain(fd, cert, NULL, key, kea);
+}
+
+/* This implements a limited check that is consistent with the checks performed
+ * by older versions of NSS. This is less rigorous than the checks in
+ * ssl_ConfigCertByUsage(), only checking against the type of key and ignoring
+ * things like usage. */
+static PRBool
+ssl_CertSuitableForAuthType(CERTCertificate *cert, SSLAuthType authType)
+{
+ SECOidTag tag = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm);
+ switch (authType) {
+ case ssl_auth_rsa_decrypt:
+ case ssl_auth_rsa_sign:
+ return tag == SEC_OID_X500_RSA_ENCRYPTION ||
+ tag == SEC_OID_PKCS1_RSA_ENCRYPTION;
+ case ssl_auth_dsa:
+ return tag == SEC_OID_ANSIX9_DSA_SIGNATURE;
+ case ssl_auth_ecdsa:
+ case ssl_auth_ecdh_rsa:
+ case ssl_auth_ecdh_ecdsa:
+ return tag == SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+ case ssl_auth_null:
+ case ssl_auth_kea:
+ case ssl_auth_rsa_pss: /* not supported with deprecated APIs */
+ return PR_FALSE;
+ default:
+ PORT_Assert(0);
+ return PR_FALSE;
+ }
+}
+
+/* This finds an existing server cert slot and unlinks it, or it makes a new
+ * server cert slot of the right type. */
+static sslServerCert *
+ssl_FindOrMakeCertType(sslSocket *ss, SSLAuthType authType)
+{
+ sslServerCert *sc;
+ sslServerCertType certType;
+
+ certType.authType = authType;
+ /* Setting the named curve to NULL ensures that all EC certificates
+ * are matched when searching for this slot. */
+ certType.namedCurve = NULL;
+ sc = ssl_FindServerCert(ss, &certType);
+ if (sc) {
+ PR_REMOVE_LINK(&sc->link);
+ return sc;
+ }
+
+ return ssl_NewServerCert(&certType);
+}
+
+static void
+ssl_RemoveCertAndKeyByAuthType(sslSocket *ss, SSLAuthType authType)
+{
+ sslServerCert *sc;
+
+ sc = ssl_FindServerCertByAuthType(ss, authType);
+ if (sc) {
+ (void)ssl_PopulateServerCert(sc, NULL, NULL);
+ (void)ssl_PopulateKeyPair(sc, NULL);
+ /* Leave the entry linked here because the old API expects that. There
+ * might be OCSP stapling values or signed certificate timestamps still
+ * present that will subsequently be used. */
+ /* For ECC certificates, also leave the namedCurve parameter on the slot
+ * unchanged; the value will be updated when a key is added. */
+ }
+}
+
+static SECStatus
+ssl_AddCertAndKeyByAuthType(sslSocket *ss, SSLAuthType authType,
+ CERTCertificate *cert,
+ const CERTCertificateList *certChainOpt,
+ sslKeyPair *keyPair)
+{
+ sslServerCert *sc;
+ SECStatus rv;
+
+ if (!ssl_CertSuitableForAuthType(cert, authType)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ sc = ssl_FindOrMakeCertType(ss, authType);
+ if (!sc) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ rv = ssl_PopulateKeyPair(sc, keyPair);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ /* Now that we have a key pair, update the details of the slot. Many of the
+ * legacy functions create a slot with a namedCurve of NULL, which
+ * makes the slot unusable; this corrects that. */
+ ssl_PopulateCertType(&sc->certType, authType, cert, keyPair);
+ rv = ssl_PopulateServerCert(sc, cert, certChainOpt);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PR_APPEND_LINK(&sc->link, &ss->serverCerts);
+ return ssl_OneTimeCertSetup(ss, sc);
+loser:
+ ssl_FreeServerCert(sc);
+ return SECFailure;
+}
+
+static SECStatus
+ssl_AddCertsByKEA(sslSocket *ss, CERTCertificate *cert,
+ const CERTCertificateList *certChainOpt,
+ SECKEYPrivateKey *key, SSLKEAType certType)
+{
+ SECKEYPublicKey *pubKey;
+ sslKeyPair *keyPair;
+ SECStatus rv;
+
+ pubKey = CERT_ExtractPublicKey(cert);
+ if (!pubKey) {
+ return SECFailure;
+ }
+
+ keyPair = ssl_MakeKeyPairForCert(key, pubKey);
+ if (!keyPair) {
+ /* Note: pubKey is adopted or freed by ssl_MakeKeyPairForCert()
+ * depending on whether it succeeds or not. */
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ switch (certType) {
+ case ssl_kea_rsa:
+ rv = ssl_AddCertAndKeyByAuthType(ss, ssl_auth_rsa_decrypt,
+ cert, certChainOpt, keyPair);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = ssl_AddCertAndKeyByAuthType(ss, ssl_auth_rsa_sign,
+ cert, certChainOpt, keyPair);
+ break;
+
+ case ssl_kea_dh:
+ rv = ssl_AddCertAndKeyByAuthType(ss, ssl_auth_dsa,
+ cert, certChainOpt, keyPair);
+ break;
+
+ case ssl_kea_ecdh:
+ rv = ssl_AddCertAndKeyByAuthType(ss, ssl_auth_ecdsa,
+ cert, certChainOpt, keyPair);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = ssl_AddCertAndKeyByAuthType(ss, ssl_GetEcdhAuthType(cert),
+ cert, certChainOpt, keyPair);
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ break;
+ }
+
+ ssl_FreeKeyPair(keyPair);
+ return rv;
+}
+
+/* Public deprecated function */
+SECStatus
+SSL_ConfigSecureServerWithCertChain(PRFileDesc *fd, CERTCertificate *cert,
+ const CERTCertificateList *certChainOpt,
+ SECKEYPrivateKey *key, SSLKEAType certType)
+{
+ sslSocket *ss;
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ return SECFailure;
+ }
+
+ if (!cert != !key) { /* Configure both, or neither */
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!cert) {
+ switch (certType) {
+ case ssl_kea_rsa:
+ ssl_RemoveCertAndKeyByAuthType(ss, ssl_auth_rsa_decrypt);
+ ssl_RemoveCertAndKeyByAuthType(ss, ssl_auth_rsa_sign);
+ break;
+
+ case ssl_kea_dh:
+ ssl_RemoveCertAndKeyByAuthType(ss, ssl_auth_dsa);
+ break;
+
+ case ssl_kea_ecdh:
+ ssl_RemoveCertAndKeyByAuthType(ss, ssl_auth_ecdsa);
+ ssl_RemoveCertAndKeyByAuthType(ss, ssl_auth_ecdh_rsa);
+ ssl_RemoveCertAndKeyByAuthType(ss, ssl_auth_ecdh_ecdsa);
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+
+ return ssl_AddCertsByKEA(ss, cert, certChainOpt, key, certType);
+}
+
+static SECStatus
+ssl_SetOCSPResponsesInSlot(sslSocket *ss, SSLAuthType authType,
+ const SECItemArray *responses)
+{
+ sslServerCert *sc;
+ SECStatus rv;
+
+ sc = ssl_FindOrMakeCertType(ss, authType);
+ if (!sc) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ rv = ssl_PopulateOCSPResponses(sc, responses);
+ if (rv == SECSuccess) {
+ PR_APPEND_LINK(&sc->link, &ss->serverCerts);
+ } else {
+ ssl_FreeServerCert(sc);
+ }
+ return rv;
+}
+
+/* Public deprecated function */
+SECStatus
+SSL_SetStapledOCSPResponses(PRFileDesc *fd, const SECItemArray *responses,
+ SSLKEAType certType)
+{
+ sslSocket *ss;
+ SECStatus rv;
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetStapledOCSPResponses",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ switch (certType) {
+ case ssl_kea_rsa:
+ rv = ssl_SetOCSPResponsesInSlot(ss, ssl_auth_rsa_decrypt, responses);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ return ssl_SetOCSPResponsesInSlot(ss, ssl_auth_rsa_sign, responses);
+
+ case ssl_kea_dh:
+ return ssl_SetOCSPResponsesInSlot(ss, ssl_auth_dsa, responses);
+
+ case ssl_kea_ecdh:
+ rv = ssl_SetOCSPResponsesInSlot(ss, ssl_auth_ecdsa, responses);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = ssl_SetOCSPResponsesInSlot(ss, ssl_auth_ecdh_rsa, responses);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ return ssl_SetOCSPResponsesInSlot(ss, ssl_auth_ecdh_ecdsa, responses);
+
+ default:
+ SSL_DBG(("%d: SSL[%d]: invalid cert type in SSL_SetStapledOCSPResponses",
+ SSL_GETPID(), fd));
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+}
+
+static SECStatus
+ssl_SetSignedTimestampsInSlot(sslSocket *ss, SSLAuthType authType,
+ const SECItem *scts)
+{
+ sslServerCert *sc;
+ SECStatus rv;
+
+ sc = ssl_FindOrMakeCertType(ss, authType);
+ if (!sc) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ rv = ssl_PopulateSignedCertTimestamps(sc, scts);
+ if (rv == SECSuccess) {
+ PR_APPEND_LINK(&sc->link, &ss->serverCerts);
+ } else {
+ ssl_FreeServerCert(sc);
+ }
+ return rv;
+}
+
+/* Public deprecated function */
+SECStatus
+SSL_SetSignedCertTimestamps(PRFileDesc *fd, const SECItem *scts,
+ SSLKEAType certType)
+{
+ sslSocket *ss;
+ SECStatus rv;
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetSignedCertTimestamps",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ switch (certType) {
+ case ssl_kea_rsa:
+ rv = ssl_SetSignedTimestampsInSlot(ss, ssl_auth_rsa_decrypt, scts);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ return ssl_SetSignedTimestampsInSlot(ss, ssl_auth_rsa_sign, scts);
+
+ case ssl_kea_dh:
+ return ssl_SetSignedTimestampsInSlot(ss, ssl_auth_dsa, scts);
+
+ case ssl_kea_ecdh:
+ rv = ssl_SetSignedTimestampsInSlot(ss, ssl_auth_ecdsa, scts);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = ssl_SetSignedTimestampsInSlot(ss, ssl_auth_ecdh_rsa, scts);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ return ssl_SetSignedTimestampsInSlot(ss, ssl_auth_ecdh_ecdsa, scts);
+
+ default:
+ SSL_DBG(("%d: SSL[%d]: invalid cert type in SSL_SetSignedCertTimestamps",
+ SSL_GETPID(), fd));
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+}
+
+/* Public deprecated function. */
+SSLKEAType
+NSS_FindCertKEAType(CERTCertificate *cert)
+{
+ int tag;
+
+ if (!cert)
+ return ssl_kea_null;
+
+ tag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
+ switch (tag) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ return ssl_kea_rsa;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE: /* hah, signature, not a key? */
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ return ssl_kea_dh;
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ return ssl_kea_ecdh;
+ default:
+ return ssl_kea_null;
+ }
+}