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