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;
496 ctx
->pre_auth_types
= NULL
;
499 ctx
->pre_auth_types
= NULL
;
501 ret
= init_cred(context
, &ctx
->cred
, client
, start_time
, options
);
504 krb5_get_init_creds_opt_free(context
, default_opt
);
508 ret
= krb5_init_creds_set_service(context
, ctx
, NULL
);
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
;
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
;
533 case KRB5_INIT_CREDS_TRISTATE_FALSE
:
536 case KRB5_INIT_CREDS_TRISTATE_TRUE
:
537 ctx
->addrs
= &no_addrs
;
541 if (options
->flags
& KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST
) {
545 etypes
= malloc((options
->etype_list_length
+ 1)
546 * sizeof(krb5_enctype
));
547 if (etypes
== NULL
) {
548 ret
= krb5_enomem(context
);
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
);
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;
578 ctx
->runflags
.change_password_prompt
= ctx
->prompter
!= NULL
;
581 krb5_get_init_creds_opt_free(context
, default_opt
);
586 krb5_get_init_creds_opt_free(context
, default_opt
);
590 static krb5_error_code
591 change_password (krb5_context context
,
592 krb5_principal client
,
593 const char *password
,
596 krb5_prompter_fct prompter
,
598 krb5_get_init_creds_opt
*old_options
)
600 krb5_prompt prompts
[2];
603 char buf1
[BUFSIZ
], buf2
[BUFSIZ
];
604 krb5_data password_data
[2];
606 krb5_data result_code_string
;
607 krb5_data result_string
;
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
);
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
);
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
);
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
,
643 krb5_get_init_creds_opt_free(context
, options
);
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",
667 memset (buf1
, 0, sizeof(buf1
));
668 memset (buf2
, 0, sizeof(buf2
));
672 if (strcmp (buf1
, buf2
) == 0)
674 memset (buf1
, 0, sizeof(buf1
));
675 memset (buf2
, 0, sizeof(buf2
));
678 ret
= krb5_set_password (context
,
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
);
697 /* return the result */
698 (*prompter
) (context
, data
, NULL
, p
, 0, NULL
);
700 if (result_code
== 0) {
701 strlcpy (newpw
, buf1
, newpw_sz
);
704 krb5_set_error_message(context
, ret
= KRB5_CHPW_FAIL
,
705 N_("failed changing password: %s", ""), p
);
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
);
719 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
720 krb5_keyblock_key_proc (krb5_context context
,
723 krb5_const_pointer keyseed
,
726 return krb5_copy_keyblock (context
, keyseed
, key
);
733 static krb5_error_code
734 init_as_req (krb5_context context
,
736 const krb5_creds
*creds
,
737 const krb5_addresses
*addrs
,
738 const krb5_enctype
*etypes
,
743 memset(a
, 0, sizeof(*a
));
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
);
753 a
->req_body
.sname
= calloc(1, sizeof(*a
->req_body
.sname
));
754 if (a
->req_body
.sname
== NULL
) {
755 ret
= krb5_enomem(context
);
759 ret
= _krb5_principal2principalname (a
->req_body
.cname
, creds
->client
);
762 ret
= copy_Realm(&creds
->client
->realm
, &a
->req_body
.realm
);
766 ret
= _krb5_principal2principalname (a
->req_body
.sname
, creds
->server
);
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
);
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
;
782 ret
= krb5_enomem(context
);
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
);
792 *a
->req_body
.rtime
= creds
->times
.renew_till
;
794 a
->req_body
.nonce
= 0;
795 ret
= _krb5_init_etype(context
,
797 &a
->req_body
.etype
.len
,
798 &a
->req_body
.etype
.val
,
804 * This means no addresses
807 if (addrs
&& addrs
->len
== 0) {
808 a
->req_body
.addresses
= NULL
;
810 a
->req_body
.addresses
= malloc(sizeof(*a
->req_body
.addresses
));
811 if (a
->req_body
.addresses
== NULL
) {
812 ret
= krb5_enomem(context
);
817 ret
= krb5_copy_addresses(context
, addrs
, a
->req_body
.addresses
);
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
;
829 a
->req_body
.enc_authorization_data
= NULL
;
830 a
->req_body
.additional_tickets
= NULL
;
837 memset_s(a
, sizeof(*a
), 0, sizeof(*a
));
842 static krb5_error_code
843 set_paid(struct pa_info_data
*paid
, krb5_context context
,
845 krb5_salttype salttype
, void *salt_string
, size_t salt_len
,
846 krb5_data
*s2kparams
)
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
;
861 ret
= krb5_copy_data(context
, s2kparams
, &paid
->s2kparams
);
863 krb5_clear_error_message(context
);
864 krb5_free_salt(context
, paid
->salt
);
868 paid
->s2kparams
= NULL
;
873 static struct pa_info_data
*
874 pa_etype_info2(krb5_context context
,
875 const krb5_principal client
,
877 struct pa_info_data
*paid
,
878 heim_octet_string
*data
)
885 memset(&e
, 0, sizeof(e
));
886 ret
= decode_ETYPE_INFO2(data
->data
, data
->length
, &e
, &sz
);
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)
897 if (asreq
->req_body
.etype
.val
[j
] == e
.val
[i
].etype
) {
899 if (e
.val
[i
].salt
== NULL
)
900 ret
= krb5_get_pw_salt(context
, client
, &salt
);
902 salt
.saltvalue
.data
= *e
.val
[i
].salt
;
903 salt
.saltvalue
.length
= strlen(*e
.val
[i
].salt
);
907 ret
= set_paid(paid
, context
, e
.val
[i
].etype
,
910 salt
.saltvalue
.length
,
912 if (e
.val
[i
].salt
== NULL
)
913 krb5_free_salt(context
, salt
);
915 free_ETYPE_INFO2(&e
);
922 free_ETYPE_INFO2(&e
);
926 static struct pa_info_data
*
927 pa_etype_info(krb5_context context
,
928 const krb5_principal client
,
930 struct pa_info_data
*paid
,
931 heim_octet_string
*data
)
938 memset(&e
, 0, sizeof(e
));
939 ret
= decode_ETYPE_INFO(data
->data
, data
->length
, &e
, &sz
);
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)
950 if (asreq
->req_body
.etype
.val
[j
] == e
.val
[i
].etype
) {
952 salt
.salttype
= KRB5_PW_SALT
;
953 if (e
.val
[i
].salt
== NULL
)
954 ret
= krb5_get_pw_salt(context
, client
, &salt
);
956 salt
.saltvalue
= *e
.val
[i
].salt
;
959 if (e
.val
[i
].salttype
)
960 salt
.salttype
= *e
.val
[i
].salttype
;
962 ret
= set_paid(paid
, context
, e
.val
[i
].etype
,
965 salt
.saltvalue
.length
,
967 if (e
.val
[i
].salt
== NULL
)
968 krb5_free_salt(context
, salt
);
982 static struct pa_info_data
*
983 pa_pw_or_afs3_salt(krb5_context context
,
984 const krb5_principal client
,
986 struct pa_info_data
*paid
,
987 heim_octet_string
*data
)
990 if (paid
->etype
== KRB5_ENCTYPE_NULL
)
992 if (krb5_enctype_valid(context
, paid
->etype
) != 0)
995 ret
= set_paid(paid
, context
,
1007 static krb5_error_code
1008 make_pa_enc_timestamp(krb5_context context
, METHOD_DATA
*md
,
1009 krb5_enctype etype
, krb5_keyblock
*key
)
1015 EncryptedData encdata
;
1016 krb5_error_code ret
;
1021 krb5_us_timeofday (context
, &p
.patimestamp
, &usec
);
1025 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC
, buf
, buf_size
, &p
, &len
, ret
);
1029 krb5_abortx(context
, "internal error in ASN.1 encoder");
1031 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
1036 ret
= krb5_encrypt_EncryptedData(context
,
1038 KRB5_KU_PA_ENC_TIMESTAMP
,
1044 krb5_crypto_destroy(context
, crypto
);
1048 ASN1_MALLOC_ENCODE(EncryptedData
, buf
, buf_size
, &encdata
, &len
, ret
);
1049 free_EncryptedData(&encdata
);
1053 krb5_abortx(context
, "internal error in ASN.1 encoder");
1055 ret
= krb5_padata_add(context
, md
, KRB5_PADATA_ENC_TIMESTAMP
, buf
, len
);
1061 static krb5_error_code
1062 add_enc_ts_padata(krb5_context context
,
1064 krb5_principal client
,
1065 krb5_s2k_proc keyproc
,
1066 krb5_const_pointer keyseed
,
1067 krb5_enctype
*enctypes
,
1070 krb5_data
*s2kparams
)
1072 krb5_error_code ret
;
1077 memset(&salt2
, 0, sizeof(salt2
));
1080 /* default to standard salt */
1081 ret
= krb5_get_pw_salt (context
, client
, &salt2
);
1087 enctypes
= context
->etypes
;
1089 for (ep
= enctypes
; *ep
!= (krb5_enctype
)ETYPE_NULL
; ep
++)
1093 for (i
= 0; i
< netypes
; ++i
) {
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
);
1102 ret
= make_pa_enc_timestamp (context
, md
, enctypes
[i
], key
);
1103 krb5_free_keyblock (context
, key
);
1108 krb5_free_salt(context
, salt2
);
1112 static krb5_error_code
1113 pa_data_to_md_ts_enc(krb5_context context
,
1115 const krb5_principal client
,
1116 krb5_init_creds_context ctx
,
1117 struct pa_info_data
*ppaid
,
1120 if (ctx
->keyproc
== NULL
|| ctx
->keyseed
== NULL
)
1124 add_enc_ts_padata(context
, md
, client
,
1125 ctx
->keyproc
, ctx
->keyseed
,
1127 &ppaid
->salt
, ppaid
->s2kparams
);
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
,
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
,
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
,
1155 krb5_data
*s2kparams
,
1157 krb5_keyblock
**key
)
1159 krb5_error_code ret
;
1161 ret
= (*ctx
->keyproc
)(context
, etype
, ctx
->keyseed
,
1162 salt
, s2kparams
, key
);
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
,
1175 const krb5_principal client
,
1177 krb5_init_creds_context ctx
,
1180 if (ctx
->pk_init_ctx
== NULL
)
1183 return _krb5_pk_mk_padata(context
,
1191 krb5_set_error_message(context
, EINVAL
,
1192 N_("no support for PKINIT compiled in", ""));
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
;
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
;
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
;
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");
1237 ret
= pa_data_to_md_pkinit(context
, a
, ctx
->cred
.client
,
1238 (pkinit_ctx
->win2k
!= 0),
1241 ret
= HEIM_ERR_PA_CONTINUE_NEEDED
;
1243 pkinit_ctx
->used_pkinit
= 1;
1246 ret
= _krb5_pk_rd_pa_reply(context
,
1249 rep
->enc_part
.etype
,
1254 &ctx
->fast_state
.reply_key
);
1256 ctx
->runflags
.allow_save_as_reply_key
= 1;
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
;
1276 static krb5_error_code
1277 pa_gss_configure(krb5_context context
,
1278 krb5_init_creds_context ctx
,
1281 krb5_gss_init_ctx gssic
= ctx
->gss_init_ctx
;
1282 struct pa_gss_context
*pa_gss_ctx
= pa_ctx
;
1285 return HEIM_ERR_PA_CANT_CONTINUE
;
1287 pa_gss_ctx
->context_handle
= NULL
;
1288 pa_gss_ctx
->open
= 0;
1293 static krb5_error_code
1294 pa_data_to_md_gss(krb5_context context
,
1296 const krb5_creds
*creds
,
1297 krb5_init_creds_context ctx
,
1298 struct pa_gss_context
*pa_gss_ctx
,
1300 METHOD_DATA
*out_md
)
1302 krb5_error_code ret
;
1303 krb5_gss_init_ctx gssic
= ctx
->gss_init_ctx
;
1305 krb5_data
*input_token
, output_token
;
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
);
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
))
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
;
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
);
1362 krb5_data_zero(&output_token
);
1366 krb5_data_free(&output_token
);
1367 krb5_data_free(&req_body
);
1372 static krb5_error_code
1373 pa_gss_step(krb5_context context
,
1374 krb5_init_creds_context ctx
,
1379 const krb5_krbhst_info
*hi
,
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 */
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
);
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",
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
1442 krb5_free_principal(context
, ctx
->cred
.client
);
1443 ctx
->cred
.client
= cname
;
1445 ctx
->ic_flags
|= KRB5_INIT_CREDS_NO_C_CANON_CHECK
;
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;
1459 static krb5_error_code
1460 pa_gss_restart(krb5_context context
,
1461 krb5_init_creds_context ctx
,
1464 krb5_gss_init_ctx gssic
= ctx
->gss_init_ctx
;
1465 struct pa_gss_context
*pa_gss_ctx
= pa_ctx
;
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;
1478 pa_gss_release(void *pa_ctx
)
1483 _krb5_make_pa_enc_challenge(krb5_context context
,
1485 krb5_key_usage usage
,
1492 EncryptedData encdata
;
1493 krb5_error_code ret
;
1497 krb5_us_timeofday (context
, &p
.patimestamp
, &usec
);
1501 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC
, buf
, buf_size
, &p
, &len
, ret
);
1505 krb5_abortx(context
, "internal error in ASN.1 encoder");
1507 ret
= krb5_encrypt_EncryptedData(context
,
1518 ASN1_MALLOC_ENCODE(EncryptedData
, buf
, buf_size
, &encdata
, &len
, ret
);
1519 free_EncryptedData(&encdata
);
1523 krb5_abortx(context
, "internal error in ASN.1 encoder");
1525 ret
= krb5_padata_add(context
, md
, KRB5_PADATA_ENCRYPTED_CHALLENGE
, buf
, len
);
1532 _krb5_validate_pa_enc_challenge(krb5_context context
,
1534 krb5_key_usage usage
,
1535 EncryptedData
*enc_data
,
1536 const char *peer_name
)
1538 krb5_error_code ret
;
1545 ret
= krb5_decrypt_EncryptedData(context
, crypto
, usage
, enc_data
, &ts_data
);
1549 ret
= decode_PA_ENC_TS_ENC(ts_data
.data
,
1553 krb5_data_free(&ts_data
);
1555 ret
= KRB5KDC_ERR_PREAUTH_FAILED
;
1556 _krb5_debug(context
, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name
);
1560 krb5_us_timeofday(context
, ×tamp
, &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",
1572 (unsigned)krb5_time_abs(timestamp
, p
.patimestamp
),
1573 (int)context
->max_skew
,
1580 free_PA_ENC_TS_ENC(&p
);
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
));
1604 paid
.etype
= KRB5_ENCTYPE_NULL
;
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
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
);
1630 _krb5_debug(context
, 5, "enc-chal: failed to build key");
1634 ret
= krb5_crypto_init(context
, ctx
->fast_state
.reply_key
, 0, &crypto
);
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
,
1648 krb5_crypto_destroy(context
, crypto
);
1652 ret
= krb5_crypto_init(context
, &challengekey
, 0, &crypto
);
1653 krb5_free_keyblock_contents(context
, &challengekey
);
1658 EncryptedData enc_data
;
1662 _krb5_debug(context
, 5, "enc-chal: failed to create reply key");
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
;
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
,
1685 ret
= KRB5KRB_AP_ERR_BAD_INTEGRITY
;
1686 _krb5_debug(context
, 5, "Failed to decode ENC_CHAL KDC reply");
1690 ret
= _krb5_validate_pa_enc_challenge(context
, crypto
,
1691 KRB5_KU_ENC_CHALLENGE_KDC
,
1694 free_EncryptedData(&enc_data
);
1695 krb5_crypto_destroy(context
, crypto
);
1701 ret
= _krb5_make_pa_enc_challenge(context
, crypto
,
1702 KRB5_KU_ENC_CHALLENGE_CLIENT
,
1704 krb5_crypto_destroy(context
, crypto
);
1706 _krb5_debug(context
, 5, "enc-chal: failed build enc challenge");
1710 return HEIM_ERR_PA_CONTINUE_NEEDED
;
1714 struct enc_ts_context
{
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
);
1732 static krb5_error_code
1733 enc_ts_step(krb5_context context
, krb5_init_creds_context ctx
, void *pa_ctx
, PA_DATA
*pa
,
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
;
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
);
1756 if (pactx
->user
== NULL
) {
1757 ret
= krb5_copy_principal(context
, ctx
->cred
.client
, &pactx
->user
);
1762 memset(&paid
, 0, sizeof(paid
));
1765 paid
.etype
= KRB5_ENCTYPE_NULL
;
1767 paid
.etype
= rep
->enc_part
.etype
;
1769 ppaid
= process_pa_info(context
, ctx
->cred
.client
, a
, &paid
, in_md
);
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
) {
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
);
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
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
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
1826 if (pactx
->used_pa_types
& USED_ENC_TS_INFO
) {
1827 flag
= USED_ENC_TS_RENEG
;
1830 flag
= USED_ENC_TS_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
);
1846 ret
= pa_data_to_md_ts_enc(context
, a
, ctx
->cred
.client
, ctx
, ppaid
, out_md
);
1850 return HEIM_ERR_PA_CONTINUE_NEEDED
;
1854 enc_ts_release(void *pa_ctx
)
1856 struct enc_ts_context
*pactx
= (struct enc_ts_context
*)pa_ctx
;
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
;
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;
1878 case KRB5_INIT_CREDS_TRISTATE_FALSE
:
1879 req
.include_pac
= 0;
1882 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST
, buf
, length
,
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
);
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);
1906 static krb5_error_code
1907 pa_fx_cookie_step(krb5_context context
,
1908 krb5_init_creds_context ctx
,
1913 const krb5_krbhst_info
*hi
,
1915 METHOD_DATA
*out_md
)
1917 krb5_error_code ret
;
1922 pad
= krb5_find_padata(in_md
->val
, in_md
->len
, KRB5_PADATA_FX_COOKIE
, &idx
);
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
;
1934 cookie
= malloc(pad
->padata_value
.length
);
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
);
1945 _krb5_debug(context
, 5, "Mirrored FX-COOKIE to KDC");
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 *);
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 */
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
1978 pa_release_f release
;
1981 KRB5_PADATA_PK_AS_REP
,
1983 PA_F_FAST
| PA_F_NOT_FAST
,
1984 sizeof(struct pkinit_context
),
1986 pkinit_configure_ietf
,
1992 KRB5_PADATA_PK_AS_REP_19
,
1994 PA_F_FAST
| PA_F_NOT_FAST
,
1995 sizeof(struct pkinit_context
),
1997 pkinit_configure_win
,
2005 PA_F_FAST
| PA_F_NOT_FAST
,
2006 sizeof(struct pa_gss_context
),
2014 KRB5_PADATA_ENCRYPTED_CHALLENGE
,
2015 "ENCRYPTED_CHALLENGE",
2025 KRB5_PADATA_ENC_TIMESTAMP
,
2026 "ENCRYPTED_TIMESTAMP",
2028 sizeof(struct enc_ts_context
),
2036 KRB5_PADATA_PA_PAC_REQUEST
,
2047 KRB5_PADATA_REQ_ENC_PA_REP
,
2058 KRB5_PADATA_FX_COOKIE
,
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
),
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
)
2086 get_pa_type_name(int type
)
2089 for (n
= 0; n
< sizeof(patypes
)/sizeof(patypes
[0]); n
++)
2090 if (type
== patypes
[n
].type
)
2091 return patypes
[n
].name
;
2099 struct pa_auth_mech
{
2100 struct patype
*patype
;
2101 struct pa_auth_mech
*next
; /* when doing authentication sets */
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
,
2116 struct pa_info_data
*p
= NULL
;
2123 for (i
= 0; p
== NULL
&& i
< sizeof(patypes
)/sizeof(patypes
[0]); i
++) {
2126 if (patypes
[i
].salt_info
== NULL
)
2129 pa
= krb5_find_padata(md
->val
, md
->len
, patypes
[i
].type
, &idx
);
2133 paid
->salt
.salttype
= (krb5_salttype
)patypes
[i
].type
;
2134 p
= patypes
[i
].salt_info(context
, client
, asreq
, paid
, &pa
->padata_value
);
2140 pa_announce(krb5_context context
,
2142 krb5_init_creds_context ctx
,
2144 METHOD_DATA
*out_md
)
2148 for (n
= 0; n
< sizeof(patypes
)/sizeof(patypes
[0]); n
++) {
2149 if ((patypes
[n
].flags
& types
) == 0)
2152 if (patypes
[n
].step
)
2153 patypes
[n
].step(context
, ctx
, NULL
, NULL
, NULL
, NULL
, NULL
, in_md
, out_md
);
2155 krb5_padata_add(context
, out_md
, patypes
[n
].type
, NULL
, 0);
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",
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
;
2186 for (n
= 0; patype
== NULL
&& n
< sizeof(patypes
)/sizeof(patypes
[0]); n
++) {
2187 if (patypes
[n
].type
== pa_type
)
2188 patype
= &patypes
[n
];
2193 pa_mech
= _heim_alloc_object(&pa_auth_mech_object
, sizeof(*pa_mech
) - 1 + patype
->pa_ctx_size
);
2194 if (pa_mech
== 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]);
2204 heim_release(pa_mech
);
2209 _krb5_debug(context
, 5, "Adding PA mech: %s", patype
->name
);
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
);
2221 heim_array_append_value(ctx
->available_pa_mechs
, mech
);
2226 static krb5_error_code
2227 pa_configure(krb5_context context
,
2228 krb5_init_creds_context ctx
,
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 */
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]);
2260 static krb5_error_code
2261 pa_step(krb5_context context
,
2262 krb5_init_creds_context ctx
,
2265 const krb5_krbhst_info
*hi
,
2267 METHOD_DATA
*out_md
)
2269 krb5_error_code ret
;
2275 if (ctx
->pa_mech
== NULL
) {
2276 size_t len
= heim_array_get_length(ctx
->available_pa_mechs
);
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
;
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
;
2304 _krb5_debug(context
, 0, "pa-mech trying: %s, searching for %d",
2305 ctx
->pa_mech
->patype
->name
, ctx
->pa_mech
->patype
->type
);
2309 pa
= krb5_find_padata(in_md
->val
, in_md
->len
, ctx
->pa_mech
->patype
->type
, &idx
);
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
);
2320 struct pa_auth_mech
*next_pa
= ctx
->pa_mech
->next
;
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
;
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
) {
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
;
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
;
2358 log_kdc_pa_types(krb5_context context
, METHOD_DATA
*in_md
)
2360 if (_krb5_have_debug(context
, 5)) {
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
,
2378 krb5_init_creds_context ctx
,
2380 METHOD_DATA
**out_md
)
2382 krb5_error_code ret
;
2385 if (*out_md
== NULL
) {
2386 return krb5_enomem(context
);
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");
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) {
2421 static krb5_error_code
2422 process_pa_data_to_key(krb5_context context
,
2423 krb5_init_creds_context ctx
,
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
));
2437 log_kdc_pa_types(context
, rep
->padata
);
2441 ppaid
= process_pa_info(context
, creds
->client
, a
, &paid
,
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
);
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 ?");
2458 } else if (ret
== 0) {
2459 _krb5_debug(context
, 0, "final pamech done step");
2465 free_paid(context
, &paid
);
2473 static krb5_error_code
2474 capture_lkdc_domain(krb5_context context
,
2475 krb5_init_creds_context ctx
)
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
] != ':')
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",
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().
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
;
2525 ctx
= calloc(1, sizeof(*ctx
));
2527 return krb5_enomem(context
);
2529 ret
= get_init_creds_common(context
, client
, prompter
, prompter_data
,
2530 start_time
, options
, ctx
);
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
);
2549 free_init_creds_ctx(context
, ctx
);
2553 ctx
->runflags
.allow_enc_pa_rep
= 1;
2555 ctx
->fast_state
.flags
|= KRB5_FAST_AS_REQ
;
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
);
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
)
2598 free(ctx
->sitename
);
2599 ctx
->sitename
= strdup(sitename
);
2600 if (ctx
->sitename
== NULL
)
2601 return krb5_enomem(context
);
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
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
);
2631 ret
= krb5_parse_name (context
, service
, &principal
);
2634 krb5_principal_set_realm (context
, principal
, client_realm
);
2636 ret
= krb5_make_principal(context
, &principal
,
2637 client_realm
, KRB5_TGS_NAME
, client_realm
,
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
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
;
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
) {
2677 len
= strlen(ctx
->password
);
2678 memset_s(ctx
->password
, len
, 0, len
);
2679 free(ctx
->password
);
2682 ctx
->password
= strdup(password
);
2683 if (ctx
->password
== NULL
)
2684 return krb5_enomem(context
);
2685 ctx
->keyseed
= (void *) ctx
->password
;
2687 ctx
->keyseed
= NULL
;
2688 ctx
->password
= NULL
;
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
);
2711 keytab
= real_keytab
;
2714 ret
= krb5_kt_get_entry (context
, keytab
, principal
, 0, enctype
, &entry
);
2716 ret
= krb5_copy_keyblock(context
, &entry
.keyblock
, key
);
2717 krb5_kt_free_entry(context
, &entry
);
2720 krb5_kt_close(context
, real_keytab
);
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
,
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
;
2747 int kvno
= 0, found
= 0;
2750 a
= malloc(sizeof(*a
));
2752 return krb5_enomem(context
);
2754 a
->principal
= ctx
->cred
.client
;
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
2768 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
2772 while(krb5_kt_next_entry(context
, keytab
, &entry
, &cursor
) == 0){
2775 if (!krb5_principal_compare(context
, entry
.principal
, ctx
->cred
.client
))
2780 /* check if we ahve this kvno already */
2781 if (entry
.vno
> kvno
) {
2782 /* remove old list of etype */
2788 } else if (entry
.vno
!= kvno
)
2791 /* check if enctype is supported */
2792 if (krb5_enctype_valid(context
, entry
.keyblock
.keytype
) != 0)
2796 * If user already provided a enctype list, use that as an
2800 for (n
= 0; ctx
->etypes
[n
] != KRB5_ENCTYPE_NULL
; n
++) {
2801 if (ctx
->etypes
[n
] == entry
.keyblock
.keytype
)
2804 if (ctx
->etypes
[n
] == KRB5_ENCTYPE_NULL
)
2808 /* add enctype to supported list */
2809 ptr
= realloc(etypes
, sizeof(etypes
[0]) * (netypes
+ 2));
2812 ret
= krb5_enomem(context
);
2817 etypes
[netypes
] = entry
.keyblock
.keytype
;
2818 etypes
[netypes
+ 1] = ETYPE_NULL
;
2821 krb5_kt_free_entry(context
, &entry
);
2823 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
2828 ctx
->etypes
= etypes
;
2834 ret
= KRB5_KT_NOTFOUND
;
2835 _krb5_kt_principal_not_found(context
, ret
, keytab
, ctx
->cred
.client
, 0, 0);
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
;
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
;
2870 ret
= _krb5_get_krbtgt(context
, fast_ccache
, NULL
, &cred
);
2874 ret
= krb5_cc_get_config(context
, fast_ccache
, cred
->server
,
2875 "fast_avail", &data
);
2876 krb5_free_creds(context
, cred
);
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
;
2882 krb5_set_error_message(context
, EINVAL
, N_("FAST not available for the KDC in the armor ccache", ""));
2888 static krb5_error_code
2889 validate_pkinit_fx(krb5_context context
,
2890 krb5_init_creds_context ctx
,
2892 krb5_keyblock
*ticket_sessionkey
)
2898 pa
= krb5_find_padata(rep
->padata
->val
, rep
->padata
->len
, KRB5_PADATA_PKINIT_KX
, &idx
);
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
;
2911 heim_assert(ctx
->fast_state
.reply_key
!= NULL
, "must have a reply key at this stage");
2913 return _krb5_pk_kx_confirm(context
,
2915 ctx
->fast_state
.reply_key
,
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
);
2934 ctx
->fast_state
.armor_service
= NULL
;
2936 ctx
->fast_state
.flags
|= KRB5_FAST_AP_ARMOR_SERVICE
;
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
)
2947 ctx
->fast_state
.flags
|= KRB5_FAST_REQUIRED
;
2948 ctx
->fast_state
.flags
|= KRB5_FAST_ANON_PKINIT_ARMOR
;
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
)
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
;
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
)
2983 static krb5_error_code
2984 init_creds_step(krb5_context context
,
2985 krb5_init_creds_context ctx
,
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
;
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
);
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 */;
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", ""),
3026 return KRB5_GET_IN_TKT_LOOP
;
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
) {
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
);
3042 unsigned eflags
= EXTRACT_TICKET_AS_REQ
| EXTRACT_TICKET_TIMESYNC
;
3048 ASN1_MALLOC_ENCODE(Ticket
, data
.data
, data
.length
,
3049 &rep
.kdc_rep
.ticket
, &size
, ret
);
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
);
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
);
3077 free_AS_REP(&rep
.kdc_rep
);
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
,
3089 ctx
->fast_state
.reply_key
,
3094 free_AS_REP(&rep
.kdc_rep
);
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
,
3109 ctx
->fast_state
.reply_key
,
3111 KRB5_KU_AS_REP_ENC_PART
,
3120 ret
= copy_EncKDCRepPart(&rep
.enc_part
, &ctx
->enc_part
);
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
;
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
;
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
);
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
;
3159 _krb5_debug(context
, 5, "krb5_get_init_creds: failed to read error");
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
,
3181 * Just ignore any error, the error will be pushed
3182 * out from krb5_error_from_rd_error() if there
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
3195 ret
= _krb5_fast_unwrap_error(context
, ctx
->nonce
, &ctx
->fast_state
,
3196 &ctx
->md
, &ctx
->error
);
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
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
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", ""));
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
)
3246 _krb5_debug(context
, 10, "init_creds: err skew updating kdc offset to %d",
3247 context
->kdc_sec_offset
);
3251 pa_restart(context
, ctx
);
3253 } else if (ret
== KRB5_KDC_ERR_WRONG_REALM
&& ctx
->flags
.canonicalize
) {
3254 /* client referral to a new realm */
3257 if (ctx
->error
.crealm
== NULL
) {
3258 krb5_set_error_message(context
, ret
,
3259 N_("Got a client referral, not but no realm", ""));
3262 ref_realm
= *ctx
->error
.crealm
;
3264 _krb5_debug(context
, 5, "krb5_get_init_creds: referral to realm %s",
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
);
3279 free_PrincipalName(ctx
->as_req
.req_body
.sname
);
3280 ret
= _krb5_principal2principalname(ctx
->as_req
.req_body
.sname
, ctx
->cred
.server
);
3285 free(ctx
->as_req
.req_body
.realm
);
3286 ret
= copy_Realm(&ref_realm
, &ctx
->as_req
.req_body
.realm
);
3290 ret
= krb5_principal_set_realm(context
,
3292 *ctx
->error
.crealm
);
3296 ret
= krb5_unparse_name(context
, ctx
->cred
.client
, &ref_realm
);
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
) {
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)
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
,
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");
3351 _krb5_debug(context
, 10, "Disabling allow_enc_pa_rep and trying again");
3352 ctx
->runflags
.allow_enc_pa_rep
= 0;
3356 if (ctx
->fast_state
.flags
& KRB5_FAST_DISABLED
) {
3357 _krb5_debug(context
, 10, "FAST disabled and got preauth failed");
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
);
3374 /* some other error code from the KDC, lets' return it to the user */
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
);
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
);
3408 ret
= copy_AS_REQ(&ctx
->as_req
, &req2
);
3412 ret
= _krb5_fast_wrap_req(context
,
3417 krb5_data_free(&checksum_data
);
3423 krb5_data_free(&ctx
->req_buffer
);
3425 ASN1_MALLOC_ENCODE(AS_REQ
,
3426 ctx
->req_buffer
.data
, ctx
->req_buffer
.length
,
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
);
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
,
3474 krb5_krbhst_info
*hostinfo
,
3475 unsigned int *flags
)
3477 krb5_error_code ret
;
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
;
3493 ((*flags
& KRB5_INIT_CREDS_STEP_FLAG_CONTINUE
) == 0) ||
3500 return init_creds_step(context
, ctx
, in
, out
, hostinfo
, flags
);
3504 * Extract the newly acquired credentials from krb5_init_creds_context
3507 * @param context A Kerberos 5 context.
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
,
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
,
3576 krb5_error_code ret
;
3578 ret
= copy_KRB_ERROR(&ctx
->error
, error
);
3580 krb5_enomem(context
);
3588 * @param context A Kerberos 5 context.
3589 * @param ctx The krb5_init_creds_context to free.
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
,
3602 krb5_error_code ret
;
3604 if (ctx
->kdc_hostname
) {
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
);
3613 if (ctx
->sitename
) {
3615 data
.length
= strlen(ctx
->sitename
);
3616 data
.data
= ctx
->sitename
;
3618 ret
= krb5_cc_set_config(context
, id
, NULL
, "sitename", &data
);
3628 * @ingroup krb5_credential
3632 krb5_init_creds_store(krb5_context context
,
3633 krb5_init_creds_context ctx
,
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");
3644 ret
= krb5_cc_initialize(context
, id
, ctx
->cred
.client
);
3648 ret
= krb5_cc_store_cred(context
, id
, &ctx
->cred
);
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
);
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
);
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
;
3696 unsigned int flags
= 0;
3698 krb5_data_zero(&in
);
3699 krb5_data_zero(&out
);
3701 ret
= krb5_sendto_ctx_alloc(context
, &stctx
);
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
);
3709 krb5_sendto_set_sitename(context
, stctx
, ctx
->sitename
);
3712 struct timeval nstart
, nend
;
3715 ret
= krb5_init_creds_step(context
, ctx
, &in
, &out
, hostinfo
, &flags
);
3716 krb5_data_free(&in
);
3720 if ((flags
& KRB5_INIT_CREDS_STEP_FLAG_CONTINUE
) == 0)
3723 gettimeofday(&nstart
, NULL
);
3725 ret
= krb5_sendto_context (context
, stctx
, &out
,
3726 ctx
->cred
.client
->realm
, &in
);
3730 gettimeofday(&nend
, NULL
);
3731 timevalsub(&nend
, &nstart
);
3732 timevaladd(&ctx
->stats
.run_time
, &nend
);
3737 krb5_sendto_ctx_free(context
, stctx
);
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
,
3752 krb5_principal client
,
3753 const char *password
,
3754 krb5_prompter_fct prompter
,
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
;
3766 ret
= krb5_init_creds_init(context
, client
, prompter
, data
, start_time
, options
, &ctx
);
3770 ret
= krb5_init_creds_set_service(context
, ctx
, in_tkt_service
);
3774 if (prompter
!= NULL
&& ctx
->password
== NULL
&& password
== NULL
) {
3776 krb5_data password_data
;
3780 ret
= krb5_unparse_name(context
, client
, &p
);
3784 aret
= asprintf(&q
, "%s's Password: ", p
);
3786 if (aret
== -1 || q
== NULL
) {
3787 ret
= krb5_enomem(context
);
3791 password_data
.data
= buf
;
3792 password_data
.length
= sizeof(buf
);
3794 prompt
.reply
= &password_data
;
3795 prompt
.type
= KRB5_PROMPT_TYPE_PASSWORD
;
3797 ret
= (*prompter
) (context
, data
, NULL
, NULL
, 1, &prompt
);
3800 memset_s(buf
, sizeof(buf
), 0, sizeof(buf
));
3801 ret
= KRB5_LIBOS_PWDINTR
;
3802 krb5_clear_error_message (context
);
3805 password
= password_data
.data
;
3809 ret
= krb5_init_creds_set_password(context
, ctx
, password
);
3814 ret
= krb5_init_creds_get(context
, ctx
);
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)
3825 /* don't try to change password if no prompter or prompting disabled */
3826 if (!ctx
->runflags
.change_password_prompt
)
3829 ret
= change_password (context
,
3841 krb5_init_creds_free(context
, ctx
);
3847 krb5_init_creds_get_creds(context
, ctx
, creds
);
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
));
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
,
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
);
3881 ret
= krb5_init_creds_set_service(context
, ctx
, in_tkt_service
);
3885 ret
= krb5_init_creds_set_keyblock(context
, ctx
, keyblock
);
3889 ret
= krb5_init_creds_get(context
, ctx
);
3892 krb5_process_last_request(context
, options
, ctx
);
3896 krb5_init_creds_get_creds(context
, ctx
, creds
);
3899 krb5_init_creds_free(context
, ctx
);
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
,
3913 krb5_principal client
,
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
);
3933 client
= ktent
.principal
;
3936 ret
= krb5_init_creds_init(context
, client
, NULL
, NULL
, start_time
, options
, &ctx
);
3940 ret
= krb5_init_creds_set_service(context
, ctx
, in_tkt_service
);
3944 ret
= krb5_init_creds_set_keytab(context
, ctx
, keytab
);
3948 ret
= krb5_init_creds_get(context
, ctx
);
3950 krb5_process_last_request(context
, options
, ctx
);
3953 krb5_kt_free_entry(context
, &ktent
);
3955 krb5_init_creds_get_creds(context
, ctx
, creds
);
3958 krb5_init_creds_free(context
, ctx
);
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
)
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
)
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
,
4008 krb5_gss_init_ctx gssic
;
4010 gssic
= calloc(1, sizeof(*gssic
));
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;
4024 gssic
->finish
= finish
;
4025 gssic
->release_cred
= release_cred
;
4026 gssic
->delete_sec_context
= delete_sec_context
;