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(OM_uint32 *minor_status,
154 krb5_context context,
155 gss_cred_id_t *delegated_cred_handle)
157 krb5_ccache ccache = NULL;
158 krb5_error_code kret;
159 int32_t ac_flags, ret = GSS_S_COMPLETE;
163 /* XXX Create a new delegated_cred_handle? */
164 if (delegated_cred_handle == NULL) {
165 ret = GSS_S_COMPLETE;
169 *delegated_cred_handle = NULL;
170 kret = krb5_cc_resolve(context, "MEMORY:anonymous", &ccache);
172 ctx->flags &= ~GSS_C_DELEG_FLAG;
176 kret = krb5_cc_initialize(context, ccache, ctx->source);
178 ctx->flags &= ~GSS_C_DELEG_FLAG;
182 krb5_auth_con_removeflags(context,
184 KRB5_AUTH_CONTEXT_DO_TIME,
186 kret = krb5_rd_cred2(context,
190 krb5_auth_con_setflags(context,
194 ctx->flags &= ~GSS_C_DELEG_FLAG;
196 *minor_status = kret;
200 if (delegated_cred_handle) {
203 ret = _gsskrb5_krb5_import_cred(minor_status,
207 delegated_cred_handle);
208 if (ret != GSS_S_COMPLETE)
211 handle = (gsskrb5_cred) *delegated_cred_handle;
212 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
215 * A root TGT is one of the form krbtgt/REALM@SAME-REALM.
217 * A destination TGT is a root TGT for the same realm as the acceptor
220 * Normally clients delegate a root TGT for the client's realm.
222 * In some deployments clients may want to delegate destination TGTs as
223 * a form of constrained delegation: so that the destination service
224 * cannot use the delegated credential to impersonate the client
225 * principal to services in its home realm (due to KDC lineage/transit
226 * checks). In those deployments there may not even be a route back to
227 * the KDCs of the client's realm, and attempting to use a
228 * non-destination TGT might even lead to timeouts.
230 * We could simply pretend not to have obtained a credential, except
231 * that a) we don't (yet) have an app name here for the appdefault we
232 * need to check, b) the application really wants to be able to log a
233 * message about the delegated credential being no good.
235 * Thus we leave it to _gsskrb5_store_cred_into2() to decide what to do
236 * with non-destination TGTs. To do that, it needs the realm of the
237 * acceptor service, which we record here.
239 handle->destination_realm =
240 strdup(krb5_principal_get_realm(context, ctx->target));
241 if (handle->destination_realm == NULL) {
242 _gsskrb5_release_cred(minor_status, delegated_cred_handle);
243 *minor_status = krb5_enomem(context);
251 /* Don't destroy the default cred cache */
252 if (delegated_cred_handle == NULL)
253 krb5_cc_close(context, ccache);
255 krb5_cc_destroy(context, ccache);
261 gsskrb5_acceptor_ready(OM_uint32 * minor_status,
263 krb5_context context,
264 gss_cred_id_t *delegated_cred_handle)
270 krb5_auth_con_getremoteseqnumber (context,
274 _gsskrb5i_is_cfx(context, ctx, 1);
275 is_cfx = (ctx->more_flags & IS_CFX);
277 ret = _gssapi_msg_order_create(minor_status,
279 _gssapi_msg_order_f(ctx->flags),
280 seq_number, 0, is_cfx);
285 * If requested, set local sequence num to remote sequence if this
286 * isn't a mutual authentication context
288 if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
289 krb5_auth_con_setlocalseqnumber(context,
295 * We should handle the delegation ticket, in case it's there
297 if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
298 ret = gsskrb5_accept_delegated_token(minor_status,
301 delegated_cred_handle);
302 if (ret != GSS_S_COMPLETE)
305 /* Well, looks like it wasn't there after all */
306 ctx->flags &= ~GSS_C_DELEG_FLAG;
309 ctx->state = ACCEPTOR_READY;
310 ctx->more_flags |= OPEN;
312 return GSS_S_COMPLETE;
316 send_error_token(OM_uint32 *minor_status,
317 krb5_context context,
318 krb5_error_code kret,
319 krb5_principal server,
321 gss_buffer_t output_token)
323 krb5_principal ap_req_server = NULL;
326 /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
327 tells windows to try again with the corrected timestamp. See
328 [MS-KILE] 2.2.1 KERB-ERROR-DATA */
329 krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
331 /* build server from request if the acceptor had not selected one */
332 if (server == NULL) {
335 ret = krb5_decode_ap_req(context, indata, &ap_req);
338 return GSS_S_FAILURE;
340 ret = _krb5_principalname2krb5_principal(context,
343 ap_req.ticket.realm);
344 free_AP_REQ(&ap_req);
347 return GSS_S_FAILURE;
349 server = ap_req_server;
352 ret = krb5_mk_error(context, kret, NULL, &e_data, NULL,
353 server, NULL, NULL, &outbuf);
355 krb5_free_principal(context, ap_req_server);
358 return GSS_S_FAILURE;
361 ret = _gsskrb5_encapsulate(minor_status,
366 krb5_data_free (&outbuf);
371 return GSS_S_CONTINUE_NEEDED;
376 gsskrb5_acceptor_start(OM_uint32 * minor_status,
378 krb5_context context,
379 gss_const_cred_id_t acceptor_cred_handle,
380 const gss_buffer_t input_token_buffer,
381 const gss_channel_bindings_t input_chan_bindings,
382 gss_name_t * src_name,
384 gss_buffer_t output_token,
385 OM_uint32 * ret_flags,
386 OM_uint32 * time_rec,
387 gss_cred_id_t * delegated_cred_handle)
389 krb5_error_code kret;
390 OM_uint32 ret = GSS_S_COMPLETE;
392 krb5_flags ap_options;
393 krb5_keytab keytab = NULL;
396 const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
399 * We may, or may not, have an escapsulation.
401 ret = _gsskrb5_decapsulate (minor_status,
408 /* Could be a raw AP-REQ (check for APPLICATION tag) */
409 if (input_token_buffer->length == 0 ||
410 ((const uint8_t *)input_token_buffer->value)[0] != 0x6E) {
411 *minor_status = ASN1_MISPLACED_FIELD;
412 return GSS_S_DEFECTIVE_TOKEN;
415 /* Assume that there is no OID wrapping. */
416 indata.length = input_token_buffer->length;
417 indata.data = input_token_buffer->value;
421 * We need to get our keytab
423 if (acceptor_cred == NULL) {
424 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
425 if (_gsskrb5_keytab != NULL) {
427 kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
429 kret = krb5_kt_resolve(context, name, &keytab);
437 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
438 } else if (acceptor_cred->keytab != NULL) {
439 keytab = acceptor_cred->keytab;
443 * We need to check the ticket and create the AP-REP packet
447 krb5_rd_req_in_ctx in = NULL;
448 krb5_rd_req_out_ctx out = NULL;
449 krb5_principal server = NULL;
452 server = acceptor_cred->principal;
454 kret = krb5_rd_req_in_ctx_alloc(context, &in);
456 kret = krb5_rd_req_in_set_keytab(context, in, keytab);
459 krb5_rd_req_in_ctx_free(context, in);
461 krb5_kt_close(context, keytab);
462 *minor_status = kret;
463 return GSS_S_FAILURE;
466 kret = krb5_rd_req_ctx(context,
471 krb5_rd_req_in_ctx_free(context, in);
473 krb5_kt_close(context, keytab);
474 if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
476 * No reply in non-MUTUAL mode, but we don't know that its
477 * non-MUTUAL mode yet, thats inside the 8003 checksum, so
478 * lets only send the error token on clock skew, that
479 * limit when send error token for non-MUTUAL.
481 return send_error_token(minor_status, context, kret,
482 server, &indata, output_token);
484 *minor_status = kret;
485 return GSS_S_FAILURE;
489 * we need to remember some data on the context_handle.
491 kret = krb5_rd_req_out_get_ap_req_options(context, out,
494 kret = krb5_rd_req_out_get_ticket(context, out,
497 kret = krb5_rd_req_out_get_keyblock(context, out,
498 &ctx->service_keyblock);
499 ctx->endtime = ctx->ticket->ticket.endtime;
501 krb5_rd_req_out_ctx_free(context, out);
504 *minor_status = kret;
511 * We need to copy the principal names to the context and the
514 kret = krb5_copy_principal(context,
519 *minor_status = kret;
523 kret = krb5_copy_principal(context,
528 *minor_status = kret;
533 * We need to setup some compat stuff, this assumes that
534 * context_handle->target is already set.
536 ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
540 if (src_name != NULL) {
541 kret = krb5_copy_principal (context,
543 (gsskrb5_name*)src_name);
546 *minor_status = kret;
552 * We need to get the flags out of the 8003 checksum.
556 krb5_authenticator authenticator;
558 kret = krb5_auth_con_getauthenticator(context,
563 *minor_status = kret;
567 if (authenticator->cksum != NULL
568 && authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
569 ret = _gsskrb5_verify_8003_checksum(context,
577 krb5_free_authenticator(context, &authenticator);
581 if (authenticator->cksum != NULL) {
584 kret = krb5_crypto_init(context,
585 ctx->auth_context->keyblock,
588 krb5_free_authenticator(context, &authenticator);
590 *minor_status = kret;
595 * Windows accepts Samba3's use of a kerberos, rather than
596 * GSSAPI checksum here
599 _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
600 kret = krb5_verify_checksum(context,
601 crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
602 authenticator->cksum);
603 krb5_crypto_destroy(context, crypto);
606 krb5_free_authenticator(context, &authenticator);
608 *minor_status = kret;
614 * If there is no checksum or a kerberos checksum (which Windows
615 * and Samba accept), we use the ap_options to guess the mutual
619 ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
620 if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
621 ctx->flags |= GSS_C_MUTUAL_FLAG;
623 krb5_free_authenticator(context, &authenticator);
626 if(ctx->flags & GSS_C_MUTUAL_FLAG) {
630 _gsskrb5i_is_cfx(context, ctx, 1);
631 is_cfx = (ctx->more_flags & IS_CFX);
633 if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
639 * If there is a initiator subkey, copy that to acceptor
640 * subkey to match Windows behavior
642 kret = krb5_auth_con_getremotesubkey(context,
646 kret = krb5_auth_con_setlocalsubkey(context,
652 krb5_free_keyblock(context, rkey);
655 ctx->more_flags |= ACCEPTOR_SUBKEY;
656 krb5_auth_con_addflags(context, ctx->auth_context,
657 KRB5_AUTH_CONTEXT_USE_SUBKEY,
661 kret = krb5_mk_rep(context,
665 *minor_status = kret;
666 return GSS_S_FAILURE;
669 if (IS_DCE_STYLE(ctx)) {
670 output_token->length = outbuf.length;
671 output_token->value = outbuf.data;
673 ret = _gsskrb5_encapsulate(minor_status,
678 krb5_data_free (&outbuf);
684 ctx->flags |= GSS_C_TRANS_FLAG;
686 /* Remember the flags */
688 ctx->endtime = ctx->ticket->ticket.endtime;
689 ctx->more_flags |= OPEN;
692 *mech_type = GSS_KRB5_MECHANISM;
695 ret = _gsskrb5_lifetime_left(minor_status,
705 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
708 if (IS_DCE_STYLE(ctx)) {
710 * Return flags to caller, but we haven't processed
714 *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
716 ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
717 return GSS_S_CONTINUE_NEEDED;
720 ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
721 delegated_cred_handle);
724 *ret_flags = ctx->flags;
730 acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
732 krb5_context context,
733 gss_const_cred_id_t acceptor_cred_handle,
734 const gss_buffer_t input_token_buffer,
735 const gss_channel_bindings_t input_chan_bindings,
736 gss_name_t * src_name,
738 gss_buffer_t output_token,
739 OM_uint32 * ret_flags,
740 OM_uint32 * time_rec,
741 gss_cred_id_t * delegated_cred_handle)
744 krb5_error_code kret;
746 int32_t r_seq_number, l_seq_number;
749 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
752 inbuf.length = input_token_buffer->length;
753 inbuf.data = input_token_buffer->value;
756 * We need to remeber the old remote seq_number, then check if the
757 * client has replied with our local seq_number, and then reset
758 * the remote seq_number to the old value
761 kret = krb5_auth_con_getlocalseqnumber(context,
765 *minor_status = kret;
766 return GSS_S_FAILURE;
769 kret = krb5_auth_con_getremoteseqnumber(context,
773 *minor_status = kret;
774 return GSS_S_FAILURE;
777 kret = krb5_auth_con_setremoteseqnumber(context,
781 *minor_status = kret;
782 return GSS_S_FAILURE;
787 * We need to verify the AP_REP, but we need to flag that this is
788 * DCE_STYLE, so don't check the timestamps this time, but put the
789 * flag DO_TIME back afterward.
792 krb5_ap_rep_enc_part *repl;
795 krb5_auth_con_removeflags(context,
797 KRB5_AUTH_CONTEXT_DO_TIME,
800 kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
802 *minor_status = kret;
803 return GSS_S_FAILURE;
805 krb5_free_ap_rep_enc_part(context, repl);
806 krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
809 /* We need to check the liftime */
811 OM_uint32 lifetime_rec;
813 ret = _gsskrb5_lifetime_left(minor_status,
820 if (lifetime_rec == 0) {
821 return GSS_S_CONTEXT_EXPIRED;
824 if (time_rec) *time_rec = lifetime_rec;
827 /* We need to give the caller the flags which are in use */
828 if (ret_flags) *ret_flags = ctx->flags;
831 kret = krb5_copy_principal(context,
833 (gsskrb5_name*)src_name);
835 *minor_status = kret;
836 return GSS_S_FAILURE;
841 * After the krb5_rd_rep() the remote and local seq_number should
842 * be the same, because the client just replies the seq_number
843 * from our AP-REP in its AP-REP, but then the client uses the
844 * seq_number from its AP-REQ for GSS_wrap()
847 int32_t tmp_r_seq_number, tmp_l_seq_number;
849 kret = krb5_auth_con_getremoteseqnumber(context,
853 *minor_status = kret;
854 return GSS_S_FAILURE;
857 kret = krb5_auth_con_getlocalseqnumber(context,
862 *minor_status = kret;
863 return GSS_S_FAILURE;
867 * Here we check if the client has responsed with our local seq_number,
869 if (tmp_r_seq_number != tmp_l_seq_number) {
870 return GSS_S_UNSEQ_TOKEN;
875 * We need to reset the remote seq_number, because the client will use,
876 * the old one for the GSS_wrap() calls
879 kret = krb5_auth_con_setremoteseqnumber(context,
883 *minor_status = kret;
884 return GSS_S_FAILURE;
888 return gsskrb5_acceptor_ready(minor_status, ctx, context,
889 delegated_cred_handle);
893 OM_uint32 GSSAPI_CALLCONV
894 _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
895 gss_ctx_id_t * context_handle,
896 gss_const_cred_id_t acceptor_cred_handle,
897 const gss_buffer_t input_token_buffer,
898 const gss_channel_bindings_t input_chan_bindings,
899 gss_name_t * src_name,
901 gss_buffer_t output_token,
902 OM_uint32 * ret_flags,
903 OM_uint32 * time_rec,
904 gss_cred_id_t * delegated_cred_handle)
906 krb5_context context;
910 GSSAPI_KRB5_INIT(&context);
912 output_token->length = 0;
913 output_token->value = NULL;
915 if (src_name != NULL)
918 *mech_type = GSS_KRB5_MECHANISM;
920 if (*context_handle == GSS_C_NO_CONTEXT) {
921 ret = _gsskrb5_create_ctx(minor_status,
930 ctx = (gsskrb5_ctx)*context_handle;
934 * TODO: check the channel_bindings
935 * (above just sets them to krb5 layer)
938 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
940 switch (ctx->state) {
942 ret = gsskrb5_acceptor_start(minor_status,
945 acceptor_cred_handle,
953 delegated_cred_handle);
955 case ACCEPTOR_WAIT_FOR_DCESTYLE:
956 ret = acceptor_wait_for_dcestyle(minor_status,
959 acceptor_cred_handle,
967 delegated_cred_handle);
971 * If we get there, the caller have called
972 * gss_accept_sec_context() one time too many.
974 ret = GSS_S_BAD_STATUS;
977 /* TODO: is this correct here? --metze */
978 ret = GSS_S_BAD_STATUS;
982 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
984 if (GSS_ERROR(ret)) {
986 _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);