krb5_renew_tgt.c revision 9acfb09f7969a69f58bd45c856b01700541853ca
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose Kerberos 5 Backend Module -- Renew a TGT automatically
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose Sumit Bose <sbose@redhat.com>
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose Copyright (C) 2010 Red Hat
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose This program is free software; you can redistribute it and/or modify
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose it under the terms of the GNU General Public License as published by
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose the Free Software Foundation; either version 3 of the License, or
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose (at your option) any later version.
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose This program is distributed in the hope that it will be useful,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose but WITHOUT ANY WARRANTY; without even the implied warranty of
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose GNU General Public License for more details.
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose You should have received a copy of the GNU General Public License
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose along with this program. If not, see <http://www.gnu.org/licenses/>.
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose const char *ccfile;
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_tgt_done(struct tevent_req *req);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_tgt(struct tevent_context *ev, struct tevent_timer *te,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose struct auth_data *auth_data = talloc_get_type(private_data,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose req = krb5_auth_send(auth_data, ev, auth_data->be_ctx, auth_data->pd,
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose/* Give back the pam data to the renewal item to be able to retry at the next
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * time the renewals re run. */
08c427fc3cdec58b670de02a6c39d2ec4d753050Sumit Bose auth_data->renew_data->pd = talloc_steal(auth_data->renew_data,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose tevent_req_set_callback(req, renew_tgt_done, auth_data);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_tgt_done(struct tevent_req *req)
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose struct auth_data *auth_data = tevent_req_callback_data(req,
08c427fc3cdec58b670de02a6c39d2ec4d753050Sumit Bose auth_data->renew_data->pd = talloc_steal(auth_data->renew_data,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose DEBUG(4, ("Successfully renewed TGT for user [%s].\n",
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose/* In general a successful renewal will update the renewal item and free the
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * old data. But if the TGT has reached the end of his renewable lifetime it
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * will not be put into the list of renewable tickets again. In this case the
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * renewal item is not updated and the value from the hash and the one we have
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * stored are the same. Since the TGT cannot be renewed anymore we want to
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * remove it from the list of renewable tickets. */
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose ret = hash_lookup(auth_data->table, &auth_data->key, &value);
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose auth_data->renew_data == talloc_get_type(value.ptr,
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose "removing list entry for user [%s].\n",
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose ret = hash_delete(auth_data->table, &auth_data->key);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose DEBUG(4, ("Cannot renewed TGT for user [%s] while offline, "
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose "will retry later.\n",
08c427fc3cdec58b670de02a6c39d2ec4d753050Sumit Bose auth_data->renew_data->pd = talloc_steal(auth_data->renew_data,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose DEBUG(1, ("Failed to renew TGT for user [%s].\n",
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose ret = hash_delete(auth_data->table, &auth_data->key);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic errno_t renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx)
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose unsigned long count;
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose ret = hash_entries(renew_tgt_ctx->tgt_table, &count, &entries);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose for (c = 0; c < count; c++) {
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose renew_data = talloc_get_type(entries[c].value.ptr, struct renew_data);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose DEBUG(9, ("Checking [%s] for renewal at [%.24s].\n", renew_data->ccfile,
08c427fc3cdec58b670de02a6c39d2ec4d753050Sumit Bose /* If renew_data->pd == NULL a renewal request for this data is
08c427fc3cdec58b670de02a6c39d2ec4d753050Sumit Bose * currently running so we skip it. */
08c427fc3cdec58b670de02a6c39d2ec4d753050Sumit Bose if (renew_data->start_renew_at < now && renew_data->pd != NULL) {
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose auth_data = talloc_zero(renew_tgt_ctx, struct auth_data);
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose/* We need to steal the pam_data here, because a successful renewal of the
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * ticket might add a new renewal item to the list with the same key (upn).
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * This would delete renew_data and all its children. But we cannot be sure
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * that adding the new renewal item is the last operation of the renewal
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * process with access the pam_data. To be on the safe side we steal the
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * pam_data and make it a child of auth_data which is only freed after the
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * renewal process is finished. In the case of an error during renewal we
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * might want to steal the pam_data back to renew_data before freeing
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose * auth_data to allow a new renewal attempt. */
cc0f97794926a426ee82df343dc223c9648ed064Sumit Bose auth_data->pd = talloc_move(auth_data, &renew_data->pd);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose DEBUG(1, ("Failed to renew TGT in [%s].\n", renew_data->ccfile));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose ret = hash_delete(renew_tgt_ctx->tgt_table, &entries[c].key);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_handler(struct renew_tgt_ctx *renew_tgt_ctx);
7591a7368078c2b4cde744ede431260fd663903aSumit Bosestatic void renew_tgt_offline_callback(void *private_data)
7591a7368078c2b4cde744ede431260fd663903aSumit Bose struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(private_data,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_tgt_online_callback(void *private_data)
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(private_data,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_tgt_timer_handler(struct tevent_context *ev,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(data,
7591a7368078c2b4cde744ede431260fd663903aSumit Bose /* forget the timer event, it will be freed by the tevent timer loop */
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bosestatic void renew_handler(struct renew_tgt_ctx *renew_tgt_ctx)
7591a7368078c2b4cde744ede431260fd663903aSumit Bose "Disabling automatic TGT renewal\n"));
7591a7368078c2b4cde744ede431260fd663903aSumit Bose sss_log(SSS_LOG_ERR, "Disabling automatic TGT renewal.");
7591a7368078c2b4cde744ede431260fd663903aSumit Bose DEBUG(7, ("There is an active renewal timer, doing nothing.\n"));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose next = tevent_timeval_current_ofs(renew_tgt_ctx->timer_interval,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose renew_tgt_ctx->te = tevent_add_timer(renew_tgt_ctx->ev, renew_tgt_ctx,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose sss_log(SSS_LOG_ERR, "Disabling automatic TGT renewal.");
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bosestatic void renew_del_cb(hash_entry_t *entry, hash_destroy_enum type, void *pvt)
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose renew_data = talloc_get_type(entry->value.ptr, struct renew_data);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose DEBUG(1, ("Unexpected value type [%d].\n", entry->value.type));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bosestatic errno_t check_ccache_file(struct renew_tgt_ctx *renew_tgt_ctx,
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose if (ccache_file == NULL || upn == NULL || user_name == NULL) {
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(6, ("Missing one of the needed attributes: [%s][%s][%s].\n",
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose ccache_file == NULL ? "cache file missing" : ccache_file,
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose user_name == NULL ? "user name missing" : user_name));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(9, ("Found ccache file [%s].\n", ccache_file));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose ret = get_ccache_file_data(ccache_file, upn, &tgtt);
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose if (tgtt.renew_till > tgtt.endtime && tgtt.renew_till > now &&
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(7, ("Adding [%s] for automatic renewal.\n", ccache_file));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose ret = add_tgt_to_renew_table(renew_tgt_ctx->krb5_ctx, ccache_file,
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose "automatic renewal not possible.\n"));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(9, ("TGT in [%s] for [%s] is too old.\n", ccache_file, upn));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bosestatic errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
28269b2c1fa38e4579853b7afbe30381a8ab8912Sumit Bose const char *ccache_filter = "(&("SYSDB_CCACHE_FILE"=*)" \
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose const char *ccache_attrs[] = { SYSDB_CCACHE_FILE, SYSDB_UPN, SYSDB_NAME,
df0596ec12bc5091608371e2977f3111241e8cafSimo Sorce base_dn = sysdb_base_dn(renew_tgt_ctx->be_ctx->domain->sysdb, tmp_ctx);
28269b2c1fa38e4579853b7afbe30381a8ab8912Sumit Bose DEBUG(SSSDBG_OP_FAILURE, ("sysdb_base_dn failed.\n"));
df0596ec12bc5091608371e2977f3111241e8cafSimo Sorce ret = sysdb_search_entry(tmp_ctx, renew_tgt_ctx->be_ctx->domain->sysdb, base_dn,
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(9, ("No entries with ccache file found in cache.\n"));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(9, ("Found [%d] entries with ccache file in cache.\n", msgs_count));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose for (c = 0; c < msgs_count; c++) {
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose user_name = ldb_msg_find_attr_as_string(msgs[c], SYSDB_NAME, NULL);
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(1, ("No user name found, this is a severe error, "
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose "but we ignore it here.\n"));
10c50d237d6e3137499fcfaa5a804e6712e002eeSumit Bose /* The DNs of users in sysdb looks like
10c50d237d6e3137499fcfaa5a804e6712e002eeSumit Bose * name=username,cn=users,cn=domain.name,cn=sysdb
10c50d237d6e3137499fcfaa5a804e6712e002eeSumit Bose * the value of the third component (index 2) is the domain name. */
10c50d237d6e3137499fcfaa5a804e6712e002eeSumit Bose user_dom_val = ldb_dn_get_component_val(msgs[c]->dn, 2);
964628ab89229e9266adc5f4f8a26222734788b7Sumit Bose DEBUG(SSSDBG_OP_FAILURE, ("Invalid user DN [%s].\n",
964628ab89229e9266adc5f4f8a26222734788b7Sumit Bose user_dom = talloc_strndup(tmp_ctx, (char *) user_dom_val->data,
964628ab89229e9266adc5f4f8a26222734788b7Sumit Bose DEBUG(SSSDBG_OP_FAILURE, ("talloc_strndup failed,\n"));
964628ab89229e9266adc5f4f8a26222734788b7Sumit Bose ret = find_or_guess_upn(tmp_ctx, msgs[c], renew_tgt_ctx->krb5_ctx,
964628ab89229e9266adc5f4f8a26222734788b7Sumit Bose DEBUG(SSSDBG_OP_FAILURE, ("find_or_guess_upn failed.\n"));
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose ccache_file = ldb_msg_find_attr_as_string(msgs[c], SYSDB_CCACHE_FILE,
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose ret = check_ccache_file(renew_tgt_ctx, ccache_file, upn, user_name);
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(5, ("Failed to check ccache file [%s].\n", ccache_file));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Boseerrno_t init_renew_tgt(struct krb5_ctx *krb5_ctx, struct be_ctx *be_ctx,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose krb5_ctx->renew_tgt_ctx = talloc_zero(krb5_ctx, struct renew_tgt_ctx);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose ret = sss_hash_create_ex(krb5_ctx->renew_tgt_ctx, INITIAL_TGT_TABLE_SIZE,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose krb5_ctx->renew_tgt_ctx->timer_interval = renew_intv;
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose ret = check_ccache_files(krb5_ctx->renew_tgt_ctx);
318f12c90208971a5b6d3574f0026601161d81c7Sumit Bose DEBUG(1, ("Failed to read ccache files, continuing ...\n"));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose next = tevent_timeval_current_ofs(krb5_ctx->renew_tgt_ctx->timer_interval,
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose krb5_ctx->renew_tgt_ctx->te = tevent_add_timer(ev, krb5_ctx->renew_tgt_ctx,
7591a7368078c2b4cde744ede431260fd663903aSumit Bose DEBUG(7, ("Adding offline callback to remove renewal timer.\n"));
7591a7368078c2b4cde744ede431260fd663903aSumit Bose ret = be_add_offline_cb(krb5_ctx->renew_tgt_ctx, be_ctx,
7591a7368078c2b4cde744ede431260fd663903aSumit Bose renew_tgt_offline_callback, krb5_ctx->renew_tgt_ctx,
7591a7368078c2b4cde744ede431260fd663903aSumit Bose DEBUG(7, ("Adding renewal task to online callbacks.\n"));
7591a7368078c2b4cde744ede431260fd663903aSumit Bose ret = be_add_online_cb(krb5_ctx->renew_tgt_ctx, be_ctx,
7591a7368078c2b4cde744ede431260fd663903aSumit Bose renew_tgt_online_callback, krb5_ctx->renew_tgt_ctx,
7591a7368078c2b4cde744ede431260fd663903aSumit Bose DEBUG(1, ("Failed to add renewal task to online callbacks.\n"));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Boseerrno_t add_tgt_to_renew_table(struct krb5_ctx *krb5_ctx, const char *ccfile,
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose const char *upn)
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose "automatic renewal not available.\n"));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose if (pd->cmd != SSS_PAM_AUTHENTICATE && pd->cmd != SSS_CMD_RENEW &&
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose DEBUG(1, ("Unexpected pam task [%d].\n", pd->cmd));
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose /* hash_enter copies the content of the hash string, so it is safe to use
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose * discard_const_p here. */
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose renew_data = talloc_zero(krb5_ctx->renew_tgt_ctx, struct renew_data);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose renew_data->ccfile = talloc_asprintf(renew_data, "FILE:%s", ccfile);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose renew_data->ccfile = talloc_strdup(renew_data, ccfile);
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose renew_data->start_renew_at = (time_t) (tgtt->starttime +
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose ret = copy_pam_data(renew_data, pd, &renew_data->pd);
9acfb09f7969a69f58bd45c856b01700541853caLukas Slebodnik sss_authtok_set_empty(renew_data->pd->newauthtok);
9acfb09f7969a69f58bd45c856b01700541853caLukas Slebodnik ret = sss_authtok_set_ccfile(renew_data->pd->authtok, renew_data->ccfile, 0);
64af76e2bef2565caa9738f675c108a4b3789237Simo Sorce DEBUG(1, ("Failed to store ccfile in auth token.\n"));
f3f9ce8024d7610439d6c70ddafab1ab025cf8a8Sumit Bose ret = hash_enter(krb5_ctx->renew_tgt_ctx->tgt_table, &key, &value);
589dd0f6600515926e4e514442c62366db0a62b3Sumit Bose DEBUG(7, ("Added [%s] for renewal at [%.24s].\n", renew_data->ccfile,