#ifndef LEAN_CLIENT
#include "k5-int.h"
+#include "int-proto.h"
#include "init_creds_ctx.h"
static krb5_error_code
krb5_data *salt,
krb5_data *params,
krb5_keyblock *as_key,
- void *gak_data)
+ void *gak_data,
+ k5_response_items *ritems)
{
krb5_keytab keytab = (krb5_keytab) gak_data;
krb5_error_code ret;
krb5_keytab_entry kt_ent;
krb5_keyblock *kt_key;
+ /* We don't need the password from the responder to create the AS key. */
+ if (as_key == NULL)
+ return 0;
+
/* if there's already a key of the correct etype, we're done.
if the etype is wrong, free the existing key, and make
a new one. */
return(ret);
}
+/* Return the list of etypes available for client in keytab. */
+static krb5_error_code
+lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
+ krb5_principal client, krb5_enctype **etypes_out)
+{
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ krb5_enctype *p, *etypes = NULL, etype;
+ krb5_kvno max_kvno = 0, vno;
+ krb5_error_code ret;
+ krb5_boolean match;
+ size_t count = 0;
+
+ *etypes_out = NULL;
+
+ if (keytab->ops->start_seq_get == NULL)
+ return EINVAL;
+ ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (ret != 0)
+ return ret;
+
+ while (!(ret = krb5_kt_next_entry(context, keytab, &entry, &cursor))) {
+ /* Extract what we need from the entry and free it. */
+ etype = entry.key.enctype;
+ vno = entry.vno;
+ match = krb5_principal_compare(context, entry.principal, client);
+ krb5_free_keytab_entry_contents(context, &entry);
+
+ /* Filter out old or non-matching entries and invalid enctypes. */
+ if (vno < max_kvno || !match || !krb5_c_valid_enctype(etype))
+ continue;
+
+ /* Update max_kvno and reset the list if we find a newer kvno. */
+ if (vno > max_kvno) {
+ max_kvno = vno;
+ free(etypes);
+ etypes = NULL;
+ count = 0;
+ }
+
+ /* Leave room for the terminator and possibly a second entry. */
+ p = realloc(etypes, (count + 3) * sizeof(*etypes));
+ if (p == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ etypes = p;
+ etypes[count++] = etype;
+ /* All DES key types work with des-cbc-crc, which is more likely to be
+ * accepted by the KDC (since MIT KDCs refuse des-cbc-md5). */
+ if (etype == ENCTYPE_DES_CBC_MD5 || etype == ENCTYPE_DES_CBC_MD4)
+ etypes[count++] = ENCTYPE_DES_CBC_CRC;
+ etypes[count] = 0;
+ }
+ if (ret != KRB5_KT_END)
+ goto cleanup;
+ ret = 0;
+
+ *etypes_out = etypes;
+ etypes = NULL;
+
+cleanup:
+ krb5_kt_end_seq_get(context, keytab, &cursor);
+ free(etypes);
+ return ret;
+}
+
+/* Move the entries in keytab_list (zero-terminated) to the front of req_list
+ * (of length req_len), preserving order otherwise. */
+static krb5_error_code
+sort_enctypes(krb5_enctype *req_list, int req_len, krb5_enctype *keytab_list)
+{
+ krb5_enctype *save_list;
+ int save_pos, req_pos, i;
+
+ save_list = malloc(req_len * sizeof(*save_list));
+ if (save_list == NULL)
+ return ENOMEM;
+
+ /* Sort req_list entries into the front of req_list or into save_list. */
+ req_pos = save_pos = 0;
+ for (i = 0; i < req_len; i++) {
+ if (k5_etypes_contains(keytab_list, req_list[i]))
+ req_list[req_pos++] = req_list[i];
+ else
+ save_list[save_pos++] = req_list[i];
+ }
+
+ /* Put the entries we saved back in at the end, in order. */
+ for (i = 0; i < save_pos; i++)
+ req_list[req_pos++] = save_list[i];
+ assert(req_pos == req_len);
+
+ free(save_list);
+ return 0;
+}
+
krb5_error_code KRB5_CALLCONV
krb5_init_creds_set_keytab(krb5_context context,
krb5_init_creds_context ctx,
krb5_keytab keytab)
{
+ krb5_enctype *etype_list;
+ krb5_error_code ret;
+ char *name;
+
ctx->gak_fct = get_as_key_keytab;
ctx->gak_data = keytab;
- return 0;
+ ret = lookup_etypes_for_keytab(context, keytab, ctx->request->client,
+ &etype_list);
+ if (ret) {
+ TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(context, ret);
+ return 0;
+ }
+ TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, etype_list);
+
+ /* Error out if we have no keys for the client principal. */
+ if (etype_list == NULL) {
+ ret = krb5_unparse_name(context, ctx->request->client, &name);
+ if (ret == 0) {
+ k5_setmsg(context, KRB5_KT_NOTFOUND,
+ _("Keytab contains no suitable keys for %s"), name);
+ }
+ krb5_free_unparsed_name(context, name);
+ return KRB5_KT_NOTFOUND;
+ }
+
+ /* Sort the request enctypes so the ones in the keytab appear first. */
+ ret = sort_enctypes(ctx->request->ktype, ctx->request->nktypes,
+ etype_list);
+ free(etype_list);
+ return ret;
+}
+
+static krb5_error_code
+get_init_creds_keytab(krb5_context context, krb5_creds *creds,
+ krb5_principal client, krb5_keytab keytab,
+ krb5_deltat start_time, const char *in_tkt_service,
+ krb5_get_init_creds_opt *options, int *use_master)
+{
+ krb5_error_code ret;
+ krb5_init_creds_context ctx = NULL;
+
+ ret = krb5_init_creds_init(context, client, NULL, NULL, start_time,
+ options, &ctx);
+ if (ret != 0)
+ goto cleanup;
+
+ if (in_tkt_service) {
+ ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
+ if (ret != 0)
+ goto cleanup;
+ }
+
+ ret = krb5_init_creds_set_keytab(context, ctx, keytab);
+ if (ret != 0)
+ goto cleanup;
+
+ ret = k5_init_creds_get(context, ctx, use_master);
+ if (ret != 0)
+ goto cleanup;
+
+ ret = krb5_init_creds_get_creds(context, ctx, creds);
+ if (ret != 0)
+ goto cleanup;
+
+cleanup:
+ krb5_init_creds_free(context, ctx);
+
+ return ret;
}
krb5_error_code KRB5_CALLCONV
krb5_principal client,
krb5_keytab arg_keytab,
krb5_deltat start_time,
- char *in_tkt_service,
+ const char *in_tkt_service,
krb5_get_init_creds_opt *options)
{
- krb5_error_code ret, ret2;
+ krb5_error_code ret;
int use_master;
krb5_keytab keytab;
+ struct errinfo errsave = EMPTY_ERRINFO;
if (arg_keytab == NULL) {
if ((ret = krb5_kt_default(context, &keytab)))
/* first try: get the requested tkt from any kdc */
- ret = krb5int_get_init_creds(context, creds, client, NULL, NULL,
- start_time, in_tkt_service, options,
- get_as_key_keytab, (void *) keytab,
- &use_master,NULL);
+ ret = get_init_creds_keytab(context, creds, client, keytab, start_time,
+ in_tkt_service, options, &use_master);
/* check for success */
if (!use_master) {
use_master = 1;
- ret2 = krb5int_get_init_creds(context, creds, client, NULL, NULL,
- start_time, in_tkt_service, options,
- get_as_key_keytab, (void *) keytab,
- &use_master, NULL);
-
- if (ret2 == 0) {
- ret = 0;
- goto cleanup;
- }
-
- /* if the master is unreachable, return the error from the
- slave we were able to contact */
-
- if ((ret2 == KRB5_KDC_UNREACH) ||
- (ret2 == KRB5_REALM_CANT_RESOLVE) ||
- (ret2 == KRB5_REALM_UNKNOWN))
+ k5_save_ctx_error(context, ret, &errsave);
+ ret = get_init_creds_keytab(context, creds, client, keytab,
+ start_time, in_tkt_service, options,
+ &use_master);
+ if (ret == 0)
goto cleanup;
- ret = ret2;
+ /* If the master is unreachable, return the error from the slave we
+ * were able to contact. */
+ if (ret == KRB5_KDC_UNREACH || ret == KRB5_REALM_CANT_RESOLVE ||
+ ret == KRB5_REALM_UNKNOWN)
+ ret = k5_restore_ctx_error(context, &errsave);
}
/* at this point, we have a response from the master. Since we don't
cleanup:
if (arg_keytab == NULL)
krb5_kt_close(context, keytab);
+ k5_clear_error(&errsave);
return(ret);
}
krb5_principal client_princ, server_princ;
int use_master = 0;
- retval = krb5int_populate_gic_opt(context, &opts,
- options, addrs, ktypes,
- pre_auth_types, creds);
+ retval = k5_populate_gic_opt(context, &opts, options, addrs, ktypes,
+ pre_auth_types, creds);
if (retval)
return retval;
goto cleanup;
server_princ = creds->server;
client_princ = creds->client;
- retval = krb5int_get_init_creds(context, creds, creds->client,
- krb5_prompter_posix, NULL,
- 0, server, opts,
- get_as_key_keytab, (void *)keytab,
- &use_master, ret_as_reply);
+ retval = k5_get_init_creds(context, creds, creds->client,
+ krb5_prompter_posix, NULL, 0, server, opts,
+ get_as_key_keytab, (void *)keytab, &use_master,
+ ret_as_reply);
krb5_free_unparsed_name( context, server);
if (retval) {
goto cleanup;