summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/sslcert.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/sslcert.c')
-rw-r--r--security/nss/lib/ssl/sslcert.c785
1 files changed, 354 insertions, 431 deletions
diff --git a/security/nss/lib/ssl/sslcert.c b/security/nss/lib/ssl/sslcert.c
index ea524552d..cc1d3c683 100644
--- a/security/nss/lib/ssl/sslcert.c
+++ b/security/nss/lib/ssl/sslcert.c
@@ -13,42 +13,91 @@
#include "nss.h" /* for NSS_RegisterShutdown */
#include "prinit.h" /* for PR_CallOnceWithArg */
-static const PRCallOnceType pristineCallOnce;
-static PRCallOnceType setupServerCAListOnce;
+/* 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
-serverCAListShutdown(void *appData, void *nssData)
+ssl_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;
+ PORT_Assert(ssl_server_ca_list.names);
+ if (ssl_server_ca_list.names) {
+ CERT_FreeDistNames(ssl_server_ca_list.names);
}
- setupServerCAListOnce = pristineCallOnce;
+ PORT_Memset(&ssl_server_ca_list, 0, sizeof(ssl_server_ca_list));
return SECSuccess;
}
static PRStatus
-serverCAListSetup(void *arg)
+ssl_SetupCAListOnce(void *arg)
{
CERTCertDBHandle *dbHandle = (CERTCertDBHandle *)arg;
- SECStatus rv = NSS_RegisterShutdown(serverCAListShutdown, NULL);
+ SECStatus rv = NSS_RegisterShutdown(ssl_ServerCAListShutdown, NULL);
PORT_Assert(SECSuccess == rv);
if (SECSuccess == rv) {
- ssl3_server_ca_list = CERT_GetSSLCACerts(dbHandle);
+ ssl_server_ca_list.names = CERT_GetSSLCACerts(dbHandle);
return PR_SUCCESS;
}
return PR_FAILURE;
}
+SECStatus
+ssl_SetupCAList(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(sslSocket *ss, unsigned int *calen,
+ SECItem **names, unsigned int *nnames)
+{
+ SECItem *name;
+ 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(const sslServerCertType *certType)
+ssl_NewServerCert()
{
sslServerCert *sc = PORT_ZNew(sslServerCert);
if (!sc) {
return NULL;
}
- memcpy(&sc->certType, certType, sizeof(sc->certType));
+ sc->authTypes = 0;
+ sc->namedCurve = NULL;
sc->serverCert = NULL;
sc->serverCertChain = NULL;
sc->certStatusArray = NULL;
@@ -61,11 +110,14 @@ ssl_CopyServerCert(const sslServerCert *oc)
{
sslServerCert *sc;
- sc = ssl_NewServerCert(&oc->certType);
+ 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)
@@ -129,9 +181,9 @@ ssl_FreeServerCert(sslServerCert *sc)
PORT_ZFree(sc, sizeof(*sc));
}
-sslServerCert *
-ssl_FindServerCert(const sslSocket *ss,
- const sslServerCertType *certType)
+const sslServerCert *
+ssl_FindServerCert(const sslSocket *ss, SSLAuthType authType,
+ const sslNamedGroupDef *namedCurve)
{
PRCList *cursor;
@@ -139,68 +191,21 @@ ssl_FindServerCert(const sslSocket *ss,
cursor != &ss->serverCerts;
cursor = PR_NEXT_LINK(cursor)) {
sslServerCert *cert = (sslServerCert *)cursor;
- if (cert->certType.authType != certType->authType) {
+ if (!SSL_CERT_IS(cert, 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;
+ 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;
}
-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)
@@ -232,21 +237,43 @@ ssl_PopulateServerCert(sslServerCert *sc, CERTCertificate *cert,
static SECStatus
ssl_PopulateKeyPair(sslServerCert *sc, sslKeyPair *keyPair)
{
- /* Copy over the key pair. */
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) {
+ 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;
}
@@ -281,12 +308,39 @@ ssl_PopulateSignedCertTimestamps(sslServerCert *sc,
return SECSuccess;
}
+/* 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, CERTCertificate *cert,
- sslKeyPair *keyPair, const SSLExtraServerCertData *data)
+ssl_ConfigCert(sslSocket *ss, sslAuthTypeMask authTypes,
+ CERTCertificate *cert, sslKeyPair *keyPair,
+ const SSLExtraServerCertData *data)
{
- sslServerCert *oldsc;
- sslServerCertType certType;
SECStatus rv;
sslServerCert *sc = NULL;
int error_code = SEC_ERROR_NO_MEMORY;
@@ -294,34 +348,26 @@ ssl_ConfigCert(sslSocket *ss, CERTCertificate *cert,
PORT_Assert(cert);
PORT_Assert(keyPair);
PORT_Assert(data);
- PORT_Assert(data->authType != ssl_auth_null);
+ PORT_Assert(authTypes);
- if (!cert || !keyPair || !data || data->authType == ssl_auth_null) {
+ if (!cert || !keyPair || !data || !authTypes) {
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);
+ 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 = SEC_ERROR_INVALID_ARGS;
+ error_code = PORT_GetError();
goto loser;
}
rv = ssl_PopulateOCSPResponses(sc, data->stapledOCSPResponses);
@@ -332,23 +378,12 @@ ssl_ConfigCert(sslSocket *ss, CERTCertificate *cert,
if (rv != SECSuccess) {
goto loser;
}
+ ssl_ClearMatchingCerts(ss, sc->authTypes, sc->namedCurve);
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. */
+ ssl_FreeServerCert(sc);
PORT_SetError(error_code);
return SECFailure;
}
@@ -382,114 +417,55 @@ ssl_GetEcdhAuthType(CERTCertificate *cert)
}
}
-/* 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.
+ * chooses which authTypes apply. For some certificates
+ * this can mean that multiple authTypes.
*
- * 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)
+ * 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)
{
- SECStatus rv = SECFailure;
- SSLExtraServerCertData arg;
+ sslAuthTypeMask authTypes = 0;
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);
+ if (cert->keyUsage & KU_DIGITAL_SIGNATURE) {
+ authTypes |= 1 << ssl_auth_rsa_sign;
+ /* This certificate is RSA, assume that it's also PSS. */
+ authTypes |= 1 << ssl_auth_rsa_pss;
+ }
+
+ 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) {
- arg.authType = ssl_auth_rsa_pss;
+ authTypes |= 1 << ssl_auth_rsa_pss;
}
break;
case SEC_OID_ANSIX9_DSA_SIGNATURE:
if (cert->keyUsage & KU_DIGITAL_SIGNATURE) {
- arg.authType = ssl_auth_dsa;
+ 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) {
- 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;
+ authTypes |= 1 << ssl_GetEcdhAuthType(cert);
}
break;
@@ -498,27 +474,33 @@ ssl_ConfigCertByUsage(sslSocket *ss, CERTCertificate *cert,
}
/* 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;
+ if (targetAuthType != ssl_auth_null) {
+ authTypes &= 1 << targetAuthType;
}
- return ssl_ConfigCert(ss, cert, keyPair, &arg);
+ return authTypes;
}
/* This function adopts pubKey and destroys it if things go wrong. */
static sslKeyPair *
-ssl_MakeKeyPairForCert(SECKEYPrivateKey *key, SECKEYPublicKey *pubKey)
+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) {
@@ -545,20 +527,18 @@ ssl_MakeKeyPairForCert(SECKEYPrivateKey *key, SECKEYPublicKey *pubKey)
if (privKeyCopy) {
SECKEY_DestroyPrivateKey(privKeyCopy);
}
- /* We adopted the public key, so we're responsible. */
- if (pubKey) {
- SECKEY_DestroyPublicKey(pubKey);
- }
+ 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 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.
+ * 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,
@@ -566,12 +546,12 @@ SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert,
const SSLExtraServerCertData *data, unsigned int data_len)
{
sslSocket *ss;
- SECKEYPublicKey *pubKey;
sslKeyPair *keyPair;
SECStatus rv;
SSLExtraServerCertData dataCopy = {
ssl_auth_null, NULL, NULL, NULL
};
+ sslAuthTypeMask authTypes;
ss = ssl_FindSocket(fd);
if (!ss) {
@@ -591,21 +571,23 @@ SSL_ConfigServerCert(PRFileDesc *fd, CERTCertificate *cert,
PORT_Memcpy(&dataCopy, data, data_len);
}
- pubKey = CERT_ExtractPublicKey(cert);
- if (!pubKey) {
+ authTypes = ssl_GetCertificateAuthTypes(cert, dataCopy.authType);
+ if (!authTypes) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- keyPair = ssl_MakeKeyPairForCert(key, pubKey);
+ keyPair = ssl_MakeKeyPairForCert(key, cert);
if (!keyPair) {
- /* pubKey is adopted by ssl_MakeKeyPairForCert() */
- PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
- rv = ssl_ConfigCertByUsage(ss, cert, keyPair, &dataCopy);
+ rv = ssl_ConfigCert(ss, authTypes, cert, keyPair, &dataCopy);
ssl_FreeKeyPair(keyPair);
- return rv;
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ return SECSuccess;
}
/*******************************************************************/
@@ -630,164 +612,148 @@ SSL_ConfigSecureServer(PRFileDesc *fd, CERTCertificate *cert,
* ssl_ConfigCertByUsage(), only checking against the type of key and ignoring
* things like usage. */
static PRBool
-ssl_CertSuitableForAuthType(CERTCertificate *cert, SSLAuthType authType)
+ssl_CertSuitableForAuthType(CERTCertificate *cert, sslAuthTypeMask authTypes)
{
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;
+ 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:
- PORT_Assert(0);
- return PR_FALSE;
+ 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 slot and unlinks it, or it makes a new
+/* 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_FindOrMakeCertType(sslSocket *ss, SSLAuthType authType)
+ssl_FindOrMakeCert(sslSocket *ss, sslAuthTypeMask authTypes)
{
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);
+ /* 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;
}
- return ssl_NewServerCert(&certType);
+ /* 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 void
-ssl_RemoveCertAndKeyByAuthType(sslSocket *ss, SSLAuthType authType)
+static sslAuthTypeMask
+ssl_KeaTypeToAuthTypeMask(SSLKEAType keaType)
{
- sslServerCert *sc;
+ switch (keaType) {
+ case ssl_kea_rsa:
+ return (1 << ssl_auth_rsa_decrypt) |
+ (1 << ssl_auth_rsa_sign);
- 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. */
+ 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_AddCertAndKeyByAuthType(sslSocket *ss, SSLAuthType authType,
- CERTCertificate *cert,
- const CERTCertificateList *certChainOpt,
- sslKeyPair *keyPair)
+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, authType)) {
+ if (!ssl_CertSuitableForAuthType(cert, authTypes)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
- sc = ssl_FindOrMakeCertType(ss, authType);
+ sc = ssl_FindOrMakeCert(ss, authTypes);
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);
+ keyPair = ssl_MakeKeyPairForCert(key, cert);
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;
+ /* Error code is set by ssl_MakeKeyPairForCert */
+ goto loser;
}
-
- 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;
+ rv = ssl_PopulateKeyPair(sc, keyPair);
+ ssl_FreeKeyPair(keyPair);
+ if (rv != SECSuccess) {
+ err = PORT_GetError();
+ goto loser;
}
- ssl_FreeKeyPair(keyPair);
- return rv;
+ PR_APPEND_LINK(&sc->link, &ss->serverCerts);
+ return SECSuccess;
+
+loser:
+ ssl_FreeServerCert(sc);
+ PORT_SetError(err);
+ return SECFailure;
}
/* Public deprecated function */
@@ -797,6 +763,7 @@ SSL_ConfigSecureServerWithCertChain(PRFileDesc *fd, CERTCertificate *cert,
SECKEYPrivateKey *key, SSLKEAType certType)
{
sslSocket *ss;
+ sslAuthTypeMask authTypes;
ss = ssl_FindSocket(fd);
if (!ss) {
@@ -808,52 +775,25 @@ SSL_ConfigSecureServerWithCertChain(PRFileDesc *fd, CERTCertificate *cert,
return SECFailure;
}
+ authTypes = ssl_KeaTypeToAuthTypeMask(certType);
+ if (!authTypes) {
+ 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;
+ 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_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;
+ return ssl_AddCertChain(ss, cert, certChainOpt, key, authTypes);
}
/* Public deprecated function */
@@ -862,6 +802,8 @@ SSL_SetStapledOCSPResponses(PRFileDesc *fd, const SECItemArray *responses,
SSLKEAType certType)
{
sslSocket *ss;
+ sslServerCert *sc;
+ sslAuthTypeMask authTypes;
SECStatus rv;
ss = ssl_FindSocket(fd);
@@ -871,49 +813,28 @@ SSL_SetStapledOCSPResponses(PRFileDesc *fd, const SECItemArray *responses,
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;
+ 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;
}
-}
-static SECStatus
-ssl_SetSignedTimestampsInSlot(sslSocket *ss, SSLAuthType authType,
- const SECItem *scts)
-{
- sslServerCert *sc;
- SECStatus rv;
+ if (!responses) {
+ sc = ssl_FindCertWithMask(ss, authTypes);
+ if (sc) {
+ (void)ssl_PopulateOCSPResponses(sc, NULL);
+ }
+ return SECSuccess;
+ }
- sc = ssl_FindOrMakeCertType(ss, authType);
+ sc = ssl_FindOrMakeCert(ss, authTypes);
if (!sc) {
- PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
- rv = ssl_PopulateSignedCertTimestamps(sc, scts);
+
+ rv = ssl_PopulateOCSPResponses(sc, responses);
if (rv == SECSuccess) {
PR_APPEND_LINK(&sc->link, &ss->serverCerts);
} else {
@@ -928,6 +849,8 @@ SSL_SetSignedCertTimestamps(PRFileDesc *fd, const SECItem *scts,
SSLKEAType certType)
{
sslSocket *ss;
+ sslServerCert *sc;
+ sslAuthTypeMask authTypes;
SECStatus rv;
ss = ssl_FindSocket(fd);
@@ -937,34 +860,34 @@ SSL_SetSignedCertTimestamps(PRFileDesc *fd, const SECItem *scts,
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);
+ 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;
+ }
- case ssl_kea_dh:
- return ssl_SetSignedTimestampsInSlot(ss, ssl_auth_dsa, scts);
+ if (!scts) {
+ sc = ssl_FindCertWithMask(ss, authTypes);
+ if (sc) {
+ (void)ssl_PopulateSignedCertTimestamps(sc, NULL);
+ }
+ return SECSuccess;
+ }
- 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);
+ sc = ssl_FindOrMakeCert(ss, authTypes);
+ if (!sc) {
+ return SECFailure;
+ }
- default:
- SSL_DBG(("%d: SSL[%d]: invalid cert type in SSL_SetSignedCertTimestamps",
- SSL_GETPID(), fd));
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- 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. */