[geary/wip/20-cert-pinning: 36/36] Implement loading and saving certs in CertificateManager



commit 8e15263660ad78cc8d517725486d6a0f10f0e400
Author: Michael Gratton <mike vee net>
Date:   Thu Jan 10 13:58:56 2019 +1100

    Implement loading and saving certs in CertificateManager

 .../application-certficate-manager.vala            | 88 +++++++++++++++++-----
 src/client/application/geary-controller.vala       |  4 +-
 2 files changed, 74 insertions(+), 18 deletions(-)
---
diff --git a/src/client/application/application-certficate-manager.vala 
b/src/client/application/application-certficate-manager.vala
index cb14b137..1fb62eb8 100644
--- a/src/client/application/application-certficate-manager.vala
+++ b/src/client/application/application-certficate-manager.vala
@@ -36,9 +36,10 @@ public class Application.CertificateManager : GLib.Object {
     /**
      * Constructs a new instance, globally installing the pinning database.
      */
-    public CertificateManager() {
+    public CertificateManager(GLib.File store_dir) {
         this.pinning_database = new TlsDatabase(
-            GLib.TlsBackend.get_default().get_default_database()
+            GLib.TlsBackend.get_default().get_default_database(),
+            store_dir
         );
         Geary.Endpoint.default_tls_database = this.pinning_database;
     }
@@ -83,7 +84,7 @@ public class Application.CertificateManager : GLib.Object {
 
         debug("Pinning certificate for %s...", endpoint.remote.to_string());
         try {
-            yield add_pinned(
+            yield this.pinning_database.pin_certificate(
                 endpoint.untrusted_certificate,
                 endpoint.remote,
                 save,
@@ -94,17 +95,6 @@ public class Application.CertificateManager : GLib.Object {
         }
     }
 
-    private async void add_pinned(GLib.TlsCertificate cert,
-                                  GLib.SocketConnectable? identity,
-                                  bool save,
-                                  GLib.Cancellable? cancellable)
-        throws GLib.Error {
-        this.pinning_database.pin_certificate(cert, identity);
-        if (save) {
-            // XXX
-        }
-    }
-
 }
 
 
@@ -134,6 +124,52 @@ private class Application.TlsDatabase : GLib.TlsDatabase {
             this.certificate = certificate;
         }
 
+        public TrustContext.lookup(GLib.File dir,
+                                   string identity,
+                                   GLib.Cancellable? cancellable)
+            throws GLib.Error {
+            // This isn't async so that we can support both
+            // verify_chain and verify_chain_async with the same call
+            GLib.File storage = dir.get_child(FILENAME_FORMAT.printf(identity));
+            GLib.FileInputStream f_in = storage.read(cancellable);
+            GLib.BufferedInputStream buf = new GLib.BufferedInputStream(f_in);
+            GLib.ByteArray cert_pem = new GLib.ByteArray.sized(buf.buffer_size);
+            bool eof = false;
+            while (!eof) {
+                size_t filled = buf.fill(-1, cancellable);
+                if (filled > 0) {
+                    cert_pem.append(buf.peek_buffer());
+                    buf.skip(filled, cancellable);
+                } else {
+                    eof = true;
+                }
+            }
+            buf.close(cancellable);
+
+            this(new GLib.TlsCertificate.from_pem((string) cert_pem.data, -1));
+        }
+
+        public async void save(GLib.File dir,
+                               string identity,
+                               GLib.Cancellable? cancellable)
+            throws GLib.Error {
+            yield Geary.Files.make_directory_with_parents(dir, cancellable);
+            GLib.File storage = dir.get_child(FILENAME_FORMAT.printf(identity));
+            GLib.FileOutputStream f_out = yield storage.replace_async(
+                null, false, GLib.FileCreateFlags.NONE, IO_PRIO, cancellable
+            );
+            GLib.BufferedOutputStream buf = new GLib.BufferedOutputStream(f_out);
+
+            size_t written = 0;
+            yield buf.write_all_async(
+                this.certificate.certificate_pem.data,
+                IO_PRIO,
+                cancellable,
+                out written
+            );
+            yield buf.close_async(IO_PRIO, cancellable);
+        }
+
     }
 
 
@@ -169,15 +205,19 @@ private class Application.TlsDatabase : GLib.TlsDatabase {
         this.store_dir = store_dir;
     }
 
-    public void pin_certificate(GLib.TlsCertificate certificate,
-                                GLib.SocketConnectable identity,
-                                GLib.Cancellable? cancellable = null)
+    public async void pin_certificate(GLib.TlsCertificate certificate,
+                                      GLib.SocketConnectable identity,
+                                      bool save,
+                                      GLib.Cancellable? cancellable = null)
         throws GLib.Error {
         string id = to_name(identity);
         TrustContext context = new TrustContext(certificate);
         lock (this.pinned_certs) {
             this.pinned_certs.set(id, context);
         }
+        if (save) {
+            yield context.save(this.store_dir, to_name(identity), cancellable);
+        }
     }
 
     public override string?
@@ -318,6 +358,20 @@ private class Application.TlsDatabase : GLib.TlsDatabase {
         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
+                    );
+                    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());
+                }
+            }
         }
         return (context != null);
     }
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 30d1ef61..1fc61df7 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -327,7 +327,9 @@ public class GearyController : Geary.BaseObject {
 
         // Hook up cert, accounts and credentials machinery
 
-        this.certificate_manager = new Application.CertificateManager();
+        this.certificate_manager = new Application.CertificateManager(
+            this.application.get_user_data_directory().get_child("pinned-certs")
+        );
 
         SecretMediator? libsecret = null;
         try {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]