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