URL: https://github.com/SSSD/sssd/pull/5450
Author: justin-stephenson
 Title: #5450: kcm: add support for kerberos tgt renewals
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/5450/head:pr5450
git checkout pr5450
From 82161222b68fea341c5d0f34af603086ae9371eb Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Tue, 1 Dec 2020 11:28:59 -0500
Subject: [PATCH 1/7] KCM: Read and set KCM renewal and krb5 options

Add new renewal options to enable KCM renewal functionality

  tgt_renewal
  tgt_renewal_inherit

Krb5 options below will be read from the [kcm] configuration
section, or a domain section when a tgt_renewal_inherit domain
is provided.

  krb5_renew_interval
  krb5_renew_lifetime
  krb5_lifetime
  krb5_validate
  krb5_canonicalize
  krb5_auth_timeout
---
 Makefile.am                   |   3 +
 src/confdb/confdb.h           |   8 +
 src/config/cfg_rules.ini      |   8 +
 src/man/sssd-kcm.8.xml        |  49 ++++++
 src/responder/kcm/kcm.c       |  43 +++++
 src/responder/kcm/kcm_renew.c | 316 ++++++++++++++++++++++++++++++++++
 src/responder/kcm/kcm_renew.h |   3 +
 7 files changed, 430 insertions(+)
 create mode 100644 src/responder/kcm/kcm_renew.c
 create mode 100644 src/responder/kcm/kcm_renew.h

diff --git a/Makefile.am b/Makefile.am
index 7bb4d6e9d2..bfc50f1e69 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1798,6 +1798,8 @@ sssd_kcm_SOURCES = \
     src/responder/kcm/kcmsrv_ccache_secdb.c \
     src/responder/kcm/kcmsrv_ops.c \
     src/responder/kcm/kcmsrv_op_queue.c \
+    src/providers/krb5/krb5_opts.c \
+    src/providers/data_provider_opts.c \
     src/util/sss_sockets.c \
     src/util/sss_krb5.c \
     src/util/sss_iobuf.c \
@@ -1815,6 +1817,7 @@ sssd_kcm_LDADD = \
     $(KRB5_LIBS) \
     $(JANSSON_LIBS) \
     $(SSSD_LIBS) \
+    $(CARES_LIBS) \
     $(UUID_LIBS) \
     $(SYSTEMD_DAEMON_LIBS) \
     $(SSSD_INTERNAL_LTLIBS) \
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 2290217739..437a473d5e 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -289,6 +289,14 @@
 #define CONFDB_KCM_MAX_CCACHES "max_ccaches"
 #define CONFDB_KCM_MAX_UID_CCACHES "max_uid_ccaches"
 #define CONFDB_KCM_MAX_CCACHE_SIZE "max_ccache_size"
+#define CONFDB_KCM_TGT_RENEWAL "tgt_renewal"
+#define CONFDB_KCM_TGT_RENEWAL_INHERIT "tgt_renewal_inherit"
+#define CONFDB_KCM_KRB5_LIFETIME "krb5_lifetime"
+#define CONFDB_KCM_KRB5_RENEWABLE_LIFETIME "krb5_renewable_lifetime"
+#define CONFDB_KCM_KRB5_RENEW_INTERVAL "krb5_renew_interval"
+#define CONFDB_KCM_KRB5_VALIDATE "krb5_validate"
+#define CONFDB_KCM_KRB5_CANONICALIZE "krb5_canonicalize"
+#define CONFDB_KCM_KRB5_AUTH_TIMEOUT "krb5_auth_timeout"
 
 /* Certificate mapping rules */
 #define CONFDB_CERTMAP_BASEDN "cn=certmap,cn=config"
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 149b4d78ef..a247b3bff2 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -330,6 +330,14 @@ option = responder_idle_timeout
 option = max_ccaches
 option = max_uid_ccaches
 option = max_ccache_size
+option = tgt_renewal
+option = tgt_renewal_inherit
+option = krb5_lifetime
+option = krb5_renewable_lifetime
+option = krb5_renew_interval
+option = krb5_validate
+option = krb5_canonicalize
+option = krb5_auth_timeout
 
 # Session recording
 [rule/allowed_session_recording_options]
diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
index 14ba122a5c..5f81af7367 100644
--- a/src/man/sssd-kcm.8.xml
+++ b/src/man/sssd-kcm.8.xml
@@ -162,6 +162,42 @@ systemctl restart sssd-kcm.service
         </para>
     </refsect1>
 
+    <refsect1 id='renewals'>
+        <title>RENEWALS</title>
+        <para>
+            The sssd-kcm service can be configured to attempt TGT
+            renewal for renewable TGTs stored in the KCM ccache.
+            Renewals are only attempted when half of the ticket
+            lifetime has been reached. KCM Renewals are configured
+            when the following option is set in the [kcm] section:
+            <programlisting>
+krb5_renew_interval
+            </programlisting>
+        </para>
+        <para>
+            If [kcm] section renewal options are absent, SSSD will
+            fallback to using the krb5_* options listed below from
+            the *first* auth_provider=krb5 domain, if available.
+        </para>
+        <para>
+            The following krb5 options can be configured in the
+            [kcm] section to control renewal behavior, these
+            options are described in detail in the
+            <citerefentry>
+                <refentrytitle>sssd-krb5</refentrytitle>
+                <manvolnum>5</manvolnum>
+            </citerefentry> manual page.
+            <programlisting>
+    krb5_renew_interval
+    krb5_renew_lifetime
+    krb5_lifetime
+    krb5_validate
+    krb5_canonicalize
+    krb5_auth_timeout
+            </programlisting>
+        </para>
+    </refsect1>
+
     <refsect1 id='options'>
         <title>CONFIGURATION OPTIONS</title>
         <para>
@@ -249,6 +285,19 @@ systemctl restart sssd-kcm.service
                     </para>
                 </listitem>
             </varlistentry>
+            <varlistentry>
+                <term>krb5_renew_interval (integer)</term>
+                <listitem>
+                    <para>
+                        The time in seconds between TGT renewal attempts for
+                        all KCM-stored renewable tickets. Renewals only trigger
+                        when about half of their ticket lifetime is exceeded.
+                    </para>
+                    <para>
+                        Default: 0 (Automatic renewal is disabled)
+                    </para>
+                </listitem>
+            </varlistentry>
         </variablelist>
     </refsect1>
 
diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
index 97e37a0144..609ca1220a 100644
--- a/src/responder/kcm/kcm.c
+++ b/src/responder/kcm/kcm.c
@@ -25,7 +25,9 @@
 
 #include "responder/kcm/kcmsrv_ccache.h"
 #include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcm_renew.h"
 #include "responder/common/responder.h"
+#include "providers/krb5/krb5_common.h"
 #include "util/util.h"
 #include "util/sss_krb5.h"
 
@@ -48,6 +50,39 @@ static int kcm_responder_ctx_destructor(void *ptr)
     return 0;
 }
 
