From 856fbf1bf25038997b3dd61fc8707370f085e6da Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Mon, 3 Aug 2020 15:28:16 +0300 Subject: Server certificate TOFU --- src/gmcerts.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) (limited to 'src/gmcerts.c') diff --git a/src/gmcerts.c b/src/gmcerts.c index cb4d8477..0aa084a4 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c @@ -1,15 +1,134 @@ #include "gmcerts.h" +#include +#include +#include +#include +#include +#include + +static const char *filename_GmCerts_ = "trusted.txt"; + +iDeclareClass(TrustEntry) + +struct Impl_TrustEntry { + iObject object; + iBlock fingerprint; + iTime validUntil; +}; + +void init_TrustEntry(iTrustEntry *d, const iBlock *fingerprint, const iDate *until) { + initCopy_Block(&d->fingerprint, fingerprint); + init_Time(&d->validUntil, until); +} + +void deinit_TrustEntry(iTrustEntry *d) { + deinit_Block(&d->fingerprint); +} + +iDefineObjectConstructionArgs(TrustEntry, (const iBlock *fingerprint, const iDate *until), fingerprint, until) +iDefineClass(TrustEntry) + +/*-----------------------------------------------------------------------------------------------*/ + struct Impl_GmCerts { iString saveDir; + iStringHash *trusted; }; iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir) +static void save_GmCerts_(const iGmCerts *d) { + iBeginCollect(); + iFile *f = new_File(collect_String(concatCStr_Path(&d->saveDir, filename_GmCerts_))); + if (open_File(f, writeOnly_FileMode | text_FileMode)) { + iString line; + init_String(&line); + iConstForEach(StringHash, i, d->trusted) { + const iTrustEntry *trust = value_StringHashNode(i.value); + format_String(&line, + "%s %ld %s\n", + cstr_String(key_StringHashConstIterator(&i)), + integralSeconds_Time(&trust->validUntil), + cstrCollect_String(hexEncode_Block(&trust->fingerprint))); + write_File(f, &line.chars); + } + deinit_String(&line); + } + iRelease(f); + iEndCollect(); +} + +static void load_GmCerts_(iGmCerts *d) { + iFile *f = new_File(collect_String(concatCStr_Path(&d->saveDir, filename_GmCerts_))); + if (open_File(f, readOnly_FileMode | text_FileMode)) { + iRegExp * pattern = new_RegExp("([^\\s]+) ([0-9]+) ([a-z0-9]+)", 0); + const iRangecc src = range_Block(collect_Block(readAll_File(f))); + iRangecc line = iNullRange; + while (nextSplit_Rangecc(&src, "\n", &line)) { + iRegExpMatch m; + if (matchRange_RegExp(pattern, line, &m)) { + const iRangecc domain = capturedRange_RegExpMatch(&m, 1); + const iRangecc until = capturedRange_RegExpMatch(&m, 2); + const iRangecc fp = capturedRange_RegExpMatch(&m, 3); + time_t sec; + sscanf(until.start, "%ld", &sec); + iDate untilDate; + initSinceEpoch_Date(&untilDate, sec); + insert_StringHash(d->trusted, + collect_String(newRange_String(domain)), + new_TrustEntry(collect_Block(hexDecode_Rangecc(fp)), + &untilDate)); + } + } + iRelease(pattern); + } + iRelease(f); +} + void init_GmCerts(iGmCerts *d, const char *saveDir) { initCStr_String(&d->saveDir, saveDir); + d->trusted = new_StringHash(); + load_GmCerts_(d); } -void deinit_GmCerts(iGmCerts *d) { +void deinit_GmCerts(iGmCerts *d) { + iRelease(d->trusted); deinit_String(&d->saveDir); } + +iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *cert) { + if (!cert) { + return iFalse; + } + if (isExpired_TlsCertificate(cert)) { + return iFalse; + } + if (!verifyDomain_TlsCertificate(cert, domain)) { + return iFalse; + } + /* Good certificate. If not already trusted, add it now. */ + const iString *key = collect_String(newRange_String(domain)); + iDate until; + validUntil_TlsCertificate(cert, &until); + iBlock *fingerprint = collect_Block(fingerprint_TlsCertificate(cert)); + iTrustEntry *trust = value_StringHash(d->trusted, key); + if (trust) { + /* We already have it, check if it matches the one we trust for this domain (if it's + still valid. */ + iTime now; + initCurrent_Time(&now); + if (secondsSince_Time(&trust->validUntil, &now) > 0) { + /* Trusted cert is still valid. */ + return cmp_Block(fingerprint, &trust->fingerprint) == 0; + } + /* Update the trusted cert. */ + init_Time(&trust->validUntil, &until); + set_Block(&trust->fingerprint, fingerprint); + } + else { + insert_StringHash(d->trusted, key, iClob(new_TrustEntry(fingerprint, &until))); + } + save_GmCerts_(d); + return iTrue; +} -- cgit v1.2.3