summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-11-08 13:45:51 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-11-08 13:45:51 +0200
commitbb53ff49396cae88836ff391fd20589a687ae83f (patch)
treeaadc25a24ed2e12809298791f4566d7e1ecfb770
parentc0280998be065ab075581e46c52c6cc27e4b21a9 (diff)
Manually trusting a server certificate
-rw-r--r--src/gmcerts.c17
-rw-r--r--src/gmcerts.h3
-rw-r--r--src/gmrequest.c10
-rw-r--r--src/gmrequest.h10
-rw-r--r--src/ui/documentwidget.c33
5 files changed, 62 insertions, 11 deletions
diff --git a/src/gmcerts.c b/src/gmcerts.c
index 27b226a0..f7475348 100644
--- a/src/gmcerts.c
+++ b/src/gmcerts.c
@@ -381,6 +381,7 @@ iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *ce
381 if (!verifyDomain_TlsCertificate(cert, domain)) { 381 if (!verifyDomain_TlsCertificate(cert, domain)) {
382 return iFalse; 382 return iFalse;
383 } 383 }
384 /* TODO: Could call setTrusted_GmCerts() instead of duplicating the trust-setting. */
384 /* Good certificate. If not already trusted, add it now. */ 385 /* Good certificate. If not already trusted, add it now. */
385 iString *key = newRange_String(domain); 386 iString *key = newRange_String(domain);
386 iDate until; 387 iDate until;
@@ -415,6 +416,22 @@ iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *ce
415 return iTrue; 416 return iTrue;
416} 417}
417 418
419void setTrusted_GmCerts(iGmCerts *d, iRangecc domain, const iBlock *fingerprint,
420 const iDate *validUntil) {
421 iString *key = collect_String(newRange_String(domain));
422 lock_Mutex(d->mtx);
423 iTrustEntry *trust = value_StringHash(d->trusted, key);
424 if (trust) {
425 init_Time(&trust->validUntil, validUntil);
426 set_Block(&trust->fingerprint, fingerprint);
427 }
428 else {
429 insert_StringHash(d->trusted, key, iClob(trust = new_TrustEntry(fingerprint, validUntil)));
430 }
431 save_GmCerts_(d);
432 unlock_Mutex(d->mtx);
433}
434
418iGmIdentity *identity_GmCerts(iGmCerts *d, unsigned int id) { 435iGmIdentity *identity_GmCerts(iGmCerts *d, unsigned int id) {
419 return at_PtrArray(&d->idents, id); 436 return at_PtrArray(&d->idents, id);
420} 437}
diff --git a/src/gmcerts.h b/src/gmcerts.h
index f0ed37b5..2fd1023a 100644
--- a/src/gmcerts.h
+++ b/src/gmcerts.h
@@ -60,7 +60,8 @@ iDeclareTypeConstructionArgs(GmCerts, const char *saveDir)
60typedef iBool (*iGmCertsIdentityFilterFunc)(void *context, const iGmIdentity *); 60typedef iBool (*iGmCertsIdentityFilterFunc)(void *context, const iGmIdentity *);
61 61
62iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, const iTlsCertificate *cert); 62iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, const iTlsCertificate *cert);
63 63void setTrusted_GmCerts (iGmCerts *, iRangecc domain, const iBlock *fingerprint,
64 const iDate *validUntil);
64/** 65/**
65 * Create a new self-signed TLS client certificate for identifying the user. 66 * Create a new self-signed TLS client certificate for identifying the user.
66 * @a commonName and the other name parameters are inserted in the subject field 67 * @a commonName and the other name parameters are inserted in the subject field
diff --git a/src/gmrequest.c b/src/gmrequest.c
index e65847e1..32b71922 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -45,6 +45,7 @@ void init_GmResponse(iGmResponse *d) {
45 init_String(&d->meta); 45 init_String(&d->meta);
46 init_Block(&d->body, 0); 46 init_Block(&d->body, 0);
47 d->certFlags = 0; 47 d->certFlags = 0;
48 init_Block(&d->certFingerprint, 0);
48 iZap(d->certValidUntil); 49 iZap(d->certValidUntil);
49 init_String(&d->certSubject); 50 init_String(&d->certSubject);
50 iZap(d->when); 51 iZap(d->when);
@@ -55,6 +56,7 @@ void initCopy_GmResponse(iGmResponse *d, const iGmResponse *other) {
55 initCopy_String(&d->meta, &other->meta); 56 initCopy_String(&d->meta, &other->meta);
56 initCopy_Block(&d->body, &other->body); 57 initCopy_Block(&d->body, &other->body);
57 d->certFlags = other->certFlags; 58 d->certFlags = other->certFlags;
59 initCopy_Block(&d->certFingerprint, &other->certFingerprint);
58 d->certValidUntil = other->certValidUntil; 60 d->certValidUntil = other->certValidUntil;
59 initCopy_String(&d->certSubject, &other->certSubject); 61 initCopy_String(&d->certSubject, &other->certSubject);
60 d->when = other->when; 62 d->when = other->when;
@@ -63,6 +65,7 @@ void initCopy_GmResponse(iGmResponse *d, const iGmResponse *other) {
63void deinit_GmResponse(iGmResponse *d) { 65void deinit_GmResponse(iGmResponse *d) {
64 deinit_String(&d->certSubject); 66 deinit_String(&d->certSubject);
65 deinit_Block(&d->body); 67 deinit_Block(&d->body);
68 deinit_Block(&d->certFingerprint);
66 deinit_String(&d->meta); 69 deinit_String(&d->meta);
67} 70}
68 71
@@ -71,6 +74,7 @@ void clear_GmResponse(iGmResponse *d) {
71 clear_String(&d->meta); 74 clear_String(&d->meta);
72 clear_Block(&d->body); 75 clear_Block(&d->body);
73 d->certFlags = 0; 76 d->certFlags = 0;
77 clear_Block(&d->certFingerprint);
74 iZap(d->certValidUntil); 78 iZap(d->certValidUntil);
75 clear_String(&d->certSubject); 79 clear_String(&d->certSubject);
76 iZap(d->when); 80 iZap(d->when);
@@ -86,7 +90,8 @@ void serialize_GmResponse(const iGmResponse *d, iStream *outs) {
86 write32_Stream(outs, d->statusCode); 90 write32_Stream(outs, d->statusCode);
87 serialize_String(&d->meta, outs); 91 serialize_String(&d->meta, outs);
88 serialize_Block(&d->body, outs); 92 serialize_Block(&d->body, outs);
89 write32_Stream(outs, d->certFlags); 93 /* TODO: Add certificate fingerprint, but need to bump file version first. */
94 write32_Stream(outs, d->certFlags & ~haveFingerprint_GmCertFlag);
90 serialize_Date(&d->certValidUntil, outs); 95 serialize_Date(&d->certValidUntil, outs);
91 serialize_String(&d->certSubject, outs); 96 serialize_String(&d->certSubject, outs);
92 writeU64_Stream(outs, d->when.ts.tv_sec); 97 writeU64_Stream(outs, d->when.ts.tv_sec);
@@ -100,6 +105,7 @@ void deserialize_GmResponse(iGmResponse *d, iStream *ins) {
100 deserialize_Date(&d->certValidUntil, ins); 105 deserialize_Date(&d->certValidUntil, ins);
101 deserialize_String(&d->certSubject, ins); 106 deserialize_String(&d->certSubject, ins);
102 iZap(d->when); 107 iZap(d->when);
108 clear_Block(&d->certFingerprint);
103 if (version_Stream(ins) >= addedResponseTimestamps_FileVersion) { 109 if (version_Stream(ins) >= addedResponseTimestamps_FileVersion) {
104 d->when.ts.tv_sec = readU64_Stream(ins); 110 d->when.ts.tv_sec = readU64_Stream(ins);
105 } 111 }
@@ -138,6 +144,8 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) {
138 if (cert) { 144 if (cert) {
139 const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req))); 145 const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req)));
140 d->resp.certFlags |= available_GmCertFlag; 146 d->resp.certFlags |= available_GmCertFlag;
147 set_Block(&d->resp.certFingerprint, collect_Block(fingerprint_TlsCertificate(cert)));
148 d->resp.certFlags |= haveFingerprint_GmCertFlag;
141 if (!isExpired_TlsCertificate(cert)) { 149 if (!isExpired_TlsCertificate(cert)) {
142 d->resp.certFlags |= timeVerified_GmCertFlag; 150 d->resp.certFlags |= timeVerified_GmCertFlag;
143 } 151 }
diff --git a/src/gmrequest.h b/src/gmrequest.h
index 311b9ad6..31059a44 100644
--- a/src/gmrequest.h
+++ b/src/gmrequest.h
@@ -31,10 +31,11 @@ iDeclareType(GmCerts)
31iDeclareType(GmResponse) 31iDeclareType(GmResponse)
32 32
33enum iGmCertFlags { 33enum iGmCertFlags {
34 available_GmCertFlag = iBit(1), /* certificate provided by server */ 34 available_GmCertFlag = iBit(1), /* certificate provided by server */
35 trusted_GmCertFlag = iBit(2), /* TOFU status */ 35 trusted_GmCertFlag = iBit(2), /* TOFU status */
36 timeVerified_GmCertFlag = iBit(3), /* has not expired */ 36 timeVerified_GmCertFlag = iBit(3), /* has not expired */
37 domainVerified_GmCertFlag = iBit(4), /* cert matches server domain */ 37 domainVerified_GmCertFlag = iBit(4), /* cert matches server domain */
38 haveFingerprint_GmCertFlag = iBit(5),
38}; 39};
39 40
40struct Impl_GmResponse { 41struct Impl_GmResponse {
@@ -42,6 +43,7 @@ struct Impl_GmResponse {
42 iString meta; /* MIME type or other metadata */ 43 iString meta; /* MIME type or other metadata */
43 iBlock body; 44 iBlock body;
44 int certFlags; 45 int certFlags;
46 iBlock certFingerprint;
45 iDate certValidUntil; 47 iDate certValidUntil;
46 iString certSubject; 48 iString certSubject;
47 iTime when; 49 iTime when;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 33f49ad9..94168d28 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
26#include "audio/player.h" 26#include "audio/player.h"
27#include "command.h" 27#include "command.h"
28#include "defs.h" 28#include "defs.h"
29#include "gmcerts.h"
29#include "gmdocument.h" 30#include "gmdocument.h"
30#include "gmrequest.h" 31#include "gmrequest.h"
31#include "gmutil.h" 32#include "gmutil.h"
@@ -137,6 +138,7 @@ struct Impl_DocumentWidget {
137 iTime sourceTime; 138 iTime sourceTime;
138 iGmDocument * doc; 139 iGmDocument * doc;
139 int certFlags; 140 int certFlags;
141 iBlock * certFingerprint;
140 iDate certExpiry; 142 iDate certExpiry;
141 iString * certSubject; 143 iString * certSubject;
142 int redirectCount; 144 int redirectCount;
@@ -177,6 +179,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
177 init_PersistentDocumentState(&d->mod); 179 init_PersistentDocumentState(&d->mod);
178 d->flags = 0; 180 d->flags = 0;
179 iZap(d->certExpiry); 181 iZap(d->certExpiry);
182 d->certFingerprint = new_Block(0);
180 d->certFlags = 0; 183 d->certFlags = 0;
181 d->certSubject = new_String(); 184 d->certSubject = new_String();
182 d->state = blank_RequestState; 185 d->state = blank_RequestState;
@@ -242,6 +245,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
242 } 245 }
243 deinit_PtrArray(&d->visiblePlayers); 246 deinit_PtrArray(&d->visiblePlayers);
244 deinit_PtrArray(&d->visibleLinks); 247 deinit_PtrArray(&d->visibleLinks);
248 delete_Block(d->certFingerprint);
245 delete_String(d->certSubject); 249 delete_String(d->certSubject);
246 delete_String(d->titleUser); 250 delete_String(d->titleUser);
247 deinit_PersistentDocumentState(&d->mod); 251 deinit_PersistentDocumentState(&d->mod);
@@ -881,6 +885,7 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r
881 if (response) { 885 if (response) {
882 d->certFlags = response->certFlags; 886 d->certFlags = response->certFlags;
883 d->certExpiry = response->certValidUntil; 887 d->certExpiry = response->certValidUntil;
888 set_Block(d->certFingerprint, &response->certFingerprint);
884 set_String(d->certSubject, &response->certSubject); 889 set_String(d->certSubject, &response->certSubject);
885 } 890 }
886 iLabelWidget *lock = findWidget_App("navbar.lock"); 891 iLabelWidget *lock = findWidget_App("navbar.lock");
@@ -1271,9 +1276,14 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1271 return iFalse; 1276 return iFalse;
1272 } 1277 }
1273 else if (equal_Command(cmd, "server.showcert") && d == document_App()) { 1278 else if (equal_Command(cmd, "server.showcert") && d == document_App()) {
1274 const char *unchecked = red_ColorEscape "\u2610"; 1279 const char *unchecked = red_ColorEscape "\u2610";
1275 const char *checked = green_ColorEscape "\u2611"; 1280 const char *checked = green_ColorEscape "\u2611";
1276 makeMessage_Widget( 1281 const char *actionLabels[] = { "Dismiss", uiTextCaution_ColorEscape "Trust" };
1282 const char *actionCmds[] = { "message.ok", "server.trustcert" };
1283 const iBool canTrust =
1284 (d->certFlags == (available_GmCertFlag | haveFingerprint_GmCertFlag |
1285 timeVerified_GmCertFlag | domainVerified_GmCertFlag));
1286 iWidget *dlg = makeQuestion_Widget(
1277 uiHeading_ColorEscape "CERTIFICATE STATUS", 1287 uiHeading_ColorEscape "CERTIFICATE STATUS",
1278 format_CStr("%s%s Domain name %s%s\n" 1288 format_CStr("%s%s Domain name %s%s\n"
1279 "%s%s %s (%04d-%02d-%02d %02d:%02d:%02d)\n" 1289 "%s%s %s (%04d-%02d-%02d %02d:%02d:%02d)\n"
@@ -1295,8 +1305,21 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1295 d->certExpiry.second, 1305 d->certExpiry.second,
1296 d->certFlags & trusted_GmCertFlag ? checked : unchecked, 1306 d->certFlags & trusted_GmCertFlag ? checked : unchecked,
1297 uiText_ColorEscape, 1307 uiText_ColorEscape,
1298 d->certFlags & trusted_GmCertFlag ? "Trusted on first use" 1308 d->certFlags & trusted_GmCertFlag ? "Trusted" : "Not trusted"),
1299 : "Not trusted")); 1309 actionLabels,
1310 actionCmds,
1311 canTrust ? 2 : 1);
1312 addAction_Widget(dlg, SDLK_ESCAPE, 0, "message.ok");
1313 addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok");
1314 return iTrue;
1315 }
1316 else if (equal_Command(cmd, "server.trustcert")) {
1317 const iRangecc host = urlHost_String(d->mod.url);
1318 if (!isEmpty_Block(d->certFingerprint) && !isEmpty_Range(&host)) {
1319 setTrusted_GmCerts(certs_App(), host, d->certFingerprint, &d->certExpiry);
1320 d->certFlags |= trusted_GmCertFlag;
1321 postCommand_App("server.showcert");
1322 }
1300 return iTrue; 1323 return iTrue;
1301 } 1324 }
1302 else if (equal_Command(cmd, "copy") && document_App() == d && !focus_Widget()) { 1325 else if (equal_Command(cmd, "copy") && document_App() == d && !focus_Widget()) {