>From 304d59f8bb14b265b80fa6ebaa962ddb1f5b13f3 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jcholast@redhat.com>
Date: Wed, 18 Apr 2012 07:05:29 -0400
Subject: [PATCH 2/2] SSH: Add support for hashed known_hosts

https://fedorahosted.org/sssd/ticket/1203
---
 src/confdb/confdb.h                         |    2 +
 src/config/SSSDConfig.py                    |    3 +
 src/config/etc/sssd.api.conf                |    1 +
 src/man/sssd.conf.5.xml                     |   25 ++++
 src/responder/ssh/sshsrv.c                  |   13 ++
 src/responder/ssh/sshsrv_cmd.c              |  192 ++++++++++++++++++++++-----
 src/responder/ssh/sshsrv_private.h          |    2 +
 src/sss_client/ssh/sss_ssh_authorizedkeys.c |    2 +-
 src/util/sss_ssh.c                          |    9 +-
 src/util/sss_ssh.h                          |    3 +-
 10 files changed, 212 insertions(+), 40 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index aebf5d8..eb46a81 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -105,6 +105,8 @@
 
 /* SSH */
 #define CONFDB_SSH_CONF_ENTRY "config/ssh"
+#define CONFDB_SSH_HASH_KNOWN_HOSTS "ssh_hash_known_hosts"
+#define CONFDB_DEFAULT_SSH_HASH_KNOWN_HOSTS true
 
 /* Data Provider */
 #define CONFDB_DP_CONF_ENTRY "config/dp"
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 488220a..e67a901 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -81,6 +81,9 @@ option_strings = {
     # [autofs]
     'autofs_negative_timeout' : _('Negative cache timeout length (seconds)'),
 
+    # [ssh]
+    'ssh_hash_known_hosts': _('Whether to hash host names and adresses in the known_hosts file'),
+
     # [provider]
     'id_provider' : _('Identity provider'),
     'auth_provider' : _('Authentication provider'),
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 155b8ef..0d49ea2 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -55,6 +55,7 @@ autofs_negative_timeout = int, None, false
 
 [ssh]
 # ssh service
+ssh_hash_known_hosts = bool, None, false
 
 [provider]
 #Available provider types
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 1a8ae2b..3ec7494 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -687,6 +687,31 @@
             </variablelist>
         </refsect2>
 
+        <refsect2 id='SSH' condition="with_ssh">
+            <title>SSH configuration options</title>
+            <para>
+                These options can be used to configure the SSH service.
+            </para>
+            <para>
+                <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/experimental.xml" />
+            </para>
+
+            <variablelist>
+                <varlistentry>
+                    <term>ssh_hash_known_hosts (bool)</term>
+                    <listitem>
+                        <para>
+                            Whether or not to hash host names and adresses in
+                            the managed known_hosts file.
+                        </para>
+                        <para>
+                            Default: true
+                        </para>
+                    </listitem>
+                </varlistentry>
+            </variablelist>
+        </refsect2>
+
     </refsect1>
 
     <refsect1 id='domain-sections'>
diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c
index f0cee9f..a423231 100644
--- a/src/responder/ssh/sshsrv.c
+++ b/src/responder/ssh/sshsrv.c
@@ -128,6 +128,19 @@ int ssh_process_init(TALLOC_CTX *mem_ctx,
                             ssh_dp_reconnect_init, iter);
     }
 
+    /* Get responder options */
+
+    /* Get ssh_hash_known_hosts option */
+    ret = confdb_get_bool(ssh_ctx->rctx->cdb,
+                          CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_HASH_KNOWN_HOSTS,
+                          CONFDB_DEFAULT_SSH_HASH_KNOWN_HOSTS,
+                          &ssh_ctx->hash_known_hosts);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Error reading from confdb (%d) [%s]\n",
+              ret, strerror(ret)));
+        return ret;
+    }
+
     DEBUG(SSSDBG_TRACE_FUNC, ("SSH Initialization complete\n"));
 
     return EOK;
diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c
index 4882bac..6dac52f 100644
--- a/src/responder/ssh/sshsrv_cmd.c
+++ b/src/responder/ssh/sshsrv_cmd.c
@@ -417,6 +417,139 @@ ssh_host_pubkeys_search_dp_callback(uint16_t err_maj,
     ssh_cmd_done(cmd_ctx, ret);
 }
 
