diff options
-rw-r--r-- | automation/abi-check/expected-report-libnss3.so.txt | 8 | ||||
-rw-r--r-- | automation/abi-check/expected-report-libssl3.so.txt | 4 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_auth_unittest.cc | 120 | ||||
-rw-r--r-- | lib/certdb/cert.h | 15 | ||||
-rw-r--r-- | lib/certdb/certdb.c | 84 | ||||
-rw-r--r-- | lib/nss/nss.def | 9 | ||||
-rw-r--r-- | lib/pk11wrap/pk11obj.c | 5 | ||||
-rw-r--r-- | lib/pk11wrap/pk11pub.h | 2 | ||||
-rw-r--r-- | lib/ssl/authcert.c | 250 | ||||
-rw-r--r-- | lib/ssl/ssl.def | 7 | ||||
-rw-r--r-- | lib/ssl/ssl.h | 14 | ||||
-rw-r--r-- | lib/ssl/ssl3con.c | 72 | ||||
-rw-r--r-- | lib/ssl/sslimpl.h | 15 | ||||
-rw-r--r-- | lib/ssl/sslsock.c | 12 | ||||
-rw-r--r-- | lib/ssl/tls13con.c | 3 | ||||
-rw-r--r-- | lib/ssl/tls13subcerts.c | 2 | ||||
-rwxr-xr-x | mach | 3 | ||||
-rwxr-xr-x | tests/ssl/ssl.sh | 27 | ||||
-rw-r--r-- | tests/ssl/sslauth.txt | 27 |
19 files changed, 586 insertions, 93 deletions
diff --git a/automation/abi-check/expected-report-libnss3.so.txt b/automation/abi-check/expected-report-libnss3.so.txt index e69de29bb..e60ded534 100644 --- a/automation/abi-check/expected-report-libnss3.so.txt +++ b/automation/abi-check/expected-report-libnss3.so.txt @@ -0,0 +1,8 @@ + +4 Added functions: + + 'function SECStatus CERT_FilterCertListByCertList(CERTCertList*, const CERTCertList*)' {CERT_FilterCertListByCertList@@NSS_3.77} + 'function SECStatus CERT_FilterCertListByNickname(CERTCertList*, char*, void*)' {CERT_FilterCertListByNickname@@NSS_3.77} + 'function PRBool CERT_IsInList(const CERTCertificate*, const CERTCertList*)' {CERT_IsInList@@NSS_3.77} + 'function CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate*, void*, PK11SlotInfo**)' {PK11_FindObjectForCert@@NSS_3.77} + diff --git a/automation/abi-check/expected-report-libssl3.so.txt b/automation/abi-check/expected-report-libssl3.so.txt index e69de29bb..6c8b7b07d 100644 --- a/automation/abi-check/expected-report-libssl3.so.txt +++ b/automation/abi-check/expected-report-libssl3.so.txt @@ -0,0 +1,4 @@ +2 Added functions: + + 'function PRBool SSL_CertIsUsable(PRFileDesc*, CERTCertificate*)' {SSL_CertIsUsable@@NSS_3.77} + 'function SECStatus SSL_FilterClientCertListBySocket(PRFileDesc*, CERTCertList*)' {SSL_FilterClientCertListBySocket@@NSS_3.77} diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc index adb4424c1..925b82721 100644 --- a/gtests/ssl_gtest/ssl_auth_unittest.cc +++ b/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -309,6 +309,126 @@ static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd, return SECSuccess; } +typedef struct AutoClientTestStr { + SECStatus result; + const std::string cert; +} AutoClientTest; + +typedef struct AutoClientResultsStr { + AutoClientTest isRsa2048; + AutoClientTest isClient; + AutoClientTest isNull; + bool hookCalled; +} AutoClientResults; + +void VerifyClientCertMatch(CERTCertificate* clientCert, + const std::string expectedName) { + const char* name = clientCert->nickname; + std::cout << "Match name=\"" << name << "\" expected=\"" << expectedName + << "\"" << std::endl; + EXPECT_TRUE(PORT_Strcmp(name, expectedName.c_str()) == 0) + << " Certmismatch: \"" << name << "\" != \"" << expectedName << "\""; +} + +static SECStatus GetAutoClientAuthDataHook(void* expectResults, PRFileDesc* fd, + CERTDistNames* caNames, + CERTCertificate** clientCert, + SECKEYPrivateKey** clientKey) { + AutoClientResults& results = *(AutoClientResults*)expectResults; + SECStatus rv; + + results.hookCalled = true; + *clientCert = NULL; + *clientKey = NULL; + rv = NSS_GetClientAuthData((void*)TlsAgent::kRsa2048.c_str(), fd, caNames, + clientCert, clientKey); + if (rv == SECSuccess) { + VerifyClientCertMatch(*clientCert, results.isRsa2048.cert); + CERT_DestroyCertificate(*clientCert); + SECKEY_DestroyPrivateKey(*clientKey); + *clientCert = NULL; + *clientKey = NULL; + } + EXPECT_EQ(results.isRsa2048.result, rv); + + rv = NSS_GetClientAuthData((void*)TlsAgent::kClient.c_str(), fd, caNames, + clientCert, clientKey); + if (rv == SECSuccess) { + VerifyClientCertMatch(*clientCert, results.isClient.cert); + CERT_DestroyCertificate(*clientCert); + SECKEY_DestroyPrivateKey(*clientKey); + *clientCert = NULL; + *clientKey = NULL; + } + EXPECT_EQ(results.isClient.result, rv); + EXPECT_EQ(*clientCert, nullptr); + EXPECT_EQ(*clientKey, nullptr); + rv = NSS_GetClientAuthData(NULL, fd, caNames, clientCert, clientKey); + if (rv == SECSuccess) { + VerifyClientCertMatch(*clientCert, results.isNull.cert); + // return this result + } + EXPECT_EQ(results.isNull.result, rv); + return rv; +} + +// while I would have liked to use a new INSTANTIATE macro the +// generates the following three tests, figuring out how to make that +// work on top of the existing TlsConnect* plumbing hurts my head. +TEST_P(TlsConnectTls12, AutoClientSelectRsaPss) { + AutoClientResults rsa = {{SECSuccess, TlsAgent::kRsa2048}, + {SECSuccess, TlsAgent::kClient}, + {SECSuccess, TlsAgent::kDelegatorRsaPss2048}, + false}; + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_pss_sha256, + ssl_sig_rsa_pkcs1_sha256, + ssl_sig_rsa_pkcs1_sha1}; + Reset("rsa_pss_noparam"); + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetAutoClientAuthDataHook, (void*)&rsa)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + Connect(); + EXPECT_TRUE(rsa.hookCalled); +} + +TEST_P(TlsConnectTls12, AutoClientSelectEcc) { + AutoClientResults ecc = {{SECFailure, TlsAgent::kClient}, + {SECFailure, TlsAgent::kClient}, + {SECSuccess, TlsAgent::kDelegatorEcdsa256}, + false}; + static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256}; + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetAutoClientAuthDataHook, (void*)&ecc)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + Connect(); + EXPECT_TRUE(ecc.hookCalled); +} + +TEST_P(TlsConnectTls12, AutoClientSelectDsa) { + AutoClientResults dsa = {{SECFailure, TlsAgent::kClient}, + {SECFailure, TlsAgent::kClient}, + {SECSuccess, TlsAgent::kServerDsa}, + false}; + static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256}; + client_->SetupClientAuth(); + server_->RequestClientAuth(true); + EXPECT_EQ(SECSuccess, + SSL_GetClientAuthDataHook(client_->ssl_fd(), + GetAutoClientAuthDataHook, (void*)&dsa)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + Connect(); + EXPECT_TRUE(dsa.hookCalled); +} + TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMultiple) { client_->SetupClientAuth(); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), diff --git a/lib/certdb/cert.h b/lib/certdb/cert.h index 1981b8f54..33d37b39a 100644 --- a/lib/certdb/cert.h +++ b/lib/certdb/cert.h @@ -1315,6 +1315,21 @@ SECStatus CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames, SECStatus CERT_FilterCertListForUserCerts(CERTCertList *certList); /* + * Filter a list of certificates, removing those certs that don't match the + * nickname. + */ +SECStatus CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname, + void *pwarg); + +/* return true if cert is in cert list */ +PRBool CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList); + +/* returned certList is the intersection of the certs on certList and the + * certs on filterList */ +SECStatus CERT_FilterCertListByCertList(CERTCertList *certList, + const CERTCertList *filterList); + +/* * Collect the nicknames from all certs in a CertList. If the cert is not * valid, append a string to that nickname. * diff --git a/lib/certdb/certdb.c b/lib/certdb/certdb.c index 4a713b6d7..e9acbb28d 100644 --- a/lib/certdb/certdb.c +++ b/lib/certdb/certdb.c @@ -2552,6 +2552,10 @@ CERT_DestroyCertList(CERTCertList *certs) { PRCList *node; + if (!certs) { + return; + } + while (!PR_CLIST_IS_EMPTY(&certs->list)) { node = PR_LIST_HEAD(&certs->list); CERT_DestroyCertificate(((CERTCertListNode *)node)->cert); @@ -2866,6 +2870,86 @@ CERT_FilterCertListForUserCerts(CERTCertList *certList) return (SECSuccess); } +/* return true if cert is in the list */ +PRBool +CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList) +{ + CERTCertListNode *node; + for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + if (node->cert == cert) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +/* returned certList is the intersection of the certs on certList and the + * certs on filterList */ +SECStatus +CERT_FilterCertListByCertList(CERTCertList *certList, + const CERTCertList *filterList) +{ + CERTCertListNode *node, *freenode; + CERTCertificate *cert; + + if (!certList) { + return SECFailure; + } + + if (!filterList || CERT_LIST_EMPTY(certList)) { + /* if the filterList is empty, just clear out certList and return */ + for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);) { + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } + return SECSuccess; + } + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + cert = node->cert; + if (!CERT_IsInList(cert, filterList)) { + // no matching cert on filter list, remove it from certlist */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* matching cert, keep it around */ + node = CERT_LIST_NEXT(node); + } + } + + return (SECSuccess); +} + +SECStatus +CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname, + void *pwarg) +{ + CERTCertList *nameList; + SECStatus rv; + + if (!certList) { + return SECFailure; + } + + /* we could try to match the nickname to the individual cert, + * but nickname parsing is quite complicated, so it's best just + * to use the existing code and get a list of certs that match the + * nickname. We can then compare that list with our input cert list + * and return only those certs that are on both. */ + nameList = PK11_FindCertsFromNickname(nickname, pwarg); + + /* namelist could be NULL, this will force certList to become empty */ + rv = CERT_FilterCertListByCertList(certList, nameList); + /* CERT_DestroyCertList can now accept a NULL pointer */ + CERT_DestroyCertList(nameList); + return rv; +} + static PZLock *certRefCountLock = NULL; /* diff --git a/lib/nss/nss.def b/lib/nss/nss.def index e87395ba9..dd352d81a 100644 --- a/lib/nss/nss.def +++ b/lib/nss/nss.def @@ -1238,3 +1238,12 @@ PK11_SlotGetLastFIPSStatus; ;+ local: ;+ *; ;+}; +;+NSS_3.77 { # NSS 3.77 release +;+ global: +CERT_FilterCertListByCertList; +CERT_FilterCertListByNickname; +CERT_IsInList; +PK11_FindObjectForCert; +;+ local: +;+ *; +;+}; diff --git a/lib/pk11wrap/pk11obj.c b/lib/pk11wrap/pk11obj.c index 8ece7a6d4..5dd4d0fc0 100644 --- a/lib/pk11wrap/pk11obj.c +++ b/lib/pk11wrap/pk11obj.c @@ -1717,7 +1717,10 @@ PK11_GetObjectHandle(PK11ObjectType objType, void *objSpec, slot = ((PK11SymKey *)objSpec)->slot; handle = ((PK11SymKey *)objSpec)->objectID; break; - case PK11_TypeCert: /* don't handle cert case for now */ + case PK11_TypeCert: + handle = PK11_FindObjectForCert((CERTCertificate *)objSpec, NULL, + &slot); + break; default: PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE); break; diff --git a/lib/pk11wrap/pk11pub.h b/lib/pk11wrap/pk11pub.h index a2a647320..6530d42bd 100644 --- a/lib/pk11wrap/pk11pub.h +++ b/lib/pk11wrap/pk11pub.h @@ -725,6 +725,8 @@ PRBool PK11_FortezzaHasKEA(CERTCertificate *cert); CK_OBJECT_HANDLE PK11_FindEncodedCertInSlot(PK11SlotInfo *slot, SECItem *derCert, void *wincx); CK_OBJECT_HANDLE PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx); +CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate *cert, + void *wincx, PK11SlotInfo **pSlot); SECStatus PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot, SECStatus (*callback)(CERTCertificate *, void *), void *arg); diff --git a/lib/ssl/authcert.c b/lib/ssl/authcert.c index 737b4e797..54b6f5950 100644 --- a/lib/ssl/authcert.c +++ b/lib/ssl/authcert.c @@ -19,6 +19,149 @@ #include "pk11func.h" /* for PK11_ function calls */ #include "sslimpl.h" +/* convert a CERTDistNameStr to an array ascii strings. + * we ignore caNames which we can't convert, so n could be less than nnames + * n is always set, even on failure. + * This function allows us to use the existing CERT_FilterCertListByCANames. */ +static char ** +ssl_DistNamesToStrings(struct CERTDistNamesStr *caNames, int *n) +{ + char **names; + int i; + SECStatus rv; + PLArenaPool *arena; + + *n = 0; + names = PORT_ZNewArray(char *, caNames->nnames); + if (names == NULL) { + return NULL; + } + arena = PORT_NewArena(2048); + if (arena == NULL) { + PORT_Free(names); + return NULL; + } + for (i = 0; i < caNames->nnames; ++i) { + CERTName dn; + rv = SEC_QuickDERDecodeItem(arena, &dn, SEC_ASN1_GET(CERT_NameTemplate), + caNames->names + i); + if (rv != SECSuccess) { + continue; + } + names[*n] = CERT_NameToAscii(&dn); + if (names[*n]) + (*n)++; + } + PORT_FreeArena(arena, PR_FALSE); + return names; +} + +/* free the dist names we allocated in the above function. n must be the + * returned n from that function. */ +static void +ssl_FreeDistNamesStrings(char **strings, int n) +{ + int i; + for (i = 0; i < n; i++) { + PORT_Free(strings[i]); + } + PORT_Free(strings); +} + +PRBool +ssl_CertIsUsable(sslSocket *ss, CERTCertificate *cert) +{ + SECStatus rv; + SSLSignatureScheme scheme; + + if ((ss == NULL) || (cert == NULL)) { + return PR_FALSE; + } + /* There are two ways of handling the old style handshake: + * 1) check the actual record we are using and return true, + * if (!ss->ssl3.hs.hashType == handshake_hash_record && + * ss->ssl3.hs.hashType == handshake_hash_single) { + * return PR_TRUE; + * 2) assume if ss->peerSignatureSchemesCount == 0 we are using the + * old handshake. + * There is one case where using 2 will be wrong: we somehow call this + * function outside the case where of out GetClientAuthData context. + * In that case we don't know that the 'real' peerScheme list is, so the + * best we can do is either always assume good or always assume bad. + * I think the best results is to always assume good, so we use + * option 2 here to handle that case as well.*/ + if (ss->peerSignatureSchemeCount == 0) { + return PR_TRUE; + } + if (ss->peerSignatureSchemes == NULL) { + return PR_FALSE; /* should this really be an assert? */ + } + rv = ssl_PickClientSignatureScheme(ss, cert, NULL, + ss->peerSignatureSchemes, + ss->peerSignatureSchemeCount, + &scheme); + if (rv != SECSuccess) { + return PR_FALSE; + } + return PR_TRUE; +} + +SECStatus +ssl_FilterClientCertListBySSLSocket(sslSocket *ss, CERTCertList *certList) +{ + CERTCertListNode *node; + CERTCertificate *cert; + + if (!certList) { + return SECFailure; + } + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + cert = node->cert; + if (PR_TRUE != ssl_CertIsUsable(ss, cert)) { + /* cert doesn't match the socket criteria, remove it */ + CERTCertListNode *freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* this cert is good, go to the next cert */ + node = CERT_LIST_NEXT(node); + } + } + + return (SECSuccess); +} + +/* This function can be called by the application's custom GetClientAuthHook + * to filter out any certs in the cert list that doesn't match the negotiated + * requirements of the current SSL connection. + */ +SECStatus +SSL_FilterClientCertListBySocket(PRFileDesc *fd, CERTCertList *certList) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (ss == NULL) { + return SECFailure; + } + return ssl_FilterClientCertListBySSLSocket(ss, certList); +} + +/* This function can be called by the application's custom GetClientAuthHook + * to determine if a single certificate matches the negotiated requirements of + * the current SSL connection. + */ +PRBool +SSL_CertIsUsable(PRFileDesc *fd, CERTCertificate *cert) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (ss == NULL) { + return PR_FALSE; + } + return ssl_CertIsUsable(ss, cert); +} + /* * This callback used by SSL to pull client certificate upon * server request @@ -31,6 +174,7 @@ NSS_GetClientAuthData(void *arg, struct SECKEYPrivateKeyStr **pRetKey) { CERTCertificate *cert = NULL; + CERTCertList *certList = NULL; SECKEYPrivateKey *privkey = NULL; char *chosenNickName = (char *)arg; /* CONST */ SECStatus rv = SECFailure; @@ -39,56 +183,68 @@ NSS_GetClientAuthData(void *arg, if (!ss) { return SECFailure; } - void *proto_win = SSL_RevealPinArg(fd); - PRTime now = ssl_Time(ss); - - if (chosenNickName) { - cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), - chosenNickName, certUsageSSLClient, - PR_FALSE, proto_win); - if (cert) { - privkey = PK11_FindKeyByAnyCert(cert, proto_win); - if (privkey) { - rv = SECSuccess; - } else { - CERT_DestroyCertificate(cert); + void *pw_arg = SSL_RevealPinArg(fd); + + /* first, handle any token authentication that may be needed */ + if (chosenNickName && pw_arg) { + certList = PK11_FindCertsFromNickname(chosenNickName, pw_arg); + if (certList) { + CERT_FilterCertListForUserCerts(certList); + rv = CERT_FilterCertListByUsage(certList, certUsageSSLClient, + PR_FALSE); + if ((rv != SECSuccess) || CERT_LIST_EMPTY(certList)) { + CERT_DestroyCertList(certList); + certList = NULL; } } - } else { /* no name given, automatically find the right cert. */ - CERTCertNicknames *names; - int i; - - names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), - SEC_CERT_NICKNAMES_USER, proto_win); - if (names != NULL) { - for (i = 0; i < names->numnicknames; i++) { - cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), - names->nicknames[i], certUsageSSLClient, - PR_FALSE, proto_win); - if (!cert) - continue; - /* Only check unexpired certs */ - if (CERT_CheckCertValidTimes(cert, now, PR_TRUE) != - secCertTimeValid) { - CERT_DestroyCertificate(cert); - continue; - } - rv = NSS_CmpCertChainWCANames(cert, caNames); - if (rv == SECSuccess) { - privkey = - PK11_FindKeyByAnyCert(cert, proto_win); - if (privkey) - break; - } - rv = SECFailure; - CERT_DestroyCertificate(cert); - } - CERT_FreeNicknames(names); + } + + /* otherwise look through the cache based on usage + * if chosenNickname is set, we ignore the expiration date */ + if (certList == NULL) { + certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), + certUsageSSLClient, + PR_FALSE, chosenNickName == NULL, + pw_arg); + /* filter only the certs that meet the nickname requirements */ + if (chosenNickName) { + rv = CERT_FilterCertListByNickname(certList, chosenNickName, + pw_arg); + } else { + int nnames = 0; + char **names = ssl_DistNamesToStrings(caNames, &nnames); + rv = CERT_FilterCertListByCANames(certList, nnames, names, + certUsageSSLClient); + ssl_FreeDistNamesStrings(names, nnames); } + if ((rv != SECSuccess) || CERT_LIST_EMPTY(certList)) { + CERT_DestroyCertList(certList); + certList = NULL; + } + } + if (certList == NULL) { + /* no user certs meeting the nickname/usage requirements found */ + return SECFailure; } - if (rv == SECSuccess) { - *pRetCert = cert; - *pRetKey = privkey; + /* now remove any certs that can't meet the connection requirements */ + rv = ssl_FilterClientCertListBySSLSocket(ss, certList); + if ((rv != SECSuccess) || CERT_LIST_EMPTY(certList)) { + // no certs left. + CERT_DestroyCertList(certList); + return SECFailure; + } + + /* now return the top cert in the list. We've strived to make the + * list ordered by the most likely usable cert, so it should be the best + * match. */ + cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert); + CERT_DestroyCertList(certList); + privkey = PK11_FindKeyByAnyCert(cert, pw_arg); + if (privkey == NULL) { + CERT_DestroyCertificate(cert); + return SECFailure; } - return rv; + *pRetCert = cert; + *pRetKey = privkey; + return SECSuccess; } diff --git a/lib/ssl/ssl.def b/lib/ssl/ssl.def index 9a447dbef..14fc2b960 100644 --- a/lib/ssl/ssl.def +++ b/lib/ssl/ssl.def @@ -240,3 +240,10 @@ SSL_GetExperimentalAPI; ;+ local: ;+*; ;+}; +;+NSS_3.77 { # NSS 3.77 release +;+ global: +SSL_CertIsUsable; +SSL_FilterClientCertListBySocket; +;+ local: +;+*; +;+}; diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h index a2c80beaf..e68e24d50 100644 --- a/lib/ssl/ssl.h +++ b/lib/ssl/ssl.h @@ -1259,6 +1259,20 @@ NSS_GetClientAuthData(void *arg, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey); +/* This function can be called by the appliation's custom GetClientAuthHook + * to filter out any certs in the cert list that doesn't match the negotiated + * requirements of the current SSL connection. + */ +SSL_IMPORT SECStatus +SSL_FilterClientCertListBySocket(PRFileDesc *socket, CERTCertList *certlist); + +/* This function can be called by the application's custom GetClientAuthHook + * to determine if a single certificate matches the negotiated requirements of + * the current SSL connection. + */ +SSL_IMPORT PRBool +SSL_CertIsUsable(PRFileDesc *socket, CERTCertificate *cert); + /* ** Configure DTLS-SRTP (RFC 5764) cipher suite preferences. ** Input is a list of ciphers in descending preference order and a length diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index ea5183962..d7d79e1c8 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -6490,11 +6490,19 @@ ssl_CanUseSignatureScheme(SSLSignatureScheme scheme, } SECStatus -ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey, - PRBool *supportsRsaPss) +ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey, CERTCertificate *cert, + void *pwarg, PRBool *supportsRsaPss) { - PK11SlotInfo *slot; - slot = PK11_GetSlotFromPrivateKey(privKey); + PK11SlotInfo *slot = NULL; + if (privKey) { + slot = PK11_GetSlotFromPrivateKey(privKey); + } else { + CK_OBJECT_HANDLE certID = PK11_FindObjectForCert(cert, pwarg, &slot); + if (certID == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + } if (!slot) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; @@ -6511,7 +6519,8 @@ ssl_PickSignatureScheme(sslSocket *ss, SECKEYPrivateKey *privKey, const SSLSignatureScheme *peerSchemes, unsigned int peerSchemeCount, - PRBool requireSha1) + PRBool requireSha1, + SSLSignatureScheme *schemePtr) { unsigned int i; PRBool doesRsaPss; @@ -6522,13 +6531,13 @@ ssl_PickSignatureScheme(sslSocket *ss, /* We can't require SHA-1 in TLS 1.3. */ PORT_Assert(!(requireSha1 && isTLS13)); - if (!pubKey || !privKey) { + if (!pubKey || !cert) { PORT_Assert(0); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } - - rv = ssl_PrivateKeySupportsRsaPss(privKey, &doesRsaPss); + rv = ssl_PrivateKeySupportsRsaPss(privKey, cert, ss->pkcs11PinArg, + &doesRsaPss); if (rv != SECSuccess) { return SECFailure; } @@ -6546,7 +6555,7 @@ ssl_PickSignatureScheme(sslSocket *ss, PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); return SECFailure; } - ss->ssl3.hs.signatureScheme = scheme; + *schemePtr = scheme; return SECSuccess; } @@ -6563,7 +6572,7 @@ ssl_PickSignatureScheme(sslSocket *ss, if (ssl_SignatureSchemeValid(scheme, spkiOid, isTLS13) && ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount, requireSha1, doesRsaPss)) { - ss->ssl3.hs.signatureScheme = scheme; + *schemePtr = scheme; return SECSuccess; } } @@ -6621,17 +6630,20 @@ ssl3_PickServerSignatureScheme(sslSocket *ss) cert->serverKeyPair->privKey, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, - PR_FALSE /* requireSha1 */); + PR_FALSE /* requireSha1 */, + &ss->ssl3.hs.signatureScheme); } -static SECStatus -ssl_PickClientSignatureScheme(sslSocket *ss, const SSLSignatureScheme *schemes, - unsigned int numSchemes) +SECStatus +ssl_PickClientSignatureScheme(sslSocket *ss, CERTCertificate *clientCertificate, + SECKEYPrivateKey *privKey, + const SSLSignatureScheme *schemes, + unsigned int numSchemes, + SSLSignatureScheme *schemePtr) { - SECKEYPrivateKey *privKey = ss->ssl3.clientPrivateKey; SECStatus rv; PRBool isTLS13 = (PRBool)ss->version >= SSL_LIBRARY_VERSION_TLS_1_3; - SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(ss->ssl3.clientCertificate); + SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(clientCertificate); PORT_Assert(pubKey); @@ -6651,9 +6663,9 @@ ssl_PickClientSignatureScheme(sslSocket *ss, const SSLSignatureScheme *schemes, * older, DSA key size is at most 1024 bits and the hash function must * be SHA-1. */ - rv = ssl_PickSignatureScheme(ss, ss->ssl3.clientCertificate, + rv = ssl_PickSignatureScheme(ss, clientCertificate, pubKey, privKey, schemes, numSchemes, - PR_TRUE /* requireSha1 */); + PR_TRUE /* requireSha1 */, schemePtr); if (rv == SECSuccess) { SECKEY_DestroyPublicKey(pubKey); return SECSuccess; @@ -6661,9 +6673,9 @@ ssl_PickClientSignatureScheme(sslSocket *ss, const SSLSignatureScheme *schemes, /* If this fails, that's because the peer doesn't advertise SHA-1, * so fall back to the full negotiation. */ } - rv = ssl_PickSignatureScheme(ss, ss->ssl3.clientCertificate, + rv = ssl_PickSignatureScheme(ss, clientCertificate, pubKey, privKey, schemes, numSchemes, - PR_FALSE /* requireSha1 */); + PR_FALSE /* requireSha1 */, schemePtr); SECKEY_DestroyPublicKey(pubKey); return rv; } @@ -7857,11 +7869,23 @@ ssl3_CompleteHandleCertificateRequest(sslSocket *ss, PORT_Assert(ss->ssl3.clientPrivateKey == NULL); PORT_Assert(ss->ssl3.clientCertificate == NULL); PORT_Assert(ss->ssl3.clientCertChain == NULL); + /* + * Peer signatures are only available while in the context of + * of a getClientAuthData callback. It is required for proper + * functioning of SSL_CertIsUsable and SSL_FilterClientCertListBySocket + * Calling these functions outside the context of a getClientAuthData + * callback will result in no filtering.*/ + ss->peerSignatureSchemes = signatureSchemes; + ss->peerSignatureSchemeCount = signatureSchemeCount; /* XXX Should pass cert_types and algorithms in this call!! */ rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg, ss->fd, ca_list, &ss->ssl3.clientCertificate, &ss->ssl3.clientPrivateKey); + /* memory for the signature schemes will go away after the request, + * so don't leave dangling pointers around */ + ss->peerSignatureSchemes = NULL; + ss->peerSignatureSchemeCount = 0; } else { rv = SECFailure; /* force it to send a no_certificate alert */ } @@ -7887,8 +7911,12 @@ ssl3_CompleteHandleCertificateRequest(sslSocket *ss, } if (ss->ssl3.hs.hashType == handshake_hash_record || ss->ssl3.hs.hashType == handshake_hash_single) { - rv = ssl_PickClientSignatureScheme(ss, signatureSchemes, - signatureSchemeCount); + rv = ssl_PickClientSignatureScheme(ss, + ss->ssl3.clientCertificate, + ss->ssl3.clientPrivateKey, + signatureSchemes, + signatureSchemeCount, + &ss->ssl3.hs.signatureScheme); if (rv != SECSuccess) { /* This should only happen if our schemes changed or * if an RSA-PSS cert was selected, but the token diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index 1ec293138..aeb3b8370 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -1138,6 +1138,10 @@ struct sslSocketStr { /* An out-of-band PSK. */ sslPsk *psk; + + /* peer data passed in during getClientAuthData */ + const SSLSignatureScheme *peerSignatureSchemes; + unsigned int peerSignatureSchemeCount; }; struct sslSelfEncryptKeysStr { @@ -1778,6 +1782,8 @@ PRBool ssl3_CipherSuiteAllowedForVersionRange(ssl3CipherSuite cipherSuite, SECStatus ssl3_SelectServerCert(sslSocket *ss); SECStatus ssl_PrivateKeySupportsRsaPss(SECKEYPrivateKey *privKey, + CERTCertificate *cert, + void *pwArg, PRBool *supportsRsaPss); SECStatus ssl_PickSignatureScheme(sslSocket *ss, CERTCertificate *cert, @@ -1785,7 +1791,14 @@ SECStatus ssl_PickSignatureScheme(sslSocket *ss, SECKEYPrivateKey *privKey, const SSLSignatureScheme *peerSchemes, unsigned int peerSchemeCount, - PRBool requireSha1); + PRBool requireSha1, + SSLSignatureScheme *schemPtr); +SECStatus ssl_PickClientSignatureScheme(sslSocket *ss, + CERTCertificate *clientCertificate, + SECKEYPrivateKey *privKey, + const SSLSignatureScheme *schemes, + unsigned int numSchemes, + SSLSignatureScheme *schemePtr); SECOidTag ssl3_HashTypeToOID(SSLHashType hashType); SECOidTag ssl3_AuthTypeToOID(SSLAuthType hashType); SSLHashType ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme); diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c index 3a5d7fe45..422d7ae37 100644 --- a/lib/ssl/sslsock.c +++ b/lib/ssl/sslsock.c @@ -399,6 +399,10 @@ ssl_DupSocket(sslSocket *os) goto loser; } } + /* The original socket 'owns' the copy of these, so + * just set the target copies to zero */ + ss->peerSignatureSchemes = NULL; + ss->peerSignatureSchemeCount = 0; /* Create security data */ rv = ssl_CopySecurityInfo(ss, os); @@ -490,6 +494,10 @@ ssl_DestroySocketContents(sslSocket *ss) tls13_ReleaseAntiReplayContext(ss->antiReplay); tls13_DestroyPsk(ss->psk); + /* data in peer Signature schemes comes from the buffer system, + * so there is nothing to free here. Make sure that's the case */ + PORT_Assert(ss->peerSignatureSchemes == NULL); + PORT_Assert(ss->peerSignatureSchemeCount == 0); tls13_DestroyEchConfigs(&ss->echConfigs); SECKEY_DestroyPrivateKey(ss->echPrivKey); @@ -2564,6 +2572,8 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd) ss->handshakeCallbackData = sm->handshakeCallbackData; if (sm->pkcs11PinArg) ss->pkcs11PinArg = sm->pkcs11PinArg; + ss->peerSignatureSchemes = NULL; + ss->peerSignatureSchemeCount = 0; return fd; } @@ -4235,6 +4245,8 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant) ss->echPubKey = NULL; ss->antiReplay = NULL; ss->psk = NULL; + ss->peerSignatureSchemes = NULL; + ss->peerSignatureSchemeCount = 0; if (makeLocks) { rv = ssl_MakeLocks(ss); diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index 67f89e908..7782760e0 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -1678,7 +1678,8 @@ tls13_SelectServerCert(sslSocket *ss) cert->serverKeyPair->privKey, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, - PR_FALSE); + PR_FALSE, + &ss->ssl3.hs.signatureScheme); if (rv == SECSuccess) { /* Found one. */ ss->sec.serverCert = cert; diff --git a/lib/ssl/tls13subcerts.c b/lib/ssl/tls13subcerts.c index 6f164c302..1fd79ae20 100644 --- a/lib/ssl/tls13subcerts.c +++ b/lib/ssl/tls13subcerts.c @@ -220,7 +220,7 @@ tls13_MaybeSetDelegatedCredential(sslSocket *ss) } priv = ss->sec.serverCert->delegCredKeyPair->privKey; - rv = ssl_PrivateKeySupportsRsaPss(priv, &doesRsaPss); + rv = ssl_PrivateKeySupportsRsaPss(priv, NULL, NULL, &doesRsaPss); if (rv != SECSuccess) { return SECFailure; } @@ -176,7 +176,8 @@ class covAction(argparse.Action): env = { "GTESTFILTER": "*", # Prevent parallel test runs. "ASAN_OPTIONS": "coverage=1:coverage_dir=" + outdir, - "NSS_DEFAULT_DB_TYPE": "dbm" + "NSS_DEFAULT_DB_TYPE": "sql", + "NSS_DISABLE_UNLOAD": "1" } run_tests("ssl_gtests", env=env, silent=True) diff --git a/tests/ssl/ssl.sh b/tests/ssl/ssl.sh index acd4c8cda..0fa24a2c6 100755 --- a/tests/ssl/ssl.sh +++ b/tests/ssl/ssl.sh @@ -471,7 +471,7 @@ ssl_auth() elif [ "$ectype" = "SNI" -a "$NORM_EXT" = "Extended Test" ] ; then echo "$SCRIPTNAME: skipping $testname for $NORM_EXT" else - cparam=`echo $cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" ` + cparam=`echo $cparam | sed -e 's;\([^\\]\)_;\1 ;g' -e 's;\\\\_;_;g' -e "s/TestUser/$USER_NICKNAME/g" ` if [ "$ectype" = "SNI" ]; then cparam=`echo $cparam | sed -e "s/Host/$HOST/g" -e "s/Dom/$DOMSUF/g" ` sparam=`echo $sparam | sed -e "s/Host/$HOST/g" -e "s/Dom/$DOMSUF/g" ` @@ -483,7 +483,7 @@ ssl_auth() SERVER_VMIN=tls1.0 SERVER_VMAX=tls1.3 fi - start_selfserv `echo "$sparam" | sed -e 's,_, ,g'` + start_selfserv `echo "$sparam" | sed -e 's;\([^\\]\)_;\1 ;g' -e 's;\\\\_;_;g'` echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\" echo " ${cparam} < ${REQUEST_FILE}" @@ -754,7 +754,10 @@ ssl_crl_ssl() fi servarg=`echo $sparam | awk '{r=split($0,a,"-r") - 1;print r;}'` pwd=`echo $cparam | grep nss` + # did we select TestUser? user=`echo $cparam | grep TestUser` + # did we explicitly select a cert? + auto=`echo $cparam | grep '\\-n'` _cparam=$cparam case $servarg in 1) if [ -z "$pwd" -o -z "$user" ]; then @@ -763,14 +766,24 @@ ssl_crl_ssl() rev_modvalue=254 fi ;; - 2) rev_modvalue=254 ;; + 2) if [ -z "$auto" ]; then + rev_modvalue=0 + else + rev_modvalue=254 + fi + ;; 3) if [ -z "$pwd" -o -z "$user" ]; then rev_modvalue=0 else rev_modvalue=1 fi ;; - 4) rev_modvalue=1 ;; + 4) if [ -z "$auto" ]; then + rev_modvalue=0 + else + rev_modvalue=1 + fi + ;; esac TEMP_NUM=0 while [ $TEMP_NUM -lt $CRL_GROUP_RANGE ] @@ -778,8 +791,8 @@ ssl_crl_ssl() CURR_SER_NUM=`expr ${CRL_GROUP_BEGIN} + ${TEMP_NUM}` TEMP_NUM=`expr $TEMP_NUM + 1` USER_NICKNAME="TestUser${CURR_SER_NUM}" - cparam=`echo $_cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" ` - start_selfserv `echo "$sparam" | sed -e 's,_, ,g'` + cparam=`echo $_cparam | sed -e 's;\([^\\]\)_;\1 ;g' -e 's;\\\\_;_;g' -e "s/TestUser/$USER_NICKNAME/g" ` + start_selfserv `echo "$sparam" | sed -e 's;\([^\\]\)_;\1 ;g' -e 's;\\\\_;_;g'` echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\" echo " ${cparam} < ${REQUEST_FILE}" @@ -1246,7 +1259,7 @@ ssl_crl_cache() CURR_SER_NUM=`expr ${CRL_GRP_1_BEGIN} + ${TEMP_NUM}` TEMP_NUM=`expr $TEMP_NUM + 1` USER_NICKNAME="TestUser${CURR_SER_NUM}" - cparam=`echo $_cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" ` + cparam=`echo $_cparam | sed -e 's;\([^\]\)_;\1 ;g' -e 's;\\_;_;g' -e "s/TestUser/$USER_NICKNAME/g" ` echo "Server Args: $SERV_ARG" echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${R_CLIENTDIR} $verbose \\" diff --git a/tests/ssl/sslauth.txt b/tests/ssl/sslauth.txt index 70e498962..61e4a6c6d 100644 --- a/tests/ssl/sslauth.txt +++ b/tests/ssl/sslauth.txt @@ -43,17 +43,18 @@ noECC 0 -r_-r_-r_-E -V_tls1.3:tls1.3_-E_-n_none_-w_nss TLS 1.3 Request don't require client auth on post hs (client does not provide auth) noECC 1 -r_-r_-r_-r_-E -V_tls1.3:tls1.3_-E_-n_none_-w_nss TLS 1.3 Require client auth on post hs (client does not provide auth) noECC 0 -r_-r_-r_-E_-u -V_tls1.3:tls1.3_-E_-n_TestUser_-w_nss TLS 1.3 Request don't require client auth on post hs with session ticket (client auth) + noECC 0 -r_-r_-J_rsa\\_pkcs1\\_sha256 -V_tls1.2:_-w_nss TLS 1.2 Require client auth auto select(RSA) (client auth) # # Use EC cert for client authentication # - ECC 0 -r -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec TLS Request don't require client auth (EC) (bad password) - ECC 0 -r -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec TLS Request don't require client auth (EC) (client auth) - ECC 254 -r_-r -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec TLS Require client auth (EC) (bad password) - ECC 0 -r_-r -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec_ TLS Require client auth (EC) (client auth) - ECC 0 -r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Request don't require client auth (EC) (bad password) - ECC 0 -r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Request don't require client auth (EC) (client auth) - ECC 254 -r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Require client auth (EC) (bad password) - ECC 0 -r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Require client auth (EC) (client auth) + ECC 0 -r -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec TLS Request don't require client auth (EC) (bad password) + ECC 0 -r -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec TLS Request don't require client auth (EC) (client auth) + ECC 254 -r_-r -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec TLS Require client auth (EC) (bad password) + ECC 0 -r_-r -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec_ TLS Require client auth (EC) (client auth) + ECC 0 -r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Request don't require client auth (EC) (bad password) + ECC 0 -r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Request don't require client auth (EC) (client auth) + ECC 254 -r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Require client auth (EC) (bad password) + ECC 0 -r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Require client auth (EC) (client auth) ECC 0 -r_-r_-r -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec TLS Request don't require client auth on 2nd hs (EC) (bad password) ECC 0 -r_-r_-r -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec TLS Request don't require client auth on 2nd hs (EC) (client auth) ECC 1 -r_-r_-r_-r -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec TLS Require client auth on 2nd hs (EC) (bad password) @@ -62,10 +63,12 @@ ECC 0 -r_-r_-r -V_ssl3:tls1.0_-w_nss_-n_TestUser-ec TLS 1.0 Request don't require client auth on 2nd hs (EC) (client auth) ECC 1 -r_-r_-r_-r -V_ssl3:tls1.0_-w_bogus_-n_TestUser-ec TLS 1.0 Require client auth on 2nd hs (EC) (bad password) ECC 0 -r_-r_-r_-r -V_ssl3:tls1.0_-w_nss_-n_TestUser-ec_ TLS 1.0 Require client auth on 2nd hs (EC) (client auth) - ECC 0 -r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Request don't require client auth on 2nd hs (EC) (bad password) - ECC 0 -r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Request don't require client auth on 2nd hs (EC) (client auth) - ECC 1 -r_-r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Require client auth on 2nd hs (EC) (bad password) - ECC 0 -r_-r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Require client auth on 2nd hs (EC) (client auth) + ECC 0 -r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Request don't require client auth on 2nd hs (EC) (bad password) + ECC 0 -r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Request don't require client auth on 2nd hs (EC) (client auth) + ECC 1 -r_-r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus SSL3 Require client auth on 2nd hs (EC) (bad password) + ECC 0 -r_-r_-r_-r -V_ssl3:ssl3_-n_TestUser-ec_-w_nss SSL3 Require client auth on 2nd hs (EC) (client auth) + ECC 0 -r_-r_-J_ecdsa\\_secp256r1\\_sha256 -V_tls1.2:_-w_nss TLS 1.2 Require client auth auto select(EC) (client auth) + ECC 0 -r_-r_-J_ecdsa\\_secp256r1\\_sha256,ecdsa\\_secp384r1\\_sha384 -V_tls1.3:_-w_nss TLS 1.3 Require client auth auto select (EC) (client auth) # # SNI Tests # |