summaryrefslogtreecommitdiff
path: root/src/providers/krb5
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2010-11-15 13:46:17 +0100
committerStephen Gallagher <sgallagh@redhat.com>2010-12-03 10:41:28 -0500
commitf3f9ce8024d7610439d6c70ddafab1ab025cf8a8 (patch)
tree415d65170f362c2df65410084cadbcc016b4673d /src/providers/krb5
parent1709edfb690bb4ffa4b96c64d08853f47390eda3 (diff)
downloadsssd-f3f9ce8024d7610439d6c70ddafab1ab025cf8a8.tar.gz
sssd-f3f9ce8024d7610439d6c70ddafab1ab025cf8a8.tar.bz2
sssd-f3f9ce8024d7610439d6c70ddafab1ab025cf8a8.zip
Add support for automatic Kerberos ticket renewal
Diffstat (limited to 'src/providers/krb5')
-rw-r--r--src/providers/krb5/krb5_auth.c18
-rw-r--r--src/providers/krb5/krb5_auth.h5
-rw-r--r--src/providers/krb5/krb5_child_handler.c2
-rw-r--r--src/providers/krb5/krb5_common.c3
-rw-r--r--src/providers/krb5/krb5_common.h3
-rw-r--r--src/providers/krb5/krb5_init.c10
-rw-r--r--src/providers/krb5/krb5_renew_tgt.c380
7 files changed, 420 insertions, 1 deletions
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 974e7684..515c181b 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -210,6 +210,7 @@ static struct krb5_ctx *get_krb5_ctx(struct be_req *be_req)
switch (pd->cmd) {
case SSS_PAM_AUTHENTICATE:
+ case SSS_CMD_RENEW:
return talloc_get_type(be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
struct krb5_ctx);
break;
@@ -327,6 +328,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
switch (pd->cmd) {
case SSS_PAM_AUTHENTICATE:
+ case SSS_CMD_RENEW:
case SSS_PAM_CHAUTHTOK:
break;
case SSS_PAM_CHAUTHTOK_PRELIM:
@@ -786,6 +788,19 @@ static void krb5_child_done(struct tevent_req *subreq)
}
}
+ if (msg_status == PAM_SUCCESS &&
+ dp_opt_get_int(kr->krb5_ctx->opts, KRB5_RENEW_INTERVAL) > 0 &&
+ (pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_CMD_RENEW ||
+ pd->cmd == SSS_PAM_CHAUTHTOK) &&
+ tgtt.renew_till > tgtt.endtime && kr->ccname != NULL) {
+ DEBUG(7, ("Adding [%s] for automatic renewal.\n", kr->ccname));
+ ret = add_tgt_to_renew_table(kr->krb5_ctx, kr->ccname, &tgtt, pd);
+ if (ret != EOK) {
+ DEBUG(1, ("add_tgt_to_renew_table failed, "
+ "automatic renewal not possible.\n"));
+ }
+ }
+
/* If the child request failed, but did not return an offline error code,
* return with the status */
if (msg_status != PAM_SUCCESS && msg_status != PAM_AUTHINFO_UNAVAIL &&
@@ -891,6 +906,7 @@ static struct tevent_req *krb5_next_server(struct tevent_req *req)
switch (pd->cmd) {
case SSS_PAM_AUTHENTICATE:
+ case SSS_CMD_RENEW:
fo_set_port_status(state->kr->srv, PORT_NOT_WORKING);
next_req = krb5_next_kdc(req);
break;
@@ -975,6 +991,7 @@ static void krb5_save_ccname_done(struct tevent_req *req)
switch(pd->cmd) {
case SSS_PAM_AUTHENTICATE:
+ case SSS_CMD_RENEW:
case SSS_PAM_CHAUTHTOK_PRELIM:
password = talloc_size(state, pd->authtok_size + 1);
if (password != NULL) {
@@ -1077,6 +1094,7 @@ void krb5_pam_handler(struct be_req *be_req)
switch (pd->cmd) {
case SSS_PAM_AUTHENTICATE:
+ case SSS_CMD_RENEW:
case SSS_PAM_CHAUTHTOK_PRELIM:
case SSS_PAM_CHAUTHTOK:
req = krb5_auth_send(be_req, be_req->be_ctx->ev, be_req->be_ctx, pd,
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 130e85db..f40cbcb0 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -80,6 +80,11 @@ errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
struct be_ctx *be_ctx,
struct tevent_context *ev);
+errno_t init_renew_tgt(struct krb5_ctx *krb5_ctx, struct be_ctx *be_ctx,
+ struct tevent_context *ev, time_t renew_intv);
+errno_t add_tgt_to_renew_table(struct krb5_ctx *krb5_ctx, const char *ccfile,
+ struct tgt_times *tgtt, struct pam_data *pd);
+
/* krb5_access.c */
struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 9da8a37b..bafa0bbf 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -104,6 +104,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
buf->size = 6*sizeof(uint32_t) + strlen(kr->upn);
if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ kr->pd->cmd == SSS_CMD_RENEW ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
@@ -137,6 +138,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp);
if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ kr->pd->cmd == SSS_CMD_RENEW ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->ccname), &rp);
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 7ee4d09f..cdc8437f 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -42,7 +42,8 @@ struct dp_option default_krb5_opts[] = {
{ "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "krb5_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
- { "krb5_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+ { "krb5_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }
};
errno_t check_and_export_options(struct dp_option *opts,
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 68e4426a..c20c7b33 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -56,6 +56,7 @@ enum krb5_opts {
KRB5_STORE_PASSWORD_IF_OFFLINE,
KRB5_RENEWABLE_LIFETIME,
KRB5_LIFETIME,
+ KRB5_RENEW_INTERVAL,
KRB5_OPTS
};
@@ -77,6 +78,7 @@ struct krb5_service {
struct fo_service;
struct deferred_auth_ctx;
+struct renew_tgt_ctx;
struct krb5_ctx {
/* opts taken from kinit */
@@ -111,6 +113,7 @@ struct krb5_ctx {
pcre *illegal_path_re;
struct deferred_auth_ctx *deferred_auth_ctx;
+ struct renew_tgt_ctx *renew_tgt_ctx;
};
struct remove_info_files_ctx {
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index f2b5dd79..0f1ed41f 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -56,6 +56,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
const char *errstr;
int errval;
int errpos;
+ time_t renew_intv;
if (krb5_options == NULL) {
krb5_options = talloc_zero(bectx, struct krb5_options);
@@ -128,6 +129,15 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
}
}
+ renew_intv = dp_opt_get_int(ctx->opts, KRB5_RENEW_INTERVAL);
+ if (renew_intv > 0) {
+ ret = init_renew_tgt(ctx, bectx, bectx->ev, renew_intv);
+ if (ret != EOK) {
+ DEBUG(1, ("init_renew_tgt failed.\n"));
+ goto fail;
+ }
+ }
+
ret = check_and_export_options(ctx->opts, bectx->domain);
if (ret != EOK) {
DEBUG(1, ("check_and_export_options failed.\n"));
diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
new file mode 100644
index 00000000..be029fdc
--- /dev/null
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -0,0 +1,380 @@
+/*
+ SSSD
+
+ Kerberos 5 Backend Module -- Renew a TGT automatically
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2010 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 <security/pam_modules.h>
+
+#include "util/util.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_auth.h"
+
+#define INITIAL_TGT_TABLE_SIZE 10
+
+struct renew_tgt_ctx {
+ hash_table_t *tgt_table;
+ struct be_ctx *be_ctx;
+ struct tevent_context *ev;
+ struct krb5_ctx *krb5_ctx;
+ time_t timer_interval;
+ struct tevent_timer *te;
+ bool added_to_online_callbacks;
+};
+
+struct renew_data {
+ time_t start_time;
+ time_t lifetime;
+ time_t start_renew_at;
+ struct pam_data *pd;
+};
+
+struct auth_data {
+ struct be_ctx *be_ctx;
+ struct krb5_ctx *krb5_ctx;
+ struct pam_data *pd;
+ hash_table_t *table;
+ hash_key_t key;
+};
+
+
+static void renew_tgt_done(struct tevent_req *req);
+static void 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;
+
+ req = krb5_auth_send(auth_data, ev, auth_data->be_ctx, auth_data->pd,
+ auth_data->krb5_ctx);
+ if (req == NULL) {
+ DEBUG(1, ("krb5_auth_send failed.\n"));
+ talloc_free(auth_data);
+ return;
+ }
+
+ tevent_req_set_callback(req, renew_tgt_done, auth_data);
+}
+
+static void renew_tgt_done(struct tevent_req *req)
+{
+ struct auth_data *auth_data = tevent_req_callback_data(req,
+ struct auth_data);
+ int ret;
+ int pam_status = PAM_SYSTEM_ERR;
+ int dp_err;
+
+ ret = krb5_auth_recv(req, &pam_status, &dp_err);
+ talloc_free(req);
+ if (ret) {
+ DEBUG(1, ("krb5_auth request failed.\n"));
+ } else {
+ switch (pam_status) {
+ case PAM_SUCCESS:
+ DEBUG(4, ("Successfully renewed TGT for user [%s].\n",
+ auth_data->pd->user));
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ case PAM_AUTHTOK_LOCK_BUSY:
+ DEBUG(4, ("Cannot renewed TGT for user [%s] while offline, "
+ "will retry later.\n",
+ auth_data->pd->user));
+ break;
+ default:
+ DEBUG(1, ("Failed to renew TGT for user [%s].\n",
+ auth_data->pd->user));
+ ret = hash_delete(auth_data->table, &auth_data->key);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_delete failed.\n"));
+ }
+ }
+ }
+
+ talloc_zfree(auth_data);
+}
+
+static errno_t renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx)
+{
+ int ret;
+ hash_entry_t *entries;
+ unsigned long count;
+ size_t c;
+ time_t now;
+ struct auth_data *auth_data;
+ struct renew_data *renew_data;
+ struct tevent_timer *te;
+
+ ret = hash_entries(renew_tgt_ctx->tgt_table, &count, &entries);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_entries failed.\n"));
+ return ENOMEM;
+ }
+
+ now = time(NULL);
+
+ for (c = 0; c < count; c++) {
+ renew_data = talloc_get_type(entries[c].value.ptr, struct renew_data);
+ DEBUG(9, ("Checking [%s] for renewal at [%.24s].\n", entries[c].key.str,
+ ctime(&renew_data->start_renew_at)));
+ if (renew_data->start_renew_at < now) {
+ auth_data = talloc_zero(renew_tgt_ctx, struct auth_data);
+ if (auth_data == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ } else {
+ auth_data->pd = renew_data->pd;
+ auth_data->krb5_ctx = renew_tgt_ctx->krb5_ctx;
+ auth_data->be_ctx = renew_tgt_ctx->be_ctx;
+ auth_data->table = renew_tgt_ctx->tgt_table;
+ auth_data->key.type = entries[c].key.type;
+ auth_data->key.str = talloc_strdup(auth_data,
+ entries[c].key.str);
+ if (auth_data->key.str == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ te = NULL;
+ } else {
+ te = tevent_add_timer(renew_tgt_ctx->ev,
+ auth_data, tevent_timeval_current(),
+ renew_tgt, auth_data);
+ if (te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ }
+ }
+ }
+
+ if (auth_data == NULL || te == NULL) {
+ DEBUG(1, ("Failed to renew TGT in [%s].\n", entries[c].key.str));
+ ret = hash_delete(renew_tgt_ctx->tgt_table, &entries[c].key);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_delete failed.\n"));
+ }
+ }
+ }
+ }
+
+ talloc_free(entries);
+
+ return EOK;
+}
+
+static void renew_handler(struct renew_tgt_ctx *renew_tgt_ctx);
+
+static void renew_tgt_online_callback(void *private_data)
+{
+ struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(private_data,
+ struct renew_tgt_ctx);
+
+ renew_tgt_ctx->added_to_online_callbacks = false;
+ renew_handler(renew_tgt_ctx);
+}
+
+static void 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);
+
+ renew_handler(renew_tgt_ctx);
+}
+
+static void renew_handler(struct renew_tgt_ctx *renew_tgt_ctx)
+{
+ struct timeval next;
+ int ret;
+
+ if (be_is_offline(renew_tgt_ctx->be_ctx)) {
+ if (renew_tgt_ctx->added_to_online_callbacks) {
+ DEBUG(3, ("Renewal task was already added to online callbacks.\n"));
+ return;
+ }
+ DEBUG(7, ("Offline, adding renewal task to online callbacks.\n"));
+ ret = be_add_online_cb(renew_tgt_ctx->krb5_ctx, renew_tgt_ctx->be_ctx,
+ renew_tgt_online_callback, renew_tgt_ctx, NULL);
+ if (ret == EOK) {
+ renew_tgt_ctx->added_to_online_callbacks = true;
+ return;
+ }
+
+ DEBUG(1, ("Failed to add the renewal task to online callbacks, "
+ "continue normal operation.\n"));
+ } else {
+ ret = renew_all_tgts(renew_tgt_ctx);
+ if (ret != EOK) {
+ DEBUG(1, ("renew_all_tgts failed. "
+ "Disabling automatic TGT renewal\n"));
+ sss_log(SSS_LOG_ERR, "Disabling automatic TGT renewal.");
+ talloc_zfree(renew_tgt_ctx);
+ return;
+ }
+ }
+
+ DEBUG(7, ("Adding new renew timer.\n"));
+
+ next = tevent_timeval_current_ofs(renew_tgt_ctx->timer_interval,
+ 0);
+ renew_tgt_ctx->te = tevent_add_timer(renew_tgt_ctx->ev, renew_tgt_ctx,
+ next, renew_tgt_timer_handler,
+ renew_tgt_ctx);
+ if (renew_tgt_ctx->te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ sss_log(SSS_LOG_ERR, "Disabling automatic TGT renewal.");
+ talloc_zfree(renew_tgt_ctx);
+ }
+
+ return;
+}
+
+errno_t init_renew_tgt(struct krb5_ctx *krb5_ctx, struct be_ctx *be_ctx,
+ struct tevent_context *ev, 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(1, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+
+ ret = sss_hash_create(krb5_ctx->renew_tgt_ctx, INITIAL_TGT_TABLE_SIZE,
+ &krb5_ctx->renew_tgt_ctx->tgt_table);
+ if (ret != EOK) {
+ DEBUG(1, ("sss_hash_create failed.\n"));
+ goto fail;
+ }
+
+ krb5_ctx->renew_tgt_ctx->be_ctx = be_ctx;
+ krb5_ctx->renew_tgt_ctx->krb5_ctx = krb5_ctx;
+ krb5_ctx->renew_tgt_ctx->ev = ev;
+ krb5_ctx->renew_tgt_ctx->timer_interval = renew_intv;
+ krb5_ctx->renew_tgt_ctx->added_to_online_callbacks = false;
+
+
+ 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, renew_tgt_timer_handler,
+ krb5_ctx->renew_tgt_ctx);
+ if (krb5_ctx->renew_tgt_ctx->te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ return EOK;
+
+fail:
+ talloc_zfree(krb5_ctx->renew_tgt_ctx);
+ return ret;
+}
+
+errno_t add_tgt_to_renew_table(struct krb5_ctx *krb5_ctx, const char *ccfile,
+ struct tgt_times *tgtt, struct pam_data *pd)
+{
+ char *key_str = NULL;
+ int ret;
+ hash_key_t key;
+ hash_value_t value;
+ struct renew_data *renew_data = NULL;
+
+ if (krb5_ctx->renew_tgt_ctx == NULL) {
+ DEBUG(7 ,("Renew context not initialized, "
+ "automatic renewal not available.\n"));
+ return EOK;
+ }
+
+ if (pd->cmd != SSS_PAM_AUTHENTICATE && pd->cmd != SSS_CMD_RENEW &&
+ pd->cmd != SSS_PAM_CHAUTHTOK) {
+ DEBUG(1, ("Unexpected pam task [%d].\n", pd->cmd));
+ return EINVAL;
+ }
+
+ key.type = HASH_KEY_STRING;
+ if (ccfile[0] == '/') {
+ key_str = talloc_asprintf(NULL, "FILE:%s", ccfile);
+ if (key_str == NULL) {
+ DEBUG(1, ("talloc_asprintf doneed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ key_str = talloc_strdup(NULL, ccfile);
+ }
+ key.str = key_str;
+
+ renew_data = talloc_zero(krb5_ctx->renew_tgt_ctx, struct renew_data);
+ if (renew_data == NULL) {
+ DEBUG(1, ("talloc_zero doneed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ 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));
+
+ ret = copy_pam_data(renew_data, pd, &renew_data->pd);
+ if (ret != EOK) {
+ DEBUG(1, ("copy_pam_data doneed.\n"));
+ goto done;
+ }
+
+ if (renew_data->pd->newauthtok_type != SSS_AUTHTOK_TYPE_EMPTY) {
+ talloc_zfree(renew_data->pd->newauthtok);
+ renew_data->pd->newauthtok_size = 0;
+ renew_data->pd->newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY;
+ }
+
+ talloc_zfree(renew_data->pd->authtok);
+ renew_data->pd->authtok = (uint8_t *) talloc_strdup(renew_data->pd, key.str);
+ if (renew_data->pd->authtok == NULL) {
+ DEBUG(1, ("talloc_strdup failed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+ renew_data->pd->authtok_size = strlen((char *) renew_data->pd->authtok) + 1;
+ renew_data->pd->authtok_type = SSS_AUTHTOK_TYPE_CCFILE;
+
+ renew_data->pd->cmd = SSS_CMD_RENEW;
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = renew_data;
+
+ ret = hash_enter(krb5_ctx->renew_tgt_ctx->tgt_table, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_enter failed.\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ DEBUG(7, ("Added [%s] for renewal at [%.24s].\n", key_str,
+ ctime(&renew_data->start_renew_at)));
+
+ ret = EOK;
+
+done:
+ talloc_free(key_str);
+ if (ret != EOK) {
+ talloc_free(renew_data);
+ }
+ return ret;
+}