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 90427e8ac3e37299d5da36f386597d21837f3c29 Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Tue, 1 Dec 2020 11:28:59 -0500 Subject: [PATCH 01/10] KCM: Read and set KCM krb5 options These options are necessary to call into existing krb5 child code. Options will be read from the [kcm] configuration section, if no options are specified here, fallback to reading these options from the first 'auth_provider=krb5' in sssd.conf also. krb5_renew_interval krb5_renew_lifetime krb5_lifetime krb5_validate krb5_canonicalize krb5_auth_timeout --- Makefile.am | 3 + src/confdb/confdb.h | 6 + src/config/cfg_rules.ini | 6 + src/man/sssd-kcm.8.xml | 49 ++++++ src/responder/kcm/kcm.c | 7 + src/responder/kcm/kcm_renew.c | 297 ++++++++++++++++++++++++++++++++++ src/responder/kcm/kcm_renew.h | 3 + 7 files changed, 371 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 cae9eae838..27a4f47f28 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1844,6 +1844,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 \ @@ -1861,6 +1863,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 a2be227ddd..9161e5cb81 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -287,6 +287,12 @@ #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_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 bf2d03b824..a270bb7bbc 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -329,6 +329,12 @@ option = responder_idle_timeout option = max_ccaches option = max_uid_ccaches option = max_ccache_size +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 f1fc958be2..bdf515d4b0 100644 --- a/src/responder/kcm/kcm.c +++ b/src/responder/kcm/kcm.c @@ -27,6 +27,7 @@ #include "responder/kcm/kcmsrv_ccache.h" #include "responder/kcm/kcmsrv_pvt.h" #include "responder/common/responder.h" +#include "providers/krb5/krb5_common.h" #include "util/util.h" #include "util/sss_krb5.h" @@ -254,6 +255,12 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, goto fail; } + ret = kcm_get_renewal_config(kctx, &krb5_ctx); + 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..c63facff33 --- /dev/null +++ b/src/responder/kcm/kcm_renew.c @@ -0,0 +1,297 @@ +extern struct dp_option default_krb5_opts[]; + +static int kcm_get_auth_provider_options(struct kcm_ctx *kctx, + struct krb5_ctx *krb5_ctx) +{ + errno_t ret = 0; + char *lifetime_str = NULL; + char *rtime = NULL; + bool validate = false; + bool canonicalize = false; + int child_timeout = 0; + struct dp_option *opts; + const char *conf_path = NULL; + char *auth_provider = NULL; + struct sss_domain_info *domains = NULL; + struct sss_domain_info *dom = NULL; + + ret = confdb_get_domains(kctx->rctx->cdb, &domains); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot get domains\n"); + goto done; + } + + for (dom = domains; dom != NULL; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + + conf_path = talloc_asprintf(kctx->rctx, CONFDB_DOMAIN_PATH_TMPL, + dom->name); + if (conf_path == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n"); + ret = ENOMEM; + goto done; + } + + ret = confdb_get_string(kctx->rctx->cdb, + kctx->rctx, + conf_path, + CONFDB_DOMAIN_AUTH_PROVIDER, + 0, &auth_provider); + + if (auth_provider != NULL) { + if (strcasecmp(auth_provider, "krb5") == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Checking auth provider options for: " + "[%s]\n", dom->name); + ret = dp_get_options(kctx->rctx, kctx->rctx->cdb, conf_path, + default_krb5_opts, KRB5_OPTS, &opts); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "dp_get_options failed\n"); + goto done; + } + + /* Lifetime */ + lifetime_str = dp_opt_get_string(opts, KRB5_LIFETIME); + if (lifetime_str != NULL) { + ret = krb5_string_to_deltat(lifetime_str, + &krb5_ctx->lifetime); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to convert lifetime string.\n"); + goto done; + } + krb5_ctx->lifetime_str = lifetime_str; + } + + /* Renewable lifetime */ + rtime = dp_opt_get_string(opts, KRB5_RENEWABLE_LIFETIME); + if (rtime != 0) { + ret = krb5_string_to_deltat(rtime, &krb5_ctx->rlife); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to convert renewable lifetime string.\n"); + goto done; + } + krb5_ctx->rlife_str = rtime; + } + + /* Validate */ + validate = dp_opt_get_bool(opts, KRB5_VALIDATE); + ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_VALIDATE, validate); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot set dp opt krb5 validate\n"); + goto done; + } + + /* Canonicalize */ + canonicalize = dp_opt_get_bool(opts, KRB5_CANONICALIZE); + ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_CANONICALIZE, + canonicalize); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set dp opt krb5 " + "canonicalize\n"); + goto done; + } + + /* Child timeout */ + child_timeout = dp_opt_get_int(opts, KRB5_AUTH_TIMEOUT); + if (child_timeout > 0) { + ret = dp_opt_set_int(krb5_ctx->opts, KRB5_AUTH_TIMEOUT, + child_timeout); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child " + "timeout\n"); + goto done; + } + } + + break; + } + } + } + + ret = EOK; +done: + return ret; +} + +static int kcm_get_krb5_config(struct kcm_ctx *kctx, + struct krb5_ctx *krb5_ctx) +{ + errno_t ret; + char *rtime = NULL; + char *lifetime_str = NULL; + bool validate = false; + bool canonicalize = false; + int child_timeout = 0; + bool kcm_renew_option_defined = false; + + /* Lifetime */ + ret = confdb_get_string(kctx->rctx->cdb, + kctx->rctx, + kctx->rctx->confdb_service_path, + CONFDB_KCM_KRB5_LIFETIME, + 0, &lifetime_str); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get confdb lifetime\n"); + goto done; + } + + if (lifetime_str != NULL) { + ret = krb5_string_to_deltat(lifetime_str, &krb5_ctx->lifetime); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to convert lifetime string.\n"); + goto done; + } + kcm_renew_option_defined = true; + krb5_ctx->lifetime_str = lifetime_str; + } + + /* Renewable lifetime */ + ret = confdb_get_string(kctx->rctx->cdb, + kctx->rctx, + kctx->rctx->confdb_service_path, + CONFDB_KCM_KRB5_RENEWABLE_LIFETIME, + 0, &rtime); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get confdb renewable lifetime\n"); + goto done; + } + + if (rtime != 0) { + ret = krb5_string_to_deltat(rtime, &krb5_ctx->rlife); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to convert renewable lifetime " + "string.\n"); + goto done; + } + kcm_renew_option_defined = true; + krb5_ctx->rlife_str = rtime; + } + + /* Validate */ + ret = confdb_get_bool(kctx->rctx->cdb, + kctx->rctx->confdb_service_path, + CONFDB_KCM_KRB5_VALIDATE, + false, &validate); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get confdb krb5 validate\n"); + goto done; + } + + ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_VALIDATE, validate); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set dp opt krb5 validate\n"); + goto done; + } + + /* Canonicalize */ + ret = confdb_get_bool(kctx->rctx->cdb, + kctx->rctx->confdb_service_path, + CONFDB_KCM_KRB5_CANONICALIZE, + false, &canonicalize); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get confdb krb5 canonicalize\n"); + goto done; + } + ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_CANONICALIZE, canonicalize); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set dp opt krb5 canonicalize\n"); + goto done; + } + + /* Child timeout */ + ret = confdb_get_int(kctx->rctx->cdb, + kctx->rctx->confdb_service_path, + CONFDB_KCM_KRB5_AUTH_TIMEOUT, + 0, &child_timeout); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get krb5 child timeout\n"); + goto done; + } + + if (child_timeout > 0) { + ret = dp_opt_set_int(krb5_ctx->opts, KRB5_AUTH_TIMEOUT, child_timeout); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child timeout\n"); + goto done; + } + kcm_renew_option_defined = true; + } + + /* Fallback to first auth_provider=krb5 domain */ + if (kcm_renew_option_defined == false) { + ret = kcm_get_auth_provider_options(kctx, krb5_ctx); + if (ret != EOK) { + /* Not fatal */ + DEBUG(SSSDBG_OP_FAILURE, "Failed to read auth provider options\n"); + } + } + + ret = EOK; +done: + return ret; +} + +int kcm_get_renewal_config(struct kcm_ctx *kctx, + struct krb5_ctx **_krb5_ctx) +{ + int ret; + struct krb5_ctx *krb5_ctx; + 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) 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 != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Failed setting default KCM kerberos " + "options\n"); + talloc_free(krb5_ctx->opts); + goto done; + } + } + + /* Override with config options */ + kcm_get_krb5_config(kctx, krb5_ctx); + + *_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 ceb5adc5d0dd7bb1aaa7919f688f7c1a008d9b7c Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Tue, 24 Nov 2020 16:18:08 -0500 Subject: [PATCH 02/10] KCM: Add renewable KCM tickets to renew table Extract TGT times from existing KCM ccache credentials, add times and ccache data(name, CACHEID) to renew table to be processed for renewal elsewhere. --- Makefile.am | 2 + src/responder/kcm/kcm.c | 20 +- src/responder/kcm/kcm_renew.c | 259 ++++++++++++++++++++++- src/responder/kcm/kcm_renew.h | 45 +++- src/responder/kcm/kcmsrv_ccache.c | 21 ++ src/responder/kcm/kcmsrv_ccache.h | 11 + src/responder/kcm/kcmsrv_ccache_be.h | 7 + src/responder/kcm/kcmsrv_ccache_binary.c | 2 +- src/responder/kcm/kcmsrv_ccache_secdb.c | 205 ++++++++++++++++++ src/util/secrets/secrets.c | 58 +++++ src/util/secrets/secrets.h | 4 + src/util/sss_krb5.c | 10 + src/util/sss_krb5.h | 5 + 13 files changed, 641 insertions(+), 8 deletions(-) diff --git a/Makefile.am b/Makefile.am index 27a4f47f28..a98727914c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -771,6 +771,7 @@ dist_noinst_HEADERS = \ src/responder/secrets/secsrv_local.h \ src/responder/secrets/secsrv_proxy.h \ src/responder/kcm/kcm.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 \ @@ -1835,6 +1836,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 \ diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c index bdf515d4b0..7512832a0c 100644 --- a/src/responder/kcm/kcm.c +++ b/src/responder/kcm/kcm.c @@ -26,6 +26,7 @@ #include "responder/kcm/kcm.h" #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" @@ -211,6 +212,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, { struct resp_ctx *rctx; struct kcm_ctx *kctx; + time_t renew_intv = 0; int ret; rctx = talloc_zero(mem_ctx, struct resp_ctx); @@ -255,12 +257,28 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, goto fail; } - ret = kcm_get_renewal_config(kctx, &krb5_ctx); + ret = kcm_get_renewal_config(kctx, &krb5_ctx, &renew_intv); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM renewal config\n"); goto fail; } + if (renew_intv > 0) { + ret = kcm_renewal_init(rctx, krb5_ctx, ev, kctx->kcm_data->db, renew_intv); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error initializing KCM renewals\n"); + goto fail; + } + + ret = kcm_ccdb_renew_init(rctx, krb5_ctx, ev, kctx->kcm_data->db); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, + "fatal error initializing KCM ccdb renewals\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 index c63facff33..0d5cd97ce3 100644 --- a/src/responder/kcm/kcm_renew.c +++ b/src/responder/kcm/kcm_renew.c @@ -1,7 +1,69 @@ +/* + 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/kcm_renew.h" + +#define INITIAL_TGT_TABLE_SIZE 10 + extern struct dp_option default_krb5_opts[]; +struct renew_tgt_ctx { + hash_table_t *tgt_table; + 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; +}; + +struct renew_data { + const char *ccname; + uid_t uid; + gid_t gid; + time_t start_time; + time_t lifetime; + time_t start_renew_at; + time_t renew_till; +}; + +struct auth_data { + struct krb5_ctx *krb5_ctx; + struct renew_data *renew_data; + hash_table_t *table; + const char *key; +}; + static int kcm_get_auth_provider_options(struct kcm_ctx *kctx, - struct krb5_ctx *krb5_ctx) + struct krb5_ctx *krb5_ctx, + time_t *_renew_intv) { errno_t ret = 0; char *lifetime_str = NULL; @@ -116,7 +178,8 @@ static int kcm_get_auth_provider_options(struct kcm_ctx *kctx, } static int kcm_get_krb5_config(struct kcm_ctx *kctx, - struct krb5_ctx *krb5_ctx) + struct krb5_ctx *krb5_ctx, + time_t *_renew_intv) { errno_t ret; char *rtime = NULL; @@ -125,6 +188,33 @@ static int kcm_get_krb5_config(struct kcm_ctx *kctx, bool canonicalize = false; int child_timeout = 0; bool kcm_renew_option_defined = false; + char *renew_intv_str = NULL; + time_t renew_intv = 0; + krb5_deltat renew_interval_delta; + krb5_error_code kerr; + + /* Renew interval */ + ret = confdb_get_string(kctx->rctx->cdb, + kctx->rctx, + kctx->rctx->confdb_service_path, + CONFDB_KCM_KRB5_RENEW_INTERVAL, + 0, &renew_intv_str); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot get renew interval\n"); + goto done; + } + + if (renew_intv_str != NULL) { + kerr = krb5_string_to_deltat(renew_intv_str, &renew_interval_delta); + if (kerr != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, "krb5_string_to_deltat failed\n"); + ret = ENOMEM; + goto done; + } + + kcm_renew_option_defined = true; + renew_intv = renew_interval_delta; + } /* Lifetime */ ret = confdb_get_string(kctx->rctx->cdb, @@ -221,20 +311,22 @@ static int kcm_get_krb5_config(struct kcm_ctx *kctx, /* Fallback to first auth_provider=krb5 domain */ if (kcm_renew_option_defined == false) { - ret = kcm_get_auth_provider_options(kctx, krb5_ctx); + ret = kcm_get_auth_provider_options(kctx, krb5_ctx, &renew_intv); if (ret != EOK) { /* Not fatal */ DEBUG(SSSDBG_OP_FAILURE, "Failed to read auth provider options\n"); } } + *_renew_intv = renew_intv; ret = EOK; done: return ret; } int kcm_get_renewal_config(struct kcm_ctx *kctx, - struct krb5_ctx **_krb5_ctx) + struct krb5_ctx **_krb5_ctx, + time_t *renew_intv) { int ret; struct krb5_ctx *krb5_ctx; @@ -282,7 +374,7 @@ int kcm_get_renewal_config(struct kcm_ctx *kctx, } /* Override with config options */ - kcm_get_krb5_config(kctx, krb5_ctx); + kcm_get_krb5_config(kctx, krb5_ctx, renew_intv); *_krb5_ctx = krb5_ctx; ret = EOK; @@ -294,4 +386,161 @@ int kcm_get_renewal_config(struct kcm_ctx *kctx, 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; + + /* forget the timer event, it will be freed by the tevent timer loop */ + renew_tgt_ctx->te = NULL; + + /* Add any renew-applicable KCM tickets to renew table */ + ret = kcm_ccdb_renew_init(renew_tgt_ctx->rctx, renew_tgt_ctx->krb5_ctx, + ev, renew_tgt_ctx->db); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add KCM tickets to table.\n"); + talloc_zfree(renew_tgt_ctx); + return; + } + + 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; + } +} + +errno_t kcm_add_tgt_to_renew_table(struct krb5_ctx *krb5_ctx, + const char *ccname, + uid_t uid, + gid_t gid, + struct tgt_times *tgtt, + const char *upn) +{ + int ret; + struct renew_data *renew_data = NULL; + struct renew_data *existing_tgt_entry = NULL; + + if (krb5_ctx->renew_tgt_ctx == NULL) { + DEBUG(SSSDBG_TRACE_LIBS, "Renew context not initialized, " + "automatic renewal not available.\n"); + return EOK; + } + + if (upn == NULL || ccname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing user principal name or ccname.\n"); + return EINVAL; + } + + renew_data = talloc_zero(krb5_ctx->renew_tgt_ctx, struct renew_data); + if (renew_data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + ret = ENOMEM; + goto done; + } + + existing_tgt_entry = talloc_zero(krb5_ctx->renew_tgt_ctx, struct renew_data); + if (existing_tgt_entry == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + ret = ENOMEM; + goto done; + } + + renew_data->ccname = talloc_strdup(renew_data, ccname); + renew_data->uid = uid; + renew_data->gid = gid; + + renew_data->start_time = tgtt->starttime; + renew_data->lifetime = tgtt->endtime; + renew_data->start_renew_at = (time_t) (tgtt->starttime + + 0.5 *(tgtt->endtime - tgtt->starttime)); + renew_data->renew_till = tgtt->renew_till; + + /* Overwrite existing entry with updated times */ + if (sss_ptr_hash_has_key(krb5_ctx->renew_tgt_ctx->tgt_table, upn)) { + existing_tgt_entry = sss_ptr_hash_lookup(krb5_ctx->renew_tgt_ctx->tgt_table, upn, + struct renew_data); + if (strcmp(existing_tgt_entry->ccname, ccname) == 0) { + sss_ptr_hash_delete(krb5_ctx->renew_tgt_ctx->tgt_table, upn, true); + } + } + + ret = sss_ptr_hash_add(krb5_ctx->renew_tgt_ctx->tgt_table, + upn, renew_data, struct renew_data); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_ptr_hash_add failed\n"); + ret = EFAULT; + goto done; + } + + DEBUG(SSSDBG_TRACE_LIBS, + "Added [%s][%s] for renewal at [%s].", + upn, renew_data->ccname, + ctime(&renew_data->start_renew_at)); + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(renew_data); + } + return ret; +} + +errno_t kcm_renewal_init(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->tgt_table = sss_ptr_hash_create(krb5_ctx->renew_tgt_ctx, + NULL, + NULL); + if (krb5_ctx->renew_tgt_ctx->tgt_table == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_ptr_hash_create failed.\n"); + goto fail; + } + + 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..5aed33b5ac 100644 --- a/src/responder/kcm/kcm_renew.h +++ b/src/responder/kcm/kcm_renew.h @@ -1,3 +1,46 @@ +/* + 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" + 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_init(struct resp_ctx *rctx, struct krb5_ctx *kctx, + struct tevent_context *ev, struct kcm_ccdb *db, + time_t renew_intv); +errno_t kcm_add_tgt_to_renew_table(struct krb5_ctx *kctx, + const char *ccname, + uid_t uid, gid_t gid, + struct tgt_times *tgtt, + const char *upn); +#endif /* __KCM_RENEW_H__ */ diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c index 60eacd4516..d39f5a10b0 100644 --- a/src/responder/kcm/kcmsrv_ccache.c +++ b/src/responder/kcm/kcmsrv_ccache.c @@ -293,6 +293,27 @@ struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd) return crd ? crd->cred_blob : NULL; } +errno_t kcm_ccdb_renew_init(TALLOC_CTX *mem_ctx, + struct krb5_ctx *kctx, + struct tevent_context *ev, + struct kcm_ccdb *ccdb) +{ + errno_t ret; + + if (kctx == NULL || ev == NULL || ccdb == NULL) { + ret = EINVAL; + return ret; + } + + ret = ccdb->ops->renew_init(kctx, ev, ccdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failure to execute ccdb renewal init\n"); + return ret; + } + + return EOK; +} + 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..76e7dd89b3 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 @@ -140,6 +141,13 @@ 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); +/* + * Initialize KCM renewals + */ +errno_t kcm_ccdb_renew_init(TALLOC_CTX *mem_ctx, + struct krb5_ctx *kctx, + struct tevent_context *ev, + struct kcm_ccdb *cdb); /* * In KCM, each ccache name is usually in the form of "UID:<num> @@ -376,4 +384,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..171acbbcfd 100644 --- a/src/responder/kcm/kcmsrv_ccache_be.h +++ b/src/responder/kcm/kcmsrv_ccache_be.h @@ -34,6 +34,11 @@ typedef errno_t struct confdb_ctx *cdb, const char *confdb_service_path); +typedef errno_t +(*ccdb_renew_init_fn)(struct krb5_ctx *kctx, + struct tevent_context *ev, + struct kcm_ccdb *cdb); + typedef struct tevent_req * (*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -164,6 +169,8 @@ typedef errno_t struct kcm_ccdb_ops { ccdb_init_fn init; + ccdb_renew_init_fn renew_init; + ccdb_nextid_send_fn nextid_send; ccdb_nextid_recv_fn nextid_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..02bdb1b230 100644 --- a/src/responder/kcm/kcmsrv_ccache_secdb.c +++ b/src/responder/kcm/kcmsrv_ccache_secdb.c @@ -27,8 +27,11 @@ #include "util/util.h" #include "util/secrets/secrets.h" #include "util/crypto/sss_crypto.h" +#include "util/sss_krb5.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"/" @@ -577,6 +580,206 @@ static errno_t ccdb_secdb_init(struct kcm_ccdb *db, return EOK; } +static errno_t renew_check_creds(struct krb5_ctx *krb5_ctx, + struct ccdb_secdb_state *state, + struct sss_sec_ctx *sctx, + struct cli_creds *cli, + char *key) +{ + errno_t ret; + char *secdb_key = NULL; + uuid_t uuid; + struct kcm_ccache *cc; + struct kcm_cred *cred; + struct tgt_times tgtt; + char *client_name = NULL; + krb5_context krb_context = NULL; + krb5_data *cred_data = NULL; + krb5_creds *extracted_creds; + krb5_error_code kerr = 0; + time_t now; + + ret = sec_key_get_uuid(key, uuid); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Malformed key, cannot get UUID\n"); + goto immediate; + } + + /* Get ccache by UUID */ + ret = key_by_uuid(state, sctx, cli, uuid, &secdb_key); + if (ret == ENOENT) { + ret = EOK; + goto immediate; + } else if (ret != EOK) { + goto immediate; + } + + ret = secdb_get_cc(state, sctx, secdb_key, cli, &cc); + if (ret != EOK) { + goto immediate; + } + /* Extract credentials */ + if (cc != NULL) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Checking ccache [%s] for creds to renew\n", cc->name); + for (cred = kcm_cc_get_cred(cc); cred != NULL; cred = kcm_cc_next_cred(cred)) { + kerr = krb5_init_context(&krb_context); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init context\n"); + goto immediate; + } + + cred_data = talloc_zero(state, struct _krb5_data); + if (cred_data == NULL) { + return ENOMEM; + } + ret = get_krb5_data_from_cred(state, cred->cred_blob, cred_data); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to convert to krb5_data\n"); + goto immediate; + } + + kerr = krb5_unmarshal_credentials(krb_context, cred_data, + &extracted_creds); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unmarshal creds\n"); + goto immediate; + } + + kerr = krb5_unparse_name(krb_context, extracted_creds->client, + &client_name); + if (kerr != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed unparsing name\n"); + goto immediate; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Found credentials for [%s]\n", + client_name); + memset(&tgtt, 0, sizeof(tgtt)); + tgtt.authtime = extracted_creds->times.authtime; + tgtt.starttime = extracted_creds->times.starttime; + tgtt.endtime = extracted_creds->times.endtime; + tgtt.renew_till = extracted_creds->times.renew_till; + + now = time(NULL); + if (tgtt.renew_till >= tgtt.endtime && tgtt.renew_till >= now + && tgtt.endtime >= now) { + ret = kcm_add_tgt_to_renew_table(krb5_ctx, cc->name, + cc->owner.uid, cc->owner.gid, + &tgtt, client_name); + } else { + DEBUG(SSSDBG_TRACE_INTERNAL, "Time not applicable\n"); + } + + talloc_zfree(cred_data); + } + } + + ret = EOK; + +immediate: + if (krb_context != NULL) { + krb5_free_context(krb_context); + } + if (kerr != 0) { + return EIO; + } + + return ret; +} + +static errno_t renew_check_ccaches(struct krb5_ctx *krb5_ctx, + struct tevent_context *ev, + struct sss_sec_ctx *sctx, + uid_t *uids, + size_t uid_count) +{ + struct cli_creds *cli_cred; + char **keys = NULL; + size_t nkeys; + struct ccdb_secdb_state *state = NULL; + struct sss_sec_req *sreq = NULL; + struct passwd *pwd = NULL; + errno_t ret; + + cli_cred = talloc_zero(ev, struct cli_creds); + if (cli_cred == NULL) { + return ENOMEM; + } + + /* Get UUID list for name */ + for (int i = 0; i < uid_count; i++) { + pwd = getpwuid(uids[i]); + if (pwd == NULL) { + talloc_zfree(cli_cred); + DEBUG(SSSDBG_OP_FAILURE, "Failed to get pwd entry for [%d]\n", + uids[i]); + return EINVAL; + } + + cli_cred->ucred.uid = pwd->pw_uid; + cli_cred->ucred.gid = pwd->pw_gid; + + ret = secdb_container_url_req(state, sctx, cli_cred, &sreq); + if (ret != EOK) { + talloc_zfree(cli_cred); + return EINVAL; + } + + ret = sss_sec_list(state, sreq, &keys, &nkeys); + if (ret == ENOENT) { + nkeys = 0; + } else if (ret != EOK) { + talloc_zfree(cli_cred); + return EINVAL; + } + DEBUG(SSSDBG_TRACE_INTERNAL, "Found [%zu] ccaches under uid [%u]\n", + nkeys, uids[i]); + + /* Examine credentials and add renewal-ready tgts to renewal table */ + for (size_t j = 0; j < nkeys; j++) { + ret = renew_check_creds(krb5_ctx, state, sctx, cli_cred, keys[j]); + if (ret != EOK && ret != ENOENT) { + talloc_zfree(cli_cred); + DEBUG(SSSDBG_OP_FAILURE, "Failed to check credentials\n"); + return EINVAL; + } + } + } + + return EOK; +} + +static errno_t ccdb_secdb_renew_init(struct krb5_ctx *krb5_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db) +{ + struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb); + uid_t *uid_list = NULL; + size_t uid_list_count = 0; + errno_t ret = 0; + + /* Search for ccache uids */ + ret = sss_sec_list_cc_uids(secdb->sctx, &uid_list, &uid_list_count); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "Error retrieving ccache uid list\n"); + return ret; + } + DEBUG(SSSDBG_TRACE_INTERNAL, "Found [%lu] ccache uids\n", uid_list_count); + + if (uid_list_count > 0) { + ret = renew_check_ccaches(krb5_ctx, ev, secdb->sctx, + uid_list, uid_list_count); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "Error checking ccaches in secdb\n"); + return ret; + } + } + + return EOK; +} + struct ccdb_secdb_nextid_state { unsigned int nextid; }; @@ -1468,6 +1671,8 @@ static errno_t ccdb_secdb_delete_recv(struct tevent_req *req) const struct kcm_ccdb_ops ccdb_secdb_ops = { .init = ccdb_secdb_init, + .renew_init = ccdb_secdb_renew_init, + .nextid_send = ccdb_secdb_nextid_send, .nextid_recv = ccdb_secdb_nextid_recv, diff --git a/src/util/secrets/secrets.c b/src/util/secrets/secrets.c index c6310b5852..92678c43dc 100644 --- a/src/util/secrets/secrets.c +++ b/src/util/secrets/secrets.c @@ -26,6 +26,7 @@ #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 +938,63 @@ static char *local_dn_to_path(TALLOC_CTX *mem_ctx, return path; } +/* Unfiltered request for names */ +errno_t sss_sec_list_cc_uids(struct sss_sec_ctx *sec, + uid_t **_uid_list, + size_t *_uid_list_count) +{ + + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + struct ldb_dn *dn; + const struct ldb_val *name_val; + static const char *attrs[] = { "distinguishedName", NULL }; + uid_t *uid_list; + 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)); + return EIO; + } + + if (res->count == 0) { + DEBUG(SSSDBG_TRACE_LIBS, "No uids found\n"); + return EOK; + } + + uid_list = talloc_array(tmp_ctx, uint32_t, res->count); + if (uid_list == NULL) { + return ENOMEM; + } + + for (unsigned i = 0; i < res->count; i++) { + name_val = ldb_dn_get_component_val(res->msgs[i]->dn, 1); + + uid_list[i] = strtouint32((const char *)name_val->data, NULL, 10); + ret = errno; + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID\n"); + return ENOMEM; + } + DEBUG(SSSDBG_TRACE_INTERNAL, "uid: [%u]\n", uid_list[i]); + } + + *_uid_list = uid_list; + *_uid_list_count = res->count; + + return EOK; +} + 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..ba6f3bc6db 100644 --- a/src/util/secrets/secrets.h +++ b/src/util/secrets/secrets.h @@ -87,6 +87,10 @@ 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_uids(struct sss_sec_ctx *sec_ctx, + uid_t **_keys, + size_t *num_keys); + 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 f2768643015543156133f1d1abc8fd08f13a53be Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Mon, 30 Nov 2020 14:43:40 -0500 Subject: [PATCH 03/10] KCM: Parse renew table and prepare for renewal Trigger renewal codepath for entries added to the renew table. --- src/responder/kcm/kcm_renew.c | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c index 0d5cd97ce3..0d164b69bc 100644 --- a/src/responder/kcm/kcm_renew.c +++ b/src/responder/kcm/kcm_renew.c @@ -61,6 +61,8 @@ struct auth_data { const char *key; }; +static void kcm_renew_tgt_done(struct tevent_req *req); + static int kcm_get_auth_provider_options(struct kcm_ctx *kctx, struct krb5_ctx *krb5_ctx, time_t *_renew_intv) @@ -386,6 +388,81 @@ int kcm_get_renewal_config(struct kcm_ctx *kctx, return ret; } +static void kcm_renew_tgt(struct tevent_context *ev, struct tevent_timer *te, + struct timeval current_time, void *private_data) +{ + struct auth_data *auth_data = talloc_get_type(private_data, + struct auth_data); + struct tevent_req *req; +} + +static void kcm_renew_tgt_done(struct tevent_req *req) +{ +} + +errno_t kcm_renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx) +{ + hash_key_t *keys = NULL; + unsigned long count; + int ret; + size_t i; + time_t now; + struct auth_data *auth_data; + struct renew_data *renew_data; + struct tevent_timer *te = NULL; + + ret = hash_keys(renew_tgt_ctx->tgt_table, &count, &keys); + if (ret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed retrieving hash keys.\n"); + return EIO; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Found [%lu] renewal entries.\n", count); + + now = time(NULL); + + for (i = 0; i < count; i++) { + renew_data = sss_ptr_hash_lookup(renew_tgt_ctx->tgt_table, keys[i].c_str, + struct renew_data); + DEBUG(SSSDBG_TRACE_INTERNAL, "Checking [%s] for renewal at [%.24s].\n", + renew_data->ccname, ctime(&renew_data->start_renew_at)); + if (renew_data->renew_till < now) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Renew time exceeded, removing [%s].\n", + renew_data->ccname); + sss_ptr_hash_delete(renew_tgt_ctx->tgt_table, keys[i].c_str, true); + } else if (renew_data->start_renew_at <= now) { + auth_data = talloc_zero(renew_tgt_ctx, struct auth_data); + if (auth_data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + } else { + auth_data->krb5_ctx = renew_tgt_ctx->krb5_ctx; + auth_data->table = renew_tgt_ctx->tgt_table; + auth_data->renew_data = renew_data; + auth_data->key = talloc_strdup(auth_data, keys[i].c_str); + if (auth_data->key == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n"); + } else { + te = tevent_add_timer(renew_tgt_ctx->ev, + auth_data, tevent_timeval_current(), + kcm_renew_tgt, auth_data); + if (te == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n"); + } + } + } + + if (auth_data == NULL || te == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to renew TGT in [%s].\n", renew_data->ccname); + sss_ptr_hash_delete(renew_tgt_ctx->tgt_table, keys[i].c_str, true); + } + } + } + + return EOK; +} + + static void kcm_renew_tgt_timer_handler(struct tevent_context *ev, struct tevent_timer *te, struct timeval current_time, @@ -408,6 +485,14 @@ static void kcm_renew_tgt_timer_handler(struct tevent_context *ev, return; } + ret = kcm_renew_all_tgts(renew_tgt_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to attempt renewal of KCM ticket" + " table.\n"); + talloc_zfree(renew_tgt_ctx); + return; + } + 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, From 4c4c767b980451791c3851171c5f4f50d1354dbc Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Tue, 1 Dec 2020 11:44:11 -0500 Subject: [PATCH 04/10] KRB5_CHILD: Workaround domain dependency Kerberos child renewals called from KCM will not have an initialized domain structure. --- src/providers/krb5/krb5_child_handler.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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; } From 8b2e1e98ae6eeaf2c06bc0ec77b5703aaa4c22cf Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Mon, 30 Nov 2020 16:05:55 -0500 Subject: [PATCH 05/10] KCM: Execute krb5 child to perform renewal --- Makefile.am | 1 + src/responder/kcm/kcm_renew.c | 169 +++++++++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index a98727914c..470bda7f2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1847,6 +1847,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/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c index 0d164b69bc..4282fda757 100644 --- a/src/responder/kcm/kcm_renew.c +++ b/src/responder/kcm/kcm_renew.c @@ -44,6 +44,20 @@ struct renew_tgt_ctx { struct tevent_timer *te; }; +struct kcm_renew_tgt_ctx { + struct tevent_context *ev; + struct krb5child_req *kr; + + struct krb5_ctx *krb5_ctx; + struct auth_data *auth_data; + struct renew_data *renew_data; + hash_table_t *table; + const char *key; + + uint8_t *buf; + ssize_t len; +}; + struct renew_data { const char *ccname; uid_t uid; @@ -388,16 +402,170 @@ 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->key, + auth_data->renew_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->renew_data->uid; + krreq->gid = auth_data->renew_data->gid; + + kcm_ccname = talloc_asprintf(mem_ctx, "KCM:%s", + auth_data->renew_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->key); + 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->key); + 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 != 0) { + 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_timer *te, struct timeval current_time, 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 != 0) { + 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; } errno_t kcm_renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx) @@ -462,7 +630,6 @@ errno_t kcm_renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx) return EOK; } - static void kcm_renew_tgt_timer_handler(struct tevent_context *ev, struct tevent_timer *te, struct timeval current_time, From 3b1e497335992a0af5d1c19e9bcc74cbd3d13f83 Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Mon, 21 Dec 2020 14:45:17 -0500 Subject: [PATCH 06/10] 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 92678c43dc..25f63cab29 100644 --- a/src/util/secrets/secrets.c +++ b/src/util/secrets/secrets.c @@ -633,12 +633,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) { @@ -698,11 +699,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; @@ -744,7 +745,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; @@ -757,6 +758,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 ba6f3bc6db..40727dc349 100644 --- a/src/util/secrets/secrets.h +++ b/src/util/secrets/secrets.h @@ -79,6 +79,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 bd485e01612c9e725746ace2bf1191255a7b1ea7 Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Fri, 29 Jan 2021 20:36:24 +0000 Subject: [PATCH 07/10] TESTS: Add kcm_renewals unit test --- Makefile.am | 35 ++++ src/tests/cmocka/test_kcm_renewals.c | 296 +++++++++++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 src/tests/cmocka/test_kcm_renewals.c diff --git a/Makefile.am b/Makefile.am index 470bda7f2e..cd46b989ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -330,6 +330,7 @@ if BUILD_KCM non_interactive_cmocka_based_tests += \ test_kcm_marshalling \ test_kcm_queue \ + test_kcm_renewals \ $(NULL) endif # BUILD_KCM @@ -4000,6 +4001,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..10f119a56e --- /dev/null +++ b/src/tests/cmocka/test_kcm_renewals.c @@ -0,0 +1,296 @@ +/* + 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(struct renew_tgt_ctx *renew_tgt_ctx); + +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; +}; + +struct renew_tgt_ctx { + hash_table_t *tgt_table; + 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; +}; + +struct renew_data { + const char *ccname; + uid_t uid; + gid_t gid; + time_t start_time; + time_t lifetime; + time_t start_renew_at; + time_t renew_till; +}; + +/* 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_table(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_ctx = NULL; + struct renew_data *renew_tgt = NULL; + const char *upn = "testrenew@TESTREALM"; + + 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 hash table */ + renew_ctx = talloc_zero(test_ctx, struct renew_tgt_ctx); + assert_non_null(renew_ctx); + + renew_ctx->ev = test_ctx->ev; + + renew_ctx->tgt_table = sss_ptr_hash_create(renew_ctx, NULL, NULL); + assert_non_null(renew_ctx->tgt_table); + + /* Add entry to hash table */ + renew_tgt = talloc_zero(test_ctx, struct renew_data); + assert_non_null(renew_tgt); + + time_t now; + + renew_tgt->ccname = talloc_strdup(test_ctx, "1000:1001"); + renew_tgt->uid = 1000; + renew_tgt->gid = 1000; + now = time(NULL); + renew_tgt->start_time = now; + renew_tgt->lifetime = now + 60; + renew_tgt->renew_till = now + 300; + + ret = sss_ptr_hash_add(renew_ctx->tgt_table, upn, + renew_tgt, struct renew_data); + assert_int_equal(ret, EOK); + + /* Parse renewal table */ + ret = kcm_renew_all_tgts(renew_ctx); + 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_table, + 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 9d8901df33415ae8f030c1f910d24b42311f17ae Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Mon, 21 Dec 2020 20:48:15 -0500 Subject: [PATCH 08/10] INTG: Add KCM Renewal integration test --- src/tests/intg/kdc.py | 2 + src/tests/intg/krb5utils.py | 15 +++++++- src/tests/intg/test_kcm.py | 77 +++++++++++++++++++++++++++++++++++++ 3 files changed, 93 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..97d0a974f4 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,29 @@ 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} + 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 +230,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 +627,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 f85ebdcd96b87c1944533d6871fc1525dfa14e12 Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Fri, 29 Jan 2021 14:59:20 -0500 Subject: [PATCH 09/10] KCM: Conditionally build KCM renewals support Use --enable-kcm-renewal, --disable-kcm-renewal or allow 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/kcm.c | 6 +++++- src/responder/kcm/kcmsrv_ccache_secdb.c | 6 ++++-- src/tests/intg/Makefile.am | 7 +++++++ src/tests/intg/test_kcm.py | 6 ++++++ 8 files changed, 68 insertions(+), 14 deletions(-) diff --git a/Makefile.am b/Makefile.am index cd46b989ef..83fb3b2f84 100644 --- a/Makefile.am +++ b/Makefile.am @@ -330,10 +330,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 \ @@ -1837,7 +1841,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 \ @@ -1847,9 +1850,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 \ @@ -1867,7 +1867,6 @@ sssd_kcm_LDADD = \ $(KRB5_LIBS) \ $(JANSSON_LIBS) \ $(SSSD_LIBS) \ - $(CARES_LIBS) \ $(UUID_LIBS) \ $(SYSTEMD_DAEMON_LIBS) \ $(SSSD_INTERNAL_LTLIBS) \ @@ -1886,6 +1885,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 = \ @@ -4001,6 +4012,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 \ @@ -4014,6 +4026,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 = \ @@ -4034,6 +4047,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/kcm.c b/src/responder/kcm/kcm.c index 7512832a0c..2219158da4 100644 --- a/src/responder/kcm/kcm.c +++ b/src/responder/kcm/kcm.c @@ -212,7 +212,10 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, { struct resp_ctx *rctx; struct kcm_ctx *kctx; +#ifdef HAVE_KCM_RENEWAL + struct krb5_ctx *krb5_ctx; time_t renew_intv = 0; +#endif int ret; rctx = talloc_zero(mem_ctx, struct resp_ctx); @@ -257,6 +260,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, goto fail; } +#ifdef HAVE_KCM_RENEWAL ret = kcm_get_renewal_config(kctx, &krb5_ctx, &renew_intv); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM renewal config\n"); @@ -278,7 +282,7 @@ static int kcm_process_init(TALLOC_CTX *mem_ctx, goto fail; } } - +#endif /* HAVE_KCM_RENEWAL */ /* Set up file descriptor limits */ responder_set_fd_limit(kctx->fd_limit); diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c index 02bdb1b230..9708687593 100644 --- a/src/responder/kcm/kcmsrv_ccache_secdb.c +++ b/src/responder/kcm/kcmsrv_ccache_secdb.c @@ -580,6 +580,7 @@ static errno_t ccdb_secdb_init(struct kcm_ccdb *db, return EOK; } +#ifdef HAVE_KCM_RENEWAL static errno_t renew_check_creds(struct krb5_ctx *krb5_ctx, struct ccdb_secdb_state *state, struct sss_sec_ctx *sctx, @@ -779,7 +780,7 @@ static errno_t ccdb_secdb_renew_init(struct krb5_ctx *krb5_ctx, return EOK; } - +#endif /* HAVE_KCM_RENEWAL */ struct ccdb_secdb_nextid_state { unsigned int nextid; }; @@ -1671,8 +1672,9 @@ static errno_t ccdb_secdb_delete_recv(struct tevent_req *req) const struct kcm_ccdb_ops ccdb_secdb_ops = { .init = ccdb_secdb_init, +#ifdef HAVE_KCM_RENEWAL .renew_init = ccdb_secdb_renew_init, - +#endif .nextid_send = ccdb_secdb_nextid_send, .nextid_recv = ccdb_secdb_nextid_recv, diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am index f717214ad3..d5ccffc50a 100644 --- a/src/tests/intg/Makefile.am +++ b/src/tests/intg/Makefile.am @@ -70,6 +70,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)!" \ @@ -194,6 +200,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 97d0a974f4..ed873518c7 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""" @@ -629,6 +633,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 550c847428a902403e8e3a3e7431934c20de30fc Mon Sep 17 00:00:00 2001 From: Justin Stephenson <jstep...@redhat.com> Date: Thu, 11 Feb 2021 19:34:26 +0000 Subject: [PATCH 10/10] KCM: Disable responder idle timeout with renewals When SSSD is configured/built with KCM renewals, 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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c index 2219158da4..25f41b9e9f 100644 --- a/src/responder/kcm/kcm.c +++ b/src/responder/kcm/kcm.c @@ -140,6 +140,7 @@ static int kcm_get_config(struct kcm_ctx *kctx) goto done; } +#ifndef HAVE_KCM_RENEWAL if (kctx->cc_be == CCDB_BE_SECRETS || kctx->cc_be == CCDB_BE_SECDB) { ret = responder_setup_idle_timeout_config(kctx->rctx); if (ret != EOK) { @@ -148,7 +149,7 @@ static int kcm_get_config(struct kcm_ctx *kctx) /* Not fatal */ } } - +#endif kctx->qctx = kcm_ops_queue_create(kctx, kctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE,
_______________________________________________ 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