summaryrefslogtreecommitdiff
path: root/src/gmcerts.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-08-03 15:28:16 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-08-03 15:28:16 +0300
commit856fbf1bf25038997b3dd61fc8707370f085e6da (patch)
tree90c6526fc839184d24bdbd770a956c8568b2956b /src/gmcerts.c
parent922ad3220909b7314ca629949b44d7d3aa0336b2 (diff)
Server certificate TOFU
Diffstat (limited to 'src/gmcerts.c')
-rw-r--r--src/gmcerts.c121
1 files changed, 120 insertions, 1 deletions
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 @@
1#include "gmcerts.h" 1#include "gmcerts.h"
2 2
3#include <the_Foundation/file.h>
4#include <the_Foundation/path.h>
5#include <the_Foundation/regexp.h>
6#include <the_Foundation/stringhash.h>
7#include <the_Foundation/time.h>
8#include <ctype.h>
9
10static const char *filename_GmCerts_ = "trusted.txt";
11
12iDeclareClass(TrustEntry)
13
14struct Impl_TrustEntry {
15 iObject object;
16 iBlock fingerprint;
17 iTime validUntil;
18};
19
20void init_TrustEntry(iTrustEntry *d, const iBlock *fingerprint, const iDate *until) {
21 initCopy_Block(&d->fingerprint, fingerprint);
22 init_Time(&d->validUntil, until);
23}
24
25void deinit_TrustEntry(iTrustEntry *d) {
26 deinit_Block(&d->fingerprint);
27}
28
29iDefineObjectConstructionArgs(TrustEntry, (const iBlock *fingerprint, const iDate *until), fingerprint, until)
30iDefineClass(TrustEntry)
31
32/*-----------------------------------------------------------------------------------------------*/
33
3struct Impl_GmCerts { 34struct Impl_GmCerts {
4 iString saveDir; 35 iString saveDir;
36 iStringHash *trusted;
5}; 37};
6 38
7iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir) 39iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir)
8 40
41static void save_GmCerts_(const iGmCerts *d) {
42 iBeginCollect();
43 iFile *f = new_File(collect_String(concatCStr_Path(&d->saveDir, filename_GmCerts_)));
44 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
45 iString line;
46 init_String(&line);
47 iConstForEach(StringHash, i, d->trusted) {
48 const iTrustEntry *trust = value_StringHashNode(i.value);
49 format_String(&line,
50 "%s %ld %s\n",
51 cstr_String(key_StringHashConstIterator(&i)),
52 integralSeconds_Time(&trust->validUntil),
53 cstrCollect_String(hexEncode_Block(&trust->fingerprint)));
54 write_File(f, &line.chars);
55 }
56 deinit_String(&line);
57 }
58 iRelease(f);
59 iEndCollect();
60}
61
62static void load_GmCerts_(iGmCerts *d) {
63 iFile *f = new_File(collect_String(concatCStr_Path(&d->saveDir, filename_GmCerts_)));
64 if (open_File(f, readOnly_FileMode | text_FileMode)) {
65 iRegExp * pattern = new_RegExp("([^\\s]+) ([0-9]+) ([a-z0-9]+)", 0);
66 const iRangecc src = range_Block(collect_Block(readAll_File(f)));
67 iRangecc line = iNullRange;
68 while (nextSplit_Rangecc(&src, "\n", &line)) {
69 iRegExpMatch m;
70 if (matchRange_RegExp(pattern, line, &m)) {
71 const iRangecc domain = capturedRange_RegExpMatch(&m, 1);
72 const iRangecc until = capturedRange_RegExpMatch(&m, 2);
73 const iRangecc fp = capturedRange_RegExpMatch(&m, 3);
74 time_t sec;
75 sscanf(until.start, "%ld", &sec);
76 iDate untilDate;
77 initSinceEpoch_Date(&untilDate, sec);
78 insert_StringHash(d->trusted,
79 collect_String(newRange_String(domain)),
80 new_TrustEntry(collect_Block(hexDecode_Rangecc(fp)),
81 &untilDate));
82 }
83 }
84 iRelease(pattern);
85 }
86 iRelease(f);
87}
88
9void init_GmCerts(iGmCerts *d, const char *saveDir) { 89void init_GmCerts(iGmCerts *d, const char *saveDir) {
10 initCStr_String(&d->saveDir, saveDir); 90 initCStr_String(&d->saveDir, saveDir);
91 d->trusted = new_StringHash();
92 load_GmCerts_(d);
11} 93}
12 94
13void deinit_GmCerts(iGmCerts *d) { 95void deinit_GmCerts(iGmCerts *d) {
96 iRelease(d->trusted);
14 deinit_String(&d->saveDir); 97 deinit_String(&d->saveDir);
15} 98}
99
100iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *cert) {
101 if (!cert) {
102 return iFalse;
103 }
104 if (isExpired_TlsCertificate(cert)) {
105 return iFalse;
106 }
107 if (!verifyDomain_TlsCertificate(cert, domain)) {
108 return iFalse;
109 }
110 /* Good certificate. If not already trusted, add it now. */
111 const iString *key = collect_String(newRange_String(domain));
112 iDate until;
113 validUntil_TlsCertificate(cert, &until);
114 iBlock *fingerprint = collect_Block(fingerprint_TlsCertificate(cert));
115 iTrustEntry *trust = value_StringHash(d->trusted, key);
116 if (trust) {
117 /* We already have it, check if it matches the one we trust for this domain (if it's
118 still valid. */
119 iTime now;
120 initCurrent_Time(&now);
121 if (secondsSince_Time(&trust->validUntil, &now) > 0) {
122 /* Trusted cert is still valid. */
123 return cmp_Block(fingerprint, &trust->fingerprint) == 0;
124 }
125 /* Update the trusted cert. */
126 init_Time(&trust->validUntil, &until);
127 set_Block(&trust->fingerprint, fingerprint);
128 }
129 else {
130 insert_StringHash(d->trusted, key, iClob(new_TrustEntry(fingerprint, &until)));
131 }
132 save_GmCerts_(d);
133 return iTrue;
134}