1 /* $NetBSD: accept_sec_context.c,v 1.1.1.2 2014/04/24 12:45:29 pettai Exp $ */
4 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include "gsskrb5_locl.h"
38 HEIMDAL_MUTEX gssapi_keytab_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
39 krb5_keytab _gsskrb5_keytab
;
41 static krb5_error_code
42 validate_keytab(krb5_context context
, const char *name
, krb5_keytab
*id
)
46 ret
= krb5_kt_resolve(context
, name
, id
);
50 ret
= krb5_kt_have_content(context
, *id
);
52 krb5_kt_close(context
, *id
);
60 _gsskrb5_register_acceptor_identity(OM_uint32
*min_stat
, const char *identity
)
67 ret
= _gsskrb5_init(&context
);
71 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex
);
73 if(_gsskrb5_keytab
!= NULL
) {
74 krb5_kt_close(context
, _gsskrb5_keytab
);
75 _gsskrb5_keytab
= NULL
;
77 if (identity
== NULL
) {
78 ret
= krb5_kt_default(context
, &_gsskrb5_keytab
);
81 * First check if we can the keytab as is and if it has content...
83 ret
= validate_keytab(context
, identity
, &_gsskrb5_keytab
);
85 * if it doesn't, lets prepend FILE: and try again
89 ret
= asprintf(&p
, "FILE:%s", identity
);
90 if(ret
< 0 || p
== NULL
) {
91 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex
);
94 ret
= validate_keytab(context
, p
, &_gsskrb5_keytab
);
98 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex
);
101 return GSS_S_FAILURE
;
103 return GSS_S_COMPLETE
;
107 _gsskrb5i_is_cfx(krb5_context context
, gsskrb5_ctx ctx
, int acceptor
)
113 if (ctx
->auth_context
->local_subkey
)
114 key
= ctx
->auth_context
->local_subkey
;
116 key
= ctx
->auth_context
->remote_subkey
;
118 if (ctx
->auth_context
->remote_subkey
)
119 key
= ctx
->auth_context
->remote_subkey
;
121 key
= ctx
->auth_context
->local_subkey
;
124 key
= ctx
->auth_context
->keyblock
;
129 switch (key
->keytype
) {
130 case ETYPE_DES_CBC_CRC
:
131 case ETYPE_DES_CBC_MD4
:
132 case ETYPE_DES_CBC_MD5
:
133 case ETYPE_DES3_CBC_MD5
:
134 case ETYPE_OLD_DES3_CBC_SHA1
:
135 case ETYPE_DES3_CBC_SHA1
:
136 case ETYPE_ARCFOUR_HMAC_MD5
:
137 case ETYPE_ARCFOUR_HMAC_MD5_56
:
140 ctx
->more_flags
|= IS_CFX
;
142 if ((acceptor
&& ctx
->auth_context
->local_subkey
) ||
143 (!acceptor
&& ctx
->auth_context
->remote_subkey
))
144 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
148 krb5_crypto_destroy(context
, ctx
->crypto
);
149 ret
= krb5_crypto_init(context
, key
, 0, &ctx
->crypto
);
154 gsskrb5_accept_delegated_token
155 (OM_uint32
* minor_status
,
157 krb5_context context
,
158 gss_cred_id_t
* delegated_cred_handle
161 krb5_ccache ccache
= NULL
;
162 krb5_error_code kret
;
163 int32_t ac_flags
, ret
= GSS_S_COMPLETE
;
167 /* XXX Create a new delegated_cred_handle? */
168 if (delegated_cred_handle
== NULL
) {
169 kret
= krb5_cc_default (context
, &ccache
);
171 *delegated_cred_handle
= NULL
;
172 kret
= krb5_cc_new_unique (context
, krb5_cc_type_memory
,
176 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
180 kret
= krb5_cc_initialize(context
, ccache
, ctx
->source
);
182 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
186 krb5_auth_con_removeflags(context
,
188 KRB5_AUTH_CONTEXT_DO_TIME
,
190 kret
= krb5_rd_cred2(context
,
194 krb5_auth_con_setflags(context
,
198 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
200 *minor_status
= kret
;
204 if (delegated_cred_handle
) {
207 ret
= _gsskrb5_krb5_import_cred(minor_status
,
211 delegated_cred_handle
);
212 if (ret
!= GSS_S_COMPLETE
)
215 handle
= (gsskrb5_cred
) *delegated_cred_handle
;
217 handle
->cred_flags
|= GSS_CF_DESTROY_CRED_ON_RELEASE
;
218 krb5_cc_close(context
, ccache
);
224 /* Don't destroy the default cred cache */
225 if (delegated_cred_handle
== NULL
)
226 krb5_cc_close(context
, ccache
);
228 krb5_cc_destroy(context
, ccache
);
234 gsskrb5_acceptor_ready(OM_uint32
* minor_status
,
236 krb5_context context
,
237 gss_cred_id_t
*delegated_cred_handle
)
243 krb5_auth_con_getremoteseqnumber (context
,
247 _gsskrb5i_is_cfx(context
, ctx
, 1);
248 is_cfx
= (ctx
->more_flags
& IS_CFX
);
250 ret
= _gssapi_msg_order_create(minor_status
,
252 _gssapi_msg_order_f(ctx
->flags
),
253 seq_number
, 0, is_cfx
);
258 * If requested, set local sequence num to remote sequence if this
259 * isn't a mutual authentication context
261 if (!(ctx
->flags
& GSS_C_MUTUAL_FLAG
) && _gssapi_msg_order_f(ctx
->flags
)) {
262 krb5_auth_con_setlocalseqnumber(context
,
268 * We should handle the delegation ticket, in case it's there
270 if (ctx
->fwd_data
.length
> 0 && (ctx
->flags
& GSS_C_DELEG_FLAG
)) {
271 ret
= gsskrb5_accept_delegated_token(minor_status
,
274 delegated_cred_handle
);
278 /* Well, looks like it wasn't there after all */
279 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
282 ctx
->state
= ACCEPTOR_READY
;
283 ctx
->more_flags
|= OPEN
;
285 return GSS_S_COMPLETE
;
289 send_error_token(OM_uint32
*minor_status
,
290 krb5_context context
,
291 krb5_error_code kret
,
292 krb5_principal server
,
294 gss_buffer_t output_token
)
296 krb5_principal ap_req_server
= NULL
;
299 /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
300 tells windows to try again with the corrected timestamp. See
301 [MS-KILE] 2.2.1 KERB-ERROR-DATA */
302 krb5_data e_data
= { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
304 /* build server from request if the acceptor had not selected one */
305 if (server
== NULL
) {
308 ret
= krb5_decode_ap_req(context
, indata
, &ap_req
);
311 return GSS_S_FAILURE
;
313 ret
= _krb5_principalname2krb5_principal(context
,
316 ap_req
.ticket
.realm
);
317 free_AP_REQ(&ap_req
);
320 return GSS_S_FAILURE
;
322 server
= ap_req_server
;
325 ret
= krb5_mk_error(context
, kret
, NULL
, &e_data
, NULL
,
326 server
, NULL
, NULL
, &outbuf
);
328 krb5_free_principal(context
, ap_req_server
);
331 return GSS_S_FAILURE
;
334 ret
= _gsskrb5_encapsulate(minor_status
,
339 krb5_data_free (&outbuf
);
344 return GSS_S_CONTINUE_NEEDED
;
349 gsskrb5_acceptor_start(OM_uint32
* minor_status
,
351 krb5_context context
,
352 const gss_cred_id_t acceptor_cred_handle
,
353 const gss_buffer_t input_token_buffer
,
354 const gss_channel_bindings_t input_chan_bindings
,
355 gss_name_t
* src_name
,
357 gss_buffer_t output_token
,
358 OM_uint32
* ret_flags
,
359 OM_uint32
* time_rec
,
360 gss_cred_id_t
* delegated_cred_handle
)
362 krb5_error_code kret
;
363 OM_uint32 ret
= GSS_S_COMPLETE
;
365 krb5_flags ap_options
;
366 krb5_keytab keytab
= NULL
;
368 const gsskrb5_cred acceptor_cred
= (gsskrb5_cred
)acceptor_cred_handle
;
371 * We may, or may not, have an escapsulation.
373 ret
= _gsskrb5_decapsulate (minor_status
,
380 /* Assume that there is no OID wrapping. */
381 indata
.length
= input_token_buffer
->length
;
382 indata
.data
= input_token_buffer
->value
;
386 * We need to get our keytab
388 if (acceptor_cred
== NULL
) {
389 if (_gsskrb5_keytab
!= NULL
)
390 keytab
= _gsskrb5_keytab
;
391 } else if (acceptor_cred
->keytab
!= NULL
) {
392 keytab
= acceptor_cred
->keytab
;
396 * We need to check the ticket and create the AP-REP packet
400 krb5_rd_req_in_ctx in
= NULL
;
401 krb5_rd_req_out_ctx out
= NULL
;
402 krb5_principal server
= NULL
;
405 server
= acceptor_cred
->principal
;
407 kret
= krb5_rd_req_in_ctx_alloc(context
, &in
);
409 kret
= krb5_rd_req_in_set_keytab(context
, in
, keytab
);
412 krb5_rd_req_in_ctx_free(context
, in
);
413 *minor_status
= kret
;
414 return GSS_S_FAILURE
;
417 kret
= krb5_rd_req_ctx(context
,
422 krb5_rd_req_in_ctx_free(context
, in
);
423 if (kret
== KRB5KRB_AP_ERR_SKEW
|| kret
== KRB5KRB_AP_ERR_TKT_NYV
) {
425 * No reply in non-MUTUAL mode, but we don't know that its
426 * non-MUTUAL mode yet, thats inside the 8003 checksum, so
427 * lets only send the error token on clock skew, that
428 * limit when send error token for non-MUTUAL.
430 return send_error_token(minor_status
, context
, kret
,
431 server
, &indata
, output_token
);
433 *minor_status
= kret
;
434 return GSS_S_FAILURE
;
438 * we need to remember some data on the context_handle.
440 kret
= krb5_rd_req_out_get_ap_req_options(context
, out
,
443 kret
= krb5_rd_req_out_get_ticket(context
, out
,
446 kret
= krb5_rd_req_out_get_keyblock(context
, out
,
447 &ctx
->service_keyblock
);
448 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
450 krb5_rd_req_out_ctx_free(context
, out
);
453 *minor_status
= kret
;
460 * We need to copy the principal names to the context and the
463 kret
= krb5_copy_principal(context
,
468 *minor_status
= kret
;
471 kret
= krb5_copy_principal(context
,
476 *minor_status
= kret
;
481 * We need to setup some compat stuff, this assumes that
482 * context_handle->target is already set.
484 ret
= _gss_DES3_get_mic_compat(minor_status
, ctx
, context
);
488 if (src_name
!= NULL
) {
489 kret
= krb5_copy_principal (context
,
491 (gsskrb5_name
*)src_name
);
494 *minor_status
= kret
;
500 * We need to get the flags out of the 8003 checksum.
504 krb5_authenticator authenticator
;
506 kret
= krb5_auth_con_getauthenticator(context
,
511 *minor_status
= kret
;
515 if (authenticator
->cksum
== NULL
) {
516 krb5_free_authenticator(context
, &authenticator
);
518 return GSS_S_BAD_BINDINGS
;
521 if (authenticator
->cksum
->cksumtype
== CKSUMTYPE_GSSAPI
) {
522 ret
= _gsskrb5_verify_8003_checksum(minor_status
,
524 authenticator
->cksum
,
528 krb5_free_authenticator(context
, &authenticator
);
535 kret
= krb5_crypto_init(context
,
536 ctx
->auth_context
->keyblock
,
539 krb5_free_authenticator(context
, &authenticator
);
542 *minor_status
= kret
;
547 * Windows accepts Samba3's use of a kerberos, rather than
548 * GSSAPI checksum here
551 kret
= krb5_verify_checksum(context
,
552 crypto
, KRB5_KU_AP_REQ_AUTH_CKSUM
, NULL
, 0,
553 authenticator
->cksum
);
554 krb5_free_authenticator(context
, &authenticator
);
555 krb5_crypto_destroy(context
, crypto
);
559 *minor_status
= kret
;
564 * Samba style get some flags (but not DCE-STYLE), use
565 * ap_options to guess the mutual flag.
567 ctx
->flags
= GSS_C_REPLAY_FLAG
| GSS_C_SEQUENCE_FLAG
;
568 if (ap_options
& AP_OPTS_MUTUAL_REQUIRED
)
569 ctx
->flags
|= GSS_C_MUTUAL_FLAG
;
573 if(ctx
->flags
& GSS_C_MUTUAL_FLAG
) {
577 _gsskrb5i_is_cfx(context
, ctx
, 1);
578 is_cfx
= (ctx
->more_flags
& IS_CFX
);
580 if (is_cfx
|| (ap_options
& AP_OPTS_USE_SUBKEY
)) {
586 * If there is a initiator subkey, copy that to acceptor
587 * subkey to match Windows behavior
589 kret
= krb5_auth_con_getremotesubkey(context
,
593 kret
= krb5_auth_con_setlocalsubkey(context
,
598 krb5_free_keyblock(context
, rkey
);
602 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
603 krb5_auth_con_addflags(context
, ctx
->auth_context
,
604 KRB5_AUTH_CONTEXT_USE_SUBKEY
,
608 kret
= krb5_mk_rep(context
,
612 *minor_status
= kret
;
613 return GSS_S_FAILURE
;
616 if (IS_DCE_STYLE(ctx
)) {
617 output_token
->length
= outbuf
.length
;
618 output_token
->value
= outbuf
.data
;
620 ret
= _gsskrb5_encapsulate(minor_status
,
625 krb5_data_free (&outbuf
);
631 ctx
->flags
|= GSS_C_TRANS_FLAG
;
633 /* Remember the flags */
635 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
636 ctx
->more_flags
|= OPEN
;
639 *mech_type
= GSS_KRB5_MECHANISM
;
642 ret
= _gsskrb5_lifetime_left(minor_status
,
652 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
655 if (IS_DCE_STYLE(ctx
)) {
657 * Return flags to caller, but we haven't processed
661 *ret_flags
= (ctx
->flags
& ~GSS_C_DELEG_FLAG
);
663 ctx
->state
= ACCEPTOR_WAIT_FOR_DCESTYLE
;
664 return GSS_S_CONTINUE_NEEDED
;
667 ret
= gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
668 delegated_cred_handle
);
671 *ret_flags
= ctx
->flags
;
677 acceptor_wait_for_dcestyle(OM_uint32
* minor_status
,
679 krb5_context context
,
680 const gss_cred_id_t acceptor_cred_handle
,
681 const gss_buffer_t input_token_buffer
,
682 const gss_channel_bindings_t input_chan_bindings
,
683 gss_name_t
* src_name
,
685 gss_buffer_t output_token
,
686 OM_uint32
* ret_flags
,
687 OM_uint32
* time_rec
,
688 gss_cred_id_t
* delegated_cred_handle
)
691 krb5_error_code kret
;
693 int32_t r_seq_number
, l_seq_number
;
696 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
699 inbuf
.length
= input_token_buffer
->length
;
700 inbuf
.data
= input_token_buffer
->value
;
703 * We need to remeber the old remote seq_number, then check if the
704 * client has replied with our local seq_number, and then reset
705 * the remote seq_number to the old value
708 kret
= krb5_auth_con_getlocalseqnumber(context
,
712 *minor_status
= kret
;
713 return GSS_S_FAILURE
;
716 kret
= krb5_auth_con_getremoteseqnumber(context
,
720 *minor_status
= kret
;
721 return GSS_S_FAILURE
;
724 kret
= krb5_auth_con_setremoteseqnumber(context
,
728 *minor_status
= kret
;
729 return GSS_S_FAILURE
;
734 * We need to verify the AP_REP, but we need to flag that this is
735 * DCE_STYLE, so don't check the timestamps this time, but put the
736 * flag DO_TIME back afterward.
739 krb5_ap_rep_enc_part
*repl
;
742 krb5_auth_con_removeflags(context
,
744 KRB5_AUTH_CONTEXT_DO_TIME
,
747 kret
= krb5_rd_rep(context
, ctx
->auth_context
, &inbuf
, &repl
);
749 *minor_status
= kret
;
750 return GSS_S_FAILURE
;
752 krb5_free_ap_rep_enc_part(context
, repl
);
753 krb5_auth_con_setflags(context
, ctx
->auth_context
, auth_flags
);
756 /* We need to check the liftime */
758 OM_uint32 lifetime_rec
;
760 ret
= _gsskrb5_lifetime_left(minor_status
,
767 if (lifetime_rec
== 0) {
768 return GSS_S_CONTEXT_EXPIRED
;
771 if (time_rec
) *time_rec
= lifetime_rec
;
774 /* We need to give the caller the flags which are in use */
775 if (ret_flags
) *ret_flags
= ctx
->flags
;
778 kret
= krb5_copy_principal(context
,
780 (gsskrb5_name
*)src_name
);
782 *minor_status
= kret
;
783 return GSS_S_FAILURE
;
788 * After the krb5_rd_rep() the remote and local seq_number should
789 * be the same, because the client just replies the seq_number
790 * from our AP-REP in its AP-REP, but then the client uses the
791 * seq_number from its AP-REQ for GSS_wrap()
794 int32_t tmp_r_seq_number
, tmp_l_seq_number
;
796 kret
= krb5_auth_con_getremoteseqnumber(context
,
800 *minor_status
= kret
;
801 return GSS_S_FAILURE
;
804 kret
= krb5_auth_con_getlocalseqnumber(context
,
809 *minor_status
= kret
;
810 return GSS_S_FAILURE
;
814 * Here we check if the client has responsed with our local seq_number,
816 if (tmp_r_seq_number
!= tmp_l_seq_number
) {
817 return GSS_S_UNSEQ_TOKEN
;
822 * We need to reset the remote seq_number, because the client will use,
823 * the old one for the GSS_wrap() calls
826 kret
= krb5_auth_con_setremoteseqnumber(context
,
830 *minor_status
= kret
;
831 return GSS_S_FAILURE
;
835 return gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
836 delegated_cred_handle
);
840 OM_uint32 GSSAPI_CALLCONV
841 _gsskrb5_accept_sec_context(OM_uint32
* minor_status
,
842 gss_ctx_id_t
* context_handle
,
843 const gss_cred_id_t acceptor_cred_handle
,
844 const gss_buffer_t input_token_buffer
,
845 const gss_channel_bindings_t input_chan_bindings
,
846 gss_name_t
* src_name
,
848 gss_buffer_t output_token
,
849 OM_uint32
* ret_flags
,
850 OM_uint32
* time_rec
,
851 gss_cred_id_t
* delegated_cred_handle
)
853 krb5_context context
;
857 GSSAPI_KRB5_INIT(&context
);
859 output_token
->length
= 0;
860 output_token
->value
= NULL
;
862 if (src_name
!= NULL
)
865 *mech_type
= GSS_KRB5_MECHANISM
;
867 if (*context_handle
== GSS_C_NO_CONTEXT
) {
868 ret
= _gsskrb5_create_ctx(minor_status
,
877 ctx
= (gsskrb5_ctx
)*context_handle
;
881 * TODO: check the channel_bindings
882 * (above just sets them to krb5 layer)
885 HEIMDAL_MUTEX_lock(&ctx
->ctx_id_mutex
);
887 switch (ctx
->state
) {
889 ret
= gsskrb5_acceptor_start(minor_status
,
892 acceptor_cred_handle
,
900 delegated_cred_handle
);
902 case ACCEPTOR_WAIT_FOR_DCESTYLE
:
903 ret
= acceptor_wait_for_dcestyle(minor_status
,
906 acceptor_cred_handle
,
914 delegated_cred_handle
);
918 * If we get there, the caller have called
919 * gss_accept_sec_context() one time too many.
921 ret
= GSS_S_BAD_STATUS
;
924 /* TODO: is this correct here? --metze */
925 ret
= GSS_S_BAD_STATUS
;
929 HEIMDAL_MUTEX_unlock(&ctx
->ctx_id_mutex
);
931 if (GSS_ERROR(ret
)) {
933 _gsskrb5_delete_sec_context(&min2
, context_handle
, GSS_C_NO_BUFFER
);