[geary/wip/20-cert-pinning: 2/5] Update Geary.Endpoint API and source



commit 5358f845b4b19cbe86895c441d830364169c117c
Author: Michael Gratton <mike vee net>
Date:   Thu Dec 27 16:27:26 2018 +1100

    Update Geary.Endpoint API and source
    
    Use a generic GSocketConnectable to specify the remote endpoint,
    simplify TLS cert management callbacks.

 src/client/application/geary-controller.vala       |  18 +-
 src/client/dialogs/certificate-warning-dialog.vala |   5 +-
 src/console/main.vala                              |   5 +-
 src/engine/api/geary-endpoint.vala                 | 221 +++++++++++----------
 src/engine/api/geary-engine.vala                   |  11 +-
 .../imap-engine/imap-engine-generic-account.vala   |  12 +-
 src/mailer/main.vala                               |   7 +-
 test/engine/api/geary-account-mock.vala            |  14 +-
 8 files changed, 158 insertions(+), 135 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index dda59ec8..926ea956 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -69,10 +69,14 @@ public class GearyController : Geary.BaseObject {
         GearyController.untitled_file_name = _("Untitled");
     }
 
-    private static void get_gcr_params(Geary.Endpoint endpoint, out Gcr.Certificate cert,
-        out string peer) {
-        cert = new Gcr.SimpleCertificate(endpoint.untrusted_certificate.certificate.data);
-        peer = "%s:%u".printf(endpoint.remote_address.hostname, endpoint.remote_address.port);
+    private static void get_gcr_params(Geary.ServiceInformation service,
+                                       Geary.Endpoint endpoint,
+                                       out Gcr.Certificate cert,
+                                       out string peer) {
+        cert = new Gcr.SimpleCertificate(
+            endpoint.untrusted_certificate.certificate.data
+        );
+        peer = service.host;
     }
 
 
@@ -681,7 +685,7 @@ public class GearyController : Geary.BaseObject {
         // get GCR parameters
         Gcr.Certificate cert;
         string peer;
-        get_gcr_params(endpoint, out cert, out peer);
+        get_gcr_params(service, endpoint, out cert, out peer);
 
         // Geary allows for user to auto-revoke all questionable server certificates without
         // digging around in a keyring/pk manager
@@ -733,8 +737,8 @@ public class GearyController : Geary.BaseObject {
                 // get GCR parameters for pinning
                 Gcr.Certificate cert;
                 string peer;
-                get_gcr_params(endpoint, out cert, out peer);
-                
+                get_gcr_params(service, endpoint, out cert, out peer);
+
                 // pinning the certificate creates an exception for the next time a connection
                 // is attempted
                 debug("Pinning certificate for %s...", peer);
diff --git a/src/client/dialogs/certificate-warning-dialog.vala 
b/src/client/dialogs/certificate-warning-dialog.vala
index f8e49f31..e29ec9a8 100644
--- a/src/client/dialogs/certificate-warning-dialog.vala
+++ b/src/client/dialogs/certificate-warning-dialog.vala
@@ -36,7 +36,7 @@ public class CertificateWarningDialog {
         title_label.label = _("Untrusted Connection: %s").printf(account.display_name);
 
         top_label.label = _("The identity of the %s mail server at %s:%u could not be verified.").printf(
-            service.protocol.to_value(), endpoint.remote_address.hostname, endpoint.remote_address.port);
+            service.protocol.to_value(), service.host, service.port);
 
         warnings_label.label = generate_warning_list(
             endpoint.tls_validation_warnings
@@ -61,8 +61,7 @@ public class CertificateWarningDialog {
             dont_trust_label.label =
                 "<b>"
                 + _("Selecting “Don’t Trust This Server” will cause Geary to stop accessing this account.")
-                + "</b> "
-                + _("Geary will exit if you have no other open email accounts.");
+                + "</b> ";
         }
         dont_trust_label.use_markup = true;
         
diff --git a/src/console/main.vala b/src/console/main.vala
index 6a2bbd61..8a95acbc 100644
--- a/src/console/main.vala
+++ b/src/console/main.vala
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Software Freedom Conservancy Inc.
+n * Copyright 2016 Software Freedom Conservancy Inc.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -311,8 +311,7 @@ class ImapConsole : Gtk.Window {
 
         cx = new Geary.Imap.ClientConnection(
             new Geary.Endpoint(
-                args[0],
-                Geary.Imap.IMAP_TLS_PORT,
+                new GLib.NetworkAddress(args[0], Geary.Imap.IMAP_TLS_PORT),
                 method,
                 IMAP_TIMEOUT_SEC
             )
diff --git a/src/engine/api/geary-endpoint.vala b/src/engine/api/geary-endpoint.vala
index fe23146d..b1174c6c 100644
--- a/src/engine/api/geary-endpoint.vala
+++ b/src/engine/api/geary-endpoint.vala
@@ -1,23 +1,78 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
 /**
- * An Endpoint represents the location of an Internet TCP connection as represented by a host,
- * a port, and flags and other parameters that specify the nature of the connection itself.
+ * Encapsulates network configuration and state for remote service.
  */
-
 public class Geary.Endpoint : BaseObject {
 
+
     public const string PROP_TRUST_UNTRUSTED_HOST = "trust-untrusted-host";
 
-    public NetworkAddress remote_address { get; private set; }
+
+    /**
+     * The default TLS certificate database to use when connecting.
+     *
+     * If not null, this will be set as the database on new TLS
+     * connections.
+     */
+    public static GLib.TlsDatabase? default_tls_database = null;
+
+
+    /** Returns {@link GLib.TlsCertificateFlags} as a string. */
+    public static string tls_flag_to_string(GLib.TlsCertificateFlags flag) {
+        // Vala to_string() for Flags enums currently doesn't work --
+        // bummer...  Should only be called when a single flag is set,
+        // otherwise returns a string indicating an unknown value
+        switch (flag) {
+            case TlsCertificateFlags.BAD_IDENTITY:
+                return "BAD_IDENTITY";
+
+            case TlsCertificateFlags.EXPIRED:
+                return "EXPIRED";
+
+            case TlsCertificateFlags.GENERIC_ERROR:
+                return "GENERIC_ERROR";
+
+            case TlsCertificateFlags.INSECURE:
+                return "INSECURE";
+
+            case TlsCertificateFlags.NOT_ACTIVATED:
+                return "NOT_ACTIVATED";
+
+            case TlsCertificateFlags.REVOKED:
+                return "REVOKED";
+
+            case TlsCertificateFlags.UNKNOWN_CA:
+                return "UNKNOWN_CA";
+
+            default:
+                return "(unknown=%Xh)".printf(flag);
+        }
+    }
+
+
+    /** Specifies how to connect to the remote endpoint. */
+    public GLib.SocketConnectable remote { get; private set; }
+
+    /** A connectivity manager for this endpoint. */
     public ConnectivityManager connectivity { get; private set; }
+
+    /** Timeout for connection attempts, in seconds. */
     public uint timeout_sec { get; private set; }
+
+    /** Transport security method to use when connecting. */
     public TlsNegotiationMethod tls_method { get; private set; }
-    public TlsCertificateFlags tls_validation_flags { get; set; default = TlsCertificateFlags.VALIDATE_ALL; }
+
+    /** Transport security certificate validation requirements. */
+    public TlsCertificateFlags tls_validation_flags {
+        get; set; default = TlsCertificateFlags.VALIDATE_ALL;
+    }
 
     /**
      * The maximum number of commands that will be pipelined at once.
@@ -34,12 +89,12 @@ public class Geary.Endpoint : BaseObject {
      * @see untrusted_host
      */
     public TlsCertificateFlags tls_validation_warnings { get; private set; default = 0; }
-    
+
     /**
      * The TLS certificate for an invalid or untrusted connection.
      */
     public TlsCertificate? untrusted_certificate { get; private set; default = null; }
-    
+
     /**
      * When set, indicates the user has acceded to trusting the host even though TLS has reported
      * certificate issues.
@@ -51,7 +106,7 @@ public class Geary.Endpoint : BaseObject {
      * @see tls_validation_warnings
      */
     public Trillian trust_untrusted_host { get; set; default = Trillian.UNKNOWN; }
-    
+
     /**
      * Returns true if (a) no TLS warnings have been detected or (b) user has explicitly acceded
      * to ignoring them and continuing the connection.
@@ -72,6 +127,7 @@ public class Geary.Endpoint : BaseObject {
                 : trust_untrusted_host.is_possible();
         }
     }
+
     private SocketClient? socket_client = null;
 
     /**
@@ -88,16 +144,35 @@ public class Geary.Endpoint : BaseObject {
                                       GLib.TlsConnection cx);
 
 
-    public Endpoint(string host_specifier,
-                    uint16 port,
+    public Endpoint(GLib.SocketConnectable remote,
                     TlsNegotiationMethod method,
                     uint timeout_sec) {
-        this.remote_address = new NetworkAddress(host_specifier, port);
-        this.connectivity = new ConnectivityManager(this.remote_address);
+        this.remote = remote;
+        this.connectivity = new ConnectivityManager((NetworkAddress) this.remote);
         this.timeout_sec = timeout_sec;
         this.tls_method = method;
     }
 
+    public async SocketConnection connect_async(Cancellable? cancellable = null) throws Error {
+        return yield get_socket_client().connect_async(this.remote, cancellable);
+    }
+
+    public async TlsClientConnection starttls_handshake_async(IOStream base_stream,
+        Cancellable? cancellable = null) throws Error {
+        TlsClientConnection tls_cx = TlsClientConnection.new(
+            base_stream, this.remote
+        );
+        prepare_tls_cx(tls_cx);
+
+        yield tls_cx.handshake_async(Priority.DEFAULT, cancellable);
+
+        return tls_cx;
+    }
+
+    public string to_string() {
+        return this.remote.to_string();
+    }
+
     private SocketClient get_socket_client() {
         if (socket_client != null)
             return socket_client;
@@ -111,72 +186,34 @@ public class Geary.Endpoint : BaseObject {
         }
 
         socket_client.set_timeout(timeout_sec);
-        
-        return socket_client;
-    }
 
-    public async SocketConnection connect_async(Cancellable? cancellable = null) throws Error {
-        return yield get_socket_client().connect_async(remote_address, cancellable);
-    }
-    
-    public async TlsClientConnection starttls_handshake_async(IOStream base_stream,
-        Cancellable? cancellable = null) throws Error {
-        TlsClientConnection tls_cx = TlsClientConnection.new(base_stream, remote_address);
-        prepare_tls_cx(tls_cx, true);
-        
-        yield tls_cx.handshake_async(Priority.DEFAULT, cancellable);
-        
-        return tls_cx;
-    }
-    
-    private void on_socket_client_event(SocketClientEvent event, SocketConnectable? connectable,
-        IOStream? ios) {
-        // get TlsClientConnection to bind signals and set flags prior to handshake
-        if (event == SocketClientEvent.TLS_HANDSHAKING)
-            prepare_tls_cx((TlsClientConnection) ios, false);
-    }
-    
-    private void prepare_tls_cx(TlsClientConnection tls_cx, bool starttls) {
-        tls_cx.set_validation_flags(tls_validation_flags);
-        
-        // Vala doesn't do delegates in a ternary operator very well
-        if (starttls)
-            tls_cx.accept_certificate.connect(on_accept_starttls_certificate);
-        else
-            tls_cx.accept_certificate.connect(on_accept_ssl_certificate);
+        return socket_client;
     }
 
-    private bool on_accept_starttls_certificate(GLib.TlsConnection cx,
-                                                GLib.TlsCertificate cert,
-                                                GLib.TlsCertificateFlags flags) {
-        return report_tls_warnings(
-            TlsNegotiationMethod.START_TLS, cx, cert, flags
-        );
-    }
+    private void prepare_tls_cx(GLib.TlsClientConnection tls_cx) {
+        tls_cx.server_identity = this.remote;
+        tls_cx.validation_flags = this.tls_validation_flags;
+        if (Endpoint.default_tls_database != null) {
+            tls_cx.set_database(Endpoint.default_tls_database);
+        }
 
-    private bool on_accept_ssl_certificate(GLib.TlsConnection cx,
-                                           GLib.TlsCertificate cert,
-                                           GLib.TlsCertificateFlags flags) {
-        return report_tls_warnings(
-            TlsNegotiationMethod.TRANSPORT, cx, cert, flags
-        );
+        tls_cx.accept_certificate.connect(on_accept_certificate);
     }
 
-    private bool report_tls_warnings(TlsNegotiationMethod method,
-                                     GLib.TlsConnection cx,
+    private bool report_tls_warnings(GLib.TlsConnection cx,
                                      GLib.TlsCertificate cert,
                                      GLib.TlsCertificateFlags warnings) {
         // TODO: Report or verify flags with user, but for now merely
         // log for informational/debugging reasons and accede
         message(
             "%s TLS warnings connecting to %s: %Xh (%s)",
-            method.to_string(), to_string(), warnings,
+            this.tls_method.to_string(), to_string(), warnings,
             tls_flags_to_string(warnings)
         );
 
         tls_validation_warnings = warnings;
         untrusted_certificate = cert;
-        
+
         // if user has marked this untrusted host as trusted already, accept warnings and move on
         if (trust_untrusted_host == Trillian.TRUE)
             return true;
@@ -186,7 +223,7 @@ public class Geary.Endpoint : BaseObject {
 
         return false;
     }
-    
+
     private string tls_flags_to_string(TlsCertificateFlags flags) {
         StringBuilder builder = new StringBuilder();
         for (int pos = 0; pos < sizeof (TlsCertificateFlags) * 8; pos++) {
@@ -194,58 +231,28 @@ public class Geary.Endpoint : BaseObject {
             if (flag != 0) {
                 if (!String.is_empty(builder.str))
                     builder.append(" | ");
-                
+
                 builder.append(tls_flag_to_string(flag));
             }
         }
-        
+
         return !String.is_empty(builder.str) ? builder.str : "(none)";
     }
-    
-    // Vala to_string() for Flags enums currently doesn't work -- bummer...
-    // Should only be called when a single flag is set, otherwise returns a string indicating an
-    // unknown value
-    public string tls_flag_to_string(TlsCertificateFlags flag) {
-        switch (flag) {
-            case TlsCertificateFlags.BAD_IDENTITY:
-                return "BAD_IDENTITY";
-            
-            case TlsCertificateFlags.EXPIRED:
-                return "EXPIRED";
-            
-            case TlsCertificateFlags.GENERIC_ERROR:
-                return "GENERIC_ERROR";
-            
-            case TlsCertificateFlags.INSECURE:
-                return "INSECURE";
-            
-            case TlsCertificateFlags.NOT_ACTIVATED:
-                return "NOT_ACTIVATED";
-            
-            case TlsCertificateFlags.REVOKED:
-                return "REVOKED";
-            
-            case TlsCertificateFlags.UNKNOWN_CA:
-                return "UNKNOWN_CA";
-            
-            default:
-                return "(unknown=%Xh)".printf(flag);
-        }
-    }
 
-    public bool equal_to(Geary.Endpoint other) {
-        if (this == other) {
-            return true;
+    private void on_socket_client_event(GLib.SocketClientEvent event,
+                                        GLib.SocketConnectable? connectable,
+                                        GLib.IOStream? ios) {
+        // get TlsClientConnection to bind signals and set flags prior
+        // to handshake
+        if (event == SocketClientEvent.TLS_HANDSHAKING) {
+            prepare_tls_cx((TlsClientConnection) ios);
         }
-
-        return (
-            this.remote_address.hostname == other.remote_address.hostname &&
-            this.remote_address.port == other.remote_address.port
-        );
     }
 
-    public string to_string() {
-        return "%s/default:%u".printf(remote_address.hostname, remote_address.port);
+    private bool on_accept_certificate(GLib.TlsConnection cx,
+                                       GLib.TlsCertificate cert,
+                                       GLib.TlsCertificateFlags flags) {
+        return report_tls_warnings(cx, cert, flags);
     }
 
 }
diff --git a/src/engine/api/geary-engine.vala b/src/engine/api/geary-engine.vala
index 66bedf3a..b6e4d4bc 100644
--- a/src/engine/api/geary-engine.vala
+++ b/src/engine/api/geary-engine.vala
@@ -480,10 +480,12 @@ public class Geary.Engine : BaseObject {
 
     private Geary.Endpoint get_shared_endpoint(ServiceProvider provider,
                                                ServiceInformation service) {
-        string key = "%s/%s:%u".printf(
-            service.protocol.to_value(),
+        // Key includes TLS method since endpoints encapsulate
+        // TLS-specific state
+        string key = "%s:%u/%s".printf(
             service.host,
-            service.port
+            service.port,
+            service.transport_security.to_value()
         );
 
         Endpoint? shared = null;
@@ -497,8 +499,7 @@ public class Geary.Engine : BaseObject {
                 : Smtp.ClientConnection.DEFAULT_TIMEOUT_SEC;
 
             shared = new Endpoint(
-                service.host,
-                service.port,
+                new NetworkAddress(service.host, service.port),
                 service.transport_security,
                 timeout
             );
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 2bde62d3..5402e9af 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -492,12 +492,14 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         throws GLib.Error {
         check_open();
 
-        // TODO: we should probably not use someone else's FQDN in something
-        // that's supposed to be globally unique...
+        // XXX work out what our public IP adddress is somehow and use
+        // that in preference to the sender's domain
+        string domain = composed.sender != null
+            ? composed.sender.domain
+            : this.information.primary_mailbox.domain;
         Geary.RFC822.Message rfc822 = new Geary.RFC822.Message.from_composed_email(
-            composed, GMime.utils_generate_message_id(
-                this.smtp.remote.remote_address.hostname
-            ));
+            composed, GMime.utils_generate_message_id(domain)
+        );
 
         yield this.smtp.queue_email(rfc822, cancellable);
     }
diff --git a/src/mailer/main.vala b/src/mailer/main.vala
index 6344a6f9..a3528604 100644
--- a/src/mailer/main.vala
+++ b/src/mailer/main.vala
@@ -140,8 +140,7 @@ int main(string[] args) {
     
     if (arg_gmail) {
         endpoint = new Geary.Endpoint(
-            "smtp.gmail.com",
-            Geary.Smtp.SUBMISSION_PORT,
+            new GLib.NetworkAddress("smtp.gmail.com", Geary.Smtp.SUBMISSION_PORT),
             Geary.TlsNegotiationMethod.START_TLS,
             SMTP_TIMEOUT_SEC
         );
@@ -151,7 +150,9 @@ int main(string[] args) {
             method = Geary.TlsNegotiationMethod.START_TLS;
         }
         endpoint = new Geary.Endpoint(
-            arg_hostname, (uint16) arg_port, method, SMTP_TIMEOUT_SEC
+            new GLib.NetworkAddress(arg_hostname, (uint16) arg_port),
+            method,
+            SMTP_TIMEOUT_SEC
         );
     }
 
diff --git a/test/engine/api/geary-account-mock.vala b/test/engine/api/geary-account-mock.vala
index 8c978f5f..b50fd206 100644
--- a/test/engine/api/geary-account-mock.vala
+++ b/test/engine/api/geary-account-mock.vala
@@ -74,12 +74,22 @@ public class Geary.MockAccount : Account, MockObject {
         this._incoming = new MockClientService(
             config,
             config.incoming,
-            new Endpoint(config.incoming.host, config.incoming.port, 0, 0)
+            new Endpoint(
+                new GLib.NetworkAddress(
+                    config.incoming.host, config.incoming.port
+                ),
+                0, 0
+            )
         );
         this._outgoing = new MockClientService(
             config,
             config.outgoing,
-            new Endpoint(config.outgoing.host, config.outgoing.port, 0, 0)
+            new Endpoint(
+                new GLib.NetworkAddress(
+                    config.outgoing.host, config.outgoing.port
+                ),
+                0, 0
+            )
         );
     }
 


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