[hotssh] Read and write ~/.ssh/known_hosts



commit 9a3b6fa4233958cba98c9eccb53b3f788980f00b
Author: Colin Walters <walters verbum org>
Date:   Wed Jan 1 15:26:40 2014 -0500

    Read and write ~/.ssh/known_hosts
    
    With this change, the button to approve the hostkey now actually
    writes it to ~/.ssh/known_hosts.  This necessitated modeling keytypes
    and exposing the raw base64 from GSsh.

 libgssh/gssh-connection-private.h |    3 +-
 libgssh/gssh-connection.c         |   39 ++++++--
 libgssh/gssh-connection.h         |    6 +-
 src/hotssh-hostdb.c               |  193 ++++++++++++++++++++++++++++++++-----
 src/hotssh-hostdb.h               |   15 ++-
 src/hotssh-tab.c                  |  167 +++++++++++++++++++++++++++-----
 src/tab.ui                        |   16 +++-
 7 files changed, 372 insertions(+), 67 deletions(-)
---
diff --git a/libgssh/gssh-connection-private.h b/libgssh/gssh-connection-private.h
index 6a0d7d3..c231ba3 100644
--- a/libgssh/gssh-connection-private.h
+++ b/libgssh/gssh-connection-private.h
@@ -47,7 +47,8 @@ struct _GSshConnection
 
   GError *cached_error;
   char *remote_hostkey_type;
-  GBytes *remote_hostkey_sha1;
+  char *remote_hostkey_sha1_text;
+  char *remote_hostkey_base64;
   GMainContext *maincontext;
   GSocketClient *socket_client;
   GSocket *socket;
diff --git a/libgssh/gssh-connection.c b/libgssh/gssh-connection.c
index 8d29761..e50a836 100644
--- a/libgssh/gssh-connection.c
+++ b/libgssh/gssh-connection.c
@@ -88,7 +88,8 @@ reset_state (GSshConnection               *self)
   /* This hash doesn't hold a ref */
   self->channels = g_hash_table_new (NULL, NULL);
 
-  g_clear_pointer (&self->remote_hostkey_sha1, g_bytes_unref);
+  g_clear_pointer (&self->remote_hostkey_sha1_text, g_free);
+  g_clear_pointer (&self->remote_hostkey_base64, g_free);
   g_clear_error (&self->cached_error);
   g_clear_object (&self->socket);
   if (self->socket_source)
