>From 992c25b5f2938ed46e01b8450098fe0b7cef2fe8 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jcholast@redhat.com>
Date: Tue, 25 Sep 2012 04:29:29 -0400
Subject: [PATCH 2/2] SSH: Expire hosts in known_hosts

---
 src/confdb/confdb.h                  |  2 +
 src/config/SSSDConfig/__init__.py.in |  1 +
 src/config/etc/sssd.api.conf         |  1 +
 src/db/sysdb.c                       |  7 +++
 src/db/sysdb_private.h               |  5 +-
 src/db/sysdb_ssh.c                   | 72 ++++++++++++++++++++++++++++-
 src/db/sysdb_ssh.h                   |  9 ++++
 src/db/sysdb_upgrade.c               | 89 ++++++++++++++++++++++++++++++++++++
 src/man/sssd.conf.5.xml              | 12 +++++
 src/responder/ssh/sshsrv.c           | 11 +++++
 src/responder/ssh/sshsrv_cmd.c       | 10 +++-
 src/responder/ssh/sshsrv_private.h   |  1 +
 12 files changed, 216 insertions(+), 4 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 39267d3..0e02e6c 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -121,6 +121,8 @@
 #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
+#define CONFDB_SSH_KNOWN_HOSTS_TIMEOUT "ssh_known_hosts_timeout"
+#define CONFDB_DEFAULT_SSH_KNOWN_HOSTS_TIMEOUT 180
 
 /* PAC */
 #define CONFDB_PAC_CONF_ENTRY "config/pac"
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 8f04839..3c6d84c 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -89,6 +89,7 @@ option_strings = {
 
     # [ssh]
     'ssh_hash_known_hosts': _('Whether to hash host names and addresses in the known_hosts file'),
+    'ssh_known_hosts_timeout': _('How many seconds to keep a host in the known_hosts file after its host keys were requested'),
 
     # [pac]
     'allowed_uids': _('List of UIDs or user names allowed to access the PAC responder'),
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index f17fbb5..2ba47d7 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -64,6 +64,7 @@ autofs_negative_timeout = int, None, false
 [ssh]
 # ssh service
 ssh_hash_known_hosts = bool, None, false
+ssh_known_hosts_timeout = int, None, false
 
 [pac]
 # PAC responder
diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index 1e5e8ad..25ff675 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -1103,6 +1103,13 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
                 }
             }
 
