diff options
Diffstat (limited to 'security/nss/lib/ssl/sslcert.c')
-rw-r--r-- | security/nss/lib/ssl/sslcert.c | 1016 |
1 files changed, 1016 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslcert.c b/security/nss/lib/ssl/sslcert.c new file mode 100644 index 0000000000..0fa6ca3538 --- /dev/null +++ b/security/nss/lib/ssl/sslcert.c @@ -0,0 +1,1016 @@ +/* -*- 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 */ +#include "tls13subcerts.h" /* for tls13_ReadDelegatedCredential */ + +/* This global item is used only in servers. It is is initialized by + * SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest(). + */ +static struct { + PRCallOnceType setup; + CERTDistNames *names; +} ssl_server_ca_list; + +static SECStatus +ssl_ServerCAListShutdown(void *appData, void *nssData) +{ + PORT_Assert(ssl_server_ca_list.names); + if (ssl_server_ca_list.names) { + CERT_FreeDistNames(ssl_server_ca_list.names); + } + PORT_Memset(&ssl_server_ca_list, 0, sizeof(ssl_server_ca_list)); + return SECSuccess; +} + +static PRStatus +ssl_SetupCAListOnce(void *arg) +{ + CERTCertDBHandle *dbHandle = (CERTCertDBHandle *)arg; + SECStatus rv = NSS_RegisterShutdown(ssl_ServerCAListShutdown, NULL); + PORT_Assert(SECSuccess == rv); + if (SECSuccess == rv) { + ssl_server_ca_list.names = CERT_GetSSLCACerts(dbHandle); + return PR_SUCCESS; + } + return PR_FAILURE; +} + +SECStatus +ssl_SetupCAList(const sslSocket *ss) +{ + if (PR_SUCCESS != PR_CallOnceWithArg(&ssl_server_ca_list.setup, + &ssl_SetupCAListOnce, + (void *)(ss->dbHandle))) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +ssl_GetCertificateRequestCAs(const sslSocket *ss, unsigned int *calen, + const SECItem **names, unsigned int *nnames) +{ + const SECItem *name; + const CERTDistNames *ca_list; + unsigned int i; + + *calen = 0; + *names = NULL; + *nnames = 0; + + /* ssl3.ca_list is initialized to NULL, and never changed. */ + ca_list = ss->ssl3.ca_list; + if (!ca_list) { + if (ssl_SetupCAList(ss) != SECSuccess) { + return SECFailure; + } + ca_list = ssl_server_ca_list.names; + } + + if (ca_list != NULL) { + *names = ca_list->names; + *nnames = ca_list->nnames; + } + + for (i = 0, name = *names; i < *nnames; i++, name++) { + *calen += 2 + name->len; + } + return SECSuccess; +} + +sslServerCert * +ssl_NewServerCert() +{ + sslServerCert *sc = PORT_ZNew(sslServerCert); + if (!sc) { + return NULL; + } + sc->authTypes = 0; + sc->namedCurve = NULL; + sc->serverCert = NULL; + sc->serverCertChain = NULL; + sc->certStatusArray = NULL; + sc->signedCertTimestamps.len = 0; + sc->delegCred.len = 0; + sc->delegCredKeyPair = NULL; + return sc; +} + +sslServerCert * +ssl_CopyServerCert(const sslServerCert *oc) +{ + sslServerCert *sc; + + sc = ssl_NewServerCert(); + if (!sc) { + return NULL; + } + + sc->authTypes = oc->authTypes; + sc->namedCurve = oc->namedCurve; + + 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; + } + + if (SECITEM_CopyItem(NULL, &sc->delegCred, &oc->delegCred) != SECSuccess) { + goto loser; + } + if (oc->delegCredKeyPair) { + sc->delegCredKeyPair = ssl_GetKeyPairRef(oc->delegCredKeyPair); + } + + 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); + } + if (sc->delegCred.len) { + SECITEM_FreeItem(&sc->delegCred, PR_FALSE); + } + if (sc->delegCredKeyPair) { + ssl_FreeKeyPair(sc->delegCredKeyPair); + } + PORT_ZFree(sc, sizeof(*sc)); +} + +const sslServerCert * +ssl_FindServerCert(const sslSocket *ss, SSLAuthType authType, + const sslNamedGroupDef *namedCurve) +{ + PRCList *cursor; + + /* Bug 1749475: avoid UB while fuzzing session tickets */ + if ((unsigned)authType >= ssl_auth_size) { + return NULL; + } + + for (cursor = PR_NEXT_LINK(&ss->serverCerts); + cursor != &ss->serverCerts; + cursor = PR_NEXT_LINK(cursor)) { + sslServerCert *cert = (sslServerCert *)cursor; + if (!SSL_CERT_IS(cert, authType)) { + continue; + } + if (SSL_CERT_IS_EC(cert)) { + /* Note: For deprecated APIs, we need to be able to find and + match a slot with any named curve. */ + if (namedCurve && cert->namedCurve != namedCurve) { + continue; + } + } + return cert; + } + return NULL; +} + +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) +{ + if (sc->serverKeyPair) { + ssl_FreeKeyPair(sc->serverKeyPair); + sc->serverKeyPair = NULL; + } + if (keyPair) { + KeyType keyType = SECKEY_GetPublicKeyType(keyPair->pubKey); + PORT_Assert(keyType == SECKEY_GetPrivateKeyType(keyPair->privKey)); + + if (keyType == ecKey) { + sc->namedCurve = ssl_ECPubKey2NamedGroup(keyPair->pubKey); + if (!sc->namedCurve) { + /* Unsupported curve. */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + } + + /* Get the size of the cert's public key, and remember it. */ + sc->serverKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->pubKey); + if (sc->serverKeyBits == 0 || + (keyType == rsaKey && sc->serverKeyBits > SSL_MAX_RSA_KEY_BITS)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + SECKEY_CacheStaticFlags(keyPair->privKey); + sc->serverKeyPair = ssl_GetKeyPairRef(keyPair); + + if (SSL_CERT_IS(sc, ssl_auth_rsa_decrypt)) { + /* This will update the global session ticket key pair with this + * key, if a value hasn't been set already. */ + if (ssl_MaybeSetSelfEncryptKeyPair(keyPair) != SECSuccess) { + return SECFailure; + } + } + } else { + sc->serverKeyPair = NULL; + sc->namedCurve = 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; +} + +/* Installs the given delegated credential (DC) and DC private key into the + * certificate. + * + * It's the caller's responsibility to ensure that the DC is well-formed and + * that the DC public key matches the DC private key. + */ +static SECStatus +ssl_PopulateDelegatedCredential(sslServerCert *sc, + const SECItem *delegCred, + const SECKEYPrivateKey *delegCredPrivKey) +{ + sslDelegatedCredential *dc = NULL; + + if (sc->delegCred.len) { + SECITEM_FreeItem(&sc->delegCred, PR_FALSE); + } + + if (sc->delegCredKeyPair) { + ssl_FreeKeyPair(sc->delegCredKeyPair); + sc->delegCredKeyPair = NULL; + } + + /* Both the DC and its private are present. */ + if (delegCred && delegCredPrivKey) { + SECStatus rv; + SECKEYPublicKey *pub; + SECKEYPrivateKey *priv; + + if (!delegCred->data || delegCred->len == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + + /* Parse the DC. */ + rv = tls13_ReadDelegatedCredential(delegCred->data, delegCred->len, &dc); + if (rv != SECSuccess) { + goto loser; + } + + /* Make a copy of the DC. */ + rv = SECITEM_CopyItem(NULL, &sc->delegCred, delegCred); + if (rv != SECSuccess) { + goto loser; + } + + /* Make a copy of the DC private key. */ + priv = SECKEY_CopyPrivateKey(delegCredPrivKey); + if (!priv) { + goto loser; + } + + /* parse public key from the DC. */ + pub = SECKEY_ExtractPublicKey(dc->spki); + if (!pub) { + goto loser; + } + + sc->delegCredKeyPair = ssl_NewKeyPair(priv, pub); + + /* Attempting to configure either the DC or DC private key, but not both. */ + } else if (delegCred || delegCredPrivKey) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + + tls13_DestroyDelegatedCredential(dc); + return SECSuccess; + +loser: + tls13_DestroyDelegatedCredential(dc); + return SECFailure; +} + +/* Find any existing certificates that overlap with the new certificate and + * either remove any supported authentication types that overlap with the new + * certificate or - if they have no types left - remove them entirely. */ +static void +ssl_ClearMatchingCerts(sslSocket *ss, sslAuthTypeMask authTypes, + const sslNamedGroupDef *namedCurve) +{ + PRCList *cursor = PR_NEXT_LINK(&ss->serverCerts); + + while (cursor != &ss->serverCerts) { + sslServerCert *sc = (sslServerCert *)cursor; + cursor = PR_NEXT_LINK(cursor); + if ((sc->authTypes & authTypes) == 0) { + continue; + } + /* namedCurve will be NULL only for legacy functions. */ + if (namedCurve != NULL && sc->namedCurve != namedCurve) { + continue; + } + + sc->authTypes &= ~authTypes; + if (sc->authTypes == 0) { + PR_REMOVE_LINK(&sc->link); + ssl_FreeServerCert(sc); + } + } +} + +static SECStatus +ssl_ConfigCert(sslSocket *ss, sslAuthTypeMask authTypes, + CERTCertificate *cert, sslKeyPair *keyPair, + const SSLExtraServerCertData *data) +{ + SECStatus rv; + sslServerCert *sc = NULL; + int error_code = SEC_ERROR_NO_MEMORY; + + PORT_Assert(cert); + PORT_Assert(keyPair); + PORT_Assert(data); + PORT_Assert(authTypes); + + if (!cert || !keyPair || !data || !authTypes) { + error_code = SEC_ERROR_INVALID_ARGS; + goto loser; + } + + sc = ssl_NewServerCert(); + if (!sc) { + goto loser; + } + + sc->authTypes = authTypes; + rv = ssl_PopulateServerCert(sc, cert, data->certChain); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl_PopulateKeyPair(sc, keyPair); + if (rv != SECSuccess) { + error_code = PORT_GetError(); + goto loser; + } + rv = ssl_PopulateOCSPResponses(sc, data->stapledOCSPResponses); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl_PopulateSignedCertTimestamps(sc, data->signedCertTimestamps); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl_PopulateDelegatedCredential(sc, data->delegCred, + data->delegCredPrivKey); + if (rv != SECSuccess) { + error_code = PORT_GetError(); + goto loser; + } + ssl_ClearMatchingCerts(ss, sc->authTypes, sc->namedCurve); + PR_APPEND_LINK(&sc->link, &ss->serverCerts); + return SECSuccess; + +loser: + ssl_FreeServerCert(sc); + 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 type of certificate and its key usage and + * chooses which authTypes apply. For some certificates + * this can mean that multiple authTypes. + * + * If the targetAuthType is not ssl_auth_null, then only that type will be used. + * If that choice is invalid, then this function will fail. */ +static sslAuthTypeMask +ssl_GetCertificateAuthTypes(CERTCertificate *cert, SSLAuthType targetAuthType) +{ + sslAuthTypeMask authTypes = 0; + SECOidTag tag; + + tag = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm); + switch (tag) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + if (cert->keyUsage & KU_DIGITAL_SIGNATURE) { + authTypes |= 1 << ssl_auth_rsa_sign; + } + + if (cert->keyUsage & KU_KEY_ENCIPHERMENT) { + /* 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. */ + authTypes |= 1 << ssl_auth_rsa_decrypt; + } + break; + + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + if (cert->keyUsage & KU_DIGITAL_SIGNATURE) { + authTypes |= 1 << ssl_auth_rsa_pss; + } + break; + + case SEC_OID_ANSIX9_DSA_SIGNATURE: + if (cert->keyUsage & KU_DIGITAL_SIGNATURE) { + authTypes |= 1 << ssl_auth_dsa; + } + break; + + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + if (cert->keyUsage & KU_DIGITAL_SIGNATURE) { + authTypes |= 1 << ssl_auth_ecdsa; + } + /* Again, bad form to have dual usage and we don't prevent it. */ + if (cert->keyUsage & KU_KEY_ENCIPHERMENT) { + authTypes |= 1 << ssl_GetEcdhAuthType(cert); + } + break; + + default: + break; + } + + /* Check that we successfully picked an authType */ + if (targetAuthType != ssl_auth_null) { + authTypes &= 1 << targetAuthType; + } + return authTypes; +} + +/* This function adopts pubKey and destroys it if things go wrong. */ +static sslKeyPair * +ssl_MakeKeyPairForCert(SECKEYPrivateKey *key, CERTCertificate *cert) +{ + sslKeyPair *keyPair = NULL; + SECKEYPublicKey *pubKey = NULL; + SECKEYPrivateKey *privKeyCopy = NULL; + PK11SlotInfo *bestSlot; + + pubKey = CERT_ExtractPublicKey(cert); + if (!pubKey) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if (SECKEY_GetPublicKeyType(pubKey) != SECKEY_GetPrivateKeyType(key)) { + SECKEY_DestroyPublicKey(pubKey); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + 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); + } + SECKEY_DestroyPublicKey(pubKey); + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + return keyPair; +} + +/* Configure a certificate and private key. + * + * This function examines the certificate and key to determine the type (or + * types) of authentication the certificate supports. As long as certificates + * are different (different authTypes and maybe keys in different ec groups), + * then this function can be called multiple times. + */ +SECStatus +SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert, + SECKEYPrivateKey *key, + const SSLExtraServerCertData *data, unsigned int data_len) +{ + sslSocket *ss; + sslKeyPair *keyPair; + SECStatus rv; + SSLExtraServerCertData dataCopy = { + ssl_auth_null, NULL, NULL, NULL, NULL, NULL + }; + sslAuthTypeMask authTypes; + + 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); + } + + authTypes = ssl_GetCertificateAuthTypes(cert, dataCopy.authType); + if (!authTypes) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + keyPair = ssl_MakeKeyPairForCert(key, cert); + if (!keyPair) { + return SECFailure; + } + + rv = ssl_ConfigCert(ss, authTypes, cert, keyPair, &dataCopy); + ssl_FreeKeyPair(keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + return SECSuccess; +} + +/*******************************************************************/ +/* 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, sslAuthTypeMask authTypes) +{ + SECOidTag tag = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm); + sslAuthTypeMask mask = 0; + switch (tag) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + mask |= 1 << ssl_auth_rsa_decrypt; + mask |= 1 << ssl_auth_rsa_sign; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + mask |= 1 << ssl_auth_dsa; + break; + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + mask |= 1 << ssl_auth_ecdsa; + mask |= 1 << ssl_auth_ecdh_rsa; + mask |= 1 << ssl_auth_ecdh_ecdsa; + break; + default: + break; + } + PORT_Assert(authTypes); + /* Simply test that no inappropriate auth types are set. */ + return (authTypes & ~mask) == 0; +} + +/* Lookup a cert for the legacy configuration functions. An exact match on + * authTypes and ignoring namedCurve will ensure that values configured using + * legacy functions are overwritten by other legacy functions. */ +static sslServerCert * +ssl_FindCertWithMask(sslSocket *ss, sslAuthTypeMask authTypes) +{ + PRCList *cursor; + + for (cursor = PR_NEXT_LINK(&ss->serverCerts); + cursor != &ss->serverCerts; + cursor = PR_NEXT_LINK(cursor)) { + sslServerCert *cert = (sslServerCert *)cursor; + if (cert->authTypes == authTypes) { + return cert; + } + } + return NULL; +} + +/* This finds an existing server cert in a matching slot that can be reused. + * Failing that, it removes any other certs that might conflict and makes a new + * server cert slot of the right type. */ +static sslServerCert * +ssl_FindOrMakeCert(sslSocket *ss, sslAuthTypeMask authTypes) +{ + sslServerCert *sc; + + /* Reuse a perfect match. Note that there is a problem here with use of + * multiple EC certificates that have keys on different curves: these + * deprecated functions will match the first found and overwrite that + * certificate, potentially leaving the other values with a duplicate curve. + * Configuring multiple EC certificates are only possible with the new + * functions, so this is not something that is worth fixing. */ + sc = ssl_FindCertWithMask(ss, authTypes); + if (sc) { + PR_REMOVE_LINK(&sc->link); + return sc; + } + + /* Ignore the namedCurve parameter. Like above, this means that legacy + * functions will clobber values set with the new functions blindly. */ + ssl_ClearMatchingCerts(ss, authTypes, NULL); + + sc = ssl_NewServerCert(); + if (sc) { + sc->authTypes = authTypes; + } + return sc; +} + +static sslAuthTypeMask +ssl_KeaTypeToAuthTypeMask(SSLKEAType keaType) +{ + switch (keaType) { + case ssl_kea_rsa: + return (1 << ssl_auth_rsa_decrypt) | + (1 << ssl_auth_rsa_sign); + + case ssl_kea_dh: + return 1 << ssl_auth_dsa; + + case ssl_kea_ecdh: + return (1 << ssl_auth_ecdsa) | + (1 << ssl_auth_ecdh_rsa) | + (1 << ssl_auth_ecdh_ecdsa); + + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + } + return 0; +} + +static SECStatus +ssl_AddCertChain(sslSocket *ss, CERTCertificate *cert, + const CERTCertificateList *certChainOpt, + SECKEYPrivateKey *key, sslAuthTypeMask authTypes) +{ + sslServerCert *sc; + sslKeyPair *keyPair; + SECStatus rv; + PRErrorCode err = SEC_ERROR_NO_MEMORY; + + if (!ssl_CertSuitableForAuthType(cert, authTypes)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + sc = ssl_FindOrMakeCert(ss, authTypes); + if (!sc) { + goto loser; + } + + rv = ssl_PopulateServerCert(sc, cert, certChainOpt); + if (rv != SECSuccess) { + goto loser; + } + + keyPair = ssl_MakeKeyPairForCert(key, cert); + if (!keyPair) { + /* Error code is set by ssl_MakeKeyPairForCert */ + goto loser; + } + rv = ssl_PopulateKeyPair(sc, keyPair); + ssl_FreeKeyPair(keyPair); + if (rv != SECSuccess) { + err = PORT_GetError(); + goto loser; + } + + PR_APPEND_LINK(&sc->link, &ss->serverCerts); + return SECSuccess; + +loser: + ssl_FreeServerCert(sc); + PORT_SetError(err); + return SECFailure; +} + +/* Public deprecated function */ +SECStatus +SSL_ConfigSecureServerWithCertChain(PRFileDesc *fd, CERTCertificate *cert, + const CERTCertificateList *certChainOpt, + SECKEYPrivateKey *key, SSLKEAType certType) +{ + sslSocket *ss; + sslAuthTypeMask authTypes; + + ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + if (!cert != !key) { /* Configure both, or neither */ + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + authTypes = ssl_KeaTypeToAuthTypeMask(certType); + if (!authTypes) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (!cert) { + sslServerCert *sc = ssl_FindCertWithMask(ss, authTypes); + 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. */ + } + return SECSuccess; + } + + return ssl_AddCertChain(ss, cert, certChainOpt, key, authTypes); +} + +/* Public deprecated function */ +SECStatus +SSL_SetStapledOCSPResponses(PRFileDesc *fd, const SECItemArray *responses, + SSLKEAType certType) +{ + sslSocket *ss; + sslServerCert *sc; + sslAuthTypeMask authTypes; + SECStatus rv; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetStapledOCSPResponses", + SSL_GETPID(), fd)); + return SECFailure; + } + + authTypes = ssl_KeaTypeToAuthTypeMask(certType); + if (!authTypes) { + SSL_DBG(("%d: SSL[%d]: invalid cert type in SSL_SetStapledOCSPResponses", + SSL_GETPID(), fd)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (!responses) { + sc = ssl_FindCertWithMask(ss, authTypes); + if (sc) { + (void)ssl_PopulateOCSPResponses(sc, NULL); + } + return SECSuccess; + } + + sc = ssl_FindOrMakeCert(ss, authTypes); + if (!sc) { + 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_SetSignedCertTimestamps(PRFileDesc *fd, const SECItem *scts, + SSLKEAType certType) +{ + sslSocket *ss; + sslServerCert *sc; + sslAuthTypeMask authTypes; + SECStatus rv; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetSignedCertTimestamps", + SSL_GETPID(), fd)); + return SECFailure; + } + + authTypes = ssl_KeaTypeToAuthTypeMask(certType); + if (!authTypes) { + SSL_DBG(("%d: SSL[%d]: invalid cert type in SSL_SetSignedCertTimestamps", + SSL_GETPID(), fd)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (!scts) { + sc = ssl_FindCertWithMask(ss, authTypes); + if (sc) { + (void)ssl_PopulateSignedCertTimestamps(sc, NULL); + } + return SECSuccess; + } + + sc = ssl_FindOrMakeCert(ss, authTypes); + if (!sc) { + 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. */ +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; + } +} |