2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
2N/A * All rights reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * A module that implements the spnego security mechanism.
2N/A * It is used to negotiate the security mechanism between
2N/A * peers using the GSS-API.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2006-2008, Novell, Inc.
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions are met:
2N/A *
2N/A * * Redistributions of source code must retain the above copyright notice,
2N/A * this list of conditions and the following disclaimer.
2N/A * * Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * * The copyright holder's name is not used to endorse or promote products
2N/A * derived from this software without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2N/A * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2N/A * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2N/A * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2N/A * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2N/A * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2N/A * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2N/A * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2N/A * POSSIBILITY OF SUCH DAMAGE.
2N/A */
2N/A/* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
2N/A
2N/A#include <sys/param.h>
2N/A#include <unistd.h>
2N/A#include <assert.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <k5-int.h>
2N/A#include <krb5.h>
2N/A#include <mglueP.h>
2N/A#include "gssapiP_spnego.h"
2N/A#include "gssapiP_generic.h"
2N/A#include <gssapi_err_generic.h>
2N/A#include <locale.h>
2N/A
2N/A/*
2N/A * SUNW17PACresync
2N/A * MIT has diff names for these GSS utilities. Solaris needs to change
2N/A * them globally to get in sync w/MIT.
2N/A * Revisit for full 1.7 resync.
2N/A */
2N/A#define gssint_get_modOptions __gss_get_modOptions
2N/A#define gssint_der_length_size der_length_size
2N/A#define gssint_get_der_length get_der_length
2N/A#define gssint_put_der_length put_der_length
2N/A#define gssint_get_mechanism __gss_get_mechanism
2N/A#define gssint_get_mech_type __gss_get_mech_type
2N/A
2N/A
2N/A#undef g_token_size
2N/A#undef g_verify_token_header
2N/A#undef g_make_token_header
2N/A
2N/A#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
2N/Atypedef const gss_OID_desc *gss_OID_const;
2N/A
2N/A/* der routines defined in libgss */
2N/Aextern unsigned int gssint_der_length_size(OM_uint32);
2N/Aextern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*);
2N/Aextern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32);
2N/A
2N/A
2N/A/* private routines for spnego_mechanism */
2N/Astatic spnego_token_t make_spnego_token(char *);
2N/Astatic gss_buffer_desc make_err_msg(char *);
2N/Astatic int g_token_size(gss_OID_const, unsigned int);
2N/Astatic int g_make_token_header(gss_OID_const, unsigned int,
2N/A unsigned char **, unsigned int);
2N/Astatic int g_verify_token_header(gss_OID_const, unsigned int *,
2N/A unsigned char **,
2N/A int, unsigned int);
2N/Astatic int g_verify_neg_token_init(unsigned char **, unsigned int);
2N/Astatic gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
2N/Astatic gss_buffer_t get_input_token(unsigned char **, unsigned int);
2N/Astatic gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
2N/Astatic OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
2N/Astatic OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
2N/A gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
2N/Astatic void release_spnego_ctx(spnego_gss_ctx_id_t *);
2N/Astatic void check_spnego_options(spnego_gss_ctx_id_t);
2N/Astatic spnego_gss_ctx_id_t create_spnego_ctx(void);
2N/Astatic int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
2N/Astatic int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
2N/Astatic int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
2N/Astatic int put_negResult(unsigned char **, OM_uint32, unsigned int);
2N/A
2N/Astatic OM_uint32
2N/Aprocess_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
2N/A gss_buffer_t *, OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Ahandle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
2N/A gss_buffer_t *, OM_uint32 *, send_token_flag *);
2N/A
2N/Astatic OM_uint32
2N/Ainit_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
2N/A gss_OID_set *, send_token_flag *);
2N/Astatic OM_uint32
2N/Ainit_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
2N/A gss_buffer_t *, gss_buffer_t *,
2N/A OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Ainit_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
2N/A gss_buffer_t *, gss_buffer_t *,
2N/A OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Ainit_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
2N/A gss_OID, gss_buffer_t *, gss_buffer_t *,
2N/A OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Ainit_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
2N/A gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
2N/A gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
2N/A OM_uint32 *, send_token_flag *);
2N/A
2N/Astatic OM_uint32
2N/Aacc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
2N/A gss_cred_id_t, gss_buffer_t *,
2N/A gss_buffer_t *, OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Aacc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
2N/A gss_buffer_t *, gss_buffer_t *,
2N/A OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Aacc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
2N/A OM_uint32 *, send_token_flag *);
2N/Astatic OM_uint32
2N/Aacc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
2N/A gss_buffer_t, gss_OID *, gss_buffer_t,
2N/A OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
2N/A OM_uint32 *, send_token_flag *);
2N/A
2N/Astatic gss_OID
2N/Anegotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
2N/A OM_uint32 *);
2N/Astatic int
2N/Ag_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
2N/A
2N/Astatic int
2N/Amake_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
2N/A int,
2N/A gss_buffer_t,
2N/A OM_uint32, gss_buffer_t, send_token_flag,
2N/A gss_buffer_t);
2N/Astatic int
2N/Amake_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
2N/A gss_buffer_t, send_token_flag,
2N/A gss_buffer_t);
2N/A
2N/Astatic OM_uint32
2N/Aget_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
2N/A gss_OID_set *, OM_uint32 *, gss_buffer_t *,
2N/A gss_buffer_t *);
2N/Astatic OM_uint32
2N/Aget_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
2N/A OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
2N/A
2N/Astatic int
2N/Ais_kerb_mech(gss_OID oid);
2N/A
2N/A/* SPNEGO oid structure */
2N/Astatic const gss_OID_desc spnego_oids[] = {
2N/A {SPNEGO_OID_LENGTH, SPNEGO_OID},
2N/A};
2N/A
2N/Aconst gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
2N/Astatic const gss_OID_set_desc spnego_oidsets[] = {
2N/A {1, (gss_OID) spnego_oids+0},
2N/A};
2N/Aconst gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
2N/A
2N/A/* encoded OID octet string for NTLMSSP security mechanism */
2N/A#define GSS_MECH_NTLMSSP_OID_LENGTH 10
2N/A#define GSS_MECH_NTLMSSP_OID "\053\006\001\004\001\202\067\002\002\012"
2N/Astatic gss_OID_desc ntlmssp_oid = {
2N/A GSS_MECH_NTLMSSP_OID_LENGTH, GSS_MECH_NTLMSSP_OID
2N/A};
2N/A
2N/Astatic int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *);
2N/Astatic int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
2N/Astatic OM_uint32
2N/Aacc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
2N/A gss_buffer_t *, OM_uint32 *, send_token_flag *);
2N/A
2N/A#ifdef _GSS_STATIC_LINK
2N/Aint gss_spnegoint_lib_init(void);
2N/Avoid gss_spnegoint_lib_fini(void);
2N/A#else
2N/Agss_mechanism gss_mech_initialize(void);
2N/A#endif /* _GSS_STATIC_LINK */
2N/A
2N/A/*
2N/A * The Mech OID for SPNEGO:
2N/A * { iso(1) org(3) dod(6) internet(1) security(5)
2N/A * mechanism(5) spnego(2) }
2N/A */
2N/Astatic struct gss_config spnego_mechanism =
2N/A{
2N/A {SPNEGO_OID_LENGTH, SPNEGO_OID},
2N/A NULL, /* context */
2N/A glue_spnego_gss_acquire_cred,
2N/A glue_spnego_gss_release_cred,
2N/A glue_spnego_gss_init_sec_context,
2N/A#ifndef LEAN_CLIENT
2N/A glue_spnego_gss_accept_sec_context,
2N/A#else
2N/A NULL,
2N/A#endif /* LEAN_CLIENT */
2N/A NULL, /* gss_process_context_token */
2N/A glue_spnego_gss_delete_sec_context, /* gss_delete_sec_context */
2N/A glue_spnego_gss_context_time,
2N/A spnego_gss_get_mic, /* gss_get_mic */
2N/A spnego_gss_verify_mic, /* gss_verify_mic */
2N/A/* EXPORT DELETE START */ /* CRYPT DELETE START */
2N/A NULL, /* seal */
2N/A/* EXPORT DELETE END */ /* CRYPT DELETE END */
2N/A/* EXPORT DELETE START */ /* CRYPT DELETE START */
2N/A NULL, /* unseal */
2N/A/* EXPORT DELETE END */ /* CRYPT DELETE END */
2N/A glue_spnego_gss_display_status,
2N/A NULL, /* gss_indicate_mechs */
2N/A glue_spnego_gss_compare_name,
2N/A glue_spnego_gss_display_name,
2N/A glue_spnego_gss_import_name, /* glue */
2N/A glue_spnego_gss_release_name,
2N/A NULL, /* gss_inquire_cred */
2N/A NULL, /* gss_add_cred */
2N/A#ifndef LEAN_CLIENT
2N/A glue_spnego_gss_export_sec_context, /* gss_export_sec_context */
2N/A glue_spnego_gss_import_sec_context, /* gss_import_sec_context */
2N/A#else
2N/A NULL, /* gss_export_sec_context */
2N/A NULL, /* gss_import_sec_context */
2N/A#endif /* LEAN_CLIENT */
2N/A NULL, /* gss_inquire_cred_by_mech */
2N/A glue_spnego_gss_inquire_names_for_mech,
2N/A glue_spnego_gss_inquire_context,
2N/A NULL, /* gss_internal_release_oid */
2N/A glue_spnego_gss_wrap_size_limit,
2N/A NULL, /* pname */
2N/A NULL, /* userok */
2N/A NULL, /* gss_export_name */
2N/A/* EXPORT DELETE START */
2N/A/* CRYPT DELETE START */
2N/A#if 0
2N/A/* CRYPT DELETE END */
2N/A NULL, /* seal */
2N/A NULL, /* unseal */
2N/A/* CRYPT DELETE START */
2N/A#endif
2N/A/* CRYPT DELETE END */
2N/A/* EXPORT DELETE END */
2N/A NULL, /* sign */
2N/A NULL, /* verify */
2N/A NULL, /* gss_store_cred */
2N/A spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
2N/A NULL, /* gss_inquire_cred_by_oid */
2N/A NULL, /* gss_set_sec_context_option */
2N/A NULL, /* gssspi_set_cred_option */
2N/A NULL, /* gssspi_mech_invoke */
2N/A/* EXPORT DELETE START */ /* CRYPT DELETE START */
2N/A NULL, /* wrap_aead */
2N/A NULL, /* unwrap_aead */
2N/A NULL, /* gss_wrap_iov */
2N/A NULL, /* gss_unwrap_iov */
2N/A NULL, /* gss_wrap_iov_length */
2N/A/* EXPORT DELETE END */ /* CRYPT DELETE END */
2N/A NULL, /* complete_auth_token */
2N/A NULL, /* gss_acquire_cred_impersonate_name */
2N/A NULL, /* display_name_ext */
2N/A NULL, /* gss_inquire_name */
2N/A NULL, /* gss_get_name_attribute */
2N/A NULL, /* gss_set_name_attribute */
2N/A NULL, /* gss_delete_name_attribute */
2N/A NULL, /* gss_export_name_composite */
2N/A NULL, /* gss_map_name_to_any */
2N/A NULL, /* gss_release_any_name_mapping */
2N/A NULL, /* gss_pseudo_random */
2N/A NULL, /* set_neg_mechs */
2N/A};
2N/A
2N/A#ifdef _GSS_STATIC_LINK
2N/A#include "mglueP.h"
2N/A
2N/Astatic
2N/Aint gss_spnegomechglue_init(void)
2N/A{
2N/A struct gss_mech_config mech_spnego;
2N/A
2N/A memset(&mech_spnego, 0, sizeof(mech_spnego));
2N/A mech_spnego.mech = &spnego_mechanism;
2N/A mech_spnego.mechNameStr = "spnego";
2N/A mech_spnego.mech_type = GSS_C_NO_OID;
2N/A
2N/A return gssint_register_mechinfo(&mech_spnego);
2N/A}
2N/A#else
2N/A/* Entry point for libgss */
2N/Agss_mechanism KRB5_CALLCONV
2N/Agss_mech_initialize(void)
2N/A{
2N/A int err;
2N/A
2N/A err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
2N/A spnego_gss_delete_error_info);
2N/A if (err) {
2N/A syslog(LOG_NOTICE,
2N/A "SPNEGO gss_mech_initialize: error message TSD key register fail");
2N/A return (NULL);
2N/A }
2N/A
2N/A return (&spnego_mechanism);
2N/A}
2N/A
2N/A#if 0 /* SUNW17PACresync */
2N/AMAKE_INIT_FUNCTION(gss_krb5int_lib_init);
2N/AMAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
2N/Aint gss_krb5int_lib_init(void)
2N/A#endif
2N/A
2N/A#endif /* _GSS_STATIC_LINK */
2N/A
2N/Astatic
2N/Aint gss_spnegoint_lib_init(void)
2N/A{
2N/A#ifdef _GSS_STATIC_LINK
2N/A return gss_spnegomechglue_init();
2N/A#else
2N/A int err;
2N/A
2N/A err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
2N/A spnego_gss_delete_error_info);
2N/A if (err) {
2N/A syslog(LOG_NOTICE,
2N/A "SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
2N/A err);
2N/A return err;
2N/A }
2N/A
2N/A return 0;
2N/A#endif
2N/A}
2N/A
2N/A#pragma fini(gss_spnegoint_lib_fini)
2N/Astatic void gss_spnegoint_lib_fini(void)
2N/A{
2N/A gssint_mecherrmap_destroy();
2N/A k5_key_delete(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_acquire_cred(
2N/A OM_uint32 *minor_status,
2N/A gss_name_t desired_name,
2N/A OM_uint32 time_req,
2N/A gss_OID_set desired_mechs,
2N/A gss_cred_usage_t cred_usage,
2N/A gss_cred_id_t *output_cred_handle,
2N/A gss_OID_set *actual_mechs,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A return(spnego_gss_acquire_cred(minor_status,
2N/A desired_name,
2N/A time_req,
2N/A desired_mechs,
2N/A cred_usage,
2N/A output_cred_handle,
2N/A actual_mechs,
2N/A time_rec));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_acquire_cred(OM_uint32 *minor_status,
2N/A gss_name_t desired_name,
2N/A OM_uint32 time_req,
2N/A gss_OID_set desired_mechs,
2N/A gss_cred_usage_t cred_usage,
2N/A gss_cred_id_t *output_cred_handle,
2N/A gss_OID_set *actual_mechs,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A OM_uint32 status;
2N/A gss_OID_set amechs;
2N/A dsyslog("Entering spnego_gss_acquire_cred\n");
2N/A
2N/A if (actual_mechs)
2N/A *actual_mechs = NULL;
2N/A
2N/A if (time_rec)
2N/A *time_rec = 0;
2N/A
2N/A /*
2N/A * If the user did not specify a list of mechs,
2N/A * use get_available_mechs to collect a list of
2N/A * mechs for which creds are available.
2N/A */
2N/A if (desired_mechs == GSS_C_NULL_OID_SET) {
2N/A status = get_available_mechs(minor_status,
2N/A desired_name, cred_usage,
2N/A output_cred_handle, &amechs);
2N/A } else {
2N/A /*
2N/A * The caller gave a specific list of mechanisms,
2N/A * so just get whatever creds are available.
2N/A * gss_acquire_creds will return the subset of mechs for
2N/A * which the given 'output_cred_handle' is valid.
2N/A */
2N/A status = gss_acquire_cred(minor_status,
2N/A desired_name, time_req,
2N/A desired_mechs, cred_usage,
2N/A output_cred_handle, &amechs,
2N/A time_rec);
2N/A }
2N/A
2N/A if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
2N/A (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
2N/A }
2N/A (void) gss_release_oid_set(minor_status, &amechs);
2N/A
2N/A dsyslog("Leaving spnego_gss_acquire_cred\n");
2N/A return (status);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_release_cred(
2N/A OM_uint32 *minor_status,
2N/A gss_cred_id_t *cred_handle)
2N/A{
2N/A return( spnego_gss_release_cred(minor_status, cred_handle));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_release_cred(OM_uint32 *minor_status,
2N/A gss_cred_id_t *cred_handle)
2N/A{
2N/A OM_uint32 status;
2N/A
2N/A dsyslog("Entering spnego_gss_release_cred\n");
2N/A
2N/A if (minor_status == NULL || cred_handle == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A *minor_status = 0;
2N/A
2N/A if (*cred_handle == GSS_C_NO_CREDENTIAL)
2N/A return (GSS_S_COMPLETE);
2N/A
2N/A status = gss_release_cred(minor_status, cred_handle);
2N/A
2N/A dsyslog("Leaving spnego_gss_release_cred\n");
2N/A return (status);
2N/A}
2N/A
2N/Astatic void
2N/Acheck_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
2N/A{
2N/A spnego_ctx->optionStr = gssint_get_modOptions(
2N/A (const gss_OID)&spnego_oids[0]);
2N/A}
2N/A
2N/Astatic spnego_gss_ctx_id_t
2N/Acreate_spnego_ctx(void)
2N/A{
2N/A spnego_gss_ctx_id_t spnego_ctx = NULL;
2N/A spnego_ctx = (spnego_gss_ctx_id_t)
2N/A malloc(sizeof (spnego_gss_ctx_id_rec));
2N/A
2N/A if (spnego_ctx == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
2N/A spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
2N/A spnego_ctx->internal_mech = NULL;
2N/A spnego_ctx->optionStr = NULL;
2N/A spnego_ctx->DER_mechTypes.length = 0;
2N/A spnego_ctx->DER_mechTypes.value = NULL;
2N/A spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
2N/A spnego_ctx->mic_reqd = 0;
2N/A spnego_ctx->mic_sent = 0;
2N/A spnego_ctx->mic_rcvd = 0;
2N/A spnego_ctx->mech_complete = 0;
2N/A spnego_ctx->nego_done = 0;
2N/A spnego_ctx->internal_name = GSS_C_NO_NAME;
2N/A spnego_ctx->actual_mech = GSS_C_NO_OID;
2N/A spnego_ctx->err.msg = NULL;
2N/A spnego_ctx->err.scratch_buf[0] = 0;
2N/A check_spnego_options(spnego_ctx);
2N/A
2N/A return (spnego_ctx);
2N/A}
2N/A
2N/A/*
2N/A * Both initiator and acceptor call here to verify and/or create
2N/A * mechListMIC, and to consistency-check the MIC state.
2N/A */
2N/Astatic OM_uint32
2N/Ahandle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
2N/A int send_mechtok, spnego_gss_ctx_id_t sc,
2N/A gss_buffer_t *mic_out,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret;
2N/A
2N/A ret = GSS_S_FAILURE;
2N/A *mic_out = GSS_C_NO_BUFFER;
2N/A if (mic_in != GSS_C_NO_BUFFER) {
2N/A if (sc->mic_rcvd) {
2N/A /* Reject MIC if we've already received a MIC. */
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A } else if (sc->mic_reqd && !send_mechtok) {
2N/A /*
2N/A * If the peer sends the final mechanism token, it
2N/A * must send the MIC with that token if the
2N/A * negotiation requires MICs.
2N/A */
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A ret = process_mic(minor_status, mic_in, sc, mic_out,
2N/A negState, tokflag);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A return ret;
2N/A }
2N/A if (sc->mic_reqd) {
2N/A assert(sc->mic_sent || sc->mic_rcvd);
2N/A }
2N/A if (sc->mic_sent && sc->mic_rcvd) {
2N/A ret = GSS_S_COMPLETE;
2N/A *negState = ACCEPT_COMPLETE;
2N/A if (*mic_out == GSS_C_NO_BUFFER) {
2N/A /*
2N/A * We sent a MIC on the previous pass; we
2N/A * shouldn't be sending a mechanism token.
2N/A */
2N/A assert(!send_mechtok);
2N/A *tokflag = NO_TOKEN_SEND;
2N/A } else {
2N/A *tokflag = CONT_TOKEN_SEND;
2N/A }
2N/A } else if (sc->mic_reqd) {
2N/A *negState = ACCEPT_INCOMPLETE;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A } else if (*negState == ACCEPT_COMPLETE) {
2N/A ret = GSS_S_COMPLETE;
2N/A } else {
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Perform the actual verification and/or generation of mechListMIC.
2N/A */
2N/Astatic OM_uint32
2N/Aprocess_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
2N/A spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret, tmpmin;
2N/A gss_qop_t qop_state;
2N/A gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
2N/A
2N/A ret = GSS_S_FAILURE;
2N/A if (mic_in != GSS_C_NO_BUFFER) {
2N/A ret = gss_verify_mic(minor_status, sc->ctx_handle,
2N/A &sc->DER_mechTypes,
2N/A mic_in, &qop_state);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A return ret;
2N/A }
2N/A /* If we got a MIC, we must send a MIC. */
2N/A sc->mic_reqd = 1;
2N/A sc->mic_rcvd = 1;
2N/A }
2N/A if (sc->mic_reqd && !sc->mic_sent) {
2N/A ret = gss_get_mic(minor_status, sc->ctx_handle,
2N/A GSS_C_QOP_DEFAULT,
2N/A &sc->DER_mechTypes,
2N/A &tmpmic);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A gss_release_buffer(&tmpmin, &tmpmic);
2N/A *tokflag = NO_TOKEN_SEND;
2N/A return ret;
2N/A }
2N/A *mic_out = malloc(sizeof(gss_buffer_desc));
2N/A if (*mic_out == GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, &tmpmic);
2N/A *tokflag = NO_TOKEN_SEND;
2N/A return GSS_S_FAILURE;
2N/A }
2N/A **mic_out = tmpmic;
2N/A sc->mic_sent = 1;
2N/A }
2N/A return GSS_S_COMPLETE;
2N/A}
2N/A
2N/A/*
2N/A * Initial call to spnego_gss_init_sec_context().
2N/A */
2N/Astatic OM_uint32
2N/Ainit_ctx_new(OM_uint32 *minor_status,
2N/A gss_cred_id_t cred,
2N/A gss_ctx_id_t *ctx,
2N/A gss_OID_set *mechSet,
2N/A send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret, tmpmin;
2N/A gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
2N/A spnego_gss_ctx_id_t sc = NULL;
2N/A
2N/A /* determine negotiation mech set */
2N/A if (cred == GSS_C_NO_CREDENTIAL) {
2N/A ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
2N/A GSS_C_INITIATE, &creds, mechSet);
2N/A gss_release_cred(&tmpmin, &creds);
2N/A } else {
2N/A /*
2N/A * Use the list of mechs included in the cred that we
2N/A * were given.
2N/A */
2N/A ret = gss_inquire_cred(minor_status, cred,
2N/A NULL, NULL, NULL, mechSet);
2N/A }
2N/A if (ret != GSS_S_COMPLETE)
2N/A return ret;
2N/A
2N/A sc = create_spnego_ctx();
2N/A if (sc == NULL)
2N/A return GSS_S_FAILURE;
2N/A
2N/A /*
2N/A * need to pull the first mech from mechSet to do first
2N/A * gss_init_sec_context()
2N/A */
2N/A ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
2N/A &sc->internal_mech);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A map_errcode(minor_status);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
2N/A ret = GSS_S_FAILURE;
2N/A goto cleanup;
2N/A }
2N/A /*
2N/A * The actual context is not yet determined, set the output
2N/A * context handle to refer to the spnego context itself.
2N/A */
2N/A sc->ctx_handle = GSS_C_NO_CONTEXT;
2N/A *ctx = (gss_ctx_id_t)sc;
2N/A sc = NULL;
2N/A *tokflag = INIT_TOKEN_SEND;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A
2N/Acleanup:
2N/A release_spnego_ctx(&sc);
2N/A gss_release_oid_set(&tmpmin, mechSet);
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Called by second and later calls to spnego_gss_init_sec_context()
2N/A * to decode reply and update state.
2N/A */
2N/Astatic OM_uint32
2N/Ainit_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
2N/A gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret, tmpmin, acc_negState;
2N/A unsigned char *ptr;
2N/A spnego_gss_ctx_id_t sc;
2N/A gss_OID supportedMech = GSS_C_NO_OID;
2N/A
2N/A sc = (spnego_gss_ctx_id_t)*ctx;
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A
2N/A ptr = buf->value;
2N/A ret = get_negTokenResp(minor_status, ptr, buf->length,
2N/A &acc_negState, &supportedMech,
2N/A responseToken, mechListMIC);
2N/A if (ret != GSS_S_COMPLETE)
2N/A goto cleanup;
2N/A if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
2N/A supportedMech == GSS_C_NO_OID &&
2N/A *responseToken == GSS_C_NO_BUFFER &&
2N/A *mechListMIC == GSS_C_NO_BUFFER) {
2N/A /* Reject "empty" token. */
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A if (acc_negState == REJECT) {
2N/A *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
2N/A /* Solaris SPNEGO */
2N/A spnego_set_error_message(sc, *minor_status,
2N/A dgettext(TEXT_DOMAIN,
2N/A "SPNEGO failed to negotiate a mechanism: server rejected request"));
2N/A map_errcode(minor_status);
2N/A *tokflag = NO_TOKEN_SEND;
2N/A ret = GSS_S_FAILURE;
2N/A goto cleanup;
2N/A }
2N/A /*
2N/A * nego_done is false for the first call to init_ctx_cont()
2N/A */
2N/A if (!sc->nego_done) {
2N/A ret = init_ctx_nego(minor_status, sc,
2N/A acc_negState,
2N/A supportedMech, responseToken,
2N/A mechListMIC,
2N/A negState, tokflag);
2N/A } else if (!sc->mech_complete &&
2N/A *responseToken == GSS_C_NO_BUFFER) {
2N/A /*
2N/A * mech not finished and mech token missing
2N/A */
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A } else if (sc->mic_reqd &&
2N/A (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
2N/A *negState = ACCEPT_INCOMPLETE;
2N/A *tokflag = CONT_TOKEN_SEND;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A } else {
2N/A *negState = ACCEPT_COMPLETE;
2N/A *tokflag = NO_TOKEN_SEND;
2N/A ret = GSS_S_COMPLETE;
2N/A }
2N/Acleanup:
2N/A if (supportedMech != GSS_C_NO_OID)
2N/A generic_gss_release_oid(&tmpmin, &supportedMech);
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Consistency checking and mechanism negotiation handling for second
2N/A * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
2N/A * update internal state if acceptor has counter-proposed.
2N/A */
2N/Astatic OM_uint32
2N/Ainit_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
2N/A OM_uint32 acc_negState, gss_OID supportedMech,
2N/A gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret;
2N/A
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A /*
2N/A * Both supportedMech and negState must be present in first
2N/A * acceptor token.
2N/A */
2N/A if (supportedMech == GSS_C_NO_OID) {
2N/A *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
2N/A map_errcode(minor_status);
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
2N/A *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
2N/A /* Solaris SPNEGO */
2N/A spnego_set_error_message(sc, *minor_status,
2N/A dgettext(TEXT_DOMAIN,
2N/A "SPNEGO failed to negotiate a mechanism: defective token"));
2N/A map_errcode(minor_status);
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A
2N/A /*
2N/A * If the mechanism we sent is not the mechanism returned from
2N/A * the server, we need to handle the server's counter
2N/A * proposal. There is a bug in SAMBA servers that always send
2N/A * the old Kerberos mech OID, even though we sent the new one.
2N/A * So we will treat all the Kerberos mech OIDS as the same.
2N/A */
2N/A if (!(is_kerb_mech(supportedMech) &&
2N/A is_kerb_mech(sc->internal_mech)) &&
2N/A !g_OID_equal(supportedMech, sc->internal_mech)) {
2N/A ret = init_ctx_reselect(minor_status, sc,
2N/A acc_negState, supportedMech,
2N/A responseToken, mechListMIC,
2N/A negState, tokflag);
2N/A
2N/A } else if (*responseToken == GSS_C_NO_BUFFER) {
2N/A if (sc->mech_complete) {
2N/A /*
2N/A * Mech completed on first call to its
2N/A * init_sec_context(). Acceptor sends no mech
2N/A * token.
2N/A */
2N/A *negState = ACCEPT_COMPLETE;
2N/A *tokflag = NO_TOKEN_SEND;
2N/A ret = GSS_S_COMPLETE;
2N/A } else {
2N/A /*
2N/A * Reject missing mech token when optimistic
2N/A * mech selected.
2N/A */
2N/A *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
2N/A map_errcode(minor_status);
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A } else if (sc->mech_complete) {
2N/A /* Reject spurious mech token. */
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A } else {
2N/A *negState = ACCEPT_INCOMPLETE;
2N/A *tokflag = CONT_TOKEN_SEND;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A sc->nego_done = 1;
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Handle acceptor's counter-proposal of an alternative mechanism.
2N/A */
2N/Astatic OM_uint32
2N/Ainit_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
2N/A OM_uint32 acc_negState, gss_OID supportedMech,
2N/A gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret, tmpmin;
2N/A
2N/A generic_gss_release_oid(&tmpmin, &sc->internal_mech);
2N/A gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
2N/A GSS_C_NO_BUFFER);
2N/A
2N/A ret = generic_gss_copy_oid(minor_status, supportedMech,
2N/A &sc->internal_mech);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A map_errcode(minor_status);
2N/A sc->internal_mech = GSS_C_NO_OID;
2N/A *tokflag = NO_TOKEN_SEND;
2N/A return ret;
2N/A }
2N/A if (*responseToken != GSS_C_NO_BUFFER) {
2N/A /* Reject spurious mech token. */
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A /*
2N/A * Windows 2003 and earlier don't correctly send a
2N/A * negState of request-mic when counter-proposing a
2N/A * mechanism. They probably don't handle mechListMICs
2N/A * properly either.
2N/A */
2N/A if (acc_negState != REQUEST_MIC)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A sc->mech_complete = 0;
2N/A sc->mic_reqd = 1;
2N/A *negState = REQUEST_MIC;
2N/A *tokflag = CONT_TOKEN_SEND;
2N/A return GSS_S_CONTINUE_NEEDED;
2N/A}
2N/A
2N/A/*
2N/A * Wrap call to mechanism gss_init_sec_context() and update state
2N/A * accordingly.
2N/A */
2N/Astatic OM_uint32
2N/Ainit_ctx_call_init(OM_uint32 *minor_status,
2N/A spnego_gss_ctx_id_t sc,
2N/A gss_cred_id_t claimant_cred_handle,
2N/A gss_name_t target_name,
2N/A OM_uint32 req_flags,
2N/A OM_uint32 time_req,
2N/A gss_buffer_t mechtok_in,
2N/A gss_OID *actual_mech,
2N/A gss_buffer_t mechtok_out,
2N/A OM_uint32 *ret_flags,
2N/A OM_uint32 *time_rec,
2N/A OM_uint32 *negState,
2N/A send_token_flag *send_token)
2N/A{
2N/A OM_uint32 ret;
2N/A
2N/A ret = gss_init_sec_context(minor_status,
2N/A claimant_cred_handle,
2N/A &sc->ctx_handle,
2N/A target_name,
2N/A sc->internal_mech,
2N/A (req_flags | GSS_C_INTEG_FLAG),
2N/A time_req,
2N/A GSS_C_NO_CHANNEL_BINDINGS,
2N/A mechtok_in,
2N/A &sc->actual_mech,
2N/A mechtok_out,
2N/A &sc->ctx_flags,
2N/A time_rec);
2N/A if (ret == GSS_S_COMPLETE) {
2N/A sc->mech_complete = 1;
2N/A if (ret_flags != NULL)
2N/A *ret_flags = sc->ctx_flags;
2N/A /*
2N/A * If this isn't the first time we've been called,
2N/A * we're done unless a MIC needs to be
2N/A * generated/handled.
2N/A */
2N/A if (*send_token == CONT_TOKEN_SEND &&
2N/A mechtok_out->length == 0 &&
2N/A (!sc->mic_reqd ||
2N/A !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
2N/A
2N/A *negState = ACCEPT_COMPLETE;
2N/A ret = GSS_S_COMPLETE;
2N/A if (mechtok_out->length == 0) {
2N/A *send_token = NO_TOKEN_SEND;
2N/A }
2N/A } else {
2N/A *negState = ACCEPT_INCOMPLETE;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A } else if (ret != GSS_S_CONTINUE_NEEDED) {
2N/A if (*send_token == INIT_TOKEN_SEND) {
2N/A /* Don't output token on error if first call. */
2N/A *send_token = NO_TOKEN_SEND;
2N/A } else {
2N/A *send_token = ERROR_TOKEN_SEND;
2N/A }
2N/A *negState = REJECT;
2N/A }
2N/A return ret;
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_init_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_cred_id_t claimant_cred_handle,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_name_t target_name,
2N/A gss_OID mech_type,
2N/A OM_uint32 req_flags,
2N/A OM_uint32 time_req,
2N/A gss_channel_bindings_t input_chan_bindings,
2N/A gss_buffer_t input_token,
2N/A gss_OID *actual_mech,
2N/A gss_buffer_t output_token,
2N/A OM_uint32 *ret_flags,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A return(spnego_gss_init_sec_context(
2N/A minor_status,
2N/A claimant_cred_handle,
2N/A context_handle,
2N/A target_name,
2N/A mech_type,
2N/A req_flags,
2N/A time_req,
2N/A input_chan_bindings,
2N/A input_token,
2N/A actual_mech,
2N/A output_token,
2N/A ret_flags,
2N/A time_rec));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_init_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_cred_id_t claimant_cred_handle,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_name_t target_name,
2N/A gss_OID mech_type,
2N/A OM_uint32 req_flags,
2N/A OM_uint32 time_req,
2N/A gss_channel_bindings_t input_chan_bindings,
2N/A gss_buffer_t input_token,
2N/A gss_OID *actual_mech,
2N/A gss_buffer_t output_token,
2N/A OM_uint32 *ret_flags,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A /*
2N/A * send_token is used to indicate in later steps
2N/A * what type of token, if any should be sent or processed.
2N/A * NO_TOKEN_SEND = no token should be sent
2N/A * INIT_TOKEN_SEND = initial token will be sent
2N/A * CONT_TOKEN_SEND = continuing tokens to be sent
2N/A * CHECK_MIC = no token to be sent, but have a MIC to check.
2N/A */
2N/A send_token_flag send_token = NO_TOKEN_SEND;
2N/A OM_uint32 tmpmin, ret, negState;
2N/A gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
2N/A gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
2N/A gss_OID_set mechSet = GSS_C_NO_OID_SET;
2N/A spnego_gss_ctx_id_t spnego_ctx = NULL;
2N/A
2N/A dsyslog("Entering init_sec_context\n");
2N/A
2N/A mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
2N/A negState = REJECT;
2N/A
2N/A if (minor_status != NULL)
2N/A *minor_status = 0;
2N/A if (output_token != GSS_C_NO_BUFFER) {
2N/A output_token->length = 0;
2N/A output_token->value = NULL;
2N/A }
2N/A if (minor_status == NULL ||
2N/A output_token == GSS_C_NO_BUFFER ||
2N/A context_handle == NULL)
2N/A return GSS_S_CALL_INACCESSIBLE_WRITE;
2N/A
2N/A if (actual_mech != NULL)
2N/A *actual_mech = GSS_C_NO_OID;
2N/A
2N/A if (*context_handle == GSS_C_NO_CONTEXT) {
2N/A ret = init_ctx_new(minor_status, claimant_cred_handle,
2N/A context_handle, &mechSet, &send_token);
2N/A if (ret != GSS_S_CONTINUE_NEEDED) {
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A ret = init_ctx_cont(minor_status, context_handle,
2N/A input_token, &mechtok_in,
2N/A &mechListMIC_in, &negState, &send_token);
2N/A if (HARD_ERROR(ret)) {
2N/A goto cleanup;
2N/A }
2N/A }
2N/A spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
2N/A
2N/A /* Solaris SPNEGO */
2N/A if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
2N/A spnego_gss_save_error_info(*minor_status, spnego_ctx);
2N/A
2N/A if (!spnego_ctx->mech_complete) {
2N/A ret = init_ctx_call_init(
2N/A minor_status, spnego_ctx,
2N/A claimant_cred_handle,
2N/A target_name, req_flags,
2N/A time_req, mechtok_in,
2N/A actual_mech, &mechtok_out,
2N/A ret_flags, time_rec,
2N/A &negState, &send_token);
2N/A }
2N/A /* create mic/check mic */
2N/A if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
2N/A (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
2N/A
2N/A ret = handle_mic(minor_status,
2N/A mechListMIC_in,
2N/A (mechtok_out.length != 0),
2N/A spnego_ctx, &mechListMIC_out,
2N/A &negState, &send_token);
2N/A }
2N/Acleanup:
2N/A if (send_token == INIT_TOKEN_SEND) {
2N/A if (make_spnego_tokenInit_msg(spnego_ctx,
2N/A 0,
2N/A mechListMIC_out,
2N/A req_flags,
2N/A &mechtok_out, send_token,
2N/A output_token) < 0) {
2N/A ret = GSS_S_FAILURE;
2N/A }
2N/A } else if (send_token != NO_TOKEN_SEND) {
2N/A if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
2N/A &mechtok_out, mechListMIC_out,
2N/A send_token,
2N/A output_token) < 0) {
2N/A ret = GSS_S_FAILURE;
2N/A }
2N/A }
2N/A gss_release_buffer(&tmpmin, &mechtok_out);
2N/A if (ret == GSS_S_COMPLETE) {
2N/A /*
2N/A * Now, switch the output context to refer to the
2N/A * negotiated mechanism's context.
2N/A */
2N/A *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
2N/A if (actual_mech != NULL)
2N/A *actual_mech = spnego_ctx->actual_mech;
2N/A if (ret_flags != NULL)
2N/A *ret_flags = spnego_ctx->ctx_flags;
2N/A release_spnego_ctx(&spnego_ctx);
2N/A } else if (ret != GSS_S_CONTINUE_NEEDED) {
2N/A if (spnego_ctx != NULL) {
2N/A gss_delete_sec_context(&tmpmin,
2N/A &spnego_ctx->ctx_handle,
2N/A GSS_C_NO_BUFFER);
2N/A release_spnego_ctx(&spnego_ctx);
2N/A }
2N/A *context_handle = GSS_C_NO_CONTEXT;
2N/A }
2N/A if (mechtok_in != GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, mechtok_in);
2N/A free(mechtok_in);
2N/A }
2N/A if (mechListMIC_in != GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, mechListMIC_in);
2N/A free(mechListMIC_in);
2N/A }
2N/A if (mechListMIC_out != GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, mechListMIC_out);
2N/A free(mechListMIC_out);
2N/A }
2N/A if (mechSet != GSS_C_NO_OID_SET) {
2N/A gss_release_oid_set(&tmpmin, &mechSet);
2N/A }
2N/A return ret;
2N/A} /* init_sec_context */
2N/A
2N/A/* We don't want to import KRB5 headers here */
2N/Astatic const gss_OID_desc gss_mech_krb5_oid =
2N/A { 9, "\052\206\110\206\367\022\001\002\002" };
2N/Astatic const gss_OID_desc gss_mech_krb5_wrong_oid =
2N/A { 9, "\052\206\110\202\367\022\001\002\002" };
2N/A
2N/A/*
2N/A * verify that the input token length is not 0. If it is, just return.
2N/A * If the token length is greater than 0, der encode as a sequence
2N/A * and place in buf_out, advancing buf_out.
2N/A */
2N/A
2N/Astatic int
2N/Aput_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
2N/A unsigned int buflen)
2N/A{
2N/A int ret;
2N/A
2N/A /* if token length is 0, we do not want to send */
2N/A if (input_token->length == 0)
2N/A return (0);
2N/A
2N/A if (input_token->length > buflen)
2N/A return (-1);
2N/A
2N/A *(*buf_out)++ = SEQUENCE;
2N/A if ((ret = gssint_put_der_length(input_token->length, buf_out,
2N/A input_token->length)))
2N/A return (ret);
2N/A TWRITE_STR(*buf_out, input_token->value, input_token->length);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * NegHints ::= SEQUENCE {
2N/A * hintName [0] GeneralString OPTIONAL,
2N/A * hintAddress [1] OCTET STRING OPTIONAL
2N/A * }
2N/A */
2N/A
2N/A#define HOST_PREFIX "host@"
2N/A#define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
2N/A
2N/Astatic int
2N/Amake_NegHints(OM_uint32 *minor_status,
2N/A gss_cred_id_t cred, gss_buffer_t *outbuf)
2N/A{
2N/A gss_buffer_desc hintNameBuf;
2N/A gss_name_t hintName = GSS_C_NO_NAME;
2N/A gss_name_t hintKerberosName;
2N/A gss_OID hintNameType;
2N/A OM_uint32 major_status;
2N/A OM_uint32 minor;
2N/A unsigned int tlen = 0;
2N/A unsigned int hintNameSize = 0;
2N/A unsigned int negHintsSize = 0;
2N/A unsigned char *ptr;
2N/A unsigned char *t;
2N/A
2N/A *outbuf = GSS_C_NO_BUFFER;
2N/A
2N/A if (cred != GSS_C_NO_CREDENTIAL) {
2N/A major_status = gss_inquire_cred(minor_status,
2N/A cred,
2N/A &hintName,
2N/A NULL,
2N/A NULL,
2N/A NULL);
2N/A if (major_status != GSS_S_COMPLETE)
2N/A return (major_status);
2N/A }
2N/A
2N/A if (hintName == GSS_C_NO_NAME) {
2N/A krb5_error_code code;
2N/A krb5int_access kaccess;
2N/A char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
2N/A
2N/A code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
2N/A if (code != 0) {
2N/A *minor_status = code;
2N/A return (GSS_S_FAILURE);
2N/A }
2N/A
2N/A /* this breaks mutual authentication but Samba relies on it */
2N/A code = (*kaccess.clean_hostname)(NULL, NULL,
2N/A &hostname[HOST_PREFIX_LEN],
2N/A MAXHOSTNAMELEN);
2N/A if (code != 0) {
2N/A *minor_status = code;
2N/A return (GSS_S_FAILURE);
2N/A }
2N/A
2N/A hintNameBuf.value = hostname;
2N/A hintNameBuf.length = strlen(hostname);
2N/A
2N/A major_status = gss_import_name(minor_status,
2N/A &hintNameBuf,
2N/A GSS_C_NT_HOSTBASED_SERVICE,
2N/A &hintName);
2N/A if (major_status != GSS_S_COMPLETE) {
2N/A return (major_status);
2N/A }
2N/A }
2N/A
2N/A hintNameBuf.value = NULL;
2N/A hintNameBuf.length = 0;
2N/A
2N/A major_status = gss_canonicalize_name(minor_status,
2N/A hintName,
2N/A (gss_OID)&gss_mech_krb5_oid,
2N/A &hintKerberosName);
2N/A if (major_status != GSS_S_COMPLETE) {
2N/A gss_release_name(&minor, &hintName);
2N/A return (major_status);
2N/A }
2N/A gss_release_name(&minor, &hintName);
2N/A
2N/A major_status = gss_display_name(minor_status,
2N/A hintKerberosName,
2N/A &hintNameBuf,
2N/A &hintNameType);
2N/A if (major_status != GSS_S_COMPLETE) {
2N/A /* Solaris SPNEGO */
2N/A gss_release_name(&minor, &hintKerberosName);
2N/A return (major_status);
2N/A }
2N/A gss_release_name(&minor, &hintKerberosName);
2N/A
2N/A /*
2N/A * Now encode the name hint into a NegHints ASN.1 type
2N/A */
2N/A major_status = GSS_S_FAILURE;
2N/A
2N/A /* Length of DER encoded GeneralString */
2N/A tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
2N/A hintNameBuf.length;
2N/A hintNameSize = tlen;
2N/A
2N/A /* Length of DER encoded hintName */
2N/A tlen += 1 + gssint_der_length_size(hintNameSize);
2N/A negHintsSize = tlen;
2N/A
2N/A t = (unsigned char *)malloc(tlen);
2N/A if (t == NULL) {
2N/A *minor_status = ENOMEM;
2N/A goto errout;
2N/A }
2N/A
2N/A ptr = t;
2N/A
2N/A *ptr++ = CONTEXT | 0x00; /* hintName identifier */
2N/A if (gssint_put_der_length(hintNameSize,
2N/A &ptr, tlen - (int)(ptr-t)))
2N/A goto errout;
2N/A
2N/A *ptr++ = GENERAL_STRING;
2N/A if (gssint_put_der_length(hintNameBuf.length,
2N/A &ptr, tlen - (int)(ptr-t)))
2N/A goto errout;
2N/A
2N/A memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
2N/A ptr += hintNameBuf.length;
2N/A
2N/A *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
2N/A if (*outbuf == NULL) {
2N/A *minor_status = ENOMEM;
2N/A goto errout;
2N/A }
2N/A (*outbuf)->value = (void *)t;
2N/A (*outbuf)->length = ptr - t;
2N/A
2N/A t = NULL; /* don't free */
2N/A
2N/A *minor_status = 0;
2N/A major_status = GSS_S_COMPLETE;
2N/A
2N/Aerrout:
2N/A if (t != NULL) {
2N/A free(t);
2N/A }
2N/A
2N/A gss_release_buffer(&minor, &hintNameBuf);
2N/A return (major_status);
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Aacc_ctx_hints(OM_uint32 *minor_status,
2N/A gss_ctx_id_t *ctx,
2N/A gss_cred_id_t cred,
2N/A gss_buffer_t *mechListMIC,
2N/A OM_uint32 *negState,
2N/A send_token_flag *return_token)
2N/A{
2N/A OM_uint32 tmpmin, ret;
2N/A gss_OID_set supported_mechSet;
2N/A spnego_gss_ctx_id_t sc = NULL;
2N/A
2N/A *mechListMIC = GSS_C_NO_BUFFER;
2N/A supported_mechSet = GSS_C_NO_OID_SET;
2N/A *return_token = ERROR_TOKEN_SEND;
2N/A *negState = REJECT;
2N/A *minor_status = 0;
2N/A
2N/A /* Solaris SPNEGO */
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (cred != GSS_C_NO_CREDENTIAL) {
2N/A ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
2N/A NULL, &supported_mechSet);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
2N/A GSS_C_ACCEPT, NULL,
2N/A &supported_mechSet);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A ret = make_NegHints(minor_status, cred, mechListMIC);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Select the best match between the list of mechs
2N/A * that the initiator requested and the list that
2N/A * the acceptor will support.
2N/A */
2N/A sc = create_spnego_ctx();
2N/A if (sc == NULL) {
2N/A ret = GSS_S_FAILURE;
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
2N/A ret = GSS_S_FAILURE;
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A sc->internal_mech = GSS_C_NO_OID;
2N/A
2N/A *negState = ACCEPT_INCOMPLETE;
2N/A *return_token = INIT_TOKEN_SEND;
2N/A sc->firstpass = 1;
2N/A *ctx = (gss_ctx_id_t)sc;
2N/A sc = NULL;
2N/A ret = GSS_S_COMPLETE;
2N/A
2N/Acleanup:
2N/A release_spnego_ctx(&sc);
2N/A gss_release_oid_set(&tmpmin, &supported_mechSet);
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Solaris SPNEGO
2N/A * mechoidset2str()
2N/A * Input an OID set of mechs and output a string like so:
2N/A * '{ x y z } (mechname0), { a b c } (mechname1) ...'.
2N/A * On error return NULL.
2N/A * Caller needs to free returned string.
2N/A */
2N/Astatic const char *mech_no_map = "Can't map OID to mechname via /etc/gss/mech";
2N/Astatic const char *oid_no_map = "Can't map OID to string";
2N/Astatic char *
2N/Amechoidset2str(gss_OID_set mechset)
2N/A{
2N/A int i, l;
2N/A char buf[256] = {0};
2N/A char *s = NULL;
2N/A
2N/A if (!mechset)
2N/A return NULL;
2N/A
2N/A for (i = 0; i < mechset->count; i++) {
2N/A OM_uint32 maj, min;
2N/A gss_buffer_desc oidstr;
2N/A gss_buffer_t oidstrp = &oidstr;
2N/A gss_OID mech_oid = &mechset->elements[i];
2N/A /* No need to free mech_name. */
2N/A const char *mech_name = __gss_oid_to_mech(mech_oid);
2N/A
2N/A if (i > 0)
2N/A if (strlcat(buf, ", ", sizeof (buf)) >= sizeof (buf)) {
2N/A if (oidstrp->value)
2N/A gss_release_buffer(&min, oidstrp);
2N/A break;
2N/A }
2N/A
2N/A /* Add '{ x y x ... }'. */
2N/A maj = gss_oid_to_str(&min, mech_oid, oidstrp);
2N/A if (strlcat(buf, maj ? oid_no_map : oidstrp->value,
2N/A sizeof (buf)) >= sizeof (buf)) {
2N/A if (oidstrp->value)
2N/A gss_release_buffer(&min, oidstrp);
2N/A break;
2N/A }
2N/A if (oidstrp->value)
2N/A gss_release_buffer(&min, oidstrp);
2N/A
2N/A /* Add '(mech name)'. */
2N/A if (strlcat(buf, " (", sizeof (buf)) >= sizeof (buf))
2N/A break;
2N/A if (strlcat(buf, mech_name ? mech_name : mech_no_map,
2N/A sizeof (buf)) >= sizeof (buf))
2N/A break;
2N/A if (strlcat(buf, ") ", sizeof (buf)) >= sizeof (buf))
2N/A break;
2N/A }
2N/A
2N/A /* Even if we have buf overflow, let's output what we got so far. */
2N/A if (mechset->count) {
2N/A l = strlen(buf);
2N/A if (l > 0) {
2N/A s = malloc(l + 1);
2N/A if (!s)
2N/A return NULL;
2N/A (void) strlcpy(s, buf, l);
2N/A }
2N/A }
2N/A
2N/A return s ? s : NULL;
2N/A}
2N/A
2N/A/*
2N/A * Set negState to REJECT if the token is defective, else
2N/A * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
2N/A * preferred mechanism is supported.
2N/A */
2N/Astatic OM_uint32
2N/Aacc_ctx_new(OM_uint32 *minor_status,
2N/A gss_buffer_t buf,
2N/A gss_ctx_id_t *ctx,
2N/A gss_cred_id_t cred,
2N/A gss_buffer_t *mechToken,
2N/A gss_buffer_t *mechListMIC,
2N/A OM_uint32 *negState,
2N/A send_token_flag *return_token)
2N/A{
2N/A OM_uint32 tmpmin, ret, req_flags;
2N/A gss_OID_set supported_mechSet, mechTypes;
2N/A gss_buffer_desc der_mechTypes;
2N/A gss_OID mech_wanted;
2N/A spnego_gss_ctx_id_t sc = NULL;
2N/A
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A der_mechTypes.length = 0;
2N/A der_mechTypes.value = NULL;
2N/A *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
2N/A supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
2N/A *return_token = ERROR_TOKEN_SEND;
2N/A *negState = REJECT;
2N/A *minor_status = 0;
2N/A
2N/A ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
2N/A &mechTypes, &req_flags,
2N/A mechToken, mechListMIC);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A goto cleanup;
2N/A }
2N/A if (cred != GSS_C_NO_CREDENTIAL) {
2N/A ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
2N/A NULL, &supported_mechSet);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
2N/A GSS_C_ACCEPT, NULL,
2N/A &supported_mechSet);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *return_token = NO_TOKEN_SEND;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A /*
2N/A * Select the best match between the list of mechs
2N/A * that the initiator requested and the list that
2N/A * the acceptor will support.
2N/A */
2N/A mech_wanted = negotiate_mech_type(minor_status,
2N/A supported_mechSet,
2N/A mechTypes,
2N/A negState);
2N/A if (*negState == REJECT) {
2N/A /* Solaris SPNEGO: Spruce-up error msg */
2N/A char *mechTypesStr = mechoidset2str(mechTypes);
2N/A spnego_gss_ctx_id_t tmpsc = create_spnego_ctx();
2N/A if (tmpsc && *minor_status == ERR_SPNEGO_NEGOTIATION_FAILED) {
2N/A spnego_set_error_message(tmpsc, *minor_status,
2N/A dgettext(TEXT_DOMAIN,
2N/A "SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
2N/A mechTypesStr ? mechTypesStr : "<null>");
2N/A }
2N/A if (mechTypesStr)
2N/A free(mechTypesStr);
2N/A
2N/A /*
2N/A * We save error here cuz the tmp ctx goes away (very) soon.
2N/A * So callers of acc_ctx_new() should NOT call it again.
2N/A */
2N/A spnego_gss_save_error_info(*minor_status, tmpsc);
2N/A if (tmpsc)
2N/A release_spnego_ctx(&tmpsc);
2N/A ret = GSS_S_BAD_MECH;
2N/A goto cleanup;
2N/A }
2N/A
2N/A sc = (spnego_gss_ctx_id_t)*ctx;
2N/A if (sc != NULL) {
2N/A gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
2N/A assert(mech_wanted != GSS_C_NO_OID);
2N/A } else
2N/A sc = create_spnego_ctx();
2N/A if (sc == NULL) {
2N/A ret = GSS_S_FAILURE;
2N/A *return_token = NO_TOKEN_SEND;
2N/A generic_gss_release_oid(&tmpmin, &mech_wanted);
2N/A goto cleanup;
2N/A }
2N/A sc->internal_mech = mech_wanted;
2N/A sc->DER_mechTypes = der_mechTypes;
2N/A der_mechTypes.length = 0;
2N/A der_mechTypes.value = NULL;
2N/A
2N/A if (*negState == REQUEST_MIC)
2N/A sc->mic_reqd = 1;
2N/A
2N/A *return_token = INIT_TOKEN_SEND;
2N/A sc->firstpass = 1;
2N/A *ctx = (gss_ctx_id_t)sc;
2N/A ret = GSS_S_COMPLETE;
2N/Acleanup:
2N/A gss_release_oid_set(&tmpmin, &mechTypes);
2N/A gss_release_oid_set(&tmpmin, &supported_mechSet);
2N/A if (der_mechTypes.length != 0)
2N/A gss_release_buffer(&tmpmin, &der_mechTypes);
2N/A return ret;
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Aacc_ctx_cont(OM_uint32 *minstat,
2N/A gss_buffer_t buf,
2N/A gss_ctx_id_t *ctx,
2N/A gss_buffer_t *responseToken,
2N/A gss_buffer_t *mechListMIC,
2N/A OM_uint32 *negState,
2N/A send_token_flag *return_token)
2N/A{
2N/A OM_uint32 ret, tmpmin;
2N/A gss_OID supportedMech;
2N/A spnego_gss_ctx_id_t sc;
2N/A unsigned int len;
2N/A unsigned char *ptr, *bufstart;
2N/A
2N/A sc = (spnego_gss_ctx_id_t)*ctx;
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A *negState = REJECT;
2N/A *minstat = 0;
2N/A supportedMech = GSS_C_NO_OID;
2N/A *return_token = ERROR_TOKEN_SEND;
2N/A *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
2N/A
2N/A ptr = bufstart = buf->value;
2N/A#define REMAIN (buf->length - (ptr - bufstart))
2N/A if (REMAIN > INT_MAX)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A /*
2N/A * Attempt to work with old Sun SPNEGO.
2N/A */
2N/A if (*ptr == HEADER_ID) {
2N/A ret = g_verify_token_header(gss_mech_spnego,
2N/A &len, &ptr, 0, REMAIN);
2N/A if (ret) {
2N/A *minstat = ret;
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A }
2N/A if (*ptr != (CONTEXT | 0x01)) {
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A ret = get_negTokenResp(minstat, ptr, REMAIN,
2N/A negState, &supportedMech,
2N/A responseToken, mechListMIC);
2N/A if (ret != GSS_S_COMPLETE)
2N/A goto cleanup;
2N/A
2N/A if (*responseToken == GSS_C_NO_BUFFER &&
2N/A *mechListMIC == GSS_C_NO_BUFFER) {
2N/A
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto cleanup;
2N/A }
2N/A if (supportedMech != GSS_C_NO_OID) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto cleanup;
2N/A }
2N/A sc->firstpass = 0;
2N/A *negState = ACCEPT_INCOMPLETE;
2N/A *return_token = CONT_TOKEN_SEND;
2N/Acleanup:
2N/A if (supportedMech != GSS_C_NO_OID) {
2N/A generic_gss_release_oid(&tmpmin, &supportedMech);
2N/A }
2N/A return ret;
2N/A#undef REMAIN
2N/A}
2N/A
2N/A/*
2N/A * Verify that mech OID is either exactly the same as the negotiated
2N/A * mech OID, or is a mech OID supported by the negotiated mech. MS
2N/A * implementations can list a most preferred mech using an incorrect
2N/A * krb5 OID while emitting a krb5 initiator mech token having the
2N/A * correct krb5 mech OID.
2N/A */
2N/Astatic OM_uint32
2N/Aacc_ctx_vfy_oid(OM_uint32 *minor_status,
2N/A spnego_gss_ctx_id_t sc, gss_OID mechoid,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret, tmpmin;
2N/A gss_mechanism mech = NULL;
2N/A gss_OID_set mech_set = GSS_C_NO_OID_SET;
2N/A int present = 0;
2N/A
2N/A if (g_OID_equal(sc->internal_mech, mechoid))
2N/A return GSS_S_COMPLETE;
2N/A
2N/A /*
2N/A * SUNW17PACresync
2N/A * If both mechs are kerb, we are done.
2N/A */
2N/A if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
2N/A return GSS_S_COMPLETE;
2N/A }
2N/A
2N/A mech = gssint_get_mechanism(mechoid);
2N/A if (mech == NULL || mech->gss_indicate_mechs == NULL) {
2N/A *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
2N/A {
2N/A /*
2N/A * Solaris SPNEGO
2N/A * Spruce-up error msg.
2N/A */
2N/A OM_uint32 maj, maj_sc, min;
2N/A gss_buffer_desc oidstr, oidstr_sc;
2N/A /* No need to free mnamestr. */
2N/A const char *mnamestr = __gss_oid_to_mech(
2N/A sc->internal_mech);
2N/A maj_sc = gss_oid_to_str(&min,
2N/A sc->internal_mech,
2N/A &oidstr_sc);
2N/A maj = gss_oid_to_str(&min, mechoid, &oidstr);
2N/A spnego_set_error_message(sc, *minor_status,
2N/A dgettext(TEXT_DOMAIN,
2N/A "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
2N/A maj ? oid_no_map: oidstr.value,
2N/A maj_sc ? oid_no_map: oidstr_sc.value,
2N/A mnamestr ? mnamestr : mech_no_map);
2N/A if (!maj)
2N/A (void) gss_release_buffer(&min, &oidstr);
2N/A if (!maj_sc)
2N/A (void) gss_release_buffer(&min, &oidstr_sc);
2N/A }
2N/A map_errcode(minor_status);
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A return GSS_S_BAD_MECH;
2N/A }
2N/A ret = mech->gss_indicate_mechs(minor_status, &mech_set);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *tokflag = NO_TOKEN_SEND;
2N/A map_error(minor_status, mech);
2N/A goto cleanup;
2N/A }
2N/A ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
2N/A mech_set, &present);
2N/A if (ret != GSS_S_COMPLETE)
2N/A goto cleanup;
2N/A if (!present) {
2N/A {
2N/A /*
2N/A * Solaris SPNEGO
2N/A * Spruce-up error msg.
2N/A */
2N/A OM_uint32 maj, min;
2N/A gss_buffer_desc oidstr;
2N/A char *mech_set_str = mechoidset2str(mech_set);
2N/A /* No need to free mnamestr. */
2N/A const char *mnamestr =
2N/A __gss_oid_to_mech(sc->internal_mech);
2N/A maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
2N/A *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
2N/A spnego_set_error_message(sc, *minor_status,
2N/A dgettext(TEXT_DOMAIN,
2N/A "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
2N/A maj ? oid_no_map: oidstr.value,
2N/A mnamestr ? mnamestr : mech_no_map,
2N/A mech_set_str ? mech_set_str : "<null>");
2N/A if (!maj)
2N/A (void) gss_release_buffer(&min, &oidstr);
2N/A if (mech_set_str)
2N/A free(mech_set_str);
2N/A }
2N/A map_errcode(minor_status);
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A ret = GSS_S_BAD_MECH;
2N/A }
2N/Acleanup:
2N/A gss_release_oid_set(&tmpmin, &mech_set);
2N/A return ret;
2N/A}
2N/A#ifndef LEAN_CLIENT
2N/A/*
2N/A * Wrap call to gss_accept_sec_context() and update state
2N/A * accordingly.
2N/A */
2N/Astatic OM_uint32
2N/Aacc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
2N/A gss_cred_id_t cred, gss_buffer_t mechtok_in,
2N/A gss_OID *mech_type, gss_buffer_t mechtok_out,
2N/A OM_uint32 *ret_flags, OM_uint32 *time_rec,
2N/A gss_cred_id_t *delegated_cred_handle,
2N/A OM_uint32 *negState, send_token_flag *tokflag)
2N/A{
2N/A OM_uint32 ret;
2N/A gss_OID_desc mechoid;
2N/A
2N/A if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
2N/A /*
2N/A * mechoid is an alias; don't free it.
2N/A */
2N/A ret = gssint_get_mech_type(&mechoid, mechtok_in);
2N/A if (ret != GSS_S_COMPLETE) {
2N/A *tokflag = NO_TOKEN_SEND;
2N/A return ret;
2N/A }
2N/A ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
2N/A negState, tokflag);
2N/A if (ret != GSS_S_COMPLETE)
2N/A return ret;
2N/A }
2N/A
2N/A ret = gss_accept_sec_context(minor_status,
2N/A &sc->ctx_handle,
2N/A cred,
2N/A mechtok_in,
2N/A GSS_C_NO_CHANNEL_BINDINGS,
2N/A &sc->internal_name,
2N/A mech_type,
2N/A mechtok_out,
2N/A &sc->ctx_flags,
2N/A time_rec,
2N/A delegated_cred_handle);
2N/A
2N/A if (ret == GSS_S_COMPLETE) {
2N/A#ifdef MS_BUG_TEST
2N/A /*
2N/A * Force MIC to be not required even if we previously
2N/A * requested a MIC.
2N/A */
2N/A char *envstr = getenv("MS_FORCE_NO_MIC");
2N/A
2N/A if (envstr != NULL && strcmp(envstr, "1") == 0 &&
2N/A !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
2N/A sc->mic_reqd) {
2N/A
2N/A sc->mic_reqd = 0;
2N/A }
2N/A#endif
2N/A sc->mech_complete = 1;
2N/A if (ret_flags != NULL)
2N/A *ret_flags = sc->ctx_flags;
2N/A
2N/A if (!sc->mic_reqd) {
2N/A *negState = ACCEPT_COMPLETE;
2N/A ret = GSS_S_COMPLETE;
2N/A } else {
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A } else if (ret != GSS_S_CONTINUE_NEEDED) {
2N/A *negState = REJECT;
2N/A *tokflag = ERROR_TOKEN_SEND;
2N/A }
2N/A return ret;
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_accept_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_cred_id_t verifier_cred_handle,
2N/A gss_buffer_t input_token,
2N/A gss_channel_bindings_t input_chan_bindings,
2N/A gss_name_t *src_name,
2N/A gss_OID *mech_type,
2N/A gss_buffer_t output_token,
2N/A OM_uint32 *ret_flags,
2N/A OM_uint32 *time_rec,
2N/A gss_cred_id_t *delegated_cred_handle)
2N/A{
2N/A return(spnego_gss_accept_sec_context(
2N/A minor_status,
2N/A context_handle,
2N/A verifier_cred_handle,
2N/A input_token,
2N/A input_chan_bindings,
2N/A src_name,
2N/A mech_type,
2N/A output_token,
2N/A ret_flags,
2N/A time_rec,
2N/A delegated_cred_handle));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_accept_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_cred_id_t verifier_cred_handle,
2N/A gss_buffer_t input_token,
2N/A gss_channel_bindings_t input_chan_bindings,
2N/A gss_name_t *src_name,
2N/A gss_OID *mech_type,
2N/A gss_buffer_t output_token,
2N/A OM_uint32 *ret_flags,
2N/A OM_uint32 *time_rec,
2N/A gss_cred_id_t *delegated_cred_handle)
2N/A{
2N/A OM_uint32 ret, tmpmin, negState;
2N/A send_token_flag return_token;
2N/A gss_buffer_t mechtok_in, mic_in, mic_out;
2N/A gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
2N/A spnego_gss_ctx_id_t sc = NULL;
2N/A OM_uint32 mechstat = GSS_S_FAILURE;
2N/A int sendTokenInit = 0, tmpret;
2N/A
2N/A mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
2N/A
2N/A if (minor_status != NULL)
2N/A *minor_status = 0;
2N/A if (output_token != GSS_C_NO_BUFFER) {
2N/A output_token->length = 0;
2N/A output_token->value = NULL;
2N/A }
2N/A
2N/A
2N/A if (minor_status == NULL ||
2N/A output_token == GSS_C_NO_BUFFER ||
2N/A context_handle == NULL) {
2N/A return GSS_S_CALL_INACCESSIBLE_WRITE;
2N/A }
2N/A
2N/A if (input_token == GSS_C_NO_BUFFER) {
2N/A return GSS_S_CALL_INACCESSIBLE_READ;
2N/A }
2N/A
2N/A sc = (spnego_gss_ctx_id_t)*context_handle;
2N/A if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
2N/A if (src_name != NULL)
2N/A *src_name = GSS_C_NO_NAME;
2N/A if (mech_type != NULL)
2N/A *mech_type = GSS_C_NO_OID;
2N/A if (time_rec != NULL)
2N/A *time_rec = 0;
2N/A if (ret_flags != NULL)
2N/A *ret_flags = 0;
2N/A if (delegated_cred_handle != NULL)
2N/A *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
2N/A if (input_token->length == 0) {
2N/A ret = acc_ctx_hints(minor_status,
2N/A context_handle,
2N/A verifier_cred_handle,
2N/A &mic_out,
2N/A &negState,
2N/A &return_token);
2N/A if (ret != GSS_S_COMPLETE)
2N/A goto cleanup;
2N/A sendTokenInit = 1;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A } else {
2N/A /* Can set negState to REQUEST_MIC */
2N/A ret = acc_ctx_new(minor_status, input_token,
2N/A context_handle, verifier_cred_handle,
2N/A &mechtok_in, &mic_in,
2N/A &negState, &return_token);
2N/A if (ret != GSS_S_COMPLETE)
2N/A goto cleanup;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A } else {
2N/A /* Can set negState to ACCEPT_INCOMPLETE */
2N/A ret = acc_ctx_cont(minor_status, input_token,
2N/A context_handle, &mechtok_in,
2N/A &mic_in, &negState, &return_token);
2N/A if (ret != GSS_S_COMPLETE)
2N/A goto cleanup;
2N/A ret = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A
2N/A sc = (spnego_gss_ctx_id_t)*context_handle;
2N/A /*
2N/A * Handle mechtok_in and mic_in only if they are
2N/A * present in input_token. If neither is present, whether
2N/A * this is an error depends on whether this is the first
2N/A * round-trip. RET is set to a default value according to
2N/A * whether it is the first round-trip.
2N/A */
2N/A mechstat = GSS_S_FAILURE;
2N/A if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
2N/A ret = acc_ctx_call_acc(minor_status, sc,
2N/A verifier_cred_handle, mechtok_in,
2N/A mech_type, &mechtok_out,
2N/A ret_flags, time_rec,
2N/A delegated_cred_handle,
2N/A &negState, &return_token);
2N/A } else if (negState == REQUEST_MIC) {
2N/A mechstat = GSS_S_CONTINUE_NEEDED;
2N/A }
2N/A
2N/A /* Solaris SPNEGO */
2N/A if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
2N/A spnego_gss_save_error_info(*minor_status, sc);
2N/A
2N/A if (!HARD_ERROR(ret) && sc->mech_complete &&
2N/A (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
2N/A
2N/A ret = handle_mic(minor_status, mic_in,
2N/A (mechtok_out.length != 0),
2N/A sc, &mic_out,
2N/A &negState, &return_token);
2N/A }
2N/A
2N/Acleanup:
2N/A if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
2N/A assert(sc != NULL);
2N/A tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
2N/A GSS_C_NO_BUFFER,
2N/A return_token, output_token);
2N/A if (tmpret < 0)
2N/A ret = GSS_S_FAILURE;
2N/A } else if (return_token != NO_TOKEN_SEND &&
2N/A return_token != CHECK_MIC) {
2N/A tmpret = make_spnego_tokenTarg_msg(negState,
2N/A sc ? sc->internal_mech :
2N/A GSS_C_NO_OID,
2N/A &mechtok_out, mic_out,
2N/A return_token,
2N/A output_token);
2N/A if (tmpret < 0)
2N/A ret = GSS_S_FAILURE;
2N/A }
2N/A if (ret == GSS_S_COMPLETE) {
2N/A *context_handle = (gss_ctx_id_t)sc->ctx_handle;
2N/A if (sc->internal_name != GSS_C_NO_NAME &&
2N/A src_name != NULL) {
2N/A *src_name = sc->internal_name;
2N/A sc->internal_name = GSS_C_NO_NAME;
2N/A }
2N/A release_spnego_ctx(&sc);
2N/A } else if (ret != GSS_S_CONTINUE_NEEDED) {
2N/A if (sc != NULL) {
2N/A gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
2N/A GSS_C_NO_BUFFER);
2N/A release_spnego_ctx(&sc);
2N/A }
2N/A *context_handle = GSS_C_NO_CONTEXT;
2N/A }
2N/A gss_release_buffer(&tmpmin, &mechtok_out);
2N/A if (mechtok_in != GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, mechtok_in);
2N/A free(mechtok_in);
2N/A }
2N/A if (mic_in != GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, mic_in);
2N/A free(mic_in);
2N/A }
2N/A if (mic_out != GSS_C_NO_BUFFER) {
2N/A gss_release_buffer(&tmpmin, mic_out);
2N/A free(mic_out);
2N/A }
2N/A return ret;
2N/A}
2N/A#endif /* LEAN_CLIENT */
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_display_status(
2N/A OM_uint32 *minor_status,
2N/A OM_uint32 status_value,
2N/A int status_type,
2N/A gss_OID mech_type,
2N/A OM_uint32 *message_context,
2N/A gss_buffer_t status_string)
2N/A{
2N/A return (spnego_gss_display_status(minor_status,
2N/A status_value,
2N/A status_type,
2N/A mech_type,
2N/A message_context,
2N/A status_string));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_display_status(
2N/A OM_uint32 *minor_status,
2N/A OM_uint32 status_value,
2N/A int status_type,
2N/A gss_OID mech_type,
2N/A OM_uint32 *message_context,
2N/A gss_buffer_t status_string)
2N/A{
2N/A dsyslog("Entering display_status\n");
2N/A
2N/A *message_context = 0;
2N/A switch (status_value) {
2N/A case ERR_SPNEGO_NO_MECHS_AVAILABLE:
2N/A /* CSTYLED */
2N/A *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2N/A break;
2N/A case ERR_SPNEGO_NO_CREDS_ACQUIRED:
2N/A /* CSTYLED */
2N/A *status_string = make_err_msg("SPNEGO failed to acquire creds");
2N/A break;
2N/A case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
2N/A /* CSTYLED */
2N/A *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
2N/A break;
2N/A case ERR_SPNEGO_NEGOTIATION_FAILED:
2N/A /* CSTYLED */
2N/A return(spnego_gss_display_status2(minor_status,
2N/A status_value,
2N/A status_type,
2N/A mech_type,
2N/A message_context,
2N/A status_string));
2N/A case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
2N/A /* CSTYLED */
2N/A *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
2N/A break;
2N/A default:
2N/A /*
2N/A * Solaris SPNEGO
2N/A * If mech_spnego calls mech_krb5 (via libgss) and an
2N/A * error occurs there, give it a shot.
2N/A */
2N/A /* CSTYLED */
2N/A return(krb5_gss_display_status2(minor_status,
2N/A status_value,
2N/A status_type,
2N/A (gss_OID)&gss_mech_krb5_oid,
2N/A message_context,
2N/A status_string));
2N/A
2N/A }
2N/A
2N/A dsyslog("Leaving display_status\n");
2N/A return (GSS_S_COMPLETE);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_import_name(
2N/A OM_uint32 *minor_status,
2N/A gss_buffer_t input_name_buffer,
2N/A gss_OID input_name_type,
2N/A gss_name_t *output_name)
2N/A{
2N/A return(spnego_gss_import_name(minor_status,
2N/A input_name_buffer,
2N/A input_name_type,
2N/A output_name));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_import_name(
2N/A OM_uint32 *minor_status,
2N/A gss_buffer_t input_name_buffer,
2N/A gss_OID input_name_type,
2N/A gss_name_t *output_name)
2N/A{
2N/A OM_uint32 status;
2N/A
2N/A dsyslog("Entering import_name\n");
2N/A
2N/A status = gss_import_name(minor_status, input_name_buffer,
2N/A input_name_type, output_name);
2N/A
2N/A dsyslog("Leaving import_name\n");
2N/A return (status);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_release_name(
2N/A OM_uint32 *minor_status,
2N/A gss_name_t *input_name)
2N/A{
2N/A return(spnego_gss_release_name(minor_status, input_name));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_release_name(
2N/A OM_uint32 *minor_status,
2N/A gss_name_t *input_name)
2N/A{
2N/A OM_uint32 status;
2N/A
2N/A dsyslog("Entering release_name\n");
2N/A
2N/A status = gss_release_name(minor_status, input_name);
2N/A
2N/A dsyslog("Leaving release_name\n");
2N/A return (status);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_compare_name(
2N/A OM_uint32 *minor_status,
2N/A const gss_name_t name1,
2N/A const gss_name_t name2,
2N/A int *name_equal)
2N/A{
2N/A return(spnego_gss_compare_name(minor_status,
2N/A name1,
2N/A name2,
2N/A name_equal));
2N/A}
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_compare_name(
2N/A OM_uint32 *minor_status,
2N/A const gss_name_t name1,
2N/A const gss_name_t name2,
2N/A int *name_equal)
2N/A{
2N/A OM_uint32 status = GSS_S_COMPLETE;
2N/A dsyslog("Entering compare_name\n");
2N/A
2N/A status = gss_compare_name(minor_status, name1, name2, name_equal);
2N/A
2N/A dsyslog("Leaving compare_name\n");
2N/A return (status);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_display_name(
2N/A OM_uint32 *minor_status,
2N/A gss_name_t input_name,
2N/A gss_buffer_t output_name_buffer,
2N/A gss_OID *output_name_type)
2N/A{
2N/A return(spnego_gss_display_name(
2N/A minor_status,
2N/A input_name,
2N/A output_name_buffer,
2N/A output_name_type));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_display_name(
2N/A OM_uint32 *minor_status,
2N/A gss_name_t input_name,
2N/A gss_buffer_t output_name_buffer,
2N/A gss_OID *output_name_type)
2N/A{
2N/A OM_uint32 status = GSS_S_COMPLETE;
2N/A dsyslog("Entering display_name\n");
2N/A
2N/A status = gss_display_name(minor_status, input_name,
2N/A output_name_buffer, output_name_type);
2N/A
2N/A dsyslog("Leaving display_name\n");
2N/A return (status);
2N/A}
2N/A
2N/A
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aglue_spnego_gss_inquire_names_for_mech(
2N/A OM_uint32 *minor_status,
2N/A gss_OID mechanism,
2N/A gss_OID_set *name_types)
2N/A{
2N/A return(spnego_gss_inquire_names_for_mech(minor_status,
2N/A mechanism,
2N/A name_types));
2N/A}
2N/A/*ARGSUSED*/
2N/AOM_uint32
2N/Aspnego_gss_inquire_names_for_mech(
2N/A OM_uint32 *minor_status,
2N/A gss_OID mechanism,
2N/A gss_OID_set *name_types)
2N/A{
2N/A OM_uint32 major, minor;
2N/A
2N/A dsyslog("Entering inquire_names_for_mech\n");
2N/A /*
2N/A * We only know how to handle our own mechanism.
2N/A */
2N/A if ((mechanism != GSS_C_NULL_OID) &&
2N/A !g_OID_equal(gss_mech_spnego, mechanism)) {
2N/A *minor_status = 0;
2N/A return (GSS_S_FAILURE);
2N/A }
2N/A
2N/A major = gss_create_empty_oid_set(minor_status, name_types);
2N/A if (major == GSS_S_COMPLETE) {
2N/A /* Now add our members. */
2N/A if (((major = gss_add_oid_set_member(minor_status,
2N/A (gss_OID) GSS_C_NT_USER_NAME,
2N/A name_types)) == GSS_S_COMPLETE) &&
2N/A ((major = gss_add_oid_set_member(minor_status,
2N/A (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2N/A name_types)) == GSS_S_COMPLETE) &&
2N/A ((major = gss_add_oid_set_member(minor_status,
2N/A (gss_OID) GSS_C_NT_STRING_UID_NAME,
2N/A name_types)) == GSS_S_COMPLETE)) {
2N/A major = gss_add_oid_set_member(minor_status,
2N/A (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2N/A name_types);
2N/A }
2N/A
2N/A if (major != GSS_S_COMPLETE)
2N/A (void) gss_release_oid_set(&minor, name_types);
2N/A }
2N/A
2N/A dsyslog("Leaving inquire_names_for_mech\n");
2N/A return (major);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_unwrap(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A gss_buffer_t input_message_buffer,
2N/A gss_buffer_t output_message_buffer,
2N/A int *conf_state,
2N/A gss_qop_t *qop_state)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_unwrap(minor_status,
2N/A context_handle,
2N/A input_message_buffer,
2N/A output_message_buffer,
2N/A conf_state,
2N/A qop_state);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_wrap(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A int conf_req_flag,
2N/A gss_qop_t qop_req,
2N/A gss_buffer_t input_message_buffer,
2N/A int *conf_state,
2N/A gss_buffer_t output_message_buffer)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_wrap(minor_status,
2N/A context_handle,
2N/A conf_req_flag,
2N/A qop_req,
2N/A input_message_buffer,
2N/A conf_state,
2N/A output_message_buffer);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_process_context_token(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A const gss_buffer_t token_buffer)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_process_context_token(minor_status,
2N/A context_handle,
2N/A token_buffer);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aglue_spnego_gss_delete_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_buffer_t output_token)
2N/A{
2N/A return(spnego_gss_delete_sec_context(minor_status,
2N/A context_handle, output_token));
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_delete_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_buffer_t output_token)
2N/A{
2N/A OM_uint32 ret = GSS_S_COMPLETE;
2N/A spnego_gss_ctx_id_t *ctx =
2N/A (spnego_gss_ctx_id_t *)context_handle;
2N/A
2N/A if (context_handle == NULL)
2N/A return (GSS_S_FAILURE);
2N/A
2N/A /*
2N/A * If this is still an SPNEGO mech, release it locally.
2N/A */
2N/A if (*ctx != NULL &&
2N/A (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2N/A (void) release_spnego_ctx(ctx);
2N/A /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2N/A if (output_token) {
2N/A output_token->length = 0;
2N/A output_token->value = NULL;
2N/A }
2N/A } else {
2N/A ret = gss_delete_sec_context(minor_status,
2N/A context_handle,
2N/A output_token);
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aglue_spnego_gss_context_time(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A return(spnego_gss_context_time(minor_status,
2N/A context_handle,
2N/A time_rec));
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_context_time(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A OM_uint32 *time_rec)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_context_time(minor_status,
2N/A context_handle,
2N/A time_rec);
2N/A return (ret);
2N/A}
2N/A
2N/A#ifndef LEAN_CLIENT
2N/AOM_uint32
2N/Aglue_spnego_gss_export_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_buffer_t interprocess_token)
2N/A{
2N/A return(spnego_gss_export_sec_context(minor_status,
2N/A context_handle,
2N/A interprocess_token));
2N/A}
2N/AOM_uint32
2N/Aspnego_gss_export_sec_context(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A gss_buffer_t interprocess_token)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_export_sec_context(minor_status,
2N/A context_handle,
2N/A interprocess_token);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aglue_spnego_gss_import_sec_context(
2N/A OM_uint32 *minor_status,
2N/A const gss_buffer_t interprocess_token,
2N/A gss_ctx_id_t *context_handle)
2N/A{
2N/A return(spnego_gss_import_sec_context(minor_status,
2N/A interprocess_token,
2N/A context_handle));
2N/A}
2N/AOM_uint32
2N/Aspnego_gss_import_sec_context(
2N/A OM_uint32 *minor_status,
2N/A const gss_buffer_t interprocess_token,
2N/A gss_ctx_id_t *context_handle)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_import_sec_context(minor_status,
2N/A interprocess_token,
2N/A context_handle);
2N/A return (ret);
2N/A}
2N/A#endif /* LEAN_CLIENT */
2N/A
2N/AOM_uint32
2N/Aglue_spnego_gss_inquire_context(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A gss_name_t *src_name,
2N/A gss_name_t *targ_name,
2N/A OM_uint32 *lifetime_rec,
2N/A gss_OID *mech_type,
2N/A OM_uint32 *ctx_flags,
2N/A int *locally_initiated,
2N/A int *opened)
2N/A{
2N/A return(spnego_gss_inquire_context(
2N/A minor_status,
2N/A context_handle,
2N/A src_name,
2N/A targ_name,
2N/A lifetime_rec,
2N/A mech_type,
2N/A ctx_flags,
2N/A locally_initiated,
2N/A opened));
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_inquire_context(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A gss_name_t *src_name,
2N/A gss_name_t *targ_name,
2N/A OM_uint32 *lifetime_rec,
2N/A gss_OID *mech_type,
2N/A OM_uint32 *ctx_flags,
2N/A int *locally_initiated,
2N/A int *opened)
2N/A{
2N/A OM_uint32 ret = GSS_S_COMPLETE;
2N/A
2N/A ret = gss_inquire_context(minor_status,
2N/A context_handle,
2N/A src_name,
2N/A targ_name,
2N/A lifetime_rec,
2N/A mech_type,
2N/A ctx_flags,
2N/A locally_initiated,
2N/A opened);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aglue_spnego_gss_wrap_size_limit(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A int conf_req_flag,
2N/A gss_qop_t qop_req,
2N/A OM_uint32 req_output_size,
2N/A OM_uint32 *max_input_size)
2N/A{
2N/A return(spnego_gss_wrap_size_limit(minor_status,
2N/A context_handle,
2N/A conf_req_flag,
2N/A qop_req,
2N/A req_output_size,
2N/A max_input_size));
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_wrap_size_limit(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A int conf_req_flag,
2N/A gss_qop_t qop_req,
2N/A OM_uint32 req_output_size,
2N/A OM_uint32 *max_input_size)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_wrap_size_limit(minor_status,
2N/A context_handle,
2N/A conf_req_flag,
2N/A qop_req,
2N/A req_output_size,
2N/A max_input_size);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_get_mic(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A gss_qop_t qop_req,
2N/A const gss_buffer_t message_buffer,
2N/A gss_buffer_t message_token)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_get_mic(minor_status,
2N/A context_handle,
2N/A qop_req,
2N/A message_buffer,
2N/A message_token);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_verify_mic(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A const gss_buffer_t msg_buffer,
2N/A const gss_buffer_t token_buffer,
2N/A gss_qop_t *qop_state)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_verify_mic(minor_status,
2N/A context_handle,
2N/A msg_buffer,
2N/A token_buffer,
2N/A qop_state);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_inquire_sec_context_by_oid(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A const gss_OID desired_object,
2N/A gss_buffer_set_t *data_set)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_inquire_sec_context_by_oid(minor_status,
2N/A context_handle,
2N/A desired_object,
2N/A data_set);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * SUNW17PACresync
2N/A * These GSS funcs not needed yet, so disable them.
2N/A * Revisit for full 1.7 resync.
2N/A */
2N/A#if 0
2N/AOM_uint32
2N/Aspnego_gss_set_sec_context_option(
2N/A OM_uint32 *minor_status,
2N/A gss_ctx_id_t *context_handle,
2N/A const gss_OID desired_object,
2N/A const gss_buffer_t value)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_set_sec_context_option(minor_status,
2N/A context_handle,
2N/A desired_object,
2N/A value);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_wrap_aead(OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A int conf_req_flag,
2N/A gss_qop_t qop_req,
2N/A gss_buffer_t input_assoc_buffer,
2N/A gss_buffer_t input_payload_buffer,
2N/A int *conf_state,
2N/A gss_buffer_t output_message_buffer)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_wrap_aead(minor_status,
2N/A context_handle,
2N/A conf_req_flag,
2N/A qop_req,
2N/A input_assoc_buffer,
2N/A input_payload_buffer,
2N/A conf_state,
2N/A output_message_buffer);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_unwrap_aead(OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A gss_buffer_t input_message_buffer,
2N/A gss_buffer_t input_assoc_buffer,
2N/A gss_buffer_t output_payload_buffer,
2N/A int *conf_state,
2N/A gss_qop_t *qop_state)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_unwrap_aead(minor_status,
2N/A context_handle,
2N/A input_message_buffer,
2N/A input_assoc_buffer,
2N/A output_payload_buffer,
2N/A conf_state,
2N/A qop_state);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_wrap_iov(OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A int conf_req_flag,
2N/A gss_qop_t qop_req,
2N/A int *conf_state,
2N/A gss_iov_buffer_desc *iov,
2N/A int iov_count)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_wrap_iov(minor_status,
2N/A context_handle,
2N/A conf_req_flag,
2N/A qop_req,
2N/A conf_state,
2N/A iov,
2N/A iov_count);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_unwrap_iov(OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A int *conf_state,
2N/A gss_qop_t *qop_state,
2N/A gss_iov_buffer_desc *iov,
2N/A int iov_count)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_unwrap_iov(minor_status,
2N/A context_handle,
2N/A conf_state,
2N/A qop_state,
2N/A iov,
2N/A iov_count);
2N/A return (ret);
2N/A}
2N/A
2N/AOM_uint32
2N/Aspnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2N/A gss_ctx_id_t context_handle,
2N/A int conf_req_flag,
2N/A gss_qop_t qop_req,
2N/A int *conf_state,
2N/A gss_iov_buffer_desc *iov,
2N/A int iov_count)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_wrap_iov_length(minor_status,
2N/A context_handle,
2N/A conf_req_flag,
2N/A qop_req,
2N/A conf_state,
2N/A iov,
2N/A iov_count);
2N/A return (ret);
2N/A}
2N/A
2N/A
2N/AOM_uint32
2N/Aspnego_gss_complete_auth_token(
2N/A OM_uint32 *minor_status,
2N/A const gss_ctx_id_t context_handle,
2N/A gss_buffer_t input_message_buffer)
2N/A{
2N/A OM_uint32 ret;
2N/A ret = gss_complete_auth_token(minor_status,
2N/A context_handle,
2N/A input_message_buffer);
2N/A return (ret);
2N/A}
2N/A#endif /* 0 */
2N/A
2N/A/*
2N/A * We will release everything but the ctx_handle so that it
2N/A * can be passed back to init/accept context. This routine should
2N/A * not be called until after the ctx_handle memory is assigned to
2N/A * the supplied context handle from init/accept context.
2N/A */
2N/Astatic void
2N/Arelease_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2N/A{
2N/A spnego_gss_ctx_id_t context;
2N/A OM_uint32 minor_stat;
2N/A context = *ctx;
2N/A
2N/A if (context != NULL) {
2N/A (void) gss_release_buffer(&minor_stat,
2N/A &context->DER_mechTypes);
2N/A
2N/A (void) generic_gss_release_oid(&minor_stat,
2N/A &context->internal_mech);
2N/A
2N/A if (context->optionStr != NULL) {
2N/A free(context->optionStr);
2N/A context->optionStr = NULL;
2N/A }
2N/A
2N/A /* Solaris SPNEGO */
2N/A spnego_clear_error_message(context);
2N/A
2N/A free(context);
2N/A *ctx = NULL;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Can't use gss_indicate_mechs by itself to get available mechs for
2N/A * SPNEGO because it will also return the SPNEGO mech and we do not
2N/A * want to consider SPNEGO as an available security mech for
2N/A * negotiation. For this reason, get_available_mechs will return
2N/A * all available mechs except SPNEGO.
2N/A *
2N/A * If a ptr to a creds list is given, this function will attempt
2N/A * to acquire creds for the creds given and trim the list of
2N/A * returned mechanisms to only those for which creds are valid.
2N/A *
2N/A */
2N/Astatic OM_uint32
2N/Aget_available_mechs(OM_uint32 *minor_status,
2N/A gss_name_t name, gss_cred_usage_t usage,
2N/A gss_cred_id_t *creds, gss_OID_set *rmechs)
2N/A{
2N/A unsigned int i;
2N/A int found = 0;
2N/A OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2N/A gss_OID_set mechs, goodmechs;
2N/A char *msinterop = getenv("MS_INTEROP");
2N/A
2N/A major_status = gss_indicate_mechs(minor_status, &mechs);
2N/A
2N/A if (major_status != GSS_S_COMPLETE) {
2N/A return (major_status);
2N/A }
2N/A
2N/A major_status = gss_create_empty_oid_set(minor_status, rmechs);
2N/A
2N/A if (major_status != GSS_S_COMPLETE) {
2N/A (void) gss_release_oid_set(minor_status, &mechs);
2N/A return (major_status);
2N/A }
2N/A
2N/A for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2N/A if ((mechs->elements[i].length
2N/A != spnego_mechanism.mech_type.length) ||
2N/A memcmp(mechs->elements[i].elements,
2N/A spnego_mechanism.mech_type.elements,
2N/A spnego_mechanism.mech_type.length)) {
2N/A /*
2N/A * Solaris SPNEGO: gss_indicate_mechs is stupid as
2N/A * it never inferences any of the related OIDs of the
2N/A * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2N/A * We add KRB5_WRONG here so that old MS clients can
2N/A * negotiate this mechanism, which allows extensions
2N/A * in Kerberos (clock skew adjustment, refresh ccache).
2N/A */
2N/A if (is_kerb_mech(&mechs->elements[i])) {
2N/A extern gss_OID_desc * const gss_mech_krb5_wrong;
2N/A
2N/A major_status =
2N/A gss_add_oid_set_member(minor_status,
2N/A gss_mech_krb5_wrong, rmechs);
2N/A }
2N/A
2N/A major_status = gss_add_oid_set_member(minor_status,
2N/A &mechs->elements[i],
2N/A rmechs);
2N/A if (major_status == GSS_S_COMPLETE)
2N/A found++;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Add NTLMSSP OID to the mech OID set only if MS_INTEROP env var
2N/A * has been set.
2N/A *
2N/A * This is a requirement until NTLMSSP is implemented as a GSS-API
2N/A * plugin.
2N/A */
2N/A if ((msinterop != NULL) && (!strcmp(msinterop, "1"))) {
2N/A major_status = gss_add_oid_set_member(minor_status,
2N/A &ntlmssp_oid, rmechs);
2N/A
2N/A if (major_status == GSS_S_COMPLETE)
2N/A found++;
2N/A }
2N/A
2N/A /*
2N/A * If the caller wanted a list of creds returned,
2N/A * trim the list of mechanisms down to only those
2N/A * for which the creds are valid.
2N/A */
2N/A if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2N/A major_status = gss_acquire_cred(minor_status,
2N/A name, GSS_C_INDEFINITE,
2N/A *rmechs, usage, creds,
2N/A &goodmechs, NULL);
2N/A
2N/A /*
2N/A * Drop the old list in favor of the new
2N/A * "trimmed" list.
2N/A */
2N/A (void) gss_release_oid_set(&tmpmin, rmechs);
2N/A if (major_status == GSS_S_COMPLETE) {
2N/A (void) gssint_copy_oid_set(&tmpmin,
2N/A goodmechs, rmechs);
2N/A (void) gss_release_oid_set(&tmpmin, &goodmechs);
2N/A }
2N/A }
2N/A
2N/A (void) gss_release_oid_set(&tmpmin, &mechs);
2N/A if (found == 0 || major_status != GSS_S_COMPLETE) {
2N/A *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2N/A if (major_status == GSS_S_COMPLETE)
2N/A major_status = GSS_S_FAILURE;
2N/A }
2N/A
2N/A return (major_status);
2N/A}
2N/A
2N/A/* following are token creation and reading routines */
2N/A
2N/A/*
2N/A * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2N/A * advance the buffer, otherwise, decode the mech_oid from the buffer and
2N/A * place in gss_OID.
2N/A */
2N/Astatic gss_OID
2N/Aget_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2N/A{
2N/A OM_uint32 status;
2N/A gss_OID_desc toid;
2N/A gss_OID mech_out = NULL;
2N/A unsigned char *start, *end;
2N/A
2N/A if (length < 1 || **buff_in != MECH_OID)
2N/A return (NULL);
2N/A
2N/A start = *buff_in;
2N/A end = start + length;
2N/A
2N/A (*buff_in)++;
2N/A toid.length = *(*buff_in)++;
2N/A
2N/A if ((*buff_in + toid.length) > end)
2N/A return (NULL);
2N/A
2N/A toid.elements = *buff_in;
2N/A *buff_in += toid.length;
2N/A
2N/A status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2N/A
2N/A if (status != GSS_S_COMPLETE) {
2N/A map_errcode(minor_status);
2N/A mech_out = NULL;
2N/A }
2N/A
2N/A return (mech_out);
2N/A}
2N/A
2N/A/*
2N/A * der encode the given mechanism oid into buf_out, advancing the
2N/A * buffer pointer.
2N/A */
2N/A
2N/Astatic int
2N/Aput_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2N/A{
2N/A if (buflen < mech->length + 2)
2N/A return (-1);
2N/A *(*buf_out)++ = MECH_OID;
2N/A *(*buf_out)++ = (unsigned char) mech->length;
2N/A memcpy((void *)(*buf_out), mech->elements, mech->length);
2N/A *buf_out += mech->length;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * verify that buff_in points to an octet string, if it does not,
2N/A * return NULL and don't advance the pointer. If it is an octet string
2N/A * decode buff_in into a gss_buffer_t and return it, advancing the
2N/A * buffer pointer.
2N/A */
2N/Astatic gss_buffer_t
2N/Aget_input_token(unsigned char **buff_in, unsigned int buff_length)
2N/A{
2N/A gss_buffer_t input_token;
2N/A unsigned int bytes;
2N/A
2N/A if (**buff_in != OCTET_STRING)
2N/A return (NULL);
2N/A
2N/A (*buff_in)++;
2N/A input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2N/A
2N/A if (input_token == NULL)
2N/A return (NULL);
2N/A
2N/A input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
2N/A if ((int)input_token->length == -1) {
2N/A free(input_token);
2N/A return (NULL);
2N/A }
2N/A input_token->value = malloc(input_token->length);
2N/A
2N/A if (input_token->value == NULL) {
2N/A free(input_token);
2N/A return (NULL);
2N/A }
2N/A
2N/A (void) memcpy(input_token->value, *buff_in, input_token->length);
2N/A *buff_in += input_token->length;
2N/A return (input_token);
2N/A}
2N/A
2N/A/*
2N/A * verify that the input token length is not 0. If it is, just return.
2N/A * If the token length is greater than 0, der encode as an octet string
2N/A * and place in buf_out, advancing buf_out.
2N/A */
2N/A
2N/Astatic int
2N/Aput_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2N/A unsigned int buflen)
2N/A{
2N/A int ret;
2N/A
2N/A /* if token length is 0, we do not want to send */
2N/A if (input_token->length == 0)
2N/A return (0);
2N/A
2N/A if (input_token->length > buflen)
2N/A return (-1);
2N/A
2N/A *(*buf_out)++ = OCTET_STRING;
2N/A if ((ret = gssint_put_der_length(input_token->length, buf_out,
2N/A input_token->length)))
2N/A return (ret);
2N/A TWRITE_STR(*buf_out, input_token->value, input_token->length);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * verify that buff_in points to a sequence of der encoding. The mech
2N/A * set is the only sequence of encoded object in the token, so if it is
2N/A * a sequence of encoding, decode the mechset into a gss_OID_set and
2N/A * return it, advancing the buffer pointer.
2N/A */
2N/Astatic gss_OID_set
2N/Aget_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2N/A unsigned int buff_length)
2N/A{
2N/A gss_OID_set returned_mechSet;
2N/A OM_uint32 major_status;
2N/A int length; /* SUNW17PACresync */
2N/A OM_uint32 bytes;
2N/A OM_uint32 set_length;
2N/A unsigned char *start;
2N/A int i;
2N/A
2N/A if (**buff_in != SEQUENCE_OF)
2N/A return (NULL);
2N/A
2N/A start = *buff_in;
2N/A (*buff_in)++;
2N/A
2N/A length = gssint_get_der_length(buff_in, buff_length, &bytes);
2N/A if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
2N/A return (NULL);
2N/A
2N/A major_status = gss_create_empty_oid_set(minor_status,
2N/A &returned_mechSet);
2N/A if (major_status != GSS_S_COMPLETE)
2N/A return (NULL);
2N/A
2N/A for (set_length = 0, i = 0; set_length < length; i++) {
2N/A gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
2N/A buff_length - (*buff_in - start));
2N/A if (temp != NULL) {
2N/A major_status = gss_add_oid_set_member(minor_status,
2N/A temp, &returned_mechSet);
2N/A if (major_status == GSS_S_COMPLETE) {
2N/A set_length += returned_mechSet->elements[i].length +2;
2N/A if (generic_gss_release_oid(minor_status, &temp))
2N/A map_errcode(minor_status);
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (returned_mechSet);
2N/A}
2N/A
2N/A/*
2N/A * Encode mechSet into buf.
2N/A */
2N/Astatic int
2N/Aput_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
2N/A{
2N/A unsigned char *ptr;
2N/A unsigned int i;
2N/A unsigned int tlen, ilen;
2N/A
2N/A tlen = ilen = 0;
2N/A for (i = 0; i < mechSet->count; i++) {
2N/A /*
2N/A * 0x06 [DER LEN] [OID]
2N/A */
2N/A ilen += 1 +
2N/A gssint_der_length_size(mechSet->elements[i].length) +
2N/A mechSet->elements[i].length;
2N/A }
2N/A /*
2N/A * 0x30 [DER LEN]
2N/A */
2N/A tlen = 1 + gssint_der_length_size(ilen) + ilen;
2N/A ptr = malloc(tlen);
2N/A if (ptr == NULL)
2N/A return -1;
2N/A
2N/A buf->value = ptr;
2N/A buf->length = tlen;
2N/A#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
2N/A
2N/A *ptr++ = SEQUENCE_OF;
2N/A if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
2N/A return -1;
2N/A for (i = 0; i < mechSet->count; i++) {
2N/A if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
2N/A return -1;
2N/A }
2N/A }
2N/A return 0;
2N/A#undef REMAIN
2N/A}
2N/A
2N/A/*
2N/A * Verify that buff_in is pointing to a BIT_STRING with the correct
2N/A * length and padding for the req_flags. If it is, decode req_flags
2N/A * and return them, otherwise, return NULL.
2N/A */
2N/Astatic OM_uint32
2N/Aget_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
2N/A OM_uint32 *req_flags)
2N/A{
2N/A unsigned int len;
2N/A
2N/A if (**buff_in != (CONTEXT | 0x01))
2N/A return (0);
2N/A
2N/A if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
2N/A bodysize, &len) < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (*(*buff_in)++ != BIT_STRING)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (*(*buff_in)++ != BIT_STRING_LENGTH)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (*(*buff_in)++ != BIT_STRING_PADDING)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
2N/A return (0);
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Aget_negTokenInit(OM_uint32 *minor_status,
2N/A gss_buffer_t buf,
2N/A gss_buffer_t der_mechSet,
2N/A gss_OID_set *mechSet,
2N/A OM_uint32 *req_flags,
2N/A gss_buffer_t *mechtok,
2N/A gss_buffer_t *mechListMIC)
2N/A{
2N/A OM_uint32 err;
2N/A unsigned char *ptr, *bufstart;
2N/A unsigned int len;
2N/A gss_buffer_desc tmpbuf;
2N/A
2N/A *minor_status = 0;
2N/A der_mechSet->length = 0;
2N/A der_mechSet->value = NULL;
2N/A *mechSet = GSS_C_NO_OID_SET;
2N/A *req_flags = 0;
2N/A *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
2N/A
2N/A ptr = bufstart = buf->value;
2N/A if ((buf->length - (ptr - bufstart)) > INT_MAX)
2N/A return GSS_S_FAILURE;
2N/A#define REMAIN (buf->length - (ptr - bufstart))
2N/A
2N/A err = g_verify_token_header(gss_mech_spnego,
2N/A &len, &ptr, 0, REMAIN);
2N/A if (err) {
2N/A *minor_status = err;
2N/A map_errcode(minor_status);
2N/A return GSS_S_FAILURE;
2N/A }
2N/A *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
2N/A if (*minor_status) {
2N/A map_errcode(minor_status);
2N/A return GSS_S_FAILURE;
2N/A }
2N/A
2N/A /* alias into input_token */
2N/A tmpbuf.value = ptr;
2N/A tmpbuf.length = REMAIN;
2N/A *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
2N/A if (*mechSet == NULL)
2N/A return GSS_S_FAILURE;
2N/A
2N/A tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
2N/A der_mechSet->value = malloc(tmpbuf.length);
2N/A if (der_mechSet->value == NULL)
2N/A return GSS_S_FAILURE;
2N/A memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
2N/A der_mechSet->length = tmpbuf.length;
2N/A
2N/A err = get_req_flags(&ptr, REMAIN, req_flags);
2N/A if (err != GSS_S_COMPLETE) {
2N/A return err;
2N/A }
2N/A if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
2N/A REMAIN, &len) >= 0) {
2N/A *mechtok = get_input_token(&ptr, len);
2N/A if (*mechtok == GSS_C_NO_BUFFER) {
2N/A return GSS_S_FAILURE;
2N/A }
2N/A }
2N/A if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
2N/A REMAIN, &len) >= 0) {
2N/A *mechListMIC = get_input_token(&ptr, len);
2N/A if (*mechListMIC == GSS_C_NO_BUFFER) {
2N/A return GSS_S_FAILURE;
2N/A }
2N/A }
2N/A return GSS_S_COMPLETE;
2N/A#undef REMAIN
2N/A}
2N/A
2N/Astatic OM_uint32
2N/Aget_negTokenResp(OM_uint32 *minor_status,
2N/A unsigned char *buf, unsigned int buflen,
2N/A OM_uint32 *negState,
2N/A gss_OID *supportedMech,
2N/A gss_buffer_t *responseToken,
2N/A gss_buffer_t *mechListMIC)
2N/A{
2N/A unsigned char *ptr, *bufstart;
2N/A unsigned int len;
2N/A int tmplen;
2N/A unsigned int tag, bytes;
2N/A
2N/A *negState = ACCEPT_DEFECTIVE_TOKEN;
2N/A *supportedMech = GSS_C_NO_OID;
2N/A *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
2N/A ptr = bufstart = buf;
2N/A#define REMAIN (buflen - (ptr - bufstart))
2N/A
2N/A if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A if (*ptr++ == SEQUENCE) {
2N/A tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
2N/A if (tmplen < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A if (REMAIN < 1)
2N/A tag = 0;
2N/A else
2N/A tag = *ptr++;
2N/A
2N/A if (tag == CONTEXT) {
2N/A tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
2N/A if (tmplen < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (g_get_tag_and_length(&ptr, ENUMERATED,
2N/A REMAIN, &len) < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (len != ENUMERATION_LENGTH)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (REMAIN < 1)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A *negState = *ptr++;
2N/A
2N/A if (REMAIN < 1)
2N/A tag = 0;
2N/A else
2N/A tag = *ptr++;
2N/A }
2N/A if (tag == (CONTEXT | 0x01)) {
2N/A tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
2N/A if (tmplen < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
2N/A if (*supportedMech == GSS_C_NO_OID)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (REMAIN < 1)
2N/A tag = 0;
2N/A else
2N/A tag = *ptr++;
2N/A }
2N/A if (tag == (CONTEXT | 0x02)) {
2N/A tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
2N/A if (tmplen < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A *responseToken = get_input_token(&ptr, REMAIN);
2N/A if (*responseToken == GSS_C_NO_BUFFER)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A if (REMAIN < 1)
2N/A tag = 0;
2N/A else
2N/A tag = *ptr++;
2N/A }
2N/A if (tag == (CONTEXT | 0x03)) {
2N/A tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
2N/A if (tmplen < 0)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A
2N/A *mechListMIC = get_input_token(&ptr, REMAIN);
2N/A if (*mechListMIC == GSS_C_NO_BUFFER)
2N/A return GSS_S_DEFECTIVE_TOKEN;
2N/A }
2N/A return GSS_S_COMPLETE;
2N/A#undef REMAIN
2N/A}
2N/A
2N/A/*
2N/A * der encode the passed negResults as an ENUMERATED type and
2N/A * place it in buf_out, advancing the buffer.
2N/A */
2N/A
2N/Astatic int
2N/Aput_negResult(unsigned char **buf_out, OM_uint32 negResult,
2N/A unsigned int buflen)
2N/A{
2N/A if (buflen < 3)
2N/A return (-1);
2N/A *(*buf_out)++ = ENUMERATED;
2N/A *(*buf_out)++ = ENUMERATION_LENGTH;
2N/A *(*buf_out)++ = (unsigned char) negResult;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This routine compares the recieved mechset to the mechset that
2N/A * this server can support. It looks sequentially through the mechset
2N/A * and the first one that matches what the server can support is
2N/A * chosen as the negotiated mechanism. If one is found, negResult
2N/A * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
2N/A * it's not the first mech, otherwise we return NULL and negResult
2N/A * is set to REJECT.
2N/A *
2N/A * NOTE: There is currently no way to specify a preference order of
2N/A * mechanisms supported by the acceptor.
2N/A */
2N/Astatic gss_OID
2N/Anegotiate_mech_type(OM_uint32 *minor_status,
2N/A gss_OID_set supported_mechSet,
2N/A gss_OID_set mechset,
2N/A OM_uint32 *negResult)
2N/A{
2N/A gss_OID returned_mech;
2N/A OM_uint32 status;
2N/A int present;
2N/A unsigned int i;
2N/A
2N/A for (i = 0; i < mechset->count; i++) {
2N/A gss_OID mech_oid = &mechset->elements[i];
2N/A
2N/A /*
2N/A * Solaris SPNEGO: MIT compares against MS' wrong OID, but
2N/A * we actually want to select it if the client supports, as this
2N/A * will enable features on MS clients that allow credential
2N/A * refresh on rekeying and caching system times from servers.
2N/A */
2N/A#if 0
2N/A /* Accept wrong mechanism OID from MS clients */
2N/A if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
2N/A memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
2N/A mech_oid = (gss_OID)&gss_mech_krb5_oid;
2N/A#endif
2N/A
2N/A gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
2N/A if (!present)
2N/A continue;
2N/A
2N/A if (i == 0)
2N/A *negResult = ACCEPT_INCOMPLETE;
2N/A else
2N/A *negResult = REQUEST_MIC;
2N/A
2N/A status = generic_gss_copy_oid(minor_status,
2N/A &mechset->elements[i],
2N/A &returned_mech);
2N/A if (status != GSS_S_COMPLETE) {
2N/A *negResult = REJECT;
2N/A map_errcode(minor_status);
2N/A return (NULL);
2N/A }
2N/A return (returned_mech);
2N/A }
2N/A /* Solaris SPNEGO */
2N/A *minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
2N/A
2N/A *negResult = REJECT;
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * the next two routines make a token buffer suitable for
2N/A * spnego_gss_display_status. These currently take the string
2N/A * in name and place it in the token. Eventually, if
2N/A * spnego_gss_display_status returns valid error messages,
2N/A * these routines will be changes to return the error string.
2N/A */
2N/Astatic spnego_token_t
2N/Amake_spnego_token(char *name)
2N/A{
2N/A return (spnego_token_t)strdup(name);
2N/A}
2N/A
2N/Astatic gss_buffer_desc
2N/Amake_err_msg(char *name)
2N/A{
2N/A gss_buffer_desc buffer;
2N/A
2N/A if (name == NULL) {
2N/A buffer.length = 0;
2N/A buffer.value = NULL;
2N/A } else {
2N/A buffer.length = strlen(name)+1;
2N/A buffer.value = make_spnego_token(name);
2N/A }
2N/A
2N/A return (buffer);
2N/A}
2N/A
2N/A/*
2N/A * Create the client side spnego token passed back to gss_init_sec_context
2N/A * and eventually up to the application program and over to the server.
2N/A *
2N/A * Use DER rules, definite length method per RFC 2478
2N/A */
2N/Astatic int
2N/Amake_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
2N/A int negHintsCompat,
2N/A gss_buffer_t mechListMIC, OM_uint32 req_flags,
2N/A gss_buffer_t data, send_token_flag sendtoken,
2N/A gss_buffer_t outbuf)
2N/A{
2N/A int ret = 0;
2N/A unsigned int tlen, dataLen = 0;
2N/A unsigned int negTokenInitSize = 0;
2N/A unsigned int negTokenInitSeqSize = 0;
2N/A unsigned int negTokenInitContSize = 0;
2N/A unsigned int rspTokenSize = 0;
2N/A unsigned int mechListTokenSize = 0;
2N/A unsigned int micTokenSize = 0;
2N/A unsigned char *t;
2N/A unsigned char *ptr;
2N/A
2N/A if (outbuf == GSS_C_NO_BUFFER)
2N/A return (-1);
2N/A
2N/A outbuf->length = 0;
2N/A outbuf->value = NULL;
2N/A
2N/A /* calculate the data length */
2N/A
2N/A /*
2N/A * 0xa0 [DER LEN] [mechTypes]
2N/A */
2N/A mechListTokenSize = 1 +
2N/A gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
2N/A spnego_ctx->DER_mechTypes.length;
2N/A dataLen += mechListTokenSize;
2N/A
2N/A /*
2N/A * If a token from gss_init_sec_context exists,
2N/A * add the length of the token + the ASN.1 overhead
2N/A */
2N/A if (data != NULL) {
2N/A /*
2N/A * Encoded in final output as:
2N/A * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
2N/A * -----s--------|--------s2----------
2N/A */
2N/A rspTokenSize = 1 +
2N/A gssint_der_length_size(data->length) +
2N/A data->length;
2N/A dataLen += 1 + gssint_der_length_size(rspTokenSize) +
2N/A rspTokenSize;
2N/A }
2N/A
2N/A if (mechListMIC) {
2N/A /*
2N/A * Encoded in final output as:
2N/A * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
2N/A * --s-- -----tlen------------
2N/A */
2N/A micTokenSize = 1 +
2N/A gssint_der_length_size(mechListMIC->length) +
2N/A mechListMIC->length;
2N/A dataLen += 1 +
2N/A gssint_der_length_size(micTokenSize) +
2N/A micTokenSize;
2N/A }
2N/A
2N/A /*
2N/A * Add size of DER encoding
2N/A * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
2N/A * 0x30 [DER_LEN] [data]
2N/A *
2N/A */
2N/A negTokenInitContSize = dataLen;
2N/A negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
2N/A dataLen = negTokenInitSeqSize;
2N/A
2N/A /*
2N/A * negTokenInitSize indicates the bytes needed to
2N/A * hold the ASN.1 encoding of the entire NegTokenInit
2N/A * SEQUENCE.
2N/A * 0xa0 [DER_LEN] + data
2N/A *
2N/A */
2N/A negTokenInitSize = 1 +
2N/A gssint_der_length_size(negTokenInitSeqSize) +
2N/A negTokenInitSeqSize;
2N/A
2N/A tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
2N/A
2N/A t = (unsigned char *) malloc(tlen);
2N/A
2N/A if (t == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A ptr = t;
2N/A
2N/A /* create the message */
2N/A if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
2N/A &ptr, tlen)))
2N/A goto errout;
2N/A
2N/A *ptr++ = CONTEXT; /* NegotiationToken identifier */
2N/A if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
2N/A goto errout;
2N/A
2N/A *ptr++ = SEQUENCE;
2N/A if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
2N/A tlen - (int)(ptr-t))))
2N/A goto errout;
2N/A
2N/A *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
2N/A if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
2N/A &ptr, tlen - (int)(ptr-t))))
2N/A goto errout;
2N/A
2N/A /* We already encoded the MechSetList */
2N/A (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
2N/A spnego_ctx->DER_mechTypes.length);
2N/A
2N/A ptr += spnego_ctx->DER_mechTypes.length;
2N/A
2N/A if (data != NULL) {
2N/A *ptr++ = CONTEXT | 0x02;
2N/A if ((ret = gssint_put_der_length(rspTokenSize,
2N/A &ptr, tlen - (int)(ptr - t))))
2N/A goto errout;
2N/A
2N/A if ((ret = put_input_token(&ptr, data,
2N/A tlen - (int)(ptr - t))))
2N/A goto errout;
2N/A }
2N/A
2N/A if (mechListMIC != GSS_C_NO_BUFFER) {
2N/A *ptr++ = CONTEXT | 0x03;
2N/A if ((ret = gssint_put_der_length(micTokenSize,
2N/A &ptr, tlen - (int)(ptr - t))))
2N/A goto errout;
2N/A
2N/A if (negHintsCompat) {
2N/A ret = put_neg_hints(&ptr, mechListMIC,
2N/A tlen - (int)(ptr - t));
2N/A if (ret)
2N/A goto errout;
2N/A } else if ((ret = put_input_token(&ptr, mechListMIC,
2N/A tlen - (int)(ptr - t))))
2N/A goto errout;
2N/A }
2N/A
2N/Aerrout:
2N/A if (ret != 0) {
2N/A if (t)
2N/A free(t);
2N/A t = NULL;
2N/A tlen = 0;
2N/A }
2N/A outbuf->length = tlen;
2N/A outbuf->value = (void *) t;
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * create the server side spnego token passed back to
2N/A * gss_accept_sec_context and eventually up to the application program
2N/A * and over to the client.
2N/A */
2N/Astatic int
2N/Amake_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
2N/A gss_buffer_t data, gss_buffer_t mechListMIC,
2N/A send_token_flag sendtoken,
2N/A gss_buffer_t outbuf)
2N/A{
2N/A unsigned int tlen = 0;
2N/A unsigned int ret = 0;
2N/A unsigned int NegTokenTargSize = 0;
2N/A unsigned int NegTokenSize = 0;
2N/A unsigned int rspTokenSize = 0;
2N/A unsigned int micTokenSize = 0;
2N/A unsigned int dataLen = 0;
2N/A unsigned char *t;
2N/A unsigned char *ptr;
2N/A
2N/A if (outbuf == GSS_C_NO_BUFFER)
2N/A return (GSS_S_DEFECTIVE_TOKEN);
2N/A
2N/A outbuf->length = 0;
2N/A outbuf->value = NULL;
2N/A
2N/A /*
2N/A * ASN.1 encoding of the negResult
2N/A * ENUMERATED type is 3 bytes
2N/A * ENUMERATED TAG, Length, Value,
2N/A * Plus 2 bytes for the CONTEXT id and length.
2N/A */
2N/A dataLen = 5;
2N/A
2N/A /*
2N/A * calculate data length
2N/A *
2N/A * If this is the initial token, include length of
2N/A * mech_type and the negotiation result fields.
2N/A */
2N/A if (sendtoken == INIT_TOKEN_SEND) {
2N/A int mechlistTokenSize;
2N/A /*
2N/A * 1 byte for the CONTEXT ID(0xa0),
2N/A * 1 byte for the OID ID(0x06)
2N/A * 1 byte for OID Length field
2N/A * Plus the rest... (OID Length, OID value)
2N/A */
2N/A mechlistTokenSize = 3 + mech_wanted->length +
2N/A gssint_der_length_size(mech_wanted->length);
2N/A
2N/A dataLen += mechlistTokenSize;
2N/A }
2N/A if (data != NULL && data->length > 0) {
2N/A /* Length of the inner token */
2N/A rspTokenSize = 1 + gssint_der_length_size(data->length) +
2N/A data->length;
2N/A
2N/A dataLen += rspTokenSize;
2N/A
2N/A /* Length of the outer token */
2N/A dataLen += 1 + gssint_der_length_size(rspTokenSize);
2N/A }
2N/A if (mechListMIC != NULL) {
2N/A
2N/A /* Length of the inner token */
2N/A micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
2N/A mechListMIC->length;
2N/A
2N/A dataLen += micTokenSize;
2N/A
2N/A /* Length of the outer token */
2N/A dataLen += 1 + gssint_der_length_size(micTokenSize);
2N/A }
2N/A /*
2N/A * Add size of DER encoded:
2N/A * NegTokenTarg [ SEQUENCE ] of
2N/A * NegResult[0] ENUMERATED {
2N/A * accept_completed(0),
2N/A * accept_incomplete(1),
2N/A * reject(2) }
2N/A * supportedMech [1] MechType OPTIONAL,
2N/A * responseToken [2] OCTET STRING OPTIONAL,
2N/A * mechListMIC [3] OCTET STRING OPTIONAL
2N/A *
2N/A * size = data->length + MechListMic + SupportedMech len +
2N/A * Result Length + ASN.1 overhead
2N/A */
2N/A NegTokenTargSize = dataLen;
2N/A dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
2N/A
2N/A /*
2N/A * NegotiationToken [ CHOICE ]{
2N/A * negTokenInit [0] NegTokenInit,
2N/A * negTokenTarg [1] NegTokenTarg }
2N/A */
2N/A NegTokenSize = dataLen;
2N/A dataLen += 1 + gssint_der_length_size(NegTokenSize);
2N/A
2N/A tlen = dataLen;
2N/A t = (unsigned char *) malloc(tlen);
2N/A
2N/A if (t == NULL) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A
2N/A ptr = t;
2N/A
2N/A /*
2N/A * Indicate that we are sending CHOICE 1
2N/A * (NegTokenTarg)
2N/A */
2N/A *ptr++ = CONTEXT | 0x01;
2N/A if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A *ptr++ = SEQUENCE;
2N/A if (gssint_put_der_length(NegTokenTargSize, &ptr,
2N/A tlen - (int)(ptr-t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A
2N/A /*
2N/A * First field of the NegTokenTarg SEQUENCE
2N/A * is the ENUMERATED NegResult.
2N/A */
2N/A *ptr++ = CONTEXT;
2N/A if (gssint_put_der_length(3, &ptr,
2N/A tlen - (int)(ptr-t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A if (sendtoken == INIT_TOKEN_SEND) {
2N/A /*
2N/A * Next, is the Supported MechType
2N/A */
2N/A *ptr++ = CONTEXT | 0x01;
2N/A if (gssint_put_der_length(mech_wanted->length + 2,
2N/A &ptr,
2N/A tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A if (put_mech_oid(&ptr, mech_wanted,
2N/A tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A }
2N/A if (data != NULL && data->length > 0) {
2N/A *ptr++ = CONTEXT | 0x02;
2N/A if (gssint_put_der_length(rspTokenSize, &ptr,
2N/A tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A if (put_input_token(&ptr, data,
2N/A tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A }
2N/A if (mechListMIC != NULL) {
2N/A *ptr++ = CONTEXT | 0x03;
2N/A if (gssint_put_der_length(micTokenSize, &ptr,
2N/A tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A if (put_input_token(&ptr, mechListMIC,
2N/A tlen - (int)(ptr - t)) < 0) {
2N/A ret = GSS_S_DEFECTIVE_TOKEN;
2N/A goto errout;
2N/A }
2N/A }
2N/A ret = GSS_S_COMPLETE;
2N/Aerrout:
2N/A if (ret != GSS_S_COMPLETE) {
2N/A if (t)
2N/A free(t);
2N/A } else {
2N/A outbuf->length = ptr - t;
2N/A outbuf->value = (void *) t;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/* determine size of token */
2N/Astatic int
2N/Ag_token_size(gss_OID_const mech, unsigned int body_size)
2N/A{
2N/A int hdrsize;
2N/A
2N/A /*
2N/A * Initialize the header size to the
2N/A * MECH_OID byte + the bytes needed to indicate the
2N/A * length of the OID + the OID itself.
2N/A *
2N/A * 0x06 [MECHLENFIELD] MECHDATA
2N/A */
2N/A hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
2N/A
2N/A /*
2N/A * Now add the bytes needed for the initial header
2N/A * token bytes:
2N/A * 0x60 + [DER_LEN] + HDRSIZE
2N/A */
2N/A hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
2N/A
2N/A return (hdrsize + body_size);
2N/A}
2N/A
2N/A/*
2N/A * generate token header.
2N/A *
2N/A * Use DER Definite Length method per RFC2478
2N/A * Use of indefinite length encoding will not be compatible
2N/A * with Microsoft or others that actually follow the spec.
2N/A */
2N/Astatic int
2N/Ag_make_token_header(gss_OID_const mech,
2N/A unsigned int body_size,
2N/A unsigned char **buf,
2N/A unsigned int totallen)
2N/A{
2N/A int ret = 0;
2N/A unsigned int hdrsize;
2N/A unsigned char *p = *buf;
2N/A
2N/A hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
2N/A
2N/A *(*buf)++ = HEADER_ID;
2N/A if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
2N/A return (ret);
2N/A
2N/A *(*buf)++ = MECH_OID;
2N/A if ((ret = gssint_put_der_length(mech->length, buf,
2N/A totallen - (int)(p - *buf))))
2N/A return (ret);
2N/A TWRITE_STR(*buf, mech->elements, mech->length);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * NOTE: This checks that the length returned by
2N/A * gssint_get_der_length() is not greater than the number of octets
2N/A * remaining, even though gssint_get_der_length() already checks, in
2N/A * theory.
2N/A */
2N/Astatic int
2N/Ag_get_tag_and_length(unsigned char **buf, int tag,
2N/A unsigned int buflen, unsigned int *outlen)
2N/A{
2N/A unsigned char *ptr = *buf;
2N/A int ret = -1; /* pessimists, assume failure ! */
2N/A unsigned int encoded_len;
2N/A unsigned int tmplen = 0;
2N/A
2N/A *outlen = 0;
2N/A if (buflen > 1 && *ptr == tag) {
2N/A ptr++;
2N/A tmplen = gssint_get_der_length(&ptr, buflen - 1,
2N/A &encoded_len);
2N/A if (tmplen < 0) {
2N/A ret = -1;
2N/A } else if (tmplen > buflen - (ptr - *buf)) {
2N/A ret = -1;
2N/A } else
2N/A ret = 0;
2N/A }
2N/A *outlen = tmplen;
2N/A *buf = ptr;
2N/A return (ret);
2N/A}
2N/A
2N/Astatic int
2N/Ag_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
2N/A{
2N/A unsigned char *buf = *buf_in;
2N/A unsigned char *endptr = buf + cur_size;
2N/A unsigned int seqsize;
2N/A int ret = 0;
2N/A unsigned int bytes;
2N/A
2N/A /*
2N/A * Verify this is a NegotiationToken type token
2N/A * - check for a0(context specific identifier)
2N/A * - get length and verify that enoughd ata exists
2N/A */
2N/A if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A cur_size = seqsize; /* should indicate bytes remaining */
2N/A
2N/A /*
2N/A * Verify the next piece, it should identify this as
2N/A * a strucure of type NegTokenInit.
2N/A */
2N/A if (*buf++ == SEQUENCE) {
2N/A if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
2N/A return (G_BAD_TOK_HEADER);
2N/A /*
2N/A * Make sure we have the entire buffer as described
2N/A */
2N/A if (buf + seqsize > endptr)
2N/A return (G_BAD_TOK_HEADER);
2N/A } else {
2N/A return (G_BAD_TOK_HEADER);
2N/A }
2N/A
2N/A cur_size = seqsize; /* should indicate bytes remaining */
2N/A
2N/A /*
2N/A * Verify that the first blob is a sequence of mechTypes
2N/A */
2N/A if (*buf++ == CONTEXT) {
2N/A if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
2N/A return (G_BAD_TOK_HEADER);
2N/A /*
2N/A * Make sure we have the entire buffer as described
2N/A */
2N/A if (buf + bytes > endptr)
2N/A return (G_BAD_TOK_HEADER);
2N/A } else {
2N/A return (G_BAD_TOK_HEADER);
2N/A }
2N/A
2N/A /*
2N/A * At this point, *buf should be at the beginning of the
2N/A * DER encoded list of mech types that are to be negotiated.
2N/A */
2N/A *buf_in = buf;
2N/A
2N/A return (ret);
2N/A
2N/A}
2N/A
2N/A/* verify token header. */
2N/Astatic int
2N/Ag_verify_token_header(gss_OID_const mech,
2N/A unsigned int *body_size,
2N/A unsigned char **buf_in,
2N/A int tok_type,
2N/A unsigned int toksize)
2N/A{
2N/A unsigned char *buf = *buf_in;
2N/A int seqsize;
2N/A gss_OID_desc toid;
2N/A int ret = 0;
2N/A unsigned int bytes;
2N/A
2N/A if (toksize-- < 1)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A if (*buf++ != HEADER_ID)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A if ((seqsize + bytes) != toksize)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A if (toksize-- < 1)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A
2N/A if (*buf++ != MECH_OID)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A if (toksize-- < 1)
2N/A return (G_BAD_TOK_HEADER);
2N/A
2N/A toid.length = *buf++;
2N/A
2N/A if (toksize < toid.length)
2N/A return (G_BAD_TOK_HEADER);
2N/A else
2N/A toksize -= toid.length;
2N/A
2N/A toid.elements = buf;
2N/A buf += toid.length;
2N/A
2N/A if (!g_OID_equal(&toid, mech))
2N/A ret = G_WRONG_MECH;
2N/A
2N/A /*
2N/A * G_WRONG_MECH is not returned immediately because it's more important
2N/A * to return G_BAD_TOK_HEADER if the token header is in fact bad
2N/A */
2N/A if (toksize < 2)
2N/A return (G_BAD_TOK_HEADER);
2N/A else
2N/A toksize -= 2;
2N/A
2N/A if (!ret) {
2N/A *buf_in = buf;
2N/A *body_size = toksize;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Return non-zero if the oid is one of the kerberos mech oids,
2N/A * otherwise return zero.
2N/A *
2N/A * N.B. There are 3 oids that represent the kerberos mech:
2N/A * RFC-specified GSS_MECH_KRB5_OID,
2N/A * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
2N/A * Incorrect MS GSS_MECH_KRB5_WRONG_OID
2N/A */
2N/A
2N/Astatic int
2N/Ais_kerb_mech(gss_OID oid)
2N/A{
2N/A int answer = 0;
2N/A OM_uint32 minor;
2N/A extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
2N/A
2N/A (void) gss_test_oid_set_member(&minor,
2N/A oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
2N/A
2N/A return (answer);
2N/A}