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 char *in_tkt_service,
441 krb5_const_principal client,
442 krb5_principal *server)
448 if (in_tkt_service) {
449 /* this is ugly, because so are the data structures involved. I'm
450 in the library, so I'm going to manipulate the data structures
451 directly, otherwise, it will be worse. */
453 if ((ret = krb5_parse_name(context, in_tkt_service, server)))
456 /* stuff the client realm into the server principal.
457 realloc if necessary */
458 if ((*server)->realm.length < client->realm.length) {
459 char *p = realloc((*server)->realm.data,
460 client->realm.length);
462 krb5_free_principal(context, *server);
466 (*server)->realm.data = p;
469 (*server)->realm.length = client->realm.length;
470 memcpy((*server)->realm.data, client->realm.data, client->realm.length);
472 ret = krb5_build_principal_ext(context, server,
473 client->realm.length,
477 client->realm.length,
484 * Windows Server 2008 R2 RODC insists on TGS principal names having the
487 if (krb5_princ_size(context, *server) == 2 &&
488 data_eq_string(*krb5_princ_component(context, *server, 0),
490 krb5_princ_type(context, *server) = KRB5_NT_SRV_INST;
496 krb5_init_creds_free(krb5_context context,
497 krb5_init_creds_context ctx)
502 if (ctx->opte != NULL && krb5_gic_opt_is_shadowed(ctx->opte)) {
503 krb5_get_init_creds_opt_free(context,
504 (krb5_get_init_creds_opt *)ctx->opte);
506 free(ctx->in_tkt_service);
507 zap(ctx->password.data, ctx->password.length);
508 krb5_free_data_contents(context, &ctx->password);
509 krb5_free_error(context, ctx->err_reply);
510 krb5_free_pa_data(context, ctx->err_padata);
511 krb5_free_cred_contents(context, &ctx->cred);
512 krb5_free_kdc_req(context, ctx->request);
513 krb5_free_kdc_rep(context, ctx->reply);
514 krb5_free_data(context, ctx->outer_request_body);
515 krb5_free_data(context, ctx->inner_request_body);
516 krb5_free_data(context, ctx->encoded_previous_request);
517 krb5int_fast_free_state(context, ctx->fast_state);
518 krb5_free_pa_data(context, ctx->preauth_to_use);
519 krb5_free_data_contents(context, &ctx->salt);
520 krb5_free_data_contents(context, &ctx->s2kparams);
521 krb5_free_keyblock_contents(context, &ctx->as_key);
526 k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
529 krb5_error_code code;
533 unsigned int flags = 0;
544 code = krb5_init_creds_step(context,
550 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) {
551 TRACE_INIT_CREDS_RETRY_TCP(context);
553 } else if (code != 0 || !(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
556 krb5_free_data_contents(context, &reply);
558 code = krb5_sendto_kdc(context, &request, &realm,
559 &reply, use_master, tcp_only);
563 krb5_free_data_contents(context, &request);
564 krb5_free_data_contents(context, &realm);
567 krb5_free_data_contents(context, &request);
568 krb5_free_data_contents(context, &reply);
569 krb5_free_data_contents(context, &realm);
575 krb5_error_code KRB5_CALLCONV
576 krb5_init_creds_get(krb5_context context,
577 krb5_init_creds_context ctx)
581 return k5_init_creds_get(context, ctx, &use_master);
584 krb5_error_code KRB5_CALLCONV
585 krb5_init_creds_get_creds(krb5_context context,
586 krb5_init_creds_context ctx,
590 return KRB5_NO_TKT_SUPPLIED;
592 return krb5int_copy_creds_contents(context, &ctx->cred, creds);
595 krb5_error_code KRB5_CALLCONV
596 krb5_init_creds_get_times(krb5_context context,
597 krb5_init_creds_context ctx,
598 krb5_ticket_times *times)
601 return KRB5_NO_TKT_SUPPLIED;
603 *times = ctx->cred.times;
608 krb5_error_code KRB5_CALLCONV
609 krb5_init_creds_get_error(krb5_context context,
610 krb5_init_creds_context ctx,
613 krb5_error_code code;
614 krb5_error *ret = NULL;
618 if (ctx->err_reply == NULL)
621 ret = k5alloc(sizeof(*ret), &code);
625 ret->magic = KV5M_ERROR;
626 ret->ctime = ctx->err_reply->ctime;
627 ret->cusec = ctx->err_reply->cusec;
628 ret->susec = ctx->err_reply->susec;
629 ret->stime = ctx->err_reply->stime;
630 ret->error = ctx->err_reply->error;
632 if (ctx->err_reply->client != NULL) {
633 code = krb5_copy_principal(context, ctx->err_reply->client,
639 code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
643 code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
648 code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
657 krb5_free_error(context, ret);
663 * Throw away any state related to specific realm either at the beginning of a
664 * request, or when a realm changes, or when we start to use FAST after
665 * assuming we would not do so.
667 * @param padata padata from an error if an error from the realm we now expect
668 * to talk to caused the restart. Used to infer negotiation characteristics
669 * such as whether FAST is used.
671 static krb5_error_code
672 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
673 krb5_pa_data **padata)
675 krb5_error_code code = 0;
676 unsigned char random_buf[4];
677 krb5_data random_data;
678 if (ctx->preauth_to_use) {
679 krb5_free_pa_data(context, ctx->preauth_to_use);
680 ctx->preauth_to_use = NULL;
683 if (ctx->fast_state) {
684 krb5int_fast_free_state(context, ctx->fast_state);
685 ctx->fast_state = NULL;
687 code = krb5int_fast_make_state(context, &ctx->fast_state);
690 ctx->preauth_rock.fast_state = ctx->fast_state;
691 krb5_preauth_request_context_init(context);
692 if (ctx->outer_request_body) {
693 krb5_free_data(context, ctx->outer_request_body);
694 ctx->outer_request_body = NULL;
697 (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
698 if ((code = make_preauth_list(context, ctx->opte->preauth_list,
699 ctx->opte->preauth_list_length,
700 &ctx->preauth_to_use)))
704 /* Set the request nonce. */
705 random_data.length = 4;
706 random_data.data = (char *)random_buf;
707 code = krb5_c_random_make_octets(context, &random_data);
711 * See RT ticket 3196 at MIT. If we set the high bit, we may have
712 * compatibility problems with Heimdal, because we (incorrectly) encode
713 * this value as signed.
715 ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
716 krb5_free_principal(context, ctx->request->server);
717 ctx->request->server = NULL;
719 code = build_in_tkt_name(context, ctx->in_tkt_service,
720 ctx->request->client,
721 &ctx->request->server);
725 ctx->request_time = time(NULL);
727 code = krb5int_fast_as_armor(context, ctx->fast_state,
728 ctx->opte, ctx->request);
731 if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
732 code = krb5int_fast_as_armor(context, ctx->fast_state,
733 ctx->opte, ctx->request);
737 /* give the preauth plugins a chance to prep the request body */
738 krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
740 ctx->request->from = krb5int_addint32(ctx->request_time,
742 ctx->request->till = krb5int_addint32(ctx->request->from,
745 if (ctx->renew_life > 0) {
746 ctx->request->rtime =
747 krb5int_addint32(ctx->request->from, ctx->renew_life);
748 if (ctx->request->rtime < ctx->request->till) {
749 /* don't ask for a smaller renewable time than the lifetime */
750 ctx->request->rtime = ctx->request->till;
752 ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
754 ctx->request->rtime = 0;
755 code = krb5int_fast_prep_req_body(context, ctx->fast_state,
757 &ctx->outer_request_body);
764 krb5_error_code KRB5_CALLCONV
765 krb5_init_creds_init(krb5_context context,
766 krb5_principal client,
767 krb5_prompter_fct prompter,
769 krb5_deltat start_time,
770 krb5_get_init_creds_opt *options,
771 krb5_init_creds_context *pctx)
773 krb5_error_code code;
774 krb5_init_creds_context ctx;
777 krb5_gic_opt_ext *opte;
778 krb5_get_init_creds_opt local_opts;
780 TRACE_INIT_CREDS(context, client);
782 ctx = k5alloc(sizeof(*ctx), &code);
786 ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
789 ctx->enc_pa_rep_permitted = 1;
790 code = krb5_copy_principal(context, client, &ctx->request->client);
794 ctx->prompter = prompter;
795 ctx->prompter_data = data;
796 ctx->gak_fct = krb5_get_as_key_password;
797 ctx->gak_data = &ctx->password;
799 ctx->request_time = 0; /* filled in later */
800 ctx->start_time = start_time;
802 if (options == NULL) {
804 * We initialize a non-extended options because that way the shadowed
805 * flag will be sent and they will be freed when the init_creds context
806 * is freed. The options will be extended and copied off the stack into
807 * storage by opt_to_opte.
809 krb5_get_init_creds_opt_init(&local_opts);
810 options = &local_opts;
813 code = krb5int_gic_opt_to_opte(context, options,
814 &ctx->opte, 1, "krb5_init_creds_init");
820 ctx->preauth_rock.magic = CLIENT_ROCK_MAGIC;
821 ctx->preauth_rock.etype = &ctx->etype;
822 ctx->preauth_rock.as_key = &ctx->as_key;
823 ctx->preauth_rock.gak_fct = &ctx->gak_fct;
824 ctx->preauth_rock.gak_data = &ctx->gak_data;
825 ctx->preauth_rock.default_salt = &ctx->default_salt;
826 ctx->preauth_rock.salt = &ctx->salt;
827 ctx->preauth_rock.s2kparams = &ctx->s2kparams;
828 ctx->preauth_rock.client = client;
829 ctx->preauth_rock.prompter = prompter;
830 ctx->preauth_rock.prompter_data = data;
832 /* Initialise request parameters as per krb5_get_init_creds() */
833 ctx->request->kdc_options = context->kdc_default_options;
836 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
837 tmp = opte->forwardable;
838 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
839 KRB5_CONF_FORWARDABLE, &tmp) == 0)
844 ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
847 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
848 tmp = opte->proxiable;
849 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
850 KRB5_CONF_PROXIABLE, &tmp) == 0)
855 ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
858 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
860 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
861 KRB5_CONF_CANONICALIZE, &tmp) == 0)
866 ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
869 if (ctx->start_time > 0)
870 ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
872 /* ticket lifetime */
873 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
874 ctx->tkt_life = options->tkt_life;
875 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
876 KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
877 code = krb5_string_to_deltat(str, &ctx->tkt_life);
883 ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
885 /* renewable lifetime */
886 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
887 ctx->renew_life = options->renew_life;
888 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
889 KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
890 code = krb5_string_to_deltat(str, &ctx->renew_life);
898 if (ctx->renew_life > 0)
899 ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
902 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
903 ctx->request->ktype =
904 k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
908 ctx->request->nktypes = opte->etype_list_length;
909 memcpy(ctx->request->ktype, opte->etype_list,
910 ctx->request->nktypes * sizeof(krb5_enctype));
911 } else if (krb5_get_default_in_tkt_ktypes(context,
912 &ctx->request->ktype) == 0) {
913 ctx->request->nktypes = krb5int_count_etypes(ctx->request->ktype);
915 /* there isn't any useful default here. */
916 code = KRB5_CONFIG_ETYPE_NOSUPP;
921 * Set a default enctype for optimistic preauth. If we're not doing
922 * optimistic preauth, this should ordinarily get overwritten when we
923 * process the etype-info2 of the preauth-required error.
925 if (ctx->request->nktypes > 0)
926 ctx->etype = ctx->request->ktype[0];
929 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
930 code = krb5_copy_addresses(context, opte->address_list,
931 &ctx->request->addresses);
934 } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
935 KRB5_CONF_NOADDRESSES, &tmp) != 0
937 ctx->request->addresses = NULL;
939 code = krb5_os_localaddr(context, &ctx->request->addresses);
944 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
945 code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
948 ctx->default_salt = FALSE;
950 ctx->salt = empty_data();
951 ctx->default_salt = TRUE;
955 if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
956 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
957 /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
958 if (client->length == 1 && client->data[0].length ==0) {
959 krb5_principal new_client;
960 code = krb5_build_principal_ext(context, &new_client,
961 client->realm.length,
963 strlen(KRB5_WELLKNOWN_NAMESTR),
964 KRB5_WELLKNOWN_NAMESTR,
965 strlen(KRB5_ANONYMOUS_PRINCSTR),
966 KRB5_ANONYMOUS_PRINCSTR,
970 krb5_free_principal(context, ctx->request->client);
971 ctx->request->client = new_client;
972 krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
975 /* We will also handle anonymous if the input principal is the anonymous
977 if (krb5_principal_compare_any_realm(context, ctx->request->client,
978 krb5_anonymous_principal())) {
979 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
980 krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
982 code = restart_init_creds_loop(context, ctx, NULL);
990 krb5_init_creds_free(context, ctx);
996 krb5_error_code KRB5_CALLCONV
997 krb5_init_creds_set_service(krb5_context context,
998 krb5_init_creds_context ctx,
1003 TRACE_INIT_CREDS_SERVICE(context, service);
1005 s = strdup(service);
1009 free(ctx->in_tkt_service);
1010 ctx->in_tkt_service = s;
1012 krb5_preauth_request_context_fini(context);
1013 return restart_init_creds_loop(context, ctx, NULL);
1016 static krb5_error_code
1017 init_creds_validate_reply(krb5_context context,
1018 krb5_init_creds_context ctx,
1021 krb5_error_code code;
1022 krb5_error *error = NULL;
1023 krb5_kdc_rep *as_reply = NULL;
1025 krb5_free_error(context, ctx->err_reply);
1026 ctx->err_reply = NULL;
1028 krb5_free_kdc_rep(context, ctx->reply);
1031 if (krb5_is_krb_error(reply)) {
1032 code = decode_krb5_error(reply, &error);
1036 assert(error != NULL);
1038 TRACE_INIT_CREDS_ERROR_REPLY(context,
1039 error->error + ERROR_TABLE_BASE_krb5);
1040 if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1041 krb5_free_error(context, error);
1042 return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1044 ctx->err_reply = error;
1050 * Check to make sure it isn't a V4 reply.
1052 if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1053 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1054 #define V4_KRB_PROT_VERSION 4
1055 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
1056 /* check here for V4 reply */
1057 unsigned int t_switch;
1059 /* From v4 g_in_tkt.c: This used to be
1060 switch (pkt_msg_type(rpkt) & ~1) {
1061 but SCO 3.2v4 cc compiled that incorrectly. */
1062 t_switch = reply->data[1];
1065 if (t_switch == V4_AUTH_MSG_ERR_REPLY
1066 && reply->data[0] == V4_KRB_PROT_VERSION) {
1067 code = KRB5KRB_AP_ERR_V4_REPLY;
1069 code = KRB5KRB_AP_ERR_MSG_TYPE;
1074 /* It must be a KRB_AS_REP message, or an bad returned packet */
1075 code = decode_krb5_as_rep(reply, &as_reply);
1079 if (as_reply->msg_type != KRB5_AS_REP) {
1080 krb5_free_kdc_rep(context, as_reply);
1081 return KRB5KRB_AP_ERR_MSG_TYPE;
1084 ctx->reply = as_reply;
1089 static krb5_error_code
1090 init_creds_step_request(krb5_context context,
1091 krb5_init_creds_context ctx,
1094 krb5_error_code code;
1095 krb5_boolean got_real;
1097 krb5_data random_data;
1099 if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1100 code = KRB5_GET_IN_TKT_LOOP;
1104 * RFC 6113 requires a new nonce for the inner request on each try. It's
1105 * permitted to change the nonce even for non-FAST so we do here.
1107 random_data.length = 4;
1108 random_data.data = (char *)random_buf;
1109 code = krb5_c_random_make_octets(context, &random_data);
1113 * See RT ticket 3196 at MIT. If we set the high bit, we may have
1114 * compatibility problems with Heimdal, because we (incorrectly) encode
1115 * this value as signed.
1117 ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
1118 krb5_free_data(context, ctx->inner_request_body);
1119 ctx->inner_request_body = NULL;
1120 code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
1124 if (ctx->err_reply == NULL) {
1125 /* either our first attempt, or retrying after PREAUTH_NEEDED */
1126 code = krb5_do_preauth(context,
1128 ctx->inner_request_body,
1129 ctx->encoded_previous_request,
1130 ctx->preauth_to_use,
1131 &ctx->request->padata,
1137 if (code == 0 && !got_real && ctx->preauth_required)
1138 code = KRB5_PREAUTH_FAILED;
1142 if (ctx->preauth_to_use != NULL) {
1144 * Retry after an error other than PREAUTH_NEEDED,
1145 * using ctx->err_padata to figure out what to change.
1147 code = krb5_do_preauth_tryagain(context,
1149 ctx->inner_request_body,
1150 ctx->encoded_previous_request,
1151 ctx->preauth_to_use,
1152 &ctx->request->padata,
1160 /* No preauth supplied, so can't query the plugins. */
1161 code = KRB5KRB_ERR_GENERIC;
1164 /* couldn't come up with anything better */
1165 code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1170 if (ctx->encoded_previous_request != NULL) {
1171 krb5_free_data(context, ctx->encoded_previous_request);
1172 ctx->encoded_previous_request = NULL;
1174 if (ctx->request->padata)
1175 ctx->sent_nontrivial_preauth = 1;
1176 if (ctx->enc_pa_rep_permitted)
1177 code = request_enc_pa_rep(&ctx->request->padata);
1180 code = krb5int_fast_prep_req(context, ctx->fast_state,
1181 ctx->request, ctx->outer_request_body,
1183 &ctx->encoded_previous_request);
1187 code = krb5int_copy_data_contents(context,
1188 ctx->encoded_previous_request,
1194 krb5_free_pa_data(context, ctx->request->padata);
1195 ctx->request->padata = NULL;
1200 * The control flow is complicated. In order to switch from non-FAST mode to
1201 * FAST mode, we need to reset our pre-authentication state. FAST negotiation
1202 * attempts to make sure we rarely have to do this. When FAST negotiation is
1203 * working, we record whether FAST is available when we obtain an armor ticket;
1204 * if so, we start out with FAST enabled . There are two complicated
1207 * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
1208 * a KDC in a case where we were not expecting to use FAST, and we have an
1209 * armor ticket available, then we want to use FAST. That involves clearing
1210 * out the pre-auth state, reinitializing the plugins and trying again with an
1213 * Secondly, using the negotiation can cause problems with some older KDCs.
1214 * Negotiation involves including a special padata item. Some KDCs, including
1215 * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
1216 * pre-authentication is required and unknown padata are included in the
1217 * request. To make matters worse, these KDCs typically do not include a list
1218 * of padata in PREAUTH_FAILED errors. So, if we get PREAUTH_FAILED and we
1219 * generated no pre-authentication other than the negotiation then we want to
1220 * retry without negotiation. In this case it is probably also desirable to
1221 * retry with the preauth plugin state cleared.
1223 * In all these cases we should not start over more than once. Control flow is
1224 * managed by several variables.
1226 * sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
1227 * no restart on PREAUTH_FAILED
1229 * KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
1230 * armor; if not set, then we can't use FAST even if the KDC wants to.
1232 * have_restarted: true if we've already restarted
1235 negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
1236 krb5_pa_data **padata)
1238 if (ctx->have_restarted)
1240 if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
1241 TRACE_INIT_CREDS_RESTART_FAST(context);
1244 if (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
1245 !ctx->sent_nontrivial_preauth) {
1246 TRACE_INIT_CREDS_RESTART_PREAUTH_FAILED(context);
1252 /* Ensure that the reply enctype was among the requested enctypes. */
1253 static krb5_error_code
1254 check_reply_enctype(krb5_init_creds_context ctx)
1258 for (i = 0; i < ctx->request->nktypes; i++) {
1259 if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1262 return KRB5_CONFIG_ETYPE_NOSUPP;
1265 /* Note the difference between the KDC's time, as reported to us in a
1266 * preauth-required error, and the current time. */
1268 note_req_timestamp(krb5_context kcontext, krb5_clpreauth_rock rock,
1269 krb5_timestamp kdc_time, krb5_int32 kdc_usec)
1274 if (k5_time_with_offset(0, 0, &now, &usec) != 0)
1276 rock->pa_offset = kdc_time - now;
1277 rock->pa_offset_usec = kdc_usec - usec;
1278 rock->pa_offset_state = (rock->fast_state->armor_key != NULL) ?
1279 AUTH_OFFSET : UNAUTH_OFFSET;
1282 static krb5_error_code
1283 init_creds_step_reply(krb5_context context,
1284 krb5_init_creds_context ctx,
1287 krb5_error_code code;
1288 krb5_pa_data **kdc_padata = NULL;
1289 krb5_boolean retry = FALSE;
1291 krb5_keyblock *strengthen_key = NULL;
1292 krb5_keyblock encrypting_key;
1293 krb5_boolean fast_avail, got_real;
1295 encrypting_key.length = 0;
1296 encrypting_key.contents = NULL;
1298 /* process previous KDC response */
1299 code = init_creds_validate_reply(context, ctx, in);
1303 /* per referrals draft, enterprise principals imply canonicalization */
1304 canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1305 ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1307 if (ctx->err_reply != NULL) {
1308 code = krb5int_fast_process_error(context, ctx->fast_state,
1309 &ctx->err_reply, &ctx->err_padata,
1313 if (negotiation_requests_restart(context, ctx, ctx->err_padata)) {
1314 ctx->have_restarted = 1;
1315 krb5_preauth_request_context_fini(context);
1316 if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
1317 ctx->enc_pa_rep_permitted = 0;
1318 code = restart_init_creds_loop(context, ctx, ctx->err_padata);
1319 krb5_free_error(context, ctx->err_reply);
1320 ctx->err_reply = NULL;
1321 krb5_free_pa_data(context, ctx->err_padata);
1322 ctx->err_padata = NULL;
1323 } else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
1325 /* reset the list of preauth types to try */
1326 krb5_free_pa_data(context, ctx->preauth_to_use);
1327 ctx->preauth_to_use = ctx->err_padata;
1328 ctx->err_padata = NULL;
1329 note_req_timestamp(context, &ctx->preauth_rock,
1330 ctx->err_reply->stime, ctx->err_reply->susec);
1331 /* this will trigger a new call to krb5_do_preauth() */
1332 krb5_free_error(context, ctx->err_reply);
1333 ctx->err_reply = NULL;
1334 code = sort_krb5_padata_sequence(context,
1335 &ctx->request->client->realm,
1336 ctx->preauth_to_use);
1337 ctx->preauth_required = TRUE;
1339 } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) {
1340 if (ctx->err_reply->client == NULL ||
1341 !krb5_princ_realm(context, ctx->err_reply->client)->length) {
1342 code = KRB5KDC_ERR_WRONG_REALM;
1345 TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
1346 /* Rewrite request.client with realm from error reply */
1347 krb5_free_data_contents(context, &ctx->request->client->realm);
1348 code = krb5int_copy_data_contents(context,
1349 &ctx->err_reply->client->realm,
1350 &ctx->request->client->realm);
1351 /* this will trigger a new call to krb5_do_preauth() */
1352 krb5_free_error(context, ctx->err_reply);
1353 ctx->err_reply = NULL;
1354 krb5_preauth_request_context_fini(context);
1355 /* Permit another negotiation based restart. */
1356 ctx->have_restarted = 0;
1357 ctx->sent_nontrivial_preauth = 0;
1358 code = restart_init_creds_loop(context, ctx, NULL);
1365 /* error + no hints = give up */
1366 code = (krb5_error_code)ctx->err_reply->error +
1367 ERROR_TABLE_BASE_krb5;
1371 /* Return error code, or continue with next iteration */
1375 /* We have a response. Process it. */
1376 assert(ctx->reply != NULL);
1378 /* Check for replies (likely forged) with unasked-for enctypes. */
1379 code = check_reply_enctype(ctx);
1383 /* process any preauth data in the as_reply */
1384 krb5_clear_preauth_context_use_counts(context);
1385 code = krb5int_fast_process_response(context, ctx->fast_state,
1386 ctx->reply, &strengthen_key);
1390 code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1391 ctx->reply->padata);
1395 ctx->etype = ctx->reply->enc_part.enctype;
1397 code = krb5_do_preauth(context,
1399 ctx->inner_request_body,
1400 ctx->encoded_previous_request,
1412 * If we haven't gotten a salt from another source yet, set up one
1413 * corresponding to the client principal returned by the KDC. We
1414 * could get the same effect by passing local_as_reply->client to
1415 * gak_fct below, but that would put the canonicalized client name
1416 * in the prompt, which raises issues of needing to sanitize
1417 * unprintable characters. So for now we just let it affect the
1418 * salt. local_as_reply->client will be checked later on in
1421 if (ctx->default_salt) {
1422 code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1423 TRACE_INIT_CREDS_SALT_PRINC(context, &ctx->salt);
1428 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
1429 the AS_REP comes back encrypted in the user's longterm key
1430 instead of in the SAD. If there was a SAM preauth, there
1431 will be an as_key here which will be the SAD. If that fails,
1432 use the gak_fct to get the password, and try again. */
1434 /* XXX because etypes are handled poorly (particularly wrt SAM,
1435 where the etype is fixed by the kdc), we may want to try
1436 decrypt_as_reply twice. If there's an as_key available, try
1437 it. If decrypting the as_rep fails, or if there isn't an
1438 as_key at all yet, then use the gak_fct to get one, and try
1440 if (ctx->as_key.length) {
1441 TRACE_INIT_CREDS_AS_KEY_PREAUTH(context, &ctx->as_key);
1442 code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1446 code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1448 TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(context, code);
1453 /* if we haven't get gotten a key, get it now */
1454 TRACE_INIT_CREDS_GAK(context, &ctx->salt, &ctx->s2kparams);
1455 code = (*ctx->gak_fct)(context, ctx->request->client,
1456 ctx->reply->enc_part.enctype,
1457 ctx->prompter, ctx->prompter_data,
1458 &ctx->salt, &ctx->s2kparams,
1459 &ctx->as_key, ctx->gak_data);
1462 TRACE_INIT_CREDS_AS_KEY_GAK(context, &ctx->as_key);
1464 code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1469 code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1474 TRACE_INIT_CREDS_DECRYPTED_REPLY(context, ctx->reply->enc_part2->session);
1476 code = krb5int_fast_verify_nego(context, ctx->fast_state,
1477 ctx->reply, ctx->encoded_previous_request,
1478 &encrypting_key, &fast_avail);
1481 code = verify_as_reply(context, ctx->request_time,
1482 ctx->request, ctx->reply);
1485 code = verify_anonymous(context, ctx->request, ctx->reply,
1490 code = stash_as_reply(context, ctx->request_time, ctx->request,
1491 ctx->reply, &ctx->cred, NULL);
1494 if (ctx->opte && ctx->opte->opt_private->out_ccache) {
1495 krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
1496 krb5_data config_data;
1497 code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
1500 code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
1504 config_data.data = "yes";
1505 config_data.length = strlen(config_data.data);
1506 code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
1507 KRB5_CONF_FAST_AVAIL, &config_data);
1512 msg = krb5_get_error_message(context, code);
1513 krb5_set_error_message(context, code,
1514 _("%s while storing credentials"), msg);
1515 krb5_free_error_message(context, msg);
1519 krb5_preauth_request_context_fini(context);
1523 ctx->complete = TRUE;
1526 krb5_free_pa_data(context, kdc_padata);
1527 krb5_free_keyblock(context, strengthen_key);
1528 krb5_free_keyblock_contents(context, &encrypting_key);
1534 * Do next step of credentials acquisition.
1536 * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1537 * should be sent with TCP.
1539 krb5_error_code KRB5_CALLCONV
1540 krb5_init_creds_step(krb5_context context,
1541 krb5_init_creds_context ctx,
1545 unsigned int *flags)
1547 krb5_error_code code = 0, code2;
1560 if (in->length != 0) {
1561 code = init_creds_step_reply(context, ctx, in);
1562 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1563 code2 = krb5int_copy_data_contents(context,
1564 ctx->encoded_previous_request,
1572 if (code != 0 || ctx->complete)
1576 code = init_creds_step_request(context, ctx, out);
1580 /* Only a new request increments the loop count, not a TCP retry */
1584 assert(ctx->request->server != NULL);
1586 code2 = krb5int_copy_data_contents(context,
1587 &ctx->request->server->realm,
1595 if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
1598 /* See if we can produce a more detailed error message */
1599 code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
1601 krb5_set_error_message(context, code,
1602 _("Client '%s' not found in Kerberos "
1603 "database"), client_name);
1604 krb5_free_unparsed_name(context, client_name);
1608 *flags = ctx->complete ? 0 : KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1612 krb5_error_code KRB5_CALLCONV
1613 krb5int_get_init_creds(krb5_context context,
1615 krb5_principal client,
1616 krb5_prompter_fct prompter,
1617 void *prompter_data,
1618 krb5_deltat start_time,
1619 char *in_tkt_service,
1620 krb5_get_init_creds_opt *options,
1621 krb5_gic_get_as_key_fct gak_fct,
1624 krb5_kdc_rep **as_reply)
1626 krb5_error_code code;
1627 krb5_init_creds_context ctx = NULL;
1629 code = krb5_init_creds_init(context,
1639 ctx->gak_fct = gak_fct;
1640 ctx->gak_data = gak_data;
1642 if (in_tkt_service) {
1643 code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1648 code = k5_init_creds_get(context, ctx, use_master);
1652 code = krb5_init_creds_get_creds(context, ctx, creds);
1656 if (as_reply != NULL) {
1657 *as_reply = ctx->reply;
1662 krb5_init_creds_free(context, ctx);
1668 krb5int_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
1669 krb5_flags options, krb5_address *const *addrs,
1670 krb5_enctype *ktypes,
1671 krb5_preauthtype *pre_auth_types, krb5_creds *creds)
1674 krb5_int32 starttime;
1675 krb5_get_init_creds_opt *opt;
1676 krb5_error_code retval;
1679 retval = krb5_get_init_creds_opt_alloc(context, &opt);
1684 krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
1686 i = krb5int_count_etypes(ktypes);
1688 krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
1690 if (pre_auth_types) {
1691 for (i=0; pre_auth_types[i]; i++);
1693 krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
1695 if (options&KDC_OPT_FORWARDABLE)
1696 krb5_get_init_creds_opt_set_forwardable(opt, 1);
1697 else krb5_get_init_creds_opt_set_forwardable(opt, 0);
1698 if (options&KDC_OPT_PROXIABLE)
1699 krb5_get_init_creds_opt_set_proxiable(opt, 1);
1700 else krb5_get_init_creds_opt_set_proxiable(opt, 0);
1701 if (creds && creds->times.endtime) {
1702 retval = krb5_timeofday(context, &starttime);
1705 if (creds->times.starttime) starttime = creds->times.starttime;
1706 krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
1712 krb5_get_init_creds_opt_free(context, opt);