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