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 "krb5/gsskrb5_locl.h"
36 RCSID("$Id: accept_sec_context.c 23433 2008-07-26 18:44:26Z lha $");
38 HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
39 krb5_keytab _gsskrb5_keytab;
42 _gsskrb5_register_acceptor_identity (const char *identity)
47 ret = _gsskrb5_init(&context);
51 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
53 if(_gsskrb5_keytab != NULL) {
54 krb5_kt_close(context, _gsskrb5_keytab);
55 _gsskrb5_keytab = NULL;
57 if (identity == NULL) {
58 ret = krb5_kt_default(context, &_gsskrb5_keytab);
62 asprintf(&p, "FILE:%s", identity);
64 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
67 ret = krb5_kt_resolve(context, p, &_gsskrb5_keytab);
70 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
73 return GSS_S_COMPLETE;
77 _gsskrb5i_is_cfx(gsskrb5_ctx ctx, int *is_cfx)
80 int acceptor = (ctx->more_flags & LOCAL) == 0;
85 if (ctx->auth_context->local_subkey)
86 key = ctx->auth_context->local_subkey;
88 key = ctx->auth_context->remote_subkey;
90 if (ctx->auth_context->remote_subkey)
91 key = ctx->auth_context->remote_subkey;
93 key = ctx->auth_context->local_subkey;
96 key = ctx->auth_context->keyblock;
101 switch (key->keytype) {
102 case ETYPE_DES_CBC_CRC:
103 case ETYPE_DES_CBC_MD4:
104 case ETYPE_DES_CBC_MD5:
105 case ETYPE_DES3_CBC_MD5:
106 case ETYPE_DES3_CBC_SHA1:
107 case ETYPE_ARCFOUR_HMAC_MD5:
108 case ETYPE_ARCFOUR_HMAC_MD5_56:
112 if ((acceptor && ctx->auth_context->local_subkey) ||
113 (!acceptor && ctx->auth_context->remote_subkey))
114 ctx->more_flags |= ACCEPTOR_SUBKEY;
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_gen_new (context, &krb5_mcc_ops, &ccache);
142 ctx->flags &= ~GSS_C_DELEG_FLAG;
146 kret = krb5_cc_initialize(context, ccache, ctx->source);
148 ctx->flags &= ~GSS_C_DELEG_FLAG;
152 krb5_auth_con_removeflags(context,
154 KRB5_AUTH_CONTEXT_DO_TIME,
156 kret = krb5_rd_cred2(context,
160 krb5_auth_con_setflags(context,
164 ctx->flags &= ~GSS_C_DELEG_FLAG;
166 *minor_status = kret;
170 if (delegated_cred_handle) {
173 ret = _gsskrb5_import_cred(minor_status,
177 delegated_cred_handle);
178 if (ret != GSS_S_COMPLETE)
181 handle = (gsskrb5_cred) *delegated_cred_handle;
183 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
184 krb5_cc_close(context, ccache);
190 /* Don't destroy the default cred cache */
191 if (delegated_cred_handle == NULL)
192 krb5_cc_close(context, ccache);
194 krb5_cc_destroy(context, ccache);
200 gsskrb5_acceptor_ready(OM_uint32 * minor_status,
202 krb5_context context,
203 gss_cred_id_t *delegated_cred_handle)
209 krb5_auth_getremoteseqnumber (context,
213 _gsskrb5i_is_cfx(ctx, &is_cfx);
215 ret = _gssapi_msg_order_create(minor_status,
217 _gssapi_msg_order_f(ctx->flags),
218 seq_number, 0, is_cfx);
223 * If requested, set local sequence num to remote sequence if this
224 * isn't a mutual authentication context
226 if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
227 krb5_auth_con_setlocalseqnumber(context,
233 * We should handle the delegation ticket, in case it's there
235 if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
236 ret = gsskrb5_accept_delegated_token(minor_status,
239 delegated_cred_handle);
243 /* Well, looks like it wasn't there after all */
244 ctx->flags &= ~GSS_C_DELEG_FLAG;
247 ctx->state = ACCEPTOR_READY;
248 ctx->more_flags |= OPEN;
250 return GSS_S_COMPLETE;
254 send_error_token(OM_uint32 *minor_status,
255 krb5_context context,
256 krb5_error_code kret,
257 krb5_principal server,
259 gss_buffer_t output_token)
261 krb5_principal ap_req_server = NULL;
265 /* build server from request if the acceptor had not selected one */
266 if (server == NULL) {
269 ret = krb5_decode_ap_req(context, indata, &ap_req);
272 return GSS_S_FAILURE;
274 ret = _krb5_principalname2krb5_principal(context,
277 ap_req.ticket.realm);
278 free_AP_REQ(&ap_req);
281 return GSS_S_FAILURE;
283 server = ap_req_server;
286 ret = krb5_mk_error(context, kret, NULL, NULL, NULL,
287 server, NULL, NULL, &outbuf);
289 krb5_free_principal(context, ap_req_server);
292 return GSS_S_FAILURE;
295 ret = _gsskrb5_encapsulate(minor_status,
300 krb5_data_free (&outbuf);
305 return GSS_S_CONTINUE_NEEDED;
310 gsskrb5_acceptor_start(OM_uint32 * minor_status,
312 krb5_context context,
313 const gss_cred_id_t acceptor_cred_handle,
314 const gss_buffer_t input_token_buffer,
315 const gss_channel_bindings_t input_chan_bindings,
316 gss_name_t * src_name,
318 gss_buffer_t output_token,
319 OM_uint32 * ret_flags,
320 OM_uint32 * time_rec,
321 gss_cred_id_t * delegated_cred_handle)
323 krb5_error_code kret;
324 OM_uint32 ret = GSS_S_COMPLETE;
326 krb5_flags ap_options;
327 krb5_keytab keytab = NULL;
329 const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
332 * We may, or may not, have an escapsulation.
334 ret = _gsskrb5_decapsulate (minor_status,
341 /* Assume that there is no OID wrapping. */
342 indata.length = input_token_buffer->length;
343 indata.data = input_token_buffer->value;
347 * We need to get our keytab
349 if (acceptor_cred == NULL) {
350 if (_gsskrb5_keytab != NULL)
351 keytab = _gsskrb5_keytab;
352 } else if (acceptor_cred->keytab != NULL) {
353 keytab = acceptor_cred->keytab;
357 * We need to check the ticket and create the AP-REP packet
361 krb5_rd_req_in_ctx in = NULL;
362 krb5_rd_req_out_ctx out = NULL;
363 krb5_principal server = NULL;
366 server = acceptor_cred->principal;
368 kret = krb5_rd_req_in_ctx_alloc(context, &in);
370 kret = krb5_rd_req_in_set_keytab(context, in, keytab);
373 krb5_rd_req_in_ctx_free(context, in);
375 *minor_status = kret;
379 kret = krb5_rd_req_ctx(context,
384 krb5_rd_req_in_ctx_free(context, in);
387 * No reply in non-MUTUAL mode, but we don't know that its
388 * non-MUTUAL mode yet, thats inside the 8003 checksum.
390 return send_error_token(minor_status, context, kret,
391 server, &indata, output_token);
395 * we need to remember some data on the context_handle.
397 kret = krb5_rd_req_out_get_ap_req_options(context, out,
400 kret = krb5_rd_req_out_get_ticket(context, out,
403 kret = krb5_rd_req_out_get_keyblock(context, out,
404 &ctx->service_keyblock);
405 ctx->lifetime = ctx->ticket->ticket.endtime;
407 krb5_rd_req_out_ctx_free(context, out);
410 *minor_status = kret;
417 * We need to copy the principal names to the context and the
420 kret = krb5_copy_principal(context,
425 *minor_status = kret;
428 kret = krb5_copy_principal(context,
433 *minor_status = kret;
438 * We need to setup some compat stuff, this assumes that
439 * context_handle->target is already set.
441 ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
445 if (src_name != NULL) {
446 kret = krb5_copy_principal (context,
448 (gsskrb5_name*)src_name);
451 *minor_status = kret;
457 * We need to get the flags out of the 8003 checksum.
460 krb5_authenticator authenticator;
462 kret = krb5_auth_con_getauthenticator(context,
467 *minor_status = kret;
471 if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
472 ret = _gsskrb5_verify_8003_checksum(minor_status,
474 authenticator->cksum,
478 krb5_free_authenticator(context, &authenticator);
485 kret = krb5_crypto_init(context,
486 ctx->auth_context->keyblock,
489 krb5_free_authenticator(context, &authenticator);
492 *minor_status = kret;
497 * Windows accepts Samba3's use of a kerberos, rather than
498 * GSSAPI checksum here
501 kret = krb5_verify_checksum(context,
502 crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
503 authenticator->cksum);
504 krb5_free_authenticator(context, &authenticator);
505 krb5_crypto_destroy(context, crypto);
509 *minor_status = kret;
514 * Samba style get some flags (but not DCE-STYLE)
517 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
521 if(ctx->flags & GSS_C_MUTUAL_FLAG) {
524 _gsskrb5i_is_cfx(ctx, &is_cfx);
527 || (ap_options & AP_OPTS_USE_SUBKEY)) {
528 kret = krb5_auth_con_addflags(context,
530 KRB5_AUTH_CONTEXT_USE_SUBKEY,
532 ctx->more_flags |= ACCEPTOR_SUBKEY;
535 kret = krb5_mk_rep(context,
539 *minor_status = kret;
540 return GSS_S_FAILURE;
543 if (IS_DCE_STYLE(ctx)) {
544 output_token->length = outbuf.length;
545 output_token->value = outbuf.data;
547 ret = _gsskrb5_encapsulate(minor_status,
552 krb5_data_free (&outbuf);
558 ctx->flags |= GSS_C_TRANS_FLAG;
560 /* Remember the flags */
562 ctx->lifetime = ctx->ticket->ticket.endtime;
563 ctx->more_flags |= OPEN;
566 *mech_type = GSS_KRB5_MECHANISM;
569 ret = _gsskrb5_lifetime_left(minor_status,
579 * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
582 if (IS_DCE_STYLE(ctx)) {
584 * Return flags to caller, but we haven't processed
588 *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
590 ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
591 return GSS_S_CONTINUE_NEEDED;
594 ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
595 delegated_cred_handle);
598 *ret_flags = ctx->flags;
604 acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
606 krb5_context context,
607 const gss_cred_id_t acceptor_cred_handle,
608 const gss_buffer_t input_token_buffer,
609 const gss_channel_bindings_t input_chan_bindings,
610 gss_name_t * src_name,
612 gss_buffer_t output_token,
613 OM_uint32 * ret_flags,
614 OM_uint32 * time_rec,
615 gss_cred_id_t * delegated_cred_handle)
618 krb5_error_code kret;
620 int32_t r_seq_number, l_seq_number;
623 * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
626 inbuf.length = input_token_buffer->length;
627 inbuf.data = input_token_buffer->value;
630 * We need to remeber the old remote seq_number, then check if the
631 * client has replied with our local seq_number, and then reset
632 * the remote seq_number to the old value
635 kret = krb5_auth_con_getlocalseqnumber(context,
639 *minor_status = kret;
640 return GSS_S_FAILURE;
643 kret = krb5_auth_getremoteseqnumber(context,
647 *minor_status = kret;
648 return GSS_S_FAILURE;
651 kret = krb5_auth_con_setremoteseqnumber(context,
655 *minor_status = kret;
656 return GSS_S_FAILURE;
661 * We need to verify the AP_REP, but we need to flag that this is
662 * DCE_STYLE, so don't check the timestamps this time, but put the
663 * flag DO_TIME back afterward.
666 krb5_ap_rep_enc_part *repl;
669 krb5_auth_con_removeflags(context,
671 KRB5_AUTH_CONTEXT_DO_TIME,
674 kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
676 *minor_status = kret;
677 return GSS_S_FAILURE;
679 krb5_free_ap_rep_enc_part(context, repl);
680 krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
683 /* We need to check the liftime */
685 OM_uint32 lifetime_rec;
687 ret = _gsskrb5_lifetime_left(minor_status,
694 if (lifetime_rec == 0) {
695 return GSS_S_CONTEXT_EXPIRED;
698 if (time_rec) *time_rec = lifetime_rec;
701 /* We need to give the caller the flags which are in use */
702 if (ret_flags) *ret_flags = ctx->flags;
705 kret = krb5_copy_principal(context,
707 (gsskrb5_name*)src_name);
709 *minor_status = kret;
710 return GSS_S_FAILURE;
715 * After the krb5_rd_rep() the remote and local seq_number should
716 * be the same, because the client just replies the seq_number
717 * from our AP-REP in its AP-REP, but then the client uses the
718 * seq_number from its AP-REQ for GSS_wrap()
721 int32_t tmp_r_seq_number, tmp_l_seq_number;
723 kret = krb5_auth_getremoteseqnumber(context,
727 *minor_status = kret;
728 return GSS_S_FAILURE;
731 kret = krb5_auth_con_getlocalseqnumber(context,
736 *minor_status = kret;
737 return GSS_S_FAILURE;
741 * Here we check if the client has responsed with our local seq_number,
743 if (tmp_r_seq_number != tmp_l_seq_number) {
744 return GSS_S_UNSEQ_TOKEN;
749 * We need to reset the remote seq_number, because the client will use,
750 * the old one for the GSS_wrap() calls
753 kret = krb5_auth_con_setremoteseqnumber(context,
757 *minor_status = kret;
758 return GSS_S_FAILURE;
762 return gsskrb5_acceptor_ready(minor_status, ctx, context,
763 delegated_cred_handle);
768 _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
769 gss_ctx_id_t * context_handle,
770 const gss_cred_id_t acceptor_cred_handle,
771 const gss_buffer_t input_token_buffer,
772 const gss_channel_bindings_t input_chan_bindings,
773 gss_name_t * src_name,
775 gss_buffer_t output_token,
776 OM_uint32 * ret_flags,
777 OM_uint32 * time_rec,
778 gss_cred_id_t * delegated_cred_handle)
780 krb5_context context;
784 GSSAPI_KRB5_INIT(&context);
786 output_token->length = 0;
787 output_token->value = NULL;
789 if (src_name != NULL)
792 *mech_type = GSS_KRB5_MECHANISM;
794 if (*context_handle == GSS_C_NO_CONTEXT) {
795 ret = _gsskrb5_create_ctx(minor_status,
804 ctx = (gsskrb5_ctx)*context_handle;
808 * TODO: check the channel_bindings
809 * (above just sets them to krb5 layer)
812 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
814 switch (ctx->state) {
816 ret = gsskrb5_acceptor_start(minor_status,
819 acceptor_cred_handle,
827 delegated_cred_handle);
829 case ACCEPTOR_WAIT_FOR_DCESTYLE:
830 ret = acceptor_wait_for_dcestyle(minor_status,
833 acceptor_cred_handle,
841 delegated_cred_handle);
845 * If we get there, the caller have called
846 * gss_accept_sec_context() one time too many.
848 ret = GSS_S_BAD_STATUS;
851 /* TODO: is this correct here? --metze */
852 ret = GSS_S_BAD_STATUS;
856 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
858 if (GSS_ERROR(ret)) {
860 _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);