2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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
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
37 #include "krb5_locl.h"
38 #include "../base/heimbasepriv.h" /* XXX */
46 struct krb5_gss_init_ctx_data {
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;
56 unsigned int release_cred : 1;
60 struct krb5_get_init_creds_ctx {
63 krb5_addresses *addrs;
65 krb5_preauthtype *pre_auth_types;
74 /* password and keytab_data is freed on completion */
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;
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;
97 struct pa_info_data paid;
101 EncKDCRepPart enc_part;
103 krb5_prompter_fct prompter;
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;
119 struct timeval run_time;
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,
142 if (_krb5_have_debug(context, 5)) {
144 ret = krb5_enctype_to_string(context, type, &str);
148 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type);
152 password.data = rk_UNCONST(keyseed);
153 password.length = keyseed ? strlen(keyseed) : 0;
157 krb5_data_zero(&opaque);
159 *key = malloc(sizeof(**key));
161 return krb5_enomem(context);
162 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
172 free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
177 if (gssic->flags.release_cred)
178 gssic->release_cred(context, gssic, gssic->cred);
183 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
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);
195 len = strlen(ctx->password);
196 memset_s(ctx->password, len, 0, len);
199 free_gss_init_ctx(context, ctx->gss_init_ctx);
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);
217 free(ctx->kdc_hostname);
219 free_paid(context, &ctx->paid);
220 memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
224 get_config_time (krb5_context context,
231 ret = krb5_config_get_time (context, NULL,
238 ret = krb5_config_get_time (context, NULL,
247 static krb5_error_code
248 init_cred (krb5_context context,
250 krb5_principal client,
251 krb5_deltat start_time,
252 krb5_get_init_creds_opt *options)
258 krb5_timeofday (context, &now);
260 memset (cred, 0, sizeof(*cred));
263 ret = krb5_copy_principal(context, client, &cred->client);
265 ret = krb5_get_default_principal(context, &cred->client);
270 cred->times.starttime = now + start_time;
272 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
273 tmp = options->tkt_life;
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;
282 tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
283 cred->times.renew_till = now + tmp;
289 krb5_free_cred_contents (context, cred);
294 * Print a message (str) to the user about the expiration in `lr'
298 report_expiration (krb5_context context,
299 krb5_prompter_fct prompter,
306 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
308 (*prompter)(context, data, NULL, p, 0, NULL);
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.
322 krb5_process_last_request(krb5_context context,
323 krb5_get_init_creds_opt *options,
324 krb5_init_creds_context ctx)
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));
340 return krb5_enomem(context);
342 for (i = 0; i < lr->len; i++) {
343 lre[i] = calloc(1, sizeof(*lre[i]));
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++)
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)
377 krb5_const_realm realm;
378 krb5_enctype weak_enctype = KRB5_ENCTYPE_NULL;
383 if (ctx->prompter == NULL)
386 if (ctx->warned_user)
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,
401 for (i = 0; i < lr->len; ++i) {
402 if (lr->val[i].lr_value <= t) {
403 switch (lr->val[i].lr_type) {
405 report_expiration(context, ctx->prompter,
407 "Your password will expire at ",
408 lr->val[i].lr_value);
410 case LR_ACCT_EXPTIME :
411 report_expiration(context, ctx->prompter,
413 "Your account will expire at ",
414 lr->val[i].lr_value);
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,
430 "suppress_weak_enctype", NULL);
432 char *str = NULL, *p = NULL;
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);
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,
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;
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);
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;
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;
495 if (krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
496 ctx->flags.canonicalize = 1;
498 ctx->pre_auth_types = NULL;
501 ctx->pre_auth_types = NULL;
503 ret = init_cred(context, &ctx->cred, client, start_time, options);
506 krb5_get_init_creds_opt_free(context, default_opt);
510 ret = krb5_init_creds_set_service(context, ctx, NULL);
514 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
515 ctx->flags.forwardable = options->forwardable;
517 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
518 ctx->flags.proxiable = options->proxiable;
521 ctx->flags.postdated = 1;
522 if (ctx->cred.times.renew_till)
523 ctx->flags.renewable = 1;
524 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
525 ctx->addrs = options->address_list;
526 } else if (options->opt_private) {
527 switch (options->opt_private->addressless) {
528 case KRB5_INIT_CREDS_TRISTATE_UNSET:
529 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
530 ctx->addrs = &no_addrs;
535 case KRB5_INIT_CREDS_TRISTATE_FALSE:
538 case KRB5_INIT_CREDS_TRISTATE_TRUE:
539 ctx->addrs = &no_addrs;
543 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
547 etypes = malloc((options->etype_list_length + 1)
548 * sizeof(krb5_enctype));
549 if (etypes == NULL) {
550 ret = krb5_enomem(context);
553 memcpy (etypes, options->etype_list,
554 options->etype_list_length * sizeof(krb5_enctype));
555 etypes[options->etype_list_length] = ETYPE_NULL;
556 ctx->etypes = etypes;
558 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
559 pre_auth_types = malloc((options->preauth_list_length + 1)
560 * sizeof(krb5_preauthtype));
561 if (pre_auth_types == NULL) {
562 ret = krb5_enomem(context);
565 memcpy (pre_auth_types, options->preauth_list,
566 options->preauth_list_length * sizeof(krb5_preauthtype));
567 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
568 ctx->pre_auth_types = pre_auth_types;
570 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
571 ctx->flags.request_anonymous = options->anonymous;
573 ctx->prompter = prompter;
574 ctx->prompter_data = prompter_data;
576 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
577 !options->change_password_prompt)
578 ctx->runflags.change_password_prompt = 0;
580 ctx->runflags.change_password_prompt = ctx->prompter != NULL;
583 krb5_get_init_creds_opt_free(context, default_opt);
588 krb5_get_init_creds_opt_free(context, default_opt);
592 static krb5_error_code
593 change_password (krb5_context context,
594 krb5_principal client,
595 const char *password,
598 krb5_prompter_fct prompter,
600 krb5_get_init_creds_opt *old_options)
602 krb5_prompt prompts[2];
605 char buf1[BUFSIZ], buf2[BUFSIZ];
606 krb5_data password_data[2];
608 krb5_data result_code_string;
609 krb5_data result_string;
611 krb5_get_init_creds_opt *options;
613 heim_assert(prompter != NULL, "unexpected NULL prompter");
615 memset (&cpw_cred, 0, sizeof(cpw_cred));
617 ret = krb5_get_init_creds_opt_alloc(context, &options);
620 krb5_get_init_creds_opt_set_tkt_life (options, 60);
621 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
622 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
624 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
625 krb5_get_init_creds_opt_set_preauth_list(options,
626 old_options->preauth_list,
627 old_options->preauth_list_length);
629 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
630 krb5_get_init_creds_opt_set_change_password_prompt(options,
631 old_options->change_password_prompt);
633 krb5_data_zero (&result_code_string);
634 krb5_data_zero (&result_string);
636 ret = krb5_get_init_creds_password (context,
645 krb5_get_init_creds_opt_free(context, options);
650 password_data[0].data = buf1;
651 password_data[0].length = sizeof(buf1);
653 prompts[0].hidden = 1;
654 prompts[0].prompt = "New password: ";
655 prompts[0].reply = &password_data[0];
656 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
658 password_data[1].data = buf2;
659 password_data[1].length = sizeof(buf2);
661 prompts[1].hidden = 1;
662 prompts[1].prompt = "Repeat new password: ";
663 prompts[1].reply = &password_data[1];
664 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
666 ret = (*prompter) (context, data, NULL, "Changing password",
669 memset (buf1, 0, sizeof(buf1));
670 memset (buf2, 0, sizeof(buf2));
674 if (strcmp (buf1, buf2) == 0)
676 memset (buf1, 0, sizeof(buf1));
677 memset (buf2, 0, sizeof(buf2));
680 ret = krb5_set_password (context,
690 if (asprintf(&p, "%s: %.*s\n",
691 result_code ? "Error" : "Success",
692 (int)result_string.length,
693 result_string.length > 0 ? (char*)result_string.data : "") < 0)
695 ret = krb5_enomem(context);
699 /* return the result */
700 (*prompter) (context, data, NULL, p, 0, NULL);
702 if (result_code == 0) {
703 strlcpy (newpw, buf1, newpw_sz);
707 krb5_set_error_message(context, ret,
708 N_("failed changing password: %s", ""), p);
713 memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
714 memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
715 krb5_data_free (&result_string);
716 krb5_data_free (&result_code_string);
717 krb5_free_cred_contents (context, &cpw_cred);
722 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
723 krb5_keyblock_key_proc (krb5_context context,
726 krb5_const_pointer keyseed,
729 return krb5_copy_keyblock (context, keyseed, key);
736 static krb5_error_code
737 init_as_req (krb5_context context,
739 const krb5_creds *creds,
740 const krb5_addresses *addrs,
741 const krb5_enctype *etypes,
746 memset(a, 0, sizeof(*a));
749 a->msg_type = krb_as_req;
750 a->req_body.kdc_options = opts;
751 a->req_body.cname = calloc(1, sizeof(*a->req_body.cname));
752 if (a->req_body.cname == NULL) {
753 ret = krb5_enomem(context);
756 a->req_body.sname = calloc(1, sizeof(*a->req_body.sname));
757 if (a->req_body.sname == NULL) {
758 ret = krb5_enomem(context);
762 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
765 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
769 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
773 if(creds->times.starttime) {
774 a->req_body.from = malloc(sizeof(*a->req_body.from));
775 if (a->req_body.from == NULL) {
776 ret = krb5_enomem(context);
779 *a->req_body.from = creds->times.starttime;
781 if(creds->times.endtime){
782 if ((ALLOC(a->req_body.till, 1)) != NULL)
783 *a->req_body.till = creds->times.endtime;
785 ret = krb5_enomem(context);
789 if(creds->times.renew_till){
790 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
791 if (a->req_body.rtime == NULL) {
792 ret = krb5_enomem(context);
795 *a->req_body.rtime = creds->times.renew_till;
797 a->req_body.nonce = 0;
798 ret = _krb5_init_etype(context,
800 &a->req_body.etype.len,
801 &a->req_body.etype.val,
807 * This means no addresses
810 if (addrs && addrs->len == 0) {
811 a->req_body.addresses = NULL;
813 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
814 if (a->req_body.addresses == NULL) {
815 ret = krb5_enomem(context);
820 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
822 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
823 if(ret == 0 && a->req_body.addresses->len == 0) {
824 free(a->req_body.addresses);
825 a->req_body.addresses = NULL;
832 a->req_body.enc_authorization_data = NULL;
833 a->req_body.additional_tickets = NULL;
840 memset_s(a, sizeof(*a), 0, sizeof(*a));
845 static krb5_error_code
846 set_paid(struct pa_info_data *paid, krb5_context context,
848 krb5_salttype salttype, void *salt_string, size_t salt_len,
849 krb5_data *s2kparams)
852 paid->salt.salttype = salttype;
853 paid->salt.saltvalue.data = malloc(salt_len + 1);
854 if (paid->salt.saltvalue.data == NULL) {
855 krb5_clear_error_message(context);
856 return krb5_enomem(context);
858 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
859 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
860 paid->salt.saltvalue.length = salt_len;
864 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
866 krb5_clear_error_message(context);
867 krb5_free_salt(context, paid->salt);
871 paid->s2kparams = NULL;
876 static struct pa_info_data *
877 pa_etype_info2(krb5_context context,
878 const krb5_principal client,
880 struct pa_info_data *paid,
881 heim_octet_string *data)
888 memset(&e, 0, sizeof(e));
889 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
894 for (j = 0; j < asreq->req_body.etype.len; j++) {
895 for (i = 0; i < e.len; i++) {
897 if (krb5_enctype_valid(context, e.val[i].etype) != 0)
900 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
902 if (e.val[i].salt == NULL)
903 ret = krb5_get_pw_salt(context, client, &salt);
905 salt.saltvalue.data = *e.val[i].salt;
906 salt.saltvalue.length = strlen(*e.val[i].salt);
910 ret = set_paid(paid, context, e.val[i].etype,
913 salt.saltvalue.length,
915 if (e.val[i].salt == NULL)
916 krb5_free_salt(context, salt);
918 free_ETYPE_INFO2(&e);
925 free_ETYPE_INFO2(&e);
929 static struct pa_info_data *
930 pa_etype_info(krb5_context context,
931 const krb5_principal client,
933 struct pa_info_data *paid,
934 heim_octet_string *data)
941 memset(&e, 0, sizeof(e));
942 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
947 for (j = 0; j < asreq->req_body.etype.len; j++) {
948 for (i = 0; i < e.len; i++) {
950 if (krb5_enctype_valid(context, e.val[i].etype) != 0)
953 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
955 salt.salttype = KRB5_PW_SALT;
956 if (e.val[i].salt == NULL)
957 ret = krb5_get_pw_salt(context, client, &salt);
959 salt.saltvalue = *e.val[i].salt;
962 if (e.val[i].salttype)
963 salt.salttype = *e.val[i].salttype;
965 ret = set_paid(paid, context, e.val[i].etype,
968 salt.saltvalue.length,
970 if (e.val[i].salt == NULL)
971 krb5_free_salt(context, salt);
985 static struct pa_info_data *
986 pa_pw_or_afs3_salt(krb5_context context,
987 const krb5_principal client,
989 struct pa_info_data *paid,
990 heim_octet_string *data)
993 if (paid->etype == KRB5_ENCTYPE_NULL)
995 if (krb5_enctype_valid(context, paid->etype) != 0)
998 ret = set_paid(paid, context,
1000 paid->salt.salttype,
1010 static krb5_error_code
1011 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
1012 krb5_enctype etype, krb5_keyblock *key)
1018 EncryptedData encdata;
1019 krb5_error_code ret;
1024 krb5_us_timeofday (context, &p.patimestamp, &usec);
1028 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1032 krb5_abortx(context, "internal error in ASN.1 encoder");
1034 ret = krb5_crypto_init(context, key, 0, &crypto);
1039 ret = krb5_encrypt_EncryptedData(context,
1041 KRB5_KU_PA_ENC_TIMESTAMP,
1047 krb5_crypto_destroy(context, crypto);
1051 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1052 free_EncryptedData(&encdata);
1056 krb5_abortx(context, "internal error in ASN.1 encoder");
1058 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1064 static krb5_error_code
1065 add_enc_ts_padata(krb5_context context,
1067 krb5_principal client,
1068 krb5_s2k_proc keyproc,
1069 krb5_const_pointer keyseed,
1070 krb5_enctype *enctypes,
1073 krb5_data *s2kparams)
1075 krb5_error_code ret;
1080 memset(&salt2, 0, sizeof(salt2));
1083 /* default to standard salt */
1084 ret = krb5_get_pw_salt (context, client, &salt2);
1090 enctypes = context->etypes;
1092 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1096 for (i = 0; i < netypes; ++i) {
1099 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1101 ret = (*keyproc)(context, enctypes[i], keyseed,
1102 *salt, s2kparams, &key);
1105 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1106 krb5_free_keyblock (context, key);
1111 krb5_free_salt(context, salt2);
1115 static krb5_error_code
1116 pa_data_to_md_ts_enc(krb5_context context,
1118 const krb5_principal client,
1119 krb5_init_creds_context ctx,
1120 struct pa_info_data *ppaid,
1123 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1127 add_enc_ts_padata(context, md, client,
1128 ctx->keyproc, ctx->keyseed,
1130 &ppaid->salt, ppaid->s2kparams);
1134 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1136 /* make a v5 salted pa-data */
1137 add_enc_ts_padata(context, md, client,
1138 ctx->keyproc, ctx->keyseed,
1139 a->req_body.etype.val, a->req_body.etype.len,
1142 /* make a v4 salted pa-data */
1143 salt.salttype = KRB5_PW_SALT;
1144 krb5_data_zero(&salt.saltvalue);
1145 add_enc_ts_padata(context, md, client,
1146 ctx->keyproc, ctx->keyseed,
1147 a->req_body.etype.val, a->req_body.etype.len,
1153 static krb5_error_code
1154 pa_data_to_key_plain(krb5_context context,
1155 const krb5_principal client,
1156 krb5_init_creds_context ctx,
1158 krb5_data *s2kparams,
1160 krb5_keyblock **key)
1162 krb5_error_code ret;
1164 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1165 salt, s2kparams, key);
1169 struct pkinit_context {
1170 unsigned int win2k : 1;
1171 unsigned int used_pkinit : 1;
1175 static krb5_error_code
1176 pa_data_to_md_pkinit(krb5_context context,
1178 const krb5_principal client,
1180 krb5_init_creds_context ctx,
1183 if (ctx->pk_init_ctx == NULL)
1186 return _krb5_pk_mk_padata(context,
1194 krb5_set_error_message(context, EINVAL,
1195 N_("no support for PKINIT compiled in", ""));
1200 static krb5_error_code
1201 pkinit_configure_ietf(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1203 struct pkinit_context *pkinit_ctx = pa_ctx;
1205 pkinit_ctx->win2k = 0;
1207 if (ctx->pk_init_ctx == NULL)
1208 return HEIM_ERR_PA_CANT_CONTINUE;
1213 static krb5_error_code
1214 pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1216 struct pkinit_context *pkinit_ctx = pa_ctx;
1218 pkinit_ctx->win2k = 1;
1219 pkinit_ctx->used_pkinit = 0;
1221 if (ctx->pk_init_ctx == NULL)
1222 return HEIM_ERR_PA_CANT_CONTINUE;
1227 static krb5_error_code
1228 pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1229 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md)
1231 krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
1232 struct pkinit_context *pkinit_ctx = pa_ctx;
1235 if (pkinit_ctx->used_pkinit) {
1236 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1237 "Already tried PKINIT(%s), looping",
1238 pkinit_ctx->win2k ? "win2k" : "ietf");
1240 ret = pa_data_to_md_pkinit(context, a, ctx->cred.client,
1241 (pkinit_ctx->win2k != 0),
1244 ret = HEIM_ERR_PA_CONTINUE_NEEDED;
1246 pkinit_ctx->used_pkinit = 1;
1249 ret = _krb5_pk_rd_pa_reply(context,
1252 rep->enc_part.etype,
1257 &ctx->fast_state.reply_key);
1259 ctx->runflags.allow_save_as_reply_key = 1;
1266 pkinit_release(void *pa_ctx)
1271 * GSS-API pre-authentication support
1274 struct pa_gss_context {
1275 struct gss_ctx_id_t_desc_struct *context_handle;
1279 static krb5_error_code
1280 pa_gss_configure(krb5_context context,
1281 krb5_init_creds_context ctx,
1284 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1285 struct pa_gss_context *pa_gss_ctx = pa_ctx;
1288 return HEIM_ERR_PA_CANT_CONTINUE;
1290 pa_gss_ctx->context_handle = NULL;
1291 pa_gss_ctx->open = 0;
1296 static krb5_error_code
1297 pa_data_to_md_gss(krb5_context context,
1299 const krb5_creds *creds,
1300 krb5_init_creds_context ctx,
1301 struct pa_gss_context *pa_gss_ctx,
1303 METHOD_DATA *out_md)
1305 krb5_error_code ret;
1306 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1308 krb5_data *input_token, output_token;
1311 krb5_data_zero(&req_body);
1312 krb5_data_zero(&output_token);
1314 input_token = pa ? &pa->padata_value : NULL;
1316 if ((input_token == NULL || input_token->length == 0) &&
1317 pa_gss_ctx->context_handle) {
1318 krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1319 "Missing GSS preauthentication data from KDC");
1320 return HEIM_ERR_PA_CANT_CONTINUE;
1323 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
1324 &ctx->as_req.req_body, &len, ret);
1327 heim_assert(req_body.length == len, "ASN.1 internal error");
1329 ret = gssic->step(context, gssic, creds, &pa_gss_ctx->context_handle,
1330 ctx->flags, &req_body,
1331 input_token, &output_token);
1334 * If FAST authenticated the KDC (which will be the case unless anonymous
1335 * PKINIT was used without KDC certificate validation) then we can relax
1336 * the mutual authentication requirement.
1338 if (ret == KRB5_MUTUAL_FAILED &&
1339 (ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1340 (ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
1344 * Always require a strengthen key if FAST was used, to avoid a MITM
1345 * attack that could result in unintended privilege escalation should
1346 * the KDC add positive authorization data from the armor ticket.
1348 if ((ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1349 ctx->fast_state.strengthen_key == NULL) {
1350 krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1351 "FAST GSS pre-authentication without strengthen key");
1352 ret = KRB5_KDCREP_MODIFIED;
1356 pa_gss_ctx->open = 1;
1359 if (output_token.length) {
1360 ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
1361 output_token.data, output_token.length);
1365 krb5_data_zero(&output_token);
1369 krb5_data_free(&output_token);
1370 krb5_data_free(&req_body);
1375 static krb5_error_code
1376 pa_gss_step(krb5_context context,
1377 krb5_init_creds_context ctx,
1382 const krb5_krbhst_info *hi,
1384 METHOD_DATA *out_md)
1386 krb5_error_code ret;
1387 krb5_principal cname;
1388 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1389 struct pa_gss_context *pa_gss_ctx = pa_ctx;
1391 heim_assert(gssic != NULL, "invalid context passed to pa_gss_step");
1393 if (!pa_gss_ctx->open) {
1394 ret = pa_data_to_md_gss(context, a, &ctx->cred, ctx,
1395 pa_gss_ctx, pa, out_md);
1396 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED && rep) {
1397 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1398 "KDC sent AS-REP before GSS "
1399 "pre-authentication completed");
1400 ret = KRB5_KDCREP_MODIFIED;
1401 } else if (ret == 0 && rep == NULL) {
1402 ret = HEIM_ERR_PA_CONTINUE_NEEDED; /* odd number of legs */
1406 } else if (pa && pa->padata_value.length) {
1407 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1408 "Already completed GSS pre-authentication");
1409 return KRB5_GET_IN_TKT_LOOP;
1410 } else if (rep == NULL) {
1411 krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1412 "Completed GSS pre-authentication before KDC");
1413 return KRB5_PREAUTH_FAILED;
1416 heim_assert(pa_gss_ctx->open,
1417 "GSS pre-authentication incomplete");
1419 ret = gssic->finish(context, gssic, &ctx->cred,
1420 pa_gss_ctx->context_handle, ctx->nonce,
1421 rep->enc_part.etype, &cname,
1422 &ctx->fast_state.reply_key);
1430 if (krb5_unparse_name(context, ctx->cred.client, &from) == 0 &&
1431 krb5_unparse_name(context, cname, &to) == 0) {
1432 _krb5_debug(context, 1, "pa_gss_step: %s as %s",
1439 if (krb5_principal_is_federated(context, ctx->cred.client)) {
1441 * The well-known federated name will be replaced with the cname
1442 * in the AS-REP, but save the locally mapped initiator name in the
1445 krb5_free_principal(context, ctx->cred.client);
1446 ctx->cred.client = cname;
1448 ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
1450 krb5_free_principal(context, cname);
1453 ctx->runflags.allow_save_as_reply_key = 1;
1455 gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1456 pa_gss_ctx->context_handle = NULL;
1457 pa_gss_ctx->open = 0;
1462 static krb5_error_code
1463 pa_gss_restart(krb5_context context,
1464 krb5_init_creds_context ctx,
1467 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1468 struct pa_gss_context *pa_gss_ctx = pa_ctx;
1471 return HEIM_ERR_PA_CANT_CONTINUE;
1473 gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1474 pa_gss_ctx->context_handle = NULL;
1475 pa_gss_ctx->open = 0;
1481 pa_gss_release(void *pa_ctx)
1486 _krb5_make_pa_enc_challenge(krb5_context context,
1488 krb5_key_usage usage,
1495 EncryptedData encdata;
1496 krb5_error_code ret;
1500 krb5_us_timeofday (context, &p.patimestamp, &usec);
1504 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1508 krb5_abortx(context, "internal error in ASN.1 encoder");
1510 ret = krb5_encrypt_EncryptedData(context,
1521 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1522 free_EncryptedData(&encdata);
1526 krb5_abortx(context, "internal error in ASN.1 encoder");
1528 ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len);
1535 _krb5_validate_pa_enc_challenge(krb5_context context,
1537 krb5_key_usage usage,
1538 EncryptedData *enc_data,
1539 const char *peer_name)
1541 krb5_error_code ret;
1548 ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data);
1552 ret = decode_PA_ENC_TS_ENC(ts_data.data,
1556 krb5_data_free(&ts_data);
1558 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1559 _krb5_debug(context, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name);
1563 krb5_us_timeofday(context, ×tamp, &usec);
1565 if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) {
1566 char client_time[100];
1568 krb5_format_time(context, p.patimestamp,
1569 client_time, sizeof(client_time), TRUE);
1571 ret = KRB5KRB_AP_ERR_SKEW;
1572 _krb5_debug(context, 0, "Too large time skew, "
1573 "client time %s is out by %u > %d seconds -- %s",
1575 (unsigned)krb5_time_abs(timestamp, p.patimestamp),
1576 (int)context->max_skew,
1583 free_PA_ENC_TS_ENC(&p);
1589 static struct pa_info_data *
1590 process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *);
1593 static krb5_error_code
1594 enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1595 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md)
1597 struct pa_info_data paid, *ppaid;
1598 krb5_keyblock challengekey;
1599 krb5_data pepper1, pepper2;
1600 krb5_crypto crypto = NULL;
1601 krb5_enctype aenctype;
1602 krb5_error_code ret;
1604 memset(&paid, 0, sizeof(paid));
1607 paid.etype = KRB5_ENCTYPE_NULL;
1609 paid.etype = rep->enc_part.etype;
1610 ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1613 * If we don't have ppaid, ts because the KDC have not sent any
1614 * salt info, lets to the first roundtrip so the KDC have a chance
1617 if (ppaid == NULL) {
1618 _krb5_debug(context, 5, "no ppaid found");
1619 return HEIM_ERR_PA_CONTINUE_NEEDED;
1621 if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1622 return HEIM_ERR_PA_CANT_CONTINUE;
1625 if (ctx->fast_state.reply_key)
1626 krb5_free_keyblock(context, ctx->fast_state.reply_key);
1628 ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1629 ppaid->salt, ppaid->s2kparams, ppaid->etype,
1630 &ctx->fast_state.reply_key);
1631 free_paid(context, &paid);
1633 _krb5_debug(context, 5, "enc-chal: failed to build key");
1637 ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto);
1641 krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype);
1643 pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor";
1644 pepper1.length = strlen(pepper1.data);
1645 pepper2.data = "challengelongterm";
1646 pepper2.length = strlen(pepper2.data);
1648 ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto,
1649 &pepper1, &pepper2, aenctype,
1651 krb5_crypto_destroy(context, crypto);
1655 ret = krb5_crypto_init(context, &challengekey, 0, &crypto);
1656 krb5_free_keyblock_contents(context, &challengekey);
1661 EncryptedData enc_data;
1665 _krb5_debug(context, 5, "enc-chal: failed to create reply key");
1669 _krb5_debug(context, 5, "ENC_CHAL rep key");
1671 if (ctx->fast_state.strengthen_key == NULL) {
1672 krb5_crypto_destroy(context, crypto);
1673 _krb5_debug(context, 5, "ENC_CHAL w/o strengthen_key");
1674 return KRB5_KDCREP_MODIFIED;
1678 krb5_crypto_destroy(context, crypto);
1679 _krb5_debug(context, 0, "KDC response missing");
1680 return HEIM_ERR_PA_CANT_CONTINUE;
1683 ret = decode_EncryptedData(pa->padata_value.data,
1684 pa->padata_value.length,
1688 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1689 _krb5_debug(context, 5, "Failed to decode ENC_CHAL KDC reply");
1693 ret = _krb5_validate_pa_enc_challenge(context, crypto,
1694 KRB5_KU_ENC_CHALLENGE_KDC,
1697 free_EncryptedData(&enc_data);
1698 krb5_crypto_destroy(context, crypto);
1704 ret = _krb5_make_pa_enc_challenge(context, crypto,
1705 KRB5_KU_ENC_CHALLENGE_CLIENT,
1707 krb5_crypto_destroy(context, crypto);
1709 _krb5_debug(context, 5, "enc-chal: failed build enc challenge");
1713 return HEIM_ERR_PA_CONTINUE_NEEDED;
1717 struct enc_ts_context {
1719 #define USED_ENC_TS_GUESS 4
1720 #define USED_ENC_TS_INFO 8
1721 #define USED_ENC_TS_RENEG 16
1722 krb5_principal user;
1725 static krb5_error_code
1726 enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1728 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1729 pactx->used_pa_types = 0;
1730 krb5_free_principal(context, pactx->user);
1735 static krb5_error_code
1736 enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa,
1739 const krb5_krbhst_info *hi,
1740 METHOD_DATA *in_md, METHOD_DATA *out_md)
1742 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1743 struct pa_info_data paid, *ppaid;
1744 krb5_error_code ret;
1749 * Keep track of the user we used so that we can restart
1750 * authentication when we get referrals.
1753 if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) {
1754 pactx->used_pa_types = 0;
1755 krb5_free_principal(context, pactx->user);
1759 if (pactx->user == NULL) {
1760 ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user);
1765 memset(&paid, 0, sizeof(paid));
1768 paid.etype = KRB5_ENCTYPE_NULL;
1770 paid.etype = rep->enc_part.etype;
1772 ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1776 * Some KDC's don't send salt info in the reply when there is
1777 * success pre-auth happned before, so use cached copy (or
1778 * even better, if there is just one pre-auth, save reply-key).
1780 if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) {
1783 } else if (ppaid == NULL) {
1784 _krb5_debug(context, 0, "no paid when building key, build a default salt structure ?");
1785 return HEIM_ERR_PA_CANT_CONTINUE;
1788 ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1789 ppaid->salt, ppaid->s2kparams, rep->enc_part.etype,
1790 &ctx->fast_state.reply_key);
1791 free_paid(context, &paid);
1796 * If we don't have ppaid, ts because the KDC have not sent any
1797 * salt info, lets to the first roundtrip so the KDC have a chance
1800 * Don't bother guessing, it sounds like a good idea until you run
1801 * into KDCs that are doing failed auth counting based on the
1804 * Stashing the salt for the next run is a diffrent issue and
1805 * could be considered in the future.
1808 if (ppaid == NULL) {
1809 _krb5_debug(context, 5,
1810 "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}");
1811 return HEIM_ERR_PA_CONTINUE_NEEDED;
1813 if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1814 free_paid(context, &paid);
1815 _krb5_debug(context, 5,
1816 "TS-ENC: kdc proposes enctype NULL ?");
1817 return HEIM_ERR_PA_CANT_CONTINUE;
1821 * We have to allow the KDC to re-negotiate the PA-TS data
1822 * once, this is since the in the case of a windows read only
1823 * KDC that doesn't have the keys simply guesses what the
1824 * master is supposed to support. In the case where this
1825 * breaks in when the RO-KDC is a newer version the the RW-KDC
1826 * and the RO-KDC announced a enctype that the older doesn't
1829 if (pactx->used_pa_types & USED_ENC_TS_INFO) {
1830 flag = USED_ENC_TS_RENEG;
1833 flag = USED_ENC_TS_INFO;
1837 if (pactx->used_pa_types & flag) {
1838 free_paid(context, &paid);
1839 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1840 "Already tried ENC-TS-%s, looping", state);
1841 return KRB5_GET_IN_TKT_LOOP;
1844 pactx->used_pa_types |= flag;
1846 free_paid(context, &ctx->paid);
1849 ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md);
1853 return HEIM_ERR_PA_CONTINUE_NEEDED;
1857 enc_ts_release(void *pa_ctx)
1859 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1862 krb5_free_principal(NULL, pactx->user);
1865 static krb5_error_code
1866 pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1867 const AS_REP *rep, const krb5_krbhst_info *hi,
1868 METHOD_DATA *in_md, METHOD_DATA *out_md)
1870 size_t len = 0, length;
1871 krb5_error_code ret;
1875 switch (ctx->req_pac) {
1876 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1877 return 0; /* don't bother */
1878 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1879 req.include_pac = 1;
1881 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1882 req.include_pac = 0;
1885 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1889 heim_assert(len == length, "internal error in ASN.1 encoder");
1891 ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1898 static krb5_error_code
1899 pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1900 const AS_REP *rep, const krb5_krbhst_info *hi,
1901 METHOD_DATA *in_md, METHOD_DATA *out_md)
1903 if (ctx->runflags.allow_enc_pa_rep)
1904 return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1909 static krb5_error_code
1910 pa_fx_cookie_step(krb5_context context,
1911 krb5_init_creds_context ctx,
1916 const krb5_krbhst_info *hi,
1918 METHOD_DATA *out_md)
1920 krb5_error_code ret;
1925 pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx);
1928 * RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
1929 * expects at least one more message from the client.
1931 if (ctx->error.error_code == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
1932 return KRB5_PREAUTH_FAILED;
1937 cookie = malloc(pad->padata_value.length);
1939 return krb5_enomem(context);
1941 memcpy(cookie, pad->padata_value.data, pad->padata_value.length);
1943 ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE,
1944 cookie, pad->padata_value.length);
1948 _krb5_debug(context, 5, "Mirrored FX-COOKIE to KDC");
1953 typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *);
1954 typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *);
1955 typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *);
1956 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 *);
1957 typedef void (*pa_release_f)(void *);
1963 #define PA_F_ANNOUNCE 1
1964 #define PA_F_CONFIG 2
1965 #define PA_F_FAST 4 /* available inside FAST */
1966 #define PA_F_NOT_FAST 8 /* only available without FAST */
1968 pa_salt_info_f salt_info;
1970 * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL.
1972 pa_configure_f configure;
1974 * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc)
1976 pa_restart_f restart;
1978 * Return 0 if the when complete, HEIM_ERR_PA_CONTINUE_NEEDED if more steps are require
1981 pa_release_f release;
1984 KRB5_PADATA_PK_AS_REP,
1986 PA_F_FAST | PA_F_NOT_FAST,
1987 sizeof(struct pkinit_context),
1989 pkinit_configure_ietf,
1995 KRB5_PADATA_PK_AS_REP_19,
1997 PA_F_FAST | PA_F_NOT_FAST,
1998 sizeof(struct pkinit_context),
2000 pkinit_configure_win,
2008 PA_F_FAST | PA_F_NOT_FAST,
2009 sizeof(struct pa_gss_context),
2017 KRB5_PADATA_ENCRYPTED_CHALLENGE,
2018 "ENCRYPTED_CHALLENGE",
2028 KRB5_PADATA_ENC_TIMESTAMP,
2029 "ENCRYPTED_TIMESTAMP",
2031 sizeof(struct enc_ts_context),
2039 KRB5_PADATA_PA_PAC_REQUEST,
2050 KRB5_PADATA_REQ_ENC_PA_REP,
2061 KRB5_PADATA_FX_COOKIE,
2071 #define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL }
2072 patype_salt(ETYPE_INFO2, pa_etype_info2),
2073 patype_salt(ETYPE_INFO, pa_etype_info),
2074 patype_salt(PW_SALT, pa_pw_or_afs3_salt),
2075 patype_salt(AFS3_SALT, pa_pw_or_afs3_salt),
2077 /* below are just for pretty printing */
2078 #define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL }
2079 patype_info(AUTHENTICATION_SET),
2080 patype_info(AUTH_SET_SELECTED),
2081 patype_info(FX_FAST),
2082 patype_info(FX_ERROR),
2083 patype_info(PKINIT_KX),
2084 patype_info(PK_AS_REQ)
2089 get_pa_type_name(int type)
2092 for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++)
2093 if (type == patypes[n].type)
2094 return patypes[n].name;
2102 struct pa_auth_mech {
2103 struct patype *patype;
2104 struct pa_auth_mech *next; /* when doing authentication sets */
2112 static struct pa_info_data *
2113 process_pa_info(krb5_context context,
2114 const krb5_principal client,
2115 const AS_REQ *asreq,
2116 struct pa_info_data *paid,
2119 struct pa_info_data *p = NULL;
2126 for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) {
2129 if (patypes[i].salt_info == NULL)
2132 pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx);
2136 paid->salt.salttype = (krb5_salttype)patypes[i].type;
2137 p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value);
2143 pa_announce(krb5_context context,
2145 krb5_init_creds_context ctx,
2147 METHOD_DATA *out_md)
2151 for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2152 if ((patypes[n].flags & types) == 0)
2155 if (patypes[n].step)
2156 patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, NULL, in_md, out_md);
2158 krb5_padata_add(context, out_md, patypes[n].type, NULL, 0);
2164 mech_dealloc(void *ctx)
2166 struct pa_auth_mech *pa_mech = ctx;
2167 if (pa_mech->patype->release)
2168 pa_mech->patype->release((void *)&pa_mech->pactx[0]);
2171 struct heim_type_data pa_auth_mech_object = {
2172 HEIM_TID_PA_AUTH_MECH,
2173 "heim-pa-mech-context",
2182 static struct pa_auth_mech *
2183 pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2185 struct pa_auth_mech *pa_mech;
2186 struct patype *patype = NULL;
2189 for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2190 if (patypes[n].type == pa_type)
2191 patype = &patypes[n];
2196 pa_mech = _heim_alloc_object(&pa_auth_mech_object, sizeof(*pa_mech) - 1 + patype->pa_ctx_size);
2197 if (pa_mech == NULL)
2200 pa_mech->patype = patype;
2202 if (pa_mech->patype->configure) {
2203 krb5_error_code ret;
2205 ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]);
2207 heim_release(pa_mech);
2212 _krb5_debug(context, 5, "Adding PA mech: %s", patype->name);
2218 pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2220 struct pa_auth_mech *mech;
2222 mech = pa_mech_create(context, ctx, pa_type);
2224 heim_array_append_value(ctx->available_pa_mechs, mech);
2229 static krb5_error_code
2230 pa_configure(krb5_context context,
2231 krb5_init_creds_context ctx,
2234 ctx->available_pa_mechs = heim_array_create();
2236 if (ctx->gss_init_ctx) {
2237 pa_mech_add(context, ctx, KRB5_PADATA_GSS);
2238 } else if (ctx->pk_init_ctx) {
2239 pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP);
2240 pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19);
2241 } else if (ctx->keyproc || ctx->keyseed || ctx->prompter) {
2242 pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE);
2243 pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP);
2245 /* XXX setup context based on KDC reply */
2250 static krb5_error_code
2251 pa_restart(krb5_context context,
2252 krb5_init_creds_context ctx)
2254 krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
2256 if (ctx->pa_mech && ctx->pa_mech->patype->restart)
2257 ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]);
2263 static krb5_error_code
2264 pa_step(krb5_context context,
2265 krb5_init_creds_context ctx,
2268 const krb5_krbhst_info *hi,
2270 METHOD_DATA *out_md)
2272 krb5_error_code ret;
2278 if (ctx->pa_mech == NULL) {
2279 size_t len = heim_array_get_length(ctx->available_pa_mechs);
2281 _krb5_debug(context, 0, "no more available_pa_mechs to try");
2282 return HEIM_ERR_NO_MORE_PA_MECHS;
2285 ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0);
2286 heim_array_delete_value(ctx->available_pa_mechs, 0);
2289 if (ctx->fast_state.armor_crypto) {
2290 if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) {
2291 _krb5_debug(context, 0, "pa-mech %s dropped under FAST (not supported)",
2292 ctx->pa_mech->patype->name);
2293 heim_release(ctx->pa_mech);
2294 ctx->pa_mech = NULL;
2298 if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) {
2299 _krb5_debug(context, 0, "dropped pa-mech %s since not running under FAST",
2300 ctx->pa_mech->patype->name);
2301 heim_release(ctx->pa_mech);
2302 ctx->pa_mech = NULL;
2307 _krb5_debug(context, 0, "pa-mech trying: %s, searching for %d",
2308 ctx->pa_mech->patype->name, ctx->pa_mech->patype->type);
2312 pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx);
2316 } while (ctx->pa_mech == NULL);
2318 _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name);
2320 ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, hi, in_md, out_md);
2321 _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret);
2323 struct pa_auth_mech *next_pa = ctx->pa_mech->next;
2326 _krb5_debug(context, 5, "Next PA type in set is: %s",
2327 next_pa->patype->name);
2328 ret = HEIM_ERR_PA_CONTINUE_NEEDED;
2329 } else if (rep == NULL) {
2330 _krb5_debug(context, 5, "PA %s done, but no ticket in sight!!!",
2331 ctx->pa_mech->patype->name);
2332 ret = HEIM_ERR_PA_CANT_CONTINUE;
2334 ctx->pa_used = ctx->pa_mech->patype->name;
2337 heim_retain(next_pa);
2338 heim_release(ctx->pa_mech);
2339 ctx->pa_mech = next_pa;
2342 if (ret == HEIM_ERR_PA_CANT_CONTINUE) {
2344 _krb5_debug(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name);
2345 heim_release(ctx->pa_mech);
2346 ctx->pa_mech = NULL;
2349 } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2350 _krb5_debug(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name);
2351 } else if (ret != 0) {
2352 _krb5_debug(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret);
2353 heim_release(ctx->pa_mech);
2354 ctx->pa_mech = NULL;
2361 log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md)
2363 if (_krb5_have_debug(context, 5)) {
2365 _krb5_debug(context, 5, "KDC sent %d patypes", in_md->len);
2366 for (i = 0; i < in_md->len; i++)
2367 _krb5_debug(context, 5, "KDC sent PA-DATA type: %d (%s)",
2368 in_md->val[i].padata_type,
2369 get_pa_type_name(in_md->val[i].padata_type));
2374 * Assumes caller always will free `out_md', even on error.
2377 static krb5_error_code
2378 process_pa_data_to_md(krb5_context context,
2379 const krb5_creds *creds,
2381 krb5_init_creds_context ctx,
2383 METHOD_DATA **out_md)
2385 krb5_error_code ret;
2388 if (*out_md == NULL) {
2389 return krb5_enomem(context);
2392 (*out_md)->val = NULL;
2394 log_kdc_pa_types(context, in_md);
2396 ret = pa_step(context, ctx, a, NULL, NULL, in_md, *out_md);
2397 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2398 _krb5_debug(context, 0, "pamech need more stepping");
2399 } else if (ret == 0) {
2400 _krb5_debug(context, 0, "pamech done step");
2406 * Send announcement (what we support) and configuration (user
2407 * introduced behavior change)
2410 pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md);
2416 if ((*out_md)->len == 0) {
2424 static krb5_error_code
2425 process_pa_data_to_key(krb5_context context,
2426 krb5_init_creds_context ctx,
2430 const krb5_krbhst_info *hi,
2431 krb5_keyblock **key)
2433 struct pa_info_data paid, *ppaid = NULL;
2434 krb5_error_code ret;
2435 krb5_enctype etype = rep->enc_part.etype;
2437 memset(&paid, 0, sizeof(paid));
2440 log_kdc_pa_types(context, rep->padata);
2444 ppaid = process_pa_info(context, creds->client, a, &paid,
2447 if (ppaid == NULL) {
2448 if (ctx->paid.etype == KRB5_ENCTYPE_NULL) {
2449 ctx->paid.etype = etype;
2450 ctx->paid.s2kparams = NULL;
2451 ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt);
2457 ret = pa_step(context, ctx, a, rep, hi, rep->padata, NULL);
2458 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2459 _krb5_debug(context, 0, "In final stretch and pa require more stepping ?");
2461 } else if (ret == 0) {
2462 _krb5_debug(context, 0, "final pamech done step");
2468 free_paid(context, &paid);
2476 static krb5_error_code
2477 capture_lkdc_domain(krb5_context context,
2478 krb5_init_creds_context ctx)
2482 len = strlen(_krb5_wellknown_lkdc);
2484 if (ctx->kdc_hostname != NULL ||
2485 strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 ||
2486 ctx->cred.client->realm[len] != ':')
2489 ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]);
2491 _krb5_debug(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s",
2497 * Start a new context to get a new initial credential.
2499 * @param context A Kerberos 5 context.
2500 * @param client The Kerberos principal to get the credential for, if
2501 * NULL is given, the default principal is used as determined by
2502 * krb5_get_default_principal().
2504 * @param prompter_data
2505 * @param start_time the time the ticket should start to be valid or 0 for now.
2506 * @param options a options structure, can be NULL for default options.
2507 * @param rctx A new allocated free with krb5_init_creds_free().
2509 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
2511 * @ingroup krb5_credential
2514 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2515 krb5_init_creds_init(krb5_context context,
2516 krb5_principal client,
2517 krb5_prompter_fct prompter,
2518 void *prompter_data,
2519 krb5_deltat start_time,
2520 krb5_get_init_creds_opt *options,
2521 krb5_init_creds_context *rctx)
2523 krb5_init_creds_context ctx;
2524 krb5_error_code ret;
2528 ctx = calloc(1, sizeof(*ctx));
2530 return krb5_enomem(context);
2532 ret = get_init_creds_common(context, client, prompter, prompter_data,
2533 start_time, options, ctx);
2539 /* Set a new nonce. */
2540 /* FIXME should generate a new nonce for each AS-REQ */
2541 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
2542 ctx->nonce &= 0x7fffffff;
2543 /* XXX these just needs to be the same when using Windows PK-INIT */
2544 ctx->pk_nonce = ctx->nonce;
2546 ctx->prompter = prompter;
2547 ctx->prompter_data = prompter_data;
2549 /* pick up hostname from LKDC realm name */
2550 ret = capture_lkdc_domain(context, ctx);
2552 free_init_creds_ctx(context, ctx);
2556 ctx->runflags.allow_enc_pa_rep = 1;
2558 ctx->fast_state.flags |= KRB5_FAST_AS_REQ;
2566 * Set the KDC hostname for the initial request, it will not be
2567 * considered in referrals to another KDC.
2569 * @param context a Kerberos 5 context.
2570 * @param ctx a krb5_init_creds_context context.
2571 * @param hostname the hostname for the KDC of realm
2573 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2574 * @ingroup krb5_credential
2577 krb5_error_code KRB5_LIB_FUNCTION
2578 krb5_init_creds_set_kdc_hostname(krb5_context context,
2579 krb5_init_creds_context ctx,
2580 const char *hostname)
2582 if (ctx->kdc_hostname)
2583 free(ctx->kdc_hostname);
2584 ctx->kdc_hostname = strdup(hostname);
2585 if (ctx->kdc_hostname == NULL)
2586 return krb5_enomem(context);
2591 * Set the sitename for the request
2595 krb5_error_code KRB5_LIB_FUNCTION
2596 krb5_init_creds_set_sitename(krb5_context context,
2597 krb5_init_creds_context ctx,
2598 const char *sitename)
2601 free(ctx->sitename);
2602 ctx->sitename = strdup(sitename);
2603 if (ctx->sitename == NULL)
2604 return krb5_enomem(context);
2609 * Sets the service that the is requested. This call is only neede for
2610 * special initial tickets, by default the a krbtgt is fetched in the default realm.
2612 * @param context a Kerberos 5 context.
2613 * @param ctx a krb5_init_creds_context context.
2614 * @param service the service given as a string, for example
2615 * "kadmind/admin". If NULL, the default krbtgt in the clients
2618 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2619 * @ingroup krb5_credential
2622 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2623 krb5_init_creds_set_service(krb5_context context,
2624 krb5_init_creds_context ctx,
2625 const char *service)
2627 krb5_const_realm client_realm;
2628 krb5_principal principal;
2629 krb5_error_code ret;
2631 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
2634 ret = krb5_parse_name (context, service, &principal);
2637 krb5_principal_set_realm (context, principal, client_realm);
2639 ret = krb5_make_principal(context, &principal,
2640 client_realm, KRB5_TGS_NAME, client_realm,
2647 * This is for Windows RODC that are picky about what name type
2648 * the server principal have, and the really strange part is that
2649 * they are picky about the AS-REQ name type and not the TGS-REQ
2653 if (krb5_principal_is_krbtgt(context, principal))
2654 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
2656 krb5_free_principal(context, ctx->cred.server);
2657 ctx->cred.server = principal;
2663 * Sets the password that will use for the request.
2665 * @param context a Kerberos 5 context.
2666 * @param ctx ctx krb5_init_creds_context context.
2667 * @param password the password to use.
2669 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2670 * @ingroup krb5_credential
2673 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2674 krb5_init_creds_set_password(krb5_context context,
2675 krb5_init_creds_context ctx,
2676 const char *password)
2678 if (ctx->password) {
2680 len = strlen(ctx->password);
2681 memset_s(ctx->password, len, 0, len);
2682 free(ctx->password);
2685 ctx->password = strdup(password);
2686 if (ctx->password == NULL)
2687 return krb5_enomem(context);
2688 ctx->keyseed = (void *) ctx->password;
2690 ctx->keyseed = NULL;
2691 ctx->password = NULL;
2697 static krb5_error_code KRB5_CALLCONV
2698 keytab_key_proc(krb5_context context, krb5_enctype enctype,
2699 krb5_const_pointer keyseed,
2700 krb5_salt salt, krb5_data *s2kparms,
2701 krb5_keyblock **key)
2703 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
2704 krb5_keytab keytab = args->keytab;
2705 krb5_principal principal = args->principal;
2706 krb5_error_code ret;
2707 krb5_keytab real_keytab;
2708 krb5_keytab_entry entry;
2710 if (keytab == NULL) {
2711 ret = krb5_kt_default(context, &real_keytab);
2715 real_keytab = keytab;
2717 ret = krb5_kt_get_entry (context, real_keytab, principal,
2718 0, enctype, &entry);
2721 krb5_kt_close (context, real_keytab);
2726 ret = krb5_copy_keyblock (context, &entry.keyblock, key);
2727 krb5_kt_free_entry(context, &entry);
2733 * Set the keytab to use for authentication.
2735 * @param context a Kerberos 5 context.
2736 * @param ctx ctx krb5_init_creds_context context.
2737 * @param keytab the keytab to read the key from.
2739 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2740 * @ingroup krb5_credential
2743 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2744 krb5_init_creds_set_keytab(krb5_context context,
2745 krb5_init_creds_context ctx,
2748 krb5_keytab_key_proc_args *a;
2749 krb5_keytab_entry entry;
2750 krb5_kt_cursor cursor;
2751 krb5_enctype *etypes = NULL;
2752 krb5_error_code ret;
2754 int kvno = 0, found = 0;
2757 a = malloc(sizeof(*a));
2759 return krb5_enomem(context);
2761 a->principal = ctx->cred.client;
2764 ctx->keytab_data = a;
2765 ctx->keyseed = (void *)a;
2766 ctx->keyproc = keytab_key_proc;
2769 * We need to the KDC what enctypes we support for this keytab,
2770 * esp if the keytab is really a password based entry, then the
2771 * KDC might have more enctypes in the database then what we have
2775 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
2779 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
2782 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
2787 /* check if we ahve this kvno already */
2788 if (entry.vno > kvno) {
2789 /* remove old list of etype */
2795 } else if (entry.vno != kvno)
2798 /* check if enctype is supported */
2799 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
2803 * If user already provided a enctype list, use that as an
2807 for (n = 0; ctx->etypes[n] != KRB5_ENCTYPE_NULL; n++) {
2808 if (ctx->etypes[n] == entry.keyblock.keytype)
2811 if (ctx->etypes[n] == KRB5_ENCTYPE_NULL)
2815 /* add enctype to supported list */
2816 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
2819 ret = krb5_enomem(context);
2824 etypes[netypes] = entry.keyblock.keytype;
2825 etypes[netypes + 1] = ETYPE_NULL;
2828 krb5_kt_free_entry(context, &entry);
2830 krb5_kt_end_seq_get(context, keytab, &cursor);
2835 ctx->etypes = etypes;
2841 ret = KRB5_KT_NOTFOUND;
2842 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
2848 static krb5_error_code KRB5_CALLCONV
2849 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
2850 krb5_const_pointer keyseed,
2851 krb5_salt salt, krb5_data *s2kparms,
2852 krb5_keyblock **key)
2854 return krb5_copy_keyblock (context, keyseed, key);
2857 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2858 krb5_init_creds_set_keyblock(krb5_context context,
2859 krb5_init_creds_context ctx,
2860 krb5_keyblock *keyblock)
2862 ctx->keyseed = (void *)keyblock;
2863 ctx->keyproc = keyblock_key_proc;
2868 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2869 krb5_init_creds_set_fast_ccache(krb5_context context,
2870 krb5_init_creds_context ctx,
2871 krb5_ccache fast_ccache)
2873 krb5_creds *cred = NULL;
2874 krb5_error_code ret;
2877 ret = _krb5_get_krbtgt(context, fast_ccache, NULL, &cred);
2881 ret = krb5_cc_get_config(context, fast_ccache, cred->server,
2882 "fast_avail", &data);
2883 krb5_free_creds(context, cred);
2885 ctx->fast_state.armor_ccache = fast_ccache;
2886 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2887 ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
2889 krb5_set_error_message(context, EINVAL, N_("FAST not available for the KDC in the armor ccache", ""));
2895 static krb5_error_code
2896 validate_pkinit_fx(krb5_context context,
2897 krb5_init_creds_context ctx,
2899 krb5_keyblock *ticket_sessionkey)
2905 pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx);
2908 if (ctx->flags.request_anonymous && ctx->pk_init_ctx) {
2909 /* XXX handle the case where pkinit is not used */
2910 krb5_set_error_message(context, KRB5_KDCREP_MODIFIED,
2911 N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", ""));
2912 return KRB5_KDCREP_MODIFIED;
2918 heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage");
2920 return _krb5_pk_kx_confirm(context,
2922 ctx->fast_state.reply_key,
2927 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2928 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
2929 krb5_init_creds_context ctx,
2930 krb5_const_principal armor_service)
2932 krb5_error_code ret;
2934 if (ctx->fast_state.armor_service)
2935 krb5_free_principal(context, ctx->fast_state.armor_service);
2936 if (armor_service) {
2937 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
2941 ctx->fast_state.armor_service = NULL;
2943 ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE;
2947 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2948 krb5_init_creds_set_fast_anon_pkinit(krb5_context context,
2949 krb5_init_creds_context ctx)
2951 if (ctx->fast_state.armor_ccache)
2954 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2955 ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
2960 available_padata_count(METHOD_DATA *md)
2962 size_t i, count = 0;
2964 for (i = 0; i < md->len; i++) {
2965 PA_DATA *pa = &md->val[i];
2967 if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
2968 pa->padata_type == KRB5_PADATA_FX_ERROR)
2977 static krb5_error_code
2978 init_creds_step(krb5_context context,
2979 krb5_init_creds_context ctx,
2982 krb5_krbhst_info *hostinfo,
2983 unsigned int *flags)
2985 struct timeval start_time, end_time;
2986 krb5_data checksum_data;
2987 krb5_error_code ret;
2992 gettimeofday(&start_time, NULL);
2994 krb5_data_zero(out);
2995 krb5_data_zero(&checksum_data);
2997 if (ctx->as_req.req_body.cname == NULL) {
2998 ret = init_as_req(context, ctx->flags, &ctx->cred,
2999 ctx->addrs, ctx->etypes, &ctx->as_req);
3002 if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
3004 else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE)
3005 /* Check with armor service if there is FAST */;
3007 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3010 /* XXX should happen after we get back reply from KDC */
3011 pa_configure(context, ctx, NULL);
3014 #define MAX_PA_COUNTER 15
3015 if (ctx->pa_counter > MAX_PA_COUNTER) {
3016 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
3017 N_("Looping %d times while getting "
3018 "initial credentials", ""),
3020 return KRB5_GET_IN_TKT_LOOP;
3024 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
3026 /* Lets process the input packet */
3027 if (in && in->length) {
3030 memset(&rep, 0, sizeof(rep));
3032 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
3034 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
3036 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
3042 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
3043 &rep.kdc_rep.ticket, &size, ret);
3046 heim_assert(data.length == size, "ASN.1 internal error");
3048 ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data,
3049 &ctx->fast_state, &rep.kdc_rep);
3050 krb5_data_free(&data);
3055 * Now check and extract the ticket
3058 if (ctx->flags.canonicalize) {
3059 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
3060 eflags |= EXTRACT_TICKET_MATCH_REALM;
3062 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
3063 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
3064 if (ctx->flags.request_anonymous)
3065 eflags |= EXTRACT_TICKET_MATCH_ANON;
3067 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
3068 &ctx->as_req, &rep.kdc_rep,
3069 hostinfo, &ctx->fast_state.reply_key);
3071 free_AS_REP(&rep.kdc_rep);
3075 if (ctx->fast_state.strengthen_key) {
3076 krb5_keyblock result;
3078 _krb5_debug(context, 5, "krb5_get_init_creds: FAST strengthen_key");
3080 ret = _krb5_fast_cf2(context,
3081 ctx->fast_state.strengthen_key,
3083 ctx->fast_state.reply_key,
3088 free_AS_REP(&rep.kdc_rep);
3092 ctx->runflags.allow_save_as_reply_key = 1;
3094 krb5_free_keyblock_contents(context, ctx->fast_state.reply_key);
3095 *ctx->fast_state.reply_key = result;
3098 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
3100 ret = _krb5_extract_ticket(context,
3103 ctx->fast_state.reply_key,
3105 KRB5_KU_AS_REP_ENC_PART,
3114 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
3116 ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session);
3118 ctx->as_enctype = ctx->fast_state.reply_key->keytype;
3120 if (ctx->runflags.allow_save_as_reply_key) {
3121 ctx->as_reply_key = ctx->fast_state.reply_key;
3122 ctx->fast_state.reply_key = NULL;
3124 krb5_free_keyblock(context, ctx->fast_state.reply_key);
3125 ctx->fast_state.reply_key = NULL;
3127 ctx->ic_flags |= KRB5_INIT_CREDS_DONE;
3130 free_AS_REP(&rep.kdc_rep);
3131 free_EncASRepPart(&rep.enc_part);
3133 gettimeofday(&end_time, NULL);
3134 timevalsub(&end_time, &start_time);
3135 timevaladd(&ctx->stats.run_time, &end_time);
3137 _krb5_debug(context, 1, "krb5_get_init_creds: wc: %lld.%06ld",
3138 (long long)ctx->stats.run_time.tv_sec,
3139 (long)ctx->stats.run_time.tv_usec);
3143 /* let's try to parse it as a KRB-ERROR */
3145 _krb5_debug(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC");
3147 free_KRB_ERROR(&ctx->error);
3149 ret = krb5_rd_error(context, in, &ctx->error);
3150 if(ret && in->length && ((char*)in->data)[0] == 4)
3151 ret = KRB5KRB_AP_ERR_V4_REPLY;
3153 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
3158 * Unwrap method-data, if there is any,
3159 * fast_unwrap_error() below might replace it with a
3160 * wrapped version if we are using FAST.
3163 free_METHOD_DATA(&ctx->md);
3164 memset(&ctx->md, 0, sizeof(ctx->md));
3166 if (ctx->error.e_data) {
3167 krb5_error_code ret2;
3169 ret2 = decode_METHOD_DATA(ctx->error.e_data->data,
3170 ctx->error.e_data->length,
3175 * Just ignore any error, the error will be pushed
3176 * out from krb5_error_from_rd_error() if there
3179 _krb5_debug(context, 5, N_("Failed to decode METHOD-DATA", ""));
3184 * Unwrap KRB-ERROR, we are always calling this so that
3185 * FAST can tell us if your peer KDC suddenly dropped FAST
3186 * wrapping and its really an attacker's packet (or a bug
3189 ret = _krb5_fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
3190 &ctx->md, &ctx->error);
3198 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
3200 /* log the failure */
3201 if (_krb5_have_debug(context, 5)) {
3202 const char *str = krb5_get_error_message(context, ret);
3203 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str);
3204 krb5_free_error_message(context, str);
3208 * Handle special error codes
3211 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED
3212 || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED
3213 || ret == KRB5KDC_ERR_ETYPE_NOSUPP)
3216 * If no preauth was set and KDC requires it, give it one
3219 * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop
3220 * one more time since that might mean we are dealing with
3221 * a Windows KDC that is confused about what enctypes are
3225 if (available_padata_count(&ctx->md) == 0) {
3226 krb5_set_error_message(context, ret,
3227 N_("Preauth required but no preauth "
3228 "options send by KDC", ""));
3231 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
3233 * Try adapt to timeskrew when we are using pre-auth, and
3234 * if there was a time skew, try again.
3236 krb5_set_real_time(context, ctx->error.stime, -1);
3237 if (context->kdc_sec_offset)
3240 _krb5_debug(context, 10, "init_creds: err skew updating kdc offset to %d",
3241 context->kdc_sec_offset);
3245 pa_restart(context, ctx);
3247 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
3248 /* client referral to a new realm */
3251 if (ctx->error.crealm == NULL) {
3252 krb5_set_error_message(context, ret,
3253 N_("Got a client referral, not but no realm", ""));
3256 ref_realm = *ctx->error.crealm;
3258 _krb5_debug(context, 5, "krb5_get_init_creds: referral to realm %s",
3262 * If its a krbtgt, lets updat the requested krbtgt too
3264 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
3266 free(ctx->cred.server->name.name_string.val[1]);
3267 ctx->cred.server->name.name_string.val[1] = strdup(ref_realm);
3268 if (ctx->cred.server->name.name_string.val[1] == NULL) {
3269 ret = krb5_enomem(context);
3273 free_PrincipalName(ctx->as_req.req_body.sname);
3274 ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server);
3279 free(ctx->as_req.req_body.realm);
3280 ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm);
3284 ret = krb5_principal_set_realm(context,
3286 *ctx->error.crealm);
3290 ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm);
3292 _krb5_debug(context, 5, "krb5_get_init_creds: got referral to %s", ref_realm);
3293 krb5_xfree(ref_realm);
3296 pa_restart(context, ctx);
3298 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 &&
3299 ctx->runflags.change_password_prompt) {
3302 ctx->runflags.change_password = 1;
3304 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
3306 /* try to avoid recursion */
3307 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
3310 /* don't include prompter in runtime */
3311 gettimeofday(&end_time, NULL);
3312 timevalsub(&end_time, &start_time);
3313 timevaladd(&ctx->stats.run_time, &end_time);
3315 ret = change_password(context,
3326 gettimeofday(&start_time, NULL);
3328 krb5_init_creds_set_password(context, ctx, buf2);
3330 pa_restart(context, ctx);
3332 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
3335 * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP,
3336 * so drop it and try again. But only try that for MIT
3337 * Kerberos servers by keying of no METHOD-DATA.
3339 if (ctx->runflags.allow_enc_pa_rep) {
3340 if (ctx->md.len != 0) {
3341 _krb5_debug(context, 10, "Server sent PA data with KRB-ERROR, "
3342 "so not a pre 1.7 MIT KDC and won't retry w/o ENC-PA-REQ");
3345 _krb5_debug(context, 10, "Disabling allow_enc_pa_rep and trying again");
3346 ctx->runflags.allow_enc_pa_rep = 0;
3350 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) {
3351 _krb5_debug(context, 10, "FAST disabled and got preauth failed");
3355 if ((ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) == 0) {
3356 _krb5_debug(context, 10, "Preauth failed");
3360 _krb5_debug(context, 10, "preauth failed with optimistic FAST, trying w/o FAST");
3362 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3363 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3366 pa_restart(context, ctx);
3368 } else if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) {
3369 _krb5_debug(context, 10,
3370 "Some other error %d failed with optimistic FAST, trying w/o FAST", ret);
3372 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3373 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3374 pa_restart(context, ctx);
3376 /* some other error code from the KDC, lets' return it to the user */
3382 if (ctx->as_req.padata) {
3383 free_METHOD_DATA(ctx->as_req.padata);
3384 free(ctx->as_req.padata);
3385 ctx->as_req.padata = NULL;
3388 ret = _krb5_fast_create_armor(context, &ctx->fast_state,
3389 ctx->cred.client->realm);
3393 /* Set a new nonce. */
3394 ctx->as_req.req_body.nonce = ctx->nonce;
3398 * Step and announce PA-DATA
3401 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
3402 &ctx->md, &ctx->as_req.padata);
3410 ret = copy_AS_REQ(&ctx->as_req, &req2);
3414 ret = _krb5_fast_wrap_req(context,
3419 krb5_data_free(&checksum_data);
3425 krb5_data_free(&ctx->req_buffer);
3427 ASN1_MALLOC_ENCODE(AS_REQ,
3428 ctx->req_buffer.data, ctx->req_buffer.length,
3433 if(len != ctx->req_buffer.length)
3434 krb5_abortx(context, "internal error in ASN.1 encoder");
3436 out->data = ctx->req_buffer.data;
3437 out->length = ctx->req_buffer.length;
3439 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
3441 gettimeofday(&end_time, NULL);
3442 timevalsub(&end_time, &start_time);
3443 timevaladd(&ctx->stats.run_time, &end_time);
3451 * The core loop if krb5_get_init_creds() function family. Create the
3452 * packets and have the caller send them off to the KDC.
3454 * If the caller want all work been done for them, use
3455 * krb5_init_creds_get() instead.
3457 * @param context a Kerberos 5 context.
3458 * @param ctx ctx krb5_init_creds_context context.
3459 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
3460 * @param out reply to KDC.
3461 * @param hostinfo KDC address info, first round it can be NULL.
3462 * @param flags status of the round, if
3463 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
3465 * @return 0 for success, or an Kerberos 5 error code, see
3466 * krb5_get_error_message().
3468 * @ingroup krb5_credential
3471 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3472 krb5_init_creds_step(krb5_context context,
3473 krb5_init_creds_context ctx,
3476 krb5_krbhst_info *hostinfo,
3477 unsigned int *flags)
3479 krb5_error_code ret;
3482 krb5_data_zero(&empty);
3484 if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) &&
3485 ctx->fast_state.armor_ccache == NULL) {
3486 ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state,
3487 in, out, hostinfo, flags);
3489 ((*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) ||
3496 return init_creds_step(context, ctx, in, out, hostinfo, flags);
3500 * Extract the newly acquired credentials from krb5_init_creds_context
3503 * @param context A Kerberos 5 context.
3505 * @param cred credentials, free with krb5_free_cred_contents().
3507 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3510 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3511 krb5_init_creds_get_creds(krb5_context context,
3512 krb5_init_creds_context ctx,
3515 return krb5_copy_creds_contents(context, &ctx->cred, cred);
3519 * Extract the as-reply key from the context.
3521 * Only allowed when the as-reply-key is not directly derived from the
3522 * password like PK-INIT, GSS, FAST hardened key, etc.
3524 * @param context A Kerberos 5 context.
3525 * @param ctx ctx krb5_init_creds_context context.
3526 * @param as_reply_key keyblock, free with krb5_free_keyblock_contents().
3528 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3531 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3532 krb5_init_creds_get_as_reply_key(krb5_context context,
3533 krb5_init_creds_context ctx,
3534 krb5_keyblock *as_reply_key)
3536 if (ctx->as_reply_key == NULL)
3537 return KRB5KDC_ERR_PREAUTH_REQUIRED;
3538 return krb5_copy_keyblock_contents(context, ctx->as_reply_key, as_reply_key);
3541 KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3542 _krb5_init_creds_get_cred_starttime(krb5_context context, krb5_init_creds_context ctx)
3544 return ctx->cred.times.starttime;
3547 KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3548 _krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx)
3550 return ctx->cred.times.endtime;
3553 KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL
3554 _krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx)
3556 return ctx->cred.client;
3560 * Get the last error from the transaction.
3562 * @return Returns 0 or an error code
3564 * @ingroup krb5_credential
3567 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3568 krb5_init_creds_get_error(krb5_context context,
3569 krb5_init_creds_context ctx,
3572 krb5_error_code ret;
3574 ret = copy_KRB_ERROR(&ctx->error, error);
3576 krb5_enomem(context);
3584 * @param context A Kerberos 5 context.
3585 * @param ctx The krb5_init_creds_context to free.
3588 * @return Returns 0 or an error code
3590 * @ingroup krb5_credential
3593 krb5_error_code KRB5_LIB_FUNCTION
3594 krb5_init_creds_store_config(krb5_context context,
3595 krb5_init_creds_context ctx,
3598 krb5_error_code ret;
3600 if (ctx->kdc_hostname) {
3602 data.length = strlen(ctx->kdc_hostname);
3603 data.data = ctx->kdc_hostname;
3605 ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data);
3609 if (ctx->sitename) {
3611 data.length = strlen(ctx->sitename);
3612 data.data = ctx->sitename;
3614 ret = krb5_cc_set_config(context, id, NULL, "sitename", &data);
3624 * @ingroup krb5_credential
3628 krb5_init_creds_store(krb5_context context,
3629 krb5_init_creds_context ctx,
3632 krb5_error_code ret;
3634 if (ctx->cred.client == NULL) {
3635 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
3636 krb5_set_error_message(context, ret, "init creds not completed yet");
3640 ret = krb5_cc_initialize(context, id, ctx->cred.client);
3644 ret = krb5_cc_store_cred(context, id, &ctx->cred);
3648 if (ctx->cred.flags.b.enc_pa_rep) {
3649 krb5_data data = { 3, rk_UNCONST("yes") };
3650 ret = krb5_cc_set_config(context, id, ctx->cred.server,
3651 "fast_avail", &data);
3660 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
3662 * @param context A Kerberos 5 context.
3663 * @param ctx The krb5_init_creds_context to free.
3665 * @ingroup krb5_credential
3668 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3669 krb5_init_creds_free(krb5_context context,
3670 krb5_init_creds_context ctx)
3672 free_init_creds_ctx(context, ctx);
3677 * Get new credentials as setup by the krb5_init_creds_context.
3679 * @param context A Kerberos 5 context.
3680 * @param ctx The krb5_init_creds_context to process.
3682 * @ingroup krb5_credential
3685 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3686 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
3688 krb5_sendto_ctx stctx = NULL;
3689 krb5_krbhst_info *hostinfo = NULL;
3690 krb5_error_code ret;
3692 unsigned int flags = 0;
3694 krb5_data_zero(&in);
3695 krb5_data_zero(&out);
3697 ret = krb5_sendto_ctx_alloc(context, &stctx);
3700 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
3702 if (ctx->kdc_hostname)
3703 krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname);
3705 krb5_sendto_set_sitename(context, stctx, ctx->sitename);
3708 struct timeval nstart, nend;
3711 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
3712 krb5_data_free(&in);
3716 if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
3719 gettimeofday(&nstart, NULL);
3721 ret = krb5_sendto_context (context, stctx, &out,
3722 ctx->cred.client->realm, &in);
3726 gettimeofday(&nend, NULL);
3727 timevalsub(&nend, &nstart);
3728 timevaladd(&ctx->stats.run_time, &nend);
3733 krb5_sendto_ctx_free(context, stctx);
3739 * Get new credentials using password.
3741 * @ingroup krb5_credential
3745 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3746 krb5_get_init_creds_password(krb5_context context,
3748 krb5_principal client,
3749 const char *password,
3750 krb5_prompter_fct prompter,
3752 krb5_deltat start_time,
3753 const char *in_tkt_service,
3754 krb5_get_init_creds_opt *options)
3756 krb5_init_creds_context ctx;
3757 char buf[BUFSIZ], buf2[BUFSIZ];
3758 krb5_error_code ret;
3762 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
3766 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3770 if (prompter != NULL && ctx->password == NULL && password == NULL) {
3772 krb5_data password_data;
3776 ret = krb5_unparse_name(context, client, &p);
3780 aret = asprintf(&q, "%s's Password: ", p);
3782 if (aret == -1 || q == NULL) {
3783 ret = krb5_enomem(context);
3787 password_data.data = buf;
3788 password_data.length = sizeof(buf);
3790 prompt.reply = &password_data;
3791 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
3793 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
3796 memset_s(buf, sizeof(buf), 0, sizeof(buf));
3797 ret = KRB5_LIBOS_PWDINTR;
3798 krb5_clear_error_message (context);
3801 password = password_data.data;
3805 ret = krb5_init_creds_set_password(context, ctx, password);
3810 ret = krb5_init_creds_get(context, ctx);
3813 krb5_process_last_request(context, options, ctx);
3816 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
3817 /* try to avoid recursion */
3818 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
3821 /* don't try to change password if no prompter or prompting disabled */
3822 if (!ctx->runflags.change_password_prompt)
3825 ret = change_password (context,
3837 krb5_init_creds_free(context, ctx);
3843 krb5_init_creds_get_creds(context, ctx, creds);
3846 krb5_init_creds_free(context, ctx);
3848 memset_s(buf, sizeof(buf), 0, sizeof(buf));
3849 memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
3854 * Get new credentials using keyblock.
3856 * @ingroup krb5_credential
3859 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3860 krb5_get_init_creds_keyblock(krb5_context context,
3862 krb5_principal client,
3863 krb5_keyblock *keyblock,
3864 krb5_deltat start_time,
3865 const char *in_tkt_service,
3866 krb5_get_init_creds_opt *options)
3868 krb5_init_creds_context ctx;
3869 krb5_error_code ret;
3871 memset(creds, 0, sizeof(*creds));
3873 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3877 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3881 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
3885 ret = krb5_init_creds_get(context, ctx);
3888 krb5_process_last_request(context, options, ctx);
3892 krb5_init_creds_get_creds(context, ctx, creds);
3895 krb5_init_creds_free(context, ctx);
3901 * Get new credentials using keytab.
3903 * @ingroup krb5_credential
3906 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3907 krb5_get_init_creds_keytab(krb5_context context,
3909 krb5_principal client,
3911 krb5_deltat start_time,
3912 const char *in_tkt_service,
3913 krb5_get_init_creds_opt *options)
3915 krb5_init_creds_context ctx;
3916 krb5_keytab_entry ktent;
3917 krb5_error_code ret;
3919 memset(&ktent, 0, sizeof(ktent));
3920 memset(creds, 0, sizeof(*creds));
3922 if (strcmp(client->realm, "") == 0) {
3924 * Referral realm. We have a keytab, so pick a realm by
3925 * matching in the keytab.
3927 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
3929 client = ktent.principal;
3932 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3936 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3940 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
3944 ret = krb5_init_creds_get(context, ctx);
3946 krb5_process_last_request(context, options, ctx);
3949 krb5_kt_free_entry(context, &ktent);
3951 krb5_init_creds_get_creds(context, ctx, creds);
3954 krb5_init_creds_free(context, ctx);
3959 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3960 _krb5_init_creds_set_gss_mechanism(krb5_context context,
3961 krb5_gss_init_ctx gssic,
3962 const struct gss_OID_desc_struct *gss_mech)
3964 gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
3967 KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
3968 _krb5_init_creds_get_gss_mechanism(krb5_context context,
3969 krb5_gss_init_ctx gssic)
3974 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3975 _krb5_init_creds_set_gss_cred(krb5_context context,
3976 krb5_gss_init_ctx gssic,
3977 struct gss_cred_id_t_desc_struct *gss_cred)
3979 if (gssic->cred != gss_cred && gssic->flags.release_cred)
3980 gssic->release_cred(context, gssic, gssic->cred);
3982 gssic->cred = gss_cred;
3983 gssic->flags.release_cred = 1;
3986 KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
3987 _krb5_init_creds_get_gss_cred(krb5_context context,
3988 krb5_gss_init_ctx gssic)
3993 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3994 _krb5_init_creds_init_gss(krb5_context context,
3995 krb5_init_creds_context ctx,
3996 krb5_gssic_step step,
3997 krb5_gssic_finish finish,
3998 krb5_gssic_release_cred release_cred,
3999 krb5_gssic_delete_sec_context delete_sec_context,
4000 const struct gss_cred_id_t_desc_struct *gss_cred,
4001 const struct gss_OID_desc_struct *gss_mech,
4004 krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
4006 gssic = calloc(1, sizeof(*gssic));
4008 return krb5_enomem(context);
4010 if (ctx->gss_init_ctx)
4011 free_gss_init_ctx(context, ctx->gss_init_ctx);
4012 ctx->gss_init_ctx = gssic;
4014 gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
4015 gssic->mech = gss_mech;
4016 if (flags & KRB5_GSS_IC_FLAG_RELEASE_CRED)
4017 gssic->flags.release_cred = 1;
4020 gssic->finish = finish;
4021 gssic->release_cred = release_cred;
4022 gssic->delete_sec_context = delete_sec_context;