diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/ssh/libssh/common/ssh-gss.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/ssh/libssh/common/ssh-gss.c')
-rw-r--r-- | usr/src/cmd/ssh/libssh/common/ssh-gss.c | 767 |
1 files changed, 767 insertions, 0 deletions
diff --git a/usr/src/cmd/ssh/libssh/common/ssh-gss.c b/usr/src/cmd/ssh/libssh/common/ssh-gss.c new file mode 100644 index 0000000000..d03aad381d --- /dev/null +++ b/usr/src/cmd/ssh/libssh/common/ssh-gss.c @@ -0,0 +1,767 @@ +/* + * Copyright (c) 2001-2003 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include "includes.h" + +#ifdef GSSAPI + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ssh.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "packet.h" +#include "compat.h" +#include <openssl/evp.h> +#include "cipher.h" +#include "kex.h" +#include "log.h" +#include "compat.h" +#include "xlist.h" +#include "monitor_wrap.h" + +#include <netdb.h> + +#include "ssh-gss.h" + +#ifdef HAVE_GSS_OID_TO_MECH +#include <gssapi/gssapi_ext.h> +#endif /* HAVE_GSS_OID_TO_MECH */ + +typedef struct { + char *encoded; + gss_OID oid; +} ssh_gss_kex_mapping; + +static ssh_gss_kex_mapping **gss_enc2oid = NULL; + +static void ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name); +static char *ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, + const char *old_kexalgs); + +/* + * Populate gss_enc2oid table and return list of kexnames. + * + * If called with both mechs == GSS_C_NULL_OID_SET and kexname_list == NULL + * then cached gss_enc2oid table is cleaned up. + */ +void +ssh_gssapi_mech_oids_to_kexnames(const gss_OID_set mechs, char **kexname_list) +{ + ssh_gss_kex_mapping **new_gss_enc2oid, **p; + Buffer buf; + char *enc_name; + int i; + + if (kexname_list != NULL) + *kexname_list = NULL; /* default to failed */ + + if (mechs != GSS_C_NULL_OID_SET || kexname_list == NULL) { + /* Cleanup gss_enc2oid table */ + for (p = gss_enc2oid ; p != NULL && *p != NULL ; p++) { + if ((*p)->encoded) + xfree((*p)->encoded); + ssh_gssapi_release_oid(&(*p)->oid); + xfree(*p); + } + if (gss_enc2oid) + xfree(gss_enc2oid); + } + + if (mechs == GSS_C_NULL_OID_SET && kexname_list == NULL) + return; /* nothing left to do */ + + if (mechs) { + gss_OID mech; + /* Populate gss_enc2oid table */ + new_gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping *) * + (mechs->count + 1)); + memset(new_gss_enc2oid, 0, + sizeof(ssh_gss_kex_mapping *) * (mechs->count + 1)); + + for (i = 0 ; i < mechs->count ; i++) { + mech = &mechs->elements[i]; + ssh_gssapi_encode_oid_for_kex((const gss_OID)mech, + &enc_name); + + if (!enc_name) + continue; + + new_gss_enc2oid[i] = + xmalloc(sizeof(ssh_gss_kex_mapping)); + (new_gss_enc2oid[i])->encoded = enc_name; + (new_gss_enc2oid[i])->oid = + ssh_gssapi_dup_oid(&mechs->elements[i]); + } + + /* Do this last to avoid run-ins with fatal_cleanups */ + gss_enc2oid = new_gss_enc2oid; + } + + if (!kexname_list) + return; /* nothing left to do */ + + /* Make kex name list */ + buffer_init(&buf); + for (p = gss_enc2oid ; p && *p ; p++) { + buffer_put_char(&buf,','); + buffer_append(&buf, (*p)->encoded, strlen((*p)->encoded)); + } + + if (buffer_len(&buf) == 0) { + buffer_free(&buf); + return; + } + + buffer_consume(&buf, 1); /* consume leading ',' */ + buffer_put_char(&buf,'\0'); + + *kexname_list = xstrdup(buffer_ptr(&buf)); + buffer_free(&buf); +} + +void +ssh_gssapi_mech_oid_to_kexname(const gss_OID mech, char **kexname) +{ + ssh_gss_kex_mapping **p; + + if (mech == GSS_C_NULL_OID || !kexname) + return; + + *kexname = NULL; /* default to not found */ + if (gss_enc2oid) { + for (p = gss_enc2oid ; p && *p ; p++) { + if (mech->length == (*p)->oid->length && + memcmp(mech->elements, (*p)->oid->elements, + mech->length) == 0) + *kexname = xstrdup((*p)->encoded); + } + } + + if (*kexname) + return; /* found */ + + ssh_gssapi_encode_oid_for_kex(mech, kexname); +} + +void +ssh_gssapi_oid_of_kexname(const char *kexname, gss_OID *mech) +{ + ssh_gss_kex_mapping **p; + + if (!mech || !kexname || !*kexname) + return; + + *mech = GSS_C_NULL_OID; /* default to not found */ + + if (!gss_enc2oid) + return; + + for (p = gss_enc2oid ; p && *p ; p++) { + if (strcmp(kexname, (*p)->encoded) == 0) { + *mech = (*p)->oid; + return; + } + } +} + +static +void +ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name) +{ + Buffer buf; + OM_uint32 oidlen; + u_int enclen; + const EVP_MD *evp_md = EVP_md5(); + EVP_MD_CTX md; + u_char digest[EVP_MAX_MD_SIZE]; + char *encoded; + + if (oid == GSS_C_NULL_OID || !enc_name) + return; + + *enc_name = NULL; + + oidlen = oid->length; + + /* No GSS mechs have OIDs as long as 128 -- simplify DER encoding */ + if (oidlen > 128) + return; /* fail gracefully */ + + /* + * NOTE: If we need to support SSH_BUG_GSSAPI_BER this is where + * we'd do it. + * + * That means using "Se3H81ismmOC3OE+FwYCiQ==" for the Kerberos + * V mech and "N3+k7/4wGxHyuP8Yxi4RhA==" for the GSI mech. Ick. + */ + + buffer_init(&buf); + + /* UNIVERSAL class tag for OBJECT IDENTIFIER */ + buffer_put_char(&buf, 0x06); + buffer_put_char(&buf, oidlen); /* one octet DER length -- see above */ + + /* OID elements */ + buffer_append(&buf, oid->elements, oidlen); + + /* Make digest */ + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&buf), buffer_len(&buf)); + EVP_DigestFinal(&md, digest, NULL); + buffer_free(&buf); + + /* Base 64 encoding */ + encoded=xmalloc(EVP_MD_size(evp_md)*2); + enclen=__b64_ntop(digest, EVP_MD_size(evp_md), + encoded,EVP_MD_size(evp_md)*2); + buffer_init(&buf); + buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1); + buffer_append(&buf, encoded, enclen); + buffer_put_char(&buf, '\0'); + + debug2("GSS-API Mechanism encoded as %s",encoded); + + *enc_name = xstrdup(buffer_ptr(&buf)); + buffer_free(&buf); +} + +static +char * +ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, const char *old_kexalgs) +{ + char *gss_kexalgs, *new_kexalgs; + int len; + + if (mechs == GSS_C_NULL_OID_SET) + return (xstrdup(old_kexalgs)); /* never null */ + + ssh_gssapi_mech_oids_to_kexnames(mechs, &gss_kexalgs); + + if (gss_kexalgs == NULL || *gss_kexalgs == '\0') + return (xstrdup(old_kexalgs)); /* never null */ + + if (old_kexalgs == NULL || *old_kexalgs == '\0') + return (gss_kexalgs); + + len = strlen(old_kexalgs) + strlen(gss_kexalgs) + 2; + new_kexalgs = xmalloc(len); + (void) snprintf(new_kexalgs, len, "%s,%s", gss_kexalgs, old_kexalgs); + + return (new_kexalgs); +} + +void +ssh_gssapi_modify_kex(Kex *kex, gss_OID_set mechs, char **proposal) +{ + char *kexalgs, *orig_kexalgs, *p; + char **hostalg, *orig_hostalgs, *new_hostalgs; + char **hostalgs; + gss_OID_set dup_mechs; + OM_uint32 maj, min; + int i; + + if (kex == NULL || proposal == NULL || + (orig_kexalgs = proposal[PROPOSAL_KEX_ALGS]) == NULL) { + fatal("INTERNAL ERROR (%s)", __func__); + } + + orig_hostalgs = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; + + if (kex->mechs == GSS_C_NULL_OID_SET && mechs == GSS_C_NULL_OID_SET) + return; /* didn't offer GSS last time, not offering now */ + + if (kex->mechs == GSS_C_NULL_OID_SET || mechs == GSS_C_NULL_OID_SET) + goto mod_offer; /* didn't offer last time or not offering now */ + + /* Check if mechs is congruent to kex->mechs (last offered) */ + if (kex->mechs->count == mechs->count) { + int present, matches = 0; + + for ( i = 0 ; i < mechs->count ; i++ ) { + maj = gss_test_oid_set_member(&min, + &kex->mechs->elements[i], mechs, + &present); + + if (GSS_ERROR(maj)) { + mechs = GSS_C_NULL_OID_SET; + break; + } + + matches += (present) ? 1 : 0; + } + + if (matches == kex->mechs->count) + return; /* no change in offer from last time */ + } + +mod_offer: + /* + * Remove previously offered mechs from PROPOSAL_KEX_ALGS proposal + * + * ASSUMPTION: GSS-API kex algs always go in front, so removing + * them is a matter of skipping them. + */ + p = kexalgs = orig_kexalgs = proposal[PROPOSAL_KEX_ALGS]; + while (p != NULL && *p != '\0' && + strncmp(p, KEX_GSS_SHA1, strlen(KEX_GSS_SHA1)) == 0) { + + if ((p = strchr(p, ',')) == NULL) + break; + p++; + kexalgs = p; + + } + kexalgs = proposal[PROPOSAL_KEX_ALGS] = xstrdup(kexalgs); + xfree(orig_kexalgs); + + (void) gss_release_oid_set(&min, &kex->mechs); /* ok if !kex->mechs */ + + /* Not offering GSS kexalgs now -> all done */ + if (mechs == GSS_C_NULL_OID_SET) + return; + + /* Remember mechs we're offering */ + maj = gss_create_empty_oid_set(&min, &dup_mechs); + if (GSS_ERROR(maj)) + return; + for ( i = 0 ; i < mechs->count ; i++ ) { + maj = gss_add_oid_set_member(&min, &mechs->elements[i], + &dup_mechs); + + if (GSS_ERROR(maj)) { + (void) gss_release_oid_set(&min, &dup_mechs); + return; + } + } + + /* Add mechs to kexalgs ... */ + proposal[PROPOSAL_KEX_ALGS] = ssh_gssapi_make_kexalgs_list(mechs, kexalgs); + kex->mechs = dup_mechs; /* remember what we offer now */ + + /* + * ... and add null host key alg, if it wasn't there before, but + * not if we're the server and we have other host key algs to + * offer. + * + * NOTE: Never remove "null" host key alg once added. + */ + if (orig_hostalgs == NULL || *orig_hostalgs == '\0') { + proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xstrdup("null"); + } else if (!kex->server) { + hostalgs = xsplit(orig_hostalgs, ','); + for ( hostalg = hostalgs ; *hostalg != NULL ; hostalg++ ) { + if (strcmp(*hostalg, "null") == 0) { + xfree_split_list(hostalgs); + return; + } + } + xfree_split_list(hostalgs); + + if (kex->mechs != GSS_C_NULL_OID_SET) { + int len; + + len = strlen(orig_hostalgs) + sizeof(",null"); + new_hostalgs = xmalloc(len); + (void) snprintf(new_hostalgs, len, "%s,null", + orig_hostalgs); + proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = new_hostalgs; + } + + xfree(orig_hostalgs); + } +} + +/* + * Yes, we harcode OIDs for some things, for now it's all we can do. + * + * We have to reference particular mechanisms due to lack of generality + * in the GSS-API in several areas: authorization, mapping principal + * names to usernames, "storing" delegated credentials, and discovering + * whether a mechanism is a pseudo-mechanism that negotiates mechanisms. + * + * Even if they were in some header file or if __gss_mech_to_oid() + * and/or __gss_oid_to_mech() were standard we'd still have to hardcode + * the mechanism names, and since the mechanisms have no standard names + * other than their OIDs it's actually worse [less portable] to hardcode + * names than OIDs, so we hardcode OIDs. + * + * SPNEGO is a difficult problem though -- it MUST NOT be used in SSHv2, + * but that's true of all possible pseudo-mechanisms that can perform + * mechanism negotiation, and SPNEGO could have new OIDs in the future. + * Ideally we could query each mechanism for its feature set and then + * ignore any mechanisms that negotiate mechanisms, but, alas, there's + * no interface to do that. + * + * In the future, if the necessary generic GSS interfaces for the issues + * listed above are made available (even if they differ by platform, as + * we can expect authorization interfaces will), then we can stop + * referencing specific mechanism OIDs here. + */ +int +ssh_gssapi_is_spnego(gss_OID oid) +{ + return (oid->length == 6 && + memcmp("\053\006\001\005\005\002", + oid->elements, 6) == 0); +} + +int +ssh_gssapi_is_krb5(gss_OID oid) +{ + return (oid->length == 9 && + memcmp("\x2A\x86\x48\x86\xF7\x12\x01\x02\x02", + oid->elements, 9) == 0); +} + +int +ssh_gssapi_is_dh(gss_OID oid) +{ + return (oid->length == 9 && + memcmp("\053\006\004\001\052\002\032\002\005", + oid->elements, 9) == 0); +} + +int +ssh_gssapi_is_gsi(gss_OID oid) +{ + return (oid->length == 9 && + memcmp("\x2B\x06\x01\x04\x01\x9B\x50\x01\x01", + oid->elements, 9) == 0); +} + +const +char * +ssh_gssapi_oid_to_name(gss_OID oid) +{ +#ifdef HAVE_GSS_OID_TO_MECH + return __gss_oid_to_mech(oid); +#else + if (ssh_gssapi_is_krb5(oid)) + return "Kerberos"; + if (ssh_gssapi_is_gsi(oid)) + return "GSI"; + return "(unknown)"; +#endif /* HAVE_GSS_OID_TO_MECH */ +} + +char * +ssh_gssapi_oid_to_str(gss_OID oid) +{ +#ifdef HAVE_GSS_OID_TO_STR + gss_buffer_desc str_buf; + char *str; + OM_uint32 maj, min; + + maj = gss_oid_to_str(&min, oid, &str_buf); + + if (GSS_ERROR(maj)) + return xstrdup("<gss_oid_to_str() failed>"); + + str = xmalloc(str_buf.length + 1); + memset(str, 0, str_buf.length + 1); + strlcpy(str, str_buf.value, str_buf.length + 1); + (void) gss_release_buffer(&min, &str_buf); + + return str; +#else + return xstrdup("<gss_oid_to_str() unsupported>"); +#endif /* HAVE_GSS_OID_TO_STR */ +} + +/* Check that the OID in a data stream matches that in the context */ +int ssh_gssapi_check_mech_oid(Gssctxt *ctx, void *data, size_t len) { + + return (ctx!=NULL && ctx->desired_mech != GSS_C_NULL_OID && + ctx->desired_mech->length == len && + memcmp(ctx->desired_mech->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->actual_mech != GSS_C_NULL_OID) { + xfree(ctx->actual_mech->elements); + xfree(ctx->actual_mech); + } + ctx->actual_mech=xmalloc(sizeof(gss_OID_desc)); + ctx->actual_mech->length=len; + ctx->actual_mech->elements=xmalloc(len); + memcpy(ctx->actual_mech->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); +} + +/* All this effort to report an error ... */ + +void +ssh_gssapi_error(Gssctxt *ctxt, const char *where) { + if (where) + debug("GSS-API error while %s: %s", where, + ssh_gssapi_last_error(ctxt,NULL,NULL)); + else + debug("GSS-API error: %s", + ssh_gssapi_last_error(ctxt,NULL,NULL)); +} + +char * +ssh_gssapi_last_error(Gssctxt *ctxt, + OM_uint32 *major_status, OM_uint32 *minor_status) { + OM_uint32 lmin, more; + OM_uint32 maj, min; + gss_OID mech = GSS_C_NULL_OID; + gss_buffer_desc msg; + Buffer b; + char *ret; + + buffer_init(&b); + + if (ctxt) { + /* Get status codes from the Gssctxt */ + maj = ctxt->major; + min = ctxt->minor; + /* Output them if desired */ + if (major_status) + *major_status = maj; + if (minor_status) + *minor_status = min; + /* Get mechanism for minor status display */ + mech = (ctxt->actual_mech != GSS_C_NULL_OID) ? + ctxt->actual_mech : ctxt->desired_mech; + } else if (major_status && minor_status) { + maj = *major_status; + min = *major_status; + } else { + maj = GSS_S_COMPLETE; + min = 0; + } + + more = 0; + /* The GSSAPI error */ + do { + gss_display_status(&lmin, maj, + GSS_C_GSS_CODE, GSS_C_NULL_OID, + &more, &msg); + + buffer_append(&b,msg.value,msg.length); + buffer_put_char(&b,'\n'); + gss_release_buffer(&lmin, &msg); + } while (more!=0); + + /* The mechanism specific error */ + do { + /* + * If mech == GSS_C_NULL_OID we may get the default + * mechanism, whatever that is, and that may not be + * useful. + */ + gss_display_status(&lmin, min, + GSS_C_MECH_CODE, mech, + &more, &msg); + + buffer_append(&b,msg.value,msg.length); + buffer_put_char(&b,'\n'); + + gss_release_buffer(&lmin, &msg); + } while (more!=0); + + buffer_put_char(&b,'\0'); + ret=xstrdup(buffer_ptr(&b)); + buffer_free(&b); + + return (ret); +} + +/* 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 + */ +void +ssh_gssapi_build_ctx(Gssctxt **ctx, int client, gss_OID mech) +{ + Gssctxt *newctx; + + ssh_gssapi_delete_ctx(ctx); + + newctx = (Gssctxt*)xmalloc(sizeof (Gssctxt)); + memset(newctx, 0, sizeof(Gssctxt)); + + newctx->local = client; + newctx->desired_mech = ssh_gssapi_dup_oid(mech); + + /* This happens to be redundant given the memset() above */ + newctx->major = GSS_S_COMPLETE; + newctx->context = GSS_C_NO_CONTEXT; + newctx->actual_mech = GSS_C_NULL_OID; + newctx->desired_name = GSS_C_NO_NAME; + newctx->src_name = GSS_C_NO_NAME; + newctx->dst_name = GSS_C_NO_NAME; + newctx->creds = GSS_C_NO_CREDENTIAL; + newctx->deleg_creds = GSS_C_NO_CREDENTIAL; + + *ctx = newctx; +} + +gss_OID +ssh_gssapi_dup_oid(gss_OID oid) +{ + gss_OID new_oid; + + new_oid = xmalloc(sizeof(gss_OID_desc)); + + new_oid->elements = xmalloc(oid->length); + new_oid->length = oid->length; + memcpy(new_oid->elements, oid->elements, oid->length); + + return (new_oid); +} + +gss_OID +ssh_gssapi_make_oid(size_t length, void *elements) +{ + gss_OID_desc oid; + + oid.length = length; + oid.elements = elements; + + return (ssh_gssapi_dup_oid(&oid)); +} + +void +ssh_gssapi_release_oid(gss_OID *oid) +{ + OM_uint32 min; + + if (oid && *oid == GSS_C_NULL_OID) + return; + (void) gss_release_oid(&min, oid); + + if (*oid == GSS_C_NULL_OID) + return; /* libgss did own this gss_OID and released it */ + + xfree((*oid)->elements); + xfree(*oid); + *oid = GSS_C_NULL_OID; +} + +struct gss_name { + gss_OID name_type; + gss_buffer_t external_name; + gss_OID mech_type; + void *mech_name; +}; + +/* Delete our context, providing it has been built correctly */ +void +ssh_gssapi_delete_ctx(Gssctxt **ctx) +{ + OM_uint32 ms; + + if ((*ctx) == NULL) + return; + + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ms,&(*ctx)->context,GSS_C_NO_BUFFER); + /* XXX if ((*ctx)->desired_mech != GSS_C_NULL_OID) + ssh_gssapi_release_oid(&(*ctx)->desired_mech);*/ + if ((*ctx)->actual_mech != GSS_C_NULL_OID) + (void) ssh_gssapi_release_oid(&(*ctx)->actual_mech); + if ((*ctx)->desired_name != GSS_C_NO_NAME) + gss_release_name(&ms,&(*ctx)->desired_name); + /* if ((*ctx)->src_name != GSS_C_NO_NAME) + gss_release_name(&ms,&(*ctx)->src_name); */ + if ((*ctx)->dst_name != GSS_C_NO_NAME) + gss_release_name(&ms,&(*ctx)->dst_name); + if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms,&(*ctx)->creds); + if ((*ctx)->deleg_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms,&(*ctx)->deleg_creds); + + xfree(*ctx); + *ctx=NULL; +} + +/* Create a GSS hostbased service principal name for a given server hostname */ +int +ssh_gssapi_import_name(Gssctxt *ctx, const char *server_host) +{ + gss_buffer_desc name_buf; + int ret; + + /* Build target principal */ + + /* Non-portable but very neat code relying on SUSv3: + name_buf.length = snprintf(NULL, 0, "%s@%S", + SSH_GSS_HOSTBASED_SERVICE, server_host); + */ + + name_buf.length = strlen(SSH_GSS_HOSTBASED_SERVICE) + + strlen(server_host) + 1; /* +1 for '@' */ + name_buf.value = xmalloc(name_buf.length + 1); /* +1 for NUL */ + ret = snprintf(name_buf.value, name_buf.length + 1, "%s@%s", + SSH_GSS_HOSTBASED_SERVICE, server_host); + + debug3("%s: snprintf() returned %d, expected %d", __func__, ret, name_buf.length + 1); + + ctx->major = gss_import_name(&ctx->minor, &name_buf, + GSS_C_NT_HOSTBASED_SERVICE, &ctx->desired_name); + + if (GSS_ERROR(ctx->major)) { + ssh_gssapi_error(ctx, "calling GSS_Import_name()"); + return 0; + } + + xfree(name_buf.value); + + return 1; +} + +OM_uint32 +ssh_gssapi_get_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) { + + ctx->major=gss_get_mic(&ctx->minor,ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash); + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx, "while getting MIC"); + return(ctx->major); +} + +OM_uint32 +ssh_gssapi_verify_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) { + gss_qop_t qop; + + ctx->major=gss_verify_mic(&ctx->minor,ctx->context, buffer, hash, &qop); + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx, "while verifying MIC"); + return(ctx->major); +} +#endif /* GSSAPI */ |