1 /*	$NetBSD: init_creds_pw.c,v 1.1.1.2 2014/04/24 12:45:50 pettai 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
free_paid(krb5_context context,struct pa_info_data * ppaid)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
default_s2k_func(krb5_context context,krb5_enctype type,krb5_const_pointer keyseed,krb5_salt salt,krb5_data * s2kparms,krb5_keyblock ** key)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
free_init_creds_ctx(krb5_context context,krb5_init_creds_context ctx)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
get_config_time(krb5_context context,const char * realm,const char * name,int def)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
init_cred(krb5_context context,krb5_creds * cred,krb5_principal client,krb5_deltat start_time,krb5_get_init_creds_opt * options)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
report_expiration(krb5_context context,krb5_prompter_fct prompter,krb5_data * data,const char * str,time_t now)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
process_last_request(krb5_context context,krb5_get_init_creds_opt * options,krb5_init_creds_context ctx)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
get_init_creds_common(krb5_context context,krb5_principal client,krb5_deltat start_time,krb5_get_init_creds_opt * options,krb5_init_creds_context ctx)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
change_password(krb5_context context,krb5_principal client,const char * password,char * newpw,size_t newpw_sz,krb5_prompter_fct prompter,void * data,krb5_get_init_creds_opt * old_options)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
krb5_keyblock_key_proc(krb5_context context,krb5_keytype type,krb5_data * salt,krb5_const_pointer keyseed,krb5_keyblock ** key)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
init_as_req(krb5_context context,KDCOptions opts,const krb5_creds * creds,const krb5_addresses * addrs,const krb5_enctype * etypes,AS_REQ * a)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 			   KRB5_PDU_AS_REQUEST,
678 			   &a->req_body.etype.len,
679 			   &a->req_body.etype.val,
680 			   etypes);
681     if (ret)
682 	goto fail;
683 
684     /*
685      * This means no addresses
686      */
687 
688     if (addrs && addrs->len == 0) {
689 	a->req_body.addresses = NULL;
690     } else {
691 	a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
692 	if (a->req_body.addresses == NULL) {
693 	    ret = ENOMEM;
694 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
695 	    goto fail;
696 	}
697 
698 	if (addrs)
699 	    ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
700 	else {
701 	    ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
702 	    if(ret == 0 && a->req_body.addresses->len == 0) {
703 		free(a->req_body.addresses);
704 		a->req_body.addresses = NULL;
705 	    }
706 	}
707 	if (ret)
708 	    goto fail;
709     }
710 
711     a->req_body.enc_authorization_data = NULL;
712     a->req_body.additional_tickets = NULL;
713 
714     a->padata = NULL;
715 
716     return 0;
717  fail:
718     free_AS_REQ(a);
719     memset(a, 0, sizeof(*a));
720     return ret;
721 }
722 
723 
724 static krb5_error_code
set_paid(struct pa_info_data * paid,krb5_context context,krb5_enctype etype,krb5_salttype salttype,void * salt_string,size_t salt_len,krb5_data * s2kparams)725 set_paid(struct pa_info_data *paid, krb5_context context,
726 	 krb5_enctype etype,
727 	 krb5_salttype salttype, void *salt_string, size_t salt_len,
728 	 krb5_data *s2kparams)
729 {
730     paid->etype = etype;
731     paid->salt.salttype = salttype;
732     paid->salt.saltvalue.data = malloc(salt_len + 1);
733     if (paid->salt.saltvalue.data == NULL) {
734 	krb5_clear_error_message(context);
735 	return ENOMEM;
736     }
737     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
738     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
739     paid->salt.saltvalue.length = salt_len;
740     if (s2kparams) {
741 	krb5_error_code ret;
742 
743 	ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
744 	if (ret) {
745 	    krb5_clear_error_message(context);
746 	    krb5_free_salt(context, paid->salt);
747 	    return ret;
748 	}
749     } else
750 	paid->s2kparams = NULL;
751 
752     return 0;
753 }
754 
755 static struct pa_info_data *
pa_etype_info2(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,heim_octet_string * data)756 pa_etype_info2(krb5_context context,
757 	       const krb5_principal client,
758 	       const AS_REQ *asreq,
759 	       struct pa_info_data *paid,
760 	       heim_octet_string *data)
761 {
762     krb5_error_code ret;
763     ETYPE_INFO2 e;
764     size_t sz;
765     size_t i, j;
766 
767     memset(&e, 0, sizeof(e));
768     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
769     if (ret)
770 	goto out;
771     if (e.len == 0)
772 	goto out;
773     for (j = 0; j < asreq->req_body.etype.len; j++) {
774 	for (i = 0; i < e.len; i++) {
775 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
776 		krb5_salt salt;
777 		if (e.val[i].salt == NULL)
778 		    ret = krb5_get_pw_salt(context, client, &salt);
779 		else {
780 		    salt.saltvalue.data = *e.val[i].salt;
781 		    salt.saltvalue.length = strlen(*e.val[i].salt);
782 		    ret = 0;
783 		}
784 		if (ret == 0)
785 		    ret = set_paid(paid, context, e.val[i].etype,
786 				   KRB5_PW_SALT,
787 				   salt.saltvalue.data,
788 				   salt.saltvalue.length,
789 				   e.val[i].s2kparams);
790 		if (e.val[i].salt == NULL)
791 		    krb5_free_salt(context, salt);
792 		if (ret == 0) {
793 		    free_ETYPE_INFO2(&e);
794 		    return paid;
795 		}
796 	    }
797 	}
798     }
799  out:
800     free_ETYPE_INFO2(&e);
801     return NULL;
802 }
803 
804 static struct pa_info_data *
pa_etype_info(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,heim_octet_string * data)805 pa_etype_info(krb5_context context,
806 	      const krb5_principal client,
807 	      const AS_REQ *asreq,
808 	      struct pa_info_data *paid,
809 	      heim_octet_string *data)
810 {
811     krb5_error_code ret;
812     ETYPE_INFO e;
813     size_t sz;
814     size_t i, j;
815 
816     memset(&e, 0, sizeof(e));
817     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
818     if (ret)
819 	goto out;
820     if (e.len == 0)
821 	goto out;
822     for (j = 0; j < asreq->req_body.etype.len; j++) {
823 	for (i = 0; i < e.len; i++) {
824 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
825 		krb5_salt salt;
826 		salt.salttype = KRB5_PW_SALT;
827 		if (e.val[i].salt == NULL)
828 		    ret = krb5_get_pw_salt(context, client, &salt);
829 		else {
830 		    salt.saltvalue = *e.val[i].salt;
831 		    ret = 0;
832 		}
833 		if (e.val[i].salttype)
834 		    salt.salttype = *e.val[i].salttype;
835 		if (ret == 0) {
836 		    ret = set_paid(paid, context, e.val[i].etype,
837 				   salt.salttype,
838 				   salt.saltvalue.data,
839 				   salt.saltvalue.length,
840 				   NULL);
841 		    if (e.val[i].salt == NULL)
842 			krb5_free_salt(context, salt);
843 		}
844 		if (ret == 0) {
845 		    free_ETYPE_INFO(&e);
846 		    return paid;
847 		}
848 	    }
849 	}
850     }
851  out:
852     free_ETYPE_INFO(&e);
853     return NULL;
854 }
855 
856 static struct pa_info_data *
pa_pw_or_afs3_salt(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,heim_octet_string * data)857 pa_pw_or_afs3_salt(krb5_context context,
858 		   const krb5_principal client,
859 		   const AS_REQ *asreq,
860 		   struct pa_info_data *paid,
861 		   heim_octet_string *data)
862 {
863     krb5_error_code ret;
864     if (paid->etype == ENCTYPE_NULL)
865 	return NULL;
866     ret = set_paid(paid, context,
867 		   paid->etype,
868 		   paid->salt.salttype,
869 		   data->data,
870 		   data->length,
871 		   NULL);
872     if (ret)
873 	return NULL;
874     return paid;
875 }
876 
877 
878 struct pa_info {
879     krb5_preauthtype type;
880     struct pa_info_data *(*salt_info)(krb5_context,
881 				      const krb5_principal,
882 				      const AS_REQ *,
883 				      struct pa_info_data *,
884 				      heim_octet_string *);
885 };
886 
887 static struct pa_info pa_prefs[] = {
888     { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
889     { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
890     { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
891     { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
892 };
893 
894 static PA_DATA *
find_pa_data(const METHOD_DATA * md,unsigned type)895 find_pa_data(const METHOD_DATA *md, unsigned type)
896 {
897     size_t i;
898     if (md == NULL)
899 	return NULL;
900     for (i = 0; i < md->len; i++)
901 	if (md->val[i].padata_type == type)
902 	    return &md->val[i];
903     return NULL;
904 }
905 
906 static struct pa_info_data *
process_pa_info(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,METHOD_DATA * md)907 process_pa_info(krb5_context context,
908 		const krb5_principal client,
909 		const AS_REQ *asreq,
910 		struct pa_info_data *paid,
911 		METHOD_DATA *md)
912 {
913     struct pa_info_data *p = NULL;
914     size_t i;
915 
916     for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
917 	PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
918 	if (pa == NULL)
919 	    continue;
920 	paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
921 	p = (*pa_prefs[i].salt_info)(context, client, asreq,
922 				     paid, &pa->padata_value);
923     }
924     return p;
925 }
926 
927 static krb5_error_code
make_pa_enc_timestamp(krb5_context context,METHOD_DATA * md,krb5_enctype etype,krb5_keyblock * key)928 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
929 		      krb5_enctype etype, krb5_keyblock *key)
930 {
931     PA_ENC_TS_ENC p;
932     unsigned char *buf;
933     size_t buf_size;
934     size_t len = 0;
935     EncryptedData encdata;
936     krb5_error_code ret;
937     int32_t usec;
938     int usec2;
939     krb5_crypto crypto;
940 
941     krb5_us_timeofday (context, &p.patimestamp, &usec);
942     usec2         = usec;
943     p.pausec      = &usec2;
944 
945     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
946     if (ret)
947 	return ret;
948     if(buf_size != len)
949 	krb5_abortx(context, "internal error in ASN.1 encoder");
950 
951     ret = krb5_crypto_init(context, key, 0, &crypto);
952     if (ret) {
953 	free(buf);
954 	return ret;
955     }
956     ret = krb5_encrypt_EncryptedData(context,
957 				     crypto,
958 				     KRB5_KU_PA_ENC_TIMESTAMP,
959 				     buf,
960 				     len,
961 				     0,
962 				     &encdata);
963     free(buf);
964     krb5_crypto_destroy(context, crypto);
965     if (ret)
966 	return ret;
967 
968     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
969     free_EncryptedData(&encdata);
970     if (ret)
971 	return ret;
972     if(buf_size != len)
973 	krb5_abortx(context, "internal error in ASN.1 encoder");
974 
975     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
976     if (ret)
977 	free(buf);
978     return ret;
979 }
980 
981 static krb5_error_code
add_enc_ts_padata(krb5_context context,METHOD_DATA * md,krb5_principal client,krb5_s2k_proc keyproc,krb5_const_pointer keyseed,krb5_enctype * enctypes,unsigned netypes,krb5_salt * salt,krb5_data * s2kparams)982 add_enc_ts_padata(krb5_context context,
983 		  METHOD_DATA *md,
984 		  krb5_principal client,
985 		  krb5_s2k_proc keyproc,
986 		  krb5_const_pointer keyseed,
987 		  krb5_enctype *enctypes,
988 		  unsigned netypes,
989 		  krb5_salt *salt,
990 		  krb5_data *s2kparams)
991 {
992     krb5_error_code ret;
993     krb5_salt salt2;
994     krb5_enctype *ep;
995     size_t i;
996 
997     if(salt == NULL) {
998 	/* default to standard salt */
999 	ret = krb5_get_pw_salt (context, client, &salt2);
1000 	if (ret)
1001 	    return ret;
1002 	salt = &salt2;
1003     }
1004     if (!enctypes) {
1005 	enctypes = context->etypes;
1006 	netypes = 0;
1007 	for (ep = enctypes; *ep != ETYPE_NULL; ep++)
1008 	    netypes++;
1009     }
1010 
1011     for (i = 0; i < netypes; ++i) {
1012 	krb5_keyblock *key;
1013 
1014 	_krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1015 
1016 	ret = (*keyproc)(context, enctypes[i], keyseed,
1017 			 *salt, s2kparams, &key);
1018 	if (ret)
1019 	    continue;
1020 	ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1021 	krb5_free_keyblock (context, key);
1022 	if (ret)
1023 	    return ret;
1024     }
1025     if(salt == &salt2)
1026 	krb5_free_salt(context, salt2);
1027     return 0;
1028 }
1029 
1030 static krb5_error_code
pa_data_to_md_ts_enc(krb5_context context,const AS_REQ * a,const krb5_principal client,krb5_get_init_creds_ctx * ctx,struct pa_info_data * ppaid,METHOD_DATA * md)1031 pa_data_to_md_ts_enc(krb5_context context,
1032 		     const AS_REQ *a,
1033 		     const krb5_principal client,
1034 		     krb5_get_init_creds_ctx *ctx,
1035 		     struct pa_info_data *ppaid,
1036 		     METHOD_DATA *md)
1037 {
1038     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1039 	return 0;
1040 
1041     if (ppaid) {
1042 	add_enc_ts_padata(context, md, client,
1043 			  ctx->keyproc, ctx->keyseed,
1044 			  &ppaid->etype, 1,
1045 			  &ppaid->salt, ppaid->s2kparams);
1046     } else {
1047 	krb5_salt salt;
1048 
1049 	_krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1050 
1051 	/* make a v5 salted pa-data */
1052 	add_enc_ts_padata(context, md, client,
1053 			  ctx->keyproc, ctx->keyseed,
1054 			  a->req_body.etype.val, a->req_body.etype.len,
1055 			  NULL, NULL);
1056 
1057 	/* make a v4 salted pa-data */
1058 	salt.salttype = KRB5_PW_SALT;
1059 	krb5_data_zero(&salt.saltvalue);
1060 	add_enc_ts_padata(context, md, client,
1061 			  ctx->keyproc, ctx->keyseed,
1062 			  a->req_body.etype.val, a->req_body.etype.len,
1063 			  &salt, NULL);
1064     }
1065     return 0;
1066 }
1067 
1068 static krb5_error_code
pa_data_to_key_plain(krb5_context context,const krb5_principal client,krb5_get_init_creds_ctx * ctx,krb5_salt salt,krb5_data * s2kparams,krb5_enctype etype,krb5_keyblock ** key)1069 pa_data_to_key_plain(krb5_context context,
1070 		     const krb5_principal client,
1071 		     krb5_get_init_creds_ctx *ctx,
1072 		     krb5_salt salt,
1073 		     krb5_data *s2kparams,
1074 		     krb5_enctype etype,
1075 		     krb5_keyblock **key)
1076 {
1077     krb5_error_code ret;
1078 
1079     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1080 			   salt, s2kparams, key);
1081     return ret;
1082 }
1083 
1084 
1085 static krb5_error_code
pa_data_to_md_pkinit(krb5_context context,const AS_REQ * a,const krb5_principal client,int win2k,krb5_get_init_creds_ctx * ctx,METHOD_DATA * md)1086 pa_data_to_md_pkinit(krb5_context context,
1087 		     const AS_REQ *a,
1088 		     const krb5_principal client,
1089 		     int win2k,
1090 		     krb5_get_init_creds_ctx *ctx,
1091 		     METHOD_DATA *md)
1092 {
1093     if (ctx->pk_init_ctx == NULL)
1094 	return 0;
1095 #ifdef PKINIT
1096     return _krb5_pk_mk_padata(context,
1097 			      ctx->pk_init_ctx,
1098 			      ctx->ic_flags,
1099 			      win2k,
1100 			      &a->req_body,
1101 			      ctx->pk_nonce,
1102 			      md);
1103 #else
1104     krb5_set_error_message(context, EINVAL,
1105 			   N_("no support for PKINIT compiled in", ""));
1106     return EINVAL;
1107 #endif
1108 }
1109 
1110 static krb5_error_code
pa_data_add_pac_request(krb5_context context,krb5_get_init_creds_ctx * ctx,METHOD_DATA * md)1111 pa_data_add_pac_request(krb5_context context,
1112 			krb5_get_init_creds_ctx *ctx,
1113 			METHOD_DATA *md)
1114 {
1115     size_t len = 0, length;
1116     krb5_error_code ret;
1117     PA_PAC_REQUEST req;
1118     void *buf;
1119 
1120     switch (ctx->req_pac) {
1121     case KRB5_INIT_CREDS_TRISTATE_UNSET:
1122 	return 0; /* don't bother */
1123     case KRB5_INIT_CREDS_TRISTATE_TRUE:
1124 	req.include_pac = 1;
1125 	break;
1126     case KRB5_INIT_CREDS_TRISTATE_FALSE:
1127 	req.include_pac = 0;
1128     }
1129 
1130     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1131 		       &req, &len, ret);
1132     if (ret)
1133 	return ret;
1134     if(len != length)
1135 	krb5_abortx(context, "internal error in ASN.1 encoder");
1136 
1137     ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1138     if (ret)
1139 	free(buf);
1140 
1141     return 0;
1142 }
1143 
1144 /*
1145  * Assumes caller always will free `out_md', even on error.
1146  */
1147 
1148 static krb5_error_code
process_pa_data_to_md(krb5_context context,const krb5_creds * creds,const AS_REQ * a,krb5_get_init_creds_ctx * ctx,METHOD_DATA * in_md,METHOD_DATA ** out_md,krb5_prompter_fct prompter,void * prompter_data)1149 process_pa_data_to_md(krb5_context context,
1150 		      const krb5_creds *creds,
1151 		      const AS_REQ *a,
1152 		      krb5_get_init_creds_ctx *ctx,
1153 		      METHOD_DATA *in_md,
1154 		      METHOD_DATA **out_md,
1155 		      krb5_prompter_fct prompter,
1156 		      void *prompter_data)
1157 {
1158     krb5_error_code ret;
1159 
1160     ALLOC(*out_md, 1);
1161     if (*out_md == NULL) {
1162 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1163 	return ENOMEM;
1164     }
1165     (*out_md)->len = 0;
1166     (*out_md)->val = NULL;
1167 
1168     if (_krb5_have_debug(context, 5)) {
1169 	unsigned i;
1170 	_krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1171 	for (i = 0; i < in_md->len; i++)
1172 	    _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1173     }
1174 
1175     /*
1176      * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1177      * need to expose our password protecting our PKCS12 key.
1178      */
1179 
1180     if (ctx->pk_init_ctx) {
1181 
1182  	_krb5_debug(context, 5, "krb5_get_init_creds: "
1183 		    "prepareing PKINIT padata (%s)",
1184  		    (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1185 
1186  	if (ctx->used_pa_types & USED_PKINIT_W2K) {
1187  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1188  				   "Already tried pkinit, looping");
1189  	    return KRB5_GET_IN_TKT_LOOP;
1190  	}
1191 
1192 	ret = pa_data_to_md_pkinit(context, a, creds->client,
1193 				   (ctx->used_pa_types & USED_PKINIT),
1194 				   ctx, *out_md);
1195 	if (ret)
1196 	    return ret;
1197 
1198 	if (ctx->used_pa_types & USED_PKINIT)
1199 	    ctx->used_pa_types |= USED_PKINIT_W2K;
1200  	else
1201  	    ctx->used_pa_types |= USED_PKINIT;
1202 
1203     } else if (in_md->len != 0) {
1204 	struct pa_info_data *paid, *ppaid;
1205  	unsigned flag;
1206 
1207 	paid = calloc(1, sizeof(*paid));
1208 
1209 	paid->etype = ENCTYPE_NULL;
1210 	ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1211 
1212  	if (ppaid)
1213  	    flag = USED_ENC_TS_INFO;
1214  	else
1215  	    flag = USED_ENC_TS_GUESS;
1216 
1217  	if (ctx->used_pa_types & flag) {
1218  	    if (ppaid)
1219  		free_paid(context, ppaid);
1220  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1221  				   "Already tried ENC-TS-%s, looping",
1222  				   flag == USED_ENC_TS_INFO ? "info" : "guess");
1223  	    return KRB5_GET_IN_TKT_LOOP;
1224  	}
1225 
1226 	pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1227 
1228 	ctx->used_pa_types |= flag;
1229 
1230 	if (ppaid) {
1231 	    if (ctx->ppaid) {
1232 		free_paid(context, ctx->ppaid);
1233 		free(ctx->ppaid);
1234 	    }
1235 	    ctx->ppaid = ppaid;
1236 	} else
1237 	    free(paid);
1238     }
1239 
1240     pa_data_add_pac_request(context, ctx, *out_md);
1241 
1242     if ((*out_md)->len == 0) {
1243 	free(*out_md);
1244 	*out_md = NULL;
1245     }
1246 
1247     return 0;
1248 }
1249 
1250 static krb5_error_code
process_pa_data_to_key(krb5_context context,krb5_get_init_creds_ctx * ctx,krb5_creds * creds,AS_REQ * a,AS_REP * rep,const krb5_krbhst_info * hi,krb5_keyblock ** key)1251 process_pa_data_to_key(krb5_context context,
1252 		       krb5_get_init_creds_ctx *ctx,
1253 		       krb5_creds *creds,
1254 		       AS_REQ *a,
1255 		       AS_REP *rep,
1256 		       const krb5_krbhst_info *hi,
1257 		       krb5_keyblock **key)
1258 {
1259     struct pa_info_data paid, *ppaid = NULL;
1260     krb5_error_code ret;
1261     krb5_enctype etype;
1262     PA_DATA *pa;
1263 
1264     memset(&paid, 0, sizeof(paid));
1265 
1266     etype = rep->enc_part.etype;
1267 
1268     if (rep->padata) {
1269 	paid.etype = etype;
1270 	ppaid = process_pa_info(context, creds->client, a, &paid,
1271 				rep->padata);
1272     }
1273     if (ppaid == NULL)
1274 	ppaid = ctx->ppaid;
1275     if (ppaid == NULL) {
1276 	ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1277 	if (ret)
1278 	    return ret;
1279 	paid.etype = etype;
1280 	paid.s2kparams = NULL;
1281 	ppaid = &paid;
1282     }
1283 
1284     pa = NULL;
1285     if (rep->padata) {
1286 	int idx = 0;
1287 	pa = krb5_find_padata(rep->padata->val,
1288 			      rep->padata->len,
1289 			      KRB5_PADATA_PK_AS_REP,
1290 			      &idx);
1291 	if (pa == NULL) {
1292 	    idx = 0;
1293 	    pa = krb5_find_padata(rep->padata->val,
1294 				  rep->padata->len,
1295 				  KRB5_PADATA_PK_AS_REP_19,
1296 				  &idx);
1297 	}
1298     }
1299     if (pa && ctx->pk_init_ctx) {
1300 #ifdef PKINIT
1301 	_krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1302 
1303 	ret = _krb5_pk_rd_pa_reply(context,
1304 				   a->req_body.realm,
1305 				   ctx->pk_init_ctx,
1306 				   etype,
1307 				   hi,
1308 				   ctx->pk_nonce,
1309 				   &ctx->req_buffer,
1310 				   pa,
1311 				   key);
1312 #else
1313 	ret = EINVAL;
1314 	krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1315 #endif
1316     } else if (ctx->keyseed) {
1317  	_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1318 	ret = pa_data_to_key_plain(context, creds->client, ctx,
1319 				   ppaid->salt, ppaid->s2kparams, etype, key);
1320     } else {
1321 	ret = EINVAL;
1322 	krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1323     }
1324 
1325     free_paid(context, &paid);
1326     return ret;
1327 }
1328 
1329 /**
1330  * Start a new context to get a new initial credential.
1331  *
1332  * @param context A Kerberos 5 context.
1333  * @param client The Kerberos principal to get the credential for, if
1334  *     NULL is given, the default principal is used as determined by
1335  *     krb5_get_default_principal().
1336  * @param prompter
1337  * @param prompter_data
1338  * @param start_time the time the ticket should start to be valid or 0 for now.
1339  * @param options a options structure, can be NULL for default options.
1340  * @param rctx A new allocated free with krb5_init_creds_free().
1341  *
1342  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1343  *
1344  * @ingroup krb5_credential
1345  */
1346 
1347 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_init(krb5_context context,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,krb5_get_init_creds_opt * options,krb5_init_creds_context * rctx)1348 krb5_init_creds_init(krb5_context context,
1349 		     krb5_principal client,
1350 		     krb5_prompter_fct prompter,
1351 		     void *prompter_data,
1352 		     krb5_deltat start_time,
1353 		     krb5_get_init_creds_opt *options,
1354 		     krb5_init_creds_context *rctx)
1355 {
1356     krb5_init_creds_context ctx;
1357     krb5_error_code ret;
1358 
1359     *rctx = NULL;
1360 
1361     ctx = calloc(1, sizeof(*ctx));
1362     if (ctx == NULL) {
1363 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1364 	return ENOMEM;
1365     }
1366 
1367     ret = get_init_creds_common(context, client, start_time, options, ctx);
1368     if (ret) {
1369 	free(ctx);
1370 	return ret;
1371     }
1372 
1373     /* Set a new nonce. */
1374     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1375     ctx->nonce &= 0x7fffffff;
1376     /* XXX these just needs to be the same when using Windows PK-INIT */
1377     ctx->pk_nonce = ctx->nonce;
1378 
1379     ctx->prompter = prompter;
1380     ctx->prompter_data = prompter_data;
1381 
1382     *rctx = ctx;
1383 
1384     return ret;
1385 }
1386 
1387 /**
1388  * Sets the service that the is requested. This call is only neede for
1389  * special initial tickets, by default the a krbtgt is fetched in the default realm.
1390  *
1391  * @param context a Kerberos 5 context.
1392  * @param ctx a krb5_init_creds_context context.
1393  * @param service the service given as a string, for example
1394  *        "kadmind/admin". If NULL, the default krbtgt in the clients
1395  *        realm is set.
1396  *
1397  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1398  * @ingroup krb5_credential
1399  */
1400 
1401 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_service(krb5_context context,krb5_init_creds_context ctx,const char * service)1402 krb5_init_creds_set_service(krb5_context context,
1403 			    krb5_init_creds_context ctx,
1404 			    const char *service)
1405 {
1406     krb5_const_realm client_realm;
1407     krb5_principal principal;
1408     krb5_error_code ret;
1409 
1410     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1411 
1412     if (service) {
1413 	ret = krb5_parse_name (context, service, &principal);
1414 	if (ret)
1415 	    return ret;
1416 	krb5_principal_set_realm (context, principal, client_realm);
1417     } else {
1418 	ret = krb5_make_principal(context, &principal,
1419 				  client_realm, KRB5_TGS_NAME, client_realm,
1420 				  NULL);
1421 	if (ret)
1422 	    return ret;
1423     }
1424 
1425     /*
1426      * This is for Windows RODC that are picky about what name type
1427      * the server principal have, and the really strange part is that
1428      * they are picky about the AS-REQ name type and not the TGS-REQ
1429      * later. Oh well.
1430      */
1431 
1432     if (krb5_principal_is_krbtgt(context, principal))
1433 	krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1434 
1435     krb5_free_principal(context, ctx->cred.server);
1436     ctx->cred.server = principal;
1437 
1438     return 0;
1439 }
1440 
1441 /**
1442  * Sets the password that will use for the request.
1443  *
1444  * @param context a Kerberos 5 context.
1445  * @param ctx ctx krb5_init_creds_context context.
1446  * @param password the password to use.
1447  *
1448  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1449  * @ingroup krb5_credential
1450  */
1451 
1452 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_password(krb5_context context,krb5_init_creds_context ctx,const char * password)1453 krb5_init_creds_set_password(krb5_context context,
1454 			     krb5_init_creds_context ctx,
1455 			     const char *password)
1456 {
1457     if (ctx->password) {
1458 	memset(ctx->password, 0, strlen(ctx->password));
1459 	free(ctx->password);
1460     }
1461     if (password) {
1462 	ctx->password = strdup(password);
1463 	if (ctx->password == NULL) {
1464 	    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1465 	    return ENOMEM;
1466 	}
1467 	ctx->keyseed = (void *) ctx->password;
1468     } else {
1469 	ctx->keyseed = NULL;
1470 	ctx->password = NULL;
1471     }
1472 
1473     return 0;
1474 }
1475 
1476 static krb5_error_code KRB5_CALLCONV
keytab_key_proc(krb5_context context,krb5_enctype enctype,krb5_const_pointer keyseed,krb5_salt salt,krb5_data * s2kparms,krb5_keyblock ** key)1477 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1478 		krb5_const_pointer keyseed,
1479 		krb5_salt salt, krb5_data *s2kparms,
1480 		krb5_keyblock **key)
1481 {
1482     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
1483     krb5_keytab keytab = args->keytab;
1484     krb5_principal principal = args->principal;
1485     krb5_error_code ret;
1486     krb5_keytab real_keytab;
1487     krb5_keytab_entry entry;
1488 
1489     if(keytab == NULL)
1490 	krb5_kt_default(context, &real_keytab);
1491     else
1492 	real_keytab = keytab;
1493 
1494     ret = krb5_kt_get_entry (context, real_keytab, principal,
1495 			     0, enctype, &entry);
1496 
1497     if (keytab == NULL)
1498 	krb5_kt_close (context, real_keytab);
1499 
1500     if (ret)
1501 	return ret;
1502 
1503     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1504     krb5_kt_free_entry(context, &entry);
1505     return ret;
1506 }
1507 
1508 
1509 /**
1510  * Set the keytab to use for authentication.
1511  *
1512  * @param context a Kerberos 5 context.
1513  * @param ctx ctx krb5_init_creds_context context.
1514  * @param keytab the keytab to read the key from.
1515  *
1516  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1517  * @ingroup krb5_credential
1518  */
1519 
1520 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_keytab(krb5_context context,krb5_init_creds_context ctx,krb5_keytab keytab)1521 krb5_init_creds_set_keytab(krb5_context context,
1522 			   krb5_init_creds_context ctx,
1523 			   krb5_keytab keytab)
1524 {
1525     krb5_keytab_key_proc_args *a;
1526     krb5_keytab_entry entry;
1527     krb5_kt_cursor cursor;
1528     krb5_enctype *etypes = NULL;
1529     krb5_error_code ret;
1530     size_t netypes = 0;
1531     int kvno = 0;
1532 
1533     a = malloc(sizeof(*a));
1534     if (a == NULL) {
1535 	krb5_set_error_message(context, ENOMEM,
1536 			       N_("malloc: out of memory", ""));
1537 	return ENOMEM;
1538     }
1539 
1540     a->principal = ctx->cred.client;
1541     a->keytab    = keytab;
1542 
1543     ctx->keytab_data = a;
1544     ctx->keyseed = (void *)a;
1545     ctx->keyproc = keytab_key_proc;
1546 
1547     /*
1548      * We need to the KDC what enctypes we support for this keytab,
1549      * esp if the keytab is really a password based entry, then the
1550      * KDC might have more enctypes in the database then what we have
1551      * in the keytab.
1552      */
1553 
1554     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1555     if(ret)
1556 	goto out;
1557 
1558     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1559 	void *ptr;
1560 
1561 	if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1562 	    goto next;
1563 
1564 	/* check if we ahve this kvno already */
1565 	if (entry.vno > kvno) {
1566 	    /* remove old list of etype */
1567 	    if (etypes)
1568 		free(etypes);
1569 	    etypes = NULL;
1570 	    netypes = 0;
1571 	    kvno = entry.vno;
1572 	} else if (entry.vno != kvno)
1573 	    goto next;
1574 
1575 	/* check if enctype is supported */
1576 	if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1577 	    goto next;
1578 
1579 	/* add enctype to supported list */
1580 	ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1581 	if (ptr == NULL)
1582 	    goto next;
1583 
1584 	etypes = ptr;
1585 	etypes[netypes] = entry.keyblock.keytype;
1586 	etypes[netypes + 1] = ETYPE_NULL;
1587 	netypes++;
1588     next:
1589 	krb5_kt_free_entry(context, &entry);
1590     }
1591     krb5_kt_end_seq_get(context, keytab, &cursor);
1592 
1593     if (etypes) {
1594 	if (ctx->etypes)
1595 	    free(ctx->etypes);
1596 	ctx->etypes = etypes;
1597     }
1598 
1599  out:
1600     return 0;
1601 }
1602 
1603 static krb5_error_code KRB5_CALLCONV
keyblock_key_proc(krb5_context context,krb5_enctype enctype,krb5_const_pointer keyseed,krb5_salt salt,krb5_data * s2kparms,krb5_keyblock ** key)1604 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1605 		  krb5_const_pointer keyseed,
1606 		  krb5_salt salt, krb5_data *s2kparms,
1607 		  krb5_keyblock **key)
1608 {
1609     return krb5_copy_keyblock (context, keyseed, key);
1610 }
1611 
1612 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_keyblock(krb5_context context,krb5_init_creds_context ctx,krb5_keyblock * keyblock)1613 krb5_init_creds_set_keyblock(krb5_context context,
1614 			     krb5_init_creds_context ctx,
1615 			     krb5_keyblock *keyblock)
1616 {
1617     ctx->keyseed = (void *)keyblock;
1618     ctx->keyproc = keyblock_key_proc;
1619 
1620     return 0;
1621 }
1622 
1623 /**
1624  * The core loop if krb5_get_init_creds() function family. Create the
1625  * packets and have the caller send them off to the KDC.
1626  *
1627  * If the caller want all work been done for them, use
1628  * krb5_init_creds_get() instead.
1629  *
1630  * @param context a Kerberos 5 context.
1631  * @param ctx ctx krb5_init_creds_context context.
1632  * @param in input data from KDC, first round it should be reset by krb5_data_zer().
1633  * @param out reply to KDC.
1634  * @param hostinfo KDC address info, first round it can be NULL.
1635  * @param flags status of the round, if
1636  *        KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
1637  *
1638  * @return 0 for success, or an Kerberos 5 error code, see
1639  *     krb5_get_error_message().
1640  *
1641  * @ingroup krb5_credential
1642  */
1643 
1644 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_step(krb5_context context,krb5_init_creds_context ctx,krb5_data * in,krb5_data * out,krb5_krbhst_info * hostinfo,unsigned int * flags)1645 krb5_init_creds_step(krb5_context context,
1646 		     krb5_init_creds_context ctx,
1647 		     krb5_data *in,
1648 		     krb5_data *out,
1649 		     krb5_krbhst_info *hostinfo,
1650 		     unsigned int *flags)
1651 {
1652     krb5_error_code ret;
1653     size_t len = 0;
1654     size_t size;
1655 
1656     krb5_data_zero(out);
1657 
1658     if (ctx->as_req.req_body.cname == NULL) {
1659 	ret = init_as_req(context, ctx->flags, &ctx->cred,
1660 			  ctx->addrs, ctx->etypes, &ctx->as_req);
1661 	if (ret) {
1662 	    free_init_creds_ctx(context, ctx);
1663 	    return ret;
1664 	}
1665     }
1666 
1667 #define MAX_PA_COUNTER 10
1668     if (ctx->pa_counter > MAX_PA_COUNTER) {
1669 	krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1670 			       N_("Looping %d times while getting "
1671 				  "initial credentials", ""),
1672 			       ctx->pa_counter);
1673 	return KRB5_GET_IN_TKT_LOOP;
1674     }
1675     ctx->pa_counter++;
1676 
1677     _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
1678 
1679     /* Lets process the input packet */
1680     if (in && in->length) {
1681 	krb5_kdc_rep rep;
1682 
1683 	memset(&rep, 0, sizeof(rep));
1684 
1685 	_krb5_debug(context, 5, "krb5_get_init_creds: processing input");
1686 
1687 	ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
1688 	if (ret == 0) {
1689 	    krb5_keyblock *key = NULL;
1690 	    unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
1691 
1692 	    if (ctx->flags.canonicalize) {
1693 		eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1694 		eflags |= EXTRACT_TICKET_MATCH_REALM;
1695 	    }
1696 	    if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
1697 		eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
1698 
1699 	    ret = process_pa_data_to_key(context, ctx, &ctx->cred,
1700 					 &ctx->as_req, &rep.kdc_rep, hostinfo, &key);
1701 	    if (ret) {
1702 		free_AS_REP(&rep.kdc_rep);
1703 		goto out;
1704 	    }
1705 
1706 	    _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
1707 
1708 	    ret = _krb5_extract_ticket(context,
1709 				       &rep,
1710 				       &ctx->cred,
1711 				       key,
1712 				       NULL,
1713 				       KRB5_KU_AS_REP_ENC_PART,
1714 				       NULL,
1715 				       ctx->nonce,
1716 				       eflags,
1717 				       NULL,
1718 				       NULL);
1719 	    krb5_free_keyblock(context, key);
1720 
1721 	    *flags = 0;
1722 
1723 	    if (ret == 0)
1724 		ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
1725 
1726 	    free_AS_REP(&rep.kdc_rep);
1727 	    free_EncASRepPart(&rep.enc_part);
1728 
1729 	    return ret;
1730 
1731 	} else {
1732 	    /* let's try to parse it as a KRB-ERROR */
1733 
1734 	    _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
1735 
1736 	    free_KRB_ERROR(&ctx->error);
1737 
1738 	    ret = krb5_rd_error(context, in, &ctx->error);
1739 	    if(ret && in->length && ((char*)in->data)[0] == 4)
1740 		ret = KRB5KRB_AP_ERR_V4_REPLY;
1741 	    if (ret) {
1742 		_krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
1743 		goto out;
1744 	    }
1745 
1746 	    ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
1747 
1748 	    _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
1749 
1750 	    /*
1751 	     * If no preauth was set and KDC requires it, give it one
1752 	     * more try.
1753 	     */
1754 
1755 	    if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1756 
1757 	        free_METHOD_DATA(&ctx->md);
1758 	        memset(&ctx->md, 0, sizeof(ctx->md));
1759 
1760 		if (ctx->error.e_data) {
1761 		    ret = decode_METHOD_DATA(ctx->error.e_data->data,
1762 					     ctx->error.e_data->length,
1763 					     &ctx->md,
1764 					     NULL);
1765 		    if (ret)
1766 			krb5_set_error_message(context, ret,
1767 					       N_("Failed to decode METHOD-DATA", ""));
1768 		} else {
1769 		    krb5_set_error_message(context, ret,
1770 					   N_("Preauth required but no preauth "
1771 					      "options send by KDC", ""));
1772 		}
1773 	    } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
1774 		/*
1775 		 * Try adapt to timeskrew when we are using pre-auth, and
1776 		 * if there was a time skew, try again.
1777 		 */
1778 		krb5_set_real_time(context, ctx->error.stime, -1);
1779 		if (context->kdc_sec_offset)
1780 		    ret = 0;
1781 
1782 		_krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
1783 			    context->kdc_sec_offset);
1784 
1785 		ctx->used_pa_types = 0;
1786 
1787 	    } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
1788 	        /* client referal to a new realm */
1789 
1790 		if (ctx->error.crealm == NULL) {
1791 		    krb5_set_error_message(context, ret,
1792 					   N_("Got a client referral, not but no realm", ""));
1793 		    goto out;
1794 		}
1795 		_krb5_debug(context, 5,
1796 			    "krb5_get_init_creds: got referal to realm %s",
1797 			    *ctx->error.crealm);
1798 
1799 		ret = krb5_principal_set_realm(context,
1800 					       ctx->cred.client,
1801 					       *ctx->error.crealm);
1802 
1803 		ctx->used_pa_types = 0;
1804 	    }
1805 	    if (ret)
1806 		goto out;
1807 	}
1808     }
1809 
1810     if (ctx->as_req.padata) {
1811 	free_METHOD_DATA(ctx->as_req.padata);
1812 	free(ctx->as_req.padata);
1813 	ctx->as_req.padata = NULL;
1814     }
1815 
1816     /* Set a new nonce. */
1817     ctx->as_req.req_body.nonce = ctx->nonce;
1818 
1819     /* fill_in_md_data */
1820     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
1821 				&ctx->md, &ctx->as_req.padata,
1822 				ctx->prompter, ctx->prompter_data);
1823     if (ret)
1824 	goto out;
1825 
1826     krb5_data_free(&ctx->req_buffer);
1827 
1828     ASN1_MALLOC_ENCODE(AS_REQ,
1829 		       ctx->req_buffer.data, ctx->req_buffer.length,
1830 		       &ctx->as_req, &len, ret);
1831     if (ret)
1832 	goto out;
1833     if(len != ctx->req_buffer.length)
1834 	krb5_abortx(context, "internal error in ASN.1 encoder");
1835 
1836     out->data = ctx->req_buffer.data;
1837     out->length = ctx->req_buffer.length;
1838 
1839     *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1840 
1841     return 0;
1842  out:
1843     return ret;
1844 }
1845 
1846 /**
1847  * Extract the newly acquired credentials from krb5_init_creds_context
1848  * context.
1849  *
1850  * @param context A Kerberos 5 context.
1851  * @param ctx
1852  * @param cred credentials, free with krb5_free_cred_contents().
1853  *
1854  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
1855  */
1856 
1857 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get_creds(krb5_context context,krb5_init_creds_context ctx,krb5_creds * cred)1858 krb5_init_creds_get_creds(krb5_context context,
1859 			  krb5_init_creds_context ctx,
1860 			  krb5_creds *cred)
1861 {
1862     return krb5_copy_creds_contents(context, &ctx->cred, cred);
1863 }
1864 
1865 /**
1866  * Get the last error from the transaction.
1867  *
1868  * @return Returns 0 or an error code
1869  *
1870  * @ingroup krb5_credential
1871  */
1872 
1873 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get_error(krb5_context context,krb5_init_creds_context ctx,KRB_ERROR * error)1874 krb5_init_creds_get_error(krb5_context context,
1875 			  krb5_init_creds_context ctx,
1876 			  KRB_ERROR *error)
1877 {
1878     krb5_error_code ret;
1879 
1880     ret = copy_KRB_ERROR(&ctx->error, error);
1881     if (ret)
1882 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1883 
1884     return ret;
1885 }
1886 
1887 /**
1888  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
1889  *
1890  * @param context A Kerberos 5 context.
1891  * @param ctx The krb5_init_creds_context to free.
1892  *
1893  * @ingroup krb5_credential
1894  */
1895 
1896 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_init_creds_free(krb5_context context,krb5_init_creds_context ctx)1897 krb5_init_creds_free(krb5_context context,
1898 		     krb5_init_creds_context ctx)
1899 {
1900     free_init_creds_ctx(context, ctx);
1901     free(ctx);
1902 }
1903 
1904 /**
1905  * Get new credentials as setup by the krb5_init_creds_context.
1906  *
1907  * @param context A Kerberos 5 context.
1908  * @param ctx The krb5_init_creds_context to process.
1909  *
1910  * @ingroup krb5_credential
1911  */
1912 
1913 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get(krb5_context context,krb5_init_creds_context ctx)1914 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
1915 {
1916     krb5_sendto_ctx stctx = NULL;
1917     krb5_krbhst_info *hostinfo = NULL;
1918     krb5_error_code ret;
1919     krb5_data in, out;
1920     unsigned int flags = 0;
1921 
1922     krb5_data_zero(&in);
1923     krb5_data_zero(&out);
1924 
1925     ret = krb5_sendto_ctx_alloc(context, &stctx);
1926     if (ret)
1927 	goto out;
1928     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
1929 
1930     while (1) {
1931 	flags = 0;
1932 	ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
1933 	krb5_data_free(&in);
1934 	if (ret)
1935 	    goto out;
1936 
1937 	if ((flags & 1) == 0)
1938 	    break;
1939 
1940 	ret = krb5_sendto_context (context, stctx, &out,
1941 				   ctx->cred.client->realm, &in);
1942     	if (ret)
1943 	    goto out;
1944 
1945     }
1946 
1947  out:
1948     if (stctx)
1949 	krb5_sendto_ctx_free(context, stctx);
1950 
1951     return ret;
1952 }
1953 
1954 /**
1955  * Get new credentials using password.
1956  *
1957  * @ingroup krb5_credential
1958  */
1959 
1960 
1961 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_password(krb5_context context,krb5_creds * creds,krb5_principal client,const char * password,krb5_prompter_fct prompter,void * data,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options)1962 krb5_get_init_creds_password(krb5_context context,
1963 			     krb5_creds *creds,
1964 			     krb5_principal client,
1965 			     const char *password,
1966 			     krb5_prompter_fct prompter,
1967 			     void *data,
1968 			     krb5_deltat start_time,
1969 			     const char *in_tkt_service,
1970 			     krb5_get_init_creds_opt *options)
1971 {
1972     krb5_init_creds_context ctx;
1973     char buf[BUFSIZ];
1974     krb5_error_code ret;
1975     int chpw = 0;
1976 
1977  again:
1978     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
1979     if (ret)
1980 	goto out;
1981 
1982     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1983     if (ret)
1984 	goto out;
1985 
1986     if (prompter != NULL && ctx->password == NULL && password == NULL) {
1987 	krb5_prompt prompt;
1988 	krb5_data password_data;
1989 	char *p, *q;
1990 
1991 	krb5_unparse_name (context, client, &p);
1992 	asprintf (&q, "%s's Password: ", p);
1993 	free (p);
1994 	prompt.prompt = q;
1995 	password_data.data   = buf;
1996 	password_data.length = sizeof(buf);
1997 	prompt.hidden = 1;
1998 	prompt.reply  = &password_data;
1999 	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
2000 
2001 	ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2002 	free (q);
2003 	if (ret) {
2004 	    memset (buf, 0, sizeof(buf));
2005 	    ret = KRB5_LIBOS_PWDINTR;
2006 	    krb5_clear_error_message (context);
2007 	    goto out;
2008 	}
2009 	password = password_data.data;
2010     }
2011 
2012     if (password) {
2013 	ret = krb5_init_creds_set_password(context, ctx, password);
2014 	if (ret)
2015 	    goto out;
2016     }
2017 
2018     ret = krb5_init_creds_get(context, ctx);
2019 
2020     if (ret == 0)
2021 	process_last_request(context, options, ctx);
2022 
2023 
2024     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2025 	char buf2[1024];
2026 
2027 	/* try to avoid recursion */
2028 	if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2029 	   goto out;
2030 
2031 	/* don't try to change password where then where none */
2032 	if (prompter == NULL)
2033 	    goto out;
2034 
2035 	ret = change_password (context,
2036 			       client,
2037 			       ctx->password,
2038 			       buf2,
2039 			       sizeof(buf),
2040 			       prompter,
2041 			       data,
2042 			       options);
2043 	if (ret)
2044 	    goto out;
2045 	chpw = 1;
2046 	krb5_init_creds_free(context, ctx);
2047 	goto again;
2048     }
2049 
2050  out:
2051     if (ret == 0)
2052 	krb5_init_creds_get_creds(context, ctx, creds);
2053 
2054     if (ctx)
2055 	krb5_init_creds_free(context, ctx);
2056 
2057     memset(buf, 0, sizeof(buf));
2058     return ret;
2059 }
2060 
2061 /**
2062  * Get new credentials using keyblock.
2063  *
2064  * @ingroup krb5_credential
2065  */
2066 
2067 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_keyblock(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_keyblock * keyblock,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options)2068 krb5_get_init_creds_keyblock(krb5_context context,
2069 			     krb5_creds *creds,
2070 			     krb5_principal client,
2071 			     krb5_keyblock *keyblock,
2072 			     krb5_deltat start_time,
2073 			     const char *in_tkt_service,
2074 			     krb5_get_init_creds_opt *options)
2075 {
2076     krb5_init_creds_context ctx;
2077     krb5_error_code ret;
2078 
2079     memset(creds, 0, sizeof(*creds));
2080 
2081     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2082     if (ret)
2083 	goto out;
2084 
2085     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2086     if (ret)
2087 	goto out;
2088 
2089     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2090     if (ret)
2091 	goto out;
2092 
2093     ret = krb5_init_creds_get(context, ctx);
2094 
2095     if (ret == 0)
2096         process_last_request(context, options, ctx);
2097 
2098  out:
2099     if (ret == 0)
2100 	krb5_init_creds_get_creds(context, ctx, creds);
2101 
2102     if (ctx)
2103 	krb5_init_creds_free(context, ctx);
2104 
2105     return ret;
2106 }
2107 
2108 /**
2109  * Get new credentials using keytab.
2110  *
2111  * @ingroup krb5_credential
2112  */
2113 
2114 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_keytab(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_keytab keytab,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options)2115 krb5_get_init_creds_keytab(krb5_context context,
2116 			   krb5_creds *creds,
2117 			   krb5_principal client,
2118 			   krb5_keytab keytab,
2119 			   krb5_deltat start_time,
2120 			   const char *in_tkt_service,
2121 			   krb5_get_init_creds_opt *options)
2122 {
2123     krb5_init_creds_context ctx;
2124     krb5_error_code ret;
2125 
2126     memset(creds, 0, sizeof(*creds));
2127 
2128     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2129     if (ret)
2130 	goto out;
2131 
2132     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2133     if (ret)
2134 	goto out;
2135 
2136     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2137     if (ret)
2138 	goto out;
2139 
2140     ret = krb5_init_creds_get(context, ctx);
2141     if (ret == 0)
2142         process_last_request(context, options, ctx);
2143 
2144  out:
2145     if (ret == 0)
2146 	krb5_init_creds_get_creds(context, ctx, creds);
2147 
2148     if (ctx)
2149 	krb5_init_creds_free(context, ctx);
2150 
2151     return ret;
2152 }
2153