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