+static errno_t kcm_renewals_init(struct tevent_context *ev,
+                                 struct resp_ctx *rctx,
+                                 struct kcm_ctx *kctx,
+                                 struct krb5_ctx *krb5_ctx,
+                                 time_t renew_intv,
+                                 bool *_renewal_enabled)
+{
+#ifndef HAVE_KCM_RENEWAL
+    return EOK;
+#else
+    errno_t ret;
+
+    ret = kcm_get_renewal_config(kctx, &krb5_ctx, &renew_intv);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM renewal config\n");
+        return ret;
+    }
+
+    if (renew_intv > 0) {
+        *_renewal_enabled = true;
+
+        ret = kcm_renewals_setup(rctx, krb5_ctx, ev, kctx->kcm_data->db, renew_intv);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "fatal error initializing KCM renewals\n");
+            return ret;
+        }
+    }
+
+    return EOK;
+#endif
+}
+
 static errno_t kcm_get_ccdb_be(struct kcm_ctx *kctx)
 {
     errno_t ret;
@@ -210,6 +245,8 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
 {
     struct resp_ctx *rctx;
     struct kcm_ctx *kctx;
+    struct krb5_ctx *krb5_ctx = NULL;
+    time_t renew_intv = 0;
     int ret;
 
     rctx = talloc_zero(mem_ctx, struct resp_ctx);
@@ -254,6 +291,12 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
+    ret = kcm_renewals_init(ev, rctx, kctx, krb5_ctx, renew_intv);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM renewal config\n");
+        goto fail;
+    }
+
     /* Set up file descriptor limits */
     responder_set_fd_limit(kctx->fd_limit);
 
diff --git a/src/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c
new file mode 100644
index 0000000000..2a9bda8312
--- /dev/null
+++ b/src/responder/kcm/kcm_renew.c
@@ -0,0 +1,316 @@
+extern struct dp_option default_krb5_opts[];
+
+struct kcm_renew_tgt_ctx {
+    struct tevent_context *ev;
+    struct krb5child_req *kr;
+
+    struct krb5_ctx *krb5_ctx;
+    struct auth_data *auth_data;
+
+    uint8_t *buf;
+    ssize_t len;
+};
+
+struct auth_data {
+    struct krb5_ctx *krb5_ctx;
+    uid_t uid;
+    gid_t gid;
+    const char *ccname;
+    const char *upn;
+};
+
+static void kcm_renew_tgt_done(struct tevent_req *req);
+
+static errno_t kcm_set_options(struct krb5_ctx *krb5_ctx,
+                               char *lifetime,
+                               char *rtime,
+                               bool validate,
+                               bool canonicalize,
+                               int timeout,
+                               char *renew_intv,
+                               time_t *_renew_intv_tm)
+{
+    errno_t ret;
+    krb5_error_code kerr;
+    krb5_deltat renew_interval_delta;
+
+    if (renew_intv != NULL) {
+        kerr = krb5_string_to_deltat(renew_intv, &renew_interval_delta);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_FATAL_FAILURE, "krb5_string_to_deltat failed\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        *_renew_intv_tm = renew_interval_delta;
+    } else {
+        *_renew_intv_tm = 0;
+    }
+
+    if (lifetime != NULL) {
+        ret = krb5_string_to_deltat(lifetime, &krb5_ctx->lifetime);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to convert lifetime string.\n");
+            goto done;
+        }
+        krb5_ctx->lifetime_str = lifetime;
+    }
+
+    if (rtime != 0) {
+        ret = krb5_string_to_deltat(rtime, &krb5_ctx->rlife);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to convert renewable lifetime "
+                                     "string.\n");
+            goto done;
+        }
+    }
+
+    ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_VALIDATE, validate);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child timeout\n");
+        goto done;
+    }
+
+    krb5_ctx->canonicalize = canonicalize;
+
+    if (timeout > 0) {
+        ret = dp_opt_set_int(krb5_ctx->opts, KRB5_AUTH_TIMEOUT, timeout);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child timeout\n");
+            goto done;
+        }
+    }
+
+    ret = EOK;
+done:
+    return ret;
+}
+
+static errno_t kcm_read_options(TALLOC_CTX *mem_ctx,
+                                struct confdb_ctx *cdb,
+                                const char *cpath,
+                                char **_lifetime,
+                                char **_rtime,
+                                bool *_validate,
+                                bool *_canonicalize,
+                                int *_timeout,
+                                char **_renew_intv)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *lifetime;
+    char *rtime;
+    bool validate;
+    bool canonicalize;
+    int timeout;
+    char *renew_intv;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = confdb_get_string(cdb, tmp_ctx, cpath,
+                            CONFDB_KCM_KRB5_LIFETIME, NULL,
+                            &lifetime);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+              CONFDB_KCM_KRB5_LIFETIME, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = confdb_get_string(cdb, tmp_ctx, cpath,
+                            CONFDB_KCM_KRB5_RENEWABLE_LIFETIME, NULL,
+                            &rtime);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+              CONFDB_KCM_KRB5_RENEWABLE_LIFETIME, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = confdb_get_bool(cdb, cpath,
+                          CONFDB_KCM_KRB5_VALIDATE, false,
+                          &validate);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+              CONFDB_KCM_KRB5_VALIDATE, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = confdb_get_bool(cdb, cpath,
+                          CONFDB_KCM_KRB5_CANONICALIZE, false,
+                          &canonicalize);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+              CONFDB_KCM_KRB5_CANONICALIZE, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = confdb_get_int(cdb, cpath,
+                         CONFDB_KCM_KRB5_AUTH_TIMEOUT, 0,
+                         &timeout);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+              CONFDB_KCM_KRB5_AUTH_TIMEOUT, ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = confdb_get_string(cdb, tmp_ctx, cpath,
+                            CONFDB_KCM_KRB5_RENEW_INTERVAL, NULL,
+                            &renew_intv);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+              CONFDB_KCM_KRB5_AUTH_TIMEOUT, ret, sss_strerror(ret));
+        goto done;
+    }
+
+
+    *_lifetime = talloc_steal(mem_ctx, lifetime);
+    *_rtime = talloc_steal(mem_ctx, rtime);
+    *_validate = validate;
+    *_canonicalize = canonicalize;
+    *_timeout = timeout;
+    *_renew_intv = renew_intv;
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+int kcm_get_renewal_config(struct kcm_ctx *kctx,
+                           struct krb5_ctx **_krb5_ctx,
+                           time_t *_renew_intv)
+{
+    int ret;
+    struct krb5_ctx *krb5_ctx;
+    char *lifetime;
+    char *rtime;
+    bool validate;
+    bool canonicalize;
+    int timeout;
+    char *renew_intv;
+    time_t renew_intv_tm;
+    bool tgt_renewal;
+    char *tgt_renewal_inherit;
+    const char *conf_path;
+    int i;
+
+    krb5_ctx = talloc_zero(kctx->rctx, struct krb5_ctx);
+    if (krb5_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "fatal error allocating krb5_ctx\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Set default Kerberos options */
+    krb5_ctx->opts = talloc_zero_array(krb5_ctx, struct dp_option, KRB5_OPTS);
+    if (krb5_ctx->opts == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "fatal error allocating krb5_ctx opts\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < KRB5_OPTS; i++) {
+        krb5_ctx->opts[i].opt_name = default_krb5_opts[i].opt_name;
+        krb5_ctx->opts[i].type = default_krb5_opts[i].type;
+        krb5_ctx->opts[i].def_val = default_krb5_opts[i].def_val;
+        switch (krb5_ctx->opts[i].type) {
+            case DP_OPT_STRING:
+                ret = dp_opt_set_string(krb5_ctx->opts, i,
+                                        default_krb5_opts[i].def_val.string);
+                break;
+            case DP_OPT_BLOB:
+                ret = dp_opt_set_blob(krb5_ctx->opts, i,
+                                      default_krb5_opts[i].def_val.blob);
+                break;
+            case DP_OPT_NUMBER:
+                ret = dp_opt_set_int(krb5_ctx->opts, i,
+                                     default_krb5_opts[i].def_val.number);
+                break;
+            case DP_OPT_BOOL:
+                ret = dp_opt_set_bool(krb5_ctx->opts, i,
+                                      default_krb5_opts[i].def_val.boolean);
+                break;
+        }
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed setting default KCM kerberos "
+                                     "options\n");
+            talloc_free(krb5_ctx->opts);
+            goto done;
+        }
+    }
+
+    ret = confdb_get_bool(kctx->rctx->cdb,
+                          kctx->rctx->confdb_service_path,
+                          CONFDB_KCM_TGT_RENEWAL, false,
+                          &tgt_renewal);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot get KCM TGT Renewal\n");
+        goto done;
+    }
+
+    if (tgt_renewal == false) {
+        goto done;
+    }
+
+    ret = confdb_get_string(kctx->rctx->cdb,
+                            kctx->rctx,
+                            kctx->rctx->confdb_service_path,
+                            CONFDB_KCM_TGT_RENEWAL_INHERIT,
+                            NULL,
+                            &tgt_renewal_inherit);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot get KCM TGT Renewal Inherit\n");
+        goto done;
+    }
+
+    /* Override with config options */
+    if (tgt_renewal_inherit == NULL) {
+        ret = kcm_read_options(kctx, kctx->rctx->cdb, kctx->rctx->confdb_service_path,
+                               &lifetime, &rtime, &validate, &canonicalize,
+                               &timeout, &renew_intv);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to read KCM renewal options\n");
+            goto done;
+        }
+    } else {
+        conf_path = talloc_asprintf(kctx->rctx, CONFDB_DOMAIN_PATH_TMPL,
+                                    tgt_renewal_inherit);
+        if (conf_path == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC, "Inherit KCM options for domain [%s]\n", conf_path);
+        ret = kcm_read_options(kctx, kctx->rctx->cdb, conf_path,
+                               &lifetime, &rtime, &validate, &canonicalize,
+                               &timeout, &renew_intv);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed reading domain inherit renewal options\n");
+            goto done;
+        }
+    }
+
+    ret = kcm_set_options(krb5_ctx, lifetime, rtime, validate, canonicalize,
+                          timeout, renew_intv, &renew_intv_tm);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed setting KCM renewal options\n");
+        goto done;
+    }
+
+    *_renew_intv = renew_intv_tm;
+    *_krb5_ctx = krb5_ctx;
+    ret = EOK;
+done:
+    if (ret != EOK) {
+        talloc_free(krb5_ctx);
+    }
+    return ret;
+}
+
+
diff --git a/src/responder/kcm/kcm_renew.h b/src/responder/kcm/kcm_renew.h
new file mode 100644
index 0000000000..0bb1bf8d5c
--- /dev/null
+++ b/src/responder/kcm/kcm_renew.h
@@ -0,0 +1,3 @@
+int kcm_get_renewal_config(struct kcm_ctx *kctx,
+                           struct krb5_ctx **_krb5_ctx);
+

From 5617633fbf5ff30b95a5f613f8c6e6ced85b1ad4 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Mon, 3 May 2021 18:21:37 +0000
Subject: [PATCH 2/7] KCM: Prepare and execute renewals

Find and unmarshal renewable tickets in the list of KCM ccaches, process
and trigger renewals for tgts aftert half of their lifetime is exceeded.
---
 Makefile.am                              |   3 +
 src/providers/krb5/krb5_child_handler.c  |  12 +-
 src/responder/kcm/kcm.c                  |   2 +-
 src/responder/kcm/kcm_renew.c            | 428 +++++++++++++++++++++++
 src/responder/kcm/kcm_renew.h            |  51 ++-
 src/responder/kcm/kcmsrv_ccache.c        | 124 +++++++
 src/responder/kcm/kcmsrv_ccache.h        |  16 +
 src/responder/kcm/kcmsrv_ccache_be.h     |   9 +
 src/responder/kcm/kcmsrv_ccache_binary.c |   2 +-
 src/responder/kcm/kcmsrv_ccache_secdb.c  | 172 +++++++++
 src/util/secrets/secrets.c               |  89 +++++
 src/util/secrets/secrets.h               |   7 +
 src/util/sss_krb5.c                      |  10 +
 src/util/sss_krb5.h                      |   5 +
 14 files changed, 919 insertions(+), 11 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index bfc50f1e69..d23fb9473a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -766,6 +766,7 @@ dist_noinst_HEADERS = \
     src/responder/secrets/secsrv_private.h \
     src/responder/secrets/secsrv_local.h \
     src/responder/secrets/secsrv_proxy.h \
+    src/responder/kcm/kcm_renew.h \
     src/responder/kcm/kcmsrv_pvt.h \
     src/responder/kcm/kcmsrv_ccache.h \
     src/responder/kcm/kcmsrv_ccache_pvt.h \
@@ -1789,6 +1790,7 @@ endif
 if BUILD_KCM
 sssd_kcm_SOURCES = \
     src/responder/kcm/kcm.c \
+    src/responder/kcm/kcm_renew.c \
     src/responder/kcm/kcmsrv_cmd.c \
     src/responder/kcm/kcmsrv_ccache.c \
     src/responder/kcm/kcmsrv_ccache_binary.c \
@@ -1799,6 +1801,7 @@ sssd_kcm_SOURCES = \
     src/responder/kcm/kcmsrv_ops.c \
     src/responder/kcm/kcmsrv_op_queue.c \
     src/providers/krb5/krb5_opts.c \
+    src/providers/krb5/krb5_child_handler.c \
     src/providers/data_provider_opts.c \
     src/util/sss_sockets.c \
     src/util/sss_krb5.c \
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 155ae3c3c9..f601bb7b8e 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -113,7 +113,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     uint32_t validate;
     uint32_t send_pac;
     uint32_t use_enterprise_principal;
-    uint32_t posix_domain;
+    uint32_t posix_domain = 0;
     size_t username_len = 0;
     errno_t ret;
 
@@ -138,14 +138,10 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
             break;
     }
 