@@ -350,6 +351,8 @@ set_hostkey_sha1 (GSshConnection           *self,
   guint8 sha1buf[20];
   gsize sha1len = sizeof (sha1buf);
   GChecksum *csum;
+  GString *buf;
+  guint i;
 
   rc = ssh_get_publickey (self->session, &key);
   if (rc != SSH_OK)
@@ -373,8 +376,18 @@ set_hostkey_sha1 (GSshConnection           *self,
   g_checksum_get_digest (csum, sha1buf, &sha1len);
   g_assert (sha1len == sizeof (sha1buf));
 
-  self->remote_hostkey_sha1 = g_bytes_new (sha1buf, sha1len);
+  buf = g_string_new ("");
+  for (i = 0; i < sha1len; i++)
+    {
+      g_string_append_printf (buf, "%02X", sha1buf[i]);
+      if (i < sha1len - 1)
+       g_string_append_c (buf, ':');
+    }
+
+    self->remote_hostkey_sha1_text = g_string_free (buf, FALSE);
   self->remote_hostkey_type = g_strdup (ssh_key_type_to_char (ssh_key_type (key)));
+  self->remote_hostkey_base64 = g_strdup (key_b64);
+  free (key_b64);  /* Possible free != g_free */
 
   ret = TRUE;
  out:
@@ -759,18 +772,24 @@ gssh_connection_get_state (GSshConnection        *self)
 }
 
 /**
- * gssh_connection_preauth_get_host_key_fingerprint_sha1:
+ * gssh_connection_preauth_get_host_key:
  * @self: Self
  * @out_key_type: (out): String representation of key type
- *
- * Returns: (transfer none): 20 bytes for the remote host's SHA1 fingerprint
+ * @out_key_sha1_text: (out): String representation of key SHA1
+ * @out_key_base64: (out): String representation of base64-encoded key
  */
-GBytes *
-gssh_connection_preauth_get_host_key_fingerprint_sha1 (GSshConnection          *self,
-                                                       char                   **out_key_type)
+void
+gssh_connection_preauth_get_host_key (GSshConnection   *self,
+                                      char            **out_key_type,
+                                      char            **out_key_sha1_text,
+                                      char            **out_key_base64)
 {
-  *out_key_type = g_strdup (self->remote_hostkey_type);
-  return self->remote_hostkey_sha1;
+  if (out_key_type)
+    *out_key_type = g_strdup (self->remote_hostkey_type);
+  if (out_key_sha1_text)
+    *out_key_sha1_text = g_strdup (self->remote_hostkey_sha1_text);
+  if (out_key_base64)
+    *out_key_base64 = g_strdup (self->remote_hostkey_base64);
 }
 
 void
diff --git a/libgssh/gssh-connection.h b/libgssh/gssh-connection.h
index e5fdbda..3e9c43b 100644
--- a/libgssh/gssh-connection.h
+++ b/libgssh/gssh-connection.h
@@ -72,8 +72,10 @@ gboolean                gssh_connection_handshake_finish (GSshConnection    *sel
 void                    gssh_connection_set_interaction (GSshConnection   *self,
                                                          GTlsInteraction  *interaction);
 
-GBytes *                gssh_connection_preauth_get_host_key_fingerprint_sha1 (GSshConnection  *self,
-                                                                               char           
**out_key_type);
+void                    gssh_connection_preauth_get_host_key (GSshConnection   *self,
+                                                              char            **out_keytype,
+                                                              char            **out_key_sha1_text,
+                                                              char            **out_key_base64);
 
 void                    gssh_connection_negotiate_async (GSshConnection      *self,
                                                          GCancellable        *cancellable,
diff --git a/src/hotssh-hostdb.c b/src/hotssh-hostdb.c
index e909513..a1e36b7 100644
--- a/src/hotssh-hostdb.c
+++ b/src/hotssh-hostdb.c
@@ -28,6 +28,8 @@
 
 #include "libgsystem.h"
 
+#define WRITE_IDLE_SECONDS (5)
+
 struct _HotSshHostDB
 {
   GObject parent;
@@ -50,22 +52,31 @@ struct _HotSshHostDBPrivate
   GFile *hotssh_hostdb_path;
   GFileMonitor *knownhosts_monitor;
 
+  GHashTable *add_knownhost_queue;
+
   guint idle_save_hostdb_id;
+  guint idle_save_knownhosts_id;
   char *new_hostdb_contents;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(HotSshHostDB, hotssh_hostdb, G_TYPE_OBJECT)
 
 static char *
-address_to_string (GNetworkAddress    *address)
+hostname_and_port_to_string (const char *hostname,
+                             guint port)
 {
-  const char *hostname = g_network_address_get_hostname (address);
-  guint port = g_network_address_get_port (address);
   if (port == 22)
-    return g_strdup (g_network_address_get_hostname (address));
+    return g_strdup (hostname);
   return g_strdup_printf ("[%s]:%u", hostname, port);
 }
 
+static char *
+address_to_string (GNetworkAddress    *address)
+{
+  return hostname_and_port_to_string (g_network_address_get_hostname (address),
+                                      g_network_address_get_port (address));
+}
+
 static void
 mark_all_entries_unknown (HotSshHostDB    *self)
 {
@@ -89,7 +100,9 @@ static void
 mark_all_entries_known_by_address (HotSshHostDB    *self,
                                    const char      *hostname,
                                    guint            port,
-                                   guint            lineno)
+                                   guint            lineno,
+                                   const char      *keytype,
+                                   const char      *key_base64)
 {
   G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
   GtkTreeIter iter;
@@ -114,6 +127,8 @@ mark_all_entries_known_by_address (HotSshHostDB    *self,
         {
           gtk_list_store_set (priv->model, &iter,
                               HOTSSH_HOSTDB_COLUMN_IS_KNOWN, TRUE,
+                              HOTSSH_HOSTDB_COLUMN_HOST_KEY_TYPE, keytype,
+                              HOTSSH_HOSTDB_COLUMN_HOST_KEY_BASE64, key_base64,
                               HOTSSH_HOSTDB_COLUMN_OPENSSH_KNOWNHOST_LINE, lineno,
                               -1);
         }
@@ -156,6 +171,8 @@ on_knownhosts_changed (GFileMonitor        *monitor,
       GNetworkAddress *address_obj = NULL;
       gs_free char *address_str = NULL;
       char *comma;
+      const char *keytype;
+      const char *key_base64;
 
       eol = strchr (iter, '\n');
       if (eol)
@@ -181,10 +198,15 @@ on_knownhosts_changed (GFileMonitor        *monitor,
         goto next;
       address_str = address_to_string (address_obj);
 
+      keytype = parts[1];
+      key_base64 = parts[2];
+
       mark_all_entries_known_by_address (self,
                                          g_network_address_get_hostname (address_obj),
                                          g_network_address_get_port (address_obj),
-                                         lineno);
+                                         lineno,
+                                         keytype,
+                                         key_base64);
 
       g_hash_table_insert (priv->openssh_knownhosts, address_str, parts);
       address_str = NULL;
@@ -340,7 +362,7 @@ queue_save_hostdb (HotSshHostDB    *self)
   G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
   if (priv->idle_save_hostdb_id > 0)
     return;
-  priv->idle_save_hostdb_id = g_timeout_add_seconds (5, idle_save_hostdb, self);
+  priv->idle_save_hostdb_id = g_timeout_add_seconds (WRITE_IDLE_SECONDS, idle_save_hostdb, self);
 }
 
 static void
@@ -464,33 +486,149 @@ hotssh_hostdb_update_last_used (HotSshHostDB    *self,
   queue_save_hostdb (self);
 }
 
+static void
+on_knownhosts_splice_complete (GObject            *src,
+                               GAsyncResult       *result,
+                               gpointer            user_data)
+{
+  HotSshHostDB *self = user_data;
+  G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
+  gssize bytes_written;
+  GError *local_error = NULL;
+
+  priv->idle_save_knownhosts_id = 0;
+
+  g_debug ("knownhosts splice complete");
+
+  bytes_written = g_output_stream_splice_finish ((GOutputStream*)src, result, &local_error);
+  if (bytes_written == -1)
+    goto out;
+
+ out:
+  if (local_error)
+    {
+      g_warning ("Failed to write '%s': %s",
+                 gs_file_get_path_cached (priv->openssh_knownhosts_path),
+                 local_error->message);
+      g_error_free (local_error);
+    }
+}
+
+static void
+on_knownhosts_opened (GObject            *src,
+                      GAsyncResult       *result,
+                      gpointer            user_data)
+{
+  HotSshHostDB *self = user_data;
+  G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
+  GHashTableIter hashiter;
+  gpointer key, value;
+  GError *local_error = NULL;
+  gs_unref_object GFileOutputStream *fileout = NULL;
+  gs_unref_object GMemoryInputStream *membuf = (GMemoryInputStream*)g_memory_input_stream_new ();
+
+  g_debug ("knownhosts opened for append");
+
+  fileout = g_file_append_to_finish ((GFile*)src, result, &local_error);
+  if (!fileout)
+    goto out;
+
+  g_hash_table_iter_init (&hashiter, priv->add_knownhost_queue);
+  while (g_hash_table_iter_next (&hashiter, &key, &value))
+    {
+      GtkTreeIter iter;
+      const char *id = key;
+      gs_free char *hostname = NULL;
+      guint port;
+      gs_free char *keytype = NULL;
+      gs_free char *key_base64 = NULL;
+      gs_free char *ipaddr = NULL;
+      gs_free char *address = NULL;
+      char *out_line;
+
+      if (!hotssh_hostdb_lookup_by_id (self, id, &iter))
+        continue;
+
+      gtk_tree_model_get ((GtkTreeModel*)priv->model, &iter,
+                          HOTSSH_HOSTDB_COLUMN_HOSTNAME, &hostname,
+                          HOTSSH_HOSTDB_COLUMN_PORT, &port,
+                          HOTSSH_HOSTDB_COLUMN_HOST_KEY_TYPE, &keytype,
+                          HOTSSH_HOSTDB_COLUMN_HOST_KEY_BASE64, &key_base64,
+                          HOTSSH_HOSTDB_COLUMN_HOST_KEY_IP_ADDRESS, &ipaddr,
+                          -1);
+
+      address = hostname_and_port_to_string (hostname, port);
+      if (ipaddr)
+        out_line = g_strdup_printf ("%s,%s %s %s\n", address, ipaddr,
+                                    keytype, key_base64);
+      else
+        out_line = g_strdup_printf ("%s %s %s\n", address,
+                                    keytype, key_base64);
+
+      g_memory_input_stream_add_bytes (membuf, g_bytes_new_take (out_line, strlen (out_line)));
+
+      g_hash_table_iter_remove (&hashiter);
+    }
+
+  g_output_stream_splice_async ((GOutputStream*)fileout, (GInputStream*)membuf,
+                                G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | 
+                                G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                                G_PRIORITY_DEFAULT,
+                                NULL,
+                                on_knownhosts_splice_complete,
+                                self);
+
+ out:
+  if (local_error)
+    {
+      priv->idle_save_knownhosts_id = 0;
+      g_warning ("Failed to write '%s': %s",
+                 gs_file_get_path_cached (priv->openssh_knownhosts_path),
+                 local_error->message);
+      g_error_free (local_error);
+    }
+}
+
+static gboolean
+idle_write_knownhosts (gpointer user_data)
+{
+  HotSshHostDB *self = user_data;
+  G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
+
+  g_file_append_to_async (priv->openssh_knownhosts_path, 0,
+                          G_PRIORITY_DEFAULT, NULL,
+                          on_knownhosts_opened, self);
+
+  return FALSE;
+}
+
 void
-hotssh_hostdb_set_entry_known (HotSshHostDB    *self,
-                               const char      *id,
-                               gboolean         make_known)
+hotssh_hostdb_set_entry_host_key_known (HotSshHostDB    *self,
+                                        const char      *id,
+                                        const char      *keytype,
+                                        const char      *key_base64,
+                                        const char      *last_ip_address)
 {
   G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
-  gboolean is_known;
   GtkTreeIter iter;
 
+  g_return_if_fail (id != NULL);
+  g_return_if_fail (keytype != NULL);
+  g_return_if_fail (key_base64 != NULL);
+
   if (!hotssh_hostdb_lookup_by_id (self, id, &iter))
     return;
 
-  gtk_tree_model_get ((GtkTreeModel*)priv->model, &iter,
-                      HOTSSH_HOSTDB_COLUMN_IS_KNOWN, &is_known,
+  gtk_list_store_set (priv->model, &iter,
+                      HOTSSH_HOSTDB_COLUMN_HOST_KEY_TYPE, keytype,
+                      HOTSSH_HOSTDB_COLUMN_HOST_KEY_BASE64, key_base64,
+                      HOTSSH_HOSTDB_COLUMN_HOST_KEY_IP_ADDRESS, last_ip_address,
                       -1);
 
-  if (is_known == make_known)
-    return;
+  g_hash_table_add (priv->add_knownhost_queue, g_strdup (id));
 
-  if (make_known)
-    {
-      g_debug ("not implemented");
-    }
-  else
-    {
-      g_debug ("not implemented");
-    }
+  if (priv->idle_save_knownhosts_id == 0)
+    priv->idle_save_knownhosts_id = g_timeout_add_seconds (WRITE_IDLE_SECONDS, idle_write_knownhosts, self);
 }
 
 static GFileMonitor *
@@ -533,7 +671,10 @@ hotssh_hostdb_init (HotSshHostDB *self)
                                     G_TYPE_STRING, /* username */
                                     G_TYPE_UINT64, /* last-used */
                                     G_TYPE_BOOLEAN, /* is-known */
-                                    G_TYPE_UINT64  /* openssh-knownhost-line */
+                                    G_TYPE_UINT64, /* openssh-knownhost-line */
+                                    G_TYPE_STRING, /* last-ip-address */
+                                    G_TYPE_STRING, /* host-key-type */
+                                    G_TYPE_STRING /* host-key-base64 */
                                     );
   gtk_tree_sortable_set_sort_column_id ((GtkTreeSortable*)priv->model, 
                                         HOTSSH_HOSTDB_COLUMN_LAST_USED,
@@ -541,6 +682,9 @@ hotssh_hostdb_init (HotSshHostDB *self)
   homedir = g_get_home_dir ();
   g_assert (homedir);
 
+  priv->add_knownhost_queue = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                     NULL, g_free);
+
   priv->openssh_knownhosts = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                     g_free, (GDestroyNotify)g_strfreev);
 
@@ -579,6 +723,7 @@ hotssh_hostdb_dispose (GObject *object)
   g_clear_pointer (&priv->hostdb, g_key_file_unref);
   g_clear_pointer (&priv->openssh_knownhosts_path, g_hash_table_unref);
   g_clear_object (&priv->model);
+  g_clear_pointer (&priv->add_knownhost_queue, g_hash_table_unref);
 
   G_OBJECT_CLASS (hotssh_hostdb_parent_class)->dispose (object);
 }
diff --git a/src/hotssh-hostdb.h b/src/hotssh-hostdb.h
index 459491d..7cb7b32 100644
--- a/src/hotssh-hostdb.h
+++ b/src/hotssh-hostdb.h
@@ -35,9 +35,12 @@ enum {
   HOTSSH_HOSTDB_COLUMN_USERNAME,
   HOTSSH_HOSTDB_COLUMN_LAST_USED,
   HOTSSH_HOSTDB_COLUMN_IS_KNOWN,
-  HOTSSH_HOSTDB_COLUMN_OPENSSH_KNOWNHOST_LINE
+  HOTSSH_HOSTDB_COLUMN_OPENSSH_KNOWNHOST_LINE,
+  HOTSSH_HOSTDB_COLUMN_HOST_KEY_IP_ADDRESS,
+  HOTSSH_HOSTDB_COLUMN_HOST_KEY_TYPE,
+  HOTSSH_HOSTDB_COLUMN_HOST_KEY_BASE64
 };
-#define HOTSSH_HOSTDB_N_COLUMNS (HOTSSH_HOSTDB_COLUMN_OPENSSH_KNOWNHOST_LINE+1)
+#define HOTSSH_HOSTDB_N_COLUMNS (HOTSSH_HOSTDB_COLUMN_HOST_KEY_BASE64+1)
 
 GType                   hotssh_hostdb_get_type     (void);
 HotSshHostDB           *hotssh_hostdb_get_instance (void);
@@ -61,6 +64,8 @@ void                   hotssh_hostdb_set_entry_basic (HotSshHostDB    *self,
 void                   hotssh_hostdb_update_last_used    (HotSshHostDB    *self,
                                                           const char      *id);
 
-void                   hotssh_hostdb_set_entry_known    (HotSshHostDB    *self,
-                                                         const char      *id,
-                                                         gboolean         known);
+void                   hotssh_hostdb_set_entry_host_key_known (HotSshHostDB    *self,
+                                                               const char      *id,
+                                                               const char      *keytype,
+                                                               const char      *key_base64,
+                                                               const char      *last_ip_address);
diff --git a/src/hotssh-tab.c b/src/hotssh-tab.c
index a32df2d..b2674e9 100644
--- a/src/hotssh-tab.c
+++ b/src/hotssh-tab.c
@@ -94,6 +94,8 @@ struct _HotSshTabPrivate
   GtkWidget *hostname_renderer;
   GtkWidget *lastused_column;
   GtkWidget *lastused_renderer;
+  GtkWidget *known_column;
+  GtkWidget *known_renderer;
 
   /* State */
   HotSshTabPage active_page;
@@ -121,6 +123,10 @@ struct _HotSshTabPrivate
 
 G_DEFINE_TYPE_WITH_PRIVATE(HotSshTab, hotssh_tab, GTK_TYPE_NOTEBOOK);
 
+static void
+on_negotiate_complete (GObject             *src,
+                       GAsyncResult        *result,
+                       gpointer             user_data);
 
 static void
 set_status (HotSshTab     *self,
@@ -428,6 +434,64 @@ iterate_authentication_modes (HotSshTab          *self)
 }
 
 static void
+handle_unknown_hostkey (HotSshTab *self,
+                        const char *connected_hostkey_type,
+                        const char *connected_hostkey_sha1)
+{
+  HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+  gtk_label_set_text ((GtkLabel*)priv->hostkey_fingerprint_label,
+                     connected_hostkey_sha1);
+  page_transition (self, HOTSSH_TAB_PAGE_HOSTKEY);
+}
+
+static void
+verify_hostkey (HotSshTab              *self,
+                const char             *connected_hostkey_type,
+                const char             *connected_hostkey_base64,
+                const char             *saved_hostkey_type,
+                const char             *saved_hostkey_base64)
+{
+  HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+  gs_free char *errdetails = NULL;
+
+  if (strcmp (connected_hostkey_type, saved_hostkey_type) != 0)
+    {
+      errdetails = g_strdup_printf (_("The remote host key type has changed; it was previously \"%s\", now 
\"%s\""),
+                                    saved_hostkey_type, connected_hostkey_type);
+    }
+  else if (strcmp (connected_hostkey_base64, saved_hostkey_base64) != 0)
+    {
+      errdetails = g_strdup (_("The remote host key has changed"));
+    }
+    
+  if (errdetails)
+    {
+      GError *local_error = NULL;
+
+      g_set_error (&local_error, G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   _("Error: The host credentials for \"%s\" (%s) do not match the previously "
+                     "saved credentials.  This may represent an attempt by a malicious party to "
+                     "intercept communication; however, it is also possible that the host key was changed "
+                     "by a system administrator.\n\nDetails: %s"),
+                   priv->hostname,
+                   g_network_address_get_hostname ((GNetworkAddress*)priv->address),
+                   errdetails);
+      page_transition_take_error (self, local_error);
+    }
+  else
+    {
+      g_debug ("Remote host key matches");
+      page_transition (self, HOTSSH_TAB_PAGE_CONNECTING);
+      set_status (self, _("Negotiating authentication…"));
+
+      gssh_connection_negotiate_async (priv->connection, priv->cancellable,
+                                       on_negotiate_complete, self);
+    }
+}
+
+static void
 on_connection_handshake (GObject         *object,
                         GAsyncResult    *result,
                         gpointer         user_data)
@@ -436,36 +500,52 @@ on_connection_handshake (GObject         *object,
   HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
   GError *local_error = NULL;
   GError **error = &local_error;
-  GBytes *hostkey_sha1_binary;
-  GString *buf;
-  guint i;
-  const guint8 *binbuf;
-  gsize len;
-  gs_free char *hostkey_sha1_text = NULL;
-  gs_free char *hostkey_type = NULL;
+  gs_free char *saved_hostkey_type = NULL;
+  gs_free char *saved_hostkey_base64 = NULL;
+  gs_unref_object GtkTreeModel *model = NULL;
+  gs_free char *connected_hostkey_type = NULL;
+  gs_free char *connected_hostkey_sha1_text = NULL;
+  gs_free char *connected_hostkey_base64 = NULL;
+  GtkTreeIter iter;
+
+  model = hotssh_hostdb_get_model (hotssh_hostdb_get_instance ());
 
   if (!gssh_connection_handshake_finish ((GSshConnection*)object, result, error))
     goto out;
 
+  gssh_connection_preauth_get_host_key (priv->connection,
+                                        &connected_hostkey_type,
+                                        &connected_hostkey_sha1_text,
+                                        &connected_hostkey_base64);
+
   g_debug ("handshake complete");
+  g_debug ("remote key type:%s SHA1:%s",
+           connected_hostkey_type,
+           connected_hostkey_sha1_text);
+
+  g_assert (hotssh_hostdb_lookup_by_id (hotssh_hostdb_get_instance (), priv->connection_id, &iter));
 
-  hostkey_sha1_binary = gssh_connection_preauth_get_host_key_fingerprint_sha1 (priv->connection,
-                                                                               &hostkey_type);
-  binbuf = g_bytes_get_data (hostkey_sha1_binary, &len);
-  buf = g_string_new ("");
-  for (i = 0; i < len; i++)
+  gtk_tree_model_get (model, &iter,
+                      HOTSSH_HOSTDB_COLUMN_HOST_KEY_TYPE,
+                      &saved_hostkey_type,
+                      HOTSSH_HOSTDB_COLUMN_HOST_KEY_BASE64,
+                      &saved_hostkey_base64,
+                      -1);
+
+  if (saved_hostkey_type == NULL)
     {
-      g_string_append_printf (buf, "%02X", binbuf[i]);
-      if (i < len - 1)
-       g_string_append_c (buf, ':');
+      handle_unknown_hostkey (self,
+                              connected_hostkey_type,
+                              connected_hostkey_sha1_text);
+    }
+  else
+    {
+      verify_hostkey (self,
+                      connected_hostkey_type,
+                      connected_hostkey_base64,
+                      saved_hostkey_type,
+                      saved_hostkey_base64);
     }
-  hostkey_sha1_text = g_string_free (buf, FALSE);
-  
-  g_debug ("remote key type:%s SHA1:%s", hostkey_type, hostkey_sha1_text);
-
-  gtk_label_set_text ((GtkLabel*)priv->hostkey_fingerprint_label,
-                     hostkey_sha1_text);
-  page_transition (self, HOTSSH_TAB_PAGE_HOSTKEY);
 
  out:
   if (local_error)
@@ -727,12 +807,19 @@ on_approve_hostkey_clicked (GtkButton     *button,
 {
   HotSshTab *self = user_data;
   HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+  gs_free char *keytype = NULL;
+  gs_free char *key_base64 = NULL;
 
   page_transition (self, HOTSSH_TAB_PAGE_CONNECTING);
   set_status (self, _("Negotiating authentication…"));
 
-  hotssh_hostdb_set_entry_known (hotssh_hostdb_get_instance (),
-                                 priv->connection_id, TRUE);
+  gssh_connection_preauth_get_host_key (priv->connection,
+                                        &keytype, NULL, &key_base64);
+
+  hotssh_hostdb_set_entry_host_key_known (hotssh_hostdb_get_instance (),
+                                          priv->connection_id,
+                                          keytype, key_base64,
+                                          NULL);
 
   gssh_connection_negotiate_async (priv->connection, priv->cancellable,
                                    on_negotiate_complete, self);
@@ -982,6 +1069,32 @@ render_last_used (GtkTreeViewColumn *tree_column,
 }
 
 static void
+render_known (GtkTreeViewColumn *tree_column,
+              GtkCellRenderer *cell,
+              GtkTreeModel *tree_model,
+              GtkTreeIter *iter,
+              gpointer data)
+{
+  gboolean known;
+  const char *text;
+  gs_free char *keytype;
+
+  gtk_tree_model_get (tree_model, iter,
+                      HOTSSH_HOSTDB_COLUMN_IS_KNOWN,
+                      &known,
+                      HOTSSH_HOSTDB_COLUMN_HOST_KEY_TYPE,
+                      &keytype,
+                      -1);
+
+  if (!known || !keytype)
+    text = _("Unknown");
+  else 
+    text = keytype;
+
+  g_object_set (cell, "text", text, NULL);
+}
+
+static void
 hotssh_tab_constructed (GObject *object)
 {
   HotSshTab *self = HOTSSH_TAB (object);
@@ -1007,6 +1120,10 @@ hotssh_tab_constructed (GObject *object)
                 "sort-indicator", TRUE,
                 "sort-order", GTK_SORT_DESCENDING,
                 NULL);
+  gtk_tree_view_column_set_cell_data_func ((GtkTreeViewColumn*)priv->known_column,
+                                           (GtkCellRenderer*)priv->known_renderer,
+                                           render_known,
+                                           self, NULL);
 }
 
 static void
@@ -1024,6 +1141,8 @@ hotssh_tab_class_init (HotSshTabClass *class)
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, hostname_renderer);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, lastused_column);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, lastused_renderer);
+  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, known_column);
+  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, known_renderer);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, host_entry);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, username_entry);
   gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, 
create_and_connect_button);
diff --git a/src/tab.ui b/src/tab.ui
index 0b5186a..841ac53 100644
--- a/src/tab.ui
+++ b/src/tab.ui
@@ -103,7 +103,7 @@
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
-                          <object class="GtkButton" id="connect_cancel_button1">
+                          <object class="GtkButton" id="create_cancel_button">
                             <property name="label">gtk-cancel</property>
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
@@ -195,6 +195,14 @@
                 </child>
               </object>
             </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="known_column">
+                <property name="title" translatable="yes">Known</property>
+                <child>
+                  <object class="GtkCellRendererText" id="known_renderer"/>
+                </child>
+              </object>
+            </child>
           </object>
           <packing>
             <property name="expand">True</property>
@@ -240,8 +248,11 @@
               <object class="GtkLabel" id="connection_text">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
                 <property name="label" translatable="yes">Connecting…</property>
                 <property name="justify">center</property>
+                <property name="wrap">True</property>
               </object>
               <packing>
                 <property name="expand">True</property>
@@ -295,7 +306,10 @@
               <object class="GtkLabel" id="error_text">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
                 <property name="label" translatable="yes">Error:</property>
+                <property name="wrap">True</property>
               </object>
               <packing>
                 <property name="expand">False</property>


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