1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * lib/krb5/krb/get_in_tkt.c
5 * Copyright 1990,1991, 2003, 2008 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
34 #include "int-proto.h"
37 #include "init_creds_ctx.h"
40 #define IN_TKT_DEBUG 0
42 #define inTktDebug(args...) printf(args)
44 #define inTktDebug(args...)
46 #endif /* APPLE_PKINIT */
49 All-purpose initial ticket routine, usually called via
50 krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
52 Attempts to get an initial ticket for creds->client to use server
53 creds->server, (realm is taken from creds->client), with options
54 options, and using creds->times.starttime, creds->times.endtime,
55 creds->times.renew_till as from, till, and rtime.
56 creds->times.renew_till is ignored unless the RENEWABLE option is requested.
58 key_proc is called to fill in the key to be used for decryption.
59 keyseed is passed on to key_proc.
61 decrypt_proc is called to perform the decryption of the response (the
62 encrypted part is in dec_rep->enc_part; the decrypted part should be
63 allocated and filled into dec_rep->enc_part2
64 arg is passed on to decrypt_proc.
66 If addrs is non-NULL, it is used for the addresses requested. If it is
67 null, the system standard addresses are used.
69 A succesful call will place the ticket in the credentials cache ccache
70 and fill in creds with the ticket information used/returned..
72 returns system errors, encryption errors
77 /* some typedef's for the function args to make things look a bit cleaner */
79 typedef krb5_error_code (*git_key_proc) (krb5_context,
85 typedef krb5_error_code (*git_decrypt_proc) (krb5_context,
86 const krb5_keyblock *,
90 static krb5_error_code make_preauth_list (krb5_context,
92 int, krb5_pa_data ***);
93 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
95 krb5_pa_data **padata);
98 * This function performs 32 bit bounded addition so we can generate
99 * lifetimes without overflowing krb5_int32
102 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
104 if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
105 /* sum will be be greater than KRB5_INT32_MAX */
106 return KRB5_INT32_MAX;
107 } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
108 /* sum will be less than KRB5_INT32_MIN */
109 return KRB5_INT32_MIN;
117 * Common code to generate krb5_kdc_req.nonce. Like the original MIT code this
118 * just uses krb5_timeofday(); it should use a PRNG. Even more unfortunately this
119 * value is used interchangeably with an explicit now_time throughout this module...
121 static krb5_error_code
122 gen_nonce(krb5_context context,
126 krb5_error_code retval = krb5_timeofday(context, &time_now);
133 #endif /* APPLE_PKINIT */
136 * This function sends a request to the KDC, and gets back a response;
137 * the response is parsed into ret_err_reply or ret_as_reply if the
138 * reponse is a KRB_ERROR or a KRB_AS_REP packet. If it is some other
139 * unexpected response, an error is returned.
141 static krb5_error_code
142 send_as_request(krb5_context context,
143 krb5_data *packet, const krb5_data *realm,
144 krb5_error ** ret_err_reply,
145 krb5_kdc_rep ** ret_as_reply,
148 krb5_kdc_rep *as_reply = 0;
149 krb5_error_code retval;
151 char k4_version; /* same type as *(krb5_data::data) */
156 /* set the nonce if the caller expects us to do it */
158 k4_version = packet->data[0];
160 retval = krb5_sendto_kdc(context, packet,
162 &reply, use_master, tcp_only);
164 inTktDebug("krb5_sendto_kdc returned %d\n", (int)retval);
165 #endif /* APPLE_PKINIT */
170 /* now decode the reply...could be error or as_rep */
171 if (krb5_is_krb_error(&reply)) {
172 krb5_error *err_reply;
174 if ((retval = decode_krb5_error(&reply, &err_reply)))
175 /* some other error code--??? */
179 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG
182 krb5_free_error(context, err_reply);
187 *ret_err_reply = err_reply;
189 krb5_free_error(context, err_reply);
194 * Check to make sure it isn't a V4 reply.
196 if (!krb5_is_as_rep(&reply)) {
197 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
198 #define V4_KRB_PROT_VERSION 4
199 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
200 /* check here for V4 reply */
201 unsigned int t_switch;
203 /* From v4 g_in_tkt.c: This used to be
204 switch (pkt_msg_type(rpkt) & ~1) {
205 but SCO 3.2v4 cc compiled that incorrectly. */
206 t_switch = reply.data[1];
209 if (t_switch == V4_AUTH_MSG_ERR_REPLY
210 && (reply.data[0] == V4_KRB_PROT_VERSION
211 || reply.data[0] == k4_version)) {
212 retval = KRB5KRB_AP_ERR_V4_REPLY;
214 retval = KRB5KRB_AP_ERR_MSG_TYPE;
219 /* It must be a KRB_AS_REP message, or an bad returned packet */
220 if ((retval = decode_krb5_as_rep(&reply, &as_reply)))
221 /* some other error code ??? */
224 if (as_reply->msg_type != KRB5_AS_REP) {
225 retval = KRB5KRB_AP_ERR_MSG_TYPE;
226 krb5_free_kdc_rep(context, as_reply);
231 *ret_as_reply = as_reply;
233 krb5_free_kdc_rep(context, as_reply);
241 static krb5_error_code
242 decrypt_as_reply(krb5_context context,
243 krb5_kdc_req *request,
244 krb5_kdc_rep *as_reply,
245 git_key_proc key_proc,
246 krb5_const_pointer keyseed,
248 git_decrypt_proc decrypt_proc,
249 krb5_const_pointer decryptarg)
251 krb5_error_code retval;
252 krb5_keyblock * decrypt_key = 0;
255 if (as_reply->enc_part2)
262 * Use salt corresponding to the client principal supplied by
263 * the KDC, which may differ from the requested principal if
264 * canonicalization is in effect. We will check
265 * as_reply->client later in verify_as_reply.
267 if ((retval = krb5_principal2salt(context, as_reply->client, &salt)))
270 retval = (*key_proc)(context, as_reply->enc_part.enctype,
271 &salt, keyseed, &decrypt_key);
277 if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply)))
281 if (!key && decrypt_key)
282 krb5_free_keyblock(context, decrypt_key);
287 * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC
288 * contribution key. This routine confirms that the session key is of the
289 * right form for fully anonymous requests. It is here rather than in the
290 * preauth code because the session key cannot be verified until the AS reply
291 * is decrypted and the preauth code all runs before the AS reply is decrypted.
293 static krb5_error_code
294 verify_anonymous( krb5_context context, krb5_kdc_req *request,
295 krb5_kdc_rep *reply, krb5_keyblock *as_key)
297 krb5_error_code ret = 0;
300 krb5_keyblock *kdc_key = NULL, *expected = NULL;
301 krb5_enc_data *enc = NULL;
302 krb5_keyblock *session = reply->enc_part2->session;
304 if (!krb5_principal_compare_any_realm(context, request->client,
305 krb5_anonymous_principal()))
306 return 0; /* Only applies to fully anonymous */
307 pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX);
309 goto verification_error;
310 scratch.length = pa->length;
311 scratch.data = (char *) pa->contents;
312 ret = decode_krb5_enc_data( &scratch, &enc);
315 scratch.data = k5alloc(enc->ciphertext.length, &ret);
318 scratch.length = enc->ciphertext.length;
319 ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX,
320 NULL /*cipherstate*/, enc, &scratch);
325 ret = decode_krb5_encryption_key( &scratch, &kdc_key);
326 zap(scratch.data, scratch.length);
330 ret = krb5_c_fx_cf2_simple(context, kdc_key, "PKINIT",
331 as_key, "KEYEXCHANGE", &expected);
334 if ((expected->enctype != session->enctype) ||
335 (expected->length != session->length) ||
336 (memcmp(expected->contents, session->contents, expected->length) != 0))
337 goto verification_error;
340 krb5_free_keyblock(context, kdc_key);
342 krb5_free_keyblock(context, expected);
344 krb5_free_enc_data(context, enc);
347 ret = KRB5_KDCREP_MODIFIED;
348 krb5_set_error_message(context, ret, "Reply has wrong form of session key "
349 "for anonymous request");
353 static krb5_error_code
354 verify_as_reply(krb5_context context,
355 krb5_timestamp time_now,
356 krb5_kdc_req *request,
357 krb5_kdc_rep *as_reply)
359 krb5_error_code retval;
363 /* check the contents for sanity: */
364 if (!as_reply->enc_part2->times.starttime)
365 as_reply->enc_part2->times.starttime =
366 as_reply->enc_part2->times.authtime;
369 * We only allow the AS-REP server name to be changed if the
370 * caller set the canonicalize flag (or requested an enterprise
371 * principal) and we requested (and received) a TGT.
373 canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
374 (krb5_princ_type(context, request->client) ==
375 KRB5_NT_ENTERPRISE_PRINCIPAL) ||
376 (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS);
378 canon_ok = IS_TGS_PRINC(context, request->server) &&
379 IS_TGS_PRINC(context, as_reply->enc_part2->server);
380 if (!canon_ok && (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS)) {
381 canon_ok = krb5_principal_compare_any_realm(context,
383 krb5_anonymous_principal());
389 (!krb5_principal_compare(context, as_reply->client, request->client) ||
390 !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)))
391 || !krb5_principal_compare(context, as_reply->enc_part2->server, as_reply->ticket->server)
392 || (request->nonce != as_reply->enc_part2->nonce)
393 /* XXX check for extraneous flags */
394 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
395 || ((request->kdc_options & KDC_OPT_POSTDATED) &&
396 (request->from != 0) &&
397 (request->from != as_reply->enc_part2->times.starttime))
398 || ((request->till != 0) &&
399 (as_reply->enc_part2->times.endtime > request->till))
400 || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
401 (request->rtime != 0) &&
402 (as_reply->enc_part2->times.renew_till > request->rtime))
403 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
404 !(request->kdc_options & KDC_OPT_RENEWABLE) &&
405 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
406 (request->till != 0) &&
407 (as_reply->enc_part2->times.renew_till > request->till))
410 inTktDebug("verify_as_reply: KDCREP_MODIFIED\n");
412 if(request->client->realm.length && request->client->data->length)
413 inTktDebug("request: name %s realm %s\n",
414 request->client->realm.data, request->client->data->data);
415 if(as_reply->client->realm.length && as_reply->client->data->length)
416 inTktDebug("reply : name %s realm %s\n",
417 as_reply->client->realm.data, as_reply->client->data->data);
419 #endif /* APPLE_PKINIT */
420 return KRB5_KDCREP_MODIFIED;
423 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
424 retval = krb5_set_real_time(context,
425 as_reply->enc_part2->times.authtime, -1);
429 if ((request->from == 0) &&
430 (labs(as_reply->enc_part2->times.starttime - time_now)
431 > context->clockskew))
432 return (KRB5_KDCREP_SKEW);
437 static krb5_error_code
438 stash_as_reply(krb5_context context,
439 krb5_timestamp time_now,
440 krb5_kdc_req *request,
441 krb5_kdc_rep *as_reply,
445 krb5_error_code retval;
447 krb5_principal client;
448 krb5_principal server;
454 if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
458 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
462 /* fill in the credentials */
463 if ((retval = krb5_copy_keyblock_contents(context,
464 as_reply->enc_part2->session,
468 creds->times = as_reply->enc_part2->times;
469 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot
470 be encrypted in skey */
471 creds->ticket_flags = as_reply->enc_part2->flags;
472 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
476 creds->second_ticket.length = 0;
477 creds->second_ticket.data = 0;
479 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
482 creds->ticket = *packet;
485 /* store it in the ccache! */
487 if ((retval = krb5_cc_store_cred(context, ccache, creds)))
491 creds->client = client;
493 creds->server = server;
498 krb5_free_principal(context, client);
500 krb5_free_principal(context, server);
501 if (creds->keyblock.contents) {
502 memset(creds->keyblock.contents, 0,
503 creds->keyblock.length);
504 free(creds->keyblock.contents);
505 creds->keyblock.contents = 0;
506 creds->keyblock.length = 0;
508 if (creds->ticket.data) {
509 free(creds->ticket.data);
510 creds->ticket.data = 0;
512 if (creds->addresses) {
513 krb5_free_addresses(context, creds->addresses);
514 creds->addresses = 0;
520 static krb5_error_code
521 make_preauth_list(krb5_context context,
522 krb5_preauthtype * ptypes,
524 krb5_pa_data *** ret_list)
526 krb5_preauthtype * ptypep;
527 krb5_pa_data ** preauthp;
531 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
535 /* allocate space for a NULL to terminate the list */
538 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
541 for (i=0; i<nptypes; i++) {
543 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
549 preauthp[i]->magic = KV5M_PA_DATA;
550 preauthp[i]->pa_type = ptypes[i];
551 preauthp[i]->length = 0;
552 preauthp[i]->contents = 0;
555 /* fill in the terminating NULL */
557 preauthp[nptypes] = NULL;
559 *ret_list = preauthp;
563 #define MAX_IN_TKT_LOOPS 16
564 static const krb5_enctype get_in_tkt_enctypes[] = {
565 ENCTYPE_DES3_CBC_SHA1,
566 ENCTYPE_ARCFOUR_HMAC,
573 static krb5_error_code
574 rewrite_server_realm(krb5_context context,
575 krb5_const_principal old_server,
576 const krb5_data *realm,
578 krb5_principal *server)
580 krb5_error_code retval;
582 assert(*server == NULL);
584 retval = krb5_copy_principal(context, old_server, server);
588 krb5_free_data_contents(context, &(*server)->realm);
589 (*server)->realm.data = NULL;
591 retval = krb5int_copy_data_contents(context, realm, &(*server)->realm);
596 krb5_free_data_contents(context, &(*server)->data[1]);
597 (*server)->data[1].data = NULL;
599 retval = krb5int_copy_data_contents(context, realm, &(*server)->data[1]);
606 krb5_free_principal(context, *server);
614 tgt_is_local_realm(krb5_creds *tgt)
616 return (tgt->server->length == 2
617 && data_eq_string(tgt->server->data[0], KRB5_TGS_NAME)
618 && data_eq(tgt->server->data[1], tgt->client->realm)
619 && data_eq(tgt->server->realm, tgt->client->realm));
622 static krb5_error_code
623 request_enc_pa_rep(krb5_pa_data ***padptr)
626 krb5_pa_data **pad = *padptr;
627 krb5_pa_data *pa= NULL;
629 for (size=0; pad[size]; size++);
630 pad = realloc(pad, sizeof(*pad)*(size+2));
635 pa = malloc(sizeof(krb5_pa_data));
640 pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
646 krb5_error_code KRB5_CALLCONV
647 krb5_get_in_tkt(krb5_context context,
649 krb5_address * const * addrs,
650 krb5_enctype * ktypes,
651 krb5_preauthtype * ptypes,
652 git_key_proc key_proc,
653 krb5_const_pointer keyseed,
654 git_decrypt_proc decrypt_proc,
655 krb5_const_pointer decryptarg,
658 krb5_kdc_rep ** ret_as_reply)
660 krb5_error_code retval;
661 krb5_timestamp time_now;
662 krb5_keyblock * decrypt_key = 0;
663 krb5_kdc_req request;
664 krb5_data *encoded_request;
665 krb5_error * err_reply;
666 krb5_kdc_rep * as_reply = 0;
667 krb5_pa_data ** preauth_to_use = 0;
669 krb5_int32 do_more = 0;
672 int referral_count = 0;
673 krb5_principal_data referred_client;
674 krb5_principal referred_server = NULL;
675 krb5_boolean is_tgt_req;
678 inTktDebug("krb5_get_in_tkt top\n");
679 #endif /* APPLE_PKINIT */
681 if (! krb5_realm_compare(context, creds->client, creds->server))
682 return KRB5_IN_TKT_REALM_MISMATCH;
687 referred_client = *(creds->client);
688 referred_client.realm.data = NULL;
689 referred_client.realm.length = 0;
691 /* per referrals draft, enterprise principals imply canonicalization */
692 canon_flag = ((options & KDC_OPT_CANONICALIZE) != 0) ||
693 creds->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
696 * Set up the basic request structure
698 request.magic = KV5M_KDC_REQ;
699 request.msg_type = KRB5_AS_REQ;
700 request.addresses = 0;
704 request.addresses = (krb5_address **) addrs;
706 if ((retval = krb5_os_localaddr(context, &request.addresses)))
708 request.kdc_options = options;
709 request.client = creds->client;
710 request.server = creds->server;
712 request.from = creds->times.starttime;
713 request.till = creds->times.endtime;
714 request.rtime = creds->times.renew_till;
716 retval = gen_nonce(context, (krb5_int32 *)&time_now);
720 request.nonce = time_now;
721 #endif /* APPLE_PKINIT */
723 retval = krb5int_copy_etypes(get_in_tkt_enctypes, &request.ktype);
724 request.nktypes = krb5int_count_etypes(request.ktype);
726 int i, req, next = 0;
727 for (req = 0; ktypes[req]; req++) {
728 if (ktypes[req] == request.ktype[next]) {
732 for (i = next + 1; i < request.nktypes; i++)
733 if (ktypes[req] == request.ktype[i]) {
734 /* Found the enctype we want, but not in the
735 position we want. Move it, but keep the old
736 one from the desired slot around in case it's
737 later in our requested-ktypes list. */
739 t = request.ktype[next];
740 request.ktype[next] = request.ktype[i];
741 request.ktype[i] = t;
745 /* If we didn't find it, don't do anything special, just
748 request.ktype[next] = 0;
749 request.nktypes = next;
751 request.authorization_data.ciphertext.length = 0;
752 request.authorization_data.ciphertext.data = 0;
753 request.unenc_authdata = 0;
754 request.second_ticket = 0;
757 * If a list of preauth types are passed in, convert it to a
758 * preauth_to_use list.
761 retval = make_preauth_list(context, ptypes, -1, &preauth_to_use);
766 is_tgt_req = tgt_is_local_realm(creds);
769 if (loopcount++ > MAX_IN_TKT_LOOPS) {
770 retval = KRB5_GET_IN_TKT_LOOP;
775 inTktDebug("krb5_get_in_tkt calling krb5_obtain_padata\n");
776 #endif /* APPLE_PKINIT */
777 if ((retval = krb5_obtain_padata(context, preauth_to_use, key_proc,
778 keyseed, creds, &request)) != 0)
781 krb5_free_pa_data(context, preauth_to_use);
787 if ((retval = krb5_timeofday(context, &time_now)))
791 * XXX we know they are the same size... and we should do
792 * something better than just the current time
794 request.nonce = (krb5_int32) time_now;
796 if ((retval = encode_krb5_as_req(&request, &encoded_request)) != 0)
798 retval = send_as_request(context, encoded_request,
799 krb5_princ_realm(context, request.client), &err_reply,
800 &as_reply, &use_master);
801 krb5_free_data(context, encoded_request);
806 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
807 err_reply->e_data.length > 0) {
808 retval = decode_krb5_padata_sequence(&err_reply->e_data,
810 krb5_free_error(context, err_reply);
813 retval = sort_krb5_padata_sequence(context,
814 &request.server->realm,
819 } else if (canon_flag && err_reply->error == KDC_ERR_WRONG_REALM) {
820 if (++referral_count > KRB5_REFERRAL_MAXHOPS ||
821 err_reply->client == NULL ||
822 err_reply->client->realm.length == 0) {
823 retval = KRB5KDC_ERR_WRONG_REALM;
824 krb5_free_error(context, err_reply);
827 /* Rewrite request.client with realm from error reply */
828 if (referred_client.realm.data) {
829 krb5_free_data_contents(context, &referred_client.realm);
830 referred_client.realm.data = NULL;
832 retval = krb5int_copy_data_contents(context,
833 &err_reply->client->realm,
834 &referred_client.realm);
835 krb5_free_error(context, err_reply);
838 request.client = &referred_client;
840 if (referred_server != NULL) {
841 krb5_free_principal(context, referred_server);
842 referred_server = NULL;
845 retval = rewrite_server_realm(context,
847 &referred_client.realm,
852 request.server = referred_server;
856 retval = (krb5_error_code) err_reply->error
857 + ERROR_TABLE_BASE_krb5;
858 krb5_free_error(context, err_reply);
861 } else if (!as_reply) {
862 retval = KRB5KRB_AP_ERR_MSG_TYPE;
865 if ((retval = krb5_process_padata(context, &request, as_reply,
866 key_proc, keyseed, decrypt_proc,
875 if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc,
876 keyseed, decrypt_key, decrypt_proc,
880 if ((retval = verify_as_reply(context, time_now, &request, as_reply)))
883 if ((retval = stash_as_reply(context, time_now, &request, as_reply,
890 if (!addrs && request.addresses)
891 krb5_free_addresses(context, request.addresses);
893 krb5_free_pa_data(context, request.padata);
895 krb5_free_pa_data(context, preauth_to_use);
897 krb5_free_keyblock(context, decrypt_key);
900 *ret_as_reply = as_reply;
902 krb5_free_kdc_rep(context, as_reply);
904 if (referred_client.realm.data)
905 krb5_free_data_contents(context, &referred_client.realm);
907 krb5_free_principal(context, referred_server);
911 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
912 * libdefaults entry are listed before any others. */
913 static krb5_error_code
914 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
915 krb5_pa_data **padata)
921 char *q, *preauth_types = NULL;
923 int need_free_string = 1;
925 if ((padata == NULL) || (padata[0] == NULL)) {
929 ret = krb5int_libdefault_string(context, realm, KRB5_CONF_PREFERRED_PREAUTH_TYPES,
931 if ((ret != 0) || (preauth_types == NULL)) {
932 /* Try to use PKINIT first. */
933 preauth_types = "17, 16, 15, 14";
934 need_free_string = 0;
938 fprintf (stderr, "preauth data types before sorting:");
939 for (i = 0; padata[i]; i++) {
940 fprintf (stderr, " %d", padata[i]->pa_type);
942 fprintf (stderr, "\n");
946 for (p = preauth_types; *p != '\0';) {
947 /* skip whitespace to find an entry */
948 p += strspn(p, ", ");
950 /* see if we can extract a number */
951 l = strtol(p, &q, 10);
952 if ((q != NULL) && (q > p)) {
953 /* got a valid number; search for a matchin entry */
954 for (i = base; padata[i] != NULL; i++) {
955 /* bubble the matching entry to the front of the list */
956 if (padata[i]->pa_type == l) {
958 for (j = i; j > base; j--)
959 padata[j] = padata[j - 1];
971 if (need_free_string)
975 fprintf (stderr, "preauth data types after sorting:");
976 for (i = 0; padata[i]; i++)
977 fprintf (stderr, " %d", padata[i]->pa_type);
978 fprintf (stderr, "\n");
984 static krb5_error_code
985 build_in_tkt_name(krb5_context context,
986 char *in_tkt_service,
987 krb5_const_principal client,
988 krb5_principal *server)
994 if (in_tkt_service) {
995 /* this is ugly, because so are the data structures involved. I'm
996 in the library, so I'm going to manipulate the data structures
997 directly, otherwise, it will be worse. */
999 if ((ret = krb5_parse_name(context, in_tkt_service, server)))
1002 /* stuff the client realm into the server principal.
1003 realloc if necessary */
1004 if ((*server)->realm.length < client->realm.length) {
1005 char *p = realloc((*server)->realm.data,
1006 client->realm.length);
1008 krb5_free_principal(context, *server);
1012 (*server)->realm.data = p;
1015 (*server)->realm.length = client->realm.length;
1016 memcpy((*server)->realm.data, client->realm.data, client->realm.length);
1018 ret = krb5_build_principal_ext(context, server,
1019 client->realm.length,
1023 client->realm.length,
1031 krb5_init_creds_free(krb5_context context,
1032 krb5_init_creds_context ctx)
1037 if (ctx->opte != NULL && krb5_gic_opt_is_shadowed(ctx->opte)) {
1038 krb5_get_init_creds_opt_free(context,
1039 (krb5_get_init_creds_opt *)ctx->opte);
1041 free(ctx->in_tkt_service);
1042 zap(ctx->password.data, ctx->password.length);
1043 krb5_free_data_contents(context, &ctx->password);
1044 krb5_free_error(context, ctx->err_reply);
1045 krb5_free_cred_contents(context, &ctx->cred);
1046 krb5_free_kdc_req(context, ctx->request);
1047 krb5_free_kdc_rep(context, ctx->reply);
1048 krb5_free_data(context, ctx->encoded_request_body);
1049 krb5_free_data(context, ctx->encoded_previous_request);
1050 krb5int_fast_free_state(context, ctx->fast_state);
1051 krb5_free_pa_data(context, ctx->preauth_to_use);
1052 krb5_free_data_contents(context, &ctx->salt);
1053 krb5_free_data_contents(context, &ctx->s2kparams);
1054 krb5_free_keyblock_contents(context, &ctx->as_key);
1058 static krb5_error_code
1059 init_creds_get(krb5_context context,
1060 krb5_init_creds_context ctx,
1063 krb5_error_code code;
1067 unsigned int flags = 0;
1071 request.data = NULL;
1078 code = krb5_init_creds_step(context,
1084 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only)
1086 else if (code != 0 || (flags & 1) == 0)
1089 krb5_free_data_contents(context, &reply);
1091 code = krb5_sendto_kdc(context, &request, &realm,
1092 &reply, use_master, tcp_only);
1096 krb5_free_data_contents(context, &request);
1097 krb5_free_data_contents(context, &realm);
1100 krb5_free_data_contents(context, &request);
1101 krb5_free_data_contents(context, &reply);
1102 krb5_free_data_contents(context, &realm);
1108 krb5_error_code KRB5_CALLCONV
1109 krb5_init_creds_get(krb5_context context,
1110 krb5_init_creds_context ctx)
1114 return init_creds_get(context, ctx, &use_master);
1117 krb5_error_code KRB5_CALLCONV
1118 krb5_init_creds_get_creds(krb5_context context,
1119 krb5_init_creds_context ctx,
1122 if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
1123 return KRB5_NO_TKT_SUPPLIED;
1125 return krb5int_copy_creds_contents(context, &ctx->cred, creds);
1128 krb5_error_code KRB5_CALLCONV
1129 krb5_init_creds_get_times(krb5_context context,
1130 krb5_init_creds_context ctx,
1131 krb5_ticket_times *times)
1133 if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
1134 return KRB5_NO_TKT_SUPPLIED;
1136 *times = ctx->cred.times;
1141 krb5_error_code KRB5_CALLCONV
1142 krb5_init_creds_get_error(krb5_context context,
1143 krb5_init_creds_context ctx,
1146 krb5_error_code code;
1147 krb5_error *ret = NULL;
1151 if (ctx->err_reply == NULL)
1154 ret = k5alloc(sizeof(*ret), &code);
1158 ret->magic = KV5M_ERROR;
1159 ret->ctime = ctx->err_reply->ctime;
1160 ret->cusec = ctx->err_reply->cusec;
1161 ret->susec = ctx->err_reply->susec;
1162 ret->stime = ctx->err_reply->stime;
1163 ret->error = ctx->err_reply->error;
1165 if (ctx->err_reply->client != NULL) {
1166 code = krb5_copy_principal(context, ctx->err_reply->client,
1172 code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
1176 code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
1181 code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
1190 krb5_free_error(context, ret);
1196 * Throw away any state related to specific realm either at the beginning of a
1197 * request, or when a realm changes, or when we start to use FAST after
1198 * assuming we would not do so.
1200 * @param padata padata from an error if an error from the realm we now expect
1201 * to talk to caused the restart. Used to infer negotiation characteristics
1202 * such as whether FAST is used.
1204 static krb5_error_code
1205 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
1206 krb5_pa_data **padata)
1208 krb5_error_code code = 0;
1209 unsigned char random_buf[4];
1210 krb5_data random_data;
1211 if (ctx->preauth_to_use) {
1212 krb5_free_pa_data(context, ctx->preauth_to_use);
1213 ctx->preauth_to_use = NULL;
1216 if (ctx->fast_state) {
1217 krb5int_fast_free_state(context, ctx->fast_state);
1218 ctx->fast_state = NULL;
1220 code = krb5int_fast_make_state(context, &ctx->fast_state);
1223 ctx->get_data_rock.fast_state = ctx->fast_state;
1224 krb5_preauth_request_context_init(context);
1225 if (ctx->encoded_request_body) {
1226 krb5_free_data(context, ctx->encoded_request_body);
1227 ctx->encoded_request_body = NULL;
1230 (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
1231 if ((code = make_preauth_list(context, ctx->opte->preauth_list,
1232 ctx->opte->preauth_list_length,
1233 &ctx->preauth_to_use)))
1237 /* Set the request nonce. */
1238 random_data.length = 4;
1239 random_data.data = (char *)random_buf;
1240 code = krb5_c_random_make_octets(context, &random_data);
1244 * See RT ticket 3196 at MIT. If we set the high bit, we may have
1245 * compatibility problems with Heimdal, because we (incorrectly) encode
1246 * this value as signed.
1248 ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
1249 krb5_free_principal(context, ctx->request->server);
1250 ctx->request->server = NULL;
1252 code = build_in_tkt_name(context, ctx->in_tkt_service,
1253 ctx->request->client,
1254 &ctx->request->server);
1258 code = krb5_timeofday(context, &ctx->request_time);
1262 code = krb5int_fast_as_armor(context, ctx->fast_state,
1263 ctx->opte, ctx->request);
1266 if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
1267 code = krb5int_fast_as_armor(context, ctx->fast_state,
1268 ctx->opte, ctx->request);
1272 /* give the preauth plugins a chance to prep the request body */
1273 krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
1275 ctx->request->from = krb5int_addint32(ctx->request_time,
1277 ctx->request->till = krb5int_addint32(ctx->request->from,
1280 if (ctx->renew_life > 0) {
1281 ctx->request->rtime =
1282 krb5int_addint32(ctx->request->from, ctx->renew_life);
1283 if (ctx->request->rtime < ctx->request->till) {
1284 /* don't ask for a smaller renewable time than the lifetime */
1285 ctx->request->rtime = ctx->request->till;
1287 ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
1289 ctx->request->rtime = 0;
1290 code = krb5int_fast_prep_req_body(context, ctx->fast_state,
1292 &ctx->encoded_request_body);
1299 krb5_error_code KRB5_CALLCONV
1300 krb5_init_creds_init(krb5_context context,
1301 krb5_principal client,
1302 krb5_prompter_fct prompter,
1304 krb5_deltat start_time,
1305 krb5_get_init_creds_opt *options,
1306 krb5_init_creds_context *pctx)
1308 krb5_error_code code;
1309 krb5_init_creds_context ctx;
1312 krb5_gic_opt_ext *opte;
1314 ctx = k5alloc(sizeof(*ctx), &code);
1318 ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
1321 ctx->enc_pa_rep_permitted = 1;
1322 code = krb5_copy_principal(context, client, &ctx->request->client);
1326 ctx->prompter = prompter;
1327 ctx->prompter_data = data;
1328 ctx->gak_fct = krb5_get_as_key_password;
1329 ctx->gak_data = &ctx->password;
1331 ctx->request_time = 0; /* filled in later */
1332 ctx->start_time = start_time;
1334 if (options == NULL) {
1335 code = krb5_get_init_creds_opt_alloc(context, &options);
1340 code = krb5int_gic_opt_to_opte(context, options,
1341 &ctx->opte, 1, "krb5_init_creds_init");
1347 ctx->get_data_rock.magic = CLIENT_ROCK_MAGIC;
1348 ctx->get_data_rock.etype = &ctx->etype;
1350 /* Initialise request parameters as per krb5_get_init_creds() */
1351 ctx->request->kdc_options = context->kdc_default_options;
1354 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
1355 tmp = opte->forwardable;
1356 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
1357 KRB5_CONF_FORWARDABLE, &tmp) == 0)
1362 ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
1365 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
1366 tmp = opte->proxiable;
1367 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
1368 KRB5_CONF_PROXIABLE, &tmp) == 0)
1373 ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
1376 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
1378 else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
1379 KRB5_CONF_CANONICALIZE, &tmp) == 0)
1384 ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
1386 /* allow_postdate */
1387 if (ctx->start_time > 0)
1388 ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
1390 /* ticket lifetime */
1391 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
1392 ctx->tkt_life = options->tkt_life;
1393 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
1394 KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
1395 code = krb5_string_to_deltat(str, &ctx->tkt_life);
1401 ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
1403 /* renewable lifetime */
1404 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
1405 ctx->renew_life = options->renew_life;
1406 else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
1407 KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
1408 code = krb5_string_to_deltat(str, &ctx->renew_life);
1414 ctx->renew_life = 0;
1416 if (ctx->renew_life > 0)
1417 ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
1420 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
1421 ctx->request->ktype =
1422 k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
1426 ctx->request->nktypes = opte->etype_list_length;
1427 memcpy(ctx->request->ktype, opte->etype_list,
1428 ctx->request->nktypes * sizeof(krb5_enctype));
1429 } else if (krb5_get_default_in_tkt_ktypes(context,
1430 &ctx->request->ktype) == 0) {
1431 ctx->request->nktypes = krb5int_count_etypes(ctx->request->ktype);
1433 /* there isn't any useful default here. */
1434 code = KRB5_CONFIG_ETYPE_NOSUPP;
1439 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
1440 code = krb5_copy_addresses(context, opte->address_list,
1441 &ctx->request->addresses);
1444 } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
1445 KRB5_CONF_NOADDRESSES, &tmp) != 0
1447 ctx->request->addresses = NULL;
1449 code = krb5_os_localaddr(context, &ctx->request->addresses);
1454 if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
1455 code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
1459 ctx->salt.length = SALT_TYPE_AFS_LENGTH;
1460 ctx->salt.data = NULL;
1464 if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
1465 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
1466 /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
1467 if (client->length == 1 && client->data[0].length ==0) {
1468 krb5_principal new_client;
1469 code = krb5_build_principal_ext(context, &new_client,
1470 client->realm.length,
1472 strlen(KRB5_WELLKNOWN_NAMESTR),
1473 KRB5_WELLKNOWN_NAMESTR,
1474 strlen(KRB5_ANONYMOUS_PRINCSTR),
1475 KRB5_ANONYMOUS_PRINCSTR,
1479 krb5_free_principal(context, ctx->request->client);
1480 ctx->request->client = new_client;
1481 krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
1484 /* We will also handle anonymous if the input principal is the anonymous
1486 if (krb5_principal_compare_any_realm(context, ctx->request->client,
1487 krb5_anonymous_principal())) {
1488 ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
1489 krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
1491 code = restart_init_creds_loop(context, ctx, NULL);
1499 krb5_init_creds_free(context, ctx);
1505 krb5_error_code KRB5_CALLCONV
1506 krb5_init_creds_set_service(krb5_context context,
1507 krb5_init_creds_context ctx,
1508 const char *service)
1512 s = strdup(service);
1516 free(ctx->in_tkt_service);
1517 ctx->in_tkt_service = s;
1519 krb5_preauth_request_context_fini(context);
1520 return restart_init_creds_loop(context, ctx, NULL);
1523 static krb5_error_code
1524 init_creds_validate_reply(krb5_context context,
1525 krb5_init_creds_context ctx,
1528 krb5_error_code code;
1529 krb5_error *error = NULL;
1530 krb5_kdc_rep *as_reply = NULL;
1532 krb5_free_error(context, ctx->err_reply);
1533 ctx->err_reply = NULL;
1535 krb5_free_kdc_rep(context, ctx->reply);
1538 if (krb5_is_krb_error(reply)) {
1539 code = decode_krb5_error(reply, &error);
1543 assert(error != NULL);
1545 if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1546 krb5_free_error(context, error);
1547 return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1549 ctx->err_reply = error;
1555 * Check to make sure it isn't a V4 reply.
1557 if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1558 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1559 #define V4_KRB_PROT_VERSION 4
1560 #define V4_AUTH_MSG_ERR_REPLY (5<<1)
1561 /* check here for V4 reply */
1562 unsigned int t_switch;
1564 /* From v4 g_in_tkt.c: This used to be
1565 switch (pkt_msg_type(rpkt) & ~1) {
1566 but SCO 3.2v4 cc compiled that incorrectly. */
1567 t_switch = reply->data[1];
1570 if (t_switch == V4_AUTH_MSG_ERR_REPLY
1571 && reply->data[0] == V4_KRB_PROT_VERSION) {
1572 code = KRB5KRB_AP_ERR_V4_REPLY;
1574 code = KRB5KRB_AP_ERR_MSG_TYPE;
1579 /* It must be a KRB_AS_REP message, or an bad returned packet */
1580 code = decode_krb5_as_rep(reply, &as_reply);
1584 if (as_reply->msg_type != KRB5_AS_REP) {
1585 krb5_free_kdc_rep(context, as_reply);
1586 return KRB5KRB_AP_ERR_MSG_TYPE;
1589 ctx->reply = as_reply;
1594 static krb5_error_code
1595 init_creds_step_request(krb5_context context,
1596 krb5_init_creds_context ctx,
1599 krb5_error_code code;
1601 if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1602 code = KRB5_GET_IN_TKT_LOOP;
1606 if (ctx->err_reply == NULL) {
1607 /* either our first attempt, or retrying after PREAUTH_NEEDED */
1608 code = krb5_do_preauth(context,
1610 ctx->encoded_request_body,
1611 ctx->encoded_previous_request,
1612 ctx->preauth_to_use,
1613 &ctx->request->padata,
1622 &ctx->get_data_rock,
1627 if (ctx->preauth_to_use != NULL) {
1629 * Retry after an error other than PREAUTH_NEEDED,
1630 * using e-data to figure out what to change.
1632 code = krb5_do_preauth_tryagain(context,
1634 ctx->encoded_request_body,
1635 ctx->encoded_previous_request,
1636 ctx->preauth_to_use,
1637 &ctx->request->padata,
1647 &ctx->get_data_rock,
1650 /* No preauth supplied, so can't query the plugins. */
1651 code = KRB5KRB_ERR_GENERIC;
1654 /* couldn't come up with anything better */
1655 code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1660 if (ctx->encoded_previous_request != NULL) {
1661 krb5_free_data(context, ctx->encoded_previous_request);
1662 ctx->encoded_previous_request = NULL;
1664 if (ctx->request->padata)
1665 ctx->sent_nontrivial_preauth = 1;
1666 if (ctx->enc_pa_rep_permitted)
1667 code = request_enc_pa_rep(&ctx->request->padata);
1670 code = krb5int_fast_prep_req(context, ctx->fast_state,
1671 ctx->request, ctx->encoded_request_body,
1673 &ctx->encoded_previous_request);
1677 code = krb5int_copy_data_contents(context,
1678 ctx->encoded_previous_request,
1688 * The control flow is complicated. In order to switch from non-FAST mode to
1689 * FAST mode, we need to reset our pre-authentication state. FAST negotiation
1690 * attempts to make sure we rarely have to do this. When FAST negotiation is
1691 * working, we record whether FAST is available when we obtain an armor ticket;
1692 * if so, we start out with FAST enabled . There are two complicated
1695 * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
1696 * a KDC in a case where we were not expecting to use FAST, and we have an
1697 * armor ticket available, then we want to use FAST. That involves clearing
1698 * out the pre-auth state, reinitializing the plugins and trying again with an
1701 * Secondly, using the negotiation can cause problems with some older KDCs.
1702 * Negotiation involves including a special padata item. Some KDCs, including
1703 * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
1704 * pre-authentication is required and unknown padata are included in the
1705 * request. To make matters worse, these KDCs typically do not include a list
1706 * of padata in PREAUTH_FAILED errors. So, if we get PREAUTH_FAILED and we
1707 * generated no pre-authentication other than the negotiation then we want to
1708 * retry without negotiation. In this case it is probably also desirable to
1709 * retry with the preauth plugin state cleared.
1711 * In all these cases we should not start over more than once. Control flow is
1712 * managed by several variables.
1714 * sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
1715 * no restart on PREAUTH_FAILED
1717 * KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
1718 * armor; if not set, then we can't use FAST even if the KDC wants to.
1720 * have_restarted: true if we've already restarted
1723 negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
1724 krb5_pa_data **padata)
1726 if (!ctx->have_restarted &&
1727 (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata) ||
1728 (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
1729 !ctx->sent_nontrivial_preauth)))
1734 /* Ensure that the reply enctype was among the requested enctypes. */
1735 static krb5_error_code
1736 check_reply_enctype(krb5_init_creds_context ctx)
1740 for (i = 0; i < ctx->request->nktypes; i++) {
1741 if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1744 return KRB5_CONFIG_ETYPE_NOSUPP;
1747 static krb5_error_code
1748 init_creds_step_reply(krb5_context context,
1749 krb5_init_creds_context ctx,
1752 krb5_error_code code;
1753 krb5_pa_data **padata = NULL;
1754 krb5_pa_data **kdc_padata = NULL;
1755 krb5_boolean retry = FALSE;
1757 krb5_keyblock *strengthen_key = NULL;
1758 krb5_keyblock encrypting_key;
1759 krb5_boolean fast_avail;
1761 encrypting_key.length = 0;
1762 encrypting_key.contents = NULL;
1764 /* process previous KDC response */
1765 code = init_creds_validate_reply(context, ctx, in);
1769 /* per referrals draft, enterprise principals imply canonicalization */
1770 canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1771 ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1773 if (ctx->err_reply != NULL) {
1774 code = krb5int_fast_process_error(context, ctx->fast_state,
1775 &ctx->err_reply, &padata, &retry);
1778 if (negotiation_requests_restart(context, ctx, padata)) {
1779 ctx->have_restarted = 1;
1780 krb5_preauth_request_context_fini(context);
1781 if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
1782 ctx->enc_pa_rep_permitted = 0;
1783 code = restart_init_creds_loop(context, ctx, padata);
1784 krb5_free_error(context, ctx->err_reply);
1785 ctx->err_reply = NULL;
1786 } else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
1788 /* reset the list of preauth types to try */
1789 krb5_free_pa_data(context, ctx->preauth_to_use);
1790 ctx->preauth_to_use = padata;
1792 /* this will trigger a new call to krb5_do_preauth() */
1793 krb5_free_error(context, ctx->err_reply);
1794 ctx->err_reply = NULL;
1795 code = sort_krb5_padata_sequence(context,
1796 &ctx->request->client->realm,
1797 ctx->preauth_to_use);
1799 } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) {
1800 if (ctx->err_reply->client == NULL ||
1801 !krb5_princ_realm(context, ctx->err_reply->client)->length) {
1802 code = KRB5KDC_ERR_WRONG_REALM;
1805 /* Rewrite request.client with realm from error reply */
1806 krb5_free_data_contents(context, &ctx->request->client->realm);
1807 code = krb5int_copy_data_contents(context,
1808 &ctx->err_reply->client->realm,
1809 &ctx->request->client->realm);
1810 /* this will trigger a new call to krb5_do_preauth() */
1811 krb5_free_error(context, ctx->err_reply);
1812 ctx->err_reply = NULL;
1813 krb5_preauth_request_context_fini(context);
1814 /* Permit another negotiation based restart. */
1815 ctx->have_restarted = 0;
1816 ctx->sent_nontrivial_preauth = 0;
1817 code = restart_init_creds_loop(context, ctx, NULL);
1824 /* error + no hints = give up */
1825 code = (krb5_error_code)ctx->err_reply->error +
1826 ERROR_TABLE_BASE_krb5;
1830 /* Return error code, or continue with next iteration */
1834 /* We have a response. Process it. */
1835 assert(ctx->reply != NULL);
1837 /* Check for replies (likely forged) with unasked-for enctypes. */
1838 code = check_reply_enctype(ctx);
1842 /* process any preauth data in the as_reply */
1843 krb5_clear_preauth_context_use_counts(context);
1844 code = krb5int_fast_process_response(context, ctx->fast_state,
1845 ctx->reply, &strengthen_key);
1849 code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1850 ctx->reply->padata);
1854 ctx->etype = ctx->reply->enc_part.enctype;
1856 code = krb5_do_preauth(context,
1858 ctx->encoded_request_body,
1859 ctx->encoded_previous_request,
1870 &ctx->get_data_rock,
1876 * If we haven't gotten a salt from another source yet, set up one
1877 * corresponding to the client principal returned by the KDC. We
1878 * could get the same effect by passing local_as_reply->client to
1879 * gak_fct below, but that would put the canonicalized client name
1880 * in the prompt, which raises issues of needing to sanitize
1881 * unprintable characters. So for now we just let it affect the
1882 * salt. local_as_reply->client will be checked later on in
1885 if (ctx->salt.length == SALT_TYPE_AFS_LENGTH && ctx->salt.data == NULL) {
1886 code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1891 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
1892 the AS_REP comes back encrypted in the user's longterm key
1893 instead of in the SAD. If there was a SAM preauth, there
1894 will be an as_key here which will be the SAD. If that fails,
1895 use the gak_fct to get the password, and try again. */
1897 /* XXX because etypes are handled poorly (particularly wrt SAM,
1898 where the etype is fixed by the kdc), we may want to try
1899 decrypt_as_reply twice. If there's an as_key available, try
1900 it. If decrypting the as_rep fails, or if there isn't an
1901 as_key at all yet, then use the gak_fct to get one, and try
1903 if (ctx->as_key.length) {
1904 code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1908 code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
1909 &encrypting_key, krb5_kdc_rep_decrypt_proc,
1915 /* if we haven't get gotten a key, get it now */
1916 code = (*ctx->gak_fct)(context, ctx->request->client,
1917 ctx->reply->enc_part.enctype,
1918 ctx->prompter, ctx->prompter_data,
1919 &ctx->salt, &ctx->s2kparams,
1920 &ctx->as_key, ctx->gak_data);
1924 code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1929 code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
1930 &encrypting_key, krb5_kdc_rep_decrypt_proc,
1936 code = krb5int_fast_verify_nego(context, ctx->fast_state,
1937 ctx->reply, ctx->encoded_previous_request,
1938 &encrypting_key, &fast_avail);
1941 code = verify_as_reply(context, ctx->request_time,
1942 ctx->request, ctx->reply);
1945 code = verify_anonymous(context, ctx->request, ctx->reply,
1950 code = stash_as_reply(context, ctx->request_time, ctx->request,
1951 ctx->reply, &ctx->cred, NULL);
1954 if (ctx->opte && ctx->opte->opt_private->out_ccache) {
1955 krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
1956 krb5_data config_data;
1957 code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
1960 code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
1964 config_data.data = "yes";
1965 config_data.length = strlen(config_data.data);
1966 code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
1967 KRB5_CONF_FAST_AVAIL, &config_data);
1972 msg = krb5_get_error_message(context, code);
1973 krb5_set_error_message(context, code,
1974 "%s while storing credentials", msg);
1975 krb5_free_error_message(context, msg);
1979 krb5_preauth_request_context_fini(context);
1983 ctx->flags |= KRB5_INIT_CREDS_STEP_FLAG_COMPLETE;
1986 krb5_free_pa_data(context, padata);
1987 krb5_free_pa_data(context, kdc_padata);
1988 krb5_free_keyblock(context, strengthen_key);
1989 krb5_free_keyblock_contents(context, &encrypting_key);
1995 * Do next step of credentials acquisition.
1997 * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1998 * should be sent with TCP.
2000 krb5_error_code KRB5_CALLCONV
2001 krb5_init_creds_step(krb5_context context,
2002 krb5_init_creds_context ctx,
2006 unsigned int *flags)
2008 krb5_error_code code = 0, code2;
2018 if (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE)
2021 if (in->length != 0) {
2022 code = init_creds_step_reply(context, ctx, in);
2023 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
2024 code2 = krb5int_copy_data_contents(context,
2025 ctx->encoded_previous_request,
2033 if (code != 0 || (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE))
2037 code = init_creds_step_request(context, ctx, out);
2041 /* Only a new request increments the loop count, not a TCP retry */
2045 assert(ctx->request->server != NULL);
2047 code2 = krb5int_copy_data_contents(context,
2048 &ctx->request->server->realm,
2056 if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
2059 /* See if we can produce a more detailed error message */
2060 code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
2062 krb5_set_error_message(context, code,
2063 "Client '%s' not found in Kerberos database",
2065 krb5_free_unparsed_name(context, client_name);
2069 *flags = (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) ? 0 : 1;
2074 krb5_error_code KRB5_CALLCONV
2075 krb5int_get_init_creds(krb5_context context,
2077 krb5_principal client,
2078 krb5_prompter_fct prompter,
2079 void *prompter_data,
2080 krb5_deltat start_time,
2081 char *in_tkt_service,
2082 krb5_get_init_creds_opt *options,
2083 krb5_gic_get_as_key_fct gak_fct,
2086 krb5_kdc_rep **as_reply)
2088 krb5_error_code code;
2089 krb5_init_creds_context ctx = NULL;
2091 code = krb5_init_creds_init(context,
2101 ctx->gak_fct = gak_fct;
2102 ctx->gak_data = gak_data;
2104 if (in_tkt_service) {
2105 code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2110 code = init_creds_get(context, ctx, use_master);
2114 code = krb5_init_creds_get_creds(context, ctx, creds);
2118 if (as_reply != NULL) {
2119 *as_reply = ctx->reply;
2124 krb5_init_creds_free(context, ctx);
2130 krb5int_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
2131 krb5_flags options, krb5_address *const *addrs,
2132 krb5_enctype *ktypes,
2133 krb5_preauthtype *pre_auth_types, krb5_creds *creds)
2136 krb5_int32 starttime;
2137 krb5_get_init_creds_opt *opt;
2138 krb5_error_code retval;
2141 retval = krb5_get_init_creds_opt_alloc(context, &opt);
2146 krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
2148 i = krb5int_count_etypes(ktypes);
2150 krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
2152 if (pre_auth_types) {
2153 for (i=0; pre_auth_types[i]; i++);
2155 krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
2157 if (options&KDC_OPT_FORWARDABLE)
2158 krb5_get_init_creds_opt_set_forwardable(opt, 1);
2159 else krb5_get_init_creds_opt_set_forwardable(opt, 0);
2160 if (options&KDC_OPT_PROXIABLE)
2161 krb5_get_init_creds_opt_set_proxiable(opt, 1);
2162 else krb5_get_init_creds_opt_set_proxiable(opt, 0);
2163 if (creds && creds->times.endtime) {
2164 retval = krb5_timeofday(context, &starttime);
2167 if (creds->times.starttime) starttime = creds->times.starttime;
2168 krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
2174 krb5_get_init_creds_opt_free(context, opt);