+            if (strcmp(version, SYSDB_VERSION_0_12) == 0) {
+                ret = sysdb_upgrade_12(sysdb, &version);
+                if (ret != EOK) {
+                    goto done;
+                }
+            }
+
             /* The version should now match SYSDB_VERSION.
              * If not, it means we didn't match any of the
              * known older versions. The DB might be
diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
index 8c9aea5..bde4c60 100644
--- a/src/db/sysdb_private.h
+++ b/src/db/sysdb_private.h
@@ -23,6 +23,7 @@
 #ifndef __INT_SYS_DB_H__
 #define __INT_SYS_DB_H__
 
+#define SYSDB_VERSION_0_13 "0.13"
 #define SYSDB_VERSION_0_12 "0.12"
 #define SYSDB_VERSION_0_11 "0.11"
 #define SYSDB_VERSION_0_10 "0.10"
@@ -36,7 +37,7 @@
 #define SYSDB_VERSION_0_2 "0.2"
 #define SYSDB_VERSION_0_1 "0.1"
 
-#define SYSDB_VERSION SYSDB_VERSION_0_12
+#define SYSDB_VERSION SYSDB_VERSION_0_13
 
 #define SYSDB_BASE_LDIF \
      "dn: @ATTRIBUTES\n" \
@@ -62,6 +63,7 @@
      "@IDXATTR: servicePort\n" \
      "@IDXATTR: serviceProtocol\n" \
      "@IDXATTR: sudoUser\n" \
+     "@IDXATTR: sshKnownHostsExpire\n" \
      "@IDXONE: 1\n" \
      "\n" \
      "dn: @MODULES\n" \
@@ -108,6 +110,7 @@ int sysdb_upgrade_08(struct sysdb_ctx *sysdb, const char **ver);
 int sysdb_upgrade_09(struct sysdb_ctx *sysdb, const char **ver);
 int sysdb_upgrade_10(struct sysdb_ctx *sysdb, const char **ver);
 int sysdb_upgrade_11(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_12(struct sysdb_ctx *sysdb, const char **ver);
 
 int add_string(struct ldb_message *msg, int flags,
                const char *attr, const char *value);
diff --git a/src/db/sysdb_ssh.c b/src/db/sysdb_ssh.c
index 48d54a4..47969bb 100644
--- a/src/db/sysdb_ssh.c
+++ b/src/db/sysdb_ssh.c
@@ -177,6 +177,52 @@ done:
 }
 
 errno_t
+sysdb_update_ssh_known_host_expire(struct sysdb_ctx *sysdb,
+                                   const char *name,
+                                   time_t now,
+                                   int known_hosts_timeout)
+{
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    struct sysdb_attrs *attrs;
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          ("Updating known_hosts expire time of host %s\n", name));
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    attrs = sysdb_new_attrs(tmp_ctx);
+    if (!attrs) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_SSH_KNOWN_HOSTS_EXPIRE,
+                                 now + known_hosts_timeout);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not set known_hosts expire time [%d]: %s\n",
+               ret, strerror(ret)));
+        goto done;
+    }
+
+    ret = sysdb_update_ssh_host(sysdb, name, attrs);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+errno_t
 sysdb_delete_ssh_host(struct sysdb_ctx *sysdb,
                       const char *name)
 {
@@ -275,10 +321,32 @@ done:
 errno_t
 sysdb_get_ssh_known_hosts(TALLOC_CTX *mem_ctx,
                           struct sysdb_ctx *sysdb,
+                          time_t now,
                           const char **attrs,
                           struct ldb_message ***hosts,
                           size_t *num_hosts)
 {
-    return sysdb_search_ssh_hosts(mem_ctx, sysdb, NULL, attrs,
-                                  hosts, num_hosts);
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    const char *filter;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    filter = talloc_asprintf(tmp_ctx, "(%s>=%ld)",
+                             SYSDB_SSH_KNOWN_HOSTS_EXPIRE, (long)now);
+    if (!filter) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_ssh_hosts(mem_ctx, sysdb, filter, attrs,
+                                 hosts, num_hosts);
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
 }
diff --git a/src/db/sysdb_ssh.h b/src/db/sysdb_ssh.h
index 49de66e..9a5159e 100644
--- a/src/db/sysdb_ssh.h
+++ b/src/db/sysdb_ssh.h
@@ -27,6 +27,8 @@
 
 #define SYSDB_SSH_HOST_OC "sshHost"
 
+#define SYSDB_SSH_KNOWN_HOSTS_EXPIRE "sshKnownHostsExpire"
+
 errno_t
 sysdb_store_ssh_host(struct sysdb_ctx *sysdb,
                      const char *name,
@@ -35,6 +37,12 @@ sysdb_store_ssh_host(struct sysdb_ctx *sysdb,
                      struct sysdb_attrs *attrs);
 
 errno_t
+sysdb_update_ssh_known_host_expire(struct sysdb_ctx *sysdb,
+                                   const char *name,
+                                   time_t now,
+                                   int known_hosts_timeout);
+
+errno_t
 sysdb_delete_ssh_host(struct sysdb_ctx *sysdb,
                       const char *name);
 
@@ -48,6 +56,7 @@ sysdb_get_ssh_host(TALLOC_CTX *mem_ctx,
 errno_t
 sysdb_get_ssh_known_hosts(TALLOC_CTX *mem_ctx,
                           struct sysdb_ctx *sysdb,
+                          time_t now,
                           const char **attrs,
                           struct ldb_message ***hosts,
                           size_t *num_hosts);
diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
index 87abd35..83ef83d 100644
--- a/src/db/sysdb_upgrade.c
+++ b/src/db/sysdb_upgrade.c
@@ -1450,3 +1450,92 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
+
+int sysdb_upgrade_12(struct sysdb_ctx *sysdb, const char **ver)
+{
+    TALLOC_CTX *tmp_ctx;
+    int ret;
+    struct ldb_message *msg;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_CRIT_FAILURE,
+          ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_13));
+
+    ret = ldb_transaction_start(sysdb->ldb);
+    if (ret != LDB_SUCCESS) {
+        ret = EIO;
+        goto done;
+    }
+
+    /* add new indexes */
+    msg = ldb_msg_new(tmp_ctx);
+    if (!msg) {
+        ret = ENOMEM;
+        goto done;
+    }
+    msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
+    if (!msg->dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* add index for sshKnownHostsExpire */
+    ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
+    if (ret != LDB_SUCCESS) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ldb_msg_add_string(msg, "@IDXATTR", "sshKnownHostsExpire");
+    if (ret != LDB_SUCCESS) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ldb_modify(sysdb->ldb, msg);
+    if (ret != LDB_SUCCESS) {
+        ret = sysdb_error_to_errno(ret);
+        goto done;
+    }
+
+    /* conversion done, upgrade version number */
+    msg = ldb_msg_new(tmp_ctx);
+    if (!msg) {
+        ret = ENOMEM;
+        goto done;
+    }
+    msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_BASE);
+    if (!msg->dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
+    if (ret != LDB_SUCCESS) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_13);
+    if (ret != LDB_SUCCESS) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ldb_modify(sysdb->ldb, msg);
+    if (ret != LDB_SUCCESS) {
+        ret = sysdb_error_to_errno(ret);
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    ret = finish_upgrade(ret, sysdb->ldb, SYSDB_VERSION_0_13, ver);
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 6fbbd9a..2dde716 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -808,6 +808,18 @@ override_homedir = /home/%u
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>ssh_known_hosts_timeout (integer)</term>
+                    <listitem>
+                        <para>
+                            How many seconds to keep a host in the managed
+                            known_hosts file after its host keys were requested.
+                        </para>
+                        <para>
+                            Default: 180
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect2>
 
diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c
index a423231..fe01f81 100644
--- a/src/responder/ssh/sshsrv.c
+++ b/src/responder/ssh/sshsrv.c
@@ -141,6 +141,17 @@ int ssh_process_init(TALLOC_CTX *mem_ctx,
         return ret;
     }
 
+    /* Get ssh_known_hosts_timeout option */
+    ret = confdb_get_int(ssh_ctx->rctx->cdb,
+                         CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_KNOWN_HOSTS_TIMEOUT,
+                         CONFDB_DEFAULT_SSH_KNOWN_HOSTS_TIMEOUT,
+                         &ssh_ctx->known_hosts_timeout);
+    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 a47894b..ec988f0 100644
--- a/src/responder/ssh/sshsrv_cmd.c
+++ b/src/responder/ssh/sshsrv_cmd.c
@@ -554,6 +554,7 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
     struct sss_domain_info *dom = cctx->rctx->domains;
     struct ssh_ctx *ssh_ctx = (struct ssh_ctx *)cctx->rctx->pvt_ctx;
     struct sysdb_ctx *sysdb;
+    time_t now = time(NULL);
     struct ldb_message **hosts;
     size_t num_hosts, i;
     struct sss_ssh_ent *ent;
@@ -567,6 +568,13 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
         return ENOMEM;
     }
 
