diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-03 15:28:16 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-03 15:28:16 +0300 |
commit | 856fbf1bf25038997b3dd61fc8707370f085e6da (patch) | |
tree | 90c6526fc839184d24bdbd770a956c8568b2956b /src/gmcerts.c | |
parent | 922ad3220909b7314ca629949b44d7d3aa0336b2 (diff) |
Server certificate TOFU
Diffstat (limited to 'src/gmcerts.c')
-rw-r--r-- | src/gmcerts.c | 121 |
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 | |||
10 | static const char *filename_GmCerts_ = "trusted.txt"; | ||
11 | |||
12 | iDeclareClass(TrustEntry) | ||
13 | |||
14 | struct Impl_TrustEntry { | ||
15 | iObject object; | ||
16 | iBlock fingerprint; | ||
17 | iTime validUntil; | ||
18 | }; | ||
19 | |||
20 | void init_TrustEntry(iTrustEntry *d, const iBlock *fingerprint, const iDate *until) { | ||
21 | initCopy_Block(&d->fingerprint, fingerprint); | ||
22 | init_Time(&d->validUntil, until); | ||
23 | } | ||
24 | |||
25 | void deinit_TrustEntry(iTrustEntry *d) { | ||
26 | deinit_Block(&d->fingerprint); | ||
27 | } | ||
28 | |||
29 | iDefineObjectConstructionArgs(TrustEntry, (const iBlock *fingerprint, const iDate *until), fingerprint, until) | ||
30 | iDefineClass(TrustEntry) | ||
31 | |||
32 | /*-----------------------------------------------------------------------------------------------*/ | ||
33 | |||
3 | struct Impl_GmCerts { | 34 | struct Impl_GmCerts { |
4 | iString saveDir; | 35 | iString saveDir; |
36 | iStringHash *trusted; | ||
5 | }; | 37 | }; |
6 | 38 | ||
7 | iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir) | 39 | iDefineTypeConstructionArgs(GmCerts, (const char *saveDir), saveDir) |
8 | 40 | ||
41 | static 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 | |||
62 | static 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 | |||
9 | void init_GmCerts(iGmCerts *d, const char *saveDir) { | 89 | void 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 | ||
13 | void deinit_GmCerts(iGmCerts *d) { | 95 | void deinit_GmCerts(iGmCerts *d) { |
96 | iRelease(d->trusted); | ||
14 | deinit_String(&d->saveDir); | 97 | deinit_String(&d->saveDir); |
15 | } | 98 | } |
99 | |||
100 | iBool 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 | } | ||