1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/gic_keytab.c */
4 * Copyright (C) 2002, 2003, 2008 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
29 #include "int-proto.h"
30 #include "init_creds_ctx.h"
32 static krb5_error_code
33 get_as_key_keytab(krb5_context context,
34 krb5_principal client,
36 krb5_prompter_fct prompter,
40 krb5_keyblock *as_key,
43 krb5_keytab keytab = (krb5_keytab) gak_data;
45 krb5_keytab_entry kt_ent;
46 krb5_keyblock *kt_key;
48 /* if there's already a key of the correct etype, we're done.
49 if the etype is wrong, free the existing key, and make
53 if (as_key->enctype == etype)
56 krb5_free_keyblock_contents(context, as_key);
60 if (!krb5_c_valid_enctype(etype))
61 return(KRB5_PROG_ETYPE_NOSUPP);
63 if ((ret = krb5_kt_get_entry(context, keytab, client,
64 0, /* don't have vno available */
68 ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);
70 /* again, krb5's memory management is lame... */
75 (void) krb5_kt_free_entry(context, &kt_ent);
80 /* Return the list of etypes available for client in keytab. */
81 static krb5_error_code
82 lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
83 krb5_principal client, krb5_enctype **etypes_out)
85 krb5_kt_cursor cursor;
86 krb5_keytab_entry entry;
87 krb5_enctype *p, *etypes = NULL;
88 krb5_kvno max_kvno = 0;
94 if (keytab->ops->start_seq_get == NULL)
96 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
101 ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
102 if (ret == KRB5_KT_END)
107 if (!krb5_c_valid_enctype(entry.key.enctype))
109 if (!krb5_principal_compare(context, entry.principal, client))
111 /* Make sure our list is for the highest kvno found for client. */
112 if (entry.vno > max_kvno) {
116 max_kvno = entry.vno;
117 } else if (entry.vno != max_kvno)
120 /* Leave room for the terminator and possibly a second entry. */
121 p = realloc(etypes, (count + 3) * sizeof(*etypes));
127 etypes[count++] = entry.key.enctype;
128 /* All DES key types work with des-cbc-crc, which is more likely to be
129 * accepted by the KDC (since MIT KDCs refuse des-cbc-md5). */
130 if (entry.key.enctype == ENCTYPE_DES_CBC_MD5 ||
131 entry.key.enctype == ENCTYPE_DES_CBC_MD4)
132 etypes[count++] = ENCTYPE_DES_CBC_CRC;
137 *etypes_out = etypes;
140 krb5_kt_end_seq_get(context, keytab, &cursor);
145 /* Return true if search_for is in etype_list. */
147 check_etypes_have(krb5_enctype *etype_list, krb5_enctype search_for)
154 for (i = 0; etype_list[i] != 0; i++) {
155 if (etype_list[i] == search_for)
162 krb5_error_code KRB5_CALLCONV
163 krb5_init_creds_set_keytab(krb5_context context,
164 krb5_init_creds_context ctx,
167 krb5_enctype *etype_list;
172 ctx->gak_fct = get_as_key_keytab;
173 ctx->gak_data = keytab;
175 ret = lookup_etypes_for_keytab(context, keytab, ctx->request->client,
178 TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(context, ret);
182 TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, etype_list);
184 /* Filter the ktypes list based on what's in the keytab */
185 for (i = 0, j = 0; i < ctx->request->nktypes; i++) {
186 if (check_etypes_have(etype_list, ctx->request->ktype[i])) {
187 ctx->request->ktype[j] = ctx->request->ktype[i];
191 ctx->request->nktypes = j;
194 /* Error out now if there's no overlap. */
195 if (ctx->request->nktypes == 0) {
196 ret = krb5_unparse_name(context, ctx->request->client, &name);
198 krb5_set_error_message(context, KRB5_KT_NOTFOUND,
199 _("Keytab contains no suitable keys for "
202 krb5_free_unparsed_name(context, name);
203 return KRB5_KT_NOTFOUND;
209 static krb5_error_code
210 get_init_creds_keytab(krb5_context context, krb5_creds *creds,
211 krb5_principal client, krb5_keytab keytab,
212 krb5_deltat start_time, char *in_tkt_service,
213 krb5_get_init_creds_opt *options, int *use_master)
216 krb5_init_creds_context ctx = NULL;
218 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time,
223 if (in_tkt_service) {
224 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
229 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
233 ret = k5_init_creds_get(context, ctx, use_master);
237 ret = krb5_init_creds_get_creds(context, ctx, creds);
242 krb5_init_creds_free(context, ctx);
247 krb5_error_code KRB5_CALLCONV
248 krb5_get_init_creds_keytab(krb5_context context,
250 krb5_principal client,
251 krb5_keytab arg_keytab,
252 krb5_deltat start_time,
253 char *in_tkt_service,
254 krb5_get_init_creds_opt *options)
256 krb5_error_code ret, ret2;
260 if (arg_keytab == NULL) {
261 if ((ret = krb5_kt_default(context, &keytab)))
269 /* first try: get the requested tkt from any kdc */
271 ret = get_init_creds_keytab(context, creds, client, keytab, start_time,
272 in_tkt_service, options, &use_master);
274 /* check for success */
279 /* If all the kdc's are unavailable fail */
281 if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE))
284 /* if the reply did not come from the master kdc, try again with
290 ret2 = get_init_creds_keytab(context, creds, client, keytab,
291 start_time, in_tkt_service, options,
299 /* if the master is unreachable, return the error from the
300 slave we were able to contact */
302 if ((ret2 == KRB5_KDC_UNREACH) ||
303 (ret2 == KRB5_REALM_CANT_RESOLVE) ||
304 (ret2 == KRB5_REALM_UNKNOWN))
310 /* at this point, we have a response from the master. Since we don't
311 do any prompting or changing for keytabs, that's it. */
314 if (arg_keytab == NULL)
315 krb5_kt_close(context, keytab);
319 krb5_error_code KRB5_CALLCONV
320 krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options,
321 krb5_address *const *addrs, krb5_enctype *ktypes,
322 krb5_preauthtype *pre_auth_types,
323 krb5_keytab arg_keytab, krb5_ccache ccache,
324 krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
326 krb5_error_code retval;
327 krb5_get_init_creds_opt *opts;
328 char * server = NULL;
330 krb5_principal client_princ, server_princ;
333 retval = krb5int_populate_gic_opt(context, &opts,
334 options, addrs, ktypes,
335 pre_auth_types, creds);
339 if (arg_keytab == NULL) {
340 retval = krb5_kt_default(context, &keytab);
344 else keytab = arg_keytab;
346 retval = krb5_unparse_name( context, creds->server, &server);
349 server_princ = creds->server;
350 client_princ = creds->client;
351 retval = krb5int_get_init_creds(context, creds, creds->client,
352 krb5_prompter_posix, NULL,
354 get_as_key_keytab, (void *)keytab,
355 &use_master, ret_as_reply);
356 krb5_free_unparsed_name( context, server);
360 krb5_free_principal(context, creds->server);
361 krb5_free_principal(context, creds->client);
362 creds->client = client_princ;
363 creds->server = server_princ;
365 /* store it in the ccache! */
367 if ((retval = krb5_cc_store_cred(context, ccache, creds)))
370 krb5_get_init_creds_opt_free(context, opts);
371 if (arg_keytab == NULL)
372 krb5_kt_close(context, keytab);
376 #endif /* LEAN_CLIENT */