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_krb5_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.
467 krb5_authenticator authenticator
;
469 kret
= krb5_auth_con_getauthenticator(context
,
474 *minor_status
= kret
;
478 if (authenticator
->cksum
== NULL
) {
479 krb5_free_authenticator(context
, &authenticator
);
481 return GSS_S_BAD_BINDINGS
;
484 if (authenticator
->cksum
->cksumtype
== CKSUMTYPE_GSSAPI
) {
485 ret
= _gsskrb5_verify_8003_checksum(minor_status
,
487 authenticator
->cksum
,
491 krb5_free_authenticator(context
, &authenticator
);
498 kret
= krb5_crypto_init(context
,
499 ctx
->auth_context
->keyblock
,
502 krb5_free_authenticator(context
, &authenticator
);
505 *minor_status
= kret
;
510 * Windows accepts Samba3's use of a kerberos, rather than
511 * GSSAPI checksum here
514 kret
= krb5_verify_checksum(context
,
515 crypto
, KRB5_KU_AP_REQ_AUTH_CKSUM
, NULL
, 0,
516 authenticator
->cksum
);
517 krb5_free_authenticator(context
, &authenticator
);
518 krb5_crypto_destroy(context
, crypto
);
522 *minor_status
= kret
;
527 * Samba style get some flags (but not DCE-STYLE), use
528 * ap_options to guess the mutual flag.
530 ctx
->flags
= GSS_C_REPLAY_FLAG
| GSS_C_SEQUENCE_FLAG
;
531 if (ap_options
& AP_OPTS_MUTUAL_REQUIRED
)
532 ctx
->flags
|= GSS_C_MUTUAL_FLAG
;
536 if(ctx
->flags
& GSS_C_MUTUAL_FLAG
) {
540 _gsskrb5i_is_cfx(context
, ctx
, 1);
541 is_cfx
= (ctx
->more_flags
& IS_CFX
);
543 if (is_cfx
|| (ap_options
& AP_OPTS_USE_SUBKEY
)) {
549 * If there is a initiator subkey, copy that to acceptor
550 * subkey to match Windows behavior
552 kret
= krb5_auth_con_getremotesubkey(context
,
556 kret
= krb5_auth_con_setlocalsubkey(context
,
561 krb5_free_keyblock(context
, rkey
);
565 ctx
->more_flags
|= ACCEPTOR_SUBKEY
;
566 krb5_auth_con_addflags(context
, ctx
->auth_context
,
567 KRB5_AUTH_CONTEXT_USE_SUBKEY
,
571 kret
= krb5_mk_rep(context
,
575 *minor_status
= kret
;
576 return GSS_S_FAILURE
;
579 if (IS_DCE_STYLE(ctx
)) {
580 output_token
->length
= outbuf
.length
;
581 output_token
->value
= outbuf
.data
;
583 ret
= _gsskrb5_encapsulate(minor_status
,
588 krb5_data_free (&outbuf
);
594 ctx
->flags
|= GSS_C_TRANS_FLAG
;
596 /* Remember the flags */
598 ctx
->lifetime
= ctx
->ticket
->ticket
.endtime
;
599 ctx
->more_flags
|= OPEN
;
602 *mech_type
= GSS_KRB5_MECHANISM
;
605 ret
= _gsskrb5_lifetime_left(minor_status
,
615 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
618 if (IS_DCE_STYLE(ctx
)) {
620 * Return flags to caller, but we haven't processed
624 *ret_flags
= (ctx
->flags
& ~GSS_C_DELEG_FLAG
);
626 ctx
->state
= ACCEPTOR_WAIT_FOR_DCESTYLE
;
627 return GSS_S_CONTINUE_NEEDED
;
630 ret
= gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
631 delegated_cred_handle
);
634 *ret_flags
= ctx
->flags
;
640 acceptor_wait_for_dcestyle(OM_uint32
* minor_status
,
642 krb5_context context
,
643 const gss_cred_id_t acceptor_cred_handle
,
644 const gss_buffer_t input_token_buffer
,
645 const gss_channel_bindings_t input_chan_bindings
,
646 gss_name_t
* src_name
,
648 gss_buffer_t output_token
,
649 OM_uint32
* ret_flags
,
650 OM_uint32
* time_rec
,
651 gss_cred_id_t
* delegated_cred_handle
)
654 krb5_error_code kret
;
656 int32_t r_seq_number
, l_seq_number
;
659 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
662 inbuf
.length
= input_token_buffer
->length
;
663 inbuf
.data
= input_token_buffer
->value
;
666 * We need to remeber the old remote seq_number, then check if the
667 * client has replied with our local seq_number, and then reset
668 * the remote seq_number to the old value
671 kret
= krb5_auth_con_getlocalseqnumber(context
,
675 *minor_status
= kret
;
676 return GSS_S_FAILURE
;
679 kret
= krb5_auth_getremoteseqnumber(context
,
683 *minor_status
= kret
;
684 return GSS_S_FAILURE
;
687 kret
= krb5_auth_con_setremoteseqnumber(context
,
691 *minor_status
= kret
;
692 return GSS_S_FAILURE
;
697 * We need to verify the AP_REP, but we need to flag that this is
698 * DCE_STYLE, so don't check the timestamps this time, but put the
699 * flag DO_TIME back afterward.
702 krb5_ap_rep_enc_part
*repl
;
705 krb5_auth_con_removeflags(context
,
707 KRB5_AUTH_CONTEXT_DO_TIME
,
710 kret
= krb5_rd_rep(context
, ctx
->auth_context
, &inbuf
, &repl
);
712 *minor_status
= kret
;
713 return GSS_S_FAILURE
;
715 krb5_free_ap_rep_enc_part(context
, repl
);
716 krb5_auth_con_setflags(context
, ctx
->auth_context
, auth_flags
);
719 /* We need to check the liftime */
721 OM_uint32 lifetime_rec
;
723 ret
= _gsskrb5_lifetime_left(minor_status
,
730 if (lifetime_rec
== 0) {
731 return GSS_S_CONTEXT_EXPIRED
;
734 if (time_rec
) *time_rec
= lifetime_rec
;
737 /* We need to give the caller the flags which are in use */
738 if (ret_flags
) *ret_flags
= ctx
->flags
;
741 kret
= krb5_copy_principal(context
,
743 (gsskrb5_name
*)src_name
);
745 *minor_status
= kret
;
746 return GSS_S_FAILURE
;
751 * After the krb5_rd_rep() the remote and local seq_number should
752 * be the same, because the client just replies the seq_number
753 * from our AP-REP in its AP-REP, but then the client uses the
754 * seq_number from its AP-REQ for GSS_wrap()
757 int32_t tmp_r_seq_number
, tmp_l_seq_number
;
759 kret
= krb5_auth_getremoteseqnumber(context
,
763 *minor_status
= kret
;
764 return GSS_S_FAILURE
;
767 kret
= krb5_auth_con_getlocalseqnumber(context
,
772 *minor_status
= kret
;
773 return GSS_S_FAILURE
;
777 * Here we check if the client has responsed with our local seq_number,
779 if (tmp_r_seq_number
!= tmp_l_seq_number
) {
780 return GSS_S_UNSEQ_TOKEN
;
785 * We need to reset the remote seq_number, because the client will use,
786 * the old one for the GSS_wrap() calls
789 kret
= krb5_auth_con_setremoteseqnumber(context
,
793 *minor_status
= kret
;
794 return GSS_S_FAILURE
;
798 return gsskrb5_acceptor_ready(minor_status
, ctx
, context
,
799 delegated_cred_handle
);
804 _gsskrb5_accept_sec_context(OM_uint32
* minor_status
,
805 gss_ctx_id_t
* context_handle
,
806 const gss_cred_id_t acceptor_cred_handle
,
807 const gss_buffer_t input_token_buffer
,
808 const gss_channel_bindings_t input_chan_bindings
,
809 gss_name_t
* src_name
,
811 gss_buffer_t output_token
,
812 OM_uint32
* ret_flags
,
813 OM_uint32
* time_rec
,
814 gss_cred_id_t
* delegated_cred_handle
)
816 krb5_context context
;
820 GSSAPI_KRB5_INIT(&context
);
822 output_token
->length
= 0;
823 output_token
->value
= NULL
;
825 if (src_name
!= NULL
)
828 *mech_type
= GSS_KRB5_MECHANISM
;
830 if (*context_handle
== GSS_C_NO_CONTEXT
) {
831 ret
= _gsskrb5_create_ctx(minor_status
,
840 ctx
= (gsskrb5_ctx
)*context_handle
;
844 * TODO: check the channel_bindings
845 * (above just sets them to krb5 layer)
848 HEIMDAL_MUTEX_lock(&ctx
->ctx_id_mutex
);
850 switch (ctx
->state
) {
852 ret
= gsskrb5_acceptor_start(minor_status
,
855 acceptor_cred_handle
,
863 delegated_cred_handle
);
865 case ACCEPTOR_WAIT_FOR_DCESTYLE
:
866 ret
= acceptor_wait_for_dcestyle(minor_status
,
869 acceptor_cred_handle
,
877 delegated_cred_handle
);
881 * If we get there, the caller have called
882 * gss_accept_sec_context() one time too many.
884 ret
= GSS_S_BAD_STATUS
;
887 /* TODO: is this correct here? --metze */
888 ret
= GSS_S_BAD_STATUS
;
892 HEIMDAL_MUTEX_unlock(&ctx
->ctx_id_mutex
);
894 if (GSS_ERROR(ret
)) {
896 _gsskrb5_delete_sec_context(&min2
, context_handle
, GSS_C_NO_BUFFER
);