kafs: Fix a warning
[heimdal.git] / lib / krb5 / init_creds_pw.c
blob0ed47e798441115acee4478773b9ea774f5f5635
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
7 * Portions Copyright (c) 2021, PADL Software Pty Ltd. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the Institute nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include "krb5_locl.h"
38 #include "../base/heimbasepriv.h" /* XXX */
40 struct pa_info_data {
41 krb5_enctype etype;
42 krb5_salt salt;
43 krb5_data *s2kparams;
46 struct krb5_gss_init_ctx_data {
47 krb5_gssic_step step;
48 krb5_gssic_finish finish;
49 krb5_gssic_release_cred release_cred;
50 krb5_gssic_delete_sec_context delete_sec_context;
52 const struct gss_OID_desc_struct *mech;
53 struct gss_cred_id_t_desc_struct *cred;
55 struct {
56 unsigned int release_cred : 1;
57 } flags;
60 struct krb5_get_init_creds_ctx {
61 KDCOptions flags;
62 krb5_creds cred;
63 krb5_addresses *addrs;
64 krb5_enctype *etypes;
65 krb5_preauthtype *pre_auth_types;
66 char *in_tkt_service;
67 unsigned nonce;
68 unsigned pk_nonce;
70 krb5_data req_buffer;
71 AS_REQ as_req;
72 int pa_counter;
74 /* password and keytab_data is freed on completion */
75 char *password;
76 krb5_keytab_key_proc_args *keytab_data;
78 krb5_pointer *keyseed;
79 krb5_s2k_proc keyproc;
81 krb5_get_init_creds_tristate req_pac;
83 krb5_pk_init_ctx pk_init_ctx;
84 krb5_gss_init_ctx gss_init_ctx;
85 int ic_flags;
87 char *kdc_hostname;
88 char *sitename;
90 struct {
91 unsigned int change_password:1;
92 unsigned int change_password_prompt:1;
93 unsigned int allow_enc_pa_rep:1;
94 unsigned int allow_save_as_reply_key:1;
95 } runflags;
97 struct pa_info_data paid;
99 METHOD_DATA md;
100 KRB_ERROR error;
101 EncKDCRepPart enc_part;
103 krb5_prompter_fct prompter;
104 void *prompter_data;
105 int warned_user;
107 struct pa_info_data *ppaid;
109 struct krb5_fast_state fast_state;
110 krb5_enctype as_enctype;
111 krb5_keyblock *as_reply_key;
113 /* current and available pa mechansm in this exchange */
114 struct pa_auth_mech *pa_mech;
115 heim_array_t available_pa_mechs;
116 const char *pa_used;
118 struct {
119 struct timeval run_time;
120 } stats;
123 static void
124 free_paid(krb5_context context, struct pa_info_data *ppaid)
126 krb5_free_salt(context, ppaid->salt);
127 if (ppaid->s2kparams)
128 krb5_free_data(context, ppaid->s2kparams);
129 memset(ppaid, 0, sizeof(*ppaid));
132 static krb5_error_code KRB5_CALLCONV
133 default_s2k_func(krb5_context context, krb5_enctype type,
134 krb5_const_pointer keyseed,
135 krb5_salt salt, krb5_data *s2kparms,
136 krb5_keyblock **key)
138 krb5_error_code ret;
139 krb5_data password;
140 krb5_data opaque;
142 if (_krb5_have_debug(context, 5)) {
143 char *str = NULL;
144 ret = krb5_enctype_to_string(context, type, &str);
145 if (ret)
146 return ret;
148 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type);
149 free(str);
152 password.data = rk_UNCONST(keyseed);
153 password.length = keyseed ? strlen(keyseed) : 0;
154 if (s2kparms)
155 opaque = *s2kparms;
156 else
157 krb5_data_zero(&opaque);
159 *key = malloc(sizeof(**key));
160 if (*key == NULL)
161 return krb5_enomem(context);
162 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
163 salt, opaque, *key);
164 if (ret) {
165 free(*key);
166 *key = NULL;
168 return ret;
171 static void
172 free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
174 if (gssic == NULL)
175 return;
177 if (gssic->flags.release_cred)
178 gssic->release_cred(context, gssic, gssic->cred);
179 free(gssic);
182 static void
183 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
185 if (ctx->etypes)
186 free(ctx->etypes);
187 if (ctx->pre_auth_types)
188 free (ctx->pre_auth_types);
189 if (ctx->in_tkt_service)
190 free(ctx->in_tkt_service);
191 if (ctx->keytab_data)
192 free(ctx->keytab_data);
193 if (ctx->password) {
194 size_t len;
195 len = strlen(ctx->password);
196 memset_s(ctx->password, len, 0, len);
197 free(ctx->password);
199 free_gss_init_ctx(context, ctx->gss_init_ctx);
201 * FAST state
203 _krb5_fast_free(context, &ctx->fast_state);
204 if (ctx->as_reply_key)
205 krb5_free_keyblock(context, ctx->as_reply_key);
207 krb5_data_free(&ctx->req_buffer);
208 krb5_free_cred_contents(context, &ctx->cred);
209 free_METHOD_DATA(&ctx->md);
210 free_EncKDCRepPart(&ctx->enc_part);
211 free_KRB_ERROR(&ctx->error);
212 free_AS_REQ(&ctx->as_req);
214 heim_release(ctx->available_pa_mechs);
215 heim_release(ctx->pa_mech);
216 ctx->pa_mech = NULL;
217 free(ctx->kdc_hostname);
218 free(ctx->sitename);
219 free_paid(context, &ctx->paid);
220 memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
223 static krb5_deltat
224 get_config_time (krb5_context context,
225 const char *realm,
226 const char *name,
227 int def)
229 krb5_deltat ret;
231 ret = krb5_config_get_time (context, NULL,
232 "realms",
233 realm,
234 name,
235 NULL);
236 if (ret >= 0)
237 return ret;
238 ret = krb5_config_get_time (context, NULL,
239 "libdefaults",
240 name,
241 NULL);
242 if (ret >= 0)
243 return ret;
244 return def;
247 static krb5_error_code
248 init_cred (krb5_context context,
249 krb5_creds *cred,
250 krb5_principal client,
251 krb5_deltat start_time,
252 krb5_get_init_creds_opt *options)
254 krb5_error_code ret;
255 krb5_deltat tmp;
256 krb5_timestamp now;
258 krb5_timeofday (context, &now);
260 memset (cred, 0, sizeof(*cred));
262 if (client)
263 ret = krb5_copy_principal(context, client, &cred->client);
264 else
265 ret = krb5_get_default_principal(context, &cred->client);
266 if (ret)
267 goto out;
269 if (start_time)
270 cred->times.starttime = now + start_time;
272 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
273 tmp = options->tkt_life;
274 else
275 tmp = KRB5_TKT_LIFETIME_DEFAULT;
276 cred->times.endtime = now + tmp;
278 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
279 if (options->renew_life > 0)
280 tmp = options->renew_life;
281 else
282 tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
283 cred->times.renew_till = now + tmp;
286 return 0;
288 out:
289 krb5_free_cred_contents (context, cred);
290 return ret;
294 * Print a message (str) to the user about the expiration in `lr'
297 static void
298 report_expiration (krb5_context context,
299 krb5_prompter_fct prompter,
300 krb5_data *data,
301 const char *str,
302 time_t now)
304 char *p = NULL;
306 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
307 return;
308 (*prompter)(context, data, NULL, p, 0, NULL);
309 free(p);
313 * Check the context, and in the case there is a expiration warning,
314 * use the prompter to print the warning.
316 * @param context A Kerberos 5 context.
317 * @param options An GIC options structure
318 * @param ctx The krb5_init_creds_context check for expiration.
321 krb5_error_code
322 krb5_process_last_request(krb5_context context,
323 krb5_get_init_creds_opt *options,
324 krb5_init_creds_context ctx)
326 LastReq *lr;
327 size_t i;
330 * First check if there is a API consumer.
333 lr = &ctx->enc_part.last_req;
335 if (options && options->opt_private && options->opt_private->lr.func) {
336 krb5_last_req_entry **lre;
338 lre = calloc(lr->len + 1, sizeof(*lre));
339 if (lre == NULL)
340 return krb5_enomem(context);
342 for (i = 0; i < lr->len; i++) {
343 lre[i] = calloc(1, sizeof(*lre[i]));
344 if (lre[i] == NULL)
345 break;
346 lre[i]->lr_type = lr->val[i].lr_type;
347 lre[i]->value = lr->val[i].lr_value;
350 (*options->opt_private->lr.func)(context, lre,
351 options->opt_private->lr.ctx);
353 for (i = 0; i < lr->len; i++)
354 free(lre[i]);
355 free(lre);
358 return krb5_init_creds_warn_user(context, ctx);
362 * Warn the user using prompter in the krb5_init_creds_context about
363 * possible password and account expiration.
365 * @param context a Kerberos 5 context.
366 * @param ctx a krb5_init_creds_context context.
368 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
369 * @ingroup krb5_credential
372 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
373 krb5_init_creds_warn_user(krb5_context context,
374 krb5_init_creds_context ctx)
376 krb5_timestamp sec;
377 krb5_const_realm realm;
378 krb5_enctype weak_enctype = KRB5_ENCTYPE_NULL;
379 LastReq *lr;
380 unsigned i;
381 time_t t;
383 if (ctx->prompter == NULL)
384 return 0;
386 if (ctx->warned_user)
387 return 0;
389 ctx->warned_user = 1;
391 krb5_timeofday (context, &sec);
393 realm = krb5_principal_get_realm (context, ctx->cred.client);
394 lr = &ctx->enc_part.last_req;
396 t = sec + get_config_time (context,
397 realm,
398 "warn_pwexpire",
399 7 * 24 * 60 * 60);
401 for (i = 0; i < lr->len; ++i) {
402 if (lr->val[i].lr_value <= t) {
403 switch (lr->val[i].lr_type) {
404 case LR_PW_EXPTIME :
405 report_expiration(context, ctx->prompter,
406 ctx->prompter_data,
407 "Your password will expire at ",
408 lr->val[i].lr_value);
409 break;
410 case LR_ACCT_EXPTIME :
411 report_expiration(context, ctx->prompter,
412 ctx->prompter_data,
413 "Your account will expire at ",
414 lr->val[i].lr_value);
415 break;
416 default:
417 break;
422 if (krb5_is_enctype_weak(context, ctx->as_enctype))
423 weak_enctype = ctx->as_enctype;
424 else if (krb5_is_enctype_weak(context, ctx->cred.session.keytype))
425 weak_enctype = ctx->cred.session.keytype;
427 if (ctx->prompter && weak_enctype != KRB5_ENCTYPE_NULL) {
428 int suppress = krb5_config_get_bool_default(context, NULL, false,
429 "libdefaults",
430 "suppress_weak_enctype", NULL);
431 if (!suppress) {
432 char *str = NULL, *p = NULL;
433 int aret;
434 krb5_enctype_to_string(context, weak_enctype, &str);
436 aret = asprintf(&p, "Encryption type %s(%d) used for authentication is weak and will be deprecated",
437 str ? str : "unknown", weak_enctype);
438 if (aret >= 0 && p) {
439 (*ctx->prompter)(context, ctx->prompter_data, NULL, p, 0, NULL);
440 free(p);
442 free(str);
446 return 0;
449 static krb5_addresses no_addrs = { 0, NULL };
451 static krb5_error_code
452 get_init_creds_common(krb5_context context,
453 krb5_principal client,
454 krb5_prompter_fct prompter,
455 void *prompter_data,
456 krb5_deltat start_time,
457 krb5_get_init_creds_opt *options,
458 krb5_init_creds_context ctx)
460 krb5_get_init_creds_opt *default_opt = NULL;
461 krb5_error_code ret;
462 krb5_enctype *etypes;
463 krb5_preauthtype *pre_auth_types;
465 memset(ctx, 0, sizeof(*ctx));
467 if (options == NULL) {
468 const char *realm = krb5_principal_get_realm(context, client);
470 krb5_get_init_creds_opt_alloc (context, &default_opt);
471 options = default_opt;
472 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
475 if (options->opt_private) {
476 if (options->opt_private->password) {
477 ret = krb5_init_creds_set_password(context, ctx,
478 options->opt_private->password);
479 if (ret)
480 goto out;
483 ctx->keyproc = options->opt_private->key_proc;
484 ctx->req_pac = options->opt_private->req_pac;
485 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
486 ctx->ic_flags = options->opt_private->flags;
487 } else
488 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
490 if (ctx->keyproc == NULL)
491 ctx->keyproc = default_s2k_func;
493 if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
494 ctx->flags.canonicalize = 1;
496 ctx->pre_auth_types = NULL;
497 ctx->addrs = NULL;
498 ctx->etypes = NULL;
499 ctx->pre_auth_types = NULL;
501 ret = init_cred(context, &ctx->cred, client, start_time, options);
502 if (ret) {
503 if (default_opt)
504 krb5_get_init_creds_opt_free(context, default_opt);
505 return ret;
508 ret = krb5_init_creds_set_service(context, ctx, NULL);
509 if (ret)
510 goto out;
512 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
513 ctx->flags.forwardable = options->forwardable;
515 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
516 ctx->flags.proxiable = options->proxiable;
518 if (start_time)
519 ctx->flags.postdated = 1;
520 if (ctx->cred.times.renew_till)
521 ctx->flags.renewable = 1;
522 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
523 ctx->addrs = options->address_list;
524 } else if (options->opt_private) {
525 switch (options->opt_private->addressless) {
526 case KRB5_INIT_CREDS_TRISTATE_UNSET:
527 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
528 ctx->addrs = &no_addrs;
529 #else
530 ctx->addrs = NULL;
531 #endif
532 break;
533 case KRB5_INIT_CREDS_TRISTATE_FALSE:
534 ctx->addrs = NULL;
535 break;
536 case KRB5_INIT_CREDS_TRISTATE_TRUE:
537 ctx->addrs = &no_addrs;
538 break;
541 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
542 if (ctx->etypes)
543 free(ctx->etypes);
545 etypes = malloc((options->etype_list_length + 1)
546 * sizeof(krb5_enctype));
547 if (etypes == NULL) {
548 ret = krb5_enomem(context);
549 goto out;
551 memcpy (etypes, options->etype_list,
552 options->etype_list_length * sizeof(krb5_enctype));
553 etypes[options->etype_list_length] = ETYPE_NULL;
554 ctx->etypes = etypes;
556 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
557 pre_auth_types = malloc((options->preauth_list_length + 1)
558 * sizeof(krb5_preauthtype));
559 if (pre_auth_types == NULL) {
560 ret = krb5_enomem(context);
561 goto out;
563 memcpy (pre_auth_types, options->preauth_list,
564 options->preauth_list_length * sizeof(krb5_preauthtype));
565 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
566 ctx->pre_auth_types = pre_auth_types;
568 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
569 ctx->flags.request_anonymous = options->anonymous;
571 ctx->prompter = prompter;
572 ctx->prompter_data = prompter_data;
574 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
575 !options->change_password_prompt)
576 ctx->runflags.change_password_prompt = 0;
577 else
578 ctx->runflags.change_password_prompt = ctx->prompter != NULL;
580 if (default_opt)
581 krb5_get_init_creds_opt_free(context, default_opt);
582 return 0;
584 out:
585 if (default_opt)
586 krb5_get_init_creds_opt_free(context, default_opt);
587 return ret;
590 static krb5_error_code
591 change_password (krb5_context context,
592 krb5_principal client,
593 const char *password,
594 char *newpw,
595 size_t newpw_sz,
596 krb5_prompter_fct prompter,
597 void *data,
598 krb5_get_init_creds_opt *old_options)
600 krb5_prompt prompts[2];
601 krb5_error_code ret;
602 krb5_creds cpw_cred;
603 char buf1[BUFSIZ], buf2[BUFSIZ];
604 krb5_data password_data[2];
605 int result_code;
606 krb5_data result_code_string;
607 krb5_data result_string;
608 char *p;
609 krb5_get_init_creds_opt *options;
611 heim_assert(prompter != NULL, "unexpected NULL prompter");
613 memset (&cpw_cred, 0, sizeof(cpw_cred));
615 ret = krb5_get_init_creds_opt_alloc(context, &options);
616 if (ret)
617 return ret;
618 krb5_get_init_creds_opt_set_tkt_life (options, 60);
619 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
620 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
621 if (old_options &&
622 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
623 krb5_get_init_creds_opt_set_preauth_list(options,
624 old_options->preauth_list,
625 old_options->preauth_list_length);
626 if (old_options &&
627 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
628 krb5_get_init_creds_opt_set_change_password_prompt(options,
629 old_options->change_password_prompt);
631 krb5_data_zero (&result_code_string);
632 krb5_data_zero (&result_string);
634 ret = krb5_get_init_creds_password (context,
635 &cpw_cred,
636 client,
637 password,
638 prompter,
639 data,
641 "kadmin/changepw",
642 options);
643 krb5_get_init_creds_opt_free(context, options);
644 if (ret)
645 goto out;
647 for(;;) {
648 password_data[0].data = buf1;
649 password_data[0].length = sizeof(buf1);
651 prompts[0].hidden = 1;
652 prompts[0].prompt = "New password: ";
653 prompts[0].reply = &password_data[0];
654 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
656 password_data[1].data = buf2;
657 password_data[1].length = sizeof(buf2);
659 prompts[1].hidden = 1;
660 prompts[1].prompt = "Repeat new password: ";
661 prompts[1].reply = &password_data[1];
662 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
664 ret = (*prompter) (context, data, NULL, "Changing password",
665 2, prompts);
666 if (ret) {
667 memset (buf1, 0, sizeof(buf1));
668 memset (buf2, 0, sizeof(buf2));
669 goto out;
672 if (strcmp (buf1, buf2) == 0)
673 break;
674 memset (buf1, 0, sizeof(buf1));
675 memset (buf2, 0, sizeof(buf2));
678 ret = krb5_set_password (context,
679 &cpw_cred,
680 buf1,
681 client,
682 &result_code,
683 &result_code_string,
684 &result_string);
685 if (ret)
686 goto out;
688 if (asprintf(&p, "%s: %.*s\n",
689 result_code ? "Error" : "Success",
690 (int)result_string.length,
691 result_string.length > 0 ? (char*)result_string.data : "") < 0)
693 ret = krb5_enomem(context);
694 goto out;
697 /* return the result */
698 (*prompter) (context, data, NULL, p, 0, NULL);
700 if (result_code == 0) {
701 strlcpy (newpw, buf1, newpw_sz);
702 ret = 0;
703 } else {
704 krb5_set_error_message(context, ret = KRB5_CHPW_FAIL,
705 N_("failed changing password: %s", ""), p);
707 free (p);
709 out:
710 memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
711 memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
712 krb5_data_free (&result_string);
713 krb5_data_free (&result_code_string);
714 krb5_free_cred_contents (context, &cpw_cred);
715 return ret;
719 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
720 krb5_keyblock_key_proc (krb5_context context,
721 krb5_keytype type,
722 krb5_data *salt,
723 krb5_const_pointer keyseed,
724 krb5_keyblock **key)
726 return krb5_copy_keyblock (context, keyseed, key);
733 static krb5_error_code
734 init_as_req (krb5_context context,
735 KDCOptions opts,
736 const krb5_creds *creds,
737 const krb5_addresses *addrs,
738 const krb5_enctype *etypes,
739 AS_REQ *a)
741 krb5_error_code ret;
743 memset(a, 0, sizeof(*a));
745 a->pvno = 5;
746 a->msg_type = krb_as_req;
747 a->req_body.kdc_options = opts;
748 a->req_body.cname = calloc(1, sizeof(*a->req_body.cname));
749 if (a->req_body.cname == NULL) {
750 ret = krb5_enomem(context);
751 goto fail;
753 a->req_body.sname = calloc(1, sizeof(*a->req_body.sname));
754 if (a->req_body.sname == NULL) {
755 ret = krb5_enomem(context);
756 goto fail;
759 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
760 if (ret)
761 goto fail;
762 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
763 if (ret)
764 goto fail;
766 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
767 if (ret)
768 goto fail;
770 if(creds->times.starttime) {
771 a->req_body.from = malloc(sizeof(*a->req_body.from));
772 if (a->req_body.from == NULL) {
773 ret = krb5_enomem(context);
774 goto fail;
776 *a->req_body.from = creds->times.starttime;
778 if(creds->times.endtime){
779 if ((ALLOC(a->req_body.till, 1)) != NULL)
780 *a->req_body.till = creds->times.endtime;
781 else {
782 ret = krb5_enomem(context);
783 goto fail;
786 if(creds->times.renew_till){
787 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
788 if (a->req_body.rtime == NULL) {
789 ret = krb5_enomem(context);
790 goto fail;
792 *a->req_body.rtime = creds->times.renew_till;
794 a->req_body.nonce = 0;
795 ret = _krb5_init_etype(context,
796 KRB5_PDU_AS_REQUEST,
797 &a->req_body.etype.len,
798 &a->req_body.etype.val,
799 etypes);
800 if (ret)
801 goto fail;
804 * This means no addresses
807 if (addrs && addrs->len == 0) {
808 a->req_body.addresses = NULL;
809 } else {
810 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
811 if (a->req_body.addresses == NULL) {
812 ret = krb5_enomem(context);
813 goto fail;
816 if (addrs)
817 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
818 else {
819 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
820 if(ret == 0 && a->req_body.addresses->len == 0) {
821 free(a->req_body.addresses);
822 a->req_body.addresses = NULL;
825 if (ret)
826 goto fail;
829 a->req_body.enc_authorization_data = NULL;
830 a->req_body.additional_tickets = NULL;
832 a->padata = NULL;
834 return 0;
835 fail:
836 free_AS_REQ(a);
837 memset_s(a, sizeof(*a), 0, sizeof(*a));
838 return ret;
842 static krb5_error_code
843 set_paid(struct pa_info_data *paid, krb5_context context,
844 krb5_enctype etype,
845 krb5_salttype salttype, void *salt_string, size_t salt_len,
846 krb5_data *s2kparams)
848 paid->etype = etype;
849 paid->salt.salttype = salttype;
850 paid->salt.saltvalue.data = malloc(salt_len + 1);
851 if (paid->salt.saltvalue.data == NULL) {
852 krb5_clear_error_message(context);
853 return krb5_enomem(context);
855 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
856 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
857 paid->salt.saltvalue.length = salt_len;
858 if (s2kparams) {
859 krb5_error_code ret;
861 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
862 if (ret) {
863 krb5_clear_error_message(context);
864 krb5_free_salt(context, paid->salt);
865 return ret;
867 } else
868 paid->s2kparams = NULL;
870 return 0;
873 static struct pa_info_data *
874 pa_etype_info2(krb5_context context,
875 const krb5_principal client,
876 const AS_REQ *asreq,
877 struct pa_info_data *paid,
878 heim_octet_string *data)
880 krb5_error_code ret;
881 ETYPE_INFO2 e;
882 size_t sz;
883 size_t i, j;
885 memset(&e, 0, sizeof(e));
886 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
887 if (ret)
888 goto out;
889 if (e.len == 0)
890 goto out;
891 for (j = 0; j < asreq->req_body.etype.len; j++) {
892 for (i = 0; i < e.len; i++) {
894 if (krb5_enctype_valid(context, e.val[i].etype) != 0)
895 continue;
897 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
898 krb5_salt salt;
899 if (e.val[i].salt == NULL)
900 ret = krb5_get_pw_salt(context, client, &salt);
901 else {
902 salt.saltvalue.data = *e.val[i].salt;
903 salt.saltvalue.length = strlen(*e.val[i].salt);
904 ret = 0;
906 if (ret == 0)
907 ret = set_paid(paid, context, e.val[i].etype,
908 KRB5_PW_SALT,
909 salt.saltvalue.data,
910 salt.saltvalue.length,
911 e.val[i].s2kparams);
912 if (e.val[i].salt == NULL)
913 krb5_free_salt(context, salt);
914 if (ret == 0) {
915 free_ETYPE_INFO2(&e);
916 return paid;
921 out:
922 free_ETYPE_INFO2(&e);
923 return NULL;
926 static struct pa_info_data *
927 pa_etype_info(krb5_context context,
928 const krb5_principal client,
929 const AS_REQ *asreq,
930 struct pa_info_data *paid,
931 heim_octet_string *data)
933 krb5_error_code ret;
934 ETYPE_INFO e;
935 size_t sz;
936 size_t i, j;
938 memset(&e, 0, sizeof(e));
939 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
940 if (ret)
941 goto out;
942 if (e.len == 0)
943 goto out;
944 for (j = 0; j < asreq->req_body.etype.len; j++) {
945 for (i = 0; i < e.len; i++) {
947 if (krb5_enctype_valid(context, e.val[i].etype) != 0)
948 continue;
950 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
951 krb5_salt salt;
952 salt.salttype = KRB5_PW_SALT;
953 if (e.val[i].salt == NULL)
954 ret = krb5_get_pw_salt(context, client, &salt);
955 else {
956 salt.saltvalue = *e.val[i].salt;
957 ret = 0;
959 if (e.val[i].salttype)
960 salt.salttype = *e.val[i].salttype;
961 if (ret == 0) {
962 ret = set_paid(paid, context, e.val[i].etype,
963 salt.salttype,
964 salt.saltvalue.data,
965 salt.saltvalue.length,
966 NULL);
967 if (e.val[i].salt == NULL)
968 krb5_free_salt(context, salt);
970 if (ret == 0) {
971 free_ETYPE_INFO(&e);
972 return paid;
977 out:
978 free_ETYPE_INFO(&e);
979 return NULL;
982 static struct pa_info_data *
983 pa_pw_or_afs3_salt(krb5_context context,
984 const krb5_principal client,
985 const AS_REQ *asreq,
986 struct pa_info_data *paid,
987 heim_octet_string *data)
989 krb5_error_code ret;
990 if (paid->etype == KRB5_ENCTYPE_NULL)
991 return NULL;
992 if (krb5_enctype_valid(context, paid->etype) != 0)
993 return NULL;
995 ret = set_paid(paid, context,
996 paid->etype,
997 paid->salt.salttype,
998 data->data,
999 data->length,
1000 NULL);
1001 if (ret)
1002 return NULL;
1003 return paid;
1007 static krb5_error_code
1008 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
1009 krb5_enctype etype, krb5_keyblock *key)
1011 PA_ENC_TS_ENC p;
1012 unsigned char *buf;
1013 size_t buf_size;
1014 size_t len = 0;
1015 EncryptedData encdata;
1016 krb5_error_code ret;
1017 int32_t usec;
1018 int usec2;
1019 krb5_crypto crypto;
1021 krb5_us_timeofday (context, &p.patimestamp, &usec);
1022 usec2 = usec;
1023 p.pausec = &usec2;
1025 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1026 if (ret)
1027 return ret;
1028 if(buf_size != len)
1029 krb5_abortx(context, "internal error in ASN.1 encoder");
1031 ret = krb5_crypto_init(context, key, 0, &crypto);
1032 if (ret) {
1033 free(buf);
1034 return ret;
1036 ret = krb5_encrypt_EncryptedData(context,
1037 crypto,
1038 KRB5_KU_PA_ENC_TIMESTAMP,
1039 buf,
1040 len,
1042 &encdata);
1043 free(buf);
1044 krb5_crypto_destroy(context, crypto);
1045 if (ret)
1046 return ret;
1048 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1049 free_EncryptedData(&encdata);
1050 if (ret)
1051 return ret;
1052 if(buf_size != len)
1053 krb5_abortx(context, "internal error in ASN.1 encoder");
1055 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1056 if (ret)
1057 free(buf);
1058 return ret;
1061 static krb5_error_code
1062 add_enc_ts_padata(krb5_context context,
1063 METHOD_DATA *md,
1064 krb5_principal client,
1065 krb5_s2k_proc keyproc,
1066 krb5_const_pointer keyseed,
1067 krb5_enctype *enctypes,
1068 unsigned netypes,
1069 krb5_salt *salt,
1070 krb5_data *s2kparams)
1072 krb5_error_code ret;
1073 krb5_salt salt2;
1074 krb5_enctype *ep;
1075 size_t i;
1077 memset(&salt2, 0, sizeof(salt2));
1079 if(salt == NULL) {
1080 /* default to standard salt */
1081 ret = krb5_get_pw_salt (context, client, &salt2);
1082 if (ret)
1083 return ret;
1084 salt = &salt2;
1086 if (!enctypes) {
1087 enctypes = context->etypes;
1088 netypes = 0;
1089 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1090 netypes++;
1093 for (i = 0; i < netypes; ++i) {
1094 krb5_keyblock *key;
1096 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1098 ret = (*keyproc)(context, enctypes[i], keyseed,
1099 *salt, s2kparams, &key);
1100 if (ret)
1101 continue;
1102 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1103 krb5_free_keyblock (context, key);
1104 if (ret)
1105 return ret;
1107 if(salt == &salt2)
1108 krb5_free_salt(context, salt2);
1109 return 0;
1112 static krb5_error_code
1113 pa_data_to_md_ts_enc(krb5_context context,
1114 const AS_REQ *a,
1115 const krb5_principal client,
1116 krb5_init_creds_context ctx,
1117 struct pa_info_data *ppaid,
1118 METHOD_DATA *md)
1120 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1121 return 0;
1123 if (ppaid) {
1124 add_enc_ts_padata(context, md, client,
1125 ctx->keyproc, ctx->keyseed,
1126 &ppaid->etype, 1,
1127 &ppaid->salt, ppaid->s2kparams);
1128 } else {
1129 krb5_salt salt;
1131 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1133 /* make a v5 salted pa-data */
1134 add_enc_ts_padata(context, md, client,
1135 ctx->keyproc, ctx->keyseed,
1136 a->req_body.etype.val, a->req_body.etype.len,
1137 NULL, NULL);
1139 /* make a v4 salted pa-data */
1140 salt.salttype = KRB5_PW_SALT;
1141 krb5_data_zero(&salt.saltvalue);
1142 add_enc_ts_padata(context, md, client,
1143 ctx->keyproc, ctx->keyseed,
1144 a->req_body.etype.val, a->req_body.etype.len,
1145 &salt, NULL);
1147 return 0;
1150 static krb5_error_code
1151 pa_data_to_key_plain(krb5_context context,
1152 const krb5_principal client,
1153 krb5_init_creds_context ctx,
1154 krb5_salt salt,
1155 krb5_data *s2kparams,
1156 krb5_enctype etype,
1157 krb5_keyblock **key)
1159 krb5_error_code ret;
1161 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1162 salt, s2kparams, key);
1163 return ret;
1166 struct pkinit_context {
1167 unsigned int win2k : 1;
1168 unsigned int used_pkinit : 1;
1172 static krb5_error_code
1173 pa_data_to_md_pkinit(krb5_context context,
1174 const AS_REQ *a,
1175 const krb5_principal client,
1176 int win2k,
1177 krb5_init_creds_context ctx,
1178 METHOD_DATA *md)
1180 if (ctx->pk_init_ctx == NULL)
1181 return 0;
1182 #ifdef PKINIT
1183 return _krb5_pk_mk_padata(context,
1184 ctx->pk_init_ctx,
1185 ctx->ic_flags,
1186 win2k,
1187 &a->req_body,
1188 ctx->pk_nonce,
1189 md);
1190 #else
1191 krb5_set_error_message(context, EINVAL,
1192 N_("no support for PKINIT compiled in", ""));
1193 return EINVAL;
1194 #endif
1197 static krb5_error_code
1198 pkinit_configure_ietf(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1200 struct pkinit_context *pkinit_ctx = pa_ctx;
1202 pkinit_ctx->win2k = 0;
1204 if (ctx->pk_init_ctx == NULL)
1205 return HEIM_ERR_PA_CANT_CONTINUE;
1207 return 0;
1210 static krb5_error_code
1211 pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1213 struct pkinit_context *pkinit_ctx = pa_ctx;
1215 pkinit_ctx->win2k = 1;
1216 pkinit_ctx->used_pkinit = 0;
1218 if (ctx->pk_init_ctx == NULL)
1219 return HEIM_ERR_PA_CANT_CONTINUE;
1221 return 0;
1224 static krb5_error_code
1225 pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1226 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md)
1228 krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
1229 struct pkinit_context *pkinit_ctx = pa_ctx;
1231 if (rep == NULL) {
1232 if (pkinit_ctx->used_pkinit) {
1233 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1234 "Already tried PKINIT(%s), looping",
1235 pkinit_ctx->win2k ? "win2k" : "ietf");
1236 } else {
1237 ret = pa_data_to_md_pkinit(context, a, ctx->cred.client,
1238 (pkinit_ctx->win2k != 0),
1239 ctx, out_md);
1240 if (ret == 0)
1241 ret = HEIM_ERR_PA_CONTINUE_NEEDED;
1243 pkinit_ctx->used_pkinit = 1;
1245 } else if (pa) {
1246 ret = _krb5_pk_rd_pa_reply(context,
1247 a->req_body.realm,
1248 ctx->pk_init_ctx,
1249 rep->enc_part.etype,
1251 ctx->pk_nonce,
1252 &ctx->req_buffer,
1254 &ctx->fast_state.reply_key);
1255 if (ret == 0)
1256 ctx->runflags.allow_save_as_reply_key = 1;
1259 return ret;
1262 static void
1263 pkinit_release(void *pa_ctx)
1268 * GSS-API pre-authentication support
1271 struct pa_gss_context {
1272 struct gss_ctx_id_t_desc_struct *context_handle;
1273 int open;
1276 static krb5_error_code
1277 pa_gss_configure(krb5_context context,
1278 krb5_init_creds_context ctx,
1279 void *pa_ctx)
1281 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1282 struct pa_gss_context *pa_gss_ctx = pa_ctx;
1284 if (gssic == NULL)
1285 return HEIM_ERR_PA_CANT_CONTINUE;
1287 pa_gss_ctx->context_handle = NULL;
1288 pa_gss_ctx->open = 0;
1290 return 0;
1293 static krb5_error_code
1294 pa_data_to_md_gss(krb5_context context,
1295 const AS_REQ *a,
1296 const krb5_creds *creds,
1297 krb5_init_creds_context ctx,
1298 struct pa_gss_context *pa_gss_ctx,
1299 PA_DATA *pa,
1300 METHOD_DATA *out_md)
1302 krb5_error_code ret;
1303 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1304 krb5_data req_body;
1305 krb5_data *input_token, output_token;
1306 size_t len = 0;
1308 krb5_data_zero(&req_body);
1309 krb5_data_zero(&output_token);
1311 input_token = pa ? &pa->padata_value : NULL;
1313 if ((input_token == NULL || input_token->length == 0) &&
1314 pa_gss_ctx->context_handle) {
1315 krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1316 "Missing GSS preauthentication data from KDC");
1317 return HEIM_ERR_PA_CANT_CONTINUE;
1320 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
1321 &ctx->as_req.req_body, &len, ret);
1322 if (ret)
1323 goto out;
1324 heim_assert(req_body.length == len, "ASN.1 internal error");
1326 ret = gssic->step(context, gssic, creds, &pa_gss_ctx->context_handle,
1327 ctx->flags, &req_body,
1328 input_token, &output_token);
1331 * If FAST authenticated the KDC (which will be the case unless anonymous
1332 * PKINIT was used without KDC certificate validation) then we can relax
1333 * the mutual authentication requirement.
1335 if (ret == KRB5_MUTUAL_FAILED &&
1336 (ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1337 (ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
1338 ret = 0;
1339 if (ret == 0) {
1341 * Always require a strengthen key if FAST was used, to avoid a MITM
1342 * attack that could result in unintended privilege escalation should
1343 * the KDC add positive authorization data from the armor ticket.
1345 if ((ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1346 ctx->fast_state.strengthen_key == NULL) {
1347 krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1348 "FAST GSS pre-authentication without strengthen key");
1349 ret = KRB5_KDCREP_MODIFIED;
1350 goto out;
1353 pa_gss_ctx->open = 1;
1356 if (output_token.length) {
1357 ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
1358 output_token.data, output_token.length);
1359 if (ret)
1360 goto out;
1362 krb5_data_zero(&output_token);
1365 out:
1366 krb5_data_free(&output_token);
1367 krb5_data_free(&req_body);
1369 return ret;
1372 static krb5_error_code
1373 pa_gss_step(krb5_context context,
1374 krb5_init_creds_context ctx,
1375 void *pa_ctx,
1376 PA_DATA *pa,
1377 const AS_REQ *a,
1378 const AS_REP *rep,
1379 const krb5_krbhst_info *hi,
1380 METHOD_DATA *in_md,
1381 METHOD_DATA *out_md)
1383 krb5_error_code ret;
1384 krb5_principal cname;
1385 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1386 struct pa_gss_context *pa_gss_ctx = pa_ctx;
1388 heim_assert(gssic != NULL, "invalid context passed to pa_gss_step");
1390 if (!pa_gss_ctx->open) {
1391 ret = pa_data_to_md_gss(context, a, &ctx->cred, ctx,
1392 pa_gss_ctx, pa, out_md);
1393 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED && rep) {
1394 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1395 "KDC sent AS-REP before GSS "
1396 "pre-authentication completed");
1397 ret = KRB5_KDCREP_MODIFIED;
1398 } else if (ret == 0 && rep == NULL) {
1399 ret = HEIM_ERR_PA_CONTINUE_NEEDED; /* odd number of legs */
1401 if (ret)
1402 return ret;
1403 } else if (pa && pa->padata_value.length) {
1404 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1405 "Already completed GSS pre-authentication");
1406 return KRB5_GET_IN_TKT_LOOP;
1407 } else if (rep == NULL) {
1408 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1409 "Completed GSS pre-authentication before KDC");
1410 return KRB5_PREAUTH_FAILED;
1413 heim_assert(pa_gss_ctx->open,
1414 "GSS pre-authentication incomplete");
1416 ret = gssic->finish(context, gssic, &ctx->cred,
1417 pa_gss_ctx->context_handle, ctx->nonce,
1418 rep->enc_part.etype, &cname,
1419 &ctx->fast_state.reply_key);
1420 if (ret)
1421 return ret;
1424 char *from = NULL;
1425 char *to = NULL;
1427 if (krb5_unparse_name(context, ctx->cred.client, &from) == 0 &&
1428 krb5_unparse_name(context, cname, &to) == 0) {
1429 _krb5_debug(context, 1, "pa_gss_step: %s as %s",
1430 from, to);
1431 krb5_xfree(from);
1432 krb5_xfree(to);
1436 if (krb5_principal_is_federated(context, ctx->cred.client)) {
1438 * The well-known federated name will be replaced with the cname
1439 * in the AS-REP, but save the locally mapped initiator name in the
1440 * cred for logging.
1442 krb5_free_principal(context, ctx->cred.client);
1443 ctx->cred.client = cname;
1445 ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
1446 } else {
1447 krb5_free_principal(context, cname);
1450 ctx->runflags.allow_save_as_reply_key = 1;
1452 gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1453 pa_gss_ctx->context_handle = NULL;
1454 pa_gss_ctx->open = 0;
1456 return 0;
1459 static krb5_error_code
1460 pa_gss_restart(krb5_context context,
1461 krb5_init_creds_context ctx,
1462 void *pa_ctx)
1464 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1465 struct pa_gss_context *pa_gss_ctx = pa_ctx;
1467 if (gssic == NULL)
1468 return HEIM_ERR_PA_CANT_CONTINUE;
1470 gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1471 pa_gss_ctx->context_handle = NULL;
1472 pa_gss_ctx->open = 0;
1474 return 0;
1477 static void
1478 pa_gss_release(void *pa_ctx)
1482 krb5_error_code
1483 _krb5_make_pa_enc_challenge(krb5_context context,
1484 krb5_crypto crypto,
1485 krb5_key_usage usage,
1486 METHOD_DATA *md)
1488 PA_ENC_TS_ENC p;
1489 unsigned char *buf;
1490 size_t buf_size;
1491 size_t len = 0;
1492 EncryptedData encdata;
1493 krb5_error_code ret;
1494 int32_t usec;
1495 int usec2;
1497 krb5_us_timeofday (context, &p.patimestamp, &usec);
1498 usec2 = usec;
1499 p.pausec = &usec2;
1501 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1502 if (ret)
1503 return ret;
1504 if(buf_size != len)
1505 krb5_abortx(context, "internal error in ASN.1 encoder");
1507 ret = krb5_encrypt_EncryptedData(context,
1508 crypto,
1509 usage,
1510 buf,
1511 len,
1513 &encdata);
1514 free(buf);
1515 if (ret)
1516 return ret;
1518 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1519 free_EncryptedData(&encdata);
1520 if (ret)
1521 return ret;
1522 if(buf_size != len)
1523 krb5_abortx(context, "internal error in ASN.1 encoder");
1525 ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len);
1526 if (ret)
1527 free(buf);
1528 return ret;
1531 krb5_error_code
1532 _krb5_validate_pa_enc_challenge(krb5_context context,
1533 krb5_crypto crypto,
1534 krb5_key_usage usage,
1535 EncryptedData *enc_data,
1536 const char *peer_name)
1538 krb5_error_code ret;
1539 krb5_data ts_data;
1540 PA_ENC_TS_ENC p;
1541 time_t timestamp;
1542 int32_t usec;
1543 size_t size;
1545 ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data);
1546 if (ret)
1547 return ret;
1549 ret = decode_PA_ENC_TS_ENC(ts_data.data,
1550 ts_data.length,
1552 &size);
1553 krb5_data_free(&ts_data);
1554 if(ret){
1555 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1556 _krb5_debug(context, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name);
1557 goto out;
1560 krb5_us_timeofday(context, &timestamp, &usec);
1562 if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) {
1563 char client_time[100];
1565 krb5_format_time(context, p.patimestamp,
1566 client_time, sizeof(client_time), TRUE);
1568 ret = KRB5KRB_AP_ERR_SKEW;
1569 _krb5_debug(context, 0, "Too large time skew, "
1570 "client time %s is out by %u > %d seconds -- %s",
1571 client_time,
1572 (unsigned)krb5_time_abs(timestamp, p.patimestamp),
1573 (int)context->max_skew,
1574 peer_name);
1575 } else {
1576 ret = 0;
1579 out:
1580 free_PA_ENC_TS_ENC(&p);
1582 return ret;
1586 static struct pa_info_data *
1587 process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *);
1590 static krb5_error_code
1591 enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1592 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md)
1594 struct pa_info_data paid, *ppaid;
1595 krb5_keyblock challengekey;
1596 krb5_data pepper1, pepper2;
1597 krb5_crypto crypto = NULL;
1598 krb5_enctype aenctype;
1599 krb5_error_code ret;
1601 memset(&paid, 0, sizeof(paid));
1603 if (rep == NULL)
1604 paid.etype = KRB5_ENCTYPE_NULL;
1605 else
1606 paid.etype = rep->enc_part.etype;
1607 ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1610 * If we don't have ppaid, ts because the KDC have not sent any
1611 * salt info, lets to the first roundtrip so the KDC have a chance
1612 * to send any.
1614 if (ppaid == NULL) {
1615 _krb5_debug(context, 5, "no ppaid found");
1616 return HEIM_ERR_PA_CONTINUE_NEEDED;
1618 if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1619 return HEIM_ERR_PA_CANT_CONTINUE;
1622 if (ctx->fast_state.reply_key)
1623 krb5_free_keyblock(context, ctx->fast_state.reply_key);
1625 ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1626 ppaid->salt, ppaid->s2kparams, ppaid->etype,
1627 &ctx->fast_state.reply_key);
1628 free_paid(context, &paid);
1629 if (ret) {
1630 _krb5_debug(context, 5, "enc-chal: failed to build key");
1631 return ret;
1634 ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto);
1635 if (ret)
1636 return ret;
1638 krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype);
1640 pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor";
1641 pepper1.length = strlen(pepper1.data);
1642 pepper2.data = "challengelongterm";
1643 pepper2.length = strlen(pepper2.data);
1645 ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto,
1646 &pepper1, &pepper2, aenctype,
1647 &challengekey);
1648 krb5_crypto_destroy(context, crypto);
1649 if (ret)
1650 return ret;
1652 ret = krb5_crypto_init(context, &challengekey, 0, &crypto);
1653 krb5_free_keyblock_contents(context, &challengekey);
1654 if (ret)
1655 return ret;
1657 if (rep) {
1658 EncryptedData enc_data;
1659 size_t size;
1661 if (ret) {
1662 _krb5_debug(context, 5, "enc-chal: failed to create reply key");
1663 return ret;
1666 _krb5_debug(context, 5, "ENC_CHAL rep key");
1668 if (ctx->fast_state.strengthen_key == NULL) {
1669 krb5_crypto_destroy(context, crypto);
1670 _krb5_debug(context, 5, "ENC_CHAL w/o strengthen_key");
1671 return KRB5_KDCREP_MODIFIED;
1674 if (pa == NULL) {
1675 krb5_crypto_destroy(context, crypto);
1676 _krb5_debug(context, 0, "KDC response missing");
1677 return HEIM_ERR_PA_CANT_CONTINUE;
1680 ret = decode_EncryptedData(pa->padata_value.data,
1681 pa->padata_value.length,
1682 &enc_data,
1683 &size);
1684 if (ret) {
1685 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1686 _krb5_debug(context, 5, "Failed to decode ENC_CHAL KDC reply");
1687 return ret;
1690 ret = _krb5_validate_pa_enc_challenge(context, crypto,
1691 KRB5_KU_ENC_CHALLENGE_KDC,
1692 &enc_data,
1693 "KDC");
1694 free_EncryptedData(&enc_data);
1695 krb5_crypto_destroy(context, crypto);
1697 return ret;
1699 } else {
1701 ret = _krb5_make_pa_enc_challenge(context, crypto,
1702 KRB5_KU_ENC_CHALLENGE_CLIENT,
1703 out_md);
1704 krb5_crypto_destroy(context, crypto);
1705 if (ret) {
1706 _krb5_debug(context, 5, "enc-chal: failed build enc challenge");
1707 return ret;
1710 return HEIM_ERR_PA_CONTINUE_NEEDED;
1714 struct enc_ts_context {
1715 int used_pa_types;
1716 #define USED_ENC_TS_GUESS 4
1717 #define USED_ENC_TS_INFO 8
1718 #define USED_ENC_TS_RENEG 16
1719 krb5_principal user;
1722 static krb5_error_code
1723 enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1725 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1726 pactx->used_pa_types = 0;
1727 krb5_free_principal(context, pactx->user);
1728 pactx->user = NULL;
1729 return 0;
1732 static krb5_error_code
1733 enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa,
1734 const AS_REQ *a,
1735 const AS_REP *rep,
1736 const krb5_krbhst_info *hi,
1737 METHOD_DATA *in_md, METHOD_DATA *out_md)
1739 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1740 struct pa_info_data paid, *ppaid;
1741 krb5_error_code ret;
1742 const char *state;
1743 unsigned flag;
1746 * Keep track of the user we used so that we can restart
1747 * authentication when we get referrals.
1750 if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) {
1751 pactx->used_pa_types = 0;
1752 krb5_free_principal(context, pactx->user);
1753 pactx->user = NULL;
1756 if (pactx->user == NULL) {
1757 ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user);
1758 if (ret)
1759 return ret;
1762 memset(&paid, 0, sizeof(paid));
1764 if (rep == NULL)
1765 paid.etype = KRB5_ENCTYPE_NULL;
1766 else
1767 paid.etype = rep->enc_part.etype;
1769 ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1771 if (rep) {
1773 * Some KDC's don't send salt info in the reply when there is
1774 * success pre-auth happned before, so use cached copy (or
1775 * even better, if there is just one pre-auth, save reply-key).
1777 if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) {
1778 ppaid = &ctx->paid;
1780 } else if (ppaid == NULL) {
1781 _krb5_debug(context, 0, "no paid when building key, build a default salt structure ?");
1782 return HEIM_ERR_PA_CANT_CONTINUE;
1785 ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1786 ppaid->salt, ppaid->s2kparams, rep->enc_part.etype,
1787 &ctx->fast_state.reply_key);
1788 free_paid(context, &paid);
1789 return ret;
1793 * If we don't have ppaid, ts because the KDC have not sent any
1794 * salt info, lets to the first roundtrip so the KDC have a chance
1795 * to send any.
1797 * Don't bother guessing, it sounds like a good idea until you run
1798 * into KDCs that are doing failed auth counting based on the
1799 * ENC_TS tries.
1801 * Stashing the salt for the next run is a diffrent issue and
1802 * could be considered in the future.
1805 if (ppaid == NULL) {
1806 _krb5_debug(context, 5,
1807 "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}");
1808 return HEIM_ERR_PA_CONTINUE_NEEDED;
1810 if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1811 free_paid(context, &paid);
1812 _krb5_debug(context, 5,
1813 "TS-ENC: kdc proposes enctype NULL ?");
1814 return HEIM_ERR_PA_CANT_CONTINUE;
1818 * We have to allow the KDC to re-negotiate the PA-TS data
1819 * once, this is since the in the case of a windows read only
1820 * KDC that doesn't have the keys simply guesses what the
1821 * master is supposed to support. In the case where this
1822 * breaks in when the RO-KDC is a newer version the the RW-KDC
1823 * and the RO-KDC announced a enctype that the older doesn't
1824 * support.
1826 if (pactx->used_pa_types & USED_ENC_TS_INFO) {
1827 flag = USED_ENC_TS_RENEG;
1828 state = "reneg";
1829 } else {
1830 flag = USED_ENC_TS_INFO;
1831 state = "info";
1834 if (pactx->used_pa_types & flag) {
1835 free_paid(context, &paid);
1836 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1837 "Already tried ENC-TS-%s, looping", state);
1838 return KRB5_GET_IN_TKT_LOOP;
1841 pactx->used_pa_types |= flag;
1843 free_paid(context, &ctx->paid);
1844 ctx->paid = *ppaid;
1846 ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md);
1847 if (ret)
1848 return ret;
1850 return HEIM_ERR_PA_CONTINUE_NEEDED;
1853 static void
1854 enc_ts_release(void *pa_ctx)
1856 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1858 if (pactx->user)
1859 krb5_free_principal(NULL, pactx->user);
1862 static krb5_error_code
1863 pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1864 const AS_REP *rep, const krb5_krbhst_info *hi,
1865 METHOD_DATA *in_md, METHOD_DATA *out_md)
1867 size_t len = 0, length;
1868 krb5_error_code ret;
1869 PA_PAC_REQUEST req;
1870 void *buf;
1872 switch (ctx->req_pac) {
1873 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1874 return 0; /* don't bother */
1875 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1876 req.include_pac = 1;
1877 break;
1878 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1879 req.include_pac = 0;
1882 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1883 &req, &len, ret);
1884 if (ret)
1885 return ret;
1886 heim_assert(len == length, "internal error in ASN.1 encoder");
1888 ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1889 if (ret)
1890 free(buf);
1892 return 0;
1895 static krb5_error_code
1896 pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1897 const AS_REP *rep, const krb5_krbhst_info *hi,
1898 METHOD_DATA *in_md, METHOD_DATA *out_md)
1900 if (ctx->runflags.allow_enc_pa_rep)
1901 return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1903 return 0;
1906 static krb5_error_code
1907 pa_fx_cookie_step(krb5_context context,
1908 krb5_init_creds_context ctx,
1909 void *pa_ctx,
1910 PA_DATA *pa,
1911 const AS_REQ *a,
1912 const AS_REP *rep,
1913 const krb5_krbhst_info *hi,
1914 METHOD_DATA *in_md,
1915 METHOD_DATA *out_md)
1917 krb5_error_code ret;
1918 void *cookie;
1919 PA_DATA *pad;
1920 int idx = 0;
1922 pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx);
1923 if (pad == NULL) {
1925 * RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
1926 * expects at least one more message from the client.
1928 if (ctx->error.error_code == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
1929 return KRB5_PREAUTH_FAILED;
1930 else
1931 return 0;
1934 cookie = malloc(pad->padata_value.length);
1935 if (cookie == NULL)
1936 return krb5_enomem(context);
1938 memcpy(cookie, pad->padata_value.data, pad->padata_value.length);
1940 ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE,
1941 cookie, pad->padata_value.length);
1942 if (ret)
1943 free(cookie);
1944 else
1945 _krb5_debug(context, 5, "Mirrored FX-COOKIE to KDC");
1947 return ret;
1950 typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *);
1951 typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *);
1952 typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *);
1953 typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, const krb5_krbhst_info *, METHOD_DATA *, METHOD_DATA *);
1954 typedef void (*pa_release_f)(void *);
1956 struct patype {
1957 int type;
1958 char *name;
1959 int flags;
1960 #define PA_F_ANNOUNCE 1
1961 #define PA_F_CONFIG 2
1962 #define PA_F_FAST 4 /* available inside FAST */
1963 #define PA_F_NOT_FAST 8 /* only available without FAST */
1964 size_t pa_ctx_size;
1965 pa_salt_info_f salt_info;
1967 * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL.
1969 pa_configure_f configure;
1971 * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc)
1973 pa_restart_f restart;
1975 * Return 0 if the when complete, HEIM_ERR_PA_CONTINUE_NEEDED if more steps are require
1977 pa_step_f step;
1978 pa_release_f release;
1979 } patypes[] = {
1981 KRB5_PADATA_PK_AS_REP,
1982 "PKINIT(IETF)",
1983 PA_F_FAST | PA_F_NOT_FAST,
1984 sizeof(struct pkinit_context),
1985 NULL,
1986 pkinit_configure_ietf,
1987 NULL,
1988 pkinit_step,
1989 pkinit_release
1992 KRB5_PADATA_PK_AS_REP_19,
1993 "PKINIT(win)",
1994 PA_F_FAST | PA_F_NOT_FAST,
1995 sizeof(struct pkinit_context),
1996 NULL,
1997 pkinit_configure_win,
1998 NULL,
1999 pkinit_step,
2000 pkinit_release
2003 KRB5_PADATA_GSS,
2004 "GSS",
2005 PA_F_FAST | PA_F_NOT_FAST,
2006 sizeof(struct pa_gss_context),
2007 NULL,
2008 pa_gss_configure,
2009 pa_gss_restart,
2010 pa_gss_step,
2011 pa_gss_release
2014 KRB5_PADATA_ENCRYPTED_CHALLENGE,
2015 "ENCRYPTED_CHALLENGE",
2016 PA_F_FAST,
2018 NULL,
2019 NULL,
2020 NULL,
2021 enc_chal_step,
2022 NULL
2025 KRB5_PADATA_ENC_TIMESTAMP,
2026 "ENCRYPTED_TIMESTAMP",
2027 PA_F_NOT_FAST,
2028 sizeof(struct enc_ts_context),
2029 NULL,
2030 NULL,
2031 enc_ts_restart,
2032 enc_ts_step,
2033 enc_ts_release
2036 KRB5_PADATA_PA_PAC_REQUEST,
2037 "PA_PAC_REQUEST",
2038 PA_F_CONFIG,
2040 NULL,
2041 NULL,
2042 NULL,
2043 pa_pac_step,
2044 NULL
2047 KRB5_PADATA_REQ_ENC_PA_REP,
2048 "REQ-ENC-PA-REP",
2049 PA_F_CONFIG,
2051 NULL,
2052 NULL,
2053 NULL,
2054 pa_enc_pa_rep_step,
2055 NULL
2058 KRB5_PADATA_FX_COOKIE,
2059 "FX-COOKIE",
2060 PA_F_CONFIG,
2062 NULL,
2063 NULL,
2064 NULL,
2065 pa_fx_cookie_step,
2066 NULL
2068 #define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL }
2069 patype_salt(ETYPE_INFO2, pa_etype_info2),
2070 patype_salt(ETYPE_INFO, pa_etype_info),
2071 patype_salt(PW_SALT, pa_pw_or_afs3_salt),
2072 patype_salt(AFS3_SALT, pa_pw_or_afs3_salt),
2073 #undef patype_salt
2074 /* below are just for pretty printing */
2075 #define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL }
2076 patype_info(AUTHENTICATION_SET),
2077 patype_info(AUTH_SET_SELECTED),
2078 patype_info(FX_FAST),
2079 patype_info(FX_ERROR),
2080 patype_info(PKINIT_KX),
2081 patype_info(PK_AS_REQ)
2082 #undef patype_info
2085 static const char *
2086 get_pa_type_name(int type)
2088 size_t n;
2089 for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++)
2090 if (type == patypes[n].type)
2091 return patypes[n].name;
2092 return "unknown";
2099 struct pa_auth_mech {
2100 struct patype *patype;
2101 struct pa_auth_mech *next; /* when doing authentication sets */
2102 char pactx[1];
2109 static struct pa_info_data *
2110 process_pa_info(krb5_context context,
2111 const krb5_principal client,
2112 const AS_REQ *asreq,
2113 struct pa_info_data *paid,
2114 METHOD_DATA *md)
2116 struct pa_info_data *p = NULL;
2117 PA_DATA *pa;
2118 size_t i;
2120 if (md == NULL)
2121 return NULL;
2123 for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) {
2124 int idx = 0;
2126 if (patypes[i].salt_info == NULL)
2127 continue;
2129 pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx);
2130 if (pa == NULL)
2131 continue;
2133 paid->salt.salttype = (krb5_salttype)patypes[i].type;
2134 p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value);
2136 return p;
2139 static void
2140 pa_announce(krb5_context context,
2141 int types,
2142 krb5_init_creds_context ctx,
2143 METHOD_DATA *in_md,
2144 METHOD_DATA *out_md)
2146 size_t n;
2148 for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2149 if ((patypes[n].flags & types) == 0)
2150 continue;
2152 if (patypes[n].step)
2153 patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, NULL, in_md, out_md);
2154 else
2155 krb5_padata_add(context, out_md, patypes[n].type, NULL, 0);
2160 static void
2161 mech_dealloc(void *ctx)
2163 struct pa_auth_mech *pa_mech = ctx;
2164 if (pa_mech->patype->release)
2165 pa_mech->patype->release((void *)&pa_mech->pactx[0]);
2168 struct heim_type_data pa_auth_mech_object = {
2169 HEIM_TID_PA_AUTH_MECH,
2170 "heim-pa-mech-context",
2171 NULL,
2172 mech_dealloc,
2173 NULL,
2174 NULL,
2175 NULL,
2176 NULL
2179 static struct pa_auth_mech *
2180 pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2182 struct pa_auth_mech *pa_mech;
2183 struct patype *patype = NULL;
2184 size_t n;
2186 for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2187 if (patypes[n].type == pa_type)
2188 patype = &patypes[n];
2190 if (patype == NULL)
2191 return NULL;
2193 pa_mech = _heim_alloc_object(&pa_auth_mech_object, sizeof(*pa_mech) - 1 + patype->pa_ctx_size);
2194 if (pa_mech == NULL)
2195 return NULL;
2197 pa_mech->patype = patype;
2199 if (pa_mech->patype->configure) {
2200 krb5_error_code ret;
2202 ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]);
2203 if (ret) {
2204 heim_release(pa_mech);
2205 return NULL;
2209 _krb5_debug(context, 5, "Adding PA mech: %s", patype->name);
2211 return pa_mech;
2214 static void
2215 pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2217 struct pa_auth_mech *mech;
2219 mech = pa_mech_create(context, ctx, pa_type);
2220 if (mech) {
2221 heim_array_append_value(ctx->available_pa_mechs, mech);
2222 heim_release(mech);
2226 static krb5_error_code
2227 pa_configure(krb5_context context,
2228 krb5_init_creds_context ctx,
2229 METHOD_DATA *in_md)
2231 ctx->available_pa_mechs = heim_array_create();
2233 if (ctx->gss_init_ctx) {
2234 pa_mech_add(context, ctx, KRB5_PADATA_GSS);
2235 } else if (ctx->pk_init_ctx) {
2236 pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP);
2237 pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19);
2238 } else if (ctx->keyproc || ctx->keyseed || ctx->prompter) {
2239 pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE);
2240 pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP);
2242 /* XXX setup context based on KDC reply */
2244 return 0;
2247 static krb5_error_code
2248 pa_restart(krb5_context context,
2249 krb5_init_creds_context ctx)
2251 krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
2253 if (ctx->pa_mech && ctx->pa_mech->patype->restart)
2254 ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]);
2256 return ret;
2260 static krb5_error_code
2261 pa_step(krb5_context context,
2262 krb5_init_creds_context ctx,
2263 const AS_REQ *a,
2264 const AS_REP *rep,
2265 const krb5_krbhst_info *hi,
2266 METHOD_DATA *in_md,
2267 METHOD_DATA *out_md)
2269 krb5_error_code ret;
2270 PA_DATA *pa = NULL;
2271 int idx;
2273 next:
2274 do {
2275 if (ctx->pa_mech == NULL) {
2276 size_t len = heim_array_get_length(ctx->available_pa_mechs);
2277 if (len == 0) {
2278 _krb5_debug(context, 0, "no more available_pa_mechs to try");
2279 return HEIM_ERR_NO_MORE_PA_MECHS;
2282 ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0);
2283 heim_array_delete_value(ctx->available_pa_mechs, 0);
2286 if (ctx->fast_state.armor_crypto) {
2287 if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) {
2288 _krb5_debug(context, 0, "pa-mech %s dropped under FAST (not supported)",
2289 ctx->pa_mech->patype->name);
2290 heim_release(ctx->pa_mech);
2291 ctx->pa_mech = NULL;
2292 continue;
2294 } else {
2295 if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) {
2296 _krb5_debug(context, 0, "dropped pa-mech %s since not running under FAST",
2297 ctx->pa_mech->patype->name);
2298 heim_release(ctx->pa_mech);
2299 ctx->pa_mech = NULL;
2300 continue;
2304 _krb5_debug(context, 0, "pa-mech trying: %s, searching for %d",
2305 ctx->pa_mech->patype->name, ctx->pa_mech->patype->type);
2307 idx = 0;
2308 if (in_md)
2309 pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx);
2310 else
2311 pa = NULL;
2313 } while (ctx->pa_mech == NULL);
2315 _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name);
2317 ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, hi, in_md, out_md);
2318 _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret);
2319 if (ret == 0) {
2320 struct pa_auth_mech *next_pa = ctx->pa_mech->next;
2322 if (next_pa) {
2323 _krb5_debug(context, 5, "Next PA type in set is: %s",
2324 next_pa->patype->name);
2325 ret = HEIM_ERR_PA_CONTINUE_NEEDED;
2326 } else if (rep == NULL) {
2327 _krb5_debug(context, 5, "PA %s done, but no ticket in sight!!!",
2328 ctx->pa_mech->patype->name);
2329 ret = HEIM_ERR_PA_CANT_CONTINUE;
2330 } else {
2331 ctx->pa_used = ctx->pa_mech->patype->name;
2334 heim_retain(next_pa);
2335 heim_release(ctx->pa_mech);
2336 ctx->pa_mech = next_pa;
2339 if (ret == HEIM_ERR_PA_CANT_CONTINUE) {
2340 if (ctx->pa_mech) {
2341 _krb5_debug(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name);
2342 heim_release(ctx->pa_mech);
2343 ctx->pa_mech = NULL;
2345 goto next;
2346 } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2347 _krb5_debug(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name);
2348 } else if (ret != 0) {
2349 _krb5_debug(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret);
2350 heim_release(ctx->pa_mech);
2351 ctx->pa_mech = NULL;
2354 return ret;
2357 static void
2358 log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md)
2360 if (_krb5_have_debug(context, 5)) {
2361 unsigned i;
2362 _krb5_debug(context, 5, "KDC sent %d patypes", in_md->len);
2363 for (i = 0; i < in_md->len; i++)
2364 _krb5_debug(context, 5, "KDC sent PA-DATA type: %d (%s)",
2365 in_md->val[i].padata_type,
2366 get_pa_type_name(in_md->val[i].padata_type));
2371 * Assumes caller always will free `out_md', even on error.
2374 static krb5_error_code
2375 process_pa_data_to_md(krb5_context context,
2376 const krb5_creds *creds,
2377 const AS_REQ *a,
2378 krb5_init_creds_context ctx,
2379 METHOD_DATA *in_md,
2380 METHOD_DATA **out_md)
2382 krb5_error_code ret;
2384 ALLOC(*out_md, 1);
2385 if (*out_md == NULL) {
2386 return krb5_enomem(context);
2388 (*out_md)->len = 0;
2389 (*out_md)->val = NULL;
2391 log_kdc_pa_types(context, in_md);
2393 ret = pa_step(context, ctx, a, NULL, NULL, in_md, *out_md);
2394 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2395 _krb5_debug(context, 0, "pamech need more stepping");
2396 } else if (ret == 0) {
2397 _krb5_debug(context, 0, "pamech done step");
2398 } else {
2399 return ret;
2403 * Send announcement (what we support) and configuration (user
2404 * introduced behavior change)
2407 pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md);
2413 if ((*out_md)->len == 0) {
2414 free(*out_md);
2415 *out_md = NULL;
2418 return 0;
2421 static krb5_error_code
2422 process_pa_data_to_key(krb5_context context,
2423 krb5_init_creds_context ctx,
2424 krb5_creds *creds,
2425 AS_REQ *a,
2426 AS_REP *rep,
2427 const krb5_krbhst_info *hi,
2428 krb5_keyblock **key)
2430 struct pa_info_data paid, *ppaid = NULL;
2431 krb5_error_code ret;
2432 krb5_enctype etype = rep->enc_part.etype;
2434 memset(&paid, 0, sizeof(paid));
2436 if (rep->padata)
2437 log_kdc_pa_types(context, rep->padata);
2439 if (rep->padata) {
2440 paid.etype = etype;
2441 ppaid = process_pa_info(context, creds->client, a, &paid,
2442 rep->padata);
2444 if (ppaid == NULL) {
2445 if (ctx->paid.etype == KRB5_ENCTYPE_NULL) {
2446 ctx->paid.etype = etype;
2447 ctx->paid.s2kparams = NULL;
2448 ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt);
2449 if (ret)
2450 return ret;
2454 ret = pa_step(context, ctx, a, rep, hi, rep->padata, NULL);
2455 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2456 _krb5_debug(context, 0, "In final stretch and pa require more stepping ?");
2457 return ret;
2458 } else if (ret == 0) {
2459 _krb5_debug(context, 0, "final pamech done step");
2460 goto out;
2461 } else {
2462 return ret;
2464 out:
2465 free_paid(context, &paid);
2466 return ret;
2473 static krb5_error_code
2474 capture_lkdc_domain(krb5_context context,
2475 krb5_init_creds_context ctx)
2477 size_t len;
2479 len = strlen(_krb5_wellknown_lkdc);
2481 if (ctx->kdc_hostname != NULL ||
2482 strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 ||
2483 ctx->cred.client->realm[len] != ':')
2484 return 0;
2486 ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]);
2488 _krb5_debug(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s",
2489 ctx->kdc_hostname);
2490 return 0;
2494 * Start a new context to get a new initial credential.
2496 * @param context A Kerberos 5 context.
2497 * @param client The Kerberos principal to get the credential for, if
2498 * NULL is given, the default principal is used as determined by
2499 * krb5_get_default_principal().
2500 * @param prompter
2501 * @param prompter_data
2502 * @param start_time the time the ticket should start to be valid or 0 for now.
2503 * @param options a options structure, can be NULL for default options.
2504 * @param rctx A new allocated free with krb5_init_creds_free().
2506 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
2508 * @ingroup krb5_credential
2511 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2512 krb5_init_creds_init(krb5_context context,
2513 krb5_principal client,
2514 krb5_prompter_fct prompter,
2515 void *prompter_data,
2516 krb5_deltat start_time,
2517 krb5_get_init_creds_opt *options,
2518 krb5_init_creds_context *rctx)
2520 krb5_init_creds_context ctx;
2521 krb5_error_code ret;
2523 *rctx = NULL;
2525 ctx = calloc(1, sizeof(*ctx));
2526 if (ctx == NULL)
2527 return krb5_enomem(context);
2529 ret = get_init_creds_common(context, client, prompter, prompter_data,
2530 start_time, options, ctx);
2531 if (ret) {
2532 free(ctx);
2533 return ret;
2536 /* Set a new nonce. */
2537 /* FIXME should generate a new nonce for each AS-REQ */
2538 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
2539 ctx->nonce &= 0x7fffffff;
2540 /* XXX these just needs to be the same when using Windows PK-INIT */
2541 ctx->pk_nonce = ctx->nonce;
2543 ctx->prompter = prompter;
2544 ctx->prompter_data = prompter_data;
2546 /* pick up hostname from LKDC realm name */
2547 ret = capture_lkdc_domain(context, ctx);
2548 if (ret) {
2549 free_init_creds_ctx(context, ctx);
2550 return ret;
2553 ctx->runflags.allow_enc_pa_rep = 1;
2555 ctx->fast_state.flags |= KRB5_FAST_AS_REQ;
2557 *rctx = ctx;
2559 return ret;
2563 * Set the KDC hostname for the initial request, it will not be
2564 * considered in referrals to another KDC.
2566 * @param context a Kerberos 5 context.
2567 * @param ctx a krb5_init_creds_context context.
2568 * @param hostname the hostname for the KDC of realm
2570 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2571 * @ingroup krb5_credential
2574 krb5_error_code KRB5_LIB_FUNCTION
2575 krb5_init_creds_set_kdc_hostname(krb5_context context,
2576 krb5_init_creds_context ctx,
2577 const char *hostname)
2579 if (ctx->kdc_hostname)
2580 free(ctx->kdc_hostname);
2581 ctx->kdc_hostname = strdup(hostname);
2582 if (ctx->kdc_hostname == NULL)
2583 return krb5_enomem(context);
2584 return 0;
2588 * Set the sitename for the request
2592 krb5_error_code KRB5_LIB_FUNCTION
2593 krb5_init_creds_set_sitename(krb5_context context,
2594 krb5_init_creds_context ctx,
2595 const char *sitename)
2597 if (ctx->sitename)
2598 free(ctx->sitename);
2599 ctx->sitename = strdup(sitename);
2600 if (ctx->sitename == NULL)
2601 return krb5_enomem(context);
2602 return 0;
2606 * Sets the service that the is requested. This call is only neede for
2607 * special initial tickets, by default the a krbtgt is fetched in the default realm.
2609 * @param context a Kerberos 5 context.
2610 * @param ctx a krb5_init_creds_context context.
2611 * @param service the service given as a string, for example
2612 * "kadmind/admin". If NULL, the default krbtgt in the clients
2613 * realm is set.
2615 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2616 * @ingroup krb5_credential
2619 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2620 krb5_init_creds_set_service(krb5_context context,
2621 krb5_init_creds_context ctx,
2622 const char *service)
2624 krb5_const_realm client_realm;
2625 krb5_principal principal;
2626 krb5_error_code ret;
2628 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
2630 if (service) {
2631 ret = krb5_parse_name (context, service, &principal);
2632 if (ret)
2633 return ret;
2634 krb5_principal_set_realm (context, principal, client_realm);
2635 } else {
2636 ret = krb5_make_principal(context, &principal,
2637 client_realm, KRB5_TGS_NAME, client_realm,
2638 NULL);
2639 if (ret)
2640 return ret;
2644 * This is for Windows RODC that are picky about what name type
2645 * the server principal have, and the really strange part is that
2646 * they are picky about the AS-REQ name type and not the TGS-REQ
2647 * later. Oh well.
2650 if (krb5_principal_is_krbtgt(context, principal))
2651 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
2653 krb5_free_principal(context, ctx->cred.server);
2654 ctx->cred.server = principal;
2656 return 0;
2660 * Sets the password that will use for the request.
2662 * @param context a Kerberos 5 context.
2663 * @param ctx ctx krb5_init_creds_context context.
2664 * @param password the password to use.
2666 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2667 * @ingroup krb5_credential
2670 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2671 krb5_init_creds_set_password(krb5_context context,
2672 krb5_init_creds_context ctx,
2673 const char *password)
2675 if (ctx->password) {
2676 size_t len;
2677 len = strlen(ctx->password);
2678 memset_s(ctx->password, len, 0, len);
2679 free(ctx->password);
2681 if (password) {
2682 ctx->password = strdup(password);
2683 if (ctx->password == NULL)
2684 return krb5_enomem(context);
2685 ctx->keyseed = (void *) ctx->password;
2686 } else {
2687 ctx->keyseed = NULL;
2688 ctx->password = NULL;
2691 return 0;
2694 static krb5_error_code KRB5_CALLCONV
2695 keytab_key_proc(krb5_context context, krb5_enctype enctype,
2696 krb5_const_pointer keyseed,
2697 krb5_salt salt, krb5_data *s2kparms,
2698 krb5_keyblock **key)
2700 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
2701 krb5_keytab keytab = args->keytab;
2702 krb5_principal principal = args->principal;
2703 krb5_error_code ret;
2704 krb5_keytab real_keytab = NULL;
2705 krb5_keytab_entry entry;
2707 if (keytab == NULL) {
2708 ret = krb5_kt_default(context, &real_keytab);
2709 if (ret)
2710 return ret;
2711 keytab = real_keytab;
2714 ret = krb5_kt_get_entry (context, keytab, principal, 0, enctype, &entry);
2715 if (ret == 0) {
2716 ret = krb5_copy_keyblock(context, &entry.keyblock, key);
2717 krb5_kt_free_entry(context, &entry);
2720 krb5_kt_close(context, real_keytab);
2721 return ret;
2726 * Set the keytab to use for authentication.
2728 * @param context a Kerberos 5 context.
2729 * @param ctx ctx krb5_init_creds_context context.
2730 * @param keytab the keytab to read the key from.
2732 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2733 * @ingroup krb5_credential
2736 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2737 krb5_init_creds_set_keytab(krb5_context context,
2738 krb5_init_creds_context ctx,
2739 krb5_keytab keytab)
2741 krb5_keytab_key_proc_args *a;
2742 krb5_keytab_entry entry;
2743 krb5_kt_cursor cursor;
2744 krb5_enctype *etypes = NULL;
2745 krb5_error_code ret;
2746 size_t netypes = 0;
2747 int kvno = 0, found = 0;
2748 unsigned n;
2750 a = malloc(sizeof(*a));
2751 if (a == NULL)
2752 return krb5_enomem(context);
2754 a->principal = ctx->cred.client;
2755 a->keytab = keytab;
2757 ctx->keytab_data = a;
2758 ctx->keyseed = (void *)a;
2759 ctx->keyproc = keytab_key_proc;
2762 * We need to the KDC what enctypes we support for this keytab,
2763 * esp if the keytab is really a password based entry, then the
2764 * KDC might have more enctypes in the database then what we have
2765 * in the keytab.
2768 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
2769 if(ret)
2770 goto out;
2772 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
2773 void *ptr;
2775 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
2776 goto next;
2778 found = 1;
2780 /* check if we ahve this kvno already */
2781 if (entry.vno > kvno) {
2782 /* remove old list of etype */
2783 if (etypes)
2784 free(etypes);
2785 etypes = NULL;
2786 netypes = 0;
2787 kvno = entry.vno;
2788 } else if (entry.vno != kvno)
2789 goto next;
2791 /* check if enctype is supported */
2792 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
2793 goto next;
2796 * If user already provided a enctype list, use that as an
2797 * additonal filter.
2799 if (ctx->etypes) {
2800 for (n = 0; ctx->etypes[n] != KRB5_ENCTYPE_NULL; n++) {
2801 if (ctx->etypes[n] == entry.keyblock.keytype)
2802 break;
2804 if (ctx->etypes[n] == KRB5_ENCTYPE_NULL)
2805 goto next;
2808 /* add enctype to supported list */
2809 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
2810 if (ptr == NULL) {
2811 free(etypes);
2812 ret = krb5_enomem(context);
2813 goto out;
2816 etypes = ptr;
2817 etypes[netypes] = entry.keyblock.keytype;
2818 etypes[netypes + 1] = ETYPE_NULL;
2819 netypes++;
2820 next:
2821 krb5_kt_free_entry(context, &entry);
2823 krb5_kt_end_seq_get(context, keytab, &cursor);
2825 if (etypes) {
2826 if (ctx->etypes)
2827 free(ctx->etypes);
2828 ctx->etypes = etypes;
2831 out:
2832 if (!found) {
2833 if (ret == 0)
2834 ret = KRB5_KT_NOTFOUND;
2835 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
2838 return ret;
2841 static krb5_error_code KRB5_CALLCONV
2842 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
2843 krb5_const_pointer keyseed,
2844 krb5_salt salt, krb5_data *s2kparms,
2845 krb5_keyblock **key)
2847 return krb5_copy_keyblock (context, keyseed, key);
2850 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2851 krb5_init_creds_set_keyblock(krb5_context context,
2852 krb5_init_creds_context ctx,
2853 krb5_keyblock *keyblock)
2855 ctx->keyseed = (void *)keyblock;
2856 ctx->keyproc = keyblock_key_proc;
2858 return 0;
2861 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2862 krb5_init_creds_set_fast_ccache(krb5_context context,
2863 krb5_init_creds_context ctx,
2864 krb5_ccache fast_ccache)
2866 krb5_creds *cred = NULL;
2867 krb5_error_code ret;
2868 krb5_data data;
2870 ret = _krb5_get_krbtgt(context, fast_ccache, NULL, &cred);
2871 if (ret)
2872 return ret;
2874 ret = krb5_cc_get_config(context, fast_ccache, cred->server,
2875 "fast_avail", &data);
2876 krb5_free_creds(context, cred);
2877 if (ret == 0) {
2878 ctx->fast_state.armor_ccache = fast_ccache;
2879 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2880 ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
2881 } else {
2882 krb5_set_error_message(context, EINVAL, N_("FAST not available for the KDC in the armor ccache", ""));
2883 return EINVAL;
2885 return 0;
2888 static krb5_error_code
2889 validate_pkinit_fx(krb5_context context,
2890 krb5_init_creds_context ctx,
2891 AS_REP *rep,
2892 krb5_keyblock *ticket_sessionkey)
2894 PA_DATA *pa = NULL;
2895 int idx = 0;
2897 if (rep->padata)
2898 pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx);
2900 if (pa == NULL) {
2901 if (ctx->flags.request_anonymous && ctx->pk_init_ctx) {
2902 /* XXX handle the case where pkinit is not used */
2903 krb5_set_error_message(context, KRB5_KDCREP_MODIFIED,
2904 N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", ""));
2905 return KRB5_KDCREP_MODIFIED;
2908 return 0;
2911 heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage");
2913 return _krb5_pk_kx_confirm(context,
2914 ctx->pk_init_ctx,
2915 ctx->fast_state.reply_key,
2916 ticket_sessionkey,
2917 pa);
2920 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2921 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
2922 krb5_init_creds_context ctx,
2923 krb5_const_principal armor_service)
2925 krb5_error_code ret;
2927 if (ctx->fast_state.armor_service)
2928 krb5_free_principal(context, ctx->fast_state.armor_service);
2929 if (armor_service) {
2930 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
2931 if (ret)
2932 return ret;
2933 } else {
2934 ctx->fast_state.armor_service = NULL;
2936 ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE;
2937 return 0;
2940 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2941 krb5_init_creds_set_fast_anon_pkinit(krb5_context context,
2942 krb5_init_creds_context ctx)
2944 if (ctx->fast_state.armor_ccache)
2945 return EINVAL;
2947 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2948 ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
2949 return 0;
2952 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2953 _krb5_init_creds_set_fast_anon_pkinit_optimistic(krb5_context context,
2954 krb5_init_creds_context ctx)
2956 if (ctx->fast_state.armor_ccache)
2957 return EINVAL;
2959 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2960 ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
2961 ctx->fast_state.flags |= KRB5_FAST_OPTIMISTIC;
2962 return 0;
2965 static size_t
2966 available_padata_count(METHOD_DATA *md)
2968 size_t i, count = 0;
2970 for (i = 0; i < md->len; i++) {
2971 PA_DATA *pa = &md->val[i];
2973 if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
2974 pa->padata_type == KRB5_PADATA_FX_ERROR)
2975 continue;
2977 count++;
2980 return count;
2983 static krb5_error_code
2984 init_creds_step(krb5_context context,
2985 krb5_init_creds_context ctx,
2986 krb5_data *in,
2987 krb5_data *out,
2988 krb5_krbhst_info *hostinfo,
2989 unsigned int *flags)
2991 struct timeval start_time, end_time;
2992 krb5_data checksum_data;
2993 krb5_error_code ret;
2994 size_t len = 0;
2995 size_t size;
2996 AS_REQ req2;
2998 gettimeofday(&start_time, NULL);
3000 krb5_data_zero(out);
3001 krb5_data_zero(&checksum_data);
3003 if (ctx->as_req.req_body.cname == NULL) {
3004 ret = init_as_req(context, ctx->flags, &ctx->cred,
3005 ctx->addrs, ctx->etypes, &ctx->as_req);
3006 if (ret)
3007 return ret;
3008 if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
3010 else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE)
3011 /* Check with armor service if there is FAST */;
3012 else
3013 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3016 /* XXX should happen after we get back reply from KDC */
3017 pa_configure(context, ctx, NULL);
3020 #define MAX_PA_COUNTER 15
3021 if (ctx->pa_counter > MAX_PA_COUNTER) {
3022 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
3023 N_("Looping %d times while getting "
3024 "initial credentials", ""),
3025 ctx->pa_counter);
3026 return KRB5_GET_IN_TKT_LOOP;
3028 ctx->pa_counter++;
3030 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
3032 /* Lets process the input packet */
3033 if (in && in->length) {
3034 krb5_kdc_rep rep;
3036 memset(&rep, 0, sizeof(rep));
3038 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
3040 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
3041 if (ret == 0) {
3042 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
3043 krb5_data data;
3046 * Unwrap AS-REP
3048 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
3049 &rep.kdc_rep.ticket, &size, ret);
3050 if (ret)
3051 goto out;
3052 heim_assert(data.length == size, "ASN.1 internal error");
3054 ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data,
3055 &ctx->fast_state, &rep.kdc_rep);
3056 krb5_data_free(&data);
3057 if (ret)
3058 goto out;
3061 * Now check and extract the ticket
3064 if (ctx->flags.canonicalize) {
3065 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
3066 eflags |= EXTRACT_TICKET_MATCH_REALM;
3068 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
3069 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
3070 if (ctx->flags.request_anonymous)
3071 eflags |= EXTRACT_TICKET_MATCH_ANON;
3073 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
3074 &ctx->as_req, &rep.kdc_rep,
3075 hostinfo, &ctx->fast_state.reply_key);
3076 if (ret) {
3077 free_AS_REP(&rep.kdc_rep);
3078 goto out;
3081 if (ctx->fast_state.strengthen_key) {
3082 krb5_keyblock result;
3084 _krb5_debug(context, 5, "krb5_get_init_creds: FAST strengthen_key");
3086 ret = _krb5_fast_cf2(context,
3087 ctx->fast_state.strengthen_key,
3088 "strengthenkey",
3089 ctx->fast_state.reply_key,
3090 "replykey",
3091 &result,
3092 NULL);
3093 if (ret) {
3094 free_AS_REP(&rep.kdc_rep);
3095 goto out;
3098 ctx->runflags.allow_save_as_reply_key = 1;
3100 krb5_free_keyblock_contents(context, ctx->fast_state.reply_key);
3101 *ctx->fast_state.reply_key = result;
3104 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
3106 ret = _krb5_extract_ticket(context,
3107 &rep,
3108 &ctx->cred,
3109 ctx->fast_state.reply_key,
3110 NULL,
3111 KRB5_KU_AS_REP_ENC_PART,
3112 NULL,
3113 ctx->nonce,
3114 eflags,
3115 &ctx->req_buffer,
3116 NULL,
3117 NULL);
3119 if (ret == 0)
3120 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
3121 if (ret == 0)
3122 ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session);
3124 ctx->as_enctype = ctx->fast_state.reply_key->keytype;
3126 if (ctx->runflags.allow_save_as_reply_key) {
3127 ctx->as_reply_key = ctx->fast_state.reply_key;
3128 ctx->fast_state.reply_key = NULL;
3129 } else {
3130 krb5_free_keyblock(context, ctx->fast_state.reply_key);
3131 ctx->fast_state.reply_key = NULL;
3133 ctx->ic_flags |= KRB5_INIT_CREDS_DONE;
3134 *flags = 0;
3136 free_AS_REP(&rep.kdc_rep);
3137 free_EncASRepPart(&rep.enc_part);
3139 gettimeofday(&end_time, NULL);
3140 timevalsub(&end_time, &start_time);
3141 timevaladd(&ctx->stats.run_time, &end_time);
3143 _krb5_debug(context, 1, "krb5_get_init_creds: wc: %lld.%06ld",
3144 (long long)ctx->stats.run_time.tv_sec,
3145 (long)ctx->stats.run_time.tv_usec);
3146 return ret;
3148 } else {
3149 /* let's try to parse it as a KRB-ERROR */
3151 _krb5_debug(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC");
3153 free_KRB_ERROR(&ctx->error);
3155 ret = krb5_rd_error(context, in, &ctx->error);
3156 if(ret && in->length && ((char*)in->data)[0] == 4)
3157 ret = KRB5KRB_AP_ERR_V4_REPLY;
3158 if (ret) {
3159 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
3160 goto out;
3164 * Unwrap method-data, if there is any,
3165 * fast_unwrap_error() below might replace it with a
3166 * wrapped version if we are using FAST.
3169 free_METHOD_DATA(&ctx->md);
3170 memset(&ctx->md, 0, sizeof(ctx->md));
3172 if (ctx->error.e_data) {
3173 krb5_error_code ret2;
3175 ret2 = decode_METHOD_DATA(ctx->error.e_data->data,
3176 ctx->error.e_data->length,
3177 &ctx->md,
3178 NULL);
3179 if (ret2) {
3181 * Just ignore any error, the error will be pushed
3182 * out from krb5_error_from_rd_error() if there
3183 * was one.
3185 _krb5_debug(context, 5, N_("Failed to decode METHOD-DATA", ""));
3190 * Unwrap KRB-ERROR, we are always calling this so that
3191 * FAST can tell us if your peer KDC suddenly dropped FAST
3192 * wrapping and its really an attacker's packet (or a bug
3193 * in the KDC).
3195 ret = _krb5_fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
3196 &ctx->md, &ctx->error);
3197 if (ret)
3198 goto out;
3204 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
3206 /* log the failure */
3207 if (_krb5_have_debug(context, 5)) {
3208 const char *str = krb5_get_error_message(context, ret);
3209 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str);
3210 krb5_free_error_message(context, str);
3214 * Handle special error codes
3217 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED
3218 || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED
3219 || ret == KRB5KDC_ERR_ETYPE_NOSUPP)
3222 * If no preauth was set and KDC requires it, give it one
3223 * more try.
3225 * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop
3226 * one more time since that might mean we are dealing with
3227 * a Windows KDC that is confused about what enctypes are
3228 * available.
3231 if (available_padata_count(&ctx->md) == 0) {
3232 krb5_set_error_message(context, ret,
3233 N_("Preauth required but no preauth "
3234 "options send by KDC", ""));
3235 goto out;
3237 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
3239 * Try adapt to timeskrew when we are using pre-auth, and
3240 * if there was a time skew, try again.
3242 krb5_set_real_time(context, ctx->error.stime, -1);
3243 if (context->kdc_sec_offset)
3244 ret = 0;
3246 _krb5_debug(context, 10, "init_creds: err skew updating kdc offset to %d",
3247 context->kdc_sec_offset);
3248 if (ret)
3249 goto out;
3251 pa_restart(context, ctx);
3253 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
3254 /* client referral to a new realm */
3255 char *ref_realm;
3257 if (ctx->error.crealm == NULL) {
3258 krb5_set_error_message(context, ret,
3259 N_("Got a client referral, not but no realm", ""));
3260 goto out;
3262 ref_realm = *ctx->error.crealm;
3264 _krb5_debug(context, 5, "krb5_get_init_creds: referral to realm %s",
3265 ref_realm);
3268 * If its a krbtgt, lets updat the requested krbtgt too
3270 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
3272 free(ctx->cred.server->name.name_string.val[1]);
3273 ctx->cred.server->name.name_string.val[1] = strdup(ref_realm);
3274 if (ctx->cred.server->name.name_string.val[1] == NULL) {
3275 ret = krb5_enomem(context);
3276 goto out;
3279 free_PrincipalName(ctx->as_req.req_body.sname);
3280 ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server);
3281 if (ret)
3282 goto out;
3285 free(ctx->as_req.req_body.realm);
3286 ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm);
3287 if (ret)
3288 goto out;
3290 ret = krb5_principal_set_realm(context,
3291 ctx->cred.client,
3292 *ctx->error.crealm);
3293 if (ret)
3294 goto out;
3296 ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm);
3297 if (ret == 0) {
3298 _krb5_debug(context, 5, "krb5_get_init_creds: got referral to %s", ref_realm);
3299 krb5_xfree(ref_realm);
3302 pa_restart(context, ctx);
3304 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 &&
3305 ctx->runflags.change_password_prompt) {
3306 char buf2[1024];
3308 ctx->runflags.change_password = 1;
3310 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
3312 /* try to avoid recursion */
3313 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
3314 goto out;
3316 /* don't include prompter in runtime */
3317 gettimeofday(&end_time, NULL);
3318 timevalsub(&end_time, &start_time);
3319 timevaladd(&ctx->stats.run_time, &end_time);
3321 ret = change_password(context,
3322 ctx->cred.client,
3323 ctx->password,
3324 buf2,
3325 sizeof(buf2),
3326 ctx->prompter,
3327 ctx->prompter_data,
3328 NULL);
3329 if (ret)
3330 goto out;
3332 gettimeofday(&start_time, NULL);
3334 krb5_init_creds_set_password(context, ctx, buf2);
3336 pa_restart(context, ctx);
3338 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
3341 * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP,
3342 * so drop it and try again. But only try that for MIT
3343 * Kerberos servers by keying of no METHOD-DATA.
3345 if (ctx->runflags.allow_enc_pa_rep) {
3346 if (ctx->md.len != 0) {
3347 _krb5_debug(context, 10, "Server sent PA data with KRB-ERROR, "
3348 "so not a pre 1.7 MIT KDC and won't retry w/o ENC-PA-REQ");
3349 goto out;
3351 _krb5_debug(context, 10, "Disabling allow_enc_pa_rep and trying again");
3352 ctx->runflags.allow_enc_pa_rep = 0;
3353 goto retry;
3356 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) {
3357 _krb5_debug(context, 10, "FAST disabled and got preauth failed");
3358 goto out;
3361 retry:
3362 pa_restart(context, ctx);
3364 } else if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) {
3365 _krb5_debug(context, 10,
3366 "Some other error %d failed with optimistic FAST, trying w/o FAST", ret);
3368 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3369 ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED;
3370 ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR;
3371 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3372 pa_restart(context, ctx);
3373 } else {
3374 /* some other error code from the KDC, lets' return it to the user */
3375 goto out;
3380 if (ctx->as_req.padata) {
3381 free_METHOD_DATA(ctx->as_req.padata);
3382 free(ctx->as_req.padata);
3383 ctx->as_req.padata = NULL;
3386 ret = _krb5_fast_create_armor(context, &ctx->fast_state,
3387 ctx->cred.client->realm);
3388 if (ret)
3389 goto out;
3391 /* Set a new nonce. */
3392 ctx->as_req.req_body.nonce = ctx->nonce;
3396 * Step and announce PA-DATA
3399 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
3400 &ctx->md, &ctx->as_req.padata);
3401 if (ret)
3402 goto out;
3406 * Wrap with FAST
3408 ret = copy_AS_REQ(&ctx->as_req, &req2);
3409 if (ret)
3410 goto out;
3412 ret = _krb5_fast_wrap_req(context,
3413 &ctx->fast_state,
3414 NULL,
3415 &req2);
3417 krb5_data_free(&checksum_data);
3418 if (ret) {
3419 free_AS_REQ(&req2);
3420 goto out;
3423 krb5_data_free(&ctx->req_buffer);
3425 ASN1_MALLOC_ENCODE(AS_REQ,
3426 ctx->req_buffer.data, ctx->req_buffer.length,
3427 &req2, &len, ret);
3428 free_AS_REQ(&req2);
3429 if (ret)
3430 goto out;
3431 if(len != ctx->req_buffer.length)
3432 krb5_abortx(context, "internal error in ASN.1 encoder");
3434 out->data = ctx->req_buffer.data;
3435 out->length = ctx->req_buffer.length;
3437 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
3439 gettimeofday(&end_time, NULL);
3440 timevalsub(&end_time, &start_time);
3441 timevaladd(&ctx->stats.run_time, &end_time);
3443 return 0;
3444 out:
3445 return ret;
3449 * The core loop if krb5_get_init_creds() function family. Create the
3450 * packets and have the caller send them off to the KDC.
3452 * If the caller want all work been done for them, use
3453 * krb5_init_creds_get() instead.
3455 * @param context a Kerberos 5 context.
3456 * @param ctx ctx krb5_init_creds_context context.
3457 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
3458 * @param out reply to KDC.
3459 * @param hostinfo KDC address info, first round it can be NULL.
3460 * @param flags status of the round, if
3461 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
3463 * @return 0 for success, or an Kerberos 5 error code, see
3464 * krb5_get_error_message().
3466 * @ingroup krb5_credential
3469 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3470 krb5_init_creds_step(krb5_context context,
3471 krb5_init_creds_context ctx,
3472 krb5_data *in,
3473 krb5_data *out,
3474 krb5_krbhst_info *hostinfo,
3475 unsigned int *flags)
3477 krb5_error_code ret;
3478 krb5_data empty;
3480 krb5_data_zero(&empty);
3482 if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) &&
3483 ctx->fast_state.armor_ccache == NULL) {
3484 ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state,
3485 in, out, hostinfo, flags);
3486 if (ret && (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC)) {
3487 _krb5_debug(context, 5, "Preauth failed with optimistic "
3488 "FAST, trying w/o FAST");
3489 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3490 ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED;
3491 ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR;
3492 } else if (ret ||
3493 ((*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) ||
3494 out->length)
3495 return ret;
3497 in = &empty;
3500 return init_creds_step(context, ctx, in, out, hostinfo, flags);
3504 * Extract the newly acquired credentials from krb5_init_creds_context
3505 * context.
3507 * @param context A Kerberos 5 context.
3508 * @param ctx
3509 * @param cred credentials, free with krb5_free_cred_contents().
3511 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3514 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3515 krb5_init_creds_get_creds(krb5_context context,
3516 krb5_init_creds_context ctx,
3517 krb5_creds *cred)
3519 return krb5_copy_creds_contents(context, &ctx->cred, cred);
3523 * Extract the as-reply key from the context.
3525 * Only allowed when the as-reply-key is not directly derived from the
3526 * password like PK-INIT, GSS, FAST hardened key, etc.
3528 * @param context A Kerberos 5 context.
3529 * @param ctx ctx krb5_init_creds_context context.
3530 * @param as_reply_key keyblock, free with krb5_free_keyblock_contents().
3532 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3535 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3536 krb5_init_creds_get_as_reply_key(krb5_context context,
3537 krb5_init_creds_context ctx,
3538 krb5_keyblock *as_reply_key)
3540 if (ctx->as_reply_key == NULL)
3541 return KRB5KDC_ERR_PREAUTH_REQUIRED;
3542 return krb5_copy_keyblock_contents(context, ctx->as_reply_key, as_reply_key);
3545 KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3546 _krb5_init_creds_get_cred_starttime(krb5_context context, krb5_init_creds_context ctx)
3548 return ctx->cred.times.starttime;
3551 KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3552 _krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx)
3554 return ctx->cred.times.endtime;
3557 KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL
3558 _krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx)
3560 return ctx->cred.client;
3564 * Get the last error from the transaction.
3566 * @return Returns 0 or an error code
3568 * @ingroup krb5_credential
3571 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3572 krb5_init_creds_get_error(krb5_context context,
3573 krb5_init_creds_context ctx,
3574 KRB_ERROR *error)
3576 krb5_error_code ret;
3578 ret = copy_KRB_ERROR(&ctx->error, error);
3579 if (ret)
3580 krb5_enomem(context);
3582 return ret;
3586 * Store config
3588 * @param context A Kerberos 5 context.
3589 * @param ctx The krb5_init_creds_context to free.
3590 * @param id store
3592 * @return Returns 0 or an error code
3594 * @ingroup krb5_credential
3597 krb5_error_code KRB5_LIB_FUNCTION
3598 krb5_init_creds_store_config(krb5_context context,
3599 krb5_init_creds_context ctx,
3600 krb5_ccache id)
3602 krb5_error_code ret;
3604 if (ctx->kdc_hostname) {
3605 krb5_data data;
3606 data.length = strlen(ctx->kdc_hostname);
3607 data.data = ctx->kdc_hostname;
3609 ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data);
3610 if (ret)
3611 return ret;
3613 if (ctx->sitename) {
3614 krb5_data data;
3615 data.length = strlen(ctx->sitename);
3616 data.data = ctx->sitename;
3618 ret = krb5_cc_set_config(context, id, NULL, "sitename", &data);
3619 if (ret)
3620 return ret;
3623 return 0;
3628 * @ingroup krb5_credential
3631 krb5_error_code
3632 krb5_init_creds_store(krb5_context context,
3633 krb5_init_creds_context ctx,
3634 krb5_ccache id)
3636 krb5_error_code ret;
3638 if (ctx->cred.client == NULL) {
3639 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
3640 krb5_set_error_message(context, ret, "init creds not completed yet");
3641 return ret;
3644 ret = krb5_cc_initialize(context, id, ctx->cred.client);
3645 if (ret)
3646 return ret;
3648 ret = krb5_cc_store_cred(context, id, &ctx->cred);
3649 if (ret)
3650 return ret;
3652 if (ctx->cred.flags.b.enc_pa_rep) {
3653 krb5_data data = { 3, rk_UNCONST("yes") };
3654 ret = krb5_cc_set_config(context, id, ctx->cred.server,
3655 "fast_avail", &data);
3656 if (ret)
3657 return ret;
3660 return 0;
3664 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
3666 * @param context A Kerberos 5 context.
3667 * @param ctx The krb5_init_creds_context to free.
3669 * @ingroup krb5_credential
3672 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3673 krb5_init_creds_free(krb5_context context,
3674 krb5_init_creds_context ctx)
3676 free_init_creds_ctx(context, ctx);
3677 free(ctx);
3681 * Get new credentials as setup by the krb5_init_creds_context.
3683 * @param context A Kerberos 5 context.
3684 * @param ctx The krb5_init_creds_context to process.
3686 * @ingroup krb5_credential
3689 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3690 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
3692 krb5_sendto_ctx stctx = NULL;
3693 krb5_krbhst_info *hostinfo = NULL;
3694 krb5_error_code ret;
3695 krb5_data in, out;
3696 unsigned int flags = 0;
3698 krb5_data_zero(&in);
3699 krb5_data_zero(&out);
3701 ret = krb5_sendto_ctx_alloc(context, &stctx);
3702 if (ret)
3703 goto out;
3704 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
3706 if (ctx->kdc_hostname)
3707 krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname);
3708 if (ctx->sitename)
3709 krb5_sendto_set_sitename(context, stctx, ctx->sitename);
3711 while (1) {
3712 struct timeval nstart, nend;
3714 flags = 0;
3715 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
3716 krb5_data_free(&in);
3717 if (ret)
3718 goto out;
3720 if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
3721 break;
3723 gettimeofday(&nstart, NULL);
3725 ret = krb5_sendto_context (context, stctx, &out,
3726 ctx->cred.client->realm, &in);
3727 if (ret)
3728 goto out;
3730 gettimeofday(&nend, NULL);
3731 timevalsub(&nend, &nstart);
3732 timevaladd(&ctx->stats.run_time, &nend);
3735 out:
3736 if (stctx)
3737 krb5_sendto_ctx_free(context, stctx);
3739 return ret;
3743 * Get new credentials using password.
3745 * @ingroup krb5_credential
3749 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3750 krb5_get_init_creds_password(krb5_context context,
3751 krb5_creds *creds,
3752 krb5_principal client,
3753 const char *password,
3754 krb5_prompter_fct prompter,
3755 void *data,
3756 krb5_deltat start_time,
3757 const char *in_tkt_service,
3758 krb5_get_init_creds_opt *options)
3760 krb5_init_creds_context ctx;
3761 char buf[BUFSIZ], buf2[BUFSIZ];
3762 krb5_error_code ret;
3763 int chpw = 0;
3765 again:
3766 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
3767 if (ret)
3768 goto out;
3770 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3771 if (ret)
3772 goto out;
3774 if (prompter != NULL && ctx->password == NULL && password == NULL) {
3775 krb5_prompt prompt;
3776 krb5_data password_data;
3777 char *p, *q = NULL;
3778 int aret;
3780 ret = krb5_unparse_name(context, client, &p);
3781 if (ret)
3782 goto out;
3784 aret = asprintf(&q, "%s's Password: ", p);
3785 free (p);
3786 if (aret == -1 || q == NULL) {
3787 ret = krb5_enomem(context);
3788 goto out;
3790 prompt.prompt = q;
3791 password_data.data = buf;
3792 password_data.length = sizeof(buf);
3793 prompt.hidden = 1;
3794 prompt.reply = &password_data;
3795 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
3797 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
3798 free (q);
3799 if (ret) {
3800 memset_s(buf, sizeof(buf), 0, sizeof(buf));
3801 ret = KRB5_LIBOS_PWDINTR;
3802 krb5_clear_error_message (context);
3803 goto out;
3805 password = password_data.data;
3808 if (password) {
3809 ret = krb5_init_creds_set_password(context, ctx, password);
3810 if (ret)
3811 goto out;
3814 ret = krb5_init_creds_get(context, ctx);
3816 if (ret == 0)
3817 krb5_process_last_request(context, options, ctx);
3820 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
3821 /* try to avoid recursion */
3822 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
3823 goto out;
3825 /* don't try to change password if no prompter or prompting disabled */
3826 if (!ctx->runflags.change_password_prompt)
3827 goto out;
3829 ret = change_password (context,
3830 client,
3831 ctx->password,
3832 buf2,
3833 sizeof(buf2),
3834 prompter,
3835 data,
3836 options);
3837 if (ret)
3838 goto out;
3839 password = buf2;
3840 chpw = 1;
3841 krb5_init_creds_free(context, ctx);
3842 goto again;
3845 out:
3846 if (ret == 0)
3847 krb5_init_creds_get_creds(context, ctx, creds);
3849 if (ctx)
3850 krb5_init_creds_free(context, ctx);
3852 memset_s(buf, sizeof(buf), 0, sizeof(buf));
3853 memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
3854 return ret;
3858 * Get new credentials using keyblock.
3860 * @ingroup krb5_credential
3863 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3864 krb5_get_init_creds_keyblock(krb5_context context,
3865 krb5_creds *creds,
3866 krb5_principal client,
3867 krb5_keyblock *keyblock,
3868 krb5_deltat start_time,
3869 const char *in_tkt_service,
3870 krb5_get_init_creds_opt *options)
3872 krb5_init_creds_context ctx;
3873 krb5_error_code ret;
3875 memset(creds, 0, sizeof(*creds));
3877 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3878 if (ret)
3879 goto out;
3881 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3882 if (ret)
3883 goto out;
3885 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
3886 if (ret)
3887 goto out;
3889 ret = krb5_init_creds_get(context, ctx);
3891 if (ret == 0)
3892 krb5_process_last_request(context, options, ctx);
3894 out:
3895 if (ret == 0)
3896 krb5_init_creds_get_creds(context, ctx, creds);
3898 if (ctx)
3899 krb5_init_creds_free(context, ctx);
3901 return ret;
3905 * Get new credentials using keytab.
3907 * @ingroup krb5_credential
3910 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3911 krb5_get_init_creds_keytab(krb5_context context,
3912 krb5_creds *creds,
3913 krb5_principal client,
3914 krb5_keytab keytab,
3915 krb5_deltat start_time,
3916 const char *in_tkt_service,
3917 krb5_get_init_creds_opt *options)
3919 krb5_init_creds_context ctx;
3920 krb5_keytab_entry ktent;
3921 krb5_error_code ret;
3923 memset(&ktent, 0, sizeof(ktent));
3924 memset(creds, 0, sizeof(*creds));
3926 if (strcmp(client->realm, "") == 0) {
3928 * Referral realm. We have a keytab, so pick a realm by
3929 * matching in the keytab.
3931 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
3932 if (ret == 0)
3933 client = ktent.principal;
3936 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3937 if (ret)
3938 goto out;
3940 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3941 if (ret)
3942 goto out;
3944 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
3945 if (ret)
3946 goto out;
3948 ret = krb5_init_creds_get(context, ctx);
3949 if (ret == 0)
3950 krb5_process_last_request(context, options, ctx);
3952 out:
3953 krb5_kt_free_entry(context, &ktent);
3954 if (ret == 0)
3955 krb5_init_creds_get_creds(context, ctx, creds);
3957 if (ctx)
3958 krb5_init_creds_free(context, ctx);
3960 return ret;
3963 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3964 _krb5_init_creds_set_gss_mechanism(krb5_context context,
3965 krb5_gss_init_ctx gssic,
3966 const struct gss_OID_desc_struct *gss_mech)
3968 gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
3971 KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
3972 _krb5_init_creds_get_gss_mechanism(krb5_context context,
3973 krb5_gss_init_ctx gssic)
3975 return gssic->mech;
3978 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3979 _krb5_init_creds_set_gss_cred(krb5_context context,
3980 krb5_gss_init_ctx gssic,
3981 struct gss_cred_id_t_desc_struct *gss_cred)
3983 if (gssic->cred != gss_cred && gssic->flags.release_cred)
3984 gssic->release_cred(context, gssic, gssic->cred);
3986 gssic->cred = gss_cred;
3987 gssic->flags.release_cred = 1;
3990 KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
3991 _krb5_init_creds_get_gss_cred(krb5_context context,
3992 krb5_gss_init_ctx gssic)
3994 return gssic->cred;
3997 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3998 _krb5_init_creds_init_gss(krb5_context context,
3999 krb5_init_creds_context ctx,
4000 krb5_gssic_step step,
4001 krb5_gssic_finish finish,
4002 krb5_gssic_release_cred release_cred,
4003 krb5_gssic_delete_sec_context delete_sec_context,
4004 const struct gss_cred_id_t_desc_struct *gss_cred,
4005 const struct gss_OID_desc_struct *gss_mech,
4006 unsigned int flags)
4008 krb5_gss_init_ctx gssic;
4010 gssic = calloc(1, sizeof(*gssic));
4011 if (gssic == NULL)
4012 return krb5_enomem(context);
4014 if (ctx->gss_init_ctx)
4015 free_gss_init_ctx(context, ctx->gss_init_ctx);
4016 ctx->gss_init_ctx = gssic;
4018 gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
4019 gssic->mech = gss_mech;
4020 if (flags & KRB5_GSS_IC_FLAG_RELEASE_CRED)
4021 gssic->flags.release_cred = 1;
4023 gssic->step = step;
4024 gssic->finish = finish;
4025 gssic->release_cred = release_cred;
4026 gssic->delete_sec_context = delete_sec_context;
4028 return 0;