>From 13c1915264bb77c0d780453de978a5eff411d656 Mon Sep 17 00:00:00 2001
From: William B <william@adelaide.edu.au>
Date: Wed, 16 Jul 2014 11:45:02 +0930
Subject: [PATCH] Allow sss_cache tool to flush known hosts cache

---
 src/confdb/confdb.h          |  2 ++
 src/config/etc/sssd.api.conf |  1 +
 src/db/sysdb_ssh.c           | 55 +++++++++++++++++++++++++++++++++++++++++---
 src/db/sysdb_ssh.h           | 14 +++++++++++
 src/man/sssd.conf.5.xml      | 12 ++++++++++
 src/tools/sss_cache.c        | 54 +++++++++++++++++++++++++++++++++++++++----
 6 files changed, 130 insertions(+), 8 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index ba33ea5..5417c4c 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -128,6 +128,8 @@
 #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
+#define CONFDB_SSH_KNOWN_HOSTS_EXPIRE "ssh_known_hosts_expire"
+#define CONFDB_DEFAULT_SSH_KNOWN_HOSTS_EXPIRE 31536000
 
 /* PAC */
 #define CONFDB_PAC_CONF_ENTRY "config/pac"
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 5e5a928..6d41bea 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -67,6 +67,7 @@ autofs_negative_timeout = int, None, false
 # ssh service
 ssh_hash_known_hosts = bool, None, false
 ssh_known_hosts_timeout = int, None, false
+ssh_known_hosts_timeout = int, None, false
 
 [pac]
 # PAC responder
diff --git a/src/db/sysdb_ssh.c b/src/db/sysdb_ssh.c
index 7dd98cf..206285d 100644
--- a/src/db/sysdb_ssh.c
+++ b/src/db/sysdb_ssh.c
@@ -23,6 +23,14 @@
 #include "db/sysdb_ssh.h"
 #include "db/sysdb_private.h"
 
+static struct ldb_dn *
+sysdb_ssh_host_dn(TALLOC_CTX *mem_ctx,
+                   struct sss_domain_info *domain,
+                   const char *name)
+{
+    return sysdb_custom_dn(mem_ctx, domain, name, SSH_HOSTS_SUBDIR);
+}
+
 static errno_t
 sysdb_update_ssh_host(struct sss_domain_info *domain,
                       const char *name,
@@ -56,6 +64,8 @@ sysdb_store_ssh_host(struct sss_domain_info *domain,
     struct ldb_message *host = NULL;
     struct ldb_message_element *el;
     unsigned int i;
+    //This is a year: It could be made a param to the function though.
+    time_t cache_timeout = 31536000;
 
     DEBUG(SSSDBG_TRACE_FUNC, "Storing host %s\n", name);
 
@@ -147,6 +157,14 @@ sysdb_store_ssh_host(struct sss_domain_info *domain,
         goto done;
     }
 
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+                                  now + cache_timeout);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, "Could not set sysdb cache expire [%d]: %s\n",
+              ret, strerror(ret));
+        goto done;
+    }
+
     ret = sysdb_update_ssh_host(domain, name, attrs);
     if (ret != EOK) {
         goto done;
@@ -175,6 +193,36 @@ done:
     return ret;
 }
 
