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