Path: blob/main/crypto/krb5/src/lib/gssapi/spnego/spnego_mech.c
39563 views
/*1* Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.2* All rights reserved.3*4* Export of this software from the United States of America may5* require a specific license from the United States Government.6* It is the responsibility of any person or organization contemplating7* export to obtain such a license before exporting.8*9* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and10* distribute this software and its documentation for any purpose and11* without fee is hereby granted, provided that the above copyright12* notice appear in all copies and that both that copyright notice and13* this permission notice appear in supporting documentation, and that14* the name of M.I.T. not be used in advertising or publicity pertaining15* to distribution of the software without specific, written prior16* permission. Furthermore if you modify this software you must label17* your software as modified software and not distribute it in such a18* fashion that it might be confused with the original M.I.T. software.19* M.I.T. makes no representations about the suitability of20* this software for any purpose. It is provided "as is" without express21* or implied warranty.22*/23/*24* Copyright 2004 Sun Microsystems, Inc. All rights reserved.25* Use is subject to license terms.26*27* A module that implements the spnego security mechanism.28* It is used to negotiate the security mechanism between29* peers using the GSS-API. SPNEGO is specified in RFC 4178.30*31*/32/*33* Copyright (c) 2006-2008, Novell, Inc.34* All rights reserved.35*36* Redistribution and use in source and binary forms, with or without37* modification, are permitted provided that the following conditions are met:38*39* * Redistributions of source code must retain the above copyright notice,40* this list of conditions and the following disclaimer.41* * Redistributions in binary form must reproduce the above copyright42* notice, this list of conditions and the following disclaimer in the43* documentation and/or other materials provided with the distribution.44* * The copyright holder's name is not used to endorse or promote products45* derived from this software without specific prior written permission.46*47* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"48* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE49* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE50* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE51* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR52* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF53* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS54* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN55* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)56* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE57* POSSIBILITY OF SUCH DAMAGE.58*/59/* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */6061#include <k5-int.h>62#include <k5-der.h>63#include <krb5.h>64#include <mglueP.h>65#include "gssapiP_spnego.h"66#include <gssapi_err_generic.h>676869#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)70typedef const gss_OID_desc *gss_OID_const;7172/* private routines for spnego_mechanism */73static spnego_token_t make_spnego_token(const char *);74static gss_buffer_desc make_err_msg(const char *);75static int verify_token_header(struct k5input *, gss_OID_const);76static gss_OID get_mech_oid(OM_uint32 *minor_status, struct k5input *);77static gss_buffer_t get_octet_string(struct k5input *);78static gss_OID_set get_mech_set(OM_uint32 *, struct k5input *);79static OM_uint32 get_req_flags(struct k5input *, OM_uint32 *);80static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,81gss_const_key_value_set_t,82gss_cred_id_t *, gss_OID_set *,83OM_uint32 *);84static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_ctx_id_t,85spnego_gss_cred_id_t, gss_cred_usage_t);86static void release_spnego_ctx(spnego_gss_ctx_id_t *);87static spnego_gss_ctx_id_t create_spnego_ctx(int);88static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);8990static OM_uint3291process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,92gss_buffer_t *, OM_uint32 *, send_token_flag *);93static OM_uint3294handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,95gss_buffer_t *, OM_uint32 *, send_token_flag *);9697static OM_uint3298init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, send_token_flag *,99spnego_gss_ctx_id_t *);100static OM_uint32101init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,102gss_buffer_t *, gss_buffer_t *, send_token_flag *);103static OM_uint32104init_ctx_cont(OM_uint32 *, spnego_gss_ctx_id_t, gss_buffer_t,105gss_buffer_t *, gss_buffer_t *,106OM_uint32 *, send_token_flag *);107static OM_uint32108init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,109gss_OID, gss_buffer_t *, gss_buffer_t *, send_token_flag *);110static OM_uint32111init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,112OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,113gss_channel_bindings_t,114gss_buffer_t, OM_uint32 *, send_token_flag *);115116static OM_uint32117acc_ctx_new(OM_uint32 *, gss_buffer_t, spnego_gss_cred_id_t, gss_buffer_t *,118gss_buffer_t *, OM_uint32 *, send_token_flag *,119spnego_gss_ctx_id_t *);120static OM_uint32121acc_ctx_cont(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, gss_buffer_t *,122gss_buffer_t *, OM_uint32 *, send_token_flag *);123static OM_uint32124acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,125OM_uint32 *, send_token_flag *);126static OM_uint32127acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,128gss_buffer_t, gss_channel_bindings_t, gss_buffer_t,129OM_uint32 *, OM_uint32 *, send_token_flag *);130131static gss_OID132negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);133134static int135make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,136int,137gss_buffer_t,138OM_uint32, gss_buffer_t, send_token_flag,139gss_buffer_t);140static OM_uint32141make_spnego_tokenTarg_msg(uint8_t, gss_OID, gss_buffer_t,142gss_buffer_t, send_token_flag,143gss_buffer_t);144145static OM_uint32146get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,147gss_OID_set *, OM_uint32 *, gss_buffer_t *,148gss_buffer_t *);149static OM_uint32150get_negTokenResp(OM_uint32 *, struct k5input *, OM_uint32 *, gss_OID *,151gss_buffer_t *, gss_buffer_t *);152153static int154is_kerb_mech(gss_OID oid);155156/* SPNEGO oid structure */157static const gss_OID_desc spnego_oids[] = {158{SPNEGO_OID_LENGTH, SPNEGO_OID},159};160161const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;162static const gss_OID_set_desc spnego_oidsets[] = {163{1, (gss_OID) spnego_oids+0},164};165const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;166167static gss_OID_desc negoex_mech = { NEGOEX_OID_LENGTH, NEGOEX_OID };168169static int make_NegHints(OM_uint32 *, gss_buffer_t *);170static OM_uint32171acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *,172send_token_flag *, spnego_gss_ctx_id_t *);173174/*175* The Mech OID for SPNEGO:176* { iso(1) org(3) dod(6) internet(1) security(5)177* mechanism(5) spnego(2) }178*/179static struct gss_config spnego_mechanism =180{181{SPNEGO_OID_LENGTH, SPNEGO_OID},182NULL,183spnego_gss_acquire_cred,184spnego_gss_release_cred,185spnego_gss_init_sec_context,186#ifndef LEAN_CLIENT187spnego_gss_accept_sec_context,188#else189NULL,190#endif /* LEAN_CLIENT */191NULL, /* gss_process_context_token */192spnego_gss_delete_sec_context, /* gss_delete_sec_context */193spnego_gss_context_time, /* gss_context_time */194spnego_gss_get_mic, /* gss_get_mic */195spnego_gss_verify_mic, /* gss_verify_mic */196spnego_gss_wrap, /* gss_wrap */197spnego_gss_unwrap, /* gss_unwrap */198spnego_gss_display_status,199NULL, /* gss_indicate_mechs */200spnego_gss_compare_name,201spnego_gss_display_name,202spnego_gss_import_name,203spnego_gss_release_name,204spnego_gss_inquire_cred, /* gss_inquire_cred */205NULL, /* gss_add_cred */206#ifndef LEAN_CLIENT207spnego_gss_export_sec_context, /* gss_export_sec_context */208spnego_gss_import_sec_context, /* gss_import_sec_context */209#else210NULL, /* gss_export_sec_context */211NULL, /* gss_import_sec_context */212#endif /* LEAN_CLIENT */213NULL, /* gss_inquire_cred_by_mech */214spnego_gss_inquire_names_for_mech,215spnego_gss_inquire_context, /* gss_inquire_context */216NULL, /* gss_internal_release_oid */217spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */218spnego_gss_localname,219NULL, /* gss_userok */220NULL, /* gss_export_name */221spnego_gss_duplicate_name, /* gss_duplicate_name */222NULL, /* gss_store_cred */223spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */224spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */225spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */226spnego_gss_set_cred_option, /* gssspi_set_cred_option */227NULL, /* gssspi_mech_invoke */228spnego_gss_wrap_aead,229spnego_gss_unwrap_aead,230spnego_gss_wrap_iov,231spnego_gss_unwrap_iov,232spnego_gss_wrap_iov_length,233spnego_gss_complete_auth_token,234spnego_gss_acquire_cred_impersonate_name,235NULL, /* gss_add_cred_impersonate_name */236spnego_gss_display_name_ext,237spnego_gss_inquire_name,238spnego_gss_get_name_attribute,239spnego_gss_set_name_attribute,240spnego_gss_delete_name_attribute,241spnego_gss_export_name_composite,242spnego_gss_map_name_to_any,243spnego_gss_release_any_name_mapping,244spnego_gss_pseudo_random,245spnego_gss_set_neg_mechs,246spnego_gss_inquire_saslname_for_mech,247spnego_gss_inquire_mech_for_saslname,248spnego_gss_inquire_attrs_for_mech,249spnego_gss_acquire_cred_from,250NULL, /* gss_store_cred_into */251spnego_gss_acquire_cred_with_password,252spnego_gss_export_cred,253spnego_gss_import_cred,254NULL, /* gssspi_import_sec_context_by_mech */255NULL, /* gssspi_import_name_by_mech */256NULL, /* gssspi_import_cred_by_mech */257spnego_gss_get_mic_iov,258spnego_gss_verify_mic_iov,259spnego_gss_get_mic_iov_length260};261262#ifdef _GSS_STATIC_LINK263#include "mglueP.h"264265static int gss_spnegomechglue_init(void)266{267struct gss_mech_config mech_spnego;268269memset(&mech_spnego, 0, sizeof(mech_spnego));270mech_spnego.mech = &spnego_mechanism;271mech_spnego.mechNameStr = "spnego";272mech_spnego.mech_type = GSS_C_NO_OID;273274return gssint_register_mechinfo(&mech_spnego);275}276#else277gss_mechanism KRB5_CALLCONV278gss_mech_initialize(void)279{280return (&spnego_mechanism);281}282283MAKE_INIT_FUNCTION(gss_krb5int_lib_init);284MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);285int gss_krb5int_lib_init(void);286#endif /* _GSS_STATIC_LINK */287288int gss_spnegoint_lib_init(void)289{290int err;291292err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL);293if (err)294return err;295296#ifdef _GSS_STATIC_LINK297return gss_spnegomechglue_init();298#else299return 0;300#endif301}302303void gss_spnegoint_lib_fini(void)304{305k5_key_delete(K5_KEY_GSS_SPNEGO_STATUS);306}307308static OM_uint32309create_spnego_cred(OM_uint32 *minor_status, gss_cred_id_t mcred,310spnego_gss_cred_id_t *cred_out)311{312spnego_gss_cred_id_t spcred;313314*cred_out = NULL;315spcred = calloc(1, sizeof(*spcred));316if (spcred == NULL) {317*minor_status = ENOMEM;318return GSS_S_FAILURE;319}320spcred->mcred = mcred;321*cred_out = spcred;322return GSS_S_COMPLETE;323}324325/*ARGSUSED*/326OM_uint32 KRB5_CALLCONV327spnego_gss_acquire_cred(OM_uint32 *minor_status,328gss_name_t desired_name,329OM_uint32 time_req,330gss_OID_set desired_mechs,331gss_cred_usage_t cred_usage,332gss_cred_id_t *output_cred_handle,333gss_OID_set *actual_mechs,334OM_uint32 *time_rec)335{336return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,337desired_mechs, cred_usage, NULL,338output_cred_handle, actual_mechs,339time_rec);340}341342/*ARGSUSED*/343OM_uint32 KRB5_CALLCONV344spnego_gss_acquire_cred_from(OM_uint32 *minor_status,345const gss_name_t desired_name,346OM_uint32 time_req,347const gss_OID_set desired_mechs,348gss_cred_usage_t cred_usage,349gss_const_key_value_set_t cred_store,350gss_cred_id_t *output_cred_handle,351gss_OID_set *actual_mechs,352OM_uint32 *time_rec)353{354OM_uint32 status, tmpmin;355gss_OID_set amechs;356gss_cred_id_t mcred = NULL;357spnego_gss_cred_id_t spcred = NULL;358dsyslog("Entering spnego_gss_acquire_cred\n");359360if (actual_mechs)361*actual_mechs = NULL;362363if (time_rec)364*time_rec = 0;365366/* We will obtain a mechglue credential and wrap it in a367* spnego_gss_cred_id_rec structure. Allocate the wrapper. */368status = create_spnego_cred(minor_status, mcred, &spcred);369if (status != GSS_S_COMPLETE)370return (status);371372/*373* Always use get_available_mechs to collect a list of374* mechs for which creds are available.375*/376status = get_available_mechs(minor_status, desired_name,377cred_usage, cred_store, &mcred,378&amechs, time_rec);379380if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {381(void) generic_gss_copy_oid_set(&tmpmin, amechs, actual_mechs);382}383(void) gss_release_oid_set(&tmpmin, &amechs);384385if (status == GSS_S_COMPLETE) {386spcred->mcred = mcred;387*output_cred_handle = (gss_cred_id_t)spcred;388} else {389free(spcred);390*output_cred_handle = GSS_C_NO_CREDENTIAL;391}392393dsyslog("Leaving spnego_gss_acquire_cred\n");394return (status);395}396397/*ARGSUSED*/398OM_uint32 KRB5_CALLCONV399spnego_gss_release_cred(OM_uint32 *minor_status,400gss_cred_id_t *cred_handle)401{402spnego_gss_cred_id_t spcred = NULL;403404dsyslog("Entering spnego_gss_release_cred\n");405406if (minor_status == NULL || cred_handle == NULL)407return (GSS_S_CALL_INACCESSIBLE_WRITE);408409*minor_status = 0;410411if (*cred_handle == GSS_C_NO_CREDENTIAL)412return (GSS_S_COMPLETE);413414spcred = (spnego_gss_cred_id_t)*cred_handle;415*cred_handle = GSS_C_NO_CREDENTIAL;416gss_release_oid_set(minor_status, &spcred->neg_mechs);417gss_release_cred(minor_status, &spcred->mcred);418free(spcred);419420dsyslog("Leaving spnego_gss_release_cred\n");421return (GSS_S_COMPLETE);422}423424static spnego_gss_ctx_id_t425create_spnego_ctx(int initiate)426{427spnego_gss_ctx_id_t spnego_ctx = NULL;428429spnego_ctx = malloc(sizeof(*spnego_ctx));430if (spnego_ctx == NULL) {431return (NULL);432}433434spnego_ctx->magic_num = SPNEGO_MAGIC_ID;435spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;436spnego_ctx->mech_set = NULL;437spnego_ctx->internal_mech = NULL;438spnego_ctx->DER_mechTypes.length = 0;439spnego_ctx->DER_mechTypes.value = NULL;440spnego_ctx->mic_reqd = 0;441spnego_ctx->mic_sent = 0;442spnego_ctx->mic_rcvd = 0;443spnego_ctx->mech_complete = 0;444spnego_ctx->nego_done = 0;445spnego_ctx->opened = 0;446spnego_ctx->initiate = initiate;447spnego_ctx->internal_name = GSS_C_NO_NAME;448spnego_ctx->actual_mech = GSS_C_NO_OID;449spnego_ctx->deleg_cred = GSS_C_NO_CREDENTIAL;450spnego_ctx->negoex_step = 0;451memset(&spnego_ctx->negoex_transcript, 0, sizeof(struct k5buf));452spnego_ctx->negoex_seqnum = 0;453K5_TAILQ_INIT(&spnego_ctx->negoex_mechs);454spnego_ctx->kctx = NULL;455memset(spnego_ctx->negoex_conv_id, 0, GUID_LENGTH);456457return (spnego_ctx);458}459460/* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)461* gssntlmssp(655) controls(1) spnego_req_mechlistMIC(2) */462static const gss_OID_desc spnego_req_mechlistMIC_oid =463{ 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x02" };464465/*466* Return nonzero if the mechanism has reason to believe that a mechlistMIC467* exchange will be required. Microsoft servers erroneously require SPNEGO468* mechlistMIC if they see an internal MIC within an NTLMSSP Authenticate469* message, even if NTLMSSP was the preferred mechanism.470*/471static int472mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)473{474OM_uint32 major, minor;475gss_ctx_id_t ctx = sc->ctx_handle;476gss_OID oid = (gss_OID)&spnego_req_mechlistMIC_oid;477gss_buffer_set_t bufs;478int result;479480major = gss_inquire_sec_context_by_oid(&minor, ctx, oid, &bufs);481if (major != GSS_S_COMPLETE)482return 0;483484/* Report true if the mech returns a single buffer containing a single485* byte with value 1. */486result = (bufs != NULL && bufs->count == 1 &&487bufs->elements[0].length == 1 &&488memcmp(bufs->elements[0].value, "\1", 1) == 0);489(void) gss_release_buffer_set(&minor, &bufs);490return result;491}492493/* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) Microsoft(311)494* security(2) mechanisms(2) NTLM(10) */495static const gss_OID_desc gss_mech_ntlmssp_oid =496{ 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };497498/* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)499* gssntlmssp(655) controls(1) ntlmssp_reset_crypto(3) */500static const gss_OID_desc ntlmssp_reset_crypto_oid =501{ 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x03" };502503/*504* MS-SPNG section 3.3.5.1 warns that the NTLM mechanism requires special505* handling of the crypto state to interop with Windows. If the mechanism for506* sc is SPNEGO, invoke a mechanism-specific operation on the context to reset507* the RC4 state after producing or verifying a MIC. Ignore a result of508* GSS_S_UNAVAILABLE for compatibility with older versions of the mechanism509* that do not support this functionality.510*/511static OM_uint32512ntlmssp_reset_crypto_state(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,513OM_uint32 verify)514{515OM_uint32 major, minor;516gss_buffer_desc value;517518if (!g_OID_equal(sc->internal_mech, &gss_mech_ntlmssp_oid))519return GSS_S_COMPLETE;520521value.length = sizeof(verify);522value.value = &verify;523major = gss_set_sec_context_option(&minor, &sc->ctx_handle,524(gss_OID)&ntlmssp_reset_crypto_oid,525&value);526if (major == GSS_S_UNAVAILABLE)527return GSS_S_COMPLETE;528*minor_status = minor;529return major;530}531532/*533* Both initiator and acceptor call here to verify and/or create mechListMIC,534* and to consistency-check the MIC state. handle_mic is invoked only if the535* negotiated mech has completed and supports MICs.536*/537static OM_uint32538handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,539int send_mechtok, spnego_gss_ctx_id_t sc,540gss_buffer_t *mic_out,541OM_uint32 *negState, send_token_flag *tokflag)542{543OM_uint32 ret;544545ret = GSS_S_FAILURE;546*mic_out = GSS_C_NO_BUFFER;547if (mic_in != GSS_C_NO_BUFFER) {548if (sc->mic_rcvd) {549/* Reject MIC if we've already received a MIC. */550*negState = REJECT;551*tokflag = ERROR_TOKEN_SEND;552return GSS_S_DEFECTIVE_TOKEN;553}554} else if (sc->mic_reqd && !send_mechtok) {555/*556* If the peer sends the final mechanism token, it557* must send the MIC with that token if the558* negotiation requires MICs.559*/560*negState = REJECT;561*tokflag = ERROR_TOKEN_SEND;562return GSS_S_DEFECTIVE_TOKEN;563}564ret = process_mic(minor_status, mic_in, sc, mic_out,565negState, tokflag);566if (ret != GSS_S_COMPLETE) {567return ret;568}569if (sc->mic_reqd) {570assert(sc->mic_sent || sc->mic_rcvd);571}572if (sc->mic_sent && sc->mic_rcvd) {573ret = GSS_S_COMPLETE;574*negState = ACCEPT_COMPLETE;575if (*mic_out == GSS_C_NO_BUFFER) {576/*577* We sent a MIC on the previous pass; we578* shouldn't be sending a mechanism token.579*/580assert(!send_mechtok);581*tokflag = NO_TOKEN_SEND;582} else {583*tokflag = CONT_TOKEN_SEND;584}585} else if (sc->mic_reqd) {586*negState = ACCEPT_INCOMPLETE;587ret = GSS_S_CONTINUE_NEEDED;588} else if (*negState == ACCEPT_COMPLETE) {589ret = GSS_S_COMPLETE;590} else {591ret = GSS_S_CONTINUE_NEEDED;592}593return ret;594}595596/*597* Perform the actual verification and/or generation of mechListMIC.598*/599static OM_uint32600process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,601spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,602OM_uint32 *negState, send_token_flag *tokflag)603{604OM_uint32 ret, tmpmin;605gss_qop_t qop_state;606gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;607608ret = GSS_S_FAILURE;609if (mic_in != GSS_C_NO_BUFFER) {610ret = gss_verify_mic(minor_status, sc->ctx_handle,611&sc->DER_mechTypes,612mic_in, &qop_state);613if (ret == GSS_S_COMPLETE)614ret = ntlmssp_reset_crypto_state(minor_status, sc, 1);615if (ret != GSS_S_COMPLETE) {616*negState = REJECT;617*tokflag = ERROR_TOKEN_SEND;618return ret;619}620/* If we got a MIC, we must send a MIC. */621sc->mic_reqd = 1;622sc->mic_rcvd = 1;623}624if (sc->mic_reqd && !sc->mic_sent) {625ret = gss_get_mic(minor_status, sc->ctx_handle,626GSS_C_QOP_DEFAULT,627&sc->DER_mechTypes,628&tmpmic);629if (ret == GSS_S_COMPLETE)630ret = ntlmssp_reset_crypto_state(minor_status, sc, 0);631if (ret != GSS_S_COMPLETE) {632gss_release_buffer(&tmpmin, &tmpmic);633*tokflag = NO_TOKEN_SEND;634return ret;635}636*mic_out = malloc(sizeof(gss_buffer_desc));637if (*mic_out == GSS_C_NO_BUFFER) {638gss_release_buffer(&tmpmin, &tmpmic);639*tokflag = NO_TOKEN_SEND;640return GSS_S_FAILURE;641}642**mic_out = tmpmic;643sc->mic_sent = 1;644}645return GSS_S_COMPLETE;646}647648/* Create a new SPNEGO context handle for the initial call to649* spnego_gss_init_sec_context(). */650static OM_uint32651init_ctx_new(OM_uint32 *minor_status,652spnego_gss_cred_id_t spcred,653send_token_flag *tokflag,654spnego_gss_ctx_id_t *sc_out)655{656OM_uint32 ret;657spnego_gss_ctx_id_t sc = NULL;658659*sc_out = NULL;660661sc = create_spnego_ctx(1);662if (sc == NULL)663return GSS_S_FAILURE;664665/* determine negotiation mech set */666ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_INITIATE);667if (ret != GSS_S_COMPLETE)668goto cleanup;669670/* Set an initial internal mech to make the first context token. */671sc->internal_mech = &sc->mech_set->elements[0];672673if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {674ret = GSS_S_FAILURE;675goto cleanup;676}677678sc->ctx_handle = GSS_C_NO_CONTEXT;679*sc_out = sc;680sc = NULL;681*tokflag = INIT_TOKEN_SEND;682ret = GSS_S_COMPLETE;683684cleanup:685release_spnego_ctx(&sc);686return ret;687}688689/*690* Called by second and later calls to spnego_gss_init_sec_context()691* to decode reply and update state.692*/693static OM_uint32694init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,695gss_buffer_t buf, gss_buffer_t *responseToken,696gss_buffer_t *mechListMIC, OM_uint32 *acc_negState,697send_token_flag *tokflag)698{699OM_uint32 ret, tmpmin;700gss_OID supportedMech = GSS_C_NO_OID;701struct k5input in;702703*acc_negState = UNSPECIFIED;704*tokflag = ERROR_TOKEN_SEND;705706k5_input_init(&in, buf->value, buf->length);707ret = get_negTokenResp(minor_status, &in, acc_negState, &supportedMech,708responseToken, mechListMIC);709if (ret != GSS_S_COMPLETE)710goto cleanup;711712/* Bail out now on a reject with no error token. If we have an error713* token, keep going and get a better error status from the mech. */714if (*acc_negState == REJECT && *responseToken == GSS_C_NO_BUFFER) {715if (!sc->nego_done) {716/* RFC 4178 says to return GSS_S_BAD_MECH on a717* mechanism negotiation failure. */718*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;719map_errcode(minor_status);720ret = GSS_S_BAD_MECH;721} else {722ret = GSS_S_FAILURE;723}724*tokflag = NO_TOKEN_SEND;725goto cleanup;726}727/*728* nego_done is false for the first call to init_ctx_cont()729*/730if (!sc->nego_done) {731ret = init_ctx_nego(minor_status, sc, *acc_negState,732supportedMech, responseToken, mechListMIC,733tokflag);734} else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||735(sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {736/* Missing or spurious token from acceptor. */737ret = GSS_S_DEFECTIVE_TOKEN;738} else if (!sc->mech_complete ||739(sc->mic_reqd &&740(sc->ctx_flags & GSS_C_INTEG_FLAG))) {741/* Not obviously done; we may decide we're done later in742* init_ctx_call_init or handle_mic. */743*tokflag = CONT_TOKEN_SEND;744ret = GSS_S_COMPLETE;745} else {746/* mech finished on last pass and no MIC required, so done. */747*tokflag = NO_TOKEN_SEND;748ret = GSS_S_COMPLETE;749}750cleanup:751if (supportedMech != GSS_C_NO_OID)752generic_gss_release_oid(&tmpmin, &supportedMech);753return ret;754}755756/*757* Consistency checking and mechanism negotiation handling for second758* call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to759* update internal state if acceptor has counter-proposed.760*/761static OM_uint32762init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,763OM_uint32 acc_negState, gss_OID supportedMech,764gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,765send_token_flag *tokflag)766{767OM_uint32 ret;768769*tokflag = ERROR_TOKEN_SEND;770ret = GSS_S_DEFECTIVE_TOKEN;771772/*773* According to RFC 4178, both supportedMech and negState must be774* present in the first acceptor token. However, some Java775* implementations include only a responseToken in the first776* NegTokenResp. In this case we can use sc->internal_mech as the777* negotiated mechanism. (We do not currently look at acc_negState778* when continuing with the optimistic mechanism.)779*/780if (supportedMech == GSS_C_NO_OID)781supportedMech = sc->internal_mech;782783/*784* If the mechanism we sent is not the mechanism returned from785* the server, we need to handle the server's counter786* proposal. There is a bug in SAMBA servers that always send787* the old Kerberos mech OID, even though we sent the new one.788* So we will treat all the Kerberos mech OIDS as the same.789*/790if (!(is_kerb_mech(supportedMech) &&791is_kerb_mech(sc->internal_mech)) &&792!g_OID_equal(supportedMech, sc->internal_mech)) {793ret = init_ctx_reselect(minor_status, sc,794acc_negState, supportedMech,795responseToken, mechListMIC, tokflag);796797} else if (*responseToken == GSS_C_NO_BUFFER) {798if (sc->mech_complete) {799/*800* Mech completed on first call to its801* init_sec_context(). Acceptor sends no mech802* token.803*/804*tokflag = NO_TOKEN_SEND;805ret = GSS_S_COMPLETE;806} else {807/*808* Reject missing mech token when optimistic809* mech selected.810*/811*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;812map_errcode(minor_status);813ret = GSS_S_DEFECTIVE_TOKEN;814}815} else if ((*responseToken)->length == 0 && sc->mech_complete) {816/* Handle old IIS servers returning empty token instead of817* null tokens in the non-mutual auth case. */818*tokflag = NO_TOKEN_SEND;819ret = GSS_S_COMPLETE;820} else if (sc->mech_complete) {821/* Reject spurious mech token. */822ret = GSS_S_DEFECTIVE_TOKEN;823} else {824*tokflag = CONT_TOKEN_SEND;825ret = GSS_S_COMPLETE;826}827sc->nego_done = 1;828return ret;829}830831/*832* Handle acceptor's counter-proposal of an alternative mechanism.833*/834static OM_uint32835init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,836OM_uint32 acc_negState, gss_OID supportedMech,837gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,838send_token_flag *tokflag)839{840OM_uint32 tmpmin;841size_t i;842843gss_delete_sec_context(&tmpmin, &sc->ctx_handle,844GSS_C_NO_BUFFER);845846/* Find supportedMech in sc->mech_set. */847for (i = 0; i < sc->mech_set->count; i++) {848if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))849break;850}851if (i == sc->mech_set->count)852return GSS_S_DEFECTIVE_TOKEN;853sc->internal_mech = &sc->mech_set->elements[i];854855/*856* A server conforming to RFC4178 MUST set REQUEST_MIC here, but857* Windows Server 2003 and earlier implement (roughly) RFC2478 instead,858* and send ACCEPT_INCOMPLETE. Tolerate that only if we are falling859* back to NTLMSSP.860*/861if (acc_negState == ACCEPT_INCOMPLETE) {862if (!g_OID_equal(supportedMech, &gss_mech_ntlmssp_oid))863return GSS_S_DEFECTIVE_TOKEN;864} else if (acc_negState != REQUEST_MIC) {865return GSS_S_DEFECTIVE_TOKEN;866}867868sc->mech_complete = 0;869sc->mic_reqd = (acc_negState == REQUEST_MIC);870*tokflag = CONT_TOKEN_SEND;871return GSS_S_COMPLETE;872}873874/*875* Wrap call to mechanism gss_init_sec_context() and update state876* accordingly.877*/878static OM_uint32879init_ctx_call_init(OM_uint32 *minor_status,880spnego_gss_ctx_id_t sc,881spnego_gss_cred_id_t spcred,882OM_uint32 acc_negState,883gss_name_t target_name,884OM_uint32 req_flags,885OM_uint32 time_req,886gss_buffer_t mechtok_in,887gss_channel_bindings_t bindings,888gss_buffer_t mechtok_out,889OM_uint32 *time_rec,890send_token_flag *send_token)891{892OM_uint32 ret, tmpret, tmpmin, mech_req_flags;893gss_cred_id_t mcred;894895mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;896897mech_req_flags = req_flags;898if (spcred == NULL || !spcred->no_ask_integ)899mech_req_flags |= GSS_C_INTEG_FLAG;900901if (gss_oid_equal(sc->internal_mech, &negoex_mech)) {902ret = negoex_init(minor_status, sc, mcred, target_name,903mech_req_flags, time_req, mechtok_in,904bindings, mechtok_out, time_rec);905} else {906ret = gss_init_sec_context(minor_status, mcred,907&sc->ctx_handle, target_name,908sc->internal_mech, mech_req_flags,909time_req, bindings, mechtok_in,910&sc->actual_mech, mechtok_out,911&sc->ctx_flags, time_rec);912}913914/* Bail out if the acceptor gave us an error token but the mech didn't915* see it as an error. */916if (acc_negState == REJECT && !GSS_ERROR(ret)) {917ret = GSS_S_DEFECTIVE_TOKEN;918goto fail;919}920921if (ret == GSS_S_COMPLETE) {922sc->mech_complete = 1;923/*924* Microsoft SPNEGO implementations expect an even number of925* token exchanges. So if we're sending a final token, ask for926* a zero-length token back from the server. Also ask for a927* token back if this is the first token or if a MIC exchange928* is required.929*/930if (*send_token == CONT_TOKEN_SEND &&931mechtok_out->length == 0 &&932(!sc->mic_reqd || !(sc->ctx_flags & GSS_C_INTEG_FLAG)))933*send_token = NO_TOKEN_SEND;934935return GSS_S_COMPLETE;936}937938if (ret == GSS_S_CONTINUE_NEEDED)939return GSS_S_COMPLETE;940941if (*send_token != INIT_TOKEN_SEND) {942*send_token = ERROR_TOKEN_SEND;943return ret;944}945946/*947* Since this is the first token, we can fall back to later mechanisms948* in the list. Since the mechanism list is expected to be short, we949* can do this with recursion. If all mechanisms produce errors, the950* caller should get the error from the first mech in the list.951*/952gssalloc_free(sc->mech_set->elements->elements);953memmove(sc->mech_set->elements, sc->mech_set->elements + 1,954--sc->mech_set->count * sizeof(*sc->mech_set->elements));955if (sc->mech_set->count == 0)956goto fail;957gss_release_buffer(&tmpmin, &sc->DER_mechTypes);958if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)959goto fail;960gss_delete_sec_context(&tmpmin, &sc->ctx_handle, GSS_C_NO_BUFFER);961tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState,962target_name, req_flags, time_req,963mechtok_in, bindings, mechtok_out,964time_rec, send_token);965if (HARD_ERROR(tmpret))966goto fail;967*minor_status = tmpmin;968return tmpret;969970fail:971/* Don't output token on error from first call. */972*send_token = NO_TOKEN_SEND;973return ret;974}975976/*ARGSUSED*/977OM_uint32 KRB5_CALLCONV978spnego_gss_init_sec_context(979OM_uint32 *minor_status,980gss_cred_id_t claimant_cred_handle,981gss_ctx_id_t *context_handle,982gss_name_t target_name,983gss_OID mech_type,984OM_uint32 req_flags,985OM_uint32 time_req,986gss_channel_bindings_t bindings,987gss_buffer_t input_token,988gss_OID *actual_mech,989gss_buffer_t output_token,990OM_uint32 *ret_flags,991OM_uint32 *time_rec)992{993send_token_flag send_token = NO_TOKEN_SEND;994OM_uint32 tmpmin, ret, negState = UNSPECIFIED, acc_negState;995gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;996gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;997spnego_gss_cred_id_t spcred = NULL;998spnego_gss_ctx_id_t spnego_ctx = NULL;9991000dsyslog("Entering init_sec_context\n");10011002mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;10031004/*1005* This function works in three steps:1006*1007* 1. Perform mechanism negotiation.1008* 2. Invoke the negotiated or optimistic mech's gss_init_sec_context1009* function and examine the results.1010* 3. Process or generate MICs if necessary.1011*1012* The three steps share responsibility for determining when the1013* exchange is complete. If the selected mech completed in a previous1014* call and no MIC exchange is expected, then step 1 will decide. If1015* the selected mech completes in this call and no MIC exchange is1016* expected, then step 2 will decide. If a MIC exchange is expected,1017* then step 3 will decide. If an error occurs in any step, the1018* exchange will be aborted, possibly with an error token.1019*1020* negState determines the state of the negotiation, and is1021* communicated to the acceptor if a continuing token is sent.1022* send_token is used to indicate what type of token, if any, should be1023* generated.1024*/10251026/* Validate arguments. */1027if (minor_status != NULL)1028*minor_status = 0;1029if (output_token != GSS_C_NO_BUFFER) {1030output_token->length = 0;1031output_token->value = NULL;1032}1033if (minor_status == NULL ||1034output_token == GSS_C_NO_BUFFER ||1035context_handle == NULL)1036return GSS_S_CALL_INACCESSIBLE_WRITE;10371038if (actual_mech != NULL)1039*actual_mech = GSS_C_NO_OID;1040if (time_rec != NULL)1041*time_rec = 0;10421043/* Step 1: perform mechanism negotiation. */1044spcred = (spnego_gss_cred_id_t)claimant_cred_handle;1045spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;1046if (spnego_ctx == NULL) {1047ret = init_ctx_new(minor_status, spcred, &send_token,1048&spnego_ctx);1049if (ret != GSS_S_COMPLETE)1050goto cleanup;1051*context_handle = (gss_ctx_id_t)spnego_ctx;1052acc_negState = UNSPECIFIED;1053} else {1054ret = init_ctx_cont(minor_status, spnego_ctx, input_token,1055&mechtok_in, &mechListMIC_in,1056&acc_negState, &send_token);1057if (ret != GSS_S_COMPLETE)1058goto cleanup;1059}10601061/* Step 2: invoke the selected or optimistic mechanism's1062* gss_init_sec_context function, if it didn't complete previously. */1063if (!spnego_ctx->mech_complete) {1064ret = init_ctx_call_init(minor_status, spnego_ctx, spcred,1065acc_negState, target_name, req_flags,1066time_req, mechtok_in, bindings,1067&mechtok_out, time_rec, &send_token);1068if (ret != GSS_S_COMPLETE)1069goto cleanup;10701071/* Give the mechanism a chance to force a mechlistMIC. */1072if (mech_requires_mechlistMIC(spnego_ctx))1073spnego_ctx->mic_reqd = 1;1074}10751076/* Step 3: process or generate the MIC, if the negotiated mech is1077* complete and supports MICs. Also decide the outgoing negState. */1078negState = ACCEPT_INCOMPLETE;1079if (spnego_ctx->mech_complete &&1080(spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {10811082ret = handle_mic(minor_status,1083mechListMIC_in,1084(mechtok_out.length != 0),1085spnego_ctx, &mechListMIC_out,1086&negState, &send_token);1087if (HARD_ERROR(ret))1088goto cleanup;1089}10901091if (ret_flags != NULL)1092*ret_flags = spnego_ctx->ctx_flags & ~GSS_C_PROT_READY_FLAG;10931094ret = (send_token == NO_TOKEN_SEND || negState == ACCEPT_COMPLETE) ?1095GSS_S_COMPLETE : GSS_S_CONTINUE_NEEDED;10961097cleanup:1098if (send_token == INIT_TOKEN_SEND) {1099if (make_spnego_tokenInit_msg(spnego_ctx,11000,1101mechListMIC_out,1102req_flags,1103&mechtok_out, send_token,1104output_token) < 0) {1105ret = GSS_S_FAILURE;1106}1107} else if (send_token != NO_TOKEN_SEND) {1108if (send_token == ERROR_TOKEN_SEND)1109negState = REJECT;1110if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,1111&mechtok_out, mechListMIC_out,1112send_token,1113output_token) < 0) {1114ret = GSS_S_FAILURE;1115}1116}1117gss_release_buffer(&tmpmin, &mechtok_out);1118if (ret == GSS_S_COMPLETE) {1119spnego_ctx->opened = 1;1120if (actual_mech != NULL)1121*actual_mech = spnego_ctx->actual_mech;1122/* Get an updated lifetime if we didn't call into the mech. */1123if (time_rec != NULL && *time_rec == 0) {1124(void) gss_context_time(&tmpmin,1125spnego_ctx->ctx_handle,1126time_rec);1127}1128} else if (ret != GSS_S_CONTINUE_NEEDED) {1129if (spnego_ctx != NULL) {1130gss_delete_sec_context(&tmpmin,1131&spnego_ctx->ctx_handle,1132GSS_C_NO_BUFFER);1133release_spnego_ctx(&spnego_ctx);1134}1135*context_handle = GSS_C_NO_CONTEXT;1136}1137if (mechtok_in != GSS_C_NO_BUFFER) {1138gss_release_buffer(&tmpmin, mechtok_in);1139free(mechtok_in);1140}1141if (mechListMIC_in != GSS_C_NO_BUFFER) {1142gss_release_buffer(&tmpmin, mechListMIC_in);1143free(mechListMIC_in);1144}1145if (mechListMIC_out != GSS_C_NO_BUFFER) {1146gss_release_buffer(&tmpmin, mechListMIC_out);1147free(mechListMIC_out);1148}1149return ret;1150} /* init_sec_context */11511152/* We don't want to import KRB5 headers here */1153static const gss_OID_desc gss_mech_krb5_oid =1154{ 9, "\052\206\110\206\367\022\001\002\002" };1155static const gss_OID_desc gss_mech_krb5_wrong_oid =1156{ 9, "\052\206\110\202\367\022\001\002\002" };11571158/*1159* NegHints ::= SEQUENCE {1160* hintName [0] GeneralString OPTIONAL,1161* hintAddress [1] OCTET STRING OPTIONAL1162* }1163*/11641165#define HOST_PREFIX "host@"1166#define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)11671168/* Encode the dummy hintname string (as specified in [MS-SPNG]) into a1169* DER-encoded [0] tagged GeneralString, and place the result in *outbuf. */1170static int1171make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf)1172{1173OM_uint32 major_status;1174size_t hint_len, tlen;1175uint8_t *t;1176const char *hintname = "not_defined_in_RFC4178@please_ignore";1177const size_t hintname_len = strlen(hintname);1178struct k5buf buf;11791180*outbuf = GSS_C_NO_BUFFER;1181major_status = GSS_S_FAILURE;11821183hint_len = k5_der_value_len(hintname_len);1184tlen = k5_der_value_len(hint_len);11851186t = gssalloc_malloc(tlen);1187if (t == NULL) {1188*minor_status = ENOMEM;1189goto errout;1190}1191k5_buf_init_fixed(&buf, t, tlen);11921193k5_der_add_taglen(&buf, CONTEXT | 0x00, hint_len);1194k5_der_add_value(&buf, GENERAL_STRING, hintname, hintname_len);1195assert(buf.len == tlen);11961197*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));1198if (*outbuf == NULL) {1199*minor_status = ENOMEM;1200goto errout;1201}1202(*outbuf)->value = (void *)t;1203(*outbuf)->length = tlen;12041205t = NULL; /* don't free */12061207*minor_status = 0;1208major_status = GSS_S_COMPLETE;12091210errout:1211if (t != NULL) {1212free(t);1213}12141215return (major_status);1216}12171218/*1219* Create a new SPNEGO context handle for the initial call to1220* spnego_gss_accept_sec_context() when the request is empty. For empty1221* requests, we implement the Microsoft NegHints extension to SPNEGO for1222* compatibility with some versions of Samba. See:1223* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/8e71cf53-e867-4b79-b5b5-38c92be3d4721224*/1225static OM_uint321226acc_ctx_hints(OM_uint32 *minor_status,1227spnego_gss_cred_id_t spcred,1228gss_buffer_t *mechListMIC,1229OM_uint32 *negState,1230send_token_flag *return_token,1231spnego_gss_ctx_id_t *sc_out)1232{1233OM_uint32 ret;1234spnego_gss_ctx_id_t sc = NULL;12351236*mechListMIC = GSS_C_NO_BUFFER;1237*return_token = NO_TOKEN_SEND;1238*negState = REJECT;1239*minor_status = 0;1240*sc_out = NULL;12411242ret = make_NegHints(minor_status, mechListMIC);1243if (ret != GSS_S_COMPLETE)1244goto cleanup;12451246sc = create_spnego_ctx(0);1247if (sc == NULL) {1248ret = GSS_S_FAILURE;1249goto cleanup;1250}12511252ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);1253if (ret != GSS_S_COMPLETE)1254goto cleanup;12551256if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {1257ret = GSS_S_FAILURE;1258goto cleanup;1259}1260sc->internal_mech = GSS_C_NO_OID;12611262*negState = ACCEPT_INCOMPLETE;1263*return_token = INIT_TOKEN_SEND;1264sc->firstpass = 1;1265*sc_out = sc;1266sc = NULL;1267ret = GSS_S_COMPLETE;12681269cleanup:1270release_spnego_ctx(&sc);12711272return ret;1273}12741275/*1276* Create a new SPNEGO context handle for the initial call to1277* spnego_gss_accept_sec_context(). Set negState to REJECT if the token is1278* defective, else ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether1279* the initiator's preferred mechanism is supported.1280*/1281static OM_uint321282acc_ctx_new(OM_uint32 *minor_status,1283gss_buffer_t buf,1284spnego_gss_cred_id_t spcred,1285gss_buffer_t *mechToken,1286gss_buffer_t *mechListMIC,1287OM_uint32 *negState,1288send_token_flag *return_token,1289spnego_gss_ctx_id_t *sc_out)1290{1291OM_uint32 tmpmin, ret, req_flags;1292gss_OID_set mechTypes;1293gss_buffer_desc der_mechTypes;1294gss_OID mech_wanted;1295spnego_gss_ctx_id_t sc = NULL;12961297ret = GSS_S_DEFECTIVE_TOKEN;1298der_mechTypes.length = 0;1299der_mechTypes.value = NULL;1300*mechToken = *mechListMIC = GSS_C_NO_BUFFER;1301mechTypes = GSS_C_NO_OID_SET;1302*return_token = ERROR_TOKEN_SEND;1303*negState = REJECT;1304*minor_status = 0;13051306ret = get_negTokenInit(minor_status, buf, &der_mechTypes,1307&mechTypes, &req_flags,1308mechToken, mechListMIC);1309if (ret != GSS_S_COMPLETE) {1310goto cleanup;1311}13121313sc = create_spnego_ctx(0);1314if (sc == NULL) {1315ret = GSS_S_FAILURE;1316*return_token = NO_TOKEN_SEND;1317goto cleanup;1318}13191320ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);1321if (ret != GSS_S_COMPLETE) {1322*return_token = NO_TOKEN_SEND;1323goto cleanup;1324}1325/*1326* Select the best match between the list of mechs1327* that the initiator requested and the list that1328* the acceptor will support.1329*/1330mech_wanted = negotiate_mech(sc, mechTypes, negState);1331if (*negState == REJECT) {1332ret = GSS_S_BAD_MECH;1333goto cleanup;1334}13351336sc->internal_mech = mech_wanted;1337sc->DER_mechTypes = der_mechTypes;1338der_mechTypes.length = 0;1339der_mechTypes.value = NULL;13401341if (*negState == REQUEST_MIC)1342sc->mic_reqd = 1;13431344*return_token = INIT_TOKEN_SEND;1345sc->firstpass = 1;1346*sc_out = sc;1347sc = NULL;1348ret = GSS_S_COMPLETE;13491350cleanup:1351release_spnego_ctx(&sc);1352gss_release_oid_set(&tmpmin, &mechTypes);1353if (der_mechTypes.length != 0)1354gss_release_buffer(&tmpmin, &der_mechTypes);13551356return ret;1357}13581359static OM_uint321360acc_ctx_cont(OM_uint32 *minstat,1361gss_buffer_t buf,1362spnego_gss_ctx_id_t sc,1363gss_buffer_t *responseToken,1364gss_buffer_t *mechListMIC,1365OM_uint32 *negState,1366send_token_flag *return_token)1367{1368OM_uint32 ret, tmpmin;1369gss_OID supportedMech;1370struct k5input in;13711372ret = GSS_S_DEFECTIVE_TOKEN;1373*negState = REJECT;1374*minstat = 0;1375supportedMech = GSS_C_NO_OID;1376*return_token = ERROR_TOKEN_SEND;1377*responseToken = *mechListMIC = GSS_C_NO_BUFFER;13781379k5_input_init(&in, buf->value, buf->length);13801381/* Attempt to work with old Sun SPNEGO. */1382if (in.len > 0 && *in.ptr == HEADER_ID) {1383ret = verify_token_header(&in, gss_mech_spnego);1384if (ret) {1385*minstat = ret;1386return GSS_S_DEFECTIVE_TOKEN;1387}1388}13891390ret = get_negTokenResp(minstat, &in, negState, &supportedMech,1391responseToken, mechListMIC);1392if (ret != GSS_S_COMPLETE)1393goto cleanup;13941395if (*responseToken == GSS_C_NO_BUFFER &&1396*mechListMIC == GSS_C_NO_BUFFER) {13971398ret = GSS_S_DEFECTIVE_TOKEN;1399goto cleanup;1400}1401if (supportedMech != GSS_C_NO_OID) {1402ret = GSS_S_DEFECTIVE_TOKEN;1403goto cleanup;1404}1405sc->firstpass = 0;1406*negState = ACCEPT_INCOMPLETE;1407*return_token = CONT_TOKEN_SEND;1408cleanup:1409if (supportedMech != GSS_C_NO_OID) {1410generic_gss_release_oid(&tmpmin, &supportedMech);1411}1412return ret;1413}14141415/*1416* Verify that mech OID is either exactly the same as the negotiated1417* mech OID, or is a mech OID supported by the negotiated mech. MS1418* implementations can list a most preferred mech using an incorrect1419* krb5 OID while emitting a krb5 initiator mech token having the1420* correct krb5 mech OID.1421*/1422static OM_uint321423acc_ctx_vfy_oid(OM_uint32 *minor_status,1424spnego_gss_ctx_id_t sc, gss_OID mechoid,1425OM_uint32 *negState, send_token_flag *tokflag)1426{1427OM_uint32 ret, tmpmin;1428gss_mechanism mech = NULL;1429gss_OID_set mech_set = GSS_C_NO_OID_SET;1430int present = 0;14311432if (g_OID_equal(sc->internal_mech, mechoid))1433return GSS_S_COMPLETE;14341435mech = gssint_get_mechanism(sc->internal_mech);1436if (mech == NULL || mech->gss_indicate_mechs == NULL) {1437*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;1438map_errcode(minor_status);1439*negState = REJECT;1440*tokflag = ERROR_TOKEN_SEND;1441return GSS_S_BAD_MECH;1442}1443ret = mech->gss_indicate_mechs(minor_status, &mech_set);1444if (ret != GSS_S_COMPLETE) {1445*tokflag = NO_TOKEN_SEND;1446map_error(minor_status, mech);1447goto cleanup;1448}1449ret = gss_test_oid_set_member(minor_status, mechoid,1450mech_set, &present);1451if (ret != GSS_S_COMPLETE)1452goto cleanup;1453if (!present) {1454*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;1455map_errcode(minor_status);1456*negState = REJECT;1457*tokflag = ERROR_TOKEN_SEND;1458ret = GSS_S_BAD_MECH;1459}1460cleanup:1461gss_release_oid_set(&tmpmin, &mech_set);1462return ret;1463}1464#ifndef LEAN_CLIENT1465/*1466* Wrap call to gss_accept_sec_context() and update state1467* accordingly.1468*/1469static OM_uint321470acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,1471spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,1472gss_channel_bindings_t bindings, gss_buffer_t mechtok_out,1473OM_uint32 *time_rec, OM_uint32 *negState,1474send_token_flag *tokflag)1475{1476OM_uint32 ret, tmpmin;1477gss_OID_desc mechoid;1478gss_cred_id_t mcred;1479int negoex = gss_oid_equal(sc->internal_mech, &negoex_mech);14801481if (sc->ctx_handle == GSS_C_NO_CONTEXT && !negoex) {1482/*1483* mechoid is an alias; don't free it.1484*/1485ret = gssint_get_mech_type(&mechoid, mechtok_in);1486if (ret != GSS_S_COMPLETE) {1487*tokflag = NO_TOKEN_SEND;1488return ret;1489}1490ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,1491negState, tokflag);1492if (ret != GSS_S_COMPLETE)1493return ret;1494}14951496mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;1497if (negoex) {1498ret = negoex_accept(minor_status, sc, mcred, mechtok_in,1499bindings, mechtok_out, time_rec);1500} else {1501(void) gss_release_name(&tmpmin, &sc->internal_name);1502(void) gss_release_cred(&tmpmin, &sc->deleg_cred);1503ret = gss_accept_sec_context(minor_status, &sc->ctx_handle,1504mcred, mechtok_in, bindings,1505&sc->internal_name,1506&sc->actual_mech, mechtok_out,1507&sc->ctx_flags, time_rec,1508&sc->deleg_cred);1509}1510if (ret == GSS_S_COMPLETE) {1511#ifdef MS_BUG_TEST1512/*1513* Force MIC to be not required even if we previously1514* requested a MIC.1515*/1516char *envstr = getenv("MS_FORCE_NO_MIC");15171518if (envstr != NULL && strcmp(envstr, "1") == 0 &&1519!(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&1520sc->mic_reqd) {15211522sc->mic_reqd = 0;1523}1524#endif1525sc->mech_complete = 1;15261527if (!sc->mic_reqd ||1528!(sc->ctx_flags & GSS_C_INTEG_FLAG)) {1529/* No MIC exchange required, so we're done. */1530*negState = ACCEPT_COMPLETE;1531ret = GSS_S_COMPLETE;1532} else {1533/* handle_mic will decide if we're done. */1534ret = GSS_S_CONTINUE_NEEDED;1535}1536} else if (ret != GSS_S_CONTINUE_NEEDED) {1537*negState = REJECT;1538*tokflag = ERROR_TOKEN_SEND;1539}1540return ret;1541}15421543/*ARGSUSED*/1544OM_uint32 KRB5_CALLCONV1545spnego_gss_accept_sec_context(1546OM_uint32 *minor_status,1547gss_ctx_id_t *context_handle,1548gss_cred_id_t verifier_cred_handle,1549gss_buffer_t input_token,1550gss_channel_bindings_t bindings,1551gss_name_t *src_name,1552gss_OID *mech_type,1553gss_buffer_t output_token,1554OM_uint32 *ret_flags,1555OM_uint32 *time_rec,1556gss_cred_id_t *delegated_cred_handle)1557{1558OM_uint32 ret, tmpmin, negState;1559send_token_flag return_token;1560gss_buffer_t mechtok_in, mic_in, mic_out;1561gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;1562spnego_gss_ctx_id_t sc = NULL;1563spnego_gss_cred_id_t spcred = NULL;1564int sendTokenInit = 0, tmpret;15651566mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;15671568/*1569* This function works in three steps:1570*1571* 1. Perform mechanism negotiation.1572* 2. Invoke the negotiated mech's gss_accept_sec_context function1573* and examine the results.1574* 3. Process or generate MICs if necessary.1575*1576* Step one determines whether the negotiation requires a MIC exchange,1577* while steps two and three share responsibility for determining when1578* the exchange is complete. If the selected mech completes in this1579* call and no MIC exchange is expected, then step 2 will decide. If a1580* MIC exchange is expected, then step 3 will decide. If an error1581* occurs in any step, the exchange will be aborted, possibly with an1582* error token.1583*1584* negState determines the state of the negotiation, and is1585* communicated to the acceptor if a continuing token is sent.1586* return_token is used to indicate what type of token, if any, should1587* be generated.1588*/15891590/* Validate arguments. */1591if (minor_status != NULL)1592*minor_status = 0;1593if (output_token != GSS_C_NO_BUFFER) {1594output_token->length = 0;1595output_token->value = NULL;1596}1597if (src_name != NULL)1598*src_name = GSS_C_NO_NAME;1599if (mech_type != NULL)1600*mech_type = GSS_C_NO_OID;1601if (time_rec != NULL)1602*time_rec = 0;1603if (ret_flags != NULL)1604*ret_flags = 0;1605if (delegated_cred_handle != NULL)1606*delegated_cred_handle = GSS_C_NO_CREDENTIAL;16071608if (minor_status == NULL ||1609output_token == GSS_C_NO_BUFFER ||1610context_handle == NULL)1611return GSS_S_CALL_INACCESSIBLE_WRITE;16121613if (input_token == GSS_C_NO_BUFFER)1614return GSS_S_CALL_INACCESSIBLE_READ;16151616/* Step 1: Perform mechanism negotiation. */1617sc = (spnego_gss_ctx_id_t)*context_handle;1618spcred = (spnego_gss_cred_id_t)verifier_cred_handle;1619if (sc == NULL && input_token->length == 0) {1620/* Process a request for NegHints. */1621ret = acc_ctx_hints(minor_status, spcred, &mic_out, &negState,1622&return_token, &sc);1623if (ret != GSS_S_COMPLETE)1624goto cleanup;1625*context_handle = (gss_ctx_id_t)sc;1626sendTokenInit = 1;1627ret = GSS_S_CONTINUE_NEEDED;1628} else if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {1629if (sc != NULL) {1630/* Discard the context from the NegHints request. */1631release_spnego_ctx(&sc);1632*context_handle = GSS_C_NO_CONTEXT;1633}1634/* Process an initial token; can set negState to1635* REQUEST_MIC. */1636ret = acc_ctx_new(minor_status, input_token, spcred,1637&mechtok_in, &mic_in, &negState,1638&return_token, &sc);1639if (ret != GSS_S_COMPLETE)1640goto cleanup;1641*context_handle = (gss_ctx_id_t)sc;1642ret = GSS_S_CONTINUE_NEEDED;1643} else {1644/* Process a response token. Can set negState to1645* ACCEPT_INCOMPLETE. */1646ret = acc_ctx_cont(minor_status, input_token, sc, &mechtok_in,1647&mic_in, &negState, &return_token);1648if (ret != GSS_S_COMPLETE)1649goto cleanup;1650ret = GSS_S_CONTINUE_NEEDED;1651}16521653/* Step 2: invoke the negotiated mechanism's gss_accept_sec_context1654* function. */1655/*1656* Handle mechtok_in and mic_in only if they are1657* present in input_token. If neither is present, whether1658* this is an error depends on whether this is the first1659* round-trip. RET is set to a default value according to1660* whether it is the first round-trip.1661*/1662if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {1663ret = acc_ctx_call_acc(minor_status, sc, spcred, mechtok_in,1664bindings, &mechtok_out, time_rec,1665&negState, &return_token);1666}16671668/* Step 3: process or generate the MIC, if the negotiated mech is1669* complete and supports MICs. */1670if (!HARD_ERROR(ret) && sc->mech_complete &&1671(sc->ctx_flags & GSS_C_INTEG_FLAG)) {16721673ret = handle_mic(minor_status, mic_in,1674(mechtok_out.length != 0),1675sc, &mic_out,1676&negState, &return_token);1677}16781679if (!HARD_ERROR(ret) && ret_flags != NULL)1680*ret_flags = sc->ctx_flags & ~GSS_C_PROT_READY_FLAG;16811682cleanup:1683if (return_token == INIT_TOKEN_SEND && sendTokenInit) {1684assert(sc != NULL);1685tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,1686GSS_C_NO_BUFFER,1687return_token, output_token);1688if (tmpret < 0)1689ret = GSS_S_FAILURE;1690} else if (return_token != NO_TOKEN_SEND &&1691return_token != CHECK_MIC) {1692tmpret = make_spnego_tokenTarg_msg(negState,1693sc ? sc->internal_mech :1694GSS_C_NO_OID,1695&mechtok_out, mic_out,1696return_token,1697output_token);1698if (tmpret < 0)1699ret = GSS_S_FAILURE;1700}1701if (ret == GSS_S_COMPLETE) {1702sc->opened = 1;1703if (sc->internal_name != GSS_C_NO_NAME &&1704src_name != NULL) {1705*src_name = sc->internal_name;1706sc->internal_name = GSS_C_NO_NAME;1707}1708if (mech_type != NULL)1709*mech_type = sc->actual_mech;1710/* Get an updated lifetime if we didn't call into the mech. */1711if (time_rec != NULL && *time_rec == 0) {1712(void) gss_context_time(&tmpmin, sc->ctx_handle,1713time_rec);1714}1715if (delegated_cred_handle != NULL) {1716*delegated_cred_handle = sc->deleg_cred;1717sc->deleg_cred = GSS_C_NO_CREDENTIAL;1718}1719} else if (ret != GSS_S_CONTINUE_NEEDED) {1720if (sc != NULL) {1721gss_delete_sec_context(&tmpmin, &sc->ctx_handle,1722GSS_C_NO_BUFFER);1723release_spnego_ctx(&sc);1724}1725*context_handle = GSS_C_NO_CONTEXT;1726}1727gss_release_buffer(&tmpmin, &mechtok_out);1728if (mechtok_in != GSS_C_NO_BUFFER) {1729gss_release_buffer(&tmpmin, mechtok_in);1730free(mechtok_in);1731}1732if (mic_in != GSS_C_NO_BUFFER) {1733gss_release_buffer(&tmpmin, mic_in);1734free(mic_in);1735}1736if (mic_out != GSS_C_NO_BUFFER) {1737gss_release_buffer(&tmpmin, mic_out);1738free(mic_out);1739}1740return ret;1741}1742#endif /* LEAN_CLIENT */17431744static struct {1745OM_uint32 status;1746const char *msg;1747} msg_table[] = {1748{ ERR_SPNEGO_NO_MECHS_AVAILABLE,1749N_("SPNEGO cannot find mechanisms to negotiate") },1750{ ERR_SPNEGO_NO_CREDS_ACQUIRED,1751N_("SPNEGO failed to acquire creds") },1752{ ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR,1753N_("SPNEGO acceptor did not select a mechanism") },1754{ ERR_SPNEGO_NEGOTIATION_FAILED,1755N_("SPNEGO failed to negotiate a mechanism") },1756{ ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR,1757N_("SPNEGO acceptor did not return a valid token") },1758{ ERR_NEGOEX_INVALID_MESSAGE_SIGNATURE,1759N_("Invalid NegoEx signature") },1760{ ERR_NEGOEX_INVALID_MESSAGE_TYPE,1761N_("Invalid NegoEx message type") },1762{ ERR_NEGOEX_INVALID_MESSAGE_SIZE,1763N_("Invalid NegoEx message size") },1764{ ERR_NEGOEX_INVALID_CONVERSATION_ID,1765N_("Invalid NegoEx conversation ID") },1766{ ERR_NEGOEX_AUTH_SCHEME_NOT_FOUND,1767N_("NegoEx authentication scheme not found") },1768{ ERR_NEGOEX_MISSING_NEGO_MESSAGE,1769N_("Missing NegoEx negotiate message") },1770{ ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE,1771N_("Missing NegoEx authentication protocol request message") },1772{ ERR_NEGOEX_NO_AVAILABLE_MECHS,1773N_("No mutually supported NegoEx authentication schemes") },1774{ ERR_NEGOEX_NO_VERIFY_KEY,1775N_("No NegoEx verify key") },1776{ ERR_NEGOEX_UNKNOWN_CHECKSUM_SCHEME,1777N_("Unknown NegoEx checksum scheme") },1778{ ERR_NEGOEX_INVALID_CHECKSUM,1779N_("Invalid NegoEx checksum") },1780{ ERR_NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION,1781N_("Unsupported critical NegoEx extension") },1782{ ERR_NEGOEX_UNSUPPORTED_VERSION,1783N_("Unsupported NegoEx version") },1784{ ERR_NEGOEX_MESSAGE_OUT_OF_SEQUENCE,1785N_("NegoEx message out of sequence") },1786};17871788/*ARGSUSED*/1789OM_uint32 KRB5_CALLCONV1790spnego_gss_display_status(1791OM_uint32 *minor_status,1792OM_uint32 status_value,1793int status_type,1794gss_OID mech_type,1795OM_uint32 *message_context,1796gss_buffer_t status_string)1797{1798OM_uint32 maj = GSS_S_COMPLETE;1799const char *msg;1800size_t i;1801int ret;18021803*message_context = 0;1804for (i = 0; i < sizeof(msg_table) / sizeof(*msg_table); i++) {1805if (status_value == msg_table[i].status) {1806msg = dgettext(KRB5_TEXTDOMAIN, msg_table[i].msg);1807*status_string = make_err_msg(msg);1808return GSS_S_COMPLETE;1809}1810}18111812/* Not one of our minor codes; might be from a mech. Call back1813* to gss_display_status, but first check for recursion. */1814if (k5_getspecific(K5_KEY_GSS_SPNEGO_STATUS) != NULL) {1815/* Perhaps we returned a com_err code like ENOMEM. */1816const char *err = error_message(status_value);1817*status_string = make_err_msg(err);1818return GSS_S_COMPLETE;1819}1820/* Set a non-null pointer value; doesn't matter which one. */1821ret = k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, &ret);1822if (ret != 0) {1823*minor_status = ret;1824return GSS_S_FAILURE;1825}18261827maj = gss_display_status(minor_status, status_value,1828status_type, mech_type,1829message_context, status_string);1830/* This is unlikely to fail; not much we can do if it does. */1831(void)k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, NULL);18321833return maj;1834}183518361837/*ARGSUSED*/1838OM_uint32 KRB5_CALLCONV1839spnego_gss_import_name(1840OM_uint32 *minor_status,1841gss_buffer_t input_name_buffer,1842gss_OID input_name_type,1843gss_name_t *output_name)1844{1845OM_uint32 status;18461847dsyslog("Entering import_name\n");18481849status = gss_import_name(minor_status, input_name_buffer,1850input_name_type, output_name);18511852dsyslog("Leaving import_name\n");1853return (status);1854}18551856/*ARGSUSED*/1857OM_uint32 KRB5_CALLCONV1858spnego_gss_release_name(1859OM_uint32 *minor_status,1860gss_name_t *input_name)1861{1862OM_uint32 status;18631864dsyslog("Entering release_name\n");18651866status = gss_release_name(minor_status, input_name);18671868dsyslog("Leaving release_name\n");1869return (status);1870}18711872/*ARGSUSED*/1873OM_uint32 KRB5_CALLCONV1874spnego_gss_duplicate_name(1875OM_uint32 *minor_status,1876const gss_name_t input_name,1877gss_name_t *output_name)1878{1879OM_uint32 status;18801881dsyslog("Entering duplicate_name\n");18821883status = gss_duplicate_name(minor_status, input_name, output_name);18841885dsyslog("Leaving duplicate_name\n");1886return (status);1887}18881889OM_uint32 KRB5_CALLCONV1890spnego_gss_inquire_cred(1891OM_uint32 *minor_status,1892gss_cred_id_t cred_handle,1893gss_name_t *name,1894OM_uint32 *lifetime,1895int *cred_usage,1896gss_OID_set *mechanisms)1897{1898OM_uint32 status;1899spnego_gss_cred_id_t spcred = NULL;1900gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;1901OM_uint32 tmp_minor_status;1902OM_uint32 initiator_lifetime, acceptor_lifetime;19031904dsyslog("Entering inquire_cred\n");19051906/*1907* To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is1908* supplied we call gss_inquire_cred_by_mech() on the1909* first non-SPNEGO mechanism.1910*/1911spcred = (spnego_gss_cred_id_t)cred_handle;1912if (spcred == NULL) {1913status = get_available_mechs(minor_status,1914GSS_C_NO_NAME,1915GSS_C_BOTH,1916GSS_C_NO_CRED_STORE,1917&creds,1918mechanisms, NULL);1919if (status != GSS_S_COMPLETE) {1920dsyslog("Leaving inquire_cred\n");1921return (status);1922}19231924if ((*mechanisms)->count == 0) {1925gss_release_cred(&tmp_minor_status, &creds);1926gss_release_oid_set(&tmp_minor_status, mechanisms);1927dsyslog("Leaving inquire_cred\n");1928return (GSS_S_DEFECTIVE_CREDENTIAL);1929}19301931assert((*mechanisms)->elements != NULL);19321933status = gss_inquire_cred_by_mech(minor_status,1934creds,1935&(*mechanisms)->elements[0],1936name,1937&initiator_lifetime,1938&acceptor_lifetime,1939cred_usage);1940if (status != GSS_S_COMPLETE) {1941gss_release_cred(&tmp_minor_status, &creds);1942dsyslog("Leaving inquire_cred\n");1943return (status);1944}19451946if (lifetime != NULL)1947*lifetime = (*cred_usage == GSS_C_ACCEPT) ?1948acceptor_lifetime : initiator_lifetime;19491950gss_release_cred(&tmp_minor_status, &creds);1951} else {1952status = gss_inquire_cred(minor_status, spcred->mcred,1953name, lifetime,1954cred_usage, mechanisms);1955}19561957dsyslog("Leaving inquire_cred\n");19581959return (status);1960}19611962/*ARGSUSED*/1963OM_uint32 KRB5_CALLCONV1964spnego_gss_compare_name(1965OM_uint32 *minor_status,1966const gss_name_t name1,1967const gss_name_t name2,1968int *name_equal)1969{1970OM_uint32 status = GSS_S_COMPLETE;1971dsyslog("Entering compare_name\n");19721973status = gss_compare_name(minor_status, name1, name2, name_equal);19741975dsyslog("Leaving compare_name\n");1976return (status);1977}19781979/*ARGSUSED*/1980/*ARGSUSED*/1981OM_uint32 KRB5_CALLCONV1982spnego_gss_display_name(1983OM_uint32 *minor_status,1984gss_name_t input_name,1985gss_buffer_t output_name_buffer,1986gss_OID *output_name_type)1987{1988OM_uint32 status = GSS_S_COMPLETE;1989dsyslog("Entering display_name\n");19901991status = gss_display_name(minor_status, input_name,1992output_name_buffer, output_name_type);19931994dsyslog("Leaving display_name\n");1995return (status);1996}199719981999/*ARGSUSED*/2000OM_uint32 KRB5_CALLCONV2001spnego_gss_inquire_names_for_mech(2002OM_uint32 *minor_status,2003gss_OID mechanism,2004gss_OID_set *name_types)2005{2006OM_uint32 major, minor;20072008dsyslog("Entering inquire_names_for_mech\n");2009/*2010* We only know how to handle our own mechanism.2011*/2012if ((mechanism != GSS_C_NULL_OID) &&2013!g_OID_equal(gss_mech_spnego, mechanism)) {2014*minor_status = 0;2015return (GSS_S_FAILURE);2016}20172018major = gss_create_empty_oid_set(minor_status, name_types);2019if (major == GSS_S_COMPLETE) {2020/* Now add our members. */2021if (((major = gss_add_oid_set_member(minor_status,2022(gss_OID) GSS_C_NT_USER_NAME,2023name_types)) == GSS_S_COMPLETE) &&2024((major = gss_add_oid_set_member(minor_status,2025(gss_OID) GSS_C_NT_MACHINE_UID_NAME,2026name_types)) == GSS_S_COMPLETE) &&2027((major = gss_add_oid_set_member(minor_status,2028(gss_OID) GSS_C_NT_STRING_UID_NAME,2029name_types)) == GSS_S_COMPLETE)) {2030major = gss_add_oid_set_member(minor_status,2031(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,2032name_types);2033}20342035if (major != GSS_S_COMPLETE)2036(void) gss_release_oid_set(&minor, name_types);2037}20382039dsyslog("Leaving inquire_names_for_mech\n");2040return (major);2041}20422043OM_uint32 KRB5_CALLCONV2044spnego_gss_unwrap(2045OM_uint32 *minor_status,2046gss_ctx_id_t context_handle,2047gss_buffer_t input_message_buffer,2048gss_buffer_t output_message_buffer,2049int *conf_state,2050gss_qop_t *qop_state)2051{2052OM_uint32 ret;2053spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;20542055if (sc->ctx_handle == GSS_C_NO_CONTEXT)2056return (GSS_S_NO_CONTEXT);20572058ret = gss_unwrap(minor_status,2059sc->ctx_handle,2060input_message_buffer,2061output_message_buffer,2062conf_state,2063qop_state);20642065return (ret);2066}20672068OM_uint32 KRB5_CALLCONV2069spnego_gss_wrap(2070OM_uint32 *minor_status,2071gss_ctx_id_t context_handle,2072int conf_req_flag,2073gss_qop_t qop_req,2074gss_buffer_t input_message_buffer,2075int *conf_state,2076gss_buffer_t output_message_buffer)2077{2078OM_uint32 ret;2079spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;20802081if (sc->ctx_handle == GSS_C_NO_CONTEXT)2082return (GSS_S_NO_CONTEXT);20832084ret = gss_wrap(minor_status,2085sc->ctx_handle,2086conf_req_flag,2087qop_req,2088input_message_buffer,2089conf_state,2090output_message_buffer);20912092return (ret);2093}20942095OM_uint32 KRB5_CALLCONV2096spnego_gss_process_context_token(2097OM_uint32 *minor_status,2098const gss_ctx_id_t context_handle,2099const gss_buffer_t token_buffer)2100{2101OM_uint32 ret;2102spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;21032104/* SPNEGO doesn't have its own context tokens. */2105if (!sc->opened)2106return (GSS_S_DEFECTIVE_TOKEN);21072108ret = gss_process_context_token(minor_status,2109sc->ctx_handle,2110token_buffer);21112112return (ret);2113}21142115OM_uint32 KRB5_CALLCONV2116spnego_gss_delete_sec_context(2117OM_uint32 *minor_status,2118gss_ctx_id_t *context_handle,2119gss_buffer_t output_token)2120{2121OM_uint32 ret = GSS_S_COMPLETE;2122spnego_gss_ctx_id_t *ctx =2123(spnego_gss_ctx_id_t *)context_handle;21242125*minor_status = 0;21262127if (context_handle == NULL)2128return (GSS_S_FAILURE);21292130if (*ctx == NULL)2131return (GSS_S_COMPLETE);21322133(void) gss_delete_sec_context(minor_status, &(*ctx)->ctx_handle,2134output_token);2135(void) release_spnego_ctx(ctx);21362137return (ret);2138}21392140OM_uint32 KRB5_CALLCONV2141spnego_gss_context_time(2142OM_uint32 *minor_status,2143const gss_ctx_id_t context_handle,2144OM_uint32 *time_rec)2145{2146OM_uint32 ret;2147spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;21482149if (sc->ctx_handle == GSS_C_NO_CONTEXT)2150return (GSS_S_NO_CONTEXT);21512152ret = gss_context_time(minor_status,2153sc->ctx_handle,2154time_rec);2155return (ret);2156}2157#ifndef LEAN_CLIENT2158OM_uint32 KRB5_CALLCONV2159spnego_gss_export_sec_context(2160OM_uint32 *minor_status,2161gss_ctx_id_t *context_handle,2162gss_buffer_t interprocess_token)2163{2164OM_uint32 ret;2165spnego_gss_ctx_id_t sc = *(spnego_gss_ctx_id_t *)context_handle;21662167/* We don't currently support exporting partially established2168* contexts. */2169if (!sc->opened)2170return GSS_S_UNAVAILABLE;21712172ret = gss_export_sec_context(minor_status,2173&sc->ctx_handle,2174interprocess_token);2175if (sc->ctx_handle == GSS_C_NO_CONTEXT) {2176release_spnego_ctx(&sc);2177*context_handle = GSS_C_NO_CONTEXT;2178}2179return (ret);2180}21812182OM_uint32 KRB5_CALLCONV2183spnego_gss_import_sec_context(2184OM_uint32 *minor_status,2185const gss_buffer_t interprocess_token,2186gss_ctx_id_t *context_handle)2187{2188OM_uint32 ret, tmpmin;2189gss_ctx_id_t mctx;2190spnego_gss_ctx_id_t sc;2191int initiate, opened;21922193ret = gss_import_sec_context(minor_status, interprocess_token, &mctx);2194if (ret != GSS_S_COMPLETE)2195return ret;21962197ret = gss_inquire_context(&tmpmin, mctx, NULL, NULL, NULL, NULL, NULL,2198&initiate, &opened);2199if (ret != GSS_S_COMPLETE || !opened) {2200/* We don't currently support importing partially established2201* contexts. */2202(void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);2203return GSS_S_FAILURE;2204}22052206sc = create_spnego_ctx(initiate);2207if (sc == NULL) {2208(void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);2209return GSS_S_FAILURE;2210}2211sc->ctx_handle = mctx;2212sc->opened = 1;2213*context_handle = (gss_ctx_id_t)sc;2214return GSS_S_COMPLETE;2215}2216#endif /* LEAN_CLIENT */22172218OM_uint32 KRB5_CALLCONV2219spnego_gss_inquire_context(2220OM_uint32 *minor_status,2221const gss_ctx_id_t context_handle,2222gss_name_t *src_name,2223gss_name_t *targ_name,2224OM_uint32 *lifetime_rec,2225gss_OID *mech_type,2226OM_uint32 *ctx_flags,2227int *locally_initiated,2228int *opened)2229{2230OM_uint32 ret = GSS_S_COMPLETE;2231spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;22322233if (src_name != NULL)2234*src_name = GSS_C_NO_NAME;2235if (targ_name != NULL)2236*targ_name = GSS_C_NO_NAME;2237if (lifetime_rec != NULL)2238*lifetime_rec = 0;2239if (mech_type != NULL)2240*mech_type = (gss_OID)gss_mech_spnego;2241if (ctx_flags != NULL)2242*ctx_flags = 0;2243if (locally_initiated != NULL)2244*locally_initiated = sc->initiate;2245if (opened != NULL)2246*opened = sc->opened;22472248if (sc->ctx_handle != GSS_C_NO_CONTEXT) {2249ret = gss_inquire_context(minor_status, sc->ctx_handle,2250src_name, targ_name, lifetime_rec,2251mech_type, ctx_flags, NULL, NULL);2252}22532254if (!sc->opened) {2255/*2256* We are still doing SPNEGO negotiation, so report SPNEGO as2257* the OID. After negotiation is complete we will report the2258* underlying mechanism OID.2259*/2260if (mech_type != NULL)2261*mech_type = (gss_OID)gss_mech_spnego;22622263/*2264* Remove flags we don't support with partially-established2265* contexts. (Change this to keep GSS_C_TRANS_FLAG if we add2266* support for exporting partial SPNEGO contexts.)2267*/2268if (ctx_flags != NULL) {2269*ctx_flags &= ~GSS_C_PROT_READY_FLAG;2270*ctx_flags &= ~GSS_C_TRANS_FLAG;2271}2272}22732274return (ret);2275}22762277OM_uint32 KRB5_CALLCONV2278spnego_gss_wrap_size_limit(2279OM_uint32 *minor_status,2280const gss_ctx_id_t context_handle,2281int conf_req_flag,2282gss_qop_t qop_req,2283OM_uint32 req_output_size,2284OM_uint32 *max_input_size)2285{2286OM_uint32 ret;2287spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;22882289if (sc->ctx_handle == GSS_C_NO_CONTEXT)2290return (GSS_S_NO_CONTEXT);22912292ret = gss_wrap_size_limit(minor_status,2293sc->ctx_handle,2294conf_req_flag,2295qop_req,2296req_output_size,2297max_input_size);2298return (ret);2299}23002301OM_uint32 KRB5_CALLCONV2302spnego_gss_localname(OM_uint32 *minor_status, const gss_name_t pname,2303const gss_const_OID mech_type, gss_buffer_t localname)2304{2305return gss_localname(minor_status, pname, GSS_C_NO_OID, localname);2306}23072308OM_uint32 KRB5_CALLCONV2309spnego_gss_get_mic(2310OM_uint32 *minor_status,2311const gss_ctx_id_t context_handle,2312gss_qop_t qop_req,2313const gss_buffer_t message_buffer,2314gss_buffer_t message_token)2315{2316OM_uint32 ret;2317spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;23182319if (sc->ctx_handle == GSS_C_NO_CONTEXT)2320return (GSS_S_NO_CONTEXT);23212322ret = gss_get_mic(minor_status,2323sc->ctx_handle,2324qop_req,2325message_buffer,2326message_token);2327return (ret);2328}23292330OM_uint32 KRB5_CALLCONV2331spnego_gss_verify_mic(2332OM_uint32 *minor_status,2333const gss_ctx_id_t context_handle,2334const gss_buffer_t msg_buffer,2335const gss_buffer_t token_buffer,2336gss_qop_t *qop_state)2337{2338OM_uint32 ret;2339spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;23402341if (sc->ctx_handle == GSS_C_NO_CONTEXT)2342return (GSS_S_NO_CONTEXT);23432344ret = gss_verify_mic(minor_status,2345sc->ctx_handle,2346msg_buffer,2347token_buffer,2348qop_state);2349return (ret);2350}23512352OM_uint32 KRB5_CALLCONV2353spnego_gss_inquire_sec_context_by_oid(2354OM_uint32 *minor_status,2355const gss_ctx_id_t context_handle,2356const gss_OID desired_object,2357gss_buffer_set_t *data_set)2358{2359OM_uint32 ret;2360spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;23612362/* There are no SPNEGO-specific OIDs for this function. */2363if (sc->ctx_handle == GSS_C_NO_CONTEXT)2364return (GSS_S_UNAVAILABLE);23652366ret = gss_inquire_sec_context_by_oid(minor_status,2367sc->ctx_handle,2368desired_object,2369data_set);2370return (ret);2371}23722373OM_uint32 KRB5_CALLCONV2374spnego_gss_inquire_cred_by_oid(2375OM_uint32 *minor_status,2376const gss_cred_id_t cred_handle,2377const gss_OID desired_object,2378gss_buffer_set_t *data_set)2379{2380OM_uint32 ret;2381spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;2382gss_cred_id_t mcred;2383mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;2384ret = gss_inquire_cred_by_oid(minor_status,2385mcred,2386desired_object,2387data_set);2388return (ret);2389}23902391/* This is the same OID as KRB5_NO_CI_FLAGS_X_OID. */2392#define NO_CI_FLAGS_X_OID_LENGTH 62393#define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d"2394static const gss_OID_desc no_ci_flags_oid[] = {2395{NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID},2396};23972398OM_uint32 KRB5_CALLCONV2399spnego_gss_set_cred_option(2400OM_uint32 *minor_status,2401gss_cred_id_t *cred_handle,2402const gss_OID desired_object,2403const gss_buffer_t value)2404{2405OM_uint32 ret;2406OM_uint32 tmp_minor_status;2407spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;2408gss_cred_id_t mcred;24092410mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;2411ret = gss_set_cred_option(minor_status,2412&mcred,2413desired_object,2414value);2415if (ret == GSS_S_COMPLETE && spcred == NULL) {2416/*2417* If the mechanism allocated a new credential handle, then2418* we need to wrap it up in an SPNEGO credential handle.2419*/24202421ret = create_spnego_cred(minor_status, mcred, &spcred);2422if (ret != GSS_S_COMPLETE) {2423gss_release_cred(&tmp_minor_status, &mcred);2424return (ret);2425}2426*cred_handle = (gss_cred_id_t)spcred;2427}24282429if (ret != GSS_S_COMPLETE)2430return (ret);24312432/* Recognize KRB5_NO_CI_FLAGS_X_OID and avoid asking for integrity. */2433if (g_OID_equal(desired_object, no_ci_flags_oid))2434spcred->no_ask_integ = 1;24352436return (GSS_S_COMPLETE);2437}24382439OM_uint32 KRB5_CALLCONV2440spnego_gss_set_sec_context_option(2441OM_uint32 *minor_status,2442gss_ctx_id_t *context_handle,2443const gss_OID desired_object,2444const gss_buffer_t value)2445{2446OM_uint32 ret;2447spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)*context_handle;24482449/* There are no SPNEGO-specific OIDs for this function, and we cannot2450* construct an empty SPNEGO context with it. */2451if (sc == NULL || sc->ctx_handle == GSS_C_NO_CONTEXT)2452return (GSS_S_UNAVAILABLE);24532454ret = gss_set_sec_context_option(minor_status,2455&sc->ctx_handle,2456desired_object,2457value);2458return (ret);2459}24602461OM_uint32 KRB5_CALLCONV2462spnego_gss_wrap_aead(OM_uint32 *minor_status,2463gss_ctx_id_t context_handle,2464int conf_req_flag,2465gss_qop_t qop_req,2466gss_buffer_t input_assoc_buffer,2467gss_buffer_t input_payload_buffer,2468int *conf_state,2469gss_buffer_t output_message_buffer)2470{2471OM_uint32 ret;2472spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;24732474if (sc->ctx_handle == GSS_C_NO_CONTEXT)2475return (GSS_S_NO_CONTEXT);24762477ret = gss_wrap_aead(minor_status,2478sc->ctx_handle,2479conf_req_flag,2480qop_req,2481input_assoc_buffer,2482input_payload_buffer,2483conf_state,2484output_message_buffer);24852486return (ret);2487}24882489OM_uint32 KRB5_CALLCONV2490spnego_gss_unwrap_aead(OM_uint32 *minor_status,2491gss_ctx_id_t context_handle,2492gss_buffer_t input_message_buffer,2493gss_buffer_t input_assoc_buffer,2494gss_buffer_t output_payload_buffer,2495int *conf_state,2496gss_qop_t *qop_state)2497{2498OM_uint32 ret;2499spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;25002501if (sc->ctx_handle == GSS_C_NO_CONTEXT)2502return (GSS_S_NO_CONTEXT);25032504ret = gss_unwrap_aead(minor_status,2505sc->ctx_handle,2506input_message_buffer,2507input_assoc_buffer,2508output_payload_buffer,2509conf_state,2510qop_state);2511return (ret);2512}25132514OM_uint32 KRB5_CALLCONV2515spnego_gss_wrap_iov(OM_uint32 *minor_status,2516gss_ctx_id_t context_handle,2517int conf_req_flag,2518gss_qop_t qop_req,2519int *conf_state,2520gss_iov_buffer_desc *iov,2521int iov_count)2522{2523OM_uint32 ret;2524spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;25252526if (sc->ctx_handle == GSS_C_NO_CONTEXT)2527return (GSS_S_NO_CONTEXT);25282529ret = gss_wrap_iov(minor_status,2530sc->ctx_handle,2531conf_req_flag,2532qop_req,2533conf_state,2534iov,2535iov_count);2536return (ret);2537}25382539OM_uint32 KRB5_CALLCONV2540spnego_gss_unwrap_iov(OM_uint32 *minor_status,2541gss_ctx_id_t context_handle,2542int *conf_state,2543gss_qop_t *qop_state,2544gss_iov_buffer_desc *iov,2545int iov_count)2546{2547OM_uint32 ret;2548spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;25492550if (sc->ctx_handle == GSS_C_NO_CONTEXT)2551return (GSS_S_NO_CONTEXT);25522553ret = gss_unwrap_iov(minor_status,2554sc->ctx_handle,2555conf_state,2556qop_state,2557iov,2558iov_count);2559return (ret);2560}25612562OM_uint32 KRB5_CALLCONV2563spnego_gss_wrap_iov_length(OM_uint32 *minor_status,2564gss_ctx_id_t context_handle,2565int conf_req_flag,2566gss_qop_t qop_req,2567int *conf_state,2568gss_iov_buffer_desc *iov,2569int iov_count)2570{2571OM_uint32 ret;2572spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;25732574if (sc->ctx_handle == GSS_C_NO_CONTEXT)2575return (GSS_S_NO_CONTEXT);25762577ret = gss_wrap_iov_length(minor_status,2578sc->ctx_handle,2579conf_req_flag,2580qop_req,2581conf_state,2582iov,2583iov_count);2584return (ret);2585}258625872588OM_uint32 KRB5_CALLCONV2589spnego_gss_complete_auth_token(2590OM_uint32 *minor_status,2591const gss_ctx_id_t context_handle,2592gss_buffer_t input_message_buffer)2593{2594OM_uint32 ret;2595spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;25962597if (sc->ctx_handle == GSS_C_NO_CONTEXT)2598return (GSS_S_UNAVAILABLE);25992600ret = gss_complete_auth_token(minor_status,2601sc->ctx_handle,2602input_message_buffer);2603return (ret);2604}26052606OM_uint32 KRB5_CALLCONV2607spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,2608const gss_cred_id_t impersonator_cred_handle,2609const gss_name_t desired_name,2610OM_uint32 time_req,2611gss_OID_set desired_mechs,2612gss_cred_usage_t cred_usage,2613gss_cred_id_t *output_cred_handle,2614gss_OID_set *actual_mechs,2615OM_uint32 *time_rec)2616{2617OM_uint32 status, tmpmin;2618gss_OID_set amechs = GSS_C_NULL_OID_SET;2619spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;2620gss_cred_id_t imp_mcred, out_mcred = GSS_C_NO_CREDENTIAL;26212622dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");26232624if (actual_mechs)2625*actual_mechs = NULL;26262627if (time_rec)2628*time_rec = 0;26292630imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;2631imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;2632status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,2633NULL, &amechs);2634if (status != GSS_S_COMPLETE)2635return status;26362637status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,2638desired_name, time_req,2639amechs, cred_usage,2640&out_mcred, actual_mechs,2641time_rec);2642if (status != GSS_S_COMPLETE)2643goto cleanup;26442645status = create_spnego_cred(minor_status, out_mcred, &out_spcred);2646if (status != GSS_S_COMPLETE)2647goto cleanup;26482649out_mcred = GSS_C_NO_CREDENTIAL;2650*output_cred_handle = (gss_cred_id_t)out_spcred;26512652cleanup:2653(void) gss_release_oid_set(&tmpmin, &amechs);2654(void) gss_release_cred(&tmpmin, &out_mcred);26552656dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");2657return (status);2658}26592660OM_uint32 KRB5_CALLCONV2661spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,2662const gss_name_t desired_name,2663const gss_buffer_t password,2664OM_uint32 time_req,2665const gss_OID_set desired_mechs,2666gss_cred_usage_t cred_usage,2667gss_cred_id_t *output_cred_handle,2668gss_OID_set *actual_mechs,2669OM_uint32 *time_rec)2670{2671OM_uint32 status, tmpmin;2672gss_OID_set amechs = GSS_C_NULL_OID_SET;2673gss_cred_id_t mcred = NULL;2674spnego_gss_cred_id_t spcred = NULL;26752676dsyslog("Entering spnego_gss_acquire_cred_with_password\n");26772678if (actual_mechs)2679*actual_mechs = NULL;26802681if (time_rec)2682*time_rec = 0;26832684status = get_available_mechs(minor_status, desired_name,2685cred_usage, GSS_C_NO_CRED_STORE,2686NULL, &amechs, NULL);2687if (status != GSS_S_COMPLETE)2688goto cleanup;26892690status = gss_acquire_cred_with_password(minor_status, desired_name,2691password, time_req, amechs,2692cred_usage, &mcred,2693actual_mechs, time_rec);2694if (status != GSS_S_COMPLETE)2695goto cleanup;26962697status = create_spnego_cred(minor_status, mcred, &spcred);2698if (status != GSS_S_COMPLETE)2699goto cleanup;27002701mcred = GSS_C_NO_CREDENTIAL;2702*output_cred_handle = (gss_cred_id_t)spcred;27032704cleanup:27052706(void) gss_release_oid_set(&tmpmin, &amechs);2707(void) gss_release_cred(&tmpmin, &mcred);27082709dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");2710return (status);2711}27122713OM_uint32 KRB5_CALLCONV2714spnego_gss_display_name_ext(OM_uint32 *minor_status,2715gss_name_t name,2716gss_OID display_as_name_type,2717gss_buffer_t display_name)2718{2719OM_uint32 ret;2720ret = gss_display_name_ext(minor_status,2721name,2722display_as_name_type,2723display_name);2724return (ret);2725}272627272728OM_uint32 KRB5_CALLCONV2729spnego_gss_inquire_name(OM_uint32 *minor_status,2730gss_name_t name,2731int *name_is_MN,2732gss_OID *MN_mech,2733gss_buffer_set_t *attrs)2734{2735OM_uint32 ret;2736ret = gss_inquire_name(minor_status,2737name,2738name_is_MN,2739MN_mech,2740attrs);2741return (ret);2742}27432744OM_uint32 KRB5_CALLCONV2745spnego_gss_get_name_attribute(OM_uint32 *minor_status,2746gss_name_t name,2747gss_buffer_t attr,2748int *authenticated,2749int *complete,2750gss_buffer_t value,2751gss_buffer_t display_value,2752int *more)2753{2754OM_uint32 ret;2755ret = gss_get_name_attribute(minor_status,2756name,2757attr,2758authenticated,2759complete,2760value,2761display_value,2762more);2763return (ret);2764}27652766OM_uint32 KRB5_CALLCONV2767spnego_gss_set_name_attribute(OM_uint32 *minor_status,2768gss_name_t name,2769int complete,2770gss_buffer_t attr,2771gss_buffer_t value)2772{2773OM_uint32 ret;2774ret = gss_set_name_attribute(minor_status,2775name,2776complete,2777attr,2778value);2779return (ret);2780}27812782OM_uint32 KRB5_CALLCONV2783spnego_gss_delete_name_attribute(OM_uint32 *minor_status,2784gss_name_t name,2785gss_buffer_t attr)2786{2787OM_uint32 ret;2788ret = gss_delete_name_attribute(minor_status,2789name,2790attr);2791return (ret);2792}27932794OM_uint32 KRB5_CALLCONV2795spnego_gss_export_name_composite(OM_uint32 *minor_status,2796gss_name_t name,2797gss_buffer_t exp_composite_name)2798{2799OM_uint32 ret;2800ret = gss_export_name_composite(minor_status,2801name,2802exp_composite_name);2803return (ret);2804}28052806OM_uint32 KRB5_CALLCONV2807spnego_gss_map_name_to_any(OM_uint32 *minor_status,2808gss_name_t name,2809int authenticated,2810gss_buffer_t type_id,2811gss_any_t *output)2812{2813OM_uint32 ret;2814ret = gss_map_name_to_any(minor_status,2815name,2816authenticated,2817type_id,2818output);2819return (ret);2820}28212822OM_uint32 KRB5_CALLCONV2823spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,2824gss_name_t name,2825gss_buffer_t type_id,2826gss_any_t *input)2827{2828OM_uint32 ret;2829ret = gss_release_any_name_mapping(minor_status,2830name,2831type_id,2832input);2833return (ret);2834}28352836OM_uint32 KRB5_CALLCONV2837spnego_gss_pseudo_random(OM_uint32 *minor_status,2838gss_ctx_id_t context,2839int prf_key,2840const gss_buffer_t prf_in,2841ssize_t desired_output_len,2842gss_buffer_t prf_out)2843{2844OM_uint32 ret;2845spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context;28462847if (sc->ctx_handle == GSS_C_NO_CONTEXT)2848return (GSS_S_NO_CONTEXT);28492850ret = gss_pseudo_random(minor_status,2851sc->ctx_handle,2852prf_key,2853prf_in,2854desired_output_len,2855prf_out);2856return (ret);2857}28582859OM_uint32 KRB5_CALLCONV2860spnego_gss_set_neg_mechs(OM_uint32 *minor_status,2861gss_cred_id_t cred_handle,2862const gss_OID_set mech_list)2863{2864OM_uint32 ret;2865spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;28662867/* Store mech_list in spcred for use in negotiation logic. */2868gss_release_oid_set(minor_status, &spcred->neg_mechs);2869ret = generic_gss_copy_oid_set(minor_status, mech_list,2870&spcred->neg_mechs);2871if (ret == GSS_S_COMPLETE) {2872(void) gss_set_neg_mechs(minor_status,2873spcred->mcred,2874spcred->neg_mechs);2875}28762877return (ret);2878}28792880#define SPNEGO_SASL_NAME "SPNEGO"2881#define SPNEGO_SASL_NAME_LEN (sizeof(SPNEGO_SASL_NAME) - 1)28822883OM_uint32 KRB5_CALLCONV2884spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,2885const gss_buffer_t sasl_mech_name,2886gss_OID *mech_type)2887{2888if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&2889memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,2890SPNEGO_SASL_NAME_LEN) == 0) {2891if (mech_type != NULL)2892*mech_type = (gss_OID)gss_mech_spnego;2893return (GSS_S_COMPLETE);2894}28952896return (GSS_S_BAD_MECH);2897}28982899OM_uint32 KRB5_CALLCONV2900spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,2901const gss_OID desired_mech,2902gss_buffer_t sasl_mech_name,2903gss_buffer_t mech_name,2904gss_buffer_t mech_description)2905{2906*minor_status = 0;29072908if (!g_OID_equal(desired_mech, gss_mech_spnego))2909return (GSS_S_BAD_MECH);29102911if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||2912!g_make_string_buffer("spnego", mech_name) ||2913!g_make_string_buffer("Simple and Protected GSS-API "2914"Negotiation Mechanism", mech_description))2915goto fail;29162917return (GSS_S_COMPLETE);29182919fail:2920*minor_status = ENOMEM;2921return (GSS_S_FAILURE);2922}29232924OM_uint32 KRB5_CALLCONV2925spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,2926gss_const_OID mech,2927gss_OID_set *mech_attrs,2928gss_OID_set *known_mech_attrs)2929{2930OM_uint32 major, tmpMinor;29312932/* known_mech_attrs is handled by mechglue */2933*minor_status = 0;29342935if (mech_attrs == NULL)2936return (GSS_S_COMPLETE);29372938major = gss_create_empty_oid_set(minor_status, mech_attrs);2939if (GSS_ERROR(major))2940goto cleanup;29412942#define MA_SUPPORTED(ma) do { \2943major = gss_add_oid_set_member(minor_status, \2944(gss_OID)ma, mech_attrs); \2945if (GSS_ERROR(major)) \2946goto cleanup; \2947} while (0)29482949MA_SUPPORTED(GSS_C_MA_MECH_NEGO);2950MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);29512952cleanup:2953if (GSS_ERROR(major))2954gss_release_oid_set(&tmpMinor, mech_attrs);29552956return (major);2957}29582959OM_uint32 KRB5_CALLCONV2960spnego_gss_export_cred(OM_uint32 *minor_status,2961gss_cred_id_t cred_handle,2962gss_buffer_t token)2963{2964spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;29652966return (gss_export_cred(minor_status, spcred->mcred, token));2967}29682969OM_uint32 KRB5_CALLCONV2970spnego_gss_import_cred(OM_uint32 *minor_status,2971gss_buffer_t token,2972gss_cred_id_t *cred_handle)2973{2974OM_uint32 ret;2975spnego_gss_cred_id_t spcred;2976gss_cred_id_t mcred;29772978ret = gss_import_cred(minor_status, token, &mcred);2979if (GSS_ERROR(ret))2980return (ret);29812982ret = create_spnego_cred(minor_status, mcred, &spcred);2983if (GSS_ERROR(ret))2984return (ret);29852986*cred_handle = (gss_cred_id_t)spcred;2987return (ret);2988}29892990OM_uint32 KRB5_CALLCONV2991spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,2992gss_qop_t qop_req, gss_iov_buffer_desc *iov,2993int iov_count)2994{2995spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;29962997if (sc->ctx_handle == GSS_C_NO_CONTEXT)2998return (GSS_S_NO_CONTEXT);29993000return gss_get_mic_iov(minor_status, sc->ctx_handle, qop_req, iov,3001iov_count);3002}30033004OM_uint32 KRB5_CALLCONV3005spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,3006gss_qop_t *qop_state, gss_iov_buffer_desc *iov,3007int iov_count)3008{3009spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;30103011if (sc->ctx_handle == GSS_C_NO_CONTEXT)3012return (GSS_S_NO_CONTEXT);30133014return gss_verify_mic_iov(minor_status, sc->ctx_handle, qop_state, iov,3015iov_count);3016}30173018OM_uint32 KRB5_CALLCONV3019spnego_gss_get_mic_iov_length(OM_uint32 *minor_status,3020gss_ctx_id_t context_handle, gss_qop_t qop_req,3021gss_iov_buffer_desc *iov, int iov_count)3022{3023spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;30243025if (sc->ctx_handle == GSS_C_NO_CONTEXT)3026return (GSS_S_NO_CONTEXT);30273028return gss_get_mic_iov_length(minor_status, sc->ctx_handle, qop_req, iov,3029iov_count);3030}30313032/*3033* We will release everything but the ctx_handle so that it3034* can be passed back to init/accept context. This routine should3035* not be called until after the ctx_handle memory is assigned to3036* the supplied context handle from init/accept context.3037*/3038static void3039release_spnego_ctx(spnego_gss_ctx_id_t *ctx)3040{3041spnego_gss_ctx_id_t context;3042OM_uint32 minor_stat;3043context = *ctx;30443045if (context != NULL) {3046(void) gss_release_buffer(&minor_stat,3047&context->DER_mechTypes);30483049(void) gss_release_oid_set(&minor_stat, &context->mech_set);30503051(void) gss_release_name(&minor_stat, &context->internal_name);3052(void) gss_release_cred(&minor_stat, &context->deleg_cred);30533054negoex_release_context(context);30553056free(context);3057*ctx = NULL;3058}3059}30603061/*3062* Can't use gss_indicate_mechs by itself to get available mechs for3063* SPNEGO because it will also return the SPNEGO mech and we do not3064* want to consider SPNEGO as an available security mech for3065* negotiation. For this reason, get_available_mechs will return3066* all available, non-deprecated mechs except SPNEGO and NegoEx-3067* only mechanisms.3068*3069* Note that gss_acquire_cred_from(GSS_C_NO_OID_SET) will filter3070* out hidden (GSS_C_MA_NOT_INDICATED) mechanisms such as NegoEx, so3071* calling gss_indicate_mechs_by_attrs() also works around that.3072*3073* If a ptr to a creds list is given, this function will attempt3074* to acquire creds for the creds given and trim the list of3075* returned mechanisms to only those for which creds are valid.3076*3077*/3078static OM_uint323079get_available_mechs(OM_uint32 *minor_status,3080gss_name_t name, gss_cred_usage_t usage,3081gss_const_key_value_set_t cred_store,3082gss_cred_id_t *creds, gss_OID_set *rmechs, OM_uint32 *time_rec)3083{3084OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;3085gss_OID_set mechs, goodmechs;3086gss_OID_set_desc except_attrs;3087gss_OID_desc attr_oids[3];30883089*rmechs = GSS_C_NO_OID_SET;30903091attr_oids[0] = *GSS_C_MA_DEPRECATED;3092attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH;3093attr_oids[2] = *GSS_C_MA_MECH_NEGO; /* Exclude ourselves */3094except_attrs.count = sizeof(attr_oids) / sizeof(attr_oids[0]);3095except_attrs.elements = attr_oids;3096major_status = gss_indicate_mechs_by_attrs(minor_status,3097GSS_C_NO_OID_SET,3098&except_attrs,3099GSS_C_NO_OID_SET, &mechs);31003101/*3102* If the caller wanted a list of creds returned,3103* trim the list of mechanisms down to only those3104* for which the creds are valid.3105*/3106if (mechs->count > 0 && major_status == GSS_S_COMPLETE &&3107creds != NULL) {3108major_status = gss_acquire_cred_from(minor_status, name,3109GSS_C_INDEFINITE,3110mechs, usage,3111cred_store, creds,3112&goodmechs, time_rec);31133114/*3115* Drop the old list in favor of the new3116* "trimmed" list.3117*/3118if (major_status == GSS_S_COMPLETE) {3119(void) gss_release_oid_set(&tmpmin, &mechs);3120mechs = goodmechs;3121}3122}31233124if (mechs->count > 0 && major_status == GSS_S_COMPLETE) {3125*rmechs = mechs;3126} else {3127(void) gss_release_oid_set(&tmpmin, &mechs);3128*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;3129map_errcode(minor_status);3130if (major_status == GSS_S_COMPLETE)3131major_status = GSS_S_FAILURE;3132}31333134return (major_status);3135}31363137/* Return true if mech asserts the GSS_C_MA_NEGOEX_AND_SPNEGO attribute. */3138static int3139negoex_and_spnego(gss_OID mech)3140{3141OM_uint32 ret, minor;3142gss_OID_set attrs;3143int present;31443145ret = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);3146if (ret != GSS_S_COMPLETE || attrs == GSS_C_NO_OID_SET)3147return 0;31483149(void) generic_gss_test_oid_set_member(&minor,3150GSS_C_MA_NEGOEX_AND_SPNEGO,3151attrs, &present);3152(void) gss_release_oid_set(&minor, &attrs);3153return present;3154}31553156/*3157* Fill sc->mech_set with the SPNEGO-negotiable mechanism OIDs, and3158* sc->negoex_mechs with an entry for each NegoEx-negotiable mechanism. Take3159* into account the mech set provided with gss_set_neg_mechs() if it exists.3160*/3161static OM_uint323162get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,3163spnego_gss_cred_id_t spcred, gss_cred_usage_t usage)3164{3165OM_uint32 ret, tmpmin;3166gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;3167gss_OID_set cred_mechs = GSS_C_NULL_OID_SET, mechs;3168unsigned int i;3169int present, added_negoex = 0;3170auth_scheme scheme;31713172if (spcred != NULL) {3173/* Get the list of mechs in the mechglue cred. */3174ret = gss_inquire_cred(minor_status, spcred->mcred, NULL,3175NULL, NULL, &cred_mechs);3176if (ret != GSS_S_COMPLETE)3177return (ret);3178} else {3179/* Start with the list of available mechs. */3180ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,3181GSS_C_NO_CRED_STORE, &creds,3182&cred_mechs, NULL);3183if (ret != GSS_S_COMPLETE)3184return (ret);3185gss_release_cred(&tmpmin, &creds);3186}31873188/* If gss_set_neg_mechs() was called, use that to determine the3189* iteration order. Otherwise iterate over the credential mechs. */3190mechs = (spcred != NULL && spcred->neg_mechs != GSS_C_NULL_OID_SET) ?3191spcred->neg_mechs : cred_mechs;31923193ret = gss_create_empty_oid_set(minor_status, &sc->mech_set);3194if (ret != GSS_S_COMPLETE)3195goto cleanup;31963197for (i = 0; i < mechs->count; i++) {3198if (mechs != cred_mechs) {3199/* Intersect neg_mechs with cred_mechs. */3200gss_test_oid_set_member(&tmpmin, &mechs->elements[i],3201cred_mechs, &present);3202if (!present)3203continue;3204}32053206/* Query the auth scheme to see if this is a NegoEx mech. */3207ret = gssspi_query_mechanism_info(&tmpmin, &mechs->elements[i],3208scheme);3209if (ret == GSS_S_COMPLETE) {3210/* Add an entry for this mech to the NegoEx list. */3211ret = negoex_add_auth_mech(minor_status, sc,3212&mechs->elements[i],3213scheme);3214if (ret != GSS_S_COMPLETE)3215goto cleanup;32163217/* Add the NegoEx OID to the SPNEGO list at the3218* position of the first NegoEx mechanism. */3219if (!added_negoex) {3220ret = gss_add_oid_set_member(minor_status,3221&negoex_mech,3222&sc->mech_set);3223if (ret != GSS_S_COMPLETE)3224goto cleanup;3225added_negoex = 1;3226}32273228/* Skip this mech in the SPNEGO list unless it asks for3229* direct SPNEGO negotiation. */3230if (!negoex_and_spnego(&mechs->elements[i]))3231continue;3232}32333234/* Add this mech to the SPNEGO list. */3235ret = gss_add_oid_set_member(minor_status, &mechs->elements[i],3236&sc->mech_set);3237if (ret != GSS_S_COMPLETE)3238goto cleanup;3239}32403241*minor_status = 0;32423243cleanup:3244if (ret != GSS_S_COMPLETE || sc->mech_set->count == 0) {3245*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;3246map_errcode(minor_status);3247ret = GSS_S_FAILURE;3248}32493250gss_release_oid_set(&tmpmin, &cred_mechs);3251return (ret);3252}32533254/* following are token creation and reading routines */32553256/*3257* If in contains a tagged OID encoding, return a copy of the contents as a3258* gss_OID and advance in past the encoding. Otherwise return NULL and do not3259* advance in.3260*/3261static gss_OID3262get_mech_oid(OM_uint32 *minor_status, struct k5input *in)3263{3264struct k5input oidrep;3265OM_uint32 status;3266gss_OID_desc oid;3267gss_OID mech_out = NULL;32683269if (!k5_der_get_value(in, MECH_OID, &oidrep))3270return (NULL);32713272oid.length = oidrep.len;3273oid.elements = (uint8_t *)oidrep.ptr;3274status = generic_gss_copy_oid(minor_status, &oid, &mech_out);3275if (status != GSS_S_COMPLETE) {3276map_errcode(minor_status);3277mech_out = NULL;3278}32793280return (mech_out);3281}32823283/*3284* If in contains a tagged octet string encoding, return a copy of the contents3285* as a gss_buffer_t and advance in past the encoding. Otherwise return NULL3286* and do not advance in.3287*/3288static gss_buffer_t3289get_octet_string(struct k5input *in)3290{3291gss_buffer_t input_token;3292struct k5input ostr;32933294if (!k5_der_get_value(in, OCTET_STRING, &ostr))3295return (NULL);32963297input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));3298if (input_token == NULL)3299return (NULL);33003301input_token->length = ostr.len;3302if (input_token->length > 0) {3303input_token->value = gssalloc_malloc(input_token->length);3304if (input_token->value == NULL) {3305free(input_token);3306return (NULL);3307}33083309memcpy(input_token->value, ostr.ptr, input_token->length);3310} else {3311input_token->value = NULL;3312}3313return (input_token);3314}33153316/*3317* verify that buff_in points to a sequence of der encoding. The mech3318* set is the only sequence of encoded object in the token, so if it is3319* a sequence of encoding, decode the mechset into a gss_OID_set and3320* return it, advancing the buffer pointer.3321*/3322static gss_OID_set3323get_mech_set(OM_uint32 *minor_status, struct k5input *in)3324{3325gss_OID_set returned_mechSet;3326OM_uint32 major_status, tmpmin;3327struct k5input seq;33283329if (!k5_der_get_value(in, SEQUENCE_OF, &seq))3330return (NULL);33313332major_status = gss_create_empty_oid_set(minor_status,3333&returned_mechSet);3334if (major_status != GSS_S_COMPLETE)3335return (NULL);33363337while (!seq.status && seq.len > 0) {3338gss_OID_desc *oid = get_mech_oid(minor_status, &seq);33393340if (oid == NULL) {3341gss_release_oid_set(&tmpmin, &returned_mechSet);3342return (NULL);3343}33443345major_status = gss_add_oid_set_member(minor_status,3346oid, &returned_mechSet);3347generic_gss_release_oid(minor_status, &oid);3348if (major_status != GSS_S_COMPLETE) {3349gss_release_oid_set(&tmpmin, &returned_mechSet);3350return (NULL);3351}3352}33533354return (returned_mechSet);3355}33563357/*3358* Encode mechSet into buf.3359*/3360static int3361put_mech_set(gss_OID_set mechSet, gss_buffer_t buffer_out)3362{3363uint8_t *ptr;3364size_t ilen, tlen, i;3365struct k5buf buf;33663367ilen = 0;3368for (i = 0; i < mechSet->count; i++)3369ilen += k5_der_value_len(mechSet->elements[i].length);3370tlen = k5_der_value_len(ilen);33713372ptr = gssalloc_malloc(tlen);3373if (ptr == NULL)3374return -1;3375k5_buf_init_fixed(&buf, ptr, tlen);33763377k5_der_add_taglen(&buf, SEQUENCE_OF, ilen);3378for (i = 0; i < mechSet->count; i++) {3379k5_der_add_value(&buf, MECH_OID,3380mechSet->elements[i].elements,3381mechSet->elements[i].length);3382}3383assert(buf.len == tlen);33843385buffer_out->value = ptr;3386buffer_out->length = tlen;3387return 0;3388}33893390/* Decode SPNEGO request flags from the DER encoding of a bit string and set3391* them in *ret_flags. */3392static OM_uint323393get_req_flags(struct k5input *in, OM_uint32 *req_flags)3394{3395if (in->status || in->len != 4 ||3396k5_input_get_byte(in) != BIT_STRING ||3397k5_input_get_byte(in) != BIT_STRING_LENGTH ||3398k5_input_get_byte(in) != BIT_STRING_PADDING)3399return GSS_S_DEFECTIVE_TOKEN;34003401*req_flags = k5_input_get_byte(in) >> 1;3402return GSS_S_COMPLETE;3403}34043405static OM_uint323406get_negTokenInit(OM_uint32 *minor_status,3407gss_buffer_t buf,3408gss_buffer_t der_mechSet,3409gss_OID_set *mechSet,3410OM_uint32 *req_flags,3411gss_buffer_t *mechtok,3412gss_buffer_t *mechListMIC)3413{3414OM_uint32 err;3415struct k5input in, seq, field;34163417*minor_status = 0;3418der_mechSet->length = 0;3419der_mechSet->value = NULL;3420*mechSet = GSS_C_NO_OID_SET;3421*req_flags = 0;3422*mechtok = *mechListMIC = GSS_C_NO_BUFFER;34233424k5_input_init(&in, buf->value, buf->length);34253426/* Advance past the framing header. */3427err = verify_token_header(&in, gss_mech_spnego);3428if (err)3429return GSS_S_DEFECTIVE_TOKEN;34303431/* Advance past the [0] tag for the NegotiationToken choice. */3432if (!k5_der_get_value(&in, CONTEXT, &seq))3433return GSS_S_DEFECTIVE_TOKEN;34343435/* Advance past the SEQUENCE tag. */3436if (!k5_der_get_value(&seq, SEQUENCE, &seq))3437return GSS_S_DEFECTIVE_TOKEN;34383439/* Get the contents of the mechTypes field. Reject an empty field here3440* since we musn't allocate a zero-length buffer in the next step. */3441if (!k5_der_get_value(&seq, CONTEXT, &field) || field.len == 0)3442return GSS_S_DEFECTIVE_TOKEN;34433444/* Store a copy of the contents for MIC computation. */3445der_mechSet->value = gssalloc_malloc(field.len);3446if (der_mechSet->value == NULL)3447return GSS_S_FAILURE;3448memcpy(der_mechSet->value, field.ptr, field.len);3449der_mechSet->length = field.len;34503451/* Decode the contents into an OID set. */3452*mechSet = get_mech_set(minor_status, &field);3453if (*mechSet == NULL)3454return GSS_S_FAILURE;34553456if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {3457err = get_req_flags(&field, req_flags);3458if (err != GSS_S_COMPLETE)3459return err;3460}34613462if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {3463*mechtok = get_octet_string(&field);3464if (*mechtok == GSS_C_NO_BUFFER)3465return GSS_S_FAILURE;3466}34673468if (k5_der_get_value(&seq, CONTEXT | 0x03, &field)) {3469*mechListMIC = get_octet_string(&field);3470if (*mechListMIC == GSS_C_NO_BUFFER)3471return GSS_S_FAILURE;3472}34733474return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;3475}34763477/* Decode a NegotiationToken of type negTokenResp. */3478static OM_uint323479get_negTokenResp(OM_uint32 *minor_status, struct k5input *in,3480OM_uint32 *negState, gss_OID *supportedMech,3481gss_buffer_t *responseToken, gss_buffer_t *mechListMIC)3482{3483struct k5input seq, field, en;34843485*negState = UNSPECIFIED;3486*supportedMech = GSS_C_NO_OID;3487*responseToken = *mechListMIC = GSS_C_NO_BUFFER;34883489/* Advance past the [1] tag for the NegotiationToken choice. */3490if (!k5_der_get_value(in, CONTEXT | 0x01, &seq))3491return GSS_S_DEFECTIVE_TOKEN;34923493/* Advance seq past the SEQUENCE tag (historically this code allows the3494* tag to be missing). */3495(void)k5_der_get_value(&seq, SEQUENCE, &seq);34963497if (k5_der_get_value(&seq, CONTEXT, &field)) {3498if (!k5_der_get_value(&field, ENUMERATED, &en))3499return GSS_S_DEFECTIVE_TOKEN;3500if (en.len != ENUMERATION_LENGTH)3501return GSS_S_DEFECTIVE_TOKEN;3502*negState = *en.ptr;3503}35043505if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {3506*supportedMech = get_mech_oid(minor_status, &field);3507if (*supportedMech == GSS_C_NO_OID)3508return GSS_S_DEFECTIVE_TOKEN;3509}35103511if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {3512*responseToken = get_octet_string(&field);3513if (*responseToken == GSS_C_NO_BUFFER)3514return GSS_S_DEFECTIVE_TOKEN;3515}35163517if (k5_der_get_value(&seq, CONTEXT | 0x04, &field)) {3518*mechListMIC = get_octet_string(&field);35193520/* Handle Windows 2000 duplicate response token */3521if (*responseToken &&3522((*responseToken)->length == (*mechListMIC)->length) &&3523!memcmp((*responseToken)->value, (*mechListMIC)->value,3524(*responseToken)->length)) {3525OM_uint32 tmpmin;35263527gss_release_buffer(&tmpmin, *mechListMIC);3528free(*mechListMIC);3529*mechListMIC = NULL;3530}3531}35323533return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;3534}35353536/*3537* This routine compares the received mechset to the mechset that3538* this server can support. It looks sequentially through the mechset3539* and the first one that matches what the server can support is3540* chosen as the negotiated mechanism. If one is found, negResult3541* is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if3542* it's not the first mech, otherwise we return NULL and negResult3543* is set to REJECT. The returned pointer is an alias into3544* received->elements and should not be freed.3545*3546* NOTE: There is currently no way to specify a preference order of3547* mechanisms supported by the acceptor.3548*/3549static gss_OID3550negotiate_mech(spnego_gss_ctx_id_t ctx, gss_OID_set received,3551OM_uint32 *negResult)3552{3553size_t i, j;3554int wrong_krb5_oid;35553556for (i = 0; i < received->count; i++) {3557gss_OID mech_oid = &received->elements[i];35583559/* Accept wrong mechanism OID from MS clients */3560wrong_krb5_oid = 0;3561if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid)) {3562mech_oid = (gss_OID)&gss_mech_krb5_oid;3563wrong_krb5_oid = 1;3564}35653566for (j = 0; j < ctx->mech_set->count; j++) {3567if (g_OID_equal(mech_oid,3568&ctx->mech_set->elements[j])) {3569*negResult = (i == 0) ? ACCEPT_INCOMPLETE :3570REQUEST_MIC;3571return wrong_krb5_oid ?3572(gss_OID)&gss_mech_krb5_wrong_oid :3573&ctx->mech_set->elements[j];3574}3575}3576}3577*negResult = REJECT;3578return (NULL);3579}35803581/*3582* the next two routines make a token buffer suitable for3583* spnego_gss_display_status. These currently take the string3584* in name and place it in the token. Eventually, if3585* spnego_gss_display_status returns valid error messages,3586* these routines will be changes to return the error string.3587*/3588static spnego_token_t3589make_spnego_token(const char *name)3590{3591return (spnego_token_t)gssalloc_strdup(name);3592}35933594static gss_buffer_desc3595make_err_msg(const char *name)3596{3597gss_buffer_desc buffer;35983599if (name == NULL) {3600buffer.length = 0;3601buffer.value = NULL;3602} else {3603buffer.length = strlen(name)+1;3604buffer.value = make_spnego_token(name);3605}36063607return (buffer);3608}36093610/*3611* Create the client side spnego token passed back to gss_init_sec_context3612* and eventually up to the application program and over to the server.3613*3614* Use DER rules, definite length method per RFC 24783615*/3616static int3617make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, int negHintsCompat,3618gss_buffer_t mic, OM_uint32 req_flags,3619gss_buffer_t token, send_token_flag sendtoken,3620gss_buffer_t outbuf)3621{3622size_t f0len, f2len, f3len, fields_len, seq_len, choice_len;3623size_t mech_len, framed_len;3624uint8_t *t;3625struct k5buf buf;36263627if (outbuf == GSS_C_NO_BUFFER)3628return (-1);36293630outbuf->length = 0;3631outbuf->value = NULL;36323633/* Calculate the length of each field and the total fields length. */3634fields_len = 0;3635/* mechTypes [0] MechTypeList, previously assembled in spnego_ctx */3636f0len = spnego_ctx->DER_mechTypes.length;3637fields_len += k5_der_value_len(f0len);3638if (token != NULL) {3639/* mechToken [2] OCTET STRING OPTIONAL */3640f2len = k5_der_value_len(token->length);3641fields_len += k5_der_value_len(f2len);3642}3643if (mic != GSS_C_NO_BUFFER) {3644/* mechListMIC [3] OCTET STRING OPTIONAL */3645f3len = k5_der_value_len(mic->length);3646fields_len += k5_der_value_len(f3len);3647}36483649/* Calculate the length of the sequence and choice. */3650seq_len = k5_der_value_len(fields_len);3651choice_len = k5_der_value_len(seq_len);36523653/* Calculate the framed token length. */3654mech_len = k5_der_value_len(gss_mech_spnego->length);3655framed_len = k5_der_value_len(mech_len + choice_len);36563657/* Allocate space and prepare a buffer. */3658t = gssalloc_malloc(framed_len);3659if (t == NULL)3660return (-1);3661k5_buf_init_fixed(&buf, t, framed_len);36623663/* Add generic token framing. */3664k5_der_add_taglen(&buf, HEADER_ID, mech_len + choice_len);3665k5_der_add_value(&buf, MECH_OID, gss_mech_spnego->elements,3666gss_mech_spnego->length);36673668/* Add NegotiationToken choice tag and NegTokenInit sequence tag. */3669k5_der_add_taglen(&buf, CONTEXT | 0x00, seq_len);3670k5_der_add_taglen(&buf, SEQUENCE, fields_len);36713672/* Add the already-encoded mechanism list as mechTypes. */3673k5_der_add_value(&buf, CONTEXT | 0x00, spnego_ctx->DER_mechTypes.value,3674spnego_ctx->DER_mechTypes.length);36753676if (token != NULL) {3677k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);3678k5_der_add_value(&buf, OCTET_STRING, token->value,3679token->length);3680}36813682if (mic != GSS_C_NO_BUFFER) {3683uint8_t id = negHintsCompat ? SEQUENCE : OCTET_STRING;3684k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);3685k5_der_add_value(&buf, id, mic->value, mic->length);3686}36873688assert(buf.len == framed_len);3689outbuf->length = framed_len;3690outbuf->value = t;36913692return (0);3693}36943695/*3696* create the server side spnego token passed back to3697* gss_accept_sec_context and eventually up to the application program3698* and over to the client.3699*/3700static OM_uint323701make_spnego_tokenTarg_msg(uint8_t status, gss_OID mech_wanted,3702gss_buffer_t token, gss_buffer_t mic,3703send_token_flag sendtoken,3704gss_buffer_t outbuf)3705{3706size_t f0len, f1len, f2len, f3len, fields_len, seq_len, choice_len;3707uint8_t *t;3708struct k5buf buf;37093710if (outbuf == GSS_C_NO_BUFFER)3711return (GSS_S_DEFECTIVE_TOKEN);3712if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)3713return (GSS_S_DEFECTIVE_TOKEN);37143715outbuf->length = 0;3716outbuf->value = NULL;37173718/* Calculate the length of each field and the total fields length. */3719fields_len = 0;3720/* negState [0] ENUMERATED { ... } OPTIONAL */3721f0len = k5_der_value_len(1);3722fields_len += k5_der_value_len(f0len);3723if (sendtoken == INIT_TOKEN_SEND) {3724/* supportedMech [1] MechType OPTIONAL */3725f1len = k5_der_value_len(mech_wanted->length);3726fields_len += k5_der_value_len(f1len);3727}3728if (token != NULL && token->length > 0) {3729/* mechToken [2] OCTET STRING OPTIONAL */3730f2len = k5_der_value_len(token->length);3731fields_len += k5_der_value_len(f2len);3732}3733if (mic != NULL) {3734/* mechListMIC [3] OCTET STRING OPTIONAL */3735f3len = k5_der_value_len(mic->length);3736fields_len += k5_der_value_len(f3len);3737}37383739/* Calculate the length of the sequence and choice. */3740seq_len = k5_der_value_len(fields_len);3741choice_len = k5_der_value_len(seq_len);37423743/* Allocate space and prepare a buffer. */3744t = gssalloc_malloc(choice_len);3745if (t == NULL)3746return (GSS_S_DEFECTIVE_TOKEN);3747k5_buf_init_fixed(&buf, t, choice_len);37483749/* Add the choice tag and begin the sequence. */3750k5_der_add_taglen(&buf, CONTEXT | 0x01, seq_len);3751k5_der_add_taglen(&buf, SEQUENCE, fields_len);37523753/* Add the negState field. */3754k5_der_add_taglen(&buf, CONTEXT | 0x00, f0len);3755k5_der_add_value(&buf, ENUMERATED, &status, 1);37563757if (sendtoken == INIT_TOKEN_SEND) {3758/* Add the supportedMech field. */3759k5_der_add_taglen(&buf, CONTEXT | 0x01, f1len);3760k5_der_add_value(&buf, MECH_OID, mech_wanted->elements,3761mech_wanted->length);3762}37633764if (token != NULL && token->length > 0) {3765/* Add the mechToken field. */3766k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);3767k5_der_add_value(&buf, OCTET_STRING, token->value,3768token->length);3769}37703771if (mic != NULL) {3772/* Add the mechListMIC field. */3773k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);3774k5_der_add_value(&buf, OCTET_STRING, mic->value, mic->length);3775}37763777assert(buf.len == choice_len);3778outbuf->length = choice_len;3779outbuf->value = t;37803781return (0);3782}37833784/* Advance in past the [APPLICATION 0] tag and thisMech field of an3785* InitialContextToken encoding, checking that thisMech matches mech. */3786static int3787verify_token_header(struct k5input *in, gss_OID_const mech)3788{3789gss_OID_desc oid;3790struct k5input field;37913792if (!k5_der_get_value(in, HEADER_ID, in))3793return (G_BAD_TOK_HEADER);3794if (!k5_der_get_value(in, MECH_OID, &field))3795return (G_BAD_TOK_HEADER);37963797oid.length = field.len;3798oid.elements = (uint8_t *)field.ptr;3799return g_OID_equal(&oid, mech) ? 0 : G_WRONG_MECH;3800}38013802/*3803* Return non-zero if the oid is one of the kerberos mech oids,3804* otherwise return zero.3805*3806* N.B. There are 3 oids that represent the kerberos mech:3807* RFC-specified GSS_MECH_KRB5_OID,3808* Old pre-RFC GSS_MECH_KRB5_OLD_OID,3809* Incorrect MS GSS_MECH_KRB5_WRONG_OID3810*/38113812static int3813is_kerb_mech(gss_OID oid)3814{3815int answer = 0;3816OM_uint32 minor;3817extern const gss_OID_set_desc * const gss_mech_set_krb5_both;38183819(void) gss_test_oid_set_member(&minor,3820oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);38213822return (answer);3823}382438253826