X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/23987cb804fd95e78ffefe5fb614b621e8395a62..fda78dbf3da093595023d208c826c7540ccefcaa:/openssh/gss-genr.c diff --git a/openssh/gss-genr.c b/openssh/gss-genr.c index e91dc39..4a7bd47 100644 --- a/openssh/gss-genr.c +++ b/openssh/gss-genr.c @@ -1,5 +1,8 @@ +/* $OpenBSD: gss-genr.c,v 1.17 2006/08/29 12:02:30 dtucker Exp $ */ + /* - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. * + * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -25,261 +28,262 @@ #ifdef GSSAPI -#include "ssh.h" -#include "ssh2.h" +#include +#include + +#include +#include +#include + #include "xmalloc.h" #include "buffer.h" -#include "bufaux.h" -#include "packet.h" -#include "compat.h" -#include -#include "cipher.h" -#include "kex.h" #include "log.h" -#include "compat.h" -#include "monitor_wrap.h" #include "canohost.h" - -#include +#include "ssh2.h" +#include "cipher.h" +#include "key.h" +#include "kex.h" +#include #include "ssh-gss.h" +extern u_char *session_id2; +extern u_int session_id2_len; + typedef struct { char *encoded; gss_OID oid; } ssh_gss_kex_mapping; - -static ssh_gss_kex_mapping *gss_enc2oid; -/* Return a list of the gss-group1-sha1-x mechanisms supported by this - * program. - * - * On the client side, we don't need to worry about whether we 'know' - * about the mechanism or not - we assume that any mechanism that we've been - * linked against is suitable for inclusion. +/* + * XXX - It would be nice to find a more elegant way of handling the + * XXX passing of the key exchange context to the userauth routines + */ + +Gssctxt *gss_kex_context = NULL; + +static ssh_gss_kex_mapping *gss_enc2oid = NULL; + +int +ssh_gssapi_oid_table_ok() { + return (gss_enc2oid != NULL); +} + +/* + * Return a list of the gss-group1-sha1 mechanisms supported by this program * - * XXX - We might want to make this configurable in the future, so as to - * XXX - allow the user control over which mechanisms to use. + * We test mechanisms to ensure that we can use them, to avoid starting + * a key exchange with a bad mechanism */ - -char * -ssh_gssapi_client_mechanisms(char *host) { - gss_OID_set supported; - OM_uint32 min_status; - Buffer buf; - int i = 0; - char *mechs; - char *encoded; - int enclen; - char digest[EVP_MAX_MD_SIZE]; - char deroid[2]; - const EVP_MD *evp_md = EVP_md5(); - EVP_MD_CTX md; - int oidpos=0; - - if (datafellows & SSH_OLD_GSSAPI) return NULL; - - gss_indicate_mechs(&min_status,&supported); - if (datafellows & SSH_BUG_GSSAPI_BER) { - gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping) - *((supported->count*2)+1)); - } else { - gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping) - *(supported->count+1)); + +char * +ssh_gssapi_client_mechanisms(const char *host) { + gss_OID_set gss_supported; + OM_uint32 min_status; + + gss_indicate_mechs(&min_status, &gss_supported); + + return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, + host)); +} + +char * +ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, + const char *data) { + Buffer buf; + size_t i; + int oidpos, enclen; + char *mechs, *encoded; + u_char digest[EVP_MAX_MD_SIZE]; + char deroid[2]; + const EVP_MD *evp_md = EVP_md5(); + EVP_MD_CTX md; + + if (gss_enc2oid != NULL) { + for (i = 0; gss_enc2oid[i].encoded != NULL; i++) + xfree(gss_enc2oid[i].encoded); + xfree(gss_enc2oid); } - + + gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * + (gss_supported->count + 1)); + buffer_init(&buf); + oidpos = 0; + for (i = 0; i < gss_supported->count; i++) { + if (gss_supported->elements[i].length < 128 && + (*check)(NULL, &(gss_supported->elements[i]), data)) { - for (i=0;icount;i++) { - - gss_enc2oid[oidpos].encoded=NULL; - - if (supported->elements[i].length<128 && - ssh_gssapi_check_mechanism(&(supported->elements[i]),host)) { - - /* Earlier versions of this code interpreted the - * spec incorrectly with regard to OID encoding. They - * also mis-encoded the krb5 OID. The following - * _temporary_ code interfaces with these broken - * servers */ - - if (datafellows & SSH_BUG_GSSAPI_BER) { - char *bodge=NULL; - gss_OID_desc krb5oid={9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}; - gss_OID_desc gsioid={9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"}; - - if (supported->elements[i].length==krb5oid.length && - memcmp(supported->elements[i].elements, - krb5oid.elements, krb5oid.length)==0) { - bodge="Se3H81ismmOC3OE+FwYCiQ=="; - } - - if (supported->elements[i].length==gsioid.length && - memcmp(supported->elements[i].elements, - gsioid.elements, gsioid.length)==0) { - bodge="N3+k7/4wGxHyuP8Yxi4RhA=="; - } - - if (bodge) { - if (oidpos!=0) { - buffer_put_char(&buf,','); - } - - buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1); - buffer_append(&buf, bodge, strlen(bodge)); - - gss_enc2oid[oidpos].oid=&(supported->elements[i]); - gss_enc2oid[oidpos].encoded=bodge; - - oidpos++; - } - } - - /* Add the required DER encoding octets and MD5 hash */ - deroid[0]=0x06; /* Object Identifier */ - deroid[1]=supported->elements[i].length; + deroid[0] = SSH_GSS_OIDTYPE; + deroid[1] = gss_supported->elements[i].length; EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md,deroid,2); + EVP_DigestUpdate(&md, deroid, 2); EVP_DigestUpdate(&md, - supported->elements[i].elements, - supported->elements[i].length); + gss_supported->elements[i].elements, + gss_supported->elements[i].length); EVP_DigestFinal(&md, digest, NULL); - - /* Base64 encode it */ - encoded=xmalloc(EVP_MD_size(evp_md)*2); - enclen=__b64_ntop(digest, EVP_MD_size(evp_md), - encoded,EVP_MD_size(evp_md)*2); - if (oidpos!=0) { - buffer_put_char(&buf,','); - } - buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1); - buffer_append(&buf, encoded, enclen); - debug("Mechanism encoded as %s",encoded); + encoded = xmalloc(EVP_MD_size(evp_md) * 2); + enclen = __b64_ntop(digest, EVP_MD_size(evp_md), + encoded, EVP_MD_size(evp_md) * 2); + + if (oidpos != 0) + buffer_put_char(&buf, ','); + + buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, + sizeof(KEX_GSS_GEX_SHA1_ID) - 1); + buffer_append(&buf, encoded, enclen); + buffer_put_char(&buf, ','); + buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, + sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); + buffer_append(&buf, encoded, enclen); + buffer_put_char(&buf, ','); + buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, + sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); + buffer_append(&buf, encoded, enclen); - gss_enc2oid[oidpos].oid=&(supported->elements[i]); - gss_enc2oid[oidpos].encoded=encoded; + gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); + gss_enc2oid[oidpos].encoded = encoded; oidpos++; } } - gss_enc2oid[oidpos].oid=NULL; - gss_enc2oid[oidpos].encoded=NULL; - - buffer_put_char(&buf,'\0'); - - mechs=xmalloc(buffer_len(&buf)); - buffer_get(&buf,mechs,buffer_len(&buf)); + gss_enc2oid[oidpos].oid = NULL; + gss_enc2oid[oidpos].encoded = NULL; + + buffer_put_char(&buf, '\0'); + + mechs = xmalloc(buffer_len(&buf)); + buffer_get(&buf, mechs, buffer_len(&buf)); buffer_free(&buf); - if (strlen(mechs)==0) - return(NULL); - else - return(mechs); -} -gss_OID -ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name) { - int i=0; - - if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) { - return(NULL); + if (strlen(mechs) == 0) { + xfree(mechs); + mechs = NULL; } - name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the ID string */ - - while (gss_enc2oid[i].encoded!=NULL && - strcmp(name,gss_enc2oid[i].encoded)!=0) { - i++; - } + return (mechs); +} + +gss_OID +ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { + int i = 0; - if (gss_enc2oid[i].oid!=NULL) { - ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid); + switch (kex_type) { + case KEX_GSS_GRP1_SHA1: + name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; + break; + case KEX_GSS_GRP14_SHA1: + name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; + break; + case KEX_GSS_GEX_SHA1: + name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; + break; + default: + return GSS_C_NO_OID; } + while (gss_enc2oid[i].encoded != NULL && + strcmp(name, gss_enc2oid[i].encoded) != 0) + i++; + + if (gss_enc2oid[i].oid != NULL && ctx != NULL) + ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); + return gss_enc2oid[i].oid; } /* Check that the OID in a data stream matches that in the context */ -int ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) { - - return (ctx!=NULL && ctx->oid != GSS_C_NO_OID && - ctx->oid->length == len && - memcmp(ctx->oid->elements,data,len)==0); +int +ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +{ + return (ctx != NULL && ctx->oid != GSS_C_NO_OID && + ctx->oid->length == len && + memcmp(ctx->oid->elements, data, len) == 0); } - + /* Set the contexts OID from a data stream */ -void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) { - if (ctx->oid != GSS_C_NO_OID) { - xfree(ctx->oid->elements); - xfree(ctx->oid); - } - ctx->oid=xmalloc(sizeof(gss_OID_desc)); - ctx->oid->length=len; - ctx->oid->elements=xmalloc(len); - memcpy(ctx->oid->elements,data,len); +void +ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) +{ + if (ctx->oid != GSS_C_NO_OID) { + xfree(ctx->oid->elements); + xfree(ctx->oid); + } + ctx->oid = xmalloc(sizeof(gss_OID_desc)); + ctx->oid->length = len; + ctx->oid->elements = xmalloc(len); + memcpy(ctx->oid->elements, data, len); } /* Set the contexts OID */ -void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) { - ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length); +void +ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) +{ + ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); } /* All this effort to report an error ... */ - void -ssh_gssapi_error(Gssctxt *ctxt) { - - debug(ssh_gssapi_last_error(ctxt,NULL,NULL)); +ssh_gssapi_error(Gssctxt *ctxt) +{ + char *s; + + s = ssh_gssapi_last_error(ctxt, NULL, NULL); + debug("%s", s); + xfree(s); } char * -ssh_gssapi_last_error(Gssctxt *ctxt, - OM_uint32 *major_status, OM_uint32 *minor_status) { +ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, + OM_uint32 *minor_status) +{ OM_uint32 lmin; - gss_buffer_desc msg; - OM_uint32 ctx; - Buffer b; - char *ret; - - buffer_init(&b); - - if (major_status!=NULL) *major_status=ctxt->major; - if (minor_status!=NULL) *minor_status=ctxt->minor; - - ctx = 0; + gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; + OM_uint32 ctx; + Buffer b; + char *ret; + + buffer_init(&b); + + if (major_status != NULL) + *major_status = ctxt->major; + if (minor_status != NULL) + *minor_status = ctxt->minor; + + ctx = 0; /* The GSSAPI error */ - do { - gss_display_status(&lmin, ctxt->major, - GSS_C_GSS_CODE, ctxt->oid, - &ctx, &msg); - - buffer_append(&b,msg.value,msg.length); - buffer_put_char(&b,'\n'); - - gss_release_buffer(&lmin, &msg); - } while (ctx!=0); - - /* The mechanism specific error */ - do { - gss_display_status(&lmin, ctxt->minor, - GSS_C_MECH_CODE, ctxt->oid, - &ctx, &msg); - - buffer_append(&b,msg.value,msg.length); - buffer_put_char(&b,'\n'); - - gss_release_buffer(&lmin, &msg); - } while (ctx!=0); - - buffer_put_char(&b,'\0'); - ret=xmalloc(buffer_len(&b)); - buffer_get(&b,ret,buffer_len(&b)); - buffer_free(&b); - return(ret); + do { + gss_display_status(&lmin, ctxt->major, + GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); + + buffer_append(&b, msg.value, msg.length); + buffer_put_char(&b, '\n'); + + gss_release_buffer(&lmin, &msg); + } while (ctx != 0); + + /* The mechanism specific error */ + do { + gss_display_status(&lmin, ctxt->minor, + GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); + + buffer_append(&b, msg.value, msg.length); + buffer_put_char(&b, '\n'); + + gss_release_buffer(&lmin, &msg); + } while (ctx != 0); + + buffer_put_char(&b, '\0'); + ret = xmalloc(buffer_len(&b)); + buffer_get(&b, ret, buffer_len(&b)); + buffer_free(&b); + return (ret); } -/* Initialise our GSSAPI context. We use this opaque structure to contain all +/* + * Initialise our GSSAPI context. We use this opaque structure to contain all * of the data which both the client and server need to persist across * {accept,init}_sec_context calls, so that when we do it from the userauth * stuff life is a little easier @@ -287,15 +291,13 @@ ssh_gssapi_last_error(Gssctxt *ctxt, void ssh_gssapi_build_ctx(Gssctxt **ctx) { - *ctx=xmalloc(sizeof (Gssctxt)); - (*ctx)->major=0; - (*ctx)->minor=0; - (*ctx)->context=GSS_C_NO_CONTEXT; - (*ctx)->name=GSS_C_NO_NAME; - (*ctx)->oid=GSS_C_NO_OID; - (*ctx)->creds=GSS_C_NO_CREDENTIAL; - (*ctx)->client=GSS_C_NO_NAME; - (*ctx)->client_creds=GSS_C_NO_CREDENTIAL; + *ctx = xcalloc(1, sizeof (Gssctxt)); + (*ctx)->context = GSS_C_NO_CONTEXT; + (*ctx)->name = GSS_C_NO_NAME; + (*ctx)->oid = GSS_C_NO_OID; + (*ctx)->creds = GSS_C_NO_CREDENTIAL; + (*ctx)->client = GSS_C_NO_NAME; + (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; } /* Delete our context, providing it has been built correctly */ @@ -305,186 +307,158 @@ ssh_gssapi_delete_ctx(Gssctxt **ctx) #if !defined(MECHGLUE) OM_uint32 ms; #endif - - /* Return if there's no context */ - if ((*ctx)==NULL) + + if ((*ctx) == NULL) return; - #if !defined(MECHGLUE) /* mechglue has some memory management issues */ - if ((*ctx)->context != GSS_C_NO_CONTEXT) - gss_delete_sec_context(&ms,&(*ctx)->context,GSS_C_NO_BUFFER); + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); if ((*ctx)->name != GSS_C_NO_NAME) - gss_release_name(&ms,&(*ctx)->name); + gss_release_name(&ms, &(*ctx)->name); if ((*ctx)->oid != GSS_C_NO_OID) { xfree((*ctx)->oid->elements); xfree((*ctx)->oid); (*ctx)->oid = GSS_C_NO_OID; } if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) - gss_release_cred(&ms,&(*ctx)->creds); + gss_release_cred(&ms, &(*ctx)->creds); if ((*ctx)->client != GSS_C_NO_NAME) - gss_release_name(&ms,&(*ctx)->client); + gss_release_name(&ms, &(*ctx)->client); if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) - gss_release_cred(&ms,&(*ctx)->client_creds); + gss_release_cred(&ms, &(*ctx)->client_creds); #endif - + xfree(*ctx); - *ctx=NULL; + *ctx = NULL; } -/* Wrapper to init_sec_context +/* + * Wrapper to init_sec_context * Requires that the context contains: * oid - * server name (from ssh_gssapi_import_name) + * server name (from ssh_gssapi_import_name) */ -OM_uint32 +OM_uint32 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, - gss_buffer_desc* send_tok, OM_uint32 *flags) + gss_buffer_desc* send_tok, OM_uint32 *flags) { int deleg_flag = 0; - + if (deleg_creds) { - deleg_flag=GSS_C_DELEG_FLAG; + deleg_flag = GSS_C_DELEG_FLAG; debug("Delegating credentials"); } - - ctx->major=gss_init_sec_context(&ctx->minor, - GSS_C_NO_CREDENTIAL, /* def. cred */ - &ctx->context, - ctx->name, - ctx->oid, - GSS_C_MUTUAL_FLAG | - GSS_C_INTEG_FLAG | - deleg_flag, - 0, /* default lifetime */ - NULL, /* no channel bindings */ - recv_tok, - NULL, - send_tok, - flags, - NULL); - if (GSS_ERROR(ctx->major)) { - ssh_gssapi_error(ctx); - } - return(ctx->major); + + ctx->major = gss_init_sec_context(&ctx->minor, + GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx); + + return (ctx->major); } /* Create a service name for the given host */ OM_uint32 -ssh_gssapi_import_name(Gssctxt *ctx, const char *host) { +ssh_gssapi_import_name(Gssctxt *ctx, const char *host) +{ gss_buffer_desc gssbuf; char *xhost; - + char *val; + /* Make a copy of the host name, in case it was returned by a - * previous call to gethostbyname(). */ + * previous call to gethostbyname(). */ xhost = xstrdup(host); /* Make sure we have the FQDN. Some GSSAPI implementations don't do - * this for us themselves */ + * this for us themselves */ resolve_localhost(&xhost); - gssbuf.length = sizeof("host@")+strlen(xhost); - - gssbuf.value = xmalloc(gssbuf.length); - if (gssbuf.value == NULL) { - xfree(xhost); - return(-1); - } - snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost); - if ((ctx->major=gss_import_name(&ctx->minor, - &gssbuf, - GSS_C_NT_HOSTBASED_SERVICE, - &ctx->name))) { + xasprintf(&val, "host@%s", xhost); + gssbuf.value = val; + gssbuf.length = strlen(gssbuf.value); + + if ((ctx->major = gss_import_name(&ctx->minor, + &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) ssh_gssapi_error(ctx); - } - + xfree(xhost); xfree(gssbuf.value); - return(ctx->major); + return (ctx->major); } -/* Acquire credentials for a server running on the current host. - * Requires that the context structure contains a valid OID - */ - -/* Returns a GSSAPI error code */ OM_uint32 -ssh_gssapi_acquire_cred(Gssctxt *ctx) { - OM_uint32 status; - char lname[MAXHOSTNAMELEN]; - gss_OID_set oidset; - - gss_create_empty_oid_set(&status,&oidset); - gss_add_oid_set_member(&status,ctx->oid,&oidset); - - if (gethostname(lname, MAXHOSTNAMELEN)) { - return(-1); - } +ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) +{ + if (ctx == NULL) + return -1; - if (GSS_ERROR(ssh_gssapi_import_name(ctx,lname))) { - return(ctx->major); - } - - if ((ctx->major=gss_acquire_cred(&ctx->minor, - ctx->name, - 0, - oidset, - GSS_C_ACCEPT, - &ctx->creds, - NULL, - NULL))) { + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) ssh_gssapi_error(ctx); - } - - gss_release_oid_set(&status, &oidset); - return(ctx->major); + + return (ctx->major); } +/* Priviledged when used by server */ OM_uint32 -ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) { - - /* ssh1 needs to exchange the hash of the keys */ - /* will us this hash to return it */ - if (!compat20) { - if ((ctx->major=gss_wrap(&ctx->minor,ctx->context, - 0, - GSS_C_QOP_DEFAULT, - buffer, - NULL, - hash))) - ssh_gssapi_error(ctx); - } - else +ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +{ + if (ctx == NULL) + return -1; - if ((ctx->major=gss_get_mic(&ctx->minor,ctx->context, - GSS_C_QOP_DEFAULT, buffer, hash))) { - ssh_gssapi_error(ctx); - } - - return(ctx->major); + ctx->major = gss_verify_mic(&ctx->minor, ctx->context, + gssbuf, gssmic, NULL); + + return (ctx->major); } -OM_uint32 -ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid) { - if (*ctx) ssh_gssapi_delete_ctx(ctx); - ssh_gssapi_build_ctx(ctx); - ssh_gssapi_set_oid(*ctx,oid); - return(ssh_gssapi_acquire_cred(*ctx)); +void +ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, + const char *context) +{ + buffer_init(b); + buffer_put_string(b, session_id2, session_id2_len); + buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(b, user); + buffer_put_cstring(b, service); + buffer_put_cstring(b, context); } int -ssh_gssapi_check_mechanism(gss_OID oid, char *host) { - Gssctxt * ctx = NULL; - gss_buffer_desc token; - OM_uint32 major,minor; - - ssh_gssapi_build_ctx(&ctx); - ssh_gssapi_set_oid(ctx,oid); - ssh_gssapi_import_name(ctx,host); - major=ssh_gssapi_init_ctx(ctx,0, GSS_C_NO_BUFFER, &token, NULL); - gss_release_buffer(&minor,&token); - ssh_gssapi_delete_ctx(&ctx); - return(!GSS_ERROR(major)); +ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) +{ + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; + Gssctxt *intctx = NULL; + + if (ctx == NULL) + ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && + (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) + return 0; /* false */ + + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); + gss_release_buffer(&minor, &token); + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor, &(*ctx)->context, + GSS_C_NO_BUFFER); + } + + if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); } #endif /* GSSAPI */