[geary/wip/20-cert-pinning] Re-enable GCR support for cert pinning
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/20-cert-pinning] Re-enable GCR support for cert pinning
- Date: Thu, 10 Jan 2019 06:35:48 +0000 (UTC)
commit 6b9ae903fb275c07ad840eb304b4922a1a337f52
Author: Michael Gratton <mike vee net>
Date: Thu Jan 10 17:33:37 2019 +1100
Re-enable GCR support for cert pinning
This re-adds support for using GCR for pinning certs, but only if GCR
is in a known good state. If so, pinned certs will be stored using GCR,
if not, they will be stored in Geary's XDG data dirs as a fallback.
meson.build | 1 +
.../application-certificate-manager.vala | 128 ++++++++++++++++++---
src/client/application/geary-controller.vala | 5 +-
src/client/meson.build | 1 +
src/meson.build | 5 +-
5 files changed, 118 insertions(+), 22 deletions(-)
---
diff --git a/meson.build b/meson.build
index d18d3983..ce612543 100644
--- a/meson.build
+++ b/meson.build
@@ -52,6 +52,7 @@ webkit2gtk = dependency('webkit2gtk-4.0', version: '>=' + target_webkit)
# Secondary deps - keep sorted alphabetically
enchant = dependency('enchant', version: '>= 1.6')
+gck = dependency('gck-1')
gcr = dependency('gcr-3', version: '>= 3.10.1')
gdk = dependency('gdk-3.0', version: '>=' + target_gtk)
gee = dependency('gee-0.8', version: '>= 0.8.5')
diff --git a/src/client/application/application-certificate-manager.vala
b/src/client/application/application-certificate-manager.vala
index 1fb62eb8..4881d73c 100644
--- a/src/client/application/application-certificate-manager.vala
+++ b/src/client/application/application-certificate-manager.vala
@@ -6,6 +6,21 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
+// Required because GCR's VAPI is behind-the-times. See:
+// https://gitlab.gnome.org/GNOME/gcr/merge_requests/7
+extern async bool gcr_trust_add_pinned_certificate_async(
+ Gcr.Certificate cert,
+ string purpose,
+ string peer,
+ Cancellable? cancellable
+) throws Error;
+extern bool gcr_trust_is_certificate_pinned(
+ Gcr.Certificate cert,
+ string purpose,
+ string peer,
+ Cancellable? cancellable
+) throws Error;
+
// All of the below basically exists since cert pinning using GCR
// stopped working (GNOME/gcr#10) after gnome-keyring stopped
@@ -30,16 +45,56 @@ public errordomain Application.CertificateManagerError {
public class Application.CertificateManager : GLib.Object {
+ // PCKS11 flag value lifted from pkcs11.h
+ private const ulong CKF_WRITE_PROTECTED = 1UL << 1;
+
+
+ private static async bool is_gcr_enabled(GLib.Cancellable? cancellable) {
+ // Use GCR if it looks like it should be able to be
+ // used. Specifically, if we can initialise the trust store
+ // must have both lookup and store PKCS11 slot URIs or else it
+ // won't be able to lookup or store pinned certs, secondly,
+ // there must be at least a read-write store slot available.
+ bool init_okay = false;
+ try {
+ init_okay = yield Gcr.pkcs11_initialize_async(cancellable);
+ } catch (GLib.Error err) {
+ warning("Failed to initialise GCR PCKS#11 modules: %s", err.message);
+ }
+
+ bool has_uris = false;
+ if (init_okay) {
+ has_uris = (
+ !Geary.String.is_empty(Gcr.pkcs11_get_trust_store_uri()) &&
+ Gcr.pkcs11_get_trust_lookup_uris().length > 0
+ );
+ debug("GCR slot URIs found: %s", has_uris.to_string());
+ }
+
+ bool has_rw_store = false;
+ if (has_uris) {
+ Gck.Slot? store = Gcr.pkcs11_get_trust_store_slot();
+ has_rw_store = !store.has_flags(CKF_WRITE_PROTECTED);
+ debug("GCR store is R/W: %s", has_rw_store.to_string());
+ }
+
+ return has_rw_store;
+ }
+
+
private TlsDatabase? pinning_database;
/**
* Constructs a new instance, globally installing the pinning database.
*/
- public CertificateManager(GLib.File store_dir) {
+ public async CertificateManager(GLib.File store_dir,
+ GLib.Cancellable? cancellable) {
+ bool use_gcr = yield is_gcr_enabled(cancellable);
this.pinning_database = new TlsDatabase(
GLib.TlsBackend.get_default().get_default_database(),
- store_dir
+ store_dir,
+ use_gcr
);
Geary.Endpoint.default_tls_database = this.pinning_database;
}
@@ -193,16 +248,20 @@ private class Application.TlsDatabase : GLib.TlsDatabase {
}
- public GLib.TlsDatabase parent { get; private set; }
-
+ private GLib.TlsDatabase parent { get; private set; }
private GLib.File store_dir;
+ private bool use_gcr;
+
private Gee.Map<string,TrustContext> pinned_certs =
new Gee.HashMap<string,TrustContext>();
- public TlsDatabase(GLib.TlsDatabase parent, GLib.File store_dir) {
+ public TlsDatabase(GLib.TlsDatabase parent,
+ GLib.File store_dir,
+ bool use_gcr) {
this.parent = parent;
this.store_dir = store_dir;
+ this.use_gcr = use_gcr;
}
public async void pin_certificate(GLib.TlsCertificate certificate,
@@ -216,7 +275,18 @@ private class Application.TlsDatabase : GLib.TlsDatabase {
this.pinned_certs.set(id, context);
}
if (save) {
- yield context.save(this.store_dir, to_name(identity), cancellable);
+ if (this.use_gcr) {
+ yield gcr_trust_add_pinned_certificate_async(
+ new Gcr.SimpleCertificate(certificate.certificate.data),
+ GLib.TlsDatabase.PURPOSE_AUTHENTICATE_SERVER,
+ id,
+ cancellable
+ );
+ } else {
+ yield context.save(
+ this.store_dir, to_name(identity), cancellable
+ );
+ }
}
}
@@ -354,26 +424,48 @@ private class Application.TlsDatabase : GLib.TlsDatabase {
GLib.SocketConnectable identity,
GLib.Cancellable? cancellable)
throws GLib.Error {
+ bool is_verified = false;
string id = to_name(identity);
TrustContext? context = null;
lock (this.pinned_certs) {
context = this.pinned_certs.get(id);
- if (context == null) {
- try {
- context = new TrustContext.lookup(
- this.store_dir, id, cancellable
+ if (context != null) {
+ is_verified = true;
+ } else {
+ // Cert not found in memory, check with GCR if
+ // enabled.
+ if (this.use_gcr) {
+ is_verified = gcr_trust_is_certificate_pinned(
+ new Gcr.SimpleCertificate(chain.certificate.data),
+ GLib.TlsDatabase.PURPOSE_AUTHENTICATE_SERVER,
+ id,
+ cancellable
);
- this.pinned_certs.set(id, context);
- } catch (GLib.IOError.NOT_FOUND err) {
- // Cert was not found saved, so it not pinned
- } catch (GLib.Error err) {
- Geary.ErrorContext err_context = new Geary.ErrorContext(err);
- debug("Error loading pinned certificate: %s",
- err_context.format_full_error());
+ }
+
+ if (!is_verified) {
+ // Cert is not pinned in memory or in GCR, so look
+ // for it on disk. Do this even if GCR support is
+ // enabled, since if the cert was previously saved
+ // to disk, it should still be able to be used
+ try {
+ context = new TrustContext.lookup(
+ this.store_dir, id, cancellable
+ );
+ this.pinned_certs.set(id, context);
+ is_verified = true;
+ } catch (GLib.IOError.NOT_FOUND err) {
+ // Cert was not found saved, so it not pinned
+ } catch (GLib.Error err) {
+ Geary.ErrorContext err_context =
+ new Geary.ErrorContext(err);
+ debug("Error loading pinned certificate: %s",
+ err_context.format_full_error());
+ }
}
}
}
- return (context != null);
+ return is_verified;
}
private async bool verify_async(GLib.TlsCertificate chain,
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 1fc61df7..2a956691 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -327,8 +327,9 @@ public class GearyController : Geary.BaseObject {
// Hook up cert, accounts and credentials machinery
- this.certificate_manager = new Application.CertificateManager(
- this.application.get_user_data_directory().get_child("pinned-certs")
+ this.certificate_manager = yield new Application.CertificateManager(
+ this.application.get_user_data_directory().get_child("pinned-certs"),
+ cancellable
);
SecretMediator? libsecret = null;
diff --git a/src/client/meson.build b/src/client/meson.build
index 8f6fd54d..a037366e 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -109,6 +109,7 @@ geary_client_sources = [
geary_client_dependencies = [
libmath,
enchant,
+ gck,
gcr,
gee,
gio,
diff --git a/src/meson.build b/src/meson.build
index 2eef6927..c569a6a9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -38,8 +38,9 @@ geary_c_options = [
# Select libunwind's optimised, local-only backtrace unwiding. See
# libunwind(3).
'-DUNW_LOCAL_ONLY',
- # Yes yes, GOA's API is unstable. :(
- '-DGOA_API_IS_SUBJECT_TO_CHANGE'
+ # Neither GOA nor GCK want to hang out unless you are cool enough
+ '-DGOA_API_IS_SUBJECT_TO_CHANGE',
+ '-DGCK_API_SUBJECT_TO_CHANGE',
]
subdir('sqlite3-unicodesn')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]