+static char *
+ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx,
+                                         struct sss_ssh_ent *ent)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *name, *pubkey, *result;
+    size_t i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return NULL;
+    }
+
+    name = talloc_strdup(tmp_ctx, ent->name);
+    if (!name) {
+        goto done;
+    }
+
+    for (i = 0; i < ent->num_aliases; i++) {
+        name = talloc_asprintf_append(name, ",%s", ent->aliases[i]);
+        if (!name) {
+            goto done;
+        }
+    }
+
+    result = talloc_strdup(tmp_ctx, "");
+    if (!result) {
+        goto done;
+    }
+
+    for (i = 0; i < ent->num_pubkeys; i++) {
+        pubkey = sss_ssh_format_pubkey(tmp_ctx, ent, &ent->pubkeys[i],
+                                       SSS_SSH_FORMAT_OPENSSH, "");
+        if (!pubkey) {
+            result = NULL;
+            goto done;
+        }
+
+        result = talloc_asprintf_append(result, "%s %s\n", name, pubkey);
+        if (!result) {
+            goto done;
+        }
+
+        talloc_free(pubkey);
+    }
+
+    talloc_steal(mem_ctx, result);
+
+done:
+    talloc_free(tmp_ctx);
+
+    return result;
+}
+
+static char *
+ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx,
+                                          struct sss_ssh_ent *ent)
+{
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    char *name, *pubkey, *saltstr, *hashstr, *result;
+    unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH];
+    size_t i, j, k;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return NULL;
+    }
+
+    result = talloc_strdup(tmp_ctx, "");
+    if (!result) {
+        goto done;
+    }
+
+    for (i = 0; i < ent->num_pubkeys; i++) {
+        pubkey = sss_ssh_format_pubkey(tmp_ctx, ent, &ent->pubkeys[i],
+                                       SSS_SSH_FORMAT_OPENSSH, "");
+        if (!pubkey) {
+            result = NULL;
+            goto done;
+        }
+
+        for (j = 0; j <= ent->num_aliases; j++) {
+            name = (j == 0 ? ent->name : ent->aliases[j-1]);
+
+            for (k = 0; k < SSS_SHA1_LENGTH; k++) {
+                salt[k] = rand();
+            }
+
+            ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH,
+                                (unsigned char *)name, strlen(name),
+                                hash);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("sss_hmac_sha1() failed (%d): %s\n",
+                       ret, strerror(ret)));
+                result = NULL;
+                goto done;
+            }
+
+            saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH);
+            if (!saltstr) {
+                result = NULL;
+                goto done;
+            }
+
+            hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH);
+            if (!hashstr) {
+                result = NULL;
+                goto done;
+            }
+
+            result = talloc_asprintf_append(result, "|1|%s|%s %s\n",
+                                            saltstr, hashstr, pubkey);
+            if (!result) {
+                goto done;
+            }
+
+            talloc_free(saltstr);
+            talloc_free(hashstr);
+        }
+
+        talloc_free(pubkey);
+    }
+
+    talloc_steal(mem_ctx, result);
+
+done:
+    talloc_free(tmp_ctx);
+
+    return result;
+}
+
 static errno_t
 ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
 {
@@ -430,12 +563,13 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
     };
     struct cli_ctx *cctx = cmd_ctx->cctx;
     struct sss_domain_info *dom = cctx->rctx->domains;
+    struct ssh_ctx *ssh_ctx = (struct ssh_ctx *)cctx->rctx->pvt_ctx;
     struct sysdb_ctx *sysdb;
     struct ldb_message **hosts;
-    size_t num_hosts, i, j, k;
+    size_t num_hosts, i;
     struct sss_ssh_ent *ent;
     int fd = -1;
-    char *filename, *pubkey, *line;
+    char *filename, *entstr;
     ssize_t wret;
     mode_t old_mask;
 
@@ -487,43 +621,29 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
                 continue;
             }
 
