diff options
Diffstat (limited to 'security/nss/lib/ssl/sslcert.c')
-rw-r--r-- | security/nss/lib/ssl/sslcert.c | 992 |
1 files changed, 992 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslcert.c b/security/nss/lib/ssl/sslcert.c new file mode 100644 index 000000000..ea524552d --- /dev/null +++ b/security/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; + } +} |