>From 539b74fb3eebf3e6f097a816ced8ade484d348c7 Mon Sep 17 00:00:00 2001
From: William Brown <william@firstyear.id.au>
Date: Wed, 30 Apr 2014 16:29:32 +0930
Subject: [PATCH] Allow sss_cache to expire sshKnownHosts

---
 src/confdb/confdb.c                     |  11 ++
 src/confdb/confdb.h                     |   3 +
 src/config/etc/sssd.api.conf            |   1 +
 src/db/sysdb_ssh.c                      |  57 +++++-
 src/db/sysdb_ssh.h                      |  17 +-
 src/man/po/sssd-docs.pot                |  17 ++
 src/providers/ipa/ipa_hostid.c          |   2 +-
 src/responder/ifp/ifp_iface_generated.c | 318 ++++++++++++++++----------------
 src/tools/sss_cache.c                   |  54 +++++-
 9 files changed, 310 insertions(+), 170 deletions(-)

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index 15de961..5106b32 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -1040,6 +1040,17 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
         goto done;
     }
 
+    /* Override the ssh known hosts timeout, if specified */
+    ret = get_entry_as_uint32(res->msgs[0], &domain->ssh_host_timeout,
+                              CONFDB_SSH_KNOWN_HOSTS_EXPIRE,
+                              entry_cache_timeout);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Invalid value for [%s]\n",
+               CONFDB_SSH_KNOWN_HOSTS_EXPIRE);
+        goto done;
+    }
+
     /* Set refresh_expired_interval, if specified */
     ret = get_entry_as_uint32(res->msgs[0], &domain->refresh_expired_interval,
                               CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL,
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index ba33ea5..7586cc7 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 "entry_cache_ssh_host_timeout"
+#define CONFDB_DEFAULT_SSH_KNOWN_HOSTS_EXPIRE 31536000
 
 /* PAC */
 #define CONFDB_PAC_CONF_ENTRY "config/pac"
@@ -232,6 +234,7 @@ struct sss_domain_info {
     uint32_t service_timeout;
     uint32_t autofsmap_timeout;
     uint32_t sudo_timeout;
+    uint32_t ssh_host_timeout;
 
     uint32_t refresh_expired_interval;
     uint32_t subdomain_refresh_interval;
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 5e5a928..94c296e 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
+entry_cache_ssh_host_timeout = int, None, false
 
 [pac]
 # PAC responder
diff --git a/src/db/sysdb_ssh.c b/src/db/sysdb_ssh.c
index 7dd98cf..1d3f5b9 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,
@@ -46,7 +54,8 @@ sysdb_store_ssh_host(struct sss_domain_info *domain,
                      const char *name,
                      const char *alias,
                      time_t now,
-                     struct sysdb_attrs *attrs)
+                     struct sysdb_attrs *attrs,
+                     struct confdb_ctx *confdb)
 {
     TALLOC_CTX *tmp_ctx;
     errno_t ret, sret;
@@ -56,6 +65,7 @@ sysdb_store_ssh_host(struct sss_domain_info *domain,
     struct ldb_message *host = NULL;
     struct ldb_message_element *el;
     unsigned int i;
+    time_t cache_timeout = domain->ssh_host_timeout;
 
     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..394b8b3 100644
--- a/src/db/sysdb_ssh.h
+++ b/src/db/sysdb_ssh.h
@@ -34,7 +34,8 @@ sysdb_store_ssh_host(struct sss_domain_info *domain,
                      const char *name,
                      const char *alias,
                      time_t now,
-                     struct sysdb_attrs *attrs);
+                     struct sysdb_attrs *attrs,
+                     struct confdb_ctx *confdb);
 
 errno_t
 sysdb_update_ssh_known_host_expire(struct sss_domain_info *domain,
@@ -42,11 +43,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/po/sssd-docs.pot b/src/man/po/sssd-docs.pot
index df0456d..5341a29 100644
--- a/src/man/po/sssd-docs.pot
+++ b/src/man/po/sssd-docs.pot
@@ -1140,6 +1140,23 @@ msgstr ""
 msgid "Default: 180"
 msgstr ""
 
+#. type: Content of: <reference><refentry><refsect1><refsect2><variablelist><varlistentry><term>
+#: sssd.conf.5.xml:878
+msgid "entry_cache_ssh_host_timeout (integer)"
+msgstr ""
+
+#. type: Content of: <reference><refentry><refsect1><refsect2><variablelist><varlistentry><listitem><para>
+#: sssd.conf.5.xml:881
+msgid ""
+"How many seconds to keep a host ssh key after refresh. IE how long to cache "
+"the host key for."
+msgstr ""
+
+#. type: Content of: <reference><refentry><refsect1><refsect2><variablelist><varlistentry><listitem><para>
+#: sssd.conf.5.xml:885
+msgid "Default: 31536000 (1 Year)"
+msgstr ""
+
 #. type: Content of: <reference><refentry><refsect1><refsect2><title>
 #: sssd.conf.5.xml:893
 msgid "PAC responder configuration options"
diff --git a/src/providers/ipa/ipa_hostid.c b/src/providers/ipa/ipa_hostid.c
index 905454d..26560b4 100644
--- a/src/providers/ipa/ipa_hostid.c
+++ b/src/providers/ipa/ipa_hostid.c
@@ -301,7 +301,7 @@ hosts_get_done(struct tevent_req *subreq)
     }
 
     ret = sysdb_store_ssh_host(state->domain, state->name, state->alias, now,
-                               attrs);
+                               attrs, NULL);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
index 20ad563..62a21e9 100644
--- a/src/responder/ifp/ifp_iface_generated.c
+++ b/src/responder/ifp/ifp_iface_generated.c
@@ -356,16 +356,66 @@ const struct sbus_method_meta infopipe_component__methods[] = {
     { NULL, }
 };
 
+/* property info for org.freedesktop.sssd.infopipe.Components */
+const struct sbus_property_meta infopipe_component__properties[] = {
+    {
+        "name", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_component, infopipe_component_get_name),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "debug_level", /* name */
+        "u", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_component, infopipe_component_get_debug_level),
+        invoke_get_u,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "enabled", /* name */
+        "b", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_component, infopipe_component_get_enabled),
+        invoke_get_b,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "type", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_component, infopipe_component_get_type),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "providers", /* name */
+        "as", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_component, infopipe_component_get_providers),
+        invoke_get_as,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    { NULL, }
+};
+
 /* invokes GetAll for the 'org.freedesktop.sssd.infopipe.Components' interface */
 static int invoke_infopipe_component_get_all(struct sbus_request *dbus_req, void *function_ptr)
 {
-    struct sbus_interface *intf = dbus_req->intf;
-    const struct sbus_property_meta *property;
     DBusMessage *reply;
     dbus_bool_t dbret;
     DBusMessageIter iter;
     DBusMessageIter iter_dict;
     int ret;
+    struct sbus_interface *intf = dbus_req->intf;
+    const struct sbus_property_meta *property;
     const char * s_prop_val;
     const char * s_out_val;
     void (*s_handler)(struct sbus_request *, void *data, const char * *);
@@ -453,75 +503,156 @@ static int invoke_infopipe_component_get_all(struct sbus_request *dbus_req, void
     return sbus_request_finish(dbus_req, reply);
 }
 
-/* property info for org.freedesktop.sssd.infopipe.Components */
-const struct sbus_property_meta infopipe_component__properties[] = {
+/* interface info for org.freedesktop.sssd.infopipe.Components */
+const struct sbus_interface_meta infopipe_component_meta = {
+    "org.freedesktop.sssd.infopipe.Components", /* name */
+    infopipe_component__methods,
+    NULL, /* no signals */
+    infopipe_component__properties,
+    invoke_infopipe_component_get_all, /* GetAll invoker */
+};
+
+/* property info for org.freedesktop.sssd.infopipe.Domains */
+const struct sbus_property_meta infopipe_domain__properties[] = {
     {
         "name", /* name */
         "s", /* type */
         SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_component, infopipe_component_get_name),
+        offsetof(struct infopipe_domain, infopipe_domain_get_name),
         invoke_get_s,
         0, /* not writable */
         NULL, /* no invoker */
     },
     {
-        "debug_level", /* name */
+        "provider", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_provider),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "primary_servers", /* name */
+        "as", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_primary_servers),
+        invoke_get_as,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "backup_servers", /* name */
+        "as", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_backup_servers),
+        invoke_get_as,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "min_id", /* name */
         "u", /* type */
         SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_component, infopipe_component_get_debug_level),
