#include <sys/param.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <k5-int.h>
#include <krb5.h>
#include <mglueP.h>
#include "gssapiP_spnego.h"
#include "gssapiP_generic.h"
#include <gssapi_err_generic.h>
#include <locale.h>
#define gssint_get_modOptions __gss_get_modOptions
#define gssint_der_length_size der_length_size
#define gssint_get_der_length get_der_length
#define gssint_put_der_length put_der_length
#define gssint_get_mechanism __gss_get_mechanism
#define gssint_copy_oid_set gss_copy_oid_set
#define gssint_get_mech_type __gss_get_mech_type
#undef g_token_size
#undef g_verify_token_header
#undef g_make_token_header
#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
typedef const gss_OID_desc *gss_OID_const;
extern unsigned int gssint_der_length_size(OM_uint32);
extern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*);
extern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32);
static spnego_token_t make_spnego_token(char *);
static gss_buffer_desc make_err_msg(char *);
static int g_token_size(gss_OID_const, unsigned int);
static int g_make_token_header(gss_OID_const, unsigned int,
unsigned char **, unsigned int);
static int g_verify_token_header(gss_OID_const, unsigned int *,
unsigned char **,
int, unsigned int);
static int g_verify_neg_token_init(unsigned char **, unsigned int);
static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
static gss_buffer_t get_input_token(unsigned char **, unsigned int);
static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
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 int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
static int put_negResult(unsigned char **, OM_uint32, unsigned int);
static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
gss_buffer_t *, OM_uint32 *, send_token_flag *);
static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
gss_buffer_t *, OM_uint32 *, send_token_flag *);
static OM_uint32
init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
gss_OID_set *, send_token_flag *);
static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
gss_buffer_t *, gss_buffer_t *,
OM_uint32 *, send_token_flag *);
static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
gss_buffer_t *, gss_buffer_t *,
OM_uint32 *, send_token_flag *);
static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
gss_OID, gss_buffer_t *, gss_buffer_t *,
OM_uint32 *, send_token_flag *);
static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
OM_uint32 *, send_token_flag *);
static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
gss_cred_id_t, gss_buffer_t *,
gss_buffer_t *, OM_uint32 *, send_token_flag *);
static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
gss_buffer_t *, gss_buffer_t *,
OM_uint32 *, send_token_flag *);
static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
OM_uint32 *, send_token_flag *);
static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
gss_buffer_t, gss_OID *, gss_buffer_t,
OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
OM_uint32 *, send_token_flag *);
static gss_OID
negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
OM_uint32 *);
static int
g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
int,
gss_buffer_t,
OM_uint32, gss_buffer_t, send_token_flag,
gss_buffer_t);
static int
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
gss_buffer_t, send_token_flag,
gss_buffer_t);
static OM_uint32
get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
gss_OID_set *, OM_uint32 *, gss_buffer_t *,
gss_buffer_t *);
static OM_uint32
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
static int
is_kerb_mech(gss_OID oid);
static const gss_OID_desc spnego_oids[] = {
{SPNEGO_OID_LENGTH, SPNEGO_OID},
};
const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
static const gss_OID_set_desc spnego_oidsets[] = {
{1, (gss_OID) spnego_oids+0},
};
const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
static int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *);
static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
static OM_uint32
acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
gss_buffer_t *, OM_uint32 *, send_token_flag *);
#ifdef _GSS_STATIC_LINK
int gss_spnegoint_lib_init(void);
void gss_spnegoint_lib_fini(void);
#else
gss_mechanism gss_mech_initialize(void);
#endif
static struct gss_config spnego_mechanism =
{
{SPNEGO_OID_LENGTH, SPNEGO_OID},
NULL,
glue_spnego_gss_acquire_cred,
glue_spnego_gss_release_cred,
glue_spnego_gss_init_sec_context,
#ifndef LEAN_CLIENT
glue_spnego_gss_accept_sec_context,
#else
NULL,
#endif
NULL,
NULL,
glue_spnego_gss_delete_sec_context,
glue_spnego_gss_context_time,
glue_spnego_gss_display_status,
NULL,
glue_spnego_gss_compare_name,
glue_spnego_gss_display_name,
glue_spnego_gss_import_name,
glue_spnego_gss_release_name,
NULL,
NULL,
NULL,
#ifndef LEAN_CLIENT
glue_spnego_gss_export_sec_context,
glue_spnego_gss_import_sec_context,
#else
NULL,
NULL,
#endif
NULL,
glue_spnego_gss_inquire_names_for_mech,
glue_spnego_gss_inquire_context,
NULL,
glue_spnego_gss_wrap_size_limit,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
spnego_gss_inquire_sec_context_by_oid,
};
#ifdef _GSS_STATIC_LINK
#include "mglueP.h"
static
int gss_spnegomechglue_init(void)
{
struct gss_mech_config mech_spnego;
memset(&mech_spnego, 0, sizeof(mech_spnego));
mech_spnego.mech = &spnego_mechanism;
mech_spnego.mechNameStr = "spnego";
mech_spnego.mech_type = GSS_C_NO_OID;
return gssint_register_mechinfo(&mech_spnego);
}
#else
gss_mechanism KRB5_CALLCONV
gss_mech_initialize(void)
{
int err;
err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
spnego_gss_delete_error_info);
if (err) {
syslog(LOG_NOTICE,
"SPNEGO gss_mech_initialize: error message TSD key register fail");
return (NULL);
}
return (&spnego_mechanism);
}
#if 0
MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
int gss_krb5int_lib_init(void)
#endif
#endif
static
int gss_spnegoint_lib_init(void)
{
#ifdef _GSS_STATIC_LINK
return gss_spnegomechglue_init();
#else
int err;
err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
spnego_gss_delete_error_info);
if (err) {
syslog(LOG_NOTICE,
"SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
err);
return err;
}
return 0;
#endif
}
static void gss_spnegoint_lib_fini(void)
{
}
OM_uint32
glue_spnego_gss_acquire_cred(
void *context,
OM_uint32 *minor_status,
gss_name_t desired_name,
OM_uint32 time_req,
gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
return(spnego_gss_acquire_cred(minor_status,
desired_name,
time_req,
desired_mechs,
cred_usage,
output_cred_handle,
actual_mechs,
time_rec));
}
OM_uint32
spnego_gss_acquire_cred(OM_uint32 *minor_status,
gss_name_t desired_name,
OM_uint32 time_req,
gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
OM_uint32 status;
gss_OID_set amechs;
dsyslog("Entering spnego_gss_acquire_cred\n");
if (actual_mechs)
*actual_mechs = NULL;
if (time_rec)
*time_rec = 0;
if (desired_mechs == GSS_C_NULL_OID_SET) {
status = get_available_mechs(minor_status,
desired_name, cred_usage,
output_cred_handle, &amechs);
} else {
status = gss_acquire_cred(minor_status,
desired_name, time_req,
desired_mechs, cred_usage,
output_cred_handle, &amechs,
time_rec);
}
if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
(void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
}
(void) gss_release_oid_set(minor_status, &amechs);
dsyslog("Leaving spnego_gss_acquire_cred\n");
return (status);
}
OM_uint32
glue_spnego_gss_release_cred(void *context,
OM_uint32 *minor_status,
gss_cred_id_t *cred_handle)
{
return( spnego_gss_release_cred(minor_status, cred_handle));
}
OM_uint32
spnego_gss_release_cred(OM_uint32 *minor_status,
gss_cred_id_t *cred_handle)
{
OM_uint32 status;
dsyslog("Entering spnego_gss_release_cred\n");
if (minor_status == NULL || cred_handle == NULL)
return (GSS_S_CALL_INACCESSIBLE_WRITE);
*minor_status = 0;
if (*cred_handle == GSS_C_NO_CREDENTIAL)
return (GSS_S_COMPLETE);
status = gss_release_cred(minor_status, cred_handle);
dsyslog("Leaving spnego_gss_release_cred\n");
return (status);
}
static void
check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
{
spnego_ctx->optionStr = gssint_get_modOptions(
(const gss_OID)&spnego_oids[0]);
}
static spnego_gss_ctx_id_t
create_spnego_ctx(void)
{
spnego_gss_ctx_id_t spnego_ctx = NULL;
spnego_ctx = (spnego_gss_ctx_id_t)
malloc(sizeof (spnego_gss_ctx_id_rec));
if (spnego_ctx == NULL) {
return (NULL);
}
spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
spnego_ctx->internal_mech = NULL;
spnego_ctx->optionStr = NULL;
spnego_ctx->DER_mechTypes.length = 0;
spnego_ctx->DER_mechTypes.value = NULL;
spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
spnego_ctx->mic_reqd = 0;
spnego_ctx->mic_sent = 0;
spnego_ctx->mic_rcvd = 0;
spnego_ctx->mech_complete = 0;
spnego_ctx->nego_done = 0;
spnego_ctx->internal_name = GSS_C_NO_NAME;
spnego_ctx->actual_mech = GSS_C_NO_OID;
spnego_ctx->err.msg = NULL;
spnego_ctx->err.scratch_buf[0] = 0;
check_spnego_options(spnego_ctx);
return (spnego_ctx);
}
static OM_uint32
handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
int send_mechtok, spnego_gss_ctx_id_t sc,
gss_buffer_t *mic_out,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret;
ret = GSS_S_FAILURE;
*mic_out = GSS_C_NO_BUFFER;
if (mic_in != GSS_C_NO_BUFFER) {
if (sc->mic_rcvd) {
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
return GSS_S_DEFECTIVE_TOKEN;
}
} else if (sc->mic_reqd && !send_mechtok) {
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
return GSS_S_DEFECTIVE_TOKEN;
}
ret = process_mic(minor_status, mic_in, sc, mic_out,
negState, tokflag);
if (ret != GSS_S_COMPLETE) {
return ret;
}
if (sc->mic_reqd) {
assert(sc->mic_sent || sc->mic_rcvd);
}
if (sc->mic_sent && sc->mic_rcvd) {
ret = GSS_S_COMPLETE;
*negState = ACCEPT_COMPLETE;
if (*mic_out == GSS_C_NO_BUFFER) {
assert(!send_mechtok);
*tokflag = NO_TOKEN_SEND;
} else {
*tokflag = CONT_TOKEN_SEND;
}
} else if (sc->mic_reqd) {
*negState = ACCEPT_INCOMPLETE;
ret = GSS_S_CONTINUE_NEEDED;
} else if (*negState == ACCEPT_COMPLETE) {
ret = GSS_S_COMPLETE;
} else {
ret = GSS_S_CONTINUE_NEEDED;
}
return ret;
}
static OM_uint32
process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret, tmpmin;
gss_qop_t qop_state;
gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
ret = GSS_S_FAILURE;
if (mic_in != GSS_C_NO_BUFFER) {
ret = gss_verify_mic(minor_status, sc->ctx_handle,
&sc->DER_mechTypes,
mic_in, &qop_state);
if (ret != GSS_S_COMPLETE) {
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
return ret;
}
sc->mic_reqd = 1;
sc->mic_rcvd = 1;
}
if (sc->mic_reqd && !sc->mic_sent) {
ret = gss_get_mic(minor_status, sc->ctx_handle,
GSS_C_QOP_DEFAULT,
&sc->DER_mechTypes,
&tmpmic);
if (ret != GSS_S_COMPLETE) {
gss_release_buffer(&tmpmin, &tmpmic);
*tokflag = NO_TOKEN_SEND;
return ret;
}
*mic_out = malloc(sizeof(gss_buffer_desc));
if (*mic_out == GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, &tmpmic);
*tokflag = NO_TOKEN_SEND;
return GSS_S_FAILURE;
}
**mic_out = tmpmic;
sc->mic_sent = 1;
}
return GSS_S_COMPLETE;
}
static OM_uint32
init_ctx_new(OM_uint32 *minor_status,
gss_cred_id_t cred,
gss_ctx_id_t *ctx,
gss_OID_set *mechSet,
send_token_flag *tokflag)
{
OM_uint32 ret, tmpmin;
gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
spnego_gss_ctx_id_t sc = NULL;
if (cred == GSS_C_NO_CREDENTIAL) {
ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
GSS_C_INITIATE, &creds, mechSet);
gss_release_cred(&tmpmin, &creds);
} else {
ret = gss_inquire_cred(minor_status, cred,
NULL, NULL, NULL, mechSet);
}
if (ret != GSS_S_COMPLETE)
return ret;
sc = create_spnego_ctx();
if (sc == NULL)
return GSS_S_FAILURE;
ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
&sc->internal_mech);
if (ret != GSS_S_COMPLETE) {
map_errcode(minor_status);
goto cleanup;
}
if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
generic_gss_release_oid(&tmpmin, &sc->internal_mech);
ret = GSS_S_FAILURE;
goto cleanup;
}
sc->ctx_handle = GSS_C_NO_CONTEXT;
*ctx = (gss_ctx_id_t)sc;
*tokflag = INIT_TOKEN_SEND;
ret = GSS_S_CONTINUE_NEEDED;
cleanup:
gss_release_oid_set(&tmpmin, mechSet);
return ret;
}
static OM_uint32
init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret, tmpmin, acc_negState;
unsigned char *ptr;
spnego_gss_ctx_id_t sc;
gss_OID supportedMech = GSS_C_NO_OID;
sc = (spnego_gss_ctx_id_t)*ctx;
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
ptr = buf->value;
ret = get_negTokenResp(minor_status, ptr, buf->length,
&acc_negState, &supportedMech,
responseToken, mechListMIC);
if (ret != GSS_S_COMPLETE)
goto cleanup;
if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
supportedMech == GSS_C_NO_OID &&
*responseToken == GSS_C_NO_BUFFER &&
*mechListMIC == GSS_C_NO_BUFFER) {
ret = GSS_S_DEFECTIVE_TOKEN;
}
if (acc_negState == REJECT) {
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
spnego_set_error_message(sc, *minor_status,
dgettext(TEXT_DOMAIN,
"SPNEGO failed to negotiate a mechanism: server rejected request"));
map_errcode(minor_status);
*tokflag = NO_TOKEN_SEND;
ret = GSS_S_FAILURE;
goto cleanup;
}
if (!sc->nego_done) {
ret = init_ctx_nego(minor_status, sc,
acc_negState,
supportedMech, responseToken,
mechListMIC,
negState, tokflag);
} else if (!sc->mech_complete &&
*responseToken == GSS_C_NO_BUFFER) {
ret = GSS_S_DEFECTIVE_TOKEN;
} else if (sc->mic_reqd &&
(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
*negState = ACCEPT_INCOMPLETE;
*tokflag = CONT_TOKEN_SEND;
ret = GSS_S_CONTINUE_NEEDED;
} else {
*negState = ACCEPT_COMPLETE;
*tokflag = NO_TOKEN_SEND;
ret = GSS_S_COMPLETE;
}
cleanup:
if (supportedMech != GSS_C_NO_OID)
generic_gss_release_oid(&tmpmin, &supportedMech);
return ret;
}
static OM_uint32
init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
OM_uint32 acc_negState, gss_OID supportedMech,
gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret;
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
ret = GSS_S_DEFECTIVE_TOKEN;
if (supportedMech == GSS_C_NO_OID) {
*minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
map_errcode(minor_status);
return GSS_S_DEFECTIVE_TOKEN;
}
if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
spnego_set_error_message(sc, *minor_status,
dgettext(TEXT_DOMAIN,
"SPNEGO failed to negotiate a mechanism: defective token"));
map_errcode(minor_status);
return GSS_S_DEFECTIVE_TOKEN;
}
if (!(is_kerb_mech(supportedMech) &&
is_kerb_mech(sc->internal_mech)) &&
!g_OID_equal(supportedMech, sc->internal_mech)) {
ret = init_ctx_reselect(minor_status, sc,
acc_negState, supportedMech,
responseToken, mechListMIC,
negState, tokflag);
} else if (*responseToken == GSS_C_NO_BUFFER) {
if (sc->mech_complete) {
*negState = ACCEPT_COMPLETE;
*tokflag = NO_TOKEN_SEND;
ret = GSS_S_COMPLETE;
} else {
*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
map_errcode(minor_status);
ret = GSS_S_DEFECTIVE_TOKEN;
}
} else if (sc->mech_complete) {
ret = GSS_S_DEFECTIVE_TOKEN;
} else {
*negState = ACCEPT_INCOMPLETE;
*tokflag = CONT_TOKEN_SEND;
ret = GSS_S_CONTINUE_NEEDED;
}
sc->nego_done = 1;
return ret;
}
static OM_uint32
init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
OM_uint32 acc_negState, gss_OID supportedMech,
gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret, tmpmin;
generic_gss_release_oid(&tmpmin, &sc->internal_mech);
gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
GSS_C_NO_BUFFER);
ret = generic_gss_copy_oid(minor_status, supportedMech,
&sc->internal_mech);
if (ret != GSS_S_COMPLETE) {
map_errcode(minor_status);
sc->internal_mech = GSS_C_NO_OID;
*tokflag = NO_TOKEN_SEND;
return ret;
}
if (*responseToken != GSS_C_NO_BUFFER) {
return GSS_S_DEFECTIVE_TOKEN;
}
if (acc_negState != REQUEST_MIC)
return GSS_S_DEFECTIVE_TOKEN;
sc->mech_complete = 0;
sc->mic_reqd = 1;
*negState = REQUEST_MIC;
*tokflag = CONT_TOKEN_SEND;
return GSS_S_CONTINUE_NEEDED;
}
static OM_uint32
init_ctx_call_init(OM_uint32 *minor_status,
spnego_gss_ctx_id_t sc,
gss_cred_id_t claimant_cred_handle,
gss_name_t target_name,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_buffer_t mechtok_in,
gss_OID *actual_mech,
gss_buffer_t mechtok_out,
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
OM_uint32 *negState,
send_token_flag *send_token)
{
OM_uint32 ret;
ret = gss_init_sec_context(minor_status,
claimant_cred_handle,
&sc->ctx_handle,
target_name,
sc->internal_mech,
(req_flags | GSS_C_INTEG_FLAG),
time_req,
GSS_C_NO_CHANNEL_BINDINGS,
mechtok_in,
&sc->actual_mech,
mechtok_out,
&sc->ctx_flags,
time_rec);
if (ret == GSS_S_COMPLETE) {
sc->mech_complete = 1;
if (ret_flags != NULL)
*ret_flags = sc->ctx_flags;
if (*send_token == CONT_TOKEN_SEND &&
mechtok_out->length == 0 &&
(!sc->mic_reqd ||
!(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
*negState = ACCEPT_COMPLETE;
ret = GSS_S_COMPLETE;
if (mechtok_out->length == 0) {
*send_token = NO_TOKEN_SEND;
}
} else {
*negState = ACCEPT_INCOMPLETE;
ret = GSS_S_CONTINUE_NEEDED;
}
} else if (ret != GSS_S_CONTINUE_NEEDED) {
if (*send_token == INIT_TOKEN_SEND) {
*send_token = NO_TOKEN_SEND;
} else {
*send_token = ERROR_TOKEN_SEND;
}
*negState = REJECT;
}
return ret;
}
OM_uint32
glue_spnego_gss_init_sec_context(
void *context,
OM_uint32 *minor_status,
gss_cred_id_t claimant_cred_handle,
gss_ctx_id_t *context_handle,
gss_name_t target_name,
gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_channel_bindings_t input_chan_bindings,
gss_buffer_t input_token,
gss_OID *actual_mech,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
return(spnego_gss_init_sec_context(
minor_status,
claimant_cred_handle,
context_handle,
target_name,
mech_type,
req_flags,
time_req,
input_chan_bindings,
input_token,
actual_mech,
output_token,
ret_flags,
time_rec));
}
OM_uint32
spnego_gss_init_sec_context(
OM_uint32 *minor_status,
gss_cred_id_t claimant_cred_handle,
gss_ctx_id_t *context_handle,
gss_name_t target_name,
gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_channel_bindings_t input_chan_bindings,
gss_buffer_t input_token,
gss_OID *actual_mech,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
send_token_flag send_token = NO_TOKEN_SEND;
OM_uint32 tmpmin, ret, negState;
gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
gss_OID_set mechSet = GSS_C_NO_OID_SET;
spnego_gss_ctx_id_t spnego_ctx = NULL;
dsyslog("Entering init_sec_context\n");
mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
negState = REJECT;
if (minor_status != NULL)
*minor_status = 0;
if (output_token != GSS_C_NO_BUFFER) {
output_token->length = 0;
output_token->value = NULL;
}
if (minor_status == NULL ||
output_token == GSS_C_NO_BUFFER ||
context_handle == NULL)
return GSS_S_CALL_INACCESSIBLE_WRITE;
if (actual_mech != NULL)
*actual_mech = GSS_C_NO_OID;
if (*context_handle == GSS_C_NO_CONTEXT) {
ret = init_ctx_new(minor_status, claimant_cred_handle,
context_handle, &mechSet, &send_token);
if (ret != GSS_S_CONTINUE_NEEDED) {
goto cleanup;
}
} else {
ret = init_ctx_cont(minor_status, context_handle,
input_token, &mechtok_in,
&mechListMIC_in, &negState, &send_token);
if (HARD_ERROR(ret)) {
goto cleanup;
}
}
spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
spnego_gss_save_error_info(*minor_status, spnego_ctx);
if (!spnego_ctx->mech_complete) {
ret = init_ctx_call_init(
minor_status, spnego_ctx,
claimant_cred_handle,
target_name, req_flags,
time_req, mechtok_in,
actual_mech, &mechtok_out,
ret_flags, time_rec,
&negState, &send_token);
}
if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
(spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
ret = handle_mic(minor_status,
mechListMIC_in,
(mechtok_out.length != 0),
spnego_ctx, &mechListMIC_out,
&negState, &send_token);
}
cleanup:
if (send_token == INIT_TOKEN_SEND) {
if (make_spnego_tokenInit_msg(spnego_ctx,
0,
mechListMIC_out,
req_flags,
&mechtok_out, send_token,
output_token) < 0) {
ret = GSS_S_FAILURE;
}
} else if (send_token != NO_TOKEN_SEND) {
if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
&mechtok_out, mechListMIC_out,
send_token,
output_token) < 0) {
ret = GSS_S_FAILURE;
}
}
gss_release_buffer(&tmpmin, &mechtok_out);
if (ret == GSS_S_COMPLETE) {
*context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
if (actual_mech != NULL)
*actual_mech = spnego_ctx->actual_mech;
if (ret_flags != NULL)
*ret_flags = spnego_ctx->ctx_flags;
release_spnego_ctx(&spnego_ctx);
} else if (ret != GSS_S_CONTINUE_NEEDED) {
if (spnego_ctx != NULL) {
gss_delete_sec_context(&tmpmin,
&spnego_ctx->ctx_handle,
GSS_C_NO_BUFFER);
release_spnego_ctx(&spnego_ctx);
}
*context_handle = GSS_C_NO_CONTEXT;
}
if (mechtok_in != GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, mechtok_in);
free(mechtok_in);
}
if (mechListMIC_in != GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, mechListMIC_in);
free(mechListMIC_in);
}
if (mechListMIC_out != GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, mechListMIC_out);
free(mechListMIC_out);
}
if (mechSet != GSS_C_NO_OID_SET) {
gss_release_oid_set(&tmpmin, &mechSet);
}
return ret;
}
static const gss_OID_desc gss_mech_krb5_oid =
{ 9, "\052\206\110\206\367\022\001\002\002" };
static int
put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
unsigned int buflen)
{
int ret;
if (input_token->length == 0)
return (0);
if (input_token->length > buflen)
return (-1);
*(*buf_out)++ = SEQUENCE;
if ((ret = gssint_put_der_length(input_token->length, buf_out,
input_token->length)))
return (ret);
TWRITE_STR(*buf_out, input_token->value, input_token->length);
return (0);
}
#define HOST_PREFIX "host@"
#define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
static int
make_NegHints(OM_uint32 *minor_status,
gss_cred_id_t cred, gss_buffer_t *outbuf)
{
gss_buffer_desc hintNameBuf;
gss_name_t hintName = GSS_C_NO_NAME;
gss_name_t hintKerberosName;
gss_OID hintNameType;
OM_uint32 major_status;
OM_uint32 minor;
unsigned int tlen = 0;
unsigned int hintNameSize = 0;
unsigned char *ptr;
unsigned char *t;
*outbuf = GSS_C_NO_BUFFER;
if (cred != GSS_C_NO_CREDENTIAL) {
major_status = gss_inquire_cred(minor_status,
cred,
&hintName,
NULL,
NULL,
NULL);
if (major_status != GSS_S_COMPLETE)
return (major_status);
}
if (hintName == GSS_C_NO_NAME) {
krb5_error_code code;
krb5int_access kaccess;
char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
if (code != 0) {
*minor_status = code;
return (GSS_S_FAILURE);
}
code = (*kaccess.clean_hostname)(NULL, NULL,
&hostname[HOST_PREFIX_LEN],
MAXHOSTNAMELEN);
if (code != 0) {
*minor_status = code;
return (GSS_S_FAILURE);
}
hintNameBuf.value = hostname;
hintNameBuf.length = strlen(hostname);
major_status = gss_import_name(minor_status,
&hintNameBuf,
GSS_C_NT_HOSTBASED_SERVICE,
&hintName);
if (major_status != GSS_S_COMPLETE) {
return (major_status);
}
}
hintNameBuf.value = NULL;
hintNameBuf.length = 0;
major_status = gss_canonicalize_name(minor_status,
hintName,
(gss_OID)&gss_mech_krb5_oid,
&hintKerberosName);
if (major_status != GSS_S_COMPLETE) {
gss_release_name(&minor, &hintName);
return (major_status);
}
gss_release_name(&minor, &hintName);
major_status = gss_display_name(minor_status,
hintKerberosName,
&hintNameBuf,
&hintNameType);
if (major_status != GSS_S_COMPLETE) {
gss_release_name(&minor, &hintKerberosName);
return (major_status);
}
gss_release_name(&minor, &hintKerberosName);
major_status = GSS_S_FAILURE;
tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
hintNameBuf.length;
hintNameSize = tlen;
tlen += 1 + gssint_der_length_size(hintNameSize);
t = (unsigned char *)malloc(tlen);
if (t == NULL) {
*minor_status = ENOMEM;
goto errout;
}
ptr = t;
*ptr++ = CONTEXT | 0x00;
if (gssint_put_der_length(hintNameSize,
&ptr, tlen - (int)(ptr-t)))
goto errout;
*ptr++ = GENERAL_STRING;
if (gssint_put_der_length(hintNameBuf.length,
&ptr, tlen - (int)(ptr-t)))
goto errout;
memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
ptr += hintNameBuf.length;
*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
if (*outbuf == NULL) {
*minor_status = ENOMEM;
goto errout;
}
(*outbuf)->value = (void *)t;
(*outbuf)->length = ptr - t;
t = NULL;
*minor_status = 0;
major_status = GSS_S_COMPLETE;
errout:
if (t != NULL) {
free(t);
}
gss_release_buffer(&minor, &hintNameBuf);
return (major_status);
}
static OM_uint32
acc_ctx_hints(OM_uint32 *minor_status,
gss_ctx_id_t *ctx,
gss_cred_id_t cred,
gss_buffer_t *mechListMIC,
OM_uint32 *negState,
send_token_flag *return_token)
{
OM_uint32 tmpmin, ret;
gss_OID_set supported_mechSet;
spnego_gss_ctx_id_t sc = NULL;
*mechListMIC = GSS_C_NO_BUFFER;
supported_mechSet = GSS_C_NO_OID_SET;
*return_token = ERROR_TOKEN_SEND;
*negState = REJECT;
*minor_status = 0;
*ctx = GSS_C_NO_CONTEXT;
ret = GSS_S_DEFECTIVE_TOKEN;
if (cred != GSS_C_NO_CREDENTIAL) {
ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
NULL, &supported_mechSet);
if (ret != GSS_S_COMPLETE) {
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
} else {
ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
GSS_C_ACCEPT, NULL,
&supported_mechSet);
if (ret != GSS_S_COMPLETE) {
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
}
ret = make_NegHints(minor_status, cred, mechListMIC);
if (ret != GSS_S_COMPLETE) {
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
sc = create_spnego_ctx();
if (sc == NULL) {
ret = GSS_S_FAILURE;
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
ret = GSS_S_FAILURE;
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
sc->internal_mech = GSS_C_NO_OID;
*negState = ACCEPT_INCOMPLETE;
*return_token = INIT_TOKEN_SEND;
sc->firstpass = 1;
*ctx = (gss_ctx_id_t)sc;
ret = GSS_S_COMPLETE;
cleanup:
gss_release_oid_set(&tmpmin, &supported_mechSet);
return ret;
}
static const char *mech_no_map = "Can't map OID to mechname via /etc/gss/mech";
static const char *oid_no_map = "Can't map OID to string";
static char *
mechoidset2str(gss_OID_set mechset)
{
int i, l;
char buf[256] = {0};
char *s = NULL;
if (!mechset)
return NULL;
for (i = 0; i < mechset->count; i++) {
OM_uint32 maj, min;
gss_buffer_desc oidstr;
gss_buffer_t oidstrp = &oidstr;
gss_OID mech_oid = &mechset->elements[i];
const char *mech_name = __gss_oid_to_mech(mech_oid);
if (i > 0)
if (strlcat(buf, ", ", sizeof (buf)) >= sizeof (buf)) {
if (oidstrp->value)
gss_release_buffer(&min, oidstrp);
break;
}
maj = gss_oid_to_str(&min, mech_oid, oidstrp);
if (strlcat(buf, maj ? oid_no_map : oidstrp->value,
sizeof (buf)) >= sizeof (buf)) {
if (oidstrp->value)
gss_release_buffer(&min, oidstrp);
break;
}
if (oidstrp->value)
gss_release_buffer(&min, oidstrp);
if (strlcat(buf, " (", sizeof (buf)) >= sizeof (buf))
break;
if (strlcat(buf, mech_name ? mech_name : mech_no_map,
sizeof (buf)) >= sizeof (buf))
break;
if (strlcat(buf, ") ", sizeof (buf)) >= sizeof (buf))
break;
}
if (mechset->count) {
l = strlen(buf);
if (l > 0) {
s = malloc(l + 1);
if (!s)
return NULL;
(void) strlcpy(s, buf, l);
}
}
return s ? s : NULL;
}
static OM_uint32
acc_ctx_new(OM_uint32 *minor_status,
gss_buffer_t buf,
gss_ctx_id_t *ctx,
gss_cred_id_t cred,
gss_buffer_t *mechToken,
gss_buffer_t *mechListMIC,
OM_uint32 *negState,
send_token_flag *return_token)
{
OM_uint32 tmpmin, ret, req_flags;
gss_OID_set supported_mechSet, mechTypes;
gss_buffer_desc der_mechTypes;
gss_OID mech_wanted;
spnego_gss_ctx_id_t sc = NULL;
ret = GSS_S_DEFECTIVE_TOKEN;
der_mechTypes.length = 0;
der_mechTypes.value = NULL;
*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
*return_token = ERROR_TOKEN_SEND;
*negState = REJECT;
*minor_status = 0;
ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
&mechTypes, &req_flags,
mechToken, mechListMIC);
if (ret != GSS_S_COMPLETE) {
goto cleanup;
}
if (cred != GSS_C_NO_CREDENTIAL) {
ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
NULL, &supported_mechSet);
if (ret != GSS_S_COMPLETE) {
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
} else {
ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
GSS_C_ACCEPT, NULL,
&supported_mechSet);
if (ret != GSS_S_COMPLETE) {
*return_token = NO_TOKEN_SEND;
goto cleanup;
}
}
mech_wanted = negotiate_mech_type(minor_status,
supported_mechSet,
mechTypes,
negState);
if (*negState == REJECT) {
char *mechTypesStr = mechoidset2str(mechTypes);
spnego_gss_ctx_id_t tmpsc = create_spnego_ctx();
if (tmpsc && *minor_status == ERR_SPNEGO_NEGOTIATION_FAILED) {
spnego_set_error_message(tmpsc, *minor_status,
dgettext(TEXT_DOMAIN,
"SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
mechTypesStr ? mechTypesStr : "<null>");
}
if (mechTypesStr)
free(mechTypesStr);
spnego_gss_save_error_info(*minor_status, tmpsc);
if (tmpsc)
release_spnego_ctx(&tmpsc);
ret = GSS_S_BAD_MECH;
goto cleanup;
}
sc = (spnego_gss_ctx_id_t)*ctx;
if (sc != NULL) {
gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
assert(mech_wanted != GSS_C_NO_OID);
} else
sc = create_spnego_ctx();
if (sc == NULL) {
ret = GSS_S_FAILURE;
*return_token = NO_TOKEN_SEND;
generic_gss_release_oid(&tmpmin, &mech_wanted);
goto cleanup;
}
sc->internal_mech = mech_wanted;
sc->DER_mechTypes = der_mechTypes;
der_mechTypes.length = 0;
der_mechTypes.value = NULL;
if (*negState == REQUEST_MIC)
sc->mic_reqd = 1;
*return_token = INIT_TOKEN_SEND;
sc->firstpass = 1;
*ctx = (gss_ctx_id_t)sc;
ret = GSS_S_COMPLETE;
cleanup:
gss_release_oid_set(&tmpmin, &mechTypes);
gss_release_oid_set(&tmpmin, &supported_mechSet);
if (der_mechTypes.length != 0)
gss_release_buffer(&tmpmin, &der_mechTypes);
return ret;
}
static OM_uint32
acc_ctx_cont(OM_uint32 *minstat,
gss_buffer_t buf,
gss_ctx_id_t *ctx,
gss_buffer_t *responseToken,
gss_buffer_t *mechListMIC,
OM_uint32 *negState,
send_token_flag *return_token)
{
OM_uint32 ret, tmpmin;
gss_OID supportedMech;
spnego_gss_ctx_id_t sc;
unsigned int len;
unsigned char *ptr, *bufstart;
sc = (spnego_gss_ctx_id_t)*ctx;
ret = GSS_S_DEFECTIVE_TOKEN;
*negState = REJECT;
*minstat = 0;
supportedMech = GSS_C_NO_OID;
*return_token = ERROR_TOKEN_SEND;
*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
ptr = bufstart = buf->value;
#define REMAIN (buf->length - (ptr - bufstart))
if (REMAIN > INT_MAX)
return GSS_S_DEFECTIVE_TOKEN;
if (*ptr == HEADER_ID) {
ret = g_verify_token_header(gss_mech_spnego,
&len, &ptr, 0, REMAIN);
if (ret) {
*minstat = ret;
return GSS_S_DEFECTIVE_TOKEN;
}
}
if (*ptr != (CONTEXT | 0x01)) {
return GSS_S_DEFECTIVE_TOKEN;
}
ret = get_negTokenResp(minstat, ptr, REMAIN,
negState, &supportedMech,
responseToken, mechListMIC);
if (ret != GSS_S_COMPLETE)
goto cleanup;
if (*responseToken == GSS_C_NO_BUFFER &&
*mechListMIC == GSS_C_NO_BUFFER) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
}
if (supportedMech != GSS_C_NO_OID) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
}
sc->firstpass = 0;
*negState = ACCEPT_INCOMPLETE;
*return_token = CONT_TOKEN_SEND;
cleanup:
if (supportedMech != GSS_C_NO_OID) {
generic_gss_release_oid(&tmpmin, &supportedMech);
}
return ret;
#undef REMAIN
}
static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *minor_status,
spnego_gss_ctx_id_t sc, gss_OID mechoid,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret, tmpmin;
gss_mechanism mech = NULL;
gss_OID_set mech_set = GSS_C_NO_OID_SET;
int present = 0;
if (g_OID_equal(sc->internal_mech, mechoid))
return GSS_S_COMPLETE;
if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
return GSS_S_COMPLETE;
}
mech = gssint_get_mechanism(mechoid);
if (mech == NULL || mech->gss_indicate_mechs == NULL) {
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
{
OM_uint32 maj, maj_sc, min;
gss_buffer_desc oidstr, oidstr_sc;
const char *mnamestr = __gss_oid_to_mech(
sc->internal_mech);
maj_sc = gss_oid_to_str(&min,
sc->internal_mech,
&oidstr_sc);
maj = gss_oid_to_str(&min, mechoid, &oidstr);
spnego_set_error_message(sc, *minor_status,
dgettext(TEXT_DOMAIN,
"SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
maj ? oid_no_map: oidstr.value,
maj_sc ? oid_no_map: oidstr_sc.value,
mnamestr ? mnamestr : mech_no_map);
if (!maj)
(void) gss_release_buffer(&min, &oidstr);
if (!maj_sc)
(void) gss_release_buffer(&min, &oidstr_sc);
}
map_errcode(minor_status);
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
return GSS_S_BAD_MECH;
}
ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
if (ret != GSS_S_COMPLETE) {
*tokflag = NO_TOKEN_SEND;
map_error(minor_status, mech);
goto cleanup;
}
ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
mech_set, &present);
if (ret != GSS_S_COMPLETE)
goto cleanup;
if (!present) {
{
OM_uint32 maj, min;
gss_buffer_desc oidstr;
char *mech_set_str = mechoidset2str(mech_set);
const char *mnamestr =
__gss_oid_to_mech(sc->internal_mech);
maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
spnego_set_error_message(sc, *minor_status,
dgettext(TEXT_DOMAIN,
"SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
maj ? oid_no_map: oidstr.value,
mnamestr ? mnamestr : mech_no_map,
mech_set_str ? mech_set_str : "<null>");
if (!maj)
(void) gss_release_buffer(&min, &oidstr);
if (mech_set_str)
free(mech_set_str);
}
map_errcode(minor_status);
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
ret = GSS_S_BAD_MECH;
}
cleanup:
gss_release_oid_set(&tmpmin, &mech_set);
return ret;
}
#ifndef LEAN_CLIENT
static OM_uint32
acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
gss_cred_id_t cred, gss_buffer_t mechtok_in,
gss_OID *mech_type, gss_buffer_t mechtok_out,
OM_uint32 *ret_flags, OM_uint32 *time_rec,
gss_cred_id_t *delegated_cred_handle,
OM_uint32 *negState, send_token_flag *tokflag)
{
OM_uint32 ret;
gss_OID_desc mechoid;
if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
ret = gssint_get_mech_type(&mechoid, mechtok_in);
if (ret != GSS_S_COMPLETE) {
*tokflag = NO_TOKEN_SEND;
return ret;
}
ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
negState, tokflag);
if (ret != GSS_S_COMPLETE)
return ret;
}
ret = gss_accept_sec_context(minor_status,
&sc->ctx_handle,
cred,
mechtok_in,
GSS_C_NO_CHANNEL_BINDINGS,
&sc->internal_name,
mech_type,
mechtok_out,
&sc->ctx_flags,
time_rec,
delegated_cred_handle);
if (ret == GSS_S_COMPLETE) {
#ifdef MS_BUG_TEST
char *envstr = getenv("MS_FORCE_NO_MIC");
if (envstr != NULL && strcmp(envstr, "1") == 0 &&
!(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
sc->mic_reqd) {
sc->mic_reqd = 0;
}
#endif
sc->mech_complete = 1;
if (ret_flags != NULL)
*ret_flags = sc->ctx_flags;
if (!sc->mic_reqd) {
*negState = ACCEPT_COMPLETE;
ret = GSS_S_COMPLETE;
} else {
ret = GSS_S_CONTINUE_NEEDED;
}
} else if (ret != GSS_S_CONTINUE_NEEDED) {
*negState = REJECT;
*tokflag = ERROR_TOKEN_SEND;
}
return ret;
}
OM_uint32
glue_spnego_gss_accept_sec_context(
void *context,
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle,
gss_buffer_t input_token,
gss_channel_bindings_t input_chan_bindings,
gss_name_t *src_name,
gss_OID *mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
gss_cred_id_t *delegated_cred_handle)
{
return(spnego_gss_accept_sec_context(
minor_status,
context_handle,
verifier_cred_handle,
input_token,
input_chan_bindings,
src_name,
mech_type,
output_token,
ret_flags,
time_rec,
delegated_cred_handle));
}
OM_uint32
spnego_gss_accept_sec_context(
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle,
gss_buffer_t input_token,
gss_channel_bindings_t input_chan_bindings,
gss_name_t *src_name,
gss_OID *mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
gss_cred_id_t *delegated_cred_handle)
{
OM_uint32 ret, tmpmin, negState;
send_token_flag return_token;
gss_buffer_t mechtok_in, mic_in, mic_out;
gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
spnego_gss_ctx_id_t sc = NULL;
int sendTokenInit = 0, tmpret;
mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
if (minor_status != NULL)
*minor_status = 0;
if (output_token != GSS_C_NO_BUFFER) {
output_token->length = 0;
output_token->value = NULL;
}
if (minor_status == NULL ||
output_token == GSS_C_NO_BUFFER ||
context_handle == NULL) {
return GSS_S_CALL_INACCESSIBLE_WRITE;
}
if (input_token == GSS_C_NO_BUFFER) {
return GSS_S_CALL_INACCESSIBLE_READ;
}
sc = (spnego_gss_ctx_id_t)*context_handle;
if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
if (src_name != NULL)
*src_name = GSS_C_NO_NAME;
if (mech_type != NULL)
*mech_type = GSS_C_NO_OID;
if (time_rec != NULL)
*time_rec = 0;
if (ret_flags != NULL)
*ret_flags = 0;
if (delegated_cred_handle != NULL)
*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
if (input_token->length == 0) {
ret = acc_ctx_hints(minor_status,
context_handle,
verifier_cred_handle,
&mic_out,
&negState,
&return_token);
if (ret != GSS_S_COMPLETE)
goto cleanup;
sendTokenInit = 1;
ret = GSS_S_CONTINUE_NEEDED;
} else {
ret = acc_ctx_new(minor_status, input_token,
context_handle, verifier_cred_handle,
&mechtok_in, &mic_in,
&negState, &return_token);
if (ret != GSS_S_COMPLETE)
goto cleanup;
ret = GSS_S_CONTINUE_NEEDED;
}
} else {
ret = acc_ctx_cont(minor_status, input_token,
context_handle, &mechtok_in,
&mic_in, &negState, &return_token);
if (ret != GSS_S_COMPLETE)
goto cleanup;
ret = GSS_S_CONTINUE_NEEDED;
}
sc = (spnego_gss_ctx_id_t)*context_handle;
if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
ret = acc_ctx_call_acc(minor_status, sc,
verifier_cred_handle, mechtok_in,
mech_type, &mechtok_out,
ret_flags, time_rec,
delegated_cred_handle,
&negState, &return_token);
}
if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
spnego_gss_save_error_info(*minor_status, sc);
if (!HARD_ERROR(ret) && sc->mech_complete &&
(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
ret = handle_mic(minor_status, mic_in,
(mechtok_out.length != 0),
sc, &mic_out,
&negState, &return_token);
}
cleanup:
if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
assert(sc != NULL);
tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
GSS_C_NO_BUFFER,
return_token, output_token);
if (tmpret < 0)
ret = GSS_S_FAILURE;
} else if (return_token != NO_TOKEN_SEND &&
return_token != CHECK_MIC) {
tmpret = make_spnego_tokenTarg_msg(negState,
sc ? sc->internal_mech :
GSS_C_NO_OID,
&mechtok_out, mic_out,
return_token,
output_token);
if (tmpret < 0)
ret = GSS_S_FAILURE;
}
if (ret == GSS_S_COMPLETE) {
*context_handle = (gss_ctx_id_t)sc->ctx_handle;
if (sc->internal_name != GSS_C_NO_NAME &&
src_name != NULL) {
*src_name = sc->internal_name;
}
release_spnego_ctx(&sc);
}
gss_release_buffer(&tmpmin, &mechtok_out);
if (mechtok_in != GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, mechtok_in);
free(mechtok_in);
}
if (mic_in != GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, mic_in);
free(mic_in);
}
if (mic_out != GSS_C_NO_BUFFER) {
gss_release_buffer(&tmpmin, mic_out);
free(mic_out);
}
return ret;
}
#endif
OM_uint32
glue_spnego_gss_display_status(
void *context,
OM_uint32 *minor_status,
OM_uint32 status_value,
int status_type,
gss_OID mech_type,
OM_uint32 *message_context,
gss_buffer_t status_string)
{
return (spnego_gss_display_status(minor_status,
status_value,
status_type,
mech_type,
message_context,
status_string));
}
OM_uint32
spnego_gss_display_status(
OM_uint32 *minor_status,
OM_uint32 status_value,
int status_type,
gss_OID mech_type,
OM_uint32 *message_context,
gss_buffer_t status_string)
{
dsyslog("Entering display_status\n");
*message_context = 0;
switch (status_value) {
case ERR_SPNEGO_NO_MECHS_AVAILABLE:
*status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
break;
case ERR_SPNEGO_NO_CREDS_ACQUIRED:
*status_string = make_err_msg("SPNEGO failed to acquire creds");
break;
case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
*status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
break;
case ERR_SPNEGO_NEGOTIATION_FAILED:
return(spnego_gss_display_status2(minor_status,
status_value,
status_type,
mech_type,
message_context,
status_string));
case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
*status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
break;
default:
return(krb5_gss_display_status2(minor_status,
status_value,
status_type,
(gss_OID)&gss_mech_krb5_oid,
message_context,
status_string));
}
dsyslog("Leaving display_status\n");
return (GSS_S_COMPLETE);
}
OM_uint32
glue_spnego_gss_import_name(
void *context,
OM_uint32 *minor_status,
gss_buffer_t input_name_buffer,
gss_OID input_name_type,
gss_name_t *output_name)
{
return(spnego_gss_import_name(minor_status,
input_name_buffer,
input_name_type,
output_name));
}
OM_uint32
spnego_gss_import_name(
OM_uint32 *minor_status,
gss_buffer_t input_name_buffer,
gss_OID input_name_type,
gss_name_t *output_name)
{
OM_uint32 status;
dsyslog("Entering import_name\n");
status = gss_import_name(minor_status, input_name_buffer,
input_name_type, output_name);
dsyslog("Leaving import_name\n");
return (status);
}
OM_uint32
glue_spnego_gss_release_name(
void *context,
OM_uint32 *minor_status,
gss_name_t *input_name)
{
return(spnego_gss_release_name(minor_status, input_name));
}
OM_uint32
spnego_gss_release_name(
OM_uint32 *minor_status,
gss_name_t *input_name)
{
OM_uint32 status;
dsyslog("Entering release_name\n");
status = gss_release_name(minor_status, input_name);
dsyslog("Leaving release_name\n");
return (status);
}
OM_uint32
glue_spnego_gss_compare_name(
void *context,
OM_uint32 *minor_status,
const gss_name_t name1,
const gss_name_t name2,
int *name_equal)
{
return(spnego_gss_compare_name(minor_status,
name1,
name2,
name_equal));
}
OM_uint32
spnego_gss_compare_name(
OM_uint32 *minor_status,
const gss_name_t name1,
const gss_name_t name2,
int *name_equal)
{
OM_uint32 status = GSS_S_COMPLETE;
dsyslog("Entering compare_name\n");
status = gss_compare_name(minor_status, name1, name2, name_equal);
dsyslog("Leaving compare_name\n");
return (status);
}
OM_uint32
glue_spnego_gss_display_name(
void *context,
OM_uint32 *minor_status,
gss_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID *output_name_type)
{
return(spnego_gss_display_name(
minor_status,
input_name,
output_name_buffer,
output_name_type));
}
OM_uint32
spnego_gss_display_name(
OM_uint32 *minor_status,
gss_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID *output_name_type)
{
OM_uint32 status = GSS_S_COMPLETE;
dsyslog("Entering display_name\n");
status = gss_display_name(minor_status, input_name,
output_name_buffer, output_name_type);
dsyslog("Leaving display_name\n");
return (status);
}
OM_uint32
glue_spnego_gss_inquire_names_for_mech(
void *context,
OM_uint32 *minor_status,
gss_OID mechanism,
gss_OID_set *name_types)
{
return(spnego_gss_inquire_names_for_mech(minor_status,
mechanism,
name_types));
}
OM_uint32
spnego_gss_inquire_names_for_mech(
OM_uint32 *minor_status,
gss_OID mechanism,
gss_OID_set *name_types)
{
OM_uint32 major, minor;
dsyslog("Entering inquire_names_for_mech\n");
if ((mechanism != GSS_C_NULL_OID) &&
!g_OID_equal(gss_mech_spnego, mechanism)) {
*minor_status = 0;
return (GSS_S_FAILURE);
}
major = gss_create_empty_oid_set(minor_status, name_types);
if (major == GSS_S_COMPLETE) {
if (((major = gss_add_oid_set_member(minor_status,
(gss_OID) GSS_C_NT_USER_NAME,
name_types)) == GSS_S_COMPLETE) &&
((major = gss_add_oid_set_member(minor_status,
(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
name_types)) == GSS_S_COMPLETE) &&
((major = gss_add_oid_set_member(minor_status,
(gss_OID) GSS_C_NT_STRING_UID_NAME,
name_types)) == GSS_S_COMPLETE)) {
major = gss_add_oid_set_member(minor_status,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
name_types);
}
if (major != GSS_S_COMPLETE)
(void) gss_release_oid_set(&minor, name_types);
}
dsyslog("Leaving inquire_names_for_mech\n");
return (major);
}
OM_uint32
spnego_gss_unwrap(
OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
gss_buffer_t input_message_buffer,
gss_buffer_t output_message_buffer,
int *conf_state,
gss_qop_t *qop_state)
{
OM_uint32 ret;
ret = gss_unwrap(minor_status,
context_handle,
input_message_buffer,
output_message_buffer,
conf_state,
qop_state);
return (ret);
}
OM_uint32
spnego_gss_wrap(
OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
gss_buffer_t input_message_buffer,
int *conf_state,
gss_buffer_t output_message_buffer)
{
OM_uint32 ret;
ret = gss_wrap(minor_status,
context_handle,
conf_req_flag,
qop_req,
input_message_buffer,
conf_state,
output_message_buffer);
return (ret);
}
OM_uint32
spnego_gss_process_context_token(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
const gss_buffer_t token_buffer)
{
OM_uint32 ret;
ret = gss_process_context_token(minor_status,
context_handle,
token_buffer);
return (ret);
}
OM_uint32
glue_spnego_gss_delete_sec_context(
void *context,
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t output_token)
{
return(spnego_gss_delete_sec_context(minor_status,
context_handle, output_token));
}
OM_uint32
spnego_gss_delete_sec_context(
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t output_token)
{
OM_uint32 ret = GSS_S_COMPLETE;
spnego_gss_ctx_id_t *ctx =
(spnego_gss_ctx_id_t *)context_handle;
if (context_handle == NULL)
return (GSS_S_FAILURE);
if (*ctx != NULL &&
(*ctx)->magic_num == SPNEGO_MAGIC_ID) {
(void) release_spnego_ctx(ctx);
if (output_token) {
output_token->length = 0;
output_token->value = NULL;
}
} else {
ret = gss_delete_sec_context(minor_status,
context_handle,
output_token);
}
return (ret);
}
OM_uint32
glue_spnego_gss_context_time(
void *context,
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
OM_uint32 *time_rec)
{
return(spnego_gss_context_time(minor_status,
context_handle,
time_rec));
}
OM_uint32
spnego_gss_context_time(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
OM_uint32 *time_rec)
{
OM_uint32 ret;
ret = gss_context_time(minor_status,
context_handle,
time_rec);
return (ret);
}
#ifndef LEAN_CLIENT
OM_uint32
glue_spnego_gss_export_sec_context(
void *context,
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t interprocess_token)
{
return(spnego_gss_export_sec_context(minor_status,
context_handle,
interprocess_token));
}
OM_uint32
spnego_gss_export_sec_context(
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t interprocess_token)
{
OM_uint32 ret;
ret = gss_export_sec_context(minor_status,
context_handle,
interprocess_token);
return (ret);
}
OM_uint32
glue_spnego_gss_import_sec_context(
void *context,
OM_uint32 *minor_status,
const gss_buffer_t interprocess_token,
gss_ctx_id_t *context_handle)
{
return(spnego_gss_import_sec_context(minor_status,
interprocess_token,
context_handle));
}
OM_uint32
spnego_gss_import_sec_context(
OM_uint32 *minor_status,
const gss_buffer_t interprocess_token,
gss_ctx_id_t *context_handle)
{
OM_uint32 ret;
ret = gss_import_sec_context(minor_status,
interprocess_token,
context_handle);
return (ret);
}
#endif
OM_uint32
glue_spnego_gss_inquire_context(
void *context,
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
gss_name_t *src_name,
gss_name_t *targ_name,
OM_uint32 *lifetime_rec,
gss_OID *mech_type,
OM_uint32 *ctx_flags,
int *locally_initiated,
int *opened)
{
return(spnego_gss_inquire_context(
minor_status,
context_handle,
src_name,
targ_name,
lifetime_rec,
mech_type,
ctx_flags,
locally_initiated,
opened));
}
OM_uint32
spnego_gss_inquire_context(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
gss_name_t *src_name,
gss_name_t *targ_name,
OM_uint32 *lifetime_rec,
gss_OID *mech_type,
OM_uint32 *ctx_flags,
int *locally_initiated,
int *opened)
{
OM_uint32 ret = GSS_S_COMPLETE;
ret = gss_inquire_context(minor_status,
context_handle,
src_name,
targ_name,
lifetime_rec,
mech_type,
ctx_flags,
locally_initiated,
opened);
return (ret);
}
OM_uint32
glue_spnego_gss_wrap_size_limit(
void *context,
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
OM_uint32 *max_input_size)
{
return(spnego_gss_wrap_size_limit(minor_status,
context_handle,
conf_req_flag,
qop_req,
req_output_size,
max_input_size));
}
OM_uint32
spnego_gss_wrap_size_limit(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
OM_uint32 *max_input_size)
{
OM_uint32 ret;
ret = gss_wrap_size_limit(minor_status,
context_handle,
conf_req_flag,
qop_req,
req_output_size,
max_input_size);
return (ret);
}
#if 0
OM_uint32
spnego_gss_get_mic(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
gss_qop_t qop_req,
const gss_buffer_t message_buffer,
gss_buffer_t message_token)
{
OM_uint32 ret;
ret = gss_get_mic(minor_status,
context_handle,
qop_req,
message_buffer,
message_token);
return (ret);
}
#endif
OM_uint32
spnego_gss_verify_mic(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
const gss_buffer_t msg_buffer,
const gss_buffer_t token_buffer,
gss_qop_t *qop_state)
{
OM_uint32 ret;
ret = gss_verify_mic(minor_status,
context_handle,
msg_buffer,
token_buffer,
qop_state);
return (ret);
}
OM_uint32
spnego_gss_inquire_sec_context_by_oid(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
OM_uint32 ret;
ret = gss_inquire_sec_context_by_oid(minor_status,
context_handle,
desired_object,
data_set);
return (ret);
}
#if 0
OM_uint32
spnego_gss_set_sec_context_option(
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
const gss_OID desired_object,
const gss_buffer_t value)
{
OM_uint32 ret;
ret = gss_set_sec_context_option(minor_status,
context_handle,
desired_object,
value);
return (ret);
}
OM_uint32
spnego_gss_wrap_aead(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
gss_buffer_t input_assoc_buffer,
gss_buffer_t input_payload_buffer,
int *conf_state,
gss_buffer_t output_message_buffer)
{
OM_uint32 ret;
ret = gss_wrap_aead(minor_status,
context_handle,
conf_req_flag,
qop_req,
input_assoc_buffer,
input_payload_buffer,
conf_state,
output_message_buffer);
return (ret);
}
OM_uint32
spnego_gss_unwrap_aead(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
gss_buffer_t input_message_buffer,
gss_buffer_t input_assoc_buffer,
gss_buffer_t output_payload_buffer,
int *conf_state,
gss_qop_t *qop_state)
{
OM_uint32 ret;
ret = gss_unwrap_aead(minor_status,
context_handle,
input_message_buffer,
input_assoc_buffer,
output_payload_buffer,
conf_state,
qop_state);
return (ret);
}
OM_uint32
spnego_gss_wrap_iov(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
int *conf_state,
gss_iov_buffer_desc *iov,
int iov_count)
{
OM_uint32 ret;
ret = gss_wrap_iov(minor_status,
context_handle,
conf_req_flag,
qop_req,
conf_state,
iov,
iov_count);
return (ret);
}
OM_uint32
spnego_gss_unwrap_iov(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int *conf_state,
gss_qop_t *qop_state,
gss_iov_buffer_desc *iov,
int iov_count)
{
OM_uint32 ret;
ret = gss_unwrap_iov(minor_status,
context_handle,
conf_state,
qop_state,
iov,
iov_count);
return (ret);
}
OM_uint32
spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
int *conf_state,
gss_iov_buffer_desc *iov,
int iov_count)
{
OM_uint32 ret;
ret = gss_wrap_iov_length(minor_status,
context_handle,
conf_req_flag,
qop_req,
conf_state,
iov,
iov_count);
return (ret);
}
OM_uint32
spnego_gss_complete_auth_token(
OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
gss_buffer_t input_message_buffer)
{
OM_uint32 ret;
ret = gss_complete_auth_token(minor_status,
context_handle,
input_message_buffer);
return (ret);
}
#endif
static void
release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
{
spnego_gss_ctx_id_t context;
OM_uint32 minor_stat;
context = *ctx;
if (context != NULL) {
(void) gss_release_buffer(&minor_stat,
&context->DER_mechTypes);
(void) generic_gss_release_oid(&minor_stat,
&context->internal_mech);
if (context->optionStr != NULL) {
free(context->optionStr);
context->optionStr = NULL;
}
free(context);
*ctx = NULL;
}
}
static OM_uint32
get_available_mechs(OM_uint32 *minor_status,
gss_name_t name, gss_cred_usage_t usage,
gss_cred_id_t *creds, gss_OID_set *rmechs)
{
unsigned int i;
int found = 0;
OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
gss_OID_set mechs, goodmechs;
major_status = gss_indicate_mechs(minor_status, &mechs);
if (major_status != GSS_S_COMPLETE) {
return (major_status);
}
major_status = gss_create_empty_oid_set(minor_status, rmechs);
if (major_status != GSS_S_COMPLETE) {
(void) gss_release_oid_set(minor_status, &mechs);
return (major_status);
}
for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
if ((mechs->elements[i].length
!= spnego_mechanism.mech_type.length) ||
memcmp(mechs->elements[i].elements,
spnego_mechanism.mech_type.elements,
spnego_mechanism.mech_type.length)) {
if (is_kerb_mech(&mechs->elements[i])) {
extern gss_OID_desc * const gss_mech_krb5_wrong;
major_status =
gss_add_oid_set_member(minor_status,
gss_mech_krb5_wrong, rmechs);
}
major_status = gss_add_oid_set_member(minor_status,
&mechs->elements[i],
rmechs);
if (major_status == GSS_S_COMPLETE)
found++;
}
}
if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
major_status = gss_acquire_cred(minor_status,
name, GSS_C_INDEFINITE,
*rmechs, usage, creds,
&goodmechs, NULL);
(void) gss_release_oid_set(&tmpmin, rmechs);
if (major_status == GSS_S_COMPLETE) {
(void) gssint_copy_oid_set(&tmpmin,
goodmechs, rmechs);
(void) gss_release_oid_set(&tmpmin, &goodmechs);
}
}
(void) gss_release_oid_set(&tmpmin, &mechs);
if (found == 0 || major_status != GSS_S_COMPLETE) {
*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
map_errcode(minor_status);
if (major_status == GSS_S_COMPLETE)
major_status = GSS_S_FAILURE;
}
return (major_status);
}
static gss_OID
get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
{
OM_uint32 status;
gss_OID_desc toid;
gss_OID mech_out = NULL;
unsigned char *start, *end;
if (length < 1 || **buff_in != MECH_OID)
return (NULL);
start = *buff_in;
end = start + length;
(*buff_in)++;
toid.length = *(*buff_in)++;
if ((*buff_in + toid.length) > end)
return (NULL);
toid.elements = *buff_in;
*buff_in += toid.length;
status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
if (status != GSS_S_COMPLETE) {
map_errcode(minor_status);
mech_out = NULL;
}
return (mech_out);
}
static int
put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
{
if (buflen < mech->length + 2)
return (-1);
*(*buf_out)++ = MECH_OID;
*(*buf_out)++ = (unsigned char) mech->length;
memcpy((void *)(*buf_out), mech->elements, mech->length);
*buf_out += mech->length;
return (0);
}
static gss_buffer_t
get_input_token(unsigned char **buff_in, unsigned int buff_length)
{
gss_buffer_t input_token;
unsigned int bytes;
if (**buff_in != OCTET_STRING)
return (NULL);
(*buff_in)++;
input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
if (input_token == NULL)
return (NULL);
input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
if ((int)input_token->length == -1) {
free(input_token);
return (NULL);
}
input_token->value = malloc(input_token->length);
if (input_token->value == NULL) {
free(input_token);
return (NULL);
}
(void) memcpy(input_token->value, *buff_in, input_token->length);
*buff_in += input_token->length;
return (input_token);
}
static int
put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
unsigned int buflen)
{
int ret;
if (input_token->length == 0)
return (0);
if (input_token->length > buflen)
return (-1);
*(*buf_out)++ = OCTET_STRING;
if ((ret = gssint_put_der_length(input_token->length, buf_out,
input_token->length)))
return (ret);
TWRITE_STR(*buf_out, input_token->value, input_token->length);
return (0);
}
static gss_OID_set
get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
unsigned int buff_length)
{
gss_OID_set returned_mechSet;
OM_uint32 major_status;
int length;
OM_uint32 bytes;
OM_uint32 set_length;
unsigned char *start;
int i;
if (**buff_in != SEQUENCE_OF)
return (NULL);
start = *buff_in;
(*buff_in)++;
length = gssint_get_der_length(buff_in, buff_length, &bytes);
if (length < 0)
return (NULL);
major_status = gss_create_empty_oid_set(minor_status,
&returned_mechSet);
if (major_status != GSS_S_COMPLETE)
return (NULL);
for (set_length = 0, i = 0; set_length < length; i++) {
gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
buff_length - (*buff_in - start));
if (temp != NULL) {
major_status = gss_add_oid_set_member(minor_status,
temp, &returned_mechSet);
if (major_status == GSS_S_COMPLETE) {
set_length += returned_mechSet->elements[i].length +2;
if (generic_gss_release_oid(minor_status, &temp))
map_errcode(minor_status);
}
}
}
return (returned_mechSet);
}
static int
put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
{
unsigned char *ptr;
unsigned int i;
unsigned int tlen, ilen;
tlen = ilen = 0;
for (i = 0; i < mechSet->count; i++) {
ilen += 1 +
gssint_der_length_size(mechSet->elements[i].length) +
mechSet->elements[i].length;
}
tlen = 1 + gssint_der_length_size(ilen) + ilen;
ptr = malloc(tlen);
if (ptr == NULL)
return -1;
buf->value = ptr;
buf->length = tlen;
#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
*ptr++ = SEQUENCE_OF;
if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
return -1;
for (i = 0; i < mechSet->count; i++) {
if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
return -1;
}
}
return 0;
#undef REMAIN
}
static OM_uint32
get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
OM_uint32 *req_flags)
{
unsigned int len;
if (**buff_in != (CONTEXT | 0x01))
return (0);
if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
bodysize, &len) < 0)
return GSS_S_DEFECTIVE_TOKEN;
if (*(*buff_in)++ != BIT_STRING)
return GSS_S_DEFECTIVE_TOKEN;
if (*(*buff_in)++ != BIT_STRING_LENGTH)
return GSS_S_DEFECTIVE_TOKEN;
if (*(*buff_in)++ != BIT_STRING_PADDING)
return GSS_S_DEFECTIVE_TOKEN;
*req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
return (0);
}
static OM_uint32
get_negTokenInit(OM_uint32 *minor_status,
gss_buffer_t buf,
gss_buffer_t der_mechSet,
gss_OID_set *mechSet,
OM_uint32 *req_flags,
gss_buffer_t *mechtok,
gss_buffer_t *mechListMIC)
{
OM_uint32 err;
unsigned char *ptr, *bufstart;
unsigned int len;
gss_buffer_desc tmpbuf;
*minor_status = 0;
der_mechSet->length = 0;
der_mechSet->value = NULL;
*mechSet = GSS_C_NO_OID_SET;
*req_flags = 0;
*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
ptr = bufstart = buf->value;
if ((buf->length - (ptr - bufstart)) > INT_MAX)
return GSS_S_FAILURE;
#define REMAIN (buf->length - (ptr - bufstart))
err = g_verify_token_header(gss_mech_spnego,
&len, &ptr, 0, REMAIN);
if (err) {
*minor_status = err;
map_errcode(minor_status);
return GSS_S_FAILURE;
}
*minor_status = g_verify_neg_token_init(&ptr, REMAIN);
if (*minor_status) {
map_errcode(minor_status);
return GSS_S_FAILURE;
}
tmpbuf.value = ptr;
tmpbuf.length = REMAIN;
*mechSet = get_mech_set(minor_status, &ptr, REMAIN);
if (*mechSet == NULL)
return GSS_S_FAILURE;
tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
der_mechSet->value = malloc(tmpbuf.length);
if (der_mechSet->value == NULL)
return GSS_S_FAILURE;
memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
der_mechSet->length = tmpbuf.length;
err = get_req_flags(&ptr, REMAIN, req_flags);
if (err != GSS_S_COMPLETE) {
return err;
}
if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
REMAIN, &len) >= 0) {
*mechtok = get_input_token(&ptr, len);
if (*mechtok == GSS_C_NO_BUFFER) {
return GSS_S_FAILURE;
}
}
if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
REMAIN, &len) >= 0) {
*mechListMIC = get_input_token(&ptr, len);
if (*mechListMIC == GSS_C_NO_BUFFER) {
return GSS_S_FAILURE;
}
}
return GSS_S_COMPLETE;
#undef REMAIN
}
static OM_uint32
get_negTokenResp(OM_uint32 *minor_status,
unsigned char *buf, unsigned int buflen,
OM_uint32 *negState,
gss_OID *supportedMech,
gss_buffer_t *responseToken,
gss_buffer_t *mechListMIC)
{
unsigned char *ptr, *bufstart;
unsigned int len;
int tmplen;
unsigned int tag, bytes;
*negState = ACCEPT_DEFECTIVE_TOKEN;
*supportedMech = GSS_C_NO_OID;
*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
ptr = bufstart = buf;
#define REMAIN (buflen - (ptr - bufstart))
if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
return GSS_S_DEFECTIVE_TOKEN;
if (*ptr++ == SEQUENCE) {
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
if (tmplen < 0)
return GSS_S_DEFECTIVE_TOKEN;
}
if (REMAIN < 1)
tag = 0;
else
tag = *ptr++;
if (tag == CONTEXT) {
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
if (tmplen < 0)
return GSS_S_DEFECTIVE_TOKEN;
if (g_get_tag_and_length(&ptr, ENUMERATED,
REMAIN, &len) < 0)
return GSS_S_DEFECTIVE_TOKEN;
if (len != ENUMERATION_LENGTH)
return GSS_S_DEFECTIVE_TOKEN;
if (REMAIN < 1)
return GSS_S_DEFECTIVE_TOKEN;
*negState = *ptr++;
if (REMAIN < 1)
tag = 0;
else
tag = *ptr++;
}
if (tag == (CONTEXT | 0x01)) {
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
if (tmplen < 0)
return GSS_S_DEFECTIVE_TOKEN;
*supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
if (*supportedMech == GSS_C_NO_OID)
return GSS_S_DEFECTIVE_TOKEN;
if (REMAIN < 1)
tag = 0;
else
tag = *ptr++;
}
if (tag == (CONTEXT | 0x02)) {
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
if (tmplen < 0)
return GSS_S_DEFECTIVE_TOKEN;
*responseToken = get_input_token(&ptr, REMAIN);
if (*responseToken == GSS_C_NO_BUFFER)
return GSS_S_DEFECTIVE_TOKEN;
if (REMAIN < 1)
tag = 0;
else
tag = *ptr++;
}
if (tag == (CONTEXT | 0x03)) {
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
if (tmplen < 0)
return GSS_S_DEFECTIVE_TOKEN;
*mechListMIC = get_input_token(&ptr, REMAIN);
if (*mechListMIC == GSS_C_NO_BUFFER)
return GSS_S_DEFECTIVE_TOKEN;
}
return GSS_S_COMPLETE;
#undef REMAIN
}
static int
put_negResult(unsigned char **buf_out, OM_uint32 negResult,
unsigned int buflen)
{
if (buflen < 3)
return (-1);
*(*buf_out)++ = ENUMERATED;
*(*buf_out)++ = ENUMERATION_LENGTH;
*(*buf_out)++ = (unsigned char) negResult;
return (0);
}
static gss_OID
negotiate_mech_type(OM_uint32 *minor_status,
gss_OID_set supported_mechSet,
gss_OID_set mechset,
OM_uint32 *negResult)
{
gss_OID returned_mech;
OM_uint32 status;
int present;
unsigned int i;
for (i = 0; i < mechset->count; i++) {
gss_OID mech_oid = &mechset->elements[i];
#if 0
if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
mech_oid = (gss_OID)&gss_mech_krb5_oid;
#endif
gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
if (!present)
continue;
if (i == 0)
*negResult = ACCEPT_INCOMPLETE;
else
*negResult = REQUEST_MIC;
status = generic_gss_copy_oid(minor_status,
&mechset->elements[i],
&returned_mech);
if (status != GSS_S_COMPLETE) {
*negResult = REJECT;
map_errcode(minor_status);
return (NULL);
}
return (returned_mech);
}
*minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
*negResult = REJECT;
return (NULL);
}
static spnego_token_t
make_spnego_token(char *name)
{
return (spnego_token_t)strdup(name);
}
static gss_buffer_desc
make_err_msg(char *name)
{
gss_buffer_desc buffer;
if (name == NULL) {
buffer.length = 0;
buffer.value = NULL;
} else {
buffer.length = strlen(name)+1;
buffer.value = make_spnego_token(name);
}
return (buffer);
}
static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
int negHintsCompat,
gss_buffer_t mechListMIC, OM_uint32 req_flags,
gss_buffer_t data, send_token_flag sendtoken,
gss_buffer_t outbuf)
{
int ret = 0;
unsigned int tlen, dataLen = 0;
unsigned int negTokenInitSize = 0;
unsigned int negTokenInitSeqSize = 0;
unsigned int negTokenInitContSize = 0;
unsigned int rspTokenSize = 0;
unsigned int mechListTokenSize = 0;
unsigned int micTokenSize = 0;
unsigned char *t;
unsigned char *ptr;
if (outbuf == GSS_C_NO_BUFFER)
return (-1);
outbuf->length = 0;
outbuf->value = NULL;
mechListTokenSize = 1 +
gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
spnego_ctx->DER_mechTypes.length;
dataLen += mechListTokenSize;
if (data != NULL) {
rspTokenSize = 1 +
gssint_der_length_size(data->length) +
data->length;
dataLen += 1 + gssint_der_length_size(rspTokenSize) +
rspTokenSize;
}
if (mechListMIC) {
micTokenSize = 1 +
gssint_der_length_size(mechListMIC->length) +
mechListMIC->length;
dataLen += 1 +
gssint_der_length_size(micTokenSize) +
micTokenSize;
}
negTokenInitContSize = dataLen;
negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
dataLen = negTokenInitSeqSize;
negTokenInitSize = 1 +
gssint_der_length_size(negTokenInitSeqSize) +
negTokenInitSeqSize;
tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
t = (unsigned char *) malloc(tlen);
if (t == NULL) {
return (-1);
}
ptr = t;
if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
&ptr, tlen)))
goto errout;
*ptr++ = CONTEXT;
if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
goto errout;
*ptr++ = SEQUENCE;
if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
tlen - (int)(ptr-t))))
goto errout;
*ptr++ = CONTEXT | 0x00;
if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
&ptr, tlen - (int)(ptr-t))))
goto errout;
(void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
spnego_ctx->DER_mechTypes.length);
ptr += spnego_ctx->DER_mechTypes.length;
if (data != NULL) {
*ptr++ = CONTEXT | 0x02;
if ((ret = gssint_put_der_length(rspTokenSize,
&ptr, tlen - (int)(ptr - t))))
goto errout;
if ((ret = put_input_token(&ptr, data,
tlen - (int)(ptr - t))))
goto errout;
}
if (mechListMIC != GSS_C_NO_BUFFER) {
*ptr++ = CONTEXT | 0x03;
if ((ret = gssint_put_der_length(micTokenSize,
&ptr, tlen - (int)(ptr - t))))
goto errout;
if (negHintsCompat) {
ret = put_neg_hints(&ptr, mechListMIC,
tlen - (int)(ptr - t));
if (ret)
goto errout;
} else if ((ret = put_input_token(&ptr, mechListMIC,
tlen - (int)(ptr - t))))
goto errout;
}
errout:
if (ret != 0) {
if (t)
free(t);
t = NULL;
tlen = 0;
}
outbuf->length = tlen;
outbuf->value = (void *) t;
return (ret);
}
static int
make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
gss_buffer_t data, gss_buffer_t mechListMIC,
send_token_flag sendtoken,
gss_buffer_t outbuf)
{
unsigned int tlen = 0;
unsigned int ret = 0;
unsigned int NegTokenTargSize = 0;
unsigned int NegTokenSize = 0;
unsigned int rspTokenSize = 0;
unsigned int micTokenSize = 0;
unsigned int dataLen = 0;
unsigned char *t;
unsigned char *ptr;
if (outbuf == GSS_C_NO_BUFFER)
return (GSS_S_DEFECTIVE_TOKEN);
outbuf->length = 0;
outbuf->value = NULL;
dataLen = 5;
if (sendtoken == INIT_TOKEN_SEND) {
int mechlistTokenSize;
mechlistTokenSize = 3 + mech_wanted->length +
gssint_der_length_size(mech_wanted->length);
dataLen += mechlistTokenSize;
}
if (data != NULL && data->length > 0) {
rspTokenSize = 1 + gssint_der_length_size(data->length) +
data->length;
dataLen += rspTokenSize;
dataLen += 1 + gssint_der_length_size(rspTokenSize);
}
if (mechListMIC != NULL) {
micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
mechListMIC->length;
dataLen += micTokenSize;
dataLen += 1 + gssint_der_length_size(micTokenSize);
}
NegTokenTargSize = dataLen;
dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
NegTokenSize = dataLen;
dataLen += 1 + gssint_der_length_size(NegTokenSize);
tlen = dataLen;
t = (unsigned char *) malloc(tlen);
if (t == NULL) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
ptr = t;
*ptr++ = CONTEXT | 0x01;
if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
*ptr++ = SEQUENCE;
if (gssint_put_der_length(NegTokenTargSize, &ptr,
tlen - (int)(ptr-t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
*ptr++ = CONTEXT;
if (gssint_put_der_length(3, &ptr,
tlen - (int)(ptr-t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
if (sendtoken == INIT_TOKEN_SEND) {
*ptr++ = CONTEXT | 0x01;
if (gssint_put_der_length(mech_wanted->length + 2,
&ptr,
tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
if (put_mech_oid(&ptr, mech_wanted,
tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
}
if (data != NULL && data->length > 0) {
*ptr++ = CONTEXT | 0x02;
if (gssint_put_der_length(rspTokenSize, &ptr,
tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
if (put_input_token(&ptr, data,
tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
}
if (mechListMIC != NULL) {
*ptr++ = CONTEXT | 0x03;
if (gssint_put_der_length(micTokenSize, &ptr,
tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
if (put_input_token(&ptr, mechListMIC,
tlen - (int)(ptr - t)) < 0) {
ret = GSS_S_DEFECTIVE_TOKEN;
goto errout;
}
}
ret = GSS_S_COMPLETE;
errout:
if (ret != GSS_S_COMPLETE) {
if (t)
free(t);
} else {
outbuf->length = ptr - t;
outbuf->value = (void *) t;
}
return (ret);
}
static int
g_token_size(gss_OID_const mech, unsigned int body_size)
{
int hdrsize;
hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
return (hdrsize + body_size);
}
static int
g_make_token_header(gss_OID_const mech,
unsigned int body_size,
unsigned char **buf,
unsigned int totallen)
{
int ret = 0;
unsigned int hdrsize;
unsigned char *p = *buf;
hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
*(*buf)++ = HEADER_ID;
if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
return (ret);
*(*buf)++ = MECH_OID;
if ((ret = gssint_put_der_length(mech->length, buf,
totallen - (int)(p - *buf))))
return (ret);
TWRITE_STR(*buf, mech->elements, mech->length);
return (0);
}
static int
g_get_tag_and_length(unsigned char **buf, int tag,
unsigned int buflen, unsigned int *outlen)
{
unsigned char *ptr = *buf;
int ret = -1;
unsigned int encoded_len;
unsigned int tmplen = 0;
*outlen = 0;
if (buflen > 1 && *ptr == tag) {
ptr++;
tmplen = gssint_get_der_length(&ptr, buflen - 1,
&encoded_len);
if (tmplen < 0) {
ret = -1;
} else if (tmplen > buflen - (ptr - *buf)) {
ret = -1;
} else
ret = 0;
}
*outlen = tmplen;
*buf = ptr;
return (ret);
}
static int
g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
{
unsigned char *buf = *buf_in;
unsigned char *endptr = buf + cur_size;
unsigned int seqsize;
int ret = 0;
unsigned int bytes;
if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
return (G_BAD_TOK_HEADER);
cur_size = seqsize;
if (*buf++ == SEQUENCE) {
if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
return (G_BAD_TOK_HEADER);
if (buf + seqsize > endptr)
return (G_BAD_TOK_HEADER);
} else {
return (G_BAD_TOK_HEADER);
}
cur_size = seqsize;
if (*buf++ == CONTEXT) {
if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
return (G_BAD_TOK_HEADER);
if (buf + bytes > endptr)
return (G_BAD_TOK_HEADER);
} else {
return (G_BAD_TOK_HEADER);
}
*buf_in = buf;
return (ret);
}
static int
g_verify_token_header(gss_OID_const mech,
unsigned int *body_size,
unsigned char **buf_in,
int tok_type,
unsigned int toksize)
{
unsigned char *buf = *buf_in;
int seqsize;
gss_OID_desc toid;
int ret = 0;
unsigned int bytes;
if (toksize-- < 1)
return (G_BAD_TOK_HEADER);
if (*buf++ != HEADER_ID)
return (G_BAD_TOK_HEADER);
if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
return (G_BAD_TOK_HEADER);
if ((seqsize + bytes) != toksize)
return (G_BAD_TOK_HEADER);
if (toksize-- < 1)
return (G_BAD_TOK_HEADER);
if (*buf++ != MECH_OID)
return (G_BAD_TOK_HEADER);
if (toksize-- < 1)
return (G_BAD_TOK_HEADER);
toid.length = *buf++;
if (toksize < toid.length)
return (G_BAD_TOK_HEADER);
else
toksize -= toid.length;
toid.elements = buf;
buf += toid.length;
if (!g_OID_equal(&toid, mech))
ret = G_WRONG_MECH;
if (toksize < 2)
return (G_BAD_TOK_HEADER);
else
toksize -= 2;
if (!ret) {
*buf_in = buf;
*body_size = toksize;
}
return (ret);
}
static int
is_kerb_mech(gss_OID oid)
{
int answer = 0;
OM_uint32 minor;
extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
(void) gss_test_oid_set_member(&minor,
oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
return (answer);
}