-            for (j = 0; j < ent->num_pubkeys; j++) {
-                pubkey = sss_ssh_format_pubkey(tmp_ctx, ent, &ent->pubkeys[j],
-                                               SSS_SSH_FORMAT_OPENSSH);
-                if (!pubkey) {
-                    DEBUG(SSSDBG_OP_FAILURE,
-                          ("Out of memory formatting SSH public key\n"));
-                    continue;
-                }
-
-                line = talloc_strdup(tmp_ctx, ent->name);
-                if (!line) {
-                    ret = ENOMEM;
-                    goto done;
-                }
-
-                for (k = 0; k < ent->num_aliases; k++) {
-                    line = talloc_asprintf_append(line, ",%s", ent->aliases[k]);
-                    if (!line) {
-                        ret = ENOMEM;
-                        goto done;
-                    }
-                }
-
-                line = talloc_asprintf_append(line, " %s\n", pubkey);
-                if (!line) {
-                    ret = ENOMEM;
-                    goto done;
-                }
-
-                wret = sss_atomic_write(fd, line, strlen(line));
-                if (wret == -1) {
-                    ret = errno;
-                    goto done;
-                }
+            if (ssh_ctx->hash_known_hosts) {
+                entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent);
+            } else {
+                entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent);
+            }
+            if (!entstr) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("Failed to format known_hosts data for [%s]\n",
+                       ent->name));
+                continue;
             }
+
+            wret = sss_atomic_write(fd, entstr, strlen(entstr));
+            if (wret == -1) {
+                ret = errno;
+                goto done;
+            }
+
+            talloc_free(ent);
         }
 
+        talloc_free(hosts);
+
         dom = dom->next;
     }
 
diff --git a/src/responder/ssh/sshsrv_private.h b/src/responder/ssh/sshsrv_private.h
index ab9edf7..d74b492 100644
--- a/src/responder/ssh/sshsrv_private.h
+++ b/src/responder/ssh/sshsrv_private.h
@@ -31,6 +31,8 @@
 
 struct ssh_ctx {
     struct resp_ctx *rctx;
+
+    bool hash_known_hosts;
 };
 
 struct ssh_cmd_ctx {
diff --git a/src/sss_client/ssh/sss_ssh_authorizedkeys.c b/src/sss_client/ssh/sss_ssh_authorizedkeys.c
index b64bbc3..dd9a062 100644
--- a/src/sss_client/ssh/sss_ssh_authorizedkeys.c
+++ b/src/sss_client/ssh/sss_ssh_authorizedkeys.c
@@ -110,7 +110,7 @@ int main(int argc, const char **argv)
     /* print results */
     for (i = 0; i < ent->num_pubkeys; i++) {
         repr = sss_ssh_format_pubkey(mem_ctx, ent, &ent->pubkeys[i],
-                                     SSS_SSH_FORMAT_OPENSSH);
+                                     SSS_SSH_FORMAT_OPENSSH, NULL);
         if (!repr) {
             ERROR("Not enough memory\n");
             ret = EXIT_FAILURE;
diff --git a/src/util/sss_ssh.c b/src/util/sss_ssh.c
index 0163c1d..60ed587 100644
--- a/src/util/sss_ssh.c
+++ b/src/util/sss_ssh.c
@@ -136,13 +136,18 @@ char *
 sss_ssh_format_pubkey(TALLOC_CTX *mem_ctx,
                       struct sss_ssh_ent *ent,
                       struct sss_ssh_pubkey *pubkey,
-                      enum sss_ssh_pubkey_format format)
+                      enum sss_ssh_pubkey_format format,
+                      const char *comment)
 {
     TALLOC_CTX *tmp_ctx;
     char *blob;
     char *algo;
     char *result = NULL;
 
+    if (!comment) {
+        comment = ent->name;
+    }
+
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) {
         return NULL;
@@ -166,7 +171,7 @@ sss_ssh_format_pubkey(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        result = talloc_asprintf(mem_ctx, "%s %s %s", algo, blob, ent->name);
+        result = talloc_asprintf(mem_ctx, "%s %s %s", algo, blob, comment);
         break;
     }
 
diff --git a/src/util/sss_ssh.h b/src/util/sss_ssh.h
index a4ac739..29743a0 100644
--- a/src/util/sss_ssh.h
+++ b/src/util/sss_ssh.h
@@ -54,6 +54,7 @@ char *
 sss_ssh_format_pubkey(TALLOC_CTX *mem_ctx,
                       struct sss_ssh_ent *ent,
                       struct sss_ssh_pubkey *pubkey,
-                      enum sss_ssh_pubkey_format format);
+                      enum sss_ssh_pubkey_format format,
+                      const char *comment);
 
 #endif /* _SSS_SSH_H_ */
-- 
1.7.7.6