+    ret = sysdb_update_ssh_known_host_expire(cmd_ctx->domain->sysdb,
+                                             cmd_ctx->name, now,
+                                             ssh_ctx->known_hosts_timeout);
+    if (ret != EOK) {
+        goto done;
+    }
+
     /* write known_hosts file */
     filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL);
     if (!filename) {
@@ -592,7 +600,7 @@ ssh_host_pubkeys_update_known_hosts(struct ssh_cmd_ctx *cmd_ctx)
             goto done;
         }
 
-        ret = sysdb_get_ssh_known_hosts(tmp_ctx, sysdb, attrs,
+        ret = sysdb_get_ssh_known_hosts(tmp_ctx, sysdb, now, attrs,
                                         &hosts, &num_hosts);
         if (ret != EOK) {
             if (ret != ENOENT) {
diff --git a/src/responder/ssh/sshsrv_private.h b/src/responder/ssh/sshsrv_private.h
index e228af4..4b13ca1 100644
--- a/src/responder/ssh/sshsrv_private.h
+++ b/src/responder/ssh/sshsrv_private.h
@@ -33,6 +33,7 @@ struct ssh_ctx {
     struct resp_ctx *rctx;
 
     bool hash_known_hosts;
+    int known_hosts_timeout;
 };
 
 struct ssh_cmd_ctx {
-- 
1.7.11.4

