spnego_mech.c revision 354d1447ce995f3923a8f53d41c49fd3e6543282
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* A module that implements the spnego security mechanism.
* It is used to negotiate the security mechanism between
* peers using the GSS-API.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include "gssapiP_spnego.h"
#include <mechglueP.h>
#include <gssapi_err_generic.h>
#include <libintl.h>
/* der routines defined in libgss */
extern unsigned int der_length_size(OM_uint32);
/* private routines for spnego_mechanism */
static spnego_token_t make_spnego_token(char *);
static gss_buffer_desc make_err_msg(char *);
static int g_verify_neg_token_init(uchar_t **, int);
static OM_uint32 get_negResult(unsigned char **, int);
static gss_buffer_t get_input_token(unsigned char **, int);
static void release_spnego_ctx(spnego_gss_ctx_id_t *);
static void check_spnego_options(spnego_gss_ctx_id_t);
static spnego_gss_ctx_id_t create_spnego_ctx(void);
static gss_OID
static int
g_get_tag_and_length(unsigned char **, uchar_t, int, int *);
static int
static int
gss_buffer_t, send_token_flag, int,
/*
* The Mech OID for SPNEGO:
* { iso(1) org(3) dod(6) internet(1) security(5)
* mechanism(5) spnego(2) }
*/
static struct gss_config spnego_mechanism =
NULL,
/* EXPORT DELETE START */ /* CRYPT DELETE START */
spnego_gss_unseal, /* gss_unseal */
/* EXPORT DELETE END */ /* CRYPT DELETE END */
NULL, /* gss_process_context_token */
spnego_gss_delete_sec_context, /* gss_delete_sec_context */
spnego_gss_context_time, /* gss_context_time */
NULL, /* gss_indicate_mechs */
NULL, /* gss_compare_name */
spnego_gss_inquire_cred, /* gss_inquire_cred */
NULL, /* gss_add_cred */
/* EXPORT DELETE START */ /* CRYPT DELETE START */
spnego_gss_seal, /* gss_seal */
/* EXPORT DELETE END */ /* CRYPT DELETE END */
spnego_gss_export_sec_context, /* gss_export_sec_context */
spnego_gss_import_sec_context, /* gss_import_sec_context */
NULL, /* gss_inquire_cred_by_mech */
spnego_gss_inquire_context, /* gss_inquire_context */
NULL, /* gss_internal_release_oid */
spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
NULL, /* gss_pname_to_uid */
NULL, /* __gss_userok */
NULL, /* gss_export_name */
/* EXPORT DELETE START */
/* CRYPT DELETE START */
#if 0
/* CRYPT DELETE END */
/* CRYPT DELETE START */
#endif
/* CRYPT DELETE END */
/* EXPORT DELETE END */
spnego_gss_sign, /* gss_sign */
spnego_gss_verify, /* gss_verify */
NULL, /* gss_store_cred */
};
{
dsyslog("Entering gss_mech_initialize\n");
dsyslog("invalid spnego mechanism oid.\n");
return (NULL);
}
dsyslog("Leaving gss_mech_initialize\n");
return (&spnego_mechanism);
}
/*ARGSUSED*/
spnego_gss_acquire_cred(void *ctx,
{
dsyslog("Entering spnego_gss_acquire_cred\n");
if (actual_mechs)
*actual_mechs = NULL;
if (time_rec)
*time_rec = 0;
/*
* If the user did not specify a list of mechs,
* use get_available_mechs to collect a list of
* mechs for which creds are available.
*/
if (desired_mechs == GSS_C_NULL_OID_SET) {
} else {
/*
* The caller gave a specific list of mechanisms,
* so just get whatever creds are available.
* gss_acquire_creds will return the subset of mechs for
* which the given 'output_cred_handle' is valid.
*/
time_rec);
}
}
dsyslog("Leaving spnego_gss_acquire_cred\n");
return (status);
}
/*ARGSUSED*/
spnego_gss_release_cred(void *ctx,
{
dsyslog("Entering spnego_gss_release_cred\n");
return (GSS_S_CALL_INACCESSIBLE_WRITE);
*minor_status = 0;
if (*cred_handle == GSS_C_NO_CREDENTIAL)
return (GSS_S_COMPLETE);
dsyslog("Leaving spnego_gss_release_cred\n");
return (status);
}
static void
{
(const gss_OID)&spnego_oids[0]);
} else {
spnego_ctx->MS_Interop = 0;
}
}
static spnego_gss_ctx_id_t
create_spnego_ctx(void)
{
malloc(sizeof (spnego_gss_ctx_id_rec));
if (spnego_ctx == NULL) {
return (NULL);
}
spnego_ctx->optimistic = 0;
spnego_ctx->MS_Interop = 0;
return (spnego_ctx);
}
/*ARGSUSED*/
spnego_gss_init_sec_context(void *ct,
{
OM_uint32 local_ret_flags = 0;
/*
* send_token is used to indicate in later steps
* what type of token, if any should be sent or processed.
* NO_TOKEN_SEND = no token should be sent
* INIT_TOKEN_SEND = initial token will be sent
* CONT_TOKEN_SEND = continuing tokens to be sent
* CHECK_MIC = no token to be sent, but have a MIC to check.
*/
unsigned char *ptr;
int len;
dsyslog("Entering init_sec_context\n");
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
*minor_status = 0;
output_token->length = 0;
if (actual_mech)
*actual_mech = NULL;
if (*context_handle == GSS_C_NO_CONTEXT) {
/* determine negotiation mech set */
if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
credlistptr = &credlist;
credlistptr, &mechSet);
} else {
/*
* Use the list of mechs included in the
* cred that we were given.
*/
&mechSet);
}
if (mstat != GSS_S_COMPLETE)
return (mstat);
ret = GSS_S_FAILURE;
goto cleanup;
}
/*
* need to pull the first mech from mechSet to do first
* init ctx
*/
if (status != GSS_S_COMPLETE) {
ret = GSS_S_FAILURE;
goto cleanup;
}
goto cleanup;
}
/*
* The actual context is not yet determined,
* set the output context_handle to refer to
* the spnego context itself.
*/
} else {
goto cleanup;
}
case ACCEPT_DEFECTIVE_TOKEN:
*minor_status = 1;
break;
case ACCEPT_INCOMPLETE: {
/* pull out mech from token */
/*
* check if first mech in neg set, if it isn't,
* release and copy chosen mech to context,
* delete internal context from prior mech
*/
if (internal_mech != NULL &&
((internal_mech->length !=
/* CSTYLED */
(void) gss_delete_sec_context(&mstat,
(void) generic_gss_release_oid(
if (status != GSS_S_COMPLETE)
else
(void) generic_gss_release_oid(&mstat,
} else if (internal_mech == NULL) {
} else {
}
if (ret == GSS_S_COMPLETE) {
/*
* Check for a token, it may contain
* an error message.
*/
if (g_get_tag_and_length(&ptr,
(CONTEXT | 0x02),
&len) < 0) {
} else {
len);
if (i_input_token != NULL) {
} else {
}
}
}
}
break;
}
case ACCEPT_COMPLETE:
/* pull out mech from token */
(void) generic_gss_release_oid(&mstat,
/* CSTYLED */
ret = GSS_S_FAILURE;
}
&len) < 0) {
} else {
if (i_input_token != NULL) {
} else {
}
}
} else if (ret == GSS_S_CONTINUE_NEEDED ||
ret == GSS_S_COMPLETE) {
}
/*
* If we sent "optimistic" initial token,
* but the acceptor did not send a response token,
* this is an error.
*/
if (ret == GSS_S_COMPLETE &&
i_input_token == GSS_C_NO_BUFFER &&
spnego_ctx->optimistic) {
/* CSTYLED */
}
if (send_token != NO_TOKEN_SEND) {
if (i_input_token == NULL)
else
}
break;
case REJECT:
break;
default:
ret = GSS_S_FAILURE;
break;
}
}
if (send_token == NO_TOKEN_SEND) {
output_token->length = 0;
goto cleanup;
}
if (i_output_token == NULL) {
goto cleanup;
}
i_output_token->length = 0;
if (ret == GSS_S_CONTINUE_NEEDED) {
NULL,
time_rec);
if (ret_flags)
if (i_input_token != GSS_C_NO_BUFFER) {
}
if ((status != GSS_S_COMPLETE) &&
(status != GSS_S_CONTINUE_NEEDED)) {
}
if ((i_output_token->length == 0) &&
(status == GSS_S_COMPLETE) &&
(local_ret_flags & GSS_C_INTEG_FLAG)) {
if (g_get_tag_and_length(&ptr,
(CONTEXT | 0x03),
&len) < 0) {
} else {
if (mechListMIC == NULL)
else if (!spnego_ctx->MS_Interop &&
}
}
} else if (!spnego_ctx->MS_Interop) {
/*
* If no MIC was sent and we are in
* "standard" mode (i.e. NOT MS_Interop),
* the MIC must be present.
*/
} else {
/* In "MS_Interop" mode, MIC is ignored. */
}
}
}
if ((status == GSS_S_COMPLETE) &&
(ret == GSS_S_COMPLETE)) {
if (actual_mech) {
if (ret != GSS_S_COMPLETE)
goto cleanup;
}
} else if (ret == GSS_S_CONTINUE_NEEDED) {
output_token) < 0) {
}
}
if (status != GSS_S_COMPLETE)
if (ret != GSS_S_COMPLETE &&
ret != GSS_S_CONTINUE_NEEDED) {
if (spnego_ctx != NULL &&
if (spnego_ctx != NULL)
if (output_token)
}
if (i_output_token != GSS_C_NO_BUFFER) {
}
if (mechListMIC != GSS_C_NO_BUFFER) {
}
if (credlistptr != NULL)
return (ret);
} /* init_sec_context */
/*ARGSUSED*/
spnego_gss_accept_sec_context(void *ct,
{
unsigned char *ptr;
unsigned char *bufstart;
int bodysize;
OM_uint32 local_ret_flags = 0;
dsyslog("Entering accept_sec_context\n");
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
if (src_name)
output_token->length = 0;
*minor_status = 0;
if (mech_type)
/* return a bogus cred handle */
if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
}
/* Check for defective input token. */
&ptr, 0,
input_token->length)) {
*minor_status = err;
goto senderror;
}
/*
* set up of context, determine mech to be used, save mechset
* for use later in integrety check.
*/
if (*context_handle == GSS_C_NO_CONTEXT) {
return (GSS_S_FAILURE);
/*
* Until the accept operation is complete, the
* context_handle returned should refer to
* the spnego context.
*/
if (minor_stat != GSS_S_COMPLETE) {
return (minor_stat);
}
if (Need_Cred) {
NULL);
if (minor_stat != GSS_S_COMPLETE) {
(void) gss_release_oid_set(minor_status,
return (minor_stat);
} else {
}
}
*minor_status = err;
goto senderror;
}
/*
* Allocate space to hold the mechTypes
* because we need it later.
*/
ret = GSS_S_FAILURE;
goto cleanup;
}
/*
* Get pointers to the DER encoded MechSet so we
* can properly check and calculate a MIC later.
*/
goto senderror;
}
/*
* Select the best match between the list of mechs
* that the initiator requested and the list that
* the acceptor will support.
*/
&firstMech);
}
if (negResult == ACCEPT_COMPLETE) {
&len) < 0) {
} else {
if (i_input_token == NULL) {
}
}
}
} else {
}
/*
* Check to see if there is a MechListMIC field
*/
mechsetlen, &len) >= 0) {
if (mechListMIC == GSS_C_NO_BUFFER) {
}
}
}
} else {
/*
* get internal input token and context for continued
* calls of spnego_gss_init_sec_context.
*/
if (i_input_token == NULL) {
} else {
}
}
/*
* If we still don't have a cred, we have an error.
*/
if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
ret = GSS_S_FAILURE;
goto cleanup;
}
/* If we have an error already, bail out */
goto senderror;
if (i_input_token != GSS_C_NO_BUFFER) {
if (i_output_token == NULL) {
ret = GSS_S_FAILURE;
goto cleanup;
}
i_output_token->length = 0;
if ((status != GSS_S_COMPLETE) &&
(status != GSS_S_CONTINUE_NEEDED)) {
if (i_input_token != GSS_C_NO_BUFFER) {
}
/*
* Reject the request with an error token.
*/
goto senderror;
}
if (ret_flags)
if (i_input_token != GSS_C_NO_BUFFER) {
}
/* If we got a MIC, verify it if possible */
if ((status == GSS_S_COMPLETE) &&
(local_ret_flags & GSS_C_INTEG_FLAG) &&
mechListMIC != GSS_C_NO_BUFFER &&
!spnego_ctx->MS_Interop) {
&qop_state);
if (ret != GSS_S_COMPLETE) {
goto senderror;
}
}
/*
* If the MIC was verified OK, create a new MIC
* for the response message.
*/
if (status == GSS_S_COMPLETE &&
(local_ret_flags & GSS_C_INTEG_FLAG) &&
!spnego_ctx->MS_Interop) {
malloc(sizeof (gss_buffer_desc));
if (mechListMIC == NULL ||
ret = GSS_S_FAILURE;
goto cleanup;
}
if (ret != GSS_S_COMPLETE) {
goto senderror;
}
}
if (status == GSS_S_COMPLETE) {
}
if (status == GSS_S_CONTINUE_NEEDED) {
if (return_token == INIT_TOKEN_SEND)
}
}
if ((return_token == INIT_TOKEN_SEND) ||
(return_token == CONT_TOKEN_SEND) ||
(return_token == ERROR_TOKEN_SEND)) {
int MS_Interop = 0;
if (spnego_ctx)
/*
* create response for the initiator.
*/
/*
* If we could not make the response token,
* we will have to fail without sending a response.
*/
if (err) {
}
} else {
}
if (ret != GSS_S_COMPLETE &&
ret != GSS_S_CONTINUE_NEEDED) {
if (spnego_ctx != NULL) {
(void) gss_delete_sec_context(&mstat,
}
}
if (mech_wanted != NULL) {
}
return (ret);
}
/*ARGSUSED*/
spnego_gss_display_status(void *ctx,
int status_type,
{
dsyslog("Entering display_status\n");
*message_context = 0;
switch (status_value) {
/* CSTYLED */
break;
/* CSTYLED */
break;
/* CSTYLED */
break;
/* CSTYLED */
break;
/* CSTYLED */
break;
default:
status_string->length = 0;
break;
}
dsyslog("Leaving display_status\n");
return (GSS_S_COMPLETE);
}
/*ARGSUSED*/
spnego_gss_import_name(void *ctx,
{
dsyslog("Entering import_name\n");
dsyslog("Leaving import_name\n");
return (status);
}
/*ARGSUSED*/
spnego_gss_release_name(void *ctx,
{
dsyslog("Entering release_name\n");
dsyslog("Leaving release_name\n");
return (status);
}
/*ARGSUSED*/
spnego_gss_display_name(void *ctx,
{
dsyslog("Entering display_name\n");
dsyslog("Leaving display_name\n");
return (status);
}
/*ARGSUSED*/
spnego_gss_inquire_cred(void *ctx,
const gss_cred_id_t cred_handle,
{
/* A SPNEGO cred is just a standard libgss cred record */
return (gss_inquire_cred(minor_status,
mechanisms));
}
/*ARGSUSED*/
{
dsyslog("Entering inquire_names_for_mech\n");
/*
* We only know how to handle our own mechanism.
*/
if ((mechanism != GSS_C_NULL_OID) &&
*minor_status = 0;
return (GSS_S_FAILURE);
}
if (major == GSS_S_COMPLETE) {
/* Now add our members. */
name_types)) == GSS_S_COMPLETE) &&
name_types)) == GSS_S_COMPLETE) &&
name_types)) == GSS_S_COMPLETE)) {
}
if (major != GSS_S_COMPLETE)
}
dsyslog("Leaving inquire_names_for_mech\n");
return (major);
}
spnego_gss_unseal(void *context,
int *conf_state,
int *qop_state)
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
return (ret);
}
spnego_gss_seal(void *context,
int conf_req_flag,
int qop_req,
int *conf_state,
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
return (ret);
}
const gss_ctx_id_t context_handle,
const gss_buffer_t token_buffer)
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
return (ret);
}
{
return (GSS_S_NO_CONTEXT);
/*
* If this is still a SPNEGO mech, release it locally.
*/
(void) release_spnego_ctx(ctx);
} else {
&(*ctx)->ctx_handle,
}
return (ret);
}
spnego_gss_context_time(void *context,
const gss_ctx_id_t context_handle,
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
time_rec);
return (ret);
}
{
return (GSS_S_NO_CONTEXT);
&(*ctx)->ctx_handle,
return (ret);
}
const gss_buffer_t interprocess_token,
{
return (GSS_S_NO_CONTEXT);
&(*ctx)->ctx_handle);
return (ret);
}
const gss_ctx_id_t context_handle,
int *locally_initiated,
int *open)
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
open);
return (ret);
}
const gss_ctx_id_t context_handle,
int conf_req_flag,
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
return (ret);
}
spnego_gss_sign(void *context,
const gss_ctx_id_t context_handle,
int qop_req,
const gss_buffer_t message_buffer,
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
return (ret);
}
spnego_gss_verify(void *context,
const gss_ctx_id_t context_handle,
const gss_buffer_t msg_buffer,
const gss_buffer_t token_buffer,
int *qop_state)
{
if (context_handle == NULL)
return (GSS_S_NO_CONTEXT);
return (ret);
}
/*
* We will release everything but the ctx_handle so that it
* not be called until after the ctx_handle memory is assigned to
*/
static void
{
else
return;
(void) gss_release_buffer(&minor_stat,
&context->DER_mechTypes);
(void) generic_gss_release_oid(&minor_stat,
&context->internal_mech);
}
}
}
/*
* Can't use gss_indicate_mechs by itself to get available mechs for
* SPNEGO because it will also return the SPNEGO mech and we do not
* want to consider SPNEGO as an available security mech for
* negotiation. For this reason, get_available_mechs will return
* all available mechs except SPNEGO.
*
* If a ptr to a creds list is given, this function will attempt
* to acquire creds for the creds given and trim the list of
* returned mechanisms to only those for which creds are valid.
*
*/
static OM_uint32
{
int i;
int found = 0;
if (stat != GSS_S_COMPLETE) {
return (stat);
}
if (stat != GSS_S_COMPLETE) {
return (stat);
}
rmechs);
if (stat == GSS_S_COMPLETE)
found++;
}
}
/*
* If the caller wanted a list of creds returned,
* trim the list of mechanisms down to only those
* for which the creds are valid.
*/
/*
* Drop the old list in favor of the new
* "trimmed" list.
*/
if (stat == GSS_S_COMPLETE) {
(void) gss_copy_oid_set(minor_status,
}
}
if (stat == GSS_S_COMPLETE)
}
return (stat);
}
/* following are token creation and reading routines */
/*
* If buff_in is not pointing to a MECH_OID, then return NULL and do not
* advance the buffer, otherwise, decode the mech_oid from the buffer and
* place in gss_OID.
*/
static gss_OID
{
return (NULL);
(*buff_in)++;
return (NULL);
if (status != GSS_S_COMPLETE)
return (mech_out);
}
/*
* der encode the given mechanism oid into buf_out, advancing the
* buffer pointer.
*/
static int
{
return (-1);
return (0);
}
/*
* verify that buff_in points to an octet string, if it does not,
* return NULL and don't advance the pointer. If it is an octet string
* decode buff_in into a gss_buffer_t and return it, advancing the
* buffer pointer.
*/
static gss_buffer_t
{
unsigned int bytes;
if (**buff_in != OCTET_STRING)
return (NULL);
(*buff_in)++;
if (input_token == NULL)
return (NULL);
return (NULL);
}
return (NULL);
}
return (input_token);
}
/*
* verify that the input token length is not 0. If it is, just return.
* If the token length is greater than 0, der encode as an octet string
* and place in buf_out, advancing buf_out.
*/
static int
int buflen)
{
int ret;
/* if token length is 0, we do not want to send */
if (input_token->length == 0)
return (0);
return (-1);
*(*buf_out)++ = OCTET_STRING;
input_token->length)))
return (ret);
return (0);
}
/*
* verify that buff_in points to a sequence of der encoding. The mech
* set is the only sequence of encoded object in the token, so if it is
* a sequence of encoding, decode the mechset into a gss_OID_set and
* return it, advancing the buffer pointer.
*/
static gss_OID_set
{
int i;
if (**buff_in != SEQUENCE_OF)
return (NULL);
(*buff_in)++;
if (major_status != GSS_S_COMPLETE)
return (NULL);
temp, &returned_mechSet);
if (major_status == GSS_S_COMPLETE) {
}
}
}
return (returned_mechSet);
}
/*
* der encode the passed mechSet and place it into buf_out,
* advancing the buffer pointer.
*/
static int
{
int i, ret;
return (-1);
*(*buf_out)++ = SEQUENCE_OF;
/*
* Mech OID ASN.1 size = 2 + length.
* 1 = 0x06, 1 for length of OID
* typically, less than 128, so only 1 byte needed.
*/
}
return (-1);
return (-1);
return (ret);
}
return (0);
}
/*
* Verify that buff_in is pointing to a BIT_STRING with the correct
* length and padding for the req_flags. If it is, decode req_flags
* and return them, otherwise, return NULL.
*/
static OM_uint32
{
int len;
/* It is OK if no ReqFlags data is sent. */
return (0);
/* If they are sent, make sure the fields are correct. */
return (ACCEPT_DEFECTIVE_TOKEN);
/* We don't care what the flags are. */
/* Don't return any flags, this field is useless */
*req_flags = 0;
return (0);
}
/*
* get the negotiation results, decoding the ENUMERATED type result
* from the buffer, advancing the buffer pointer.
*/
static OM_uint32
{
int len;
unsigned int bytes;
/*
* Verify that the data is ASN.1 encoded correctly
*/
return (ACCEPT_DEFECTIVE_TOKEN);
&bytes)) < 0)
return (ACCEPT_DEFECTIVE_TOKEN);
} else {
return (ACCEPT_INCOMPLETE);
}
/*
* if we find an octet string, we need to return
* incomplete so that we process the token correctly.
* Anything else unexpected, we reject.
*/
return (ACCEPT_DEFECTIVE_TOKEN);
} else {
return (ACCEPT_INCOMPLETE);
}
if (*(*buff_in) == OCTET_STRING)
return (ACCEPT_INCOMPLETE);
if (*(*buff_in)++ != ENUMERATED)
return (ACCEPT_DEFECTIVE_TOKEN);
if (*(*buff_in)++ != ENUMERATION_LENGTH)
return (ACCEPT_DEFECTIVE_TOKEN);
/*
* Save the result byte to return later.
* This is the result
*/
&len) < 0)
return (result);
}
/*
* der encode the passed negResults as an ENUMERATED type and
* place it in buf_out, advancing the buffer.
*/
static int
{
if (buflen < 3)
return (-1);
*(*buf_out)++ = ENUMERATED;
*(*buf_out)++ = ENUMERATION_LENGTH;
return (0);
}
/*
* This routine compares the recieved mechset to the mechset that
* this server can support. It looks sequentially through the mechset
* and the first one that matches what the server can support is
* chosen as the negotiated mechanism. If one is found, negResult
* is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
* is set to REJECT. Also, for purposes of determining latter behavior,
* the flag, firstMech is used to indicate if the chosen mechanism is the
* first of the mechset or not.
*/
static gss_OID
{
int present;
int i;
if (i == 0)
else
if (status != GSS_S_COMPLETE) {
return (NULL);
}
return (returned_mech);
}
}
return (NULL);
}
/*
* the next two routines make a token buffer suitable for
* spnego_gss_display_status. These currently take the string
* in name and place it in the token. Eventually, if
* spnego_gss_display_status returns valid error messages,
* these routines will be changes to return the error string.
*/
static spnego_token_t
make_spnego_token(char *name)
{
return (NULL);
return (token);
}
static gss_buffer_desc
make_err_msg(char *name)
{
} else {
}
return (buffer);
}
/*
* Create the client side spnego token passed back to gss_init_sec_context
* and eventually up to the application program and over to the server.
*
* Use DER rules, definite length method per RFC 2478
*/
static int
{
int MechSetLen = 0;
int negTokenInitSize = 0;
int i;
unsigned char *t;
unsigned char *ptr;
unsigned char *MechListPtr = NULL;
if (outbuf == GSS_C_NO_BUFFER)
return (-1);
/* calculate the data length */
/* no token generated if sendtoken is not init or cont */
if ((sendtoken < INIT_TOKEN_SEND) ||
(sendtoken > CONT_TOKEN_SEND)) {
return (-1);
}
/*
* if this is the init token, we will send the mechset
* so include it's length.
*/
if (sendtoken == INIT_TOKEN_SEND) {
/*
* Count bytes for the mechSet data
* Encoded in final output as:
* 0xa0 [DER LEN] 0x30 [DER LEN] [DATA]
*/
MechSetLen += 1 +
if (MechListPtr != NULL) {
goto errout;
}
} else {
ret = -1;
goto errout;
}
/*
* The MIC is done over the DER encoded mechSet.
*/
/*
* Only send the MIC if we are *NOT* interoperating
* with Microsoft.
*/
if (!spnego_ctx->MS_Interop) {
/*
* MechListMIC = DER(MIC(DER(MechSet)))
* Calculate it here, stick it in the buffer later.
*/
&MICbuff);
/*
* If the MIC operation succeeded, use it,
* but don't fail if it did not succeed.
* MIC is optional and is not supported by all
* mechanisms all the time.
*/
if (status == GSS_S_COMPLETE) {
/*
* Encoded in final output as:
* 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
* --s-- -------tlen------------
*/
}
}
}
/*
* If a token from gss_init_sec_context exists,
* add the length of the token + the ASN.1 overhead
*/
/*
* Encoded in final output as:
* 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
* -----s--------|--------s2----------
*/
}
/*
* Add size of DER encoding
* [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
* 0x30 [DER_LEN] [data]
*
*/
/*
* negTokenInitSize indicates the bytes needed to
* hold the ASN.1 encoding of the entire NegTokenInit
* SEQUENCE.
* 0xa0 [DER_LEN] + data
*
*/
negTokenInitSize + 1 +
if (t == NULL) {
return (-1);
}
ptr = t;
/* create the message */
1 + negTokenInitSize +
goto errout;
if (sendtoken == INIT_TOKEN_SEND) {
goto errout;
goto errout;
goto errout;
/* We already encoded the MechSetList */
}
goto errout;
goto errout;
/*
* We are in "optimistic" mode if we send a token
* with out initial message.
*/
}
/* We already calculated the MechListMIC above */
goto errout;
goto errout;
}
if (ret != 0) {
if (t)
free(t);
t = NULL;
tlen = 0;
}
return (ret);
}
/*
* create the server side spnego token passed back to
* gss_accept_sec_context and eventually up to the application program
* and over to the client.
*/
static int
{
int tlen;
int ret;
int NegTokenTargSize;
int negresultTokenSize;
int NegTokenSize;
int rspTokenSize;
int micTokenSize;
int dataLen = 0;
unsigned char *t;
unsigned char *ptr;
if (outbuf == GSS_C_NO_BUFFER)
return (GSS_S_DEFECTIVE_TOKEN);
/*
* ASN.1 encoding of the negResult
* ENUMERATED type is 3 bytes
* ENUMERATED TAG, Length, Value,
* Plus 2 bytes for the CONTEXT id and length.
*/
negresultTokenSize = 5;
/*
* calculate data length
*
* If this is the initial token, include length of
* mech_type and the negotiation result fields.
*/
if (sendtoken == INIT_TOKEN_SEND) {
if (mech_wanted != NULL) {
int mechlistTokenSize;
/*
* 1 byte for the CONTEXT ID(0xa0),
* 1 byte for the OID ID(0x06)
* 1 byte for OID Length field
* Plus the rest... (OID Length, OID value)
*/
}
} else {
/*
* If this is a response from a server, count
* the space needed for the negResult field.
* LENGTH(2) + ENUM(2) + result
*/
}
/* Length of the inner token */
dataLen += rspTokenSize;
/* Length of the outer token */
}
if (mechListMIC != NULL) {
/* Length of the inner token */
dataLen += micTokenSize;
/* Length of the outer token */
dataLen += rspTokenSize;
}
/*
* Add size of DER encoded:
* NegTokenTarg [ SEQUENCE ] of
* NegResult[0] ENUMERATED {
* accept_completed(0),
* accept_incomplete(1),
* reject(2) }
* supportedMech [1] MechType OPTIONAL,
* responseToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
*
* size = data->length + MechListMic + SupportedMech len +
* Result Length + ASN.1 overhead
*/
/*
* NegotiationToken [ CHOICE ]{
* negTokenInit [0] NegTokenInit,
* negTokenTarg [1] NegTokenTarg }
*/
if (t == NULL) {
goto errout;
}
ptr = t;
if (sendtoken == INIT_TOKEN_SEND ||
sendtoken == ERROR_TOKEN_SEND) {
/*
* Indicate that we are sending CHOICE 1
* (NegTokenTarg)
*/
goto errout;
}
goto errout;
}
/*
* First field of the NegTokenTarg SEQUENCE
* is the ENUMERATED NegResult.
*/
goto errout;
}
goto errout;
}
/*
* Next, is the Supported MechType
*/
goto errout;
}
goto errout;
}
}
}
goto errout;
}
goto errout;
}
}
if (mechListMIC != NULL) {
goto errout;
}
goto errout;
}
goto errout;
}
}
}
if (ret != 0) {
if (t)
free(t);
} else {
}
return (ret);
}
/* determine size of token */
static int
{
int hdrsize;
/*
* Initialize the header size to the
* MECH_OID byte + the bytes needed to indicate the
* length of the OID + the OID itself.
*
* 0x06 [MECHLENFIELD] MECHDATA
*/
/*
* Now add the bytes needed for the initial header
* token bytes:
* 0x60 + [DER_LEN] + HDRSIZE
*/
}
/*
* generate token header.
*
* Use DER Definite Length method per RFC2478
* Use of indefinite length encoding will not be compatible
* with Microsoft or others that actually follow the spec.
*/
static int
int body_size,
unsigned char **buf,
int totallen)
{
unsigned char *p = *buf;
return (ret);
return (ret);
return (0);
}
static int
{
ptr++;
if (*outlen < 0)
ret = -1;
else
ret = 0;
}
return (ret);
}
static int
{
int seqsize;
int ret = 0;
unsigned int bytes;
/*
* Verify this is a NegotiationToken type token
* - check for a0(context specific identifier)
* - get length and verify that enoughd ata exists
*/
return (G_BAD_TOK_HEADER);
/*
* Verify the next piece, it should identify this as
* a strucure of type NegTokenInit.
*/
return (G_BAD_TOK_HEADER);
/*
* Make sure we have the entire buffer as described
*/
return (G_BAD_TOK_HEADER);
} else {
return (G_BAD_TOK_HEADER);
}
/*
* Verify that the first blob is a sequence of mechTypes
*/
return (G_BAD_TOK_HEADER);
/*
* Make sure we have the entire buffer as described
*/
return (G_BAD_TOK_HEADER);
} else {
return (G_BAD_TOK_HEADER);
}
/*
* At this point, *buf should be at the beginning of the
* DER encoded list of mech types that are to be negotiated.
*/
return (ret);
}
/* verify token header. */
static int
int *body_size,
unsigned char **buf_in,
int tok_type,
int toksize)
{
int seqsize;
int ret = 0;
unsigned int bytes;
if ((toksize -= 1) < 0)
return (G_BAD_TOK_HEADER);
return (G_BAD_TOK_HEADER);
return (G_BAD_TOK_HEADER);
return (G_BAD_TOK_HEADER);
if ((toksize -= 1) < 0)
return (G_BAD_TOK_HEADER);
return (G_BAD_TOK_HEADER);
if ((toksize -= 1) < 0)
return (G_BAD_TOK_HEADER);
return (G_BAD_TOK_HEADER);
ret = G_WRONG_MECH;
/*
* G_WRONG_MECH is not returned immediately because it's more important
* to return G_BAD_TOK_HEADER if the token header is in fact bad
*/
if ((toksize -= 2) < 0)
return (G_BAD_TOK_HEADER);
if (!ret) {
}
return (ret);
}