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