Unify krb5_get_init_creds_keytab code paths
[krb5.git] / src / lib / krb5 / krb / get_in_tkt.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/get_in_tkt.c */
3 /*
4  * Copyright 1990,1991, 2003, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include <string.h>
28
29 #include "k5-int.h"
30 #include "int-proto.h"
31 #include "os-proto.h"
32 #include "fast.h"
33 #include "init_creds_ctx.h"
34
35 #if APPLE_PKINIT
36 #define     IN_TKT_DEBUG    0
37 #if         IN_TKT_DEBUG
38 #define     inTktDebug(args...)       printf(args)
39 #else
40 #define     inTktDebug(args...)
41 #endif
42 #endif /* APPLE_PKINIT */
43
44 /* some typedef's for the function args to make things look a bit cleaner */
45
46 static krb5_error_code make_preauth_list (krb5_context,
47                                           krb5_preauthtype *,
48                                           int, krb5_pa_data ***);
49 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
50                                                  krb5_data *realm,
51                                                  krb5_pa_data **padata);
52
53 /*
54  * This function performs 32 bit bounded addition so we can generate
55  * lifetimes without overflowing krb5_int32
56  */
57 static krb5_int32
58 krb5int_addint32 (krb5_int32 x, krb5_int32 y)
59 {
60     if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
61         /* sum will be be greater than KRB5_INT32_MAX */
62         return KRB5_INT32_MAX;
63     } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
64         /* sum will be less than KRB5_INT32_MIN */
65         return KRB5_INT32_MIN;
66     }
67
68     return x + y;
69 }
70
71 static krb5_error_code
72 decrypt_as_reply(krb5_context context, krb5_kdc_req *request,
73                  krb5_kdc_rep *as_reply, krb5_keyblock *key)
74 {
75     if (as_reply->enc_part2)
76         return 0;
77
78     return krb5_kdc_rep_decrypt_proc(context, key, NULL, as_reply);
79 }
80
81 /**
82  * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC
83  * contribution key.  This routine confirms that the session key is of the
84  * right form for fully anonymous requests.  It is here rather than in the
85  * preauth code because the session key cannot be verified until the AS reply
86  * is decrypted and the preauth code all runs before the AS reply is decrypted.
87  */
88 static krb5_error_code
89 verify_anonymous( krb5_context context, krb5_kdc_req *request,
90                   krb5_kdc_rep *reply, krb5_keyblock *as_key)
91 {
92     krb5_error_code ret = 0;
93     krb5_pa_data *pa;
94     krb5_data scratch;
95     krb5_keyblock *kdc_key = NULL, *expected = NULL;
96     krb5_enc_data *enc = NULL;
97     krb5_keyblock *session = reply->enc_part2->session;
98
99     if (!krb5_principal_compare_any_realm(context, request->client,
100                                           krb5_anonymous_principal()))
101         return 0; /* Only applies to fully anonymous */
102     pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX);
103     if (pa == NULL)
104         goto verification_error;
105     scratch.length = pa->length;
106     scratch.data = (char *) pa->contents;
107     ret = decode_krb5_enc_data( &scratch, &enc);
108     if (ret)
109         goto cleanup;
110     scratch.data = k5alloc(enc->ciphertext.length, &ret);
111     if (ret)
112         goto cleanup;
113     scratch.length = enc->ciphertext.length;
114     ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX,
115                          NULL /*cipherstate*/, enc, &scratch);
116     if (ret) {
117         free(scratch.data);
118         goto cleanup;
119     }
120     ret = decode_krb5_encryption_key( &scratch, &kdc_key);
121     zap(scratch.data, scratch.length);
122     free(scratch.data);
123     if (ret)
124         goto cleanup;
125     ret = krb5_c_fx_cf2_simple(context, kdc_key, "PKINIT",
126                                as_key, "KEYEXCHANGE", &expected);
127     if (ret)
128         goto cleanup;
129     if ((expected->enctype != session->enctype) ||
130         (expected->length != session->length) ||
131         (memcmp(expected->contents, session->contents, expected->length) != 0))
132         goto verification_error;
133 cleanup:
134     if (kdc_key)
135         krb5_free_keyblock(context, kdc_key);
136     if (expected)
137         krb5_free_keyblock(context, expected);
138     if (enc)
139         krb5_free_enc_data(context, enc);
140     return ret;
141 verification_error:
142     ret = KRB5_KDCREP_MODIFIED;
143     krb5_set_error_message(context, ret, _("Reply has wrong form of session "
144                                            "key for anonymous request"));
145     goto cleanup;
146 }
147
148 static krb5_error_code
149 verify_as_reply(krb5_context            context,
150                 krb5_timestamp          time_now,
151                 krb5_kdc_req            *request,
152                 krb5_kdc_rep            *as_reply)
153 {
154     krb5_error_code             retval;
155     int                         canon_req;
156     int                         canon_ok;
157     krb5_timestamp              time_offset;
158
159     /* check the contents for sanity: */
160     if (!as_reply->enc_part2->times.starttime)
161         as_reply->enc_part2->times.starttime =
162             as_reply->enc_part2->times.authtime;
163
164     /*
165      * We only allow the AS-REP server name to be changed if the
166      * caller set the canonicalize flag (or requested an enterprise
167      * principal) and we requested (and received) a TGT.
168      */
169     canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
170         (krb5_princ_type(context, request->client) ==
171          KRB5_NT_ENTERPRISE_PRINCIPAL) ||
172         (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS);
173     if (canon_req) {
174         canon_ok = IS_TGS_PRINC(context, request->server) &&
175             IS_TGS_PRINC(context, as_reply->enc_part2->server);
176         if (!canon_ok && (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS)) {
177             canon_ok = krb5_principal_compare_any_realm(context,
178                                                         as_reply->client,
179                                                         krb5_anonymous_principal());
180         }
181     } else
182         canon_ok = 0;
183
184     if ((!canon_ok &&
185          (!krb5_principal_compare(context, as_reply->client, request->client) ||
186           !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)))
187         || !krb5_principal_compare(context, as_reply->enc_part2->server, as_reply->ticket->server)
188         || (request->nonce != as_reply->enc_part2->nonce)
189         /* XXX check for extraneous flags */
190         /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
191         || ((request->kdc_options & KDC_OPT_POSTDATED) &&
192             (request->from != 0) &&
193             (request->from != as_reply->enc_part2->times.starttime))
194         || ((request->till != 0) &&
195             (as_reply->enc_part2->times.endtime > request->till))
196         || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
197             (request->rtime != 0) &&
198             (as_reply->enc_part2->times.renew_till > request->rtime))
199         || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
200             !(request->kdc_options & KDC_OPT_RENEWABLE) &&
201             (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
202             (request->till != 0) &&
203             (as_reply->enc_part2->times.renew_till > request->till))
204     ) {
205 #if APPLE_PKINIT
206         inTktDebug("verify_as_reply: KDCREP_MODIFIED\n");
207 #if IN_TKT_DEBUG
208         if(request->client->realm.length && request->client->data->length)
209             inTktDebug("request: name %s realm %s\n",
210                        request->client->realm.data, request->client->data->data);
211         if(as_reply->client->realm.length && as_reply->client->data->length)
212             inTktDebug("reply  : name %s realm %s\n",
213                        as_reply->client->realm.data, as_reply->client->data->data);
214 #endif
215 #endif /* APPLE_PKINIT */
216         return KRB5_KDCREP_MODIFIED;
217     }
218
219     if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
220         time_offset = as_reply->enc_part2->times.authtime - time_now;
221         retval = krb5_set_time_offsets(context, time_offset, 0);
222         if (retval)
223             return retval;
224     } else {
225         if ((request->from == 0) &&
226             (labs(as_reply->enc_part2->times.starttime - time_now)
227              > context->clockskew))
228             return (KRB5_KDCREP_SKEW);
229     }
230     return 0;
231 }
232
233 static krb5_error_code
234 stash_as_reply(krb5_context             context,
235                krb5_timestamp           time_now,
236                krb5_kdc_req             *request,
237                krb5_kdc_rep             *as_reply,
238                krb5_creds *             creds,
239                krb5_ccache              ccache)
240 {
241     krb5_error_code             retval;
242     krb5_data *                 packet;
243     krb5_principal              client;
244     krb5_principal              server;
245
246     client = NULL;
247     server = NULL;
248
249     if (!creds->client)
250         if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
251             goto cleanup;
252
253     if (!creds->server)
254         if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
255                                           &server)))
256             goto cleanup;
257
258     /* fill in the credentials */
259     if ((retval = krb5_copy_keyblock_contents(context,
260                                               as_reply->enc_part2->session,
261                                               &creds->keyblock)))
262         goto cleanup;
263
264     creds->times = as_reply->enc_part2->times;
265     creds->is_skey = FALSE;             /* this is an AS_REQ, so cannot
266                                            be encrypted in skey */
267     creds->ticket_flags = as_reply->enc_part2->flags;
268     if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
269                                       &creds->addresses)))
270         goto cleanup;
271
272     creds->second_ticket.length = 0;
273     creds->second_ticket.data = 0;
274
275     if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
276         goto cleanup;
277
278     creds->ticket = *packet;
279     free(packet);
280
281     /* store it in the ccache! */
282     if (ccache)
283         if ((retval = krb5_cc_store_cred(context, ccache, creds)))
284             goto cleanup;
285
286     if (!creds->client)
287         creds->client = client;
288     if (!creds->server)
289         creds->server = server;
290
291 cleanup:
292     if (retval) {
293         if (client)
294             krb5_free_principal(context, client);
295         if (server)
296             krb5_free_principal(context, server);
297         if (creds->keyblock.contents) {
298             memset(creds->keyblock.contents, 0,
299                    creds->keyblock.length);
300             free(creds->keyblock.contents);
301             creds->keyblock.contents = 0;
302             creds->keyblock.length = 0;
303         }
304         if (creds->ticket.data) {
305             free(creds->ticket.data);
306             creds->ticket.data = 0;
307         }
308         if (creds->addresses) {
309             krb5_free_addresses(context, creds->addresses);
310             creds->addresses = 0;
311         }
312     }
313     return (retval);
314 }
315
316 static krb5_error_code
317 make_preauth_list(krb5_context  context,
318                   krb5_preauthtype *    ptypes,
319                   int                   nptypes,
320                   krb5_pa_data ***      ret_list)
321 {
322     krb5_preauthtype *          ptypep;
323     krb5_pa_data **             preauthp;
324     int                         i;
325
326     if (nptypes < 0) {
327         for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
328             ;
329     }
330
331     /* allocate space for a NULL to terminate the list */
332
333     if ((preauthp =
334          (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
335         return(ENOMEM);
336
337     for (i=0; i<nptypes; i++) {
338         if ((preauthp[i] =
339              (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
340             for (; i>=0; i--)
341                 free(preauthp[i]);
342             free(preauthp);
343             return (ENOMEM);
344         }
345         preauthp[i]->magic = KV5M_PA_DATA;
346         preauthp[i]->pa_type = ptypes[i];
347         preauthp[i]->length = 0;
348         preauthp[i]->contents = 0;
349     }
350
351     /* fill in the terminating NULL */
352
353     preauthp[nptypes] = NULL;
354
355     *ret_list = preauthp;
356     return 0;
357 }
358
359 #define MAX_IN_TKT_LOOPS 16
360
361 static krb5_error_code
362 request_enc_pa_rep(krb5_pa_data ***padptr)
363 {
364     size_t size = 0;
365     krb5_pa_data **pad = *padptr;
366     krb5_pa_data *pa= NULL;
367     if (pad)
368         for (size=0; pad[size]; size++);
369     pad = realloc(pad, sizeof(*pad)*(size+2));
370
371     if (pad == NULL)
372         return ENOMEM;
373     pad[size+1] = NULL;
374     pa = malloc(sizeof(krb5_pa_data));
375     if (pa == NULL)
376         return ENOMEM;
377     pa->contents = NULL;
378     pa->length = 0;
379     pa->pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
380     pad[size] = pa;
381     *padptr = pad;
382     return 0;
383 }
384
385 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
386  * libdefaults entry are listed before any others. */
387 static krb5_error_code
388 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
389                           krb5_pa_data **padata)
390 {
391     int i, j, base;
392     krb5_error_code ret;
393     const char *p;
394     long l;
395     char *q, *preauth_types = NULL;
396     krb5_pa_data *tmp;
397     int need_free_string = 1;
398
399     if ((padata == NULL) || (padata[0] == NULL)) {
400         return 0;
401     }
402
403     ret = krb5int_libdefault_string(context, realm, KRB5_CONF_PREFERRED_PREAUTH_TYPES,
404                                     &preauth_types);
405     if ((ret != 0) || (preauth_types == NULL)) {
406         /* Try to use PKINIT first. */
407         preauth_types = "17, 16, 15, 14";
408         need_free_string = 0;
409     }
410
411 #ifdef DEBUG
412     fprintf (stderr, "preauth data types before sorting:");
413     for (i = 0; padata[i]; i++) {
414         fprintf (stderr, " %d", padata[i]->pa_type);
415     }
416     fprintf (stderr, "\n");
417 #endif
418
419     base = 0;
420     for (p = preauth_types; *p != '\0';) {
421         /* skip whitespace to find an entry */
422         p += strspn(p, ", ");
423         if (*p != '\0') {
424             /* see if we can extract a number */
425             l = strtol(p, &q, 10);
426             if ((q != NULL) && (q > p)) {
427                 /* got a valid number; search for a matchin entry */
428                 for (i = base; padata[i] != NULL; i++) {
429                     /* bubble the matching entry to the front of the list */
430                     if (padata[i]->pa_type == l) {
431                         tmp = padata[i];
432                         for (j = i; j > base; j--)
433                             padata[j] = padata[j - 1];
434                         padata[base] = tmp;
435                         base++;
436                         break;
437                     }
438                 }
439                 p = q;
440             } else {
441                 break;
442             }
443         }
444     }
445     if (need_free_string)
446         free(preauth_types);
447
448 #ifdef DEBUG
449     fprintf (stderr, "preauth data types after sorting:");
450     for (i = 0; padata[i]; i++)
451         fprintf (stderr, " %d", padata[i]->pa_type);
452     fprintf (stderr, "\n");
453 #endif
454
455     return 0;
456 }
457
458 static krb5_error_code
459 build_in_tkt_name(krb5_context context,
460                   char *in_tkt_service,
461                   krb5_const_principal client,
462                   krb5_principal *server)
463 {
464     krb5_error_code ret;
465
466     *server = NULL;
467
468     if (in_tkt_service) {
469         /* this is ugly, because so are the data structures involved.  I'm
470            in the library, so I'm going to manipulate the data structures
471            directly, otherwise, it will be worse. */
472
473         if ((ret = krb5_parse_name(context, in_tkt_service, server)))
474             return ret;
475
476         /* stuff the client realm into the server principal.
477            realloc if necessary */
478         if ((*server)->realm.length < client->realm.length) {
479             char *p = realloc((*server)->realm.data,
480                               client->realm.length);
481             if (p == NULL) {
482                 krb5_free_principal(context, *server);
483                 *server = NULL;
484                 return ENOMEM;
485             }
486             (*server)->realm.data = p;
487         }
488
489         (*server)->realm.length = client->realm.length;
490         memcpy((*server)->realm.data, client->realm.data, client->realm.length);
491     } else {
492         ret = krb5_build_principal_ext(context, server,
493                                        client->realm.length,
494                                        client->realm.data,
495                                        KRB5_TGS_NAME_SIZE,
496                                        KRB5_TGS_NAME,
497                                        client->realm.length,
498                                        client->realm.data,
499                                        0);
500         if (ret)
501             return ret;
502     }
503     /*
504      * Windows Server 2008 R2 RODC insists on TGS principal names having the
505      * right name type.
506      */
507     if (krb5_princ_size(context, *server) == 2 &&
508         data_eq_string(*krb5_princ_component(context, *server, 0),
509                        KRB5_TGS_NAME)) {
510         krb5_princ_type(context, *server) = KRB5_NT_SRV_INST;
511     }
512     return 0;
513 }
514
515 void KRB5_CALLCONV
516 krb5_init_creds_free(krb5_context context,
517                      krb5_init_creds_context ctx)
518 {
519     if (ctx == NULL)
520         return;
521
522     if (ctx->opte != NULL && krb5_gic_opt_is_shadowed(ctx->opte)) {
523         krb5_get_init_creds_opt_free(context,
524                                      (krb5_get_init_creds_opt *)ctx->opte);
525     }
526     free(ctx->in_tkt_service);
527     zap(ctx->password.data, ctx->password.length);
528     krb5_free_data_contents(context, &ctx->password);
529     krb5_free_error(context, ctx->err_reply);
530     krb5_free_pa_data(context, ctx->err_padata);
531     krb5_free_cred_contents(context, &ctx->cred);
532     krb5_free_kdc_req(context, ctx->request);
533     krb5_free_kdc_rep(context, ctx->reply);
534     krb5_free_data(context, ctx->outer_request_body);
535     krb5_free_data(context, ctx->inner_request_body);
536     krb5_free_data(context, ctx->encoded_previous_request);
537     krb5int_fast_free_state(context, ctx->fast_state);
538     krb5_free_pa_data(context, ctx->preauth_to_use);
539     krb5_free_data_contents(context, &ctx->salt);
540     krb5_free_data_contents(context, &ctx->s2kparams);
541     krb5_free_keyblock_contents(context, &ctx->as_key);
542     free(ctx);
543 }
544
545 krb5_error_code
546 k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
547                   int *use_master)
548 {
549     krb5_error_code code;
550     krb5_data request;
551     krb5_data reply;
552     krb5_data realm;
553     unsigned int flags = 0;
554     int tcp_only = 0;
555
556     request.length = 0;
557     request.data = NULL;
558     reply.length = 0;
559     reply.data = NULL;
560     realm.length = 0;
561     realm.data = NULL;
562
563     for (;;) {
564         code = krb5_init_creds_step(context,
565                                     ctx,
566                                     &reply,
567                                     &request,
568                                     &realm,
569                                     &flags);
570         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) {
571             TRACE_INIT_CREDS_RETRY_TCP(context);
572             tcp_only = 1;
573         } else if (code != 0 || !(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
574             break;
575
576         krb5_free_data_contents(context, &reply);
577
578         code = krb5_sendto_kdc(context, &request, &realm,
579                                &reply, use_master, tcp_only);
580         if (code != 0)
581             break;
582
583         krb5_free_data_contents(context, &request);
584         krb5_free_data_contents(context, &realm);
585     }
586
587     krb5_free_data_contents(context, &request);
588     krb5_free_data_contents(context, &reply);
589     krb5_free_data_contents(context, &realm);
590
591     return code;
592 }
593
594 /* Heimdal API */
595 krb5_error_code KRB5_CALLCONV
596 krb5_init_creds_get(krb5_context context,
597                     krb5_init_creds_context ctx)
598 {
599     int use_master = 0;
600
601     return k5_init_creds_get(context, ctx, &use_master);
602 }
603
604 krb5_error_code KRB5_CALLCONV
605 krb5_init_creds_get_creds(krb5_context context,
606                           krb5_init_creds_context ctx,
607                           krb5_creds *creds)
608 {
609     if (!ctx->complete)
610         return KRB5_NO_TKT_SUPPLIED;
611
612     return krb5int_copy_creds_contents(context, &ctx->cred, creds);
613 }
614
615 krb5_error_code KRB5_CALLCONV
616 krb5_init_creds_get_times(krb5_context context,
617                           krb5_init_creds_context ctx,
618                           krb5_ticket_times *times)
619 {
620     if (!ctx->complete)
621         return KRB5_NO_TKT_SUPPLIED;
622
623     *times = ctx->cred.times;
624
625     return 0;
626 }
627
628 krb5_error_code KRB5_CALLCONV
629 krb5_init_creds_get_error(krb5_context context,
630                           krb5_init_creds_context ctx,
631                           krb5_error **error)
632 {
633     krb5_error_code code;
634     krb5_error *ret = NULL;
635
636     *error = NULL;
637
638     if (ctx->err_reply == NULL)
639         return 0;
640
641     ret = k5alloc(sizeof(*ret), &code);
642     if (code != 0)
643         goto cleanup;
644
645     ret->magic = KV5M_ERROR;
646     ret->ctime = ctx->err_reply->ctime;
647     ret->cusec = ctx->err_reply->cusec;
648     ret->susec = ctx->err_reply->susec;
649     ret->stime = ctx->err_reply->stime;
650     ret->error = ctx->err_reply->error;
651
652     if (ctx->err_reply->client != NULL) {
653         code = krb5_copy_principal(context, ctx->err_reply->client,
654                                    &ret->client);
655         if (code != 0)
656             goto cleanup;
657     }
658
659     code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
660     if (code != 0)
661         goto cleanup;
662
663     code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
664                                       &ret->text);
665     if (code != 0)
666         goto cleanup;
667
668     code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
669                                       &ret->e_data);
670     if (code != 0)
671         goto cleanup;
672
673     *error = ret;
674
675 cleanup:
676     if (code != 0)
677         krb5_free_error(context, ret);
678
679     return code;
680 }
681
682 /**
683  * Throw away any state related to specific realm either at the beginning of a
684  * request, or when a realm changes, or when we start to use FAST after
685  * assuming we would not do so.
686  *
687  * @param padata padata from an error if an error from the realm we now expect
688  * to talk to caused the restart.  Used to infer negotiation characteristics
689  * such as whether FAST is used.
690  */
691 static krb5_error_code
692 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
693                         krb5_pa_data **padata)
694 {
695     krb5_error_code code = 0;
696     unsigned char random_buf[4];
697     krb5_data random_data;
698     if (ctx->preauth_to_use) {
699         krb5_free_pa_data(context, ctx->preauth_to_use);
700         ctx->preauth_to_use = NULL;
701     }
702
703     if (ctx->fast_state) {
704         krb5int_fast_free_state(context, ctx->fast_state);
705         ctx->fast_state = NULL;
706     }
707     code = krb5int_fast_make_state(context, &ctx->fast_state);
708     if (code != 0)
709         goto cleanup;
710     ctx->preauth_rock.fast_state = ctx->fast_state;
711     krb5_preauth_request_context_init(context);
712     if (ctx->outer_request_body) {
713         krb5_free_data(context, ctx->outer_request_body);
714         ctx->outer_request_body = NULL;
715     }
716     if (ctx->opte &&
717         (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
718         if ((code = make_preauth_list(context, ctx->opte->preauth_list,
719                                       ctx->opte->preauth_list_length,
720                                       &ctx->preauth_to_use)))
721             goto cleanup;
722     }
723
724     /* Set the request nonce. */
725     random_data.length = 4;
726     random_data.data = (char *)random_buf;
727     code = krb5_c_random_make_octets(context, &random_data);
728     if (code !=0)
729         goto cleanup;
730     /*
731      * See RT ticket 3196 at MIT.  If we set the high bit, we may have
732      * compatibility problems with Heimdal, because we (incorrectly) encode
733      * this value as signed.
734      */
735     ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
736     krb5_free_principal(context, ctx->request->server);
737     ctx->request->server = NULL;
738
739     code = build_in_tkt_name(context, ctx->in_tkt_service,
740                              ctx->request->client,
741                              &ctx->request->server);
742     if (code != 0)
743         goto cleanup;
744
745     ctx->request_time = time(NULL);
746
747     code = krb5int_fast_as_armor(context, ctx->fast_state,
748                                  ctx->opte, ctx->request);
749     if (code != 0)
750         goto cleanup;
751     if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
752         code = krb5int_fast_as_armor(context, ctx->fast_state,
753                                      ctx->opte, ctx->request);
754         if (code != 0)
755             goto cleanup;
756     }
757     /* give the preauth plugins a chance to prep the request body */
758     krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
759
760     ctx->request->from = krb5int_addint32(ctx->request_time,
761                                           ctx->start_time);
762     ctx->request->till = krb5int_addint32(ctx->request->from,
763                                           ctx->tkt_life);
764
765     if (ctx->renew_life > 0) {
766         ctx->request->rtime =
767             krb5int_addint32(ctx->request->from, ctx->renew_life);
768         if (ctx->request->rtime < ctx->request->till) {
769             /* don't ask for a smaller renewable time than the lifetime */
770             ctx->request->rtime = ctx->request->till;
771         }
772         ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
773     } else
774         ctx->request->rtime = 0;
775     code = krb5int_fast_prep_req_body(context, ctx->fast_state,
776                                       ctx->request,
777                                       &ctx->outer_request_body);
778     if (code != 0)
779         goto cleanup;
780 cleanup:
781     return code;
782 }
783
784 krb5_error_code KRB5_CALLCONV
785 krb5_init_creds_init(krb5_context context,
786                      krb5_principal client,
787                      krb5_prompter_fct prompter,
788                      void *data,
789                      krb5_deltat start_time,
790                      krb5_get_init_creds_opt *options,
791                      krb5_init_creds_context *pctx)
792 {
793     krb5_error_code code;
794     krb5_init_creds_context ctx;
795     int tmp;
796     char *str = NULL;
797     krb5_gic_opt_ext *opte;
798     krb5_get_init_creds_opt local_opts;
799
800     TRACE_INIT_CREDS(context, client);
801
802     ctx = k5alloc(sizeof(*ctx), &code);
803     if (code != 0)
804         goto cleanup;
805
806     ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
807     if (code != 0)
808         goto cleanup;
809     ctx->enc_pa_rep_permitted = 1;
810     code = krb5_copy_principal(context, client, &ctx->request->client);
811     if (code != 0)
812         goto cleanup;
813
814     ctx->prompter = prompter;
815     ctx->prompter_data = data;
816     ctx->gak_fct = krb5_get_as_key_password;
817     ctx->gak_data = &ctx->password;
818
819     ctx->request_time = 0; /* filled in later */
820     ctx->start_time = start_time;
821
822     if (options == NULL) {
823         /*
824          * We initialize a non-extended options because that way the shadowed
825          * flag will be sent and they will be freed when the init_creds context
826          * is freed. The options will be extended and copied off the stack into
827          * storage by opt_to_opte.
828          */
829         krb5_get_init_creds_opt_init(&local_opts);
830         options = &local_opts;
831     }
832
833     code = krb5int_gic_opt_to_opte(context, options,
834                                    &ctx->opte, 1, "krb5_init_creds_init");
835     if (code != 0)
836         goto cleanup;
837
838     opte = ctx->opte;
839
840     ctx->preauth_rock.magic = CLIENT_ROCK_MAGIC;
841     ctx->preauth_rock.etype = &ctx->etype;
842     ctx->preauth_rock.as_key = &ctx->as_key;
843     ctx->preauth_rock.gak_fct = &ctx->gak_fct;
844     ctx->preauth_rock.gak_data = &ctx->gak_data;
845     ctx->preauth_rock.salt = &ctx->salt;
846     ctx->preauth_rock.s2kparams = &ctx->s2kparams;
847     ctx->preauth_rock.client = client;
848     ctx->preauth_rock.prompter = prompter;
849     ctx->preauth_rock.prompter_data = data;
850
851     /* Initialise request parameters as per krb5_get_init_creds() */
852     ctx->request->kdc_options = context->kdc_default_options;
853
854     /* forwaradble */
855     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
856         tmp = opte->forwardable;
857     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
858                                         KRB5_CONF_FORWARDABLE, &tmp) == 0)
859         ;
860     else
861         tmp = 0;
862     if (tmp)
863         ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
864
865     /* proxiable */
866     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
867         tmp = opte->proxiable;
868     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
869                                         KRB5_CONF_PROXIABLE, &tmp) == 0)
870         ;
871     else
872         tmp = 0;
873     if (tmp)
874         ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
875
876     /* canonicalize */
877     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
878         tmp = 1;
879     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
880                                         KRB5_CONF_CANONICALIZE, &tmp) == 0)
881         ;
882     else
883         tmp = 0;
884     if (tmp)
885         ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
886
887     /* allow_postdate */
888     if (ctx->start_time > 0)
889         ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
890
891     /* ticket lifetime */
892     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
893         ctx->tkt_life = options->tkt_life;
894     else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
895                                        KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
896         code = krb5_string_to_deltat(str, &ctx->tkt_life);
897         if (code != 0)
898             goto cleanup;
899         free(str);
900         str = NULL;
901     } else
902         ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
903
904     /* renewable lifetime */
905     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
906         ctx->renew_life = options->renew_life;
907     else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
908                                        KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
909         code = krb5_string_to_deltat(str, &ctx->renew_life);
910         if (code != 0)
911             goto cleanup;
912         free(str);
913         str = NULL;
914     } else
915         ctx->renew_life = 0;
916
917     if (ctx->renew_life > 0)
918         ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
919
920     /* enctypes */
921     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
922         ctx->request->ktype =
923             k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
924                     &code);
925         if (code != 0)
926             goto cleanup;
927         ctx->request->nktypes = opte->etype_list_length;
928         memcpy(ctx->request->ktype, opte->etype_list,
929                ctx->request->nktypes * sizeof(krb5_enctype));
930     } else if (krb5_get_default_in_tkt_ktypes(context,
931                                               &ctx->request->ktype) == 0) {
932         ctx->request->nktypes = krb5int_count_etypes(ctx->request->ktype);
933     } else {
934         /* there isn't any useful default here. */
935         code = KRB5_CONFIG_ETYPE_NOSUPP;
936         goto cleanup;
937     }
938
939     /*
940      * Set a default enctype for optimistic preauth.  If we're not doing
941      * optimistic preauth, this should ordinarily get overwritten when we
942      * process the etype-info2 of the preauth-required error.
943      */
944     if (ctx->request->nktypes > 0)
945         ctx->etype = ctx->request->ktype[0];
946
947     /* addresess */
948     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
949         code = krb5_copy_addresses(context, opte->address_list,
950                                    &ctx->request->addresses);
951         if (code != 0)
952             goto cleanup;
953     } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
954                                           KRB5_CONF_NOADDRESSES, &tmp) != 0
955                || tmp) {
956         ctx->request->addresses = NULL;
957     } else {
958         code = krb5_os_localaddr(context, &ctx->request->addresses);
959         if (code != 0)
960             goto cleanup;
961     }
962
963     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
964         code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
965         if (code != 0)
966             goto cleanup;
967     } else {
968         ctx->salt.length = SALT_TYPE_AFS_LENGTH;
969         ctx->salt.data = NULL;
970     }
971
972     /* Anonymous. */
973     if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
974         ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
975         /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
976         if (client->length == 1 && client->data[0].length ==0) {
977             krb5_principal new_client;
978             code = krb5_build_principal_ext(context, &new_client,
979                                             client->realm.length,
980                                             client->realm.data,
981                                             strlen(KRB5_WELLKNOWN_NAMESTR),
982                                             KRB5_WELLKNOWN_NAMESTR,
983                                             strlen(KRB5_ANONYMOUS_PRINCSTR),
984                                             KRB5_ANONYMOUS_PRINCSTR,
985                                             0);
986             if (code)
987                 goto cleanup;
988             krb5_free_principal(context, ctx->request->client);
989             ctx->request->client = new_client;
990             krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
991         }
992     }
993     /* We will also handle anonymous if the input principal is the anonymous
994      * principal. */
995     if (krb5_principal_compare_any_realm(context, ctx->request->client,
996                                          krb5_anonymous_principal())) {
997         ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
998         krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
999     }
1000     code = restart_init_creds_loop(context, ctx, NULL);
1001     if (code)
1002         goto cleanup;
1003
1004     *pctx = ctx;
1005     ctx = NULL;
1006
1007 cleanup:
1008     krb5_init_creds_free(context, ctx);
1009     free(str);
1010
1011     return code;
1012 }
1013
1014 krb5_error_code KRB5_CALLCONV
1015 krb5_init_creds_set_service(krb5_context context,
1016                             krb5_init_creds_context ctx,
1017                             const char *service)
1018 {
1019     char *s;
1020
1021     TRACE_INIT_CREDS_SERVICE(context, service);
1022
1023     s = strdup(service);
1024     if (s == NULL)
1025         return ENOMEM;
1026
1027     free(ctx->in_tkt_service);
1028     ctx->in_tkt_service = s;
1029
1030     krb5_preauth_request_context_fini(context);
1031     return restart_init_creds_loop(context, ctx, NULL);
1032 }
1033
1034 static krb5_error_code
1035 init_creds_validate_reply(krb5_context context,
1036                           krb5_init_creds_context ctx,
1037                           krb5_data *reply)
1038 {
1039     krb5_error_code code;
1040     krb5_error *error = NULL;
1041     krb5_kdc_rep *as_reply = NULL;
1042
1043     krb5_free_error(context, ctx->err_reply);
1044     ctx->err_reply = NULL;
1045
1046     krb5_free_kdc_rep(context, ctx->reply);
1047     ctx->reply = NULL;
1048
1049     if (krb5_is_krb_error(reply)) {
1050         code = decode_krb5_error(reply, &error);
1051         if (code != 0)
1052             return code;
1053
1054         assert(error != NULL);
1055
1056         TRACE_INIT_CREDS_ERROR_REPLY(context,
1057                                      error->error + ERROR_TABLE_BASE_krb5);
1058         if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1059             krb5_free_error(context, error);
1060             return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1061         } else {
1062             ctx->err_reply = error;
1063             return 0;
1064         }
1065     }
1066
1067     /*
1068      * Check to make sure it isn't a V4 reply.
1069      */
1070     if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1071 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1072 #define V4_KRB_PROT_VERSION     4
1073 #define V4_AUTH_MSG_ERR_REPLY   (5<<1)
1074         /* check here for V4 reply */
1075         unsigned int t_switch;
1076
1077         /* From v4 g_in_tkt.c: This used to be
1078            switch (pkt_msg_type(rpkt) & ~1) {
1079            but SCO 3.2v4 cc compiled that incorrectly.  */
1080         t_switch = reply->data[1];
1081         t_switch &= ~1;
1082
1083         if (t_switch == V4_AUTH_MSG_ERR_REPLY
1084             && reply->data[0] == V4_KRB_PROT_VERSION) {
1085             code = KRB5KRB_AP_ERR_V4_REPLY;
1086         } else {
1087             code = KRB5KRB_AP_ERR_MSG_TYPE;
1088         }
1089         return code;
1090     }
1091
1092     /* It must be a KRB_AS_REP message, or an bad returned packet */
1093     code = decode_krb5_as_rep(reply, &as_reply);
1094     if (code != 0)
1095         return code;
1096
1097     if (as_reply->msg_type != KRB5_AS_REP) {
1098         krb5_free_kdc_rep(context, as_reply);
1099         return KRB5KRB_AP_ERR_MSG_TYPE;
1100     }
1101
1102     ctx->reply = as_reply;
1103
1104     return 0;
1105 }
1106
1107 static krb5_error_code
1108 init_creds_step_request(krb5_context context,
1109                         krb5_init_creds_context ctx,
1110                         krb5_data *out)
1111 {
1112     krb5_error_code code;
1113     krb5_boolean got_real;
1114     char random_buf[4];
1115     krb5_data random_data;
1116
1117     if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1118         code = KRB5_GET_IN_TKT_LOOP;
1119         goto cleanup;
1120     }
1121     /*
1122      * RFC 6113 requires a new nonce for the inner request on each try. It's
1123      * permitted to change the nonce even for non-FAST so we do here.
1124      */
1125     random_data.length = 4;
1126     random_data.data = (char *)random_buf;
1127     code = krb5_c_random_make_octets(context, &random_data);
1128     if (code !=0)
1129         goto cleanup;
1130     /*
1131      * See RT ticket 3196 at MIT.  If we set the high bit, we may have
1132      * compatibility problems with Heimdal, because we (incorrectly) encode
1133      * this value as signed.
1134      */
1135     ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
1136     krb5_free_data(context, ctx->inner_request_body);
1137     ctx->inner_request_body = NULL;
1138     code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
1139     if (code)
1140         goto cleanup;
1141
1142     if (ctx->err_reply == NULL) {
1143         /* either our first attempt, or retrying after PREAUTH_NEEDED */
1144         code = krb5_do_preauth(context,
1145                                ctx->request,
1146                                ctx->inner_request_body,
1147                                ctx->encoded_previous_request,
1148                                ctx->preauth_to_use,
1149                                &ctx->request->padata,
1150                                ctx->prompter,
1151                                ctx->prompter_data,
1152                                &ctx->preauth_rock,
1153                                ctx->opte,
1154                                &got_real);
1155         if (code == 0 && !got_real && ctx->preauth_required)
1156             code = KRB5_PREAUTH_FAILED;
1157         if (code != 0)
1158             goto cleanup;
1159     } else {
1160         if (ctx->preauth_to_use != NULL) {
1161             /*
1162              * Retry after an error other than PREAUTH_NEEDED,
1163              * using ctx->err_padata to figure out what to change.
1164              */
1165             code = krb5_do_preauth_tryagain(context,
1166                                             ctx->request,
1167                                             ctx->inner_request_body,
1168                                             ctx->encoded_previous_request,
1169                                             ctx->preauth_to_use,
1170                                             &ctx->request->padata,
1171                                             ctx->err_reply,
1172                                             ctx->err_padata,
1173                                             ctx->prompter,
1174                                             ctx->prompter_data,
1175                                             &ctx->preauth_rock,
1176                                             ctx->opte);
1177         } else {
1178             /* No preauth supplied, so can't query the plugins. */
1179             code = KRB5KRB_ERR_GENERIC;
1180         }
1181         if (code != 0) {
1182             /* couldn't come up with anything better */
1183             code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1184             goto cleanup;
1185         }
1186     }
1187
1188     if (ctx->encoded_previous_request != NULL) {
1189         krb5_free_data(context, ctx->encoded_previous_request);
1190         ctx->encoded_previous_request = NULL;
1191     }
1192     if (ctx->request->padata)
1193         ctx->sent_nontrivial_preauth = 1;
1194     if (ctx->enc_pa_rep_permitted)
1195         code = request_enc_pa_rep(&ctx->request->padata);
1196     if (code)
1197         goto cleanup;
1198     code = krb5int_fast_prep_req(context, ctx->fast_state,
1199                                  ctx->request, ctx->outer_request_body,
1200                                  encode_krb5_as_req,
1201                                  &ctx->encoded_previous_request);
1202     if (code != 0)
1203         goto cleanup;
1204
1205     code = krb5int_copy_data_contents(context,
1206                                       ctx->encoded_previous_request,
1207                                       out);
1208     if (code != 0)
1209         goto cleanup;
1210
1211 cleanup:
1212     krb5_free_pa_data(context, ctx->request->padata);
1213     ctx->request->padata = NULL;
1214     return code;
1215 }
1216
1217 /*
1218  * The control flow is complicated.  In order to switch from non-FAST mode to
1219  * FAST mode, we need to reset our pre-authentication state.  FAST negotiation
1220  * attempts to make sure we rarely have to do this.  When FAST negotiation is
1221  * working, we record whether FAST is available when we obtain an armor ticket;
1222  * if so, we start out with FAST enabled .  There are two complicated
1223  * situations.
1224  *
1225  * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
1226  * a KDC in a case where we were not expecting to use FAST, and we have an
1227  * armor ticket available, then we want to use FAST.  That involves clearing
1228  * out the pre-auth state, reinitializing the plugins and trying again with an
1229  * armor key.
1230  *
1231  * Secondly, using the negotiation can cause problems with some older KDCs.
1232  * Negotiation involves including a special padata item.  Some KDCs, including
1233  * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
1234  * pre-authentication is required and unknown padata are included in the
1235  * request.  To make matters worse, these KDCs typically do not include a list
1236  * of padata in PREAUTH_FAILED errors.  So, if we get PREAUTH_FAILED and we
1237  * generated no pre-authentication other than the negotiation then we want to
1238  * retry without negotiation.  In this case it is probably also desirable to
1239  * retry with the preauth plugin state cleared.
1240  *
1241  * In all these cases we should not start over more than once.  Control flow is
1242  * managed by several variables.
1243  *
1244  *   sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
1245  *   no restart on PREAUTH_FAILED
1246  *
1247  *   KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
1248  *   armor; if not set, then we can't use FAST even if the KDC wants to.
1249  *
1250  *   have_restarted: true if we've already restarted
1251  */
1252 static krb5_boolean
1253 negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
1254                              krb5_pa_data **padata)
1255 {
1256     if (ctx->have_restarted)
1257         return FALSE;
1258     if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
1259         TRACE_INIT_CREDS_RESTART_FAST(context);
1260         return TRUE;
1261     }
1262     if (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
1263         !ctx->sent_nontrivial_preauth) {
1264         TRACE_INIT_CREDS_RESTART_PREAUTH_FAILED(context);
1265         return TRUE;
1266     }
1267     return FALSE;
1268 }
1269
1270 /* Ensure that the reply enctype was among the requested enctypes. */
1271 static krb5_error_code
1272 check_reply_enctype(krb5_init_creds_context ctx)
1273 {
1274     int i;
1275
1276     for (i = 0; i < ctx->request->nktypes; i++) {
1277         if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1278             return 0;
1279     }
1280     return KRB5_CONFIG_ETYPE_NOSUPP;
1281 }
1282
1283 /* Note the difference between the KDC's time, as reported to us in a
1284  * preauth-required error, and the current time. */
1285 static void
1286 note_req_timestamp(krb5_context kcontext, krb5_clpreauth_rock rock,
1287                    krb5_timestamp kdc_time, krb5_int32 kdc_usec)
1288 {
1289     krb5_timestamp now;
1290     krb5_int32 usec;
1291
1292     if (k5_time_with_offset(0, 0, &now, &usec) != 0)
1293         return;
1294     rock->pa_offset = kdc_time - now;
1295     rock->pa_offset_usec = kdc_usec - usec;
1296     rock->pa_offset_state = (rock->fast_state->armor_key != NULL) ?
1297         AUTH_OFFSET : UNAUTH_OFFSET;
1298 }
1299
1300 static krb5_error_code
1301 init_creds_step_reply(krb5_context context,
1302                       krb5_init_creds_context ctx,
1303                       krb5_data *in)
1304 {
1305     krb5_error_code code;
1306     krb5_pa_data **kdc_padata = NULL;
1307     krb5_boolean retry = FALSE;
1308     int canon_flag = 0;
1309     krb5_keyblock *strengthen_key = NULL;
1310     krb5_keyblock encrypting_key;
1311     krb5_boolean fast_avail, got_real;
1312
1313     encrypting_key.length = 0;
1314     encrypting_key.contents = NULL;
1315
1316     /* process previous KDC response */
1317     code = init_creds_validate_reply(context, ctx, in);
1318     if (code != 0)
1319         goto cleanup;
1320
1321     /* per referrals draft, enterprise principals imply canonicalization */
1322     canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1323         ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1324
1325     if (ctx->err_reply != NULL) {
1326         code = krb5int_fast_process_error(context, ctx->fast_state,
1327                                           &ctx->err_reply, &ctx->err_padata,
1328                                           &retry);
1329         if (code != 0)
1330             goto cleanup;
1331         if (negotiation_requests_restart(context, ctx, ctx->err_padata)) {
1332             ctx->have_restarted = 1;
1333             krb5_preauth_request_context_fini(context);
1334             if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
1335                 ctx->enc_pa_rep_permitted = 0;
1336             code = restart_init_creds_loop(context, ctx, ctx->err_padata);
1337             krb5_free_error(context, ctx->err_reply);
1338             ctx->err_reply = NULL;
1339             krb5_free_pa_data(context, ctx->err_padata);
1340             ctx->err_padata = NULL;
1341         } else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
1342                    retry) {
1343             /* reset the list of preauth types to try */
1344             krb5_free_pa_data(context, ctx->preauth_to_use);
1345             ctx->preauth_to_use = ctx->err_padata;
1346             ctx->err_padata = NULL;
1347             note_req_timestamp(context, &ctx->preauth_rock,
1348                                ctx->err_reply->stime, ctx->err_reply->susec);
1349             /* this will trigger a new call to krb5_do_preauth() */
1350             krb5_free_error(context, ctx->err_reply);
1351             ctx->err_reply = NULL;
1352             code = sort_krb5_padata_sequence(context,
1353                                              &ctx->request->client->realm,
1354                                              ctx->preauth_to_use);
1355             ctx->preauth_required = TRUE;
1356
1357         } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) {
1358             if (ctx->err_reply->client == NULL ||
1359                 !krb5_princ_realm(context, ctx->err_reply->client)->length) {
1360                 code = KRB5KDC_ERR_WRONG_REALM;
1361                 goto cleanup;
1362             }
1363             TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
1364             /* Rewrite request.client with realm from error reply */
1365             krb5_free_data_contents(context, &ctx->request->client->realm);
1366             code = krb5int_copy_data_contents(context,
1367                                               &ctx->err_reply->client->realm,
1368                                               &ctx->request->client->realm);
1369             /* this will trigger a new call to krb5_do_preauth() */
1370             krb5_free_error(context, ctx->err_reply);
1371             ctx->err_reply = NULL;
1372             krb5_preauth_request_context_fini(context);
1373             /* Permit another negotiation based restart. */
1374             ctx->have_restarted = 0;
1375             ctx->sent_nontrivial_preauth = 0;
1376             code = restart_init_creds_loop(context, ctx, NULL);
1377             if (code != 0)
1378                 goto cleanup;
1379         } else {
1380             if (retry) {
1381                 code = 0;
1382             } else {
1383                 /* error + no hints = give up */
1384                 code = (krb5_error_code)ctx->err_reply->error +
1385                     ERROR_TABLE_BASE_krb5;
1386             }
1387         }
1388
1389         /* Return error code, or continue with next iteration */
1390         goto cleanup;
1391     }
1392
1393     /* We have a response. Process it. */
1394     assert(ctx->reply != NULL);
1395
1396     /* Check for replies (likely forged) with unasked-for enctypes. */
1397     code = check_reply_enctype(ctx);
1398     if (code != 0)
1399         goto cleanup;
1400
1401     /* process any preauth data in the as_reply */
1402     krb5_clear_preauth_context_use_counts(context);
1403     code = krb5int_fast_process_response(context, ctx->fast_state,
1404                                          ctx->reply, &strengthen_key);
1405     if (code != 0)
1406         goto cleanup;
1407
1408     code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1409                                      ctx->reply->padata);
1410     if (code != 0)
1411         goto cleanup;
1412
1413     ctx->etype = ctx->reply->enc_part.enctype;
1414
1415     code = krb5_do_preauth(context,
1416                            ctx->request,
1417                            ctx->inner_request_body,
1418                            ctx->encoded_previous_request,
1419                            ctx->reply->padata,
1420                            &kdc_padata,
1421                            ctx->prompter,
1422                            ctx->prompter_data,
1423                            &ctx->preauth_rock,
1424                            ctx->opte,
1425                            &got_real);
1426     if (code != 0)
1427         goto cleanup;
1428
1429     /*
1430      * If we haven't gotten a salt from another source yet, set up one
1431      * corresponding to the client principal returned by the KDC.  We
1432      * could get the same effect by passing local_as_reply->client to
1433      * gak_fct below, but that would put the canonicalized client name
1434      * in the prompt, which raises issues of needing to sanitize
1435      * unprintable characters.  So for now we just let it affect the
1436      * salt.  local_as_reply->client will be checked later on in
1437      * verify_as_reply.
1438      */
1439     if (ctx->salt.length == SALT_TYPE_AFS_LENGTH && ctx->salt.data == NULL) {
1440         code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1441         TRACE_INIT_CREDS_SALT_PRINC(context, &ctx->salt);
1442         if (code != 0)
1443             goto cleanup;
1444     }
1445
1446     /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
1447        the AS_REP comes back encrypted in the user's longterm key
1448        instead of in the SAD. If there was a SAM preauth, there
1449        will be an as_key here which will be the SAD. If that fails,
1450        use the gak_fct to get the password, and try again. */
1451
1452     /* XXX because etypes are handled poorly (particularly wrt SAM,
1453        where the etype is fixed by the kdc), we may want to try
1454        decrypt_as_reply twice.  If there's an as_key available, try
1455        it.  If decrypting the as_rep fails, or if there isn't an
1456        as_key at all yet, then use the gak_fct to get one, and try
1457        again.  */
1458     if (ctx->as_key.length) {
1459         TRACE_INIT_CREDS_AS_KEY_PREAUTH(context, &ctx->as_key);
1460         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1461                                       &encrypting_key);
1462         if (code != 0)
1463             goto cleanup;
1464         code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1465         if (code != 0)
1466             TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(context, code);
1467     } else
1468         code = -1;
1469
1470     if (code != 0) {
1471         /* if we haven't get gotten a key, get it now */
1472         TRACE_INIT_CREDS_GAK(context, &ctx->salt, &ctx->s2kparams);
1473         code = (*ctx->gak_fct)(context, ctx->request->client,
1474                                ctx->reply->enc_part.enctype,
1475                                ctx->prompter, ctx->prompter_data,
1476                                &ctx->salt, &ctx->s2kparams,
1477                                &ctx->as_key, ctx->gak_data);
1478         if (code != 0)
1479             goto cleanup;
1480         TRACE_INIT_CREDS_AS_KEY_GAK(context, &ctx->as_key);
1481
1482         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1483                                       &encrypting_key);
1484         if (code != 0)
1485             goto cleanup;
1486
1487         code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1488         if (code != 0)
1489             goto cleanup;
1490     }
1491
1492     TRACE_INIT_CREDS_DECRYPTED_REPLY(context, ctx->reply->enc_part2->session);
1493
1494     code = krb5int_fast_verify_nego(context, ctx->fast_state,
1495                                     ctx->reply, ctx->encoded_previous_request,
1496                                     &encrypting_key, &fast_avail);
1497     if (code)
1498         goto cleanup;
1499     code = verify_as_reply(context, ctx->request_time,
1500                            ctx->request, ctx->reply);
1501     if (code != 0)
1502         goto cleanup;
1503     code = verify_anonymous(context, ctx->request, ctx->reply,
1504                             &ctx->as_key);
1505     if (code)
1506         goto cleanup;
1507
1508     code = stash_as_reply(context, ctx->request_time, ctx->request,
1509                           ctx->reply, &ctx->cred, NULL);
1510     if (code != 0)
1511         goto cleanup;
1512     if (ctx->opte && ctx->opte->opt_private->out_ccache) {
1513         krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
1514         krb5_data config_data;
1515         code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
1516         if (code != 0)
1517             goto cc_cleanup;
1518         code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
1519         if (code != 0)
1520             goto cc_cleanup;
1521         if (fast_avail) {
1522             config_data.data = "yes";
1523             config_data.length = strlen(config_data.data);
1524             code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
1525                                       KRB5_CONF_FAST_AVAIL, &config_data);
1526         }
1527     cc_cleanup:
1528         if (code !=0) {
1529             const char *msg;
1530             msg = krb5_get_error_message(context, code);
1531             krb5_set_error_message(context, code,
1532                                    _("%s while storing credentials"), msg);
1533             krb5_free_error_message(context, msg);
1534         }
1535     }
1536
1537     krb5_preauth_request_context_fini(context);
1538
1539     /* success */
1540     code = 0;
1541     ctx->complete = TRUE;
1542
1543 cleanup:
1544     krb5_free_pa_data(context, kdc_padata);
1545     krb5_free_keyblock(context, strengthen_key);
1546     krb5_free_keyblock_contents(context, &encrypting_key);
1547
1548     return code;
1549 }
1550
1551 /*
1552  * Do next step of credentials acquisition.
1553  *
1554  * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1555  * should be sent with TCP.
1556  */
1557 krb5_error_code KRB5_CALLCONV
1558 krb5_init_creds_step(krb5_context context,
1559                      krb5_init_creds_context ctx,
1560                      krb5_data *in,
1561                      krb5_data *out,
1562                      krb5_data *realm,
1563                      unsigned int *flags)
1564 {
1565     krb5_error_code code = 0, code2;
1566
1567     *flags = 0;
1568
1569     out->data = NULL;
1570     out->length = 0;
1571
1572     realm->data = NULL;
1573     realm->length = 0;
1574
1575     if (ctx->complete)
1576         return EINVAL;
1577
1578     if (in->length != 0) {
1579         code = init_creds_step_reply(context, ctx, in);
1580         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1581             code2 = krb5int_copy_data_contents(context,
1582                                                ctx->encoded_previous_request,
1583                                                out);
1584             if (code2 != 0) {
1585                 code = code2;
1586                 goto cleanup;
1587             }
1588             goto copy_realm;
1589         }
1590         if (code != 0 || ctx->complete)
1591             goto cleanup;
1592     }
1593
1594     code = init_creds_step_request(context, ctx, out);
1595     if (code != 0)
1596         goto cleanup;
1597
1598     /* Only a new request increments the loop count, not a TCP retry */
1599     ctx->loopcount++;
1600
1601 copy_realm:
1602     assert(ctx->request->server != NULL);
1603
1604     code2 = krb5int_copy_data_contents(context,
1605                                        &ctx->request->server->realm,
1606                                        realm);
1607     if (code2 != 0) {
1608         code = code2;
1609         goto cleanup;
1610     }
1611
1612 cleanup:
1613     if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
1614         char *client_name;
1615
1616         /* See if we can produce a more detailed error message */
1617         code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
1618         if (code2 == 0) {
1619             krb5_set_error_message(context, code,
1620                                    _("Client '%s' not found in Kerberos "
1621                                      "database"), client_name);
1622             krb5_free_unparsed_name(context, client_name);
1623         }
1624     }
1625
1626     *flags = ctx->complete ? 0 : KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1627     return code;
1628 }
1629
1630 krb5_error_code KRB5_CALLCONV
1631 krb5int_get_init_creds(krb5_context context,
1632                        krb5_creds *creds,
1633                        krb5_principal client,
1634                        krb5_prompter_fct prompter,
1635                        void *prompter_data,
1636                        krb5_deltat start_time,
1637                        char *in_tkt_service,
1638                        krb5_get_init_creds_opt *options,
1639                        krb5_gic_get_as_key_fct gak_fct,
1640                        void *gak_data,
1641                        int  *use_master,
1642                        krb5_kdc_rep **as_reply)
1643 {
1644     krb5_error_code code;
1645     krb5_init_creds_context ctx = NULL;
1646
1647     code = krb5_init_creds_init(context,
1648                                 client,
1649                                 prompter,
1650                                 prompter_data,
1651                                 start_time,
1652                                 options,
1653                                 &ctx);
1654     if (code != 0)
1655         goto cleanup;
1656
1657     ctx->gak_fct = gak_fct;
1658     ctx->gak_data = gak_data;
1659
1660     if (in_tkt_service) {
1661         code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1662         if (code != 0)
1663             goto cleanup;
1664     }
1665
1666     code = k5_init_creds_get(context, ctx, use_master);
1667     if (code != 0)
1668         goto cleanup;
1669
1670     code = krb5_init_creds_get_creds(context, ctx, creds);
1671     if (code != 0)
1672         goto cleanup;
1673
1674     if (as_reply != NULL) {
1675         *as_reply = ctx->reply;
1676         ctx->reply = NULL;
1677     }
1678
1679 cleanup:
1680     krb5_init_creds_free(context, ctx);
1681
1682     return code;
1683 }
1684
1685 krb5_error_code
1686 krb5int_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
1687                          krb5_flags options, krb5_address *const *addrs,
1688                          krb5_enctype *ktypes,
1689                          krb5_preauthtype *pre_auth_types, krb5_creds *creds)
1690 {
1691     int i;
1692     krb5_int32 starttime;
1693     krb5_get_init_creds_opt *opt;
1694     krb5_error_code retval;
1695
1696     *out = NULL;
1697     retval = krb5_get_init_creds_opt_alloc(context, &opt);
1698     if (retval)
1699         return(retval);
1700
1701     if (addrs)
1702         krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
1703     if (ktypes) {
1704         i = krb5int_count_etypes(ktypes);
1705         if (i)
1706             krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
1707     }
1708     if (pre_auth_types) {
1709         for (i=0; pre_auth_types[i]; i++);
1710         if (i)
1711             krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
1712     }
1713     if (options&KDC_OPT_FORWARDABLE)
1714         krb5_get_init_creds_opt_set_forwardable(opt, 1);
1715     else krb5_get_init_creds_opt_set_forwardable(opt, 0);
1716     if (options&KDC_OPT_PROXIABLE)
1717         krb5_get_init_creds_opt_set_proxiable(opt, 1);
1718     else krb5_get_init_creds_opt_set_proxiable(opt, 0);
1719     if (creds && creds->times.endtime) {
1720         retval = krb5_timeofday(context, &starttime);
1721         if (retval)
1722             goto cleanup;
1723         if (creds->times.starttime) starttime = creds->times.starttime;
1724         krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
1725     }
1726     *out = opt;
1727     return 0;
1728
1729 cleanup:
1730     krb5_get_init_creds_opt_free(context, opt);
1731     return retval;
1732 }