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