summaryrefslogtreecommitdiff
path: root/src/lib/gssapi/krb5/accept_sec_context.c
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
committerCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
commit33a9b234e7087f573ef08cd7318c6497ba08b439 (patch)
treed0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/gssapi/krb5/accept_sec_context.c
Notes
Diffstat (limited to 'src/lib/gssapi/krb5/accept_sec_context.c')
-rw-r--r--src/lib/gssapi/krb5/accept_sec_context.c1347
1 files changed, 1347 insertions, 0 deletions
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
new file mode 100644
index 000000000000..580d08cbf53e
--- /dev/null
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -0,0 +1,1347 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2000, 2004, 2007, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "gssapiP_krb5.h"
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <assert.h>
+
+#ifdef CFX_EXERCISE
+#define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
+#else
+#define CFX_ACCEPTOR_SUBKEY 1
+#endif
+
+#ifndef LEAN_CLIENT
+
+static OM_uint32
+create_constrained_deleg_creds(OM_uint32 *minor_status,
+ krb5_gss_cred_id_t verifier_cred_handle,
+ krb5_ticket *ticket,
+ krb5_gss_cred_id_t *out_cred,
+ krb5_context context)
+{
+ OM_uint32 major_status;
+ krb5_creds krb_creds;
+ krb5_data *data;
+ krb5_error_code code;
+
+ assert(out_cred != NULL);
+ assert(verifier_cred_handle->usage == GSS_C_BOTH);
+
+ memset(&krb_creds, 0, sizeof(krb_creds));
+ krb_creds.client = ticket->enc_part2->client;
+ krb_creds.server = ticket->server;
+ krb_creds.keyblock = *(ticket->enc_part2->session);
+ krb_creds.ticket_flags = ticket->enc_part2->flags;
+ krb_creds.times = ticket->enc_part2->times;
+ krb_creds.magic = KV5M_CREDS;
+ krb_creds.authdata = NULL;
+
+ code = encode_krb5_ticket(ticket, &data);
+ if (code) {
+ *minor_status = code;
+ return GSS_S_FAILURE;
+ }
+
+ krb_creds.ticket = *data;
+
+ major_status = kg_compose_deleg_cred(minor_status,
+ verifier_cred_handle,
+ &krb_creds,
+ GSS_C_INDEFINITE,
+ out_cred,
+ NULL,
+ context);
+
+ krb5_free_data(context, data);
+
+ return major_status;
+}
+
+/* Decode, decrypt and store the forwarded creds in the local ccache. */
+static krb5_error_code
+rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
+ krb5_context context;
+ krb5_auth_context auth_context;
+ krb5_data *inbuf;
+ krb5_gss_cred_id_t *out_cred;
+{
+ krb5_creds ** creds = NULL;
+ krb5_error_code retval;
+ krb5_ccache ccache = NULL;
+ krb5_gss_cred_id_t cred = NULL;
+ krb5_auth_context new_auth_ctx = NULL;
+ krb5_int32 flags_org;
+
+ if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
+ return retval;
+ krb5_auth_con_setflags(context, auth_context,
+ 0);
+
+ /*
+ * By the time krb5_rd_cred is called here (after krb5_rd_req has been
+ * called in krb5_gss_accept_sec_context), the "keyblock" field of
+ * auth_context contains a pointer to the session key, and the
+ * "recv_subkey" field might contain a session subkey. Either of
+ * these (the "recv_subkey" if it isn't NULL, otherwise the
+ * "keyblock") might have been used to encrypt the encrypted part of
+ * the KRB_CRED message that contains the forwarded credentials. (The
+ * Java Crypto and Security Implementation from the DSTC in Australia
+ * always uses the session key. But apparently it never negotiates a
+ * subkey, so this code works fine against a JCSI client.) Up to the
+ * present, though, GSSAPI clients linked against the MIT code (which
+ * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at
+ * all -- at this level. So if the first call to krb5_rd_cred fails,
+ * we should call it a second time with another auth context freshly
+ * created by krb5_auth_con_init. All of its keyblock fields will be
+ * NULL, so krb5_rd_cred will assume that the KRB_CRED message is
+ * unencrypted. (The MIT code doesn't actually send the KRB_CRED
+ * message in the clear -- the "authenticator" whose "checksum" ends up
+ * containing the KRB_CRED message does get encrypted.)
+ */
+ if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) {
+ if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
+ goto cleanup;
+ krb5_auth_con_setflags(context, new_auth_ctx, 0);
+ if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
+ &creds, NULL)))
+ goto cleanup;
+ }
+
+ if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
+ ccache = NULL;
+ goto cleanup;
+ }
+
+ if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client)))
+ goto cleanup;
+
+ if ((retval = krb5_cc_store_cred(context, ccache, creds[0])))
+ goto cleanup;
+
+ /* generate a delegated credential handle */
+ if (out_cred) {
+ /* allocate memory for a cred_t... */
+ if (!(cred =
+ (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
+ retval = ENOMEM; /* out of memory? */
+ goto cleanup;
+ }
+
+ /* zero it out... */
+ memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
+
+ retval = k5_mutex_init(&cred->lock);
+ if (retval) {
+ xfree(cred);
+ cred = NULL;
+ goto cleanup;
+ }
+
+ /* copy the client principle into it... */
+ if ((retval =
+ kg_init_name(context, creds[0]->client, NULL, NULL, NULL, 0,
+ &cred->name))) {
+ k5_mutex_destroy(&cred->lock);
+ retval = ENOMEM; /* out of memory? */
+ xfree(cred); /* clean up memory on failure */
+ cred = NULL;
+ goto cleanup;
+ }
+
+ cred->usage = GSS_C_INITIATE; /* we can't accept with this */
+ /* cred->name already set */
+ cred->keytab = NULL; /* no keytab associated with this... */
+ cred->expire = creds[0]->times.endtime; /* store the end time */
+ cred->ccache = ccache; /* the ccache containing the credential */
+ cred->destroy_ccache = 1;
+ ccache = NULL; /* cred takes ownership so don't destroy */
+ }
+
+ /* If there were errors, there might have been a memory leak
+ if (!cred)
+ if ((retval = krb5_cc_close(context, ccache)))
+ goto cleanup;
+ */
+cleanup:
+ if (creds)
+ krb5_free_tgt_creds(context, creds);
+
+ if (ccache)
+ (void)krb5_cc_destroy(context, ccache);
+
+ if (out_cred)
+ *out_cred = cred; /* return credential */
+
+ if (new_auth_ctx)
+ krb5_auth_con_free(context, new_auth_ctx);
+
+ krb5_auth_con_setflags(context, auth_context, flags_org);
+
+ return retval;
+}
+
+
+/*
+ * Performs third leg of DCE authentication
+ */
+static OM_uint32
+kg_accept_dce(minor_status, context_handle, verifier_cred_handle,
+ input_token, input_chan_bindings, src_name, mech_type,
+ output_token, ret_flags, time_rec, delegated_cred_handle)
+ OM_uint32 *minor_status;
+ gss_ctx_id_t *context_handle;
+ gss_cred_id_t verifier_cred_handle;
+ gss_buffer_t input_token;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_name_t *src_name;
+ gss_OID *mech_type;
+ gss_buffer_t output_token;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+ gss_cred_id_t *delegated_cred_handle;
+{
+ krb5_error_code code;
+ krb5_gss_ctx_id_rec *ctx = 0;
+ krb5_timestamp now;
+ krb5_gss_name_t name = NULL;
+ krb5_ui_4 nonce = 0;
+ krb5_data ap_rep;
+ OM_uint32 major_status = GSS_S_FAILURE;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (mech_type)
+ *mech_type = GSS_C_NULL_OID;
+ /* return a bogus cred handle */
+ if (delegated_cred_handle)
+ *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ ctx = (krb5_gss_ctx_id_rec *)*context_handle;
+
+ code = krb5_timeofday(ctx->k5_context, &now);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ ap_rep.data = input_token->value;
+ ap_rep.length = input_token->length;
+
+ code = krb5_rd_rep_dce(ctx->k5_context,
+ ctx->auth_context,
+ &ap_rep,
+ &nonce);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ ctx->established = 1;
+
+ if (src_name) {
+ code = kg_duplicate_name(ctx->k5_context, ctx->there, &name);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ *src_name = (gss_name_t) name;
+ }
+
+ if (mech_type)
+ *mech_type = ctx->mech_used;
+
+ if (time_rec)
+ *time_rec = ctx->krb_times.endtime + ctx->k5_context->clockskew - now;
+
+ /* Never return GSS_C_DELEG_FLAG since we don't support DCE credential
+ * delegation yet. */
+ if (ret_flags)
+ *ret_flags = (ctx->gss_flags & ~GSS_C_DELEG_FLAG);
+
+ *minor_status = 0;
+
+ return GSS_S_COMPLETE;
+
+fail:
+ /* real failure code follows */
+
+ (void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx,
+ NULL);
+ *context_handle = GSS_C_NO_CONTEXT;
+ *minor_status = code;
+
+ return major_status;
+}
+
+static krb5_error_code
+kg_process_extension(krb5_context context,
+ krb5_auth_context auth_context,
+ int ext_type,
+ krb5_data *ext_data,
+ krb5_gss_ctx_ext_t exts)
+{
+ krb5_error_code code = 0;
+
+ assert(exts != NULL);
+
+ switch (ext_type) {
+ case KRB5_GSS_EXTS_IAKERB_FINISHED:
+ if (exts->iakerb.conv == NULL) {
+ code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
+ } else {
+ krb5_key key;
+
+ code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key);
+ if (code != 0)
+ break;
+
+ code = iakerb_verify_finished(context, key, exts->iakerb.conv,
+ ext_data);
+ if (code == 0)
+ exts->iakerb.verified = 1;
+
+ krb5_k_free_key(context, key);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return code;
+}
+
+static OM_uint32
+kg_accept_krb5(minor_status, context_handle,
+ verifier_cred_handle, input_token,
+ input_chan_bindings, src_name, mech_type,
+ output_token, ret_flags, time_rec,
+ delegated_cred_handle, exts)
+ OM_uint32 *minor_status;
+ gss_ctx_id_t *context_handle;
+ gss_cred_id_t verifier_cred_handle;
+ gss_buffer_t input_token;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_name_t *src_name;
+ gss_OID *mech_type;
+ gss_buffer_t output_token;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+ gss_cred_id_t *delegated_cred_handle;
+ krb5_gss_ctx_ext_t exts;
+{
+ krb5_context context;
+ unsigned char *ptr, *ptr2;
+ char *sptr;
+ OM_uint32 tmp;
+ size_t md5len;
+ krb5_gss_cred_id_t cred = 0;
+ krb5_data ap_rep, ap_req;
+ unsigned int i;
+ krb5_error_code code;
+ krb5_address addr, *paddr;
+ krb5_authenticator *authdat = 0;
+ krb5_checksum reqcksum;
+ krb5_gss_name_t name = NULL;
+ krb5_ui_4 gss_flags = 0;
+ krb5_gss_ctx_id_rec *ctx = NULL;
+ krb5_timestamp now;
+ gss_buffer_desc token;
+ krb5_auth_context auth_context = NULL;
+ krb5_ticket * ticket = NULL;
+ int option_id;
+ krb5_data option;
+ const gss_OID_desc *mech_used = NULL;
+ OM_uint32 major_status = GSS_S_FAILURE;
+ OM_uint32 tmp_minor_status;
+ krb5_error krb_error_data;
+ krb5_data scratch;
+ gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
+ krb5_gss_cred_id_t deleg_cred = NULL;
+ krb5int_access kaccess;
+ int cred_rcache = 0;
+ int no_encap = 0;
+ int token_deleg_flag = 0;
+ krb5_flags ap_req_options = 0;
+ krb5_enctype negotiated_etype;
+ krb5_authdata_context ad_context = NULL;
+ krb5_principal accprinc = NULL;
+ krb5_ap_req *request = NULL;
+
+ code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
+ if (code) {
+ *minor_status = code;
+ return(GSS_S_FAILURE);
+ }
+
+ code = krb5_gss_init_context(&context);
+ if (code) {
+ *minor_status = code;
+ return GSS_S_FAILURE;
+ }
+
+ /* set up returns to be freeable */
+
+ if (src_name)
+ *src_name = (gss_name_t) NULL;
+ output_token->length = 0;
+ output_token->value = NULL;
+ token.value = 0;
+ reqcksum.contents = 0;
+ ap_req.data = 0;
+ ap_rep.data = 0;
+
+ if (mech_type)
+ *mech_type = GSS_C_NULL_OID;
+ /* return a bogus cred handle */
+ if (delegated_cred_handle)
+ *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ /* handle default cred handle */
+ if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
+ major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
+ GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
+ GSS_C_ACCEPT, &defcred,
+ NULL, NULL);
+ if (major_status != GSS_S_COMPLETE) {
+ code = *minor_status;
+ goto fail;
+ }
+ verifier_cred_handle = defcred;
+ }
+
+ /* Resolve any initiator state in the verifier cred and lock it. */
+ major_status = kg_cred_resolve(minor_status, context, verifier_cred_handle,
+ GSS_C_NO_NAME);
+ if (GSS_ERROR(major_status)) {
+ code = *minor_status;
+ goto fail;
+ }
+ cred = (krb5_gss_cred_id_t)verifier_cred_handle;
+
+ /* make sure the supplied credentials are valid for accept */
+
+ if ((cred->usage != GSS_C_ACCEPT) &&
+ (cred->usage != GSS_C_BOTH)) {
+ code = 0;
+ major_status = GSS_S_NO_CRED;
+ goto fail;
+ }
+
+ /* verify the token's integrity, and leave the token in ap_req.
+ figure out which mech oid was used, and save it */
+
+ ptr = (unsigned char *) input_token->value;
+
+ if (!(code = g_verify_token_header(gss_mech_krb5,
+ &(ap_req.length),
+ &ptr, KG_TOK_CTX_AP_REQ,
+ input_token->length, 1))) {
+ mech_used = gss_mech_krb5;
+ } else if ((code == G_WRONG_MECH)
+ &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb,
+ &(ap_req.length),
+ &ptr, KG_TOK_CTX_AP_REQ,
+ input_token->length, 1))) {
+ mech_used = gss_mech_iakerb;
+ } else if ((code == G_WRONG_MECH)
+ &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
+ &(ap_req.length),
+ &ptr, KG_TOK_CTX_AP_REQ,
+ input_token->length, 1))) {
+ mech_used = gss_mech_krb5_wrong;
+ } else if ((code == G_WRONG_MECH) &&
+ !(code = g_verify_token_header(gss_mech_krb5_old,
+ &(ap_req.length),
+ &ptr, KG_TOK_CTX_AP_REQ,
+ input_token->length, 1))) {
+ /*
+ * Previous versions of this library used the old mech_id
+ * and some broken behavior (wrong IV on checksum
+ * encryption). We support the old mech_id for
+ * compatibility, and use it to decide when to use the
+ * old behavior.
+ */
+ mech_used = gss_mech_krb5_old;
+ } else if (code == G_WRONG_TOKID) {
+ major_status = GSS_S_CONTINUE_NEEDED;
+ code = KRB5KRB_AP_ERR_MSG_TYPE;
+ mech_used = gss_mech_krb5;
+ goto fail;
+ } else if (code == G_BAD_TOK_HEADER) {
+ /* DCE style not encapsulated */
+ ap_req.length = input_token->length;
+ ap_req.data = input_token->value;
+ mech_used = gss_mech_krb5;
+ no_encap = 1;
+ } else {
+ major_status = GSS_S_DEFECTIVE_TOKEN;
+ goto fail;
+ }
+
+ sptr = (char *) ptr;
+ TREAD_STR(sptr, ap_req.data, ap_req.length);
+
+ /* construct the sender_addr */
+
+ if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
+ (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
+ /* XXX is this right? */
+ addr.addrtype = ADDRTYPE_INET;
+ addr.length = input_chan_bindings->initiator_address.length;
+ addr.contents = input_chan_bindings->initiator_address.value;
+
+ paddr = &addr;
+ } else {
+ paddr = NULL;
+ }
+
+ /* decode the AP_REQ message */
+ code = decode_krb5_ap_req(&ap_req, &request);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto done;
+ }
+ ticket = request->ticket;
+
+ /* decode the message */
+
+ if ((code = krb5_auth_con_init(context, &auth_context))) {
+ major_status = GSS_S_FAILURE;
+ save_error_info((OM_uint32)code, context);
+ goto fail;
+ }
+ if (cred->rcache) {
+ cred_rcache = 1;
+ if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+ if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* Limit the encryption types negotiated (if requested). */
+ if (cred->req_enctypes) {
+ if ((code = krb5_auth_con_setpermetypes(context, auth_context,
+ cred->req_enctypes))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ if (!cred->default_identity) {
+ if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ code = krb5_rd_req_decoded(context, &auth_context, request, accprinc,
+ cred->keytab, &ap_req_options, NULL);
+
+ krb5_free_principal(context, accprinc);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ krb5_auth_con_setflags(context, auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+
+ krb5_auth_con_getauthenticator(context, auth_context, &authdat);
+
+#if 0
+ /* make sure the necessary parts of the authdat are present */
+
+ if ((authdat->authenticator->subkey == NULL) ||
+ (authdat->ticket->enc_part2 == NULL)) {
+ code = KG_NO_SUBKEY;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+#endif
+
+ if (authdat->checksum == NULL) {
+ /*
+ * Some SMB client implementations use handcrafted GSSAPI code that
+ * does not provide a checksum. MS-KILE documents that the Microsoft
+ * implementation considers a missing checksum acceptable; the server
+ * assumes all flags are unset in this case, and does not check channel
+ * bindings.
+ */
+ gss_flags = 0;
+ } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
+ /* Samba does not send 0x8003 GSS-API checksums */
+ krb5_boolean valid;
+ krb5_key subkey;
+ krb5_data zero;
+
+ code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ zero.length = 0;
+ zero.data = "";
+
+ code = krb5_k_verify_checksum(context,
+ subkey,
+ KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
+ &zero,
+ authdat->checksum,
+ &valid);
+ krb5_k_free_key(context, subkey);
+ if (code || !valid) {
+ major_status = GSS_S_BAD_SIG;
+ goto fail;
+ }
+
+ /* Use ap_options from the request to guess the mutual flag. */
+ gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
+ gss_flags |= GSS_C_MUTUAL_FLAG;
+ } else {
+ /* gss krb5 v1 */
+
+ /* stash this now, for later. */
+ code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* verify that the checksum is correct */
+
+ /*
+ The checksum may be either exactly 24 bytes, in which case
+ no options are specified, or greater than 24 bytes, in which case
+ one or more options are specified. Currently, the only valid
+ option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
+ */
+
+ if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
+ (authdat->checksum->length < 24)) {
+ code = 0;
+ major_status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+
+ ptr = (unsigned char *) authdat->checksum->contents;
+
+ TREAD_INT(ptr, tmp, 0);
+
+ if (tmp != md5len) {
+ code = KG_BAD_LENGTH;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /*
+ The following section of code attempts to implement the
+ optional channel binding facility as described in RFC2743.
+
+ Since this facility is optional channel binding may or may
+ not have been provided by either the client or the server.
+
+ If the server has specified input_chan_bindings equal to
+ GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If
+ the server does provide channel bindings then we compute
+ a checksum and compare against those provided by the
+ client. */
+
+ if ((code = kg_checksum_channel_bindings(context,
+ input_chan_bindings,
+ &reqcksum))) {
+ major_status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+
+ /* Always read the clients bindings - eventhough we might ignore them */
+ TREAD_STR(ptr, ptr2, reqcksum.length);
+
+ if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
+ if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
+ xfree(reqcksum.contents);
+ reqcksum.contents = 0;
+ code = 0;
+ major_status = GSS_S_BAD_BINDINGS;
+ goto fail;
+ }
+
+ }
+
+ xfree(reqcksum.contents);
+ reqcksum.contents = 0;
+
+ /* Read the token flags. Remember if GSS_C_DELEG_FLAG was set, but
+ * mask it out until we actually read a delegated credential. */
+ TREAD_INT(ptr, gss_flags, 0);
+ token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG);
+ gss_flags &= ~GSS_C_DELEG_FLAG;
+
+ /* if the checksum length > 24, there are options to process */
+
+ i = authdat->checksum->length - 24;
+ if (i && token_deleg_flag) {
+ if (i >= 4) {
+ TREAD_INT16(ptr, option_id, 0);
+ TREAD_INT16(ptr, option.length, 0);
+ i -= 4;
+
+ if (i < option.length) {
+ code = KG_BAD_LENGTH;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* have to use ptr2, since option.data is wrong type and
+ macro uses ptr as both lvalue and rvalue */
+
+ TREAD_STR(ptr, ptr2, option.length);
+ option.data = (char *) ptr2;
+
+ i -= option.length;
+
+ if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* store the delegated credential */
+
+ code = rd_and_store_for_creds(context, auth_context, &option,
+ (delegated_cred_handle) ?
+ &deleg_cred : NULL);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ gss_flags |= GSS_C_DELEG_FLAG;
+ } /* if i >= 4 */
+ /* ignore any additional trailing data, for now */
+ }
+ while (i > 0) {
+ /* Process Type-Length-Data options */
+ if (i < 8) {
+ code = KG_BAD_LENGTH;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ TREAD_INT(ptr, option_id, 1);
+ TREAD_INT(ptr, option.length, 1);
+ i -= 8;
+ if (i < option.length) {
+ code = KG_BAD_LENGTH;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ TREAD_STR(ptr, ptr2, option.length);
+ option.data = (char *)ptr2;
+
+ i -= option.length;
+
+ code = kg_process_extension(context, auth_context,
+ option_id, &option, exts);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+ }
+
+ if (exts->iakerb.conv && !exts->iakerb.verified) {
+ major_status = GSS_S_BAD_SIG;
+ goto fail;
+ }
+
+ /* only DCE_STYLE clients are allowed to send raw AP-REQs */
+ if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
+ major_status = GSS_S_DEFECTIVE_TOKEN;
+ goto fail;
+ }
+
+ /* create the ctx struct and start filling it in */
+
+ if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
+ == NULL) {
+ code = ENOMEM;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+ ctx->magic = KG_CONTEXT;
+ ctx->mech_used = (gss_OID) mech_used;
+ ctx->auth_context = auth_context;
+ ctx->initiate = 0;
+ ctx->gss_flags = (GSS_C_TRANS_FLAG |
+ ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
+ GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
+ GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
+ GSS_C_EXTENDED_ERROR_FLAG)));
+ ctx->seed_init = 0;
+ ctx->cred_rcache = cred_rcache;
+
+ /* XXX move this into gss_name_t */
+ if ( (code = krb5_merge_authdata(context,
+ ticket->enc_part2->authorization_data,
+ authdat->authorization_data,
+ &ctx->authdata))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0,
+ &ctx->here))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ if ((code = krb5_auth_con_get_authdata_context(context, auth_context,
+ &ad_context))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ if ((code = kg_init_name(context, authdat->client, NULL, NULL,
+ ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ /* Now owned by ctx->there */
+ authdat->client = NULL;
+ krb5_auth_con_set_authdata_context(context, auth_context, NULL);
+
+ if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context,
+ &ctx->subkey))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* use the session key if the subkey isn't present */
+
+ if (ctx->subkey == NULL) {
+ if ((code = krb5_auth_con_getkey_k(context, auth_context,
+ &ctx->subkey))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ if (ctx->subkey == NULL) {
+ /* this isn't a very good error, but it's not clear to me this
+ can actually happen */
+ major_status = GSS_S_FAILURE;
+ code = KRB5KDC_ERR_NULL_KEY;
+ goto fail;
+ }
+
+ ctx->enc = NULL;
+ ctx->seq = NULL;
+ ctx->have_acceptor_subkey = 0;
+ /* DCE_STYLE implies acceptor_subkey */
+ if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
+ code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+ ctx->krb_times = ticket->enc_part2->times; /* struct copy */
+ ctx->krb_flags = ticket->enc_part2->flags;
+
+ if (delegated_cred_handle != NULL &&
+ deleg_cred == NULL && /* no unconstrained delegation */
+ cred->usage == GSS_C_BOTH &&
+ (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
+ /*
+ * Now, we always fabricate a delegated credentials handle
+ * containing the service ticket to ourselves, which can be
+ * used for S4U2Proxy.
+ */
+ major_status = create_constrained_deleg_creds(minor_status, cred,
+ ticket, &deleg_cred,
+ context);
+ if (GSS_ERROR(major_status))
+ goto fail;
+ ctx->gss_flags |= GSS_C_DELEG_FLAG;
+ }
+
+ {
+ krb5_int32 seq_temp;
+ krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
+ ctx->seq_recv = seq_temp;
+ }
+
+ if ((code = krb5_timeofday(context, &now))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
+ (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
+ (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
+ ctx->proto);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ /* DCE_STYLE implies mutual authentication */
+ if (ctx->gss_flags & GSS_C_DCE_STYLE)
+ ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
+
+ /* at this point, the entire context structure is filled in,
+ so it can be released. */
+
+ /* generate an AP_REP if necessary */
+
+ if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
+ unsigned char * ptr3;
+ krb5_int32 seq_temp;
+ int cfx_generate_subkey;
+
+ /*
+ * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX,
+ * because pre-CFX tokens do not indicate which key to use. (Note that
+ * DCE_STYLE implies that we will use a subkey.)
+ */
+ if (ctx->proto == 0 &&
+ (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
+ (ap_req_options & AP_OPTS_USE_SUBKEY)) {
+ code = (*kaccess.auth_con_get_subkey_enctype)(context,
+ auth_context,
+ &negotiated_etype);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ switch (negotiated_etype) {
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_DES_CBC_MD4:
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES3_CBC_SHA1:
+ case ENCTYPE_ARCFOUR_HMAC:
+ case ENCTYPE_ARCFOUR_HMAC_EXP:
+ /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer"
+ * enctype, even though RFC 4757 treats it as one. */
+ ap_req_options &= ~(AP_OPTS_USE_SUBKEY);
+ break;
+ }
+ }
+
+ if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
+ (ap_req_options & AP_OPTS_USE_SUBKEY))
+ cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
+ else
+ cfx_generate_subkey = 0;
+
+ if (cfx_generate_subkey) {
+ krb5_int32 acflags;
+ code = krb5_auth_con_getflags(context, auth_context, &acflags);
+ if (code == 0) {
+ acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
+ code = krb5_auth_con_setflags(context, auth_context, acflags);
+ }
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
+ ctx->seq_send = seq_temp & 0xffffffffL;
+
+ if (cfx_generate_subkey) {
+ /* Get the new acceptor subkey. With the code above, there
+ should always be one if we make it to this point. */
+ code = krb5_auth_con_getsendsubkey_k(context, auth_context,
+ &ctx->acceptor_subkey);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ ctx->have_acceptor_subkey = 1;
+
+ code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
+ &ctx->acceptor_subkey_cksumtype);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ /* the reply token hasn't been sent yet, but that's ok. */
+ if (ctx->gss_flags & GSS_C_DCE_STYLE) {
+ assert(ctx->have_acceptor_subkey);
+
+ /* in order to force acceptor subkey to be used, don't set PROT_READY */
+
+ /* Raw AP-REP is returned */
+ code = data_to_gss(&ap_rep, output_token);
+ if (code)
+ {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+
+ ctx->established = 0;
+
+ *context_handle = (gss_ctx_id_t)ctx;
+ *minor_status = 0;
+ major_status = GSS_S_CONTINUE_NEEDED;
+
+ /* Only last leg should set return arguments */
+ goto fail;
+ } else
+ ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
+
+ ctx->established = 1;
+
+ token.length = g_token_size(mech_used, ap_rep.length);
+
+ if ((token.value = (unsigned char *) gssalloc_malloc(token.length))
+ == NULL) {
+ major_status = GSS_S_FAILURE;
+ code = ENOMEM;
+ goto fail;
+ }
+ ptr3 = token.value;
+ g_make_token_header(mech_used, ap_rep.length,
+ &ptr3, KG_TOK_CTX_AP_REP);
+
+ TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
+
+ ctx->established = 1;
+
+ } else {
+ token.length = 0;
+ token.value = NULL;
+ ctx->seq_send = ctx->seq_recv;
+
+ ctx->established = 1;
+ }
+
+ /* set the return arguments */
+
+ if (src_name) {
+ code = kg_duplicate_name(context, ctx->there, &name);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ if (mech_type)
+ *mech_type = (gss_OID) mech_used;
+
+ /* Add the maximum allowable clock skew as a grace period for context
+ * expiration, just as we do for the ticket. */
+ if (time_rec)
+ *time_rec = ctx->krb_times.endtime + context->clockskew - now;
+
+ if (ret_flags)
+ *ret_flags = ctx->gss_flags;
+
+ *context_handle = (gss_ctx_id_t)ctx;
+ *output_token = token;
+
+ if (src_name)
+ *src_name = (gss_name_t) name;
+
+ if (delegated_cred_handle)
+ *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
+
+ /* finally! */
+
+ *minor_status = 0;
+ major_status = GSS_S_COMPLETE;
+
+fail:
+ if (authdat)
+ krb5_free_authenticator(context, authdat);
+ /* The ctx structure has the handle of the auth_context */
+ if (auth_context && !ctx) {
+ if (cred_rcache)
+ (void)krb5_auth_con_setrcache(context, auth_context, NULL);
+
+ krb5_auth_con_free(context, auth_context);
+ }
+ if (reqcksum.contents)
+ xfree(reqcksum.contents);
+ if (ap_rep.data)
+ krb5_free_data_contents(context, &ap_rep);
+ if (major_status == GSS_S_COMPLETE ||
+ (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) {
+ ctx->k5_context = context;
+ context = NULL;
+ goto done;
+ }
+
+ /* from here on is the real "fail" code */
+
+ if (ctx)
+ (void) krb5_gss_delete_sec_context(&tmp_minor_status,
+ (gss_ctx_id_t *) &ctx, NULL);
+ if (deleg_cred) { /* free memory associated with the deleg credential */
+ if (deleg_cred->ccache)
+ (void)krb5_cc_close(context, deleg_cred->ccache);
+ if (deleg_cred->name)
+ kg_release_name(context, &deleg_cred->name);
+ xfree(deleg_cred);
+ }
+ if (token.value)
+ xfree(token.value);
+ if (name) {
+ (void) kg_release_name(context, &name);
+ }
+
+ *minor_status = code;
+
+ /* We may have failed before being able to read the GSS flags from the
+ * authenticator, so also check the request AP options. */
+ if (cred != NULL && request != NULL &&
+ ((gss_flags & GSS_C_MUTUAL_FLAG) ||
+ (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) ||
+ major_status == GSS_S_CONTINUE_NEEDED)) {
+ unsigned int tmsglen;
+ int toktype;
+
+ /*
+ * The client is expecting a response, so we can send an
+ * error token back
+ */
+ memset(&krb_error_data, 0, sizeof(krb_error_data));
+
+ code -= ERROR_TABLE_BASE_krb5;
+ if (code < 0 || code > KRB_ERR_MAX)
+ code = 60 /* KRB_ERR_GENERIC */;
+
+ krb_error_data.error = code;
+ (void) krb5_us_timeofday(context, &krb_error_data.stime,
+ &krb_error_data.susec);
+
+ krb_error_data.server = ticket->server;
+ code = krb5_mk_error(context, &krb_error_data, &scratch);
+ if (code)
+ goto done;
+
+ tmsglen = scratch.length;
+ toktype = KG_TOK_CTX_ERROR;
+
+ token.length = g_token_size(mech_used, tmsglen);
+ token.value = gssalloc_malloc(token.length);
+ if (!token.value)
+ goto done;
+
+ ptr = token.value;
+ g_make_token_header(mech_used, tmsglen, &ptr, toktype);
+
+ TWRITE_STR(ptr, scratch.data, scratch.length);
+ krb5_free_data_contents(context, &scratch);
+
+ *output_token = token;
+ }
+
+done:
+ krb5_free_ap_req(context, request);
+ if (cred)
+ k5_mutex_unlock(&cred->lock);
+ if (defcred)
+ krb5_gss_release_cred(&tmp_minor_status, &defcred);
+ if (context) {
+ if (major_status && *minor_status)
+ save_error_info(*minor_status, context);
+ krb5_free_context(context);
+ }
+ return (major_status);
+}
+#endif /* LEAN_CLIENT */
+
+OM_uint32 KRB5_CALLCONV
+krb5_gss_accept_sec_context_ext(
+ OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t verifier_cred_handle,
+ gss_buffer_t input_token,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle,
+ krb5_gss_ctx_ext_t exts)
+{
+ krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
+
+ /*
+ * Context handle must be unspecified. Actually, it must be
+ * non-established, but currently, accept_sec_context never returns
+ * a non-established context handle.
+ */
+ /*SUPPRESS 29*/
+ if (ctx != NULL) {
+ if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
+ return kg_accept_dce(minor_status, context_handle,
+ verifier_cred_handle, input_token,
+ input_chan_bindings, src_name, mech_type,
+ output_token, ret_flags, time_rec,
+ delegated_cred_handle);
+ } else {
+ *minor_status = EINVAL;
+ save_error_string(EINVAL, "accept_sec_context called with existing context handle");
+ return GSS_S_FAILURE;
+ }
+ }
+
+ return kg_accept_krb5(minor_status, context_handle,
+ verifier_cred_handle, input_token,
+ input_chan_bindings, src_name, mech_type,
+ output_token, ret_flags, time_rec,
+ delegated_cred_handle, exts);
+}
+
+OM_uint32 KRB5_CALLCONV
+krb5_gss_accept_sec_context(minor_status, context_handle,
+ verifier_cred_handle, input_token,
+ input_chan_bindings, src_name, mech_type,
+ output_token, ret_flags, time_rec,
+ delegated_cred_handle)
+ OM_uint32 *minor_status;
+ gss_ctx_id_t *context_handle;
+ gss_cred_id_t verifier_cred_handle;
+ gss_buffer_t input_token;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_name_t *src_name;
+ gss_OID *mech_type;
+ gss_buffer_t output_token;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+ gss_cred_id_t *delegated_cred_handle;
+{
+ krb5_gss_ctx_ext_rec exts;
+
+ memset(&exts, 0, sizeof(exts));
+
+ return krb5_gss_accept_sec_context_ext(minor_status,
+ context_handle,
+ verifier_cred_handle,
+ input_token,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle,
+ &exts);
+}