summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2014-10-18 22:03:13 +0200
committerJakub Hrozek <jhrozek@redhat.com>2014-11-18 20:33:42 +0100
commit2745b0156f12df7a7eb93d57716233243658e4d9 (patch)
treec0accf228b7d15d1d9114b7522ac3bf7b8efd492
parent7c5cd2e7711621af9163a41393e88896a91ac33b (diff)
downloadsssd-2745b0156f12df7a7eb93d57716233243658e4d9.zip
sssd-2745b0156f12df7a7eb93d57716233243658e4d9.tar.gz
sssd-2745b0156f12df7a7eb93d57716233243658e4d9.tar.xz
KRB5: Move all ccache operations to krb5_child.c
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 <sbose@redhat.com> Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-rw-r--r--Makefile.am13
-rw-r--r--src/providers/krb5/krb5_auth.c223
-rw-r--r--src/providers/krb5/krb5_ccache.c62
-rw-r--r--src/providers/krb5/krb5_ccache.h5
-rw-r--r--src/providers/krb5/krb5_child.c208
-rw-r--r--src/providers/krb5/krb5_child_handler.c13
-rw-r--r--src/tests/krb5_child-test.c3
-rw-r--r--src/util/util_errors.c1
-rw-r--r--src/util/util_errors.h1
9 files changed, 281 insertions, 248 deletions
diff --git a/Makefile.am b/Makefile.am
index 4a69ecb..5325d51 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 5ed5616..e791aee 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);
- /* 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;
- }
+ 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 c0f5b7b..7aa36b7 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 e39f96c..e47df36 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 7fa5f0c..94cd34e 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 71c7f9c..9396117 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 8826a28..a59863b 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 d5da646..c1ed0fb 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 2bc5766..f71ede8 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,