>From 01ff3039a720abe42f91bacdb5f2e4be4570dfb3 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik Date: Wed, 29 May 2013 09:57:38 +0200 Subject: [PATCH 2/2] Every time return directory for krb5 cache collection. Function krb5_cc_get_full_name is called only as a way to validate that, we have the right cache. Instead of returned name, location will be returned from function cc_dir_cache_for_princ. If old valid credential is found, than it will be reused. Old unvaid credential will be removed, new credential will be created and we switch to new credentails only if default principal is the same as newly created principal. https://fedorahosted.org/sssd/ticket/1936 --- src/providers/krb5/krb5_child.c | 201 ++++++++++++++++++++++++++++++++++++---- src/providers/krb5/krb5_utils.c | 5 +- 2 files changed, 187 insertions(+), 19 deletions(-) diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 74d730aaa2e84af111982a450dafd524d411f472..b95109f167f63667a85656a3619b3aee4c4ccdc9 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -436,6 +436,66 @@ done: return kerr; } +#ifdef HAVE_KRB5_DIRCACHE +static bool need_switch_to_principal(krb5_context ctx, krb5_principal princ) +{ + krb5_error_code kerr; + krb5_ccache default_cc = NULL; + krb5_principal default_princ = NULL; + char *default_full_name = NULL; + char *full_name = NULL; + bool ret = false; + + kerr = krb5_cc_default(ctx, &default_cc); + if (kerr !=0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + + kerr = krb5_cc_get_principal(ctx, default_cc, &default_princ); + if (kerr == KRB5_FCC_NOFILE) { + /* There is not any default cache. */ + ret = true; + goto done; + } else if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + + kerr = krb5_unparse_name(ctx, default_princ, &default_full_name); + if (kerr !=0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + + kerr = krb5_unparse_name(ctx, princ, &full_name); + if (kerr !=0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + + if (0 == strcmp(default_full_name, full_name)) { + ret = true; + } + +done: + if (default_cc != NULL) { + kerr = krb5_cc_close(ctx, default_cc); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } + } + + /* all functions can be safely called with NULL. */ + krb5_free_principal(ctx, default_princ); + krb5_free_unparsed_name(ctx, default_full_name); + krb5_free_unparsed_name(ctx, full_name); + + return ret; +} +#endif /* HAVE_KRB5_DIRCACHE */ + static krb5_error_code store_creds_in_ccache(krb5_context ctx, krb5_principal princ, krb5_ccache cc, krb5_creds *creds) @@ -466,10 +526,12 @@ store_creds_in_ccache(krb5_context ctx, krb5_principal princ, } #ifdef HAVE_KRB5_DIRCACHE - kerr = krb5_cc_switch(ctx, cc); - if (kerr != 0) { - KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr); - goto done; + if (need_switch_to_principal(ctx, princ)) { + kerr = krb5_cc_switch(ctx, cc); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr); + goto done; + } } #endif /* HAVE_KRB5_DIRCACHE */ @@ -1080,12 +1142,107 @@ done: } +static bool retrieved_valid_cred_from_cache(krb5_context ctx, + krb5_principal principal, + const char *realm, + const char *ccname, + krb5_creds **_creds) +{ + krb5_ccache tmp_cc = NULL; + krb5_error_code kerr; + krb5_creds mcred; + krb5_creds cred; + bool ret = false; + + krb5_principal server_principal = NULL; + krb5_principal client_principal = NULL; + TALLOC_CTX *tmp_ctx = NULL; + char *server_name = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new failed.\n")); + return false; + } + + kerr = krb5_cc_set_default_name(ctx, ccname); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr); + return false; + } + + kerr = krb5_cc_cache_match(ctx, principal, &tmp_cc); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + return false; + } + + server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm); + if (server_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf failed.\n")); + goto done; + } + + kerr = krb5_parse_name(ctx, server_name, &server_principal); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + + kerr = krb5_copy_principal(ctx, principal, &client_principal); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + + memset(&mcred, 0, sizeof(mcred)); + memset(&cred, 0, sizeof(cred)); + mcred.client = client_principal; + mcred.server = server_principal; + + kerr = krb5_cc_retrieve_cred(ctx, tmp_cc, 0, &mcred, &cred); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_INTERNAL, kerr); + goto done; + } + krb5_free_cred_contents(ctx, &mcred); + + if (cred.times.endtime <= time(NULL)) { + DEBUG(SSSDBG_MINOR_FAILURE, ("TGT is not valid.\n")); + kerr = krb5_cc_destroy(ctx, tmp_cc); + if (kerr !=0) { + KRB5_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr); + } + tmp_cc = NULL; + goto done; + } + + kerr = krb5_copy_creds(ctx, &cred, _creds); + krb5_free_cred_contents(ctx, &cred); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr); + goto done; + } + + ret = true; +done: + if (tmp_cc != NULL) { + kerr = krb5_cc_close(ctx, tmp_cc); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr); + } + } + talloc_zfree(tmp_ctx); + return ret; +} + static krb5_error_code get_and_save_tgt(struct krb5_req *kr, const char *password) { const char *realm_name; int realm_length; krb5_error_code kerr; + bool have_old_cache; kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options, @@ -1100,13 +1257,19 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, DEBUG(SSSDBG_TRACE_FUNC, ("Attempting kinit for realm [%s]\n",realm_name)); - kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, - discard_const(password), - sss_krb5_prompter, kr, 0, - NULL, kr->options); - if (kerr != 0) { - KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); - return kerr; + + have_old_cache = retrieved_valid_cred_from_cache(kr->ctx, kr->princ, + realm_name, kr->ccname, + &kr->creds); + if (!have_old_cache) { + kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, + discard_const(password), + sss_krb5_prompter, kr, 0, + NULL, kr->options); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + return kerr; + } } if (kr->validate) { @@ -1131,13 +1294,15 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, } } - /* Use the updated principal in the creds in case canonicalized */ - kerr = create_ccache(kr->uid, kr->gid, kr->ctx, - kr->creds ? kr->creds->client : kr->princ, - kr->ccname, kr->creds); - if (kerr != 0) { - KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); - goto done; + if (!have_old_cache) { + /* Use the updated principal in the creds in case canonicalized */ + kerr = create_ccache(kr->uid, kr->gid, kr->ctx, + kr->creds ? kr->creds->client : kr->princ, + kr->ccname, kr->creds); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } } kerr = add_ticket_times_and_upn_to_response(kr); diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c index 1883d785e6746c45e51c8b0fabe01afbad688d6d..3f16faa7fb238bbb9801029beed66293cd873c15 100644 --- a/src/providers/krb5/krb5_utils.c +++ b/src/providers/krb5/krb5_utils.c @@ -1164,6 +1164,9 @@ cc_dir_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location, return NULL; } + /* This function is called only as a way to validate that, + * we have the right cache + */ krberr = krb5_cc_get_full_name(context, ccache, &name); if (ccache) krb5_cc_close(context, ccache); krb5_free_context(context); @@ -1173,7 +1176,7 @@ cc_dir_cache_for_princ(TALLOC_CTX *mem_ctx, const char *location, return NULL; } - return talloc_strdup(mem_ctx, name); + return talloc_strdup(mem_ctx, location); } errno_t -- 1.8.1.4