summaryrefslogtreecommitdiffstats
path: root/src/lib/gssapi
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/gssapi')
-rw-r--r--src/lib/gssapi/krb5/acquire_cred.c511
-rw-r--r--src/lib/gssapi/krb5/gssapiP_krb5.h10
-rw-r--r--src/lib/gssapi/krb5/iakerb.c68
-rw-r--r--src/lib/gssapi/krb5/init_sec_context.c24
-rw-r--r--src/lib/gssapi/krb5/rel_cred.c3
-rw-r--r--src/lib/gssapi/krb5/val_cred.c2
6 files changed, 395 insertions, 223 deletions
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index 8b31990835..1972b1eb26 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -185,7 +185,6 @@ cleanup:
static OM_uint32
acquire_accept_cred(krb5_context context,
OM_uint32 *minor_status,
- krb5_gss_name_t desired_name,
krb5_keytab req_keytab,
krb5_gss_cred_id_rec *cred)
{
@@ -223,9 +222,9 @@ acquire_accept_cred(krb5_context context,
return GSS_S_CRED_UNAVAIL;
}
- if (desired_name != NULL) {
+ if (cred->name != NULL) {
/* Make sure we keys matching the desired name in the keytab. */
- code = check_keytab(context, kt, desired_name);
+ code = check_keytab(context, kt, cred->name);
if (code) {
krb5_kt_close(context, kt);
if (code == KRB5_KT_NOTFOUND) {
@@ -238,15 +237,8 @@ acquire_accept_cred(krb5_context context,
return GSS_S_CRED_UNAVAIL;
}
- assert(cred->name == NULL);
- code = kg_duplicate_name(context, desired_name, &cred->name);
- if (code) {
- *minor_status = code;
- return GSS_S_FAILURE;
- }
-
/* Open the replay cache for this principal. */
- code = krb5_get_server_rcache(context, &desired_name->princ->data[0],
+ code = krb5_get_server_rcache(context, &cred->name->princ->data[0],
&cred->rcache);
if (code) {
*minor_status = code;
@@ -307,65 +299,6 @@ get_ccache_leash(krb5_context context, krb5_principal desired_princ,
}
#endif /* USE_LEASH */
-/* Prepare to acquire credentials into ccache using password at
- * init_sec_context time. On success, cred takes ownership of ccache. */
-static krb5_error_code
-prep_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
- krb5_ccache ccache, krb5_principal desired_princ,
- gss_buffer_t password)
-{
- krb5_error_code code;
- krb5_principal ccache_princ;
- krb5_data pwdata = make_data(password->value, password->length), pwcopy;
- krb5_boolean eq;
- const char *cctype;
- krb5_ccache newcache = NULL;
-
- /* Check the ccache principal or initialize a new cache. */
- code = krb5_cc_get_principal(context, ccache, &ccache_princ);
- if (code == 0) {
- eq = krb5_principal_compare(context, ccache_princ, desired_princ);
- krb5_free_principal(context, ccache_princ);
- if (!eq) {
- cctype = krb5_cc_get_type(context, ccache);
- if (krb5_cc_support_switch(context, cctype)) {
- /* Make a new ccache within the collection. */
- code = krb5_cc_new_unique(context, cctype, NULL, &newcache);
- if (code)
- return code;
- } else
- return KG_CCACHE_NOMATCH;
- }
- } else if (code == KRB5_FCC_NOFILE) {
- /* Cache file does not exist; create and initialize one. */
- code = krb5_cc_initialize(context, ccache, desired_princ);
- if (code)
- return code;
- } else
- return code;
-
- /* Save the desired principal as the credential name if not already set. */
- if (!cred->name) {
- code = kg_init_name(context, desired_princ, NULL, NULL, NULL, 0,
- &cred->name);
- if (code)
- return code;
- }
-
- /* Stash the password for later. */
- code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
- if (code)
- return code;
- cred->password = pwcopy.data;
-
- if (newcache) {
- krb5_cc_close(context, ccache);
- cred->ccache = newcache;
- } else
- cred->ccache = ccache;
- return 0;
-}
-
/* Set fields in cred according to a ccache config entry whose key (in
* principal form) is config_princ and whose value is value. */
static krb5_error_code
@@ -386,36 +319,67 @@ scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred,
krb5_free_data_contents(context, &data0);
if (code)
return code;
+ } else if (data_eq_string(config_princ->data[1], KRB5_CONF_REFRESH_TIME) &&
+ cred->refresh_time == 0) {
+ code = krb5int_copy_data_contents_add0(context, value, &data0);
+ if (code)
+ return code;
+ cred->refresh_time = atol(data0.data);
+ krb5_free_data_contents(context, &data0);
}
return 0;
}
-/* Check ccache and scan it for its expiry time. On success, cred takes
- * ownership of ccache. */
+/* Return true if it appears that we can non-interactively get initial
+ * tickets for cred. */
+static krb5_boolean
+can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
+{
+ krb5_error_code code;
+ krb5_keytab_entry entry;
+
+ if (cred->password != NULL)
+ return TRUE;
+
+ /* If we don't know the client principal yet, check for any keytab keys. */
+ if (cred->name == NULL)
+ return !krb5_kt_have_content(context, cred->client_keytab);
+
+ /* Check if we have a keytab key for the client principal. */
+ code = krb5_kt_get_entry(context, cred->client_keytab, cred->name->princ,
+ 0, 0, &entry);
+ if (code) {
+ krb5_clear_error_message(context);
+ return FALSE;
+ }
+ krb5_free_keytab_entry_contents(context, &entry);
+ return TRUE;
+}
+
+/* Scan cred->ccache for name, expiry time, impersonator, refresh time. */
static krb5_error_code
-scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
- krb5_ccache ccache, krb5_principal desired_princ)
+scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
+ krb5_ccache ccache = cred->ccache;
krb5_principal ccache_princ = NULL, tgt_princ = NULL;
krb5_data *realm;
krb5_cc_cursor cursor;
krb5_creds creds;
krb5_timestamp endtime;
- int got_endtime = 0, is_tgt;
+ krb5_boolean is_tgt;
/* Turn off OPENCLOSE mode while extensive frobbing is going on. */
code = krb5_cc_set_flags(context, ccache, 0);
if (code)
return code;
+ /* Credentials cache principal must match the initiator name. */
code = krb5_cc_get_principal(context, ccache, &ccache_princ);
if (code != 0)
- return code;
-
- /* Credentials cache principal must match the initiator name. */
- if (desired_princ != NULL &&
- !krb5_principal_compare(context, ccache_princ, desired_princ)) {
+ goto cleanup;
+ if (cred->name != NULL &&
+ !krb5_principal_compare(context, ccache_princ, cred->name->princ)) {
code = KG_CCACHE_NOMATCH;
goto cleanup;
}
@@ -457,109 +421,298 @@ scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
is_tgt = krb5_principal_compare(context, tgt_princ, creds.server);
endtime = creds.times.endtime;
krb5_free_cred_contents(context, &creds);
- if (is_tgt || !got_endtime)
- cred->expire = creds.times.endtime;
- got_endtime = 1;
+ if (is_tgt)
+ cred->have_tgt = TRUE;
+ if (is_tgt || cred->expire == 0)
+ cred->expire = endtime;
}
krb5_cc_end_seq_get(context, ccache, &cursor);
if (code && code != KRB5_CC_END)
goto cleanup;
code = 0;
- if (!got_endtime) { /* ccache is empty. */
+ if (cred->expire == 0 && !can_get_initial_creds(context, cred)) {
code = KG_EMPTY_CCACHE;
goto cleanup;
}
(void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
- cred->ccache = ccache;
cleanup:
+ (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
krb5_free_principal(context, ccache_princ);
krb5_free_principal(context, tgt_princ);
return code;
}
-/* get credentials corresponding to the default credential cache.
- If successful, set the ccache-specific fields in cred.
-*/
+/* Find an existing or destination ccache for cred->name. */
+static krb5_error_code
+get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
+{
+ krb5_error_code code;
+ krb5_boolean can_get, have_collection;
+ krb5_ccache defcc = NULL;
+ krb5_principal princ = NULL;
+ const char *cctype;
+
+ assert(cred->name != NULL && cred->ccache == NULL);
+#ifdef USE_LEASH
+ return get_ccache_leash(context, cred->name->princ, &cred->ccache);
+#else
+ /* Check first whether we can acquire tickets, to avoid overwriting the
+ * extended error message from krb5_cc_cache_match. */
+ can_get = can_get_initial_creds(context, cred);
+
+ /* Look for an existing cache for the client principal. */
+ code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
+ if (code == 0)
+ return scan_ccache(context, cred);
+ if (code != KRB5_CC_NOTFOUND || !can_get)
+ return code;
+ krb5_clear_error_message(context);
+
+ /* There is no existing ccache, but we can acquire credentials. Get the
+ * default ccache to help decide where we should put them. */
+ code = krb5_cc_default(context, &defcc);
+ if (code)
+ return code;
+ cctype = krb5_cc_get_type(context, defcc);
+ have_collection = krb5_cc_support_switch(context, cctype);
+
+ /* We can use an empty default ccache if we're using a password or if
+ * there's no collection. */
+ if (cred->password != NULL || !have_collection) {
+ if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) {
+ cred->ccache = defcc;
+ defcc = NULL;
+ }
+ krb5_clear_error_message(context);
+ }
+
+ /* Otherwise, try to use a new cache in the collection. */
+ if (cred->ccache == NULL) {
+ if (!have_collection) {
+ code = KG_CCACHE_NOMATCH;
+ goto cleanup;
+ }
+ code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache);
+ if (code)
+ goto cleanup;
+ }
+
+cleanup:
+ krb5_free_principal(context, princ);
+ if (defcc != NULL)
+ krb5_cc_close(context, defcc);
+ return code;
+#endif /* not USE_LEASH */
+}
+
+/* Try to set cred->name using the client keytab. */
+static krb5_error_code
+get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred)
+{
+ krb5_error_code code;
+ krb5_principal princ;
+
+ assert(cred->name == NULL);
+ code = k5_kt_get_principal(context, cred->client_keytab, &princ);
+ if (code)
+ return code;
+ code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY,
+ &cred->name);
+ if (code) {
+ krb5_free_principal(context, princ);
+ return code;
+ }
+ return 0;
+}
+
+/* Make a note in ccache that we should attempt to refresh it from the client
+ * keytab at refresh_time. */
+static void
+set_refresh_time(krb5_context context, krb5_ccache ccache,
+ krb5_timestamp refresh_time)
+{
+ char buf[128];
+ krb5_data d;
+
+ snprintf(buf, sizeof(buf), "%ld", (long)refresh_time);
+ d = string2data(buf);
+ (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CONF_REFRESH_TIME,
+ &d);
+ krb5_clear_error_message(context);
+}
+
+/* Return true if it's time to refresh cred from the client keytab. If
+ * returning true, avoid retrying for 30 seconds. */
+krb5_boolean
+kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
+{
+ krb5_timestamp now;
+
+ if (krb5_timeofday(context, &now))
+ return FALSE;
+ if (cred->refresh_time != 0 && now >= cred->refresh_time) {
+ set_refresh_time(context, cred->ccache, cred->refresh_time + 30);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* If appropriate, make a note to refresh cred from the client keytab when it
+ * is halfway to expired. */
+void
+kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
+ krb5_ticket_times *times)
+{
+ krb5_timestamp refresh;
+
+ /* For now, we only mark keytab-acquired credentials for refresh. */
+ if (cred->password != NULL)
+ return;
+
+ /* Make a note to refresh these when they are halfway to expired. */
+ refresh = times->starttime + (times->endtime - times->starttime) / 2;
+ set_refresh_time(context, cred->ccache, refresh);
+}
+
+/* Get initial credentials using the supplied password or client keytab. */
+static krb5_error_code
+get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
+{
+ krb5_error_code code;
+ krb5_get_init_creds_opt *opt = NULL;
+ krb5_creds creds;
+
+ code = krb5_get_init_creds_opt_alloc(context, &opt);
+ if (code)
+ return code;
+ code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache);
+ if (code)
+ goto cleanup;
+ if (cred->password != NULL) {
+ code = krb5_get_init_creds_password(context, &creds, cred->name->princ,
+ cred->password, NULL, NULL, 0,
+ NULL, opt);
+ } else {
+ code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
+ cred->client_keytab, 0, NULL, opt);
+ }
+ if (code)
+ goto cleanup;
+ kg_cred_set_initial_refresh(context, cred, &creds.times);
+ cred->have_tgt = TRUE;
+ cred->expire = creds.times.endtime;
+ krb5_free_cred_contents(context, &creds);
+cleanup:
+ krb5_get_init_creds_opt_free(context, opt);
+ return code;
+}
+
+/* Get initial credentials if we ought to and are able to. */
+static krb5_error_code
+maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
+{
+ krb5_error_code code;
+
+ /* Don't get creds if we don't know the name or are doing IAKERB. */
+ if (cred->name == NULL || cred->iakerb_mech)
+ return 0;
+
+ /* Get creds if we have none or if it's time to refresh. */
+ if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
+ code = get_initial_cred(context, cred);
+ /* If we were trying to refresh and failed, we can keep going. */
+ if (code && cred->expire == 0)
+ return code;
+ krb5_clear_error_message(context);
+ }
+ return 0;
+}
static OM_uint32
acquire_init_cred(krb5_context context,
OM_uint32 *minor_status,
krb5_ccache req_ccache,
- krb5_principal desired_princ,
gss_buffer_t password,
krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
- krb5_ccache ccache = NULL;
+ krb5_data pwdata, pwcopy;
int caller_ccname = 0;
- cred->ccache = NULL;
-
- /* Load the GSS ccache name, if specified, into the context. */
+ /* Get ccache from caller if available. */
if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
return GSS_S_FAILURE;
if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status,
&caller_ccname)))
return GSS_S_FAILURE;
-
- /* Pick a credential cache. */
if (req_ccache != NULL) {
- code = krb5_cc_dup(context, req_ccache, &ccache);
+ code = krb5_cc_dup(context, req_ccache, &cred->ccache);
+ if (code)
+ goto error;
} else if (caller_ccname) {
/* Caller's ccache name has been set as the context default. */
- code = krb5int_cc_default(context, &ccache);
- } else if (desired_princ) {
- /* Try to find an appropriate ccache for the desired name. */
-#ifdef USE_LEASH
- code = get_ccache_leash(context, desired_princ, &ccache);
-#else
- code = krb5_cc_cache_match(context, desired_princ, &ccache);
- if (code == KRB5_CC_NOTFOUND && password != GSS_C_NO_BUFFER) {
- /* Grab the default ccache for now; if it's not empty, prep_ccache
- * will create a new one of the default type or error out. */
- krb5_clear_error_message(context);
- code = krb5_cc_default(context, &ccache);
- }
-#endif
- } else
- code = 0;
- if (code != 0) {
- *minor_status = code;
- return GSS_S_CRED_UNAVAIL;
+ code = krb5int_cc_default(context, &cred->ccache);
+ if (code)
+ goto error;
}
- if (ccache != NULL) {
- if (password != GSS_C_NO_BUFFER && desired_princ != NULL)
- code = prep_ccache(context, cred, ccache, desired_princ, password);
- else
- code = scan_ccache(context, cred, ccache, desired_princ);
- if (code != 0) {
- krb5_cc_close(context, ccache);
- *minor_status = code;
- return GSS_S_CRED_UNAVAIL;
+ code = krb5_kt_client_default(context, &cred->client_keytab);
+ if (code)
+ goto error;
+
+ if (password != GSS_C_NO_BUFFER) {
+ pwdata = make_data(password->value, password->length);
+ code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
+ if (code)
+ goto error;
+ cred->password = pwcopy.data;
+ }
+
+ if (cred->ccache != NULL) {
+ /* The caller specified a ccache; check what's in it. */
+ code = scan_ccache(context, cred);
+ if (code == KRB5_FCC_NOFILE) {
+ /* See if we can get initial creds. If the caller didn't specify
+ * a name, pick one from the client keytab. */
+ if (cred->name == NULL) {
+ if (!get_name_from_client_keytab(context, cred))
+ code = 0;
+ } else if (can_get_initial_creds(context, cred)) {
+ code = 0;
+ }
}
- cred->ccache = ccache;
- } else {
- /* We haven't decided on a ccache or principal yet, but fail now if
- * there are no krb5 credentials at all. */
+ if (code)
+ goto error;
+ } else if (cred->name != NULL) {
+ /* The caller specified a name but not a ccache; pick a cache. */
+ code = get_cache_for_name(context, cred);
+ if (code)
+ goto error;
+ }
+
+#ifndef USE_LEASH
+ /* If we haven't picked a name, make sure we have or can get any creds,
+ * unless we're using Leash and might be able to get them interactively. */
+ if (cred->name == NULL && !can_get_initial_creds(context, cred)) {
code = krb5_cccol_have_content(context);
- if (code != 0) {
- *minor_status = code;
- return GSS_S_CRED_UNAVAIL;
- }
+ if (code)
+ goto error;
}
+#endif
- /*
- * If the caller specified no ccache and no desired principal, leave
- * cred->ccache and cred->name NULL. They will be resolved later by
- * kg_cred_resolve(), possibly using the target principal name.
- */
+ code = maybe_get_initial_cred(context, cred);
+ if (code)
+ goto error;
*minor_status = 0;
return GSS_S_COMPLETE;
+
+error:
+ *minor_status = code;
+ return GSS_S_CRED_UNAVAIL;
}
static OM_uint32
@@ -619,13 +772,21 @@ acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
goto error_out;
}
+ if (name != NULL) {
+ code = kg_duplicate_name(context, name, &cred->name);
+ if (code) {
+ *minor_status = code;
+ return GSS_S_FAILURE;
+ }
+ }
+
#ifndef LEAN_CLIENT
/*
* If requested, acquire credentials for accepting. This will fill
* in cred->name if desired_princ is specified.
*/
if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
- ret = acquire_accept_cred(context, minor_status, name, keytab, cred);
+ ret = acquire_accept_cred(context, minor_status, keytab, cred);
if (ret != GSS_S_COMPLETE)
goto error_out;
}
@@ -636,8 +797,7 @@ acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
* in cred->name if it wasn't set above.
*/
if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
- ret = acquire_init_cred(context, minor_status, ccache,
- name ? name->princ : NULL, password, cred);
+ ret = acquire_init_cred(context, minor_status, ccache, password, cred);
if (ret != GSS_S_COMPLETE)
goto error_out;
}
@@ -702,8 +862,7 @@ kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
krb5_error_code code;
krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle;
krb5_gss_name_t tname = (krb5_gss_name_t)target_name;
- krb5_ccache ccache = NULL;
- krb5_principal client_princ = NULL;
+ krb5_principal client_princ;
*minor_status = 0;
@@ -712,37 +871,73 @@ kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
return maj;
k5_mutex_assert_locked(&cred->lock);
- if (cred->ccache != NULL || cred->usage == GSS_C_ACCEPT)
+ if (cred->usage == GSS_C_ACCEPT || cred->name != NULL)
return GSS_S_COMPLETE;
+ /* acquire_init_cred should have set both name and ccache, or neither. */
+ assert(cred->ccache == NULL);
- /* Pick a credential cache. */
if (tname != NULL) {
- code = krb5_cc_select(context, tname->princ, &ccache, &client_princ);
+ /* Use the target name to select an existing ccache or a principal. */
+ code = krb5_cc_select(context, tname->princ, &cred->ccache,
+ &client_princ);
if (code && code != KRB5_CC_NOTFOUND)
goto kerr;
+ if (client_princ != NULL) {
+ code = kg_init_name(context, client_princ, NULL, NULL, NULL,
+ KG_INIT_NAME_NO_COPY, &cred->name);
+ if (code) {
+ krb5_free_principal(context, client_princ);
+ goto kerr;
+ }
+ }
+ if (cred->ccache != NULL) {
+ code = scan_ccache(context, cred);
+ if (code)
+ goto kerr;
+ }
}
- if (ccache == NULL) {
- /*
- * Ideally we would get credentials for client_princ if it is set. At
- * the moment, we just get the default ccache (obtaining credentials if
- * the platform supports it) and check it against client_princ below.
- */
- code = krb5int_cc_default(context, &ccache);
+
+ /* If we still haven't picked a client principal, try using an existing
+ * default ccache. (On Windows, this may acquire initial creds.) */
+ if (cred->name == NULL) {
+ code = krb5int_cc_default(context, &cred->ccache);
if (code)
goto kerr;
+ code = scan_ccache(context, cred);
+ if (code == KRB5_FCC_NOFILE) {
+ /* Default ccache doesn't exist; fall through to client keytab. */
+ krb5_cc_close(context, cred->ccache);
+ cred->ccache = NULL;
+ } else if (code) {
+ goto kerr;
+ }
}
- code = scan_ccache(context, cred, ccache, client_princ);
- if (code) {
- krb5_cc_close(context, ccache);
- goto kerr;
+ /* If that didn't work, try getting a name from the client keytab. */
+ if (cred->name == NULL) {
+ code = get_name_from_client_keytab(context, cred);
+ if (code) {
+ code = KG_EMPTY_CCACHE;
+ goto kerr;
+ }
}
- krb5_free_principal(context, client_princ);
+ if (cred->name != NULL && cred->ccache == NULL) {
+ /* Pick a cache for the name we chose (from krb5_cc_select or from the
+ * client keytab). */
+ code = get_cache_for_name(context, cred);
+ if (code)
+ goto kerr;
+ }
+
+ /* Resolve name to ccache and possibly get initial credentials. */
+ code = maybe_get_initial_cred(context, cred);
+ if (code)
+ goto kerr;
+
return GSS_S_COMPLETE;
kerr:
- krb5_free_principal(context, client_princ);
k5_mutex_unlock(&cred->lock);
save_error_info(code, context);
*minor_status = code;
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index 5621dbdafa..9b0d6cc373 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -183,7 +183,10 @@ typedef struct _krb5_gss_cred_id_rec {
/* ccache (init) data */
krb5_ccache ccache;
+ krb5_keytab client_keytab;
+ krb5_boolean have_tgt;
krb5_timestamp expire;
+ krb5_timestamp refresh_time;
krb5_enctype *req_enctypes; /* limit negotiated enctypes to this list */
char *password;
} krb5_gss_cred_id_rec, *krb5_gss_cred_id_t;
@@ -476,6 +479,13 @@ krb5_to_gss_cred(krb5_context context,
krb5_creds *creds,
krb5_gss_cred_id_t *out_cred);
+krb5_boolean
+kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred);
+
+void
+kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
+ krb5_ticket_times *times);
+
OM_uint32
kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
gss_cred_id_t cred_handle, gss_name_t target_name);
diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
index 1b3236e664..1d73e2dab2 100644
--- a/src/lib/gssapi/krb5/iakerb.c
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -416,7 +416,7 @@ iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,
{
krb5_error_code code;
- if (cred->iakerb_mech == 0 || cred->password == NULL) {
+ if (cred->iakerb_mech == 0) {
code = EINVAL;
goto cleanup;
}
@@ -446,7 +446,13 @@ iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,
if (code != 0)
goto cleanup;
- code = krb5_init_creds_set_password(ctx->k5c, ctx->icc, cred->password);
+ if (cred->password != NULL) {
+ code = krb5_init_creds_set_password(ctx->k5c, ctx->icc,
+ cred->password);
+ } else {
+ code = krb5_init_creds_set_keytab(ctx->k5c, ctx->icc,
+ cred->client_keytab);
+ }
if (code != 0)
goto cleanup;
@@ -547,10 +553,17 @@ iakerb_initiator_step(iakerb_ctx_id_t ctx,
code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm,
&flags);
- if (code != 0)
- goto cleanup;
- if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) {
+ if (code != 0) {
+ if (cred->have_tgt) {
+ /* We were trying to refresh; keep going with current creds. */
+ ctx->state = IAKERB_TGS_REQ;
+ krb5_clear_error_message(ctx->k5c);
+ } else {
+ goto cleanup;
+ }
+ } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) {
krb5_init_creds_get_times(ctx->k5c, ctx->icc, &times);
+ kg_cred_set_initial_refresh(ctx->k5c, cred, &times);
cred->expire = times.endtime;
krb5_init_creds_free(ctx->k5c, ctx->icc);
@@ -650,43 +663,18 @@ iakerb_get_initial_state(iakerb_ctx_id_t ctx,
in_creds.times.endtime = now + time_req;
}
- code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
- cred->ccache,
+ /* Make an AS request if we have no creds or it's time to refresh them. */
+ if (cred->expire == 0 || kg_cred_time_to_refresh(ctx->k5c, cred)) {
+ *state = IAKERB_AS_REQ;
+ code = 0;
+ goto cleanup;
+ }
+
+ code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, cred->ccache,
&in_creds, &out_creds);
if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) {
- krb5_principal tgs;
- krb5_data *realm = krb5_princ_realm(ctx->k5c, in_creds.client);
-
- /* If we have a TGT for the client realm, can proceed to TGS-REQ. */
- code = krb5_build_principal_ext(ctx->k5c,
- &tgs,
- realm->length,
- realm->data,
- KRB5_TGS_NAME_SIZE,
- KRB5_TGS_NAME,
- realm->length,
- realm->data,
- NULL);
- if (code != 0)
- goto cleanup;
-
- in_creds.server = tgs;
-
- /* It would be nice if we could return KRB5KRB_AP_ERR_TKT_EXPIRED if
- * the TGT is expired, for consistency with the krb5 mech. As it
- * stands, we won't see the expired TGT and will return
- * KRB5_CC_NOTFOUND. */
- code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
- cred->ccache,
- &in_creds, &out_creds);
- if (code == KRB5_CC_NOTFOUND && cred->password != NULL) {
- *state = IAKERB_AS_REQ;
- code = 0;
- } else if (code == 0) {
- *state = IAKERB_TGS_REQ;
- krb5_free_creds(ctx->k5c, out_creds);
- }
- krb5_free_principal(ctx->k5c, tgs);
+ *state = cred->have_tgt ? IAKERB_TGS_REQ : IAKERB_AS_REQ;
+ code = 0;
} else if (code == 0) {
*state = IAKERB_AP_REQ;
krb5_free_creds(ctx->k5c, out_creds);
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index d0eda5f3d3..d4c987a09a 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -194,30 +194,6 @@ static krb5_error_code get_credentials(context, cred, server, now,
code = krb5_get_credentials(context, flags, cred->ccache,
&in_creds, &result_creds);
- if (code == KRB5_CC_NOTFOUND && cred->password != NULL &&
- !cred->iakerb_mech) {
- krb5_creds tgt_creds;
-
- memset(&tgt_creds, 0, sizeof(tgt_creds));
-
- /* No TGT in the ccache, but we can get one with the password. */
- code = krb5_get_init_creds_password(context, &tgt_creds,
- in_creds.client, cred->password,
- NULL, NULL, 0, NULL, NULL);
- if (code)
- goto cleanup;
-
- code = krb5_cc_store_cred(context, cred->ccache, &tgt_creds);
- if (code) {
- krb5_free_cred_contents(context, &tgt_creds);
- goto cleanup;
- }
- cred->expire = tgt_creds.times.endtime;
- krb5_free_cred_contents(context, &tgt_creds);
-
- code = krb5_get_credentials(context, flags, cred->ccache,
- &in_creds, &result_creds);
- }
if (code)
goto cleanup;
diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c
index a69fb19b94..8db7450860 100644
--- a/src/lib/gssapi/krb5/rel_cred.c
+++ b/src/lib/gssapi/krb5/rel_cred.c
@@ -57,6 +57,9 @@ krb5_gss_release_cred(minor_status, cred_handle)
} else
code1 = 0;
+ if (cred->client_keytab)
+ krb5_kt_close(context, cred->client_keytab);
+
#ifndef LEAN_CLIENT
if (cred->keytab)
code2 = krb5_kt_close(context, cred->keytab);
diff --git a/src/lib/gssapi/krb5/val_cred.c b/src/lib/gssapi/krb5/val_cred.c
index 46a9ae1863..234cf69c04 100644
--- a/src/lib/gssapi/krb5/val_cred.c
+++ b/src/lib/gssapi/krb5/val_cred.c
@@ -44,7 +44,7 @@ krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
return GSS_S_FAILURE;
}
- if (cred->ccache) {
+ if (cred->ccache && cred->expire != 0) {
if ((code = krb5_cc_get_principal(context, cred->ccache, &princ))) {
k5_mutex_unlock(&cred->lock);
*minor_status = code;