summaryrefslogtreecommitdiff
path: root/src/lib/gssapi/krb5/init_sec_context.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/gssapi/krb5/init_sec_context.c')
-rw-r--r--src/lib/gssapi/krb5/init_sec_context.c1104
1 files changed, 1104 insertions, 0 deletions
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
new file mode 100644
index 000000000000..70f7955ae1ae
--- /dev/null
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -0,0 +1,1104 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2000, 2002, 2003, 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 <stdlib.h>
+#include <assert.h>
+
+/*
+ * $Id$
+ */
+
+/* XXX This is for debugging only!!! Should become a real bitfield
+ at some point */
+int krb5_gss_dbg_client_expcreds = 0;
+
+/*
+ * Common code which fetches the correct krb5 credentials from the
+ * ccache.
+ */
+static krb5_error_code get_credentials(context, cred, server, now,
+ endtime, out_creds)
+ krb5_context context;
+ krb5_gss_cred_id_t cred;
+ krb5_gss_name_t server;
+ krb5_timestamp now;
+ krb5_timestamp endtime;
+ krb5_creds **out_creds;
+{
+ krb5_error_code code;
+ krb5_creds in_creds, evidence_creds, mcreds, *result_creds = NULL;
+ krb5_flags flags = 0;
+
+ *out_creds = NULL;
+
+ k5_mutex_assert_locked(&cred->lock);
+ memset(&in_creds, 0, sizeof(krb5_creds));
+ memset(&evidence_creds, 0, sizeof(krb5_creds));
+ in_creds.client = in_creds.server = NULL;
+
+ assert(cred->name != NULL);
+
+ in_creds.client = cred->name->princ;
+ in_creds.server = server->princ;
+ in_creds.times.endtime = endtime;
+ in_creds.authdata = NULL;
+ in_creds.keyblock.enctype = 0;
+
+ /*
+ * cred->name is immutable, so there is no need to acquire
+ * cred->name->lock.
+ */
+ if (cred->name->ad_context != NULL) {
+ code = krb5_authdata_export_authdata(context,
+ cred->name->ad_context,
+ AD_USAGE_TGS_REQ,
+ &in_creds.authdata);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ /*
+ * For IAKERB or constrained delegation, only check the cache in this step.
+ * For IAKERB we will ask the server to make any necessary TGS requests;
+ * for constrained delegation we will adjust in_creds and make an S4U2Proxy
+ * request below if the cache lookup fails.
+ */
+ if (cred->impersonator != NULL || cred->iakerb_mech)
+ flags |= KRB5_GC_CACHED;
+
+ code = krb5_get_credentials(context, flags, cred->ccache,
+ &in_creds, &result_creds);
+
+ /*
+ * Try constrained delegation if we have proxy credentials, unless
+ * we are trying to get a ticket to ourselves (in which case we could
+ * just use the evidence ticket directly from cache).
+ */
+ if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL &&
+ !cred->iakerb_mech &&
+ !krb5_principal_compare(context, cred->impersonator, server->princ)) {
+
+ memset(&mcreds, 0, sizeof(mcreds));
+ mcreds.magic = KV5M_CREDS;
+ mcreds.server = cred->impersonator;
+ mcreds.client = cred->name->princ;
+ code = krb5_cc_retrieve_cred(context, cred->ccache,
+ KRB5_TC_MATCH_AUTHDATA, &mcreds,
+ &evidence_creds);
+ if (code)
+ goto cleanup;
+
+ assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE);
+ in_creds.client = cred->impersonator;
+ in_creds.second_ticket = evidence_creds.ticket;
+ flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
+ code = krb5_get_credentials(context, flags, cred->ccache,
+ &in_creds, &result_creds);
+ }
+
+ if (code)
+ goto cleanup;
+
+ if (flags & KRB5_GC_CONSTRAINED_DELEGATION) {
+ if (!krb5_principal_compare(context, cred->name->princ,
+ result_creds->client)) {
+ /* server did not support constrained delegation */
+ code = KRB5_KDCREP_MODIFIED;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Enforce a stricter limit (without timeskew forgiveness at the
+ * boundaries) because accept_sec_context code is also similarly
+ * non-forgiving.
+ */
+ if (!krb5_gss_dbg_client_expcreds && result_creds->times.endtime < now) {
+ code = KRB5KRB_AP_ERR_TKT_EXPIRED;
+ goto cleanup;
+ }
+
+ *out_creds = result_creds;
+ result_creds = NULL;
+
+cleanup:
+ krb5_free_authdata(context, in_creds.authdata);
+ krb5_free_cred_contents(context, &evidence_creds);
+ krb5_free_creds(context, result_creds);
+
+ return code;
+}
+struct gss_checksum_data {
+ krb5_gss_ctx_id_rec *ctx;
+ krb5_gss_cred_id_t cred;
+ krb5_checksum md5;
+ krb5_data checksum_data;
+ krb5_gss_ctx_ext_t exts;
+};
+
+#ifdef CFX_EXERCISE
+#include "../../krb5/krb/auth_con.h"
+#endif
+static krb5_error_code KRB5_CALLCONV
+make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
+ void *cksum_data, krb5_data **out)
+{
+ krb5_error_code code;
+ krb5_int32 con_flags;
+ unsigned char *ptr;
+ struct gss_checksum_data *data = cksum_data;
+ krb5_data credmsg;
+ unsigned int junk;
+ krb5_data *finished = NULL;
+ krb5_key send_subkey;
+
+ data->checksum_data.data = 0;
+ credmsg.data = 0;
+ /* build the checksum field */
+
+ if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
+ /* first get KRB_CRED message, so we know its length */
+
+ /* clear the time check flag that was set in krb5_auth_con_init() */
+ krb5_auth_con_getflags(context, auth_context, &con_flags);
+ krb5_auth_con_setflags(context, auth_context,
+ con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
+
+ assert(data->cred->name != NULL);
+
+ /*
+ * RFC 4121 4.1.1 specifies forwarded credentials must be encrypted in
+ * the session key, but krb5_fwd_tgt_creds will use the send subkey if
+ * it's set in the auth context. Suppress the send subkey
+ * temporarily.
+ */
+ krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey);
+ krb5_auth_con_setsendsubkey_k(context, auth_context, NULL);
+
+ code = krb5_fwd_tgt_creds(context, auth_context, 0,
+ data->cred->name->princ, data->ctx->there->princ,
+ data->cred->ccache, 1,
+ &credmsg);
+
+ /* Turn KRB5_AUTH_CONTEXT_DO_TIME back on and reset the send subkey. */
+ krb5_auth_con_setflags(context, auth_context, con_flags);
+ krb5_auth_con_setsendsubkey_k(context, auth_context, send_subkey);
+ krb5_k_free_key(context, send_subkey);
+
+ if (code) {
+ /* don't fail here; just don't accept/do the delegation
+ request */
+ data->ctx->gss_flags &= ~(GSS_C_DELEG_FLAG |
+ GSS_C_DELEG_POLICY_FLAG);
+
+ data->checksum_data.length = 24;
+ } else {
+ if (credmsg.length+28 > KRB5_INT16_MAX) {
+ code = KRB5KRB_ERR_FIELD_TOOLONG;
+ goto cleanup;
+ }
+
+ data->checksum_data.length = 28+credmsg.length;
+ }
+ } else {
+ data->checksum_data.length = 24;
+ }
+#ifdef CFX_EXERCISE
+ if (data->ctx->auth_context->keyblock != NULL
+ && data->ctx->auth_context->keyblock->enctype == 18) {
+ srand(time(0) ^ getpid());
+ /* Our ftp client code stupidly assumes a base64-encoded
+ version of the token will fit in 10K, so don't make this
+ too big. */
+ junk = rand() & 0xff;
+ } else
+ junk = 0;
+#else
+ junk = 0;
+#endif
+
+ assert(data->exts != NULL);
+
+ if (data->exts->iakerb.conv) {
+ krb5_key key;
+
+ code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key);
+ if (code != 0)
+ goto cleanup;
+
+ code = iakerb_make_finished(context, key, data->exts->iakerb.conv,
+ &finished);
+ if (code != 0) {
+ krb5_k_free_key(context, key);
+ goto cleanup;
+ }
+
+ krb5_k_free_key(context, key);
+ data->checksum_data.length += 8 + finished->length;
+ }
+
+ data->checksum_data.length += junk;
+
+ /* now allocate a buffer to hold the checksum data and
+ (maybe) KRB_CRED msg */
+
+ if ((data->checksum_data.data =
+ (char *) xmalloc(data->checksum_data.length)) == NULL) {
+ code = ENOMEM;
+ goto cleanup;
+ }
+
+ ptr = (unsigned char *)data->checksum_data.data;
+
+ TWRITE_INT(ptr, data->md5.length, 0);
+ TWRITE_STR(ptr, data->md5.contents, data->md5.length);
+ TWRITE_INT(ptr, data->ctx->gss_flags, 0);
+
+ /* done with this, free it */
+ xfree(data->md5.contents);
+
+ if (credmsg.data) {
+ TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
+ TWRITE_INT16(ptr, credmsg.length, 0);
+ TWRITE_STR(ptr, credmsg.data, credmsg.length);
+ }
+ if (data->exts->iakerb.conv) {
+ TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1);
+ TWRITE_INT(ptr, finished->length, 1);
+ TWRITE_STR(ptr, finished->data, finished->length);
+ }
+ if (junk)
+ memset(ptr, 'i', junk);
+ *out = &data->checksum_data;
+ code = 0;
+cleanup:
+ krb5_free_data_contents(context, &credmsg);
+ krb5_free_data(context, finished);
+ return code;
+}
+
+static krb5_error_code
+make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
+ chan_bindings, mech_type, token, exts)
+ krb5_context context;
+ krb5_gss_ctx_id_rec *ctx;
+ krb5_gss_cred_id_t cred;
+ krb5_creds *k_cred;
+ krb5_authdata_context ad_context;
+ gss_channel_bindings_t chan_bindings;
+ gss_OID mech_type;
+ gss_buffer_t token;
+ krb5_gss_ctx_ext_t exts;
+{
+ krb5_flags mk_req_flags = 0;
+ krb5_error_code code;
+ struct gss_checksum_data cksum_struct;
+ krb5_checksum md5;
+ krb5_data ap_req;
+ unsigned char *ptr;
+ unsigned char *t;
+ unsigned int tlen;
+
+ k5_mutex_assert_locked(&cred->lock);
+ ap_req.data = 0;
+
+ /* compute the hash of the channel bindings */
+
+ if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5)))
+ return(code);
+
+ krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
+ CKSUMTYPE_KG_CB);
+ cksum_struct.md5 = md5;
+ cksum_struct.ctx = ctx;
+ cksum_struct.cred = cred;
+ cksum_struct.checksum_data.data = NULL;
+ cksum_struct.exts = exts;
+ krb5_auth_con_set_checksum_func(context, ctx->auth_context,
+ make_gss_checksum, &cksum_struct);
+
+ /* call mk_req. subkey and ap_req need to be used or destroyed */
+
+ mk_req_flags = AP_OPTS_USE_SUBKEY;
+
+ if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
+ mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION;
+
+ krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
+ code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
+ NULL, k_cred, &ap_req);
+ krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL);
+ krb5_free_data_contents(context, &cksum_struct.checksum_data);
+ if (code)
+ goto cleanup;
+
+ /* store the interesting stuff from creds and authent */
+ ctx->krb_times = k_cred->times;
+ ctx->krb_flags = k_cred->ticket_flags;
+
+ /* build up the token */
+ if (ctx->gss_flags & GSS_C_DCE_STYLE) {
+ /*
+ * For DCE RPC, do not encapsulate the AP-REQ in the
+ * typical GSS wrapping.
+ */
+ code = data_to_gss(&ap_req, token);
+ if (code)
+ goto cleanup;
+ } else {
+ /* allocate space for the token */
+ tlen = g_token_size((gss_OID) mech_type, ap_req.length);
+
+ if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) {
+ code = ENOMEM;
+ goto cleanup;
+ }
+
+ /* fill in the buffer */
+ ptr = t;
+
+ g_make_token_header(mech_type, ap_req.length,
+ &ptr, KG_TOK_CTX_AP_REQ);
+
+ TWRITE_STR(ptr, ap_req.data, ap_req.length);
+
+ /* pass it back */
+
+ token->length = tlen;
+ token->value = (void *) t;
+ }
+
+ code = 0;
+
+cleanup:
+ if (ap_req.data)
+ krb5_free_data_contents(context, &ap_req);
+
+ return (code);
+}
+
+/*
+ * new_connection
+ *
+ * Do the grunt work of setting up a new context.
+ */
+static OM_uint32
+kg_new_connection(
+ OM_uint32 *minor_status,
+ krb5_gss_cred_id_t cred,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ krb5_context context,
+ krb5_gss_ctx_ext_t exts)
+{
+ OM_uint32 major_status;
+ krb5_error_code code;
+ krb5_creds *k_cred = NULL;
+ krb5_gss_ctx_id_rec *ctx, *ctx_free;
+ krb5_timestamp now;
+ gss_buffer_desc token;
+ krb5_keyblock *keyblock;
+
+ k5_mutex_assert_locked(&cred->lock);
+ major_status = GSS_S_FAILURE;
+ token.length = 0;
+ token.value = NULL;
+
+ /* make sure the cred is usable for init */
+
+ if ((cred->usage != GSS_C_INITIATE) &&
+ (cred->usage != GSS_C_BOTH)) {
+ *minor_status = 0;
+ return(GSS_S_NO_CRED);
+ }
+
+ /* complain if the input token is non-null */
+
+ if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
+ *minor_status = 0;
+ return(GSS_S_DEFECTIVE_TOKEN);
+ }
+
+ /* create the ctx */
+
+ if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
+ == NULL) {
+ *minor_status = ENOMEM;
+ return(GSS_S_FAILURE);
+ }
+
+ /* fill in the ctx */
+ memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+ ctx->magic = KG_CONTEXT;
+ ctx_free = ctx;
+ if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
+ goto cleanup;
+ krb5_auth_con_setflags(context, ctx->auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+
+ /* limit the encryption types negotiated (if requested) */
+ if (cred->req_enctypes) {
+ if ((code = krb5_set_default_tgs_enctypes(context,
+ cred->req_enctypes))) {
+ goto cleanup;
+ }
+ }
+
+ ctx->initiate = 1;
+ ctx->seed_init = 0;
+ ctx->seqstate = 0;
+
+ ctx->gss_flags = req_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_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->gss_flags |= GSS_C_TRANS_FLAG;
+ if (!cred->suppress_ci_flags)
+ ctx->gss_flags |= (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
+ if (req_flags & GSS_C_DCE_STYLE)
+ ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
+
+ if ((code = krb5_timeofday(context, &now)))
+ goto cleanup;
+
+ if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
+ ctx->krb_times.endtime = 0;
+ } else {
+ ctx->krb_times.endtime = now + time_req;
+ }
+
+ if ((code = kg_duplicate_name(context, cred->name, &ctx->here)))
+ goto cleanup;
+
+ if ((code = kg_duplicate_name(context, (krb5_gss_name_t)target_name,
+ &ctx->there)))
+ goto cleanup;
+
+ code = get_credentials(context, cred, ctx->there, now,
+ ctx->krb_times.endtime, &k_cred);
+ if (code)
+ goto cleanup;
+
+ ctx->krb_times = k_cred->times;
+
+ /*
+ * GSS_C_DELEG_POLICY_FLAG means to delegate only if the
+ * ok-as-delegate ticket flag is set.
+ */
+ if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
+ && (k_cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
+ ctx->gss_flags |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
+
+ if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used)
+ != GSS_S_COMPLETE) {
+ code = *minor_status;
+ goto cleanup;
+ }
+ /*
+ * Now try to make it static if at all possible....
+ */
+ ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used);
+
+ {
+ /* gsskrb5 v1 */
+ krb5_int32 seq_temp;
+ if ((code = make_ap_req_v1(context, ctx,
+ cred, k_cred, ctx->here->ad_context,
+ input_chan_bindings,
+ mech_type, &token, exts))) {
+ if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
+ (code == KG_EMPTY_CCACHE))
+ major_status = GSS_S_NO_CRED;
+ if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
+ major_status = GSS_S_CREDENTIALS_EXPIRED;
+ goto cleanup;
+ }
+
+ krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp);
+ ctx->seq_send = seq_temp;
+ code = krb5_auth_con_getsendsubkey(context, ctx->auth_context,
+ &keyblock);
+ if (code != 0)
+ goto cleanup;
+ code = krb5_k_create_key(context, keyblock, &ctx->subkey);
+ krb5_free_keyblock(context, keyblock);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ ctx->enc = NULL;
+ ctx->seq = NULL;
+ ctx->have_acceptor_subkey = 0;
+ code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
+ if (code != 0)
+ goto cleanup;
+
+ if (!(ctx->gss_flags & GSS_C_MUTUAL_FLAG)) {
+ /* There will be no AP-REP, so set up sequence state now. */
+ ctx->seq_recv = ctx->seq_send;
+ 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 != 0)
+ goto cleanup;
+ }
+
+ /* compute time_rec */
+ if (time_rec) {
+ if ((code = krb5_timeofday(context, &now)))
+ goto cleanup;
+ *time_rec = ctx->krb_times.endtime - now;
+ }
+
+ /* set the other returns */
+ *output_token = token;
+
+ if (ret_flags)
+ *ret_flags = ctx->gss_flags;
+
+ if (actual_mech_type)
+ *actual_mech_type = mech_type;
+
+ /* return successfully */
+
+ *context_handle = (gss_ctx_id_t) ctx;
+ ctx_free = NULL;
+ if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
+ ctx->established = 0;
+ major_status = GSS_S_CONTINUE_NEEDED;
+ } else {
+ ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
+ ctx->established = 1;
+ major_status = GSS_S_COMPLETE;
+ }
+
+cleanup:
+ krb5_free_creds(context, k_cred);
+ if (ctx_free) {
+ if (ctx_free->auth_context)
+ krb5_auth_con_free(context, ctx_free->auth_context);
+ if (ctx_free->here)
+ kg_release_name(context, &ctx_free->here);
+ if (ctx_free->there)
+ kg_release_name(context, &ctx_free->there);
+ if (ctx_free->subkey)
+ krb5_k_free_key(context, ctx_free->subkey);
+ xfree(ctx_free);
+ }
+
+ *minor_status = code;
+ return (major_status);
+}
+
+/*
+ * mutual_auth
+ *
+ * Handle the reply from the acceptor, if we're doing mutual auth.
+ */
+static OM_uint32
+mutual_auth(
+ OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ krb5_context context)
+{
+ OM_uint32 major_status;
+ unsigned char *ptr;
+ char *sptr;
+ krb5_data ap_rep;
+ krb5_ap_rep_enc_part *ap_rep_data;
+ krb5_timestamp now;
+ krb5_gss_ctx_id_rec *ctx;
+ krb5_error *krb_error;
+ krb5_error_code code;
+ krb5int_access kaccess;
+
+ major_status = GSS_S_FAILURE;
+
+ code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
+ if (code)
+ goto fail;
+
+ ctx = (krb5_gss_ctx_id_t) *context_handle;
+
+ /* make sure the context is non-established, and that certain
+ arguments are unchanged */
+
+ if ((ctx->established) ||
+ ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
+ code = KG_CONTEXT_ESTABLISHED;
+ goto fail;
+ }
+
+ if (! kg_compare_name(context, ctx->there, (krb5_gss_name_t)target_name)) {
+ (void)krb5_gss_delete_sec_context(minor_status,
+ context_handle, NULL);
+ code = 0;
+ major_status = GSS_S_BAD_NAME;
+ goto fail;
+ }
+
+ /* verify the token and leave the AP_REP message in ap_rep */
+
+ if (input_token == GSS_C_NO_BUFFER) {
+ (void)krb5_gss_delete_sec_context(minor_status,
+ context_handle, NULL);
+ code = 0;
+ major_status = GSS_S_DEFECTIVE_TOKEN;
+ goto fail;
+ }
+
+ ptr = (unsigned char *) input_token->value;
+
+ if (ctx->gss_flags & GSS_C_DCE_STYLE) {
+ /* Raw AP-REP */
+ ap_rep.length = input_token->length;
+ ap_rep.data = (char *)input_token->value;
+ } else if (g_verify_token_header(ctx->mech_used,
+ &(ap_rep.length),
+ &ptr, KG_TOK_CTX_AP_REP,
+ input_token->length, 1)) {
+ if (g_verify_token_header((gss_OID) ctx->mech_used,
+ &(ap_rep.length),
+ &ptr, KG_TOK_CTX_ERROR,
+ input_token->length, 1) == 0) {
+
+ /* Handle a KRB_ERROR message from the server */
+
+ sptr = (char *) ptr; /* PC compiler bug */
+ TREAD_STR(sptr, ap_rep.data, ap_rep.length);
+
+ code = krb5_rd_error(context, &ap_rep, &krb_error);
+ if (code)
+ goto fail;
+ if (krb_error->error)
+ code = (krb5_error_code)krb_error->error + ERROR_TABLE_BASE_krb5;
+ else
+ code = 0;
+ krb5_free_error(context, krb_error);
+ goto fail;
+ } else {
+ *minor_status = 0;
+ return(GSS_S_DEFECTIVE_TOKEN);
+ }
+ }
+
+ sptr = (char *) ptr; /* PC compiler bug */
+ TREAD_STR(sptr, ap_rep.data, ap_rep.length);
+
+ /* decode the ap_rep */
+ if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
+ &ap_rep_data))) {
+ /*
+ * XXX A hack for backwards compatiblity.
+ * To be removed in 1999 -- proven
+ */
+ krb5_auth_con_setuseruserkey(context, ctx->auth_context,
+ &ctx->subkey->keyblock);
+ if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
+ &ap_rep_data)))
+ goto fail;
+ }
+
+ /* store away the sequence number */
+ ctx->seq_recv = ap_rep_data->seq_number;
+ 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) {
+ krb5_free_ap_rep_enc_part(context, ap_rep_data);
+ goto fail;
+ }
+
+ if (ap_rep_data->subkey != NULL &&
+ (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
+ ap_rep_data->subkey->enctype != ctx->subkey->keyblock.enctype)) {
+ /* Keep acceptor's subkey. */
+ ctx->have_acceptor_subkey = 1;
+ code = krb5_k_create_key(context, ap_rep_data->subkey,
+ &ctx->acceptor_subkey);
+ if (code) {
+ krb5_free_ap_rep_enc_part(context, ap_rep_data);
+ goto fail;
+ }
+ code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
+ &ctx->acceptor_subkey_cksumtype);
+ if (code) {
+ krb5_free_ap_rep_enc_part(context, ap_rep_data);
+ goto fail;
+ }
+ }
+ /* free the ap_rep_data */
+ krb5_free_ap_rep_enc_part(context, ap_rep_data);
+
+ if (ctx->gss_flags & GSS_C_DCE_STYLE) {
+ krb5_data outbuf;
+
+ code = krb5_mk_rep_dce(context, ctx->auth_context, &outbuf);
+ if (code)
+ goto fail;
+
+ code = data_to_gss(&outbuf, output_token);
+ if (code)
+ goto fail;
+ }
+
+ /* set established */
+ ctx->established = 1;
+
+ /* set returns */
+
+ if (time_rec) {
+ if ((code = krb5_timeofday(context, &now)))
+ goto fail;
+ *time_rec = ctx->krb_times.endtime - now;
+ }
+
+ if (ret_flags)
+ *ret_flags = ctx->gss_flags;
+
+ if (actual_mech_type)
+ *actual_mech_type = mech_type;
+
+ /* success */
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+
+fail:
+ (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
+
+ *minor_status = code;
+ return (major_status);
+}
+
+OM_uint32
+krb5_gss_init_sec_context_ext(
+ OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ krb5_gss_ctx_ext_t exts)
+{
+ krb5_context context;
+ gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
+ krb5_gss_cred_id_t cred;
+ krb5_error_code kerr;
+ OM_uint32 major_status;
+ OM_uint32 tmp_min_stat;
+
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ kerr = krb5_gss_init_context(&context);
+ if (kerr) {
+ *minor_status = kerr;
+ return GSS_S_FAILURE;
+ }
+ if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
+ save_error_info(*minor_status, context);
+ krb5_free_context(context);
+ return GSS_S_FAILURE;
+ }
+ } else {
+ context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
+ }
+
+ /* set up return values so they can be "freed" successfully */
+
+ major_status = GSS_S_FAILURE; /* Default major code */
+ output_token->length = 0;
+ output_token->value = NULL;
+ if (actual_mech_type)
+ *actual_mech_type = NULL;
+
+ /* verify the mech_type */
+
+ if (mech_type == GSS_C_NULL_OID || g_OID_equal(mech_type, gss_mech_krb5)) {
+ mech_type = (gss_OID) gss_mech_krb5;
+ } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
+ mech_type = (gss_OID) gss_mech_krb5_old;
+ } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
+ mech_type = (gss_OID) gss_mech_krb5_wrong;
+ } else if (g_OID_equal(mech_type, gss_mech_iakerb)) {
+ mech_type = (gss_OID) gss_mech_iakerb;
+ } else {
+ *minor_status = 0;
+ if (*context_handle == GSS_C_NO_CONTEXT)
+ krb5_free_context(context);
+ return(GSS_S_BAD_MECH);
+ }
+
+ /* is this a new connection or not? */
+
+ /*SUPPRESS 29*/
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ /* verify the credential, or use the default */
+ /*SUPPRESS 29*/
+ if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
+ major_status = kg_get_defcred(minor_status, &defcred);
+ if (major_status && GSS_ERROR(major_status)) {
+ if (*context_handle == GSS_C_NO_CONTEXT)
+ krb5_free_context(context);
+ return(major_status);
+ }
+ claimant_cred_handle = defcred;
+ }
+
+ major_status = kg_cred_resolve(minor_status, context,
+ claimant_cred_handle, target_name);
+ if (GSS_ERROR(major_status)) {
+ save_error_info(*minor_status, context);
+ krb5_gss_release_cred(&tmp_min_stat, &defcred);
+ if (*context_handle == GSS_C_NO_CONTEXT)
+ krb5_free_context(context);
+ return(major_status);
+ }
+ cred = (krb5_gss_cred_id_t)claimant_cred_handle;
+
+ major_status = kg_new_connection(minor_status, cred, context_handle,
+ target_name, mech_type, req_flags,
+ time_req, input_chan_bindings,
+ input_token, actual_mech_type,
+ output_token, ret_flags, time_rec,
+ context, exts);
+ k5_mutex_unlock(&cred->lock);
+ krb5_gss_release_cred(&tmp_min_stat, &defcred);
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ save_error_info (*minor_status, context);
+ krb5_free_context(context);
+ } else
+ ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
+ } else {
+ /* mutual_auth doesn't care about the credentials */
+ major_status = mutual_auth(minor_status, context_handle,
+ target_name, mech_type, req_flags,
+ time_req, input_chan_bindings,
+ input_token, actual_mech_type,
+ output_token, ret_flags, time_rec,
+ context);
+ /* If context_handle is now NO_CONTEXT, mutual_auth called
+ delete_sec_context, which would've zapped the krb5 context
+ too. */
+ }
+
+ return(major_status);
+}
+
+#ifndef _WIN32
+k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
+static int kdc_flag = 0;
+#endif
+
+krb5_error_code
+krb5_gss_init_context (krb5_context *ctxp)
+{
+ krb5_error_code err;
+#ifndef _WIN32
+ int is_kdc;
+#endif
+
+ err = gss_krb5int_initialize_library();
+ if (err)
+ return err;
+#ifndef _WIN32
+ k5_mutex_lock(&kg_kdc_flag_mutex);
+ is_kdc = kdc_flag;
+ k5_mutex_unlock(&kg_kdc_flag_mutex);
+
+ if (is_kdc)
+ return krb5int_init_context_kdc(ctxp);
+#endif
+
+ return krb5_init_context(ctxp);
+}
+
+#ifndef _WIN32
+OM_uint32
+krb5int_gss_use_kdc_context(OM_uint32 *minor_status,
+ const gss_OID desired_mech,
+ const gss_OID desired_object,
+ gss_buffer_t value)
+{
+ OM_uint32 err;
+
+ *minor_status = 0;
+
+ err = gss_krb5int_initialize_library();
+ if (err)
+ return err;
+ k5_mutex_lock(&kg_kdc_flag_mutex);
+ kdc_flag = 1;
+ k5_mutex_unlock(&kg_kdc_flag_mutex);
+ return GSS_S_COMPLETE;
+}
+#endif
+
+OM_uint32 KRB5_CALLCONV
+krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
+ context_handle, target_name, mech_type,
+ req_flags, time_req, input_chan_bindings,
+ input_token, actual_mech_type, output_token,
+ ret_flags, time_rec)
+ OM_uint32 *minor_status;
+ gss_cred_id_t claimant_cred_handle;
+ gss_ctx_id_t *context_handle;
+ gss_name_t target_name;
+ gss_OID mech_type;
+ OM_uint32 req_flags;
+ OM_uint32 time_req;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_buffer_t input_token;
+ gss_OID *actual_mech_type;
+ gss_buffer_t output_token;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+{
+ krb5_gss_ctx_ext_rec exts;
+
+ memset(&exts, 0, sizeof(exts));
+
+ return krb5_gss_init_sec_context_ext(minor_status,
+ claimant_cred_handle,
+ context_handle,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ &exts);
+}