+        offsetof(struct infopipe_domain, infopipe_domain_get_min_id),
         invoke_get_u,
         0, /* not writable */
         NULL, /* no invoker */
     },
     {
-        "enabled", /* name */
-        "b", /* type */
+        "max_id", /* name */
+        "u", /* type */
         SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_component, infopipe_component_get_enabled),
-        invoke_get_b,
+        offsetof(struct infopipe_domain, infopipe_domain_get_max_id),
+        invoke_get_u,
         0, /* not writable */
         NULL, /* no invoker */
     },
     {
-        "type", /* name */
+        "realm", /* name */
         "s", /* type */
         SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_component, infopipe_component_get_type),
+        offsetof(struct infopipe_domain, infopipe_domain_get_realm),
         invoke_get_s,
         0, /* not writable */
         NULL, /* no invoker */
     },
     {
-        "providers", /* name */
-        "as", /* type */
+        "forest", /* name */
+        "s", /* type */
         SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_component, infopipe_component_get_providers),
-        invoke_get_as,
+        offsetof(struct infopipe_domain, infopipe_domain_get_forest),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "login_format", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_login_format),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "fully_qualified_name_format", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_fully_qualified_name_format),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "enumerable", /* name */
+        "b", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_enumerable),
+        invoke_get_b,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "use_fully_qualified_names", /* name */
+        "b", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_use_fully_qualified_names),
+        invoke_get_b,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "subdomain", /* name */
+        "b", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_subdomain),
+        invoke_get_b,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "parent_domain", /* name */
+        "o", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct infopipe_domain, infopipe_domain_get_parent_domain),
+        invoke_get_o,
         0, /* not writable */
         NULL, /* no invoker */
     },
     { NULL, }
 };
 
