diff options
Diffstat (limited to 'src/responder/kcm/kcm_renew.c')
-rw-r--r-- | src/responder/kcm/kcm_renew.c | 775 |
1 files changed, 775 insertions, 0 deletions
diff --git a/src/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c new file mode 100644 index 0000000..39e9470 --- /dev/null +++ b/src/responder/kcm/kcm_renew.c @@ -0,0 +1,775 @@ +/* + SSSD + + KCM Kerberos renewals -- Renew a TGT automatically + + Authors: + Justin Stephenson <jstephen@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_auth_ctx { + struct tevent_context *ev; + struct krb5child_req *kr; + + struct krb5_ctx *krb5_ctx; + struct kcm_auth_data *auth_data; + + uint8_t *buf; + ssize_t len; +}; + +struct kcm_auth_data { + struct kcm_renew_auth_ctx *auth_ctx; + 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; + } + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_KRB5_RENEW_INTERVAL, + renew_intv == NULL ? "none" : renew_intv); + + if (lifetime != NULL) { + ret = krb5_string_to_deltat(lifetime, &krb5_ctx->lifetime); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to convert lifetime string [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + krb5_ctx->lifetime_str = lifetime; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_KRB5_LIFETIME, + krb5_ctx->lifetime_str == NULL ? "none" : krb5_ctx->lifetime_str); + + 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 [%d]: %s.\n", ret, sss_strerror(ret)); + goto done; + } + } + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_KRB5_RENEWABLE_LIFETIME, + rtime == NULL ? "none" : rtime); + + ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_VALIDATE, validate); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child timeout [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_KRB5_VALIDATE, + validate ? "true" : "false"); + + krb5_ctx->canonicalize = canonicalize; + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_KRB5_CANONICALIZE, + canonicalize ? "true" : "false"); + + 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 [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%d]\n", + CONFDB_KCM_KRB5_AUTH_TIMEOUT, + timeout); + + 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) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error allocating krb5_ctx\n"); + goto done; + } + + /* Set default Kerberos options */ + krb5_ctx->opts = talloc_zero_array(krb5_ctx, struct dp_option, KRB5_OPTS); + if (krb5_ctx->opts == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error allocating krb5_ctx opts\n"); + 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; + default: + ret = EINVAL; + } + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed setting default renewal kerberos " + "options [%d]: %s\n", ret, sss_strerror(ret)); + 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, "Unable to retrieve TGT Renewal confdb value " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_TGT_RENEWAL, + tgt_renewal ? "true" : "false"); + if (tgt_renewal == false) { + ret = ENOTSUP; + 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, "Unable to retrieve TGT Renewal inherit confdb " + "valule [%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n", + CONFDB_KCM_TGT_RENEWAL_INHERIT, + tgt_renewal_inherit == NULL ? "none" : tgt_renewal_inherit); + + /* 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 krb5 options from " + "[kcm] section [%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + } else { + conf_path = talloc_asprintf(kctx->rctx, CONFDB_DOMAIN_PATH_TMPL, + tgt_renewal_inherit); + if (conf_path == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "fatal error allocating conf_path\n"); + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Inherit krb5 options for domain [%s] for renewals\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 [%s] inherit krb5 options " + "[%d]: %s\n", conf_path, ret, sss_strerror(ret)); + 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 krb5 options for renewal " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + *_renew_intv = renew_intv_tm; + *_krb5_ctx = krb5_ctx; + ret = EOK; +done: + if (ret != EOK) { + talloc_free(krb5_ctx); + } + return ret; +} + +static errno_t kcm_child_req_setup(TALLOC_CTX *mem_ctx, + struct kcm_auth_data *auth_data, + struct krb5_ctx *krb5_ctx, + struct krb5child_req **_req) +{ + struct krb5child_req *krreq; + 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) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to alloc krreq [%d]: %s\n", + ret, strerror(ret)); + goto fail; + } + + krreq->krb5_ctx = krb5_ctx; + + /* Set uid and gid */ + krreq->uid = auth_data->uid; + krreq->gid = auth_data->gid; + + krreq->upn = talloc_strdup(krreq, auth_data->upn); + if (krreq->upn == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to strdup krreq->upn [%d]: %s\n", + ret, strerror(ret)); + goto fail; + } + + krreq->ccname = talloc_asprintf(krreq, "KCM:%s", auth_data->ccname); + if (krreq->ccname == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to strdup krreq->ccname [%d]: %s\n", + ret, strerror(ret)); + goto fail; + } + + /* Set PAM Data */ + krreq->pd = create_pam_data(krreq); + if (krreq->pd == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed creating pam data on krreq->pd " + "[%d]: %s\n", ret, strerror(ret)); + goto fail; + } + + krreq->pd->cmd = SSS_CMD_RENEW; + krreq->pd->user = talloc_strdup(krreq->pd, auth_data->upn); + if (krreq->pd->user == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to strdup krreq->pd->user " + "[%d]: %s\n", ret, strerror(ret)); + goto fail; + } + + /* Set authtok values */ + sss_authtok_set_empty(krreq->pd->newauthtok); + + ret = sss_authtok_set_ccfile(krreq->pd->authtok, krreq->ccname, 0); + if (ret != EOK) { + ret = ENOMEM; + DEBUG(SSSDBG_FATAL_FAILURE, "Failed setting authtok krreq->ccname" + "[%d]: %s\n", ret, strerror(ret)); + goto fail; + } + + krreq->old_ccname = krreq->ccname; + + *_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 kcm_auth_data *auth_data; + struct tevent_req *req; + struct kcm_renew_auth_ctx *ctx; + errno_t ret; + + auth_data = talloc_get_type(private_data, struct kcm_auth_data); + + ctx = talloc_zero(auth_data, struct kcm_renew_auth_ctx); + if (ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to allocate renew auth ctx\n"); + return; + } + auth_data->auth_ctx = ctx; + + ret = kcm_child_req_setup(ctx, auth_data, auth_data->krb5_ctx, &ctx->kr); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to setup krb5 child for renewal [%d]: %s\n", + ret, sss_strerror(ret)); + talloc_free(auth_data); + return; + } + + req = handle_child_send(ctx, ev, ctx->kr); + if (req == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to trigger krb5 child process request" + "[%d]: %s\n", ret, sss_strerror(ret)); + talloc_free(auth_data); + return; + } + + tevent_req_set_callback(req, kcm_renew_tgt_done, auth_data); + + return; +} + +static void kcm_renew_tgt_done(struct tevent_req *req) +{ + struct kcm_auth_data *auth_data; + struct kcm_renew_auth_ctx *ctx; + int ret; + struct krb5_child_response *res; + + auth_data = tevent_req_callback_data(req, struct kcm_auth_data); + ctx = auth_data->auth_ctx; + + ret = handle_child_recv(req, ctx, &ctx->buf, &ctx->len); + talloc_free(req); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Failed to receive krb5 child process request" + "[%d]: %s\n", ret, sss_strerror(ret)); + 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 error! Please " \ + "inspect the krb5_child.log file. " + " Error [%d]: %s\n", ret, sss_strerror(ret)); + 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); + talloc_zfree(auth_data); + return; +} + +static errno_t kcm_creds_check_times(TALLOC_CTX *mem_ctx, + struct kcm_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 kcm_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 kcm_auth_data); + if (auth_data == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate auth_data for renewals\n"); + 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) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate auth_data->upn for renewals\n"); + goto done; + } + + imm = tevent_create_immediate(auth_data); + if (imm == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed\n"); + 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 kcm_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; + + if (cc_list == NULL) { + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create tmp talloc_ctx\n"); + return ENOMEM; + } + + kerr = krb5_init_context(&krb_context); + if (kerr != 0) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init krb5 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 [%zu] 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(tmp_ctx, krb_context, cc); + if (extracted_creds == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, "Failed unmarshaling creds\n"); + goto done; + } + + for (int j = 0; extracted_creds[j] != NULL; j++) { + kerr = krb5_unparse_name(tmp_ctx, extracted_creds[j]->client, + &client_name); + if (kerr != 0) { + ret = EIO; + DEBUG(SSSDBG_CRIT_FAILURE, "Failed unparsing name\n"); + goto done; + } + + kcm_creds_check_times(tmp_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 kcm_renew_tgt_ctx *renew_tgt_ctx; + errno_t ret; + struct timeval next; + struct kcm_ccache **cc_list; + TALLOC_CTX *tmp_ctx; + + renew_tgt_ctx = talloc_get_type(data, struct kcm_renew_tgt_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; + + /* Prepare KCM ccache list for renewals */ + ret = kcm_ccdb_renew_tgts(tmp_ctx, 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"); + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve list of TGTs for renewal " + "preparation [%d]: %s\n", ret, sss_strerror(ret)); + } + + if (ret == EOK) { + ret = kcm_renew_all_tgts(tmp_ctx, renew_tgt_ctx, cc_list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to successfully execute renewal of TGT list" + "[%d]: %s\n", ret, sss_strerror(ret)); + } + } + + /* Reschedule timer */ + next = sss_tevent_timeval_current_ofs_time_t(renew_tgt_ctx->timer_interval); + 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, renewals will be " + "disabled until the next interval triggers\n"); + talloc_zfree(renew_tgt_ctx); + } + + talloc_free(tmp_ctx); + return; +} + +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->kcm_renew_tgt_ctx = talloc_zero(krb5_ctx, struct kcm_renew_tgt_ctx); + if (krb5_ctx->kcm_renew_tgt_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n"); + return ENOMEM; + } + + krb5_ctx->kcm_renew_tgt_ctx->rctx = rctx; + krb5_ctx->kcm_renew_tgt_ctx->krb5_ctx = krb5_ctx; + krb5_ctx->kcm_renew_tgt_ctx->db = db, + krb5_ctx->kcm_renew_tgt_ctx->ev = ev; + krb5_ctx->kcm_renew_tgt_ctx->timer_interval = renew_intv; + + /* Check KCM for tickets to renew */ + next = sss_tevent_timeval_current_ofs_time_t( + krb5_ctx->kcm_renew_tgt_ctx->timer_interval); + krb5_ctx->kcm_renew_tgt_ctx->te = tevent_add_timer(ev, krb5_ctx->kcm_renew_tgt_ctx, + next, + kcm_renew_tgt_timer_handler, + krb5_ctx->kcm_renew_tgt_ctx); + if (krb5_ctx->kcm_renew_tgt_ctx->te == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup renewal timer\n"); + ret = ENOMEM; + goto fail; + } + + return EOK; + +fail: + talloc_zfree(krb5_ctx->renew_tgt_ctx); + return ret; +} |