+
+errno_t
+sysdb_set_ssh_host_attr(struct sss_domain_info *domain,
+                        const char *name,
+                        struct sysdb_attrs *attrs,
+                        int mod_op)
+{
+
+    errno_t ret;
+    struct ldb_dn *dn;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    dn = sysdb_ssh_host_dn(tmp_ctx, domain, name);
+    if (!dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 errno_t
 sysdb_update_ssh_known_host_expire(struct sss_domain_info *domain,
                                    const char *name,
@@ -229,7 +277,7 @@ sysdb_delete_ssh_host(struct sss_domain_info *domain,
     return sysdb_delete_custom(domain, name, SSH_HOSTS_SUBDIR);
 }
 
-static errno_t
+errno_t
 sysdb_search_ssh_hosts(TALLOC_CTX *mem_ctx,
                        struct sss_domain_info *domain,
                        const char *filter,
@@ -335,8 +383,9 @@ sysdb_get_ssh_known_hosts(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    filter = talloc_asprintf(tmp_ctx, "(%s>=%ld)",
-                             SYSDB_SSH_KNOWN_HOSTS_EXPIRE, (long)now);
+    filter = talloc_asprintf(tmp_ctx, "(&(%s>=%ld)(%s>=%ld))",
+                             SYSDB_SSH_KNOWN_HOSTS_EXPIRE, (long)now,
+                             SYSDB_CACHE_EXPIRE, (long)now);
     if (!filter) {
         ret = ENOMEM;
         goto done;
diff --git a/src/db/sysdb_ssh.h b/src/db/sysdb_ssh.h
index e8aca77..631e7d9 100644
--- a/src/db/sysdb_ssh.h
+++ b/src/db/sysdb_ssh.h
@@ -42,11 +42,25 @@ sysdb_update_ssh_known_host_expire(struct sss_domain_info *domain,
                                    time_t now,
                                    int known_hosts_timeout);
 
+int
+sysdb_set_ssh_host_attr(struct sss_domain_info *domain,
+                        const char *name,
+                        struct sysdb_attrs *attrs,
+                        int mod_op);
+
 errno_t
 sysdb_delete_ssh_host(struct sss_domain_info *domain,
                       const char *name);
 
 errno_t
+sysdb_search_ssh_hosts(TALLOC_CTX *mem_ctx,
+                       struct sss_domain_info *domain,
+                       const char *filter,
+                       const char **attrs,
+                       struct ldb_message ***hosts,
+                       size_t *num_hosts);
+
+errno_t
 sysdb_get_ssh_host(TALLOC_CTX *mem_ctx,
                    struct sss_domain_info *domain,
                    const char *name,
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 27d22f4..84d828c 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -886,6 +886,18 @@ fallback_homedir = /home/%u
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>ssh_known_hosts_expire (integer)</term>
+                    <listitem>
+                        <para>
+                            How many seconds to keep a host ssh key after refresh. IE how long to cache
+                            the host key for.
+                        </para>
+                        <para>
+                            Default: 31536000 (1 Year)
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </refsect2>
 
diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index 7cd5852..7b726a2 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -30,6 +30,7 @@
 #include "db/sysdb.h"
 #include "db/sysdb_services.h"
 #include "db/sysdb_autofs.h"
+#include "db/sysdb_ssh.h"
 
 #define INVALIDATE_NONE 0
 #define INVALIDATE_USERS 1
@@ -37,14 +38,16 @@
 #define INVALIDATE_NETGROUPS 4
 #define INVALIDATE_SERVICES 8
 #define INVALIDATE_AUTOFSMAPS 16
+#define INVALIDATE_SSH_HOSTS 32
 
 #ifdef BUILD_AUTOFS
 #define INVALIDATE_EVERYTHING (INVALIDATE_USERS | INVALIDATE_GROUPS | \
                                INVALIDATE_NETGROUPS | INVALIDATE_SERVICES | \
-                               INVALIDATE_AUTOFSMAPS)
+                               INVALIDATE_AUTOFSMAPS | INVALIDATE_SSH_HOSTS )
 #else
 #define INVALIDATE_EVERYTHING (INVALIDATE_USERS | INVALIDATE_GROUPS | \
-                               INVALIDATE_NETGROUPS | INVALIDATE_SERVICES)
+                               INVALIDATE_NETGROUPS | INVALIDATE_SERVICES | \
+                                INVALIDATE_SSH_HOSTS )
 #endif
 
 enum sss_cache_entry {
@@ -52,7 +55,8 @@ enum sss_cache_entry {
     TYPE_GROUP,
     TYPE_NETGROUP,
     TYPE_SERVICE,
-    TYPE_AUTOFSMAP
+    TYPE_AUTOFSMAP,
+    TYPE_SSH_HOST
 };
 
 static errno_t search_autofsmaps(TALLOC_CTX *mem_ctx,
@@ -69,18 +73,21 @@ struct cache_tool_ctx {
     char *netgroup_filter;
     char *service_filter;
     char *autofs_filter;
+    char *ssh_host_filter;
 
     char *user_name;
     char *group_name;
     char *netgroup_name;
     char *service_name;
     char *autofs_name;
+    char *ssh_host_name;
 
     bool update_user_filter;
     bool update_group_filter;
     bool update_netgroup_filter;
     bool update_service_filter;
     bool update_autofs_filter;
+    bool update_ssh_host_filter;
 };
 
 errno_t init_domains(struct cache_tool_ctx *ctx, const char *domain);
@@ -152,6 +159,9 @@ int main(int argc, const char *argv[])
         skipped &= !invalidate_entries(tctx, dinfo, TYPE_AUTOFSMAP,
                                        tctx->autofs_filter,
                                        tctx->autofs_name);
+        skipped &= !invalidate_entries(tctx, dinfo, TYPE_SSH_HOST,
+                                       tctx->ssh_host_filter,
+                                       tctx->ssh_host_name);
 
         ret = sysdb_transaction_commit(sysdb);
         if (ret != EOK) {
@@ -328,6 +338,14 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
         return ret;
     }
 
+    /* Update ssh host filter */
+    ret = update_filter(tctx, dinfo, tctx->ssh_host_name,
+                        tctx->update_ssh_host_filter, "(%s=%s)", false,
+                        &tctx->ssh_host_filter);
+    if (ret != EOK) {
+        return ret;
+    }
+
     return EOK;
 }
 
@@ -371,6 +389,11 @@ static bool invalidate_entries(TALLOC_CTX *ctx,
         type_string = "autofs map";
         ret = search_autofsmaps(ctx, dinfo, filter, attrs, &msg_count, &msgs);
         break;
+    case TYPE_SSH_HOST:
+        type_string = "ssh_host";
+        ret = sysdb_search_ssh_hosts(ctx, dinfo,
+                                    filter, attrs, &msgs, &msg_count);
+        break;
     }
 
     if (ret != EOK) {
@@ -446,6 +469,10 @@ static errno_t invalidate_entry(TALLOC_CTX *ctx,
                     ret = sysdb_set_autofsmap_attr(domain, name,
                                                    sys_attrs, SYSDB_MOD_REP);
                     break;
+                case TYPE_SSH_HOST:
+                    ret = sysdb_set_ssh_host_attr(domain, name,
+                                                    sys_attrs, SYSDB_MOD_REP);
+                    break;
                 default:
                     return EINVAL;
             }
@@ -529,6 +556,7 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
     char *group = NULL;
     char *netgroup = NULL;
     char *service = NULL;
+    char *ssh_host = NULL;
     char *map = NULL;
     char *domain = NULL;
     int debug = SSSDBG_DEFAULT;
@@ -563,6 +591,10 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         { "autofs-maps", 'A', POPT_ARG_NONE, NULL, 'a',
             _("Invalidate all autofs maps"), NULL },
 #endif /* BUILD_AUTOFS */
+        { "ssh_host", 'h', POPT_ARG_STRING, &ssh_host, 0,
+            _("Invalidate particular ssh host"), NULL },
+        { "ssh_hosts", 'H', POPT_ARG_NONE, NULL, 'h',
+            _("Invalidate all ssh hosts"), NULL },
         { "domain", 'd', POPT_ARG_STRING, &domain, 0,
             _("Only invalidate entries from a particular domain"), NULL },
         POPT_TABLEEND
@@ -594,6 +626,9 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
             case 'a':
                 idb |= INVALIDATE_AUTOFSMAPS;
                 break;
+            case 'h':
+                idb |= INVALIDATE_SSH_HOSTS;
+                break;
             case 'e':
                 idb = INVALIDATE_EVERYTHING;
                 break;
@@ -608,7 +643,7 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
     }
 
     if (idb == INVALIDATE_NONE && !user && !group &&
-        !netgroup && !service && !map) {
+        !netgroup && !service && !ssh_host && !map) {
         BAD_POPT_PARAMS(pc,
                 _("Please select at least one object to invalidate\n"),
                 ret, fini);
@@ -665,14 +700,23 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         ctx->update_autofs_filter = true;
     }
 
+    if (idb & INVALIDATE_SSH_HOSTS) {
+        ctx->ssh_host_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+        ctx->update_ssh_host_filter = false;
+    } else if (ssh_host) {
+        ctx->ssh_host_name = talloc_strdup(ctx, ssh_host);
+        ctx->update_ssh_host_filter = true;
+    }
+
     if (((idb & INVALIDATE_USERS) && !ctx->user_filter) ||
         ((idb & INVALIDATE_GROUPS) && !ctx->group_filter) ||
         ((idb & INVALIDATE_NETGROUPS) && !ctx->netgroup_filter) ||
         ((idb & INVALIDATE_SERVICES) && !ctx->service_filter) ||
         ((idb & INVALIDATE_AUTOFSMAPS) && !ctx->autofs_filter) ||
+        ((idb & INVALIDATE_SSH_HOSTS) && !ctx->ssh_host_filter) ||
          (user && !ctx->user_name) || (group && !ctx->group_name) ||
          (netgroup && !ctx->netgroup_name) || (map && !ctx->autofs_name) ||
-         (service && !ctx->service_name)) {
+         (service && !ctx->service_name) || (map && !ctx->ssh_host_name)) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Construction of filters failed\n");
         ret = ENOMEM;
         goto fini;
-- 
1.9.3

