summaryrefslogtreecommitdiff
path: root/lib/certdb
diff options
context:
space:
mode:
authorRobert Relyea <rrelyea@redhat.com>2022-03-18 15:21:12 -0700
committerRobert Relyea <rrelyea@redhat.com>2022-03-18 15:21:12 -0700
commit1e1791002a5bddb0b5dee8a75889f312b193ff6e (patch)
treec5a1c17469b094c5c94fd3000fb8a7a2d595a898 /lib/certdb
parent7d2a566944c72a7347b569de6669c550fe846f6f (diff)
downloadnss-hg-1e1791002a5bddb0b5dee8a75889f312b193ff6e.tar.gz
Bug 1552254 internal_error alert on Certificate Request with sha1+ecdsa in TLS 1.3
We need to be able to select Client certificates based on the schemes sent to us from the server. Rather than changing the callback function, this patch adds those schemes to the ssl socket info as suggested by Dana. In addition, two helpful functions have been added to aid User applications in properly selecting the Certificate: PRBool SSL_CertIsUsable(PRFileDesc *fd, CERTCertificate *cert) - returns true if the given cert matches the schemes of the server, the schemes configured on the socket, capability of the token the private key resides on, and the current policy. For future SSL protocol, additional restrictions may be parsed. SSL_FilterCertListBySocket(PRFileDesc *fd, CERTCertList *certlist) - removes the certs from the cert list that doesn't pass the SSL_CertIsUsable() call. In addition the built in cert selection function (NSS_GetClientAuthData) uses the above functions to filter the list. In order to support the NSS_GetClientAuthData three new functions have been added: SECStatus CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname, void *pwarg) -- removes the certs that don't match the 'nickname'. SECStatus CERT_FilterCertListByCertList(CERTCertlist *certList, const CERTCertlist *filterList ) -- removes all the certs on the first cert list that isn't on the second. PRBool CERT_IsInList(CERTCertificate *, const CERTCertList *certList) -- returns true if cert is on certList. In addition * PK11_FindObjectForCert() is exported so the token the cert lives on can be accessed. * the ssle ssl_PickClientSignatureScheme() function (along with several supporing functions) have been modified so it can be used by SSL_CertIsUsable() Differential Revision: https://phabricator.services.mozilla.com/D135715
Diffstat (limited to 'lib/certdb')
-rw-r--r--lib/certdb/cert.h15
-rw-r--r--lib/certdb/certdb.c84
2 files changed, 99 insertions, 0 deletions
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;
/*