From 663c5a61e6ab5aeee901684b8b43176711d5554e Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Sat, 18 Oct 2014 22:03:13 +0200 Subject: [PATCH 87/92] KRB5: Move all ccache operations to krb5_child.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The credential cache operations must be now performed by the krb5_child completely, because the sssd_be process might be running as the sssd user who doesn't have access to the ccaches. src/providers/krb5/krb5_ccache.c is still linked against libsss_krb5 until we fix Kerberos ticket renewal as non-root. Also includes a new error code that indicates that the back end should remove the old ccache attribute -- the child can't do that if it's running as the user. Related: https://fedorahosted.org/sssd/ticket/2370 Reviewed-by: Sumit Bose Reviewed-by: Lukáš Slebodník --- Makefile.am | 13 +- src/providers/krb5/krb5_auth.c | 223 ++++---------------------------- src/providers/krb5/krb5_ccache.c | 62 ++++----- src/providers/krb5/krb5_ccache.h | 5 +- src/providers/krb5/krb5_child.c | 208 +++++++++++++++++++++++++++-- src/providers/krb5/krb5_child_handler.c | 13 ++ src/tests/krb5_child-test.c | 3 +- src/util/util_errors.c | 1 + src/util/util_errors.h | 1 + 9 files changed, 281 insertions(+), 248 deletions(-) diff --git a/Makefile.am b/Makefile.am index 4a69ecb0cfe48e20bde958c9351c2a5ece5ffffa..5325d51e7240ae39a546e68b2a2aea202b3dfdfa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2496,27 +2496,36 @@ libsss_ad_la_LDFLAGS = \ krb5_child_SOURCES = \ src/providers/krb5/krb5_child.c \ + src/providers/krb5/krb5_ccache.c \ src/providers/dp_pam_data_util.c \ src/util/user_info_msg.c \ src/util/sss_krb5.c \ + src/util/find_uid.c \ src/util/atomic_io.c \ src/util/authtok.c \ src/util/util.c \ src/util/signal.c \ + src/util/strtonum.c \ src/util/become_user.c \ src/sss_client/common.c \ $(NULL) krb5_child_CFLAGS = \ $(AM_CFLAGS) \ $(POPT_CFLAGS) \ - $(KRB5_CFLAGS) + $(KRB5_CFLAGS) \ + $(PCRE_CFLAGS) \ + $(SYSTEMD_LOGIN_CFLAGS) \ + $(NULL) krb5_child_LDADD = \ libsss_debug.la \ $(TALLOC_LIBS) \ $(POPT_LIBS) \ $(DHASH_LIBS) \ $(KRB5_LIBS) \ - $(CLIENT_LIBS) + $(CLIENT_LIBS) \ + $(PCRE_LIBS) \ + $(SYSTEMD_LOGIN_LIBS) \ + $(NULL) ldap_child_SOURCES = \ src/providers/ldap/ldap_child.c \ diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 5ed561601ac80e53ee795b458c5bf0ca410951bc..e791aee1c2d83f84ba617db1d5d93948c0e4e2a1 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -41,45 +41,6 @@ #include "providers/krb5/krb5_utils.h" #include "providers/krb5/krb5_ccache.h" -static errno_t -check_old_ccache(const char *old_ccache, struct krb5child_req *kr, - const char *realm, bool *active, bool *valid) -{ - errno_t ret; - - *active = false; - *valid = false; - - ret = sss_krb5_cc_verify_ccache(old_ccache, - kr->uid, kr->gid, - realm, kr->upn); - switch (ret) { - case ERR_NOT_FOUND: - case ENOENT: - DEBUG(SSSDBG_TRACE_FUNC, - "Saved ccache %s doesn't exist.\n", old_ccache); - return ENOENT; - case EINVAL: - /* cache found but no tgt or expired */ - case EOK: - *valid = true; - break; - default: - DEBUG(SSSDBG_OP_FAILURE, - "Cannot check if saved ccache %s is valid\n", - old_ccache); - return ret; - } - - ret = check_if_uid_is_active(kr->uid, active); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "check_if_uid_is_active failed.\n"); - return ret; - } - - return EOK; -} - static int krb5_mod_ccname(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, struct sss_domain_info *domain, @@ -225,7 +186,6 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd, return ENOMEM; } kr->is_offline = false; - kr->active_ccache = true; kr->run_as_user = true; talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup); @@ -276,47 +236,26 @@ static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx, } static errno_t krb5_auth_prepare_ccache_name(struct krb5child_req *kr, + struct ldb_message *user_msg, struct be_ctx *be_ctx) { const char *ccname_template; - errno_t ret; - if (!kr->is_offline) { - kr->is_offline = be_is_offline(be_ctx); + ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL); + + kr->ccname = expand_ccname_template(kr, kr, ccname_template, + kr->krb5_ctx->illegal_path_re, true, + be_ctx->domain->case_sensitive); + if (kr->ccname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n"); + return ENOMEM; } - /* The ccache file should be (re)created if one of the following conditions - * is true: - * - it doesn't exist (kr->ccname == NULL) - * - the backend is online and the current ccache file is not used, i.e - * the related user is currently not logged in and it is not a renewal - * request - * (!kr->is_offline && !kr->active_ccache && kr->pd->cmd != SSS_CMD_RENEW) - * - the backend is offline and the current cache file not used and - * it does not contain a valid tgt - * (kr->is_offline && !kr->active_ccache && !kr->valid_tgt) - */ - if (kr->ccname == NULL || - (kr->is_offline && !kr->active_ccache && !kr->valid_tgt) || - (!kr->is_offline && !kr->active_ccache && kr->pd->cmd != SSS_CMD_RENEW)) { - DEBUG(SSSDBG_TRACE_ALL, "Recreating ccache file.\n"); - ccname_template = dp_opt_get_cstring(kr->krb5_ctx->opts, - KRB5_CCNAME_TMPL); - kr->ccname = expand_ccname_template(kr, kr, ccname_template, - kr->krb5_ctx->illegal_path_re, - true, - be_ctx->domain->case_sensitive); - if (kr->ccname == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "expand_ccname_template failed.\n"); - return ENOMEM; - } - - ret = sss_krb5_precreate_ccache(kr->ccname, - kr->uid, kr->gid); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n"); - return ret; - } + kr->old_ccname = ldb_msg_find_attr_as_string(user_msg, + SYSDB_CCACHE_FILE, NULL); + if (kr->old_ccname == NULL) { + DEBUG(SSSDBG_TRACE_LIBS, + "No ccache file for user [%s] found.\n", kr->pd->user); } return EOK; @@ -402,7 +341,6 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, struct krb5_auth_state *state; struct ldb_result *res; struct krb5child_req *kr = NULL; - const char *ccache_file = NULL; const char *realm; struct tevent_req *req; struct tevent_req *subreq; @@ -588,45 +526,10 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, goto done; } - ccache_file = ldb_msg_find_attr_as_string(res->msgs[0], - SYSDB_CCACHE_FILE, - NULL); - if (ccache_file != NULL) { - ret = check_old_ccache(ccache_file, kr, realm, - &kr->active_ccache, - &kr->valid_tgt); - if (ret == ENOENT) { - DEBUG(SSSDBG_FUNC_DATA, - "Ignoring ccache attribute [%s], because it doesn't" - "exist.\n", ccache_file); - ccache_file = NULL; - } else if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "check_if_ccache_file_is_used failed.\n"); - ccache_file = NULL; - } - } else { - kr->active_ccache = false; - kr->valid_tgt = false; - DEBUG(SSSDBG_CONF_SETTINGS, - "No ccache file for user [%s] found.\n", pd->user); - } - DEBUG(SSSDBG_TRACE_ALL, - "Ccache_file is [%s] and is %s active and TGT is %s valid.\n", - ccache_file ? ccache_file : "not set", - kr->active_ccache ? "" : "not", - kr->valid_tgt ? "" : "not"); - if (ccache_file != NULL) { - kr->ccname = ccache_file; - kr->old_ccname = talloc_strdup(kr, ccache_file); - if (kr->old_ccname == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n"); - ret = ENOMEM; - goto done; - } - } else { - kr->ccname = NULL; - kr->old_ccname = NULL; + ret = krb5_auth_prepare_ccache_name(kr, res->msgs[0], state->be_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot prepare ccache names!\n"); + goto done; } break; @@ -669,7 +572,6 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq) struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state); struct krb5child_req *kr = state->kr; - char *msg; int ret; if (!state->search_kpasswd) { @@ -728,45 +630,8 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq) } } - ret = krb5_auth_prepare_ccache_name(kr, state->be_ctx); - if (ret) { - goto done; - } - - if (kr->is_offline) { - DEBUG(SSSDBG_TRACE_ALL, "Preparing for offline operation.\n"); - - if (kr->valid_tgt || kr->active_ccache) { - DEBUG(SSSDBG_TRACE_ALL, "Valid TGT available or " - "ccache file is already in use.\n"); - kr->ccname = kr->old_ccname; - msg = talloc_asprintf(kr->pd, - "%s=%s", CCACHE_ENV_NAME, kr->ccname); - if (msg == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); - } else { - ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM, - strlen(msg) + 1, (uint8_t *) msg); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); - } - } - - if (dp_opt_get_bool(kr->krb5_ctx->opts, - KRB5_STORE_PASSWORD_IF_OFFLINE)) { - krb5_auth_cache_creds(state->kr->krb5_ctx, - state->domain, - state->be_ctx->cdb, - kr->pd, kr->uid, - &state->pam_status, &state->dp_err); - } else { - state->pam_status = PAM_AUTHINFO_UNAVAIL; - state->dp_err = DP_ERR_OFFLINE; - } - ret = EOK; - goto done; - - } + if (!kr->is_offline) { + kr->is_offline = be_is_offline(state->be_ctx); } /* We need to keep the root privileges to read the keytab file if @@ -814,7 +679,6 @@ static void krb5_auth_done(struct tevent_req *subreq) char *renew_interval_str; time_t renew_interval_time = 0; bool use_enterprise_principal; - uint32_t user_info_type; ret = handle_child_recv(subreq, pd, &buf, &len); talloc_zfree(subreq); @@ -974,6 +838,14 @@ static void krb5_auth_done(struct tevent_req *subreq) } break; + case ERR_CREDS_EXPIRED_CCACHE: + ret = krb5_delete_ccname(state, state->sysdb, state->domain, + pd->user, kr->old_ccname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "krb5_delete_ccname failed.\n"); + } + /* FALLTHROUGH */ + case ERR_CREDS_EXPIRED: /* If the password is expired we can safely remove the ccache from the * cache and disk if it is not actively used anymore. This will allow @@ -981,14 +853,6 @@ static void krb5_auth_done(struct tevent_req *subreq) * used. */ if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache) { if (kr->old_ccname != NULL) { - ret = safe_remove_old_ccache_file(kr->old_ccname, NULL, - kr->uid, kr->gid); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Failed to remove old ccache file [%s], " - "please remove it manually.\n", kr->old_ccname); - } - ret = krb5_delete_ccname(state, state->sysdb, state->domain, pd->user, kr->old_ccname); if (ret != EOK) { @@ -1062,37 +926,6 @@ static void krb5_auth_done(struct tevent_req *subreq) goto done; } - ret = sss_krb5_check_ccache_princ(kr->uid, kr->gid, kr->ccname, kr->upn); - if (ret) { - if (res->otp == true && pd->cmd == SSS_PAM_CHAUTHTOK) { - DEBUG(SSSDBG_IMPORTANT_INFO, - "Password change succeeded but currently " - "post-chpass kinit is not implemented\n"); - - user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS; - ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t), - (const uint8_t *) &user_info_type); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); - /* Not fatal */ - } - } else { - DEBUG(SSSDBG_CRIT_FAILURE, - "No ccache for %s in %s?\n", kr->upn, kr->ccname); - goto done; - } - } - - if (kr->old_ccname) { - ret = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname, - kr->uid, kr->gid); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, - "Failed to remove old ccache file [%s], " - "please remove it manually.\n", kr->old_ccname); - } - } - ret = krb5_save_ccname(state, state->sysdb, state->domain, pd->user, kr->ccname); if (ret) { diff --git a/src/providers/krb5/krb5_ccache.c b/src/providers/krb5/krb5_ccache.c index c0f5b7b8ced3fd2d6d8cbbf4e3339caba60888ff..7aa36b744ddcf7e46edcc26405a5101645b8b546 100644 --- a/src/providers/krb5/krb5_ccache.c +++ b/src/providers/krb5/krb5_ccache.c @@ -374,49 +374,32 @@ done: /* This function is called only as a way to validate that we have the * right cache */ -errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid, - const char *ccname, const char *principal) +errno_t sss_krb5_check_ccache_princ(krb5_context kctx, + const char *ccname, + krb5_principal user_princ) { - struct sss_krb5_ccache *cc = NULL; + krb5_ccache kcc = NULL; krb5_principal ccprinc = NULL; - krb5_principal kprinc = NULL; krb5_error_code kerr; const char *cc_type; - TALLOC_CTX *tmp_ctx; errno_t ret; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); - return ENOMEM; - } - - ret = sss_open_ccache_as_user(tmp_ctx, ccname, uid, gid, &cc); - if (ret) { - goto done; - } - - cc_type = krb5_cc_get_type(cc->context, cc->ccache); - - DEBUG(SSSDBG_TRACE_INTERNAL, - "Searching for [%s] in cache of type [%s]\n", principal, cc_type); - - kerr = krb5_parse_name(cc->context, principal, &kprinc); - if (kerr != 0) { - KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr); - DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n"); + kerr = krb5_cc_resolve(kctx, ccname, &kcc); + if (kerr) { ret = ERR_INTERNAL; goto done; } - kerr = krb5_cc_get_principal(cc->context, cc->ccache, &ccprinc); + cc_type = krb5_cc_get_type(kctx, kcc); + + kerr = krb5_cc_get_principal(kctx, kcc, &ccprinc); if (kerr != 0) { - KRB5_DEBUG(SSSDBG_OP_FAILURE, cc->context, kerr); + KRB5_DEBUG(SSSDBG_OP_FAILURE, kctx, kerr); DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_get_principal failed.\n"); } if (ccprinc) { - if (krb5_principal_compare(cc->context, kprinc, ccprinc) == TRUE) { + if (krb5_principal_compare(kctx, user_princ, ccprinc) == TRUE) { /* found in the primary ccache */ ret = EOK; goto done; @@ -425,23 +408,23 @@ errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid, #ifdef HAVE_KRB5_CC_COLLECTION - if (krb5_cc_support_switch(cc->context, cc_type)) { + if (krb5_cc_support_switch(kctx, cc_type)) { - krb5_cc_close(cc->context, cc->ccache); - cc->ccache = NULL; + krb5_cc_close(kctx, kcc); + kcc = NULL; - kerr = krb5_cc_set_default_name(cc->context, ccname); + kerr = krb5_cc_set_default_name(kctx, ccname); if (kerr != 0) { - KRB5_DEBUG(SSSDBG_MINOR_FAILURE, cc->context, kerr); + KRB5_DEBUG(SSSDBG_MINOR_FAILURE, kctx, kerr); /* try to continue despite failure */ } - kerr = krb5_cc_cache_match(cc->context, kprinc, &cc->ccache); + kerr = krb5_cc_cache_match(kctx, user_princ, &kcc); if (kerr == 0) { ret = EOK; goto done; } - KRB5_DEBUG(SSSDBG_TRACE_INTERNAL, cc->context, kerr); + KRB5_DEBUG(SSSDBG_TRACE_INTERNAL, kctx, kerr); } #endif /* HAVE_KRB5_CC_COLLECTION */ @@ -449,11 +432,12 @@ errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid, ret = ERR_NOT_FOUND; done: - if (cc) { - krb5_free_principal(cc->context, ccprinc); - krb5_free_principal(cc->context, kprinc); + if (ccprinc) { + krb5_free_principal(kctx, ccprinc); + } + if (kcc) { + krb5_cc_close(kctx, kcc); } - talloc_free(tmp_ctx); return ret; } diff --git a/src/providers/krb5/krb5_ccache.h b/src/providers/krb5/krb5_ccache.h index e39f96cad6f46c4003103dce4eadf007bc0f8920..e47df3665e3f325cc56d34767b416662577cc048 100644 --- a/src/providers/krb5/krb5_ccache.h +++ b/src/providers/krb5/krb5_ccache.h @@ -39,8 +39,9 @@ errno_t sss_krb5_precreate_ccache(const char *ccname, uid_t uid, gid_t gid); errno_t sss_krb5_cc_destroy(const char *ccname, uid_t uid, gid_t gid); -errno_t sss_krb5_check_ccache_princ(uid_t uid, gid_t gid, - const char *ccname, const char *principal); +errno_t sss_krb5_check_ccache_princ(krb5_context kctx, + const char *ccname, + krb5_principal user_princ); errno_t sss_krb5_cc_verify_ccache(const char *ccname, uid_t uid, gid_t gid, const char *realm, const char *principal); diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 7fa5f0c344a4afe110afa08f479f283aefce8d23..94cd34e433cf6a197860d233fbf9ca30cd3eb535 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -33,6 +33,7 @@ #include "util/sss_krb5.h" #include "util/user_info_msg.h" #include "util/child_common.h" +#include "util/find_uid.h" #include "providers/dp_backend.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" @@ -61,6 +62,10 @@ struct krb5_req { const char *upn; uid_t uid; gid_t gid; + + char *old_ccname; + bool old_cc_valid; + bool old_cc_active; }; static krb5_context krb5_error_ctx; @@ -1021,6 +1026,24 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, goto done; } + /* Successfull authentication! Check if ccache contains the + * right principal... + */ + kerr = sss_krb5_check_ccache_princ(kr->ctx, kr->ccname, kr->creds->client); + if (kerr) { + DEBUG(SSSDBG_CRIT_FAILURE, + "No ccache for %s in %s?\n", kr->upn, kr->ccname); + goto done; + } + + kerr = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname, + kr->uid, kr->gid); + if (kerr != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Failed to remove old ccache file [%s], " + "please remove it manually.\n", kr->old_ccname); + } + kerr = add_ticket_times_and_upn_to_response(kr); if (kerr != 0) { DEBUG(SSSDBG_CRIT_FAILURE, @@ -1094,6 +1117,7 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim) int realm_length; size_t msg_len; uint8_t *msg; + uint32_t user_info_type; DEBUG(SSSDBG_TRACE_LIBS, "Password change operation\n"); @@ -1222,6 +1246,14 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim) krb5_free_cred_contents(kr->ctx, kr->creds); if (kr->otp == true) { + user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS; + ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, sizeof(uint32_t), + (const uint8_t *) &user_info_type); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n"); + /* Not fatal */ + } + sss_authtok_set_empty(kr->pd->newauthtok); return map_krb5_error(kerr); } @@ -1298,6 +1330,21 @@ static errno_t tgt_req_child(struct krb5_req *kr) krb5_free_cred_contents(kr->ctx, kr->creds); if (kerr == 0) { ret = ERR_CREDS_EXPIRED; + + /* If the password is expired we can safely remove the ccache from the + * cache and disk if it is not actively used anymore. This will allow + * to create a new random ccache if sshd with privilege separation is + * used. */ + if (kr->old_cc_active == false && kr->old_ccname) { + ret = safe_remove_old_ccache_file(kr->old_ccname, NULL, + kr->uid, kr->gid); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to remove old ccache file [%s], " + "please remove it manually.\n", kr->old_ccname); + } + ret = ERR_CREDS_EXPIRED_CCACHE; + } } else { ret = map_krb5_error(kerr); } @@ -1423,12 +1470,17 @@ static errno_t create_empty_ccache(struct krb5_req *kr) krb5_creds *creds = NULL; krb5_error_code kerr; - DEBUG(SSSDBG_TRACE_LIBS, "Creating empty ccache\n"); - - kerr = create_empty_cred(kr->ctx, kr->princ, &creds); - if (kerr == 0) { - kerr = create_ccache(kr->ccname, creds); + if (kr->old_cc_valid == false) { + DEBUG(SSSDBG_TRACE_LIBS, "Creating empty ccache\n"); + kerr = create_empty_cred(kr->ctx, kr->princ, &creds); + if (kerr == 0) { + kerr = create_ccache(kr->ccname, creds); + } + } else { + DEBUG(SSSDBG_TRACE_LIBS, "Existing ccache still valid, reusing\n"); + kerr = 0; } + if (kerr != 0) { KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); } else { @@ -1529,6 +1581,17 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p); if ((p + len ) > size) return EINVAL; + + if (len > 0) { + kr->old_ccname = talloc_strndup(pd, (char *)(buf + p), len); + if (kr->old_ccname == NULL) return ENOMEM; + p += len; + } else { + DEBUG(SSSDBG_TRACE_INTERNAL, "No old ccache\n"); + } + + SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p); + if ((p + len ) > size) return EINVAL; kr->keytab = talloc_strndup(pd, (char *)(buf + p), len); if (kr->keytab == NULL) return ENOMEM; p += len; @@ -1538,10 +1601,14 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, return ret; } - DEBUG(SSSDBG_CONF_SETTINGS, "ccname: [%s] keytab: [%s]\n", - kr->ccname, kr->keytab); + DEBUG(SSSDBG_CONF_SETTINGS, + "ccname: [%s] old_ccname: [%s] keytab: [%s]\n", + kr->ccname, + kr->old_ccname ? kr->old_ccname : "not set", + kr->keytab); } else { kr->ccname = NULL; + kr->old_ccname = NULL; kr->keytab = NULL; sss_authtok_set_empty(pd->authtok); } @@ -1870,6 +1937,126 @@ static errno_t check_use_fast(enum k5c_fast_opt *_fast_val) return EOK; } +static errno_t old_ccache_valid(struct krb5_req *kr, bool *_valid) +{ + errno_t ret; + bool valid; + + valid = false; + + ret = sss_krb5_cc_verify_ccache(kr->old_ccname, + kr->uid, kr->gid, + kr->realm, kr->upn); + switch (ret) { + case ERR_NOT_FOUND: + case ENOENT: + DEBUG(SSSDBG_TRACE_FUNC, + "Saved ccache %s doesn't exist, ignoring\n", kr->old_ccname); + break; + case EINVAL: + /* cache found but no tgt or expired */ + case EOK: + valid = true; + break; + default: + DEBUG(SSSDBG_OP_FAILURE, + "Cannot check if saved ccache %s is valid\n", + kr->old_ccname); + return ret; + } + + *_valid = valid; + return EOK; +} + +static int k5c_check_old_ccache(struct krb5_req *kr) +{ + errno_t ret; + + if (kr->old_ccname) { + ret = old_ccache_valid(kr, &kr->old_cc_valid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "old_ccache_valid failed.\n"); + return ret; + } + + ret = check_if_uid_is_active(kr->uid, &kr->old_cc_active); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "check_if_uid_is_active failed.\n"); + return ret; + } + + DEBUG(SSSDBG_TRACE_ALL, + "Ccache_file is [%s] and is %s active and TGT is %s valid.\n", + kr->old_ccname ? kr->old_ccname : "not set", + kr->old_cc_active ? "" : "not", + kr->old_cc_valid ? "" : "not"); + } + + return EOK; +} + +static int k5c_precreate_ccache(struct krb5_req *kr, uint32_t offline) +{ + errno_t ret; + + /* The ccache file should be (re)created if one of the following conditions + * is true: + * - it doesn't exist (kr->old_ccname == NULL) + * - the backend is online and the current ccache file is not used, i.e + * the related user is currently not logged in and it is not a renewal + * request + * (offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW) + * - the backend is offline and the current cache file not used and + * it does not contain a valid tgt + * (offline && !kr->old_cc_active && !kr->valid_tgt) + */ + if (kr->old_ccname == NULL || + (offline && !kr->old_cc_active && !kr->old_cc_valid) || + (!offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)) { + DEBUG(SSSDBG_TRACE_ALL, "Recreating ccache\n"); + + ret = sss_krb5_precreate_ccache(kr->ccname, kr->uid, kr->gid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n"); + return ret; + } + } else { + /* We can reuse the old ccache */ + kr->ccname = kr->old_ccname; + } + + return EOK; +} + +static int k5c_ccache_setup(struct krb5_req *kr, uint32_t offline) +{ + errno_t ret; + + if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) { + return EOK; + } + + ret = k5c_check_old_ccache(kr); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot check old ccache\n"); + return ret; + } + + /* Pre-creating the ccache must be done as root, otherwise we can't mkdir + * some of the DIR: cache components. One example is /run/user/$UID because + * logind doesn't create the directory until the session phase, whereas + * we need the directory during the auth phase already + */ + ret = k5c_precreate_ccache(kr, offline); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot precreate ccache\n"); + return ret; + } + + return EOK; +} + static int k5c_setup(struct krb5_req *kr, uint32_t offline) { krb5_error_code kerr; @@ -1881,6 +2068,11 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline) return kerr; } + kerr = k5c_ccache_setup(kr, offline); + if (kerr != EOK) { + return kerr; + } + if (offline || (fast_val == K5C_FAST_NEVER && kr->validate == false)) { /* If krb5_child was started as setuid, but we don't need to * perform either validation or FAST, just drop privileges to @@ -1974,7 +2166,7 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline) kerr = set_lifetime_options(kr->options); if (kerr != 0) { - DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n")); + DEBUG(SSSDBG_OP_FAILURE, "set_lifetime_options failed.\n"); return kerr; } diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c index 71c7f9c9f662e16b94afda0c8c0ae24666f0ba15..93961172c7a3a5d8f2a4fb320370037f188b5909 100644 --- a/src/providers/krb5/krb5_child_handler.c +++ b/src/providers/krb5/krb5_child_handler.c @@ -144,6 +144,11 @@ static errno_t create_send_buffer(struct krb5child_req *kr, kr->pd->cmd == SSS_PAM_CHAUTHTOK) { buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) + sss_authtok_get_size(kr->pd->authtok); + + buf->size += sizeof(uint32_t); + if (kr->old_ccname) { + buf->size += strlen(kr->old_ccname); + } } if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) { @@ -182,6 +187,14 @@ static errno_t create_send_buffer(struct krb5child_req *kr, SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->ccname), &rp); safealign_memcpy(&buf->data[rp], kr->ccname, strlen(kr->ccname), &rp); + if (kr->old_ccname) { + SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->old_ccname), &rp); + safealign_memcpy(&buf->data[rp], kr->old_ccname, + strlen(kr->old_ccname), &rp); + } else { + SAFEALIGN_SET_UINT32(&buf->data[rp], 0, &rp); + } + SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(keytab), &rp); safealign_memcpy(&buf->data[rp], keytab, strlen(keytab), &rp); diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c index 8826a28ed5ea064317c62682003dc0e9a6df01b6..a59863b4d8aa8bdcc241c87befd672a5e5c876a3 100644 --- a/src/tests/krb5_child-test.c +++ b/src/tests/krb5_child-test.c @@ -239,8 +239,7 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user, kr->ccname = expand_ccname_template(kr, kr, dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_CCNAME_TMPL), - kr->krb5_ctx->illegal_path_re, - true, true); + kr->krb5_ctx->illegal_path_re, true, true); if (!kr->ccname) goto fail; DEBUG(SSSDBG_FUNC_DATA, "ccname [%s] uid [%llu] gid [%llu]\n", diff --git a/src/util/util_errors.c b/src/util/util_errors.c index d5da64622eebe7f779816c7f2090da5b9a9b13f0..c1ed0fb634c447904b63335d1cd161b7e7914a08 100644 --- a/src/util/util_errors.c +++ b/src/util/util_errors.c @@ -31,6 +31,7 @@ struct err_string error_to_str[] = { { "Invalid credential type" }, /* ERR_INVALID_CRED_TYPE */ { "No credentials available" }, /* ERR_NO_CREDS */ { "Credentials are expired" }, /* ERR_CREDS_EXPIRED */ + { "Credentials are expired, old ccache was removed" }, /* ERR_CREDS_EXPIRED_CCACHE */ { "Failure setting user credentials"}, /* ERR_CREDS_INVALID */ { "No cached credentials available" }, /* ERR_NO_CACHED_CREDS */ { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */ diff --git a/src/util/util_errors.h b/src/util/util_errors.h index 2bc576605e613d674d38b54aae1604c0b044635f..f71ede8d0fa000627a1bd994ec8bd94a632b35b2 100644 --- a/src/util/util_errors.h +++ b/src/util/util_errors.h @@ -56,6 +56,7 @@ enum sssd_errors { ERR_CREDS_INVALID, ERR_NO_CACHED_CREDS, ERR_CACHED_CREDS_EXPIRED, + ERR_CREDS_EXPIRED_CCACHE, ERR_AUTH_DENIED, ERR_AUTH_FAILED, ERR_CHPASS_DENIED, -- 1.9.3