2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright 2000, 2004, 2007, 2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A/*
2N/A * Copyright 1993 by OpenVision Technologies, Inc.
2N/A *
2N/A * Permission to use, copy, modify, distribute, and sell this software
2N/A * and its documentation for any purpose is hereby granted without fee,
2N/A * provided that the above copyright notice appears in all copies and
2N/A * that both that copyright notice and this permission notice appear in
2N/A * supporting documentation, and that the name of OpenVision not be used
2N/A * in advertising or publicity pertaining to distribution of the software
2N/A * without specific, written prior permission. OpenVision makes no
2N/A * representations about the suitability of this software for any
2N/A * purpose. It is provided "as is" without express or implied warranty.
2N/A *
2N/A * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
2N/A * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
2N/A * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
2N/A * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
2N/A * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
2N/A * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2N/A * PERFORMANCE OF THIS SOFTWARE.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (C) 1998 by the FundsXpress, INC.
2N/A *
2N/A * All rights reserved.
2N/A *
2N/A * Export of this software from the United States of America may require
2N/A * a specific license from the United States Government. It is the
2N/A * responsibility of any person or organization contemplating export to
2N/A * obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of FundsXpress. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. FundsXpress makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
2N/A * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
2N/A * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2N/A */
2N/A/*
2N/A * Copyright (c) 2006-2008, Novell, Inc.
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions are met:
2N/A *
2N/A * * Redistributions of source code must retain the above copyright notice,
2N/A * this list of conditions and the following disclaimer.
2N/A * * Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * * The copyright holder's name is not used to endorse or promote products
2N/A * derived from this software without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2N/A * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2N/A * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2N/A * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2N/A * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2N/A * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2N/A * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2N/A * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2N/A * POSSIBILITY OF SUCH DAMAGE.
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include "gssapiP_krb5.h"
2N/A#ifdef HAVE_MEMORY_H
2N/A#include <memory.h>
2N/A#endif
2N/A#include <assert.h>
2N/A#include <locale.h> /* Solaris Kerberos */
2N/A
2N/A
2N/A#ifdef CFX_EXERCISE
2N/A#define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
2N/A#else
2N/A#define CFX_ACCEPTOR_SUBKEY 1
2N/A#endif
2N/A
2N/A#ifndef LEAN_CLIENT
2N/A
2N/Astatic OM_uint32
2N/Acreate_constrained_deleg_creds(OM_uint32 *minor_status,
2N/A krb5_gss_cred_id_t verifier_cred_handle,
2N/A krb5_ticket *ticket,
2N/A krb5_gss_cred_id_t *out_cred,
2N/A krb5_context context)
2N/A{
2N/A OM_uint32 major_status;
2N/A krb5_creds krb_creds;
2N/A krb5_data *data;
2N/A krb5_error_code code;
2N/A
2N/A assert(out_cred != NULL);
2N/A assert(verifier_cred_handle->usage == GSS_C_BOTH);
2N/A
2N/A memset(&krb_creds, 0, sizeof(krb_creds));
2N/A krb_creds.client = ticket->enc_part2->client;
2N/A krb_creds.server = ticket->server;
2N/A krb_creds.keyblock = *(ticket->enc_part2->session);
2N/A krb_creds.ticket_flags = ticket->enc_part2->flags;
2N/A krb_creds.times = ticket->enc_part2->times;
2N/A krb_creds.magic = KV5M_CREDS;
2N/A krb_creds.authdata = NULL;
2N/A
2N/A code = encode_krb5_ticket(ticket, &data);
2N/A if (code) {
2N/A *minor_status = code;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A krb_creds.ticket = *data;
2N/A
2N/A major_status = kg_compose_deleg_cred(minor_status,
2N/A verifier_cred_handle,
2N/A &krb_creds,
2N/A GSS_C_INDEFINITE,
2N/A GSS_C_NO_OID_SET,
2N/A out_cred,
2N/A NULL,
2N/A NULL,
2N/A context);
2N/A
2N/A krb5_free_data(context, data);
2N/A
2N/A return major_status;
2N/A}
2N/A
2N/A/* Decode, decrypt and store the forwarded creds in the local ccache. */
2N/Astatic krb5_error_code
2N/Ard_and_store_for_creds(context, auth_context, inbuf, out_cred)
2N/A krb5_context context;
2N/A krb5_auth_context auth_context;
2N/A krb5_data *inbuf;
2N/A krb5_gss_cred_id_t *out_cred;
2N/A{
2N/A krb5_creds ** creds = NULL;
2N/A krb5_error_code retval;
2N/A krb5_ccache ccache = NULL;
2N/A krb5_gss_cred_id_t cred = NULL;
2N/A krb5_auth_context new_auth_ctx = NULL;
2N/A krb5_int32 flags_org;
2N/A
2N/A if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
2N/A return retval;
2N/A krb5_auth_con_setflags(context, auth_context,
2N/A 0);
2N/A
2N/A /*
2N/A * By the time krb5_rd_cred is called here (after krb5_rd_req has been
2N/A * called in krb5_gss_accept_sec_context), the "keyblock" field of
2N/A * auth_context contains a pointer to the session key, and the
2N/A * "recv_subkey" field might contain a session subkey. Either of
2N/A * these (the "recv_subkey" if it isn't NULL, otherwise the
2N/A * "keyblock") might have been used to encrypt the encrypted part of
2N/A * the KRB_CRED message that contains the forwarded credentials. (The
2N/A * Java Crypto and Security Implementation from the DSTC in Australia
2N/A * always uses the session key. But apparently it never negotiates a
2N/A * subkey, so this code works fine against a JCSI client.) Up to the
2N/A * present, though, GSSAPI clients linked against the MIT code (which
2N/A * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at
2N/A * all -- at this level. So if the first call to krb5_rd_cred fails,
2N/A * we should call it a second time with another auth context freshly
2N/A * created by krb5_auth_con_init. All of its keyblock fields will be
2N/A * NULL, so krb5_rd_cred will assume that the KRB_CRED message is
2N/A * unencrypted. (The MIT code doesn't actually send the KRB_CRED
2N/A * message in the clear -- the "authenticator" whose "checksum" ends up
2N/A * containing the KRB_CRED message does get encrypted.)
2N/A */
2N/A if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) {
2N/A krb5_keyblock *tmpkeyblock;
2N/A krb5_enctype enctype = ENCTYPE_NULL;
2N/A /*
2N/A * Solaris Kerberos: if the client is using non-DES enctypes it really
2N/A * ought to send encrypted KRB-CREDs...
2N/A */
2N/A if (krb5_auth_con_getkey(context, auth_context, &tmpkeyblock) == 0) {
2N/A if (tmpkeyblock != NULL) {
2N/A enctype = tmpkeyblock->enctype;
2N/A krb5int_c_free_keyblock(context, tmpkeyblock);
2N/A }
2N/A } else
2N/A goto cleanup;
2N/A
2N/A switch (enctype) {
2N/A case ENCTYPE_DES_CBC_MD5:
2N/A case ENCTYPE_DES_CBC_CRC:
2N/A case ENCTYPE_DES3_CBC_SHA1:
2N/A break;
2N/A default:
2N/A goto cleanup;
2N/A /* NOTREACHED */
2N/A break;
2N/A }
2N/A
2N/A /* Try to krb5_rd_cred() likely unencrypted KRB-CRED */
2N/A if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
2N/A goto cleanup;
2N/A krb5_auth_con_setflags(context, new_auth_ctx, 0);
2N/A if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
2N/A &creds, NULL)))
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
2N/A ccache = NULL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client)))
2N/A goto cleanup;
2N/A
2N/A if ((retval = krb5_cc_store_cred(context, ccache, creds[0])))
2N/A goto cleanup;
2N/A
2N/A /* generate a delegated credential handle */
2N/A if (out_cred) {
2N/A /* allocate memory for a cred_t... */
2N/A if (!(cred =
2N/A (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
2N/A retval = ENOMEM; /* out of memory? */
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* zero it out... */
2N/A memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
2N/A
2N/A retval = k5_mutex_init(&cred->lock);
2N/A if (retval) {
2N/A xfree(cred);
2N/A cred = NULL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* copy the client principle into it... */
2N/A if ((retval =
2N/A kg_init_name(context, creds[0]->client, NULL, 0, &cred->name))) {
2N/A k5_mutex_destroy(&cred->lock);
2N/A retval = ENOMEM; /* out of memory? */
2N/A xfree(cred); /* clean up memory on failure */
2N/A cred = NULL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A cred->usage = GSS_C_INITIATE; /* we can't accept with this */
2N/A /* cred->name already set */
2N/A cred->prerfc_mech = 1; /* this cred will work with all three mechs */
2N/A cred->rfc_mech = 1;
2N/A cred->keytab = NULL; /* no keytab associated with this... */
2N/A cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
2N/A cred->ccache = ccache; /* the ccache containing the credential */
2N/A ccache = NULL; /* cred takes ownership so don't destroy */
2N/A }
2N/A
2N/A /* If there were errors, there might have been a memory leak
2N/A if (!cred)
2N/A if ((retval = krb5_cc_close(context, ccache)))
2N/A goto cleanup;
2N/A */
2N/Acleanup:
2N/A if (creds)
2N/A krb5_free_tgt_creds(context, creds);
2N/A
2N/A if (ccache)
2N/A (void)krb5_cc_destroy(context, ccache);
2N/A
2N/A if (out_cred)
2N/A *out_cred = cred; /* return credential */
2N/A
2N/A if (new_auth_ctx)
2N/A krb5_auth_con_free(context, new_auth_ctx);
2N/A
2N/A krb5_auth_con_setflags(context, auth_context, flags_org);
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Performs third leg of DCE authentication
2N/A */
2N/Astatic OM_uint32
2N/Akg_accept_dce(minor_status, context_handle, verifier_cred_handle,
2N/A input_token, input_chan_bindings, src_name, mech_type,
2N/A output_token, ret_flags, time_rec, delegated_cred_handle)
2N/A OM_uint32 *minor_status;
2N/A gss_ctx_id_t *context_handle;
2N/A gss_cred_id_t verifier_cred_handle;
2N/A gss_buffer_t input_token;
2N/A gss_channel_bindings_t input_chan_bindings;
2N/A gss_name_t *src_name;
2N/A gss_OID *mech_type;
2N/A gss_buffer_t output_token;
2N/A OM_uint32 *ret_flags;
2N/A OM_uint32 *time_rec;
2N/A gss_cred_id_t *delegated_cred_handle;
2N/A{
2N/A krb5_error_code code;
2N/A krb5_gss_ctx_id_rec *ctx = 0;
2N/A krb5_timestamp now;
2N/A krb5_gss_name_t name = NULL;
2N/A krb5_ui_4 nonce = 0;
2N/A krb5_data ap_rep;
2N/A OM_uint32 major_status = GSS_S_FAILURE;
2N/A
2N/A output_token->length = 0;
2N/A output_token->value = NULL;
2N/A
2N/A if (mech_type)
2N/A *mech_type = GSS_C_NULL_OID;
2N/A /* return a bogus cred handle */
2N/A if (delegated_cred_handle)
2N/A *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
2N/A
2N/A ctx = (krb5_gss_ctx_id_rec *)*context_handle;
2N/A
2N/A code = krb5_timeofday(ctx->k5_context, &now);
2N/A if (code != 0) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A if (ctx->krb_times.endtime < now) {
2N/A code = 0;
2N/A major_status = GSS_S_CREDENTIALS_EXPIRED;
2N/A goto fail;
2N/A }
2N/A
2N/A ap_rep.data = input_token->value;
2N/A ap_rep.length = input_token->length;
2N/A
2N/A code = krb5_rd_rep_dce(ctx->k5_context,
2N/A ctx->auth_context,
2N/A &ap_rep,
2N/A &nonce);
2N/A if (code != 0) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A ctx->established = 1;
2N/A
2N/A if (src_name) {
2N/A if ((code = kg_duplicate_name(ctx->k5_context, ctx->there,
2N/A KG_INIT_NAME_INTERN, &name))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A *src_name = (gss_name_t) name;
2N/A }
2N/A
2N/A if (mech_type)
2N/A *mech_type = ctx->mech_used;
2N/A
2N/A if (time_rec)
2N/A *time_rec = ctx->krb_times.endtime - now;
2N/A
2N/A if (ret_flags)
2N/A *ret_flags = ctx->gss_flags;
2N/A
2N/A /* XXX no support for delegated credentials yet */
2N/A
2N/A *minor_status = 0;
2N/A
2N/A return GSS_S_COMPLETE;
2N/A
2N/Afail:
2N/A /* real failure code follows */
2N/A
2N/A (void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx,
2N/A NULL);
2N/A *context_handle = GSS_C_NO_CONTEXT;
2N/A *minor_status = code;
2N/A
2N/A return major_status;
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Akg_accept_krb5(minor_status, context_handle,
2N/A verifier_cred_handle, input_token,
2N/A input_chan_bindings, src_name, mech_type,
2N/A output_token, ret_flags, time_rec,
2N/A delegated_cred_handle)
2N/A OM_uint32 *minor_status;
2N/A gss_ctx_id_t *context_handle;
2N/A gss_cred_id_t verifier_cred_handle;
2N/A gss_buffer_t input_token;
2N/A gss_channel_bindings_t input_chan_bindings;
2N/A gss_name_t *src_name;
2N/A gss_OID *mech_type;
2N/A gss_buffer_t output_token;
2N/A OM_uint32 *ret_flags;
2N/A OM_uint32 *time_rec;
2N/A gss_cred_id_t *delegated_cred_handle;
2N/A{
2N/A krb5_context context;
2N/A unsigned char *ptr, *ptr2;
2N/A char *sptr;
2N/A OM_uint32 tmp;
2N/A size_t md5len;
2N/A int bigend;
2N/A krb5_gss_cred_id_t cred = 0;
2N/A krb5_data ap_rep, ap_req;
2N/A unsigned int i;
2N/A krb5_error_code code;
2N/A krb5_address addr, *paddr;
2N/A krb5_authenticator *authdat = 0;
2N/A krb5_checksum reqcksum;
2N/A krb5_gss_name_t name = NULL;
2N/A krb5_ui_4 gss_flags = 0;
2N/A int decode_req_message = 0;
2N/A krb5_gss_ctx_id_rec *ctx = NULL;
2N/A krb5_timestamp now;
2N/A gss_buffer_desc token;
2N/A krb5_auth_context auth_context = NULL;
2N/A krb5_ticket * ticket = NULL;
2N/A int option_id;
2N/A krb5_data option;
2N/A const gss_OID_desc *mech_used = NULL;
2N/A OM_uint32 major_status = GSS_S_FAILURE;
2N/A OM_uint32 tmp_minor_status;
2N/A krb5_error krb_error_data;
2N/A krb5_data scratch;
2N/A gss_cred_id_t cred_handle = NULL;
2N/A krb5_gss_cred_id_t deleg_cred = NULL;
2N/A krb5int_access kaccess;
2N/A int cred_rcache = 0;
2N/A int no_encap = 0;
2N/A krb5_flags ap_req_options = 0;
2N/A krb5_enctype negotiated_etype;
2N/A krb5_authdata_context ad_context = NULL;
2N/A krb5_ap_req *request = NULL; /* Solaris Kerberos */
2N/A int acquire_fail = 0;
2N/A krb5_principal client_name = NULL, server_name = NULL; /* Solaris Kerberos */
2N/A OM_uint32 saved_ap_options = 0; /* Solaris Kerberos */
2N/A
2N/A /* Solaris Kerberos */
2N/A memset(&krb_error_data, 0, sizeof(krb_error_data));
2N/A
2N/A code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
2N/A if (code) {
2N/A *minor_status = code;
2N/A return(GSS_S_FAILURE);
2N/A }
2N/A
2N/A code = krb5_gss_init_context(&context);
2N/A if (code) {
2N/A *minor_status = code;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A /* set up returns to be freeable */
2N/A
2N/A if (src_name)
2N/A *src_name = (gss_name_t) NULL;
2N/A output_token->length = 0;
2N/A output_token->value = NULL;
2N/A token.value = 0;
2N/A reqcksum.contents = 0;
2N/A ap_req.data = 0;
2N/A ap_rep.data = 0;
2N/A
2N/A if (mech_type)
2N/A *mech_type = GSS_C_NULL_OID;
2N/A /* return a bogus cred handle */
2N/A if (delegated_cred_handle)
2N/A *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
2N/A
2N/A /* Solaris Kerberos: better acquire cred handling, more below */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A /* handle default cred handle */
2N/A if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
2N/A major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
2N/A GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
2N/A GSS_C_ACCEPT, &cred_handle,
2N/A NULL, NULL);
2N/A if (major_status != GSS_S_COMPLETE) {
2N/A code = *minor_status;
2N/A goto fail;
2N/A }
2N/A } else {
2N/A major_status = krb5_gss_validate_cred(minor_status,
2N/A verifier_cred_handle);
2N/A if (GSS_ERROR(major_status)) {
2N/A code = *minor_status;
2N/A goto fail;
2N/A }
2N/A cred_handle = verifier_cred_handle;
2N/A }
2N/A
2N/A cred = (krb5_gss_cred_id_t) cred_handle;
2N/A
2N/A /* make sure the supplied credentials are valid for accept */
2N/A
2N/A if ((cred->usage != GSS_C_ACCEPT) &&
2N/A (cred->usage != GSS_C_BOTH)) {
2N/A code = 0;
2N/A major_status = GSS_S_NO_CRED;
2N/A goto fail;
2N/A }
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A /* verify the token's integrity, and leave the token in ap_req.
2N/A figure out which mech oid was used, and save it */
2N/A
2N/A ptr = (unsigned char *) input_token->value;
2N/A
2N/A if (!(code = g_verify_token_header(gss_mech_krb5,
2N/A &(ap_req.length),
2N/A &ptr, KG_TOK_CTX_AP_REQ,
2N/A input_token->length, 1))) {
2N/A mech_used = gss_mech_krb5;
2N/A } else if ((code == G_WRONG_MECH)
2N/A &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
2N/A &(ap_req.length),
2N/A &ptr, KG_TOK_CTX_AP_REQ,
2N/A input_token->length, 1))) {
2N/A mech_used = gss_mech_krb5_wrong;
2N/A } else if ((code == G_WRONG_MECH) &&
2N/A !(code = g_verify_token_header(gss_mech_krb5_old,
2N/A &(ap_req.length),
2N/A &ptr, KG_TOK_CTX_AP_REQ,
2N/A input_token->length, 1))) {
2N/A /*
2N/A * Previous versions of this library used the old mech_id
2N/A * and some broken behavior (wrong IV on checksum
2N/A * encryption). We support the old mech_id for
2N/A * compatibility, and use it to decide when to use the
2N/A * old behavior.
2N/A */
2N/A mech_used = gss_mech_krb5_old;
2N/A } else if (code == G_WRONG_TOKID) {
2N/A major_status = GSS_S_CONTINUE_NEEDED;
2N/A code = KRB5KRB_AP_ERR_MSG_TYPE;
2N/A mech_used = gss_mech_krb5;
2N/A goto fail;
2N/A } else if (code == G_BAD_TOK_HEADER) {
2N/A /* DCE style not encapsulated */
2N/A ap_req.length = input_token->length;
2N/A ap_req.data = input_token->value;
2N/A mech_used = gss_mech_krb5;
2N/A no_encap = 1;
2N/A } else {
2N/A major_status = GSS_S_DEFECTIVE_TOKEN;
2N/A goto fail;
2N/A }
2N/A
2N/A sptr = (char *) ptr;
2N/A TREAD_STR(sptr, ap_req.data, ap_req.length);
2N/A /* decode_req_message = 1; */ /* Solaris Kerberos */
2N/A
2N/A /*
2N/A * Solaris Kerberos:
2N/A * We need to decode the request now so that we can get the
2N/A * service principal in order to try and acquire a cred for it.
2N/A * below in the "handle default cred handle" code block.
2N/A */
2N/A if (!krb5_is_ap_req(&ap_req)) {
2N/A code = KRB5KRB_AP_ERR_MSG_TYPE;
2N/A goto fail;
2N/A }
2N/A /* decode the AP-REQ into request */
2N/A if ((code = decode_krb5_ap_req(&ap_req, &request))) {
2N/A if (code == KRB5_BADMSGTYPE)
2N/A code = KRB5KRB_AP_ERR_BADVERSION;
2N/A goto fail;
2N/A }
2N/A
2N/A /* handle default cred handle */
2N/A /*
2N/A * Solaris Kerberos:
2N/A * If there is no princ associated with the cred then treat it the
2N/A * the same as GSS_C_NO_CREDENTIAL.
2N/A */
2N/A if (verifier_cred_handle == GSS_C_NO_CREDENTIAL ||
2N/A ((krb5_gss_cred_id_t)verifier_cred_handle)->name == NULL) {
2N/A /* Note that we try to acquire a cred for the service principal
2N/A * named in the AP-REQ. This allows us to implement option (ii)
2N/A * of the recommended behaviour for GSS_Accept_sec_context() as
2N/A * described in section 1.1.1.3 of RFC2743.
2N/A
2N/A * This is far more useful that option (i), for which we would
2N/A * acquire a cred for GSS_C_NO_NAME.
2N/A */
2N/A /* copy the princ from the ap-req or we'll lose it when we free
2N/A the ap-req */
2N/A krb5_gss_name_rec desired_name;
2N/A
2N/A memset(&desired_name, 0, sizeof (krb5_gss_name_rec));
2N/A
2N/A if ((code = krb5_copy_principal(context, request->ticket->server,
2N/A &desired_name.princ))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* intern the acceptor name */
2N/A if (! kg_save_name((gss_name_t) &desired_name)) {
2N/A /* Solaris Kerberos */
2N/A krb5_free_principal(context, desired_name.princ);
2N/A
2N/A code = G_VALIDATE_FAILED;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A major_status = krb5_gss_acquire_cred((OM_uint32*) &code,
2N/A (gss_name_t) &desired_name,
2N/A GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
2N/A GSS_C_ACCEPT, &cred_handle,
2N/A NULL, NULL);
2N/A
2N/A /* Solaris Kerberos */
2N/A krb5_free_principal(context, desired_name.princ);
2N/A
2N/A if (major_status != GSS_S_COMPLETE){
2N/A /* Solaris kerberos: RFC2743 indicate this should be returned if we
2N/A * can't aquire a default cred.
2N/A */
2N/A acquire_fail = 1;
2N/A major_status = GSS_S_NO_CRED;
2N/A goto fail;
2N/A }
2N/A
2N/A } else {
2N/A cred_handle = verifier_cred_handle;
2N/A }
2N/A
2N/A major_status = krb5_gss_validate_cred((OM_uint32*) &code,
2N/A cred_handle);
2N/A
2N/A if (GSS_ERROR(major_status)){
2N/A /*
2N/A * Solaris Kerberos: RFC2743 indicate GSS_S_NO_CRED should be returned if
2N/A * the supplied cred isn't valid.
2N/A */
2N/A major_status = GSS_S_NO_CRED;
2N/A goto fail;
2N/A }
2N/A
2N/A cred = (krb5_gss_cred_id_t) cred_handle;
2N/A
2N/A /* make sure the supplied credentials are valid for accept */
2N/A
2N/A if ((cred->usage != GSS_C_ACCEPT) &&
2N/A (cred->usage != GSS_C_BOTH)) {
2N/A code = 0;
2N/A major_status = GSS_S_NO_CRED;
2N/A goto fail;
2N/A }
2N/A
2N/A /* construct the sender_addr */
2N/A
2N/A if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
2N/A (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
2N/A /* XXX is this right? */
2N/A addr.addrtype = ADDRTYPE_INET;
2N/A addr.length = input_chan_bindings->initiator_address.length;
2N/A addr.contents = input_chan_bindings->initiator_address.value;
2N/A
2N/A paddr = &addr;
2N/A } else {
2N/A paddr = NULL;
2N/A }
2N/A
2N/A /*
2N/A * Solaris Kerberos comment: verify the AP_REQ message - setup the
2N/A * auth_context and rcache
2N/A */
2N/A
2N/A if ((code = krb5_auth_con_init(context, &auth_context))) {
2N/A major_status = GSS_S_FAILURE;
2N/A save_error_info((OM_uint32)code, context);
2N/A goto fail;
2N/A }
2N/A
2N/A /* Solaris Kerberos */
2N/A (void) krb5_auth_con_setflags(context, auth_context,
2N/A KRB5_AUTH_CONTEXT_DO_SEQUENCE);
2N/A
2N/A if (cred->rcache) {
2N/A cred_rcache = 1;
2N/A if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* Solaris Kerberos related to better acquire cred handling */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A if ((code = krb5_rd_req(context, &auth_context, &ap_req,
2N/A cred->default_identity ? NULL : cred->name->princ,
2N/A cred->keytab,
2N/A &ap_req_options,
2N/A &ticket))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A#else /* Solaris Kerberos */
2N/A /* XXX WAF I may need to fix this */
2N/A if ((code = krb5_rd_req_decoded_ext(context, &auth_context, request, &ap_req,
2N/A cred->name->princ, cred->keytab, NULL,
2N/A &ticket))) {
2N/A /* Solaris Kerberos */
2N/A if (code == KRB5KRB_AP_ERR_SKEW) {
2N/A save_error_info(code, context);
2N/A }
2N/A if (code == KRB5_KT_KVNONOTFOUND) {
2N/A char *s_name;
2N/A if (krb5_unparse_name(context, cred->name->princ, &s_name) == 0) {
2N/A krb5_set_error_message(context, KRB5KRB_AP_ERR_BADKEYVER,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Key version %d is not "
2N/A "available for principal %s"),
2N/A request->ticket->enc_part.kvno,
2N/A s_name);
2N/A krb5_free_unparsed_name(context, s_name);
2N/A }
2N/A major_status = GSS_S_DEFECTIVE_CREDENTIAL;
2N/A code = KRB5KRB_AP_ERR_BADKEYVER;
2N/A } else if (code == KRB5_KT_NOTFOUND) {
2N/A char *s_name;
2N/A if (krb5_unparse_name(context, cred->name->princ, &s_name) == 0) {
2N/A krb5_set_error_message(context, KRB5KRB_AP_ERR_NOKEY,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Service key %s not available"),
2N/A s_name);
2N/A krb5_free_unparsed_name(context, s_name);
2N/A }
2N/A major_status = GSS_S_DEFECTIVE_CREDENTIAL;
2N/A code = KRB5KRB_AP_ERR_NOKEY;
2N/A }
2N/A else if (code == KRB5KRB_AP_WRONG_PRINC) {
2N/A major_status = GSS_S_NO_CRED;
2N/A code = KRB5KRB_AP_ERR_NOT_US;
2N/A }
2N/A else if (code == KRB5KRB_AP_ERR_REPEAT)
2N/A major_status = GSS_S_DUPLICATE_TOKEN;
2N/A else
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A krb5_auth_con_setflags(context, auth_context,
2N/A KRB5_AUTH_CONTEXT_DO_SEQUENCE);
2N/A
2N/A /* Solaris Kerberos */
2N/A code = krb5_auth_con_getauthenticator(context, auth_context, &authdat);
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A#if 0
2N/A /* make sure the necessary parts of the authdat are present */
2N/A
2N/A if ((authdat->authenticator->subkey == NULL) ||
2N/A (authdat->ticket->enc_part2 == NULL)) {
2N/A code = KG_NO_SUBKEY;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A#endif
2N/A
2N/A if (authdat->checksum == NULL) {
2N/A /* missing checksum counts as "inappropriate type" */
2N/A code = KRB5KRB_AP_ERR_INAPP_CKSUM;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
2N/A /* Samba does not send 0x8003 GSS-API checksums */
2N/A krb5_boolean valid;
2N/A krb5_key subkey;
2N/A krb5_data zero;
2N/A
2N/A code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A zero.length = 0;
2N/A zero.data = "";
2N/A
2N/A code = krb5_k_verify_checksum(context,
2N/A subkey,
2N/A KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
2N/A &zero,
2N/A authdat->checksum,
2N/A &valid);
2N/A krb5_k_free_key(context, subkey);
2N/A if (code || !valid) {
2N/A major_status = GSS_S_BAD_SIG;
2N/A goto fail;
2N/A }
2N/A
2N/A gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
2N/A bigend = 0;
2N/A decode_req_message = 0;
2N/A } else {
2N/A /* gss krb5 v1 */
2N/A
2N/A /* stash this now, for later. */
2N/A code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* verify that the checksum is correct */
2N/A
2N/A /*
2N/A The checksum may be either exactly 24 bytes, in which case
2N/A no options are specified, or greater than 24 bytes, in which case
2N/A one or more options are specified. Currently, the only valid
2N/A option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
2N/A */
2N/A
2N/A if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
2N/A (authdat->checksum->length < 24)) {
2N/A code = 0;
2N/A major_status = GSS_S_BAD_BINDINGS;
2N/A goto fail;
2N/A }
2N/A
2N/A /*
2N/A "Be liberal in what you accept, and
2N/A conservative in what you send"
2N/A -- rfc1123
2N/A
2N/A This code will let this acceptor interoperate with an initiator
2N/A using little-endian or big-endian integer encoding.
2N/A */
2N/A
2N/A ptr = (unsigned char *) authdat->checksum->contents;
2N/A bigend = 0;
2N/A
2N/A TREAD_INT(ptr, tmp, bigend);
2N/A
2N/A if (tmp != md5len) {
2N/A ptr = (unsigned char *) authdat->checksum->contents;
2N/A bigend = 1;
2N/A
2N/A TREAD_INT(ptr, tmp, bigend);
2N/A
2N/A if (tmp != md5len) {
2N/A code = KG_BAD_LENGTH;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A /* at this point, bigend is set according to the initiator's
2N/A byte order */
2N/A
2N/A
2N/A /*
2N/A The following section of code attempts to implement the
2N/A optional channel binding facility as described in RFC2743.
2N/A
2N/A Since this facility is optional channel binding may or may
2N/A not have been provided by either the client or the server.
2N/A
2N/A If the server has specified input_chan_bindings equal to
2N/A GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If
2N/A the server does provide channel bindings then we compute
2N/A a checksum and compare against those provided by the
2N/A client. */
2N/A
2N/A if ((code = kg_checksum_channel_bindings(context,
2N/A input_chan_bindings,
2N/A &reqcksum, bigend))) {
2N/A major_status = GSS_S_BAD_BINDINGS;
2N/A goto fail;
2N/A }
2N/A
2N/A /* Always read the clients bindings - eventhough we might ignore them */
2N/A TREAD_STR(ptr, ptr2, reqcksum.length);
2N/A
2N/A if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
2N/A if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
2N/A xfree(reqcksum.contents);
2N/A reqcksum.contents = 0;
2N/A /* Solaris Kerberos begin */
2N/A if ((code = kg_checksum_channel_bindings(context,
2N/A GSS_C_NO_CHANNEL_BINDINGS,
2N/A &reqcksum, bigend))) {
2N/A major_status = GSS_S_BAD_BINDINGS;
2N/A goto fail;
2N/A }
2N/A if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
2N/A code = 0;
2N/A major_status = GSS_S_BAD_BINDINGS;
2N/A goto fail;
2N/A }
2N/A /* Solaris Kerberos end */
2N/A }
2N/A
2N/A }
2N/A /* Solaris Kerberos */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A xfree(reqcksum.contents);
2N/A reqcksum.contents = 0;
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A TREAD_INT(ptr, gss_flags, bigend);
2N/A#if 0
2N/A gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if
2N/A there's a delegation, we'll set
2N/A it below */
2N/A#endif
2N/A /* decode_req_message = 0; */ /* Solaris Kerberos */
2N/A
2N/A /* if the checksum length > 24, there are options to process */
2N/A
2N/A if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
2N/A
2N/A i = authdat->checksum->length - 24;
2N/A
2N/A if (i >= 4) {
2N/A
2N/A TREAD_INT16(ptr, option_id, bigend);
2N/A
2N/A TREAD_INT16(ptr, option.length, bigend);
2N/A
2N/A i -= 4;
2N/A
2N/A if (i < option.length || option.length < 0) {
2N/A code = KG_BAD_LENGTH;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* have to use ptr2, since option.data is wrong type and
2N/A macro uses ptr as both lvalue and rvalue */
2N/A
2N/A TREAD_STR(ptr, ptr2, option.length);
2N/A option.data = (char *) ptr2;
2N/A
2N/A i -= option.length;
2N/A
2N/A if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* store the delegated credential */
2N/A
2N/A code = rd_and_store_for_creds(context, auth_context, &option,
2N/A (delegated_cred_handle) ?
2N/A &deleg_cred : NULL);
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A } /* if i >= 4 */
2N/A /* ignore any additional trailing data, for now */
2N/A#ifdef CFX_EXERCISE
2N/A {
2N/A FILE *f = fopen("/tmp/gsslog", "a");
2N/A if (f) {
2N/A fprintf(f,
2N/A "initial context token with delegation, %d extra bytes\n",
2N/A i);
2N/A fclose(f);
2N/A }
2N/A }
2N/A#endif
2N/A } else {
2N/A#ifdef CFX_EXERCISE
2N/A {
2N/A FILE *f = fopen("/tmp/gsslog", "a");
2N/A if (f) {
2N/A if (gss_flags & GSS_C_DELEG_FLAG)
2N/A fprintf(f,
2N/A "initial context token, delegation flag but too small\n");
2N/A else
2N/A /* no deleg flag, length might still be too big */
2N/A fprintf(f,
2N/A "initial context token, %d extra bytes\n",
2N/A authdat->checksum->length - 24);
2N/A fclose(f);
2N/A }
2N/A }
2N/A#endif
2N/A }
2N/A }
2N/A
2N/A /* only DCE_STYLE clients are allowed to send raw AP-REQs */
2N/A if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
2N/A major_status = GSS_S_DEFECTIVE_TOKEN;
2N/A goto fail;
2N/A }
2N/A
2N/A /* create the ctx struct and start filling it in */
2N/A
2N/A if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
2N/A == NULL) {
2N/A code = ENOMEM;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
2N/A ctx->mech_used = (gss_OID) mech_used;
2N/A ctx->auth_context = auth_context;
2N/A ctx->initiate = 0;
2N/A ctx->gss_flags = (GSS_C_TRANS_FLAG |
2N/A ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
2N/A GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
2N/A GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
2N/A GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
2N/A GSS_C_EXTENDED_ERROR_FLAG)));
2N/A ctx->seed_init = 0;
2N/A ctx->big_endian = bigend;
2N/A ctx->cred_rcache = cred_rcache;
2N/A
2N/A /* Intern the ctx pointer so that delete_sec_context works */
2N/A if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
2N/A xfree(ctx);
2N/A ctx = 0;
2N/A
2N/A code = G_VALIDATE_FAILED;
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* XXX move this into gss_name_t */
2N/A if ( (code = krb5_merge_authdata(context,
2N/A ticket->enc_part2->authorization_data,
2N/A authdat->authorization_data,
2N/A &ctx->authdata))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A if ((code = kg_init_name(context, ticket->server, NULL, 0, &ctx->here))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A if ((code = krb5_auth_con_get_authdata_context(context, auth_context,
2N/A &ad_context))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A if ((code = kg_init_name(context, authdat->client,
2N/A ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A /* Now owned by ctx->there */
2N/A authdat->client = NULL;
2N/A krb5_auth_con_set_authdata_context(context, auth_context, NULL);
2N/A
2N/A if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context,
2N/A &ctx->subkey))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A /* use the session key if the subkey isn't present */
2N/A
2N/A if (ctx->subkey == NULL) {
2N/A if ((code = krb5_auth_con_getkey_k(context, auth_context,
2N/A &ctx->subkey))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A if (ctx->subkey == NULL) {
2N/A /* this isn't a very good error, but it's not clear to me this
2N/A can actually happen */
2N/A major_status = GSS_S_FAILURE;
2N/A code = KRB5KDC_ERR_NULL_KEY;
2N/A goto fail;
2N/A }
2N/A
2N/A ctx->enc = NULL;
2N/A ctx->seq = NULL;
2N/A ctx->have_acceptor_subkey = 0;
2N/A /* DCE_STYLE implies acceptor_subkey */
2N/A if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
2N/A code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A ctx->krb_times = ticket->enc_part2->times; /* struct copy */
2N/A ctx->krb_flags = ticket->enc_part2->flags;
2N/A
2N/A if (delegated_cred_handle != NULL &&
2N/A deleg_cred == NULL && /* no unconstrained delegation */
2N/A cred->usage == GSS_C_BOTH &&
2N/A (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
2N/A /*
2N/A * Now, we always fabricate a delegated credentials handle
2N/A * containing the service ticket to ourselves, which can be
2N/A * used for S4U2Proxy.
2N/A */
2N/A major_status = create_constrained_deleg_creds(minor_status, cred,
2N/A ticket, &deleg_cred,
2N/A context);
2N/A if (GSS_ERROR(major_status))
2N/A goto fail;
2N/A ctx->gss_flags |= GSS_C_DELEG_FLAG;
2N/A }
2N/A
2N/A krb5_free_ticket(context, ticket); /* Done with ticket */
2N/A
2N/A {
2N/A krb5_int32 seq_temp;
2N/A krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
2N/A ctx->seq_recv = seq_temp;
2N/A }
2N/A
2N/A if ((code = krb5_timeofday(context, &now))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A if (ctx->krb_times.endtime < now) {
2N/A code = 0;
2N/A major_status = GSS_S_CREDENTIALS_EXPIRED;
2N/A goto fail;
2N/A }
2N/A
2N/A g_order_init(&(ctx->seqstate), ctx->seq_recv,
2N/A (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
2N/A (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
2N/A
2N/A /* DCE_STYLE implies mutual authentication */
2N/A if (ctx->gss_flags & GSS_C_DCE_STYLE)
2N/A ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
2N/A
2N/A /* at this point, the entire context structure is filled in,
2N/A so it can be released. */
2N/A
2N/A /* generate an AP_REP if necessary */
2N/A
2N/A if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
2N/A unsigned char * ptr3;
2N/A krb5_int32 seq_temp;
2N/A int cfx_generate_subkey;
2N/A
2N/A /*
2N/A * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX,
2N/A * because pre-CFX tokens do not indicate which key to use. (Note that
2N/A * DCE_STYLE implies that we will use a subkey.)
2N/A */
2N/A if (ctx->proto == 0 &&
2N/A (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
2N/A (ap_req_options & AP_OPTS_USE_SUBKEY)) {
2N/A code = (*kaccess.auth_con_get_subkey_enctype)(context,
2N/A auth_context,
2N/A &negotiated_etype);
2N/A if (code != 0) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A switch (negotiated_etype) {
2N/A case ENCTYPE_DES_CBC_MD5:
2N/A case ENCTYPE_DES_CBC_MD4:
2N/A case ENCTYPE_DES_CBC_CRC:
2N/A case ENCTYPE_DES3_CBC_SHA1:
2N/A case ENCTYPE_ARCFOUR_HMAC:
2N/A case ENCTYPE_ARCFOUR_HMAC_EXP:
2N/A ap_req_options &= ~(AP_OPTS_USE_SUBKEY);
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
2N/A (ap_req_options & AP_OPTS_USE_SUBKEY))
2N/A cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
2N/A else
2N/A cfx_generate_subkey = 0;
2N/A
2N/A if (cfx_generate_subkey) {
2N/A krb5_int32 acflags;
2N/A code = krb5_auth_con_getflags(context, auth_context, &acflags);
2N/A if (code == 0) {
2N/A acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
2N/A code = krb5_auth_con_setflags(context, auth_context, acflags);
2N/A }
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
2N/A ctx->seq_send = seq_temp & 0xffffffffL;
2N/A
2N/A if (cfx_generate_subkey) {
2N/A /* Get the new acceptor subkey. With the code above, there
2N/A should always be one if we make it to this point. */
2N/A code = krb5_auth_con_getsendsubkey_k(context, auth_context,
2N/A &ctx->acceptor_subkey);
2N/A if (code != 0) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A ctx->have_acceptor_subkey = 1;
2N/A
2N/A code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
2N/A &ctx->acceptor_subkey_cksumtype);
2N/A if (code) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A /* the reply token hasn't been sent yet, but that's ok. */
2N/A if (ctx->gss_flags & GSS_C_DCE_STYLE) {
2N/A assert(ctx->have_acceptor_subkey);
2N/A
2N/A /* in order to force acceptor subkey to be used, don't set PROT_READY */
2N/A
2N/A /* Raw AP-REP is returned */
2N/A output_token->length = ap_rep.length;
2N/A output_token->value = ap_rep.data;
2N/A ap_rep.data = NULL; /* don't double free */
2N/A
2N/A ctx->established = 0;
2N/A
2N/A *context_handle = (gss_ctx_id_t)ctx;
2N/A *minor_status = 0;
2N/A major_status = GSS_S_CONTINUE_NEEDED;
2N/A
2N/A /* Only last leg should set return arguments */
2N/A goto fail;
2N/A } else
2N/A ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
2N/A
2N/A ctx->established = 1;
2N/A
2N/A token.length = g_token_size(mech_used, ap_rep.length);
2N/A
2N/A if ((token.value = (unsigned char *) xmalloc(token.length))
2N/A == NULL) {
2N/A major_status = GSS_S_FAILURE;
2N/A code = ENOMEM;
2N/A goto fail;
2N/A }
2N/A ptr3 = token.value;
2N/A g_make_token_header(mech_used, ap_rep.length,
2N/A &ptr3, KG_TOK_CTX_AP_REP);
2N/A
2N/A TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
2N/A
2N/A ctx->established = 1;
2N/A
2N/A } else {
2N/A token.length = 0;
2N/A token.value = NULL;
2N/A ctx->seq_send = ctx->seq_recv;
2N/A
2N/A ctx->established = 1;
2N/A }
2N/A
2N/A /* set the return arguments */
2N/A
2N/A /*
2N/A * Solaris Kerberos
2N/A * Regardless of src_name, get name for error msg if neeeded.
2N/A */
2N/A if ((code = krb5_copy_principal(context, ctx->there->princ, &client_name))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A if ((code = krb5_copy_principal(context, ctx->here->princ, &server_name))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A
2N/A if (src_name) {
2N/A if ((code = kg_duplicate_name(context, ctx->there,
2N/A KG_INIT_NAME_INTERN, &name))) {
2N/A major_status = GSS_S_FAILURE;
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A /* Solaris Kerberos */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A if (mech_type)
2N/A *mech_type = (gss_OID) mech_used;
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A if (time_rec)
2N/A *time_rec = ctx->krb_times.endtime - now;
2N/A
2N/A if (ret_flags)
2N/A *ret_flags = ctx->gss_flags;
2N/A
2N/A *context_handle = (gss_ctx_id_t)ctx;
2N/A *output_token = token;
2N/A
2N/A if (src_name)
2N/A *src_name = (gss_name_t) name;
2N/A
2N/A if (delegated_cred_handle) {
2N/A if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
2N/A major_status = GSS_S_FAILURE;
2N/A code = G_VALIDATE_FAILED;
2N/A goto fail;
2N/A }
2N/A
2N/A *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
2N/A }
2N/A
2N/A /* finally! */
2N/A
2N/A *minor_status = 0;
2N/A major_status = GSS_S_COMPLETE;
2N/A
2N/Afail:
2N/A /* Solaris Kerberos */
2N/A if (mech_type) {
2N/A /*
2N/A * This needs to be set/returned even on fail so
2N/A * gss_accept_sec_context() can map_error_oid() the correct
2N/A * error/oid for later use by gss_display_status().
2N/A * (needed in CIFS/SPNEGO case)
2N/A */
2N/A *mech_type = (gss_OID) mech_used;
2N/A }
2N/A if (authdat)
2N/A krb5_free_authenticator(context, authdat);
2N/A /* The ctx structure has the handle of the auth_context */
2N/A if (auth_context && !ctx) {
2N/A if (cred_rcache)
2N/A (void)krb5_auth_con_setrcache(context, auth_context, NULL);
2N/A
2N/A krb5_auth_con_free(context, auth_context);
2N/A }
2N/A if (reqcksum.contents)
2N/A xfree(reqcksum.contents);
2N/A if (ap_rep.data)
2N/A krb5_free_data_contents(context, &ap_rep);
2N/A /* Solaris Kerberos */
2N/A if (request != NULL) {
2N/A saved_ap_options = request->ap_options;
2N/A krb5_free_ap_req(context, request);
2N/A request = NULL;
2N/A }
2N/A if (major_status == GSS_S_COMPLETE ||
2N/A (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) {
2N/A ctx->k5_context = context;
2N/A context = NULL;
2N/A goto done;
2N/A }
2N/A
2N/A /* from here on is the real "fail" code */
2N/A
2N/A if (ctx)
2N/A (void) krb5_gss_delete_sec_context(&tmp_minor_status,
2N/A (gss_ctx_id_t *) &ctx, NULL);
2N/A if (deleg_cred) { /* free memory associated with the deleg credential */
2N/A if (deleg_cred->ccache)
2N/A (void)krb5_cc_close(context, deleg_cred->ccache);
2N/A if (deleg_cred->name)
2N/A kg_release_name(context, 0, &deleg_cred->name);
2N/A xfree(deleg_cred);
2N/A }
2N/A if (token.value)
2N/A xfree(token.value);
2N/A if (name) {
2N/A (void) kg_release_name(context, 0, &name);
2N/A }
2N/A
2N/A *minor_status = code;
2N/A
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A /*
2N/A * If decode_req_message is set, then we need to decode the ap_req
2N/A * message to determine whether or not to send a response token.
2N/A * We need to do this because for some errors we won't be able to
2N/A * decode the authenticator to read out the gss_flags field.
2N/A */
2N/A if (decode_req_message) {
2N/A krb5_ap_req * request;
2N/A
2N/A if (decode_krb5_ap_req(&ap_req, &request))
2N/A goto done;
2N/A
2N/A if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
2N/A gss_flags |= GSS_C_MUTUAL_FLAG;
2N/A krb5_free_ap_req(context, request);
2N/A }
2N/A#else /* Solaris Kerberos */
2N/A if (saved_ap_options & AP_OPTS_MUTUAL_REQUIRED)
2N/A gss_flags |= GSS_C_MUTUAL_FLAG;
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A if (cred
2N/A && ((gss_flags & GSS_C_MUTUAL_FLAG)
2N/A || (major_status == GSS_S_CONTINUE_NEEDED))) {
2N/A unsigned int tmsglen;
2N/A int toktype;
2N/A
2N/A /*
2N/A * The client is expecting a response, so we can send an
2N/A * error token back
2N/A */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A memset(&krb_error_data, 0, sizeof(krb_error_data));
2N/A#else
2N/A /*
2N/A * Solaris Kerberos: We need to remap error conditions for buggy
2N/A * Windows clients if the MS_INTEROP env var has been set.
2N/A */
2N/A if ((code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
2N/A code == KRB5KRB_AP_ERR_NOKEY || code == KRB5KRB_AP_ERR_BADKEYVER)
2N/A && krb5_getenv("MS_INTEROP")) {
2N/A code = KRB5KRB_AP_ERR_MODIFIED;
2N/A major_status = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A
2N/A /*
2N/A * SUNW17PACresync / Solaris Kerberos
2N/A * Set e-data to Windows constant.
2N/A * (verified by MSFT)
2N/A *
2N/A * This facilitates the Windows CIFS client clock skew
2N/A * recovery feature.
2N/A */
2N/A if (code == KRB5KRB_AP_ERR_SKEW && krb5_getenv("MS_INTEROP")) {
2N/A char *ms_e_data = "\x30\x05\xa1\x03\x02\x01\x02";
2N/A int len = strlen(ms_e_data);
2N/A
2N/A krb_error_data.e_data.data = malloc(len);
2N/A if (krb_error_data.e_data.data) {
2N/A (void) memcpy(krb_error_data.e_data.data, ms_e_data, len);
2N/A krb_error_data.e_data.length = len;
2N/A }
2N/A major_status = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A code -= ERROR_TABLE_BASE_krb5;
2N/A if (code < 0 || code > 128)
2N/A code = 60 /* KRB_ERR_GENERIC */;
2N/A
2N/A krb_error_data.error = code;
2N/A (void) krb5_us_timeofday(context, &krb_error_data.stime,
2N/A &krb_error_data.susec);
2N/A krb_error_data.server = cred->name ? cred->name->princ : NULL;
2N/A
2N/A code = krb5_mk_error(context, &krb_error_data, &scratch);
2N/A if (code)
2N/A goto done;
2N/A
2N/A tmsglen = scratch.length;
2N/A toktype = KG_TOK_CTX_ERROR;
2N/A
2N/A token.length = g_token_size(mech_used, tmsglen);
2N/A token.value = (unsigned char *) xmalloc(token.length);
2N/A if (!token.value)
2N/A goto done;
2N/A
2N/A ptr = token.value;
2N/A g_make_token_header(mech_used, tmsglen, &ptr, toktype);
2N/A
2N/A TWRITE_STR(ptr, scratch.data, scratch.length);
2N/A krb5_free_data_contents(context, &scratch);
2N/A
2N/A *output_token = token;
2N/A }
2N/A
2N/Adone:
2N/A
2N/A /* Solaris Kerberos */
2N/A if (krb_error_data.e_data.data != NULL)
2N/A free(krb_error_data.e_data.data);
2N/A
2N/A if (!verifier_cred_handle && cred_handle) {
2N/A krb5_gss_release_cred(&tmp_minor_status, &cred_handle);
2N/A }
2N/A
2N/A /*
2N/A * Solaris Kerberos
2N/A * Enhance the error message.
2N/A */
2N/A if (GSS_ERROR(major_status)) {
2N/A if (client_name && server_name &&
2N/A (*minor_status == (OM_uint32)KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
2N/A char *c_name = NULL;
2N/A char *s_name = NULL;
2N/A krb5_error_code cret, sret;
2N/A cret = krb5_unparse_name(context, (krb5_principal) client_name,
2N/A &c_name);
2N/A sret = krb5_unparse_name(context, (krb5_principal) server_name,
2N/A &s_name);
2N/A krb5_set_error_message(context, *minor_status,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Decrypt integrity check failed "
2N/A "for client '%s' and server '%s'"),
2N/A cret == 0 ? c_name : "unknown",
2N/A sret == 0 ? s_name : "unknown");
2N/A if (s_name)
2N/A krb5_free_unparsed_name(context, s_name);
2N/A if (c_name)
2N/A krb5_free_unparsed_name(context, c_name);
2N/A }
2N/A
2N/A }
2N/A if (client_name) {
2N/A krb5_free_principal(context, client_name);
2N/A }
2N/A if (server_name)
2N/A krb5_free_principal(context, server_name);
2N/A
2N/A if (context) {
2N/A /*
2N/A * Solaris Kerberos
2N/A * krb5_gss_acquire_cred() does not take a context arg
2N/A * (and does a save_error_info() itself) so re-calling
2N/A * save_error_info() here is trouble.
2N/A */
2N/A if (!acquire_fail && major_status && *minor_status)
2N/A save_error_info(*minor_status, context);
2N/A krb5_free_context(context);
2N/A }
2N/A return (major_status);
2N/A}
2N/A#endif /* LEAN_CLIENT */
2N/A
2N/AOM_uint32
2N/Akrb5_gss_accept_sec_context(minor_status, context_handle,
2N/A verifier_cred_handle, input_token,
2N/A input_chan_bindings, src_name, mech_type,
2N/A output_token, ret_flags, time_rec,
2N/A delegated_cred_handle)
2N/A OM_uint32 *minor_status;
2N/A gss_ctx_id_t *context_handle;
2N/A gss_cred_id_t verifier_cred_handle;
2N/A gss_buffer_t input_token;
2N/A gss_channel_bindings_t input_chan_bindings;
2N/A gss_name_t *src_name;
2N/A gss_OID *mech_type;
2N/A gss_buffer_t output_token;
2N/A OM_uint32 *ret_flags;
2N/A OM_uint32 *time_rec;
2N/A gss_cred_id_t *delegated_cred_handle;
2N/A{
2N/A krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
2N/A
2N/A /*
2N/A * Context handle must be unspecified. Actually, it must be
2N/A * non-established, but currently, accept_sec_context never returns
2N/A * a non-established context handle.
2N/A */
2N/A /*SUPPRESS 29*/
2N/A if (ctx != NULL) {
2N/A if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
2N/A return kg_accept_dce(minor_status, context_handle,
2N/A verifier_cred_handle, input_token,
2N/A input_chan_bindings, src_name, mech_type,
2N/A output_token, ret_flags, time_rec,
2N/A delegated_cred_handle);
2N/A } else {
2N/A *minor_status = EINVAL;
2N/A save_error_string(EINVAL, "accept_sec_context called with existing context handle");
2N/A /*
2N/A * Solaris kerberos: the original Solaris code returned
2N/A * GSS_S_NO_CONTEXT for this error. This conflicts somewhat with
2N/A * RFC2743 which states GSS_S_NO_CONTEXT should be returned only
2N/A * for sucessor calls following GSS_S_CONTINUE_NEEDED status
2N/A * returns. Note the MIT code doesn't return GSS_S_NO_CONTEXT at
2N/A * all.
2N/A */
2N/A return GSS_S_NO_CONTEXT;
2N/A }
2N/A }
2N/A
2N/A return kg_accept_krb5(minor_status, context_handle,
2N/A verifier_cred_handle, input_token,
2N/A input_chan_bindings, src_name, mech_type,
2N/A output_token, ret_flags, time_rec,
2N/A delegated_cred_handle);
2N/A}