s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / source4 / heimdal / lib / krb5 / init_creds_pw.c
1 /*
2  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
7  * Portions Copyright (c) 2021, PADL Software Pty Ltd. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * 3. Neither the name of the Institute nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include "krb5_locl.h"
38 #include "../base/heimbasepriv.h" /* XXX */
39
40 struct pa_info_data {
41     krb5_enctype etype;
42     krb5_salt salt;
43     krb5_data *s2kparams;
44 };
45
46 struct krb5_gss_init_ctx_data {
47     krb5_gssic_step step;
48     krb5_gssic_finish finish;
49     krb5_gssic_release_cred release_cred;
50     krb5_gssic_delete_sec_context delete_sec_context;
51
52     const struct gss_OID_desc_struct *mech;
53     struct gss_cred_id_t_desc_struct *cred;
54
55     struct {
56         unsigned int release_cred : 1;
57     } flags;
58 };
59
60 struct krb5_get_init_creds_ctx {
61     KDCOptions flags;
62     krb5_creds cred;
63     krb5_addresses *addrs;
64     krb5_enctype *etypes;
65     krb5_preauthtype *pre_auth_types;
66     char *in_tkt_service;
67     unsigned nonce;
68     unsigned pk_nonce;
69
70     krb5_data req_buffer;
71     AS_REQ as_req;
72     int pa_counter;
73
74     /* password and keytab_data is freed on completion */
75     char *password;
76     krb5_keytab_key_proc_args *keytab_data;
77
78     krb5_pointer *keyseed;
79     krb5_s2k_proc keyproc;
80
81     krb5_get_init_creds_tristate req_pac;
82
83     krb5_pk_init_ctx pk_init_ctx;
84     krb5_gss_init_ctx gss_init_ctx;
85     int ic_flags;
86
87     char *kdc_hostname;
88     char *sitename;
89
90     struct {
91         unsigned int change_password:1;
92         unsigned int change_password_prompt:1;
93         unsigned int allow_enc_pa_rep:1;
94         unsigned int allow_save_as_reply_key:1;
95     } runflags;
96
97     struct pa_info_data paid;
98
99     METHOD_DATA md;
100     KRB_ERROR error;
101     EncKDCRepPart enc_part;
102
103     krb5_prompter_fct prompter;
104     void *prompter_data;
105     int warned_user;
106
107     struct pa_info_data *ppaid;
108
109     struct krb5_fast_state fast_state;
110     krb5_enctype as_enctype;
111     krb5_keyblock *as_reply_key;
112
113     /* current and available pa mechansm in this exchange */
114     struct pa_auth_mech *pa_mech;
115     heim_array_t available_pa_mechs;
116     const char *pa_used;
117
118     struct {
119         struct timeval run_time;
120     } stats;
121 };
122
123 static void
124 free_paid(krb5_context context, struct pa_info_data *ppaid)
125 {
126     krb5_free_salt(context, ppaid->salt);
127     if (ppaid->s2kparams)
128         krb5_free_data(context, ppaid->s2kparams);
129     memset(ppaid, 0, sizeof(*ppaid));
130 }
131
132 static krb5_error_code KRB5_CALLCONV
133 default_s2k_func(krb5_context context, krb5_enctype type,
134                  krb5_const_pointer keyseed,
135                  krb5_salt salt, krb5_data *s2kparms,
136                  krb5_keyblock **key)
137 {
138     krb5_error_code ret;
139     krb5_data password;
140     krb5_data opaque;
141
142     if (_krb5_have_debug(context, 5)) {
143         char *str = NULL;
144         ret = krb5_enctype_to_string(context, type, &str);
145         if (ret)
146             return ret;
147
148         _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type);
149         free(str);
150     }
151
152     password.data = rk_UNCONST(keyseed);
153     password.length = keyseed ? strlen(keyseed) : 0;
154     if (s2kparms)
155         opaque = *s2kparms;
156     else
157         krb5_data_zero(&opaque);
158
159     *key = malloc(sizeof(**key));
160     if (*key == NULL)
161         return krb5_enomem(context);
162     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
163                                               salt, opaque, *key);
164     if (ret) {
165         free(*key);
166         *key = NULL;
167     }
168     return ret;
169 }
170
171 static void
172 free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
173 {
174     if (gssic == NULL)
175         return;
176
177     if (gssic->flags.release_cred)
178         gssic->release_cred(context, gssic, gssic->cred);
179     free(gssic);
180 }
181
182 static void
183 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
184 {
185     if (ctx->etypes)
186         free(ctx->etypes);
187     if (ctx->pre_auth_types)
188         free (ctx->pre_auth_types);
189     if (ctx->in_tkt_service)
190         free(ctx->in_tkt_service);
191     if (ctx->keytab_data)
192         free(ctx->keytab_data);
193     if (ctx->password) {
194         size_t len;
195         len = strlen(ctx->password);
196         memset_s(ctx->password, len, 0, len);
197         free(ctx->password);
198     }
199     free_gss_init_ctx(context, ctx->gss_init_ctx);
200     /*
201      * FAST state
202      */
203     _krb5_fast_free(context, &ctx->fast_state);
204     if (ctx->as_reply_key)
205         krb5_free_keyblock(context, ctx->as_reply_key);
206
207     krb5_data_free(&ctx->req_buffer);
208     krb5_free_cred_contents(context, &ctx->cred);
209     free_METHOD_DATA(&ctx->md);
210     free_EncKDCRepPart(&ctx->enc_part);
211     free_KRB_ERROR(&ctx->error);
212     free_AS_REQ(&ctx->as_req);
213
214     heim_release(ctx->available_pa_mechs);
215     heim_release(ctx->pa_mech);
216     ctx->pa_mech = NULL;
217     free(ctx->kdc_hostname);
218     free(ctx->sitename);
219     free_paid(context, &ctx->paid);
220     memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
221 }
222
223 static krb5_deltat
224 get_config_time (krb5_context context,
225                  const char *realm,
226                  const char *name,
227                  int def)
228 {
229     krb5_deltat ret;
230
231     ret = krb5_config_get_time (context, NULL,
232                                 "realms",
233                                 realm,
234                                 name,
235                                 NULL);
236     if (ret >= 0)
237         return ret;
238     ret = krb5_config_get_time (context, NULL,
239                                 "libdefaults",
240                                 name,
241                                 NULL);
242     if (ret >= 0)
243         return ret;
244     return def;
245 }
246
247 static krb5_error_code
248 init_cred (krb5_context context,
249            krb5_creds *cred,
250            krb5_principal client,
251            krb5_deltat start_time,
252            krb5_get_init_creds_opt *options)
253 {
254     krb5_error_code ret;
255     krb5_deltat tmp;
256     krb5_timestamp now;
257
258     krb5_timeofday (context, &now);
259
260     memset (cred, 0, sizeof(*cred));
261
262     if (client)
263         ret = krb5_copy_principal(context, client, &cred->client);
264     else
265         ret = krb5_get_default_principal(context, &cred->client);
266     if (ret)
267         goto out;
268
269     if (start_time)
270         cred->times.starttime  = now + start_time;
271
272     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
273         tmp = options->tkt_life;
274     else
275         tmp = KRB5_TKT_LIFETIME_DEFAULT;
276     cred->times.endtime = now + tmp;
277
278     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
279         if (options->renew_life > 0)
280             tmp = options->renew_life;
281         else
282             tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
283         cred->times.renew_till = now + tmp;
284     }
285
286     return 0;
287
288 out:
289     krb5_free_cred_contents (context, cred);
290     return ret;
291 }
292
293 /*
294  * Print a message (str) to the user about the expiration in `lr'
295  */
296
297 static void
298 report_expiration (krb5_context context,
299                    krb5_prompter_fct prompter,
300                    krb5_data *data,
301                    const char *str,
302                    time_t now)
303 {
304     char *p = NULL;
305
306     if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
307         return;
308     (*prompter)(context, data, NULL, p, 0, NULL);
309     free(p);
310 }
311
312 /*
313  * Check the context, and in the case there is a expiration warning,
314  * use the prompter to print the warning.
315  *
316  * @param context A Kerberos 5 context.
317  * @param options An GIC options structure
318  * @param ctx The krb5_init_creds_context check for expiration.
319  */
320
321 krb5_error_code
322 krb5_process_last_request(krb5_context context,
323                           krb5_get_init_creds_opt *options,
324                           krb5_init_creds_context ctx)
325 {
326     LastReq *lr;
327     size_t i;
328
329     /*
330      * First check if there is a API consumer.
331      */
332
333     lr = &ctx->enc_part.last_req;
334
335     if (options && options->opt_private && options->opt_private->lr.func) {
336         krb5_last_req_entry **lre;
337
338         lre = calloc(lr->len + 1, sizeof(*lre));
339         if (lre == NULL)
340             return krb5_enomem(context);
341
342         for (i = 0; i < lr->len; i++) {
343             lre[i] = calloc(1, sizeof(*lre[i]));
344             if (lre[i] == NULL)
345                 break;
346             lre[i]->lr_type = lr->val[i].lr_type;
347             lre[i]->value = lr->val[i].lr_value;
348         }
349
350         (*options->opt_private->lr.func)(context, lre,
351                                          options->opt_private->lr.ctx);
352
353         for (i = 0; i < lr->len; i++)
354             free(lre[i]);
355         free(lre);
356     }
357
358     return krb5_init_creds_warn_user(context, ctx);
359 }
360
361 /**
362  * Warn the user using prompter in the krb5_init_creds_context about
363  * possible password and account expiration.
364  *
365  * @param context a Kerberos 5 context.
366  * @param ctx a krb5_init_creds_context context.
367  *
368  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
369  * @ingroup krb5_credential
370  */
371
372 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
373 krb5_init_creds_warn_user(krb5_context context,
374                           krb5_init_creds_context ctx)
375 {
376     krb5_timestamp sec;
377     krb5_const_realm realm;
378     krb5_enctype weak_enctype = KRB5_ENCTYPE_NULL;
379     LastReq *lr;
380     unsigned i;
381     time_t t;
382
383     if (ctx->prompter == NULL)
384         return 0;
385
386     if (ctx->warned_user)
387         return 0;
388
389     ctx->warned_user = 1;
390
391     krb5_timeofday (context, &sec);
392
393     realm = krb5_principal_get_realm (context, ctx->cred.client);
394     lr = &ctx->enc_part.last_req;
395
396     t = sec + get_config_time (context,
397                                realm,
398                                "warn_pwexpire",
399                                7 * 24 * 60 * 60);
400
401     for (i = 0; i < lr->len; ++i) {
402         if (lr->val[i].lr_value <= t) {
403             switch (lr->val[i].lr_type) {
404             case LR_PW_EXPTIME :
405                 report_expiration(context, ctx->prompter,
406                                   ctx->prompter_data,
407                                   "Your password will expire at ",
408                                   lr->val[i].lr_value);
409                 break;
410             case LR_ACCT_EXPTIME :
411                 report_expiration(context, ctx->prompter,
412                                   ctx->prompter_data,
413                                   "Your account will expire at ",
414                                   lr->val[i].lr_value);
415                 break;
416             default:
417                 break;
418             }
419         }
420     }
421
422     if (krb5_is_enctype_weak(context, ctx->as_enctype))
423         weak_enctype = ctx->as_enctype;
424     else if (krb5_is_enctype_weak(context, ctx->cred.session.keytype))
425         weak_enctype = ctx->cred.session.keytype;
426
427     if (ctx->prompter && weak_enctype != KRB5_ENCTYPE_NULL) {
428         int suppress = krb5_config_get_bool_default(context, NULL, false,
429                                                     "libdefaults",
430                                                     "suppress_weak_enctype", NULL);
431         if (!suppress) {
432             char *str = NULL, *p = NULL;
433             int aret;
434             krb5_enctype_to_string(context, weak_enctype, &str);
435
436             aret = asprintf(&p, "Encryption type %s(%d) used for authentication is weak and will be deprecated",
437                             str ? str : "unknown", weak_enctype);
438             if (aret >= 0 && p) {
439                 (*ctx->prompter)(context, ctx->prompter_data, NULL, p, 0, NULL);
440                 free(p);
441             }
442             free(str);
443         }
444     }
445
446     return 0;
447 }
448
449 static krb5_addresses no_addrs = { 0, NULL };
450
451 static krb5_error_code
452 get_init_creds_common(krb5_context context,
453                       krb5_principal client,
454                       krb5_prompter_fct prompter,
455                       void *prompter_data,
456                       krb5_deltat start_time,
457                       krb5_get_init_creds_opt *options,
458                       krb5_init_creds_context ctx)
459 {
460     krb5_get_init_creds_opt *default_opt = NULL;
461     krb5_error_code ret;
462     krb5_enctype *etypes;
463     krb5_preauthtype *pre_auth_types;
464
465     memset(ctx, 0, sizeof(*ctx));
466
467     if (options == NULL) {
468         const char *realm = krb5_principal_get_realm(context, client);
469
470         krb5_get_init_creds_opt_alloc (context, &default_opt);
471         options = default_opt;
472         krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
473     }
474
475     if (options->opt_private) {
476         if (options->opt_private->password) {
477             ret = krb5_init_creds_set_password(context, ctx,
478                                                options->opt_private->password);
479             if (ret)
480                 goto out;
481         }
482
483         ctx->keyproc = options->opt_private->key_proc;
484         ctx->req_pac = options->opt_private->req_pac;
485         ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
486         ctx->ic_flags = options->opt_private->flags;
487     } else
488         ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
489
490     if (ctx->keyproc == NULL)
491         ctx->keyproc = default_s2k_func;
492
493     if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
494         ctx->flags.canonicalize = 1;
495     if (krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
496         ctx->flags.canonicalize = 1;
497
498     ctx->pre_auth_types = NULL;
499     ctx->addrs = NULL;
500     ctx->etypes = NULL;
501     ctx->pre_auth_types = NULL;
502
503     ret = init_cred(context, &ctx->cred, client, start_time, options);
504     if (ret) {
505         if (default_opt)
506             krb5_get_init_creds_opt_free(context, default_opt);
507         return ret;
508     }
509
510     ret = krb5_init_creds_set_service(context, ctx, NULL);
511     if (ret)
512         goto out;
513
514     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
515         ctx->flags.forwardable = options->forwardable;
516
517     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
518         ctx->flags.proxiable = options->proxiable;
519
520     if (start_time)
521         ctx->flags.postdated = 1;
522     if (ctx->cred.times.renew_till)
523         ctx->flags.renewable = 1;
524     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
525         ctx->addrs = options->address_list;
526     } else if (options->opt_private) {
527         switch (options->opt_private->addressless) {
528         case KRB5_INIT_CREDS_TRISTATE_UNSET:
529 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
530             ctx->addrs = &no_addrs;
531 #else
532             ctx->addrs = NULL;
533 #endif
534             break;
535         case KRB5_INIT_CREDS_TRISTATE_FALSE:
536             ctx->addrs = NULL;
537             break;
538         case KRB5_INIT_CREDS_TRISTATE_TRUE:
539             ctx->addrs = &no_addrs;
540             break;
541         }
542     }
543     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
544         if (ctx->etypes)
545             free(ctx->etypes);
546
547         etypes = malloc((options->etype_list_length + 1)
548                         * sizeof(krb5_enctype));
549         if (etypes == NULL) {
550             ret = krb5_enomem(context);
551             goto out;
552         }
553         memcpy (etypes, options->etype_list,
554                 options->etype_list_length * sizeof(krb5_enctype));
555         etypes[options->etype_list_length] = ETYPE_NULL;
556         ctx->etypes = etypes;
557     }
558     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
559         pre_auth_types = malloc((options->preauth_list_length + 1)
560                                 * sizeof(krb5_preauthtype));
561         if (pre_auth_types == NULL) {
562             ret = krb5_enomem(context);
563             goto out;
564         }
565         memcpy (pre_auth_types, options->preauth_list,
566                 options->preauth_list_length * sizeof(krb5_preauthtype));
567         pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
568         ctx->pre_auth_types = pre_auth_types;
569     }
570     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
571         ctx->flags.request_anonymous = options->anonymous;
572
573     ctx->prompter = prompter;
574     ctx->prompter_data = prompter_data;
575
576     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
577         !options->change_password_prompt)
578         ctx->runflags.change_password_prompt = 0;
579     else
580         ctx->runflags.change_password_prompt = ctx->prompter != NULL;
581
582     if (default_opt)
583         krb5_get_init_creds_opt_free(context, default_opt);
584     return 0;
585
586  out:
587     if (default_opt)
588         krb5_get_init_creds_opt_free(context, default_opt);
589     return ret;
590 }
591
592 static krb5_error_code
593 change_password (krb5_context context,
594                  krb5_principal client,
595                  const char *password,
596                  char *newpw,
597                  size_t newpw_sz,
598                  krb5_prompter_fct prompter,
599                  void *data,
600                  krb5_get_init_creds_opt *old_options)
601 {
602     krb5_prompt prompts[2];
603     krb5_error_code ret;
604     krb5_creds cpw_cred;
605     char buf1[BUFSIZ], buf2[BUFSIZ];
606     krb5_data password_data[2];
607     int result_code;
608     krb5_data result_code_string;
609     krb5_data result_string;
610     char *p;
611     krb5_get_init_creds_opt *options;
612
613     heim_assert(prompter != NULL, "unexpected NULL prompter");
614
615     memset (&cpw_cred, 0, sizeof(cpw_cred));
616
617     ret = krb5_get_init_creds_opt_alloc(context, &options);
618     if (ret)
619         return ret;
620     krb5_get_init_creds_opt_set_tkt_life (options, 60);
621     krb5_get_init_creds_opt_set_forwardable (options, FALSE);
622     krb5_get_init_creds_opt_set_proxiable (options, FALSE);
623     if (old_options &&
624         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
625         krb5_get_init_creds_opt_set_preauth_list(options,
626                                                  old_options->preauth_list,
627                                                  old_options->preauth_list_length);
628     if (old_options &&
629         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
630         krb5_get_init_creds_opt_set_change_password_prompt(options,
631                                                            old_options->change_password_prompt);
632
633     krb5_data_zero (&result_code_string);
634     krb5_data_zero (&result_string);
635
636     ret = krb5_get_init_creds_password (context,
637                                         &cpw_cred,
638                                         client,
639                                         password,
640                                         prompter,
641                                         data,
642                                         0,
643                                         "kadmin/changepw",
644                                         options);
645     krb5_get_init_creds_opt_free(context, options);
646     if (ret)
647         goto out;
648
649     for(;;) {
650         password_data[0].data   = buf1;
651         password_data[0].length = sizeof(buf1);
652
653         prompts[0].hidden = 1;
654         prompts[0].prompt = "New password: ";
655         prompts[0].reply  = &password_data[0];
656         prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
657
658         password_data[1].data   = buf2;
659         password_data[1].length = sizeof(buf2);
660
661         prompts[1].hidden = 1;
662         prompts[1].prompt = "Repeat new password: ";
663         prompts[1].reply  = &password_data[1];
664         prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
665
666         ret = (*prompter) (context, data, NULL, "Changing password",
667                            2, prompts);
668         if (ret) {
669             memset (buf1, 0, sizeof(buf1));
670             memset (buf2, 0, sizeof(buf2));
671             goto out;
672         }
673
674         if (strcmp (buf1, buf2) == 0)
675             break;
676         memset (buf1, 0, sizeof(buf1));
677         memset (buf2, 0, sizeof(buf2));
678     }
679
680     ret = krb5_set_password (context,
681                              &cpw_cred,
682                              buf1,
683                              client,
684                              &result_code,
685                              &result_code_string,
686                              &result_string);
687     if (ret)
688         goto out;
689
690     if (asprintf(&p, "%s: %.*s\n",
691                  result_code ? "Error" : "Success",
692                  (int)result_string.length,
693                  result_string.length > 0 ? (char*)result_string.data : "") < 0)
694     {
695         ret = krb5_enomem(context);
696         goto out;
697     }
698
699     /* return the result */
700     (*prompter) (context, data, NULL, p, 0, NULL);
701
702     if (result_code == 0) {
703         strlcpy (newpw, buf1, newpw_sz);
704         ret = 0;
705     } else {
706         ret = ENOTTY;
707         krb5_set_error_message(context, ret,
708                                N_("failed changing password: %s", ""), p);
709     }
710     free (p);
711
712 out:
713     memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
714     memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
715     krb5_data_free (&result_string);
716     krb5_data_free (&result_code_string);
717     krb5_free_cred_contents (context, &cpw_cred);
718     return ret;
719 }
720
721
722 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
723 krb5_keyblock_key_proc (krb5_context context,
724                         krb5_keytype type,
725                         krb5_data *salt,
726                         krb5_const_pointer keyseed,
727                         krb5_keyblock **key)
728 {
729     return krb5_copy_keyblock (context, keyseed, key);
730 }
731
732 /*
733  *
734  */
735
736 static krb5_error_code
737 init_as_req (krb5_context context,
738              KDCOptions opts,
739              const krb5_creds *creds,
740              const krb5_addresses *addrs,
741              const krb5_enctype *etypes,
742              AS_REQ *a)
743 {
744     krb5_error_code ret;
745
746     memset(a, 0, sizeof(*a));
747
748     a->pvno = 5;
749     a->msg_type = krb_as_req;
750     a->req_body.kdc_options = opts;
751     a->req_body.cname = calloc(1, sizeof(*a->req_body.cname));
752     if (a->req_body.cname == NULL) {
753         ret = krb5_enomem(context);
754         goto fail;
755     }
756     a->req_body.sname = calloc(1, sizeof(*a->req_body.sname));
757     if (a->req_body.sname == NULL) {
758         ret = krb5_enomem(context);
759         goto fail;
760     }
761
762     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
763     if (ret)
764         goto fail;
765     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
766     if (ret)
767         goto fail;
768
769     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
770     if (ret)
771         goto fail;
772
773     if(creds->times.starttime) {
774         a->req_body.from = malloc(sizeof(*a->req_body.from));
775         if (a->req_body.from == NULL) {
776             ret = krb5_enomem(context);
777             goto fail;
778         }
779         *a->req_body.from = creds->times.starttime;
780     }
781     if(creds->times.endtime){
782         if ((ALLOC(a->req_body.till, 1)) != NULL)
783             *a->req_body.till = creds->times.endtime;
784         else {
785             ret = krb5_enomem(context);
786             goto fail;
787         }
788     }
789     if(creds->times.renew_till){
790         a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
791         if (a->req_body.rtime == NULL) {
792             ret = krb5_enomem(context);
793             goto fail;
794         }
795         *a->req_body.rtime = creds->times.renew_till;
796     }
797     a->req_body.nonce = 0;
798     ret = _krb5_init_etype(context,
799                            KRB5_PDU_AS_REQUEST,
800                            &a->req_body.etype.len,
801                            &a->req_body.etype.val,
802                            etypes);
803     if (ret)
804         goto fail;
805
806     /*
807      * This means no addresses
808      */
809
810     if (addrs && addrs->len == 0) {
811         a->req_body.addresses = NULL;
812     } else {
813         a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
814         if (a->req_body.addresses == NULL) {
815             ret = krb5_enomem(context);
816             goto fail;
817         }
818
819         if (addrs)
820             ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
821         else {
822             ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
823             if(ret == 0 && a->req_body.addresses->len == 0) {
824                 free(a->req_body.addresses);
825                 a->req_body.addresses = NULL;
826             }
827         }
828         if (ret)
829             goto fail;
830     }
831
832     a->req_body.enc_authorization_data = NULL;
833     a->req_body.additional_tickets = NULL;
834
835     a->padata = NULL;
836
837     return 0;
838  fail:
839     free_AS_REQ(a);
840     memset_s(a, sizeof(*a), 0, sizeof(*a));
841     return ret;
842 }
843
844
845 static krb5_error_code
846 set_paid(struct pa_info_data *paid, krb5_context context,
847          krb5_enctype etype,
848          krb5_salttype salttype, void *salt_string, size_t salt_len,
849          krb5_data *s2kparams)
850 {
851     paid->etype = etype;
852     paid->salt.salttype = salttype;
853     paid->salt.saltvalue.data = malloc(salt_len + 1);
854     if (paid->salt.saltvalue.data == NULL) {
855         krb5_clear_error_message(context);
856         return krb5_enomem(context);
857     }
858     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
859     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
860     paid->salt.saltvalue.length = salt_len;
861     if (s2kparams) {
862         krb5_error_code ret;
863
864         ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
865         if (ret) {
866             krb5_clear_error_message(context);
867             krb5_free_salt(context, paid->salt);
868             return ret;
869         }
870     } else
871         paid->s2kparams = NULL;
872
873     return 0;
874 }
875
876 static struct pa_info_data *
877 pa_etype_info2(krb5_context context,
878                const krb5_principal client,
879                const AS_REQ *asreq,
880                struct pa_info_data *paid,
881                heim_octet_string *data)
882 {
883     krb5_error_code ret;
884     ETYPE_INFO2 e;
885     size_t sz;
886     size_t i, j;
887
888     memset(&e, 0, sizeof(e));
889     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
890     if (ret)
891         goto out;
892     if (e.len == 0)
893         goto out;
894     for (j = 0; j < asreq->req_body.etype.len; j++) {
895         for (i = 0; i < e.len; i++) {
896
897             if (krb5_enctype_valid(context, e.val[i].etype) != 0)
898                 continue;
899
900             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
901                 krb5_salt salt;
902                 if (e.val[i].salt == NULL)
903                     ret = krb5_get_pw_salt(context, client, &salt);
904                 else {
905                     salt.saltvalue.data = *e.val[i].salt;
906                     salt.saltvalue.length = strlen(*e.val[i].salt);
907                     ret = 0;
908                 }
909                 if (ret == 0)
910                     ret = set_paid(paid, context, e.val[i].etype,
911                                    KRB5_PW_SALT,
912                                    salt.saltvalue.data,
913                                    salt.saltvalue.length,
914                                    e.val[i].s2kparams);
915                 if (e.val[i].salt == NULL)
916                     krb5_free_salt(context, salt);
917                 if (ret == 0) {
918                     free_ETYPE_INFO2(&e);
919                     return paid;
920                 }
921             }
922         }
923     }
924  out:
925     free_ETYPE_INFO2(&e);
926     return NULL;
927 }
928
929 static struct pa_info_data *
930 pa_etype_info(krb5_context context,
931               const krb5_principal client,
932               const AS_REQ *asreq,
933               struct pa_info_data *paid,
934               heim_octet_string *data)
935 {
936     krb5_error_code ret;
937     ETYPE_INFO e;
938     size_t sz;
939     size_t i, j;
940
941     memset(&e, 0, sizeof(e));
942     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
943     if (ret)
944         goto out;
945     if (e.len == 0)
946         goto out;
947     for (j = 0; j < asreq->req_body.etype.len; j++) {
948         for (i = 0; i < e.len; i++) {
949
950             if (krb5_enctype_valid(context, e.val[i].etype) != 0)
951                 continue;
952
953             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
954                 krb5_salt salt;
955                 salt.salttype = KRB5_PW_SALT;
956                 if (e.val[i].salt == NULL)
957                     ret = krb5_get_pw_salt(context, client, &salt);
958                 else {
959                     salt.saltvalue = *e.val[i].salt;
960                     ret = 0;
961                 }
962                 if (e.val[i].salttype)
963                     salt.salttype = *e.val[i].salttype;
964                 if (ret == 0) {
965                     ret = set_paid(paid, context, e.val[i].etype,
966                                    salt.salttype,
967                                    salt.saltvalue.data,
968                                    salt.saltvalue.length,
969                                    NULL);
970                     if (e.val[i].salt == NULL)
971                         krb5_free_salt(context, salt);
972                 }
973                 if (ret == 0) {
974                     free_ETYPE_INFO(&e);
975                     return paid;
976                 }
977             }
978         }
979     }
980  out:
981     free_ETYPE_INFO(&e);
982     return NULL;
983 }
984
985 static struct pa_info_data *
986 pa_pw_or_afs3_salt(krb5_context context,
987                    const krb5_principal client,
988                    const AS_REQ *asreq,
989                    struct pa_info_data *paid,
990                    heim_octet_string *data)
991 {
992     krb5_error_code ret;
993     if (paid->etype == KRB5_ENCTYPE_NULL)
994         return NULL;
995     if (krb5_enctype_valid(context, paid->etype) != 0)
996         return NULL;
997
998     ret = set_paid(paid, context,
999                    paid->etype,
1000                    paid->salt.salttype,
1001                    data->data,
1002                    data->length,
1003                    NULL);
1004     if (ret)
1005         return NULL;
1006     return paid;
1007 }
1008
1009
1010 static krb5_error_code
1011 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
1012                       krb5_enctype etype, krb5_keyblock *key)
1013 {
1014     PA_ENC_TS_ENC p;
1015     unsigned char *buf;
1016     size_t buf_size;
1017     size_t len = 0;
1018     EncryptedData encdata;
1019     krb5_error_code ret;
1020     int32_t usec;
1021     int usec2;
1022     krb5_crypto crypto;
1023
1024     krb5_us_timeofday (context, &p.patimestamp, &usec);
1025     usec2         = usec;
1026     p.pausec      = &usec2;
1027
1028     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1029     if (ret)
1030         return ret;
1031     if(buf_size != len)
1032         krb5_abortx(context, "internal error in ASN.1 encoder");
1033
1034     ret = krb5_crypto_init(context, key, 0, &crypto);
1035     if (ret) {
1036         free(buf);
1037         return ret;
1038     }
1039     ret = krb5_encrypt_EncryptedData(context,
1040                                      crypto,
1041                                      KRB5_KU_PA_ENC_TIMESTAMP,
1042                                      buf,
1043                                      len,
1044                                      0,
1045                                      &encdata);
1046     free(buf);
1047     krb5_crypto_destroy(context, crypto);
1048     if (ret)
1049         return ret;
1050
1051     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1052     free_EncryptedData(&encdata);
1053     if (ret)
1054         return ret;
1055     if(buf_size != len)
1056         krb5_abortx(context, "internal error in ASN.1 encoder");
1057
1058     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1059     if (ret)
1060         free(buf);
1061     return ret;
1062 }
1063
1064 static krb5_error_code
1065 add_enc_ts_padata(krb5_context context,
1066                   METHOD_DATA *md,
1067                   krb5_principal client,
1068                   krb5_s2k_proc keyproc,
1069                   krb5_const_pointer keyseed,
1070                   krb5_enctype *enctypes,
1071                   unsigned netypes,
1072                   krb5_salt *salt,
1073                   krb5_data *s2kparams)
1074 {
1075     krb5_error_code ret;
1076     krb5_salt salt2;
1077     krb5_enctype *ep;
1078     size_t i;
1079
1080     memset(&salt2, 0, sizeof(salt2));
1081
1082     if(salt == NULL) {
1083         /* default to standard salt */
1084         ret = krb5_get_pw_salt (context, client, &salt2);
1085         if (ret)
1086             return ret;
1087         salt = &salt2;
1088     }
1089     if (!enctypes) {
1090         enctypes = context->etypes;
1091         netypes = 0;
1092         for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1093             netypes++;
1094     }
1095
1096     for (i = 0; i < netypes; ++i) {
1097         krb5_keyblock *key;
1098
1099         _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1100
1101         ret = (*keyproc)(context, enctypes[i], keyseed,
1102                          *salt, s2kparams, &key);
1103         if (ret)
1104             continue;
1105         ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1106         krb5_free_keyblock (context, key);
1107         if (ret)
1108             return ret;
1109     }
1110     if(salt == &salt2)
1111         krb5_free_salt(context, salt2);
1112     return 0;
1113 }
1114
1115 static krb5_error_code
1116 pa_data_to_md_ts_enc(krb5_context context,
1117                      const AS_REQ *a,
1118                      const krb5_principal client,
1119                      krb5_init_creds_context ctx,
1120                      struct pa_info_data *ppaid,
1121                      METHOD_DATA *md)
1122 {
1123     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1124         return 0;
1125
1126     if (ppaid) {
1127         add_enc_ts_padata(context, md, client,
1128                           ctx->keyproc, ctx->keyseed,
1129                           &ppaid->etype, 1,
1130                           &ppaid->salt, ppaid->s2kparams);
1131     } else {
1132         krb5_salt salt;
1133
1134         _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1135
1136         /* make a v5 salted pa-data */
1137         add_enc_ts_padata(context, md, client,
1138                           ctx->keyproc, ctx->keyseed,
1139                           a->req_body.etype.val, a->req_body.etype.len,
1140                           NULL, NULL);
1141
1142         /* make a v4 salted pa-data */
1143         salt.salttype = KRB5_PW_SALT;
1144         krb5_data_zero(&salt.saltvalue);
1145         add_enc_ts_padata(context, md, client,
1146                           ctx->keyproc, ctx->keyseed,
1147                           a->req_body.etype.val, a->req_body.etype.len,
1148                           &salt, NULL);
1149     }
1150     return 0;
1151 }
1152
1153 static krb5_error_code
1154 pa_data_to_key_plain(krb5_context context,
1155                      const krb5_principal client,
1156                      krb5_init_creds_context ctx,
1157                      krb5_salt salt,
1158                      krb5_data *s2kparams,
1159                      krb5_enctype etype,
1160                      krb5_keyblock **key)
1161 {
1162     krb5_error_code ret;
1163
1164     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1165                            salt, s2kparams, key);
1166     return ret;
1167 }
1168
1169 struct pkinit_context {
1170     unsigned int win2k : 1;
1171     unsigned int used_pkinit : 1;
1172 };
1173
1174
1175 static krb5_error_code
1176 pa_data_to_md_pkinit(krb5_context context,
1177                      const AS_REQ *a,
1178                      const krb5_principal client,
1179                      int win2k,
1180                      krb5_init_creds_context ctx,
1181                      METHOD_DATA *md)
1182 {
1183     if (ctx->pk_init_ctx == NULL)
1184         return 0;
1185 #ifdef PKINIT
1186     return _krb5_pk_mk_padata(context,
1187                               ctx->pk_init_ctx,
1188                               ctx->ic_flags,
1189                               win2k,
1190                               &a->req_body,
1191                               ctx->pk_nonce,
1192                               md);
1193 #else
1194     krb5_set_error_message(context, EINVAL,
1195                            N_("no support for PKINIT compiled in", ""));
1196     return EINVAL;
1197 #endif
1198 }
1199
1200 static krb5_error_code
1201 pkinit_configure_ietf(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1202 {
1203     struct pkinit_context *pkinit_ctx = pa_ctx;
1204
1205     pkinit_ctx->win2k = 0;
1206
1207     if (ctx->pk_init_ctx == NULL)
1208         return HEIM_ERR_PA_CANT_CONTINUE;
1209
1210     return 0;
1211 }
1212
1213 static krb5_error_code
1214 pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1215 {
1216     struct pkinit_context *pkinit_ctx = pa_ctx;
1217
1218     pkinit_ctx->win2k = 1;
1219     pkinit_ctx->used_pkinit = 0;
1220
1221     if (ctx->pk_init_ctx == NULL)
1222         return HEIM_ERR_PA_CANT_CONTINUE;
1223
1224     return 0;
1225 }
1226
1227 static krb5_error_code
1228 pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1229             const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md)
1230 {
1231     krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
1232     struct pkinit_context *pkinit_ctx = pa_ctx;
1233
1234     if (rep == NULL) {
1235         if (pkinit_ctx->used_pkinit) {
1236             krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1237                                    "Already tried PKINIT(%s), looping",
1238                                    pkinit_ctx->win2k ? "win2k" : "ietf");
1239         } else {
1240             ret = pa_data_to_md_pkinit(context, a, ctx->cred.client,
1241                                        (pkinit_ctx->win2k != 0),
1242                                        ctx, out_md);
1243             if (ret == 0)
1244                 ret = HEIM_ERR_PA_CONTINUE_NEEDED;
1245
1246             pkinit_ctx->used_pkinit = 1;
1247         }
1248     } else if (pa) {
1249         ret = _krb5_pk_rd_pa_reply(context,
1250                                    a->req_body.realm,
1251                                    ctx->pk_init_ctx,
1252                                    rep->enc_part.etype,
1253                                    hi,
1254                                    ctx->pk_nonce,
1255                                    &ctx->req_buffer,
1256                                    pa,
1257                                    &ctx->fast_state.reply_key);
1258         if (ret == 0)
1259             ctx->runflags.allow_save_as_reply_key = 1;
1260     }
1261
1262     return ret;
1263 }
1264
1265 static void
1266 pkinit_release(void *pa_ctx)
1267 {
1268 }
1269
1270 /*
1271  * GSS-API pre-authentication support
1272  */
1273
1274 struct pa_gss_context {
1275     struct gss_ctx_id_t_desc_struct *context_handle;
1276     int open;
1277 };
1278
1279 static krb5_error_code
1280 pa_gss_configure(krb5_context context,
1281                  krb5_init_creds_context ctx,
1282                  void *pa_ctx)
1283 {
1284     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1285     struct pa_gss_context *pa_gss_ctx = pa_ctx;
1286
1287     if (gssic == NULL)
1288         return HEIM_ERR_PA_CANT_CONTINUE;
1289
1290     pa_gss_ctx->context_handle = NULL;
1291     pa_gss_ctx->open = 0;
1292
1293     return 0;
1294 }
1295
1296 static krb5_error_code
1297 pa_data_to_md_gss(krb5_context context,
1298                   const AS_REQ *a,
1299                   const krb5_creds *creds,
1300                   krb5_init_creds_context ctx,
1301                   struct pa_gss_context *pa_gss_ctx,
1302                   PA_DATA *pa,
1303                   METHOD_DATA *out_md)
1304 {
1305     krb5_error_code ret;
1306     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1307     krb5_data req_body;
1308     krb5_data *input_token, output_token;
1309     size_t len = 0;
1310
1311     krb5_data_zero(&req_body);
1312     krb5_data_zero(&output_token);
1313
1314     input_token = pa ? &pa->padata_value : NULL;
1315
1316     if ((input_token == NULL || input_token->length == 0) &&
1317         pa_gss_ctx->context_handle) {
1318         krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1319                                "Missing GSS preauthentication data from KDC");
1320         return HEIM_ERR_PA_CANT_CONTINUE;
1321     }
1322
1323     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
1324                        &ctx->as_req.req_body, &len, ret);
1325     if (ret)
1326         goto out;
1327     heim_assert(req_body.length == len, "ASN.1 internal error");
1328
1329     ret = gssic->step(context, gssic, creds, &pa_gss_ctx->context_handle,
1330                       ctx->flags, &req_body,
1331                       input_token, &output_token);
1332
1333     /*
1334      * If FAST authenticated the KDC (which will be the case unless anonymous
1335      * PKINIT was used without KDC certificate validation) then we can relax
1336      * the mutual authentication requirement.
1337      */
1338     if (ret == KRB5_MUTUAL_FAILED &&
1339         (ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1340         (ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
1341         ret = 0;
1342     if (ret == 0) {
1343         /*
1344          * Always require a strengthen key if FAST was used, to avoid a MITM
1345          * attack that could result in unintended privilege escalation should
1346          * the KDC add positive authorization data from the armor ticket.
1347          */
1348         if ((ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1349             ctx->fast_state.strengthen_key == NULL) {
1350             krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1351                                    "FAST GSS pre-authentication without strengthen key");
1352             ret = KRB5_KDCREP_MODIFIED;
1353             goto out;
1354         }
1355
1356         pa_gss_ctx->open = 1;
1357     }
1358
1359     if (output_token.length) {
1360         ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
1361                               output_token.data, output_token.length);
1362         if (ret)
1363             goto out;
1364
1365         krb5_data_zero(&output_token);
1366     }
1367
1368 out:
1369     krb5_data_free(&output_token);
1370     krb5_data_free(&req_body);
1371
1372     return ret;
1373 }
1374
1375 static krb5_error_code
1376 pa_gss_step(krb5_context context,
1377             krb5_init_creds_context ctx,
1378             void *pa_ctx,
1379             PA_DATA *pa,
1380             const AS_REQ *a,
1381             const AS_REP *rep,
1382             const krb5_krbhst_info *hi,
1383             METHOD_DATA *in_md,
1384             METHOD_DATA *out_md)
1385 {
1386     krb5_error_code ret;
1387     krb5_principal cname;
1388     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1389     struct pa_gss_context *pa_gss_ctx = pa_ctx;
1390
1391     heim_assert(gssic != NULL, "invalid context passed to pa_gss_step");
1392
1393     if (!pa_gss_ctx->open) {
1394         ret = pa_data_to_md_gss(context, a, &ctx->cred, ctx,
1395                                 pa_gss_ctx, pa, out_md);
1396         if (ret == HEIM_ERR_PA_CONTINUE_NEEDED && rep) {
1397             krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1398                                    "KDC sent AS-REP before GSS "
1399                                    "pre-authentication completed");
1400             ret = KRB5_KDCREP_MODIFIED;
1401         } else if (ret == 0 && rep == NULL) {
1402             ret = HEIM_ERR_PA_CONTINUE_NEEDED; /* odd number of legs */
1403         }
1404         if (ret)
1405             return ret;
1406     } else if (pa && pa->padata_value.length) {
1407         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1408                                "Already completed GSS pre-authentication");
1409         return KRB5_GET_IN_TKT_LOOP;
1410     } else if (rep == NULL) {
1411         krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1412                                "Completed GSS pre-authentication before KDC");
1413         return KRB5_PREAUTH_FAILED;
1414     }
1415
1416     heim_assert(pa_gss_ctx->open,
1417                 "GSS pre-authentication incomplete");
1418
1419     ret = gssic->finish(context, gssic, &ctx->cred,
1420                         pa_gss_ctx->context_handle, ctx->nonce,
1421                         rep->enc_part.etype, &cname,
1422                         &ctx->fast_state.reply_key);
1423     if (ret)
1424         return ret;
1425
1426     {
1427         char *from = NULL;
1428         char *to = NULL;
1429
1430         if (krb5_unparse_name(context, ctx->cred.client, &from) == 0 &&
1431             krb5_unparse_name(context, cname, &to) == 0) {
1432             _krb5_debug(context, 1, "pa_gss_step: %s as %s",
1433                         from, to);
1434             krb5_xfree(from);
1435             krb5_xfree(to);
1436         }
1437     }
1438
1439     if (krb5_principal_is_federated(context, ctx->cred.client)) {
1440         /*
1441          * The well-known federated name will be replaced with the cname
1442          * in the AS-REP, but save the locally mapped initiator name in the
1443          * cred for logging.
1444          */
1445         krb5_free_principal(context, ctx->cred.client);
1446         ctx->cred.client = cname;
1447
1448         ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
1449     } else {
1450         krb5_free_principal(context, cname);
1451     }
1452
1453     ctx->runflags.allow_save_as_reply_key = 1;
1454
1455     gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1456     pa_gss_ctx->context_handle = NULL;
1457     pa_gss_ctx->open = 0;
1458
1459     return 0;
1460 }
1461
1462 static krb5_error_code
1463 pa_gss_restart(krb5_context context,
1464                krb5_init_creds_context ctx,
1465                void *pa_ctx)
1466 {
1467     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1468     struct pa_gss_context *pa_gss_ctx = pa_ctx;
1469
1470     if (gssic == NULL)
1471         return HEIM_ERR_PA_CANT_CONTINUE;
1472
1473     gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1474     pa_gss_ctx->context_handle = NULL;
1475     pa_gss_ctx->open = 0;
1476
1477     return 0;
1478 }
1479
1480 static void
1481 pa_gss_release(void *pa_ctx)
1482 {
1483 }
1484
1485 krb5_error_code
1486 _krb5_make_pa_enc_challenge(krb5_context context,
1487                             krb5_crypto crypto,
1488                             krb5_key_usage usage,
1489                             METHOD_DATA *md)
1490 {
1491     PA_ENC_TS_ENC p;
1492     unsigned char *buf;
1493     size_t buf_size;
1494     size_t len = 0;
1495     EncryptedData encdata;
1496     krb5_error_code ret;
1497     int32_t usec;
1498     int usec2;
1499
1500     krb5_us_timeofday (context, &p.patimestamp, &usec);
1501     usec2         = usec;
1502     p.pausec      = &usec2;
1503
1504     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1505     if (ret)
1506         return ret;
1507     if(buf_size != len)
1508         krb5_abortx(context, "internal error in ASN.1 encoder");
1509
1510     ret = krb5_encrypt_EncryptedData(context,
1511                                      crypto,
1512                                      usage,
1513                                      buf,
1514                                      len,
1515                                      0,
1516                                      &encdata);
1517     free(buf);
1518     if (ret)
1519         return ret;
1520
1521     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1522     free_EncryptedData(&encdata);
1523     if (ret)
1524         return ret;
1525     if(buf_size != len)
1526         krb5_abortx(context, "internal error in ASN.1 encoder");
1527
1528     ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len);
1529     if (ret)
1530         free(buf);
1531     return ret;
1532 }
1533
1534 krb5_error_code
1535 _krb5_validate_pa_enc_challenge(krb5_context context,
1536                                 krb5_crypto crypto,
1537                                 krb5_key_usage usage,
1538                                 EncryptedData *enc_data,
1539                                 const char *peer_name)
1540 {
1541     krb5_error_code ret;
1542     krb5_data ts_data;
1543     PA_ENC_TS_ENC p;
1544     time_t timestamp;
1545     int32_t usec;
1546     size_t size;
1547
1548     ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data);
1549     if (ret)
1550         return ret;
1551
1552     ret = decode_PA_ENC_TS_ENC(ts_data.data,
1553                                ts_data.length,
1554                                &p,
1555                                &size);
1556     krb5_data_free(&ts_data);
1557     if(ret){
1558         ret = KRB5KDC_ERR_PREAUTH_FAILED;
1559         _krb5_debug(context, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name);
1560         goto out;
1561     }
1562
1563     krb5_us_timeofday(context, &timestamp, &usec);
1564
1565     if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) {
1566         char client_time[100];
1567
1568         krb5_format_time(context, p.patimestamp,
1569                          client_time, sizeof(client_time), TRUE);
1570
1571         ret = KRB5KRB_AP_ERR_SKEW;
1572         _krb5_debug(context, 0, "Too large time skew, "
1573                     "client time %s is out by %u > %d seconds -- %s",
1574                     client_time,
1575                     (unsigned)krb5_time_abs(timestamp, p.patimestamp),
1576                     (int)context->max_skew,
1577                     peer_name);
1578     } else {
1579         ret = 0;
1580     }
1581
1582  out:
1583     free_PA_ENC_TS_ENC(&p);
1584
1585     return ret;
1586 }
1587
1588
1589 static struct pa_info_data *
1590 process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *);
1591
1592
1593 static krb5_error_code
1594 enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1595               const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md)
1596 {
1597     struct pa_info_data paid, *ppaid;
1598     krb5_keyblock challengekey;
1599     krb5_data pepper1, pepper2;
1600     krb5_crypto crypto = NULL;
1601     krb5_enctype aenctype;
1602     krb5_error_code ret;
1603
1604     memset(&paid, 0, sizeof(paid));
1605
1606     if (rep == NULL)
1607         paid.etype = KRB5_ENCTYPE_NULL;
1608     else
1609         paid.etype = rep->enc_part.etype;
1610     ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1611
1612     /*
1613      * If we don't have ppaid, ts because the KDC have not sent any
1614      * salt info, lets to the first roundtrip so the KDC have a chance
1615      * to send any.
1616      */
1617     if (ppaid == NULL) {
1618         _krb5_debug(context, 5, "no ppaid found");
1619         return HEIM_ERR_PA_CONTINUE_NEEDED;
1620     }
1621     if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1622         return HEIM_ERR_PA_CANT_CONTINUE;
1623     }
1624
1625     if (ctx->fast_state.reply_key)
1626         krb5_free_keyblock(context, ctx->fast_state.reply_key);
1627
1628     ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1629                                ppaid->salt, ppaid->s2kparams, ppaid->etype,
1630                                &ctx->fast_state.reply_key);
1631     free_paid(context, &paid);
1632     if (ret) {
1633         _krb5_debug(context, 5, "enc-chal: failed to build key");
1634         return ret;
1635     }
1636
1637     ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto);
1638     if (ret)
1639         return ret;
1640
1641     krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype);
1642
1643     pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor";
1644     pepper1.length = strlen(pepper1.data);
1645     pepper2.data = "challengelongterm";
1646     pepper2.length = strlen(pepper2.data);
1647
1648     ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto,
1649                              &pepper1, &pepper2, aenctype,
1650                              &challengekey);
1651     krb5_crypto_destroy(context, crypto);
1652     if (ret)
1653         return ret;
1654
1655     ret = krb5_crypto_init(context, &challengekey, 0, &crypto);
1656     krb5_free_keyblock_contents(context, &challengekey);
1657     if (ret)
1658         return ret;
1659
1660     if (rep) {
1661         EncryptedData enc_data;
1662         size_t size;
1663
1664         if (ret) {
1665             _krb5_debug(context, 5, "enc-chal: failed to create reply key");
1666             return ret;
1667         }
1668
1669         _krb5_debug(context, 5, "ENC_CHAL rep key");
1670
1671         if (ctx->fast_state.strengthen_key == NULL) {
1672             krb5_crypto_destroy(context, crypto);
1673             _krb5_debug(context, 5, "ENC_CHAL w/o strengthen_key");
1674             return KRB5_KDCREP_MODIFIED;
1675         }
1676
1677         if (pa == NULL) {
1678             krb5_crypto_destroy(context, crypto);
1679             _krb5_debug(context, 0, "KDC response missing");
1680             return HEIM_ERR_PA_CANT_CONTINUE;
1681         }
1682
1683         ret = decode_EncryptedData(pa->padata_value.data,
1684                                    pa->padata_value.length,
1685                                    &enc_data,
1686                                    &size);
1687         if (ret) {
1688             ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1689             _krb5_debug(context, 5, "Failed to decode ENC_CHAL KDC reply");
1690             return ret;
1691         }
1692
1693         ret = _krb5_validate_pa_enc_challenge(context, crypto,
1694                                               KRB5_KU_ENC_CHALLENGE_KDC,
1695                                               &enc_data,
1696                                               "KDC");
1697         free_EncryptedData(&enc_data);
1698         krb5_crypto_destroy(context, crypto);
1699
1700         return ret;
1701
1702     } else {
1703
1704         ret = _krb5_make_pa_enc_challenge(context, crypto,
1705                                           KRB5_KU_ENC_CHALLENGE_CLIENT,
1706                                           out_md);
1707         krb5_crypto_destroy(context, crypto);
1708         if (ret) {
1709             _krb5_debug(context, 5, "enc-chal: failed build enc challenge");
1710             return ret;
1711         }
1712
1713         return HEIM_ERR_PA_CONTINUE_NEEDED;
1714     }
1715 }
1716
1717 struct enc_ts_context {
1718     int used_pa_types;
1719 #define  USED_ENC_TS_GUESS      4
1720 #define  USED_ENC_TS_INFO       8
1721 #define  USED_ENC_TS_RENEG      16
1722     krb5_principal user;
1723 };
1724
1725 static krb5_error_code
1726 enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1727 {
1728     struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1729     pactx->used_pa_types = 0;
1730     krb5_free_principal(context, pactx->user);
1731     pactx->user = NULL;
1732     return 0;
1733 }
1734
1735 static krb5_error_code
1736 enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa,
1737             const AS_REQ *a,
1738             const AS_REP *rep,
1739             const krb5_krbhst_info *hi,
1740             METHOD_DATA *in_md, METHOD_DATA *out_md)
1741 {
1742     struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1743     struct pa_info_data paid, *ppaid;
1744     krb5_error_code ret;
1745     const char *state;
1746     unsigned flag;
1747
1748     /*
1749      * Keep track of the user we used so that we can restart
1750      * authentication when we get referrals.
1751      */
1752
1753     if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) {
1754         pactx->used_pa_types = 0;
1755         krb5_free_principal(context, pactx->user);
1756         pactx->user = NULL;
1757     }
1758
1759     if (pactx->user == NULL) {
1760         ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user);
1761         if (ret)
1762             return ret;
1763     }
1764
1765     memset(&paid, 0, sizeof(paid));
1766
1767     if (rep == NULL)
1768         paid.etype = KRB5_ENCTYPE_NULL;
1769     else
1770         paid.etype = rep->enc_part.etype;
1771
1772     ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1773
1774     if (rep) {
1775         /*
1776          * Some KDC's don't send salt info in the reply when there is
1777          * success pre-auth happned before, so use cached copy (or
1778          * even better, if there is just one pre-auth, save reply-key).
1779          */
1780         if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) {
1781             ppaid = &ctx->paid;
1782
1783         } else if (ppaid == NULL) {
1784             _krb5_debug(context, 0, "no paid when building key, build a default salt structure ?");
1785             return HEIM_ERR_PA_CANT_CONTINUE;
1786         }
1787
1788         ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1789                                    ppaid->salt, ppaid->s2kparams, rep->enc_part.etype,
1790                                    &ctx->fast_state.reply_key);
1791         free_paid(context, &paid);
1792         return ret;
1793     }
1794
1795     /*
1796      * If we don't have ppaid, ts because the KDC have not sent any
1797      * salt info, lets to the first roundtrip so the KDC have a chance
1798      * to send any.
1799      *
1800      * Don't bother guessing, it sounds like a good idea until you run
1801      * into KDCs that are doing failed auth counting based on the
1802      * ENC_TS tries.
1803      *
1804      * Stashing the salt for the next run is a diffrent issue and
1805      * could be considered in the future.
1806      */
1807
1808     if (ppaid == NULL) {
1809         _krb5_debug(context, 5,
1810                      "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}");
1811         return HEIM_ERR_PA_CONTINUE_NEEDED;
1812     }
1813     if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1814         free_paid(context, &paid);
1815         _krb5_debug(context, 5,
1816                      "TS-ENC: kdc proposes enctype NULL ?");
1817         return HEIM_ERR_PA_CANT_CONTINUE;
1818     }
1819
1820     /*
1821      * We have to allow the KDC to re-negotiate the PA-TS data
1822      * once, this is since the in the case of a windows read only
1823      * KDC that doesn't have the keys simply guesses what the
1824      * master is supposed to support. In the case where this
1825      * breaks in when the RO-KDC is a newer version the the RW-KDC
1826      * and the RO-KDC announced a enctype that the older doesn't
1827      * support.
1828      */
1829     if (pactx->used_pa_types & USED_ENC_TS_INFO) {
1830         flag = USED_ENC_TS_RENEG;
1831         state = "reneg";
1832     } else {
1833         flag = USED_ENC_TS_INFO;
1834         state = "info";
1835     }
1836
1837     if (pactx->used_pa_types & flag) {
1838         free_paid(context, &paid);
1839         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1840                                "Already tried ENC-TS-%s, looping", state);
1841         return KRB5_GET_IN_TKT_LOOP;
1842     }
1843
1844     pactx->used_pa_types |= flag;
1845
1846     free_paid(context, &ctx->paid);
1847     ctx->paid = *ppaid;
1848
1849     ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md);
1850     if (ret)
1851         return ret;
1852
1853     return HEIM_ERR_PA_CONTINUE_NEEDED;
1854 }
1855
1856 static void
1857 enc_ts_release(void *pa_ctx)
1858 {
1859     struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1860
1861     if (pactx->user)
1862         krb5_free_principal(NULL, pactx->user);
1863 }
1864
1865 static krb5_error_code
1866 pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1867             const AS_REP *rep, const krb5_krbhst_info *hi,
1868             METHOD_DATA *in_md, METHOD_DATA *out_md)
1869 {
1870     size_t len = 0, length;
1871     krb5_error_code ret;
1872     PA_PAC_REQUEST req;
1873     void *buf;
1874
1875     switch (ctx->req_pac) {
1876     case KRB5_INIT_CREDS_TRISTATE_UNSET:
1877         return 0; /* don't bother */
1878     case KRB5_INIT_CREDS_TRISTATE_TRUE:
1879         req.include_pac = 1;
1880         break;
1881     case KRB5_INIT_CREDS_TRISTATE_FALSE:
1882         req.include_pac = 0;
1883     }
1884
1885     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1886                        &req, &len, ret);
1887     if (ret)
1888         return ret;
1889     heim_assert(len == length, "internal error in ASN.1 encoder");
1890
1891     ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1892     if (ret)
1893         free(buf);
1894
1895     return 0;
1896 }
1897
1898 static krb5_error_code
1899 pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1900                    const AS_REP *rep, const krb5_krbhst_info *hi,
1901                    METHOD_DATA *in_md, METHOD_DATA *out_md)
1902 {
1903     if (ctx->runflags.allow_enc_pa_rep)
1904         return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1905
1906     return 0;
1907 }
1908
1909 static krb5_error_code
1910 pa_fx_cookie_step(krb5_context context,
1911                   krb5_init_creds_context ctx,
1912                   void *pa_ctx,
1913                   PA_DATA *pa,
1914                   const AS_REQ *a,
1915                   const AS_REP *rep,
1916                   const krb5_krbhst_info *hi,
1917                   METHOD_DATA *in_md,
1918                   METHOD_DATA *out_md)
1919 {
1920     krb5_error_code ret;
1921     void *cookie;
1922     PA_DATA *pad;
1923     int idx = 0;
1924
1925     pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx);
1926     if (pad == NULL) {
1927         /*
1928          * RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
1929          * expects at least one more message from the client.
1930          */
1931         if (ctx->error.error_code == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
1932             return KRB5_PREAUTH_FAILED;
1933         else
1934             return 0;
1935     }
1936
1937     cookie = malloc(pad->padata_value.length);
1938     if (cookie == NULL)
1939         return krb5_enomem(context);
1940
1941     memcpy(cookie, pad->padata_value.data, pad->padata_value.length);
1942
1943     ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE,
1944                           cookie, pad->padata_value.length);
1945     if (ret)
1946         free(cookie);
1947     else
1948         _krb5_debug(context, 5, "Mirrored FX-COOKIE to KDC");
1949
1950     return ret;
1951 }
1952
1953 typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *);
1954 typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *);
1955 typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *);
1956 typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, const krb5_krbhst_info *, METHOD_DATA *, METHOD_DATA *);
1957 typedef void            (*pa_release_f)(void *);
1958
1959 struct patype {
1960     int type;
1961     char *name;
1962     int flags;
1963 #define PA_F_ANNOUNCE           1
1964 #define PA_F_CONFIG             2
1965 #define PA_F_FAST               4 /* available inside FAST */
1966 #define PA_F_NOT_FAST           8 /* only available without FAST */
1967     size_t pa_ctx_size;
1968     pa_salt_info_f salt_info;
1969     /**
1970      * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL.
1971      */
1972     pa_configure_f configure;
1973     /**
1974      * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc)
1975      */
1976     pa_restart_f restart;
1977     /**
1978      * Return 0 if the when complete, HEIM_ERR_PA_CONTINUE_NEEDED if more steps are require
1979      */
1980     pa_step_f step;
1981     pa_release_f release;
1982 } patypes[] = {
1983     {
1984         KRB5_PADATA_PK_AS_REP,
1985         "PKINIT(IETF)",
1986         PA_F_FAST | PA_F_NOT_FAST,
1987         sizeof(struct pkinit_context),
1988         NULL,
1989         pkinit_configure_ietf,
1990         NULL,
1991         pkinit_step,
1992         pkinit_release
1993     },
1994     {
1995         KRB5_PADATA_PK_AS_REP_19,
1996         "PKINIT(win)",
1997         PA_F_FAST | PA_F_NOT_FAST,
1998         sizeof(struct pkinit_context),
1999         NULL,
2000         pkinit_configure_win,
2001         NULL,
2002         pkinit_step,
2003         pkinit_release
2004     },
2005     {
2006         KRB5_PADATA_GSS,
2007         "GSS",
2008         PA_F_FAST | PA_F_NOT_FAST,
2009         sizeof(struct pa_gss_context),
2010         NULL,
2011         pa_gss_configure,
2012         pa_gss_restart,
2013         pa_gss_step,
2014         pa_gss_release
2015     },
2016     {
2017         KRB5_PADATA_ENCRYPTED_CHALLENGE,
2018         "ENCRYPTED_CHALLENGE",
2019         PA_F_FAST,
2020         0,
2021         NULL,
2022         NULL,
2023         NULL,
2024         enc_chal_step,
2025         NULL
2026     },
2027     {
2028         KRB5_PADATA_ENC_TIMESTAMP,
2029         "ENCRYPTED_TIMESTAMP",
2030         PA_F_NOT_FAST,
2031         sizeof(struct enc_ts_context),
2032         NULL,
2033         NULL,
2034         enc_ts_restart,
2035         enc_ts_step,
2036         enc_ts_release
2037     },
2038     {
2039         KRB5_PADATA_PA_PAC_REQUEST,
2040         "PA_PAC_REQUEST",
2041         PA_F_CONFIG,
2042         0,
2043         NULL,
2044         NULL,
2045         NULL,
2046         pa_pac_step,
2047         NULL
2048     },
2049     {
2050         KRB5_PADATA_REQ_ENC_PA_REP,
2051         "REQ-ENC-PA-REP",
2052         PA_F_CONFIG,
2053         0,
2054         NULL,
2055         NULL,
2056         NULL,
2057         pa_enc_pa_rep_step,
2058         NULL
2059     },
2060     {
2061         KRB5_PADATA_FX_COOKIE,
2062         "FX-COOKIE",
2063         PA_F_CONFIG,
2064         0,
2065         NULL,
2066         NULL,
2067         NULL,
2068         pa_fx_cookie_step,
2069         NULL
2070     },
2071 #define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL }
2072     patype_salt(ETYPE_INFO2, pa_etype_info2),
2073     patype_salt(ETYPE_INFO, pa_etype_info),
2074     patype_salt(PW_SALT, pa_pw_or_afs3_salt),
2075     patype_salt(AFS3_SALT, pa_pw_or_afs3_salt),
2076 #undef patype_salt
2077     /* below are just for pretty printing */
2078 #define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL }
2079     patype_info(AUTHENTICATION_SET),
2080     patype_info(AUTH_SET_SELECTED),
2081     patype_info(FX_FAST),
2082     patype_info(FX_ERROR),
2083     patype_info(PKINIT_KX),
2084     patype_info(PK_AS_REQ)
2085 #undef patype_info
2086 };
2087
2088 static const char *
2089 get_pa_type_name(int type)
2090 {
2091     size_t n;
2092     for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++)
2093         if (type == patypes[n].type)
2094             return patypes[n].name;
2095     return "unknown";
2096 }
2097
2098 /*
2099  *
2100  */
2101
2102 struct pa_auth_mech {
2103     struct patype *patype;
2104     struct pa_auth_mech *next; /* when doing authentication sets */
2105     char pactx[1];
2106 };
2107
2108 /*
2109  *
2110  */
2111
2112 static struct pa_info_data *
2113 process_pa_info(krb5_context context,
2114                 const krb5_principal client,
2115                 const AS_REQ *asreq,
2116                 struct pa_info_data *paid,
2117                 METHOD_DATA *md)
2118 {
2119     struct pa_info_data *p = NULL;
2120     PA_DATA *pa;
2121     size_t i;
2122
2123     if (md == NULL)
2124         return NULL;
2125
2126     for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) {
2127         int idx = 0;
2128
2129         if (patypes[i].salt_info == NULL)
2130             continue;
2131
2132         pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx);
2133         if (pa == NULL)
2134             continue;
2135
2136         paid->salt.salttype = (krb5_salttype)patypes[i].type;
2137         p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value);
2138     }
2139     return p;
2140 }
2141
2142 static void
2143 pa_announce(krb5_context context,
2144             int types,
2145             krb5_init_creds_context ctx,
2146             METHOD_DATA *in_md,
2147             METHOD_DATA *out_md)
2148 {
2149     size_t n;
2150
2151     for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2152         if ((patypes[n].flags & types) == 0)
2153             continue;
2154
2155         if (patypes[n].step)
2156             patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, NULL, in_md, out_md);
2157         else
2158             krb5_padata_add(context, out_md, patypes[n].type, NULL, 0);
2159     }
2160 }
2161
2162
2163 static void
2164 mech_dealloc(void *ctx)
2165 {
2166     struct pa_auth_mech *pa_mech = ctx;
2167     if (pa_mech->patype->release)
2168         pa_mech->patype->release((void *)&pa_mech->pactx[0]);
2169 }
2170
2171 struct heim_type_data pa_auth_mech_object = {
2172     HEIM_TID_PA_AUTH_MECH,
2173     "heim-pa-mech-context",
2174     NULL,
2175     mech_dealloc,
2176     NULL,
2177     NULL,
2178     NULL,
2179     NULL
2180 };
2181
2182 static struct pa_auth_mech *
2183 pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2184 {
2185     struct pa_auth_mech *pa_mech;
2186     struct patype *patype = NULL;
2187     size_t n;
2188
2189     for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2190         if (patypes[n].type == pa_type)
2191             patype = &patypes[n];
2192     }
2193     if (patype == NULL)
2194         return NULL;
2195
2196     pa_mech = _heim_alloc_object(&pa_auth_mech_object, sizeof(*pa_mech) - 1 + patype->pa_ctx_size);
2197     if (pa_mech == NULL)
2198         return NULL;
2199
2200     pa_mech->patype = patype;
2201
2202     if (pa_mech->patype->configure) {
2203         krb5_error_code ret;
2204
2205         ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]);
2206         if (ret) {
2207             heim_release(pa_mech);
2208             return NULL;
2209         }
2210     }
2211
2212     _krb5_debug(context, 5, "Adding PA mech: %s", patype->name);
2213
2214     return pa_mech;
2215 }
2216
2217 static void
2218 pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2219 {
2220     struct pa_auth_mech *mech;
2221
2222     mech = pa_mech_create(context, ctx, pa_type);
2223     if (mech) {
2224         heim_array_append_value(ctx->available_pa_mechs, mech);
2225         heim_release(mech);
2226     }
2227 }
2228
2229 static krb5_error_code
2230 pa_configure(krb5_context context,
2231              krb5_init_creds_context ctx,
2232              METHOD_DATA *in_md)
2233 {
2234     ctx->available_pa_mechs = heim_array_create();
2235
2236     if (ctx->gss_init_ctx) {
2237         pa_mech_add(context, ctx, KRB5_PADATA_GSS);
2238     } else if (ctx->pk_init_ctx) {
2239         pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP);
2240         pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19);
2241     } else if (ctx->keyproc || ctx->keyseed || ctx->prompter) {
2242         pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE);
2243         pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP);
2244     }
2245     /* XXX setup context based on KDC reply */
2246
2247     return 0;
2248 }
2249
2250 static krb5_error_code
2251 pa_restart(krb5_context context,
2252            krb5_init_creds_context ctx)
2253 {
2254     krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
2255
2256     if (ctx->pa_mech && ctx->pa_mech->patype->restart)
2257         ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]);
2258
2259     return ret;
2260 }
2261
2262
2263 static krb5_error_code
2264 pa_step(krb5_context context,
2265         krb5_init_creds_context ctx,
2266         const AS_REQ *a,
2267         const AS_REP *rep,
2268         const krb5_krbhst_info *hi,
2269         METHOD_DATA *in_md,
2270         METHOD_DATA *out_md)
2271 {
2272     krb5_error_code ret;
2273     PA_DATA *pa = NULL;
2274     int idx;
2275
2276  next:
2277     do {
2278         if (ctx->pa_mech == NULL) {
2279             size_t len = heim_array_get_length(ctx->available_pa_mechs);
2280             if (len == 0) {
2281                 _krb5_debug(context, 0, "no more available_pa_mechs to try");
2282                 return HEIM_ERR_NO_MORE_PA_MECHS;
2283             }
2284
2285             ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0);
2286             heim_array_delete_value(ctx->available_pa_mechs, 0);
2287         }
2288
2289         if (ctx->fast_state.armor_crypto) {
2290             if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) {
2291                 _krb5_debug(context, 0, "pa-mech %s dropped under FAST (not supported)",
2292                             ctx->pa_mech->patype->name);
2293                 heim_release(ctx->pa_mech);
2294                 ctx->pa_mech = NULL;
2295                 continue;
2296             }
2297         } else {
2298             if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) {
2299                 _krb5_debug(context, 0, "dropped pa-mech %s since not running under FAST",
2300                             ctx->pa_mech->patype->name);
2301                 heim_release(ctx->pa_mech);
2302                 ctx->pa_mech = NULL;
2303                 continue;
2304             }
2305         }
2306
2307         _krb5_debug(context, 0, "pa-mech trying: %s, searching for %d",
2308                     ctx->pa_mech->patype->name, ctx->pa_mech->patype->type);
2309
2310         idx = 0;
2311         if (in_md)
2312             pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx);
2313         else
2314             pa = NULL;
2315
2316     } while (ctx->pa_mech == NULL);
2317
2318     _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name);
2319
2320     ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, hi, in_md, out_md);
2321     _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret);
2322     if (ret == 0) {
2323         struct pa_auth_mech *next_pa = ctx->pa_mech->next;
2324
2325         if (next_pa) {
2326             _krb5_debug(context, 5, "Next PA type in set is: %s",
2327                          next_pa->patype->name);
2328             ret = HEIM_ERR_PA_CONTINUE_NEEDED;
2329         } else if (rep == NULL) {
2330             _krb5_debug(context, 5, "PA %s done, but no ticket in sight!!!",
2331                          ctx->pa_mech->patype->name);
2332             ret = HEIM_ERR_PA_CANT_CONTINUE;
2333         } else {
2334             ctx->pa_used = ctx->pa_mech->patype->name;
2335         }
2336
2337         heim_retain(next_pa);
2338         heim_release(ctx->pa_mech);
2339         ctx->pa_mech = next_pa;
2340     }
2341
2342     if (ret == HEIM_ERR_PA_CANT_CONTINUE) {
2343         if (ctx->pa_mech) {
2344             _krb5_debug(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name);
2345             heim_release(ctx->pa_mech);
2346             ctx->pa_mech = NULL;
2347         }
2348         goto next;
2349     } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2350         _krb5_debug(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name);
2351     } else if (ret != 0) {
2352         _krb5_debug(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret);
2353         heim_release(ctx->pa_mech);
2354         ctx->pa_mech = NULL;
2355     }
2356
2357     return ret;
2358 }
2359
2360 static void
2361 log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md)
2362 {
2363     if (_krb5_have_debug(context, 5)) {
2364         unsigned i;
2365         _krb5_debug(context, 5, "KDC sent %d patypes", in_md->len);
2366         for (i = 0; i < in_md->len; i++)
2367             _krb5_debug(context, 5, "KDC sent PA-DATA type: %d (%s)",
2368                          in_md->val[i].padata_type,
2369                          get_pa_type_name(in_md->val[i].padata_type));
2370     }
2371 }
2372
2373 /*
2374  * Assumes caller always will free `out_md', even on error.
2375  */
2376
2377 static krb5_error_code
2378 process_pa_data_to_md(krb5_context context,
2379                       const krb5_creds *creds,
2380                       const AS_REQ *a,
2381                       krb5_init_creds_context ctx,
2382                       METHOD_DATA *in_md,
2383                       METHOD_DATA **out_md)
2384 {
2385     krb5_error_code ret;
2386
2387     ALLOC(*out_md, 1);
2388     if (*out_md == NULL) {
2389         return krb5_enomem(context);
2390     }
2391     (*out_md)->len = 0;
2392     (*out_md)->val = NULL;
2393
2394     log_kdc_pa_types(context, in_md);
2395
2396     ret = pa_step(context, ctx, a, NULL, NULL, in_md, *out_md);
2397     if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2398         _krb5_debug(context, 0, "pamech need more stepping");
2399     } else if (ret == 0) {
2400         _krb5_debug(context, 0, "pamech done step");
2401     } else {
2402         return ret;
2403     }
2404
2405     /*
2406      * Send announcement (what we support) and configuration (user
2407      * introduced behavior change)
2408      */
2409
2410     pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md);
2411
2412     /*
2413      *
2414      */
2415
2416     if ((*out_md)->len == 0) {
2417         free(*out_md);
2418         *out_md = NULL;
2419     }
2420
2421     return 0;
2422 }
2423
2424 static krb5_error_code
2425 process_pa_data_to_key(krb5_context context,
2426                        krb5_init_creds_context ctx,
2427                        krb5_creds *creds,
2428                        AS_REQ *a,
2429                        AS_REP *rep,
2430                        const krb5_krbhst_info *hi,
2431                        krb5_keyblock **key)
2432 {
2433     struct pa_info_data paid, *ppaid = NULL;
2434     krb5_error_code ret;
2435     krb5_enctype etype = rep->enc_part.etype;
2436
2437     memset(&paid, 0, sizeof(paid));
2438
2439     if (rep->padata)
2440         log_kdc_pa_types(context, rep->padata);
2441
2442     if (rep->padata) {
2443         paid.etype = etype;
2444         ppaid = process_pa_info(context, creds->client, a, &paid,
2445                                 rep->padata);
2446     }
2447     if (ppaid == NULL) {
2448         if (ctx->paid.etype == KRB5_ENCTYPE_NULL) {
2449             ctx->paid.etype = etype;
2450             ctx->paid.s2kparams = NULL;
2451             ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt);
2452             if (ret)
2453                 return ret;
2454         }
2455     }
2456
2457     ret = pa_step(context, ctx, a, rep, hi, rep->padata, NULL);
2458     if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2459         _krb5_debug(context, 0, "In final stretch and pa require more stepping ?");
2460         return ret;
2461     } else if (ret == 0) {
2462         _krb5_debug(context, 0, "final pamech done step");
2463         goto out;
2464     } else {
2465         return ret;
2466     }
2467  out:
2468     free_paid(context, &paid);
2469     return ret;
2470 }
2471
2472 /*
2473  *
2474  */
2475
2476 static krb5_error_code
2477 capture_lkdc_domain(krb5_context context,
2478                     krb5_init_creds_context ctx)
2479 {
2480     size_t len;
2481
2482     len = strlen(_krb5_wellknown_lkdc);
2483
2484     if (ctx->kdc_hostname != NULL ||
2485         strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 ||
2486         ctx->cred.client->realm[len] != ':')
2487         return 0;
2488
2489     ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]);
2490
2491     _krb5_debug(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s",
2492                 ctx->kdc_hostname);
2493     return 0;
2494 }
2495
2496 /**
2497  * Start a new context to get a new initial credential.
2498  *
2499  * @param context A Kerberos 5 context.
2500  * @param client The Kerberos principal to get the credential for, if
2501  *     NULL is given, the default principal is used as determined by
2502  *     krb5_get_default_principal().
2503  * @param prompter
2504  * @param prompter_data
2505  * @param start_time the time the ticket should start to be valid or 0 for now.
2506  * @param options a options structure, can be NULL for default options.
2507  * @param rctx A new allocated free with krb5_init_creds_free().
2508  *
2509  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
2510  *
2511  * @ingroup krb5_credential
2512  */
2513
2514 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2515 krb5_init_creds_init(krb5_context context,
2516                      krb5_principal client,
2517                      krb5_prompter_fct prompter,
2518                      void *prompter_data,
2519                      krb5_deltat start_time,
2520                      krb5_get_init_creds_opt *options,
2521                      krb5_init_creds_context *rctx)
2522 {
2523     krb5_init_creds_context ctx;
2524     krb5_error_code ret;
2525
2526     *rctx = NULL;
2527
2528     ctx = calloc(1, sizeof(*ctx));
2529     if (ctx == NULL)
2530         return krb5_enomem(context);
2531
2532     ret = get_init_creds_common(context, client, prompter, prompter_data,
2533                                  start_time, options, ctx);
2534     if (ret) {
2535         free(ctx);
2536         return ret;
2537     }
2538
2539     /* Set a new nonce. */
2540     /* FIXME should generate a new nonce for each AS-REQ */
2541     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
2542     ctx->nonce &= 0x7fffffff;
2543     /* XXX these just needs to be the same when using Windows PK-INIT */
2544     ctx->pk_nonce = ctx->nonce;
2545
2546     ctx->prompter = prompter;
2547     ctx->prompter_data = prompter_data;
2548
2549     /* pick up hostname from LKDC realm name */
2550     ret = capture_lkdc_domain(context, ctx);
2551     if (ret) {
2552         free_init_creds_ctx(context, ctx);
2553         return ret;
2554     }
2555
2556     ctx->runflags.allow_enc_pa_rep = 1;
2557
2558     ctx->fast_state.flags |= KRB5_FAST_AS_REQ;
2559
2560     *rctx = ctx;
2561
2562     return ret;
2563 }
2564
2565 /**
2566  * Set the KDC hostname for the initial request, it will not be
2567  * considered in referrals to another KDC.
2568  *
2569  * @param context a Kerberos 5 context.
2570  * @param ctx a krb5_init_creds_context context.
2571  * @param hostname the hostname for the KDC of realm
2572  *
2573  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2574  * @ingroup krb5_credential
2575  */
2576
2577 krb5_error_code KRB5_LIB_FUNCTION
2578 krb5_init_creds_set_kdc_hostname(krb5_context context,
2579                                  krb5_init_creds_context ctx,
2580                                  const char *hostname)
2581 {
2582     if (ctx->kdc_hostname)
2583         free(ctx->kdc_hostname);
2584     ctx->kdc_hostname = strdup(hostname);
2585     if (ctx->kdc_hostname == NULL)
2586         return krb5_enomem(context);
2587     return 0;
2588 }
2589
2590 /**
2591  * Set the sitename for the request
2592  *
2593  */
2594
2595 krb5_error_code KRB5_LIB_FUNCTION
2596 krb5_init_creds_set_sitename(krb5_context context,
2597                              krb5_init_creds_context ctx,
2598                              const char *sitename)
2599 {
2600     if (ctx->sitename)
2601         free(ctx->sitename);
2602     ctx->sitename = strdup(sitename);
2603     if (ctx->sitename == NULL)
2604         return krb5_enomem(context);
2605     return 0;
2606 }
2607
2608 /**
2609  * Sets the service that the is requested. This call is only neede for
2610  * special initial tickets, by default the a krbtgt is fetched in the default realm.
2611  *
2612  * @param context a Kerberos 5 context.
2613  * @param ctx a krb5_init_creds_context context.
2614  * @param service the service given as a string, for example
2615  *        "kadmind/admin". If NULL, the default krbtgt in the clients
2616  *        realm is set.
2617  *
2618  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2619  * @ingroup krb5_credential
2620  */
2621
2622 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2623 krb5_init_creds_set_service(krb5_context context,
2624                             krb5_init_creds_context ctx,
2625                             const char *service)
2626 {
2627     krb5_const_realm client_realm;
2628     krb5_principal principal;
2629     krb5_error_code ret;
2630
2631     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
2632
2633     if (service) {
2634         ret = krb5_parse_name (context, service, &principal);
2635         if (ret)
2636             return ret;
2637         krb5_principal_set_realm (context, principal, client_realm);
2638     } else {
2639         ret = krb5_make_principal(context, &principal,
2640                                   client_realm, KRB5_TGS_NAME, client_realm,
2641                                   NULL);
2642         if (ret)
2643             return ret;
2644     }
2645
2646     /*
2647      * This is for Windows RODC that are picky about what name type
2648      * the server principal have, and the really strange part is that
2649      * they are picky about the AS-REQ name type and not the TGS-REQ
2650      * later. Oh well.
2651      */
2652
2653     if (krb5_principal_is_krbtgt(context, principal))
2654         krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
2655
2656     krb5_free_principal(context, ctx->cred.server);
2657     ctx->cred.server = principal;
2658
2659     return 0;
2660 }
2661
2662 /**
2663  * Sets the password that will use for the request.
2664  *
2665  * @param context a Kerberos 5 context.
2666  * @param ctx ctx krb5_init_creds_context context.
2667  * @param password the password to use.
2668  *
2669  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2670  * @ingroup krb5_credential
2671  */
2672
2673 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2674 krb5_init_creds_set_password(krb5_context context,
2675                              krb5_init_creds_context ctx,
2676                              const char *password)
2677 {
2678     if (ctx->password) {
2679         size_t len;
2680         len = strlen(ctx->password);
2681         memset_s(ctx->password, len, 0, len);
2682         free(ctx->password);
2683     }
2684     if (password) {
2685         ctx->password = strdup(password);
2686         if (ctx->password == NULL)
2687             return krb5_enomem(context);
2688         ctx->keyseed = (void *) ctx->password;
2689     } else {
2690         ctx->keyseed = NULL;
2691         ctx->password = NULL;
2692     }
2693
2694     return 0;
2695 }
2696
2697 static krb5_error_code KRB5_CALLCONV
2698 keytab_key_proc(krb5_context context, krb5_enctype enctype,
2699                 krb5_const_pointer keyseed,
2700                 krb5_salt salt, krb5_data *s2kparms,
2701                 krb5_keyblock **key)
2702 {
2703     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
2704     krb5_keytab keytab = args->keytab;
2705     krb5_principal principal = args->principal;
2706     krb5_error_code ret;
2707     krb5_keytab real_keytab;
2708     krb5_keytab_entry entry;
2709
2710     if (keytab == NULL) {
2711         ret = krb5_kt_default(context, &real_keytab);
2712         if (ret)
2713             return ret;
2714     } else
2715         real_keytab = keytab;
2716
2717     ret = krb5_kt_get_entry (context, real_keytab, principal,
2718                              0, enctype, &entry);
2719
2720     if (keytab == NULL)
2721         krb5_kt_close (context, real_keytab);
2722
2723     if (ret)
2724         return ret;
2725
2726     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
2727     krb5_kt_free_entry(context, &entry);
2728     return ret;
2729 }
2730
2731
2732 /**
2733  * Set the keytab to use for authentication.
2734  *
2735  * @param context a Kerberos 5 context.
2736  * @param ctx ctx krb5_init_creds_context context.
2737  * @param keytab the keytab to read the key from.
2738  *
2739  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2740  * @ingroup krb5_credential
2741  */
2742
2743 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2744 krb5_init_creds_set_keytab(krb5_context context,
2745                            krb5_init_creds_context ctx,
2746                            krb5_keytab keytab)
2747 {
2748     krb5_keytab_key_proc_args *a;
2749     krb5_keytab_entry entry;
2750     krb5_kt_cursor cursor;
2751     krb5_enctype *etypes = NULL;
2752     krb5_error_code ret;
2753     size_t netypes = 0;
2754     int kvno = 0, found = 0;
2755     unsigned n;
2756
2757     a = malloc(sizeof(*a));
2758     if (a == NULL)
2759         return krb5_enomem(context);
2760
2761     a->principal = ctx->cred.client;
2762     a->keytab    = keytab;
2763
2764     ctx->keytab_data = a;
2765     ctx->keyseed = (void *)a;
2766     ctx->keyproc = keytab_key_proc;
2767
2768     /*
2769      * We need to the KDC what enctypes we support for this keytab,
2770      * esp if the keytab is really a password based entry, then the
2771      * KDC might have more enctypes in the database then what we have
2772      * in the keytab.
2773      */
2774
2775     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
2776     if(ret)
2777         goto out;
2778
2779     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
2780         void *ptr;
2781
2782         if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
2783             goto next;
2784
2785         found = 1;
2786
2787         /* check if we ahve this kvno already */
2788         if (entry.vno > kvno) {
2789             /* remove old list of etype */
2790             if (etypes)
2791                 free(etypes);
2792             etypes = NULL;
2793             netypes = 0;
2794             kvno = entry.vno;
2795         } else if (entry.vno != kvno)
2796             goto next;
2797
2798         /* check if enctype is supported */
2799         if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
2800             goto next;
2801
2802         /*
2803          * If user already provided a enctype list, use that as an
2804          * additonal filter.
2805          */
2806         if (ctx->etypes) {
2807             for (n = 0; ctx->etypes[n] != KRB5_ENCTYPE_NULL; n++) {
2808                 if (ctx->etypes[n] == entry.keyblock.keytype)
2809                     break;
2810             }
2811             if (ctx->etypes[n] == KRB5_ENCTYPE_NULL)
2812                 goto next;
2813         }
2814
2815         /* add enctype to supported list */
2816         ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
2817         if (ptr == NULL) {
2818             free(etypes);
2819             ret = krb5_enomem(context);
2820             goto out;
2821         }
2822
2823         etypes = ptr;
2824         etypes[netypes] = entry.keyblock.keytype;
2825         etypes[netypes + 1] = ETYPE_NULL;
2826         netypes++;
2827     next:
2828         krb5_kt_free_entry(context, &entry);
2829     }
2830     krb5_kt_end_seq_get(context, keytab, &cursor);
2831
2832     if (etypes) {
2833         if (ctx->etypes)
2834             free(ctx->etypes);
2835         ctx->etypes = etypes;
2836     }
2837
2838  out:
2839     if (!found) {
2840         if (ret == 0)
2841             ret = KRB5_KT_NOTFOUND;
2842         _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
2843     }
2844
2845     return ret;
2846 }
2847
2848 static krb5_error_code KRB5_CALLCONV
2849 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
2850                   krb5_const_pointer keyseed,
2851                   krb5_salt salt, krb5_data *s2kparms,
2852                   krb5_keyblock **key)
2853 {
2854     return krb5_copy_keyblock (context, keyseed, key);
2855 }
2856
2857 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2858 krb5_init_creds_set_keyblock(krb5_context context,
2859                              krb5_init_creds_context ctx,
2860                              krb5_keyblock *keyblock)
2861 {
2862     ctx->keyseed = (void *)keyblock;
2863     ctx->keyproc = keyblock_key_proc;
2864
2865     return 0;
2866 }
2867
2868 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2869 krb5_init_creds_set_fast_ccache(krb5_context context,
2870                                 krb5_init_creds_context ctx,
2871                                 krb5_ccache fast_ccache)
2872 {
2873     krb5_creds *cred = NULL;
2874     krb5_error_code ret;
2875     krb5_data data;
2876
2877     ret = _krb5_get_krbtgt(context, fast_ccache, NULL, &cred);
2878     if (ret)
2879         return ret;
2880
2881     ret = krb5_cc_get_config(context, fast_ccache, cred->server,
2882                              "fast_avail", &data);
2883     krb5_free_creds(context, cred);
2884     if (ret == 0) {
2885         ctx->fast_state.armor_ccache = fast_ccache;
2886         ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2887         ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
2888     } else {
2889         krb5_set_error_message(context, EINVAL, N_("FAST not available for the KDC in the armor ccache", ""));
2890         return EINVAL;
2891     }
2892     return 0;
2893 }
2894
2895 static krb5_error_code
2896 validate_pkinit_fx(krb5_context context,
2897                    krb5_init_creds_context ctx,
2898                    AS_REP *rep,
2899                    krb5_keyblock *ticket_sessionkey)
2900 {
2901     PA_DATA *pa = NULL;
2902     int idx = 0;
2903
2904     if (rep->padata)
2905         pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx);
2906
2907     if (pa == NULL) {
2908         if (ctx->flags.request_anonymous && ctx->pk_init_ctx) {
2909             /* XXX handle the case where pkinit is not used */
2910             krb5_set_error_message(context, KRB5_KDCREP_MODIFIED,
2911                                    N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", ""));
2912             return KRB5_KDCREP_MODIFIED;
2913         }
2914
2915         return 0;
2916     }
2917
2918     heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage");
2919
2920     return _krb5_pk_kx_confirm(context,
2921                                ctx->pk_init_ctx,
2922                                ctx->fast_state.reply_key,
2923                                ticket_sessionkey,
2924                                pa);
2925 }
2926
2927 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2928 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
2929                                           krb5_init_creds_context ctx,
2930                                           krb5_const_principal armor_service)
2931 {
2932     krb5_error_code ret;
2933
2934     if (ctx->fast_state.armor_service)
2935         krb5_free_principal(context, ctx->fast_state.armor_service);
2936     if (armor_service) {
2937         ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
2938         if (ret)
2939             return ret;
2940     } else {
2941         ctx->fast_state.armor_service = NULL;
2942     }
2943     ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE;
2944     return 0;
2945 }
2946
2947 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2948 krb5_init_creds_set_fast_anon_pkinit(krb5_context context,
2949                                      krb5_init_creds_context ctx)
2950 {
2951     if (ctx->fast_state.armor_ccache)
2952         return EINVAL;
2953
2954     ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2955     ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
2956     return 0;
2957 }
2958
2959 static size_t
2960 available_padata_count(METHOD_DATA *md)
2961 {
2962     size_t i, count = 0;
2963
2964     for (i = 0; i < md->len; i++) {
2965         PA_DATA *pa = &md->val[i];
2966
2967         if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
2968             pa->padata_type == KRB5_PADATA_FX_ERROR)
2969             continue;
2970
2971         count++;
2972     }
2973
2974     return count;
2975 }
2976
2977 static krb5_error_code
2978 init_creds_step(krb5_context context,
2979                 krb5_init_creds_context ctx,
2980                 krb5_data *in,
2981                 krb5_data *out,
2982                 krb5_krbhst_info *hostinfo,
2983                 unsigned int *flags)
2984 {
2985     struct timeval start_time, end_time;
2986     krb5_data checksum_data;
2987     krb5_error_code ret;
2988     size_t len = 0;
2989     size_t size;
2990     AS_REQ req2;
2991
2992     gettimeofday(&start_time, NULL);
2993
2994     krb5_data_zero(out);
2995     krb5_data_zero(&checksum_data);
2996
2997     if (ctx->as_req.req_body.cname == NULL) {
2998         ret = init_as_req(context, ctx->flags, &ctx->cred,
2999                           ctx->addrs, ctx->etypes, &ctx->as_req);
3000         if (ret)
3001             return ret;
3002         if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
3003             ;
3004         else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE)
3005             /* Check with armor service if there is FAST */;
3006         else
3007             ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3008
3009
3010         /* XXX should happen after we get back reply from KDC */
3011         pa_configure(context, ctx, NULL);
3012     }
3013
3014 #define MAX_PA_COUNTER 15
3015     if (ctx->pa_counter > MAX_PA_COUNTER) {
3016         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
3017                                N_("Looping %d times while getting "
3018                                   "initial credentials", ""),
3019                                ctx->pa_counter);
3020         return KRB5_GET_IN_TKT_LOOP;
3021     }
3022     ctx->pa_counter++;
3023
3024     _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
3025
3026     /* Lets process the input packet */
3027     if (in && in->length) {
3028         krb5_kdc_rep rep;
3029
3030         memset(&rep, 0, sizeof(rep));
3031
3032         _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
3033
3034         ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
3035         if (ret == 0) {
3036             unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
3037             krb5_data data;
3038
3039             /*
3040              * Unwrap AS-REP
3041              */
3042             ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
3043                                &rep.kdc_rep.ticket, &size, ret);
3044             if (ret)
3045                 goto out;
3046             heim_assert(data.length == size, "ASN.1 internal error");
3047
3048             ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data,
3049                                             &ctx->fast_state, &rep.kdc_rep);
3050             krb5_data_free(&data);
3051             if (ret)
3052                 goto out;
3053
3054             /*
3055              * Now check and extract the ticket
3056              */
3057
3058             if (ctx->flags.canonicalize) {
3059                 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
3060                 eflags |= EXTRACT_TICKET_MATCH_REALM;
3061             }
3062             if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
3063                 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
3064             if (ctx->flags.request_anonymous)
3065                 eflags |= EXTRACT_TICKET_MATCH_ANON;
3066
3067             ret = process_pa_data_to_key(context, ctx, &ctx->cred,
3068                                          &ctx->as_req, &rep.kdc_rep,
3069                                          hostinfo, &ctx->fast_state.reply_key);
3070             if (ret) {
3071                 free_AS_REP(&rep.kdc_rep);
3072                 goto out;
3073             }
3074
3075             if (ctx->fast_state.strengthen_key) {
3076                 krb5_keyblock result;
3077
3078                 _krb5_debug(context, 5, "krb5_get_init_creds: FAST strengthen_key");
3079
3080                 ret = _krb5_fast_cf2(context,
3081                                      ctx->fast_state.strengthen_key,
3082                                      "strengthenkey",
3083                                      ctx->fast_state.reply_key,
3084                                      "replykey",
3085                                      &result,
3086                                      NULL);
3087                 if (ret) {
3088                     free_AS_REP(&rep.kdc_rep);
3089                     goto out;
3090                 }
3091
3092                 ctx->runflags.allow_save_as_reply_key = 1;
3093
3094                 krb5_free_keyblock_contents(context, ctx->fast_state.reply_key);
3095                 *ctx->fast_state.reply_key = result;
3096             }
3097
3098             _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
3099
3100             ret = _krb5_extract_ticket(context,
3101                                        &rep,
3102                                        &ctx->cred,
3103                                        ctx->fast_state.reply_key,
3104                                        NULL,
3105                                        KRB5_KU_AS_REP_ENC_PART,
3106                                        NULL,
3107                                        ctx->nonce,
3108                                        eflags,
3109                                        &ctx->req_buffer,
3110                                        NULL,
3111                                        NULL);
3112
3113             if (ret == 0)
3114                 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
3115             if (ret == 0)
3116                 ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session);
3117
3118             ctx->as_enctype = ctx->fast_state.reply_key->keytype;
3119
3120             if (ctx->runflags.allow_save_as_reply_key) {
3121                 ctx->as_reply_key = ctx->fast_state.reply_key;
3122                 ctx->fast_state.reply_key = NULL;
3123             } else {
3124                 krb5_free_keyblock(context, ctx->fast_state.reply_key);
3125                 ctx->fast_state.reply_key = NULL;
3126             }
3127             ctx->ic_flags |= KRB5_INIT_CREDS_DONE;
3128             *flags = 0;
3129
3130             free_AS_REP(&rep.kdc_rep);
3131             free_EncASRepPart(&rep.enc_part);
3132
3133             gettimeofday(&end_time, NULL);
3134             timevalsub(&end_time, &start_time);
3135             timevaladd(&ctx->stats.run_time, &end_time);
3136
3137             _krb5_debug(context, 1, "krb5_get_init_creds: wc: %lld.%06ld",
3138                         (long long)ctx->stats.run_time.tv_sec,
3139                         (long)ctx->stats.run_time.tv_usec);
3140             return ret;
3141
3142         } else {
3143             /* let's try to parse it as a KRB-ERROR */
3144
3145             _krb5_debug(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC");
3146
3147             free_KRB_ERROR(&ctx->error);
3148
3149             ret = krb5_rd_error(context, in, &ctx->error);
3150             if(ret && in->length && ((char*)in->data)[0] == 4)
3151                 ret = KRB5KRB_AP_ERR_V4_REPLY;
3152             if (ret) {
3153                 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
3154                 goto out;
3155             }
3156
3157             /*
3158              * Unwrap method-data, if there is any,
3159              * fast_unwrap_error() below might replace it with a
3160              * wrapped version if we are using FAST.
3161              */
3162
3163             free_METHOD_DATA(&ctx->md);
3164             memset(&ctx->md, 0, sizeof(ctx->md));
3165
3166             if (ctx->error.e_data) {
3167                 krb5_error_code ret2;
3168
3169                 ret2 = decode_METHOD_DATA(ctx->error.e_data->data,
3170                                          ctx->error.e_data->length,
3171                                          &ctx->md,
3172                                          NULL);
3173                 if (ret2) {
3174                     /*
3175                      * Just ignore any error, the error will be pushed
3176                      * out from krb5_error_from_rd_error() if there
3177                      * was one.
3178                      */
3179                     _krb5_debug(context, 5, N_("Failed to decode METHOD-DATA", ""));
3180                 }
3181             }
3182
3183             /*
3184              * Unwrap KRB-ERROR, we are always calling this so that
3185              * FAST can tell us if your peer KDC suddenly dropped FAST
3186              * wrapping and its really an attacker's packet (or a bug
3187              * in the KDC).
3188              */
3189             ret = _krb5_fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
3190                                           &ctx->md, &ctx->error);
3191             if (ret)
3192                 goto out;
3193
3194             /*
3195              *
3196              */
3197
3198             ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
3199
3200             /* log the failure */
3201             if (_krb5_have_debug(context, 5)) {
3202                 const char *str = krb5_get_error_message(context, ret);
3203                 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str);
3204                 krb5_free_error_message(context, str);
3205             }
3206
3207             /*
3208              * Handle special error codes
3209              */
3210
3211             if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED
3212                 || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED
3213                 || ret == KRB5KDC_ERR_ETYPE_NOSUPP)
3214             {
3215                 /*
3216                  * If no preauth was set and KDC requires it, give it one
3217                  * more try.
3218                  *
3219                  * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop
3220                  * one more time since that might mean we are dealing with
3221                  * a Windows KDC that is confused about what enctypes are
3222                  * available.
3223                  */
3224
3225                 if (available_padata_count(&ctx->md) == 0) {
3226                     krb5_set_error_message(context, ret,
3227                                            N_("Preauth required but no preauth "
3228                                               "options send by KDC", ""));
3229                     goto out;
3230                 }
3231             } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
3232                 /*
3233                  * Try adapt to timeskrew when we are using pre-auth, and
3234                  * if there was a time skew, try again.
3235                  */
3236                 krb5_set_real_time(context, ctx->error.stime, -1);
3237                 if (context->kdc_sec_offset)
3238                     ret = 0;
3239
3240                 _krb5_debug(context, 10, "init_creds: err skew updating kdc offset to %d",
3241                             context->kdc_sec_offset);
3242                 if (ret)
3243                     goto out;
3244
3245                 pa_restart(context, ctx);
3246
3247             } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
3248                 /* client referral to a new realm */
3249                 char *ref_realm;
3250
3251                 if (ctx->error.crealm == NULL) {
3252                     krb5_set_error_message(context, ret,
3253                                            N_("Got a client referral, not but no realm", ""));
3254                     goto out;
3255                 }
3256                 ref_realm = *ctx->error.crealm;
3257
3258                 _krb5_debug(context, 5, "krb5_get_init_creds: referral to realm %s",
3259                             ref_realm);
3260
3261                 /*
3262                  * If its a krbtgt, lets updat the requested krbtgt too
3263                  */
3264                 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
3265
3266                     free(ctx->cred.server->name.name_string.val[1]);
3267                     ctx->cred.server->name.name_string.val[1] = strdup(ref_realm);
3268                     if (ctx->cred.server->name.name_string.val[1] == NULL) {
3269                         ret = krb5_enomem(context);
3270                         goto out;
3271                     }
3272
3273                     free_PrincipalName(ctx->as_req.req_body.sname);
3274                     ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server);
3275                     if (ret)
3276                         goto out;
3277                 }
3278
3279                 free(ctx->as_req.req_body.realm);
3280                 ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm);
3281                 if (ret)
3282                     goto out;
3283
3284                 ret = krb5_principal_set_realm(context,
3285                                                ctx->cred.client,
3286                                                *ctx->error.crealm);
3287                 if (ret)
3288                     goto out;
3289
3290                 ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm);
3291                 if (ret == 0) {
3292                     _krb5_debug(context, 5, "krb5_get_init_creds: got referral to %s", ref_realm);
3293                     krb5_xfree(ref_realm);
3294                 }
3295
3296                 pa_restart(context, ctx);
3297
3298             } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 &&
3299                        ctx->runflags.change_password_prompt) {
3300                 char buf2[1024];
3301
3302                 ctx->runflags.change_password = 1;
3303
3304                 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
3305
3306                 /* try to avoid recursion */
3307                 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
3308                     goto out;
3309
3310                 /* don't include prompter in runtime */
3311                 gettimeofday(&end_time, NULL);
3312                 timevalsub(&end_time, &start_time);
3313                 timevaladd(&ctx->stats.run_time, &end_time);
3314
3315                 ret = change_password(context,
3316                                       ctx->cred.client,
3317                                       ctx->password,
3318                                       buf2,
3319                                       sizeof(buf2),
3320                                       ctx->prompter,
3321                                       ctx->prompter_data,
3322                                       NULL);
3323                 if (ret)
3324                     goto out;
3325
3326                 gettimeofday(&start_time, NULL);
3327
3328                 krb5_init_creds_set_password(context, ctx, buf2);
3329
3330                 pa_restart(context, ctx);
3331
3332             } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
3333
3334                 /*
3335                  * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP,
3336                  * so drop it and try again. But only try that for MIT
3337                  * Kerberos servers by keying of no METHOD-DATA.
3338                  */
3339                 if (ctx->runflags.allow_enc_pa_rep) {
3340                     if (ctx->md.len != 0) {
3341                         _krb5_debug(context, 10, "Server sent PA data with KRB-ERROR, "
3342                                     "so not a pre 1.7 MIT KDC and won't retry w/o ENC-PA-REQ");
3343                         goto out;
3344                     }
3345                     _krb5_debug(context, 10, "Disabling allow_enc_pa_rep and trying again");
3346                     ctx->runflags.allow_enc_pa_rep = 0;
3347                     goto retry;
3348                 }
3349
3350                 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) {
3351                     _krb5_debug(context, 10, "FAST disabled and got preauth failed");
3352                     goto out;
3353                 }
3354
3355                 if ((ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) == 0) {
3356                     _krb5_debug(context, 10, "Preauth failed");
3357                     goto out;
3358                 }
3359
3360                 _krb5_debug(context, 10, "preauth failed with optimistic FAST, trying w/o FAST");
3361
3362                 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3363                 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3364
3365             retry:
3366                 pa_restart(context, ctx);
3367
3368             } else if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) {
3369                 _krb5_debug(context, 10,
3370                             "Some other error %d failed with optimistic FAST, trying w/o FAST", ret);
3371
3372                 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3373                 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3374                 pa_restart(context, ctx);
3375             } else {
3376                 /* some other error code from the KDC, lets' return it to the user */
3377                 goto out;
3378             }
3379         }
3380     }
3381
3382     if (ctx->as_req.padata) {
3383         free_METHOD_DATA(ctx->as_req.padata);
3384         free(ctx->as_req.padata);
3385         ctx->as_req.padata = NULL;
3386     }
3387
3388     ret = _krb5_fast_create_armor(context, &ctx->fast_state,
3389                                   ctx->cred.client->realm);
3390     if (ret)
3391         goto out;
3392
3393     /* Set a new nonce. */
3394     ctx->as_req.req_body.nonce = ctx->nonce;
3395
3396
3397     /*
3398      * Step and announce PA-DATA
3399      */
3400
3401     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
3402                                 &ctx->md, &ctx->as_req.padata);
3403     if (ret)
3404         goto out;
3405
3406
3407     /*
3408      * Wrap with FAST
3409      */
3410     ret = copy_AS_REQ(&ctx->as_req, &req2);
3411     if (ret)
3412         goto out;
3413
3414     ret = _krb5_fast_wrap_req(context,
3415                               &ctx->fast_state,
3416                               NULL,
3417                               &req2);
3418
3419     krb5_data_free(&checksum_data);
3420     if (ret) {
3421         free_AS_REQ(&req2);
3422         goto out;
3423     }
3424
3425     krb5_data_free(&ctx->req_buffer);
3426
3427     ASN1_MALLOC_ENCODE(AS_REQ,
3428                        ctx->req_buffer.data, ctx->req_buffer.length,
3429                        &req2, &len, ret);
3430     free_AS_REQ(&req2);
3431     if (ret)
3432         goto out;
3433     if(len != ctx->req_buffer.length)
3434         krb5_abortx(context, "internal error in ASN.1 encoder");
3435
3436     out->data = ctx->req_buffer.data;
3437     out->length = ctx->req_buffer.length;
3438
3439     *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
3440
3441     gettimeofday(&end_time, NULL);
3442     timevalsub(&end_time, &start_time);
3443     timevaladd(&ctx->stats.run_time, &end_time);
3444
3445     return 0;
3446  out:
3447     return ret;
3448 }
3449
3450 /**
3451  * The core loop if krb5_get_init_creds() function family. Create the
3452  * packets and have the caller send them off to the KDC.
3453  *
3454  * If the caller want all work been done for them, use
3455  * krb5_init_creds_get() instead.
3456  *
3457  * @param context a Kerberos 5 context.
3458  * @param ctx ctx krb5_init_creds_context context.
3459  * @param in input data from KDC, first round it should be reset by krb5_data_zer().
3460  * @param out reply to KDC.
3461  * @param hostinfo KDC address info, first round it can be NULL.
3462  * @param flags status of the round, if
3463  *        KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
3464  *
3465  * @return 0 for success, or an Kerberos 5 error code, see
3466  *     krb5_get_error_message().
3467  *
3468  * @ingroup krb5_credential
3469  */
3470
3471 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3472 krb5_init_creds_step(krb5_context context,
3473                      krb5_init_creds_context ctx,
3474                      krb5_data *in,
3475                      krb5_data *out,
3476                      krb5_krbhst_info *hostinfo,
3477                      unsigned int *flags)
3478 {
3479     krb5_error_code ret;
3480     krb5_data empty;
3481
3482     krb5_data_zero(&empty);
3483
3484     if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) &&
3485         ctx->fast_state.armor_ccache == NULL) {
3486         ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state,
3487                                           in, out, hostinfo, flags);
3488         if (ret ||
3489             ((*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) ||
3490             out->length)
3491             return ret;
3492
3493         in = &empty;
3494     }
3495
3496     return init_creds_step(context, ctx, in, out, hostinfo, flags);
3497 }
3498
3499 /**
3500  * Extract the newly acquired credentials from krb5_init_creds_context
3501  * context.
3502  *
3503  * @param context A Kerberos 5 context.
3504  * @param ctx
3505  * @param cred credentials, free with krb5_free_cred_contents().
3506  *
3507  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3508  */
3509
3510 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3511 krb5_init_creds_get_creds(krb5_context context,
3512                           krb5_init_creds_context ctx,
3513                           krb5_creds *cred)
3514 {
3515     return krb5_copy_creds_contents(context, &ctx->cred, cred);
3516 }
3517
3518 /**
3519  * Extract the as-reply key from the context.
3520  *
3521  * Only allowed when the as-reply-key is not directly derived from the
3522  * password like PK-INIT, GSS, FAST hardened key, etc.
3523  *
3524  * @param context A Kerberos 5 context.
3525  * @param ctx ctx krb5_init_creds_context context.
3526  * @param as_reply_key keyblock, free with krb5_free_keyblock_contents().
3527  *
3528  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3529  */
3530
3531 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3532 krb5_init_creds_get_as_reply_key(krb5_context context,
3533                                  krb5_init_creds_context ctx,
3534                                  krb5_keyblock *as_reply_key)
3535 {
3536     if (ctx->as_reply_key == NULL)
3537         return KRB5KDC_ERR_PREAUTH_REQUIRED;
3538     return krb5_copy_keyblock_contents(context, ctx->as_reply_key, as_reply_key);
3539 }
3540
3541 KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3542 _krb5_init_creds_get_cred_starttime(krb5_context context, krb5_init_creds_context ctx)
3543 {
3544     return ctx->cred.times.starttime;
3545 }
3546
3547 KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3548 _krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx)
3549 {
3550     return ctx->cred.times.endtime;
3551 }
3552
3553 KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL
3554 _krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx)
3555 {
3556     return ctx->cred.client;
3557 }
3558
3559 /**
3560  * Get the last error from the transaction.
3561  *
3562  * @return Returns 0 or an error code
3563  *
3564  * @ingroup krb5_credential
3565  */
3566
3567 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3568 krb5_init_creds_get_error(krb5_context context,
3569                           krb5_init_creds_context ctx,
3570                           KRB_ERROR *error)
3571 {
3572     krb5_error_code ret;
3573
3574     ret = copy_KRB_ERROR(&ctx->error, error);
3575     if (ret)
3576         krb5_enomem(context);
3577
3578     return ret;
3579 }
3580
3581 /**
3582  * Store config
3583  *
3584  * @param context A Kerberos 5 context.
3585  * @param ctx The krb5_init_creds_context to free.
3586  * @param id store
3587  *
3588  * @return Returns 0 or an error code
3589  *
3590  * @ingroup krb5_credential
3591  */
3592
3593 krb5_error_code KRB5_LIB_FUNCTION
3594 krb5_init_creds_store_config(krb5_context context,
3595                              krb5_init_creds_context ctx,
3596                              krb5_ccache id)
3597 {
3598     krb5_error_code ret;
3599
3600     if (ctx->kdc_hostname) {
3601         krb5_data data;
3602         data.length = strlen(ctx->kdc_hostname);
3603         data.data = ctx->kdc_hostname;
3604
3605         ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data);
3606         if (ret)
3607             return ret;
3608     }
3609     if (ctx->sitename) {
3610         krb5_data data;
3611         data.length = strlen(ctx->sitename);
3612         data.data = ctx->sitename;
3613
3614         ret = krb5_cc_set_config(context, id, NULL, "sitename", &data);
3615         if (ret)
3616             return ret;
3617     }
3618
3619     return 0;
3620 }
3621
3622 /**
3623  *
3624  * @ingroup krb5_credential
3625  */
3626
3627 krb5_error_code
3628 krb5_init_creds_store(krb5_context context,
3629                       krb5_init_creds_context ctx,
3630                       krb5_ccache id)
3631 {
3632     krb5_error_code ret;
3633
3634     if (ctx->cred.client == NULL) {
3635         ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
3636         krb5_set_error_message(context, ret, "init creds not completed yet");
3637         return ret;
3638     }
3639
3640     ret = krb5_cc_initialize(context, id, ctx->cred.client);
3641     if (ret)
3642         return ret;
3643
3644     ret = krb5_cc_store_cred(context, id, &ctx->cred);
3645     if (ret)
3646         return ret;
3647
3648     if (ctx->cred.flags.b.enc_pa_rep) {
3649         krb5_data data = { 3, rk_UNCONST("yes") };
3650         ret = krb5_cc_set_config(context, id, ctx->cred.server,
3651                                  "fast_avail", &data);
3652         if (ret)
3653             return ret;
3654     }
3655
3656     return 0;
3657 }
3658
3659 /**
3660  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
3661  *
3662  * @param context A Kerberos 5 context.
3663  * @param ctx The krb5_init_creds_context to free.
3664  *
3665  * @ingroup krb5_credential
3666  */
3667
3668 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3669 krb5_init_creds_free(krb5_context context,
3670                      krb5_init_creds_context ctx)
3671 {
3672     free_init_creds_ctx(context, ctx);
3673     free(ctx);
3674 }
3675
3676 /**
3677  * Get new credentials as setup by the krb5_init_creds_context.
3678  *
3679  * @param context A Kerberos 5 context.
3680  * @param ctx The krb5_init_creds_context to process.
3681  *
3682  * @ingroup krb5_credential
3683  */
3684
3685 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3686 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
3687 {
3688     krb5_sendto_ctx stctx = NULL;
3689     krb5_krbhst_info *hostinfo = NULL;
3690     krb5_error_code ret;
3691     krb5_data in, out;
3692     unsigned int flags = 0;
3693
3694     krb5_data_zero(&in);
3695     krb5_data_zero(&out);
3696
3697     ret = krb5_sendto_ctx_alloc(context, &stctx);
3698     if (ret)
3699         goto out;
3700     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
3701
3702     if (ctx->kdc_hostname)
3703         krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname);
3704     if (ctx->sitename)
3705         krb5_sendto_set_sitename(context, stctx, ctx->sitename);
3706
3707     while (1) {
3708         struct timeval nstart, nend;
3709
3710         flags = 0;
3711         ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
3712         krb5_data_free(&in);
3713         if (ret)
3714             goto out;
3715
3716         if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
3717             break;
3718
3719         gettimeofday(&nstart, NULL);
3720
3721         ret = krb5_sendto_context (context, stctx, &out,
3722                                    ctx->cred.client->realm, &in);
3723         if (ret)
3724             goto out;
3725
3726         gettimeofday(&nend, NULL);
3727         timevalsub(&nend, &nstart);
3728         timevaladd(&ctx->stats.run_time, &nend);
3729     }
3730
3731  out:
3732     if (stctx)
3733         krb5_sendto_ctx_free(context, stctx);
3734
3735     return ret;
3736 }
3737
3738 /**
3739  * Get new credentials using password.
3740  *
3741  * @ingroup krb5_credential
3742  */
3743
3744
3745 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3746 krb5_get_init_creds_password(krb5_context context,
3747                              krb5_creds *creds,
3748                              krb5_principal client,
3749                              const char *password,
3750                              krb5_prompter_fct prompter,
3751                              void *data,
3752                              krb5_deltat start_time,
3753                              const char *in_tkt_service,
3754                              krb5_get_init_creds_opt *options)
3755 {
3756     krb5_init_creds_context ctx;
3757     char buf[BUFSIZ], buf2[BUFSIZ];
3758     krb5_error_code ret;
3759     int chpw = 0;
3760
3761  again:
3762     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
3763     if (ret)
3764         goto out;
3765
3766     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3767     if (ret)
3768         goto out;
3769
3770     if (prompter != NULL && ctx->password == NULL && password == NULL) {
3771         krb5_prompt prompt;
3772         krb5_data password_data;
3773         char *p, *q = NULL;
3774         int aret;
3775
3776         ret = krb5_unparse_name(context, client, &p);
3777         if (ret)
3778             goto out;
3779
3780         aret = asprintf(&q, "%s's Password: ", p);
3781         free (p);
3782         if (aret == -1 || q == NULL) {
3783             ret = krb5_enomem(context);
3784             goto out;
3785         }
3786         prompt.prompt = q;
3787         password_data.data   = buf;
3788         password_data.length = sizeof(buf);
3789         prompt.hidden = 1;
3790         prompt.reply  = &password_data;
3791         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
3792
3793         ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
3794         free (q);
3795         if (ret) {
3796             memset_s(buf, sizeof(buf), 0, sizeof(buf));
3797             ret = KRB5_LIBOS_PWDINTR;
3798             krb5_clear_error_message (context);
3799             goto out;
3800         }
3801         password = password_data.data;
3802     }
3803
3804     if (password) {
3805         ret = krb5_init_creds_set_password(context, ctx, password);
3806         if (ret)
3807             goto out;
3808     }
3809
3810     ret = krb5_init_creds_get(context, ctx);
3811
3812     if (ret == 0)
3813         krb5_process_last_request(context, options, ctx);
3814
3815
3816     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
3817         /* try to avoid recursion */
3818         if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
3819            goto out;
3820
3821         /* don't try to change password if no prompter or prompting disabled */
3822         if (!ctx->runflags.change_password_prompt)
3823             goto out;
3824
3825         ret = change_password (context,
3826                                client,
3827                                ctx->password,
3828                                buf2,
3829                                sizeof(buf2),
3830                                prompter,
3831                                data,
3832                                options);
3833         if (ret)
3834             goto out;
3835         password = buf2;
3836         chpw = 1;
3837         krb5_init_creds_free(context, ctx);
3838         goto again;
3839     }
3840
3841  out:
3842     if (ret == 0)
3843         krb5_init_creds_get_creds(context, ctx, creds);
3844
3845     if (ctx)
3846         krb5_init_creds_free(context, ctx);
3847
3848     memset_s(buf, sizeof(buf), 0, sizeof(buf));
3849     memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
3850     return ret;
3851 }
3852
3853 /**
3854  * Get new credentials using keyblock.
3855  *
3856  * @ingroup krb5_credential
3857  */
3858
3859 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3860 krb5_get_init_creds_keyblock(krb5_context context,
3861                              krb5_creds *creds,
3862                              krb5_principal client,
3863                              krb5_keyblock *keyblock,
3864                              krb5_deltat start_time,
3865                              const char *in_tkt_service,
3866                              krb5_get_init_creds_opt *options)
3867 {
3868     krb5_init_creds_context ctx;
3869     krb5_error_code ret;
3870
3871     memset(creds, 0, sizeof(*creds));
3872
3873     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3874     if (ret)
3875         goto out;
3876
3877     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3878     if (ret)
3879         goto out;
3880
3881     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
3882     if (ret)
3883         goto out;
3884
3885     ret = krb5_init_creds_get(context, ctx);
3886
3887     if (ret == 0)
3888         krb5_process_last_request(context, options, ctx);
3889
3890  out:
3891     if (ret == 0)
3892         krb5_init_creds_get_creds(context, ctx, creds);
3893
3894     if (ctx)
3895         krb5_init_creds_free(context, ctx);
3896
3897     return ret;
3898 }
3899
3900 /**
3901  * Get new credentials using keytab.
3902  *
3903  * @ingroup krb5_credential
3904  */
3905
3906 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3907 krb5_get_init_creds_keytab(krb5_context context,
3908                            krb5_creds *creds,
3909                            krb5_principal client,
3910                            krb5_keytab keytab,
3911                            krb5_deltat start_time,
3912                            const char *in_tkt_service,
3913                            krb5_get_init_creds_opt *options)
3914 {
3915     krb5_init_creds_context ctx;
3916     krb5_keytab_entry ktent;
3917     krb5_error_code ret;
3918
3919     memset(&ktent, 0, sizeof(ktent));
3920     memset(creds, 0, sizeof(*creds));
3921
3922     if (strcmp(client->realm, "") == 0) {
3923         /*
3924          * Referral realm.  We have a keytab, so pick a realm by
3925          * matching in the keytab.
3926          */
3927         ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
3928         if (ret == 0)
3929             client = ktent.principal;
3930     }
3931
3932     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3933     if (ret)
3934         goto out;
3935
3936     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3937     if (ret)
3938         goto out;
3939
3940     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
3941     if (ret)
3942         goto out;
3943
3944     ret = krb5_init_creds_get(context, ctx);
3945     if (ret == 0)
3946         krb5_process_last_request(context, options, ctx);
3947
3948  out:
3949     krb5_kt_free_entry(context, &ktent);
3950     if (ret == 0)
3951         krb5_init_creds_get_creds(context, ctx, creds);
3952
3953     if (ctx)
3954         krb5_init_creds_free(context, ctx);
3955
3956     return ret;
3957 }
3958
3959 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3960 _krb5_init_creds_set_gss_mechanism(krb5_context context,
3961                                    krb5_gss_init_ctx gssic,
3962                                    const struct gss_OID_desc_struct *gss_mech)
3963 {
3964     gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
3965 }
3966
3967 KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
3968 _krb5_init_creds_get_gss_mechanism(krb5_context context,
3969                                    krb5_gss_init_ctx gssic)
3970 {
3971     return gssic->mech;
3972 }
3973
3974 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3975 _krb5_init_creds_set_gss_cred(krb5_context context,
3976                               krb5_gss_init_ctx gssic,
3977                               struct gss_cred_id_t_desc_struct *gss_cred)
3978 {
3979     if (gssic->cred != gss_cred && gssic->flags.release_cred)
3980         gssic->release_cred(context, gssic, gssic->cred);
3981
3982     gssic->cred = gss_cred;
3983     gssic->flags.release_cred = 1;
3984 }
3985
3986 KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
3987 _krb5_init_creds_get_gss_cred(krb5_context context,
3988                               krb5_gss_init_ctx gssic)
3989 {
3990     return gssic->cred;
3991 }
3992
3993 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3994 _krb5_init_creds_init_gss(krb5_context context,
3995                           krb5_init_creds_context ctx,
3996                           krb5_gssic_step step,
3997                           krb5_gssic_finish finish,
3998                           krb5_gssic_release_cred release_cred,
3999                           krb5_gssic_delete_sec_context delete_sec_context,
4000                           const struct gss_cred_id_t_desc_struct *gss_cred,
4001                           const struct gss_OID_desc_struct *gss_mech,
4002                           unsigned int flags)
4003 {
4004     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
4005
4006     gssic = calloc(1, sizeof(*gssic));
4007     if (gssic == NULL)
4008         return krb5_enomem(context);
4009
4010     if (ctx->gss_init_ctx)
4011         free_gss_init_ctx(context, ctx->gss_init_ctx);
4012     ctx->gss_init_ctx = gssic;
4013
4014     gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
4015     gssic->mech = gss_mech;
4016     if (flags & KRB5_GSS_IC_FLAG_RELEASE_CRED)
4017         gssic->flags.release_cred = 1;
4018
4019     gssic->step = step;
4020     gssic->finish = finish;
4021     gssic->release_cred = release_cred;
4022     gssic->delete_sec_context = delete_sec_context;
4023
4024     return 0;
4025 }