xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/init_creds_pw.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: init_creds_pw.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * 3. Neither the name of the Institute nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include "krb5_locl.h"
39 #ifndef WIN32
40 #include <heim-ipc.h>
41 #endif /* WIN32 */
42 
43 typedef struct krb5_get_init_creds_ctx {
44     KDCOptions flags;
45     krb5_creds cred;
46     krb5_addresses *addrs;
47     krb5_enctype *etypes;
48     krb5_preauthtype *pre_auth_types;
49     char *in_tkt_service;
50     unsigned nonce;
51     unsigned pk_nonce;
52 
53     krb5_data req_buffer;
54     AS_REQ as_req;
55     int pa_counter;
56 
57     /* password and keytab_data is freed on completion */
58     char *password;
59     krb5_keytab_key_proc_args *keytab_data;
60 
61     krb5_pointer *keyseed;
62     krb5_s2k_proc keyproc;
63 
64     krb5_get_init_creds_tristate req_pac;
65 
66     krb5_pk_init_ctx pk_init_ctx;
67     int ic_flags;
68 
69     struct {
70 	unsigned change_password:1;
71     } runflags;
72 
73     int used_pa_types;
74 #define  USED_PKINIT	1
75 #define  USED_PKINIT_W2K	2
76 #define  USED_ENC_TS_GUESS	4
77 #define  USED_ENC_TS_INFO	8
78 
79     METHOD_DATA md;
80     KRB_ERROR error;
81     AS_REP as_rep;
82     EncKDCRepPart enc_part;
83 
84     krb5_prompter_fct prompter;
85     void *prompter_data;
86 
87     struct pa_info_data *ppaid;
88     struct fast_state {
89 	enum PA_FX_FAST_REQUEST_enum type;
90 	unsigned int flags;
91 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
92 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
93 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
94 #define KRB5_FAST_REPLY_REPLY_VERIFED 8
95 #define KRB5_FAST_STRONG 16
96 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
97 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
98 #define KRB5_FAST_DISABLED 128
99 #define KRB5_FAST_AP_ARMOR_SERVICE 256
100 	krb5_keyblock *reply_key;
101 	krb5_ccache armor_ccache;
102 	krb5_principal armor_service;
103 	krb5_crypto armor_crypto;
104 	krb5_keyblock armor_key;
105 	krb5_keyblock *strengthen_key;
106     } fast_state;
107 } krb5_get_init_creds_ctx;
108 
109 
110 struct pa_info_data {
111     krb5_enctype etype;
112     krb5_salt salt;
113     krb5_data *s2kparams;
114 };
115 
116 static void
117 free_paid(krb5_context context, struct pa_info_data *ppaid)
118 {
119     krb5_free_salt(context, ppaid->salt);
120     if (ppaid->s2kparams)
121 	krb5_free_data(context, ppaid->s2kparams);
122 }
123 
124 static krb5_error_code KRB5_CALLCONV
125 default_s2k_func(krb5_context context, krb5_enctype type,
126 		 krb5_const_pointer keyseed,
127 		 krb5_salt salt, krb5_data *s2kparms,
128 		 krb5_keyblock **key)
129 {
130     krb5_error_code ret;
131     krb5_data password;
132     krb5_data opaque;
133 
134     _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
135 
136     password.data = rk_UNCONST(keyseed);
137     password.length = strlen(keyseed);
138     if (s2kparms)
139 	opaque = *s2kparms;
140     else
141 	krb5_data_zero(&opaque);
142 
143     *key = malloc(sizeof(**key));
144     if (*key == NULL)
145 	return ENOMEM;
146     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
147 					      salt, opaque, *key);
148     if (ret) {
149 	free(*key);
150 	*key = NULL;
151     }
152     return ret;
153 }
154 
155 static void
156 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
157 {
158     if (ctx->etypes)
159 	free(ctx->etypes);
160     if (ctx->pre_auth_types)
161 	free (ctx->pre_auth_types);
162     if (ctx->in_tkt_service)
163 	free(ctx->in_tkt_service);
164     if (ctx->keytab_data)
165 	free(ctx->keytab_data);
166     if (ctx->password) {
167 	memset(ctx->password, 0, strlen(ctx->password));
168 	free(ctx->password);
169     }
170     /*
171      * FAST state (we don't close the armor_ccache because we might have
172      * to destroy it, and how would we know? also, the caller should
173      * take care of cleaning up the armor_ccache).
174      */
175     if (ctx->fast_state.armor_service)
176 	krb5_free_principal(context, ctx->fast_state.armor_service);
177     if (ctx->fast_state.armor_crypto)
178 	krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
179     if (ctx->fast_state.strengthen_key)
180 	krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
181     krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
182 
183     krb5_data_free(&ctx->req_buffer);
184     krb5_free_cred_contents(context, &ctx->cred);
185     free_METHOD_DATA(&ctx->md);
186     free_AS_REP(&ctx->as_rep);
187     free_EncKDCRepPart(&ctx->enc_part);
188     free_KRB_ERROR(&ctx->error);
189     free_AS_REQ(&ctx->as_req);
190     if (ctx->ppaid) {
191 	free_paid(context, ctx->ppaid);
192 	free(ctx->ppaid);
193     }
194     memset(ctx, 0, sizeof(*ctx));
195 }
196 
197 static int
198 get_config_time (krb5_context context,
199 		 const char *realm,
200 		 const char *name,
201 		 int def)
202 {
203     int ret;
204 
205     ret = krb5_config_get_time (context, NULL,
206 				"realms",
207 				realm,
208 				name,
209 				NULL);
210     if (ret >= 0)
211 	return ret;
212     ret = krb5_config_get_time (context, NULL,
213 				"libdefaults",
214 				name,
215 				NULL);
216     if (ret >= 0)
217 	return ret;
218     return def;
219 }
220 
221 static krb5_error_code
222 init_cred (krb5_context context,
223 	   krb5_creds *cred,
224 	   krb5_principal client,
225 	   krb5_deltat start_time,
226 	   krb5_get_init_creds_opt *options)
227 {
228     krb5_error_code ret;
229     int tmp;
230     krb5_timestamp now;
231 
232     krb5_timeofday (context, &now);
233 
234     memset (cred, 0, sizeof(*cred));
235 
236     if (client)
237 	ret = krb5_copy_principal(context, client, &cred->client);
238     else
239 	ret = krb5_get_default_principal(context, &cred->client);
240     if (ret)
241         goto out;
242 
243     if (start_time)
244 	cred->times.starttime  = now + start_time;
245 
246     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
247 	tmp = options->tkt_life;
248     else
249 	tmp = KRB5_TKT_LIFETIME_DEFAULT;
250     cred->times.endtime = now + tmp;
251 
252     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
253 	if (options->renew_life > 0)
254 	    tmp = options->renew_life;
255 	else
256 	    tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
257 	cred->times.renew_till = now + tmp;
258     }
259 
260     return 0;
261 
262 out:
263     krb5_free_cred_contents (context, cred);
264     return ret;
265 }
266 
267 /*
268  * Print a message (str) to the user about the expiration in `lr'
269  */
270 
271 static void
272 report_expiration (krb5_context context,
273 		   krb5_prompter_fct prompter,
274 		   krb5_data *data,
275 		   const char *str,
276 		   time_t now)
277 {
278     char *p = NULL;
279 
280     if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
281 	return;
282     (*prompter)(context, data, NULL, p, 0, NULL);
283     free(p);
284 }
285 
286 /*
287  * Check the context, and in the case there is a expiration warning,
288  * use the prompter to print the warning.
289  *
290  * @param context A Kerberos 5 context.
291  * @param options An GIC options structure
292  * @param ctx The krb5_init_creds_context check for expiration.
293  */
294 
295 krb5_error_code
296 krb5_process_last_request(krb5_context context,
297 			  krb5_get_init_creds_opt *options,
298 			  krb5_init_creds_context ctx)
299 {
300     krb5_const_realm realm;
301     LastReq *lr;
302     krb5_boolean reported = FALSE;
303     krb5_timestamp sec;
304     time_t t;
305     size_t i;
306 
307     /*
308      * First check if there is a API consumer.
309      */
310 
311     realm = krb5_principal_get_realm (context, ctx->cred.client);
312     lr = &ctx->enc_part.last_req;
313 
314     if (options && options->opt_private && options->opt_private->lr.func) {
315 	krb5_last_req_entry **lre;
316 
317 	lre = calloc(lr->len + 1, sizeof(*lre));
318 	if (lre == NULL)
319 	    return krb5_enomem(context);
320 	for (i = 0; i < lr->len; i++) {
321 	    lre[i] = calloc(1, sizeof(*lre[i]));
322 	    if (lre[i] == NULL)
323 		break;
324 	    lre[i]->lr_type = lr->val[i].lr_type;
325 	    lre[i]->value = lr->val[i].lr_value;
326 	}
327 
328 	(*options->opt_private->lr.func)(context, lre,
329 					 options->opt_private->lr.ctx);
330 
331 	for (i = 0; i < lr->len; i++)
332 	    free(lre[i]);
333 	free(lre);
334     }
335 
336     /*
337      * Now check if we should prompt the user
338      */
339 
340     if (ctx->prompter == NULL)
341         return 0;
342 
343     krb5_timeofday (context, &sec);
344 
345     t = sec + get_config_time (context,
346 			       realm,
347 			       "warn_pwexpire",
348 			       7 * 24 * 60 * 60);
349 
350     for (i = 0; i < lr->len; ++i) {
351 	if (lr->val[i].lr_value <= t) {
352 	    switch (lr->val[i].lr_type) {
353 	    case LR_PW_EXPTIME :
354 		report_expiration(context, ctx->prompter,
355 				  ctx->prompter_data,
356 				  "Your password will expire at ",
357 				  lr->val[i].lr_value);
358 		reported = TRUE;
359 		break;
360 	    case LR_ACCT_EXPTIME :
361 		report_expiration(context, ctx->prompter,
362 				  ctx->prompter_data,
363 				  "Your account will expire at ",
364 				  lr->val[i].lr_value);
365 		reported = TRUE;
366 		break;
367             default:
368                 break;
369 	    }
370 	}
371     }
372 
373     if (!reported
374 	&& ctx->enc_part.key_expiration
375 	&& *ctx->enc_part.key_expiration <= t) {
376         report_expiration(context, ctx->prompter,
377 			  ctx->prompter_data,
378 			  "Your password/account will expire at ",
379 			  *ctx->enc_part.key_expiration);
380     }
381     return 0;
382 }
383 
384 static krb5_addresses no_addrs = { 0, NULL };
385 
386 static krb5_error_code
387 get_init_creds_common(krb5_context context,
388 		      krb5_principal client,
389 		      krb5_deltat start_time,
390 		      krb5_get_init_creds_opt *options,
391 		      krb5_init_creds_context ctx)
392 {
393     krb5_get_init_creds_opt *default_opt = NULL;
394     krb5_error_code ret;
395     krb5_enctype *etypes;
396     krb5_preauthtype *pre_auth_types;
397 
398     memset(ctx, 0, sizeof(*ctx));
399 
400     if (options == NULL) {
401 	const char *realm = krb5_principal_get_realm(context, client);
402 
403         krb5_get_init_creds_opt_alloc (context, &default_opt);
404 	options = default_opt;
405 	krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
406     }
407 
408     if (options->opt_private) {
409 	if (options->opt_private->password) {
410 	    ret = krb5_init_creds_set_password(context, ctx,
411 					       options->opt_private->password);
412 	    if (ret)
413 		goto out;
414 	}
415 
416 	ctx->keyproc = options->opt_private->key_proc;
417 	ctx->req_pac = options->opt_private->req_pac;
418 	ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
419 	ctx->ic_flags = options->opt_private->flags;
420     } else
421 	ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
422 
423     if (ctx->keyproc == NULL)
424 	ctx->keyproc = default_s2k_func;
425 
426     /* Enterprise name implicitly turns on canonicalize */
427     if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
428 	krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
429 	ctx->flags.canonicalize = 1;
430 
431     ctx->pre_auth_types = NULL;
432     ctx->addrs = NULL;
433     ctx->etypes = NULL;
434     ctx->pre_auth_types = NULL;
435 
436     ret = init_cred(context, &ctx->cred, client, start_time, options);
437     if (ret) {
438 	if (default_opt)
439 	    krb5_get_init_creds_opt_free(context, default_opt);
440 	return ret;
441     }
442 
443     ret = krb5_init_creds_set_service(context, ctx, NULL);
444     if (ret)
445 	goto out;
446 
447     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
448 	ctx->flags.forwardable = options->forwardable;
449 
450     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
451 	ctx->flags.proxiable = options->proxiable;
452 
453     if (start_time)
454 	ctx->flags.postdated = 1;
455     if (ctx->cred.times.renew_till)
456 	ctx->flags.renewable = 1;
457     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
458 	ctx->addrs = options->address_list;
459     } else if (options->opt_private) {
460 	switch (options->opt_private->addressless) {
461 	case KRB5_INIT_CREDS_TRISTATE_UNSET:
462 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
463 	    ctx->addrs = &no_addrs;
464 #else
465 	    ctx->addrs = NULL;
466 #endif
467 	    break;
468 	case KRB5_INIT_CREDS_TRISTATE_FALSE:
469 	    ctx->addrs = NULL;
470 	    break;
471 	case KRB5_INIT_CREDS_TRISTATE_TRUE:
472 	    ctx->addrs = &no_addrs;
473 	    break;
474 	}
475     }
476     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
477 	if (ctx->etypes)
478 	    free(ctx->etypes);
479 
480 	etypes = malloc((options->etype_list_length + 1)
481 			* sizeof(krb5_enctype));
482 	if (etypes == NULL) {
483 	    ret = krb5_enomem(context);
484 	    goto out;
485 	}
486 	memcpy (etypes, options->etype_list,
487 		options->etype_list_length * sizeof(krb5_enctype));
488 	etypes[options->etype_list_length] = ETYPE_NULL;
489 	ctx->etypes = etypes;
490     }
491     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
492 	pre_auth_types = malloc((options->preauth_list_length + 1)
493 				* sizeof(krb5_preauthtype));
494 	if (pre_auth_types == NULL) {
495 	    ret = krb5_enomem(context);
496 	    goto out;
497 	}
498 	memcpy (pre_auth_types, options->preauth_list,
499 		options->preauth_list_length * sizeof(krb5_preauthtype));
500 	pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
501 	ctx->pre_auth_types = pre_auth_types;
502     }
503     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
504 	ctx->flags.request_anonymous = options->anonymous;
505     if (default_opt)
506         krb5_get_init_creds_opt_free(context, default_opt);
507     return 0;
508  out:
509     if (default_opt)
510 	krb5_get_init_creds_opt_free(context, default_opt);
511     return ret;
512 }
513 
514 static krb5_error_code
515 change_password (krb5_context context,
516 		 krb5_principal client,
517 		 const char *password,
518 		 char *newpw,
519 		 size_t newpw_sz,
520 		 krb5_prompter_fct prompter,
521 		 void *data,
522 		 krb5_get_init_creds_opt *old_options)
523 {
524     krb5_prompt prompts[2];
525     krb5_error_code ret;
526     krb5_creds cpw_cred;
527     char buf1[BUFSIZ], buf2[BUFSIZ];
528     krb5_data password_data[2];
529     int result_code;
530     krb5_data result_code_string;
531     krb5_data result_string;
532     char *p;
533     krb5_get_init_creds_opt *options;
534 
535     heim_assert(prompter != NULL, "unexpected NULL prompter");
536 
537     memset (&cpw_cred, 0, sizeof(cpw_cred));
538 
539     ret = krb5_get_init_creds_opt_alloc(context, &options);
540     if (ret)
541         return ret;
542     krb5_get_init_creds_opt_set_tkt_life (options, 60);
543     krb5_get_init_creds_opt_set_forwardable (options, FALSE);
544     krb5_get_init_creds_opt_set_proxiable (options, FALSE);
545     if (old_options &&
546         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
547 	krb5_get_init_creds_opt_set_preauth_list(options,
548 						 old_options->preauth_list,
549 						 old_options->preauth_list_length);
550     if (old_options &&
551         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
552         krb5_get_init_creds_opt_set_change_password_prompt(options,
553                                                            old_options->change_password_prompt);
554 
555     krb5_data_zero (&result_code_string);
556     krb5_data_zero (&result_string);
557 
558     ret = krb5_get_init_creds_password (context,
559 					&cpw_cred,
560 					client,
561 					password,
562 					prompter,
563 					data,
564 					0,
565 					"kadmin/changepw",
566 					options);
567     krb5_get_init_creds_opt_free(context, options);
568     if (ret)
569 	goto out;
570 
571     for(;;) {
572 	password_data[0].data   = buf1;
573 	password_data[0].length = sizeof(buf1);
574 
575 	prompts[0].hidden = 1;
576 	prompts[0].prompt = "New password: ";
577 	prompts[0].reply  = &password_data[0];
578 	prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
579 
580 	password_data[1].data   = buf2;
581 	password_data[1].length = sizeof(buf2);
582 
583 	prompts[1].hidden = 1;
584 	prompts[1].prompt = "Repeat new password: ";
585 	prompts[1].reply  = &password_data[1];
586 	prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
587 
588 	ret = (*prompter) (context, data, NULL, "Changing password",
589 			   2, prompts);
590 	if (ret) {
591 	    memset (buf1, 0, sizeof(buf1));
592 	    memset (buf2, 0, sizeof(buf2));
593 	    goto out;
594 	}
595 
596 	if (strcmp (buf1, buf2) == 0)
597 	    break;
598 	memset (buf1, 0, sizeof(buf1));
599 	memset (buf2, 0, sizeof(buf2));
600     }
601 
602     ret = krb5_set_password (context,
603 			     &cpw_cred,
604 			     buf1,
605 			     client,
606 			     &result_code,
607 			     &result_code_string,
608 			     &result_string);
609     if (ret)
610 	goto out;
611     if (asprintf(&p, "%s: %.*s\n",
612 		 result_code ? "Error" : "Success",
613 		 (int)result_string.length,
614 		 result_string.length > 0 ? (char*)result_string.data : "") < 0)
615     {
616 	ret = ENOMEM;
617 	goto out;
618     }
619 
620     /* return the result */
621     (*prompter) (context, data, NULL, p, 0, NULL);
622 
623     free (p);
624     if (result_code == 0) {
625 	strlcpy (newpw, buf1, newpw_sz);
626 	ret = 0;
627     } else {
628 	ret = ENOTTY;
629 	krb5_set_error_message(context, ret,
630 			       N_("failed changing password", ""));
631     }
632 
633 out:
634     memset (buf1, 0, sizeof(buf1));
635     memset (buf2, 0, sizeof(buf2));
636     krb5_data_free (&result_string);
637     krb5_data_free (&result_code_string);
638     krb5_free_cred_contents (context, &cpw_cred);
639     return ret;
640 }
641 
642 
643 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
644 krb5_keyblock_key_proc (krb5_context context,
645 			krb5_keytype type,
646 			krb5_data *salt,
647 			krb5_const_pointer keyseed,
648 			krb5_keyblock **key)
649 {
650     return krb5_copy_keyblock (context, keyseed, key);
651 }
652 
653 /*
654  *
655  */
656 
657 static krb5_error_code
658 init_as_req (krb5_context context,
659 	     KDCOptions opts,
660 	     const krb5_creds *creds,
661 	     const krb5_addresses *addrs,
662 	     const krb5_enctype *etypes,
663 	     AS_REQ *a)
664 {
665     krb5_error_code ret;
666 
667     memset(a, 0, sizeof(*a));
668 
669     a->pvno = 5;
670     a->msg_type = krb_as_req;
671     a->req_body.kdc_options = opts;
672     a->req_body.cname = malloc(sizeof(*a->req_body.cname));
673     if (a->req_body.cname == NULL) {
674 	ret = krb5_enomem(context);
675 	goto fail;
676     }
677     a->req_body.sname = malloc(sizeof(*a->req_body.sname));
678     if (a->req_body.sname == NULL) {
679 	ret = krb5_enomem(context);
680 	goto fail;
681     }
682 
683     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
684     if (ret)
685 	goto fail;
686     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
687     if (ret)
688 	goto fail;
689 
690     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
691     if (ret)
692 	goto fail;
693 
694     if(creds->times.starttime) {
695 	a->req_body.from = malloc(sizeof(*a->req_body.from));
696 	if (a->req_body.from == NULL) {
697 	    ret = krb5_enomem(context);
698 	    goto fail;
699 	}
700 	*a->req_body.from = creds->times.starttime;
701     }
702     if(creds->times.endtime){
703 	if ((ALLOC(a->req_body.till, 1)) != NULL)
704             *a->req_body.till = creds->times.endtime;
705         else {
706             ret = krb5_enomem(context);
707             goto fail;
708         }
709     }
710     if(creds->times.renew_till){
711 	a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
712 	if (a->req_body.rtime == NULL) {
713 	    ret = krb5_enomem(context);
714 	    goto fail;
715 	}
716 	*a->req_body.rtime = creds->times.renew_till;
717     }
718     a->req_body.nonce = 0;
719     ret = _krb5_init_etype(context,
720 			   KRB5_PDU_AS_REQUEST,
721 			   &a->req_body.etype.len,
722 			   &a->req_body.etype.val,
723 			   etypes);
724     if (ret)
725 	goto fail;
726 
727     /*
728      * This means no addresses
729      */
730 
731     if (addrs && addrs->len == 0) {
732 	a->req_body.addresses = NULL;
733     } else {
734 	a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
735 	if (a->req_body.addresses == NULL) {
736 	    ret = krb5_enomem(context);
737 	    goto fail;
738 	}
739 
740 	if (addrs)
741 	    ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
742 	else {
743 	    ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
744 	    if(ret == 0 && a->req_body.addresses->len == 0) {
745 		free(a->req_body.addresses);
746 		a->req_body.addresses = NULL;
747 	    }
748 	}
749 	if (ret)
750 	    goto fail;
751     }
752 
753     a->req_body.enc_authorization_data = NULL;
754     a->req_body.additional_tickets = NULL;
755 
756     a->padata = NULL;
757 
758     return 0;
759  fail:
760     free_AS_REQ(a);
761     memset(a, 0, sizeof(*a));
762     return ret;
763 }
764 
765 
766 static krb5_error_code
767 set_paid(struct pa_info_data *paid, krb5_context context,
768 	 krb5_enctype etype,
769 	 krb5_salttype salttype, void *salt_string, size_t salt_len,
770 	 krb5_data *s2kparams)
771 {
772     paid->etype = etype;
773     paid->salt.salttype = salttype;
774     paid->salt.saltvalue.data = malloc(salt_len + 1);
775     if (paid->salt.saltvalue.data == NULL) {
776 	krb5_clear_error_message(context);
777 	return ENOMEM;
778     }
779     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
780     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
781     paid->salt.saltvalue.length = salt_len;
782     if (s2kparams) {
783 	krb5_error_code ret;
784 
785 	ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
786 	if (ret) {
787 	    krb5_clear_error_message(context);
788 	    krb5_free_salt(context, paid->salt);
789 	    return ret;
790 	}
791     } else
792 	paid->s2kparams = NULL;
793 
794     return 0;
795 }
796 
797 static struct pa_info_data *
798 pa_etype_info2(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     ETYPE_INFO2 e;
806     size_t sz;
807     size_t i, j;
808 
809     memset(&e, 0, sizeof(e));
810     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
811     if (ret)
812 	goto out;
813     if (e.len == 0)
814 	goto out;
815     for (j = 0; j < asreq->req_body.etype.len; j++) {
816 	for (i = 0; i < e.len; i++) {
817 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
818 		krb5_salt salt;
819 		if (e.val[i].salt == NULL)
820 		    ret = krb5_get_pw_salt(context, client, &salt);
821 		else {
822 		    salt.saltvalue.data = *e.val[i].salt;
823 		    salt.saltvalue.length = strlen(*e.val[i].salt);
824 		    ret = 0;
825 		}
826 		if (ret == 0)
827 		    ret = set_paid(paid, context, e.val[i].etype,
828 				   KRB5_PW_SALT,
829 				   salt.saltvalue.data,
830 				   salt.saltvalue.length,
831 				   e.val[i].s2kparams);
832 		if (e.val[i].salt == NULL)
833 		    krb5_free_salt(context, salt);
834 		if (ret == 0) {
835 		    free_ETYPE_INFO2(&e);
836 		    return paid;
837 		}
838 	    }
839 	}
840     }
841  out:
842     free_ETYPE_INFO2(&e);
843     return NULL;
844 }
845 
846 static struct pa_info_data *
847 pa_etype_info(krb5_context context,
848 	      const krb5_principal client,
849 	      const AS_REQ *asreq,
850 	      struct pa_info_data *paid,
851 	      heim_octet_string *data)
852 {
853     krb5_error_code ret;
854     ETYPE_INFO e;
855     size_t sz;
856     size_t i, j;
857 
858     memset(&e, 0, sizeof(e));
859     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
860     if (ret)
861 	goto out;
862     if (e.len == 0)
863 	goto out;
864     for (j = 0; j < asreq->req_body.etype.len; j++) {
865 	for (i = 0; i < e.len; i++) {
866 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
867 		krb5_salt salt;
868 		salt.salttype = KRB5_PW_SALT;
869 		if (e.val[i].salt == NULL)
870 		    ret = krb5_get_pw_salt(context, client, &salt);
871 		else {
872 		    salt.saltvalue = *e.val[i].salt;
873 		    ret = 0;
874 		}
875 		if (e.val[i].salttype)
876 		    salt.salttype = *e.val[i].salttype;
877 		if (ret == 0) {
878 		    ret = set_paid(paid, context, e.val[i].etype,
879 				   salt.salttype,
880 				   salt.saltvalue.data,
881 				   salt.saltvalue.length,
882 				   NULL);
883 		    if (e.val[i].salt == NULL)
884 			krb5_free_salt(context, salt);
885 		}
886 		if (ret == 0) {
887 		    free_ETYPE_INFO(&e);
888 		    return paid;
889 		}
890 	    }
891 	}
892     }
893  out:
894     free_ETYPE_INFO(&e);
895     return NULL;
896 }
897 
898 static struct pa_info_data *
899 pa_pw_or_afs3_salt(krb5_context context,
900 		   const krb5_principal client,
901 		   const AS_REQ *asreq,
902 		   struct pa_info_data *paid,
903 		   heim_octet_string *data)
904 {
905     krb5_error_code ret;
906     if (paid->etype == KRB5_ENCTYPE_NULL)
907 	return NULL;
908     ret = set_paid(paid, context,
909 		   paid->etype,
910 		   paid->salt.salttype,
911 		   data->data,
912 		   data->length,
913 		   NULL);
914     if (ret)
915 	return NULL;
916     return paid;
917 }
918 
919 
920 struct pa_info {
921     krb5_preauthtype type;
922     struct pa_info_data *(*salt_info)(krb5_context,
923 				      const krb5_principal,
924 				      const AS_REQ *,
925 				      struct pa_info_data *,
926 				      heim_octet_string *);
927 };
928 
929 static struct pa_info pa_prefs[] = {
930     { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
931     { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
932     { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
933     { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
934 };
935 
936 static PA_DATA *
937 find_pa_data(const METHOD_DATA *md, unsigned type)
938 {
939     size_t i;
940     if (md == NULL)
941 	return NULL;
942     for (i = 0; i < md->len; i++)
943 	if (md->val[i].padata_type == type)
944 	    return &md->val[i];
945     return NULL;
946 }
947 
948 static struct pa_info_data *
949 process_pa_info(krb5_context context,
950 		const krb5_principal client,
951 		const AS_REQ *asreq,
952 		struct pa_info_data *paid,
953 		METHOD_DATA *md)
954 {
955     struct pa_info_data *p = NULL;
956     size_t i;
957 
958     for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
959 	PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
960 	if (pa == NULL)
961 	    continue;
962 	paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
963 	p = (*pa_prefs[i].salt_info)(context, client, asreq,
964 				     paid, &pa->padata_value);
965     }
966     return p;
967 }
968 
969 static krb5_error_code
970 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
971 		      krb5_enctype etype, krb5_keyblock *key)
972 {
973     PA_ENC_TS_ENC p;
974     unsigned char *buf;
975     size_t buf_size;
976     size_t len = 0;
977     EncryptedData encdata;
978     krb5_error_code ret;
979     int32_t usec;
980     int usec2;
981     krb5_crypto crypto;
982 
983     krb5_us_timeofday (context, &p.patimestamp, &usec);
984     usec2         = usec;
985     p.pausec      = &usec2;
986 
987     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
988     if (ret)
989 	return ret;
990     if(buf_size != len)
991 	krb5_abortx(context, "internal error in ASN.1 encoder");
992 
993     ret = krb5_crypto_init(context, key, 0, &crypto);
994     if (ret) {
995 	free(buf);
996 	return ret;
997     }
998     ret = krb5_encrypt_EncryptedData(context,
999 				     crypto,
1000 				     KRB5_KU_PA_ENC_TIMESTAMP,
1001 				     buf,
1002 				     len,
1003 				     0,
1004 				     &encdata);
1005     free(buf);
1006     krb5_crypto_destroy(context, crypto);
1007     if (ret)
1008 	return ret;
1009 
1010     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1011     free_EncryptedData(&encdata);
1012     if (ret)
1013 	return ret;
1014     if(buf_size != len)
1015 	krb5_abortx(context, "internal error in ASN.1 encoder");
1016 
1017     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1018     if (ret)
1019 	free(buf);
1020     return ret;
1021 }
1022 
1023 static krb5_error_code
1024 add_enc_ts_padata(krb5_context context,
1025 		  METHOD_DATA *md,
1026 		  krb5_principal client,
1027 		  krb5_s2k_proc keyproc,
1028 		  krb5_const_pointer keyseed,
1029 		  krb5_enctype *enctypes,
1030 		  unsigned netypes,
1031 		  krb5_salt *salt,
1032 		  krb5_data *s2kparams)
1033 {
1034     krb5_error_code ret;
1035     krb5_salt salt2;
1036     krb5_enctype *ep;
1037     size_t i;
1038 
1039     if(salt == NULL) {
1040 	/* default to standard salt */
1041 	ret = krb5_get_pw_salt (context, client, &salt2);
1042 	if (ret)
1043 	    return ret;
1044 	salt = &salt2;
1045     }
1046     if (!enctypes) {
1047 	enctypes = context->etypes;
1048 	netypes = 0;
1049 	for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1050 	    netypes++;
1051     }
1052 
1053     for (i = 0; i < netypes; ++i) {
1054 	krb5_keyblock *key;
1055 
1056 	_krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1057 
1058 	ret = (*keyproc)(context, enctypes[i], keyseed,
1059 			 *salt, s2kparams, &key);
1060 	if (ret)
1061 	    continue;
1062 	ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1063 	krb5_free_keyblock (context, key);
1064 	if (ret)
1065 	    return ret;
1066     }
1067     if(salt == &salt2)
1068 	krb5_free_salt(context, salt2);
1069     return 0;
1070 }
1071 
1072 static krb5_error_code
1073 pa_data_to_md_ts_enc(krb5_context context,
1074 		     const AS_REQ *a,
1075 		     const krb5_principal client,
1076 		     krb5_get_init_creds_ctx *ctx,
1077 		     struct pa_info_data *ppaid,
1078 		     METHOD_DATA *md)
1079 {
1080     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1081 	return 0;
1082 
1083     if (ppaid) {
1084 	add_enc_ts_padata(context, md, client,
1085 			  ctx->keyproc, ctx->keyseed,
1086 			  &ppaid->etype, 1,
1087 			  &ppaid->salt, ppaid->s2kparams);
1088     } else {
1089 	krb5_salt salt;
1090 
1091 	_krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1092 
1093 	/* make a v5 salted pa-data */
1094 	add_enc_ts_padata(context, md, client,
1095 			  ctx->keyproc, ctx->keyseed,
1096 			  a->req_body.etype.val, a->req_body.etype.len,
1097 			  NULL, NULL);
1098 
1099 	/* make a v4 salted pa-data */
1100 	salt.salttype = KRB5_PW_SALT;
1101 	krb5_data_zero(&salt.saltvalue);
1102 	add_enc_ts_padata(context, md, client,
1103 			  ctx->keyproc, ctx->keyseed,
1104 			  a->req_body.etype.val, a->req_body.etype.len,
1105 			  &salt, NULL);
1106     }
1107     return 0;
1108 }
1109 
1110 static krb5_error_code
1111 pa_data_to_key_plain(krb5_context context,
1112 		     const krb5_principal client,
1113 		     krb5_get_init_creds_ctx *ctx,
1114 		     krb5_salt salt,
1115 		     krb5_data *s2kparams,
1116 		     krb5_enctype etype,
1117 		     krb5_keyblock **key)
1118 {
1119     krb5_error_code ret;
1120 
1121     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1122 			   salt, s2kparams, key);
1123     return ret;
1124 }
1125 
1126 
1127 static krb5_error_code
1128 pa_data_to_md_pkinit(krb5_context context,
1129 		     const AS_REQ *a,
1130 		     const krb5_principal client,
1131 		     int win2k,
1132 		     krb5_get_init_creds_ctx *ctx,
1133 		     METHOD_DATA *md)
1134 {
1135     if (ctx->pk_init_ctx == NULL)
1136 	return 0;
1137 #ifdef PKINIT
1138     return _krb5_pk_mk_padata(context,
1139 			      ctx->pk_init_ctx,
1140 			      ctx->ic_flags,
1141 			      win2k,
1142 			      &a->req_body,
1143 			      ctx->pk_nonce,
1144 			      md);
1145 #else
1146     krb5_set_error_message(context, EINVAL,
1147 			   N_("no support for PKINIT compiled in", ""));
1148     return EINVAL;
1149 #endif
1150 }
1151 
1152 static krb5_error_code
1153 pa_data_add_pac_request(krb5_context context,
1154 			krb5_get_init_creds_ctx *ctx,
1155 			METHOD_DATA *md)
1156 {
1157     size_t len = 0, length;
1158     krb5_error_code ret;
1159     PA_PAC_REQUEST req;
1160     void *buf;
1161 
1162     switch (ctx->req_pac) {
1163     case KRB5_INIT_CREDS_TRISTATE_UNSET:
1164 	return 0; /* don't bother */
1165     case KRB5_INIT_CREDS_TRISTATE_TRUE:
1166 	req.include_pac = 1;
1167 	break;
1168     case KRB5_INIT_CREDS_TRISTATE_FALSE:
1169 	req.include_pac = 0;
1170     }
1171 
1172     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1173 		       &req, &len, ret);
1174     if (ret)
1175 	return ret;
1176     if(len != length)
1177 	krb5_abortx(context, "internal error in ASN.1 encoder");
1178 
1179     ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1180     if (ret)
1181 	free(buf);
1182 
1183     return 0;
1184 }
1185 
1186 /*
1187  * Assumes caller always will free `out_md', even on error.
1188  */
1189 
1190 static krb5_error_code
1191 process_pa_data_to_md(krb5_context context,
1192 		      const krb5_creds *creds,
1193 		      const AS_REQ *a,
1194 		      krb5_get_init_creds_ctx *ctx,
1195 		      METHOD_DATA *in_md,
1196 		      METHOD_DATA **out_md,
1197 		      krb5_prompter_fct prompter,
1198 		      void *prompter_data)
1199 {
1200     krb5_error_code ret;
1201 
1202     ALLOC(*out_md, 1);
1203     if (*out_md == NULL)
1204 	return krb5_enomem(context);
1205 
1206     (*out_md)->len = 0;
1207     (*out_md)->val = NULL;
1208 
1209     if (_krb5_have_debug(context, 5)) {
1210 	unsigned i;
1211 	_krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1212 	for (i = 0; i < in_md->len; i++)
1213 	    _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1214     }
1215 
1216     /*
1217      * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1218      * need to expose our password protecting our PKCS12 key.
1219      */
1220 
1221     if (ctx->pk_init_ctx) {
1222 
1223  	_krb5_debug(context, 5, "krb5_get_init_creds: "
1224 		    "prepareing PKINIT padata (%s)",
1225  		    (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1226 
1227  	if (ctx->used_pa_types & USED_PKINIT_W2K) {
1228  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1229  				   "Already tried pkinit, looping");
1230  	    return KRB5_GET_IN_TKT_LOOP;
1231  	}
1232 
1233 	ret = pa_data_to_md_pkinit(context, a, creds->client,
1234 				   (ctx->used_pa_types & USED_PKINIT),
1235 				   ctx, *out_md);
1236 	if (ret)
1237 	    return ret;
1238 
1239 	if (ctx->used_pa_types & USED_PKINIT)
1240 	    ctx->used_pa_types |= USED_PKINIT_W2K;
1241  	else
1242  	    ctx->used_pa_types |= USED_PKINIT;
1243 
1244     } else if (in_md->len != 0) {
1245 	struct pa_info_data *paid, *ppaid;
1246  	unsigned flag;
1247 
1248 	paid = calloc(1, sizeof(*paid));
1249         if (paid == NULL)
1250             return krb5_enomem(context);
1251 
1252 	paid->etype = KRB5_ENCTYPE_NULL;
1253 	ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1254 
1255  	if (ppaid)
1256  	    flag = USED_ENC_TS_INFO;
1257  	else
1258  	    flag = USED_ENC_TS_GUESS;
1259 
1260  	if (ctx->used_pa_types & flag) {
1261  	    if (ppaid)
1262  		free_paid(context, ppaid);
1263             free(paid);
1264  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1265  				   "Already tried ENC-TS-%s, looping",
1266  				   flag == USED_ENC_TS_INFO ? "info" : "guess");
1267  	    return KRB5_GET_IN_TKT_LOOP;
1268  	}
1269 
1270 	pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1271 
1272 	ctx->used_pa_types |= flag;
1273 
1274 	if (ppaid) {
1275 	    if (ctx->ppaid) {
1276 		free_paid(context, ctx->ppaid);
1277 		free(ctx->ppaid);
1278 	    }
1279 	    ctx->ppaid = ppaid;
1280 	} else
1281 	    free(paid);
1282     }
1283 
1284     pa_data_add_pac_request(context, ctx, *out_md);
1285 
1286     if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
1287  	ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1288  	if (ret)
1289  	    return ret;
1290     }
1291 
1292     if ((*out_md)->len == 0) {
1293 	free(*out_md);
1294 	*out_md = NULL;
1295     }
1296 
1297     return 0;
1298 }
1299 
1300 static krb5_error_code
1301 process_pa_data_to_key(krb5_context context,
1302 		       krb5_get_init_creds_ctx *ctx,
1303 		       krb5_creds *creds,
1304 		       AS_REQ *a,
1305 		       AS_REP *rep,
1306 		       const krb5_krbhst_info *hi,
1307 		       krb5_keyblock **key)
1308 {
1309     struct pa_info_data paid, *ppaid = NULL;
1310     krb5_error_code ret;
1311     krb5_enctype etype;
1312     PA_DATA *pa;
1313 
1314     memset(&paid, 0, sizeof(paid));
1315 
1316     etype = rep->enc_part.etype;
1317 
1318     if (rep->padata) {
1319 	paid.etype = etype;
1320 	ppaid = process_pa_info(context, creds->client, a, &paid,
1321 				rep->padata);
1322     }
1323     if (ppaid == NULL)
1324 	ppaid = ctx->ppaid;
1325     if (ppaid == NULL) {
1326 	ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1327 	if (ret)
1328 	    return ret;
1329 	paid.etype = etype;
1330 	paid.s2kparams = NULL;
1331 	ppaid = &paid;
1332     }
1333 
1334     pa = NULL;
1335     if (rep->padata) {
1336 	int idx = 0;
1337 	pa = krb5_find_padata(rep->padata->val,
1338 			      rep->padata->len,
1339 			      KRB5_PADATA_PK_AS_REP,
1340 			      &idx);
1341 	if (pa == NULL) {
1342 	    idx = 0;
1343 	    pa = krb5_find_padata(rep->padata->val,
1344 				  rep->padata->len,
1345 				  KRB5_PADATA_PK_AS_REP_19,
1346 				  &idx);
1347 	}
1348     }
1349     if (pa && ctx->pk_init_ctx) {
1350 #ifdef PKINIT
1351 	_krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1352 
1353 	ret = _krb5_pk_rd_pa_reply(context,
1354 				   a->req_body.realm,
1355 				   ctx->pk_init_ctx,
1356 				   etype,
1357 				   hi,
1358 				   ctx->pk_nonce,
1359 				   &ctx->req_buffer,
1360 				   pa,
1361 				   key);
1362 #else
1363 	ret = EINVAL;
1364 	krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1365 #endif
1366     } else if (ctx->keyseed) {
1367  	_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1368 	ret = pa_data_to_key_plain(context, creds->client, ctx,
1369 				   ppaid->salt, ppaid->s2kparams, etype, key);
1370     } else {
1371 	ret = EINVAL;
1372 	krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1373     }
1374 
1375     free_paid(context, &paid);
1376     return ret;
1377 }
1378 
1379 /**
1380  * Start a new context to get a new initial credential.
1381  *
1382  * @param context A Kerberos 5 context.
1383  * @param client The Kerberos principal to get the credential for, if
1384  *     NULL is given, the default principal is used as determined by
1385  *     krb5_get_default_principal().
1386  * @param prompter
1387  * @param prompter_data
1388  * @param start_time the time the ticket should start to be valid or 0 for now.
1389  * @param options a options structure, can be NULL for default options.
1390  * @param rctx A new allocated free with krb5_init_creds_free().
1391  *
1392  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1393  *
1394  * @ingroup krb5_credential
1395  */
1396 
1397 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1398 krb5_init_creds_init(krb5_context context,
1399 		     krb5_principal client,
1400 		     krb5_prompter_fct prompter,
1401 		     void *prompter_data,
1402 		     krb5_deltat start_time,
1403 		     krb5_get_init_creds_opt *options,
1404 		     krb5_init_creds_context *rctx)
1405 {
1406     krb5_init_creds_context ctx;
1407     krb5_error_code ret;
1408 
1409     *rctx = NULL;
1410 
1411     ctx = calloc(1, sizeof(*ctx));
1412     if (ctx == NULL)
1413 	return krb5_enomem(context);
1414 
1415     ret = get_init_creds_common(context, client, start_time, options, ctx);
1416     if (ret) {
1417 	free(ctx);
1418 	return ret;
1419     }
1420 
1421     /* Set a new nonce. */
1422     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1423     ctx->nonce &= 0x7fffffff;
1424     /* XXX these just needs to be the same when using Windows PK-INIT */
1425     ctx->pk_nonce = ctx->nonce;
1426 
1427     ctx->prompter = prompter;
1428     ctx->prompter_data = prompter_data;
1429 
1430     *rctx = ctx;
1431 
1432     return ret;
1433 }
1434 
1435 /**
1436  * Sets the service that the is requested. This call is only neede for
1437  * special initial tickets, by default the a krbtgt is fetched in the default realm.
1438  *
1439  * @param context a Kerberos 5 context.
1440  * @param ctx a krb5_init_creds_context context.
1441  * @param service the service given as a string, for example
1442  *        "kadmind/admin". If NULL, the default krbtgt in the clients
1443  *        realm is set.
1444  *
1445  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1446  * @ingroup krb5_credential
1447  */
1448 
1449 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1450 krb5_init_creds_set_service(krb5_context context,
1451 			    krb5_init_creds_context ctx,
1452 			    const char *service)
1453 {
1454     krb5_const_realm client_realm;
1455     krb5_principal principal;
1456     krb5_error_code ret;
1457 
1458     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1459 
1460     if (service) {
1461 	ret = krb5_parse_name (context, service, &principal);
1462 	if (ret)
1463 	    return ret;
1464 	krb5_principal_set_realm (context, principal, client_realm);
1465     } else {
1466 	ret = krb5_make_principal(context, &principal,
1467 				  client_realm, KRB5_TGS_NAME, client_realm,
1468 				  NULL);
1469 	if (ret)
1470 	    return ret;
1471     }
1472 
1473     /*
1474      * This is for Windows RODC that are picky about what name type
1475      * the server principal have, and the really strange part is that
1476      * they are picky about the AS-REQ name type and not the TGS-REQ
1477      * later. Oh well.
1478      */
1479 
1480     if (krb5_principal_is_krbtgt(context, principal))
1481 	krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1482 
1483     krb5_free_principal(context, ctx->cred.server);
1484     ctx->cred.server = principal;
1485 
1486     return 0;
1487 }
1488 
1489 /**
1490  * Sets the password that will use for the request.
1491  *
1492  * @param context a Kerberos 5 context.
1493  * @param ctx ctx krb5_init_creds_context context.
1494  * @param password the password to use.
1495  *
1496  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1497  * @ingroup krb5_credential
1498  */
1499 
1500 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1501 krb5_init_creds_set_password(krb5_context context,
1502 			     krb5_init_creds_context ctx,
1503 			     const char *password)
1504 {
1505     if (ctx->password) {
1506 	memset(ctx->password, 0, strlen(ctx->password));
1507 	free(ctx->password);
1508     }
1509     if (password) {
1510 	ctx->password = strdup(password);
1511 	if (ctx->password == NULL)
1512 	    return krb5_enomem(context);
1513 	ctx->keyseed = (void *) ctx->password;
1514     } else {
1515 	ctx->keyseed = NULL;
1516 	ctx->password = NULL;
1517     }
1518 
1519     return 0;
1520 }
1521 
1522 static krb5_error_code KRB5_CALLCONV
1523 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1524 		krb5_const_pointer keyseed,
1525 		krb5_salt salt, krb5_data *s2kparms,
1526 		krb5_keyblock **key)
1527 {
1528     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
1529     krb5_keytab keytab = args->keytab;
1530     krb5_principal principal = args->principal;
1531     krb5_error_code ret;
1532     krb5_keytab real_keytab;
1533     krb5_keytab_entry entry;
1534 
1535     if(keytab == NULL)
1536 	krb5_kt_default(context, &real_keytab);
1537     else
1538 	real_keytab = keytab;
1539 
1540     ret = krb5_kt_get_entry (context, real_keytab, principal,
1541 			     0, enctype, &entry);
1542 
1543     if (keytab == NULL)
1544 	krb5_kt_close (context, real_keytab);
1545 
1546     if (ret)
1547 	return ret;
1548 
1549     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1550     krb5_kt_free_entry(context, &entry);
1551     return ret;
1552 }
1553 
1554 
1555 /**
1556  * Set the keytab to use for authentication.
1557  *
1558  * @param context a Kerberos 5 context.
1559  * @param ctx ctx krb5_init_creds_context context.
1560  * @param keytab the keytab to read the key from.
1561  *
1562  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1563  * @ingroup krb5_credential
1564  */
1565 
1566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1567 krb5_init_creds_set_keytab(krb5_context context,
1568 			   krb5_init_creds_context ctx,
1569 			   krb5_keytab keytab)
1570 {
1571     krb5_keytab_key_proc_args *a;
1572     krb5_keytab_entry entry;
1573     krb5_kt_cursor cursor;
1574     krb5_enctype *etypes = NULL;
1575     krb5_error_code ret;
1576     size_t netypes = 0;
1577     int kvno = 0, found = 0;
1578 
1579     a = malloc(sizeof(*a));
1580     if (a == NULL)
1581 	return krb5_enomem(context);
1582 
1583     a->principal = ctx->cred.client;
1584     a->keytab    = keytab;
1585 
1586     ctx->keytab_data = a;
1587     ctx->keyseed = (void *)a;
1588     ctx->keyproc = keytab_key_proc;
1589 
1590     /*
1591      * We need to the KDC what enctypes we support for this keytab,
1592      * esp if the keytab is really a password based entry, then the
1593      * KDC might have more enctypes in the database then what we have
1594      * in the keytab.
1595      */
1596 
1597     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1598     if(ret)
1599 	goto out;
1600 
1601     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1602 	void *ptr;
1603 
1604 	if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1605 	    goto next;
1606 
1607 	found = 1;
1608 
1609 	/* check if we ahve this kvno already */
1610 	if (entry.vno > kvno) {
1611 	    /* remove old list of etype */
1612 	    if (etypes)
1613 		free(etypes);
1614 	    etypes = NULL;
1615 	    netypes = 0;
1616 	    kvno = entry.vno;
1617 	} else if (entry.vno != kvno)
1618 	    goto next;
1619 
1620 	/* check if enctype is supported */
1621 	if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1622 	    goto next;
1623 
1624 	/* add enctype to supported list */
1625 	ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1626 	if (ptr == NULL) {
1627 	    free(etypes);
1628 	    ret = krb5_enomem(context);
1629 	    goto out;
1630 	}
1631 
1632 	etypes = ptr;
1633 	etypes[netypes] = entry.keyblock.keytype;
1634 	etypes[netypes + 1] = ETYPE_NULL;
1635 	netypes++;
1636     next:
1637 	krb5_kt_free_entry(context, &entry);
1638     }
1639     krb5_kt_end_seq_get(context, keytab, &cursor);
1640 
1641     if (etypes) {
1642 	if (ctx->etypes)
1643 	    free(ctx->etypes);
1644 	ctx->etypes = etypes;
1645     }
1646 
1647  out:
1648     if (!found) {
1649 	if (ret == 0)
1650 	    ret = KRB5_KT_NOTFOUND;
1651 	_krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
1652     }
1653 
1654     return ret;
1655 }
1656 
1657 static krb5_error_code KRB5_CALLCONV
1658 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1659 		  krb5_const_pointer keyseed,
1660 		  krb5_salt salt, krb5_data *s2kparms,
1661 		  krb5_keyblock **key)
1662 {
1663     return krb5_copy_keyblock (context, keyseed, key);
1664 }
1665 
1666 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1667 krb5_init_creds_set_keyblock(krb5_context context,
1668 			     krb5_init_creds_context ctx,
1669 			     krb5_keyblock *keyblock)
1670 {
1671     ctx->keyseed = (void *)keyblock;
1672     ctx->keyproc = keyblock_key_proc;
1673 
1674     return 0;
1675 }
1676 
1677 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1678 krb5_init_creds_set_fast_ccache(krb5_context context,
1679 				krb5_init_creds_context ctx,
1680 				krb5_ccache fast_ccache)
1681 {
1682     ctx->fast_state.armor_ccache = fast_ccache;
1683     ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
1684     return 0;
1685 }
1686 
1687 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1688 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
1689 					  krb5_init_creds_context ctx,
1690 					  krb5_const_principal armor_service)
1691 {
1692     krb5_error_code ret;
1693 
1694     if (ctx->fast_state.armor_service)
1695 	krb5_free_principal(context, ctx->fast_state.armor_service);
1696     if (armor_service) {
1697 	ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
1698 	if (ret)
1699 	    return ret;
1700     } else {
1701 	ctx->fast_state.armor_service = NULL;
1702     }
1703     ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
1704     return 0;
1705 }
1706 
1707 /*
1708  * FAST
1709  */
1710 
1711 static krb5_error_code
1712 check_fast(krb5_context context, struct fast_state *state)
1713 {
1714     if (state->flags & KRB5_FAST_EXPECTED) {
1715 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
1716 			       "Expected FAST, but no FAST "
1717 			       "was in the response from the KDC");
1718 	return KRB5KRB_AP_ERR_MODIFIED;
1719     }
1720     return 0;
1721 }
1722 
1723 
1724 static krb5_error_code
1725 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
1726 		   krb5_data *chksumdata,
1727 		   struct fast_state *state, AS_REP *rep)
1728 {
1729     PA_FX_FAST_REPLY fxfastrep;
1730     KrbFastResponse fastrep;
1731     krb5_error_code ret;
1732     PA_DATA *pa = NULL;
1733     int idx = 0;
1734 
1735     if (state->armor_crypto == NULL || rep->padata == NULL)
1736 	return check_fast(context, state);
1737 
1738     /* find PA_FX_FAST_REPLY */
1739 
1740     pa = krb5_find_padata(rep->padata->val, rep->padata->len,
1741 			  KRB5_PADATA_FX_FAST, &idx);
1742     if (pa == NULL)
1743 	return check_fast(context, state);
1744 
1745     memset(&fxfastrep, 0, sizeof(fxfastrep));
1746     memset(&fastrep, 0, sizeof(fastrep));
1747 
1748     ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
1749     if (ret)
1750 	return ret;
1751 
1752     if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
1753 	krb5_data data;
1754 	ret = krb5_decrypt_EncryptedData(context,
1755 					 state->armor_crypto,
1756 					 KRB5_KU_FAST_REP,
1757 					 &fxfastrep.u.armored_data.enc_fast_rep,
1758 					 &data);
1759 	if (ret)
1760 	    goto out;
1761 
1762 	ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
1763 	krb5_data_free(&data);
1764 	if (ret)
1765 	    goto out;
1766 
1767     } else {
1768 	ret = KRB5KDC_ERR_PREAUTH_FAILED;
1769 	goto out;
1770     }
1771 
1772     free_METHOD_DATA(rep->padata);
1773     ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
1774     if (ret)
1775 	goto out;
1776 
1777     if (fastrep.strengthen_key) {
1778 	if (state->strengthen_key)
1779 	    krb5_free_keyblock(context, state->strengthen_key);
1780 
1781 	ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
1782 	if (ret)
1783 	    goto out;
1784     }
1785 
1786     if (nonce != fastrep.nonce) {
1787 	ret = KRB5KDC_ERR_PREAUTH_FAILED;
1788 	goto out;
1789     }
1790     if (fastrep.finished) {
1791 	PrincipalName cname;
1792 	krb5_realm crealm = NULL;
1793 
1794 	if (chksumdata == NULL) {
1795 	    ret = KRB5KDC_ERR_PREAUTH_FAILED;
1796 	    goto out;
1797 	}
1798 
1799 	ret = krb5_verify_checksum(context, state->armor_crypto,
1800 				   KRB5_KU_FAST_FINISHED,
1801 				   chksumdata->data, chksumdata->length,
1802 				   &fastrep.finished->ticket_checksum);
1803 	if (ret)
1804 	    goto out;
1805 
1806 	/* update */
1807 	ret = copy_Realm(&fastrep.finished->crealm, &crealm);
1808 	if (ret)
1809 	    goto out;
1810 	free_Realm(&rep->crealm);
1811 	rep->crealm = crealm;
1812 
1813 	ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
1814 	if (ret)
1815 	    goto out;
1816 	free_PrincipalName(&rep->cname);
1817 	rep->cname = cname;
1818 
1819 #if 0 /* store authenticated checksum as kdc-offset */
1820 	fastrep->finished.timestamp;
1821 	fastrep->finished.usec = 0;
1822 #endif
1823 
1824     } else if (chksumdata) {
1825 	/* expected fastrep.finish but didn't get it */
1826 	ret = KRB5KDC_ERR_PREAUTH_FAILED;
1827     }
1828 
1829  out:
1830     free_PA_FX_FAST_REPLY(&fxfastrep);
1831 
1832     return ret;
1833 }
1834 
1835 static krb5_error_code
1836 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
1837 {
1838     if (state->armor_crypto == NULL)
1839 	return check_fast(context, state);
1840 
1841     return 0;
1842 }
1843 
1844 krb5_error_code
1845 _krb5_make_fast_ap_fxarmor(krb5_context context,
1846 			   krb5_ccache armor_ccache,
1847 			   krb5_data *armor_value,
1848 			   krb5_keyblock *armor_key,
1849 			   krb5_crypto *armor_crypto)
1850 {
1851     krb5_auth_context auth_context = NULL;
1852     krb5_creds cred, *credp = NULL;
1853     krb5_error_code ret;
1854     krb5_data empty;
1855 
1856     krb5_data_zero(&empty);
1857 
1858     memset(&cred, 0, sizeof(cred));
1859 
1860     ret = krb5_auth_con_init (context, &auth_context);
1861     if (ret)
1862 	goto out;
1863 
1864     ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
1865     if (ret)
1866 	goto out;
1867 
1868     ret = krb5_make_principal(context, &cred.server,
1869 			      cred.client->realm,
1870 			      KRB5_TGS_NAME,
1871 			      cred.client->realm,
1872 			      NULL);
1873     if (ret) {
1874 	krb5_free_principal(context, cred.client);
1875 	goto out;
1876     }
1877 
1878     ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
1879     krb5_free_principal(context, cred.server);
1880     krb5_free_principal(context, cred.client);
1881     if (ret)
1882 	goto out;
1883 
1884     ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
1885     if (ret)
1886 	goto out;
1887 
1888     ret = krb5_mk_req_extended(context,
1889 			       &auth_context,
1890 			       AP_OPTS_USE_SUBKEY,
1891 			       NULL,
1892 			       credp,
1893 			       armor_value);
1894     krb5_free_creds(context, credp);
1895     if (ret)
1896 	goto out;
1897 
1898     ret = _krb5_fast_armor_key(context,
1899 			       auth_context->local_subkey,
1900 			       auth_context->keyblock,
1901 			       armor_key,
1902 			       armor_crypto);
1903     if (ret)
1904 	goto out;
1905 
1906  out:
1907     krb5_auth_con_free(context, auth_context);
1908     return ret;
1909 }
1910 
1911 #ifndef WIN32
1912 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
1913 static heim_ipc armor_service = NULL;
1914 
1915 static void
1916 fast_armor_init_ipc(void *ctx)
1917 {
1918     heim_ipc *ipc = ctx;
1919     heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
1920 }
1921 #endif /* WIN32 */
1922 
1923 
1924 static krb5_error_code
1925 make_fast_ap_fxarmor(krb5_context context,
1926 		     struct fast_state *state,
1927 		     const char *realm,
1928 		     KrbFastArmor **armor)
1929 {
1930     KrbFastArmor *fxarmor = NULL;
1931     krb5_error_code ret;
1932 
1933     if (state->armor_crypto)
1934 	krb5_crypto_destroy(context, state->armor_crypto);
1935     krb5_free_keyblock_contents(context, &state->armor_key);
1936 
1937 
1938     ALLOC(fxarmor, 1);
1939     if (fxarmor == NULL)
1940 	return krb5_enomem(context);
1941 
1942     if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
1943 #ifdef WIN32
1944 	krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
1945 	ret = ENOTSUP;
1946         goto out;
1947 #else /* WIN32 */
1948 	KERB_ARMOR_SERVICE_REPLY msg;
1949 	krb5_data request, reply;
1950 
1951 	heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
1952 	if (armor_service == NULL) {
1953 	    krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
1954             ret = ENOENT;
1955 	    goto out;
1956 	}
1957 
1958 	krb5_data_zero(&reply);
1959 
1960 	request.data = rk_UNCONST(realm);
1961 	request.length = strlen(realm);
1962 
1963 	ret = heim_ipc_call(armor_service, &request, &reply, NULL);
1964 	heim_release(send);
1965 	if (ret) {
1966 	    krb5_set_error_message(context, ret, "Failed to get armor service credential");
1967 	    goto out;
1968 	}
1969 
1970 	ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
1971 	krb5_data_free(&reply);
1972 	if (ret)
1973 	    goto out;
1974 
1975 	ret = copy_KrbFastArmor(fxarmor, &msg.armor);
1976 	if (ret) {
1977 	    free_KERB_ARMOR_SERVICE_REPLY(&msg);
1978 	    goto out;
1979 	}
1980 
1981 	ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
1982 	free_KERB_ARMOR_SERVICE_REPLY(&msg);
1983 	if (ret)
1984 	    goto out;
1985 
1986 	ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
1987 	if (ret)
1988 	    goto out;
1989 #endif /* WIN32 */
1990     } else {
1991 
1992 	fxarmor->armor_type = 1;
1993 
1994 	ret = _krb5_make_fast_ap_fxarmor(context,
1995 					 state->armor_ccache,
1996 					 &fxarmor->armor_value,
1997 					 &state->armor_key,
1998 					 &state->armor_crypto);
1999 	if (ret)
2000 	    goto out;
2001     }
2002 
2003 
2004     *armor = fxarmor;
2005     fxarmor = NULL;
2006  out:
2007     if (fxarmor) {
2008 	free_KrbFastArmor(fxarmor);
2009 	free(fxarmor);
2010     }
2011     return ret;
2012 }
2013 
2014 static krb5_error_code
2015 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
2016 {
2017     KrbFastArmor *fxarmor = NULL;
2018     PA_FX_FAST_REQUEST fxreq;
2019     krb5_error_code ret;
2020     KrbFastReq fastreq;
2021     krb5_data data;
2022     size_t size;
2023 
2024     if (state->flags & KRB5_FAST_DISABLED) {
2025 	_krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
2026 	return 0;
2027     }
2028 
2029     memset(&fxreq, 0, sizeof(fxreq));
2030     memset(&fastreq, 0, sizeof(fastreq));
2031     krb5_data_zero(&data);
2032 
2033     if (state->armor_crypto == NULL) {
2034 	if (state->armor_ccache) {
2035 	    /*
2036 	     * Instead of keeping state in FX_COOKIE in the KDC, we
2037 	     * rebuild a new armor key for every request, because this
2038 	     * is what the MIT KDC expect and RFC6113 is vage about
2039 	     * what the behavior should be.
2040 	     */
2041 	    state->type = choice_PA_FX_FAST_REQUEST_armored_data;
2042 	} else {
2043 	    return check_fast(context, state);
2044 	}
2045     }
2046 
2047     state->flags |= KRB5_FAST_EXPECTED;
2048 
2049     fastreq.fast_options.hide_client_names = 1;
2050 
2051     ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
2052     free_KDC_REQ_BODY(&req->req_body);
2053 
2054     req->req_body.realm = strdup(KRB5_ANON_REALM);
2055     if ((ALLOC(req->req_body.cname, 1)) != NULL) {
2056         req->req_body.cname->name_type = KRB5_NT_WELLKNOWN;
2057     if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) {
2058         req->req_body.cname->name_string.len = 2;
2059         req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
2060         req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
2061         if (req->req_body.cname->name_string.val[0] == NULL ||
2062             req->req_body.cname->name_string.val[1] == NULL)
2063             ret = krb5_enomem(context);
2064       } else
2065           ret = krb5_enomem(context);
2066     } else
2067         ret = krb5_enomem(context);
2068     if ((ALLOC(req->req_body.till, 1)) != NULL)
2069         *req->req_body.till = 0;
2070     else
2071         ret = krb5_enomem(context);
2072     if (ret)
2073         goto out;
2074 
2075     if (req->padata) {
2076 	ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
2077 	free_METHOD_DATA(req->padata);
2078     } else {
2079 	if ((ALLOC(req->padata, 1)) == NULL)
2080             ret = krb5_enomem(context);
2081     }
2082     if (ret)
2083         goto out;
2084 
2085     ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
2086     if (ret)
2087 	goto out;
2088     heim_assert(data.length == size, "ASN.1 internal error");
2089 
2090     fxreq.element = state->type;
2091 
2092     if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
2093 	size_t len;
2094 	void *buf;
2095 
2096 	ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
2097 	if (ret)
2098 	    goto out;
2099 
2100 	heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
2101 
2102 	ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
2103 	if (ret)
2104 	    goto out;
2105 	heim_assert(len == size, "ASN.1 internal error");
2106 
2107 	ret = krb5_create_checksum(context, state->armor_crypto,
2108 				   KRB5_KU_FAST_REQ_CHKSUM, 0,
2109 				   buf, len,
2110 				   &fxreq.u.armored_data.req_checksum);
2111 	free(buf);
2112 	if (ret)
2113 	    goto out;
2114 
2115 	ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
2116 					 KRB5_KU_FAST_ENC,
2117 					 data.data,
2118 					 data.length,
2119 					 0,
2120 					 &fxreq.u.armored_data.enc_fast_req);
2121 	krb5_data_free(&data);
2122         if (ret)
2123             goto out;
2124 
2125     } else {
2126 	krb5_data_free(&data);
2127 	heim_assert(false, "unknown FAST type, internal error");
2128     }
2129 
2130     ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
2131     if (ret)
2132 	goto out;
2133     heim_assert(data.length == size, "ASN.1 internal error");
2134 
2135 
2136     ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
2137     if (ret)
2138 	goto out;
2139     krb5_data_zero(&data);
2140 
2141  out:
2142     free_PA_FX_FAST_REQUEST(&fxreq);
2143     free_KrbFastReq(&fastreq);
2144     if (fxarmor) {
2145 	free_KrbFastArmor(fxarmor);
2146 	free(fxarmor);
2147     }
2148     krb5_data_free(&data);
2149 
2150     return ret;
2151 }
2152 
2153 
2154 /**
2155  * The core loop if krb5_get_init_creds() function family. Create the
2156  * packets and have the caller send them off to the KDC.
2157  *
2158  * If the caller want all work been done for them, use
2159  * krb5_init_creds_get() instead.
2160  *
2161  * @param context a Kerberos 5 context.
2162  * @param ctx ctx krb5_init_creds_context context.
2163  * @param in input data from KDC, first round it should be reset by krb5_data_zer().
2164  * @param out reply to KDC.
2165  * @param hostinfo KDC address info, first round it can be NULL.
2166  * @param flags status of the round, if
2167  *        KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
2168  *
2169  * @return 0 for success, or an Kerberos 5 error code, see
2170  *     krb5_get_error_message().
2171  *
2172  * @ingroup krb5_credential
2173  */
2174 
2175 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2176 krb5_init_creds_step(krb5_context context,
2177 		     krb5_init_creds_context ctx,
2178 		     krb5_data *in,
2179 		     krb5_data *out,
2180 		     krb5_krbhst_info *hostinfo,
2181 		     unsigned int *flags)
2182 {
2183     krb5_error_code ret;
2184     size_t len = 0;
2185     size_t size;
2186     AS_REQ req2;
2187 
2188     krb5_data_zero(out);
2189 
2190     if (ctx->as_req.req_body.cname == NULL) {
2191 	ret = init_as_req(context, ctx->flags, &ctx->cred,
2192 			  ctx->addrs, ctx->etypes, &ctx->as_req);
2193 	if (ret) {
2194 	    free_init_creds_ctx(context, ctx);
2195 	    return ret;
2196 	}
2197     }
2198 
2199 #define MAX_PA_COUNTER 10
2200     if (ctx->pa_counter > MAX_PA_COUNTER) {
2201 	krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
2202 			       N_("Looping %d times while getting "
2203 				  "initial credentials", ""),
2204 			       ctx->pa_counter);
2205 	return KRB5_GET_IN_TKT_LOOP;
2206     }
2207     ctx->pa_counter++;
2208 
2209     _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
2210 
2211     /* Lets process the input packet */
2212     if (in && in->length) {
2213 	krb5_kdc_rep rep;
2214 
2215 	memset(&rep, 0, sizeof(rep));
2216 
2217 	_krb5_debug(context, 5, "krb5_get_init_creds: processing input");
2218 
2219 	ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
2220 	if (ret == 0) {
2221 	    unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
2222 	    krb5_data data;
2223 
2224 	    /*
2225 	     * Unwrap AS-REP
2226 	     */
2227 	    ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
2228 			       &rep.kdc_rep.ticket, &size, ret);
2229 	    if (ret)
2230 		goto out;
2231 	    heim_assert(data.length == size, "ASN.1 internal error");
2232 
2233 	    ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
2234 				     &ctx->fast_state, &rep.kdc_rep);
2235 	    krb5_data_free(&data);
2236 	    if (ret)
2237 		goto out;
2238 
2239 	    /*
2240 	     * Now check and extract the ticket
2241 	     */
2242 
2243 	    if (ctx->flags.canonicalize) {
2244 		eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
2245 		eflags |= EXTRACT_TICKET_MATCH_REALM;
2246 	    }
2247 	    if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
2248 		eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
2249 
2250 	    ret = process_pa_data_to_key(context, ctx, &ctx->cred,
2251 					 &ctx->as_req, &rep.kdc_rep,
2252 					 hostinfo, &ctx->fast_state.reply_key);
2253 	    if (ret) {
2254 		free_AS_REP(&rep.kdc_rep);
2255 		goto out;
2256 	    }
2257 
2258 	    _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
2259 
2260 	    ret = _krb5_extract_ticket(context,
2261 				       &rep,
2262 				       &ctx->cred,
2263 				       ctx->fast_state.reply_key,
2264 				       NULL,
2265 				       KRB5_KU_AS_REP_ENC_PART,
2266 				       NULL,
2267 				       ctx->nonce,
2268 				       eflags,
2269 				       &ctx->req_buffer,
2270 				       NULL,
2271 				       NULL);
2272 	    if (ret == 0)
2273 		ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
2274 
2275 	    krb5_free_keyblock(context, ctx->fast_state.reply_key);
2276 	    ctx->fast_state.reply_key = NULL;
2277 	    *flags = 0;
2278 
2279 	    free_AS_REP(&rep.kdc_rep);
2280 	    free_EncASRepPart(&rep.enc_part);
2281 
2282 	    return ret;
2283 
2284 	} else {
2285 	    /* let's try to parse it as a KRB-ERROR */
2286 
2287 	    _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
2288 
2289 	    free_KRB_ERROR(&ctx->error);
2290 
2291 	    ret = krb5_rd_error(context, in, &ctx->error);
2292 	    if(ret && in->length && ((char*)in->data)[0] == 4)
2293 		ret = KRB5KRB_AP_ERR_V4_REPLY;
2294 	    if (ret) {
2295 		_krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
2296 		goto out;
2297 	    }
2298 
2299 	    /*
2300 	     * Unwrap KRB-ERROR
2301 	     */
2302 	    ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
2303 	    if (ret)
2304 		goto out;
2305 
2306 	    /*
2307 	     *
2308 	     */
2309 
2310 	    ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
2311 
2312 	    _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
2313 
2314 	    /*
2315 	     * If no preauth was set and KDC requires it, give it one
2316 	     * more try.
2317 	     */
2318 
2319 	    if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
2320 
2321 	        free_METHOD_DATA(&ctx->md);
2322 	        memset(&ctx->md, 0, sizeof(ctx->md));
2323 
2324 		if (ctx->error.e_data) {
2325 		    ret = decode_METHOD_DATA(ctx->error.e_data->data,
2326 					     ctx->error.e_data->length,
2327 					     &ctx->md,
2328 					     NULL);
2329 		    if (ret)
2330 			krb5_set_error_message(context, ret,
2331 					       N_("Failed to decode METHOD-DATA", ""));
2332 		} else {
2333 		    krb5_set_error_message(context, ret,
2334 					   N_("Preauth required but no preauth "
2335 					      "options send by KDC", ""));
2336 		}
2337 	    } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
2338 		/*
2339 		 * Try adapt to timeskrew when we are using pre-auth, and
2340 		 * if there was a time skew, try again.
2341 		 */
2342 		krb5_set_real_time(context, ctx->error.stime, -1);
2343 		if (context->kdc_sec_offset)
2344 		    ret = 0;
2345 
2346 		_krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
2347 			    context->kdc_sec_offset);
2348 
2349 		ctx->used_pa_types = 0;
2350 
2351 	    } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
2352 	        /* client referal to a new realm */
2353 
2354 		if (ctx->error.crealm == NULL) {
2355 		    krb5_set_error_message(context, ret,
2356 					   N_("Got a client referral, not but no realm", ""));
2357 		    goto out;
2358 		}
2359 		_krb5_debug(context, 5,
2360 			    "krb5_get_init_creds: got referal to realm %s",
2361 			    *ctx->error.crealm);
2362 
2363 		ret = krb5_principal_set_realm(context,
2364 					       ctx->cred.client,
2365 					       *ctx->error.crealm);
2366 		if (ret)
2367 		    goto out;
2368 
2369 		if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
2370 		    ret = krb5_init_creds_set_service(context, ctx, NULL);
2371 		    if (ret)
2372 			goto out;
2373 		}
2374 
2375 		free_AS_REQ(&ctx->as_req);
2376 		memset(&ctx->as_req, 0, sizeof(ctx->as_req));
2377 
2378 		ctx->used_pa_types = 0;
2379 	    } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
2380 		char buf2[1024];
2381 
2382 		ctx->runflags.change_password = 1;
2383 
2384 		ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
2385 
2386 
2387 		/* try to avoid recursion */
2388 		if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
2389 		    goto out;
2390 
2391                 /* don't try to change password where then where none */
2392                 if (ctx->prompter == NULL)
2393                     goto out;
2394 
2395 		ret = change_password(context,
2396 				      ctx->cred.client,
2397 				      ctx->password,
2398 				      buf2,
2399 				      sizeof(buf2),
2400 				      ctx->prompter,
2401 				      ctx->prompter_data,
2402 				      NULL);
2403 		if (ret)
2404 		    goto out;
2405 
2406 		krb5_init_creds_set_password(context, ctx, buf2);
2407 
2408  		ctx->used_pa_types = 0;
2409 		ret = 0;
2410 
2411  	    } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
2412 
2413  		if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
2414  		    goto out;
2415  		if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
2416  		    goto out;
2417 
2418  		_krb5_debug(context, 10, "preauth failed with FAST, "
2419 			    "and told by KD or user, trying w/o FAST");
2420 
2421  		ctx->fast_state.flags |= KRB5_FAST_DISABLED;
2422  		ctx->used_pa_types = 0;
2423 		ret = 0;
2424 	    }
2425 	    if (ret)
2426 		goto out;
2427 	}
2428     }
2429 
2430     if (ctx->as_req.req_body.cname == NULL) {
2431 	ret = init_as_req(context, ctx->flags, &ctx->cred,
2432 			  ctx->addrs, ctx->etypes, &ctx->as_req);
2433 	if (ret) {
2434 	    free_init_creds_ctx(context, ctx);
2435 	    return ret;
2436 	}
2437     }
2438 
2439     if (ctx->as_req.padata) {
2440 	free_METHOD_DATA(ctx->as_req.padata);
2441 	free(ctx->as_req.padata);
2442 	ctx->as_req.padata = NULL;
2443     }
2444 
2445     /* Set a new nonce. */
2446     ctx->as_req.req_body.nonce = ctx->nonce;
2447 
2448     /* fill_in_md_data */
2449     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
2450 				&ctx->md, &ctx->as_req.padata,
2451 				ctx->prompter, ctx->prompter_data);
2452     if (ret)
2453 	goto out;
2454 
2455     /*
2456      * Wrap with FAST
2457      */
2458     copy_AS_REQ(&ctx->as_req, &req2);
2459 
2460     ret = fast_wrap_req(context, &ctx->fast_state, &req2);
2461     if (ret) {
2462 	free_AS_REQ(&req2);
2463 	goto out;
2464     }
2465 
2466     krb5_data_free(&ctx->req_buffer);
2467 
2468     ASN1_MALLOC_ENCODE(AS_REQ,
2469 		       ctx->req_buffer.data, ctx->req_buffer.length,
2470 		       &req2, &len, ret);
2471     free_AS_REQ(&req2);
2472     if (ret)
2473 	goto out;
2474     if(len != ctx->req_buffer.length)
2475 	krb5_abortx(context, "internal error in ASN.1 encoder");
2476 
2477     out->data = ctx->req_buffer.data;
2478     out->length = ctx->req_buffer.length;
2479 
2480     *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
2481 
2482     return 0;
2483  out:
2484     return ret;
2485 }
2486 
2487 /**
2488  * Extract the newly acquired credentials from krb5_init_creds_context
2489  * context.
2490  *
2491  * @param context A Kerberos 5 context.
2492  * @param ctx
2493  * @param cred credentials, free with krb5_free_cred_contents().
2494  *
2495  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
2496  */
2497 
2498 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2499 krb5_init_creds_get_creds(krb5_context context,
2500 			  krb5_init_creds_context ctx,
2501 			  krb5_creds *cred)
2502 {
2503     return krb5_copy_creds_contents(context, &ctx->cred, cred);
2504 }
2505 
2506 /**
2507  * Get the last error from the transaction.
2508  *
2509  * @return Returns 0 or an error code
2510  *
2511  * @ingroup krb5_credential
2512  */
2513 
2514 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2515 krb5_init_creds_get_error(krb5_context context,
2516 			  krb5_init_creds_context ctx,
2517 			  KRB_ERROR *error)
2518 {
2519     krb5_error_code ret;
2520 
2521     ret = copy_KRB_ERROR(&ctx->error, error);
2522     if (ret)
2523 	krb5_enomem(context);
2524 
2525     return ret;
2526 }
2527 
2528 /**
2529  *
2530  * @ingroup krb5_credential
2531  */
2532 
2533 krb5_error_code
2534 krb5_init_creds_store(krb5_context context,
2535 		      krb5_init_creds_context ctx,
2536 		      krb5_ccache id)
2537 {
2538     krb5_error_code ret;
2539 
2540     if (ctx->cred.client == NULL) {
2541 	ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
2542 	krb5_set_error_message(context, ret, "init creds not completed yet");
2543 	return ret;
2544     }
2545 
2546     ret = krb5_cc_initialize(context, id, ctx->cred.client);
2547     if (ret)
2548 	return ret;
2549 
2550     ret = krb5_cc_store_cred(context, id, &ctx->cred);
2551     if (ret)
2552 	return ret;
2553 
2554     if (ctx->cred.flags.b.enc_pa_rep) {
2555 	krb5_data data = { 3, rk_UNCONST("yes") };
2556 	ret = krb5_cc_set_config(context, id, ctx->cred.server,
2557 				 "fast_avail", &data);
2558 	if (ret)
2559 	    return ret;
2560     }
2561 
2562     return ret;
2563 }
2564 
2565 /**
2566  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
2567  *
2568  * @param context A Kerberos 5 context.
2569  * @param ctx The krb5_init_creds_context to free.
2570  *
2571  * @ingroup krb5_credential
2572  */
2573 
2574 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2575 krb5_init_creds_free(krb5_context context,
2576 		     krb5_init_creds_context ctx)
2577 {
2578     free_init_creds_ctx(context, ctx);
2579     free(ctx);
2580 }
2581 
2582 /**
2583  * Get new credentials as setup by the krb5_init_creds_context.
2584  *
2585  * @param context A Kerberos 5 context.
2586  * @param ctx The krb5_init_creds_context to process.
2587  *
2588  * @ingroup krb5_credential
2589  */
2590 
2591 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2592 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
2593 {
2594     krb5_sendto_ctx stctx = NULL;
2595     krb5_krbhst_info *hostinfo = NULL;
2596     krb5_error_code ret;
2597     krb5_data in, out;
2598     unsigned int flags = 0;
2599 
2600     krb5_data_zero(&in);
2601     krb5_data_zero(&out);
2602 
2603     ret = krb5_sendto_ctx_alloc(context, &stctx);
2604     if (ret)
2605 	goto out;
2606     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
2607 
2608     while (1) {
2609 	flags = 0;
2610 	ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
2611 	krb5_data_free(&in);
2612 	if (ret)
2613 	    goto out;
2614 
2615 	if ((flags & 1) == 0)
2616 	    break;
2617 
2618 	ret = krb5_sendto_context (context, stctx, &out,
2619 				   ctx->cred.client->realm, &in);
2620     	if (ret)
2621 	    goto out;
2622 
2623     }
2624 
2625  out:
2626     if (stctx)
2627 	krb5_sendto_ctx_free(context, stctx);
2628 
2629     return ret;
2630 }
2631 
2632 /**
2633  * Get new credentials using password.
2634  *
2635  * @ingroup krb5_credential
2636  */
2637 
2638 
2639 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2640 krb5_get_init_creds_password(krb5_context context,
2641 			     krb5_creds *creds,
2642 			     krb5_principal client,
2643 			     const char *password,
2644 			     krb5_prompter_fct prompter,
2645 			     void *data,
2646 			     krb5_deltat start_time,
2647 			     const char *in_tkt_service,
2648 			     krb5_get_init_creds_opt *options)
2649 {
2650     krb5_init_creds_context ctx;
2651     char buf[BUFSIZ], buf2[BUFSIZ];
2652     krb5_error_code ret;
2653     int chpw = 0;
2654 
2655  again:
2656     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
2657     if (ret)
2658 	goto out;
2659 
2660     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2661     if (ret)
2662 	goto out;
2663 
2664     if (prompter != NULL && ctx->password == NULL && password == NULL) {
2665 	krb5_prompt prompt;
2666 	krb5_data password_data;
2667 	char *p, *q = NULL;
2668 	int aret;
2669 
2670 	ret = krb5_unparse_name(context, client, &p);
2671 	if (ret)
2672 	    goto out;
2673 
2674 	aret = asprintf(&q, "%s's Password: ", p);
2675 	free (p);
2676 	if (aret == -1 || q == NULL) {
2677 	    ret = krb5_enomem(context);
2678 	    goto out;
2679 	}
2680 	prompt.prompt = q;
2681 	password_data.data   = buf;
2682 	password_data.length = sizeof(buf);
2683 	prompt.hidden = 1;
2684 	prompt.reply  = &password_data;
2685 	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
2686 
2687 	ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2688 	free (q);
2689 	if (ret) {
2690 	    memset (buf, 0, sizeof(buf));
2691 	    ret = KRB5_LIBOS_PWDINTR;
2692 	    krb5_clear_error_message (context);
2693 	    goto out;
2694 	}
2695 	password = password_data.data;
2696     }
2697 
2698     if (password) {
2699 	ret = krb5_init_creds_set_password(context, ctx, password);
2700 	if (ret)
2701 	    goto out;
2702     }
2703 
2704     ret = krb5_init_creds_get(context, ctx);
2705 
2706     if (ret == 0)
2707 	krb5_process_last_request(context, options, ctx);
2708 
2709 
2710     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2711 	/* try to avoid recursion */
2712 	if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2713 	   goto out;
2714 
2715 	/* don't try to change password where then where none */
2716 	if (prompter == NULL)
2717 	    goto out;
2718 
2719 	if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
2720             !options->change_password_prompt)
2721 		goto out;
2722 
2723 	ret = change_password (context,
2724 			       client,
2725 			       ctx->password,
2726 			       buf2,
2727 			       sizeof(buf2),
2728 			       prompter,
2729 			       data,
2730 			       options);
2731 	if (ret)
2732 	    goto out;
2733 	password = buf2;
2734 	chpw = 1;
2735 	krb5_init_creds_free(context, ctx);
2736 	goto again;
2737     }
2738 
2739  out:
2740     if (ret == 0)
2741 	krb5_init_creds_get_creds(context, ctx, creds);
2742 
2743     if (ctx)
2744 	krb5_init_creds_free(context, ctx);
2745 
2746     memset(buf, 0, sizeof(buf));
2747     memset(buf2, 0, sizeof(buf2));
2748     return ret;
2749 }
2750 
2751 /**
2752  * Get new credentials using keyblock.
2753  *
2754  * @ingroup krb5_credential
2755  */
2756 
2757 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2758 krb5_get_init_creds_keyblock(krb5_context context,
2759 			     krb5_creds *creds,
2760 			     krb5_principal client,
2761 			     krb5_keyblock *keyblock,
2762 			     krb5_deltat start_time,
2763 			     const char *in_tkt_service,
2764 			     krb5_get_init_creds_opt *options)
2765 {
2766     krb5_init_creds_context ctx;
2767     krb5_error_code ret;
2768 
2769     memset(creds, 0, sizeof(*creds));
2770 
2771     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2772     if (ret)
2773 	goto out;
2774 
2775     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2776     if (ret)
2777 	goto out;
2778 
2779     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2780     if (ret)
2781 	goto out;
2782 
2783     ret = krb5_init_creds_get(context, ctx);
2784 
2785     if (ret == 0)
2786         krb5_process_last_request(context, options, ctx);
2787 
2788  out:
2789     if (ret == 0)
2790 	krb5_init_creds_get_creds(context, ctx, creds);
2791 
2792     if (ctx)
2793 	krb5_init_creds_free(context, ctx);
2794 
2795     return ret;
2796 }
2797 
2798 /**
2799  * Get new credentials using keytab.
2800  *
2801  * @ingroup krb5_credential
2802  */
2803 
2804 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2805 krb5_get_init_creds_keytab(krb5_context context,
2806 			   krb5_creds *creds,
2807 			   krb5_principal client,
2808 			   krb5_keytab keytab,
2809 			   krb5_deltat start_time,
2810 			   const char *in_tkt_service,
2811 			   krb5_get_init_creds_opt *options)
2812 {
2813     krb5_init_creds_context ctx;
2814     krb5_keytab_entry ktent;
2815     krb5_error_code ret;
2816 
2817     memset(&ktent, 0, sizeof(ktent));
2818     memset(creds, 0, sizeof(*creds));
2819 
2820     if (strcmp(client->realm, "") == 0) {
2821         /*
2822          * Referral realm.  We have a keytab, so pick a realm by
2823          * matching in the keytab.
2824          */
2825         ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
2826         if (ret == 0)
2827             client = ktent.principal;
2828     }
2829 
2830     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2831     if (ret)
2832 	goto out;
2833 
2834     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2835     if (ret)
2836 	goto out;
2837 
2838     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2839     if (ret)
2840 	goto out;
2841 
2842     ret = krb5_init_creds_get(context, ctx);
2843     if (ret == 0)
2844         krb5_process_last_request(context, options, ctx);
2845 
2846  out:
2847     krb5_kt_free_entry(context, &ktent);
2848     if (ret == 0)
2849 	krb5_init_creds_get_creds(context, ctx, creds);
2850 
2851     if (ctx)
2852 	krb5_init_creds_free(context, ctx);
2853 
2854     return ret;
2855 }
2856