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
;
40 _gsskrb5_register_acceptor_identity (const char *identity
)
45 ret
= _gsskrb5_init(&context
);
49 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex
);
51 if(_gsskrb5_keytab
!= NULL
) {
52 krb5_kt_close(context
, _gsskrb5_keytab
);
53 _gsskrb5_keytab
= NULL
;
55 if (identity
== NULL
) {
56 ret
= krb5_kt_default(context
, &_gsskrb5_keytab
);
60 asprintf(&p
, "FILE:%s", identity
);
62 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex
);
65 ret
= krb5_kt_resolve(context
, p
, &_gsskrb5_keytab
);
68 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex
);
71 return GSS_S_COMPLETE
;
75 _gsskrb5i_is_cfx(krb5_context context
, gsskrb5_ctx ctx
, int acceptor
)
81 if (ctx
->auth_context
->local_subkey
)
82 key
= ctx
->auth_context
->local_subkey
;
84 key
= ctx
->auth_context
->remote_subkey
;
86 if (ctx
->auth_context
->remote_subkey
)
87 key
= ctx
->auth_context
->remote_subkey
;
89 key
= ctx
->auth_context
->local_subkey
;
92 key
= ctx
->auth_context
->keyblock
;
97 switch (key
->keytype
) {
98 case ETYPE_DES_CBC_CRC
:
99 case ETYPE_DES_CBC_MD4
:
100 case ETYPE_DES_CBC_MD5
:
101 case ETYPE_DES3_CBC_MD5
:
102 case ETYPE_DES3_CBC_SHA1
:
103 case ETYPE_ARCFOUR_HMAC_MD5
:
104 case ETYPE_ARCFOUR_HMAC_MD5_56
:
107 ctx
->more_flags
|= IS_CFX
;
109 if ((acceptor
&& ctx
->auth_context
->local_subkey
) ||
110 (!acceptor
&& ctx
->auth_context
->remote_subkey
))
111 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
115 krb5_crypto_destroy(context
, ctx
->crypto
);
116 ret
= krb5_crypto_init(context
, key
, 0, &ctx
->crypto
);
121 gsskrb5_accept_delegated_token
122 (OM_uint32
* minor_status
,
124 krb5_context context
,
125 gss_cred_id_t
* delegated_cred_handle
128 krb5_ccache ccache
= NULL
;
129 krb5_error_code kret
;
130 int32_t ac_flags
, ret
= GSS_S_COMPLETE
;
134 /* XXX Create a new delegated_cred_handle? */
135 if (delegated_cred_handle
== NULL
) {
136 kret
= krb5_cc_default (context
, &ccache
);
138 *delegated_cred_handle
= NULL
;
139 kret
= krb5_cc_new_unique (context
, krb5_cc_type_memory
,
143 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
147 kret
= krb5_cc_initialize(context
, ccache
, ctx
->source
);
149 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
153 krb5_auth_con_removeflags(context
,
155 KRB5_AUTH_CONTEXT_DO_TIME
,
157 kret
= krb5_rd_cred2(context
,
161 krb5_auth_con_setflags(context
,
165 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
167 *minor_status
= kret
;
171 if (delegated_cred_handle
) {
174 ret
= _gsskrb5_import_cred(minor_status
,
178 delegated_cred_handle
);
179 if (ret
!= GSS_S_COMPLETE
)
182 handle
= (gsskrb5_cred
) *delegated_cred_handle
;
184 handle
->cred_flags
|= GSS_CF_DESTROY_CRED_ON_RELEASE
;
185 krb5_cc_close(context
, ccache
);
191 /* Don't destroy the default cred cache */
192 if (delegated_cred_handle
== NULL
)
193 krb5_cc_close(context
, ccache
);
195 krb5_cc_destroy(context
, ccache
);
201 gsskrb5_acceptor_ready(OM_uint32
* minor_status
,
203 krb5_context context
,
204 gss_cred_id_t
*delegated_cred_handle
)
210 krb5_auth_getremoteseqnumber (context
,
214 _gsskrb5i_is_cfx(context
, ctx
, 1);
215 is_cfx
= (ctx
->more_flags
& IS_CFX
);
217 ret
= _gssapi_msg_order_create(minor_status
,
219 _gssapi_msg_order_f(ctx
->flags
),
220 seq_number
, 0, is_cfx
);
225 * If requested, set local sequence num to remote sequence if this
226 * isn't a mutual authentication context
228 if (!(ctx
->flags
& GSS_C_MUTUAL_FLAG
) && _gssapi_msg_order_f(ctx
->flags
)) {
229 krb5_auth_con_setlocalseqnumber(context
,
235 * We should handle the delegation ticket, in case it's there
237 if (ctx
->fwd_data
.length
> 0 && (ctx
->flags
& GSS_C_DELEG_FLAG
)) {
238 ret
= gsskrb5_accept_delegated_token(minor_status
,
241 delegated_cred_handle
);
245 /* Well, looks like it wasn't there after all */
246 ctx
->flags
&= ~GSS_C_DELEG_FLAG
;
249 ctx
->state
= ACCEPTOR_READY
;
250 ctx
->more_flags
|= OPEN
;
252 return GSS_S_COMPLETE
;
256 send_error_token(OM_uint32
*minor_status
,
257 krb5_context context
,
258 krb5_error_code kret
,
259 krb5_principal server
,
261 gss_buffer_t output_token
)
263 krb5_principal ap_req_server
= NULL
;
267 /* build server from request if the acceptor had not selected one */
268 if (server
== NULL
) {
271 ret
= krb5_decode_ap_req(context
, indata
, &ap_req
);
274 return GSS_S_FAILURE
;
276 ret
= _krb5_principalname2krb5_principal(context
,
279 ap_req
.ticket
.realm
);
280 free_AP_REQ(&ap_req
);
283 return GSS_S_FAILURE
;
285 server
= ap_req_server
;
288 ret
= krb5_mk_error(context
, kret
, NULL
, NULL
, NULL
,
289 server
, NULL
, NULL
, &outbuf
);
291 krb5_free_principal(context
, ap_req_server
);
294 return GSS_S_FAILURE
;
297 ret
= _gsskrb5_encapsulate(minor_status
,
302 krb5_data_free (&outbuf
);
307 return GSS_S_CONTINUE_NEEDED
;
312 gsskrb5_acceptor_start(OM_uint32
* minor_status
,
314 krb5_context context
,
315 const gss_cred_id_t acceptor_cred_handle
,
316 const gss_buffer_t input_token_buffer
,
317 const gss_channel_bindings_t input_chan_bindings
,
318 gss_name_t
* src_name
,
320 gss_buffer_t output_token
,
321 OM_uint32
* ret_flags
,
322 OM_uint32
* time_rec
,
323 gss_cred_id_t
* delegated_cred_handle
)
325 krb5_error_code kret
;
326 OM_uint32 ret
= GSS_S_COMPLETE
;
328 krb5_flags ap_options
;
329 krb5_keytab keytab
= NULL
;
331 const gsskrb5_cred acceptor_cred
= (gsskrb5_cred
)acceptor_cred_handle
;
334 * We may, or may not, have an escapsulation.
336 ret
= _gsskrb5_decapsulate (minor_status
,
343 /* Assume that there is no OID wrapping. */
344 indata
.length
= input_token_buffer
->length
;
345 indata
.data
= input_token_buffer
->value
;
349 * We need to get our keytab
351 if (acceptor_cred
== NULL
) {
352 if (_gsskrb5_keytab
!= NULL
)
353 keytab
= _gsskrb5_keytab
;
354 } else if (acceptor_cred
->keytab
!= NULL
) {
355 keytab
= acceptor_cred
->keytab
;
359 * We need to check the ticket and create the AP-REP packet
363 krb5_rd_req_in_ctx in
= NULL
;
364 krb5_rd_req_out_ctx out
= NULL
;
365 krb5_principal server
= NULL
;
368 server
= acceptor_cred
->principal
;
370 kret
= krb5_rd_req_in_ctx_alloc(context
, &in
);
372 kret
= krb5_rd_req_in_set_keytab(context
, in
, keytab
);
375 krb5_rd_req_in_ctx_free(context
, in
);
376 *minor_status
= kret
;
377 return GSS_S_FAILURE
;
380 kret
= krb5_rd_req_ctx(context
,
385 krb5_rd_req_in_ctx_free(context
, in
);
386 if (kret
== KRB5KRB_AP_ERR_SKEW
|| kret
== KRB5KRB_AP_ERR_TKT_NYV
) {
388 * No reply in non-MUTUAL mode, but we don't know that its
389 * non-MUTUAL mode yet, thats inside the 8003 checksum, so
390 * lets only send the error token on clock skew, that
391 * limit when send error token for non-MUTUAL.
393 return send_error_token(minor_status
, context
, kret
,
394 server
, &indata
, output_token
);
396 *minor_status
= kret
;
397 return GSS_S_FAILURE
;
401 * we need to remember some data on the context_handle.
403 kret
= krb5_rd_req_out_get_ap_req_options(context
, out
,
406 kret
= krb5_rd_req_out_get_ticket(context
, out
,
409 kret
= krb5_rd_req_out_get_keyblock(context
, out
,
410 &ctx
->service_keyblock
);
411 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
413 krb5_rd_req_out_ctx_free(context
, out
);
416 *minor_status
= kret
;
423 * We need to copy the principal names to the context and the
426 kret
= krb5_copy_principal(context
,
431 *minor_status
= kret
;
434 kret
= krb5_copy_principal(context
,
439 *minor_status
= kret
;
444 * We need to setup some compat stuff, this assumes that
445 * context_handle->target is already set.
447 ret
= _gss_DES3_get_mic_compat(minor_status
, ctx
, context
);
451 if (src_name
!= NULL
) {
452 kret
= krb5_copy_principal (context
,
454 (gsskrb5_name
*)src_name
);
457 *minor_status
= kret
;
463 * We need to get the flags out of the 8003 checksum.
466 krb5_authenticator authenticator
;
468 kret
= krb5_auth_con_getauthenticator(context
,
473 *minor_status
= kret
;
477 if (authenticator
->cksum
->cksumtype
== CKSUMTYPE_GSSAPI
) {
478 ret
= _gsskrb5_verify_8003_checksum(minor_status
,
480 authenticator
->cksum
,
484 krb5_free_authenticator(context
, &authenticator
);
491 kret
= krb5_crypto_init(context
,
492 ctx
->auth_context
->keyblock
,
495 krb5_free_authenticator(context
, &authenticator
);
498 *minor_status
= kret
;
503 * Windows accepts Samba3's use of a kerberos, rather than
504 * GSSAPI checksum here
507 kret
= krb5_verify_checksum(context
,
508 crypto
, KRB5_KU_AP_REQ_AUTH_CKSUM
, NULL
, 0,
509 authenticator
->cksum
);
510 krb5_free_authenticator(context
, &authenticator
);
511 krb5_crypto_destroy(context
, crypto
);
515 *minor_status
= kret
;
520 * Samba style get some flags (but not DCE-STYLE), use
521 * ap_options to guess the mutual flag.
523 ctx
->flags
= GSS_C_REPLAY_FLAG
| GSS_C_SEQUENCE_FLAG
;
524 if (ap_options
& AP_OPTS_MUTUAL_REQUIRED
)
525 ctx
->flags
|= GSS_C_MUTUAL_FLAG
;
529 if(ctx
->flags
& GSS_C_MUTUAL_FLAG
) {
533 _gsskrb5i_is_cfx(context
, ctx
, 1);
534 is_cfx
= (ctx
->more_flags
& IS_CFX
);
536 if (is_cfx
|| (ap_options
& AP_OPTS_USE_SUBKEY
)) {
542 * If there is a initiator subkey, copy that to acceptor
543 * subkey to match Windows behavior
545 kret
= krb5_auth_con_getremotesubkey(context
,
549 kret
= krb5_auth_con_setlocalsubkey(context
,
554 krb5_free_keyblock(context
, rkey
);
558 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
559 krb5_auth_con_addflags(context
, ctx
->auth_context
,
560 KRB5_AUTH_CONTEXT_USE_SUBKEY
,
564 kret
= krb5_mk_rep(context
,
568 *minor_status
= kret
;
569 return GSS_S_FAILURE
;
572 if (IS_DCE_STYLE(ctx
)) {
573 output_token
->length
= outbuf
.length
;
574 output_token
->value
= outbuf
.data
;
576 ret
= _gsskrb5_encapsulate(minor_status
,
581 krb5_data_free (&outbuf
);
587 ctx
->flags
|= GSS_C_TRANS_FLAG
;
589 /* Remember the flags */
591 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
592 ctx
->more_flags
|= OPEN
;
595 *mech_type
= GSS_KRB5_MECHANISM
;
598 ret
= _gsskrb5_lifetime_left(minor_status
,
608 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
611 if (IS_DCE_STYLE(ctx
)) {
613 * Return flags to caller, but we haven't processed
617 *ret_flags
= (ctx
->flags
& ~GSS_C_DELEG_FLAG
);
619 ctx
->state
= ACCEPTOR_WAIT_FOR_DCESTYLE
;
620 return GSS_S_CONTINUE_NEEDED
;
623 ret
= gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
624 delegated_cred_handle
);
627 *ret_flags
= ctx
->flags
;
633 acceptor_wait_for_dcestyle(OM_uint32
* minor_status
,
635 krb5_context context
,
636 const gss_cred_id_t acceptor_cred_handle
,
637 const gss_buffer_t input_token_buffer
,
638 const gss_channel_bindings_t input_chan_bindings
,
639 gss_name_t
* src_name
,
641 gss_buffer_t output_token
,
642 OM_uint32
* ret_flags
,
643 OM_uint32
* time_rec
,
644 gss_cred_id_t
* delegated_cred_handle
)
647 krb5_error_code kret
;
649 int32_t r_seq_number
, l_seq_number
;
652 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
655 inbuf
.length
= input_token_buffer
->length
;
656 inbuf
.data
= input_token_buffer
->value
;
659 * We need to remeber the old remote seq_number, then check if the
660 * client has replied with our local seq_number, and then reset
661 * the remote seq_number to the old value
664 kret
= krb5_auth_con_getlocalseqnumber(context
,
668 *minor_status
= kret
;
669 return GSS_S_FAILURE
;
672 kret
= krb5_auth_getremoteseqnumber(context
,
676 *minor_status
= kret
;
677 return GSS_S_FAILURE
;
680 kret
= krb5_auth_con_setremoteseqnumber(context
,
684 *minor_status
= kret
;
685 return GSS_S_FAILURE
;
690 * We need to verify the AP_REP, but we need to flag that this is
691 * DCE_STYLE, so don't check the timestamps this time, but put the
692 * flag DO_TIME back afterward.
695 krb5_ap_rep_enc_part
*repl
;
698 krb5_auth_con_removeflags(context
,
700 KRB5_AUTH_CONTEXT_DO_TIME
,
703 kret
= krb5_rd_rep(context
, ctx
->auth_context
, &inbuf
, &repl
);
705 *minor_status
= kret
;
706 return GSS_S_FAILURE
;
708 krb5_free_ap_rep_enc_part(context
, repl
);
709 krb5_auth_con_setflags(context
, ctx
->auth_context
, auth_flags
);
712 /* We need to check the liftime */
714 OM_uint32 lifetime_rec
;
716 ret
= _gsskrb5_lifetime_left(minor_status
,
723 if (lifetime_rec
== 0) {
724 return GSS_S_CONTEXT_EXPIRED
;
727 if (time_rec
) *time_rec
= lifetime_rec
;
730 /* We need to give the caller the flags which are in use */
731 if (ret_flags
) *ret_flags
= ctx
->flags
;
734 kret
= krb5_copy_principal(context
,
736 (gsskrb5_name
*)src_name
);
738 *minor_status
= kret
;
739 return GSS_S_FAILURE
;
744 * After the krb5_rd_rep() the remote and local seq_number should
745 * be the same, because the client just replies the seq_number
746 * from our AP-REP in its AP-REP, but then the client uses the
747 * seq_number from its AP-REQ for GSS_wrap()
750 int32_t tmp_r_seq_number
, tmp_l_seq_number
;
752 kret
= krb5_auth_getremoteseqnumber(context
,
756 *minor_status
= kret
;
757 return GSS_S_FAILURE
;
760 kret
= krb5_auth_con_getlocalseqnumber(context
,
765 *minor_status
= kret
;
766 return GSS_S_FAILURE
;
770 * Here we check if the client has responsed with our local seq_number,
772 if (tmp_r_seq_number
!= tmp_l_seq_number
) {
773 return GSS_S_UNSEQ_TOKEN
;
778 * We need to reset the remote seq_number, because the client will use,
779 * the old one for the GSS_wrap() calls
782 kret
= krb5_auth_con_setremoteseqnumber(context
,
786 *minor_status
= kret
;
787 return GSS_S_FAILURE
;
791 return gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
792 delegated_cred_handle
);
797 _gsskrb5_accept_sec_context(OM_uint32
* minor_status
,
798 gss_ctx_id_t
* context_handle
,
799 const gss_cred_id_t acceptor_cred_handle
,
800 const gss_buffer_t input_token_buffer
,
801 const gss_channel_bindings_t input_chan_bindings
,
802 gss_name_t
* src_name
,
804 gss_buffer_t output_token
,
805 OM_uint32
* ret_flags
,
806 OM_uint32
* time_rec
,
807 gss_cred_id_t
* delegated_cred_handle
)
809 krb5_context context
;
813 GSSAPI_KRB5_INIT(&context
);
815 output_token
->length
= 0;
816 output_token
->value
= NULL
;
818 if (src_name
!= NULL
)
821 *mech_type
= GSS_KRB5_MECHANISM
;
823 if (*context_handle
== GSS_C_NO_CONTEXT
) {
824 ret
= _gsskrb5_create_ctx(minor_status
,
833 ctx
= (gsskrb5_ctx
)*context_handle
;
837 * TODO: check the channel_bindings
838 * (above just sets them to krb5 layer)
841 HEIMDAL_MUTEX_lock(&ctx
->ctx_id_mutex
);
843 switch (ctx
->state
) {
845 ret
= gsskrb5_acceptor_start(minor_status
,
848 acceptor_cred_handle
,
856 delegated_cred_handle
);
858 case ACCEPTOR_WAIT_FOR_DCESTYLE
:
859 ret
= acceptor_wait_for_dcestyle(minor_status
,
862 acceptor_cred_handle
,
870 delegated_cred_handle
);
874 * If we get there, the caller have called
875 * gss_accept_sec_context() one time too many.
877 ret
= GSS_S_BAD_STATUS
;
880 /* TODO: is this correct here? --metze */
881 ret
= GSS_S_BAD_STATUS
;
885 HEIMDAL_MUTEX_unlock(&ctx
->ctx_id_mutex
);
887 if (GSS_ERROR(ret
)) {
889 _gsskrb5_delete_sec_context(&min2
, context_handle
, GSS_C_NO_BUFFER
);