xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/init_creds_pw.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: init_creds_pw.c,v 1.1.1.1 2011/04/13 18:15:34 elric 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 
40 typedef struct krb5_get_init_creds_ctx {
41     KDCOptions flags;
42     krb5_creds cred;
43     krb5_addresses *addrs;
44     krb5_enctype *etypes;
45     krb5_preauthtype *pre_auth_types;
46     char *in_tkt_service;
47     unsigned nonce;
48     unsigned pk_nonce;
49 
50     krb5_data req_buffer;
51     AS_REQ as_req;
52     int pa_counter;
53 
54     /* password and keytab_data is freed on completion */
55     char *password;
56     krb5_keytab_key_proc_args *keytab_data;
57 
58     krb5_pointer *keyseed;
59     krb5_s2k_proc keyproc;
60 
61     krb5_get_init_creds_tristate req_pac;
62 
63     krb5_pk_init_ctx pk_init_ctx;
64     int ic_flags;
65 
66     int used_pa_types;
67 #define  USED_PKINIT	1
68 #define  USED_PKINIT_W2K	2
69 #define  USED_ENC_TS_GUESS	4
70 #define  USED_ENC_TS_INFO	8
71 
72     METHOD_DATA md;
73     KRB_ERROR error;
74     AS_REP as_rep;
75     EncKDCRepPart enc_part;
76 
77     krb5_prompter_fct prompter;
78     void *prompter_data;
79 
80     struct pa_info_data *ppaid;
81 
82 } krb5_get_init_creds_ctx;
83 
84 
85 struct pa_info_data {
86     krb5_enctype etype;
87     krb5_salt salt;
88     krb5_data *s2kparams;
89 };
90 
91 static void
92 free_paid(krb5_context context, struct pa_info_data *ppaid)
93 {
94     krb5_free_salt(context, ppaid->salt);
95     if (ppaid->s2kparams)
96 	krb5_free_data(context, ppaid->s2kparams);
97 }
98 
99 static krb5_error_code KRB5_CALLCONV
100 default_s2k_func(krb5_context context, krb5_enctype type,
101 		 krb5_const_pointer keyseed,
102 		 krb5_salt salt, krb5_data *s2kparms,
103 		 krb5_keyblock **key)
104 {
105     krb5_error_code ret;
106     krb5_data password;
107     krb5_data opaque;
108 
109     _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
110 
111     password.data = rk_UNCONST(keyseed);
112     password.length = strlen(keyseed);
113     if (s2kparms)
114 	opaque = *s2kparms;
115     else
116 	krb5_data_zero(&opaque);
117 
118     *key = malloc(sizeof(**key));
119     if (*key == NULL)
120 	return ENOMEM;
121     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
122 					      salt, opaque, *key);
123     if (ret) {
124 	free(*key);
125 	*key = NULL;
126     }
127     return ret;
128 }
129 
130 static void
131 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
132 {
133     if (ctx->etypes)
134 	free(ctx->etypes);
135     if (ctx->pre_auth_types)
136 	free (ctx->pre_auth_types);
137     if (ctx->in_tkt_service)
138 	free(ctx->in_tkt_service);
139     if (ctx->keytab_data)
140 	free(ctx->keytab_data);
141     if (ctx->password) {
142 	memset(ctx->password, 0, strlen(ctx->password));
143 	free(ctx->password);
144     }
145     krb5_data_free(&ctx->req_buffer);
146     krb5_free_cred_contents(context, &ctx->cred);
147     free_METHOD_DATA(&ctx->md);
148     free_AS_REP(&ctx->as_rep);
149     free_EncKDCRepPart(&ctx->enc_part);
150     free_KRB_ERROR(&ctx->error);
151     free_AS_REQ(&ctx->as_req);
152     if (ctx->ppaid) {
153 	free_paid(context, ctx->ppaid);
154 	free(ctx->ppaid);
155     }
156     memset(ctx, 0, sizeof(*ctx));
157 }
158 
159 static int
160 get_config_time (krb5_context context,
161 		 const char *realm,
162 		 const char *name,
163 		 int def)
164 {
165     int ret;
166 
167     ret = krb5_config_get_time (context, NULL,
168 				"realms",
169 				realm,
170 				name,
171 				NULL);
172     if (ret >= 0)
173 	return ret;
174     ret = krb5_config_get_time (context, NULL,
175 				"libdefaults",
176 				name,
177 				NULL);
178     if (ret >= 0)
179 	return ret;
180     return def;
181 }
182 
183 static krb5_error_code
184 init_cred (krb5_context context,
185 	   krb5_creds *cred,
186 	   krb5_principal client,
187 	   krb5_deltat start_time,
188 	   krb5_get_init_creds_opt *options)
189 {
190     krb5_error_code ret;
191     int tmp;
192     krb5_timestamp now;
193 
194     krb5_timeofday (context, &now);
195 
196     memset (cred, 0, sizeof(*cred));
197 
198     if (client)
199 	krb5_copy_principal(context, client, &cred->client);
200     else {
201 	ret = krb5_get_default_principal (context,
202 					  &cred->client);
203 	if (ret)
204 	    goto out;
205     }
206 
207     if (start_time)
208 	cred->times.starttime  = now + start_time;
209 
210     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
211 	tmp = options->tkt_life;
212     else
213 	tmp = 10 * 60 * 60;
214     cred->times.endtime = now + tmp;
215 
216     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
217 	options->renew_life > 0) {
218 	cred->times.renew_till = now + options->renew_life;
219     }
220 
221     return 0;
222 
223 out:
224     krb5_free_cred_contents (context, cred);
225     return ret;
226 }
227 
228 /*
229  * Print a message (str) to the user about the expiration in `lr'
230  */
231 
232 static void
233 report_expiration (krb5_context context,
234 		   krb5_prompter_fct prompter,
235 		   krb5_data *data,
236 		   const char *str,
237 		   time_t now)
238 {
239     char *p = NULL;
240 
241     if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
242 	return;
243     (*prompter)(context, data, NULL, p, 0, NULL);
244     free(p);
245 }
246 
247 /*
248  * Check the context, and in the case there is a expiration warning,
249  * use the prompter to print the warning.
250  *
251  * @param context A Kerberos 5 context.
252  * @param options An GIC options structure
253  * @param ctx The krb5_init_creds_context check for expiration.
254  */
255 
256 static krb5_error_code
257 process_last_request(krb5_context context,
258 		     krb5_get_init_creds_opt *options,
259 		     krb5_init_creds_context ctx)
260 {
261     krb5_const_realm realm;
262     LastReq *lr;
263     krb5_boolean reported = FALSE;
264     krb5_timestamp sec;
265     time_t t;
266     size_t i;
267 
268     /*
269      * First check if there is a API consumer.
270      */
271 
272     realm = krb5_principal_get_realm (context, ctx->cred.client);
273     lr = &ctx->enc_part.last_req;
274 
275     if (options && options->opt_private && options->opt_private->lr.func) {
276 	krb5_last_req_entry **lre;
277 
278 	lre = calloc(lr->len + 1, sizeof(**lre));
279 	if (lre == NULL) {
280 	    krb5_set_error_message(context, ENOMEM,
281 				   N_("malloc: out of memory", ""));
282 	    return ENOMEM;
283 	}
284 	for (i = 0; i < lr->len; i++) {
285 	    lre[i] = calloc(1, sizeof(*lre[i]));
286 	    if (lre[i] == NULL)
287 		break;
288 	    lre[i]->lr_type = lr->val[i].lr_type;
289 	    lre[i]->value = lr->val[i].lr_value;
290 	}
291 
292 	(*options->opt_private->lr.func)(context, lre,
293 					 options->opt_private->lr.ctx);
294 
295 	for (i = 0; i < lr->len; i++)
296 	    free(lre[i]);
297 	free(lre);
298     }
299 
300     /*
301      * Now check if we should prompt the user
302      */
303 
304     if (ctx->prompter == NULL)
305         return 0;
306 
307     krb5_timeofday (context, &sec);
308 
309     t = sec + get_config_time (context,
310 			       realm,
311 			       "warn_pwexpire",
312 			       7 * 24 * 60 * 60);
313 
314     for (i = 0; i < lr->len; ++i) {
315 	if (lr->val[i].lr_value <= t) {
316 	    switch (abs(lr->val[i].lr_type)) {
317 	    case LR_PW_EXPTIME :
318 		report_expiration(context, ctx->prompter,
319 				  ctx->prompter_data,
320 				  "Your password will expire at ",
321 				  lr->val[i].lr_value);
322 		reported = TRUE;
323 		break;
324 	    case LR_ACCT_EXPTIME :
325 		report_expiration(context, ctx->prompter,
326 				  ctx->prompter_data,
327 				  "Your account will expire at ",
328 				  lr->val[i].lr_value);
329 		reported = TRUE;
330 		break;
331 	    }
332 	}
333     }
334 
335     if (!reported
336 	&& ctx->enc_part.key_expiration
337 	&& *ctx->enc_part.key_expiration <= t) {
338         report_expiration(context, ctx->prompter,
339 			  ctx->prompter_data,
340 			  "Your password/account will expire at ",
341 			  *ctx->enc_part.key_expiration);
342     }
343     return 0;
344 }
345 
346 static krb5_addresses no_addrs = { 0, NULL };
347 
348 static krb5_error_code
349 get_init_creds_common(krb5_context context,
350 		      krb5_principal client,
351 		      krb5_deltat start_time,
352 		      krb5_get_init_creds_opt *options,
353 		      krb5_init_creds_context ctx)
354 {
355     krb5_get_init_creds_opt *default_opt = NULL;
356     krb5_error_code ret;
357     krb5_enctype *etypes;
358     krb5_preauthtype *pre_auth_types;
359 
360     memset(ctx, 0, sizeof(*ctx));
361 
362     if (options == NULL) {
363 	const char *realm = krb5_principal_get_realm(context, client);
364 
365         krb5_get_init_creds_opt_alloc (context, &default_opt);
366 	options = default_opt;
367 	krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
368     }
369 
370     if (options->opt_private) {
371 	if (options->opt_private->password) {
372 	    ret = krb5_init_creds_set_password(context, ctx,
373 					       options->opt_private->password);
374 	    if (ret)
375 		goto out;
376 	}
377 
378 	ctx->keyproc = options->opt_private->key_proc;
379 	ctx->req_pac = options->opt_private->req_pac;
380 	ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
381 	ctx->ic_flags = options->opt_private->flags;
382     } else
383 	ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
384 
385     if (ctx->keyproc == NULL)
386 	ctx->keyproc = default_s2k_func;
387 
388     /* Enterprise name implicitly turns on canonicalize */
389     if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
390 	krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
391 	ctx->flags.canonicalize = 1;
392 
393     ctx->pre_auth_types = NULL;
394     ctx->addrs = NULL;
395     ctx->etypes = NULL;
396     ctx->pre_auth_types = NULL;
397 
398     ret = init_cred(context, &ctx->cred, client, start_time, options);
399     if (ret) {
400 	if (default_opt)
401 	    krb5_get_init_creds_opt_free(context, default_opt);
402 	return ret;
403     }
404 
405     ret = krb5_init_creds_set_service(context, ctx, NULL);
406     if (ret)
407 	goto out;
408 
409     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
410 	ctx->flags.forwardable = options->forwardable;
411 
412     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
413 	ctx->flags.proxiable = options->proxiable;
414 
415     if (start_time)
416 	ctx->flags.postdated = 1;
417     if (ctx->cred.times.renew_till)
418 	ctx->flags.renewable = 1;
419     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
420 	ctx->addrs = options->address_list;
421     } else if (options->opt_private) {
422 	switch (options->opt_private->addressless) {
423 	case KRB5_INIT_CREDS_TRISTATE_UNSET:
424 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
425 	    ctx->addrs = &no_addrs;
426 #else
427 	    ctx->addrs = NULL;
428 #endif
429 	    break;
430 	case KRB5_INIT_CREDS_TRISTATE_FALSE:
431 	    ctx->addrs = NULL;
432 	    break;
433 	case KRB5_INIT_CREDS_TRISTATE_TRUE:
434 	    ctx->addrs = &no_addrs;
435 	    break;
436 	}
437     }
438     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
439 	if (ctx->etypes)
440 	    free(ctx->etypes);
441 
442 	etypes = malloc((options->etype_list_length + 1)
443 			* sizeof(krb5_enctype));
444 	if (etypes == NULL) {
445 	    ret = ENOMEM;
446 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
447 	    goto out;
448 	}
449 	memcpy (etypes, options->etype_list,
450 		options->etype_list_length * sizeof(krb5_enctype));
451 	etypes[options->etype_list_length] = ETYPE_NULL;
452 	ctx->etypes = etypes;
453     }
454     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
455 	pre_auth_types = malloc((options->preauth_list_length + 1)
456 				* sizeof(krb5_preauthtype));
457 	if (pre_auth_types == NULL) {
458 	    ret = ENOMEM;
459 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
460 	    goto out;
461 	}
462 	memcpy (pre_auth_types, options->preauth_list,
463 		options->preauth_list_length * sizeof(krb5_preauthtype));
464 	pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
465 	ctx->pre_auth_types = pre_auth_types;
466     }
467     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
468 	ctx->flags.request_anonymous = options->anonymous;
469     if (default_opt)
470         krb5_get_init_creds_opt_free(context, default_opt);
471     return 0;
472  out:
473     if (default_opt)
474 	krb5_get_init_creds_opt_free(context, default_opt);
475     return ret;
476 }
477 
478 static krb5_error_code
479 change_password (krb5_context context,
480 		 krb5_principal client,
481 		 const char *password,
482 		 char *newpw,
483 		 size_t newpw_sz,
484 		 krb5_prompter_fct prompter,
485 		 void *data,
486 		 krb5_get_init_creds_opt *old_options)
487 {
488     krb5_prompt prompts[2];
489     krb5_error_code ret;
490     krb5_creds cpw_cred;
491     char buf1[BUFSIZ], buf2[BUFSIZ];
492     krb5_data password_data[2];
493     int result_code;
494     krb5_data result_code_string;
495     krb5_data result_string;
496     char *p;
497     krb5_get_init_creds_opt *options;
498 
499     memset (&cpw_cred, 0, sizeof(cpw_cred));
500 
501     ret = krb5_get_init_creds_opt_alloc(context, &options);
502     if (ret)
503         return ret;
504     krb5_get_init_creds_opt_set_tkt_life (options, 60);
505     krb5_get_init_creds_opt_set_forwardable (options, FALSE);
506     krb5_get_init_creds_opt_set_proxiable (options, FALSE);
507     if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
508 	krb5_get_init_creds_opt_set_preauth_list (options,
509 						  old_options->preauth_list,
510 						  old_options->preauth_list_length);
511 
512     krb5_data_zero (&result_code_string);
513     krb5_data_zero (&result_string);
514 
515     ret = krb5_get_init_creds_password (context,
516 					&cpw_cred,
517 					client,
518 					password,
519 					prompter,
520 					data,
521 					0,
522 					"kadmin/changepw",
523 					options);
524     krb5_get_init_creds_opt_free(context, options);
525     if (ret)
526 	goto out;
527 
528     for(;;) {
529 	password_data[0].data   = buf1;
530 	password_data[0].length = sizeof(buf1);
531 
532 	prompts[0].hidden = 1;
533 	prompts[0].prompt = "New password: ";
534 	prompts[0].reply  = &password_data[0];
535 	prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
536 
537 	password_data[1].data   = buf2;
538 	password_data[1].length = sizeof(buf2);
539 
540 	prompts[1].hidden = 1;
541 	prompts[1].prompt = "Repeat new password: ";
542 	prompts[1].reply  = &password_data[1];
543 	prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
544 
545 	ret = (*prompter) (context, data, NULL, "Changing password",
546 			   2, prompts);
547 	if (ret) {
548 	    memset (buf1, 0, sizeof(buf1));
549 	    memset (buf2, 0, sizeof(buf2));
550 	    goto out;
551 	}
552 
553 	if (strcmp (buf1, buf2) == 0)
554 	    break;
555 	memset (buf1, 0, sizeof(buf1));
556 	memset (buf2, 0, sizeof(buf2));
557     }
558 
559     ret = krb5_set_password (context,
560 			     &cpw_cred,
561 			     buf1,
562 			     client,
563 			     &result_code,
564 			     &result_code_string,
565 			     &result_string);
566     if (ret)
567 	goto out;
568     if (asprintf(&p, "%s: %.*s\n",
569 		 result_code ? "Error" : "Success",
570 		 (int)result_string.length,
571 		 result_string.length > 0 ? (char*)result_string.data : "") < 0)
572     {
573 	ret = ENOMEM;
574 	goto out;
575     }
576 
577     /* return the result */
578     (*prompter) (context, data, NULL, p, 0, NULL);
579 
580     free (p);
581     if (result_code == 0) {
582 	strlcpy (newpw, buf1, newpw_sz);
583 	ret = 0;
584     } else {
585 	ret = ENOTTY;
586 	krb5_set_error_message(context, ret,
587 			       N_("failed changing password", ""));
588     }
589 
590 out:
591     memset (buf1, 0, sizeof(buf1));
592     memset (buf2, 0, sizeof(buf2));
593     krb5_data_free (&result_string);
594     krb5_data_free (&result_code_string);
595     krb5_free_cred_contents (context, &cpw_cred);
596     return ret;
597 }
598 
599 
600 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
601 krb5_keyblock_key_proc (krb5_context context,
602 			krb5_keytype type,
603 			krb5_data *salt,
604 			krb5_const_pointer keyseed,
605 			krb5_keyblock **key)
606 {
607     return krb5_copy_keyblock (context, keyseed, key);
608 }
609 
610 /*
611  *
612  */
613 
614 static krb5_error_code
615 init_as_req (krb5_context context,
616 	     KDCOptions opts,
617 	     const krb5_creds *creds,
618 	     const krb5_addresses *addrs,
619 	     const krb5_enctype *etypes,
620 	     AS_REQ *a)
621 {
622     krb5_error_code ret;
623 
624     memset(a, 0, sizeof(*a));
625 
626     a->pvno = 5;
627     a->msg_type = krb_as_req;
628     a->req_body.kdc_options = opts;
629     a->req_body.cname = malloc(sizeof(*a->req_body.cname));
630     if (a->req_body.cname == NULL) {
631 	ret = ENOMEM;
632 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
633 	goto fail;
634     }
635     a->req_body.sname = malloc(sizeof(*a->req_body.sname));
636     if (a->req_body.sname == NULL) {
637 	ret = ENOMEM;
638 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
639 	goto fail;
640     }
641 
642     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
643     if (ret)
644 	goto fail;
645     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
646     if (ret)
647 	goto fail;
648 
649     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
650     if (ret)
651 	goto fail;
652 
653     if(creds->times.starttime) {
654 	a->req_body.from = malloc(sizeof(*a->req_body.from));
655 	if (a->req_body.from == NULL) {
656 	    ret = ENOMEM;
657 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
658 	    goto fail;
659 	}
660 	*a->req_body.from = creds->times.starttime;
661     }
662     if(creds->times.endtime){
663 	ALLOC(a->req_body.till, 1);
664 	*a->req_body.till = creds->times.endtime;
665     }
666     if(creds->times.renew_till){
667 	a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
668 	if (a->req_body.rtime == NULL) {
669 	    ret = ENOMEM;
670 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
671 	    goto fail;
672 	}
673 	*a->req_body.rtime = creds->times.renew_till;
674     }
675     a->req_body.nonce = 0;
676     ret = krb5_init_etype (context,
677 			   &a->req_body.etype.len,
678 			   &a->req_body.etype.val,
679 			   etypes);
680     if (ret)
681 	goto fail;
682 
683     /*
684      * This means no addresses
685      */
686 
687     if (addrs && addrs->len == 0) {
688 	a->req_body.addresses = NULL;
689     } else {
690 	a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
691 	if (a->req_body.addresses == NULL) {
692 	    ret = ENOMEM;
693 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
694 	    goto fail;
695 	}
696 
697 	if (addrs)
698 	    ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
699 	else {
700 	    ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
701 	    if(ret == 0 && a->req_body.addresses->len == 0) {
702 		free(a->req_body.addresses);
703 		a->req_body.addresses = NULL;
704 	    }
705 	}
706 	if (ret)
707 	    goto fail;
708     }
709 
710     a->req_body.enc_authorization_data = NULL;
711     a->req_body.additional_tickets = NULL;
712 
713     a->padata = NULL;
714 
715     return 0;
716  fail:
717     free_AS_REQ(a);
718     memset(a, 0, sizeof(*a));
719     return ret;
720 }
721 
722 
723 static krb5_error_code
724 set_paid(struct pa_info_data *paid, krb5_context context,
725 	 krb5_enctype etype,
726 	 krb5_salttype salttype, void *salt_string, size_t salt_len,
727 	 krb5_data *s2kparams)
728 {
729     paid->etype = etype;
730     paid->salt.salttype = salttype;
731     paid->salt.saltvalue.data = malloc(salt_len + 1);
732     if (paid->salt.saltvalue.data == NULL) {
733 	krb5_clear_error_message(context);
734 	return ENOMEM;
735     }
736     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
737     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
738     paid->salt.saltvalue.length = salt_len;
739     if (s2kparams) {
740 	krb5_error_code ret;
741 
742 	ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
743 	if (ret) {
744 	    krb5_clear_error_message(context);
745 	    krb5_free_salt(context, paid->salt);
746 	    return ret;
747 	}
748     } else
749 	paid->s2kparams = NULL;
750 
751     return 0;
752 }
753 
754 static struct pa_info_data *
755 pa_etype_info2(krb5_context context,
756 	       const krb5_principal client,
757 	       const AS_REQ *asreq,
758 	       struct pa_info_data *paid,
759 	       heim_octet_string *data)
760 {
761     krb5_error_code ret;
762     ETYPE_INFO2 e;
763     size_t sz;
764     int i, j;
765 
766     memset(&e, 0, sizeof(e));
767     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
768     if (ret)
769 	goto out;
770     if (e.len == 0)
771 	goto out;
772     for (j = 0; j < asreq->req_body.etype.len; j++) {
773 	for (i = 0; i < e.len; i++) {
774 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
775 		krb5_salt salt;
776 		if (e.val[i].salt == NULL)
777 		    ret = krb5_get_pw_salt(context, client, &salt);
778 		else {
779 		    salt.saltvalue.data = *e.val[i].salt;
780 		    salt.saltvalue.length = strlen(*e.val[i].salt);
781 		    ret = 0;
782 		}
783 		if (ret == 0)
784 		    ret = set_paid(paid, context, e.val[i].etype,
785 				   KRB5_PW_SALT,
786 				   salt.saltvalue.data,
787 				   salt.saltvalue.length,
788 				   e.val[i].s2kparams);
789 		if (e.val[i].salt == NULL)
790 		    krb5_free_salt(context, salt);
791 		if (ret == 0) {
792 		    free_ETYPE_INFO2(&e);
793 		    return paid;
794 		}
795 	    }
796 	}
797     }
798  out:
799     free_ETYPE_INFO2(&e);
800     return NULL;
801 }
802 
803 static struct pa_info_data *
804 pa_etype_info(krb5_context context,
805 	      const krb5_principal client,
806 	      const AS_REQ *asreq,
807 	      struct pa_info_data *paid,
808 	      heim_octet_string *data)
809 {
810     krb5_error_code ret;
811     ETYPE_INFO e;
812     size_t sz;
813     int i, j;
814 
815     memset(&e, 0, sizeof(e));
816     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
817     if (ret)
818 	goto out;
819     if (e.len == 0)
820 	goto out;
821     for (j = 0; j < asreq->req_body.etype.len; j++) {
822 	for (i = 0; i < e.len; i++) {
823 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
824 		krb5_salt salt;
825 		salt.salttype = KRB5_PW_SALT;
826 		if (e.val[i].salt == NULL)
827 		    ret = krb5_get_pw_salt(context, client, &salt);
828 		else {
829 		    salt.saltvalue = *e.val[i].salt;
830 		    ret = 0;
831 		}
832 		if (e.val[i].salttype)
833 		    salt.salttype = *e.val[i].salttype;
834 		if (ret == 0) {
835 		    ret = set_paid(paid, context, e.val[i].etype,
836 				   salt.salttype,
837 				   salt.saltvalue.data,
838 				   salt.saltvalue.length,
839 				   NULL);
840 		    if (e.val[i].salt == NULL)
841 			krb5_free_salt(context, salt);
842 		}
843 		if (ret == 0) {
844 		    free_ETYPE_INFO(&e);
845 		    return paid;
846 		}
847 	    }
848 	}
849     }
850  out:
851     free_ETYPE_INFO(&e);
852     return NULL;
853 }
854 
855 static struct pa_info_data *
856 pa_pw_or_afs3_salt(krb5_context context,
857 		   const krb5_principal client,
858 		   const AS_REQ *asreq,
859 		   struct pa_info_data *paid,
860 		   heim_octet_string *data)
861 {
862     krb5_error_code ret;
863     if (paid->etype == ENCTYPE_NULL)
864 	return NULL;
865     ret = set_paid(paid, context,
866 		   paid->etype,
867 		   paid->salt.salttype,
868 		   data->data,
869 		   data->length,
870 		   NULL);
871     if (ret)
872 	return NULL;
873     return paid;
874 }
875 
876 
877 struct pa_info {
878     krb5_preauthtype type;
879     struct pa_info_data *(*salt_info)(krb5_context,
880 				      const krb5_principal,
881 				      const AS_REQ *,
882 				      struct pa_info_data *,
883 				      heim_octet_string *);
884 };
885 
886 static struct pa_info pa_prefs[] = {
887     { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
888     { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
889     { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
890     { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
891 };
892 
893 static PA_DATA *
894 find_pa_data(const METHOD_DATA *md, int type)
895 {
896     int i;
897     if (md == NULL)
898 	return NULL;
899     for (i = 0; i < md->len; i++)
900 	if (md->val[i].padata_type == type)
901 	    return &md->val[i];
902     return NULL;
903 }
904 
905 static struct pa_info_data *
906 process_pa_info(krb5_context context,
907 		const krb5_principal client,
908 		const AS_REQ *asreq,
909 		struct pa_info_data *paid,
910 		METHOD_DATA *md)
911 {
912     struct pa_info_data *p = NULL;
913     int i;
914 
915     for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
916 	PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
917 	if (pa == NULL)
918 	    continue;
919 	paid->salt.salttype = pa_prefs[i].type;
920 	p = (*pa_prefs[i].salt_info)(context, client, asreq,
921 				     paid, &pa->padata_value);
922     }
923     return p;
924 }
925 
926 static krb5_error_code
927 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
928 		      krb5_enctype etype, krb5_keyblock *key)
929 {
930     PA_ENC_TS_ENC p;
931     unsigned char *buf;
932     size_t buf_size;
933     size_t len;
934     EncryptedData encdata;
935     krb5_error_code ret;
936     int32_t usec;
937     int usec2;
938     krb5_crypto crypto;
939 
940     krb5_us_timeofday (context, &p.patimestamp, &usec);
941     usec2         = usec;
942     p.pausec      = &usec2;
943 
944     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
945     if (ret)
946 	return ret;
947     if(buf_size != len)
948 	krb5_abortx(context, "internal error in ASN.1 encoder");
949 
950     ret = krb5_crypto_init(context, key, 0, &crypto);
951     if (ret) {
952 	free(buf);
953 	return ret;
954     }
955     ret = krb5_encrypt_EncryptedData(context,
956 				     crypto,
957 				     KRB5_KU_PA_ENC_TIMESTAMP,
958 				     buf,
959 				     len,
960 				     0,
961 				     &encdata);
962     free(buf);
963     krb5_crypto_destroy(context, crypto);
964     if (ret)
965 	return ret;
966 
967     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
968     free_EncryptedData(&encdata);
969     if (ret)
970 	return ret;
971     if(buf_size != len)
972 	krb5_abortx(context, "internal error in ASN.1 encoder");
973 
974     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
975     if (ret)
976 	free(buf);
977     return ret;
978 }
979 
980 static krb5_error_code
981 add_enc_ts_padata(krb5_context context,
982 		  METHOD_DATA *md,
983 		  krb5_principal client,
984 		  krb5_s2k_proc keyproc,
985 		  krb5_const_pointer keyseed,
986 		  krb5_enctype *enctypes,
987 		  unsigned netypes,
988 		  krb5_salt *salt,
989 		  krb5_data *s2kparams)
990 {
991     krb5_error_code ret;
992     krb5_salt salt2;
993     krb5_enctype *ep;
994     int i;
995 
996     if(salt == NULL) {
997 	/* default to standard salt */
998 	ret = krb5_get_pw_salt (context, client, &salt2);
999 	if (ret)
1000 	    return ret;
1001 	salt = &salt2;
1002     }
1003     if (!enctypes) {
1004 	enctypes = context->etypes;
1005 	netypes = 0;
1006 	for (ep = enctypes; *ep != ETYPE_NULL; ep++)
1007 	    netypes++;
1008     }
1009 
1010     for (i = 0; i < netypes; ++i) {
1011 	krb5_keyblock *key;
1012 
1013 	_krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1014 
1015 	ret = (*keyproc)(context, enctypes[i], keyseed,
1016 			 *salt, s2kparams, &key);
1017 	if (ret)
1018 	    continue;
1019 	ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1020 	krb5_free_keyblock (context, key);
1021 	if (ret)
1022 	    return ret;
1023     }
1024     if(salt == &salt2)
1025 	krb5_free_salt(context, salt2);
1026     return 0;
1027 }
1028 
1029 static krb5_error_code
1030 pa_data_to_md_ts_enc(krb5_context context,
1031 		     const AS_REQ *a,
1032 		     const krb5_principal client,
1033 		     krb5_get_init_creds_ctx *ctx,
1034 		     struct pa_info_data *ppaid,
1035 		     METHOD_DATA *md)
1036 {
1037     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1038 	return 0;
1039 
1040     if (ppaid) {
1041 	add_enc_ts_padata(context, md, client,
1042 			  ctx->keyproc, ctx->keyseed,
1043 			  &ppaid->etype, 1,
1044 			  &ppaid->salt, ppaid->s2kparams);
1045     } else {
1046 	krb5_salt salt;
1047 
1048 	_krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1049 
1050 	/* make a v5 salted pa-data */
1051 	add_enc_ts_padata(context, md, client,
1052 			  ctx->keyproc, ctx->keyseed,
1053 			  a->req_body.etype.val, a->req_body.etype.len,
1054 			  NULL, NULL);
1055 
1056 	/* make a v4 salted pa-data */
1057 	salt.salttype = KRB5_PW_SALT;
1058 	krb5_data_zero(&salt.saltvalue);
1059 	add_enc_ts_padata(context, md, client,
1060 			  ctx->keyproc, ctx->keyseed,
1061 			  a->req_body.etype.val, a->req_body.etype.len,
1062 			  &salt, NULL);
1063     }
1064     return 0;
1065 }
1066 
1067 static krb5_error_code
1068 pa_data_to_key_plain(krb5_context context,
1069 		     const krb5_principal client,
1070 		     krb5_get_init_creds_ctx *ctx,
1071 		     krb5_salt salt,
1072 		     krb5_data *s2kparams,
1073 		     krb5_enctype etype,
1074 		     krb5_keyblock **key)
1075 {
1076     krb5_error_code ret;
1077 
1078     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1079 			   salt, s2kparams, key);
1080     return ret;
1081 }
1082 
1083 
1084 static krb5_error_code
1085 pa_data_to_md_pkinit(krb5_context context,
1086 		     const AS_REQ *a,
1087 		     const krb5_principal client,
1088 		     int win2k,
1089 		     krb5_get_init_creds_ctx *ctx,
1090 		     METHOD_DATA *md)
1091 {
1092     if (ctx->pk_init_ctx == NULL)
1093 	return 0;
1094 #ifdef PKINIT
1095     return _krb5_pk_mk_padata(context,
1096 			      ctx->pk_init_ctx,
1097 			      ctx->ic_flags,
1098 			      win2k,
1099 			      &a->req_body,
1100 			      ctx->pk_nonce,
1101 			      md);
1102 #else
1103     krb5_set_error_message(context, EINVAL,
1104 			   N_("no support for PKINIT compiled in", ""));
1105     return EINVAL;
1106 #endif
1107 }
1108 
1109 static krb5_error_code
1110 pa_data_add_pac_request(krb5_context context,
1111 			krb5_get_init_creds_ctx *ctx,
1112 			METHOD_DATA *md)
1113 {
1114     size_t len, length;
1115     krb5_error_code ret;
1116     PA_PAC_REQUEST req;
1117     void *buf;
1118 
1119     switch (ctx->req_pac) {
1120     case KRB5_INIT_CREDS_TRISTATE_UNSET:
1121 	return 0; /* don't bother */
1122     case KRB5_INIT_CREDS_TRISTATE_TRUE:
1123 	req.include_pac = 1;
1124 	break;
1125     case KRB5_INIT_CREDS_TRISTATE_FALSE:
1126 	req.include_pac = 0;
1127     }
1128 
1129     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1130 		       &req, &len, ret);
1131     if (ret)
1132 	return ret;
1133     if(len != length)
1134 	krb5_abortx(context, "internal error in ASN.1 encoder");
1135 
1136     ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1137     if (ret)
1138 	free(buf);
1139 
1140     return 0;
1141 }
1142 
1143 /*
1144  * Assumes caller always will free `out_md', even on error.
1145  */
1146 
1147 static krb5_error_code
1148 process_pa_data_to_md(krb5_context context,
1149 		      const krb5_creds *creds,
1150 		      const AS_REQ *a,
1151 		      krb5_get_init_creds_ctx *ctx,
1152 		      METHOD_DATA *in_md,
1153 		      METHOD_DATA **out_md,
1154 		      krb5_prompter_fct prompter,
1155 		      void *prompter_data)
1156 {
1157     krb5_error_code ret;
1158 
1159     ALLOC(*out_md, 1);
1160     if (*out_md == NULL) {
1161 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1162 	return ENOMEM;
1163     }
1164     (*out_md)->len = 0;
1165     (*out_md)->val = NULL;
1166 
1167     if (_krb5_have_debug(context, 5)) {
1168 	unsigned i;
1169 	_krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1170 	for (i = 0; i < in_md->len; i++)
1171 	    _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1172     }
1173 
1174     /*
1175      * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1176      * need to expose our password protecting our PKCS12 key.
1177      */
1178 
1179     if (ctx->pk_init_ctx) {
1180 
1181  	_krb5_debug(context, 5, "krb5_get_init_creds: "
1182 		    "prepareing PKINIT padata (%s)",
1183  		    (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1184 
1185  	if (ctx->used_pa_types & USED_PKINIT_W2K) {
1186  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1187  				   "Already tried pkinit, looping");
1188  	    return KRB5_GET_IN_TKT_LOOP;
1189  	}
1190 
1191 	ret = pa_data_to_md_pkinit(context, a, creds->client,
1192 				   (ctx->used_pa_types & USED_PKINIT),
1193 				   ctx, *out_md);
1194 	if (ret)
1195 	    return ret;
1196 
1197 	if (ctx->used_pa_types & USED_PKINIT)
1198 	    ctx->used_pa_types |= USED_PKINIT_W2K;
1199  	else
1200  	    ctx->used_pa_types |= USED_PKINIT;
1201 
1202     } else if (in_md->len != 0) {
1203 	struct pa_info_data *paid, *ppaid;
1204  	unsigned flag;
1205 
1206 	paid = calloc(1, sizeof(*paid));
1207 
1208 	paid->etype = ENCTYPE_NULL;
1209 	ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1210 
1211  	if (ppaid)
1212  	    flag = USED_ENC_TS_INFO;
1213  	else
1214  	    flag = USED_ENC_TS_GUESS;
1215 
1216  	if (ctx->used_pa_types & flag) {
1217  	    if (ppaid)
1218  		free_paid(context, ppaid);
1219  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1220  				   "Already tried ENC-TS-%s, looping",
1221  				   flag == USED_ENC_TS_INFO ? "info" : "guess");
1222  	    return KRB5_GET_IN_TKT_LOOP;
1223  	}
1224 
1225 	pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1226 
1227 	ctx->used_pa_types |= flag;
1228 
1229 	if (ppaid) {
1230 	    if (ctx->ppaid) {
1231 		free_paid(context, ctx->ppaid);
1232 		free(ctx->ppaid);
1233 	    }
1234 	    ctx->ppaid = ppaid;
1235 	} else
1236 	    free(paid);
1237     }
1238 
1239     pa_data_add_pac_request(context, ctx, *out_md);
1240 
1241     if ((*out_md)->len == 0) {
1242 	free(*out_md);
1243 	*out_md = NULL;
1244     }
1245 
1246     return 0;
1247 }
1248 
1249 static krb5_error_code
1250 process_pa_data_to_key(krb5_context context,
1251 		       krb5_get_init_creds_ctx *ctx,
1252 		       krb5_creds *creds,
1253 		       AS_REQ *a,
1254 		       AS_REP *rep,
1255 		       const krb5_krbhst_info *hi,
1256 		       krb5_keyblock **key)
1257 {
1258     struct pa_info_data paid, *ppaid = NULL;
1259     krb5_error_code ret;
1260     krb5_enctype etype;
1261     PA_DATA *pa;
1262 
1263     memset(&paid, 0, sizeof(paid));
1264 
1265     etype = rep->enc_part.etype;
1266 
1267     if (rep->padata) {
1268 	paid.etype = etype;
1269 	ppaid = process_pa_info(context, creds->client, a, &paid,
1270 				rep->padata);
1271     }
1272     if (ppaid == NULL)
1273 	ppaid = ctx->ppaid;
1274     if (ppaid == NULL) {
1275 	ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1276 	if (ret)
1277 	    return ret;
1278 	paid.etype = etype;
1279 	paid.s2kparams = NULL;
1280 	ppaid = &paid;
1281     }
1282 
1283     pa = NULL;
1284     if (rep->padata) {
1285 	int idx = 0;
1286 	pa = krb5_find_padata(rep->padata->val,
1287 			      rep->padata->len,
1288 			      KRB5_PADATA_PK_AS_REP,
1289 			      &idx);
1290 	if (pa == NULL) {
1291 	    idx = 0;
1292 	    pa = krb5_find_padata(rep->padata->val,
1293 				  rep->padata->len,
1294 				  KRB5_PADATA_PK_AS_REP_19,
1295 				  &idx);
1296 	}
1297     }
1298     if (pa && ctx->pk_init_ctx) {
1299 #ifdef PKINIT
1300 	_krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1301 
1302 	ret = _krb5_pk_rd_pa_reply(context,
1303 				   a->req_body.realm,
1304 				   ctx->pk_init_ctx,
1305 				   etype,
1306 				   hi,
1307 				   ctx->pk_nonce,
1308 				   &ctx->req_buffer,
1309 				   pa,
1310 				   key);
1311 #else
1312 	ret = EINVAL;
1313 	krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1314 #endif
1315     } else if (ctx->keyseed) {
1316  	_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1317 	ret = pa_data_to_key_plain(context, creds->client, ctx,
1318 				   ppaid->salt, ppaid->s2kparams, etype, key);
1319     } else {
1320 	ret = EINVAL;
1321 	krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1322     }
1323 
1324     free_paid(context, &paid);
1325     return ret;
1326 }
1327 
1328 /**
1329  * Start a new context to get a new initial credential.
1330  *
1331  * @param context A Kerberos 5 context.
1332  * @param client The Kerberos principal to get the credential for, if
1333  *     NULL is given, the default principal is used as determined by
1334  *     krb5_get_default_principal().
1335  * @param prompter
1336  * @param prompter_data
1337  * @param start_time the time the ticket should start to be valid or 0 for now.
1338  * @param options a options structure, can be NULL for default options.
1339  * @param rctx A new allocated free with krb5_init_creds_free().
1340  *
1341  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1342  *
1343  * @ingroup krb5_credential
1344  */
1345 
1346 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1347 krb5_init_creds_init(krb5_context context,
1348 		     krb5_principal client,
1349 		     krb5_prompter_fct prompter,
1350 		     void *prompter_data,
1351 		     krb5_deltat start_time,
1352 		     krb5_get_init_creds_opt *options,
1353 		     krb5_init_creds_context *rctx)
1354 {
1355     krb5_init_creds_context ctx;
1356     krb5_error_code ret;
1357 
1358     *rctx = NULL;
1359 
1360     ctx = calloc(1, sizeof(*ctx));
1361     if (ctx == NULL) {
1362 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1363 	return ENOMEM;
1364     }
1365 
1366     ret = get_init_creds_common(context, client, start_time, options, ctx);
1367     if (ret) {
1368 	free(ctx);
1369 	return ret;
1370     }
1371 
1372     /* Set a new nonce. */
1373     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1374     ctx->nonce &= 0x7fffffff;
1375     /* XXX these just needs to be the same when using Windows PK-INIT */
1376     ctx->pk_nonce = ctx->nonce;
1377 
1378     ctx->prompter = prompter;
1379     ctx->prompter_data = prompter_data;
1380 
1381     *rctx = ctx;
1382 
1383     return ret;
1384 }
1385 
1386 /**
1387  * Sets the service that the is requested. This call is only neede for
1388  * special initial tickets, by default the a krbtgt is fetched in the default realm.
1389  *
1390  * @param context a Kerberos 5 context.
1391  * @param ctx a krb5_init_creds_context context.
1392  * @param service the service given as a string, for example
1393  *        "kadmind/admin". If NULL, the default krbtgt in the clients
1394  *        realm is set.
1395  *
1396  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1397  * @ingroup krb5_credential
1398  */
1399 
1400 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1401 krb5_init_creds_set_service(krb5_context context,
1402 			    krb5_init_creds_context ctx,
1403 			    const char *service)
1404 {
1405     krb5_const_realm client_realm;
1406     krb5_principal principal;
1407     krb5_error_code ret;
1408 
1409     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1410 
1411     if (service) {
1412 	ret = krb5_parse_name (context, service, &principal);
1413 	if (ret)
1414 	    return ret;
1415 	krb5_principal_set_realm (context, principal, client_realm);
1416     } else {
1417 	ret = krb5_make_principal(context, &principal,
1418 				  client_realm, KRB5_TGS_NAME, client_realm,
1419 				  NULL);
1420 	if (ret)
1421 	    return ret;
1422     }
1423 
1424     /*
1425      * This is for Windows RODC that are picky about what name type
1426      * the server principal have, and the really strange part is that
1427      * they are picky about the AS-REQ name type and not the TGS-REQ
1428      * later. Oh well.
1429      */
1430 
1431     if (krb5_principal_is_krbtgt(context, principal))
1432 	krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1433 
1434     krb5_free_principal(context, ctx->cred.server);
1435     ctx->cred.server = principal;
1436 
1437     return 0;
1438 }
1439 
1440 /**
1441  * Sets the password that will use for the request.
1442  *
1443  * @param context a Kerberos 5 context.
1444  * @param ctx ctx krb5_init_creds_context context.
1445  * @param password the password to use.
1446  *
1447  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1448  * @ingroup krb5_credential
1449  */
1450 
1451 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1452 krb5_init_creds_set_password(krb5_context context,
1453 			     krb5_init_creds_context ctx,
1454 			     const char *password)
1455 {
1456     if (ctx->password) {
1457 	memset(ctx->password, 0, strlen(ctx->password));
1458 	free(ctx->password);
1459     }
1460     if (password) {
1461 	ctx->password = strdup(password);
1462 	if (ctx->password == NULL) {
1463 	    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1464 	    return ENOMEM;
1465 	}
1466 	ctx->keyseed = (void *) ctx->password;
1467     } else {
1468 	ctx->keyseed = NULL;
1469 	ctx->password = NULL;
1470     }
1471 
1472     return 0;
1473 }
1474 
1475 static krb5_error_code KRB5_CALLCONV
1476 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1477 		krb5_const_pointer keyseed,
1478 		krb5_salt salt, krb5_data *s2kparms,
1479 		krb5_keyblock **key)
1480 {
1481     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
1482     krb5_keytab keytab = args->keytab;
1483     krb5_principal principal = args->principal;
1484     krb5_error_code ret;
1485     krb5_keytab real_keytab;
1486     krb5_keytab_entry entry;
1487 
1488     if(keytab == NULL)
1489 	krb5_kt_default(context, &real_keytab);
1490     else
1491 	real_keytab = keytab;
1492 
1493     ret = krb5_kt_get_entry (context, real_keytab, principal,
1494 			     0, enctype, &entry);
1495 
1496     if (keytab == NULL)
1497 	krb5_kt_close (context, real_keytab);
1498 
1499     if (ret)
1500 	return ret;
1501 
1502     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1503     krb5_kt_free_entry(context, &entry);
1504     return ret;
1505 }
1506 
1507 
1508 /**
1509  * Set the keytab to use for authentication.
1510  *
1511  * @param context a Kerberos 5 context.
1512  * @param ctx ctx krb5_init_creds_context context.
1513  * @param keytab the keytab to read the key from.
1514  *
1515  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1516  * @ingroup krb5_credential
1517  */
1518 
1519 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1520 krb5_init_creds_set_keytab(krb5_context context,
1521 			   krb5_init_creds_context ctx,
1522 			   krb5_keytab keytab)
1523 {
1524     krb5_keytab_key_proc_args *a;
1525     krb5_keytab_entry entry;
1526     krb5_kt_cursor cursor;
1527     krb5_enctype *etypes = NULL;
1528     krb5_error_code ret;
1529     size_t netypes = 0;
1530     int kvno = 0;
1531 
1532     a = malloc(sizeof(*a));
1533     if (a == NULL) {
1534 	krb5_set_error_message(context, ENOMEM,
1535 			       N_("malloc: out of memory", ""));
1536 	return ENOMEM;
1537     }
1538 
1539     a->principal = ctx->cred.client;
1540     a->keytab    = keytab;
1541 
1542     ctx->keytab_data = a;
1543     ctx->keyseed = (void *)a;
1544     ctx->keyproc = keytab_key_proc;
1545 
1546     /*
1547      * We need to the KDC what enctypes we support for this keytab,
1548      * esp if the keytab is really a password based entry, then the
1549      * KDC might have more enctypes in the database then what we have
1550      * in the keytab.
1551      */
1552 
1553     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1554     if(ret)
1555 	goto out;
1556 
1557     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1558 	void *ptr;
1559 
1560 	if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1561 	    goto next;
1562 
1563 	/* check if we ahve this kvno already */
1564 	if (entry.vno > kvno) {
1565 	    /* remove old list of etype */
1566 	    if (etypes)
1567 		free(etypes);
1568 	    etypes = NULL;
1569 	    netypes = 0;
1570 	    kvno = entry.vno;
1571 	} else if (entry.vno != kvno)
1572 	    goto next;
1573 
1574 	/* check if enctype is supported */
1575 	if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1576 	    goto next;
1577 
1578 	/* add enctype to supported list */
1579 	ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1580 	if (ptr == NULL)
1581 	    goto next;
1582 
1583 	etypes = ptr;
1584 	etypes[netypes] = entry.keyblock.keytype;
1585 	etypes[netypes + 1] = ETYPE_NULL;
1586 	netypes++;
1587     next:
1588 	krb5_kt_free_entry(context, &entry);
1589     }
1590     krb5_kt_end_seq_get(context, keytab, &cursor);
1591 
1592     if (etypes) {
1593 	if (ctx->etypes)
1594 	    free(ctx->etypes);
1595 	ctx->etypes = etypes;
1596     }
1597 
1598  out:
1599     return 0;
1600 }
1601 
1602 static krb5_error_code KRB5_CALLCONV
1603 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1604 		  krb5_const_pointer keyseed,
1605 		  krb5_salt salt, krb5_data *s2kparms,
1606 		  krb5_keyblock **key)
1607 {
1608     return krb5_copy_keyblock (context, keyseed, key);
1609 }
1610 
1611 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1612 krb5_init_creds_set_keyblock(krb5_context context,
1613 			     krb5_init_creds_context ctx,
1614 			     krb5_keyblock *keyblock)
1615 {
1616     ctx->keyseed = (void *)keyblock;
1617     ctx->keyproc = keyblock_key_proc;
1618 
1619     return 0;
1620 }
1621 
1622 /**
1623  * The core loop if krb5_get_init_creds() function family. Create the
1624  * packets and have the caller send them off to the KDC.
1625  *
1626  * If the caller want all work been done for them, use
1627  * krb5_init_creds_get() instead.
1628  *
1629  * @param context a Kerberos 5 context.
1630  * @param ctx ctx krb5_init_creds_context context.
1631  * @param in input data from KDC, first round it should be reset by krb5_data_zer().
1632  * @param out reply to KDC.
1633  * @param hostinfo KDC address info, first round it can be NULL.
1634  * @param flags status of the round, if
1635  *        KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
1636  *
1637  * @return 0 for success, or an Kerberos 5 error code, see
1638  *     krb5_get_error_message().
1639  *
1640  * @ingroup krb5_credential
1641  */
1642 
1643 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1644 krb5_init_creds_step(krb5_context context,
1645 		     krb5_init_creds_context ctx,
1646 		     krb5_data *in,
1647 		     krb5_data *out,
1648 		     krb5_krbhst_info *hostinfo,
1649 		     unsigned int *flags)
1650 {
1651     krb5_error_code ret;
1652     size_t len;
1653     size_t size;
1654 
1655     krb5_data_zero(out);
1656 
1657     if (ctx->as_req.req_body.cname == NULL) {
1658 	ret = init_as_req(context, ctx->flags, &ctx->cred,
1659 			  ctx->addrs, ctx->etypes, &ctx->as_req);
1660 	if (ret) {
1661 	    free_init_creds_ctx(context, ctx);
1662 	    return ret;
1663 	}
1664     }
1665 
1666 #define MAX_PA_COUNTER 10
1667     if (ctx->pa_counter > MAX_PA_COUNTER) {
1668 	krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1669 			       N_("Looping %d times while getting "
1670 				  "initial credentials", ""),
1671 			       ctx->pa_counter);
1672 	return KRB5_GET_IN_TKT_LOOP;
1673     }
1674     ctx->pa_counter++;
1675 
1676     _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
1677 
1678     /* Lets process the input packet */
1679     if (in && in->length) {
1680 	krb5_kdc_rep rep;
1681 
1682 	memset(&rep, 0, sizeof(rep));
1683 
1684 	_krb5_debug(context, 5, "krb5_get_init_creds: processing input");
1685 
1686 	ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
1687 	if (ret == 0) {
1688 	    krb5_keyblock *key = NULL;
1689 	    unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
1690 
1691 	    if (ctx->flags.canonicalize) {
1692 		eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1693 		eflags |= EXTRACT_TICKET_MATCH_REALM;
1694 	    }
1695 	    if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
1696 		eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
1697 
1698 	    ret = process_pa_data_to_key(context, ctx, &ctx->cred,
1699 					 &ctx->as_req, &rep.kdc_rep, hostinfo, &key);
1700 	    if (ret) {
1701 		free_AS_REP(&rep.kdc_rep);
1702 		goto out;
1703 	    }
1704 
1705 	    _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
1706 
1707 	    ret = _krb5_extract_ticket(context,
1708 				       &rep,
1709 				       &ctx->cred,
1710 				       key,
1711 				       NULL,
1712 				       KRB5_KU_AS_REP_ENC_PART,
1713 				       NULL,
1714 				       ctx->nonce,
1715 				       eflags,
1716 				       NULL,
1717 				       NULL);
1718 	    krb5_free_keyblock(context, key);
1719 
1720 	    *flags = 0;
1721 
1722 	    if (ret == 0)
1723 		ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
1724 
1725 	    free_AS_REP(&rep.kdc_rep);
1726 	    free_EncASRepPart(&rep.enc_part);
1727 
1728 	    return ret;
1729 
1730 	} else {
1731 	    /* let's try to parse it as a KRB-ERROR */
1732 
1733 	    _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
1734 
1735 	    free_KRB_ERROR(&ctx->error);
1736 
1737 	    ret = krb5_rd_error(context, in, &ctx->error);
1738 	    if(ret && in->length && ((char*)in->data)[0] == 4)
1739 		ret = KRB5KRB_AP_ERR_V4_REPLY;
1740 	    if (ret) {
1741 		_krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
1742 		goto out;
1743 	    }
1744 
1745 	    ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
1746 
1747 	    _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
1748 
1749 	    /*
1750 	     * If no preauth was set and KDC requires it, give it one
1751 	     * more try.
1752 	     */
1753 
1754 	    if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1755 
1756 	        free_METHOD_DATA(&ctx->md);
1757 	        memset(&ctx->md, 0, sizeof(ctx->md));
1758 
1759 		if (ctx->error.e_data) {
1760 		    ret = decode_METHOD_DATA(ctx->error.e_data->data,
1761 					     ctx->error.e_data->length,
1762 					     &ctx->md,
1763 					     NULL);
1764 		    if (ret)
1765 			krb5_set_error_message(context, ret,
1766 					       N_("Failed to decode METHOD-DATA", ""));
1767 		} else {
1768 		    krb5_set_error_message(context, ret,
1769 					   N_("Preauth required but no preauth "
1770 					      "options send by KDC", ""));
1771 		}
1772 	    } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
1773 		/*
1774 		 * Try adapt to timeskrew when we are using pre-auth, and
1775 		 * if there was a time skew, try again.
1776 		 */
1777 		krb5_set_real_time(context, ctx->error.stime, -1);
1778 		if (context->kdc_sec_offset)
1779 		    ret = 0;
1780 
1781 		_krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
1782 			    context->kdc_sec_offset);
1783 
1784 		ctx->used_pa_types = 0;
1785 
1786 	    } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
1787 	        /* client referal to a new realm */
1788 
1789 		if (ctx->error.crealm == NULL) {
1790 		    krb5_set_error_message(context, ret,
1791 					   N_("Got a client referral, not but no realm", ""));
1792 		    goto out;
1793 		}
1794 		_krb5_debug(context, 5,
1795 			    "krb5_get_init_creds: got referal to realm %s",
1796 			    *ctx->error.crealm);
1797 
1798 		ret = krb5_principal_set_realm(context,
1799 					       ctx->cred.client,
1800 					       *ctx->error.crealm);
1801 
1802 		ctx->used_pa_types = 0;
1803 	    }
1804 	    if (ret)
1805 		goto out;
1806 	}
1807     }
1808 
1809     if (ctx->as_req.padata) {
1810 	free_METHOD_DATA(ctx->as_req.padata);
1811 	free(ctx->as_req.padata);
1812 	ctx->as_req.padata = NULL;
1813     }
1814 
1815     /* Set a new nonce. */
1816     ctx->as_req.req_body.nonce = ctx->nonce;
1817 
1818     /* fill_in_md_data */
1819     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
1820 				&ctx->md, &ctx->as_req.padata,
1821 				ctx->prompter, ctx->prompter_data);
1822     if (ret)
1823 	goto out;
1824 
1825     krb5_data_free(&ctx->req_buffer);
1826 
1827     ASN1_MALLOC_ENCODE(AS_REQ,
1828 		       ctx->req_buffer.data, ctx->req_buffer.length,
1829 		       &ctx->as_req, &len, ret);
1830     if (ret)
1831 	goto out;
1832     if(len != ctx->req_buffer.length)
1833 	krb5_abortx(context, "internal error in ASN.1 encoder");
1834 
1835     out->data = ctx->req_buffer.data;
1836     out->length = ctx->req_buffer.length;
1837 
1838     *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1839 
1840     return 0;
1841  out:
1842     return ret;
1843 }
1844 
1845 /**
1846  * Extract the newly acquired credentials from krb5_init_creds_context
1847  * context.
1848  *
1849  * @param context A Kerberos 5 context.
1850  * @param ctx
1851  * @param cred credentials, free with krb5_free_cred_contents().
1852  *
1853  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
1854  */
1855 
1856 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1857 krb5_init_creds_get_creds(krb5_context context,
1858 			  krb5_init_creds_context ctx,
1859 			  krb5_creds *cred)
1860 {
1861     return krb5_copy_creds_contents(context, &ctx->cred, cred);
1862 }
1863 
1864 /**
1865  * Get the last error from the transaction.
1866  *
1867  * @return Returns 0 or an error code
1868  *
1869  * @ingroup krb5_credential
1870  */
1871 
1872 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1873 krb5_init_creds_get_error(krb5_context context,
1874 			  krb5_init_creds_context ctx,
1875 			  KRB_ERROR *error)
1876 {
1877     krb5_error_code ret;
1878 
1879     ret = copy_KRB_ERROR(&ctx->error, error);
1880     if (ret)
1881 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1882 
1883     return ret;
1884 }
1885 
1886 /**
1887  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
1888  *
1889  * @param context A Kerberos 5 context.
1890  * @param ctx The krb5_init_creds_context to free.
1891  *
1892  * @ingroup krb5_credential
1893  */
1894 
1895 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1896 krb5_init_creds_free(krb5_context context,
1897 		     krb5_init_creds_context ctx)
1898 {
1899     free_init_creds_ctx(context, ctx);
1900     free(ctx);
1901 }
1902 
1903 /**
1904  * Get new credentials as setup by the krb5_init_creds_context.
1905  *
1906  * @param context A Kerberos 5 context.
1907  * @param ctx The krb5_init_creds_context to process.
1908  *
1909  * @ingroup krb5_credential
1910  */
1911 
1912 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1913 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
1914 {
1915     krb5_sendto_ctx stctx = NULL;
1916     krb5_krbhst_info *hostinfo = NULL;
1917     krb5_error_code ret;
1918     krb5_data in, out;
1919     unsigned int flags = 0;
1920 
1921     krb5_data_zero(&in);
1922     krb5_data_zero(&out);
1923 
1924     ret = krb5_sendto_ctx_alloc(context, &stctx);
1925     if (ret)
1926 	goto out;
1927     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
1928 
1929     while (1) {
1930 	flags = 0;
1931 	ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
1932 	krb5_data_free(&in);
1933 	if (ret)
1934 	    goto out;
1935 
1936 	if ((flags & 1) == 0)
1937 	    break;
1938 
1939 	ret = krb5_sendto_context (context, stctx, &out,
1940 				   ctx->cred.client->realm, &in);
1941     	if (ret)
1942 	    goto out;
1943 
1944     }
1945 
1946  out:
1947     if (stctx)
1948 	krb5_sendto_ctx_free(context, stctx);
1949 
1950     return ret;
1951 }
1952 
1953 /**
1954  * Get new credentials using password.
1955  *
1956  * @ingroup krb5_credential
1957  */
1958 
1959 
1960 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1961 krb5_get_init_creds_password(krb5_context context,
1962 			     krb5_creds *creds,
1963 			     krb5_principal client,
1964 			     const char *password,
1965 			     krb5_prompter_fct prompter,
1966 			     void *data,
1967 			     krb5_deltat start_time,
1968 			     const char *in_tkt_service,
1969 			     krb5_get_init_creds_opt *options)
1970 {
1971     krb5_init_creds_context ctx;
1972     char buf[BUFSIZ];
1973     krb5_error_code ret;
1974     int chpw = 0;
1975 
1976  again:
1977     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
1978     if (ret)
1979 	goto out;
1980 
1981     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1982     if (ret)
1983 	goto out;
1984 
1985     if (prompter != NULL && ctx->password == NULL && password == NULL) {
1986 	krb5_prompt prompt;
1987 	krb5_data password_data;
1988 	char *p, *q;
1989 
1990 	krb5_unparse_name (context, client, &p);
1991 	asprintf (&q, "%s's Password: ", p);
1992 	free (p);
1993 	prompt.prompt = q;
1994 	password_data.data   = buf;
1995 	password_data.length = sizeof(buf);
1996 	prompt.hidden = 1;
1997 	prompt.reply  = &password_data;
1998 	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1999 
2000 	ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2001 	free (q);
2002 	if (ret) {
2003 	    memset (buf, 0, sizeof(buf));
2004 	    ret = KRB5_LIBOS_PWDINTR;
2005 	    krb5_clear_error_message (context);
2006 	    goto out;
2007 	}
2008 	password = password_data.data;
2009     }
2010 
2011     if (password) {
2012 	ret = krb5_init_creds_set_password(context, ctx, password);
2013 	if (ret)
2014 	    goto out;
2015     }
2016 
2017     ret = krb5_init_creds_get(context, ctx);
2018 
2019     if (ret == 0)
2020 	process_last_request(context, options, ctx);
2021 
2022 
2023     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2024 	char buf2[1024];
2025 
2026 	/* try to avoid recursion */
2027 	if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2028 	   goto out;
2029 
2030 	/* don't try to change password where then where none */
2031 	if (prompter == NULL)
2032 	    goto out;
2033 
2034 	ret = change_password (context,
2035 			       client,
2036 			       ctx->password,
2037 			       buf2,
2038 			       sizeof(buf),
2039 			       prompter,
2040 			       data,
2041 			       options);
2042 	if (ret)
2043 	    goto out;
2044 	chpw = 1;
2045 	krb5_init_creds_free(context, ctx);
2046 	goto again;
2047     }
2048 
2049  out:
2050     if (ret == 0)
2051 	krb5_init_creds_get_creds(context, ctx, creds);
2052 
2053     if (ctx)
2054 	krb5_init_creds_free(context, ctx);
2055 
2056     memset(buf, 0, sizeof(buf));
2057     return ret;
2058 }
2059 
2060 /**
2061  * Get new credentials using keyblock.
2062  *
2063  * @ingroup krb5_credential
2064  */
2065 
2066 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2067 krb5_get_init_creds_keyblock(krb5_context context,
2068 			     krb5_creds *creds,
2069 			     krb5_principal client,
2070 			     krb5_keyblock *keyblock,
2071 			     krb5_deltat start_time,
2072 			     const char *in_tkt_service,
2073 			     krb5_get_init_creds_opt *options)
2074 {
2075     krb5_init_creds_context ctx;
2076     krb5_error_code ret;
2077 
2078     memset(creds, 0, sizeof(*creds));
2079 
2080     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2081     if (ret)
2082 	goto out;
2083 
2084     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2085     if (ret)
2086 	goto out;
2087 
2088     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2089     if (ret)
2090 	goto out;
2091 
2092     ret = krb5_init_creds_get(context, ctx);
2093 
2094     if (ret == 0)
2095         process_last_request(context, options, ctx);
2096 
2097  out:
2098     if (ret == 0)
2099 	krb5_init_creds_get_creds(context, ctx, creds);
2100 
2101     if (ctx)
2102 	krb5_init_creds_free(context, ctx);
2103 
2104     return ret;
2105 }
2106 
2107 /**
2108  * Get new credentials using keytab.
2109  *
2110  * @ingroup krb5_credential
2111  */
2112 
2113 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2114 krb5_get_init_creds_keytab(krb5_context context,
2115 			   krb5_creds *creds,
2116 			   krb5_principal client,
2117 			   krb5_keytab keytab,
2118 			   krb5_deltat start_time,
2119 			   const char *in_tkt_service,
2120 			   krb5_get_init_creds_opt *options)
2121 {
2122     krb5_init_creds_context ctx;
2123     krb5_error_code ret;
2124 
2125     memset(creds, 0, sizeof(*creds));
2126 
2127     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2128     if (ret)
2129 	goto out;
2130 
2131     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2132     if (ret)
2133 	goto out;
2134 
2135     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2136     if (ret)
2137 	goto out;
2138 
2139     ret = krb5_init_creds_get(context, ctx);
2140     if (ret == 0)
2141         process_last_request(context, options, ctx);
2142 
2143  out:
2144     if (ret == 0)
2145 	krb5_init_creds_get_creds(context, ctx, creds);
2146 
2147     if (ctx)
2148 	krb5_init_creds_free(context, ctx);
2149 
2150     return ret;
2151 }
2152