r19604: This is a massive commit, and I appologise in advance for it's size.
[samba.git] / source4 / heimdal / lib / krb5 / init_creds_pw.c
1 /*
2  * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "krb5_locl.h"
35
36 RCSID("$Id: init_creds_pw.c,v 1.101 2006/10/02 12:00:59 lha Exp $");
37
38 typedef struct krb5_get_init_creds_ctx {
39     KDCOptions flags;
40     krb5_creds cred;
41     krb5_addresses *addrs;
42     krb5_enctype *etypes;
43     krb5_preauthtype *pre_auth_types;
44     const char *in_tkt_service;
45     unsigned nonce;
46     unsigned pk_nonce;
47
48     krb5_data req_buffer;
49     AS_REQ as_req;
50     int pa_counter;
51
52     const char *password;
53     krb5_s2k_proc key_proc;
54
55     krb5_get_init_creds_tristate req_pac;
56
57     krb5_pk_init_ctx pk_init_ctx;
58 } krb5_get_init_creds_ctx;
59
60 static krb5_error_code
61 default_s2k_func(krb5_context context, krb5_enctype type, 
62                  krb5_const_pointer keyseed,
63                  krb5_salt salt, krb5_data *s2kparms,
64                  krb5_keyblock **key)
65 {
66     krb5_error_code ret;
67     krb5_data password;
68     krb5_data opaque;
69
70     password.data = rk_UNCONST(keyseed);
71     password.length = strlen(keyseed);
72     if (s2kparms)
73         opaque = *s2kparms;
74     else
75         krb5_data_zero(&opaque);
76         
77     *key = malloc(sizeof(**key));
78     if (*key == NULL)
79         return ENOMEM;
80     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
81                                               salt, opaque, *key);
82     if (ret) {
83         free(*key);
84         *key = NULL;
85     }
86     return ret;
87 }
88
89 static void
90 free_init_creds_ctx(krb5_context context, krb5_get_init_creds_ctx *ctx)
91 {
92     if (ctx->etypes)
93         free(ctx->etypes);
94     if (ctx->pre_auth_types)
95         free (ctx->pre_auth_types);
96     free_AS_REQ(&ctx->as_req);
97     memset(&ctx->as_req, 0, sizeof(ctx->as_req));
98 }
99
100 static int
101 get_config_time (krb5_context context,
102                  const char *realm,
103                  const char *name,
104                  int def)
105 {
106     int ret;
107
108     ret = krb5_config_get_time (context, NULL,
109                                 "realms",
110                                 realm,
111                                 name,
112                                 NULL);
113     if (ret >= 0)
114         return ret;
115     ret = krb5_config_get_time (context, NULL,
116                                 "libdefaults",
117                                 name,
118                                 NULL);
119     if (ret >= 0)
120         return ret;
121     return def;
122 }
123
124 static krb5_error_code
125 init_cred (krb5_context context,
126            krb5_creds *cred,
127            krb5_principal client,
128            krb5_deltat start_time,
129            const char *in_tkt_service,
130            krb5_get_init_creds_opt *options)
131 {
132     krb5_error_code ret;
133     krb5_const_realm client_realm;
134     int tmp;
135     krb5_timestamp now;
136
137     krb5_timeofday (context, &now);
138
139     memset (cred, 0, sizeof(*cred));
140     
141     if (client)
142         krb5_copy_principal(context, client, &cred->client);
143     else {
144         ret = krb5_get_default_principal (context,
145                                           &cred->client);
146         if (ret)
147             goto out;
148     }
149
150     client_realm = krb5_principal_get_realm (context, cred->client);
151
152     if (start_time)
153         cred->times.starttime  = now + start_time;
154
155     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
156         tmp = options->tkt_life;
157     else
158         tmp = 10 * 60 * 60;
159     cred->times.endtime = now + tmp;
160
161     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
162         options->renew_life > 0) {
163         cred->times.renew_till = now + options->renew_life;
164     }
165
166     if (in_tkt_service) {
167         krb5_realm server_realm;
168
169         ret = krb5_parse_name (context, in_tkt_service, &cred->server);
170         if (ret)
171             goto out;
172         server_realm = strdup (client_realm);
173         free (*krb5_princ_realm(context, cred->server));
174         krb5_princ_set_realm (context, cred->server, &server_realm);
175     } else {
176         ret = krb5_make_principal(context, &cred->server, 
177                                   client_realm, KRB5_TGS_NAME, client_realm,
178                                   NULL);
179         if (ret)
180             goto out;
181     }
182     return 0;
183
184 out:
185     krb5_free_cred_contents (context, cred);
186     return ret;
187 }
188
189 /*
190  * Print a message (str) to the user about the expiration in `lr'
191  */
192
193 static void
194 report_expiration (krb5_context context,
195                    krb5_prompter_fct prompter,
196                    krb5_data *data,
197                    const char *str,
198                    time_t now)
199 {
200     char *p;
201             
202     asprintf (&p, "%s%s", str, ctime(&now));
203     (*prompter) (context, data, NULL, p, 0, NULL);
204     free (p);
205 }
206
207 /*
208  * Parse the last_req data and show it to the user if it's interesting
209  */
210
211 static void
212 print_expire (krb5_context context,
213               krb5_const_realm realm,
214               krb5_kdc_rep *rep,
215               krb5_prompter_fct prompter,
216               krb5_data *data)
217 {
218     int i;
219     LastReq *lr = &rep->enc_part.last_req;
220     krb5_timestamp sec;
221     time_t t;
222     krb5_boolean reported = FALSE;
223
224     krb5_timeofday (context, &sec);
225
226     t = sec + get_config_time (context,
227                                realm,
228                                "warn_pwexpire",
229                                7 * 24 * 60 * 60);
230
231     for (i = 0; i < lr->len; ++i) {
232         if (lr->val[i].lr_value <= t) {
233             switch (abs(lr->val[i].lr_type)) {
234             case LR_PW_EXPTIME :
235                 report_expiration(context, prompter, data,
236                                   "Your password will expire at ",
237                                   lr->val[i].lr_value);
238                 reported = TRUE;
239                 break;
240             case LR_ACCT_EXPTIME :
241                 report_expiration(context, prompter, data,
242                                   "Your account will expire at ",
243                                   lr->val[i].lr_value);
244                 reported = TRUE;
245                 break;
246             }
247         }
248     }
249
250     if (!reported
251         && rep->enc_part.key_expiration
252         && *rep->enc_part.key_expiration <= t) {
253         report_expiration(context, prompter, data,
254                           "Your password/account will expire at ",
255                           *rep->enc_part.key_expiration);
256     }
257 }
258
259 static krb5_addresses no_addrs = { 0, NULL };
260
261 static krb5_error_code
262 get_init_creds_common(krb5_context context,
263                       krb5_principal client,
264                       krb5_deltat start_time,
265                       const char *in_tkt_service,
266                       krb5_get_init_creds_opt *options,
267                       krb5_get_init_creds_ctx *ctx)
268 {
269     krb5_get_init_creds_opt default_opt;
270     krb5_error_code ret;
271     krb5_enctype *etypes;
272     krb5_preauthtype *pre_auth_types;
273
274     memset(ctx, 0, sizeof(*ctx));
275
276     if (options == NULL) {
277         krb5_get_init_creds_opt_init (&default_opt);
278         options = &default_opt;
279     } else {
280         _krb5_get_init_creds_opt_free_krb5_error(options);
281     }
282
283     if (options->opt_private) {
284         ctx->password = options->opt_private->password;
285         ctx->key_proc = options->opt_private->key_proc;
286         ctx->req_pac = options->opt_private->req_pac;
287         ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
288     } else
289         ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
290
291     if (ctx->key_proc == NULL)
292         ctx->key_proc = default_s2k_func;
293
294     ctx->pre_auth_types = NULL;
295     ctx->addrs = NULL;
296     ctx->etypes = NULL;
297     ctx->pre_auth_types = NULL;
298     ctx->in_tkt_service = in_tkt_service;
299
300     ret = init_cred (context, &ctx->cred, client, start_time,
301                      in_tkt_service, options);
302     if (ret)
303         return ret;
304
305     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
306         ctx->flags.forwardable = options->forwardable;
307
308     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
309         ctx->flags.proxiable = options->proxiable;
310
311     if (start_time)
312         ctx->flags.postdated = 1;
313     if (ctx->cred.times.renew_till)
314         ctx->flags.renewable = 1;
315     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
316         ctx->addrs = options->address_list;
317     } else if (options->opt_private) {
318         switch (options->opt_private->addressless) {
319         case KRB5_INIT_CREDS_TRISTATE_UNSET:
320 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
321             ctx->addrs = &no_addrs;
322 #else
323             ctx->addrs = NULL;
324 #endif
325             break;
326         case KRB5_INIT_CREDS_TRISTATE_FALSE:
327             ctx->addrs = NULL;
328             break;
329         case KRB5_INIT_CREDS_TRISTATE_TRUE:
330             ctx->addrs = &no_addrs;
331             break;
332         }
333     }
334     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
335         etypes = malloc((options->etype_list_length + 1)
336                         * sizeof(krb5_enctype));
337         if (etypes == NULL) {
338             krb5_set_error_string(context, "malloc: out of memory");
339             return ENOMEM;
340         }
341         memcpy (etypes, options->etype_list,
342                 options->etype_list_length * sizeof(krb5_enctype));
343         etypes[options->etype_list_length] = ETYPE_NULL;
344         ctx->etypes = etypes;
345     }
346     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
347         pre_auth_types = malloc((options->preauth_list_length + 1)
348                                 * sizeof(krb5_preauthtype));
349         if (pre_auth_types == NULL) {
350             krb5_set_error_string(context, "malloc: out of memory");
351             return ENOMEM;
352         }
353         memcpy (pre_auth_types, options->preauth_list,
354                 options->preauth_list_length * sizeof(krb5_preauthtype));
355         pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
356         ctx->pre_auth_types = pre_auth_types;
357     }
358     if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)
359         ;                       /* XXX */
360     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
361         ctx->flags.request_anonymous = options->anonymous;
362     return 0;
363 }
364
365 static krb5_error_code
366 change_password (krb5_context context,
367                  krb5_principal client,
368                  const char *password,
369                  char *newpw,
370                  size_t newpw_sz,
371                  krb5_prompter_fct prompter,
372                  void *data,
373                  krb5_get_init_creds_opt *old_options)
374 {
375     krb5_prompt prompts[2];
376     krb5_error_code ret;
377     krb5_creds cpw_cred;
378     char buf1[BUFSIZ], buf2[BUFSIZ];
379     krb5_data password_data[2];
380     int result_code;
381     krb5_data result_code_string;
382     krb5_data result_string;
383     char *p;
384     krb5_get_init_creds_opt options;
385
386     memset (&cpw_cred, 0, sizeof(cpw_cred));
387
388     krb5_get_init_creds_opt_init (&options);
389     krb5_get_init_creds_opt_set_tkt_life (&options, 60);
390     krb5_get_init_creds_opt_set_forwardable (&options, FALSE);
391     krb5_get_init_creds_opt_set_proxiable (&options, FALSE);
392     if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
393         krb5_get_init_creds_opt_set_preauth_list (&options,
394                                                   old_options->preauth_list,
395                                                   old_options->preauth_list_length);                                          
396
397     krb5_data_zero (&result_code_string);
398     krb5_data_zero (&result_string);
399
400     ret = krb5_get_init_creds_password (context,
401                                         &cpw_cred,
402                                         client,
403                                         password,
404                                         prompter,
405                                         data,
406                                         0,
407                                         "kadmin/changepw",
408                                         &options);
409     if (ret)
410         goto out;
411
412     for(;;) {
413         password_data[0].data   = buf1;
414         password_data[0].length = sizeof(buf1);
415
416         prompts[0].hidden = 1;
417         prompts[0].prompt = "New password: ";
418         prompts[0].reply  = &password_data[0];
419         prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
420
421         password_data[1].data   = buf2;
422         password_data[1].length = sizeof(buf2);
423
424         prompts[1].hidden = 1;
425         prompts[1].prompt = "Repeat new password: ";
426         prompts[1].reply  = &password_data[1];
427         prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
428
429         ret = (*prompter) (context, data, NULL, "Changing password",
430                            2, prompts);
431         if (ret) {
432             memset (buf1, 0, sizeof(buf1));
433             memset (buf2, 0, sizeof(buf2));
434             goto out;
435         }
436
437         if (strcmp (buf1, buf2) == 0)
438             break;
439         memset (buf1, 0, sizeof(buf1));
440         memset (buf2, 0, sizeof(buf2));
441     }
442     
443     ret = krb5_change_password (context,
444                                 &cpw_cred,
445                                 buf1,
446                                 &result_code,
447                                 &result_code_string,
448                                 &result_string);
449     if (ret)
450         goto out;
451     asprintf (&p, "%s: %.*s\n",
452               result_code ? "Error" : "Success",
453               (int)result_string.length,
454               result_string.length > 0 ? (char*)result_string.data : "");
455
456     ret = (*prompter) (context, data, NULL, p, 0, NULL);
457     free (p);
458     if (result_code == 0) {
459         strlcpy (newpw, buf1, newpw_sz);
460         ret = 0;
461     } else {
462         krb5_set_error_string (context, "failed changing password");
463         ret = ENOTTY;
464     }
465
466 out:
467     memset (buf1, 0, sizeof(buf1));
468     memset (buf2, 0, sizeof(buf2));
469     krb5_data_free (&result_string);
470     krb5_data_free (&result_code_string);
471     krb5_free_cred_contents (context, &cpw_cred);
472     return ret;
473 }
474
475 krb5_error_code KRB5_LIB_FUNCTION
476 krb5_keyblock_key_proc (krb5_context context,
477                         krb5_keytype type,
478                         krb5_data *salt,
479                         krb5_const_pointer keyseed,
480                         krb5_keyblock **key)
481 {
482     return krb5_copy_keyblock (context, keyseed, key);
483 }
484
485 krb5_error_code KRB5_LIB_FUNCTION
486 krb5_get_init_creds_keytab(krb5_context context,
487                            krb5_creds *creds,
488                            krb5_principal client,
489                            krb5_keytab keytab,
490                            krb5_deltat start_time,
491                            const char *in_tkt_service,
492                            krb5_get_init_creds_opt *options)
493 {
494     krb5_get_init_creds_ctx ctx;
495     krb5_error_code ret;
496     krb5_keytab_key_proc_args *a;
497     
498     ret = get_init_creds_common(context, client, start_time,
499                                 in_tkt_service, options, &ctx);
500     if (ret)
501         goto out;
502
503     a = malloc (sizeof(*a));
504     if (a == NULL) {
505         krb5_set_error_string(context, "malloc: out of memory");
506         ret = ENOMEM;
507         goto out;
508     }
509     a->principal = ctx.cred.client;
510     a->keytab    = keytab;
511
512     ret = krb5_get_in_cred (context,
513                             KDCOptions2int(ctx.flags),
514                             ctx.addrs,
515                             ctx.etypes,
516                             ctx.pre_auth_types,
517                             NULL,
518                             krb5_keytab_key_proc,
519                             a,
520                             NULL,
521                             NULL,
522                             &ctx.cred,
523                             NULL);
524     free (a);
525
526     if (ret == 0 && creds)
527         *creds = ctx.cred;
528     else
529         krb5_free_cred_contents (context, &ctx.cred);
530
531  out:
532     free_init_creds_ctx(context, &ctx);
533     return ret;
534 }
535
536 /*
537  *
538  */
539
540 static krb5_error_code
541 init_creds_init_as_req (krb5_context context,
542                         KDCOptions opts,
543                         const krb5_creds *creds,
544                         const krb5_addresses *addrs,
545                         const krb5_enctype *etypes,
546                         AS_REQ *a)
547 {
548     krb5_error_code ret;
549
550     memset(a, 0, sizeof(*a));
551
552     a->pvno = 5;
553     a->msg_type = krb_as_req;
554     a->req_body.kdc_options = opts;
555     a->req_body.cname = malloc(sizeof(*a->req_body.cname));
556     if (a->req_body.cname == NULL) {
557         ret = ENOMEM;
558         krb5_set_error_string(context, "malloc: out of memory");
559         goto fail;
560     }
561     a->req_body.sname = malloc(sizeof(*a->req_body.sname));
562     if (a->req_body.sname == NULL) {
563         ret = ENOMEM;
564         krb5_set_error_string(context, "malloc: out of memory");
565         goto fail;
566     }
567
568     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
569     if (ret)
570         goto fail;
571     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
572     if (ret)
573         goto fail;
574
575     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
576     if (ret)
577         goto fail;
578
579     if(creds->times.starttime) {
580         a->req_body.from = malloc(sizeof(*a->req_body.from));
581         if (a->req_body.from == NULL) {
582             ret = ENOMEM;
583             krb5_set_error_string(context, "malloc: out of memory");
584             goto fail;
585         }
586         *a->req_body.from = creds->times.starttime;
587     }
588     if(creds->times.endtime){
589         ALLOC(a->req_body.till, 1);
590         *a->req_body.till = creds->times.endtime;
591     }
592     if(creds->times.renew_till){
593         a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
594         if (a->req_body.rtime == NULL) {
595             ret = ENOMEM;
596             krb5_set_error_string(context, "malloc: out of memory");
597             goto fail;
598         }
599         *a->req_body.rtime = creds->times.renew_till;
600     }
601     a->req_body.nonce = 0;
602     ret = krb5_init_etype (context,
603                            &a->req_body.etype.len,
604                            &a->req_body.etype.val,
605                            etypes);
606     if (ret)
607         goto fail;
608
609     /*
610      * This means no addresses
611      */
612
613     if (addrs && addrs->len == 0) {
614         a->req_body.addresses = NULL;
615     } else {
616         a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
617         if (a->req_body.addresses == NULL) {
618             ret = ENOMEM;
619             krb5_set_error_string(context, "malloc: out of memory");
620             goto fail;
621         }
622
623         if (addrs)
624             ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
625         else {
626             ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
627             if(ret == 0 && a->req_body.addresses->len == 0) {
628                 free(a->req_body.addresses);
629                 a->req_body.addresses = NULL;
630             }
631         }
632         if (ret)
633             goto fail;
634     }
635
636     a->req_body.enc_authorization_data = NULL;
637     a->req_body.additional_tickets = NULL;
638
639     a->padata = NULL;
640
641     return 0;
642  fail:
643     free_AS_REQ(a);
644     memset(a, 0, sizeof(*a));
645     return ret;
646 }
647
648 struct pa_info_data {
649     krb5_enctype etype;
650     krb5_salt salt;
651     krb5_data *s2kparams;
652 };
653
654 static void
655 free_paid(krb5_context context, struct pa_info_data *ppaid)
656 {
657     krb5_free_salt(context, ppaid->salt);
658     if (ppaid->s2kparams)
659         krb5_data_free(ppaid->s2kparams);
660 }
661
662
663 static krb5_error_code
664 set_paid(struct pa_info_data *paid, krb5_context context,
665          krb5_enctype etype,
666          krb5_salttype salttype, void *salt_string, size_t salt_len,
667          krb5_data *s2kparams)
668 {
669     paid->etype = etype;
670     paid->salt.salttype = salttype;
671     paid->salt.saltvalue.data = malloc(salt_len + 1);
672     if (paid->salt.saltvalue.data == NULL) {
673         krb5_clear_error_string(context);
674         return ENOMEM;
675     }
676     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
677     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
678     paid->salt.saltvalue.length = salt_len;
679     if (s2kparams) {
680         krb5_error_code ret;
681
682         ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
683         if (ret) {
684             krb5_clear_error_string(context);
685             krb5_free_salt(context, paid->salt);
686             return ret;
687         }
688     } else
689         paid->s2kparams = NULL;
690
691     return 0;
692 }
693
694 static struct pa_info_data *
695 pa_etype_info2(krb5_context context,
696                const krb5_principal client, 
697                const AS_REQ *asreq,
698                struct pa_info_data *paid, 
699                heim_octet_string *data)
700 {
701     krb5_error_code ret;
702     ETYPE_INFO2 e;
703     size_t sz;
704     int i, j;
705
706     memset(&e, 0, sizeof(e));
707     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
708     if (ret)
709         goto out;
710     if (e.len == 0)
711         goto out;
712     for (j = 0; j < asreq->req_body.etype.len; j++) {
713         for (i = 0; i < e.len; i++) {
714             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
715                 krb5_salt salt;
716                 if (e.val[i].salt == NULL)
717                     ret = krb5_get_pw_salt(context, client, &salt);
718                 else {
719                     salt.saltvalue.data = *e.val[i].salt;
720                     salt.saltvalue.length = strlen(*e.val[i].salt);
721                     ret = 0;
722                 }
723                 if (ret == 0)
724                     ret = set_paid(paid, context, e.val[i].etype,
725                                    KRB5_PW_SALT,
726                                    salt.saltvalue.data, 
727                                    salt.saltvalue.length,
728                                    e.val[i].s2kparams);
729                 if (e.val[i].salt == NULL)
730                     krb5_free_salt(context, salt);
731                 if (ret == 0) {
732                         free_ETYPE_INFO2(&e);
733                         return paid;
734                 }
735             }
736         }
737     }
738  out:
739     free_ETYPE_INFO2(&e);
740     return NULL;
741 }
742
743 static struct pa_info_data *
744 pa_etype_info(krb5_context context,
745               const krb5_principal client, 
746               const AS_REQ *asreq,
747               struct pa_info_data *paid,
748               heim_octet_string *data)
749 {
750     krb5_error_code ret;
751     ETYPE_INFO e;
752     size_t sz;
753     int i, j;
754
755     memset(&e, 0, sizeof(e));
756     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
757     if (ret)
758         goto out;
759     if (e.len == 0)
760         goto out;
761     for (j = 0; j < asreq->req_body.etype.len; j++) {
762         for (i = 0; i < e.len; i++) {
763             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
764                 krb5_salt salt;
765                 salt.salttype = KRB5_PW_SALT;
766                 if (e.val[i].salt == NULL)
767                     ret = krb5_get_pw_salt(context, client, &salt);
768                 else {
769                     salt.saltvalue = *e.val[i].salt;
770                     ret = 0;
771                 }
772                 if (e.val[i].salttype)
773                     salt.salttype = *e.val[i].salttype;
774                 if (ret == 0) {
775                     ret = set_paid(paid, context, e.val[i].etype,
776                                    salt.salttype,
777                                    salt.saltvalue.data, 
778                                    salt.saltvalue.length,
779                                    NULL);
780                     if (e.val[i].salt == NULL)
781                         krb5_free_salt(context, salt);
782                 }
783                 if (ret == 0) {
784                     free_ETYPE_INFO(&e);
785                     return paid;
786                 }
787             }
788         }
789     }
790  out:
791     free_ETYPE_INFO(&e);
792     return NULL;
793 }
794
795 static struct pa_info_data *
796 pa_pw_or_afs3_salt(krb5_context context,
797                    const krb5_principal client, 
798                    const AS_REQ *asreq,
799                    struct pa_info_data *paid,
800                    heim_octet_string *data)
801 {
802     krb5_error_code ret;
803     if (paid->etype == ENCTYPE_NULL)
804         return NULL;
805     ret = set_paid(paid, context, 
806                    paid->etype,
807                    paid->salt.salttype,
808                    data->data, 
809                    data->length,
810                    NULL);
811     if (ret)
812         return NULL;
813     return paid;
814 }
815
816
817 struct pa_info {
818     krb5_preauthtype type;
819     struct pa_info_data *(*salt_info)(krb5_context,
820                                       const krb5_principal, 
821                                       const AS_REQ *,
822                                       struct pa_info_data *, 
823                                       heim_octet_string *);
824 };
825
826 static struct pa_info pa_prefs[] = {
827     { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
828     { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
829     { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
830     { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
831 };
832     
833 static PA_DATA *
834 find_pa_data(const METHOD_DATA *md, int type)
835 {
836     int i;
837     for (i = 0; i < md->len; i++)
838         if (md->val[i].padata_type == type)
839             return &md->val[i];
840     return NULL;
841 }
842
843 static struct pa_info_data *
844 process_pa_info(krb5_context context, 
845                 const krb5_principal client, 
846                 const AS_REQ *asreq,
847                 struct pa_info_data *paid,
848                 METHOD_DATA *md)
849 {
850     struct pa_info_data *p = NULL;
851     int i;
852
853     for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
854         PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
855         if (pa == NULL)
856             continue;
857         paid->salt.salttype = pa_prefs[i].type;
858         p = (*pa_prefs[i].salt_info)(context, client, asreq,
859                                      paid, &pa->padata_value);
860     }
861     return p;
862 }
863
864 static krb5_error_code
865 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, 
866                       krb5_enctype etype, krb5_keyblock *key)
867 {
868     PA_ENC_TS_ENC p;
869     unsigned char *buf;
870     size_t buf_size;
871     size_t len;
872     EncryptedData encdata;
873     krb5_error_code ret;
874     int32_t usec;
875     int usec2;
876     krb5_crypto crypto;
877     
878     krb5_us_timeofday (context, &p.patimestamp, &usec);
879     usec2         = usec;
880     p.pausec      = &usec2;
881
882     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
883     if (ret)
884         return ret;
885     if(buf_size != len)
886         krb5_abortx(context, "internal error in ASN.1 encoder");
887
888     ret = krb5_crypto_init(context, key, 0, &crypto);
889     if (ret) {
890         free(buf);
891         return ret;
892     }
893     ret = krb5_encrypt_EncryptedData(context, 
894                                      crypto,
895                                      KRB5_KU_PA_ENC_TIMESTAMP,
896                                      buf,
897                                      len,
898                                      0,
899                                      &encdata);
900     free(buf);
901     krb5_crypto_destroy(context, crypto);
902     if (ret)
903         return ret;
904                     
905     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
906     free_EncryptedData(&encdata);
907     if (ret)
908         return ret;
909     if(buf_size != len)
910         krb5_abortx(context, "internal error in ASN.1 encoder");
911
912     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
913     if (ret)
914         free(buf);
915     return ret;
916 }
917
918 static krb5_error_code
919 add_enc_ts_padata(krb5_context context,
920                   METHOD_DATA *md, 
921                   krb5_principal client,
922                   krb5_s2k_proc key_proc,
923                   krb5_const_pointer keyseed,
924                   krb5_enctype *enctypes,
925                   unsigned netypes,
926                   krb5_salt *salt,
927                   krb5_data *s2kparams)
928 {
929     krb5_error_code ret;
930     krb5_salt salt2;
931     krb5_enctype *ep;
932     int i;
933     
934     if(salt == NULL) {
935         /* default to standard salt */
936         ret = krb5_get_pw_salt (context, client, &salt2);
937         salt = &salt2;
938     }
939     if (!enctypes) {
940         enctypes = context->etypes;
941         netypes = 0;
942         for (ep = enctypes; *ep != ETYPE_NULL; ep++)
943             netypes++;
944     }
945
946     for (i = 0; i < netypes; ++i) {
947         krb5_keyblock *key;
948
949         ret = (*key_proc)(context, enctypes[i], keyseed,
950                           *salt, s2kparams, &key);
951         if (ret)
952             continue;
953         ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
954         krb5_free_keyblock (context, key);
955         if (ret)
956             return ret;
957     }
958     if(salt == &salt2)
959         krb5_free_salt(context, salt2);
960     return 0;
961 }
962
963 static krb5_error_code
964 pa_data_to_md_ts_enc(krb5_context context,
965                      const AS_REQ *a,
966                      const krb5_principal client,
967                      krb5_get_init_creds_ctx *ctx,
968                      struct pa_info_data *ppaid,
969                      METHOD_DATA *md)
970 {
971     if (ctx->key_proc == NULL || ctx->password == NULL)
972         return 0;
973
974     if (ppaid) {
975         add_enc_ts_padata(context, md, client, 
976                           ctx->key_proc, ctx->password,
977                           &ppaid->etype, 1,
978                           &ppaid->salt, ppaid->s2kparams);
979     } else {
980         krb5_salt salt;
981         
982         /* make a v5 salted pa-data */
983         add_enc_ts_padata(context, md, client, 
984                           ctx->key_proc, ctx->password,
985                           a->req_body.etype.val, a->req_body.etype.len, 
986                           NULL, NULL);
987         
988         /* make a v4 salted pa-data */
989         salt.salttype = KRB5_PW_SALT;
990         krb5_data_zero(&salt.saltvalue);
991         add_enc_ts_padata(context, md, client, 
992                           ctx->key_proc, ctx->password, 
993                           a->req_body.etype.val, a->req_body.etype.len, 
994                           &salt, NULL);
995     }
996     return 0;
997 }
998
999 static krb5_error_code
1000 pa_data_to_key_plain(krb5_context context,
1001                      const krb5_principal client,
1002                      krb5_get_init_creds_ctx *ctx,
1003                      krb5_salt salt,
1004                      krb5_data *s2kparams,
1005                      krb5_enctype etype,
1006                      krb5_keyblock **key)
1007 {
1008     krb5_error_code ret;
1009
1010     ret = (*ctx->key_proc)(context, etype, ctx->password,
1011                            salt, s2kparams, key);
1012     return ret;
1013 }
1014
1015
1016 static krb5_error_code
1017 pa_data_to_md_pkinit(krb5_context context,
1018                      const AS_REQ *a,
1019                      const krb5_principal client,
1020                      krb5_get_init_creds_ctx *ctx,
1021                      METHOD_DATA *md)
1022 {
1023     if (ctx->pk_init_ctx == NULL)
1024         return 0;
1025 #ifdef PKINIT
1026     return _krb5_pk_mk_padata(context,
1027                              ctx->pk_init_ctx,
1028                              &a->req_body,
1029                              ctx->pk_nonce,
1030                              md);
1031 #else
1032     krb5_set_error_string(context, "no support for PKINIT compiled in");
1033     return EINVAL;
1034 #endif
1035 }
1036
1037 static krb5_error_code
1038 pa_data_add_pac_request(krb5_context context,
1039                         krb5_get_init_creds_ctx *ctx,
1040                         METHOD_DATA *md)
1041 {
1042     size_t len, length;
1043     krb5_error_code ret;
1044     PA_PAC_REQUEST req;
1045     void *buf;
1046     
1047     switch (ctx->req_pac) {
1048     case KRB5_INIT_CREDS_TRISTATE_UNSET:
1049         return 0; /* don't bother */
1050     case KRB5_INIT_CREDS_TRISTATE_TRUE:
1051         req.include_pac = 1;
1052         break;
1053     case KRB5_INIT_CREDS_TRISTATE_FALSE:
1054         req.include_pac = 0;
1055     }   
1056
1057     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, 
1058                        &req, &len, ret);
1059     if (ret)
1060         return ret;
1061     if(len != length)
1062         krb5_abortx(context, "internal error in ASN.1 encoder");
1063
1064     ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1065     if (ret)
1066         free(buf);
1067
1068     return 0;
1069 }
1070
1071 /*
1072  * Assumes caller always will free `out_md', even on error.
1073  */
1074
1075 static krb5_error_code
1076 process_pa_data_to_md(krb5_context context,
1077                       const krb5_creds *creds,
1078                       const AS_REQ *a,
1079                       krb5_get_init_creds_ctx *ctx,
1080                       METHOD_DATA *in_md,
1081                       METHOD_DATA **out_md,
1082                       krb5_prompter_fct prompter,
1083                       void *prompter_data)
1084 {
1085     krb5_error_code ret;
1086
1087     ALLOC(*out_md, 1);
1088     if (*out_md == NULL) {
1089         krb5_set_error_string(context, "malloc: out of memory");
1090         return ENOMEM;
1091     }
1092     (*out_md)->len = 0;
1093     (*out_md)->val = NULL;
1094     
1095     if (in_md->len != 0) {
1096         struct pa_info_data paid, *ppaid;
1097
1098         memset(&paid, 0, sizeof(paid));
1099
1100         paid.etype = ENCTYPE_NULL;
1101         ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
1102
1103         pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1104         if (ppaid)
1105             free_paid(context, ppaid);
1106     }
1107
1108     pa_data_add_pac_request(context, ctx, *out_md);
1109     ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
1110     if (ret)
1111         return ret;
1112
1113     if ((*out_md)->len == 0) {
1114         free(*out_md);
1115         *out_md = NULL;
1116     }
1117
1118     return 0;
1119 }
1120
1121 static krb5_error_code
1122 process_pa_data_to_key(krb5_context context,
1123                        krb5_get_init_creds_ctx *ctx,
1124                        krb5_creds *creds,
1125                        AS_REQ *a,
1126                        krb5_kdc_rep *rep,
1127                        const krb5_krbhst_info *hi,
1128                        krb5_keyblock **key)
1129 {
1130     struct pa_info_data paid, *ppaid = NULL;
1131     krb5_error_code ret;
1132     krb5_enctype etype;
1133     PA_DATA *pa;
1134
1135     memset(&paid, 0, sizeof(paid));
1136
1137     etype = rep->kdc_rep.enc_part.etype;
1138
1139     if (rep->kdc_rep.padata) {
1140         paid.etype = etype;
1141         ppaid = process_pa_info(context, creds->client, a, &paid, 
1142                                 rep->kdc_rep.padata);
1143     }
1144     if (ppaid == NULL) {
1145         ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1146         if (ret)
1147             return ret;
1148         paid.etype = etype;
1149         paid.s2kparams = NULL;
1150     }
1151
1152     pa = NULL;
1153     if (rep->kdc_rep.padata) {
1154         int idx = 0;
1155         pa = krb5_find_padata(rep->kdc_rep.padata->val, 
1156                               rep->kdc_rep.padata->len,
1157                               KRB5_PADATA_PK_AS_REP,
1158                               &idx);
1159         if (pa == NULL) {
1160             idx = 0;
1161             pa = krb5_find_padata(rep->kdc_rep.padata->val, 
1162                                   rep->kdc_rep.padata->len,
1163                                   KRB5_PADATA_PK_AS_REP_19,
1164                                   &idx);
1165         }
1166     }
1167     if (pa && ctx->pk_init_ctx) {
1168 #ifdef PKINIT
1169         ret = _krb5_pk_rd_pa_reply(context,
1170                                    a->req_body.realm,
1171                                    ctx->pk_init_ctx,
1172                                    etype,
1173                                    hi,
1174                                    ctx->pk_nonce,
1175                                    &ctx->req_buffer,
1176                                    pa,
1177                                    key);
1178 #else
1179         krb5_set_error_string(context, "no support for PKINIT compiled in");
1180         ret = EINVAL;
1181 #endif
1182     } else if (ctx->password)
1183         ret = pa_data_to_key_plain(context, creds->client, ctx, 
1184                                    paid.salt, paid.s2kparams, etype, key);
1185     else {
1186         krb5_set_error_string(context, "No usable pa data type");
1187         ret = EINVAL;
1188     }
1189
1190     free_paid(context, &paid);
1191     return ret;
1192 }
1193
1194 static krb5_error_code
1195 init_cred_loop(krb5_context context,
1196                krb5_get_init_creds_opt *init_cred_opts,
1197                const krb5_prompter_fct prompter,
1198                void *prompter_data,
1199                krb5_get_init_creds_ctx *ctx,
1200                krb5_creds *creds,
1201                krb5_kdc_rep *ret_as_reply)
1202 {
1203     krb5_error_code ret;
1204     krb5_kdc_rep rep;
1205     METHOD_DATA md;
1206     krb5_data resp;
1207     size_t len;
1208     size_t size;
1209     int send_to_kdc_flags = 0;
1210     krb5_krbhst_info *hi = NULL;
1211
1212
1213     memset(&md, 0, sizeof(md));
1214     memset(&rep, 0, sizeof(rep));
1215
1216     _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts);
1217
1218     if (ret_as_reply)
1219         memset(ret_as_reply, 0, sizeof(*ret_as_reply));
1220
1221     ret = init_creds_init_as_req(context, ctx->flags, creds,
1222                                  ctx->addrs, ctx->etypes, &ctx->as_req);
1223     if (ret)
1224         return ret;
1225
1226     /* Set a new nonce. */
1227     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1228     ctx->nonce &= 0xffffffff;
1229     /* XXX these just needs to be the same when using Windows PK-INIT */
1230     ctx->pk_nonce = ctx->nonce;
1231
1232     /*
1233      * Increase counter when we want other pre-auth types then
1234      * KRB5_PA_ENC_TIMESTAMP.
1235      */
1236 #define MAX_PA_COUNTER 3 
1237
1238     ctx->pa_counter = 0;
1239     while (ctx->pa_counter < MAX_PA_COUNTER) {
1240
1241         ctx->pa_counter++;
1242
1243         if (ctx->as_req.padata) {
1244             free_METHOD_DATA(ctx->as_req.padata);
1245             free(ctx->as_req.padata);
1246             ctx->as_req.padata = NULL;
1247         }
1248
1249         /* Set a new nonce. */
1250         ctx->as_req.req_body.nonce = ctx->nonce;
1251
1252         /* fill_in_md_data */
1253         ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
1254                                     &md, &ctx->as_req.padata,
1255                                     prompter, prompter_data);
1256         if (ret)
1257             goto out;
1258
1259         krb5_data_free(&ctx->req_buffer);
1260
1261         ASN1_MALLOC_ENCODE(AS_REQ, 
1262                            ctx->req_buffer.data, ctx->req_buffer.length, 
1263                            &ctx->as_req, &len, ret);
1264         if (ret)
1265             goto out;
1266         if(len != ctx->req_buffer.length)
1267             krb5_abortx(context, "internal error in ASN.1 encoder");
1268
1269         ret = krb5_sendto_kdc_flags (context, &ctx->req_buffer, 
1270                                      &creds->client->realm, &resp,
1271                                      send_to_kdc_flags);
1272         if (ret)
1273             goto out;
1274
1275         memset (&rep, 0, sizeof(rep));
1276         ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
1277         if (ret == 0) {
1278             krb5_data_free(&resp);
1279             krb5_clear_error_string(context);
1280             break;
1281         } else {
1282             /* let's try to parse it as a KRB-ERROR */
1283             KRB_ERROR error;
1284
1285             ret = krb5_rd_error(context, &resp, &error);
1286             if(ret && resp.data && ((char*)resp.data)[0] == 4)
1287                 ret = KRB5KRB_AP_ERR_V4_REPLY;
1288             krb5_data_free(&resp);
1289             if (ret)
1290                 goto out;
1291
1292             ret = krb5_error_from_rd_error(context, &error, creds);
1293
1294             /*
1295              * If no preauth was set and KDC requires it, give it one
1296              * more try.
1297              */
1298
1299             if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1300                 free_METHOD_DATA(&md);
1301                 memset(&md, 0, sizeof(md));
1302
1303                 if (error.e_data) {
1304                     ret = decode_METHOD_DATA(error.e_data->data, 
1305                                              error.e_data->length, 
1306                                              &md, 
1307                                              NULL);
1308                     if (ret)
1309                         krb5_set_error_string(context,
1310                                               "failed to decode METHOD DATA");
1311                 } else {
1312                     /* XXX guess what the server want here add add md */
1313                 }
1314                 krb5_free_error_contents(context, &error);
1315                 if (ret)
1316                     goto out;
1317             } else if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1318                 if (send_to_kdc_flags & KRB5_KRBHST_FLAGS_LARGE_MSG) {
1319                     if (ret_as_reply)
1320                         rep.error = error;
1321                     else
1322                         krb5_free_error_contents(context, &error);
1323                     goto out;
1324                 }
1325                 krb5_free_error_contents(context, &error);
1326                 send_to_kdc_flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
1327             } else {
1328                 _krb5_get_init_creds_opt_set_krb5_error(context,
1329                                                         init_cred_opts,
1330                                                         &error);
1331                 if (ret_as_reply)
1332                     rep.error = error;
1333                 else
1334                     krb5_free_error_contents(context, &error);
1335                 goto out;
1336             }
1337         }
1338     }
1339
1340     {
1341         krb5_keyblock *key = NULL;
1342
1343         ret = process_pa_data_to_key(context, ctx, creds, 
1344                                      &ctx->as_req, &rep, hi, &key);
1345         if (ret)
1346             goto out;
1347         
1348         ret = _krb5_extract_ticket(context,
1349                                    &rep,
1350                                    creds,
1351                                    key,
1352                                    NULL,
1353                                    KRB5_KU_AS_REP_ENC_PART,
1354                                    NULL,
1355                                    ctx->nonce,
1356                                    FALSE,
1357                                    ctx->flags.request_anonymous,
1358                                    NULL,
1359                                    NULL);
1360         krb5_free_keyblock(context, key);
1361     }
1362 out:
1363     krb5_data_free(&ctx->req_buffer);
1364     free_METHOD_DATA(&md);
1365     memset(&md, 0, sizeof(md));
1366
1367     if (ret == 0 && ret_as_reply)
1368         *ret_as_reply = rep;
1369     else 
1370         krb5_free_kdc_rep (context, &rep);
1371     return ret;
1372 }
1373
1374 krb5_error_code KRB5_LIB_FUNCTION
1375 krb5_get_init_creds(krb5_context context,
1376                     krb5_creds *creds,
1377                     krb5_principal client,
1378                     krb5_prompter_fct prompter,
1379                     void *data,
1380                     krb5_deltat start_time,
1381                     const char *in_tkt_service,
1382                     krb5_get_init_creds_opt *options)
1383 {
1384     krb5_get_init_creds_ctx ctx;
1385     krb5_kdc_rep kdc_reply;
1386     krb5_error_code ret;
1387     char buf[BUFSIZ];
1388     int done;
1389
1390     memset(&kdc_reply, 0, sizeof(kdc_reply));
1391
1392     ret = get_init_creds_common(context, client, start_time,
1393                                 in_tkt_service, options, &ctx);
1394     if (ret)
1395         goto out;
1396
1397     done = 0;
1398     while(!done) {
1399         memset(&kdc_reply, 0, sizeof(kdc_reply));
1400
1401         ret = init_cred_loop(context,
1402                              options,
1403                              prompter,
1404                              data,
1405                              &ctx,
1406                              &ctx.cred,
1407                              &kdc_reply);
1408         
1409         switch (ret) {
1410         case 0 :
1411             done = 1;
1412             break;
1413         case KRB5KDC_ERR_KEY_EXPIRED :
1414             /* try to avoid recursion */
1415
1416             /* don't try to change password where then where none */
1417             if (prompter == NULL || ctx.password == NULL)
1418                 goto out;
1419
1420             krb5_clear_error_string (context);
1421
1422             if (ctx.in_tkt_service != NULL
1423                 && strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
1424                 goto out;
1425
1426             ret = change_password (context,
1427                                    client,
1428                                    ctx.password,
1429                                    buf,
1430                                    sizeof(buf),
1431                                    prompter,
1432                                    data,
1433                                    options);
1434             if (ret)
1435                 goto out;
1436             ctx.password = buf;
1437             break;
1438         default:
1439             goto out;
1440         }
1441     }
1442
1443     if (prompter)
1444         print_expire (context,
1445                       krb5_principal_get_realm (context, ctx.cred.client),
1446                       &kdc_reply,
1447                       prompter,
1448                       data);
1449
1450  out:
1451     memset (buf, 0, sizeof(buf));
1452     free_init_creds_ctx(context, &ctx);
1453     krb5_free_kdc_rep (context, &kdc_reply);
1454     if (ret == 0)
1455         *creds = ctx.cred;
1456     else
1457         krb5_free_cred_contents (context, &ctx.cred);
1458
1459     return ret;
1460 }
1461
1462 krb5_error_code KRB5_LIB_FUNCTION
1463 krb5_get_init_creds_password(krb5_context context,
1464                              krb5_creds *creds,
1465                              krb5_principal client,
1466                              const char *password,
1467                              krb5_prompter_fct prompter,
1468                              void *data,
1469                              krb5_deltat start_time,
1470                              const char *in_tkt_service,
1471                              krb5_get_init_creds_opt *in_options)
1472 {
1473     krb5_get_init_creds_opt *options;
1474     char buf[BUFSIZ];
1475     krb5_error_code ret;
1476
1477     if (in_options == NULL)
1478         ret = krb5_get_init_creds_opt_alloc(context, &options);
1479     else
1480         ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
1481     if (ret)
1482         return ret;
1483
1484     if (password == NULL &&
1485         options->opt_private->password == NULL &&
1486         options->opt_private->pk_init_ctx == NULL)
1487     {
1488         krb5_prompt prompt;
1489         krb5_data password_data;
1490         char *p, *q;
1491
1492         krb5_unparse_name (context, client, &p);
1493         asprintf (&q, "%s's Password: ", p);
1494         free (p);
1495         prompt.prompt = q;
1496         password_data.data   = buf;
1497         password_data.length = sizeof(buf);
1498         prompt.hidden = 1;
1499         prompt.reply  = &password_data;
1500         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1501
1502         ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
1503         free (q);
1504         if (ret) {
1505             memset (buf, 0, sizeof(buf));
1506             krb5_get_init_creds_opt_free(options);
1507             ret = KRB5_LIBOS_PWDINTR;
1508             krb5_clear_error_string (context);
1509             return ret;
1510         }
1511         password = password_data.data;
1512     }
1513
1514     if (options->opt_private->password == NULL) {
1515         ret = krb5_get_init_creds_opt_set_pa_password(context, options,
1516                                                       password, NULL);
1517         if (ret) {
1518             krb5_get_init_creds_opt_free(options);
1519             memset(buf, 0, sizeof(buf));
1520             return ret;
1521         }
1522     }
1523
1524     ret = krb5_get_init_creds(context, creds, client, prompter,
1525                               data, start_time, in_tkt_service, options);
1526     krb5_get_init_creds_opt_free(options);
1527     memset(buf, 0, sizeof(buf));
1528     return ret;
1529 }
1530
1531 static krb5_error_code
1532 init_creds_keyblock_key_proc (krb5_context context,
1533                               krb5_enctype type,
1534                               krb5_salt salt,
1535                               krb5_const_pointer keyseed,
1536                               krb5_keyblock **key)
1537 {
1538     return krb5_copy_keyblock (context, keyseed, key);
1539 }
1540
1541 krb5_error_code KRB5_LIB_FUNCTION
1542 krb5_get_init_creds_keyblock(krb5_context context,
1543                              krb5_creds *creds,
1544                              krb5_principal client,
1545                              krb5_keyblock *keyblock,
1546                              krb5_deltat start_time,
1547                              const char *in_tkt_service,
1548                              krb5_get_init_creds_opt *options)
1549 {
1550     struct krb5_get_init_creds_ctx ctx;
1551     krb5_error_code ret;
1552     
1553     ret = get_init_creds_common(context, client, start_time,
1554                                 in_tkt_service, options, &ctx);
1555     if (ret)
1556         goto out;
1557
1558     ret = krb5_get_in_cred (context,
1559                             KDCOptions2int(ctx.flags),
1560                             ctx.addrs,
1561                             ctx.etypes,
1562                             ctx.pre_auth_types,
1563                             NULL,
1564                             init_creds_keyblock_key_proc,
1565                             keyblock,
1566                             NULL,
1567                             NULL,
1568                             &ctx.cred,
1569                             NULL);
1570
1571     if (ret == 0 && creds)
1572         *creds = ctx.cred;
1573     else
1574         krb5_free_cred_contents (context, &ctx.cred);
1575
1576  out:
1577     free_init_creds_ctx(context, &ctx);
1578     return ret;
1579 }