1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/get_in_tkt.c */
3 /*
4 * Copyright 1990,1991, 2003, 2008 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
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.
11 *
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.
25 */
26
27 #include "k5-int.h"
28 #include "int-proto.h"
29 #include "os-proto.h"
30 #include "fast.h"
31 #include "init_creds_ctx.h"
32
33 /* some typedef's for the function args to make things look a bit cleaner */
34
35 static krb5_error_code make_preauth_list (krb5_context,
36 krb5_preauthtype *,
37 int, krb5_pa_data ***);
38 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
39 krb5_data *realm,
40 krb5_pa_data **padata);
41
42 /*
43 * Decrypt the AS reply in ctx, populating ctx->reply->enc_part2. If
44 * strengthen_key is not null, combine it with the reply key as specified in
45 * RFC 6113 section 5.4.3. Place the key used in *key_out.
46 */
47 static krb5_error_code
decrypt_as_reply(krb5_context context,krb5_init_creds_context ctx,const krb5_keyblock * strengthen_key,krb5_keyblock * key_out)48 decrypt_as_reply(krb5_context context, krb5_init_creds_context ctx,
49 const krb5_keyblock *strengthen_key, krb5_keyblock *key_out)
50 {
51 krb5_error_code ret;
52 krb5_keyblock key;
53 krb5_responder_fn responder;
54 void *responder_data;
55
56 memset(key_out, 0, sizeof(*key_out));
57 memset(&key, 0, sizeof(key));
58
59 if (ctx->as_key.length) {
60 /* The reply key was computed or replaced during preauth processing;
61 * try it. */
62 TRACE_INIT_CREDS_AS_KEY_PREAUTH(context, &ctx->as_key);
63 ret = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
64 &key);
65 if (ret)
66 return ret;
67 ret = krb5_kdc_rep_decrypt_proc(context, &key, NULL, ctx->reply);
68 if (!ret) {
69 *key_out = key;
70 return 0;
71 }
72 krb5_free_keyblock_contents(context, &key);
73 TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(context, ret);
74
75 /*
76 * For two reasons, we fall back to trying or retrying the gak_fct if
77 * this fails:
78 *
79 * 1. The KDC might encrypt the reply using a different enctype than
80 * the AS key we computed during preauth.
81 *
82 * 2. For 1.1.1 and prior KDC's, when SAM is used with USE_SAD_AS_KEY,
83 * the AS-REP is encrypted in the client long-term key instead of
84 * the SAD.
85 *
86 * The gak_fct for krb5_get_init_creds_with_password() caches the
87 * password, so this fallback does not result in a second password
88 * prompt.
89 */
90 } else {
91 /*
92 * No AS key was computed during preauth processing, perhaps because
93 * preauth was not used. If the caller supplied a responder callback,
94 * possibly invoke it before calling the gak_fct for real.
95 */
96 k5_gic_opt_get_responder(ctx->opt, &responder, &responder_data);
97 if (responder != NULL) {
98 /* Indicate a need for the AS key by calling the gak_fct with a
99 * NULL as_key. */
100 ret = ctx->gak_fct(context, ctx->request->client, ctx->etype, NULL,
101 NULL, NULL, NULL, NULL, ctx->gak_data,
102 ctx->rctx.items);
103 if (ret)
104 return ret;
105
106 /* If that produced a responder question, invoke the responder. */
107 if (!k5_response_items_empty(ctx->rctx.items)) {
108 ret = (*responder)(context, responder_data, &ctx->rctx);
109 if (ret)
110 return ret;
111 }
112 }
113 }
114
115 /* Compute or re-compute the AS key, prompting for the password if
116 * necessary. */
117 TRACE_INIT_CREDS_GAK(context, &ctx->salt, &ctx->s2kparams);
118 ret = ctx->gak_fct(context, ctx->request->client,
119 ctx->reply->enc_part.enctype, ctx->prompter,
120 ctx->prompter_data, &ctx->salt, &ctx->s2kparams,
121 &ctx->as_key, ctx->gak_data, ctx->rctx.items);
122 if (ret)
123 return ret;
124 TRACE_INIT_CREDS_AS_KEY_GAK(context, &ctx->as_key);
125
126 ret = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key, &key);
127 if (ret)
128 return ret;
129 ret = krb5_kdc_rep_decrypt_proc(context, &key, NULL, ctx->reply);
130 if (ret) {
131 krb5_free_keyblock_contents(context, &key);
132 return ret;
133 }
134
135 *key_out = key;
136 return 0;
137 }
138
139 /**
140 * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC
141 * contribution key. This routine confirms that the session key is of the
142 * right form for fully anonymous requests. It is here rather than in the
143 * preauth code because the session key cannot be verified until the AS reply
144 * is decrypted and the preauth code all runs before the AS reply is decrypted.
145 */
146 static krb5_error_code
verify_anonymous(krb5_context context,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * as_key)147 verify_anonymous( krb5_context context, krb5_kdc_req *request,
148 krb5_kdc_rep *reply, krb5_keyblock *as_key)
149 {
150 krb5_error_code ret = 0;
151 krb5_pa_data *pa;
152 krb5_data scratch;
153 krb5_keyblock *kdc_key = NULL, *expected = NULL;
154 krb5_enc_data *enc = NULL;
155 krb5_keyblock *session = reply->enc_part2->session;
156
157 if (!krb5_principal_compare_any_realm(context, request->client,
158 krb5_anonymous_principal()))
159 return 0; /* Only applies to fully anonymous */
160 pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX);
161 if (pa == NULL)
162 goto verification_error;
163 scratch.length = pa->length;
164 scratch.data = (char *) pa->contents;
165 ret = decode_krb5_enc_data( &scratch, &enc);
166 if (ret)
167 goto cleanup;
168 scratch.data = k5alloc(enc->ciphertext.length, &ret);
169 if (ret)
170 goto cleanup;
171 scratch.length = enc->ciphertext.length;
172 ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX,
173 NULL /*cipherstate*/, enc, &scratch);
174 if (ret) {
175 free(scratch.data);
176 goto cleanup;
177 }
178 ret = decode_krb5_encryption_key( &scratch, &kdc_key);
179 zap(scratch.data, scratch.length);
180 free(scratch.data);
181 if (ret)
182 goto cleanup;
183 ret = krb5_c_fx_cf2_simple(context, kdc_key, "PKINIT",
184 as_key, "KEYEXCHANGE", &expected);
185 if (ret)
186 goto cleanup;
187 if ((expected->enctype != session->enctype) ||
188 (expected->length != session->length) ||
189 (memcmp(expected->contents, session->contents, expected->length) != 0))
190 goto verification_error;
191 cleanup:
192 if (kdc_key)
193 krb5_free_keyblock(context, kdc_key);
194 if (expected)
195 krb5_free_keyblock(context, expected);
196 if (enc)
197 krb5_free_enc_data(context, enc);
198 return ret;
199 verification_error:
200 ret = KRB5_KDCREP_MODIFIED;
201 k5_setmsg(context, ret,
202 _("Reply has wrong form of session key for anonymous request"));
203 goto cleanup;
204 }
205
206 static krb5_error_code
verify_as_reply(krb5_context context,krb5_timestamp time_now,krb5_kdc_req * request,krb5_kdc_rep * as_reply)207 verify_as_reply(krb5_context context,
208 krb5_timestamp time_now,
209 krb5_kdc_req *request,
210 krb5_kdc_rep *as_reply)
211 {
212 krb5_error_code retval;
213 int canon_req;
214 int canon_ok;
215 krb5_timestamp time_offset;
216
217 /* check the contents for sanity: */
218 if (!as_reply->enc_part2->times.starttime)
219 as_reply->enc_part2->times.starttime =
220 as_reply->enc_part2->times.authtime;
221
222 /*
223 * We only allow the AS-REP server name to be changed if the
224 * caller set the canonicalize flag (or requested an enterprise
225 * principal) and we requested (and received) a TGT.
226 */
227 canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
228 request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL ||
229 (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS);
230 if (canon_req) {
231 canon_ok = IS_TGS_PRINC(request->server) &&
232 IS_TGS_PRINC(as_reply->enc_part2->server);
233 } else
234 canon_ok = 0;
235
236 if ((!canon_ok &&
237 !krb5_principal_compare(context, as_reply->enc_part2->server, request->server))
238 || (!canon_req && !krb5_principal_compare(context, as_reply->client, request->client))
239 || !krb5_principal_compare(context, as_reply->enc_part2->server, as_reply->ticket->server)
240 || (request->nonce != as_reply->enc_part2->nonce)
241 /* XXX check for extraneous flags */
242 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
243 || ((request->kdc_options & KDC_OPT_POSTDATED) &&
244 (request->from != 0) &&
245 (request->from != as_reply->enc_part2->times.starttime))
246 || ((request->till != 0) &&
247 ts_after(as_reply->enc_part2->times.endtime, request->till))
248 || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
249 (request->rtime != 0) &&
250 ts_after(as_reply->enc_part2->times.renew_till, request->rtime))
251 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
252 !(request->kdc_options & KDC_OPT_RENEWABLE) &&
253 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
254 (request->till != 0) &&
255 ts_after(as_reply->enc_part2->times.renew_till, request->till))
256 ) {
257 return KRB5_KDCREP_MODIFIED;
258 }
259
260 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
261 time_offset = ts_delta(as_reply->enc_part2->times.authtime, time_now);
262 retval = krb5_set_time_offsets(context, time_offset, 0);
263 if (retval)
264 return retval;
265 } else {
266 if ((request->from == 0) &&
267 !ts_within(as_reply->enc_part2->times.starttime, time_now,
268 context->clockskew))
269 return (KRB5_KDCREP_SKEW);
270 }
271 return 0;
272 }
273
274 static krb5_error_code
stash_as_reply(krb5_context context,krb5_kdc_rep * as_reply,krb5_creds * creds,krb5_ccache ccache)275 stash_as_reply(krb5_context context,
276 krb5_kdc_rep *as_reply,
277 krb5_creds * creds,
278 krb5_ccache ccache)
279 {
280 krb5_error_code retval;
281 krb5_data * packet;
282 krb5_principal client;
283 krb5_principal server;
284
285 client = NULL;
286 server = NULL;
287
288 if (!creds->client)
289 if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
290 goto cleanup;
291
292 if (!creds->server)
293 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
294 &server)))
295 goto cleanup;
296
297 /* fill in the credentials */
298 if ((retval = krb5_copy_keyblock_contents(context,
299 as_reply->enc_part2->session,
300 &creds->keyblock)))
301 goto cleanup;
302
303 creds->times = as_reply->enc_part2->times;
304 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
305 be encrypted in skey */
306 creds->ticket_flags = as_reply->enc_part2->flags;
307 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
308 &creds->addresses)))
309 goto cleanup;
310
311 creds->second_ticket.length = 0;
312 creds->second_ticket.data = 0;
313
314 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
315 goto cleanup;
316
317 creds->ticket = *packet;
318 free(packet);
319
320 /* store it in the ccache! */
321 if (ccache)
322 if ((retval = krb5_cc_store_cred(context, ccache, creds)))
323 goto cleanup;
324
325 if (!creds->client)
326 creds->client = client;
327 if (!creds->server)
328 creds->server = server;
329
330 cleanup:
331 if (retval) {
332 if (client)
333 krb5_free_principal(context, client);
334 if (server)
335 krb5_free_principal(context, server);
336 if (creds->keyblock.contents) {
337 memset(creds->keyblock.contents, 0,
338 creds->keyblock.length);
339 free(creds->keyblock.contents);
340 creds->keyblock.contents = 0;
341 creds->keyblock.length = 0;
342 }
343 if (creds->ticket.data) {
344 free(creds->ticket.data);
345 creds->ticket.data = 0;
346 }
347 if (creds->addresses) {
348 krb5_free_addresses(context, creds->addresses);
349 creds->addresses = 0;
350 }
351 }
352 return (retval);
353 }
354
355 static krb5_error_code
make_preauth_list(krb5_context context,krb5_preauthtype * ptypes,int nptypes,krb5_pa_data *** ret_list)356 make_preauth_list(krb5_context context,
357 krb5_preauthtype * ptypes,
358 int nptypes,
359 krb5_pa_data *** ret_list)
360 {
361 krb5_preauthtype * ptypep;
362 krb5_pa_data ** preauthp;
363 int i;
364
365 if (nptypes < 0) {
366 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
367 ;
368 }
369
370 /* allocate space for a NULL to terminate the list */
371
372 if ((preauthp =
373 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
374 return(ENOMEM);
375
376 for (i=0; i<nptypes; i++) {
377 if ((preauthp[i] =
378 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
379 for (; i>=0; i--)
380 free(preauthp[i]);
381 free(preauthp);
382 return (ENOMEM);
383 }
384 preauthp[i]->magic = KV5M_PA_DATA;
385 preauthp[i]->pa_type = ptypes[i];
386 preauthp[i]->length = 0;
387 preauthp[i]->contents = 0;
388 }
389
390 /* fill in the terminating NULL */
391
392 preauthp[nptypes] = NULL;
393
394 *ret_list = preauthp;
395 return 0;
396 }
397
398 #define MAX_IN_TKT_LOOPS 16
399
400 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
401 * libdefaults entry are listed before any others. */
402 static krb5_error_code
sort_krb5_padata_sequence(krb5_context context,krb5_data * realm,krb5_pa_data ** padata)403 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
404 krb5_pa_data **padata)
405 {
406 int i, j, base;
407 krb5_error_code ret;
408 const char *p;
409 long l;
410 char *q, *preauth_types = NULL;
411 krb5_pa_data *tmp;
412 int need_free_string = 1;
413
414 if ((padata == NULL) || (padata[0] == NULL)) {
415 return 0;
416 }
417
418 ret = krb5int_libdefault_string(context, realm, KRB5_CONF_PREFERRED_PREAUTH_TYPES,
419 &preauth_types);
420 if ((ret != 0) || (preauth_types == NULL)) {
421 /* Try to use PKINIT first. */
422 preauth_types = "17, 16, 15, 14";
423 need_free_string = 0;
424 }
425
426 #ifdef DEBUG
427 fprintf (stderr, "preauth data types before sorting:");
428 for (i = 0; padata[i]; i++) {
429 fprintf (stderr, " %d", padata[i]->pa_type);
430 }
431 fprintf (stderr, "\n");
432 #endif
433
434 base = 0;
435 for (p = preauth_types; *p != '\0';) {
436 /* skip whitespace to find an entry */
437 p += strspn(p, ", ");
438 if (*p != '\0') {
439 /* see if we can extract a number */
440 l = strtol(p, &q, 10);
441 if ((q != NULL) && (q > p)) {
442 /* got a valid number; search for a matching entry */
443 for (i = base; padata[i] != NULL; i++) {
444 /* bubble the matching entry to the front of the list */
445 if (padata[i]->pa_type == l) {
446 tmp = padata[i];
447 for (j = i; j > base; j--)
448 padata[j] = padata[j - 1];
449 padata[base] = tmp;
450 base++;
451 break;
452 }
453 }
454 p = q;
455 } else {
456 break;
457 }
458 }
459 }
460 if (need_free_string)
461 free(preauth_types);
462
463 #ifdef DEBUG
464 fprintf (stderr, "preauth data types after sorting:");
465 for (i = 0; padata[i]; i++)
466 fprintf (stderr, " %d", padata[i]->pa_type);
467 fprintf (stderr, "\n");
468 #endif
469
470 return 0;
471 }
472
473 static krb5_error_code
build_in_tkt_name(krb5_context context,const char * in_tkt_service,krb5_const_principal client,krb5_principal * server_out)474 build_in_tkt_name(krb5_context context,
475 const char *in_tkt_service,
476 krb5_const_principal client,
477 krb5_principal *server_out)
478 {
479 krb5_error_code ret;
480 krb5_principal server = NULL;
481
482 *server_out = NULL;
483
484 if (in_tkt_service) {
485 ret = krb5_parse_name_flags(context, in_tkt_service,
486 KRB5_PRINCIPAL_PARSE_IGNORE_REALM,
487 &server);
488 if (ret)
489 return ret;
490 krb5_free_data_contents(context, &server->realm);
491 ret = krb5int_copy_data_contents(context, &client->realm,
492 &server->realm);
493 if (ret) {
494 krb5_free_principal(context, server);
495 return ret;
496 }
497 } else {
498 ret = krb5_build_principal_ext(context, &server,
499 client->realm.length,
500 client->realm.data,
501 KRB5_TGS_NAME_SIZE,
502 KRB5_TGS_NAME,
503 client->realm.length,
504 client->realm.data,
505 0);
506 if (ret)
507 return ret;
508 }
509
510 *server_out = server;
511 return 0;
512 }
513
514 void KRB5_CALLCONV
krb5_init_creds_free(krb5_context context,krb5_init_creds_context ctx)515 krb5_init_creds_free(krb5_context context,
516 krb5_init_creds_context ctx)
517 {
518 if (ctx == NULL)
519 return;
520
521 k5_response_items_free(ctx->rctx.items);
522 free(ctx->in_tkt_service);
523 zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length);
524 k5_preauth_request_context_fini(context, ctx);
525 krb5_free_error(context, ctx->err_reply);
526 krb5_free_pa_data(context, ctx->err_padata);
527 krb5_free_cred_contents(context, &ctx->cred);
528 krb5_free_kdc_req(context, ctx->request);
529 krb5_free_kdc_rep(context, ctx->reply);
530 krb5_free_data(context, ctx->outer_request_body);
531 krb5_free_data(context, ctx->inner_request_body);
532 krb5_free_data(context, ctx->encoded_previous_request);
533 krb5int_fast_free_state(context, ctx->fast_state);
534 krb5_free_pa_data(context, ctx->optimistic_padata);
535 krb5_free_pa_data(context, ctx->method_padata);
536 krb5_free_pa_data(context, ctx->more_padata);
537 krb5_free_data_contents(context, &ctx->salt);
538 krb5_free_data_contents(context, &ctx->s2kparams);
539 krb5_free_keyblock_contents(context, &ctx->as_key);
540 k5_json_release(ctx->cc_config_in);
541 k5_json_release(ctx->cc_config_out);
542 free(ctx);
543 }
544
545 krb5_error_code
k5_init_creds_get(krb5_context context,krb5_init_creds_context ctx,krb5_boolean use_primary,struct kdclist * kdcs)546 k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
547 krb5_boolean use_primary, struct kdclist *kdcs)
548 {
549 krb5_error_code code;
550 krb5_data request;
551 krb5_data reply;
552 krb5_data realm;
553 unsigned int flags = 0;
554 int no_udp = 0;
555
556 request.length = 0;
557 request.data = NULL;
558 reply.length = 0;
559 reply.data = NULL;
560 realm.length = 0;
561 realm.data = NULL;
562
563 for (;;) {
564 code = krb5_init_creds_step(context,
565 ctx,
566 &reply,
567 &request,
568 &realm,
569 &flags);
570 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !no_udp) {
571 TRACE_INIT_CREDS_RETRY_TCP(context);
572 no_udp = 1;
573 } else if (code != 0 || !(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
574 break;
575
576 krb5_free_data_contents(context, &reply);
577
578 code = k5_sendto_kdc(context, &request, &realm, use_primary, no_udp,
579 &reply, kdcs);
580 if (code != 0)
581 break;
582
583 krb5_free_data_contents(context, &request);
584 krb5_free_data_contents(context, &realm);
585 }
586
587 krb5_free_data_contents(context, &request);
588 krb5_free_data_contents(context, &reply);
589 krb5_free_data_contents(context, &realm);
590
591 return code;
592 }
593
594 /* Heimdal API */
595 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get(krb5_context context,krb5_init_creds_context ctx)596 krb5_init_creds_get(krb5_context context,
597 krb5_init_creds_context ctx)
598 {
599 return k5_init_creds_get(context, ctx, FALSE, NULL);
600 }
601
602 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get_creds(krb5_context context,krb5_init_creds_context ctx,krb5_creds * creds)603 krb5_init_creds_get_creds(krb5_context context,
604 krb5_init_creds_context ctx,
605 krb5_creds *creds)
606 {
607 if (!ctx->complete)
608 return KRB5_NO_TKT_SUPPLIED;
609
610 return k5_copy_creds_contents(context, &ctx->cred, creds);
611 }
612
613 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get_times(krb5_context context,krb5_init_creds_context ctx,krb5_ticket_times * times)614 krb5_init_creds_get_times(krb5_context context,
615 krb5_init_creds_context ctx,
616 krb5_ticket_times *times)
617 {
618 if (!ctx->complete)
619 return KRB5_NO_TKT_SUPPLIED;
620
621 *times = ctx->cred.times;
622
623 return 0;
624 }
625
626 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get_error(krb5_context context,krb5_init_creds_context ctx,krb5_error ** error)627 krb5_init_creds_get_error(krb5_context context,
628 krb5_init_creds_context ctx,
629 krb5_error **error)
630 {
631 krb5_error_code code;
632 krb5_error *ret = NULL;
633
634 *error = NULL;
635
636 if (ctx->err_reply == NULL)
637 return 0;
638
639 ret = k5alloc(sizeof(*ret), &code);
640 if (code != 0)
641 goto cleanup;
642
643 ret->magic = KV5M_ERROR;
644 ret->ctime = ctx->err_reply->ctime;
645 ret->cusec = ctx->err_reply->cusec;
646 ret->susec = ctx->err_reply->susec;
647 ret->stime = ctx->err_reply->stime;
648 ret->error = ctx->err_reply->error;
649
650 if (ctx->err_reply->client != NULL) {
651 code = krb5_copy_principal(context, ctx->err_reply->client,
652 &ret->client);
653 if (code != 0)
654 goto cleanup;
655 }
656
657 code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
658 if (code != 0)
659 goto cleanup;
660
661 code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
662 &ret->text);
663 if (code != 0)
664 goto cleanup;
665
666 code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
667 &ret->e_data);
668 if (code != 0)
669 goto cleanup;
670
671 *error = ret;
672
673 cleanup:
674 if (code != 0)
675 krb5_free_error(context, ret);
676
677 return code;
678 }
679
680 /* Return the current time, possibly using the offset from a previously
681 * received preauth-required error. */
682 krb5_error_code
k5_init_creds_current_time(krb5_context context,krb5_init_creds_context ctx,krb5_boolean allow_unauth,krb5_timestamp * time_out,krb5_int32 * usec_out)683 k5_init_creds_current_time(krb5_context context, krb5_init_creds_context ctx,
684 krb5_boolean allow_unauth, krb5_timestamp *time_out,
685 krb5_int32 *usec_out)
686 {
687 if (ctx->pa_offset_state != NO_OFFSET &&
688 (allow_unauth || ctx->pa_offset_state == AUTH_OFFSET) &&
689 (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)) {
690 /* Use the offset we got from a preauth-required error. */
691 return k5_time_with_offset(ctx->pa_offset, ctx->pa_offset_usec,
692 time_out, usec_out);
693 } else {
694 /* Use the time offset from the context, or no offset. */
695 return krb5_us_timeofday(context, time_out, usec_out);
696 }
697 }
698
699 /* Set the timestamps for ctx->request based on the desired lifetimes. */
700 static krb5_error_code
set_request_times(krb5_context context,krb5_init_creds_context ctx)701 set_request_times(krb5_context context, krb5_init_creds_context ctx)
702 {
703 krb5_error_code code;
704 krb5_timestamp from, now;
705 krb5_int32 now_ms;
706
707 code = k5_init_creds_current_time(context, ctx, TRUE, &now, &now_ms);
708 if (code != 0)
709 return code;
710
711 /* Omit request start time unless the caller explicitly asked for one. */
712 from = ts_incr(now, ctx->start_time);
713 if (ctx->start_time != 0)
714 ctx->request->from = from;
715
716 ctx->request->till = ts_incr(from, ctx->tkt_life);
717
718 if (ctx->renew_life > 0) {
719 /* Don't ask for a smaller renewable time than the lifetime. */
720 ctx->request->rtime = ts_incr(from, ctx->renew_life);
721 if (ts_after(ctx->request->till, ctx->request->rtime))
722 ctx->request->rtime = ctx->request->till;
723 ctx->request->kdc_options &= ~KDC_OPT_RENEWABLE_OK;
724 } else {
725 ctx->request->rtime = 0;
726 }
727
728 return 0;
729 }
730
731 static void
read_allowed_preauth_type(krb5_context context,krb5_init_creds_context ctx)732 read_allowed_preauth_type(krb5_context context, krb5_init_creds_context ctx)
733 {
734 krb5_error_code ret;
735 krb5_data config;
736 char *tmp, *p;
737 krb5_ccache in_ccache = k5_gic_opt_get_in_ccache(ctx->opt);
738
739 ctx->allowed_preauth_type = KRB5_PADATA_NONE;
740 if (in_ccache == NULL)
741 return;
742 memset(&config, 0, sizeof(config));
743 if (krb5_cc_get_config(context, in_ccache, ctx->request->server,
744 KRB5_CC_CONF_PA_TYPE, &config) != 0)
745 return;
746 tmp = k5memdup0(config.data, config.length, &ret);
747 krb5_free_data_contents(context, &config);
748 if (tmp == NULL)
749 return;
750 ctx->allowed_preauth_type = strtol(tmp, &p, 10);
751 if (p == NULL || *p != '\0')
752 ctx->allowed_preauth_type = KRB5_PADATA_NONE;
753 free(tmp);
754 }
755
756 /* Return true if encrypted timestamp is disabled for realm. */
757 static krb5_boolean
encts_disabled(profile_t profile,const krb5_data * realm)758 encts_disabled(profile_t profile, const krb5_data *realm)
759 {
760 krb5_error_code ret;
761 char *realmstr;
762 int bval;
763
764 realmstr = k5memdup0(realm->data, realm->length, &ret);
765 if (realmstr == NULL)
766 return FALSE;
767 ret = profile_get_boolean(profile, KRB5_CONF_REALMS, realmstr,
768 KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP, FALSE,
769 &bval);
770 free(realmstr);
771 return (ret == 0) ? bval : FALSE;
772 }
773
774 /**
775 * Throw away any pre-authentication realm state and begin with a
776 * unauthenticated or optimistically authenticated request. If fast_upgrade is
777 * set, use FAST for this request.
778 */
779 static krb5_error_code
restart_init_creds_loop(krb5_context context,krb5_init_creds_context ctx,krb5_boolean fast_upgrade)780 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
781 krb5_boolean fast_upgrade)
782 {
783 krb5_error_code code = 0;
784
785 krb5_free_pa_data(context, ctx->optimistic_padata);
786 krb5_free_pa_data(context, ctx->method_padata);
787 krb5_free_pa_data(context, ctx->more_padata);
788 krb5_free_pa_data(context, ctx->err_padata);
789 krb5_free_error(context, ctx->err_reply);
790 ctx->optimistic_padata = ctx->method_padata = ctx->more_padata = NULL;
791 ctx->err_padata = NULL;
792 ctx->err_reply = NULL;
793 ctx->selected_preauth_type = KRB5_PADATA_NONE;
794
795 krb5int_fast_free_state(context, ctx->fast_state);
796 ctx->fast_state = NULL;
797 code = krb5int_fast_make_state(context, &ctx->fast_state);
798 if (code != 0)
799 goto cleanup;
800 if (fast_upgrade)
801 ctx->fast_state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
802
803 k5_preauth_request_context_fini(context, ctx);
804 k5_preauth_request_context_init(context, ctx);
805 krb5_free_data(context, ctx->outer_request_body);
806 ctx->outer_request_body = NULL;
807 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
808 code = make_preauth_list(context, ctx->opt->preauth_list,
809 ctx->opt->preauth_list_length,
810 &ctx->optimistic_padata);
811 if (code)
812 goto cleanup;
813 }
814
815 /* Never set encts_disabled back to false, so it can't be circumvented with
816 * client realm referrals. */
817 if (encts_disabled(context->profile, &ctx->request->client->realm))
818 ctx->encts_disabled = TRUE;
819
820 krb5_free_principal(context, ctx->request->server);
821 ctx->request->server = NULL;
822
823 code = build_in_tkt_name(context, ctx->in_tkt_service,
824 ctx->request->client,
825 &ctx->request->server);
826 if (code != 0)
827 goto cleanup;
828
829 code = krb5int_fast_as_armor(context, ctx->fast_state, ctx->opt,
830 ctx->request);
831 if (code != 0)
832 goto cleanup;
833 /* give the preauth plugins a chance to prep the request body */
834 k5_preauth_prepare_request(context, ctx->opt, ctx->request);
835
836 code = krb5int_fast_prep_req_body(context, ctx->fast_state,
837 ctx->request,
838 &ctx->outer_request_body);
839 if (code != 0)
840 goto cleanup;
841
842 /* Read the allowed preauth type for this server principal from the input
843 * ccache, if the application supplied one. */
844 read_allowed_preauth_type(context, ctx);
845
846 cleanup:
847 return code;
848 }
849
850 krb5_error_code KRB5_CALLCONV
krb5_init_creds_init(krb5_context context,krb5_principal client,krb5_prompter_fct prompter,void * data,krb5_deltat start_time,krb5_get_init_creds_opt * opt,krb5_init_creds_context * pctx)851 krb5_init_creds_init(krb5_context context,
852 krb5_principal client,
853 krb5_prompter_fct prompter,
854 void *data,
855 krb5_deltat start_time,
856 krb5_get_init_creds_opt *opt,
857 krb5_init_creds_context *pctx)
858 {
859 krb5_error_code code;
860 krb5_init_creds_context ctx;
861 int tmp;
862 char *str = NULL;
863
864 TRACE_INIT_CREDS(context, client);
865
866 ctx = k5alloc(sizeof(*ctx), &code);
867 if (code != 0)
868 goto cleanup;
869
870 ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
871 if (code != 0)
872 goto cleanup;
873 ctx->info_pa_permitted = TRUE;
874 code = krb5_copy_principal(context, client, &ctx->request->client);
875 if (code != 0)
876 goto cleanup;
877
878 ctx->prompter = prompter;
879 ctx->prompter_data = data;
880 ctx->gak_fct = krb5_get_as_key_password;
881 ctx->gak_data = &ctx->gakpw;
882
883 ctx->start_time = start_time;
884
885 if (opt == NULL) {
886 ctx->opt = &ctx->opt_storage;
887 krb5_get_init_creds_opt_init(ctx->opt);
888 } else {
889 ctx->opt = opt;
890 }
891
892 code = k5_response_items_new(&ctx->rctx.items);
893 if (code != 0)
894 goto cleanup;
895
896 /* Initialise request parameters as per krb5_get_init_creds() */
897 ctx->request->kdc_options = context->kdc_default_options;
898
899 /* forwardable */
900 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
901 tmp = ctx->opt->forwardable;
902 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
903 KRB5_CONF_FORWARDABLE, &tmp) == 0)
904 ;
905 else
906 tmp = 0;
907 if (tmp)
908 ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
909
910 /* proxiable */
911 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
912 tmp = ctx->opt->proxiable;
913 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
914 KRB5_CONF_PROXIABLE, &tmp) == 0)
915 ;
916 else
917 tmp = 0;
918 if (tmp)
919 ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
920
921 /* canonicalize */
922 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
923 tmp = 1;
924 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
925 KRB5_CONF_CANONICALIZE, &tmp) == 0)
926 ;
927 else
928 tmp = 0;
929 if (tmp)
930 ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
931
932 /* allow_postdate */
933 if (ctx->start_time > 0)
934 ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
935
936 /* ticket lifetime */
937 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
938 ctx->tkt_life = ctx->opt->tkt_life;
939 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
940 KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
941 code = krb5_string_to_deltat(str, &ctx->tkt_life);
942 if (code != 0)
943 goto cleanup;
944 free(str);
945 str = NULL;
946 } else
947 ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
948
949 /* renewable lifetime */
950 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
951 ctx->renew_life = ctx->opt->renew_life;
952 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
953 KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
954 code = krb5_string_to_deltat(str, &ctx->renew_life);
955 if (code != 0)
956 goto cleanup;
957 free(str);
958 str = NULL;
959 } else
960 ctx->renew_life = 0;
961
962 if (ctx->renew_life > 0)
963 ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
964
965 /* enctypes */
966 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
967 ctx->request->ktype =
968 k5memdup(ctx->opt->etype_list,
969 ctx->opt->etype_list_length * sizeof(krb5_enctype),
970 &code);
971 if (code != 0)
972 goto cleanup;
973 ctx->request->nktypes = ctx->opt->etype_list_length;
974 } else if (krb5_get_default_in_tkt_ktypes(context,
975 &ctx->request->ktype) == 0) {
976 ctx->request->nktypes = k5_count_etypes(ctx->request->ktype);
977 } else {
978 /* there isn't any useful default here. */
979 code = KRB5_CONFIG_ETYPE_NOSUPP;
980 goto cleanup;
981 }
982
983 /*
984 * Set a default enctype for optimistic preauth. If we're not doing
985 * optimistic preauth, this should ordinarily get overwritten when we
986 * process the etype-info2 of the preauth-required error.
987 */
988 if (ctx->request->nktypes > 0)
989 ctx->etype = ctx->request->ktype[0];
990
991 /* addresses */
992 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
993 code = krb5_copy_addresses(context, ctx->opt->address_list,
994 &ctx->request->addresses);
995 if (code != 0)
996 goto cleanup;
997 } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
998 KRB5_CONF_NOADDRESSES, &tmp) != 0
999 || tmp) {
1000 ctx->request->addresses = NULL;
1001 } else {
1002 code = krb5_os_localaddr(context, &ctx->request->addresses);
1003 if (code != 0)
1004 goto cleanup;
1005 }
1006
1007 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
1008 code = krb5int_copy_data_contents(context, ctx->opt->salt, &ctx->salt);
1009 if (code != 0)
1010 goto cleanup;
1011 ctx->default_salt = FALSE;
1012 } else {
1013 ctx->salt = empty_data();
1014 ctx->default_salt = TRUE;
1015 }
1016
1017 /* Anonymous. */
1018 if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
1019 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
1020 /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
1021 if (client->length == 1 && client->data[0].length ==0) {
1022 krb5_principal new_client;
1023 code = krb5_build_principal_ext(context, &new_client,
1024 client->realm.length,
1025 client->realm.data,
1026 strlen(KRB5_WELLKNOWN_NAMESTR),
1027 KRB5_WELLKNOWN_NAMESTR,
1028 strlen(KRB5_ANONYMOUS_PRINCSTR),
1029 KRB5_ANONYMOUS_PRINCSTR,
1030 0);
1031 if (code)
1032 goto cleanup;
1033 krb5_free_principal(context, ctx->request->client);
1034 ctx->request->client = new_client;
1035 ctx->request->client->type = KRB5_NT_WELLKNOWN;
1036 }
1037 }
1038 /* We will also handle anonymous if the input principal is the anonymous
1039 * principal. */
1040 if (krb5_principal_compare_any_realm(context, ctx->request->client,
1041 krb5_anonymous_principal())) {
1042 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
1043 ctx->request->client->type = KRB5_NT_WELLKNOWN;
1044 }
1045
1046 *pctx = ctx;
1047 ctx = NULL;
1048
1049 cleanup:
1050 krb5_init_creds_free(context, ctx);
1051 free(str);
1052
1053 return code;
1054 }
1055
1056 krb5_error_code KRB5_CALLCONV
krb5_init_creds_set_service(krb5_context context,krb5_init_creds_context ctx,const char * service)1057 krb5_init_creds_set_service(krb5_context context,
1058 krb5_init_creds_context ctx,
1059 const char *service)
1060 {
1061 char *s;
1062
1063 TRACE_INIT_CREDS_SERVICE(context, service);
1064
1065 s = strdup(service);
1066 if (s == NULL)
1067 return ENOMEM;
1068
1069 free(ctx->in_tkt_service);
1070 ctx->in_tkt_service = s;
1071
1072 return restart_init_creds_loop(context, ctx, FALSE);
1073 }
1074
1075 static krb5_error_code
init_creds_validate_reply(krb5_context context,krb5_init_creds_context ctx,krb5_data * reply)1076 init_creds_validate_reply(krb5_context context,
1077 krb5_init_creds_context ctx,
1078 krb5_data *reply)
1079 {
1080 krb5_error_code code;
1081 krb5_error *error = NULL;
1082 krb5_kdc_rep *as_reply = NULL;
1083
1084 krb5_free_error(context, ctx->err_reply);
1085 ctx->err_reply = NULL;
1086
1087 krb5_free_kdc_rep(context, ctx->reply);
1088 ctx->reply = NULL;
1089
1090 if (krb5_is_krb_error(reply)) {
1091 code = decode_krb5_error(reply, &error);
1092 if (code != 0)
1093 return code;
1094
1095 assert(error != NULL);
1096
1097 TRACE_INIT_CREDS_ERROR_REPLY(context,
1098 error->error + ERROR_TABLE_BASE_krb5);
1099 if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1100 krb5_free_error(context, error);
1101 return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1102 } else {
1103 ctx->err_reply = error;
1104 return 0;
1105 }
1106 }
1107
1108 /*
1109 * Check to make sure it isn't a V4 reply.
1110 */
1111 if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1112 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1113 #define V4_KRB_PROT_VERSION 4
1114 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
1115 /* check here for V4 reply */
1116 unsigned int t_switch;
1117
1118 /* From v4 g_in_tkt.c: This used to be
1119 switch (pkt_msg_type(rpkt) & ~1) {
1120 but SCO 3.2v4 cc compiled that incorrectly. */
1121 t_switch = reply->data[1];
1122 t_switch &= ~1;
1123
1124 if (t_switch == V4_AUTH_MSG_ERR_REPLY
1125 && reply->data[0] == V4_KRB_PROT_VERSION) {
1126 code = KRB5KRB_AP_ERR_V4_REPLY;
1127 } else {
1128 code = KRB5KRB_AP_ERR_MSG_TYPE;
1129 }
1130 return code;
1131 }
1132
1133 /* It must be a KRB_AS_REP message, or an bad returned packet */
1134 code = decode_krb5_as_rep(reply, &as_reply);
1135 if (code != 0)
1136 return code;
1137
1138 if (as_reply->msg_type != KRB5_AS_REP) {
1139 krb5_free_kdc_rep(context, as_reply);
1140 return KRB5KRB_AP_ERR_MSG_TYPE;
1141 }
1142
1143 ctx->reply = as_reply;
1144
1145 return 0;
1146 }
1147
1148 static krb5_error_code
save_selected_preauth_type(krb5_context context,krb5_ccache ccache,krb5_init_creds_context ctx)1149 save_selected_preauth_type(krb5_context context, krb5_ccache ccache,
1150 krb5_init_creds_context ctx)
1151 {
1152 krb5_data config_data;
1153 char *tmp;
1154 krb5_error_code code;
1155
1156 if (ctx->selected_preauth_type == KRB5_PADATA_NONE)
1157 return 0;
1158 if (asprintf(&tmp, "%ld", (long)ctx->selected_preauth_type) < 0)
1159 return ENOMEM;
1160 config_data = string2data(tmp);
1161 code = krb5_cc_set_config(context, ccache, ctx->cred.server,
1162 KRB5_CC_CONF_PA_TYPE, &config_data);
1163 free(tmp);
1164 return code;
1165 }
1166
1167 static krb5_error_code
clear_cc_config_out_data(krb5_context context,krb5_init_creds_context ctx)1168 clear_cc_config_out_data(krb5_context context, krb5_init_creds_context ctx)
1169 {
1170 k5_json_release(ctx->cc_config_out);
1171 ctx->cc_config_out = NULL;
1172 return k5_json_object_create(&ctx->cc_config_out);
1173 }
1174
1175 static krb5_error_code
read_cc_config_in_data(krb5_context context,krb5_init_creds_context ctx)1176 read_cc_config_in_data(krb5_context context, krb5_init_creds_context ctx)
1177 {
1178 k5_json_value val;
1179 krb5_data config;
1180 char *encoded;
1181 krb5_error_code code;
1182 krb5_ccache in_ccache = k5_gic_opt_get_in_ccache(ctx->opt);
1183
1184 k5_json_release(ctx->cc_config_in);
1185 ctx->cc_config_in = NULL;
1186
1187 if (in_ccache == NULL)
1188 return 0;
1189
1190 memset(&config, 0, sizeof(config));
1191 code = krb5_cc_get_config(context, in_ccache, ctx->request->server,
1192 KRB5_CC_CONF_PA_CONFIG_DATA, &config);
1193 if (code)
1194 return code;
1195
1196 encoded = k5memdup0(config.data, config.length, &code);
1197 krb5_free_data_contents(context, &config);
1198 if (encoded == NULL)
1199 return ENOMEM;
1200
1201 code = k5_json_decode(encoded, &val);
1202 free(encoded);
1203 if (code)
1204 return code;
1205 if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT) {
1206 k5_json_release(val);
1207 return EINVAL;
1208 }
1209 ctx->cc_config_in = val;
1210 return 0;
1211 }
1212
1213 static krb5_error_code
save_cc_config_out_data(krb5_context context,krb5_ccache ccache,krb5_init_creds_context ctx)1214 save_cc_config_out_data(krb5_context context, krb5_ccache ccache,
1215 krb5_init_creds_context ctx)
1216 {
1217 krb5_data config;
1218 char *encoded;
1219 krb5_error_code code;
1220
1221 if (ctx->cc_config_out == NULL ||
1222 k5_json_object_count(ctx->cc_config_out) == 0)
1223 return 0;
1224 code = k5_json_encode(ctx->cc_config_out, &encoded);
1225 if (code)
1226 return code;
1227 config = string2data(encoded);
1228 code = krb5_cc_set_config(context, ccache, ctx->cred.server,
1229 KRB5_CC_CONF_PA_CONFIG_DATA, &config);
1230 free(encoded);
1231 return code;
1232 }
1233
1234 /* Add a KERB-PA-PAC-REQUEST pa-data item if the gic options require one. */
1235 static krb5_error_code
maybe_add_pac_request(krb5_context context,krb5_init_creds_context ctx)1236 maybe_add_pac_request(krb5_context context, krb5_init_creds_context ctx)
1237 {
1238 krb5_error_code code;
1239 krb5_pa_pac_req pac_req;
1240 krb5_data *encoded;
1241 int val;
1242
1243 val = k5_gic_opt_pac_request(ctx->opt);
1244 if (val == -1)
1245 return 0;
1246
1247 pac_req.include_pac = val;
1248 code = encode_krb5_pa_pac_req(&pac_req, &encoded);
1249 if (code)
1250 return code;
1251 code = k5_add_pa_data_from_data(&ctx->request->padata,
1252 KRB5_PADATA_PAC_REQUEST, encoded);
1253 krb5_free_data(context, encoded);
1254 return code;
1255 }
1256
1257 static krb5_error_code
init_creds_step_request(krb5_context context,krb5_init_creds_context ctx,krb5_data * out)1258 init_creds_step_request(krb5_context context,
1259 krb5_init_creds_context ctx,
1260 krb5_data *out)
1261 {
1262 krb5_error_code code;
1263 krb5_preauthtype pa_type;
1264 krb5_data copy;
1265 struct errinfo save = EMPTY_ERRINFO;
1266 uint32_t rcode = (ctx->err_reply == NULL) ? 0 : ctx->err_reply->error;
1267
1268 if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1269 code = KRB5_GET_IN_TKT_LOOP;
1270 goto cleanup;
1271 }
1272
1273 /* RFC 6113 requires a new nonce for the inner request on each try. */
1274 code = k5_generate_nonce(context, &ctx->request->nonce);
1275 if (code != 0)
1276 goto cleanup;
1277
1278 /* Reset the request timestamps, possibly adjusting to the KDC time. */
1279 code = set_request_times(context, ctx);
1280 if (code != 0)
1281 goto cleanup;
1282
1283 krb5_free_data(context, ctx->inner_request_body);
1284 ctx->inner_request_body = NULL;
1285 code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
1286 if (code)
1287 goto cleanup;
1288
1289 /*
1290 * Read cached preauth configuration data for this server principal from
1291 * the in_ccache, if the application supplied one, and delete any that was
1292 * stored by a previous (clearly failed) module.
1293 */
1294 read_cc_config_in_data(context, ctx);
1295 clear_cc_config_out_data(context, ctx);
1296
1297 ctx->request->padata = NULL;
1298 if (ctx->optimistic_padata != NULL) {
1299 /* Our first attempt, using an optimistic padata list. */
1300 TRACE_INIT_CREDS_PREAUTH_OPTIMISTIC(context);
1301 code = k5_preauth(context, ctx, ctx->optimistic_padata, TRUE,
1302 &ctx->request->padata, &ctx->selected_preauth_type);
1303 krb5_free_pa_data(context, ctx->optimistic_padata);
1304 ctx->optimistic_padata = NULL;
1305 if (code) {
1306 /* Make an unauthenticated request. */
1307 krb5_clear_error_message(context);
1308 code = 0;
1309 }
1310 } else if (ctx->more_padata != NULL) {
1311 /* Continuing after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED. */
1312 TRACE_INIT_CREDS_PREAUTH_MORE(context, ctx->selected_preauth_type);
1313 code = k5_preauth(context, ctx, ctx->more_padata, TRUE,
1314 &ctx->request->padata, &pa_type);
1315 } else if (rcode == KDC_ERR_PREAUTH_FAILED) {
1316 /* Report the KDC-side failure code if we can't try another mech. */
1317 code = KRB5KDC_ERR_PREAUTH_FAILED;
1318 } else if (rcode && rcode != KDC_ERR_PREAUTH_REQUIRED) {
1319 /* Retrying after an error (possibly mechanism-specific), using error
1320 * padata to figure out what to change. */
1321 TRACE_INIT_CREDS_PREAUTH_TRYAGAIN(context, ctx->err_reply->error,
1322 ctx->selected_preauth_type);
1323 code = k5_preauth_tryagain(context, ctx, ctx->selected_preauth_type,
1324 ctx->err_reply, ctx->err_padata,
1325 &ctx->request->padata);
1326 if (code) {
1327 krb5_clear_error_message(context);
1328 code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1329 }
1330 }
1331 /* Don't continue after a keyboard interrupt. */
1332 if (code == KRB5_LIBOS_PWDINTR)
1333 goto cleanup;
1334 /* Don't continue if fallback is disabled. */
1335 if (code && ctx->fallback_disabled)
1336 goto cleanup;
1337 if (code) {
1338 /* See if we can try a different preauth mech before giving up. */
1339 k5_save_ctx_error(context, code, &save);
1340 ctx->selected_preauth_type = KRB5_PADATA_NONE;
1341 }
1342
1343 if (ctx->request->padata == NULL && ctx->method_padata != NULL) {
1344 /* Retrying after KDC_ERR_PREAUTH_REQUIRED, or trying again with a
1345 * different mechanism after a failure. */
1346 TRACE_INIT_CREDS_PREAUTH(context);
1347 code = k5_preauth(context, ctx, ctx->method_padata, TRUE,
1348 &ctx->request->padata, &ctx->selected_preauth_type);
1349 if (code) {
1350 if (save.code != 0)
1351 code = k5_restore_ctx_error(context, &save);
1352 goto cleanup;
1353 }
1354 }
1355 if (ctx->request->padata == NULL)
1356 TRACE_INIT_CREDS_PREAUTH_NONE(context);
1357
1358 /* Remember when we sent this request (after any preauth delay). */
1359 ctx->request_time = time(NULL);
1360
1361 if (ctx->encoded_previous_request != NULL) {
1362 krb5_free_data(context, ctx->encoded_previous_request);
1363 ctx->encoded_previous_request = NULL;
1364 }
1365 if (ctx->info_pa_permitted) {
1366 code = k5_add_empty_pa_data(&ctx->request->padata,
1367 KRB5_PADATA_AS_FRESHNESS);
1368 if (code)
1369 goto cleanup;
1370 code = k5_add_empty_pa_data(&ctx->request->padata,
1371 KRB5_ENCPADATA_REQ_ENC_PA_REP);
1372 }
1373 if (code)
1374 goto cleanup;
1375
1376 if (ctx->subject_cert != NULL) {
1377 code = krb5int_copy_data_contents(context, ctx->subject_cert, ©);
1378 if (code)
1379 goto cleanup;
1380 code = k5_add_pa_data_from_data(&ctx->request->padata,
1381 KRB5_PADATA_S4U_X509_USER, ©);
1382 krb5_free_data_contents(context, ©);
1383 if (code)
1384 goto cleanup;
1385 }
1386
1387 code = maybe_add_pac_request(context, ctx);
1388 if (code)
1389 goto cleanup;
1390
1391 code = krb5int_fast_prep_req(context, ctx->fast_state,
1392 ctx->request, ctx->outer_request_body,
1393 encode_krb5_as_req,
1394 &ctx->encoded_previous_request);
1395 if (code != 0)
1396 goto cleanup;
1397
1398 code = krb5int_copy_data_contents(context,
1399 ctx->encoded_previous_request,
1400 out);
1401 if (code != 0)
1402 goto cleanup;
1403
1404 cleanup:
1405 krb5_free_pa_data(context, ctx->request->padata);
1406 ctx->request->padata = NULL;
1407 k5_clear_error(&save);
1408 return code;
1409 }
1410
1411 /* Ensure that the reply enctype was among the requested enctypes. */
1412 static krb5_error_code
check_reply_enctype(krb5_init_creds_context ctx)1413 check_reply_enctype(krb5_init_creds_context ctx)
1414 {
1415 int i;
1416
1417 for (i = 0; i < ctx->request->nktypes; i++) {
1418 if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1419 return 0;
1420 }
1421 return KRB5_CONFIG_ETYPE_NOSUPP;
1422 }
1423
1424 /* Note the difference between the KDC's time, as reported to us in a
1425 * preauth-required error, and the current time. */
1426 static void
note_req_timestamp(krb5_context context,krb5_init_creds_context ctx,krb5_timestamp kdc_time,krb5_int32 kdc_usec)1427 note_req_timestamp(krb5_context context, krb5_init_creds_context ctx,
1428 krb5_timestamp kdc_time, krb5_int32 kdc_usec)
1429 {
1430 krb5_timestamp now;
1431 krb5_int32 usec;
1432
1433 if (k5_time_with_offset(0, 0, &now, &usec) != 0)
1434 return;
1435 ctx->pa_offset = ts_delta(kdc_time, now);
1436 ctx->pa_offset_usec = kdc_usec - usec;
1437 ctx->pa_offset_state = (ctx->fast_state->armor_key != NULL) ?
1438 AUTH_OFFSET : UNAUTH_OFFSET;
1439 }
1440
1441 /*
1442 * Determine whether err is a client referral to another realm, given the
1443 * previously requested client principal name.
1444 *
1445 * RFC 6806 Section 7 requires that KDCs return the referral realm in an error
1446 * type WRONG_REALM, but Microsoft Windows Server 2003 (and possibly others)
1447 * return the realm in a PRINCIPAL_UNKNOWN message.
1448 */
1449 static krb5_boolean
is_referral(krb5_context context,krb5_error * err,krb5_principal client)1450 is_referral(krb5_context context, krb5_error *err, krb5_principal client)
1451 {
1452 if (err->error != KDC_ERR_WRONG_REALM &&
1453 err->error != KDC_ERR_C_PRINCIPAL_UNKNOWN)
1454 return FALSE;
1455 if (err->client == NULL)
1456 return FALSE;
1457 return !krb5_realm_compare(context, err->client, client);
1458 }
1459
1460 /* Transfer error padata to method data in ctx and sort it according to
1461 * configuration. */
1462 static krb5_error_code
accept_method_data(krb5_context context,krb5_init_creds_context ctx)1463 accept_method_data(krb5_context context, krb5_init_creds_context ctx)
1464 {
1465 krb5_free_pa_data(context, ctx->method_padata);
1466 ctx->method_padata = ctx->err_padata;
1467 ctx->err_padata = NULL;
1468 return sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1469 ctx->method_padata);
1470 }
1471
1472 /* Return the password expiry time indicated by enc_part2. Set *is_last_req
1473 * if the information came from a last_req value. */
1474 static void
get_expiry_times(krb5_enc_kdc_rep_part * enc_part2,krb5_timestamp * pw_exp,krb5_timestamp * acct_exp,krb5_boolean * is_last_req)1475 get_expiry_times(krb5_enc_kdc_rep_part *enc_part2, krb5_timestamp *pw_exp,
1476 krb5_timestamp *acct_exp, krb5_boolean *is_last_req)
1477 {
1478 krb5_last_req_entry **last_req;
1479 krb5_int32 lr_type;
1480
1481 *pw_exp = 0;
1482 *acct_exp = 0;
1483 *is_last_req = FALSE;
1484
1485 /* Look for last-req entries for password or account expiration. */
1486 if (enc_part2->last_req) {
1487 for (last_req = enc_part2->last_req; *last_req; last_req++) {
1488 lr_type = (*last_req)->lr_type;
1489 if (lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
1490 lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
1491 *is_last_req = TRUE;
1492 *pw_exp = (*last_req)->value;
1493 } else if (lr_type == KRB5_LRQ_ALL_ACCT_EXPTIME ||
1494 lr_type == KRB5_LRQ_ONE_ACCT_EXPTIME) {
1495 *is_last_req = TRUE;
1496 *acct_exp = (*last_req)->value;
1497 }
1498 }
1499 }
1500
1501 /* If we didn't find any, use the ambiguous key_exp field. */
1502 if (*is_last_req == FALSE)
1503 *pw_exp = enc_part2->key_exp;
1504 }
1505
1506 /*
1507 * Send an appropriate warning prompter if as_reply indicates that the password
1508 * is going to expire soon. If an expire callback was provided, use that
1509 * instead.
1510 */
1511 static void
warn_pw_expiry(krb5_context context,krb5_get_init_creds_opt * options,krb5_prompter_fct prompter,void * data,const char * in_tkt_service,krb5_kdc_rep * as_reply)1512 warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options,
1513 krb5_prompter_fct prompter, void *data,
1514 const char *in_tkt_service, krb5_kdc_rep *as_reply)
1515 {
1516 krb5_error_code ret;
1517 krb5_expire_callback_func expire_cb;
1518 void *expire_data;
1519 krb5_timestamp pw_exp, acct_exp, now;
1520 krb5_boolean is_last_req;
1521 uint32_t interval;
1522 char ts[256], banner[1024];
1523
1524 if (as_reply == NULL || as_reply->enc_part2 == NULL)
1525 return;
1526
1527 get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req);
1528
1529 k5_gic_opt_get_expire_cb(options, &expire_cb, &expire_data);
1530 if (expire_cb != NULL) {
1531 /* Invoke the expire callback and don't send prompter warnings. */
1532 (*expire_cb)(context, expire_data, pw_exp, acct_exp, is_last_req);
1533 return;
1534 }
1535
1536 /* Don't warn if no password expiry value was sent. */
1537 if (pw_exp == 0)
1538 return;
1539
1540 /* Don't warn if the password is being changed. */
1541 if (in_tkt_service && strcmp(in_tkt_service, "kadmin/changepw") == 0)
1542 return;
1543
1544 /*
1545 * If the expiry time came from a last_req field, assume the KDC wants us
1546 * to warn. Otherwise, warn only if the expiry time is less than a week
1547 * from now.
1548 */
1549 ret = krb5_timeofday(context, &now);
1550 if (ret != 0)
1551 return;
1552 interval = ts_interval(now, pw_exp);
1553 if (!is_last_req && (!interval || interval > 7 * 24 * 60 * 60))
1554 return;
1555
1556 if (!prompter)
1557 return;
1558
1559 ret = krb5_timestamp_to_string(pw_exp, ts, sizeof(ts));
1560 if (ret != 0)
1561 return;
1562
1563 if (interval < 3600) {
1564 snprintf(banner, sizeof(banner),
1565 _("Warning: Your password will expire in less than one hour "
1566 "on %s"), ts);
1567 } else if (interval < 86400 * 2) {
1568 snprintf(banner, sizeof(banner),
1569 _("Warning: Your password will expire in %d hour%s on %s"),
1570 interval / 3600, interval < 7200 ? "" : "s", ts);
1571 } else {
1572 snprintf(banner, sizeof(banner),
1573 _("Warning: Your password will expire in %d days on %s"),
1574 interval / 86400, ts);
1575 }
1576
1577 /* PROMPTER_INVOCATION */
1578 (*prompter)(context, data, 0, banner, 0, 0);
1579 }
1580
1581 /* Display a warning via the prompter if a deprecated enctype was used for
1582 * either the reply key or the session key. */
1583 static void
warn_deprecated(krb5_context context,krb5_init_creds_context ctx,krb5_enctype as_key_enctype)1584 warn_deprecated(krb5_context context, krb5_init_creds_context ctx,
1585 krb5_enctype as_key_enctype)
1586 {
1587 krb5_enctype etype;
1588 char encbuf[128], banner[256];
1589
1590 if (ctx->prompter == NULL)
1591 return;
1592
1593 if (krb5int_c_deprecated_enctype(as_key_enctype))
1594 etype = as_key_enctype;
1595 else if (krb5int_c_deprecated_enctype(ctx->cred.keyblock.enctype))
1596 etype = ctx->cred.keyblock.enctype;
1597 else
1598 return;
1599
1600 if (krb5_enctype_to_name(etype, FALSE, encbuf, sizeof(encbuf)) != 0)
1601 return;
1602 snprintf(banner, sizeof(banner),
1603 _("Warning: encryption type %s used for authentication is "
1604 "deprecated and will be disabled"), encbuf);
1605
1606 /* PROMPTER_INVOCATION */
1607 (*ctx->prompter)(context, ctx->prompter_data, NULL, banner, 0, NULL);
1608 }
1609
1610 /*
1611 * If ctx specifies an output ccache, create or refresh it (atomically, if
1612 * possible) with the obtained credential and any appropriate ccache
1613 * configuration.
1614 */
1615 static krb5_error_code
write_out_ccache(krb5_context context,krb5_init_creds_context ctx,krb5_boolean fast_avail)1616 write_out_ccache(krb5_context context, krb5_init_creds_context ctx,
1617 krb5_boolean fast_avail)
1618 {
1619 krb5_error_code ret;
1620 krb5_ccache out_ccache = k5_gic_opt_get_out_ccache(ctx->opt);
1621 krb5_ccache mcc = NULL;
1622 krb5_data yes = string2data("yes");
1623
1624 if (out_ccache == NULL)
1625 return 0;
1626
1627 ret = krb5_cc_new_unique(context, "MEMORY", NULL, &mcc);
1628 if (ret)
1629 goto cleanup;
1630
1631 ret = krb5_cc_initialize(context, mcc, ctx->cred.client);
1632 if (ret)
1633 goto cleanup;
1634
1635 if (fast_avail) {
1636 ret = krb5_cc_set_config(context, mcc, ctx->cred.server,
1637 KRB5_CC_CONF_FAST_AVAIL, &yes);
1638 if (ret)
1639 goto cleanup;
1640 }
1641
1642 ret = save_selected_preauth_type(context, mcc, ctx);
1643 if (ret)
1644 goto cleanup;
1645
1646 ret = save_cc_config_out_data(context, mcc, ctx);
1647 if (ret)
1648 goto cleanup;
1649
1650 ret = k5_cc_store_primary_cred(context, mcc, &ctx->cred);
1651 if (ret)
1652 goto cleanup;
1653
1654 ret = krb5_cc_move(context, mcc, out_ccache);
1655 if (ret)
1656 goto cleanup;
1657 mcc = NULL;
1658
1659 cleanup:
1660 if (mcc != NULL)
1661 krb5_cc_destroy(context, mcc);
1662 return ret;
1663 }
1664
1665 static krb5_error_code
init_creds_step_reply(krb5_context context,krb5_init_creds_context ctx,krb5_data * in)1666 init_creds_step_reply(krb5_context context,
1667 krb5_init_creds_context ctx,
1668 krb5_data *in)
1669 {
1670 krb5_error_code code;
1671 krb5_pa_data **kdc_padata = NULL;
1672 krb5_preauthtype kdc_pa_type;
1673 krb5_boolean retry = FALSE;
1674 int canon_flag = 0;
1675 uint32_t reply_code;
1676 krb5_keyblock *strengthen_key = NULL;
1677 krb5_keyblock encrypting_key;
1678 krb5_boolean fast_avail;
1679
1680 encrypting_key.length = 0;
1681 encrypting_key.contents = NULL;
1682
1683 /* process previous KDC response */
1684 code = init_creds_validate_reply(context, ctx, in);
1685 if (code != 0)
1686 goto cleanup;
1687
1688 /* per referrals draft, enterprise principals imply canonicalization */
1689 canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1690 ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1691
1692 if (ctx->err_reply != NULL) {
1693 krb5_free_pa_data(context, ctx->more_padata);
1694 krb5_free_pa_data(context, ctx->err_padata);
1695 ctx->more_padata = ctx->err_padata = NULL;
1696 code = krb5int_fast_process_error(context, ctx->fast_state,
1697 &ctx->err_reply, &ctx->err_padata,
1698 &retry);
1699 if (code != 0)
1700 goto cleanup;
1701 reply_code = ctx->err_reply->error;
1702 if (!ctx->restarted &&
1703 k5_upgrade_to_fast_p(context, ctx->fast_state, ctx->err_padata)) {
1704 /* Retry with FAST after discovering that the KDC supports
1705 * it. (FAST negotiation usually avoids this restart.) */
1706 TRACE_FAST_PADATA_UPGRADE(context);
1707 ctx->restarted = TRUE;
1708 code = restart_init_creds_loop(context, ctx, TRUE);
1709 } else if (!ctx->restarted && reply_code == KDC_ERR_PREAUTH_FAILED &&
1710 ctx->selected_preauth_type == KRB5_PADATA_NONE) {
1711 /* The KDC didn't like our informational padata (probably a pre-1.7
1712 * MIT krb5 KDC). Retry without it. */
1713 ctx->info_pa_permitted = FALSE;
1714 ctx->restarted = TRUE;
1715 code = restart_init_creds_loop(context, ctx, FALSE);
1716 } else if (reply_code == KDC_ERR_PREAUTH_EXPIRED) {
1717 /* We sent an expired KDC cookie. Start over, allowing another
1718 * FAST upgrade. */
1719 ctx->restarted = FALSE;
1720 code = restart_init_creds_loop(context, ctx, FALSE);
1721 } else if (ctx->identify_realm &&
1722 (reply_code == KDC_ERR_PREAUTH_REQUIRED ||
1723 reply_code == KDC_ERR_KEY_EXP)) {
1724 /* The client exists in this realm; we can stop. */
1725 ctx->complete = TRUE;
1726 goto cleanup;
1727 } else if (reply_code == KDC_ERR_PREAUTH_REQUIRED && retry) {
1728 note_req_timestamp(context, ctx, ctx->err_reply->stime,
1729 ctx->err_reply->susec);
1730 code = accept_method_data(context, ctx);
1731 } else if (reply_code == KDC_ERR_PREAUTH_FAILED && retry) {
1732 note_req_timestamp(context, ctx, ctx->err_reply->stime,
1733 ctx->err_reply->susec);
1734 /* Don't try again with the mechanism that failed. */
1735 code = k5_preauth_note_failed(ctx, ctx->selected_preauth_type);
1736 if (code)
1737 goto cleanup;
1738 ctx->selected_preauth_type = KRB5_PADATA_NONE;
1739 /* Accept or update method data if the KDC sent it. */
1740 if (ctx->err_padata != NULL)
1741 code = accept_method_data(context, ctx);
1742 } else if (reply_code == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED && retry) {
1743 ctx->more_padata = ctx->err_padata;
1744 ctx->err_padata = NULL;
1745 } else if (canon_flag && is_referral(context, ctx->err_reply,
1746 ctx->request->client)) {
1747 TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
1748 /* Rewrite request.client with realm from error reply */
1749 krb5_free_data_contents(context, &ctx->request->client->realm);
1750 code = krb5int_copy_data_contents(context,
1751 &ctx->err_reply->client->realm,
1752 &ctx->request->client->realm);
1753 if (code != 0)
1754 goto cleanup;
1755 /* Reset per-realm negotiation state. */
1756 ctx->restarted = FALSE;
1757 ctx->info_pa_permitted = TRUE;
1758 code = restart_init_creds_loop(context, ctx, FALSE);
1759 } else {
1760 if (retry && ctx->selected_preauth_type != KRB5_PADATA_NONE) {
1761 code = 0;
1762 } else {
1763 /* error + no hints (or no preauth mech) = give up */
1764 code = (krb5_error_code)reply_code + ERROR_TABLE_BASE_krb5;
1765 }
1766 }
1767
1768 /* Return error code, or continue with next iteration */
1769 goto cleanup;
1770 }
1771
1772 /* We have a response. Process it. */
1773 assert(ctx->reply != NULL);
1774
1775 /* Check for replies (likely forged) with unasked-for enctypes. */
1776 code = check_reply_enctype(ctx);
1777 if (code != 0)
1778 goto cleanup;
1779
1780 /* process any preauth data in the as_reply */
1781 code = krb5int_fast_process_response(context, ctx->fast_state,
1782 ctx->reply, &strengthen_key);
1783 if (code != 0)
1784 goto cleanup;
1785
1786 if (ctx->identify_realm) {
1787 /* Just getting a reply means the client exists in this realm. */
1788 ctx->complete = TRUE;
1789 goto cleanup;
1790 }
1791
1792 code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1793 ctx->reply->padata);
1794 if (code != 0)
1795 goto cleanup;
1796
1797 ctx->etype = ctx->reply->enc_part.enctype;
1798
1799 /* Process the final reply padata. Don't restrict the preauth types or
1800 * record a selected preauth type. */
1801 ctx->allowed_preauth_type = KRB5_PADATA_NONE;
1802 code = k5_preauth(context, ctx, ctx->reply->padata, FALSE, &kdc_padata,
1803 &kdc_pa_type);
1804 if (code != 0)
1805 goto cleanup;
1806
1807 /*
1808 * If we haven't gotten a salt from another source yet, set up one
1809 * corresponding to the client principal returned by the KDC. We
1810 * could get the same effect by passing local_as_reply->client to
1811 * gak_fct below, but that would put the canonicalized client name
1812 * in the prompt, which raises issues of needing to sanitize
1813 * unprintable characters. So for now we just let it affect the
1814 * salt. local_as_reply->client will be checked later on in
1815 * verify_as_reply.
1816 */
1817 if (ctx->default_salt) {
1818 code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1819 TRACE_INIT_CREDS_SALT_PRINC(context, &ctx->salt);
1820 if (code != 0)
1821 goto cleanup;
1822 }
1823
1824 code = decrypt_as_reply(context, ctx, strengthen_key, &encrypting_key);
1825 if (code)
1826 goto cleanup;
1827 TRACE_INIT_CREDS_DECRYPTED_REPLY(context, ctx->reply->enc_part2->session);
1828
1829 code = krb5int_fast_verify_nego(context, ctx->fast_state,
1830 ctx->reply, ctx->encoded_previous_request,
1831 &encrypting_key, &fast_avail);
1832 if (code)
1833 goto cleanup;
1834 code = verify_as_reply(context, ctx->request_time,
1835 ctx->request, ctx->reply);
1836 if (code != 0)
1837 goto cleanup;
1838 code = verify_anonymous(context, ctx->request, ctx->reply,
1839 &ctx->as_key);
1840 if (code)
1841 goto cleanup;
1842
1843 code = stash_as_reply(context, ctx->reply, &ctx->cred, NULL);
1844 if (code != 0)
1845 goto cleanup;
1846 code = write_out_ccache(context, ctx, fast_avail);
1847 if (code)
1848 k5_prependmsg(context, code, _("Failed to store credentials"));
1849
1850 k5_preauth_request_context_fini(context, ctx);
1851
1852 /* success */
1853 ctx->complete = TRUE;
1854 warn_pw_expiry(context, ctx->opt, ctx->prompter, ctx->prompter_data,
1855 ctx->in_tkt_service, ctx->reply);
1856 warn_deprecated(context, ctx, encrypting_key.enctype);
1857
1858 cleanup:
1859 krb5_free_pa_data(context, kdc_padata);
1860 krb5_free_keyblock(context, strengthen_key);
1861 krb5_free_keyblock_contents(context, &encrypting_key);
1862
1863 return code;
1864 }
1865
1866 /*
1867 * Do next step of credentials acquisition.
1868 *
1869 * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1870 * should be sent with TCP.
1871 */
1872 krb5_error_code KRB5_CALLCONV
krb5_init_creds_step(krb5_context context,krb5_init_creds_context ctx,krb5_data * in,krb5_data * out,krb5_data * realm,unsigned int * flags)1873 krb5_init_creds_step(krb5_context context,
1874 krb5_init_creds_context ctx,
1875 krb5_data *in,
1876 krb5_data *out,
1877 krb5_data *realm,
1878 unsigned int *flags)
1879 {
1880 krb5_error_code code, code2;
1881
1882 *flags = 0;
1883
1884 out->data = NULL;
1885 out->length = 0;
1886
1887 realm->data = NULL;
1888 realm->length = 0;
1889
1890 if (ctx->complete)
1891 return EINVAL;
1892
1893 code = k5_preauth_check_context(context, ctx);
1894 if (code)
1895 return code;
1896
1897 if (in->length != 0) {
1898 code = init_creds_step_reply(context, ctx, in);
1899 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1900 code2 = krb5int_copy_data_contents(context,
1901 ctx->encoded_previous_request,
1902 out);
1903 if (code2 != 0) {
1904 code = code2;
1905 goto cleanup;
1906 }
1907 goto copy_realm;
1908 }
1909 if (code != 0 || ctx->complete)
1910 goto cleanup;
1911 } else {
1912 code = restart_init_creds_loop(context, ctx, FALSE);
1913 if (code)
1914 goto cleanup;
1915 }
1916
1917 code = init_creds_step_request(context, ctx, out);
1918 if (code != 0)
1919 goto cleanup;
1920
1921 /* Only a new request increments the loop count, not a TCP retry */
1922 ctx->loopcount++;
1923
1924 copy_realm:
1925 assert(ctx->request->server != NULL);
1926
1927 code2 = krb5int_copy_data_contents(context,
1928 &ctx->request->server->realm,
1929 realm);
1930 if (code2 != 0) {
1931 code = code2;
1932 goto cleanup;
1933 }
1934
1935 cleanup:
1936 if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
1937 char *client_name;
1938
1939 /* See if we can produce a more detailed error message */
1940 code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
1941 if (code2 == 0) {
1942 k5_setmsg(context, code,
1943 _("Client '%s' not found in Kerberos database"),
1944 client_name);
1945 krb5_free_unparsed_name(context, client_name);
1946 }
1947 }
1948
1949 *flags = ctx->complete ? 0 : KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1950 return code;
1951 }
1952
1953 static krb5_error_code
try_init_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options,get_as_key_fn gak_fct,void * gak_data,krb5_boolean use_primary,struct kdclist * kdcs,krb5_kdc_rep ** as_reply)1954 try_init_creds(krb5_context context, krb5_creds *creds, krb5_principal client,
1955 krb5_prompter_fct prompter, void *prompter_data,
1956 krb5_deltat start_time, const char *in_tkt_service,
1957 krb5_get_init_creds_opt *options, get_as_key_fn gak_fct,
1958 void *gak_data, krb5_boolean use_primary, struct kdclist *kdcs,
1959 krb5_kdc_rep **as_reply)
1960 {
1961 krb5_error_code code;
1962 krb5_init_creds_context ctx = NULL;
1963
1964 code = krb5_init_creds_init(context,
1965 client,
1966 prompter,
1967 prompter_data,
1968 start_time,
1969 options,
1970 &ctx);
1971 if (code != 0)
1972 goto cleanup;
1973
1974 ctx->gak_fct = gak_fct;
1975 ctx->gak_data = gak_data;
1976
1977 if (in_tkt_service) {
1978 code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1979 if (code != 0)
1980 goto cleanup;
1981 }
1982
1983 code = k5_init_creds_get(context, ctx, use_primary, kdcs);
1984 if (code != 0)
1985 goto cleanup;
1986
1987 code = krb5_init_creds_get_creds(context, ctx, creds);
1988 if (code != 0)
1989 goto cleanup;
1990
1991 if (as_reply != NULL) {
1992 *as_reply = ctx->reply;
1993 ctx->reply = NULL;
1994 }
1995
1996 cleanup:
1997 krb5_init_creds_free(context, ctx);
1998
1999 return code;
2000 }
2001
2002 krb5_error_code
k5_get_init_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options,get_as_key_fn gak_fct,void * gak_data,krb5_kdc_rep ** as_reply)2003 k5_get_init_creds(krb5_context context, krb5_creds *creds,
2004 krb5_principal client, krb5_prompter_fct prompter,
2005 void *prompter_data, krb5_deltat start_time,
2006 const char *in_tkt_service, krb5_get_init_creds_opt *options,
2007 get_as_key_fn gak_fct, void *gak_data,
2008 krb5_kdc_rep **as_reply)
2009 {
2010 krb5_error_code ret;
2011 struct kdclist *kdcs = NULL;
2012 struct errinfo errsave = EMPTY_ERRINFO;
2013
2014 ret = k5_kdclist_create(&kdcs);
2015 if (ret)
2016 goto cleanup;
2017
2018 /* Try getting the requested ticket from any KDC. */
2019 ret = try_init_creds(context, creds, client, prompter, prompter_data,
2020 start_time, in_tkt_service, options, gak_fct,
2021 gak_data, FALSE, kdcs, as_reply);
2022 if (!ret)
2023 goto cleanup;
2024
2025 /* If all of the KDCs are unavailable, or if the error was due to a user
2026 * interrupt, fail. */
2027 if (ret == KRB5_KDC_UNREACH || ret == KRB5_REALM_CANT_RESOLVE ||
2028 ret == KRB5_LIBOS_PWDINTR || ret == KRB5_LIBOS_CANTREADPWD)
2029 goto cleanup;
2030
2031 /* If any reply came from a replica, try again with only primary KDCs. */
2032 if (k5_kdclist_any_replicas(context, kdcs)) {
2033 k5_save_ctx_error(context, ret, &errsave);
2034 TRACE_INIT_CREDS_PRIMARY(context);
2035 ret = try_init_creds(context, creds, client, prompter, prompter_data,
2036 start_time, in_tkt_service, options, gak_fct,
2037 gak_data, TRUE, NULL, as_reply);
2038 if (ret == KRB5_KDC_UNREACH || ret == KRB5_REALM_CANT_RESOLVE ||
2039 ret == KRB5_REALM_UNKNOWN) {
2040 /* We couldn't contact a primary KDC; return the error from the
2041 * replica we were able to contact. */
2042 ret = k5_restore_ctx_error(context, &errsave);
2043 }
2044 }
2045
2046 cleanup:
2047 k5_kdclist_free(kdcs);
2048 k5_clear_error(&errsave);
2049 return ret;
2050 }
2051
2052 krb5_error_code
k5_identify_realm(krb5_context context,krb5_principal client,const krb5_data * subject_cert,krb5_principal * client_out)2053 k5_identify_realm(krb5_context context, krb5_principal client,
2054 const krb5_data *subject_cert, krb5_principal *client_out)
2055 {
2056 krb5_error_code ret;
2057 krb5_get_init_creds_opt *opts = NULL;
2058 krb5_init_creds_context ctx = NULL;
2059
2060 *client_out = NULL;
2061
2062 ret = krb5_get_init_creds_opt_alloc(context, &opts);
2063 if (ret)
2064 goto cleanup;
2065 krb5_get_init_creds_opt_set_tkt_life(opts, 15);
2066 krb5_get_init_creds_opt_set_renew_life(opts, 0);
2067 krb5_get_init_creds_opt_set_forwardable(opts, 0);
2068 krb5_get_init_creds_opt_set_proxiable(opts, 0);
2069 krb5_get_init_creds_opt_set_canonicalize(opts, 1);
2070
2071 ret = krb5_init_creds_init(context, client, NULL, NULL, 0, opts, &ctx);
2072 if (ret)
2073 goto cleanup;
2074
2075 ctx->identify_realm = TRUE;
2076 ctx->subject_cert = subject_cert;
2077
2078 ret = k5_init_creds_get(context, ctx, FALSE, NULL);
2079 if (ret)
2080 goto cleanup;
2081
2082 TRACE_INIT_CREDS_IDENTIFIED_REALM(context, &ctx->request->client->realm);
2083 ret = krb5_copy_principal(context, ctx->request->client, client_out);
2084
2085 cleanup:
2086 krb5_get_init_creds_opt_free(context, opts);
2087 krb5_init_creds_free(context, ctx);
2088 return ret;
2089 }
2090
2091 krb5_error_code
k5_populate_gic_opt(krb5_context context,krb5_get_init_creds_opt ** out,krb5_flags options,krb5_address * const * addrs,krb5_enctype * ktypes,krb5_preauthtype * pre_auth_types,krb5_creds * creds)2092 k5_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
2093 krb5_flags options, krb5_address *const *addrs,
2094 krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types,
2095 krb5_creds *creds)
2096 {
2097 int i;
2098 krb5_timestamp starttime;
2099 krb5_deltat lifetime;
2100 krb5_get_init_creds_opt *opt;
2101 krb5_error_code retval;
2102
2103 *out = NULL;
2104 retval = krb5_get_init_creds_opt_alloc(context, &opt);
2105 if (retval)
2106 return(retval);
2107
2108 if (addrs)
2109 krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
2110 if (ktypes) {
2111 i = k5_count_etypes(ktypes);
2112 if (i)
2113 krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
2114 }
2115 if (pre_auth_types) {
2116 for (i=0; pre_auth_types[i]; i++);
2117 if (i)
2118 krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
2119 }
2120 if (options&KDC_OPT_FORWARDABLE)
2121 krb5_get_init_creds_opt_set_forwardable(opt, 1);
2122 else krb5_get_init_creds_opt_set_forwardable(opt, 0);
2123 if (options&KDC_OPT_PROXIABLE)
2124 krb5_get_init_creds_opt_set_proxiable(opt, 1);
2125 else krb5_get_init_creds_opt_set_proxiable(opt, 0);
2126 if (creds && creds->times.endtime) {
2127 retval = krb5_timeofday(context, &starttime);
2128 if (retval)
2129 goto cleanup;
2130 if (creds->times.starttime) starttime = creds->times.starttime;
2131 lifetime = ts_delta(creds->times.endtime, starttime);
2132 krb5_get_init_creds_opt_set_tkt_life(opt, lifetime);
2133 }
2134 *out = opt;
2135 return 0;
2136
2137 cleanup:
2138 krb5_get_init_creds_opt_free(context, opt);
2139 return retval;
2140 }
2141