r20640: Commit part 2/2
[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.105 2007/01/09 10:44: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_free_data(context, 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     /*
1096      * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1097      * need to expose our password protecting our PKCS12 key.
1098      */
1099
1100     if (ctx->pk_init_ctx) {
1101
1102         ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
1103         if (ret)
1104             return ret;
1105
1106     } else if (in_md->len != 0) {
1107         struct pa_info_data paid, *ppaid;
1108         
1109         memset(&paid, 0, sizeof(paid));
1110         
1111         paid.etype = ENCTYPE_NULL;
1112         ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
1113         
1114         pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1115         if (ppaid)
1116             free_paid(context, ppaid);
1117     }
1118
1119     pa_data_add_pac_request(context, ctx, *out_md);
1120
1121     if ((*out_md)->len == 0) {
1122         free(*out_md);
1123         *out_md = NULL;
1124     }
1125
1126     return 0;
1127 }
1128
1129 static krb5_error_code
1130 process_pa_data_to_key(krb5_context context,
1131                        krb5_get_init_creds_ctx *ctx,
1132                        krb5_creds *creds,
1133                        AS_REQ *a,
1134                        krb5_kdc_rep *rep,
1135                        const krb5_krbhst_info *hi,
1136                        krb5_keyblock **key)
1137 {
1138     struct pa_info_data paid, *ppaid = NULL;
1139     krb5_error_code ret;
1140     krb5_enctype etype;
1141     PA_DATA *pa;
1142
1143     memset(&paid, 0, sizeof(paid));
1144
1145     etype = rep->kdc_rep.enc_part.etype;
1146
1147     if (rep->kdc_rep.padata) {
1148         paid.etype = etype;
1149         ppaid = process_pa_info(context, creds->client, a, &paid, 
1150                                 rep->kdc_rep.padata);
1151     }
1152     if (ppaid == NULL) {
1153         ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1154         if (ret)
1155             return ret;
1156         paid.etype = etype;
1157         paid.s2kparams = NULL;
1158     }
1159
1160     pa = NULL;
1161     if (rep->kdc_rep.padata) {
1162         int idx = 0;
1163         pa = krb5_find_padata(rep->kdc_rep.padata->val, 
1164                               rep->kdc_rep.padata->len,
1165                               KRB5_PADATA_PK_AS_REP,
1166                               &idx);
1167         if (pa == NULL) {
1168             idx = 0;
1169             pa = krb5_find_padata(rep->kdc_rep.padata->val, 
1170                                   rep->kdc_rep.padata->len,
1171                                   KRB5_PADATA_PK_AS_REP_19,
1172                                   &idx);
1173         }
1174     }
1175     if (pa && ctx->pk_init_ctx) {
1176 #ifdef PKINIT
1177         ret = _krb5_pk_rd_pa_reply(context,
1178                                    a->req_body.realm,
1179                                    ctx->pk_init_ctx,
1180                                    etype,
1181                                    hi,
1182                                    ctx->pk_nonce,
1183                                    &ctx->req_buffer,
1184                                    pa,
1185                                    key);
1186 #else
1187         krb5_set_error_string(context, "no support for PKINIT compiled in");
1188         ret = EINVAL;
1189 #endif
1190     } else if (ctx->password)
1191         ret = pa_data_to_key_plain(context, creds->client, ctx, 
1192                                    paid.salt, paid.s2kparams, etype, key);
1193     else {
1194         krb5_set_error_string(context, "No usable pa data type");
1195         ret = EINVAL;
1196     }
1197
1198     free_paid(context, &paid);
1199     return ret;
1200 }
1201
1202 static krb5_error_code
1203 init_cred_loop(krb5_context context,
1204                krb5_get_init_creds_opt *init_cred_opts,
1205                const krb5_prompter_fct prompter,
1206                void *prompter_data,
1207                krb5_get_init_creds_ctx *ctx,
1208                krb5_creds *creds,
1209                krb5_kdc_rep *ret_as_reply)
1210 {
1211     krb5_error_code ret;
1212     krb5_kdc_rep rep;
1213     METHOD_DATA md;
1214     krb5_data resp;
1215     size_t len;
1216     size_t size;
1217     int send_to_kdc_flags = 0;
1218     krb5_krbhst_info *hi = NULL;
1219
1220
1221     memset(&md, 0, sizeof(md));
1222     memset(&rep, 0, sizeof(rep));
1223
1224     _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts);
1225
1226     if (ret_as_reply)
1227         memset(ret_as_reply, 0, sizeof(*ret_as_reply));
1228
1229     ret = init_creds_init_as_req(context, ctx->flags, creds,
1230                                  ctx->addrs, ctx->etypes, &ctx->as_req);
1231     if (ret)
1232         return ret;
1233
1234     /* Set a new nonce. */
1235     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1236     ctx->nonce &= 0xffffffff;
1237     /* XXX these just needs to be the same when using Windows PK-INIT */
1238     ctx->pk_nonce = ctx->nonce;
1239
1240     /*
1241      * Increase counter when we want other pre-auth types then
1242      * KRB5_PA_ENC_TIMESTAMP.
1243      */
1244 #define MAX_PA_COUNTER 3 
1245
1246     ctx->pa_counter = 0;
1247     while (ctx->pa_counter < MAX_PA_COUNTER) {
1248
1249         ctx->pa_counter++;
1250
1251         if (ctx->as_req.padata) {
1252             free_METHOD_DATA(ctx->as_req.padata);
1253             free(ctx->as_req.padata);
1254             ctx->as_req.padata = NULL;
1255         }
1256
1257         /* Set a new nonce. */
1258         ctx->as_req.req_body.nonce = ctx->nonce;
1259
1260         /* fill_in_md_data */
1261         ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
1262                                     &md, &ctx->as_req.padata,
1263                                     prompter, prompter_data);
1264         if (ret)
1265             goto out;
1266
1267         krb5_data_free(&ctx->req_buffer);
1268
1269         ASN1_MALLOC_ENCODE(AS_REQ, 
1270                            ctx->req_buffer.data, ctx->req_buffer.length, 
1271                            &ctx->as_req, &len, ret);
1272         if (ret)
1273             goto out;
1274         if(len != ctx->req_buffer.length)
1275             krb5_abortx(context, "internal error in ASN.1 encoder");
1276
1277         ret = krb5_sendto_kdc_flags (context, &ctx->req_buffer, 
1278                                      &creds->client->realm, &resp,
1279                                      send_to_kdc_flags);
1280         if (ret)
1281             goto out;
1282
1283         memset (&rep, 0, sizeof(rep));
1284         ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
1285         if (ret == 0) {
1286             krb5_data_free(&resp);
1287             krb5_clear_error_string(context);
1288             break;
1289         } else {
1290             /* let's try to parse it as a KRB-ERROR */
1291             KRB_ERROR error;
1292
1293             ret = krb5_rd_error(context, &resp, &error);
1294             if(ret && resp.data && ((char*)resp.data)[0] == 4)
1295                 ret = KRB5KRB_AP_ERR_V4_REPLY;
1296             krb5_data_free(&resp);
1297             if (ret)
1298                 goto out;
1299
1300             ret = krb5_error_from_rd_error(context, &error, creds);
1301
1302             /*
1303              * If no preauth was set and KDC requires it, give it one
1304              * more try.
1305              */
1306
1307             if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1308                 free_METHOD_DATA(&md);
1309                 memset(&md, 0, sizeof(md));
1310
1311                 if (error.e_data) {
1312                     ret = decode_METHOD_DATA(error.e_data->data, 
1313                                              error.e_data->length, 
1314                                              &md, 
1315                                              NULL);
1316                     if (ret)
1317                         krb5_set_error_string(context,
1318                                               "failed to decode METHOD DATA");
1319                 } else {
1320                     /* XXX guess what the server want here add add md */
1321                 }
1322                 krb5_free_error_contents(context, &error);
1323                 if (ret)
1324                     goto out;
1325             } else if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1326                 if (send_to_kdc_flags & KRB5_KRBHST_FLAGS_LARGE_MSG) {
1327                     if (ret_as_reply)
1328                         rep.error = error;
1329                     else
1330                         krb5_free_error_contents(context, &error);
1331                     goto out;
1332                 }
1333                 krb5_free_error_contents(context, &error);
1334                 send_to_kdc_flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
1335             } else {
1336                 _krb5_get_init_creds_opt_set_krb5_error(context,
1337                                                         init_cred_opts,
1338                                                         &error);
1339                 if (ret_as_reply)
1340                     rep.error = error;
1341                 else
1342                     krb5_free_error_contents(context, &error);
1343                 goto out;
1344             }
1345         }
1346     }
1347
1348     {
1349         krb5_keyblock *key = NULL;
1350
1351         ret = process_pa_data_to_key(context, ctx, creds, 
1352                                      &ctx->as_req, &rep, hi, &key);
1353         if (ret)
1354             goto out;
1355         
1356         ret = _krb5_extract_ticket(context,
1357                                    &rep,
1358                                    creds,
1359                                    key,
1360                                    NULL,
1361                                    KRB5_KU_AS_REP_ENC_PART,
1362                                    NULL,
1363                                    ctx->nonce,
1364                                    FALSE,
1365                                    ctx->flags.request_anonymous,
1366                                    NULL,
1367                                    NULL);
1368         krb5_free_keyblock(context, key);
1369     }
1370 out:
1371     krb5_data_free(&ctx->req_buffer);
1372     free_METHOD_DATA(&md);
1373     memset(&md, 0, sizeof(md));
1374
1375     if (ret == 0 && ret_as_reply)
1376         *ret_as_reply = rep;
1377     else 
1378         krb5_free_kdc_rep (context, &rep);
1379     return ret;
1380 }
1381
1382 krb5_error_code KRB5_LIB_FUNCTION
1383 krb5_get_init_creds(krb5_context context,
1384                     krb5_creds *creds,
1385                     krb5_principal client,
1386                     krb5_prompter_fct prompter,
1387                     void *data,
1388                     krb5_deltat start_time,
1389                     const char *in_tkt_service,
1390                     krb5_get_init_creds_opt *options)
1391 {
1392     krb5_get_init_creds_ctx ctx;
1393     krb5_kdc_rep kdc_reply;
1394     krb5_error_code ret;
1395     char buf[BUFSIZ];
1396     int done;
1397
1398     memset(&kdc_reply, 0, sizeof(kdc_reply));
1399
1400     ret = get_init_creds_common(context, client, start_time,
1401                                 in_tkt_service, options, &ctx);
1402     if (ret)
1403         goto out;
1404
1405     done = 0;
1406     while(!done) {
1407         memset(&kdc_reply, 0, sizeof(kdc_reply));
1408
1409         ret = init_cred_loop(context,
1410                              options,
1411                              prompter,
1412                              data,
1413                              &ctx,
1414                              &ctx.cred,
1415                              &kdc_reply);
1416         
1417         switch (ret) {
1418         case 0 :
1419             done = 1;
1420             break;
1421         case KRB5KDC_ERR_KEY_EXPIRED :
1422             /* try to avoid recursion */
1423
1424             /* don't try to change password where then where none */
1425             if (prompter == NULL || ctx.password == NULL)
1426                 goto out;
1427
1428             krb5_clear_error_string (context);
1429
1430             if (ctx.in_tkt_service != NULL
1431                 && strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
1432                 goto out;
1433
1434             ret = change_password (context,
1435                                    client,
1436                                    ctx.password,
1437                                    buf,
1438                                    sizeof(buf),
1439                                    prompter,
1440                                    data,
1441                                    options);
1442             if (ret)
1443                 goto out;
1444             ctx.password = buf;
1445             break;
1446         default:
1447             goto out;
1448         }
1449     }
1450
1451     if (prompter)
1452         print_expire (context,
1453                       krb5_principal_get_realm (context, ctx.cred.client),
1454                       &kdc_reply,
1455                       prompter,
1456                       data);
1457
1458  out:
1459     memset (buf, 0, sizeof(buf));
1460     free_init_creds_ctx(context, &ctx);
1461     krb5_free_kdc_rep (context, &kdc_reply);
1462     if (ret == 0)
1463         *creds = ctx.cred;
1464     else
1465         krb5_free_cred_contents (context, &ctx.cred);
1466
1467     return ret;
1468 }
1469
1470 krb5_error_code KRB5_LIB_FUNCTION
1471 krb5_get_init_creds_password(krb5_context context,
1472                              krb5_creds *creds,
1473                              krb5_principal client,
1474                              const char *password,
1475                              krb5_prompter_fct prompter,
1476                              void *data,
1477                              krb5_deltat start_time,
1478                              const char *in_tkt_service,
1479                              krb5_get_init_creds_opt *in_options)
1480 {
1481     krb5_get_init_creds_opt *options;
1482     char buf[BUFSIZ];
1483     krb5_error_code ret;
1484
1485     if (in_options == NULL)
1486         ret = krb5_get_init_creds_opt_alloc(context, &options);
1487     else
1488         ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
1489     if (ret)
1490         return ret;
1491
1492     if (password == NULL &&
1493         options->opt_private->password == NULL &&
1494         options->opt_private->pk_init_ctx == NULL)
1495     {
1496         krb5_prompt prompt;
1497         krb5_data password_data;
1498         char *p, *q;
1499
1500         krb5_unparse_name (context, client, &p);
1501         asprintf (&q, "%s's Password: ", p);
1502         free (p);
1503         prompt.prompt = q;
1504         password_data.data   = buf;
1505         password_data.length = sizeof(buf);
1506         prompt.hidden = 1;
1507         prompt.reply  = &password_data;
1508         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1509
1510         ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
1511         free (q);
1512         if (ret) {
1513             memset (buf, 0, sizeof(buf));
1514             krb5_get_init_creds_opt_free(context, options);
1515             ret = KRB5_LIBOS_PWDINTR;
1516             krb5_clear_error_string (context);
1517             return ret;
1518         }
1519         password = password_data.data;
1520     }
1521
1522     if (options->opt_private->password == NULL) {
1523         ret = krb5_get_init_creds_opt_set_pa_password(context, options,
1524                                                       password, NULL);
1525         if (ret) {
1526             krb5_get_init_creds_opt_free(context, options);
1527             memset(buf, 0, sizeof(buf));
1528             return ret;
1529         }
1530     }
1531
1532     ret = krb5_get_init_creds(context, creds, client, prompter,
1533                               data, start_time, in_tkt_service, options);
1534     krb5_get_init_creds_opt_free(context, options);
1535     memset(buf, 0, sizeof(buf));
1536     return ret;
1537 }
1538
1539 static krb5_error_code
1540 init_creds_keyblock_key_proc (krb5_context context,
1541                               krb5_enctype type,
1542                               krb5_salt salt,
1543                               krb5_const_pointer keyseed,
1544                               krb5_keyblock **key)
1545 {
1546     return krb5_copy_keyblock (context, keyseed, key);
1547 }
1548
1549 krb5_error_code KRB5_LIB_FUNCTION
1550 krb5_get_init_creds_keyblock(krb5_context context,
1551                              krb5_creds *creds,
1552                              krb5_principal client,
1553                              krb5_keyblock *keyblock,
1554                              krb5_deltat start_time,
1555                              const char *in_tkt_service,
1556                              krb5_get_init_creds_opt *options)
1557 {
1558     struct krb5_get_init_creds_ctx ctx;
1559     krb5_error_code ret;
1560     
1561     ret = get_init_creds_common(context, client, start_time,
1562                                 in_tkt_service, options, &ctx);
1563     if (ret)
1564         goto out;
1565
1566     ret = krb5_get_in_cred (context,
1567                             KDCOptions2int(ctx.flags),
1568                             ctx.addrs,
1569                             ctx.etypes,
1570                             ctx.pre_auth_types,
1571                             NULL,
1572                             init_creds_keyblock_key_proc,
1573                             keyblock,
1574                             NULL,
1575                             NULL,
1576                             &ctx.cred,
1577                             NULL);
1578
1579     if (ret == 0 && creds)
1580         *creds = ctx.cred;
1581     else
1582         krb5_free_cred_contents (context, &ctx.cred);
1583
1584  out:
1585     free_init_creds_ctx(context, &ctx);
1586     return ret;
1587 }