1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/get_in_tkt.c */
4 * Copyright 1990,1991, 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.
30 #include "int-proto.h"
33 #include "init_creds_ctx.h"
35 /* some typedef's for the function args to make things look a bit cleaner */
37 static krb5_error_code make_preauth_list (krb5_context,
39 int, krb5_pa_data ***);
40 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
42 krb5_pa_data **padata);
45 * This function performs 32 bit bounded addition so we can generate
46 * lifetimes without overflowing krb5_int32
49 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
51 if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
52 /* sum will be be greater than KRB5_INT32_MAX */
53 return KRB5_INT32_MAX;
54 } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
55 /* sum will be less than KRB5_INT32_MIN */
56 return KRB5_INT32_MIN;
62 static krb5_error_code
63 decrypt_as_reply(krb5_context context, krb5_kdc_req *request,
64 krb5_kdc_rep *as_reply, krb5_keyblock *key)
66 if (as_reply->enc_part2)
69 return krb5_kdc_rep_decrypt_proc(context, key, NULL, as_reply);
73 * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC
74 * contribution key. This routine confirms that the session key is of the
75 * right form for fully anonymous requests. It is here rather than in the
76 * preauth code because the session key cannot be verified until the AS reply
77 * is decrypted and the preauth code all runs before the AS reply is decrypted.
79 static krb5_error_code
80 verify_anonymous( krb5_context context, krb5_kdc_req *request,
81 krb5_kdc_rep *reply, krb5_keyblock *as_key)
83 krb5_error_code ret = 0;
86 krb5_keyblock *kdc_key = NULL, *expected = NULL;
87 krb5_enc_data *enc = NULL;
88 krb5_keyblock *session = reply->enc_part2->session;
90 if (!krb5_principal_compare_any_realm(context, request->client,
91 krb5_anonymous_principal()))
92 return 0; /* Only applies to fully anonymous */
93 pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX);
95 goto verification_error;
96 scratch.length = pa->length;
97 scratch.data = (char *) pa->contents;
98 ret = decode_krb5_enc_data( &scratch, &enc);
101 scratch.data = k5alloc(enc->ciphertext.length, &ret);
104 scratch.length = enc->ciphertext.length;
105 ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX,
106 NULL /*cipherstate*/, enc, &scratch);
111 ret = decode_krb5_encryption_key( &scratch, &kdc_key);
112 zap(scratch.data, scratch.length);
116 ret = krb5_c_fx_cf2_simple(context, kdc_key, "PKINIT",
117 as_key, "KEYEXCHANGE", &expected);
120 if ((expected->enctype != session->enctype) ||
121 (expected->length != session->length) ||
122 (memcmp(expected->contents, session->contents, expected->length) != 0))
123 goto verification_error;
126 krb5_free_keyblock(context, kdc_key);
128 krb5_free_keyblock(context, expected);
130 krb5_free_enc_data(context, enc);
133 ret = KRB5_KDCREP_MODIFIED;
134 krb5_set_error_message(context, ret, _("Reply has wrong form of session "
135 "key for anonymous request"));
139 static krb5_error_code
140 verify_as_reply(krb5_context context,
141 krb5_timestamp time_now,
142 krb5_kdc_req *request,
143 krb5_kdc_rep *as_reply)
145 krb5_error_code retval;
148 krb5_timestamp time_offset;
150 /* check the contents for sanity: */
151 if (!as_reply->enc_part2->times.starttime)
152 as_reply->enc_part2->times.starttime =
153 as_reply->enc_part2->times.authtime;
156 * We only allow the AS-REP server name to be changed if the
157 * caller set the canonicalize flag (or requested an enterprise
158 * principal) and we requested (and received) a TGT.
160 canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
161 (krb5_princ_type(context, request->client) ==
162 KRB5_NT_ENTERPRISE_PRINCIPAL) ||
163 (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS);
165 canon_ok = IS_TGS_PRINC(context, request->server) &&
166 IS_TGS_PRINC(context, as_reply->enc_part2->server);
167 if (!canon_ok && (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS)) {
168 canon_ok = krb5_principal_compare_any_realm(context,
170 krb5_anonymous_principal());
176 (!krb5_principal_compare(context, as_reply->client, request->client) ||
177 !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)))
178 || !krb5_principal_compare(context, as_reply->enc_part2->server, as_reply->ticket->server)
179 || (request->nonce != as_reply->enc_part2->nonce)
180 /* XXX check for extraneous flags */
181 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
182 || ((request->kdc_options & KDC_OPT_POSTDATED) &&
183 (request->from != 0) &&
184 (request->from != as_reply->enc_part2->times.starttime))
185 || ((request->till != 0) &&
186 (as_reply->enc_part2->times.endtime > request->till))
187 || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
188 (request->rtime != 0) &&
189 (as_reply->enc_part2->times.renew_till > request->rtime))
190 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
191 !(request->kdc_options & KDC_OPT_RENEWABLE) &&
192 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
193 (request->till != 0) &&
194 (as_reply->enc_part2->times.renew_till > request->till))
196 return KRB5_KDCREP_MODIFIED;
199 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
200 time_offset = as_reply->enc_part2->times.authtime - time_now;
201 retval = krb5_set_time_offsets(context, time_offset, 0);
205 if ((request->from == 0) &&
206 (labs(as_reply->enc_part2->times.starttime - time_now)
207 > context->clockskew))
208 return (KRB5_KDCREP_SKEW);
213 static krb5_error_code
214 stash_as_reply(krb5_context context,
215 krb5_timestamp time_now,
216 krb5_kdc_req *request,
217 krb5_kdc_rep *as_reply,
221 krb5_error_code retval;
223 krb5_principal client;
224 krb5_principal server;
230 if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
234 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
238 /* fill in the credentials */
239 if ((retval = krb5_copy_keyblock_contents(context,
240 as_reply->enc_part2->session,
244 creds->times = as_reply->enc_part2->times;
245 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
246 be encrypted in skey */
247 creds->ticket_flags = as_reply->enc_part2->flags;
248 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
252 creds->second_ticket.length = 0;
253 creds->second_ticket.data = 0;
255 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
258 creds->ticket = *packet;
261 /* store it in the ccache! */
263 if ((retval = krb5_cc_store_cred(context, ccache, creds)))
267 creds->client = client;
269 creds->server = server;
274 krb5_free_principal(context, client);
276 krb5_free_principal(context, server);
277 if (creds->keyblock.contents) {
278 memset(creds->keyblock.contents, 0,
279 creds->keyblock.length);
280 free(creds->keyblock.contents);
281 creds->keyblock.contents = 0;
282 creds->keyblock.length = 0;
284 if (creds->ticket.data) {
285 free(creds->ticket.data);
286 creds->ticket.data = 0;
288 if (creds->addresses) {
289 krb5_free_addresses(context, creds->addresses);
290 creds->addresses = 0;
296 static krb5_error_code
297 make_preauth_list(krb5_context context,
298 krb5_preauthtype * ptypes,
300 krb5_pa_data *** ret_list)
302 krb5_preauthtype * ptypep;
303 krb5_pa_data ** preauthp;
307 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
311 /* allocate space for a NULL to terminate the list */
314 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
317 for (i=0; i<nptypes; i++) {
319 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
325 preauthp[i]->magic = KV5M_PA_DATA;
326 preauthp[i]->pa_type = ptypes[i];
327 preauthp[i]->length = 0;
328 preauthp[i]->contents = 0;
331 /* fill in the terminating NULL */
333 preauthp[nptypes] = NULL;
335 *ret_list = preauthp;
339 #define MAX_IN_TKT_LOOPS 16
341 static krb5_error_code
342 request_enc_pa_rep(krb5_pa_data ***padptr)
345 krb5_pa_data **pad = *padptr;
346 krb5_pa_data *pa= NULL;
348 for (size=0; pad[size]; size++);
349 pad = realloc(pad, sizeof(*pad)*(size+2));
354 pa = malloc(sizeof(krb5_pa_data));
359 pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
365 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
366 * libdefaults entry are listed before any others. */
367 static krb5_error_code
368 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
369 krb5_pa_data **padata)
375 char *q, *preauth_types = NULL;
377 int need_free_string = 1;
379 if ((padata == NULL) || (padata[0] == NULL)) {
383 ret = krb5int_libdefault_string(context, realm, KRB5_CONF_PREFERRED_PREAUTH_TYPES,
385 if ((ret != 0) || (preauth_types == NULL)) {
386 /* Try to use PKINIT first. */
387 preauth_types = "17, 16, 15, 14";
388 need_free_string = 0;
392 fprintf (stderr, "preauth data types before sorting:");
393 for (i = 0; padata[i]; i++) {
394 fprintf (stderr, " %d", padata[i]->pa_type);
396 fprintf (stderr, "\n");
400 for (p = preauth_types; *p != '\0';) {
401 /* skip whitespace to find an entry */
402 p += strspn(p, ", ");
404 /* see if we can extract a number */
405 l = strtol(p, &q, 10);
406 if ((q != NULL) && (q > p)) {
407 /* got a valid number; search for a matchin entry */
408 for (i = base; padata[i] != NULL; i++) {
409 /* bubble the matching entry to the front of the list */
410 if (padata[i]->pa_type == l) {
412 for (j = i; j > base; j--)
413 padata[j] = padata[j - 1];
425 if (need_free_string)
429 fprintf (stderr, "preauth data types after sorting:");
430 for (i = 0; padata[i]; i++)
431 fprintf (stderr, " %d", padata[i]->pa_type);
432 fprintf (stderr, "\n");
438 static krb5_error_code
439 build_in_tkt_name(krb5_context context,
440 const char *in_tkt_service,
441 krb5_const_principal client,
442 krb5_principal *server_out)
445 krb5_principal server = NULL;
449 if (in_tkt_service) {
450 ret = krb5_parse_name_flags(context, in_tkt_service,
451 KRB5_PRINCIPAL_PARSE_IGNORE_REALM,
455 krb5_free_data_contents(context, &server->realm);
456 ret = krb5int_copy_data_contents(context, &client->realm,
459 krb5_free_principal(context, server);
463 ret = krb5_build_principal_ext(context, &server,
464 client->realm.length,
468 client->realm.length,
475 * Windows Server 2008 R2 RODC insists on TGS principal names having the
478 if (krb5_princ_size(context, server) == 2 &&
479 data_eq_string(*krb5_princ_component(context, server, 0),
481 krb5_princ_type(context, server) = KRB5_NT_SRV_INST;
483 *server_out = server;
488 krb5_init_creds_free(krb5_context context,
489 krb5_init_creds_context ctx)
494 if (ctx->opte != NULL && krb5_gic_opt_is_shadowed(ctx->opte)) {
495 krb5_get_init_creds_opt_free(context,
496 (krb5_get_init_creds_opt *)ctx->opte);
498 free(ctx->in_tkt_service);
499 zap(ctx->password.data, ctx->password.length);
500 krb5_free_data_contents(context, &ctx->password);
501 krb5_free_error(context, ctx->err_reply);
502 krb5_free_pa_data(context, ctx->err_padata);
503 krb5_free_cred_contents(context, &ctx->cred);
504 krb5_free_kdc_req(context, ctx->request);
505 krb5_free_kdc_rep(context, ctx->reply);
506 krb5_free_data(context, ctx->outer_request_body);
507 krb5_free_data(context, ctx->inner_request_body);
508 krb5_free_data(context, ctx->encoded_previous_request);
509 krb5int_fast_free_state(context, ctx->fast_state);
510 krb5_free_pa_data(context, ctx->preauth_to_use);
511 krb5_free_data_contents(context, &ctx->salt);
512 krb5_free_data_contents(context, &ctx->s2kparams);
513 krb5_free_keyblock_contents(context, &ctx->as_key);
518 k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
521 krb5_error_code code;
525 unsigned int flags = 0;
536 code = krb5_init_creds_step(context,
542 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) {
543 TRACE_INIT_CREDS_RETRY_TCP(context);
545 } else if (code != 0 || !(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
548 krb5_free_data_contents(context, &reply);
550 code = krb5_sendto_kdc(context, &request, &realm,
551 &reply, use_master, tcp_only);
555 krb5_free_data_contents(context, &request);
556 krb5_free_data_contents(context, &realm);
559 krb5_free_data_contents(context, &request);
560 krb5_free_data_contents(context, &reply);
561 krb5_free_data_contents(context, &realm);
567 krb5_error_code KRB5_CALLCONV
568 krb5_init_creds_get(krb5_context context,
569 krb5_init_creds_context ctx)
573 return k5_init_creds_get(context, ctx, &use_master);
576 krb5_error_code KRB5_CALLCONV
577 krb5_init_creds_get_creds(krb5_context context,
578 krb5_init_creds_context ctx,
582 return KRB5_NO_TKT_SUPPLIED;
584 return krb5int_copy_creds_contents(context, &ctx->cred, creds);
587 krb5_error_code KRB5_CALLCONV
588 krb5_init_creds_get_times(krb5_context context,
589 krb5_init_creds_context ctx,
590 krb5_ticket_times *times)
593 return KRB5_NO_TKT_SUPPLIED;
595 *times = ctx->cred.times;
600 krb5_error_code KRB5_CALLCONV
601 krb5_init_creds_get_error(krb5_context context,
602 krb5_init_creds_context ctx,
605 krb5_error_code code;
606 krb5_error *ret = NULL;
610 if (ctx->err_reply == NULL)
613 ret = k5alloc(sizeof(*ret), &code);
617 ret->magic = KV5M_ERROR;
618 ret->ctime = ctx->err_reply->ctime;
619 ret->cusec = ctx->err_reply->cusec;
620 ret->susec = ctx->err_reply->susec;
621 ret->stime = ctx->err_reply->stime;
622 ret->error = ctx->err_reply->error;
624 if (ctx->err_reply->client != NULL) {
625 code = krb5_copy_principal(context, ctx->err_reply->client,
631 code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
635 code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
640 code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
649 krb5_free_error(context, ret);
655 * Throw away any state related to specific realm either at the beginning of a
656 * request, or when a realm changes, or when we start to use FAST after
657 * assuming we would not do so.
659 * @param padata padata from an error if an error from the realm we now expect
660 * to talk to caused the restart. Used to infer negotiation characteristics
661 * such as whether FAST is used.
663 static krb5_error_code
664 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
665 krb5_pa_data **padata)
667 krb5_error_code code = 0;
668 unsigned char random_buf[4];
669 krb5_data random_data;
672 if (ctx->preauth_to_use) {
673 krb5_free_pa_data(context, ctx->preauth_to_use);
674 ctx->preauth_to_use = NULL;
677 if (ctx->fast_state) {
678 krb5int_fast_free_state(context, ctx->fast_state);
679 ctx->fast_state = NULL;
681 code = krb5int_fast_make_state(context, &ctx->fast_state);
684 ctx->preauth_rock.fast_state = ctx->fast_state;
685 krb5_preauth_request_context_init(context);
686 if (ctx->outer_request_body) {
687 krb5_free_data(context, ctx->outer_request_body);
688 ctx->outer_request_body = NULL;
691 (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
692 if ((code = make_preauth_list(context, ctx->opte->preauth_list,
693 ctx->opte->preauth_list_length,
694 &ctx->preauth_to_use)))
698 /* Set the request nonce. */
699 random_data.length = 4;
700 random_data.data = (char *)random_buf;
701 code = krb5_c_random_make_octets(context, &random_data);
705 * See RT ticket 3196 at MIT. If we set the high bit, we may have
706 * compatibility problems with Heimdal, because we (incorrectly) encode
707 * this value as signed.
709 ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
710 krb5_free_principal(context, ctx->request->server);
711 ctx->request->server = NULL;
713 code = build_in_tkt_name(context, ctx->in_tkt_service,
714 ctx->request->client,
715 &ctx->request->server);
719 ctx->request_time = time(NULL);
721 code = krb5int_fast_as_armor(context, ctx->fast_state,
722 ctx->opte, ctx->request);
725 if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
726 code = krb5int_fast_as_armor(context, ctx->fast_state,
727 ctx->opte, ctx->request);
731 /* give the preauth plugins a chance to prep the request body */
732 krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
734 /* Omit request start time in the common case. MIT and Heimdal KDCs will
735 * ignore it for non-postdated tickets anyway. */
736 from = krb5int_addint32(ctx->request_time, ctx->start_time);
737 if (ctx->start_time != 0)
738 ctx->request->from = from;
739 ctx->request->till = krb5int_addint32(from, ctx->tkt_life);
741 if (ctx->renew_life > 0) {
742 ctx->request->rtime =
743 krb5int_addint32(from, ctx->renew_life);
744 if (ctx->request->rtime < ctx->request->till) {
745 /* don't ask for a smaller renewable time than the lifetime */
746 ctx->request->rtime = ctx->request->till;
748 ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
750 ctx->request->rtime = 0;
751 code = krb5int_fast_prep_req_body(context, ctx->fast_state,
753 &ctx->outer_request_body);
760 krb5_error_code KRB5_CALLCONV
761 krb5_init_creds_init(krb5_context context,
762 krb5_principal client,
763 krb5_prompter_fct prompter,
765 krb5_deltat start_time,
766 krb5_get_init_creds_opt *options,
767 krb5_init_creds_context *pctx)
769 krb5_error_code code;
770 krb5_init_creds_context ctx;
773 krb5_gic_opt_ext *opte;
774 krb5_get_init_creds_opt local_opts;
776 TRACE_INIT_CREDS(context, client);
778 ctx = k5alloc(sizeof(*ctx), &code);
782 ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
785 ctx->enc_pa_rep_permitted = 1;
786 code = krb5_copy_principal(context, client, &ctx->request->client);
790 ctx->prompter = prompter;
791 ctx->prompter_data = data;
792 ctx->gak_fct = krb5_get_as_key_password;
793 ctx->gak_data = &ctx->password;
795 ctx->request_time = 0; /* filled in later */
796 ctx->start_time = start_time;
798 if (options == NULL) {
800 * We initialize a non-extended options because that way the shadowed
801 * flag will be sent and they will be freed when the init_creds context
802 * is freed. The options will be extended and copied off the stack into
803 * storage by opt_to_opte.
805 krb5_get_init_creds_opt_init(&local_opts);
806 options = &local_opts;
809 code = krb5int_gic_opt_to_opte(context, options,
810 &ctx->opte, 1, "krb5_init_creds_init");
816 ctx->preauth_rock.magic = CLIENT_ROCK_MAGIC;
817 ctx->preauth_rock.etype = &ctx->etype;
818 ctx->preauth_rock.as_key = &ctx->as_key;
819 ctx->preauth_rock.gak_fct = &ctx->gak_fct;
820 ctx->preauth_rock.gak_data = &ctx->gak_data;
821 ctx->preauth_rock.default_salt = &ctx->default_salt;
822 ctx->preauth_rock.salt = &ctx->salt;
823 ctx->preauth_rock.s2kparams = &ctx->s2kparams;
824 ctx->preauth_rock.client = client;
825 ctx->preauth_rock.prompter = prompter;
826 ctx->preauth_rock.prompter_data = data;
828 /* Initialise request parameters as per krb5_get_init_creds() */
829 ctx->request->kdc_options = context->kdc_default_options;
832 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
833 tmp = opte->forwardable;
834 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
835 KRB5_CONF_FORWARDABLE, &tmp) == 0)
840 ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
843 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
844 tmp = opte->proxiable;
845 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
846 KRB5_CONF_PROXIABLE, &tmp) == 0)
851 ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
854 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
856 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
857 KRB5_CONF_CANONICALIZE, &tmp) == 0)
862 ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
865 if (ctx->start_time > 0)
866 ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
868 /* ticket lifetime */
869 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
870 ctx->tkt_life = options->tkt_life;
871 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
872 KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
873 code = krb5_string_to_deltat(str, &ctx->tkt_life);
879 ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
881 /* renewable lifetime */
882 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
883 ctx->renew_life = options->renew_life;
884 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
885 KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
886 code = krb5_string_to_deltat(str, &ctx->renew_life);
894 if (ctx->renew_life > 0)
895 ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
898 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
899 ctx->request->ktype =
900 k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
904 ctx->request->nktypes = opte->etype_list_length;
905 memcpy(ctx->request->ktype, opte->etype_list,
906 ctx->request->nktypes * sizeof(krb5_enctype));
907 } else if (krb5_get_default_in_tkt_ktypes(context,
908 &ctx->request->ktype) == 0) {
909 ctx->request->nktypes = krb5int_count_etypes(ctx->request->ktype);
911 /* there isn't any useful default here. */
912 code = KRB5_CONFIG_ETYPE_NOSUPP;
917 * Set a default enctype for optimistic preauth. If we're not doing
918 * optimistic preauth, this should ordinarily get overwritten when we
919 * process the etype-info2 of the preauth-required error.
921 if (ctx->request->nktypes > 0)
922 ctx->etype = ctx->request->ktype[0];
925 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
926 code = krb5_copy_addresses(context, opte->address_list,
927 &ctx->request->addresses);
930 } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
931 KRB5_CONF_NOADDRESSES, &tmp) != 0
933 ctx->request->addresses = NULL;
935 code = krb5_os_localaddr(context, &ctx->request->addresses);
940 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
941 code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
944 ctx->default_salt = FALSE;
946 ctx->salt = empty_data();
947 ctx->default_salt = TRUE;
951 if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
952 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
953 /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
954 if (client->length == 1 && client->data[0].length ==0) {
955 krb5_principal new_client;
956 code = krb5_build_principal_ext(context, &new_client,
957 client->realm.length,
959 strlen(KRB5_WELLKNOWN_NAMESTR),
960 KRB5_WELLKNOWN_NAMESTR,
961 strlen(KRB5_ANONYMOUS_PRINCSTR),
962 KRB5_ANONYMOUS_PRINCSTR,
966 krb5_free_principal(context, ctx->request->client);
967 ctx->request->client = new_client;
968 krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
971 /* We will also handle anonymous if the input principal is the anonymous
973 if (krb5_principal_compare_any_realm(context, ctx->request->client,
974 krb5_anonymous_principal())) {
975 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
976 krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
978 code = restart_init_creds_loop(context, ctx, NULL);
986 krb5_init_creds_free(context, ctx);
992 krb5_error_code KRB5_CALLCONV
993 krb5_init_creds_set_service(krb5_context context,
994 krb5_init_creds_context ctx,
999 TRACE_INIT_CREDS_SERVICE(context, service);
1001 s = strdup(service);
1005 free(ctx->in_tkt_service);
1006 ctx->in_tkt_service = s;
1008 krb5_preauth_request_context_fini(context);
1009 return restart_init_creds_loop(context, ctx, NULL);
1012 static krb5_error_code
1013 init_creds_validate_reply(krb5_context context,
1014 krb5_init_creds_context ctx,
1017 krb5_error_code code;
1018 krb5_error *error = NULL;
1019 krb5_kdc_rep *as_reply = NULL;
1021 krb5_free_error(context, ctx->err_reply);
1022 ctx->err_reply = NULL;
1024 krb5_free_kdc_rep(context, ctx->reply);
1027 if (krb5_is_krb_error(reply)) {
1028 code = decode_krb5_error(reply, &error);
1032 assert(error != NULL);
1034 TRACE_INIT_CREDS_ERROR_REPLY(context,
1035 error->error + ERROR_TABLE_BASE_krb5);
1036 if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1037 krb5_free_error(context, error);
1038 return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1040 ctx->err_reply = error;
1046 * Check to make sure it isn't a V4 reply.
1048 if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1049 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1050 #define V4_KRB_PROT_VERSION 4
1051 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
1052 /* check here for V4 reply */
1053 unsigned int t_switch;
1055 /* From v4 g_in_tkt.c: This used to be
1056 switch (pkt_msg_type(rpkt) & ~1) {
1057 but SCO 3.2v4 cc compiled that incorrectly. */
1058 t_switch = reply->data[1];
1061 if (t_switch == V4_AUTH_MSG_ERR_REPLY
1062 && reply->data[0] == V4_KRB_PROT_VERSION) {
1063 code = KRB5KRB_AP_ERR_V4_REPLY;
1065 code = KRB5KRB_AP_ERR_MSG_TYPE;
1070 /* It must be a KRB_AS_REP message, or an bad returned packet */
1071 code = decode_krb5_as_rep(reply, &as_reply);
1075 if (as_reply->msg_type != KRB5_AS_REP) {
1076 krb5_free_kdc_rep(context, as_reply);
1077 return KRB5KRB_AP_ERR_MSG_TYPE;
1080 ctx->reply = as_reply;
1085 static krb5_error_code
1086 init_creds_step_request(krb5_context context,
1087 krb5_init_creds_context ctx,
1090 krb5_error_code code;
1091 krb5_boolean got_real;
1093 krb5_data random_data;
1095 if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1096 code = KRB5_GET_IN_TKT_LOOP;
1100 * RFC 6113 requires a new nonce for the inner request on each try. It's
1101 * permitted to change the nonce even for non-FAST so we do here.
1103 random_data.length = 4;
1104 random_data.data = (char *)random_buf;
1105 code = krb5_c_random_make_octets(context, &random_data);
1109 * See RT ticket 3196 at MIT. If we set the high bit, we may have
1110 * compatibility problems with Heimdal, because we (incorrectly) encode
1111 * this value as signed.
1113 ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
1114 krb5_free_data(context, ctx->inner_request_body);
1115 ctx->inner_request_body = NULL;
1116 code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
1120 if (ctx->err_reply == NULL) {
1121 /* either our first attempt, or retrying after PREAUTH_NEEDED */
1122 code = krb5_do_preauth(context,
1124 ctx->inner_request_body,
1125 ctx->encoded_previous_request,
1126 ctx->preauth_to_use,
1127 &ctx->request->padata,
1133 if (code == 0 && !got_real && ctx->preauth_required)
1134 code = KRB5_PREAUTH_FAILED;
1138 if (ctx->preauth_to_use != NULL) {
1140 * Retry after an error other than PREAUTH_NEEDED,
1141 * using ctx->err_padata to figure out what to change.
1143 code = krb5_do_preauth_tryagain(context,
1145 ctx->inner_request_body,
1146 ctx->encoded_previous_request,
1147 ctx->preauth_to_use,
1148 &ctx->request->padata,
1156 /* No preauth supplied, so can't query the plugins. */
1157 code = KRB5KRB_ERR_GENERIC;
1160 /* couldn't come up with anything better */
1161 code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1166 if (ctx->encoded_previous_request != NULL) {
1167 krb5_free_data(context, ctx->encoded_previous_request);
1168 ctx->encoded_previous_request = NULL;
1170 if (ctx->request->padata)
1171 ctx->sent_nontrivial_preauth = 1;
1172 if (ctx->enc_pa_rep_permitted)
1173 code = request_enc_pa_rep(&ctx->request->padata);
1176 code = krb5int_fast_prep_req(context, ctx->fast_state,
1177 ctx->request, ctx->outer_request_body,
1179 &ctx->encoded_previous_request);
1183 code = krb5int_copy_data_contents(context,
1184 ctx->encoded_previous_request,
1190 krb5_free_pa_data(context, ctx->request->padata);
1191 ctx->request->padata = NULL;
1196 * The control flow is complicated. In order to switch from non-FAST mode to
1197 * FAST mode, we need to reset our pre-authentication state. FAST negotiation
1198 * attempts to make sure we rarely have to do this. When FAST negotiation is
1199 * working, we record whether FAST is available when we obtain an armor ticket;
1200 * if so, we start out with FAST enabled . There are two complicated
1203 * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
1204 * a KDC in a case where we were not expecting to use FAST, and we have an
1205 * armor ticket available, then we want to use FAST. That involves clearing
1206 * out the pre-auth state, reinitializing the plugins and trying again with an
1209 * Secondly, using the negotiation can cause problems with some older KDCs.
1210 * Negotiation involves including a special padata item. Some KDCs, including
1211 * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
1212 * pre-authentication is required and unknown padata are included in the
1213 * request. To make matters worse, these KDCs typically do not include a list
1214 * of padata in PREAUTH_FAILED errors. So, if we get PREAUTH_FAILED and we
1215 * generated no pre-authentication other than the negotiation then we want to
1216 * retry without negotiation. In this case it is probably also desirable to
1217 * retry with the preauth plugin state cleared.
1219 * In all these cases we should not start over more than once. Control flow is
1220 * managed by several variables.
1222 * sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
1223 * no restart on PREAUTH_FAILED
1225 * KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
1226 * armor; if not set, then we can't use FAST even if the KDC wants to.
1228 * have_restarted: true if we've already restarted
1231 negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
1232 krb5_pa_data **padata)
1234 if (ctx->have_restarted)
1236 if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
1237 TRACE_INIT_CREDS_RESTART_FAST(context);
1240 if (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
1241 !ctx->sent_nontrivial_preauth) {
1242 TRACE_INIT_CREDS_RESTART_PREAUTH_FAILED(context);
1248 /* Ensure that the reply enctype was among the requested enctypes. */
1249 static krb5_error_code
1250 check_reply_enctype(krb5_init_creds_context ctx)
1254 for (i = 0; i < ctx->request->nktypes; i++) {
1255 if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1258 return KRB5_CONFIG_ETYPE_NOSUPP;
1261 /* Note the difference between the KDC's time, as reported to us in a
1262 * preauth-required error, and the current time. */
1264 note_req_timestamp(krb5_context kcontext, krb5_clpreauth_rock rock,
1265 krb5_timestamp kdc_time, krb5_int32 kdc_usec)
1270 if (k5_time_with_offset(0, 0, &now, &usec) != 0)
1272 rock->pa_offset = kdc_time - now;
1273 rock->pa_offset_usec = kdc_usec - usec;
1274 rock->pa_offset_state = (rock->fast_state->armor_key != NULL) ?
1275 AUTH_OFFSET : UNAUTH_OFFSET;
1278 static krb5_error_code
1279 init_creds_step_reply(krb5_context context,
1280 krb5_init_creds_context ctx,
1283 krb5_error_code code;
1284 krb5_pa_data **kdc_padata = NULL;
1285 krb5_boolean retry = FALSE;
1287 krb5_keyblock *strengthen_key = NULL;
1288 krb5_keyblock encrypting_key;
1289 krb5_boolean fast_avail, got_real;
1291 encrypting_key.length = 0;
1292 encrypting_key.contents = NULL;
1294 /* process previous KDC response */
1295 code = init_creds_validate_reply(context, ctx, in);
1299 /* per referrals draft, enterprise principals imply canonicalization */
1300 canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1301 ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1303 if (ctx->err_reply != NULL) {
1304 code = krb5int_fast_process_error(context, ctx->fast_state,
1305 &ctx->err_reply, &ctx->err_padata,
1309 if (negotiation_requests_restart(context, ctx, ctx->err_padata)) {
1310 ctx->have_restarted = 1;
1311 krb5_preauth_request_context_fini(context);
1312 if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
1313 ctx->enc_pa_rep_permitted = 0;
1314 code = restart_init_creds_loop(context, ctx, ctx->err_padata);
1315 krb5_free_error(context, ctx->err_reply);
1316 ctx->err_reply = NULL;
1317 krb5_free_pa_data(context, ctx->err_padata);
1318 ctx->err_padata = NULL;
1319 } else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
1321 /* reset the list of preauth types to try */
1322 krb5_free_pa_data(context, ctx->preauth_to_use);
1323 ctx->preauth_to_use = ctx->err_padata;
1324 ctx->err_padata = NULL;
1325 note_req_timestamp(context, &ctx->preauth_rock,
1326 ctx->err_reply->stime, ctx->err_reply->susec);
1327 /* this will trigger a new call to krb5_do_preauth() */
1328 krb5_free_error(context, ctx->err_reply);
1329 ctx->err_reply = NULL;
1330 code = sort_krb5_padata_sequence(context,
1331 &ctx->request->client->realm,
1332 ctx->preauth_to_use);
1333 ctx->preauth_required = TRUE;
1335 } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) {
1336 if (ctx->err_reply->client == NULL ||
1337 !krb5_princ_realm(context, ctx->err_reply->client)->length) {
1338 code = KRB5KDC_ERR_WRONG_REALM;
1341 TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
1342 /* Rewrite request.client with realm from error reply */
1343 krb5_free_data_contents(context, &ctx->request->client->realm);
1344 code = krb5int_copy_data_contents(context,
1345 &ctx->err_reply->client->realm,
1346 &ctx->request->client->realm);
1347 /* this will trigger a new call to krb5_do_preauth() */
1348 krb5_free_error(context, ctx->err_reply);
1349 ctx->err_reply = NULL;
1350 krb5_preauth_request_context_fini(context);
1351 /* Permit another negotiation based restart. */
1352 ctx->have_restarted = 0;
1353 ctx->sent_nontrivial_preauth = 0;
1354 code = restart_init_creds_loop(context, ctx, NULL);
1361 /* error + no hints = give up */
1362 code = (krb5_error_code)ctx->err_reply->error +
1363 ERROR_TABLE_BASE_krb5;
1367 /* Return error code, or continue with next iteration */
1371 /* We have a response. Process it. */
1372 assert(ctx->reply != NULL);
1374 /* Check for replies (likely forged) with unasked-for enctypes. */
1375 code = check_reply_enctype(ctx);
1379 /* process any preauth data in the as_reply */
1380 krb5_clear_preauth_context_use_counts(context);
1381 code = krb5int_fast_process_response(context, ctx->fast_state,
1382 ctx->reply, &strengthen_key);
1386 code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1387 ctx->reply->padata);
1391 ctx->etype = ctx->reply->enc_part.enctype;
1393 code = krb5_do_preauth(context,
1395 ctx->inner_request_body,
1396 ctx->encoded_previous_request,
1408 * If we haven't gotten a salt from another source yet, set up one
1409 * corresponding to the client principal returned by the KDC. We
1410 * could get the same effect by passing local_as_reply->client to
1411 * gak_fct below, but that would put the canonicalized client name
1412 * in the prompt, which raises issues of needing to sanitize
1413 * unprintable characters. So for now we just let it affect the
1414 * salt. local_as_reply->client will be checked later on in
1417 if (ctx->default_salt) {
1418 code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1419 TRACE_INIT_CREDS_SALT_PRINC(context, &ctx->salt);
1424 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
1425 the AS_REP comes back encrypted in the user's longterm key
1426 instead of in the SAD. If there was a SAM preauth, there
1427 will be an as_key here which will be the SAD. If that fails,
1428 use the gak_fct to get the password, and try again. */
1430 /* XXX because etypes are handled poorly (particularly wrt SAM,
1431 where the etype is fixed by the kdc), we may want to try
1432 decrypt_as_reply twice. If there's an as_key available, try
1433 it. If decrypting the as_rep fails, or if there isn't an
1434 as_key at all yet, then use the gak_fct to get one, and try
1436 if (ctx->as_key.length) {
1437 TRACE_INIT_CREDS_AS_KEY_PREAUTH(context, &ctx->as_key);
1438 code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1442 code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1444 TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(context, code);
1449 /* if we haven't get gotten a key, get it now */
1450 TRACE_INIT_CREDS_GAK(context, &ctx->salt, &ctx->s2kparams);
1451 code = (*ctx->gak_fct)(context, ctx->request->client,
1452 ctx->reply->enc_part.enctype,
1453 ctx->prompter, ctx->prompter_data,
1454 &ctx->salt, &ctx->s2kparams,
1455 &ctx->as_key, ctx->gak_data);
1458 TRACE_INIT_CREDS_AS_KEY_GAK(context, &ctx->as_key);
1460 code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1465 code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1470 TRACE_INIT_CREDS_DECRYPTED_REPLY(context, ctx->reply->enc_part2->session);
1472 code = krb5int_fast_verify_nego(context, ctx->fast_state,
1473 ctx->reply, ctx->encoded_previous_request,
1474 &encrypting_key, &fast_avail);
1477 code = verify_as_reply(context, ctx->request_time,
1478 ctx->request, ctx->reply);
1481 code = verify_anonymous(context, ctx->request, ctx->reply,
1486 code = stash_as_reply(context, ctx->request_time, ctx->request,
1487 ctx->reply, &ctx->cred, NULL);
1490 if (ctx->opte && ctx->opte->opt_private->out_ccache) {
1491 krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
1492 krb5_data config_data;
1493 code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
1496 code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
1500 config_data.data = "yes";
1501 config_data.length = strlen(config_data.data);
1502 code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
1503 KRB5_CONF_FAST_AVAIL, &config_data);
1508 msg = krb5_get_error_message(context, code);
1509 krb5_set_error_message(context, code,
1510 _("%s while storing credentials"), msg);
1511 krb5_free_error_message(context, msg);
1515 krb5_preauth_request_context_fini(context);
1519 ctx->complete = TRUE;
1522 krb5_free_pa_data(context, kdc_padata);
1523 krb5_free_keyblock(context, strengthen_key);
1524 krb5_free_keyblock_contents(context, &encrypting_key);
1530 * Do next step of credentials acquisition.
1532 * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1533 * should be sent with TCP.
1535 krb5_error_code KRB5_CALLCONV
1536 krb5_init_creds_step(krb5_context context,
1537 krb5_init_creds_context ctx,
1541 unsigned int *flags)
1543 krb5_error_code code = 0, code2;
1556 if (in->length != 0) {
1557 code = init_creds_step_reply(context, ctx, in);
1558 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1559 code2 = krb5int_copy_data_contents(context,
1560 ctx->encoded_previous_request,
1568 if (code != 0 || ctx->complete)
1572 code = init_creds_step_request(context, ctx, out);
1576 /* Only a new request increments the loop count, not a TCP retry */
1580 assert(ctx->request->server != NULL);
1582 code2 = krb5int_copy_data_contents(context,
1583 &ctx->request->server->realm,
1591 if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
1594 /* See if we can produce a more detailed error message */
1595 code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
1597 krb5_set_error_message(context, code,
1598 _("Client '%s' not found in Kerberos "
1599 "database"), client_name);
1600 krb5_free_unparsed_name(context, client_name);
1604 *flags = ctx->complete ? 0 : KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1608 krb5_error_code KRB5_CALLCONV
1609 krb5int_get_init_creds(krb5_context context,
1611 krb5_principal client,
1612 krb5_prompter_fct prompter,
1613 void *prompter_data,
1614 krb5_deltat start_time,
1615 const char *in_tkt_service,
1616 krb5_get_init_creds_opt *options,
1617 krb5_gic_get_as_key_fct gak_fct,
1620 krb5_kdc_rep **as_reply)
1622 krb5_error_code code;
1623 krb5_init_creds_context ctx = NULL;
1625 code = krb5_init_creds_init(context,
1635 ctx->gak_fct = gak_fct;
1636 ctx->gak_data = gak_data;
1638 if (in_tkt_service) {
1639 code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1644 code = k5_init_creds_get(context, ctx, use_master);
1648 code = krb5_init_creds_get_creds(context, ctx, creds);
1652 if (as_reply != NULL) {
1653 *as_reply = ctx->reply;
1658 krb5_init_creds_free(context, ctx);
1664 krb5int_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
1665 krb5_flags options, krb5_address *const *addrs,
1666 krb5_enctype *ktypes,
1667 krb5_preauthtype *pre_auth_types, krb5_creds *creds)
1670 krb5_int32 starttime;
1671 krb5_get_init_creds_opt *opt;
1672 krb5_error_code retval;
1675 retval = krb5_get_init_creds_opt_alloc(context, &opt);
1680 krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
1682 i = krb5int_count_etypes(ktypes);
1684 krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
1686 if (pre_auth_types) {
1687 for (i=0; pre_auth_types[i]; i++);
1689 krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
1691 if (options&KDC_OPT_FORWARDABLE)
1692 krb5_get_init_creds_opt_set_forwardable(opt, 1);
1693 else krb5_get_init_creds_opt_set_forwardable(opt, 0);
1694 if (options&KDC_OPT_PROXIABLE)
1695 krb5_get_init_creds_opt_set_proxiable(opt, 1);
1696 else krb5_get_init_creds_opt_set_proxiable(opt, 0);
1697 if (creds && creds->times.endtime) {
1698 retval = krb5_timeofday(context, &starttime);
1701 if (creds->times.starttime) starttime = creds->times.starttime;
1702 krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
1708 krb5_get_init_creds_opt_free(context, opt);