-    switch (kr->dom->type) {
-    case DOM_TYPE_POSIX:
+    /* Renewals from KCM do not initialize kr->dom  */
+    if (kr->pd->cmd == SSS_CMD_RENEW || kr->dom->type == DOM_TYPE_POSIX) {
         posix_domain = 1;
-        break;
-    case DOM_TYPE_APPLICATION:
-        posix_domain = 0;
-        break;
-    default:
+    } else if (kr->dom->type != DOM_TYPE_APPLICATION) {
         return EINVAL;
     }
 
diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
index 609ca1220a..754d1dd197 100644
--- a/src/responder/kcm/kcm.c
+++ b/src/responder/kcm/kcm.c
@@ -71,7 +71,7 @@ static errno_t kcm_renewals_init(struct tevent_context *ev,
     if (renew_intv > 0) {
         *_renewal_enabled = true;
 
-        ret = kcm_renewals_setup(rctx, krb5_ctx, ev, kctx->kcm_data->db, renew_intv);
+        ret = kcm_renewal_setup(rctx, krb5_ctx, ev, kctx->kcm_data->db, renew_intv);
         if (ret != EOK) {
             DEBUG(SSSDBG_FATAL_FAILURE,
                   "fatal error initializing KCM renewals\n");
diff --git a/src/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c
index 2a9bda8312..6f8132ebab 100644
--- a/src/responder/kcm/kcm_renew.c
+++ b/src/responder/kcm/kcm_renew.c
@@ -1,3 +1,36 @@
+/*
+    SSSD
+
+    KCM Kerberos renewals -- Renew a TGT automatically
+
+    Authors:
+        Justin Stephenson <jstep...@redhat.com>
+
+    Copyright (C) 2020 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "util/util.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_auth.h"
+#include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_ccache.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcm_renew.h"
+
 extern struct dp_option default_krb5_opts[];
 
 struct kcm_renew_tgt_ctx {
@@ -313,4 +346,399 @@ int kcm_get_renewal_config(struct kcm_ctx *kctx,
     return ret;
 }
 
+static errno_t kcm_child_req_setup(TALLOC_CTX *mem_ctx,
+                                   struct auth_data *auth_data,
+                                   struct krb5_ctx *krb5_ctx,
+                                   struct krb5child_req **_req)
+{
+    struct krb5child_req *krreq;
+    const char *kcm_ccname = NULL;
+    errno_t ret;
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Setup for renewal of [%s] " \
+                                 "for principal name [%s]\n",
+                                 auth_data->upn,
+                                 auth_data->ccname);
+
+    krreq = talloc_zero(mem_ctx, struct krb5child_req);
+    if (krreq == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "krreq talloc_zero failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
 
+    krreq->krb5_ctx = krb5_ctx;
+    if (krreq->krb5_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to create allocate krb5_ctx\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    /* Set uid and gid */
+    krreq->uid = auth_data->uid;
+    krreq->gid = auth_data->gid;
+
+    kcm_ccname = talloc_asprintf(mem_ctx, "KCM:%s",
+                                 auth_data->ccname);
+    if (kcm_ccname == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to strdup ccname\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    krreq->upn = talloc_strdup(mem_ctx, auth_data->upn);
+    if (krreq->upn == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to strdup upn");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    krreq->ccname = kcm_ccname;
+    if (krreq->ccname == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to strdup ccname");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+   /* Set PAM Data */
+    krreq->pd = create_pam_data(krreq);
+    if (krreq->pd == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "create_pam_data failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    krreq->pd->cmd = SSS_CMD_RENEW;
+    krreq->pd->user = talloc_strdup(mem_ctx, auth_data->upn);
+    if (krreq->pd->user == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "talloc_strdup key failed\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    /* Set authtok values */
+    sss_authtok_set_empty(krreq->pd->newauthtok);
+
+    ret = sss_authtok_set_ccfile(krreq->pd->authtok, kcm_ccname, 0);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Failed setting authtok ccname\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    krreq->old_ccname = kcm_ccname;
+    if (krreq->old_ccname == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Failed to strdup ccname");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    *_req = krreq;
+    return EOK;
+fail:
+    talloc_zfree(krreq);
+    return ret;
+}
+
+static void kcm_renew_tgt(struct tevent_context *ev,
+                          struct tevent_immediate *imm,
+                          void *private_data)
+{
+    struct auth_data *auth_data = talloc_get_type(private_data,
+                                                  struct auth_data);
+    struct tevent_req *req;
+    struct kcm_renew_tgt_ctx *ctx = NULL;
+    errno_t ret;
+
+    ctx = talloc_zero(auth_data, struct kcm_renew_tgt_ctx);
+    if (ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "ctx talloc_zero failed\n");
+        return;
+    }
+
+    ret = kcm_child_req_setup(auth_data, auth_data,
+                              auth_data->krb5_ctx, &ctx->kr);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Cannot setup krchild request\n");
+        talloc_free(auth_data);
+        return;
+    }
+
+    req = handle_child_send(ctx, ev, ctx->kr);
+    if (req == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Cannot create child request\n");
+        talloc_free(auth_data);
+        return;
+    }
+
+    tevent_req_set_callback(req, kcm_renew_tgt_done, ctx);
+
+    return;
+}
+
+static void kcm_renew_tgt_done(struct tevent_req *req)
+{
+    struct kcm_renew_tgt_ctx *ctx = tevent_req_callback_data(req,
+                                    struct kcm_renew_tgt_ctx);
+    int ret;
+    struct krb5_child_response *res;
+
+    ret = handle_child_recv(req, ctx, &ctx->buf, &ctx->len);
+    talloc_free(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "handle_child_recv failure\n");
+        goto done;
+    }
+    ret = parse_krb5_child_response(ctx, ctx->buf, ctx->len, ctx->kr->pd,
+                                    0, &res);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Krb5 child returned an error! Please " \
+                                 "inspect the krb5_child.log file\n");
+        goto done;
+    }
+    if (res->msg_status != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Renewal failed - krb5_child [%d]\n",
+                                 res->msg_status);
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Successfully renewed [%s]\n", res->ccname);
+done:
+    talloc_zfree(ctx);
+    return;
+}
+
+static errno_t kcm_creds_check_times(TALLOC_CTX *mem_ctx,
+                                     struct renew_tgt_ctx *renew_tgt_ctx,
+                                     krb5_creds *creds,
+                                     struct kcm_ccache *cc,
+                                     const char *client_name)
+{
+    struct tgt_times tgtt;
+    time_t now;
+    time_t start_renew;
+    struct auth_data *auth_data;
+    struct tevent_immediate *imm;
+    int ret;
+
+    memset(&tgtt, 0, sizeof(tgtt));
+    tgtt.authtime = creds->times.authtime;
+    tgtt.starttime = creds->times.starttime;
+    tgtt.endtime = creds->times.endtime;
+    tgtt.renew_till = creds->times.renew_till;
+
+    now = time(NULL);
+    /* Attempt renewal only after half of the ticket lifetime has exceeded */
+    start_renew = (time_t) (tgtt.starttime + 0.5 * (tgtt.endtime - tgtt.starttime));
+    if (tgtt.renew_till >= tgtt.endtime && tgtt.renew_till >= now
+        && tgtt.endtime >= now && start_renew <= now) {
+            DEBUG(SSSDBG_TRACE_INTERNAL, "Renewal cred ready!\n");
+            auth_data = talloc_zero(renew_tgt_ctx, struct auth_data);
+            if (auth_data == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+                ret = ENOMEM;
+                goto done;
+            }
+
+            auth_data->krb5_ctx = renew_tgt_ctx->krb5_ctx;
+            auth_data->upn = talloc_strdup(auth_data, client_name);
+            auth_data->uid = cc->owner.uid;
+            auth_data->gid = cc->owner.gid;
+            auth_data->ccname = cc->name;
+            if (auth_data->upn == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
+                ret = ENOMEM;
+                goto done;
+            }
+
+            imm = tevent_create_immediate(auth_data);
+            if (imm == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed.\n");
+                ret = ENOMEM;
+                goto done;
+            }
+
+            tevent_schedule_immediate(imm, renew_tgt_ctx->ev, kcm_renew_tgt,
+                                      auth_data);
+        } else {
+            DEBUG(SSSDBG_TRACE_INTERNAL, "Time not applicable\n");
+        }
+
+    ret = EOK;
+done:
+    return ret;
+}
+
+errno_t kcm_renew_all_tgts(TALLOC_CTX *mem_ctx,
+                                  struct renew_tgt_ctx *renew_tgt_ctx,
+                                  struct kcm_ccache **cc_list)
+{
+    TALLOC_CTX *tmp_ctx;
+    size_t count = 0;
+    int ret;
+    struct kcm_ccache *cc;
+    char *client_name;
+    krb5_context krb_context;
+    krb5_creds **extracted_creds;
+    krb5_error_code kerr;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create tmp talloc_ctx\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (cc_list == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    kerr = krb5_init_context(&krb_context);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init context\n");
+        goto done;
+    }
+
+    count = talloc_array_length(cc_list);
+    if (count <= 1) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No renewal entries found.\n");
+        ret = EOK;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Found [%lu] renewal entries.\n", count - 1);
+    for (int i = 0; i < count - 1; i++) {
+        cc = cc_list[i];
+        DEBUG(SSSDBG_TRACE_FUNC,
+          "Checking ccache [%s] for creds to renew\n", cc->name);
+
+        extracted_creds = kcm_cc_unmarshal(mem_ctx, krb_context, cc);
+        if (extracted_creds == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Failed unmarshalling creds\n");
+            goto done;
+        }
+
+        for (int j = 0; extracted_creds[j] != NULL; j++) {
+            kerr = krb5_unparse_name(mem_ctx, extracted_creds[j]->client,
+                                     &client_name);
+            if (kerr != 0) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "Failed unparsing name\n");
+                goto done;
+            }
+
+            kcm_creds_check_times(mem_ctx, renew_tgt_ctx, extracted_creds[j],
+                                  cc, client_name);
+        }
+    }
+
+    ret = EOK;
+done:
+    if (tmp_ctx != NULL) {
+        talloc_free(tmp_ctx);
+    }
+    krb5_free_context(krb_context);
+    return ret;
+}
+
+static void kcm_renew_tgt_timer_handler(struct tevent_context *ev,
+                                        struct tevent_timer *te,
+                                        struct timeval current_time,
+										void *data)
+{
+    struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(data,
+                                                          struct renew_tgt_ctx);
+    errno_t ret;
+    struct timeval next;
+    struct kcm_ccache **cc_list;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failure in tmp_ctx talloc_new\n");
+        return;
+    }
+
+    /* forget the timer event, it will be freed by the tevent timer loop */
+    renew_tgt_ctx->te = NULL;
+
+    /* Reschedule timer */
+    next = tevent_timeval_current_ofs(renew_tgt_ctx->timer_interval, 0);
+    renew_tgt_ctx->te = tevent_add_timer(ev, renew_tgt_ctx,
+                                         next, kcm_renew_tgt_timer_handler,
+                                         renew_tgt_ctx);
+    if (renew_tgt_ctx->te == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup timer\n");
+        talloc_zfree(renew_tgt_ctx);
+        return;
+    }
+
+	/* Prepare KCM ccache list for renewals */
+	ret = kcm_ccdb_renew_tgts(renew_tgt_ctx->rctx, renew_tgt_ctx->krb5_ctx,
+                              ev, renew_tgt_ctx->db, &cc_list);
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_ALL, "No ccache renewal entries to prepare.\n");
+        ret = EOK;
+        return;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add KCM tickets to table.\n");
+        talloc_zfree(renew_tgt_ctx);
+        return;
+    }
+
+    ret = kcm_renew_all_tgts(tmp_ctx, renew_tgt_ctx, cc_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to attempt renewal of KCM ticket"
+                                   " table.\n");
+        talloc_zfree(renew_tgt_ctx);
+        return;
+    }
+
+    talloc_free(tmp_ctx);
+}
+
+errno_t kcm_renewal_setup(struct resp_ctx *rctx,
+                          struct krb5_ctx *krb5_ctx,
+                          struct tevent_context *ev,
+                          struct kcm_ccdb *db,
+                          time_t renew_intv)
+{
+    int ret;
+    struct timeval next;
+
+    krb5_ctx->renew_tgt_ctx = talloc_zero(krb5_ctx, struct renew_tgt_ctx);
+    if (krb5_ctx->renew_tgt_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+        return ENOMEM;
+    }
+
+    krb5_ctx->renew_tgt_ctx->rctx = rctx;
+    krb5_ctx->renew_tgt_ctx->krb5_ctx = krb5_ctx;
+    krb5_ctx->renew_tgt_ctx->db = db,
+    krb5_ctx->renew_tgt_ctx->ev = ev;
+    krb5_ctx->renew_tgt_ctx->timer_interval = renew_intv;
+
+    /* Check KCM for tickets to renew */
+    next = tevent_timeval_current_ofs(krb5_ctx->renew_tgt_ctx->timer_interval,
+                                      0);
+    krb5_ctx->renew_tgt_ctx->te = tevent_add_timer(ev, krb5_ctx->renew_tgt_ctx,
+                                                   next,
+                                                   kcm_renew_tgt_timer_handler,
+                                                   krb5_ctx->renew_tgt_ctx);
+    if (krb5_ctx->renew_tgt_ctx->te == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    return EOK;
+
+fail:
+    talloc_zfree(krb5_ctx->renew_tgt_ctx);
+    return ret;
+}
diff --git a/src/responder/kcm/kcm_renew.h b/src/responder/kcm/kcm_renew.h
index 0bb1bf8d5c..518221e547 100644
--- a/src/responder/kcm/kcm_renew.h
+++ b/src/responder/kcm/kcm_renew.h
@@ -1,3 +1,52 @@
+/*
+    SSSD
+
+    KCM Renewal, private header file
+
+    Authors:
+        Justin Stephenson <jstep...@redhat.com>
+
+    Copyright (C) 2020 Red Hat
+
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __KCM_RENEW_H__
+#define __KCM_RENEW_H__
+
+#include "providers/krb5/krb5_common.h"
+#include "src/providers/krb5/krb5_ccache.h"
+#include "util/sss_ptr_hash.h"
+
+struct renew_tgt_ctx {
+    struct kcm_ccache **cc_list;
+    struct tevent_context *ev;
+    struct krb5_ctx *krb5_ctx;
+    struct resp_ctx *rctx;
+    struct kcm_ccdb *db;
+    time_t timer_interval;
+    struct tevent_timer *te;
+};
+
+
 int kcm_get_renewal_config(struct kcm_ctx *kctx,
-                           struct krb5_ctx **_krb5_ctx);
+                           struct krb5_ctx **_krb5_ctx,
+                           time_t *_renew_intv);
+
+errno_t kcm_renewal_setup(struct resp_ctx *rctx, struct krb5_ctx *kctx,
+                         struct tevent_context *ev, struct kcm_ccdb *db,
+                         time_t renew_intv);
 
+#endif /* __KCM_RENEW_H__ */
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
index 60eacd4516..ea250c4af0 100644
--- a/src/responder/kcm/kcmsrv_ccache.c
+++ b/src/responder/kcm/kcmsrv_ccache.c
@@ -24,10 +24,13 @@
 #include "util/crypto/sss_crypto.h"
 #include "util/util.h"
 #include "util/sss_krb5.h"
+#include "src/providers/krb5/krb5_ccache.h"
+#include "responder/kcm/kcm_renew.h"
 #include "responder/kcm/kcmsrv_ccache.h"
 #include "responder/kcm/kcmsrv_ccache_pvt.h"
 #include "responder/kcm/kcmsrv_ccache_be.h"
 
+
 static struct kcm_cred *kcm_cred_dup(TALLOC_CTX *mem_ctx,
                                      struct kcm_cred *crd);
 
@@ -279,6 +282,85 @@ errno_t kcm_cc_set_header(struct kcm_ccache *cc,
     return EOK;
 }
 
+struct kcm_unmarshall_ctx {
+    krb5_creds **creds;
+    krb5_context krb_ctx;
+};
+
+static int kcm_cc_unmarshall_destructor(struct kcm_unmarshall_ctx *kcm_uctx)
+{
+    int i;
+
+    for (i = 0; kcm_uctx->creds[i] != NULL; i++) {
+        krb5_free_creds(kcm_uctx->krb_ctx, kcm_uctx->creds[i]);
+    }
+
+    return 0;
+}
+
+krb5_creds **kcm_cc_unmarshal(TALLOC_CTX *mem_ctx,
+                              krb5_context krb_context,
+                              struct kcm_ccache *cc)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct kcm_cred *cred;
+    krb5_data *cred_data;
+    krb5_creds **cred_list;
+    errno_t ret;
+    krb5_error_code kerr;
+    struct kcm_unmarshall_ctx *kcm_uctx;
+    int i = 0;
+    int count = 0;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        goto done;
+    }
+
+    kcm_uctx = talloc_zero(tmp_ctx, struct kcm_unmarshall_ctx);
+    if (kcm_uctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to talloc kcm_uctx\n");
+        goto done;
+    }
+
+    for (cred = kcm_cc_get_cred(cc); cred != NULL; cred = kcm_cc_next_cred(cred)) {
+        count++;
+    }
+
+    cred_list = talloc_zero_array(tmp_ctx, krb5_creds *, count + 1);
+
+    for (cred = kcm_cc_get_cred(cc); cred != NULL; cred = kcm_cc_next_cred(cred), i++) {
+        cred_data = talloc_zero(mem_ctx, struct _krb5_data);
+        if (cred_data == NULL) {
+            goto done;
+        }
+        ret = get_krb5_data_from_cred(tmp_ctx, cred->cred_blob, cred_data);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to convert to krb5_data\n");
+            goto done;
+        }
+
+        kerr = krb5_unmarshal_credentials(krb_context, cred_data,
+                                          &cred_list[i]);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unmarshal creds\n");
+            goto done;
+        }
+    }
+
+    cred_list[count] = NULL;
+    kcm_uctx->krb_ctx = krb_context;
+    kcm_uctx->creds = cred_list;
+    talloc_set_destructor(kcm_uctx, kcm_cc_unmarshall_destructor);
+
+    talloc_steal(mem_ctx, cred_list);
+
+    return cred_list;
+done:
+    talloc_free(tmp_ctx);
+    return NULL;
+}
+
 errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid)
 {
     if (crd == NULL) {
@@ -293,6 +375,48 @@ struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd)
     return crd ? crd->cred_blob : NULL;
 }
 
