From 1e5dfdc840824723dfa142707aca1f0fca4c0056 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 26 Mar 2021 11:14:57 +0200 Subject: GmCerts: Relaxed domain verification Allow a certificate for a higher-level domain to be verified against any subdomains, i.e., implicitly assume every certificate uses wildcards. CA verification is still done separately, and OpenSSL does that strictly as before. --- src/gmcerts.c | 31 ++++++++++++++++++++++++++++++- src/gmcerts.h | 2 ++ src/gmrequest.c | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/gmcerts.c b/src/gmcerts.c index 6a1ba98c..2cc10a3d 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c @@ -374,6 +374,35 @@ void deinit_GmCerts(iGmCerts *d) { delete_Mutex(d->mtx); } +static iRangecc stripFirstDomainLabel_(iRangecc domain) { + iRangecc label = iNullRange; + if (nextSplit_Rangecc(domain, ".", &label) && nextSplit_Rangecc(domain, ".", &label)) { + return (iRangecc){ label.start, domain.end }; + } + return iNullRange; +} + +iBool verifyDomain_GmCerts(const iTlsCertificate *cert, iRangecc domain) { + if (verifyDomain_TlsCertificate(cert, domain)) { + return iTrue; + } + /* Allow for an implicit wildcard in the domain name. Self-signed TOFU is really only + about the public/private key pair; any other details should be considered + complementary. */ + for (iRangecc higherDomain = stripFirstDomainLabel_(domain); + !isEmpty_Range(&higherDomain); + higherDomain = stripFirstDomainLabel_(higherDomain)) { + if (!iStrStrN(higherDomain.start, ".", size_Range(&higherDomain))) { + /* Must have two labels at least. */ + break; + } + if (verifyDomain_TlsCertificate(cert, higherDomain)) { + return iTrue; + } + } + return iFalse; +} + iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *cert) { if (!cert) { return iFalse; @@ -383,7 +412,7 @@ iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *ce } /* We trust CA verification implicitly. */ const iBool isAuth = verify_TlsCertificate(cert) == authority_TlsCertificateVerifyStatus; - if (!isAuth && !verifyDomain_TlsCertificate(cert, domain)) { + if (!isAuth && !verifyDomain_GmCerts(cert, domain)) { return iFalse; } /* TODO: Could call setTrusted_GmCerts() instead of duplicating the trust-setting. */ diff --git a/src/gmcerts.h b/src/gmcerts.h index a28c050e..a9859845 100644 --- a/src/gmcerts.h +++ b/src/gmcerts.h @@ -95,3 +95,5 @@ const iPtrArray * listIdentities_GmCerts (const iGmCerts *, iGmCertsIdentityF void signIn_GmCerts (iGmCerts *, iGmIdentity *identity, const iString *url); void signOut_GmCerts (iGmCerts *, const iString *url); + +iBool verifyDomain_GmCerts (const iTlsCertificate *cert, iRangecc domain); diff --git a/src/gmrequest.c b/src/gmrequest.c index f065f935..c968990c 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c @@ -157,7 +157,7 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) { if (!isExpired_TlsCertificate(cert)) { resp->certFlags |= timeVerified_GmCertFlag; } - if (verifyDomain_TlsCertificate(cert, domain)) { + if (verifyDomain_GmCerts(cert, domain)) { resp->certFlags |= domainVerified_GmCertFlag; } if (checkTrust_GmCerts(d->certs, domain, cert)) { -- cgit v1.2.3