2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "gsskrb5_locl.h"
36 HEIMDAL_MUTEX gssapi_keytab_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
37 krb5_keytab _gsskrb5_keytab
;
39 static krb5_error_code
40 validate_keytab(krb5_context context
, const char *name
, krb5_keytab
*id
)
44 ret
= krb5_kt_resolve(context
, name
, id
);
48 ret
= krb5_kt_have_content(context
, *id
);
50 krb5_kt_close(context
, *id
);
58 _gsskrb5_register_acceptor_identity(OM_uint32
*min_stat
, const char *identity
)
65 ret
= _gsskrb5_init(&context
);
69 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex
);
71 if(_gsskrb5_keytab
!= NULL
) {
72 krb5_kt_close(context
, _gsskrb5_keytab
);
73 _gsskrb5_keytab
= NULL
;
75 if (identity
== NULL
) {
76 ret
= krb5_kt_default(context
, &_gsskrb5_keytab
);
79 * First check if we can the keytab as is and if it has content...
81 ret
= validate_keytab(context
, identity
, &_gsskrb5_keytab
);
83 * if it doesn't, lets prepend FILE: and try again
87 ret
= asprintf(&p
, "FILE:%s", identity
);
88 if(ret
< 0 || p
== NULL
) {
89 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex
);
92 ret
= validate_keytab(context
, p
, &_gsskrb5_keytab
);
96 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex
);
101 return GSS_S_COMPLETE
;
105 _gsskrb5i_is_cfx(krb5_context context
, gsskrb5_ctx ctx
, int acceptor
)
110 if (ctx
->auth_context
->local_subkey
)
111 key
= ctx
->auth_context
->local_subkey
;
113 key
= ctx
->auth_context
->remote_subkey
;
115 if (ctx
->auth_context
->remote_subkey
)
116 key
= ctx
->auth_context
->remote_subkey
;
118 key
= ctx
->auth_context
->local_subkey
;
121 key
= ctx
->auth_context
->keyblock
;
126 switch (key
->keytype
) {
127 case ETYPE_DES_CBC_CRC
:
128 case ETYPE_DES_CBC_MD4
:
129 case ETYPE_DES_CBC_MD5
:
130 case ETYPE_DES3_CBC_MD5
:
131 case ETYPE_OLD_DES3_CBC_SHA1
:
132 case ETYPE_DES3_CBC_SHA1
:
133 case ETYPE_ARCFOUR_HMAC_MD5
:
134 case ETYPE_ARCFOUR_HMAC_MD5_56
:
137 ctx
->more_flags
|= IS_CFX
;
139 if ((acceptor
&& ctx
->auth_context
->local_subkey
) ||
140 (!acceptor
&& ctx
->auth_context
->remote_subkey
))
141 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
145 krb5_crypto_destroy(context
, ctx
->crypto
);
146 /* XXX We really shouldn't ignore this; will come back to this */
147 (void) krb5_crypto_init(context
, key
, 0, &ctx
->crypto
);
152 gsskrb5_accept_delegated_token
153 (OM_uint32
* minor_status
,
155 krb5_context context
,
156 gss_cred_id_t
* delegated_cred_handle
159 krb5_ccache ccache
= NULL
;
160 krb5_error_code kret
;
161 int32_t ac_flags
, ret
= GSS_S_COMPLETE
;
165 /* XXX Create a new delegated_cred_handle? */
166 if (delegated_cred_handle
== NULL
) {
167 kret
= krb5_cc_default (context
, &ccache
);
169 *delegated_cred_handle
= NULL
;
170 kret
= krb5_cc_new_unique (context
, krb5_cc_type_memory
,
174 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
178 kret
= krb5_cc_initialize(context
, ccache
, ctx
->source
);
180 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
184 krb5_auth_con_removeflags(context
,
186 KRB5_AUTH_CONTEXT_DO_TIME
,
188 kret
= krb5_rd_cred2(context
,
192 krb5_auth_con_setflags(context
,
196 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
198 *minor_status
= kret
;
202 if (delegated_cred_handle
) {
205 ret
= _gsskrb5_krb5_import_cred(minor_status
,
209 delegated_cred_handle
);
210 if (ret
!= GSS_S_COMPLETE
)
213 handle
= (gsskrb5_cred
) *delegated_cred_handle
;
215 handle
->cred_flags
|= GSS_CF_DESTROY_CRED_ON_RELEASE
;
216 krb5_cc_close(context
, ccache
);
222 /* Don't destroy the default cred cache */
223 if (delegated_cred_handle
== NULL
)
224 krb5_cc_close(context
, ccache
);
226 krb5_cc_destroy(context
, ccache
);
232 gsskrb5_acceptor_ready(OM_uint32
* minor_status
,
234 krb5_context context
,
235 gss_cred_id_t
*delegated_cred_handle
)
241 krb5_auth_con_getremoteseqnumber (context
,
245 _gsskrb5i_is_cfx(context
, ctx
, 1);
246 is_cfx
= (ctx
->more_flags
& IS_CFX
);
248 ret
= _gssapi_msg_order_create(minor_status
,
250 _gssapi_msg_order_f(ctx
->flags
),
251 seq_number
, 0, is_cfx
);
256 * If requested, set local sequence num to remote sequence if this
257 * isn't a mutual authentication context
259 if (!(ctx
->flags
& GSS_C_MUTUAL_FLAG
) && _gssapi_msg_order_f(ctx
->flags
)) {
260 krb5_auth_con_setlocalseqnumber(context
,
266 * We should handle the delegation ticket, in case it's there
268 if (ctx
->fwd_data
.length
> 0 && (ctx
->flags
& GSS_C_DELEG_FLAG
)) {
269 ret
= gsskrb5_accept_delegated_token(minor_status
,
272 delegated_cred_handle
);
276 /* Well, looks like it wasn't there after all */
277 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
280 ctx
->state
= ACCEPTOR_READY
;
281 ctx
->more_flags
|= OPEN
;
283 return GSS_S_COMPLETE
;
287 send_error_token(OM_uint32
*minor_status
,
288 krb5_context context
,
289 krb5_error_code kret
,
290 krb5_principal server
,
292 gss_buffer_t output_token
)
294 krb5_principal ap_req_server
= NULL
;
297 /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
298 tells windows to try again with the corrected timestamp. See
299 [MS-KILE] 2.2.1 KERB-ERROR-DATA */
300 krb5_data e_data
= { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
302 /* build server from request if the acceptor had not selected one */
303 if (server
== NULL
) {
306 ret
= krb5_decode_ap_req(context
, indata
, &ap_req
);
309 return GSS_S_FAILURE
;
311 ret
= _krb5_principalname2krb5_principal(context
,
314 ap_req
.ticket
.realm
);
315 free_AP_REQ(&ap_req
);
318 return GSS_S_FAILURE
;
320 server
= ap_req_server
;
323 ret
= krb5_mk_error(context
, kret
, NULL
, &e_data
, NULL
,
324 server
, NULL
, NULL
, &outbuf
);
326 krb5_free_principal(context
, ap_req_server
);
329 return GSS_S_FAILURE
;
332 ret
= _gsskrb5_encapsulate(minor_status
,
337 krb5_data_free (&outbuf
);
342 return GSS_S_CONTINUE_NEEDED
;
347 gsskrb5_acceptor_start(OM_uint32
* minor_status
,
349 krb5_context context
,
350 gss_const_cred_id_t acceptor_cred_handle
,
351 const gss_buffer_t input_token_buffer
,
352 const gss_channel_bindings_t input_chan_bindings
,
353 gss_name_t
* src_name
,
355 gss_buffer_t output_token
,
356 OM_uint32
* ret_flags
,
357 OM_uint32
* time_rec
,
358 gss_cred_id_t
* delegated_cred_handle
)
360 krb5_error_code kret
;
361 OM_uint32 ret
= GSS_S_COMPLETE
;
363 krb5_flags ap_options
;
364 krb5_keytab keytab
= NULL
;
366 const gsskrb5_cred acceptor_cred
= (gsskrb5_cred
)acceptor_cred_handle
;
369 * We may, or may not, have an escapsulation.
371 ret
= _gsskrb5_decapsulate (minor_status
,
378 /* Assume that there is no OID wrapping. */
379 indata
.length
= input_token_buffer
->length
;
380 indata
.data
= input_token_buffer
->value
;
384 * We need to get our keytab
386 if (acceptor_cred
== NULL
) {
387 if (_gsskrb5_keytab
!= NULL
)
388 keytab
= _gsskrb5_keytab
;
389 } else if (acceptor_cred
->keytab
!= NULL
) {
390 keytab
= acceptor_cred
->keytab
;
394 * We need to check the ticket and create the AP-REP packet
398 krb5_rd_req_in_ctx in
= NULL
;
399 krb5_rd_req_out_ctx out
= NULL
;
400 krb5_principal server
= NULL
;
403 server
= acceptor_cred
->principal
;
405 kret
= krb5_rd_req_in_ctx_alloc(context
, &in
);
407 kret
= krb5_rd_req_in_set_keytab(context
, in
, keytab
);
410 krb5_rd_req_in_ctx_free(context
, in
);
411 *minor_status
= kret
;
412 return GSS_S_FAILURE
;
415 kret
= krb5_rd_req_ctx(context
,
420 krb5_rd_req_in_ctx_free(context
, in
);
421 if (kret
== KRB5KRB_AP_ERR_SKEW
|| kret
== KRB5KRB_AP_ERR_TKT_NYV
) {
423 * No reply in non-MUTUAL mode, but we don't know that its
424 * non-MUTUAL mode yet, thats inside the 8003 checksum, so
425 * lets only send the error token on clock skew, that
426 * limit when send error token for non-MUTUAL.
428 return send_error_token(minor_status
, context
, kret
,
429 server
, &indata
, output_token
);
431 *minor_status
= kret
;
432 return GSS_S_FAILURE
;
436 * we need to remember some data on the context_handle.
438 kret
= krb5_rd_req_out_get_ap_req_options(context
, out
,
441 kret
= krb5_rd_req_out_get_ticket(context
, out
,
444 kret
= krb5_rd_req_out_get_keyblock(context
, out
,
445 &ctx
->service_keyblock
);
446 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
448 krb5_rd_req_out_ctx_free(context
, out
);
451 *minor_status
= kret
;
458 * We need to copy the principal names to the context and the
461 kret
= krb5_copy_principal(context
,
466 *minor_status
= kret
;
469 kret
= krb5_copy_principal(context
,
474 *minor_status
= kret
;
479 * We need to setup some compat stuff, this assumes that
480 * context_handle->target is already set.
482 ret
= _gss_DES3_get_mic_compat(minor_status
, ctx
, context
);
486 if (src_name
!= NULL
) {
487 kret
= krb5_copy_principal (context
,
489 (gsskrb5_name
*)src_name
);
492 *minor_status
= kret
;
498 * We need to get the flags out of the 8003 checksum.
502 krb5_authenticator authenticator
;
504 kret
= krb5_auth_con_getauthenticator(context
,
509 *minor_status
= kret
;
513 if (authenticator
->cksum
== NULL
) {
514 krb5_free_authenticator(context
, &authenticator
);
516 return GSS_S_BAD_BINDINGS
;
519 if (authenticator
->cksum
->cksumtype
== CKSUMTYPE_GSSAPI
) {
520 ret
= _gsskrb5_verify_8003_checksum(minor_status
,
522 authenticator
->cksum
,
526 krb5_free_authenticator(context
, &authenticator
);
533 kret
= krb5_crypto_init(context
,
534 ctx
->auth_context
->keyblock
,
537 krb5_free_authenticator(context
, &authenticator
);
540 *minor_status
= kret
;
545 * Windows accepts Samba3's use of a kerberos, rather than
546 * GSSAPI checksum here
549 kret
= krb5_verify_checksum(context
,
550 crypto
, KRB5_KU_AP_REQ_AUTH_CKSUM
, NULL
, 0,
551 authenticator
->cksum
);
552 krb5_free_authenticator(context
, &authenticator
);
553 krb5_crypto_destroy(context
, crypto
);
557 *minor_status
= kret
;
562 * Samba style get some flags (but not DCE-STYLE), use
563 * ap_options to guess the mutual flag.
565 ctx
->flags
= GSS_C_REPLAY_FLAG
| GSS_C_SEQUENCE_FLAG
;
566 if (ap_options
& AP_OPTS_MUTUAL_REQUIRED
)
567 ctx
->flags
|= GSS_C_MUTUAL_FLAG
;
571 if(ctx
->flags
& GSS_C_MUTUAL_FLAG
) {
575 _gsskrb5i_is_cfx(context
, ctx
, 1);
576 is_cfx
= (ctx
->more_flags
& IS_CFX
);
578 if (is_cfx
|| (ap_options
& AP_OPTS_USE_SUBKEY
)) {
584 * If there is a initiator subkey, copy that to acceptor
585 * subkey to match Windows behavior
587 kret
= krb5_auth_con_getremotesubkey(context
,
591 kret
= krb5_auth_con_setlocalsubkey(context
,
596 krb5_free_keyblock(context
, rkey
);
600 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
601 krb5_auth_con_addflags(context
, ctx
->auth_context
,
602 KRB5_AUTH_CONTEXT_USE_SUBKEY
,
606 kret
= krb5_mk_rep(context
,
610 *minor_status
= kret
;
611 return GSS_S_FAILURE
;
614 if (IS_DCE_STYLE(ctx
)) {
615 output_token
->length
= outbuf
.length
;
616 output_token
->value
= outbuf
.data
;
618 ret
= _gsskrb5_encapsulate(minor_status
,
623 krb5_data_free (&outbuf
);
629 ctx
->flags
|= GSS_C_TRANS_FLAG
;
631 /* Remember the flags */
633 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
634 ctx
->more_flags
|= OPEN
;
637 *mech_type
= GSS_KRB5_MECHANISM
;
640 ret
= _gsskrb5_lifetime_left(minor_status
,
650 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
653 if (IS_DCE_STYLE(ctx
)) {
655 * Return flags to caller, but we haven't processed
659 *ret_flags
= (ctx
->flags
& ~GSS_C_DELEG_FLAG
);
661 ctx
->state
= ACCEPTOR_WAIT_FOR_DCESTYLE
;
662 return GSS_S_CONTINUE_NEEDED
;
665 ret
= gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
666 delegated_cred_handle
);
669 *ret_flags
= ctx
->flags
;
675 acceptor_wait_for_dcestyle(OM_uint32
* minor_status
,
677 krb5_context context
,
678 gss_const_cred_id_t acceptor_cred_handle
,
679 const gss_buffer_t input_token_buffer
,
680 const gss_channel_bindings_t input_chan_bindings
,
681 gss_name_t
* src_name
,
683 gss_buffer_t output_token
,
684 OM_uint32
* ret_flags
,
685 OM_uint32
* time_rec
,
686 gss_cred_id_t
* delegated_cred_handle
)
689 krb5_error_code kret
;
691 int32_t r_seq_number
, l_seq_number
;
694 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
697 inbuf
.length
= input_token_buffer
->length
;
698 inbuf
.data
= input_token_buffer
->value
;
701 * We need to remeber the old remote seq_number, then check if the
702 * client has replied with our local seq_number, and then reset
703 * the remote seq_number to the old value
706 kret
= krb5_auth_con_getlocalseqnumber(context
,
710 *minor_status
= kret
;
711 return GSS_S_FAILURE
;
714 kret
= krb5_auth_con_getremoteseqnumber(context
,
718 *minor_status
= kret
;
719 return GSS_S_FAILURE
;
722 kret
= krb5_auth_con_setremoteseqnumber(context
,
726 *minor_status
= kret
;
727 return GSS_S_FAILURE
;
732 * We need to verify the AP_REP, but we need to flag that this is
733 * DCE_STYLE, so don't check the timestamps this time, but put the
734 * flag DO_TIME back afterward.
737 krb5_ap_rep_enc_part
*repl
;
740 krb5_auth_con_removeflags(context
,
742 KRB5_AUTH_CONTEXT_DO_TIME
,
745 kret
= krb5_rd_rep(context
, ctx
->auth_context
, &inbuf
, &repl
);
747 *minor_status
= kret
;
748 return GSS_S_FAILURE
;
750 krb5_free_ap_rep_enc_part(context
, repl
);
751 krb5_auth_con_setflags(context
, ctx
->auth_context
, auth_flags
);
754 /* We need to check the liftime */
756 OM_uint32 lifetime_rec
;
758 ret
= _gsskrb5_lifetime_left(minor_status
,
765 if (lifetime_rec
== 0) {
766 return GSS_S_CONTEXT_EXPIRED
;
769 if (time_rec
) *time_rec
= lifetime_rec
;
772 /* We need to give the caller the flags which are in use */
773 if (ret_flags
) *ret_flags
= ctx
->flags
;
776 kret
= krb5_copy_principal(context
,
778 (gsskrb5_name
*)src_name
);
780 *minor_status
= kret
;
781 return GSS_S_FAILURE
;
786 * After the krb5_rd_rep() the remote and local seq_number should
787 * be the same, because the client just replies the seq_number
788 * from our AP-REP in its AP-REP, but then the client uses the
789 * seq_number from its AP-REQ for GSS_wrap()
792 int32_t tmp_r_seq_number
, tmp_l_seq_number
;
794 kret
= krb5_auth_con_getremoteseqnumber(context
,
798 *minor_status
= kret
;
799 return GSS_S_FAILURE
;
802 kret
= krb5_auth_con_getlocalseqnumber(context
,
807 *minor_status
= kret
;
808 return GSS_S_FAILURE
;
812 * Here we check if the client has responsed with our local seq_number,
814 if (tmp_r_seq_number
!= tmp_l_seq_number
) {
815 return GSS_S_UNSEQ_TOKEN
;
820 * We need to reset the remote seq_number, because the client will use,
821 * the old one for the GSS_wrap() calls
824 kret
= krb5_auth_con_setremoteseqnumber(context
,
828 *minor_status
= kret
;
829 return GSS_S_FAILURE
;
833 return gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
834 delegated_cred_handle
);
838 OM_uint32 GSSAPI_CALLCONV
839 _gsskrb5_accept_sec_context(OM_uint32
* minor_status
,
840 gss_ctx_id_t
* context_handle
,
841 gss_const_cred_id_t acceptor_cred_handle
,
842 const gss_buffer_t input_token_buffer
,
843 const gss_channel_bindings_t input_chan_bindings
,
844 gss_name_t
* src_name
,
846 gss_buffer_t output_token
,
847 OM_uint32
* ret_flags
,
848 OM_uint32
* time_rec
,
849 gss_cred_id_t
* delegated_cred_handle
)
851 krb5_context context
;
855 GSSAPI_KRB5_INIT(&context
);
857 output_token
->length
= 0;
858 output_token
->value
= NULL
;
860 if (src_name
!= NULL
)
863 *mech_type
= GSS_KRB5_MECHANISM
;
865 if (*context_handle
== GSS_C_NO_CONTEXT
) {
866 ret
= _gsskrb5_create_ctx(minor_status
,
875 ctx
= (gsskrb5_ctx
)*context_handle
;
879 * TODO: check the channel_bindings
880 * (above just sets them to krb5 layer)
883 HEIMDAL_MUTEX_lock(&ctx
->ctx_id_mutex
);
885 switch (ctx
->state
) {
887 ret
= gsskrb5_acceptor_start(minor_status
,
890 acceptor_cred_handle
,
898 delegated_cred_handle
);
900 case ACCEPTOR_WAIT_FOR_DCESTYLE
:
901 ret
= acceptor_wait_for_dcestyle(minor_status
,
904 acceptor_cred_handle
,
912 delegated_cred_handle
);
916 * If we get there, the caller have called
917 * gss_accept_sec_context() one time too many.
919 ret
= GSS_S_BAD_STATUS
;
922 /* TODO: is this correct here? --metze */
923 ret
= GSS_S_BAD_STATUS
;
927 HEIMDAL_MUTEX_unlock(&ctx
->ctx_id_mutex
);
929 if (GSS_ERROR(ret
)) {
931 _gsskrb5_delete_sec_context(&min2
, context_handle
, GSS_C_NO_BUFFER
);