+errno_t kcm_ccdb_renew_tgts(TALLOC_CTX *mem_ctx,
+                            struct krb5_ctx *krb5_ctx,
+                            struct tevent_context *ev,
+                            struct kcm_ccdb *ccdb,
+                            struct kcm_ccache ***_cc_list)
+{
+	TALLOC_CTX *tmp_ctx;
+    struct kcm_ccache **cc;
+    errno_t ret;
+
+	tmp_ctx = talloc_new(NULL);
+	if (tmp_ctx == NULL) {
+		return ENOMEM;
+	}
+
+    if (krb5_ctx == NULL || ev == NULL || ccdb == NULL) {
+        ret = EINVAL;
+        return ret;
+    }
+
+    if (ccdb->ops->list_all_cc == NULL) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, "List all cc function not available\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = ccdb->ops->list_all_cc(tmp_ctx, krb5_ctx, ev, ccdb, &cc);
+    if (ret != EOK && ret != ENOENT) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failure to execute ccdb renewal init\n");
+        return ret;
+    } else if (ret == ENOENT) {
+        goto done;
+    }
+
+    *_cc_list = talloc_steal(mem_ctx, cc);
+
+    ret = EOK;
+done:
+
+    return ret;
+}
+
 struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
                                struct tevent_context *ev,
                                struct confdb_ctx *cdb,
diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
index 77cf8f61d5..e3d5cbbe46 100644
--- a/src/responder/kcm/kcmsrv_ccache.h
+++ b/src/responder/kcm/kcmsrv_ccache.h
@@ -29,6 +29,7 @@
 #include "util/util.h"
 #include "util/sss_iobuf.h"
 #include "util/util_creds.h"
+#include "providers/krb5/krb5_common.h"
 #include "responder/kcm/kcmsrv_pvt.h"
 
 #define UUID_BYTES    16
@@ -112,6 +113,10 @@ errno_t kcm_cc_set_header(struct kcm_ccache *cc,
                           const char *sec_key,
                           struct cli_creds *client);
 
+krb5_creds **kcm_cc_unmarshal(TALLOC_CTX *mem_ctx,
+                              krb5_context krb_context,
+                              struct kcm_ccache *cc);
+
 errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid);
 
 /*
@@ -140,6 +145,14 @@ struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
                                struct confdb_ctx *cdb,
                                const char *confdb_service_path,
                                enum kcm_ccdb_be cc_be);
+/*
+ * Prepare KCM ccache list for renewals
+ */
+errno_t kcm_ccdb_renew_tgts(TALLOC_CTX *mem_ctx,
+                            struct krb5_ctx *kctx,
+                            struct tevent_context *ev,
+                            struct kcm_ccdb *cdb,
+                            struct kcm_ccache ***_cc_list);
 
 /*
  * In KCM, each ccache name is usually in the form of "UID:<num>
@@ -376,4 +389,7 @@ errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx,
                                        struct kcm_ccache *cc,
                                        struct sss_iobuf **_payload);
 
+errno_t bin_to_krb_data(TALLOC_CTX *mem_ctx,
+                        struct sss_iobuf *buf,
+                        krb5_data *out);
 #endif /* _KCMSRV_CCACHE_H_ */
diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h
index 166af3a764..b7ea5e2454 100644
--- a/src/responder/kcm/kcmsrv_ccache_be.h
+++ b/src/responder/kcm/kcmsrv_ccache_be.h
@@ -62,6 +62,13 @@ typedef errno_t
                             uuid_t dfl);
 
 
