pull up r24020 from trunk
[krb5.git] / src / lib / krb5 / krb / get_in_tkt.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * lib/krb5/krb/get_in_tkt.c
4  *
5  * Copyright 1990,1991, 2003, 2008 by the Massachusetts Institute of Technology.
6  * All Rights Reserved.
7  *
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.
12  *
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.
26  *
27  *
28  * krb5_get_in_tkt()
29  */
30
31 #include <string.h>
32
33 #include "k5-int.h"
34 #include "int-proto.h"
35 #include "os-proto.h"
36 #include "fast.h"
37 #include "init_creds_ctx.h"
38
39 #if APPLE_PKINIT
40 #define     IN_TKT_DEBUG    0
41 #if         IN_TKT_DEBUG
42 #define     inTktDebug(args...)       printf(args)
43 #else
44 #define     inTktDebug(args...)
45 #endif
46 #endif /* APPLE_PKINIT */
47
48 /*
49   All-purpose initial ticket routine, usually called via
50   krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
51
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.
57
58   key_proc is called to fill in the key to be used for decryption.
59   keyseed is passed on to key_proc.
60
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.
65
66   If addrs is non-NULL, it is used for the addresses requested.  If it is
67   null, the system standard addresses are used.
68
69   A succesful call will place the ticket in the credentials cache ccache
70   and fill in creds with the ticket information used/returned..
71
72   returns system errors, encryption errors
73
74 */
75
76
77 /* some typedef's for the function args to make things look a bit cleaner */
78
79 typedef krb5_error_code (*git_key_proc) (krb5_context,
80                                          krb5_enctype,
81                                          krb5_data *,
82                                          krb5_const_pointer,
83                                          krb5_keyblock **);
84
85 typedef krb5_error_code (*git_decrypt_proc) (krb5_context,
86                                              const krb5_keyblock *,
87                                              krb5_const_pointer,
88                                              krb5_kdc_rep * );
89
90 static krb5_error_code make_preauth_list (krb5_context,
91                                           krb5_preauthtype *,
92                                           int, krb5_pa_data ***);
93 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
94                                                  krb5_data *realm,
95                                                  krb5_pa_data **padata);
96
97 /*
98  * This function performs 32 bit bounded addition so we can generate
99  * lifetimes without overflowing krb5_int32
100  */
101 static krb5_int32
102 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
103 {
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;
110     }
111
112     return x + y;
113 }
114
115 #if APPLE_PKINIT
116 /*
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...
120  */
121 static krb5_error_code
122 gen_nonce(krb5_context  context,
123           krb5_int32    *nonce)
124 {
125     krb5_int32 time_now;
126     krb5_error_code retval = krb5_timeofday(context, &time_now);
127     if(retval) {
128         return retval;
129     }
130     *nonce = time_now;
131     return 0;
132 }
133 #endif /* APPLE_PKINIT */
134
135 /*
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.
140  */
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,
146                 int                         *use_master)
147 {
148     krb5_kdc_rep *as_reply = 0;
149     krb5_error_code retval;
150     krb5_data reply;
151     char k4_version;            /* same type as *(krb5_data::data) */
152     int tcp_only = 0;
153
154     reply.data = 0;
155
156     /* set the nonce if the caller expects us to do it */
157
158     k4_version = packet->data[0];
159 send_again:
160     retval = krb5_sendto_kdc(context, packet,
161                              realm,
162                              &reply, use_master, tcp_only);
163 #if APPLE_PKINIT
164     inTktDebug("krb5_sendto_kdc returned %d\n", (int)retval);
165 #endif /* APPLE_PKINIT */
166
167     if (retval)
168         goto cleanup;
169
170     /* now decode the reply...could be error or as_rep */
171     if (krb5_is_krb_error(&reply)) {
172         krb5_error *err_reply;
173
174         if ((retval = decode_krb5_error(&reply, &err_reply)))
175             /* some other error code--??? */
176             goto cleanup;
177
178         if (ret_err_reply) {
179             if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG
180                 && tcp_only == 0) {
181                 tcp_only = 1;
182                 krb5_free_error(context, err_reply);
183                 free(reply.data);
184                 reply.data = 0;
185                 goto send_again;
186             }
187             *ret_err_reply = err_reply;
188         } else
189             krb5_free_error(context, err_reply);
190         goto cleanup;
191     }
192
193     /*
194      * Check to make sure it isn't a V4 reply.
195      */
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;
202
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];
207         t_switch &= ~1;
208
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;
213         } else {
214             retval = KRB5KRB_AP_ERR_MSG_TYPE;
215         }
216         goto cleanup;
217     }
218
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 ??? */
222         goto cleanup;
223
224     if (as_reply->msg_type != KRB5_AS_REP) {
225         retval = KRB5KRB_AP_ERR_MSG_TYPE;
226         krb5_free_kdc_rep(context, as_reply);
227         goto cleanup;
228     }
229
230     if (ret_as_reply)
231         *ret_as_reply = as_reply;
232     else
233         krb5_free_kdc_rep(context, as_reply);
234
235 cleanup:
236     if (reply.data)
237         free(reply.data);
238     return retval;
239 }
240
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,
247                  krb5_keyblock *        key,
248                  git_decrypt_proc       decrypt_proc,
249                  krb5_const_pointer     decryptarg)
250 {
251     krb5_error_code             retval;
252     krb5_keyblock *             decrypt_key = 0;
253     krb5_data                   salt;
254
255     if (as_reply->enc_part2)
256         return 0;
257
258     if (key)
259         decrypt_key = key;
260     else {
261         /*
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.
266          */
267         if ((retval = krb5_principal2salt(context, as_reply->client, &salt)))
268             return(retval);
269
270         retval = (*key_proc)(context, as_reply->enc_part.enctype,
271                              &salt, keyseed, &decrypt_key);
272         free(salt.data);
273         if (retval)
274             goto cleanup;
275     }
276
277     if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply)))
278         goto cleanup;
279
280 cleanup:
281     if (!key && decrypt_key)
282         krb5_free_keyblock(context, decrypt_key);
283     return (retval);
284 }
285
286 /**
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.
292  */
293 static krb5_error_code
294 verify_anonymous( krb5_context context, krb5_kdc_req *request,
295                   krb5_kdc_rep *reply, krb5_keyblock *as_key)
296 {
297     krb5_error_code ret = 0;
298     krb5_pa_data *pa;
299     krb5_data scratch;
300     krb5_keyblock *kdc_key = NULL, *expected = NULL;
301     krb5_enc_data *enc = NULL;
302     krb5_keyblock *session = reply->enc_part2->session;
303
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);
308     if (pa == NULL)
309         goto verification_error;
310     scratch.length = pa->length;
311     scratch.data = (char *) pa->contents;
312     ret = decode_krb5_enc_data( &scratch, &enc);
313     if (ret)
314         goto cleanup;
315     scratch.data = k5alloc(enc->ciphertext.length, &ret);
316     if (ret)
317         goto cleanup;
318     scratch.length = enc->ciphertext.length;
319     ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX,
320                          NULL /*cipherstate*/, enc, &scratch);
321     if (ret) {
322         free(scratch.data);
323         goto cleanup;
324     }
325     ret = decode_krb5_encryption_key( &scratch, &kdc_key);
326     zap(scratch.data, scratch.length);
327     free(scratch.data);
328     if (ret)
329         goto cleanup;
330     ret = krb5_c_fx_cf2_simple(context, kdc_key, "PKINIT",
331                                as_key, "KEYEXCHANGE", &expected);
332     if (ret)
333         goto cleanup;
334     if ((expected->enctype != session->enctype) ||
335         (expected->length != session->length) ||
336         (memcmp(expected->contents, session->contents, expected->length) != 0))
337         goto verification_error;
338 cleanup:
339     if (kdc_key)
340         krb5_free_keyblock(context, kdc_key);
341     if (expected)
342         krb5_free_keyblock(context, expected);
343     if (enc)
344         krb5_free_enc_data(context, enc);
345     return ret;
346 verification_error:
347     ret = KRB5_KDCREP_MODIFIED;
348     krb5_set_error_message(context, ret, "Reply has wrong form of session key "
349                            "for anonymous request");
350     goto cleanup;
351 }
352
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)
358 {
359     krb5_error_code             retval;
360     int                         canon_req;
361     int                         canon_ok;
362
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;
367
368     /*
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.
372      */
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);
377     if (canon_req) {
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,
382                                                         as_reply->client,
383                                                         krb5_anonymous_principal());
384         }
385     } else
386         canon_ok = 0;
387
388     if ((!canon_ok &&
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))
408     ) {
409 #if APPLE_PKINIT
410         inTktDebug("verify_as_reply: KDCREP_MODIFIED\n");
411 #if IN_TKT_DEBUG
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);
418 #endif
419 #endif /* APPLE_PKINIT */
420         return KRB5_KDCREP_MODIFIED;
421     }
422
423     if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
424         retval = krb5_set_real_time(context,
425                                     as_reply->enc_part2->times.authtime, -1);
426         if (retval)
427             return retval;
428     } else {
429         if ((request->from == 0) &&
430             (labs(as_reply->enc_part2->times.starttime - time_now)
431              > context->clockskew))
432             return (KRB5_KDCREP_SKEW);
433     }
434     return 0;
435 }
436
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,
442                krb5_creds *             creds,
443                krb5_ccache              ccache)
444 {
445     krb5_error_code             retval;
446     krb5_data *                 packet;
447     krb5_principal              client;
448     krb5_principal              server;
449
450     client = NULL;
451     server = NULL;
452
453     if (!creds->client)
454         if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
455             goto cleanup;
456
457     if (!creds->server)
458         if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
459                                           &server)))
460             goto cleanup;
461
462     /* fill in the credentials */
463     if ((retval = krb5_copy_keyblock_contents(context,
464                                               as_reply->enc_part2->session,
465                                               &creds->keyblock)))
466         goto cleanup;
467
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,
473                                       &creds->addresses)))
474         goto cleanup;
475
476     creds->second_ticket.length = 0;
477     creds->second_ticket.data = 0;
478
479     if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
480         goto cleanup;
481
482     creds->ticket = *packet;
483     free(packet);
484
485     /* store it in the ccache! */
486     if (ccache)
487         if ((retval = krb5_cc_store_cred(context, ccache, creds)))
488             goto cleanup;
489
490     if (!creds->client)
491         creds->client = client;
492     if (!creds->server)
493         creds->server = server;
494
495 cleanup:
496     if (retval) {
497         if (client)
498             krb5_free_principal(context, client);
499         if (server)
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;
507         }
508         if (creds->ticket.data) {
509             free(creds->ticket.data);
510             creds->ticket.data = 0;
511         }
512         if (creds->addresses) {
513             krb5_free_addresses(context, creds->addresses);
514             creds->addresses = 0;
515         }
516     }
517     return (retval);
518 }
519
520 static krb5_error_code
521 make_preauth_list(krb5_context  context,
522                   krb5_preauthtype *    ptypes,
523                   int                   nptypes,
524                   krb5_pa_data ***      ret_list)
525 {
526     krb5_preauthtype *          ptypep;
527     krb5_pa_data **             preauthp;
528     int                         i;
529
530     if (nptypes < 0) {
531         for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
532             ;
533     }
534
535     /* allocate space for a NULL to terminate the list */
536
537     if ((preauthp =
538          (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
539         return(ENOMEM);
540
541     for (i=0; i<nptypes; i++) {
542         if ((preauthp[i] =
543              (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
544             for (; i>=0; i--)
545                 free(preauthp[i]);
546             free(preauthp);
547             return (ENOMEM);
548         }
549         preauthp[i]->magic = KV5M_PA_DATA;
550         preauthp[i]->pa_type = ptypes[i];
551         preauthp[i]->length = 0;
552         preauthp[i]->contents = 0;
553     }
554
555     /* fill in the terminating NULL */
556
557     preauthp[nptypes] = NULL;
558
559     *ret_list = preauthp;
560     return 0;
561 }
562
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,
567     ENCTYPE_DES_CBC_MD5,
568     ENCTYPE_DES_CBC_MD4,
569     ENCTYPE_DES_CBC_CRC,
570     0
571 };
572
573 static krb5_error_code
574 rewrite_server_realm(krb5_context context,
575                      krb5_const_principal old_server,
576                      const krb5_data *realm,
577                      krb5_boolean tgs,
578                      krb5_principal *server)
579 {
580     krb5_error_code retval;
581
582     assert(*server == NULL);
583
584     retval = krb5_copy_principal(context, old_server, server);
585     if (retval)
586         return retval;
587
588     krb5_free_data_contents(context, &(*server)->realm);
589     (*server)->realm.data = NULL;
590
591     retval = krb5int_copy_data_contents(context, realm, &(*server)->realm);
592     if (retval)
593         goto cleanup;
594
595     if (tgs) {
596         krb5_free_data_contents(context, &(*server)->data[1]);
597         (*server)->data[1].data = NULL;
598
599         retval = krb5int_copy_data_contents(context, realm, &(*server)->data[1]);
600         if (retval)
601             goto cleanup;
602     }
603
604 cleanup:
605     if (retval) {
606         krb5_free_principal(context, *server);
607         *server = NULL;
608     }
609
610     return retval;
611 }
612
613 static inline int
614 tgt_is_local_realm(krb5_creds *tgt)
615 {
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));
620 }
621
622 static krb5_error_code
623 request_enc_pa_rep(krb5_pa_data ***padptr)
624 {
625     size_t size = 0;
626     krb5_pa_data **pad = *padptr;
627     krb5_pa_data *pa= NULL;
628     if (pad)
629         for (size=0; pad[size]; size++);
630     pad = realloc(pad, sizeof(*pad)*(size+2));
631
632     if (pad == NULL)
633         return ENOMEM;
634     pad[size+1] = NULL;
635     pa = malloc(sizeof(krb5_pa_data));
636     if (pa == NULL)
637         return ENOMEM;
638     pa->contents = NULL;
639     pa->length = 0;
640     pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
641     pad[size] = pa;
642     *padptr = pad;
643     return 0;
644 }
645
646 krb5_error_code KRB5_CALLCONV
647 krb5_get_in_tkt(krb5_context context,
648                 krb5_flags options,
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,
656                 krb5_creds * creds,
657                 krb5_ccache ccache,
658                 krb5_kdc_rep ** ret_as_reply)
659 {
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;
668     int                 loopcount = 0;
669     krb5_int32          do_more = 0;
670     int                 canon_flag;
671     int             use_master = 0;
672     int                 referral_count = 0;
673     krb5_principal_data referred_client;
674     krb5_principal      referred_server = NULL;
675     krb5_boolean        is_tgt_req;
676
677 #if APPLE_PKINIT
678     inTktDebug("krb5_get_in_tkt top\n");
679 #endif /* APPLE_PKINIT */
680
681     if (! krb5_realm_compare(context, creds->client, creds->server))
682         return KRB5_IN_TKT_REALM_MISMATCH;
683
684     if (ret_as_reply)
685         *ret_as_reply = 0;
686
687     referred_client = *(creds->client);
688     referred_client.realm.data = NULL;
689     referred_client.realm.length = 0;
690
691     /* per referrals draft, enterprise principals imply canonicalization */
692     canon_flag = ((options & KDC_OPT_CANONICALIZE) != 0) ||
693         creds->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
694
695     /*
696      * Set up the basic request structure
697      */
698     request.magic = KV5M_KDC_REQ;
699     request.msg_type = KRB5_AS_REQ;
700     request.addresses = 0;
701     request.ktype = 0;
702     request.padata = 0;
703     if (addrs)
704         request.addresses = (krb5_address **) addrs;
705     else
706         if ((retval = krb5_os_localaddr(context, &request.addresses)))
707             goto cleanup;
708     request.kdc_options = options;
709     request.client = creds->client;
710     request.server = creds->server;
711     request.nonce = 0;
712     request.from = creds->times.starttime;
713     request.till = creds->times.endtime;
714     request.rtime = creds->times.renew_till;
715 #if APPLE_PKINIT
716     retval = gen_nonce(context, (krb5_int32 *)&time_now);
717     if(retval) {
718         goto cleanup;
719     }
720     request.nonce = time_now;
721 #endif /* APPLE_PKINIT */
722
723     retval = krb5int_copy_etypes(get_in_tkt_enctypes, &request.ktype);
724     request.nktypes = krb5int_count_etypes(request.ktype);
725     if (ktypes) {
726         int i, req, next = 0;
727         for (req = 0; ktypes[req]; req++) {
728             if (ktypes[req] == request.ktype[next]) {
729                 next++;
730                 continue;
731             }
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.  */
738                     krb5_enctype t;
739                     t = request.ktype[next];
740                     request.ktype[next] = request.ktype[i];
741                     request.ktype[i] = t;
742                     next++;
743                     break;
744                 }
745             /* If we didn't find it, don't do anything special, just
746                drop it.  */
747         }
748         request.ktype[next] = 0;
749         request.nktypes = next;
750     }
751     request.authorization_data.ciphertext.length = 0;
752     request.authorization_data.ciphertext.data = 0;
753     request.unenc_authdata = 0;
754     request.second_ticket = 0;
755
756     /*
757      * If a list of preauth types are passed in, convert it to a
758      * preauth_to_use list.
759      */
760     if (ptypes) {
761         retval = make_preauth_list(context, ptypes, -1, &preauth_to_use);
762         if (retval)
763             goto cleanup;
764     }
765
766     is_tgt_req = tgt_is_local_realm(creds);
767
768     while (1) {
769         if (loopcount++ > MAX_IN_TKT_LOOPS) {
770             retval = KRB5_GET_IN_TKT_LOOP;
771             goto cleanup;
772         }
773
774 #if APPLE_PKINIT
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)
779             goto cleanup;
780         if (preauth_to_use)
781             krb5_free_pa_data(context, preauth_to_use);
782         preauth_to_use = 0;
783
784         err_reply = 0;
785         as_reply = 0;
786
787         if ((retval = krb5_timeofday(context, &time_now)))
788             goto cleanup;
789
790         /*
791          * XXX we know they are the same size... and we should do
792          * something better than just the current time
793          */
794         request.nonce = (krb5_int32) time_now;
795
796         if ((retval = encode_krb5_as_req(&request, &encoded_request)) != 0)
797             goto cleanup;
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);
802         if (retval != 0)
803             goto cleanup;
804
805         if (err_reply) {
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,
809                                                      &preauth_to_use);
810                 krb5_free_error(context, err_reply);
811                 if (retval)
812                     goto cleanup;
813                 retval = sort_krb5_padata_sequence(context,
814                                                    &request.server->realm,
815                                                    preauth_to_use);
816                 if (retval)
817                     goto cleanup;
818                 continue;
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);
825                     goto cleanup;
826                 }
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;
831                 }
832                 retval = krb5int_copy_data_contents(context,
833                                                     &err_reply->client->realm,
834                                                     &referred_client.realm);
835                 krb5_free_error(context, err_reply);
836                 if (retval)
837                     goto cleanup;
838                 request.client = &referred_client;
839
840                 if (referred_server != NULL) {
841                     krb5_free_principal(context, referred_server);
842                     referred_server = NULL;
843                 }
844
845                 retval = rewrite_server_realm(context,
846                                               creds->server,
847                                               &referred_client.realm,
848                                               is_tgt_req,
849                                               &referred_server);
850                 if (retval)
851                     goto cleanup;
852                 request.server = referred_server;
853
854                 continue;
855             } else {
856                 retval = (krb5_error_code) err_reply->error
857                     + ERROR_TABLE_BASE_krb5;
858                 krb5_free_error(context, err_reply);
859                 goto cleanup;
860             }
861         } else if (!as_reply) {
862             retval = KRB5KRB_AP_ERR_MSG_TYPE;
863             goto cleanup;
864         }
865         if ((retval = krb5_process_padata(context, &request, as_reply,
866                                           key_proc, keyseed, decrypt_proc,
867                                           &decrypt_key, creds,
868                                           &do_more)) != 0)
869             goto cleanup;
870
871         if (!do_more)
872             break;
873     }
874
875     if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc,
876                                    keyseed, decrypt_key, decrypt_proc,
877                                    decryptarg)))
878         goto cleanup;
879
880     if ((retval = verify_as_reply(context, time_now, &request, as_reply)))
881         goto cleanup;
882
883     if ((retval = stash_as_reply(context, time_now, &request, as_reply,
884                                  creds, ccache)))
885         goto cleanup;
886
887 cleanup:
888     if (request.ktype)
889         free(request.ktype);
890     if (!addrs && request.addresses)
891         krb5_free_addresses(context, request.addresses);
892     if (request.padata)
893         krb5_free_pa_data(context, request.padata);
894     if (preauth_to_use)
895         krb5_free_pa_data(context, preauth_to_use);
896     if (decrypt_key)
897         krb5_free_keyblock(context, decrypt_key);
898     if (as_reply) {
899         if (ret_as_reply)
900             *ret_as_reply = as_reply;
901         else
902             krb5_free_kdc_rep(context, as_reply);
903     }
904     if (referred_client.realm.data)
905         krb5_free_data_contents(context, &referred_client.realm);
906     if (referred_server)
907         krb5_free_principal(context, referred_server);
908     return (retval);
909 }
910
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)
916 {
917     int i, j, base;
918     krb5_error_code ret;
919     const char *p;
920     long l;
921     char *q, *preauth_types = NULL;
922     krb5_pa_data *tmp;
923     int need_free_string = 1;
924
925     if ((padata == NULL) || (padata[0] == NULL)) {
926         return 0;
927     }
928
929     ret = krb5int_libdefault_string(context, realm, KRB5_CONF_PREFERRED_PREAUTH_TYPES,
930                                     &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;
935     }
936
937 #ifdef DEBUG
938     fprintf (stderr, "preauth data types before sorting:");
939     for (i = 0; padata[i]; i++) {
940         fprintf (stderr, " %d", padata[i]->pa_type);
941     }
942     fprintf (stderr, "\n");
943 #endif
944
945     base = 0;
946     for (p = preauth_types; *p != '\0';) {
947         /* skip whitespace to find an entry */
948         p += strspn(p, ", ");
949         if (*p != '\0') {
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) {
957                         tmp = padata[i];
958                         for (j = i; j > base; j--)
959                             padata[j] = padata[j - 1];
960                         padata[base] = tmp;
961                         base++;
962                         break;
963                     }
964                 }
965                 p = q;
966             } else {
967                 break;
968             }
969         }
970     }
971     if (need_free_string)
972         free(preauth_types);
973
974 #ifdef DEBUG
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");
979 #endif
980
981     return 0;
982 }
983
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)
989 {
990     krb5_error_code ret;
991
992     *server = NULL;
993
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. */
998
999         if ((ret = krb5_parse_name(context, in_tkt_service, server)))
1000             return ret;
1001
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);
1007             if (p == NULL) {
1008                 krb5_free_principal(context, *server);
1009                 *server = NULL;
1010                 return ENOMEM;
1011             }
1012             (*server)->realm.data = p;
1013         }
1014
1015         (*server)->realm.length = client->realm.length;
1016         memcpy((*server)->realm.data, client->realm.data, client->realm.length);
1017     } else {
1018         ret = krb5_build_principal_ext(context, server,
1019                                        client->realm.length,
1020                                        client->realm.data,
1021                                        KRB5_TGS_NAME_SIZE,
1022                                        KRB5_TGS_NAME,
1023                                        client->realm.length,
1024                                        client->realm.data,
1025                                        0);
1026     }
1027     return ret;
1028 }
1029
1030 void KRB5_CALLCONV
1031 krb5_init_creds_free(krb5_context context,
1032                      krb5_init_creds_context ctx)
1033 {
1034     if (ctx == NULL)
1035         return;
1036
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);
1040     }
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);
1055     free(ctx);
1056 }
1057
1058 static krb5_error_code
1059 init_creds_get(krb5_context context,
1060                krb5_init_creds_context ctx,
1061                int *use_master)
1062 {
1063     krb5_error_code code;
1064     krb5_data request;
1065     krb5_data reply;
1066     krb5_data realm;
1067     unsigned int flags = 0;
1068     int tcp_only = 0;
1069
1070     request.length = 0;
1071     request.data = NULL;
1072     reply.length = 0;
1073     reply.data = NULL;
1074     realm.length = 0;
1075     realm.data = NULL;
1076
1077     for (;;) {
1078         code = krb5_init_creds_step(context,
1079                                     ctx,
1080                                     &reply,
1081                                     &request,
1082                                     &realm,
1083                                     &flags);
1084         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only)
1085             tcp_only = 1;
1086         else if (code != 0 || (flags & 1) == 0)
1087             break;
1088
1089         krb5_free_data_contents(context, &reply);
1090
1091         code = krb5_sendto_kdc(context, &request, &realm,
1092                                &reply, use_master, tcp_only);
1093         if (code != 0)
1094             break;
1095
1096         krb5_free_data_contents(context, &request);
1097         krb5_free_data_contents(context, &realm);
1098     }
1099
1100     krb5_free_data_contents(context, &request);
1101     krb5_free_data_contents(context, &reply);
1102     krb5_free_data_contents(context, &realm);
1103
1104     return code;
1105 }
1106
1107 /* Heimdal API */
1108 krb5_error_code KRB5_CALLCONV
1109 krb5_init_creds_get(krb5_context context,
1110                     krb5_init_creds_context ctx)
1111 {
1112     int use_master = 0;
1113
1114     return init_creds_get(context, ctx, &use_master);
1115 }
1116
1117 krb5_error_code KRB5_CALLCONV
1118 krb5_init_creds_get_creds(krb5_context context,
1119                           krb5_init_creds_context ctx,
1120                           krb5_creds *creds)
1121 {
1122     if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
1123         return KRB5_NO_TKT_SUPPLIED;
1124
1125     return krb5int_copy_creds_contents(context, &ctx->cred, creds);
1126 }
1127
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)
1132 {
1133     if ((ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) == 0)
1134         return KRB5_NO_TKT_SUPPLIED;
1135
1136     *times = ctx->cred.times;
1137
1138     return 0;
1139 }
1140
1141 krb5_error_code KRB5_CALLCONV
1142 krb5_init_creds_get_error(krb5_context context,
1143                           krb5_init_creds_context ctx,
1144                           krb5_error **error)
1145 {
1146     krb5_error_code code;
1147     krb5_error *ret = NULL;
1148
1149     *error = NULL;
1150
1151     if (ctx->err_reply == NULL)
1152         return 0;
1153
1154     ret = k5alloc(sizeof(*ret), &code);
1155     if (code != 0)
1156         goto cleanup;
1157
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;
1164
1165     if (ctx->err_reply->client != NULL) {
1166         code = krb5_copy_principal(context, ctx->err_reply->client,
1167                                    &ret->client);
1168         if (code != 0)
1169             goto cleanup;
1170     }
1171
1172     code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
1173     if (code != 0)
1174         goto cleanup;
1175
1176     code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
1177                                       &ret->text);
1178     if (code != 0)
1179         goto cleanup;
1180
1181     code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
1182                                       &ret->e_data);
1183     if (code != 0)
1184         goto cleanup;
1185
1186     *error = ret;
1187
1188 cleanup:
1189     if (code != 0)
1190         krb5_free_error(context, ret);
1191
1192     return code;
1193 }
1194
1195 /**
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.
1199  *
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.
1203  */
1204 static krb5_error_code
1205 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
1206                         krb5_pa_data **padata)
1207 {
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;
1214     }
1215
1216     if (ctx->fast_state) {
1217         krb5int_fast_free_state(context, ctx->fast_state);
1218         ctx->fast_state = NULL;
1219     }
1220     code = krb5int_fast_make_state(context, &ctx->fast_state);
1221     if (code != 0)
1222         goto cleanup;
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;
1228     }
1229     if (ctx->opte &&
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)))
1234             goto cleanup;
1235     }
1236
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);
1241     if (code !=0)
1242         goto cleanup;
1243     /*
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.
1247      */
1248     ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
1249     krb5_free_principal(context, ctx->request->server);
1250     ctx->request->server = NULL;
1251
1252     code = build_in_tkt_name(context, ctx->in_tkt_service,
1253                              ctx->request->client,
1254                              &ctx->request->server);
1255     if (code != 0)
1256         goto cleanup;
1257
1258     code = krb5_timeofday(context, &ctx->request_time);
1259     if (code != 0)
1260         goto cleanup;
1261
1262     code = krb5int_fast_as_armor(context, ctx->fast_state,
1263                                  ctx->opte, ctx->request);
1264     if (code != 0)
1265         goto cleanup;
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);
1269         if (code != 0)
1270             goto cleanup;
1271     }
1272     /* give the preauth plugins a chance to prep the request body */
1273     krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
1274
1275     ctx->request->from = krb5int_addint32(ctx->request_time,
1276                                           ctx->start_time);
1277     ctx->request->till = krb5int_addint32(ctx->request->from,
1278                                           ctx->tkt_life);
1279
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;
1286         }
1287         ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
1288     } else
1289         ctx->request->rtime = 0;
1290     code = krb5int_fast_prep_req_body(context, ctx->fast_state,
1291                                       ctx->request,
1292                                       &ctx->encoded_request_body);
1293     if (code != 0)
1294         goto cleanup;
1295 cleanup:
1296     return code;
1297 }
1298
1299 krb5_error_code KRB5_CALLCONV
1300 krb5_init_creds_init(krb5_context context,
1301                      krb5_principal client,
1302                      krb5_prompter_fct prompter,
1303                      void *data,
1304                      krb5_deltat start_time,
1305                      krb5_get_init_creds_opt *options,
1306                      krb5_init_creds_context *pctx)
1307 {
1308     krb5_error_code code;
1309     krb5_init_creds_context ctx;
1310     int tmp;
1311     char *str = NULL;
1312     krb5_gic_opt_ext *opte;
1313
1314     ctx = k5alloc(sizeof(*ctx), &code);
1315     if (code != 0)
1316         goto cleanup;
1317
1318     ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
1319     if (code != 0)
1320         goto cleanup;
1321     ctx->enc_pa_rep_permitted = 1;
1322     code = krb5_copy_principal(context, client, &ctx->request->client);
1323     if (code != 0)
1324         goto cleanup;
1325
1326     ctx->prompter = prompter;
1327     ctx->prompter_data = data;
1328     ctx->gak_fct = krb5_get_as_key_password;
1329     ctx->gak_data = &ctx->password;
1330
1331     ctx->request_time = 0; /* filled in later */
1332     ctx->start_time = start_time;
1333
1334     if (options == NULL) {
1335         code = krb5_get_init_creds_opt_alloc(context, &options);
1336         if (code != 0)
1337             goto cleanup;
1338     }
1339
1340     code = krb5int_gic_opt_to_opte(context, options,
1341                                    &ctx->opte, 1, "krb5_init_creds_init");
1342     if (code != 0)
1343         goto cleanup;
1344
1345     opte = ctx->opte;
1346
1347     ctx->get_data_rock.magic = CLIENT_ROCK_MAGIC;
1348     ctx->get_data_rock.etype = &ctx->etype;
1349
1350     /* Initialise request parameters as per krb5_get_init_creds() */
1351     ctx->request->kdc_options = context->kdc_default_options;
1352
1353     /* forwaradble */
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)
1358         ;
1359     else
1360         tmp = 0;
1361     if (tmp)
1362         ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
1363
1364     /* proxiable */
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)
1369         ;
1370     else
1371         tmp = 0;
1372     if (tmp)
1373         ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
1374
1375     /* canonicalize */
1376     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
1377         tmp = 1;
1378     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
1379                                         KRB5_CONF_CANONICALIZE, &tmp) == 0)
1380         ;
1381     else
1382         tmp = 0;
1383     if (tmp)
1384         ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
1385
1386     /* allow_postdate */
1387     if (ctx->start_time > 0)
1388         ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
1389
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);
1396         if (code != 0)
1397             goto cleanup;
1398         free(str);
1399         str = NULL;
1400     } else
1401         ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
1402
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);
1409         if (code != 0)
1410             goto cleanup;
1411         free(str);
1412         str = NULL;
1413     } else
1414         ctx->renew_life = 0;
1415
1416     if (ctx->renew_life > 0)
1417         ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
1418
1419     /* enctypes */
1420     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
1421         ctx->request->ktype =
1422             k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
1423                     &code);
1424         if (code != 0)
1425             goto cleanup;
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);
1432     } else {
1433         /* there isn't any useful default here. */
1434         code = KRB5_CONFIG_ETYPE_NOSUPP;
1435         goto cleanup;
1436     }
1437
1438     /* addresess */
1439     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
1440         code = krb5_copy_addresses(context, opte->address_list,
1441                                    &ctx->request->addresses);
1442         if (code != 0)
1443             goto cleanup;
1444     } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
1445                                           KRB5_CONF_NOADDRESSES, &tmp) != 0
1446                || tmp) {
1447         ctx->request->addresses = NULL;
1448     } else {
1449         code = krb5_os_localaddr(context, &ctx->request->addresses);
1450         if (code != 0)
1451             goto cleanup;
1452     }
1453
1454     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
1455         code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
1456         if (code != 0)
1457             goto cleanup;
1458     } else {
1459         ctx->salt.length = SALT_TYPE_AFS_LENGTH;
1460         ctx->salt.data = NULL;
1461     }
1462
1463     /* Anonymous. */
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,
1471                                             client->realm.data,
1472                                             strlen(KRB5_WELLKNOWN_NAMESTR),
1473                                             KRB5_WELLKNOWN_NAMESTR,
1474                                             strlen(KRB5_ANONYMOUS_PRINCSTR),
1475                                             KRB5_ANONYMOUS_PRINCSTR,
1476                                             0);
1477             if (code)
1478                 goto cleanup;
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;
1482         }
1483     }
1484     /* We will also handle anonymous if the input principal is the anonymous
1485      * principal. */
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;
1490     }
1491     code = restart_init_creds_loop(context, ctx, NULL);
1492     if (code)
1493         goto cleanup;
1494
1495     *pctx = ctx;
1496     ctx = NULL;
1497
1498 cleanup:
1499     krb5_init_creds_free(context, ctx);
1500     free(str);
1501
1502     return code;
1503 }
1504
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)
1509 {
1510     char *s;
1511
1512     s = strdup(service);
1513     if (s == NULL)
1514         return ENOMEM;
1515
1516     free(ctx->in_tkt_service);
1517     ctx->in_tkt_service = s;
1518
1519     krb5_preauth_request_context_fini(context);
1520     return restart_init_creds_loop(context, ctx, NULL);
1521 }
1522
1523 static krb5_error_code
1524 init_creds_validate_reply(krb5_context context,
1525                           krb5_init_creds_context ctx,
1526                           krb5_data *reply)
1527 {
1528     krb5_error_code code;
1529     krb5_error *error = NULL;
1530     krb5_kdc_rep *as_reply = NULL;
1531
1532     krb5_free_error(context, ctx->err_reply);
1533     ctx->err_reply = NULL;
1534
1535     krb5_free_kdc_rep(context, ctx->reply);
1536     ctx->reply = NULL;
1537
1538     if (krb5_is_krb_error(reply)) {
1539         code = decode_krb5_error(reply, &error);
1540         if (code != 0)
1541             return code;
1542
1543         assert(error != NULL);
1544
1545         if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1546             krb5_free_error(context, error);
1547             return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1548         } else {
1549             ctx->err_reply = error;
1550             return 0;
1551         }
1552     }
1553
1554     /*
1555      * Check to make sure it isn't a V4 reply.
1556      */
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;
1563
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];
1568         t_switch &= ~1;
1569
1570         if (t_switch == V4_AUTH_MSG_ERR_REPLY
1571             && reply->data[0] == V4_KRB_PROT_VERSION) {
1572             code = KRB5KRB_AP_ERR_V4_REPLY;
1573         } else {
1574             code = KRB5KRB_AP_ERR_MSG_TYPE;
1575         }
1576         return code;
1577     }
1578
1579     /* It must be a KRB_AS_REP message, or an bad returned packet */
1580     code = decode_krb5_as_rep(reply, &as_reply);
1581     if (code != 0)
1582         return code;
1583
1584     if (as_reply->msg_type != KRB5_AS_REP) {
1585         krb5_free_kdc_rep(context, as_reply);
1586         return KRB5KRB_AP_ERR_MSG_TYPE;
1587     }
1588
1589     ctx->reply = as_reply;
1590
1591     return 0;
1592 }
1593
1594 static krb5_error_code
1595 init_creds_step_request(krb5_context context,
1596                         krb5_init_creds_context ctx,
1597                         krb5_data *out)
1598 {
1599     krb5_error_code code;
1600
1601     if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1602         code = KRB5_GET_IN_TKT_LOOP;
1603         goto cleanup;
1604     }
1605
1606     if (ctx->err_reply == NULL) {
1607         /* either our first attempt, or retrying after PREAUTH_NEEDED */
1608         code = krb5_do_preauth(context,
1609                                ctx->request,
1610                                ctx->encoded_request_body,
1611                                ctx->encoded_previous_request,
1612                                ctx->preauth_to_use,
1613                                &ctx->request->padata,
1614                                &ctx->salt,
1615                                &ctx->s2kparams,
1616                                &ctx->etype,
1617                                &ctx->as_key,
1618                                ctx->prompter,
1619                                ctx->prompter_data,
1620                                ctx->gak_fct,
1621                                ctx->gak_data,
1622                                &ctx->get_data_rock,
1623                                ctx->opte);
1624         if (code != 0)
1625             goto cleanup;
1626     } else {
1627         if (ctx->preauth_to_use != NULL) {
1628             /*
1629              * Retry after an error other than PREAUTH_NEEDED,
1630              * using e-data to figure out what to change.
1631              */
1632             code = krb5_do_preauth_tryagain(context,
1633                                             ctx->request,
1634                                             ctx->encoded_request_body,
1635                                             ctx->encoded_previous_request,
1636                                             ctx->preauth_to_use,
1637                                             &ctx->request->padata,
1638                                             ctx->err_reply,
1639                                             &ctx->salt,
1640                                             &ctx->s2kparams,
1641                                             &ctx->etype,
1642                                             &ctx->as_key,
1643                                             ctx->prompter,
1644                                             ctx->prompter_data,
1645                                             ctx->gak_fct,
1646                                             ctx->gak_data,
1647                                             &ctx->get_data_rock,
1648                                             ctx->opte);
1649         } else {
1650             /* No preauth supplied, so can't query the plugins. */
1651             code = KRB5KRB_ERR_GENERIC;
1652         }
1653         if (code != 0) {
1654             /* couldn't come up with anything better */
1655             code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1656             goto cleanup;
1657         }
1658     }
1659
1660     if (ctx->encoded_previous_request != NULL) {
1661         krb5_free_data(context, ctx->encoded_previous_request);
1662         ctx->encoded_previous_request = NULL;
1663     }
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);
1668     if (code)
1669         goto cleanup;
1670     code = krb5int_fast_prep_req(context, ctx->fast_state,
1671                                  ctx->request, ctx->encoded_request_body,
1672                                  encode_krb5_as_req,
1673                                  &ctx->encoded_previous_request);
1674     if (code != 0)
1675         goto cleanup;
1676
1677     code = krb5int_copy_data_contents(context,
1678                                       ctx->encoded_previous_request,
1679                                       out);
1680     if (code != 0)
1681         goto cleanup;
1682
1683 cleanup:
1684     return code;
1685 }
1686
1687 /*
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
1693  * situations.
1694  *
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
1699  * armor key.
1700  *
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.
1710  *
1711  * In all these cases we should not start over more than once.  Control flow is
1712  * managed by several variables.
1713  *
1714  *   sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
1715  *   no restart on PREAUTH_FAILED
1716  *
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.
1719  *
1720  *   have_restarted: true if we've already restarted
1721  */
1722 static krb5_boolean
1723 negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
1724                              krb5_pa_data **padata)
1725 {
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)))
1730         return 1;
1731     return 0;
1732 }
1733
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)
1737 {
1738     int i;
1739
1740     for (i = 0; i < ctx->request->nktypes; i++) {
1741         if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1742             return 0;
1743     }
1744     return KRB5_CONFIG_ETYPE_NOSUPP;
1745 }
1746
1747 static krb5_error_code
1748 init_creds_step_reply(krb5_context context,
1749                       krb5_init_creds_context ctx,
1750                       krb5_data *in)
1751 {
1752     krb5_error_code code;
1753     krb5_pa_data **padata = NULL;
1754     krb5_pa_data **kdc_padata = NULL;
1755     krb5_boolean retry = FALSE;
1756     int canon_flag = 0;
1757     krb5_keyblock *strengthen_key = NULL;
1758     krb5_keyblock encrypting_key;
1759     krb5_boolean fast_avail;
1760
1761     encrypting_key.length = 0;
1762     encrypting_key.contents = NULL;
1763
1764     /* process previous KDC response */
1765     code = init_creds_validate_reply(context, ctx, in);
1766     if (code != 0)
1767         goto cleanup;
1768
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;
1772
1773     if (ctx->err_reply != NULL) {
1774         code = krb5int_fast_process_error(context, ctx->fast_state,
1775                                           &ctx->err_reply, &padata, &retry);
1776         if (code != 0)
1777             goto cleanup;
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 &&
1787                    retry) {
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;
1791             padata = NULL;
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);
1798
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;
1803                 goto cleanup;
1804             }
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);
1818             if (code != 0)
1819                 goto cleanup;
1820         } else {
1821             if (retry) {
1822                 code = 0;
1823             } else {
1824                 /* error + no hints = give up */
1825                 code = (krb5_error_code)ctx->err_reply->error +
1826                     ERROR_TABLE_BASE_krb5;
1827             }
1828         }
1829
1830         /* Return error code, or continue with next iteration */
1831         goto cleanup;
1832     }
1833
1834     /* We have a response. Process it. */
1835     assert(ctx->reply != NULL);
1836
1837     /* Check for replies (likely forged) with unasked-for enctypes. */
1838     code = check_reply_enctype(ctx);
1839     if (code != 0)
1840         goto cleanup;
1841
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);
1846     if (code != 0)
1847         goto cleanup;
1848
1849     code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1850                                      ctx->reply->padata);
1851     if (code != 0)
1852         goto cleanup;
1853
1854     ctx->etype = ctx->reply->enc_part.enctype;
1855
1856     code = krb5_do_preauth(context,
1857                            ctx->request,
1858                            ctx->encoded_request_body,
1859                            ctx->encoded_previous_request,
1860                            ctx->reply->padata,
1861                            &kdc_padata,
1862                            &ctx->salt,
1863                            &ctx->s2kparams,
1864                            &ctx->etype,
1865                            &ctx->as_key,
1866                            ctx->prompter,
1867                            ctx->prompter_data,
1868                            ctx->gak_fct,
1869                            ctx->gak_data,
1870                            &ctx->get_data_rock,
1871                            ctx->opte);
1872     if (code != 0)
1873         goto cleanup;
1874
1875     /*
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
1883      * verify_as_reply.
1884      */
1885     if (ctx->salt.length == SALT_TYPE_AFS_LENGTH && ctx->salt.data == NULL) {
1886         code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1887         if (code != 0)
1888             goto cleanup;
1889     }
1890
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. */
1896
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
1902        again.  */
1903     if (ctx->as_key.length) {
1904         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1905                                       &encrypting_key);
1906         if (code != 0)
1907             goto cleanup;
1908         code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
1909                                 &encrypting_key, krb5_kdc_rep_decrypt_proc,
1910                                 NULL);
1911     } else
1912         code = -1;
1913
1914     if (code != 0) {
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);
1921         if (code != 0)
1922             goto cleanup;
1923
1924         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1925                                       &encrypting_key);
1926         if (code != 0)
1927             goto cleanup;
1928
1929         code = decrypt_as_reply(context, NULL, ctx->reply, NULL, NULL,
1930                                 &encrypting_key, krb5_kdc_rep_decrypt_proc,
1931                                 NULL);
1932         if (code != 0)
1933             goto cleanup;
1934     }
1935
1936     code = krb5int_fast_verify_nego(context, ctx->fast_state,
1937                                     ctx->reply, ctx->encoded_previous_request,
1938                                     &encrypting_key, &fast_avail);
1939     if (code)
1940         goto cleanup;
1941     code = verify_as_reply(context, ctx->request_time,
1942                            ctx->request, ctx->reply);
1943     if (code != 0)
1944         goto cleanup;
1945     code = verify_anonymous(context, ctx->request, ctx->reply,
1946                             &encrypting_key);
1947     if (code)
1948         goto cleanup;
1949
1950     code = stash_as_reply(context, ctx->request_time, ctx->request,
1951                           ctx->reply, &ctx->cred, NULL);
1952     if (code != 0)
1953         goto cleanup;
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);
1958         if (code != 0)
1959             goto cc_cleanup;
1960         code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
1961         if (code != 0)
1962             goto cc_cleanup;
1963         if (fast_avail) {
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);
1968         }
1969     cc_cleanup:
1970         if (code !=0) {
1971             const char *msg;
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);
1976         }
1977     }
1978
1979     krb5_preauth_request_context_fini(context);
1980
1981     /* success */
1982     code = 0;
1983     ctx->flags |= KRB5_INIT_CREDS_STEP_FLAG_COMPLETE;
1984
1985 cleanup:
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);
1990
1991     return code;
1992 }
1993
1994 /*
1995  * Do next step of credentials acquisition.
1996  *
1997  * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1998  * should be sent with TCP.
1999  */
2000 krb5_error_code KRB5_CALLCONV
2001 krb5_init_creds_step(krb5_context context,
2002                      krb5_init_creds_context ctx,
2003                      krb5_data *in,
2004                      krb5_data *out,
2005                      krb5_data *realm,
2006                      unsigned int *flags)
2007 {
2008     krb5_error_code code = 0, code2;
2009
2010     *flags = 0;
2011
2012     out->data = NULL;
2013     out->length = 0;
2014
2015     realm->data = NULL;
2016     realm->length = 0;
2017
2018     if (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE)
2019         goto cleanup;
2020
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,
2026                                                out);
2027             if (code2 != 0) {
2028                 code = code2;
2029                 goto cleanup;
2030             }
2031             goto copy_realm;
2032         }
2033         if (code != 0 || (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE))
2034             goto cleanup;
2035     }
2036
2037     code = init_creds_step_request(context, ctx, out);
2038     if (code != 0)
2039         goto cleanup;
2040
2041     /* Only a new request increments the loop count, not a TCP retry */
2042     ctx->loopcount++;
2043
2044 copy_realm:
2045     assert(ctx->request->server != NULL);
2046
2047     code2 = krb5int_copy_data_contents(context,
2048                                        &ctx->request->server->realm,
2049                                        realm);
2050     if (code2 != 0) {
2051         code = code2;
2052         goto cleanup;
2053     }
2054
2055 cleanup:
2056     if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
2057         char *client_name;
2058
2059         /* See if we can produce a more detailed error message */
2060         code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
2061         if (code2 == 0) {
2062             krb5_set_error_message(context, code,
2063                                    "Client '%s' not found in Kerberos database",
2064                                    client_name);
2065             krb5_free_unparsed_name(context, client_name);
2066         }
2067     }
2068
2069     *flags = (ctx->flags & KRB5_INIT_CREDS_STEP_FLAG_COMPLETE) ? 0 : 1;
2070
2071     return code;
2072 }
2073
2074 krb5_error_code KRB5_CALLCONV
2075 krb5int_get_init_creds(krb5_context context,
2076                        krb5_creds *creds,
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,
2084                        void *gak_data,
2085                        int  *use_master,
2086                        krb5_kdc_rep **as_reply)
2087 {
2088     krb5_error_code code;
2089     krb5_init_creds_context ctx = NULL;
2090
2091     code = krb5_init_creds_init(context,
2092                                 client,
2093                                 prompter,
2094                                 prompter_data,
2095                                 start_time,
2096                                 options,
2097                                 &ctx);
2098     if (code != 0)
2099         goto cleanup;
2100
2101     ctx->gak_fct = gak_fct;
2102     ctx->gak_data = gak_data;
2103
2104     if (in_tkt_service) {
2105         code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2106         if (code != 0)
2107             goto cleanup;
2108     }
2109
2110     code = init_creds_get(context, ctx, use_master);
2111     if (code != 0)
2112         goto cleanup;
2113
2114     code = krb5_init_creds_get_creds(context, ctx, creds);
2115     if (code != 0)
2116         goto cleanup;
2117
2118     if (as_reply != NULL) {
2119         *as_reply = ctx->reply;
2120         ctx->reply = NULL;
2121     }
2122
2123 cleanup:
2124     krb5_init_creds_free(context, ctx);
2125
2126     return code;
2127 }
2128
2129 krb5_error_code
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)
2134 {
2135     int i;
2136     krb5_int32 starttime;
2137     krb5_get_init_creds_opt *opt;
2138     krb5_error_code retval;
2139
2140     *out = NULL;
2141     retval = krb5_get_init_creds_opt_alloc(context, &opt);
2142     if (retval)
2143         return(retval);
2144
2145     if (addrs)
2146         krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
2147     if (ktypes) {
2148         i = krb5int_count_etypes(ktypes);
2149         if (i)
2150             krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
2151     }
2152     if (pre_auth_types) {
2153         for (i=0; pre_auth_types[i]; i++);
2154         if (i)
2155             krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
2156     }
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);
2165         if (retval)
2166             goto cleanup;
2167         if (creds->times.starttime) starttime = creds->times.starttime;
2168         krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
2169     }
2170     *out = opt;
2171     return 0;
2172
2173 cleanup:
2174     krb5_get_init_creds_opt_free(context, opt);
2175     return retval;
2176 }