-/* interface info for org.freedesktop.sssd.infopipe.Components */
-const struct sbus_interface_meta infopipe_component_meta = {
-    "org.freedesktop.sssd.infopipe.Components", /* name */
-    infopipe_component__methods,
-    NULL, /* no signals */
-    infopipe_component__properties,
-    invoke_infopipe_component_get_all, /* GetAll invoker */
-};
-
 /* invokes GetAll for the 'org.freedesktop.sssd.infopipe.Domains' interface */
 static int invoke_infopipe_domain_get_all(struct sbus_request *dbus_req, void *function_ptr)
 {
-    struct sbus_interface *intf = dbus_req->intf;
-    const struct sbus_property_meta *property;
     DBusMessage *reply;
     dbus_bool_t dbret;
     DBusMessageIter iter;
     DBusMessageIter iter_dict;
     int ret;
+    struct sbus_interface *intf = dbus_req->intf;
+    const struct sbus_property_meta *property;
     const char * s_prop_val;
     const char * s_out_val;
     void (*s_handler)(struct sbus_request *, void *data, const char * *);
@@ -711,137 +842,6 @@ static int invoke_infopipe_domain_get_all(struct sbus_request *dbus_req, void *f
     return sbus_request_finish(dbus_req, reply);
 }
 
-/* property info for org.freedesktop.sssd.infopipe.Domains */
-const struct sbus_property_meta infopipe_domain__properties[] = {
-    {
-        "name", /* name */
-        "s", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_name),
-        invoke_get_s,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "provider", /* name */
-        "s", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_provider),
-        invoke_get_s,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "primary_servers", /* name */
-        "as", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_primary_servers),
-        invoke_get_as,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "backup_servers", /* name */
-        "as", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_backup_servers),
-        invoke_get_as,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "min_id", /* name */
-        "u", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_min_id),
-        invoke_get_u,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "max_id", /* name */
-        "u", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_max_id),
-        invoke_get_u,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "realm", /* name */
-        "s", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_realm),
-        invoke_get_s,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "forest", /* name */
-        "s", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_forest),
-        invoke_get_s,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "login_format", /* name */
-        "s", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_login_format),
-        invoke_get_s,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "fully_qualified_name_format", /* name */
-        "s", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_fully_qualified_name_format),
-        invoke_get_s,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "enumerable", /* name */
-        "b", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_enumerable),
-        invoke_get_b,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "use_fully_qualified_names", /* name */
-        "b", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_use_fully_qualified_names),
-        invoke_get_b,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "subdomain", /* name */
-        "b", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_subdomain),
-        invoke_get_b,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    {
-        "parent_domain", /* name */
-        "o", /* type */
-        SBUS_PROPERTY_READABLE,
-        offsetof(struct infopipe_domain, infopipe_domain_get_parent_domain),
-        invoke_get_o,
-        0, /* not writable */
-        NULL, /* no invoker */
-    },
-    { NULL, }
-};
-
 /* interface info for org.freedesktop.sssd.infopipe.Domains */
 const struct sbus_interface_meta infopipe_domain_meta = {
     "org.freedesktop.sssd.infopipe.Domains", /* name */
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

