diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-02 13:05:02 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-02 13:05:02 +0200 |
commit | f992ba117fe420a7231f005e62627380689d57ab (patch) | |
tree | cd8310f3deafb78f910832aaf47b2ff7ac98aa05 /src | |
parent | ff52105505dfa74fc93016aace904a1f1e86b570 (diff) |
Server certificates may also be verified by CAs
If the CA file/path are configured in preferences, trust CA verification
over manual TOFU checks.
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 23 | ||||
-rw-r--r-- | src/gmcerts.c | 8 | ||||
-rw-r--r-- | src/gmrequest.c | 3 | ||||
-rw-r--r-- | src/gmrequest.h | 1 | ||||
-rw-r--r-- | src/prefs.c | 10 | ||||
-rw-r--r-- | src/prefs.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 8 | ||||
-rw-r--r-- | src/ui/util.c | 11 |
8 files changed, 59 insertions, 7 deletions
@@ -224,6 +224,8 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
224 | appendFormat_String(str, "doctheme.dark.set arg:%d\n", d->prefs.docThemeDark); | 224 | appendFormat_String(str, "doctheme.dark.set arg:%d\n", d->prefs.docThemeDark); |
225 | appendFormat_String(str, "doctheme.light.set arg:%d\n", d->prefs.docThemeLight); | 225 | appendFormat_String(str, "doctheme.light.set arg:%d\n", d->prefs.docThemeLight); |
226 | appendFormat_String(str, "saturation.set arg:%d\n", (int) ((d->prefs.saturation * 100) + 0.5f)); | 226 | appendFormat_String(str, "saturation.set arg:%d\n", (int) ((d->prefs.saturation * 100) + 0.5f)); |
227 | appendFormat_String(str, "ca.file noset:1 path:%s\n", cstr_String(&d->prefs.caFile)); | ||
228 | appendFormat_String(str, "ca.path path:%s\n", cstr_String(&d->prefs.caPath)); | ||
227 | appendFormat_String(str, "proxy.gemini address:%s\n", cstr_String(&d->prefs.geminiProxy)); | 229 | appendFormat_String(str, "proxy.gemini address:%s\n", cstr_String(&d->prefs.geminiProxy)); |
228 | appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.gopherProxy)); | 230 | appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.gopherProxy)); |
229 | appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.httpProxy)); | 231 | appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.httpProxy)); |
@@ -306,6 +308,7 @@ static void loadPrefs_App_(iApp *d) { | |||
306 | } | 308 | } |
307 | else { | 309 | else { |
308 | /* default preference values */ | 310 | /* default preference values */ |
311 | setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath); | ||
309 | } | 312 | } |
310 | #if !defined (LAGRANGE_CUSTOM_FRAME) | 313 | #if !defined (LAGRANGE_CUSTOM_FRAME) |
311 | d->prefs.customFrame = iFalse; | 314 | d->prefs.customFrame = iFalse; |
@@ -1205,6 +1208,10 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
1205 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.searchurl")))); | 1208 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.searchurl")))); |
1206 | postCommandf_App("cachesize.set arg:%d", | 1209 | postCommandf_App("cachesize.set arg:%d", |
1207 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); | 1210 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); |
1211 | postCommandf_App("ca.file path:%s", | ||
1212 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.ca.file")))); | ||
1213 | postCommandf_App("ca.path path:%s", | ||
1214 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.ca.path")))); | ||
1208 | postCommandf_App("proxy.gemini address:%s", | 1215 | postCommandf_App("proxy.gemini address:%s", |
1209 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gemini")))); | 1216 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gemini")))); |
1210 | postCommandf_App("proxy.gopher address:%s", | 1217 | postCommandf_App("proxy.gopher address:%s", |
@@ -1586,6 +1593,20 @@ iBool handleCommand_App(const char *cmd) { | |||
1586 | setCStr_String(&d->prefs.downloadDir, suffixPtr_Command(cmd, "path")); | 1593 | setCStr_String(&d->prefs.downloadDir, suffixPtr_Command(cmd, "path")); |
1587 | return iTrue; | 1594 | return iTrue; |
1588 | } | 1595 | } |
1596 | else if (equal_Command(cmd, "ca.file")) { | ||
1597 | setCStr_String(&d->prefs.caFile, suffixPtr_Command(cmd, "path")); | ||
1598 | if (!argLabel_Command(cmd, "noset")) { | ||
1599 | setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath); | ||
1600 | } | ||
1601 | return iTrue; | ||
1602 | } | ||
1603 | else if (equal_Command(cmd, "ca.path")) { | ||
1604 | setCStr_String(&d->prefs.caPath, suffixPtr_Command(cmd, "path")); | ||
1605 | if (!argLabel_Command(cmd, "noset")) { | ||
1606 | setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath); | ||
1607 | } | ||
1608 | return iTrue; | ||
1609 | } | ||
1589 | else if (equal_Command(cmd, "open")) { | 1610 | else if (equal_Command(cmd, "open")) { |
1590 | iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url")); | 1611 | iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url")); |
1591 | const iBool noProxy = argLabel_Command(cmd, "noproxy"); | 1612 | const iBool noProxy = argLabel_Command(cmd, "noproxy"); |
@@ -1761,6 +1782,8 @@ iBool handleCommand_App(const char *cmd) { | |||
1761 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); | 1782 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); |
1762 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); | 1783 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); |
1763 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); | 1784 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); |
1785 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.caFile); | ||
1786 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.path"), &d->prefs.caPath); | ||
1764 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.geminiProxy); | 1787 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.geminiProxy); |
1765 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); | 1788 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); |
1766 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); | 1789 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); |
diff --git a/src/gmcerts.c b/src/gmcerts.c index da918279..3e629f8f 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c | |||
@@ -380,7 +380,9 @@ iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *ce | |||
380 | if (isExpired_TlsCertificate(cert)) { | 380 | if (isExpired_TlsCertificate(cert)) { |
381 | return iFalse; | 381 | return iFalse; |
382 | } | 382 | } |
383 | if (!verifyDomain_TlsCertificate(cert, domain)) { | 383 | /* We trust CA verification implicitly. */ |
384 | const iBool isAuth = verify_TlsCertificate(cert) == authority_TlsCertificateVerifyStatus; | ||
385 | if (!isAuth && !verifyDomain_TlsCertificate(cert, domain)) { | ||
384 | return iFalse; | 386 | return iFalse; |
385 | } | 387 | } |
386 | /* TODO: Could call setTrusted_GmCerts() instead of duplicating the trust-setting. */ | 388 | /* TODO: Could call setTrusted_GmCerts() instead of duplicating the trust-setting. */ |
@@ -394,9 +396,7 @@ iBool checkTrust_GmCerts(iGmCerts *d, iRangecc domain, const iTlsCertificate *ce | |||
394 | if (trust) { | 396 | if (trust) { |
395 | /* We already have it, check if it matches the one we trust for this domain (if it's | 397 | /* We already have it, check if it matches the one we trust for this domain (if it's |
396 | still valid. */ | 398 | still valid. */ |
397 | iTime now; | 399 | if (!isAuth && elapsedSeconds_Time(&trust->validUntil) > 0) { |
398 | initCurrent_Time(&now); | ||
399 | if (secondsSince_Time(&trust->validUntil, &now) > 0) { | ||
400 | /* Trusted cert is still valid. */ | 400 | /* Trusted cert is still valid. */ |
401 | const iBool isTrusted = cmp_Block(fingerprint, &trust->fingerprint) == 0; | 401 | const iBool isTrusted = cmp_Block(fingerprint, &trust->fingerprint) == 0; |
402 | unlock_Mutex(d->mtx); | 402 | unlock_Mutex(d->mtx); |
diff --git a/src/gmrequest.c b/src/gmrequest.c index 0208dc94..ea0a2d80 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c | |||
@@ -163,6 +163,9 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) { | |||
163 | if (checkTrust_GmCerts(d->certs, domain, cert)) { | 163 | if (checkTrust_GmCerts(d->certs, domain, cert)) { |
164 | resp->certFlags |= trusted_GmCertFlag; | 164 | resp->certFlags |= trusted_GmCertFlag; |
165 | } | 165 | } |
166 | if (verify_TlsCertificate(cert) == authority_TlsCertificateVerifyStatus) { | ||
167 | resp->certFlags |= authorityVerified_GmCertFlag; | ||
168 | } | ||
166 | validUntil_TlsCertificate(cert, &resp->certValidUntil); | 169 | validUntil_TlsCertificate(cert, &resp->certValidUntil); |
167 | set_String(&resp->certSubject, collect_String(subject_TlsCertificate(cert))); | 170 | set_String(&resp->certSubject, collect_String(subject_TlsCertificate(cert))); |
168 | } | 171 | } |
diff --git a/src/gmrequest.h b/src/gmrequest.h index 6d4eb2f8..9f20e0eb 100644 --- a/src/gmrequest.h +++ b/src/gmrequest.h | |||
@@ -36,6 +36,7 @@ enum iGmCertFlags { | |||
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 | haveFingerprint_GmCertFlag = iBit(5), |
39 | authorityVerified_GmCertFlag = iBit(6), | ||
39 | }; | 40 | }; |
40 | 41 | ||
41 | struct Impl_GmResponse { | 42 | struct Impl_GmResponse { |
diff --git a/src/prefs.c b/src/prefs.c index 97ad7f48..e3b5d603 100644 --- a/src/prefs.c +++ b/src/prefs.c | |||
@@ -22,6 +22,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "prefs.h" | 23 | #include "prefs.h" |
24 | 24 | ||
25 | #include <the_Foundation/fileinfo.h> | ||
26 | |||
25 | void init_Prefs(iPrefs *d) { | 27 | void init_Prefs(iPrefs *d) { |
26 | d->dialogTab = 0; | 28 | d->dialogTab = 0; |
27 | d->useSystemTheme = iTrue; | 29 | d->useSystemTheme = iTrue; |
@@ -48,6 +50,8 @@ void init_Prefs(iPrefs *d) { | |||
48 | d->docThemeDark = colorfulDark_GmDocumentTheme; | 50 | d->docThemeDark = colorfulDark_GmDocumentTheme; |
49 | d->docThemeLight = white_GmDocumentTheme; | 51 | d->docThemeLight = white_GmDocumentTheme; |
50 | d->saturation = 1.0f; | 52 | d->saturation = 1.0f; |
53 | init_String(&d->caFile); | ||
54 | init_String(&d->caPath); | ||
51 | init_String(&d->geminiProxy); | 55 | init_String(&d->geminiProxy); |
52 | init_String(&d->gopherProxy); | 56 | init_String(&d->gopherProxy); |
53 | init_String(&d->httpProxy); | 57 | init_String(&d->httpProxy); |
@@ -56,6 +60,10 @@ void init_Prefs(iPrefs *d) { | |||
56 | #if defined (iPlatformAppleMobile) | 60 | #if defined (iPlatformAppleMobile) |
57 | d->hoverLink = iFalse; | 61 | d->hoverLink = iFalse; |
58 | #endif | 62 | #endif |
63 | /* TODO: Add some platform-specific common locations? */ | ||
64 | if (fileExistsCStr_FileInfo("/etc/ssl/certs")) { | ||
65 | setCStr_String(&d->caPath, "/etc/ssl/certs"); | ||
66 | } | ||
59 | } | 67 | } |
60 | 68 | ||
61 | void deinit_Prefs(iPrefs *d) { | 69 | void deinit_Prefs(iPrefs *d) { |
@@ -64,4 +72,6 @@ void deinit_Prefs(iPrefs *d) { | |||
64 | deinit_String(&d->gopherProxy); | 72 | deinit_String(&d->gopherProxy); |
65 | deinit_String(&d->httpProxy); | 73 | deinit_String(&d->httpProxy); |
66 | deinit_String(&d->downloadDir); | 74 | deinit_String(&d->downloadDir); |
75 | deinit_String(&d->caPath); | ||
76 | deinit_String(&d->caFile); | ||
67 | } | 77 | } |
diff --git a/src/prefs.h b/src/prefs.h index 4bbe3ad5..56cef52c 100644 --- a/src/prefs.h +++ b/src/prefs.h | |||
@@ -51,6 +51,8 @@ struct Impl_Prefs { | |||
51 | iBool loadImageInsteadOfScrolling; | 51 | iBool loadImageInsteadOfScrolling; |
52 | iString searchUrl; | 52 | iString searchUrl; |
53 | /* Network */ | 53 | /* Network */ |
54 | iString caFile; | ||
55 | iString caPath; | ||
54 | iBool decodeUserVisibleURLs; | 56 | iBool decodeUserVisibleURLs; |
55 | int maxCacheSize; /* MB */ | 57 | int maxCacheSize; /* MB */ |
56 | iString geminiProxy; | 58 | iString geminiProxy; |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 947d9d5f..0af022c5 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1585,10 +1585,16 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1585 | } | 1585 | } |
1586 | } | 1586 | } |
1587 | appendFormat_String(msg, | 1587 | appendFormat_String(msg, |
1588 | "\n%sCertificate Status:\n%s%s Domain name %s%s\n" | 1588 | "\n%sCertificate Status:\n" |
1589 | "%s%s %s by CA\n" | ||
1590 | "%s%s Domain name %s%s\n" | ||
1589 | "%s%s %s (%04d-%02d-%02d %02d:%02d:%02d)\n" | 1591 | "%s%s %s (%04d-%02d-%02d %02d:%02d:%02d)\n" |
1590 | "%s%s %s", | 1592 | "%s%s %s", |
1591 | uiHeading_ColorEscape, | 1593 | uiHeading_ColorEscape, |
1594 | d->certFlags & authorityVerified_GmCertFlag ? | ||
1595 | checked : uiTextAction_ColorEscape "\u2610", | ||
1596 | uiText_ColorEscape, | ||
1597 | d->certFlags & authorityVerified_GmCertFlag ? "Verified" : "Not verified", | ||
1592 | d->certFlags & domainVerified_GmCertFlag ? checked : unchecked, | 1598 | d->certFlags & domainVerified_GmCertFlag ? checked : unchecked, |
1593 | uiText_ColorEscape, | 1599 | uiText_ColorEscape, |
1594 | d->certFlags & domainVerified_GmCertFlag ? "matches" : "mismatch", | 1600 | d->certFlags & domainVerified_GmCertFlag ? "matches" : "mismatch", |
diff --git a/src/ui/util.c b/src/ui/util.c index 603a65cc..5fb3e9f3 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -1177,6 +1177,8 @@ iWidget *makePreferences_Widget(void) { | |||
1177 | addChild_Widget(headings, iClob(makeHeading_Widget("Downloads folder:"))); | 1177 | addChild_Widget(headings, iClob(makeHeading_Widget("Downloads folder:"))); |
1178 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads"); | 1178 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads"); |
1179 | #endif | 1179 | #endif |
1180 | addChild_Widget(headings, iClob(makeHeading_Widget("Search URL:"))); | ||
1181 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.searchurl"); | ||
1180 | addChild_Widget(headings, iClob(makeHeading_Widget("Show URL on hover:"))); | 1182 | addChild_Widget(headings, iClob(makeHeading_Widget("Show URL on hover:"))); |
1181 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink"))); | 1183 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink"))); |
1182 | addChild_Widget(headings, iClob(makeHeading_Widget("Vertical centering:"))); | 1184 | addChild_Widget(headings, iClob(makeHeading_Widget("Vertical centering:"))); |
@@ -1300,8 +1302,6 @@ iWidget *makePreferences_Widget(void) { | |||
1300 | } | 1302 | } |
1301 | /* Network. */ { | 1303 | /* Network. */ { |
1302 | appendTwoColumnPage_(tabs, "Network", '5', &headings, &values); | 1304 | appendTwoColumnPage_(tabs, "Network", '5', &headings, &values); |
1303 | addChild_Widget(headings, iClob(makeHeading_Widget("Search URL:"))); | ||
1304 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.searchurl"); | ||
1305 | addChild_Widget(headings, iClob(makeHeading_Widget("Decode URLs:"))); | 1305 | addChild_Widget(headings, iClob(makeHeading_Widget("Decode URLs:"))); |
1306 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); | 1306 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); |
1307 | addChild_Widget(headings, iClob(makeHeading_Widget("Cache size:"))); | 1307 | addChild_Widget(headings, iClob(makeHeading_Widget("Cache size:"))); |
@@ -1312,6 +1312,11 @@ iWidget *makePreferences_Widget(void) { | |||
1312 | addChildFlags_Widget(cacheGroup, iClob(new_LabelWidget("MB", NULL)), frameless_WidgetFlag); | 1312 | addChildFlags_Widget(cacheGroup, iClob(new_LabelWidget("MB", NULL)), frameless_WidgetFlag); |
1313 | } | 1313 | } |
1314 | addChildFlags_Widget(values, iClob(cacheGroup), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | 1314 | addChildFlags_Widget(values, iClob(cacheGroup), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); |
1315 | makeTwoColumnHeading_("CERTIFICATES", headings, values); | ||
1316 | addChild_Widget(headings, iClob(makeHeading_Widget("CA file:"))); | ||
1317 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.ca.file"); | ||
1318 | addChild_Widget(headings, iClob(makeHeading_Widget("CA path:"))); | ||
1319 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.ca.path"); | ||
1315 | makeTwoColumnHeading_("PROXIES", headings, values); | 1320 | makeTwoColumnHeading_("PROXIES", headings, values); |
1316 | addChild_Widget(headings, iClob(makeHeading_Widget("Gemini proxy:"))); | 1321 | addChild_Widget(headings, iClob(makeHeading_Widget("Gemini proxy:"))); |
1317 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.gemini"); | 1322 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.gemini"); |
@@ -1331,6 +1336,8 @@ iWidget *makePreferences_Widget(void) { | |||
1331 | /* Set input field sizes. */ { | 1336 | /* Set input field sizes. */ { |
1332 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.searchurl")); | 1337 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.searchurl")); |
1333 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.downloads")); | 1338 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.downloads")); |
1339 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.ca.file")); | ||
1340 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.ca.path")); | ||
1334 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gemini")); | 1341 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gemini")); |
1335 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gopher")); | 1342 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gopher")); |
1336 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.http")); | 1343 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.http")); |