+typedef errno_t
+(*ccdb_list_all_cc_fn)(TALLOC_CTX *mem_ctx,
+                       struct krb5_ctx *kctx,
+                       struct tevent_context *ev,
+                       struct kcm_ccdb *cdb,
+                       struct kcm_ccache ***_cc_list);
+
 typedef struct tevent_req *
 (*ccdb_list_send_fn)(TALLOC_CTX *mem_ctx,
                      struct tevent_context *ev,
@@ -173,6 +180,8 @@ struct kcm_ccdb_ops {
     ccdb_get_default_send_fn get_default_send;
     ccdb_get_default_recv_fn get_default_recv;
 
+    ccdb_list_all_cc_fn list_all_cc;
+
     ccdb_list_send_fn list_send;
     ccdb_list_recv_fn list_recv;
 
diff --git a/src/responder/kcm/kcmsrv_ccache_binary.c b/src/responder/kcm/kcmsrv_ccache_binary.c
index 7bfdbf13bf..d2d5d2fa1e 100644
--- a/src/responder/kcm/kcmsrv_ccache_binary.c
+++ b/src/responder/kcm/kcmsrv_ccache_binary.c
@@ -141,7 +141,7 @@ errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
-static errno_t bin_to_krb_data(TALLOC_CTX *mem_ctx,
+errno_t bin_to_krb_data(TALLOC_CTX *mem_ctx,
                                struct sss_iobuf *buf,
                                krb5_data *out)
 {
diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c
index ea5c8f9ee3..3c44f57bb8 100644
--- a/src/responder/kcm/kcmsrv_ccache_secdb.c
+++ b/src/responder/kcm/kcmsrv_ccache_secdb.c
@@ -27,8 +27,12 @@
 #include "util/util.h"
 #include "util/secrets/secrets.h"
 #include "util/crypto/sss_crypto.h"
+#include "util/sss_krb5.h"
+#include "util/strtonum.h"
 #include "responder/kcm/kcmsrv_ccache_pvt.h"
 #include "responder/kcm/kcmsrv_ccache_be.h"
+#include "responder/kcm/kcm_renew.h"
+#include "providers/krb5/krb5_ccache.h"
 
 #define KCM_SECDB_URL        "/kcm/persistent"
 #define KCM_SECDB_BASE_FMT    KCM_SECDB_URL"/%"SPRIuid"/"
@@ -818,10 +822,176 @@ static errno_t ccdb_secdb_get_default_recv(struct tevent_req *req,
     return EOK;
 }
 
+static errno_t ccdb_secdb_get_cc_for_uuid(size_t uuid_list_count,
+                                          const char **uuid_list,
+                                          const char **uid_list,
+                                          struct ccdb_secdb *secdb,
+                                          struct kcm_ccache **cc_list,
+                                          int *_new_count)
+{
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    uid_t uid;
+    char **list;
+    uuid_t uuid;
+    char *uuid_str;
+    char *secdb_key;
+    struct cli_creds *cli_cred;
+    int real_count = 0;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        return ret;
+    }
+
+    cli_cred = talloc_zero(tmp_ctx, struct cli_creds);
+    if (cli_cred == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (size_t i = 0; i < uuid_list_count; i++) {
+        struct passwd *pwd;
+
+        cc_list[i] = talloc_zero(cc_list, struct kcm_ccache);
+        if (cc_list[i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = split_on_separator(tmp_ctx, uuid_list[i], ':', true, true, &list, NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_TRACE_INTERNAL, "split on separator failed\n");
+            goto done;
+        }
+
+        uuid_str = list[0];
+        uuid_str[UUID_STR_SIZE - 1] = '\0';
+        ret = uuid_parse(uuid_str, uuid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_TRACE_INTERNAL, "uuid parse of [%s] failed\n", list[0]);
+            goto done;
+        }
+        uid = strtouint32(uid_list[i], NULL, 10);
+        uid = strtouint32(uid_list[i], NULL, 10);
+        ret = errno;
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID conversion\n");
+            goto done;
+        }
+
+        errno = 0;
+        pwd = getpwuid(uid);
+        if (pwd == NULL) {
+            ret = errno;
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to get pwd entry for "
+                  "[%d] [%d]: %s\n", uid, ret, sss_strerror(ret));
+            /* Not fatal */
+            continue;
+        }
+
+        cli_cred->ucred.uid = pwd->pw_uid;
+        cli_cred->ucred.gid = pwd->pw_gid;
+
+        ret = key_by_uuid(tmp_ctx, secdb->sctx, cli_cred, uuid, &secdb_key);
+        if (ret == ENOENT) {
+            ret = EOK;
+            goto done;
+        } else if (ret != EOK) {
+            goto done;
+        }
+
+        ret = secdb_get_cc(cc_list, secdb->sctx, secdb_key, cli_cred, &cc_list[real_count]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed in secdb_get_cc\n");
+            goto done;
+        }
+
+        if (cc_list[real_count] == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to get cc [%s] and [%s]\n",
+                                     uuid_list[i], uid_list[i]);
+            ret = EIO;
+            goto done;
+        }
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "Retrieved ccache [%s]\n", cc_list[real_count]->name);
+        real_count++;
+    }
+
+    cc_list = talloc_realloc(tmp_ctx, cc_list, struct kcm_ccache *,
+                             real_count + 1);
+    *_new_count = real_count;
+
+    return EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 struct ccdb_secdb_list_state {
     uuid_t *uuid_list;
 };
 
+static errno_t ccdb_secdb_list_all_cc(TALLOC_CTX *mem_ctx,
+                                      struct krb5_ctx *krb5_ctx,
+                                      struct tevent_context *ev,
+                                      struct kcm_ccdb *db,
+                                      struct kcm_ccache ***_cc_list)
+{
+    struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    const char **uid_list;
+    const char **uuid_list;
+    size_t uuid_list_count;
+    struct kcm_ccache **cc_list;
+    int new_count;
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Retrieving all ccaches\n");
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        return ret;
+    }
+
+    ret = sss_sec_list_cc_uuids(tmp_ctx, secdb->sctx, &uuid_list, &uid_list, &uuid_list_count);
+    if (ret != EOK && ret != ENOENT) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Error retrieving uid list\n");
+        goto done;
+    } else if (ret == ENOENT) {
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Found [%lu] ccache uuids\n", uuid_list_count);
+
+    cc_list = talloc_zero_array(tmp_ctx, struct kcm_ccache *, uuid_list_count + 1);
+    if (cc_list == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* New count is full cc list size minus getpwuid() failures */
+    ret = ccdb_secdb_get_cc_for_uuid(uuid_list_count, uuid_list, uid_list, secdb,
+                                     cc_list, &new_count);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Error retrieving cc for uuids list\n");
+        ret = EIO;
+        goto done;
+    }
+
+    cc_list[new_count] = NULL;
+    *_cc_list = talloc_steal(mem_ctx, cc_list);
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Retrieving all caches done\n");
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 static struct tevent_req *ccdb_secdb_list_send(TALLOC_CTX *mem_ctx,
                                                struct tevent_context *ev,
                                                struct kcm_ccdb *db,
@@ -1480,6 +1650,8 @@ const struct kcm_ccdb_ops ccdb_secdb_ops = {
     .list_send = ccdb_secdb_list_send,
     .list_recv = ccdb_secdb_list_recv,
 
+    .list_all_cc = ccdb_secdb_list_all_cc,
+
     .getbyname_send = ccdb_secdb_getbyname_send,
     .getbyname_recv = ccdb_secdb_getbyname_recv,
 
diff --git a/src/util/secrets/secrets.c b/src/util/secrets/secrets.c
index c6310b5852..e51a9436fa 100644
--- a/src/util/secrets/secrets.c
+++ b/src/util/secrets/secrets.c
@@ -22,10 +22,12 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <uuid/uuid.h>
 
 #include "config.h"
 
 #include "util/util.h"
+#include "util/strtonum.h"
 #include "util/crypto/sss_crypto.h"
 #include "util/secrets/sec_pvt.h"
 #include "util/secrets/secrets.h"
@@ -937,6 +939,93 @@ static char *local_dn_to_path(TALLOC_CTX *mem_ctx,
     return path;
 }
 
+/* Complete list of ccache names(UUID:name) */
+errno_t sss_sec_list_cc_uuids(TALLOC_CTX *mem_ctx,
+                              struct sss_sec_ctx *sec,
+                              const char ***_uuid_list,
+                              const char ***_uid_list,
+                              size_t *_uuid_list_count)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_result *res;
+    struct ldb_dn *dn;
+    const struct ldb_val *name_val;
+    const struct ldb_val *uid_val;
+    static const char *attrs[] = { "distinguishedName", NULL };
+    const char **uuid_list;
+    const char **uid_list;
+    size_t real_count = 0;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    dn = ldb_dn_new(tmp_ctx, sec->ldb, "cn=persistent,cn=kcm");
+
+    ret = ldb_search(sec->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+           attrs, "%s", "(!(type=container))");
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_LIBS,
+              "ldb_search returned [%d]: %s\n", ret, ldb_strerror(ret));
+        ret = EIO;
+        goto done;
+    }
+
+    if (res->count == 0) {
+        DEBUG(SSSDBG_TRACE_LIBS, "No ccaches found\n");
+        ret = ENOENT;
+        goto done;
+    }
+
+	uuid_list = talloc_zero_array(tmp_ctx, const char *, res->count);
+    if (uuid_list == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+	uid_list = talloc_zero_array(tmp_ctx, const char *, res->count);
+    if (uuid_list == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (int i = 0; i < res->count; i++) {
+        name_val = ldb_dn_get_component_val(res->msgs[i]->dn, 0);
+        uid_val = ldb_dn_get_component_val(res->msgs[i]->dn, 2);
+        if (strcmp((const char *)name_val->data, "default") == 0) {
+            continue;
+        }
+
+        DEBUG(SSSDBG_TRACE_INTERNAL, "uid_val: [%s]\n", uid_val->data);
+        uuid_list[real_count] = talloc_strdup(uuid_list, (const char *)name_val->data);
+        if (uuid_list[real_count] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to extract UUID\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        uid_list[real_count] = talloc_strdup(uid_list, (const char *)uid_val->data);
+        if (uid_list[real_count] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to extract UUID\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        real_count++;
+    }
+
+    *_uid_list = talloc_steal(mem_ctx, uid_list);
+    *_uuid_list = talloc_steal(mem_ctx, uuid_list);
+    *_uuid_list_count = real_count;
+
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 errno_t sss_sec_list(TALLOC_CTX *mem_ctx,
                      struct sss_sec_req *req,
                      char ***_keys,
diff --git a/src/util/secrets/secrets.h b/src/util/secrets/secrets.h
index f79bfaa4b9..b92dd50cfd 100644
--- a/src/util/secrets/secrets.h
+++ b/src/util/secrets/secrets.h
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <talloc.h>
+#include <uuid/uuid.h>
 
 #include "confdb/confdb.h"
 
@@ -87,6 +88,12 @@ errno_t sss_sec_new_req(TALLOC_CTX *mem_ctx,
 
 errno_t sss_sec_delete(struct sss_sec_req *req);
 
+errno_t sss_sec_list_cc_uuids(TALLOC_CTX *mem_ctx,
+                              struct sss_sec_ctx *sec_ctx,
+                              const char ***_uuid_list,
+                              const char ***_uid_list,
+                              size_t *uuid_list_count);
+
 errno_t sss_sec_list(TALLOC_CTX *mem_ctx,
                      struct sss_sec_req *req,
                      char ***_keys,
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index b44e8d9025..cf96f3ffc4 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -1211,6 +1211,16 @@ static errno_t iobuf_get_len_bytes(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
+errno_t get_krb5_data_from_cred(TALLOC_CTX *mem_ctx,
+                                struct sss_iobuf *iobuf,
+                                krb5_data *k5data)
+{
+    k5data->data = (char *) sss_iobuf_get_data(iobuf);
+    k5data->length = sss_iobuf_get_size(iobuf);
+
+    return EOK;
+}
+
 static errno_t get_krb5_data(TALLOC_CTX *mem_ctx,
                              struct sss_iobuf *iobuf,
                              krb5_data *k5data)
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index 4d9804fa88..aa1a258eb2 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -34,6 +34,7 @@
 
 #include "util/sss_iobuf.h"
 #include "util/util.h"
+#include <uuid/uuid.h>
 
 #define KRB5_CHILD_LOG_FILE     "krb5_child"
 #define LDAP_CHILD_LOG_FILE     "ldap_child"
@@ -198,4 +199,8 @@ krb5_error_code sss_krb5_unmarshal_princ(TALLOC_CTX *mem_ctx,
 
 krb5_error_code sss_krb5_init_context(krb5_context *context);
 
+errno_t get_krb5_data_from_cred(TALLOC_CTX *mem_ctx,
+                                struct sss_iobuf *iobuf,
+                                krb5_data *k5data);
+
 #endif /* __SSS_KRB5_H__ */

From a2f2ab76057b99664d5bc3a1a02b8948cfbe2c51 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Mon, 21 Dec 2020 14:45:17 -0500
Subject: [PATCH 3/7] SECRETS: Don't hardcode SECRETS_DB_PATH

Allow for overriding in cmocka tests
---
 src/util/secrets/secrets.c | 32 ++++++++++++++++++++++++++------
 src/util/secrets/secrets.h |  5 +++++
 2 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/util/secrets/secrets.c b/src/util/secrets/secrets.c
index e51a9436fa..8546d08104 100644
--- a/src/util/secrets/secrets.c
+++ b/src/util/secrets/secrets.c
@@ -634,12 +634,13 @@ static int generate_master_key(const char *filename, size_t size)
 }
 
 static errno_t lcl_read_mkey(TALLOC_CTX *mem_ctx,
+                             const char *dbpath,
                              struct sss_sec_data *master_key)
 {
     int mfd;
     ssize_t size;
     errno_t ret;
-    const char *mkey = SECRETS_DB_PATH"/.secrets.mkey";
+    const char *mkey = dbpath;
 
     master_key->data = talloc_size(mem_ctx, MKEY_SIZE);
     if (master_key->data == NULL) {
@@ -699,11 +700,11 @@ static int set_quotas(struct sss_sec_ctx *sec_ctx,
     return EOK;
 }
 
-errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
-                     struct sss_sec_hive_config **config_list,
-                     struct sss_sec_ctx **_sec_ctx)
+errno_t sss_sec_init_with_path(TALLOC_CTX *mem_ctx,
+                               struct sss_sec_hive_config **config_list,
+                               const char *dbpath,
+                               struct sss_sec_ctx **_sec_ctx)
 {
-    const char *dbpath = SECRETS_DB_PATH"/secrets.ldb";
     struct sss_sec_ctx *sec_ctx;
     TALLOC_CTX *tmp_ctx;
     errno_t ret;
@@ -745,7 +746,7 @@ errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = lcl_read_mkey(sec_ctx, &sec_ctx->master_key);
+    ret = lcl_read_mkey(sec_ctx, dbpath, &sec_ctx->master_key);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot get the master key\n");
         goto done;
@@ -758,6 +759,25 @@ errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
+errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
+                     struct sss_sec_hive_config **config_list,
+                     struct sss_sec_ctx **_sec_ctx)
+{
+    const char *dbpath = SECRETS_DB_PATH"/secrets.ldb";
+    errno_t ret;
+
+    ret = sss_sec_init_with_path(mem_ctx, config_list, dbpath, _sec_ctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize secdb\n");
+        ret = EIO;
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    return ret;
+}
+
 static int local_db_dn(TALLOC_CTX *mem_ctx,
                        struct ldb_context *ldb,
                        const char *basedn,
diff --git a/src/util/secrets/secrets.h b/src/util/secrets/secrets.h
index b92dd50cfd..a15b99ffec 100644
--- a/src/util/secrets/secrets.h
+++ b/src/util/secrets/secrets.h
@@ -80,6 +80,11 @@ errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
                      struct sss_sec_hive_config **config_list,
                      struct sss_sec_ctx **_sec_ctx);
 
+errno_t sss_sec_init_with_path(TALLOC_CTX *mem_ctx,
+                               struct sss_sec_hive_config **config_list,
+                               const char *dbpath,
+                               struct sss_sec_ctx **_sec_ctx);
+
 errno_t sss_sec_new_req(TALLOC_CTX *mem_ctx,
                         struct sss_sec_ctx *sec_ctx,
                         const char *url,

From fbb9b38d7ca140d2b659f29614640e53a307c532 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Fri, 29 Jan 2021 20:36:24 +0000
Subject: [PATCH 4/7] TESTS: Add kcm_renewals unit test

---
 Makefile.am                          |  35 ++++
 src/tests/cmocka/test_kcm_renewals.c | 268 +++++++++++++++++++++++++++
 2 files changed, 303 insertions(+)
 create mode 100644 src/tests/cmocka/test_kcm_renewals.c

diff --git a/Makefile.am b/Makefile.am
index d23fb9473a..cddd51b981 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -326,6 +326,7 @@ if BUILD_KCM
 non_interactive_cmocka_based_tests += \
 	test_kcm_marshalling \
 	test_kcm_queue \
+	test_kcm_renewals \
         $(NULL)
 endif   # BUILD_KCM
 
@@ -3931,6 +3932,40 @@ test_kcm_queue_LDADD = \
     libsss_sbus.la \
     $(NULL)
 
+test_kcm_renewals_SOURCES = \
+	$(TEST_MOCK_RESP_OBJ) \
+	src/tests/cmocka/test_kcm_renewals.c \
+	src/responder/kcm/kcm_renew.c \
+	src/responder/kcm/kcmsrv_ccache.c \
+	src/responder/kcm/kcmsrv_ccache_key.c \
+	src/responder/kcm/kcmsrv_ccache_binary.c \
+	src/responder/kcm/kcmsrv_ccache_json.c \
+	src/util/sss_krb5.c \
+	src/util/sss_iobuf.c \
+	src/util/secrets/secrets.c \
+	src/util/secrets/config.c \
+	src/providers/krb5/krb5_child_handler.c \
+	src/providers/data_provider_opts.c \
+	$(NULL)
+test_kcm_renewals_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(NULL)
+test_kcm_renewals_LDFLAGS = \
+	-Wl,-wrap,fstat
+test_kcm_renewals_LDADD = \
+	$(LIBADD_DL) \
+	$(UUID_LIBS) \
+	$(JANSSON_LIBS) \
+	$(KRB5_LIBS) \
+	$(CARES_LIBS) \
+	$(CMOCKA_LIBS) \
+	$(SSSD_LIBS) \
+	$(SSSD_INTERNAL_LTLIBS) \
+	libsss_test_common.la \
+	libsss_iface.la \
+	libsss_sbus.la \
+	$(NULL)
+
 endif # BUILD_KCM
 
 endif # HAVE_CMOCKA
diff --git a/src/tests/cmocka/test_kcm_renewals.c b/src/tests/cmocka/test_kcm_renewals.c
new file mode 100644
index 0000000000..b992dad3cf
--- /dev/null
+++ b/src/tests/cmocka/test_kcm_renewals.c
@@ -0,0 +1,268 @@
+/*
+    Copyright (C) 2020 Red Hat
+
+    SSSD tests: Test KCM Renewals
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <popt.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "util/util.h"
+#include "util/util_creds.h"
+#include "tests/cmocka/common_mock.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcm_renew.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_secdb.c"
+
+#define TESTS_PATH "tp_" BASE_FILE_STEM
+#define TEST_CONF_DB "test_kcm_renewals_conf.ldb"
+#define TEST_DB_FULL_PATH  TESTS_PATH "/secrets.ldb"
+
+errno_t kcm_renew_all_tgts(TALLOC_CTX *mem_ctx,
+                           struct renew_tgt_ctx *renew_tgt_ctx,
+                           struct kcm_ccache **cc_list);
+
+const struct kcm_ccdb_ops ccdb_mem_ops;
+const struct kcm_ccdb_ops ccdb_sec_ops;
+const struct kcm_ccdb_ops ccdb_secdb_ops;
+
+struct test_ctx {
+    struct krb5_ctx *krb5_ctx;
+    struct tevent_context *ev;
+    struct kcm_ccdb *ccdb;
+};
+
+/* register_cli_protocol_version is required in test since it links with
+ * responder_common.c module
+ */
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+    static struct cli_protocol_version responder_test_cli_protocol_version[] = {
+        { 0, NULL, NULL }
+    };
+
+    return responder_test_cli_protocol_version;
+}
+
+/* Wrap fstat() to ignore ownership check failure
+ * from lcl_read_mkey() -> check_and_open_readonly()
+ */
+int __real_fstat(int fd, struct stat *statbuf);
+
+int __wrap_fstat(int fd, struct stat *statbuf)
+{
+    int ret;
+
+    ret = __real_fstat(fd, statbuf);
+    if (ret == 0) {
+        statbuf->st_uid = 0;
+        statbuf->st_gid = 0;
+    }
+
+    return ret;
+}
+
+/* Override perform_checks and check_fd so that fstat wrap is called */
+static errno_t perform_checks(struct stat *stat_buf,
+                              uid_t uid, gid_t gid,
+                              mode_t mode, mode_t mask)
+{
+    mode_t st_mode;
+
+    if (mask) {
+        st_mode = stat_buf->st_mode & mask;
+    } else {
+        st_mode = stat_buf->st_mode & (S_IFMT|ALLPERMS);
+    }
+
+    if ((mode & S_IFMT) != (st_mode & S_IFMT)) {
+        DEBUG(SSSDBG_TRACE_LIBS, "File is not the right type.\n");
+        return EINVAL;
+    }
+
+    if ((st_mode & ALLPERMS) != (mode & ALLPERMS)) {
+        DEBUG(SSSDBG_TRACE_LIBS,
+              "File has the wrong (bit masked) mode [%.7o], "
+              "expected [%.7o].\n",
+              (st_mode & ALLPERMS), (mode & ALLPERMS));
+        return EINVAL;
+    }
+
+    if (uid != (uid_t)(-1) && stat_buf->st_uid != uid) {
+        DEBUG(SSSDBG_TRACE_LIBS, "File must be owned by uid [%d].\n", uid);
+        return EINVAL;
+    }
+
+    if (gid != (gid_t)(-1) && stat_buf->st_gid != gid) {
+        DEBUG(SSSDBG_TRACE_LIBS, "File must be owned by gid [%d].\n", gid);
+        return EINVAL;
+    }
+
+    return EOK;
+}
+
+errno_t check_fd(int fd, uid_t uid, gid_t gid,
+                 mode_t mode, mode_t mask,
+                 struct stat *caller_stat_buf)
+{
+    int ret;
+    struct stat local_stat_buf;
+    struct stat *stat_buf;
+
+    if (caller_stat_buf == NULL) {
+        stat_buf = &local_stat_buf;
+    } else {
+        stat_buf = caller_stat_buf;
+    }
+
+    ret = fstat(fd, stat_buf);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "fstat for [%d] failed: [%d][%s].\n", fd, ret,
+                                                        strerror(ret));
+        return ret;
+    }
+
+    return perform_checks(stat_buf, uid, gid, mode, mask);
+}
+
+
+static int setup_kcm_renewals(void **state)
+{
+    struct test_ctx *tctx;
+
+    tctx = talloc_zero(NULL, struct test_ctx);
+    assert_non_null(tctx);
+
+    tctx->ev = tevent_context_init(tctx);
+    assert_non_null(tctx->ev);
+
+    tctx->ccdb = talloc_zero(tctx, struct kcm_ccdb);
+    assert_non_null(tctx->ccdb);
+    tctx->ccdb->ev = tctx->ev;
+
+    tctx->ccdb->ops = &ccdb_secdb_ops;
+    assert_non_null(tctx->ccdb->ops);
+
+    *state = tctx;
+    return 0;
+}
+
+static int teardown_kcm_renewals(void **state)
+{
+    struct test_ctx *tctx = talloc_get_type(*state, struct test_ctx);
+
+    unlink(TEST_DB_FULL_PATH);
+
+    rmdir(TESTS_PATH);
+    talloc_free(tctx);
+    return 0;
+}
+
+static void test_kcm_renewals_tgt(void **state)
+{
+    struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx);
+    errno_t ret;
+    struct ccdb_secdb *secdb = NULL;
+    struct renew_tgt_ctx *renew_tgt_ctx = NULL;
+    struct kcm_ccache **cc_list;
+    struct kcm_ccache *cc;
+
+    secdb = talloc_zero(test_ctx, struct ccdb_secdb);
+
+    assert_non_null(secdb);
+
+    ret = mkdir(TESTS_PATH, 0700);
+    assert_int_equal(ret, 0);
+
+    open(TEST_DB_FULL_PATH, O_CREAT|O_EXCL|O_WRONLY, 0600);
+
+    ret = sss_sec_init_with_path(test_ctx->ccdb, NULL, TEST_DB_FULL_PATH,
+                                 &secdb->sctx);
+
+    /* Create renew ctx */
+    renew_tgt_ctx = talloc_zero(test_ctx, struct renew_tgt_ctx);
+    renew_tgt_ctx->ev = test_ctx->ev;
+
+    /* Create cc list */
+    cc_list = talloc_zero_array(test_ctx, struct kcm_ccache *, 2);
+    assert_non_null(cc_list);
+
+    cc = talloc_zero(cc_list, struct kcm_ccache);
+    assert_non_null(cc);
+
+    cc->name = talloc_strdup(test_ctx, "1000:1001");
+    cc->owner.uid = 1000;
+    cc->owner.gid = 1000;
+
+    cc_list[0] = cc;
+    cc_list[1] = NULL;
+
+    ret = kcm_renew_all_tgts(test_ctx, renew_tgt_ctx, cc_list);
+    assert_int_equal(ret, EOK);
+}
+
+int main(int argc, const char *argv[])
+{
+    poptContext pc;
+    int opt;
+    int rv;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_kcm_renewals_tgt,
+                                        setup_kcm_renewals,
+                                        teardown_kcm_renewals),
+    };
+
+    /* Set debug level to invalid value so we can decide if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    /* Even though normally the tests should clean up after themselves
+     * they might not after a failed run. Remove the old DB to be sure
+     */
+    tests_set_cwd();
+
+    rv = cmocka_run_group_tests(tests, NULL, NULL);
+
+    return rv;
+}

From 61d51222a75b93366950163b0b7c6f4388817537 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Mon, 21 Dec 2020 20:48:15 -0500
Subject: [PATCH 5/7] INTG: Add KCM Renewal integration test

---
 src/tests/intg/kdc.py       |  2 +
 src/tests/intg/krb5utils.py | 15 ++++++-
 src/tests/intg/test_kcm.py  | 78 +++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/src/tests/intg/kdc.py b/src/tests/intg/kdc.py
index 8f82a990bc..2f14261382 100644
--- a/src/tests/intg/kdc.py
+++ b/src/tests/intg/kdc.py
@@ -145,6 +145,8 @@ def _format_kdc_conf(self):
                 kadmind_port = {self.kadmin_port}
                 database_name = {database_path}
                 key_stash_file = {key_stash}
+                max_life = 7d
+                max_renewable_life = 14d
                 acl_file = {self.acl_file}
             }}
 
diff --git a/src/tests/intg/krb5utils.py b/src/tests/intg/krb5utils.py
index 67ae430069..3da4c6a91f 100644
--- a/src/tests/intg/krb5utils.py
+++ b/src/tests/intg/krb5utils.py
@@ -58,8 +58,10 @@ def _run_in_env(self, args, stdin=None, extra_env=None):
         out, err = cmd.communicate(stdin)
         return cmd.returncode, out.decode('utf-8'), err.decode('utf-8')
 
-    def kinit(self, principal, password, env=None):
+    def kinit(self, principal, password, options=None, env=None):
         args = ["kinit", principal]
+        if options:
+            args.extend(options)
         return self._run_in_env(args, password.encode('utf-8'), env)
 
     def kvno(self, principal, env=None):
@@ -115,6 +117,17 @@ def list_princs(self, env=None):
 
         return [ln for ln in outlines[2:] if len(ln) > 0]
 
+    def list_times(self, env=None):
+        p = self.spawn_in_env(['klist', '-A'])
+        output = p.stdout.read().splitlines()
+        for line in output:
+            if not line:
+                continue
+
+            line_str = line.decode("utf-8")
+            if line_str[0].isdigit():
+                return line_str
+
     def has_principal(self, exp_principal, exp_cache=None, env=None):
         try:
             princlist = self.list_princs(env)
diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py
index 3a43491b96..395803bef7 100644
--- a/src/tests/intg/test_kcm.py
+++ b/src/tests/intg/test_kcm.py
@@ -25,6 +25,7 @@
 import time
 import signal
 import sys
+from datetime import datetime
 from requests import HTTPError
 
 import kdc
@@ -33,9 +34,15 @@
 from util import unindent
 from test_secrets import create_sssd_secrets_fixture
 from secrets import SecretsLocalClient
+from intg.files_ops import passwd_ops_setup
 
 MAX_SECRETS = 10
 
+USER1 = dict(name='user1', passwd='x', uid=1000, gid=1000,
+             gecos='User for tests',
+             dir='/home/user1',
+             shell='/bin/bash')
+
 
 class KcmTestEnv(object):
     def __init__(self, k5kdc, k5util):
@@ -138,6 +145,30 @@ def create_sssd_conf(kcm_path, ccache_storage, max_secrets=MAX_SECRETS):
     """).format(**locals())
 
 
+def create_sssd_conf_renewals(kcm_path, ccache_storage, renew_lifetime,
+                              lifetime, renew_interval,
+                              max_secrets=MAX_SECRETS):
+    return unindent("""\
+        [sssd]
+        domains = files
+        services = nss
+
+        [domain/files]
+        id_provider = files
+
+        [kcm]
+        socket_path = {kcm_path}
+        ccache_storage = {ccache_storage}
+        tgt_renewal = true
+        krb5_renewable_lifetime = {renew_lifetime}
+        krb5_lifetime = {lifetime}
+        krb5_renew_interval = {renew_interval}
+
+        [secrets]
+        max_secrets = {max_secrets}
+    """).format(**locals())
+
+
 def common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf):
     kcm_socket_include = unindent("""
     [libdefaults]
@@ -200,6 +231,18 @@ def setup_for_kcm_secdb(request, kdc_instance):
     return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf)
 
 
+@pytest.fixture
+def setup_for_kcm_renewals_secdb(passwd_ops_setup, request, kdc_instance):
+    """
+    Set up the KCM renewals backed by libsss_secrets
+    """
+    kcm_path = os.path.join(config.RUNSTATEDIR, "kcm.socket")
+    sssd_conf = create_sssd_conf_renewals(kcm_path, "secdb",
+                                          "10d", "60s", "10s")
+    passwd_ops_setup.useradd(**USER1)
+    return common_setup_for_kcm_mem(request, kdc_instance, kcm_path, sssd_conf)
+
+
 def kcm_init_list_destroy(testenv):
     """
     Test that kinit, kdestroy and klist work with KCM
@@ -585,3 +628,38 @@ def test_kcm_secrets_quota(setup_for_kcm_sec,
     princ = "%s%d" % ("kcmtest", MAX_SECRETS)
     out, _, _ = testenv.k5util.kinit(princ, princ)
     assert out != 0
+
+
+def test_kcm_renewals(setup_for_kcm_renewals_secdb):
+    """
+    Test that basic KCM renewal works
+    """
+    testenv = setup_for_kcm_renewals_secdb
+    testenv.k5kdc.add_principal("user1", "Secret123")
+
+    ok = testenv.k5util.has_principal("user1@KCMTEST")
+    assert ok is False
+    nprincs = testenv.k5util.num_princs()
+    assert nprincs == 0
+
+    # Renewal is only performed after half of lifetime exceeded,
+    # see kcm_renew_all_tgts()
+    options = ["-r", "15s", "-l", "15s"]
+    out, _, _ = testenv.k5util.kinit("user1", "Secret123", options)
+    assert out == 0
+    nprincs = testenv.k5util.num_princs()
+    assert nprincs == 1
+
+    timestr_fmt = "%m/%d/%y %H:%M:%S"
+    initial_times = testenv.k5util.list_times()
+
+    # Wait for renewal to trigger once, after renew interval
+    time.sleep(15)
+
+    renewed_times = testenv.k5util.list_times()
+
+    init_times = initial_times.split()[0] + ' ' + initial_times.split()[1]
+    renew_times = renewed_times.split()[0] + ' ' + renewed_times.split()[1]
+    dt_init = datetime.strptime(init_times, timestr_fmt)
+    dt_renew = datetime.strptime(renew_times, timestr_fmt)
+    assert dt_renew > dt_init

From d0935ac27ed35e333b1905cdf78ad6dc11a9972f Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Mon, 3 May 2021 18:14:32 +0000
Subject: [PATCH 6/7] KCM: Conditionally build KCM renewals support

Use --enable-kcm-renewal, --disable-kcm-renewal or allw
autodetection of MIT kerberos marshalling functions
required to enable KCM renewal support.
---
 Makefile.am                       | 28 +++++++++++++++++++++-------
 src/external/krb5.m4              | 19 ++++++++++++++++++-
 src/man/Makefile.am               |  6 +++++-
 src/man/sssd-kcm.8.xml            |  4 ++--
 src/responder/kcm/kcmsrv_ccache.c |  6 ++++++
 src/tests/intg/Makefile.am        |  7 +++++++
 src/tests/intg/test_kcm.py        |  6 ++++++
 7 files changed, 65 insertions(+), 11 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index cddd51b981..4d9acaef99 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -326,10 +326,14 @@ if BUILD_KCM
 non_interactive_cmocka_based_tests += \
 	test_kcm_marshalling \
 	test_kcm_queue \
-	test_kcm_renewals \
-        $(NULL)
+    $(NULL)
 endif   # BUILD_KCM
 
+if BUILD_KCM_RENEWAL
+non_interactive_cmocka_based_tests += test_kcm_renewals
+endif # BUILD_KCM_RENEWAL
+
+
 if BUILD_SAMBA
 non_interactive_cmocka_based_tests += \
     ad_access_filter_tests \
@@ -1791,7 +1795,6 @@ endif
 if BUILD_KCM
 sssd_kcm_SOURCES = \
     src/responder/kcm/kcm.c \
-    src/responder/kcm/kcm_renew.c \
     src/responder/kcm/kcmsrv_cmd.c \
     src/responder/kcm/kcmsrv_ccache.c \
     src/responder/kcm/kcmsrv_ccache_binary.c \
@@ -1801,9 +1804,6 @@ sssd_kcm_SOURCES = \
     src/responder/kcm/kcmsrv_ccache_secdb.c \
     src/responder/kcm/kcmsrv_ops.c \
     src/responder/kcm/kcmsrv_op_queue.c \
-    src/providers/krb5/krb5_opts.c \
-    src/providers/krb5/krb5_child_handler.c \
-    src/providers/data_provider_opts.c \
     src/util/sss_sockets.c \
     src/util/sss_krb5.c \
     src/util/sss_iobuf.c \
@@ -1821,7 +1821,6 @@ sssd_kcm_LDADD = \
     $(KRB5_LIBS) \
     $(JANSSON_LIBS) \
     $(SSSD_LIBS) \
-    $(CARES_LIBS) \
     $(UUID_LIBS) \
     $(SYSTEMD_DAEMON_LIBS) \
     $(SSSD_INTERNAL_LTLIBS) \
@@ -1840,6 +1839,18 @@ sssd_kcm_LDADD += \
     $(NULL)
 endif
 
+if BUILD_KCM_RENEWAL
+sssd_kcm_SOURCES += \
+    src/responder/kcm/kcm_renew.c \
+    src/providers/krb5/krb5_opts.c \
+    src/providers/krb5/krb5_child_handler.c \
+    src/providers/data_provider_opts.c \
+    $(NULL)
+sssd_kcm_LDADD += \
+    $(CARES_LIBS) \
+    $(NULL)
+endif
+
 endif
 
 sssd_be_SOURCES = \
@@ -3932,6 +3943,7 @@ test_kcm_queue_LDADD = \
     libsss_sbus.la \
     $(NULL)
 
+if BUILD_KCM_RENEWAL
 test_kcm_renewals_SOURCES = \
 	$(TEST_MOCK_RESP_OBJ) \
 	src/tests/cmocka/test_kcm_renewals.c \
@@ -3945,6 +3957,7 @@ test_kcm_renewals_SOURCES = \
 	src/util/secrets/secrets.c \
 	src/util/secrets/config.c \
 	src/providers/krb5/krb5_child_handler.c \
+	src/providers/krb5/krb5_opts.c \
 	src/providers/data_provider_opts.c \
 	$(NULL)
 test_kcm_renewals_CFLAGS = \
@@ -3965,6 +3978,7 @@ test_kcm_renewals_LDADD = \
 	libsss_iface.la \
 	libsss_sbus.la \
 	$(NULL)
+endif # BUILD_KCM_RENEWAL
 
 endif # BUILD_KCM
 
diff --git a/src/external/krb5.m4 b/src/external/krb5.m4
index b844c2fbee..1dfdbe9111 100644
--- a/src/external/krb5.m4
+++ b/src/external/krb5.m4
@@ -65,7 +65,8 @@ AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc krb5_get_error_message \
                 krb5_set_trace_callback \
                 krb5_find_authdata \
                 krb5_kt_have_content \
-                krb5_cc_get_full_name])
+                krb5_cc_get_full_name \
+                krb5_unmarshal_credentials])
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
 CFLAGS="$CFLAGS $KRB5_CFLAGS"
@@ -112,5 +113,21 @@ AM_CONDITIONAL([BUILD_KRB5_LOCALAUTH_PLUGIN],
 AM_COND_IF([BUILD_KRB5_LOCALAUTH_PLUGIN],
            [AC_DEFINE_UNQUOTED(HAVE_KRB5_LOCALAUTH_PLUGIN, 1, [Build with krb5 localauth plugin])])
 
+AC_ARG_ENABLE([kcm-renewal],
+              [AS_HELP_STRING([--disable-kcm-renewal],
+                              [do not build support for kcm renewals])],
+              [build_kcm_renewal=$enableval],
+              [build_kcm_renewal=yes])
+
+if test x$build_kcm_renewal = xyes -a x$ac_cv_func_krb5_unmarshal_credentials != xyes
+then
+  AC_MSG_WARN([krb5 unmarshalling function not available, fallback to building without KCM renewals])
+fi
+
+AM_CONDITIONAL([BUILD_KCM_RENEWAL],
+               [test x$build_kcm_renewal = xyes -a x$ac_cv_func_krb5_unmarshal_credentials = xyes])
+AM_COND_IF([BUILD_KCM_RENEWAL],
+           [AC_DEFINE_UNQUOTED(HAVE_KCM_RENEWAL, 1, [Build with kcm renewals])])
+
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
diff --git a/src/man/Makefile.am b/src/man/Makefile.am
index c6890a792e..9eedf698ab 100644
--- a/src/man/Makefile.am
+++ b/src/man/Makefile.am
@@ -49,8 +49,12 @@ endif
 if BUILD_LOCAL_PROVIDER
 LOCAL_PROVIDER_CONDS = ;enable_local_provider
 endif
+if BUILD_KCM_RENEWAL
+KCM_RENEWAL_CONDS = ;enable_kcm_renewal
+endif
+
 
-CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS)$(FILES_CONDS)$(KCM_CONDS)$(STAP_CONDS)$(LOCAL_PROVIDER_CONDS)
+CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SEC_CONDS)$(SYSTEMD_CONDS)$(FILES_CONDS)$(KCM_CONDS)$(STAP_CONDS)$(LOCAL_PROVIDER_CONDS)$(KCM_RENEWAL_CONDS)
 
 
 #Special Rules:
diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
index 5f81af7367..aa77446ce3 100644
--- a/src/man/sssd-kcm.8.xml
+++ b/src/man/sssd-kcm.8.xml
@@ -162,7 +162,7 @@ systemctl restart sssd-kcm.service
         </para>
     </refsect1>
 
-    <refsect1 id='renewals'>
+    <refsect1 id='renewals' condition="enable_kcm_renewal">
         <title>RENEWALS</title>
         <para>
             The sssd-kcm service can be configured to attempt TGT
@@ -285,7 +285,7 @@ systemctl restart sssd-kcm.service
                     </para>
                 </listitem>
             </varlistentry>
-            <varlistentry>
+            <varlistentry condition="enable_kcm_renewal">
                 <term>krb5_renew_interval (integer)</term>
                 <listitem>
                     <para>
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
index ea250c4af0..0fdd491704 100644
--- a/src/responder/kcm/kcmsrv_ccache.c
+++ b/src/responder/kcm/kcmsrv_ccache.c
@@ -282,6 +282,7 @@ errno_t kcm_cc_set_header(struct kcm_ccache *cc,
     return EOK;
 }
 
+#ifdef HAVE_KCM_RENEWAL
 struct kcm_unmarshall_ctx {
     krb5_creds **creds;
     krb5_context krb_ctx;
@@ -297,11 +298,15 @@ static int kcm_cc_unmarshall_destructor(struct kcm_unmarshall_ctx *kcm_uctx)
 
     return 0;
 }
+#endif
 
 krb5_creds **kcm_cc_unmarshal(TALLOC_CTX *mem_ctx,
                               krb5_context krb_context,
                               struct kcm_ccache *cc)
 {
+#ifndef HAVE_KCM_RENEWAL
+    return NULL;
+#else
     TALLOC_CTX *tmp_ctx;
     struct kcm_cred *cred;
     krb5_data *cred_data;
@@ -359,6 +364,7 @@ krb5_creds **kcm_cc_unmarshal(TALLOC_CTX *mem_ctx,
 done:
     talloc_free(tmp_ctx);
     return NULL;
+#endif
 }
 
 errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid)
diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index 133f07423a..d9fcc2dc3c 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -82,6 +82,12 @@ install-data-hook:
 
 endif
 
+if BUILD_KCM_RENEWAL
+KCM_RENEW = "enabled"
+else
+KCM_RENEW = "disabled"
+endif
+
 cwrap-dbus-system.conf: data/cwrap-dbus-system.conf.in Makefile
 	$(SED) -e "s!@runstatedir[@]!$(runstatedir)!" \
            -e "s!@dbusservicedir[@]!$(dbusservicedir)!" \
@@ -206,6 +212,7 @@ intgcheck-installed: config.py passwd group pam_sss_service pam_sss_alt_service
 	PAM_WRAPPER_PATH=$$(pkg-config --libs pam_wrapper) \
 	PAM_CERT_DB_PATH=$(PAM_CERT_DB_PATH) \
 	SOFTHSM2_CONF=$(SOFTHSM2_CONF) \
+	KCM_RENEW=$(KCM_RENEW) \
 	DBUS_SOCK_DIR="$(DESTDIR)$(runstatedir)/dbus/" \
 	DBUS_SESSION_BUS_ADDRESS="unix:path=$$DBUS_SOCK_DIR/fake_socket" \
 	DBUS_SYSTEM_BUS_ADDRESS="unix:path=$$DBUS_SOCK_DIR/system_bus_socket" \
diff --git a/src/tests/intg/test_kcm.py b/src/tests/intg/test_kcm.py
index 395803bef7..9f70f6e347 100644
--- a/src/tests/intg/test_kcm.py
+++ b/src/tests/intg/test_kcm.py
@@ -61,6 +61,10 @@ def ccname(self, my_uid=None):
         return "KCM:%d" % my_uid
 
 
+def have_kcm_renewal():
+    return os.environ['KCM_RENEW'] == "enabled"
+
+
 @pytest.fixture(scope="module")
 def kdc_instance(request):
     """Kerberos server instance fixture"""
@@ -630,6 +634,8 @@ def test_kcm_secrets_quota(setup_for_kcm_sec,
     assert out != 0
 
 
+@pytest.mark.skipif(not have_kcm_renewal(),
+                    reason="KCM renewal disabled, skipping")
 def test_kcm_renewals(setup_for_kcm_renewals_secdb):
     """
     Test that basic KCM renewal works

From d34db69388a419401e414d1958fc249035993c17 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <jstep...@redhat.com>
Date: Tue, 4 May 2021 13:39:13 +0000
Subject: [PATCH 7/7] KCM: Disable responder idle timeout with renewals

When KCM renewals are configured and enabled, disable the
responder idle timeout to prevent KCM from being in a shut-down
state when it should be executing TGT renewals.
---
 src/responder/kcm/kcm.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
index 754d1dd197..4fc4eed871 100644
--- a/src/responder/kcm/kcm.c
+++ b/src/responder/kcm/kcm.c
@@ -172,16 +172,6 @@ static int kcm_get_config(struct kcm_ctx *kctx)
         goto done;
     }
 
-    if (kctx->cc_be == CCDB_BE_SECRETS || kctx->cc_be == CCDB_BE_SECDB) {
-        ret = responder_setup_idle_timeout_config(kctx->rctx);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "Cannot set up idle responder timeout [%s].\n",
-                  CONFDB_RESPONDER_IDLE_TIMEOUT);
-            /* Not fatal */
-        }
-    }
-
     kctx->qctx = kcm_ops_queue_create(kctx, kctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
@@ -245,6 +235,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
 {
     struct resp_ctx *rctx;
     struct kcm_ctx *kctx;
+    bool renewal_enabled = false;
     struct krb5_ctx *krb5_ctx = NULL;
     time_t renew_intv = 0;
     int ret;
@@ -291,12 +282,19 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
-    ret = kcm_renewals_init(ev, rctx, kctx, krb5_ctx, renew_intv);
+    ret = kcm_renewals_init(ev, rctx, kctx, krb5_ctx, renew_intv, &renewal_enabled);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM renewal config\n");
         goto fail;
     }
 
+    if (renewal_enabled) {
+        /* Disable resp idle timeout to allow renewals */
+        rctx->idle_timeout = 0;
+    } else {
+        responder_setup_idle_timeout_config(kctx->rctx);
+    }
+
     /* Set up file descriptor limits */
     responder_set_fd_limit(kctx->fd_limit);
 
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org
Do not reply to spam on the list, report it: 
https://pagure.io/fedora-infrastructure

Reply via email to