[geary/wip/713247-tls] Pin certificates when requested by user



commit 4a3b8c27131b0406ca184c1efefba6cb1ac4d0cd
Author: Jim Nelson <jim yorba org>
Date:   Wed Aug 27 17:44:55 2014 -0700

    Pin certificates when requested by user
    
    Also --revoke-certs will revoke (remove) pinned certificates to
    allow the user to reset w/o using keyring/pkcs manager.

 src/CMakeLists.txt                           |    1 +
 src/client/application/geary-args.vala       |    2 +
 src/client/application/geary-controller.vala |   49 +++++++++++++++++++++++++-
 3 files changed, 51 insertions(+), 1 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cc3a5d9..5a78661 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -561,6 +561,7 @@ set(CFLAGS
     -D_GSETTINGS_DIR=\"${CMAKE_BINARY_DIR}/gsettings\"
     -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"
     -DLANGUAGE_SUPPORT_DIRECTORY=\"${LANGUAGE_SUPPORT_DIRECTORY}\"
+    -DGCR_API_SUBJECT_TO_CHANGE
     -g
 )
 
diff --git a/src/client/application/geary-args.vala b/src/client/application/geary-args.vala
index a26a9c8..70b9159 100644
--- a/src/client/application/geary-args.vala
+++ b/src/client/application/geary-args.vala
@@ -23,6 +23,7 @@ private const OptionEntry[] options = {
     /// "Normalization" can also be called "synchronization"
     { "log-folder-normalization", 0, 0, OptionArg.NONE, ref log_folder_normalization, N_("Log folder 
normalization"), null },
     { "inspector", 'i', 0, OptionArg.NONE, ref inspector, N_("Allow inspection of WebView"), null },
+    { "revoke-certs", 0, 0, OptionArg.NONE, ref revoke_certs, N_("Revoke all server certificates with TLS 
warnings"), null },
     { "version", 'V', 0, OptionArg.NONE, ref version, N_("Display program version"), null },
     { null }
 };
@@ -38,6 +39,7 @@ public bool log_periodic = false;
 public bool log_sql = false;
 public bool log_folder_normalization = false;
 public bool inspector = false;
+public bool revoke_certs = false;
 public bool version = false;
 
 public bool parse(string[] args) {
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 43af7b6..f4d33f1 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -4,6 +4,15 @@
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
+// Required because Gcr's VAPI is behind-the-times
+extern const string GCR_PURPOSE_SERVER_AUTH;
+extern bool gcr_trust_add_pinned_certificate(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;
+extern bool gcr_trust_remove_pinned_certificate(Gcr.Certificate cert, string purpose, string peer,
+    Cancellable? cancellable) throws Error;
+
 // Primary controller object for Geary.
 public class GearyController : Geary.BaseObject {
     // Named actions.
@@ -519,6 +528,33 @@ public class GearyController : Geary.BaseObject {
             if (endpoint.trust_untrusted_host != Geary.Trillian.UNKNOWN)
                 return;
             
+            // Convert into a GCR certificate
+            Gcr.Certificate cert = new Gcr.SimpleCertificate(cx.peer_certificate.certificate.data);
+            string peer = "%s:%u".printf(endpoint.remote_address.hostname, endpoint.remote_address.port);
+            
+            // Geary allows for user to auto-revoke all questionable server certificates without
+            // digging around in a keyring/pk manager
+            if (Args.revoke_certs) {
+                debug("Auto-revoking certificate for %s...", peer);
+                
+                try {
+                    gcr_trust_remove_pinned_certificate(cert, GCR_PURPOSE_SERVER_AUTH, peer, null);
+                } catch (Error err) {
+                    message("Unable to auto-revoke server certificate for %s: %s", peer, err.message);
+                }
+            }
+            
+            // if pinned, the user has already made an exception for this server and its certificate,
+            // so go ahead w/o asking
+            if (gcr_trust_is_certificate_pinned(cert, GCR_PURPOSE_SERVER_AUTH, peer, null)) {
+                debug("Certificate for %s is pinned, accepting connection...", peer);
+                
+                endpoint.trust_untrusted_host = Geary.Trillian.TRUE;
+                
+                return;
+            }
+            
+            // question the user about this certificate
             CertificateWarningDialog dialog = new CertificateWarningDialog(main_window, endpoint,
                 service, warnings);
             switch (dialog.run()) {
@@ -528,12 +564,23 @@ public class GearyController : Geary.BaseObject {
                 
                 case CertificateWarningDialog.Result.ALWAYS_TRUST:
                     endpoint.trust_untrusted_host = Geary.Trillian.TRUE;
-                    // TODO: Pin certificate
+                    
+                    // pinning the certificate creates an exception for the next time a connection
+                    // is attempted
+                    debug("Pinning certificate for %s...", peer);
+                    try {
+                        gcr_trust_add_pinned_certificate(cert, GCR_PURPOSE_SERVER_AUTH, peer, null);
+                    } catch (Error err) {
+                        ErrorDialog error_dialog = new ErrorDialog(main_window,
+                            _("Unable to store server trust exception"), err.message);
+                        error_dialog.run();
+                    }
                 break;
                 
                 default:
                     endpoint.trust_untrusted_host = Geary.Trillian.FALSE;
                     
+                    // close the account; can't go any further w/o offline mode
                     try {
                         if (Geary.Engine.instance.get_accounts().has_key(account_information.email)) {
                             Geary.Account account = 
Geary.Engine.instance.get_account_instance(account_information);


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