aaabc4e2141f4ef5971a332e2caa17c9635f6054
[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 static krb5_error_code
546 init_creds_get(krb5_context context,
547                krb5_init_creds_context ctx,
548                int *use_master)
549 {
550     krb5_error_code code;
551     krb5_data request;
552     krb5_data reply;
553     krb5_data realm;
554     unsigned int flags = 0;
555     int tcp_only = 0;
556
557     request.length = 0;
558     request.data = NULL;
559     reply.length = 0;
560     reply.data = NULL;
561     realm.length = 0;
562     realm.data = NULL;
563
564     for (;;) {
565         code = krb5_init_creds_step(context,
566                                     ctx,
567                                     &reply,
568                                     &request,
569                                     &realm,
570                                     &flags);
571         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) {
572             TRACE_INIT_CREDS_RETRY_TCP(context);
573             tcp_only = 1;
574         } else if (code != 0 || !(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
575             break;
576
577         krb5_free_data_contents(context, &reply);
578
579         code = krb5_sendto_kdc(context, &request, &realm,
580                                &reply, use_master, tcp_only);
581         if (code != 0)
582             break;
583
584         krb5_free_data_contents(context, &request);
585         krb5_free_data_contents(context, &realm);
586     }
587
588     krb5_free_data_contents(context, &request);
589     krb5_free_data_contents(context, &reply);
590     krb5_free_data_contents(context, &realm);
591
592     return code;
593 }
594
595 /* Heimdal API */
596 krb5_error_code KRB5_CALLCONV
597 krb5_init_creds_get(krb5_context context,
598                     krb5_init_creds_context ctx)
599 {
600     int use_master = 0;
601
602     return init_creds_get(context, ctx, &use_master);
603 }
604
605 krb5_error_code KRB5_CALLCONV
606 krb5_init_creds_get_creds(krb5_context context,
607                           krb5_init_creds_context ctx,
608                           krb5_creds *creds)
609 {
610     if (!ctx->complete)
611         return KRB5_NO_TKT_SUPPLIED;
612
613     return krb5int_copy_creds_contents(context, &ctx->cred, creds);
614 }
615
616 krb5_error_code KRB5_CALLCONV
617 krb5_init_creds_get_times(krb5_context context,
618                           krb5_init_creds_context ctx,
619                           krb5_ticket_times *times)
620 {
621     if (!ctx->complete)
622         return KRB5_NO_TKT_SUPPLIED;
623
624     *times = ctx->cred.times;
625
626     return 0;
627 }
628
629 krb5_error_code KRB5_CALLCONV
630 krb5_init_creds_get_error(krb5_context context,
631                           krb5_init_creds_context ctx,
632                           krb5_error **error)
633 {
634     krb5_error_code code;
635     krb5_error *ret = NULL;
636
637     *error = NULL;
638
639     if (ctx->err_reply == NULL)
640         return 0;
641
642     ret = k5alloc(sizeof(*ret), &code);
643     if (code != 0)
644         goto cleanup;
645
646     ret->magic = KV5M_ERROR;
647     ret->ctime = ctx->err_reply->ctime;
648     ret->cusec = ctx->err_reply->cusec;
649     ret->susec = ctx->err_reply->susec;
650     ret->stime = ctx->err_reply->stime;
651     ret->error = ctx->err_reply->error;
652
653     if (ctx->err_reply->client != NULL) {
654         code = krb5_copy_principal(context, ctx->err_reply->client,
655                                    &ret->client);
656         if (code != 0)
657             goto cleanup;
658     }
659
660     code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
661     if (code != 0)
662         goto cleanup;
663
664     code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
665                                       &ret->text);
666     if (code != 0)
667         goto cleanup;
668
669     code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
670                                       &ret->e_data);
671     if (code != 0)
672         goto cleanup;
673
674     *error = ret;
675
676 cleanup:
677     if (code != 0)
678         krb5_free_error(context, ret);
679
680     return code;
681 }
682
683 /**
684  * Throw away any state related to specific realm either at the beginning of a
685  * request, or when a realm changes, or when we start to use FAST after
686  * assuming we would not do so.
687  *
688  * @param padata padata from an error if an error from the realm we now expect
689  * to talk to caused the restart.  Used to infer negotiation characteristics
690  * such as whether FAST is used.
691  */
692 static krb5_error_code
693 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
694                         krb5_pa_data **padata)
695 {
696     krb5_error_code code = 0;
697     unsigned char random_buf[4];
698     krb5_data random_data;
699     if (ctx->preauth_to_use) {
700         krb5_free_pa_data(context, ctx->preauth_to_use);
701         ctx->preauth_to_use = NULL;
702     }
703
704     if (ctx->fast_state) {
705         krb5int_fast_free_state(context, ctx->fast_state);
706         ctx->fast_state = NULL;
707     }
708     code = krb5int_fast_make_state(context, &ctx->fast_state);
709     if (code != 0)
710         goto cleanup;
711     ctx->preauth_rock.fast_state = ctx->fast_state;
712     krb5_preauth_request_context_init(context);
713     if (ctx->outer_request_body) {
714         krb5_free_data(context, ctx->outer_request_body);
715         ctx->outer_request_body = NULL;
716     }
717     if (ctx->opte &&
718         (ctx->opte->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
719         if ((code = make_preauth_list(context, ctx->opte->preauth_list,
720                                       ctx->opte->preauth_list_length,
721                                       &ctx->preauth_to_use)))
722             goto cleanup;
723     }
724
725     /* Set the request nonce. */
726     random_data.length = 4;
727     random_data.data = (char *)random_buf;
728     code = krb5_c_random_make_octets(context, &random_data);
729     if (code !=0)
730         goto cleanup;
731     /*
732      * See RT ticket 3196 at MIT.  If we set the high bit, we may have
733      * compatibility problems with Heimdal, because we (incorrectly) encode
734      * this value as signed.
735      */
736     ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
737     krb5_free_principal(context, ctx->request->server);
738     ctx->request->server = NULL;
739
740     code = build_in_tkt_name(context, ctx->in_tkt_service,
741                              ctx->request->client,
742                              &ctx->request->server);
743     if (code != 0)
744         goto cleanup;
745
746     ctx->request_time = time(NULL);
747
748     code = krb5int_fast_as_armor(context, ctx->fast_state,
749                                  ctx->opte, ctx->request);
750     if (code != 0)
751         goto cleanup;
752     if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
753         code = krb5int_fast_as_armor(context, ctx->fast_state,
754                                      ctx->opte, ctx->request);
755         if (code != 0)
756             goto cleanup;
757     }
758     /* give the preauth plugins a chance to prep the request body */
759     krb5_preauth_prepare_request(context, ctx->opte, ctx->request);
760
761     ctx->request->from = krb5int_addint32(ctx->request_time,
762                                           ctx->start_time);
763     ctx->request->till = krb5int_addint32(ctx->request->from,
764                                           ctx->tkt_life);
765
766     if (ctx->renew_life > 0) {
767         ctx->request->rtime =
768             krb5int_addint32(ctx->request->from, ctx->renew_life);
769         if (ctx->request->rtime < ctx->request->till) {
770             /* don't ask for a smaller renewable time than the lifetime */
771             ctx->request->rtime = ctx->request->till;
772         }
773         ctx->request->kdc_options &= ~(KDC_OPT_RENEWABLE_OK);
774     } else
775         ctx->request->rtime = 0;
776     code = krb5int_fast_prep_req_body(context, ctx->fast_state,
777                                       ctx->request,
778                                       &ctx->outer_request_body);
779     if (code != 0)
780         goto cleanup;
781 cleanup:
782     return code;
783 }
784
785 krb5_error_code KRB5_CALLCONV
786 krb5_init_creds_init(krb5_context context,
787                      krb5_principal client,
788                      krb5_prompter_fct prompter,
789                      void *data,
790                      krb5_deltat start_time,
791                      krb5_get_init_creds_opt *options,
792                      krb5_init_creds_context *pctx)
793 {
794     krb5_error_code code;
795     krb5_init_creds_context ctx;
796     int tmp;
797     char *str = NULL;
798     krb5_gic_opt_ext *opte;
799     krb5_get_init_creds_opt local_opts;
800
801     TRACE_INIT_CREDS(context, client);
802
803     ctx = k5alloc(sizeof(*ctx), &code);
804     if (code != 0)
805         goto cleanup;
806
807     ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
808     if (code != 0)
809         goto cleanup;
810     ctx->enc_pa_rep_permitted = 1;
811     code = krb5_copy_principal(context, client, &ctx->request->client);
812     if (code != 0)
813         goto cleanup;
814
815     ctx->prompter = prompter;
816     ctx->prompter_data = data;
817     ctx->gak_fct = krb5_get_as_key_password;
818     ctx->gak_data = &ctx->password;
819
820     ctx->request_time = 0; /* filled in later */
821     ctx->start_time = start_time;
822
823     if (options == NULL) {
824         /*
825          * We initialize a non-extended options because that way the shadowed
826          * flag will be sent and they will be freed when the init_creds context
827          * is freed. The options will be extended and copied off the stack into
828          * storage by opt_to_opte.
829          */
830         krb5_get_init_creds_opt_init(&local_opts);
831         options = &local_opts;
832     }
833
834     code = krb5int_gic_opt_to_opte(context, options,
835                                    &ctx->opte, 1, "krb5_init_creds_init");
836     if (code != 0)
837         goto cleanup;
838
839     opte = ctx->opte;
840
841     ctx->preauth_rock.magic = CLIENT_ROCK_MAGIC;
842     ctx->preauth_rock.etype = &ctx->etype;
843     ctx->preauth_rock.as_key = &ctx->as_key;
844     ctx->preauth_rock.gak_fct = &ctx->gak_fct;
845     ctx->preauth_rock.gak_data = &ctx->gak_data;
846     ctx->preauth_rock.salt = &ctx->salt;
847     ctx->preauth_rock.s2kparams = &ctx->s2kparams;
848     ctx->preauth_rock.client = client;
849     ctx->preauth_rock.prompter = prompter;
850     ctx->preauth_rock.prompter_data = data;
851
852     /* Initialise request parameters as per krb5_get_init_creds() */
853     ctx->request->kdc_options = context->kdc_default_options;
854
855     /* forwaradble */
856     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
857         tmp = opte->forwardable;
858     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
859                                         KRB5_CONF_FORWARDABLE, &tmp) == 0)
860         ;
861     else
862         tmp = 0;
863     if (tmp)
864         ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
865
866     /* proxiable */
867     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
868         tmp = opte->proxiable;
869     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
870                                         KRB5_CONF_PROXIABLE, &tmp) == 0)
871         ;
872     else
873         tmp = 0;
874     if (tmp)
875         ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
876
877     /* canonicalize */
878     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
879         tmp = 1;
880     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
881                                         KRB5_CONF_CANONICALIZE, &tmp) == 0)
882         ;
883     else
884         tmp = 0;
885     if (tmp)
886         ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
887
888     /* allow_postdate */
889     if (ctx->start_time > 0)
890         ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
891
892     /* ticket lifetime */
893     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
894         ctx->tkt_life = options->tkt_life;
895     else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
896                                        KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
897         code = krb5_string_to_deltat(str, &ctx->tkt_life);
898         if (code != 0)
899             goto cleanup;
900         free(str);
901         str = NULL;
902     } else
903         ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
904
905     /* renewable lifetime */
906     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
907         ctx->renew_life = options->renew_life;
908     else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
909                                        KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
910         code = krb5_string_to_deltat(str, &ctx->renew_life);
911         if (code != 0)
912             goto cleanup;
913         free(str);
914         str = NULL;
915     } else
916         ctx->renew_life = 0;
917
918     if (ctx->renew_life > 0)
919         ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
920
921     /* enctypes */
922     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
923         ctx->request->ktype =
924             k5alloc((opte->etype_list_length * sizeof(krb5_enctype)),
925                     &code);
926         if (code != 0)
927             goto cleanup;
928         ctx->request->nktypes = opte->etype_list_length;
929         memcpy(ctx->request->ktype, opte->etype_list,
930                ctx->request->nktypes * sizeof(krb5_enctype));
931     } else if (krb5_get_default_in_tkt_ktypes(context,
932                                               &ctx->request->ktype) == 0) {
933         ctx->request->nktypes = krb5int_count_etypes(ctx->request->ktype);
934     } else {
935         /* there isn't any useful default here. */
936         code = KRB5_CONFIG_ETYPE_NOSUPP;
937         goto cleanup;
938     }
939
940     /*
941      * Set a default enctype for optimistic preauth.  If we're not doing
942      * optimistic preauth, this should ordinarily get overwritten when we
943      * process the etype-info2 of the preauth-required error.
944      */
945     if (ctx->request->nktypes > 0)
946         ctx->etype = ctx->request->ktype[0];
947
948     /* addresess */
949     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
950         code = krb5_copy_addresses(context, opte->address_list,
951                                    &ctx->request->addresses);
952         if (code != 0)
953             goto cleanup;
954     } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
955                                           KRB5_CONF_NOADDRESSES, &tmp) != 0
956                || tmp) {
957         ctx->request->addresses = NULL;
958     } else {
959         code = krb5_os_localaddr(context, &ctx->request->addresses);
960         if (code != 0)
961             goto cleanup;
962     }
963
964     if (opte->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
965         code = krb5int_copy_data_contents(context, opte->salt, &ctx->salt);
966         if (code != 0)
967             goto cleanup;
968     } else {
969         ctx->salt.length = SALT_TYPE_AFS_LENGTH;
970         ctx->salt.data = NULL;
971     }
972
973     /* Anonymous. */
974     if(opte->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
975         ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
976         /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
977         if (client->length == 1 && client->data[0].length ==0) {
978             krb5_principal new_client;
979             code = krb5_build_principal_ext(context, &new_client,
980                                             client->realm.length,
981                                             client->realm.data,
982                                             strlen(KRB5_WELLKNOWN_NAMESTR),
983                                             KRB5_WELLKNOWN_NAMESTR,
984                                             strlen(KRB5_ANONYMOUS_PRINCSTR),
985                                             KRB5_ANONYMOUS_PRINCSTR,
986                                             0);
987             if (code)
988                 goto cleanup;
989             krb5_free_principal(context, ctx->request->client);
990             ctx->request->client = new_client;
991             krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
992         }
993     }
994     /* We will also handle anonymous if the input principal is the anonymous
995      * principal. */
996     if (krb5_principal_compare_any_realm(context, ctx->request->client,
997                                          krb5_anonymous_principal())) {
998         ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
999         krb5_princ_type(context, ctx->request->client) = KRB5_NT_WELLKNOWN;
1000     }
1001     code = restart_init_creds_loop(context, ctx, NULL);
1002     if (code)
1003         goto cleanup;
1004
1005     *pctx = ctx;
1006     ctx = NULL;
1007
1008 cleanup:
1009     krb5_init_creds_free(context, ctx);
1010     free(str);
1011
1012     return code;
1013 }
1014
1015 krb5_error_code KRB5_CALLCONV
1016 krb5_init_creds_set_service(krb5_context context,
1017                             krb5_init_creds_context ctx,
1018                             const char *service)
1019 {
1020     char *s;
1021
1022     TRACE_INIT_CREDS_SERVICE(context, service);
1023
1024     s = strdup(service);
1025     if (s == NULL)
1026         return ENOMEM;
1027
1028     free(ctx->in_tkt_service);
1029     ctx->in_tkt_service = s;
1030
1031     krb5_preauth_request_context_fini(context);
1032     return restart_init_creds_loop(context, ctx, NULL);
1033 }
1034
1035 static krb5_error_code
1036 init_creds_validate_reply(krb5_context context,
1037                           krb5_init_creds_context ctx,
1038                           krb5_data *reply)
1039 {
1040     krb5_error_code code;
1041     krb5_error *error = NULL;
1042     krb5_kdc_rep *as_reply = NULL;
1043
1044     krb5_free_error(context, ctx->err_reply);
1045     ctx->err_reply = NULL;
1046
1047     krb5_free_kdc_rep(context, ctx->reply);
1048     ctx->reply = NULL;
1049
1050     if (krb5_is_krb_error(reply)) {
1051         code = decode_krb5_error(reply, &error);
1052         if (code != 0)
1053             return code;
1054
1055         assert(error != NULL);
1056
1057         TRACE_INIT_CREDS_ERROR_REPLY(context,
1058                                      error->error + ERROR_TABLE_BASE_krb5);
1059         if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1060             krb5_free_error(context, error);
1061             return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1062         } else {
1063             ctx->err_reply = error;
1064             return 0;
1065         }
1066     }
1067
1068     /*
1069      * Check to make sure it isn't a V4 reply.
1070      */
1071     if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1072 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1073 #define V4_KRB_PROT_VERSION     4
1074 #define V4_AUTH_MSG_ERR_REPLY   (5<<1)
1075         /* check here for V4 reply */
1076         unsigned int t_switch;
1077
1078         /* From v4 g_in_tkt.c: This used to be
1079            switch (pkt_msg_type(rpkt) & ~1) {
1080            but SCO 3.2v4 cc compiled that incorrectly.  */
1081         t_switch = reply->data[1];
1082         t_switch &= ~1;
1083
1084         if (t_switch == V4_AUTH_MSG_ERR_REPLY
1085             && reply->data[0] == V4_KRB_PROT_VERSION) {
1086             code = KRB5KRB_AP_ERR_V4_REPLY;
1087         } else {
1088             code = KRB5KRB_AP_ERR_MSG_TYPE;
1089         }
1090         return code;
1091     }
1092
1093     /* It must be a KRB_AS_REP message, or an bad returned packet */
1094     code = decode_krb5_as_rep(reply, &as_reply);
1095     if (code != 0)
1096         return code;
1097
1098     if (as_reply->msg_type != KRB5_AS_REP) {
1099         krb5_free_kdc_rep(context, as_reply);
1100         return KRB5KRB_AP_ERR_MSG_TYPE;
1101     }
1102
1103     ctx->reply = as_reply;
1104
1105     return 0;
1106 }
1107
1108 static krb5_error_code
1109 init_creds_step_request(krb5_context context,
1110                         krb5_init_creds_context ctx,
1111                         krb5_data *out)
1112 {
1113     krb5_error_code code;
1114     krb5_boolean got_real;
1115     char random_buf[4];
1116     krb5_data random_data;
1117
1118     if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1119         code = KRB5_GET_IN_TKT_LOOP;
1120         goto cleanup;
1121     }
1122     /*
1123      * RFC 6113 requires a new nonce for the inner request on each try. It's
1124      * permitted to change the nonce even for non-FAST so we do here.
1125      */
1126     random_data.length = 4;
1127     random_data.data = (char *)random_buf;
1128     code = krb5_c_random_make_octets(context, &random_data);
1129     if (code !=0)
1130         goto cleanup;
1131     /*
1132      * See RT ticket 3196 at MIT.  If we set the high bit, we may have
1133      * compatibility problems with Heimdal, because we (incorrectly) encode
1134      * this value as signed.
1135      */
1136     ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
1137     krb5_free_data(context, ctx->inner_request_body);
1138     ctx->inner_request_body = NULL;
1139     code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
1140     if (code)
1141         goto cleanup;
1142
1143     if (ctx->err_reply == NULL) {
1144         /* either our first attempt, or retrying after PREAUTH_NEEDED */
1145         code = krb5_do_preauth(context,
1146                                ctx->request,
1147                                ctx->inner_request_body,
1148                                ctx->encoded_previous_request,
1149                                ctx->preauth_to_use,
1150                                &ctx->request->padata,
1151                                ctx->prompter,
1152                                ctx->prompter_data,
1153                                &ctx->preauth_rock,
1154                                ctx->opte,
1155                                &got_real);
1156         if (code == 0 && !got_real && ctx->preauth_required)
1157             code = KRB5_PREAUTH_FAILED;
1158         if (code != 0)
1159             goto cleanup;
1160     } else {
1161         if (ctx->preauth_to_use != NULL) {
1162             /*
1163              * Retry after an error other than PREAUTH_NEEDED,
1164              * using ctx->err_padata to figure out what to change.
1165              */
1166             code = krb5_do_preauth_tryagain(context,
1167                                             ctx->request,
1168                                             ctx->inner_request_body,
1169                                             ctx->encoded_previous_request,
1170                                             ctx->preauth_to_use,
1171                                             &ctx->request->padata,
1172                                             ctx->err_reply,
1173                                             ctx->err_padata,
1174                                             ctx->prompter,
1175                                             ctx->prompter_data,
1176                                             &ctx->preauth_rock,
1177                                             ctx->opte);
1178         } else {
1179             /* No preauth supplied, so can't query the plugins. */
1180             code = KRB5KRB_ERR_GENERIC;
1181         }
1182         if (code != 0) {
1183             /* couldn't come up with anything better */
1184             code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1185             goto cleanup;
1186         }
1187     }
1188
1189     if (ctx->encoded_previous_request != NULL) {
1190         krb5_free_data(context, ctx->encoded_previous_request);
1191         ctx->encoded_previous_request = NULL;
1192     }
1193     if (ctx->request->padata)
1194         ctx->sent_nontrivial_preauth = 1;
1195     if (ctx->enc_pa_rep_permitted)
1196         code = request_enc_pa_rep(&ctx->request->padata);
1197     if (code)
1198         goto cleanup;
1199     code = krb5int_fast_prep_req(context, ctx->fast_state,
1200                                  ctx->request, ctx->outer_request_body,
1201                                  encode_krb5_as_req,
1202                                  &ctx->encoded_previous_request);
1203     if (code != 0)
1204         goto cleanup;
1205
1206     code = krb5int_copy_data_contents(context,
1207                                       ctx->encoded_previous_request,
1208                                       out);
1209     if (code != 0)
1210         goto cleanup;
1211
1212 cleanup:
1213     krb5_free_pa_data(context, ctx->request->padata);
1214     ctx->request->padata = NULL;
1215     return code;
1216 }
1217
1218 /*
1219  * The control flow is complicated.  In order to switch from non-FAST mode to
1220  * FAST mode, we need to reset our pre-authentication state.  FAST negotiation
1221  * attempts to make sure we rarely have to do this.  When FAST negotiation is
1222  * working, we record whether FAST is available when we obtain an armor ticket;
1223  * if so, we start out with FAST enabled .  There are two complicated
1224  * situations.
1225  *
1226  * First, if we get a PREAUTH_REQUIRED error including PADATA_FX_FAST back from
1227  * a KDC in a case where we were not expecting to use FAST, and we have an
1228  * armor ticket available, then we want to use FAST.  That involves clearing
1229  * out the pre-auth state, reinitializing the plugins and trying again with an
1230  * armor key.
1231  *
1232  * Secondly, using the negotiation can cause problems with some older KDCs.
1233  * Negotiation involves including a special padata item.  Some KDCs, including
1234  * MIT prior to 1.7, will return PREAUTH_FAILED rather than PREAUTH_REQUIRED in
1235  * pre-authentication is required and unknown padata are included in the
1236  * request.  To make matters worse, these KDCs typically do not include a list
1237  * of padata in PREAUTH_FAILED errors.  So, if we get PREAUTH_FAILED and we
1238  * generated no pre-authentication other than the negotiation then we want to
1239  * retry without negotiation.  In this case it is probably also desirable to
1240  * retry with the preauth plugin state cleared.
1241  *
1242  * In all these cases we should not start over more than once.  Control flow is
1243  * managed by several variables.
1244  *
1245  *   sent_nontrivial_preauth: if true, we sent preauth other than negotiation;
1246  *   no restart on PREAUTH_FAILED
1247  *
1248  *   KRB5INT_FAST_ARMOR_AVAIL: fast_state_flag if desired we could generate
1249  *   armor; if not set, then we can't use FAST even if the KDC wants to.
1250  *
1251  *   have_restarted: true if we've already restarted
1252  */
1253 static krb5_boolean
1254 negotiation_requests_restart(krb5_context context, krb5_init_creds_context ctx,
1255                              krb5_pa_data **padata)
1256 {
1257     if (ctx->have_restarted)
1258         return FALSE;
1259     if (krb5int_upgrade_to_fast_p(context, ctx->fast_state, padata)) {
1260         TRACE_INIT_CREDS_RESTART_FAST(context);
1261         return TRUE;
1262     }
1263     if (ctx->err_reply->error == KDC_ERR_PREAUTH_FAILED &&
1264         !ctx->sent_nontrivial_preauth) {
1265         TRACE_INIT_CREDS_RESTART_PREAUTH_FAILED(context);
1266         return TRUE;
1267     }
1268     return FALSE;
1269 }
1270
1271 /* Ensure that the reply enctype was among the requested enctypes. */
1272 static krb5_error_code
1273 check_reply_enctype(krb5_init_creds_context ctx)
1274 {
1275     int i;
1276
1277     for (i = 0; i < ctx->request->nktypes; i++) {
1278         if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1279             return 0;
1280     }
1281     return KRB5_CONFIG_ETYPE_NOSUPP;
1282 }
1283
1284 /* Note the difference between the KDC's time, as reported to us in a
1285  * preauth-required error, and the current time. */
1286 static void
1287 note_req_timestamp(krb5_context kcontext, krb5_clpreauth_rock rock,
1288                    krb5_timestamp kdc_time, krb5_int32 kdc_usec)
1289 {
1290     krb5_timestamp now;
1291     krb5_int32 usec;
1292
1293     if (k5_time_with_offset(0, 0, &now, &usec) != 0)
1294         return;
1295     rock->pa_offset = kdc_time - now;
1296     rock->pa_offset_usec = kdc_usec - usec;
1297     rock->pa_offset_state = (rock->fast_state->armor_key != NULL) ?
1298         AUTH_OFFSET : UNAUTH_OFFSET;
1299 }
1300
1301 static krb5_error_code
1302 init_creds_step_reply(krb5_context context,
1303                       krb5_init_creds_context ctx,
1304                       krb5_data *in)
1305 {
1306     krb5_error_code code;
1307     krb5_pa_data **kdc_padata = NULL;
1308     krb5_boolean retry = FALSE;
1309     int canon_flag = 0;
1310     krb5_keyblock *strengthen_key = NULL;
1311     krb5_keyblock encrypting_key;
1312     krb5_boolean fast_avail, got_real;
1313
1314     encrypting_key.length = 0;
1315     encrypting_key.contents = NULL;
1316
1317     /* process previous KDC response */
1318     code = init_creds_validate_reply(context, ctx, in);
1319     if (code != 0)
1320         goto cleanup;
1321
1322     /* per referrals draft, enterprise principals imply canonicalization */
1323     canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1324         ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1325
1326     if (ctx->err_reply != NULL) {
1327         code = krb5int_fast_process_error(context, ctx->fast_state,
1328                                           &ctx->err_reply, &ctx->err_padata,
1329                                           &retry);
1330         if (code != 0)
1331             goto cleanup;
1332         if (negotiation_requests_restart(context, ctx, ctx->err_padata)) {
1333             ctx->have_restarted = 1;
1334             krb5_preauth_request_context_fini(context);
1335             if ((ctx->fast_state->fast_state_flags & KRB5INT_FAST_DO_FAST) ==0)
1336                 ctx->enc_pa_rep_permitted = 0;
1337             code = restart_init_creds_loop(context, ctx, ctx->err_padata);
1338             krb5_free_error(context, ctx->err_reply);
1339             ctx->err_reply = NULL;
1340             krb5_free_pa_data(context, ctx->err_padata);
1341             ctx->err_padata = NULL;
1342         } else if (ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
1343                    retry) {
1344             /* reset the list of preauth types to try */
1345             krb5_free_pa_data(context, ctx->preauth_to_use);
1346             ctx->preauth_to_use = ctx->err_padata;
1347             ctx->err_padata = NULL;
1348             note_req_timestamp(context, &ctx->preauth_rock,
1349                                ctx->err_reply->stime, ctx->err_reply->susec);
1350             /* this will trigger a new call to krb5_do_preauth() */
1351             krb5_free_error(context, ctx->err_reply);
1352             ctx->err_reply = NULL;
1353             code = sort_krb5_padata_sequence(context,
1354                                              &ctx->request->client->realm,
1355                                              ctx->preauth_to_use);
1356             ctx->preauth_required = TRUE;
1357
1358         } else if (canon_flag && ctx->err_reply->error == KDC_ERR_WRONG_REALM) {
1359             if (ctx->err_reply->client == NULL ||
1360                 !krb5_princ_realm(context, ctx->err_reply->client)->length) {
1361                 code = KRB5KDC_ERR_WRONG_REALM;
1362                 goto cleanup;
1363             }
1364             TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
1365             /* Rewrite request.client with realm from error reply */
1366             krb5_free_data_contents(context, &ctx->request->client->realm);
1367             code = krb5int_copy_data_contents(context,
1368                                               &ctx->err_reply->client->realm,
1369                                               &ctx->request->client->realm);
1370             /* this will trigger a new call to krb5_do_preauth() */
1371             krb5_free_error(context, ctx->err_reply);
1372             ctx->err_reply = NULL;
1373             krb5_preauth_request_context_fini(context);
1374             /* Permit another negotiation based restart. */
1375             ctx->have_restarted = 0;
1376             ctx->sent_nontrivial_preauth = 0;
1377             code = restart_init_creds_loop(context, ctx, NULL);
1378             if (code != 0)
1379                 goto cleanup;
1380         } else {
1381             if (retry) {
1382                 code = 0;
1383             } else {
1384                 /* error + no hints = give up */
1385                 code = (krb5_error_code)ctx->err_reply->error +
1386                     ERROR_TABLE_BASE_krb5;
1387             }
1388         }
1389
1390         /* Return error code, or continue with next iteration */
1391         goto cleanup;
1392     }
1393
1394     /* We have a response. Process it. */
1395     assert(ctx->reply != NULL);
1396
1397     /* Check for replies (likely forged) with unasked-for enctypes. */
1398     code = check_reply_enctype(ctx);
1399     if (code != 0)
1400         goto cleanup;
1401
1402     /* process any preauth data in the as_reply */
1403     krb5_clear_preauth_context_use_counts(context);
1404     code = krb5int_fast_process_response(context, ctx->fast_state,
1405                                          ctx->reply, &strengthen_key);
1406     if (code != 0)
1407         goto cleanup;
1408
1409     code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1410                                      ctx->reply->padata);
1411     if (code != 0)
1412         goto cleanup;
1413
1414     ctx->etype = ctx->reply->enc_part.enctype;
1415
1416     code = krb5_do_preauth(context,
1417                            ctx->request,
1418                            ctx->inner_request_body,
1419                            ctx->encoded_previous_request,
1420                            ctx->reply->padata,
1421                            &kdc_padata,
1422                            ctx->prompter,
1423                            ctx->prompter_data,
1424                            &ctx->preauth_rock,
1425                            ctx->opte,
1426                            &got_real);
1427     if (code != 0)
1428         goto cleanup;
1429
1430     /*
1431      * If we haven't gotten a salt from another source yet, set up one
1432      * corresponding to the client principal returned by the KDC.  We
1433      * could get the same effect by passing local_as_reply->client to
1434      * gak_fct below, but that would put the canonicalized client name
1435      * in the prompt, which raises issues of needing to sanitize
1436      * unprintable characters.  So for now we just let it affect the
1437      * salt.  local_as_reply->client will be checked later on in
1438      * verify_as_reply.
1439      */
1440     if (ctx->salt.length == SALT_TYPE_AFS_LENGTH && ctx->salt.data == NULL) {
1441         code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1442         TRACE_INIT_CREDS_SALT_PRINC(context, &ctx->salt);
1443         if (code != 0)
1444             goto cleanup;
1445     }
1446
1447     /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
1448        the AS_REP comes back encrypted in the user's longterm key
1449        instead of in the SAD. If there was a SAM preauth, there
1450        will be an as_key here which will be the SAD. If that fails,
1451        use the gak_fct to get the password, and try again. */
1452
1453     /* XXX because etypes are handled poorly (particularly wrt SAM,
1454        where the etype is fixed by the kdc), we may want to try
1455        decrypt_as_reply twice.  If there's an as_key available, try
1456        it.  If decrypting the as_rep fails, or if there isn't an
1457        as_key at all yet, then use the gak_fct to get one, and try
1458        again.  */
1459     if (ctx->as_key.length) {
1460         TRACE_INIT_CREDS_AS_KEY_PREAUTH(context, &ctx->as_key);
1461         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1462                                       &encrypting_key);
1463         if (code != 0)
1464             goto cleanup;
1465         code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1466         if (code != 0)
1467             TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(context, code);
1468     } else
1469         code = -1;
1470
1471     if (code != 0) {
1472         /* if we haven't get gotten a key, get it now */
1473         TRACE_INIT_CREDS_GAK(context, &ctx->salt, &ctx->s2kparams);
1474         code = (*ctx->gak_fct)(context, ctx->request->client,
1475                                ctx->reply->enc_part.enctype,
1476                                ctx->prompter, ctx->prompter_data,
1477                                &ctx->salt, &ctx->s2kparams,
1478                                &ctx->as_key, ctx->gak_data);
1479         if (code != 0)
1480             goto cleanup;
1481         TRACE_INIT_CREDS_AS_KEY_GAK(context, &ctx->as_key);
1482
1483         code = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
1484                                       &encrypting_key);
1485         if (code != 0)
1486             goto cleanup;
1487
1488         code = decrypt_as_reply(context, NULL, ctx->reply, &encrypting_key);
1489         if (code != 0)
1490             goto cleanup;
1491     }
1492
1493     TRACE_INIT_CREDS_DECRYPTED_REPLY(context, ctx->reply->enc_part2->session);
1494
1495     code = krb5int_fast_verify_nego(context, ctx->fast_state,
1496                                     ctx->reply, ctx->encoded_previous_request,
1497                                     &encrypting_key, &fast_avail);
1498     if (code)
1499         goto cleanup;
1500     code = verify_as_reply(context, ctx->request_time,
1501                            ctx->request, ctx->reply);
1502     if (code != 0)
1503         goto cleanup;
1504     code = verify_anonymous(context, ctx->request, ctx->reply,
1505                             &ctx->as_key);
1506     if (code)
1507         goto cleanup;
1508
1509     code = stash_as_reply(context, ctx->request_time, ctx->request,
1510                           ctx->reply, &ctx->cred, NULL);
1511     if (code != 0)
1512         goto cleanup;
1513     if (ctx->opte && ctx->opte->opt_private->out_ccache) {
1514         krb5_ccache out_ccache = ctx->opte->opt_private->out_ccache;
1515         krb5_data config_data;
1516         code = krb5_cc_initialize(context, out_ccache, ctx->cred.client);
1517         if (code != 0)
1518             goto cc_cleanup;
1519         code = krb5_cc_store_cred(context, out_ccache, &ctx->cred);
1520         if (code != 0)
1521             goto cc_cleanup;
1522         if (fast_avail) {
1523             config_data.data = "yes";
1524             config_data.length = strlen(config_data.data);
1525             code = krb5_cc_set_config(context, out_ccache, ctx->cred.server,
1526                                       KRB5_CONF_FAST_AVAIL, &config_data);
1527         }
1528     cc_cleanup:
1529         if (code !=0) {
1530             const char *msg;
1531             msg = krb5_get_error_message(context, code);
1532             krb5_set_error_message(context, code,
1533                                    _("%s while storing credentials"), msg);
1534             krb5_free_error_message(context, msg);
1535         }
1536     }
1537
1538     krb5_preauth_request_context_fini(context);
1539
1540     /* success */
1541     code = 0;
1542     ctx->complete = TRUE;
1543
1544 cleanup:
1545     krb5_free_pa_data(context, kdc_padata);
1546     krb5_free_keyblock(context, strengthen_key);
1547     krb5_free_keyblock_contents(context, &encrypting_key);
1548
1549     return code;
1550 }
1551
1552 /*
1553  * Do next step of credentials acquisition.
1554  *
1555  * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1556  * should be sent with TCP.
1557  */
1558 krb5_error_code KRB5_CALLCONV
1559 krb5_init_creds_step(krb5_context context,
1560                      krb5_init_creds_context ctx,
1561                      krb5_data *in,
1562                      krb5_data *out,
1563                      krb5_data *realm,
1564                      unsigned int *flags)
1565 {
1566     krb5_error_code code = 0, code2;
1567
1568     *flags = 0;
1569
1570     out->data = NULL;
1571     out->length = 0;
1572
1573     realm->data = NULL;
1574     realm->length = 0;
1575
1576     if (ctx->complete)
1577         return EINVAL;
1578
1579     if (in->length != 0) {
1580         code = init_creds_step_reply(context, ctx, in);
1581         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1582             code2 = krb5int_copy_data_contents(context,
1583                                                ctx->encoded_previous_request,
1584                                                out);
1585             if (code2 != 0) {
1586                 code = code2;
1587                 goto cleanup;
1588             }
1589             goto copy_realm;
1590         }
1591         if (code != 0 || ctx->complete)
1592             goto cleanup;
1593     }
1594
1595     code = init_creds_step_request(context, ctx, out);
1596     if (code != 0)
1597         goto cleanup;
1598
1599     /* Only a new request increments the loop count, not a TCP retry */
1600     ctx->loopcount++;
1601
1602 copy_realm:
1603     assert(ctx->request->server != NULL);
1604
1605     code2 = krb5int_copy_data_contents(context,
1606                                        &ctx->request->server->realm,
1607                                        realm);
1608     if (code2 != 0) {
1609         code = code2;
1610         goto cleanup;
1611     }
1612
1613 cleanup:
1614     if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
1615         char *client_name;
1616
1617         /* See if we can produce a more detailed error message */
1618         code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
1619         if (code2 == 0) {
1620             krb5_set_error_message(context, code,
1621                                    _("Client '%s' not found in Kerberos "
1622                                      "database"), client_name);
1623             krb5_free_unparsed_name(context, client_name);
1624         }
1625     }
1626
1627     *flags = ctx->complete ? 0 : KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1628     return code;
1629 }
1630
1631 krb5_error_code KRB5_CALLCONV
1632 krb5int_get_init_creds(krb5_context context,
1633                        krb5_creds *creds,
1634                        krb5_principal client,
1635                        krb5_prompter_fct prompter,
1636                        void *prompter_data,
1637                        krb5_deltat start_time,
1638                        char *in_tkt_service,
1639                        krb5_get_init_creds_opt *options,
1640                        krb5_gic_get_as_key_fct gak_fct,
1641                        void *gak_data,
1642                        int  *use_master,
1643                        krb5_kdc_rep **as_reply)
1644 {
1645     krb5_error_code code;
1646     krb5_init_creds_context ctx = NULL;
1647
1648     code = krb5_init_creds_init(context,
1649                                 client,
1650                                 prompter,
1651                                 prompter_data,
1652                                 start_time,
1653                                 options,
1654                                 &ctx);
1655     if (code != 0)
1656         goto cleanup;
1657
1658     ctx->gak_fct = gak_fct;
1659     ctx->gak_data = gak_data;
1660
1661     if (in_tkt_service) {
1662         code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1663         if (code != 0)
1664             goto cleanup;
1665     }
1666
1667     code = init_creds_get(context, ctx, use_master);
1668     if (code != 0)
1669         goto cleanup;
1670
1671     code = krb5_init_creds_get_creds(context, ctx, creds);
1672     if (code != 0)
1673         goto cleanup;
1674
1675     if (as_reply != NULL) {
1676         *as_reply = ctx->reply;
1677         ctx->reply = NULL;
1678     }
1679
1680 cleanup:
1681     krb5_init_creds_free(context, ctx);
1682
1683     return code;
1684 }
1685
1686 krb5_error_code
1687 krb5int_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
1688                          krb5_flags options, krb5_address *const *addrs,
1689                          krb5_enctype *ktypes,
1690                          krb5_preauthtype *pre_auth_types, krb5_creds *creds)
1691 {
1692     int i;
1693     krb5_int32 starttime;
1694     krb5_get_init_creds_opt *opt;
1695     krb5_error_code retval;
1696
1697     *out = NULL;
1698     retval = krb5_get_init_creds_opt_alloc(context, &opt);
1699     if (retval)
1700         return(retval);
1701
1702     if (addrs)
1703         krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
1704     if (ktypes) {
1705         i = krb5int_count_etypes(ktypes);
1706         if (i)
1707             krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
1708     }
1709     if (pre_auth_types) {
1710         for (i=0; pre_auth_types[i]; i++);
1711         if (i)
1712             krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
1713     }
1714     if (options&KDC_OPT_FORWARDABLE)
1715         krb5_get_init_creds_opt_set_forwardable(opt, 1);
1716     else krb5_get_init_creds_opt_set_forwardable(opt, 0);
1717     if (options&KDC_OPT_PROXIABLE)
1718         krb5_get_init_creds_opt_set_proxiable(opt, 1);
1719     else krb5_get_init_creds_opt_set_proxiable(opt, 0);
1720     if (creds && creds->times.endtime) {
1721         retval = krb5_timeofday(context, &starttime);
1722         if (retval)
1723             goto cleanup;
1724         if (creds->times.starttime) starttime = creds->times.starttime;
1725         krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
1726     }
1727     *out = opt;
1728     return 0;
1729
1730 cleanup:
1731     krb5_get_init_creds_opt_free(context, opt);
1732     return retval;
1733 }