/*
 * 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 2006 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;


	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;

	newctx->default_creds = (*ctx != NULL) ? (*ctx)->default_creds : 0;

	ssh_gssapi_delete_ctx(ctx);

	*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 */