text: /*
   * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  /* GSSAPI SASL plugin
   * Leif Johansson
   * Rob Siemborski (SASL v2 Conversion)
   * $Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $
   */
  /* 
   * Copyright (c) 1998-2003 Carnegie Mellon University.  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.
   *
   * 3. The name "Carnegie Mellon University" must not be used to
   *    endorse or promote products derived from this software without
   *    prior written permission. For permission or any other legal
   *    details, please contact  
   *      Office of Technology Transfer
   *      Carnegie Mellon University
   *      5000 Forbes Avenue
   *      Pittsburgh, PA  15213-3890
   *      (412) 268-4387, fax: (412) 268-7395
   *      tech-transfer@andrew.cmu.edu
   *
   * 4. Redistributions of any form whatsoever must retain the following
   *    acknowledgment:
   *    "This product includes software developed by Computing Services
   *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
   *
   * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
   * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
   * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
   * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
   * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
   * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  #include <config.h>
  
  #ifdef HAVE_GSSAPI_H
  #include <gssapi.h>
  #else
  #include <gssapi/gssapi.h>
  #endif
  
  #ifdef WIN32
  #  include <winsock.h>
  
  #  ifndef R_OK
  #    define R_OK 04
  #  endif
  /* we also need io.h for access() prototype */
  #  include <io.h>
  #else
  #  include <sys/param.h>
  #  include <sys/socket.h>
  #  include <netinet/in.h>
  #  include <arpa/inet.h>
  #  include <netdb.h>
  #endif /* WIN32 */
  #include <fcntl.h>
  #include <stdio.h>
  #include <sasl.h>
  #include <saslutil.h>
  #include <saslplug.h>
  
  #include "plugin_common.h"
  
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  
  #include <errno.h>
  
  #ifdef WIN32
  /* This must be after sasl.h */
  # include "saslgssapi.h"
  #endif /* WIN32 */
  
  /*****************************  Common Section  *****************************/
  
  #ifndef _SUN_SDK_
  static const char plugin_id[] = "$Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $";
  #endif /* !_SUN_SDK_ */
  
  static const char * GSSAPI_BLANK_STRING = "";
  
  #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
  extern gss_OID gss_nt_service_name;
  #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
  #endif
  
  #ifdef _SUN_SDK_
  static int
  get_oid(const sasl_utils_t *utils, gss_OID *oid);
  #ifdef GSSAPI_PROTECT
  DEFINE_STATIC_MUTEX(global_mutex);
  #endif /* GSSAPI_PROTECT */
  #endif /* _SUN_SDK_ */
  
  /* GSSAPI SASL Mechanism by Leif Johansson <leifj@matematik.su.se>
   * inspired by the kerberos mechanism and the gssapi_server and
   * gssapi_client from the heimdal distribution by Assar Westerlund
   * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>. 
   * See the configure.in file for details on dependencies.
   * Heimdal can be obtained from http://www.pdc.kth.se/heimdal
   *
   * Important contributions from Sam Hartman <hartmans@fundsxpress.com>.
   */
  
  typedef struct context {
      int state;
      
      gss_ctx_id_t gss_ctx;
      gss_name_t   client_name;
      gss_name_t   server_name;
      gss_cred_id_t server_creds;
      sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the
  					server */
  #ifdef _SUN_SDK_
      gss_cred_id_t client_creds;
      gss_OID	mech_oid;
      int		use_authid;
  #endif /* _SUN_SDK_ */
      const sasl_utils_t *utils;
      
      /* layers buffering */
      char *buffer;
  #ifdef _SUN_SDK_
      unsigned bufsize;
  #else
      int bufsize;
  #endif /* _SUN_SDK_ */
      char sizebuf[4];
  #ifdef _SUN_SDK_
      unsigned cursize;
      unsigned size;
  #else
      int cursize;
      int size;
  #endif /* _SUN_SDK_ */
      unsigned needsize;
      
      char *encode_buf;                /* For encoding/decoding mem management */
      char *decode_buf;
      char *decode_once_buf;
      unsigned encode_buf_len;
      unsigned decode_buf_len;
      unsigned decode_once_buf_len;
      buffer_info_t *enc_in_buf;
      
      char *out_buf;                   /* per-step mem management */
      unsigned out_buf_len;    
      
      char *authid; /* hold the authid between steps - server */
      const char *user;   /* hold the userid between steps - client */
  #ifdef _SUN_SDK_
      const char *client_authid;
  #endif /* _SUN_SDK_ */
  #ifdef _INTEGRATED_SOLARIS_
      void *h;
  #endif /* _INTEGRATED_SOLARIS_ */
  } context_t;
  
  enum {
      SASL_GSSAPI_STATE_AUTHNEG = 1,
      SASL_GSSAPI_STATE_SSFCAP = 2,
      SASL_GSSAPI_STATE_SSFREQ = 3,
      SASL_GSSAPI_STATE_AUTHENTICATED = 4
  };
  
  #ifdef _SUN_SDK_
  /* sasl_gss_log only logs gss_display_status() error string */
  #define sasl_gss_log(x,y,z) sasl_gss_seterror_(text,y,z,1)
  #define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(text,y,z,0)
  static void
  sasl_gss_seterror_(const context_t *text, OM_uint32 maj, OM_uint32 min, 
  	int logonly)
  #else
  static void
  sasl_gss_seterror(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min)
  #endif /* _SUN_SDK_ */
  {
      OM_uint32 maj_stat, min_stat;
      gss_buffer_desc msg;
      OM_uint32 msg_ctx;
      int ret;
      char *out = NULL;
  #ifdef _SUN_SDK_
      unsigned len, curlen = 0;
      const sasl_utils_t *utils = text->utils;
      char *prefix = dgettext(TEXT_DOMAIN, "GSSAPI Error: ");
  #else
      size_t len, curlen = 0;
      const char prefix[] = "GSSAPI Error: ";
  #endif /* _SUN_SDK_ */
      
      if(!utils) return;
      
      len = sizeof(prefix);
      ret = _plug_buf_alloc(utils, &out, &curlen, 256);
      if(ret != SASL_OK) return;
      
      strcpy(out, prefix);
      
      msg_ctx = 0;
      while (1) {
  	maj_stat = gss_display_status(&min_stat, maj,
  #ifdef _SUN_SDK_
  				      GSS_C_GSS_CODE, text->mech_oid,
  #else
  				      GSS_C_GSS_CODE, GSS_C_NULL_OID,
  #endif /* _SUN_SDK_ */
  				      &msg_ctx, &msg);
  	if(GSS_ERROR(maj_stat)) {
  #ifdef _SUN_SDK_
  	    if (logonly) {
  		utils->log(text->utils->conn, SASL_LOG_FAIL,
  		    "GSSAPI Failure: (could not get major error message)");
  	    } else {
  #endif /* _SUN_SDK_ */
  #ifdef _INTEGRATED_SOLARIS_
  		utils->seterror(utils->conn, 0,
  				gettext("GSSAPI Failure "
  				"(could not get major error message)"));
  #ifdef _SUN_SDK_
  	    }
  #endif /* _SUN_SDK_ */
  #else
  	    utils->seterror(utils->conn, 0,
  			    "GSSAPI Failure "
  			    "(could not get major error message)");
  #ifdef _SUN_SDK_
  	    }
  #endif /* _SUN_SDK_ */
  #endif /* _INTEGRATED_SOLARIS_ */
  	    utils->free(out);
  	    return;
  	}
  	
  	len += len + msg.length;
  	ret = _plug_buf_alloc(utils, &out, &curlen, len);
  	
  	if(ret != SASL_OK) {
  	    utils->free(out);
  	    return;
  	}
  	
  	strcat(out, msg.value);
  	
  	gss_release_buffer(&min_stat, &msg);
  	
  	if (!msg_ctx)
  	    break;
      }
      
      /* Now get the minor status */
      
      len += 2;
      ret = _plug_buf_alloc(utils, &out, &curlen, len);
      if(ret != SASL_OK) {
  	utils->free(out);
  	return;
      }
      
      strcat(out, " (");
      
      msg_ctx = 0;
      while (1) {
  	maj_stat = gss_display_status(&min_stat, min,
  #ifdef _SUN_SDK_
  				      GSS_C_MECH_CODE, text->mech_oid,
  #else
  				      GSS_C_MECH_CODE, GSS_C_NULL_OID,
  #endif /* _SUN_SDK_ */
  				      &msg_ctx, &msg);
  	if(GSS_ERROR(maj_stat)) {
  #ifdef _SUN_SDK_
  	    if (logonly) {
  		utils->log(text->utils->conn, SASL_LOG_FAIL,
  		    "GSSAPI Failure: (could not get minor error message)");
  	    } else {
  #endif /* _SUN_SDK_ */
  #ifdef _INTEGRATED_SOLARIS_
  		utils->seterror(utils->conn, 0,
  				gettext("GSSAPI Failure "
  				"(could not get minor error message)"));
  #ifdef _SUN_SDK_
  	    }
  #endif /* _SUN_SDK_ */
  #else
  	    utils->seterror(utils->conn, 0,
  			    "GSSAPI Failure "
  			    "(could not get minor error message)");
  #ifdef _SUN_SDK_
  	    }
  #endif /* _SUN_SDK_ */
  #endif /* _INTEGRATED_SOLARIS_ */
  	    utils->free(out);
  	    return;
  	}
  	
  	len += len + msg.length;
  	ret = _plug_buf_alloc(utils, &out, &curlen, len);
  	
  	if(ret != SASL_OK) {
  	    utils->free(out);
  	    return;
  	}
  	
  	strcat(out, msg.value);
  	
  	gss_release_buffer(&min_stat, &msg);
  	
  	if (!msg_ctx)
  	    break;
      }
      
      len += 1;
      ret = _plug_buf_alloc(utils, &out, &curlen, len);
      if(ret != SASL_OK) {
  	utils->free(out);
  	return;
      }
      
      strcat(out, ")");
      
  #ifdef _SUN_SDK_
      if (logonly) {
  	utils->log(text->utils->conn, SASL_LOG_FAIL, out);
      } else {
  	utils->seterror(utils->conn, 0, out);
      }
  #else
      utils->seterror(utils->conn, 0, out);
  #endif /* _SUN_SDK_ */
      utils->free(out);
  }
  
  static int 
  sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov,
  		const char **output, unsigned *outputlen, int privacy)
  {
      context_t *text = (context_t *)context;
      OM_uint32 maj_stat, min_stat;
      gss_buffer_t input_token, output_token;
      gss_buffer_desc real_input_token, real_output_token;
      int ret;
      struct buffer_info *inblob, bufinfo;
      
      if(!output) return SASL_BADPARAM;
      
      if(numiov > 1) {
  	ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
  	if(ret != SASL_OK) return ret;
  	inblob = text->enc_in_buf;
      } else {
  	bufinfo.data = invec[0].iov_base;
  	bufinfo.curlen = invec[0].iov_len;
  	inblob = &bufinfo;
      }
      
      if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE;
      
      input_token = &real_input_token;
      
      real_input_token.value  = inblob->data;
      real_input_token.length = inblob->curlen;
      
      output_token = &real_output_token;
      output_token->value = NULL;
      output_token->length = 0;
      
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      if (LOCK_MUTEX(&global_mutex) < 0)
  	return (SASL_FAIL);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      maj_stat = gss_wrap (&min_stat,
  			 text->gss_ctx,
  			 privacy,
  			 GSS_C_QOP_DEFAULT,
  			 input_token,
  			 NULL,
  			 output_token);
      
      if (GSS_ERROR(maj_stat))
  	{
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	    UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  	    return SASL_FAIL;
  	}
      
      if (output_token->value && output) {
  	int len;
  	
  	ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
  			      &(text->encode_buf_len), output_token->length + 4);
  	
  	if (ret != SASL_OK) {
  	    gss_release_buffer(&min_stat, output_token);
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	    UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  	    return ret;
  	}
  	
  	len = htonl(output_token->length);
  	memcpy(text->encode_buf, &len, 4);
  	memcpy(text->encode_buf + 4, output_token->value, output_token->length);
      }
      
      if (outputlen) {
  	*outputlen = output_token->length + 4;
      }
      
      *output = text->encode_buf;
      
      if (output_token->value)
  	gss_release_buffer(&min_stat, output_token);
      
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  
      return SASL_OK;
  }
  
  static int gssapi_privacy_encode(void *context, const struct iovec *invec,
  				 unsigned numiov, const char **output,
  				 unsigned *outputlen)
  {
      return sasl_gss_encode(context,invec,numiov,output,outputlen,1);
  }
  
  static int gssapi_integrity_encode(void *context, const struct iovec *invec,
  				   unsigned numiov, const char **output,
  				   unsigned *outputlen) 
  {
      return sasl_gss_encode(context,invec,numiov,output,outputlen,0);
  }
  
  #define myMIN(a,b) (((a) < (b)) ? (a) : (b))
  
  static int gssapi_decode_once(void *context,
  			      const char **input, unsigned *inputlen,
  			      char **output, unsigned *outputlen)
  {
      context_t *text = (context_t *) context;
      OM_uint32 maj_stat, min_stat;
      gss_buffer_t input_token, output_token;
      gss_buffer_desc real_input_token, real_output_token;
      int result;
      unsigned diff;
      
      if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) {
  #ifdef _INTEGRATED_SOLARIS_
  	SETERROR(text->utils, gettext("GSSAPI Failure"));
  #else
  	SETERROR(text->utils, "GSSAPI Failure");
  #endif /* _INTEGRATED_SOLARIS_ */
  	return SASL_NOTDONE;
      }
      
      /* first we need to extract a packet */
      if (text->needsize > 0) {
  	/* how long is it? */
  	int tocopy = myMIN(text->needsize, *inputlen);
  	
  	memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy);
  	text->needsize -= tocopy;
  	*input += tocopy;
  	*inputlen -= tocopy;
  	
  	if (text->needsize == 0) {
  	    /* got the entire size */
  	    memcpy(&text->size, text->sizebuf, 4);
  	    text->size = ntohl(text->size);
  	    text->cursize = 0;
  	    
  #ifdef _SUN_SDK_
  	    if (text->size > 0xFFFFFF) {
  		text->utils->log(text->utils->conn, SASL_LOG_ERR,
  				 "Illegal size in sasl_gss_decode_once");
  #else
  	    if (text->size > 0xFFFFFF || text->size <= 0) {
  		SETERROR(text->utils, "Illegal size in sasl_gss_decode_once");
  #endif /* _SUN_SDK_ */
  		return SASL_FAIL;
  	    }
  	    
  	    if (text->bufsize < text->size + 5) {
  		result = _plug_buf_alloc(text->utils, &text->buffer,
  					 &(text->bufsize), text->size+5);
  		if(result != SASL_OK) return result;
  	    }
  	}
  	if (*inputlen == 0) {
  	    /* need more data ! */
  	    *outputlen = 0;
  	    *output = NULL;
  	    
  	    return SASL_OK;
  	}
      }
      
      diff = text->size - text->cursize;
      
      if (*inputlen < diff) {
  	/* ok, let's queue it up; not enough data */
  	memcpy(text->buffer + text->cursize, *input, *inputlen);
  	text->cursize += *inputlen;
  	*inputlen = 0;
  	*outputlen = 0;
  	*output = NULL;
  	return SASL_OK;
      } else {
  	memcpy(text->buffer + text->cursize, *input, diff);
  	*input += diff;
  	*inputlen -= diff;
      }
      
      input_token = &real_input_token; 
      real_input_token.value = text->buffer;
      real_input_token.length = text->size;
      
      output_token = &real_output_token;
      output_token->value = NULL;
      output_token->length = 0;
      
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      if (LOCK_MUTEX(&global_mutex) < 0)
  	return (SASL_FAIL);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  
      maj_stat = gss_unwrap (&min_stat,
  			   text->gss_ctx,
  			   input_token,
  			   output_token,
  			   NULL,
  			   NULL);
      
      if (GSS_ERROR(maj_stat))
  	{
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	    UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  	    return SASL_FAIL;
  	}
      
      if (outputlen)
  	*outputlen = output_token->length;
      
      if (output_token->value) {
  	if (output) {
  	    result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
  				     &text->decode_once_buf_len,
  				     *outputlen);
  	    if(result != SASL_OK) {
  		gss_release_buffer(&min_stat, output_token);
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	    UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  		return result;
  	    }
  	    *output = text->decode_once_buf;
  	    memcpy(*output, output_token->value, *outputlen);
  	}
  	gss_release_buffer(&min_stat, output_token);
      }
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	    UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      
      /* reset for the next packet */
  #ifndef _SUN_SDK_
      text->size = -1;
  #endif /* !_SUN_SDK_ */
      text->needsize = 4;
      
      return SASL_OK;
  }
  
  static int gssapi_decode(void *context,
  			 const char *input, unsigned inputlen,
  			 const char **output, unsigned *outputlen)
  {
      context_t *text = (context_t *) context;
      int ret;
      
      ret = _plug_decode(text->utils, context, input, inputlen,
  		       &text->decode_buf, &text->decode_buf_len, outputlen,
  		       gssapi_decode_once);
      
      *output = text->decode_buf;
      
      return ret;
  }
  
  static context_t *gss_new_context(const sasl_utils_t *utils)
  {
      context_t *ret;
      
      ret = utils->malloc(sizeof(context_t));
      if(!ret) return NULL;
      
      memset(ret,0,sizeof(context_t));
      ret->utils = utils;
  #ifdef _SUN_SDK_
      ret->gss_ctx = GSS_C_NO_CONTEXT;
      ret->client_name = GSS_C_NO_NAME;
      ret->server_name = GSS_C_NO_NAME;
      ret->server_creds = GSS_C_NO_CREDENTIAL;
      ret->client_creds = GSS_C_NO_CREDENTIAL;
      if (get_oid(utils, &ret->mech_oid) != SASL_OK) {
  	utils->free(ret);
  	return (NULL);
      }
  #endif /* _SUN_SDK_ */
      
      ret->needsize = 4;
      
      return ret;
  }
  
  static void sasl_gss_free_context_contents(context_t *text)
  {
      OM_uint32 min_stat;
      
      if (!text) return;
      
      if (text->gss_ctx != GSS_C_NO_CONTEXT) {
  	(void) gss_delete_sec_context(&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
  	text->gss_ctx = GSS_C_NO_CONTEXT;
      }
      
      if (text->client_name != GSS_C_NO_NAME) {
  	(void) gss_release_name(&min_stat,&text->client_name);
  	text->client_name = GSS_C_NO_NAME;
      }
      
      if (text->server_name != GSS_C_NO_NAME) {
  	(void) gss_release_name(&min_stat,&text->server_name);
  	text->server_name = GSS_C_NO_NAME;
      }
      
      if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
  	(void) gss_release_cred(&min_stat, &text->server_creds);
  	text->server_creds = GSS_C_NO_CREDENTIAL;
      }
  
  #ifdef _SUN_SDK_
      if ( text->client_creds != GSS_C_NO_CREDENTIAL) {
  	(void) gss_release_cred(&min_stat, &text->client_creds);
  	text->client_creds = GSS_C_NO_CREDENTIAL;
      }
  
      /*
       * Note that the oid returned by rpc_gss_mech_to_oid should not
       * be released
       */
  #endif /* _SUN_SDK_ */
      
      if (text->out_buf) {
  	text->utils->free(text->out_buf);
  	text->out_buf = NULL;
      }
      
      if (text->encode_buf) {
  	text->utils->free(text->encode_buf);
  	text->encode_buf = NULL;
      }
      
      if (text->decode_buf) {
  	text->utils->free(text->decode_buf);
  	text->decode_buf = NULL;
      }
      
      if (text->decode_once_buf) {
  	text->utils->free(text->decode_once_buf);
  	text->decode_once_buf = NULL;
      }
      
      if (text->enc_in_buf) {
  	if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data);
  	text->utils->free(text->enc_in_buf);
  	text->enc_in_buf = NULL;
      }
      
      if (text->buffer) {
  	text->utils->free(text->buffer);
  	text->buffer = NULL;
      }
      
      if (text->authid) { /* works for both client and server */
  	text->utils->free(text->authid);
  	text->authid = NULL;
      }
  }
  
  #ifdef _SUN_SDK_
  
  #ifdef HAVE_RPC_GSS_MECH_TO_OID
  #include <rpc/rpcsec_gss.h>
  #endif /* HAVE_RPC_GSS_MECH_TO_OID */
  
  static int
  get_oid(const sasl_utils_t *utils, gss_OID *oid)
  {
  #ifdef HAVE_RPC_GSS_MECH_TO_OID
      static gss_OID_desc kerb_v5 =
  	{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
  	/* 1.2.840.113554.1.2.2 */
      *oid = &kerb_v5;
  #endif /* HAVE_RPC_GSS_MECH_TO_OID */
      return (SASL_OK);
  }
  
  static int
  add_mech_to_set(context_t *text, gss_OID_set *desired_mechs)
  {
      OM_uint32 maj_stat, min_stat;
  
      maj_stat = gss_create_empty_oid_set(&min_stat, desired_mechs);
  
      if (GSS_ERROR(maj_stat)) {
  	sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	sasl_gss_free_context_contents(text);
  	return SASL_FAIL;
      }
  
      maj_stat = gss_add_oid_set_member(&min_stat, text->mech_oid, desired_mechs);
      if (GSS_ERROR(maj_stat)) {
  	sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	sasl_gss_free_context_contents(text);
  	(void) gss_release_oid_set(&min_stat, desired_mechs);
  	return SASL_FAIL;
      }
      return SASL_OK;
  }
  #endif /* _SUN_SDK_ */
  
  static void gssapi_common_mech_dispose(void *conn_context,
  				       const sasl_utils_t *utils)
  {
  #ifdef _SUN_SDK_
      if (conn_context == NULL)
  	return;
  #ifdef _INTEGRATED_SOLARIS_
      convert_prompt(utils, &((context_t *)conn_context)->h, NULL);
  #endif /* _INTEGRATED_SOLARIS_ */
  #endif /* _SUN_SDK_ */
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      (void) LOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      sasl_gss_free_context_contents((context_t *)(conn_context));
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      utils->free(conn_context);
  }
  
  /*****************************  Server Section  *****************************/
  
  static int 
  gssapi_server_mech_new(void *glob_context __attribute__((unused)), 
  		       sasl_server_params_t *params,
  		       const char *challenge __attribute__((unused)), 
  		       unsigned challen __attribute__((unused)),
  		       void **conn_context)
  {
      context_t *text;
      
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      if (LOCK_MUTEX(&global_mutex) < 0)
  	return (SASL_FAIL);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      text = gss_new_context(params->utils);
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      if (text == NULL) {
  #ifndef _SUN_SDK_
  	MEMERROR(params->utils);
  #endif /* !_SUN_SDK_ */
  	return SASL_NOMEM;
      }
      
      text->gss_ctx = GSS_C_NO_CONTEXT;
      text->client_name = GSS_C_NO_NAME;
      text->server_name = GSS_C_NO_NAME;
      text->server_creds = GSS_C_NO_CREDENTIAL;
      text->state = SASL_GSSAPI_STATE_AUTHNEG;
      
      *conn_context = text;
      
      return SASL_OK;
  }
  
  static int 
  gssapi_server_mech_step(void *conn_context,
  			sasl_server_params_t *params,
  			const char *clientin,
  			unsigned clientinlen,
  			const char **serverout,
  			unsigned *serveroutlen,
  			sasl_out_params_t *oparams)
  {
      context_t *text = (context_t *)conn_context;
      gss_buffer_t input_token, output_token;
      gss_buffer_desc real_input_token, real_output_token;
      OM_uint32 maj_stat, min_stat;
  #ifdef _SUN_SDK_
      OM_uint32 max_input_size;
      gss_OID_set desired_mechs = GSS_C_NULL_OID_SET;
  #endif /* _SUN_SDK_ */
      gss_buffer_desc name_token;
      int ret;
      
      input_token = &real_input_token;
      output_token = &real_output_token;
      output_token->value = NULL; output_token->length = 0;
      input_token->value = NULL; input_token->length = 0;
      
      if(!serverout) {
  	PARAMERROR(text->utils);
  	return SASL_BADPARAM;
      }
      
      *serverout = NULL;
      *serveroutlen = 0;	
  	    
      switch (text->state) {
  
      case SASL_GSSAPI_STATE_AUTHNEG:
  	if (text->server_name == GSS_C_NO_NAME) { /* only once */
  	    name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
  	    name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
  	    if (name_token.value == NULL) {
  		MEMERROR(text->utils);
  		sasl_gss_free_context_contents(text);
  		return SASL_NOMEM;
  	    }
  #ifdef _SUN_SDK_
  	    snprintf(name_token.value, name_token.length + 1,
  		"%s@%s", params->service, params->serverFQDN);
  #else
  	    sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
  #endif /* _SUN_SDK_ */
  	    
  	    maj_stat = gss_import_name (&min_stat,
  					&name_token,
  					GSS_C_NT_HOSTBASED_SERVICE,
  					&text->server_name);
  	    
  	    params->utils->free(name_token.value);
  	    name_token.value = NULL;
  	    
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  	    
  	    if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
  		maj_stat = gss_release_cred(&min_stat, &text->server_creds);
  		text->server_creds = GSS_C_NO_CREDENTIAL;
  	    }
  	    
  #ifdef _SUN_SDK_
  	    if (text->mech_oid != GSS_C_NULL_OID) {
  		ret = add_mech_to_set(text, &desired_mechs);
  		if (ret != SASL_OK)
  		    return (ret);
  	    }
  #endif /* _SUN_SDK_ */
  
  	    maj_stat = gss_acquire_cred(&min_stat, 
  					text->server_name,
  					GSS_C_INDEFINITE, 
  #ifdef _SUN_SDK_
  					desired_mechs,
  #else
  					GSS_C_NO_OID_SET,
  #endif /* _SUN_SDK_ */
  					GSS_C_ACCEPT,
  					&text->server_creds, 
  					NULL, 
  					NULL);
  	    
  #ifdef _SUN_SDK_
  	    if (desired_mechs != GSS_C_NULL_OID_SET) {
  		OM_uint32 min_stat2;
  		(void) gss_release_oid_set(&min_stat2, &desired_mechs);
  	    }
  #endif /* _SUN_SDK_ */
  
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  	}
  	
  	if (clientinlen) {
  	    real_input_token.value = (void *)clientin;
  	    real_input_token.length = clientinlen;
  	}
  	
  	maj_stat =
  	    gss_accept_sec_context(&min_stat,
  				   &(text->gss_ctx),
  				   text->server_creds,
  				   input_token,
  				   GSS_C_NO_CHANNEL_BINDINGS,
  				   &text->client_name,
  				   NULL,
  				   output_token,
  				   NULL,
  				   NULL,
  				   NULL);
  	
  	if (GSS_ERROR(maj_stat)) {
  #ifdef _SUN_SDK_
  	    /* log the local error info, set a more generic error */
  	    sasl_gss_log(text->utils, maj_stat, min_stat);
  	    text->utils->seterror(text->utils->conn, SASL_NOLOG, 
  		    gettext("GSSAPI Failure: accept security context error"));
  	    if (output_token->value) {
  		gss_release_buffer(&min_stat, output_token);
  	    }
  #else
  	    if (output_token->value) {
  		gss_release_buffer(&min_stat, output_token);
  	    }
  	    text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context");
  	    text->utils->log(NULL, SASL_LOG_DEBUG, "GSSAPI Failure: gss_accept_sec_context");
  #endif /* _SUN_SDK_ */
  	    sasl_gss_free_context_contents(text);
  	    return SASL_BADAUTH;
  	}
  	    
  	if (serveroutlen)
  	    *serveroutlen = output_token->length;
  	if (output_token->value) {
  	    if (serverout) {
  		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
  				      &(text->out_buf_len), *serveroutlen);
  		if(ret != SASL_OK) {
  		    gss_release_buffer(&min_stat, output_token);
  		    return ret;
  		}
  		memcpy(text->out_buf, output_token->value, *serveroutlen);
  		*serverout = text->out_buf;
  	    }
  	    
  	    gss_release_buffer(&min_stat, output_token);
  	} else {
  	    /* No output token, send an empty string */
  	    *serverout = GSSAPI_BLANK_STRING;
  #ifndef _SUN_SDK_
  	    serveroutlen = 0;
  #endif /* !_SUN_SDK_ */
  	}
  	
  	
  	if (maj_stat == GSS_S_COMPLETE) {
  	    /* Switch to ssf negotiation */
  	    text->state = SASL_GSSAPI_STATE_SSFCAP;
  	}
  	
  	return SASL_CONTINUE;
  
      case SASL_GSSAPI_STATE_SSFCAP: {
  	unsigned char sasldata[4];
  	gss_buffer_desc name_token;
  #ifndef _SUN_SDK_
  	gss_buffer_desc name_without_realm;
  	gss_name_t without = NULL;
  	int equal;
  #endif /* !_SUN_SDK_ */
  	
  	name_token.value = NULL;
  #ifndef _SUN_SDK_
  	name_without_realm.value = NULL;
  #endif /* !_SUN_SDK_ */
  	
  	/* We ignore whatever the client sent us at this stage */
  	
  	maj_stat = gss_display_name (&min_stat,
  				     text->client_name,
  				     &name_token,
  				     NULL);
  	
  	if (GSS_ERROR(maj_stat)) {
  #ifndef _SUN_SDK_
  	    if (name_without_realm.value)
  		params->utils->free(name_without_realm.value);
  #endif /* !_SUN_SDK_ */
  	    
  	    if (name_token.value)
  		gss_release_buffer(&min_stat, &name_token);
  #ifndef _SUN_SDK_
  	    if (without)
  		gss_release_name(&min_stat, &without);
  #endif /* !_SUN_SDK_ */
  #ifdef _INTEGRATED_SOLARIS_
  	    SETERROR(text->utils, gettext("GSSAPI Failure"));
  #else
  	    SETERROR(text->utils, "GSSAPI Failure");
  #endif /* _INTEGRATED_SOLARIS_ */
  	    sasl_gss_free_context_contents(text);
  	    return SASL_BADAUTH;
  	}
  	
  #ifndef _SUN_SDK_
  	/* If the id contains a realm get the identifier for the user
  	   without the realm and see if it's the same id (i.e. 
  	   tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want
  	   to return the id (i.e. just "tmartin" */
  	if (strchr((char *) name_token.value, (int) '@') != NULL) {
  	    /* NOTE: libc malloc, as it is freed below by a gssapi internal
  	     *       function! */
  	    name_without_realm.value = malloc(strlen(name_token.value)+1);
  	    if (name_without_realm.value == NULL) {
  		MEMERROR(text->utils);
  		return SASL_NOMEM;
  	    }
  	    
  	    strcpy(name_without_realm.value, name_token.value);
  	    
  	    /* cut off string at '@' */
  	    (strchr(name_without_realm.value,'@'))[0] = '\0';
  	    
  	    name_without_realm.length = strlen( (char *) name_without_realm.value );
  	    
  	    maj_stat = gss_import_name (&min_stat,
  					&name_without_realm,
  	    /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here,
  	       so use GSS_C_NT_USER_NAME instead if available.  */
  #ifdef HAVE_GSS_C_NT_USER_NAME
  					GSS_C_NT_USER_NAME,
  #else
  					GSS_C_NULL_OID,
  #endif
  					&without);
  	    
  	    if (GSS_ERROR(maj_stat)) {
  		params->utils->free(name_without_realm.value);
  		if (name_token.value)
  		    gss_release_buffer(&min_stat, &name_token);
  		if (without)
  		    gss_release_name(&min_stat, &without);
  		SETERROR(text->utils, "GSSAPI Failure");
  		sasl_gss_free_context_contents(text);
  		return SASL_BADAUTH;
  	    }
  	    
  	    maj_stat = gss_compare_name(&min_stat,
  					text->client_name,
  					without,
  					&equal);
  	    
  	    if (GSS_ERROR(maj_stat)) {
  		params->utils->free(name_without_realm.value);
  		if (name_token.value)
  		    gss_release_buffer(&min_stat, &name_token);
  		if (without)
  		    gss_release_name(&min_stat, &without);
  		SETERROR(text->utils, "GSSAPI Failure");
  		sasl_gss_free_context_contents(text);
  		return SASL_BADAUTH;
  	    }
  	    
  	    gss_release_name(&min_stat,&without);
  	} else {
  	    equal = 0;
  	}
  	
  	if (equal) {
  	    text->authid = strdup(name_without_realm.value);
  	    
  	    if (text->authid == NULL) {
  		MEMERROR(params->utils);
  		return SASL_NOMEM;
  	    }
  	} else {
  	    text->authid = strdup(name_token.value);
  	    
  	    if (text->authid == NULL) {
  		MEMERROR(params->utils);
  		return SASL_NOMEM;
  	    }
  	}
  #else
  	{
  	    ret = _plug_strdup(params->utils, name_token.value,
  		&text->authid, NULL);
  	}
  #endif /* _SUN_SDK_ */
  	
  	if (name_token.value)
  	    gss_release_buffer(&min_stat, &name_token);
  
  #ifdef _SUN_SDK_
  	if (ret != SASL_OK)
  	    return (ret);
  #else
  	if (name_without_realm.value)
  	    params->utils->free(name_without_realm.value);
  #endif /* _SUN_SDK_ */
  	
  	
  	/* we have to decide what sort of encryption/integrity/etc.,
  	   we support */
  	if (params->props.max_ssf < params->external_ssf) {
  	    text->limitssf = 0;
  	} else {
  	    text->limitssf = params->props.max_ssf - params->external_ssf;
  	}
  	if (params->props.min_ssf < params->external_ssf) {
  	    text->requiressf = 0;
  	} else {
  	    text->requiressf = params->props.min_ssf - params->external_ssf;
  	}
  	
  	/* build up our security properties token */
          if (params->props.maxbufsize > 0xFFFFFF) {
              /* make sure maxbufsize isn't too large */
              /* maxbufsize = 0xFFFFFF */
              sasldata[1] = sasldata[2] = sasldata[3] = 0xFF;
          } else {
              sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF;
              sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF;
              sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF;
          }
  	sasldata[0] = 0;
  	if(text->requiressf != 0 && !params->props.maxbufsize) {
  #ifdef _SUN_SDK_
  	    params->utils->log(params->utils->conn, SASL_LOG_ERR,
  		"GSSAPI needs a security layer but one is forbidden");
  #else
  	    params->utils->seterror(params->utils->conn, 0,
  				    "GSSAPI needs a security layer but one is forbidden");
  #endif /* _SUN_SDK_ */
  	    return SASL_TOOWEAK;
  	}
  	
  	if (text->requiressf == 0) {
  	    sasldata[0] |= 1; /* authentication */
  	}
  	if (text->requiressf <= 1 && text->limitssf >= 1
  	    && params->props.maxbufsize) {
  	    sasldata[0] |= 2;
  	}
  	if (text->requiressf <= 56 && text->limitssf >= 56
  	    && params->props.maxbufsize) {
  	    sasldata[0] |= 4;
  	}
  	
  	real_input_token.value = (void *)sasldata;
  	real_input_token.length = 4;
  	
  	maj_stat = gss_wrap(&min_stat,
  			    text->gss_ctx,
  			    0, /* Just integrity checking here */
  			    GSS_C_QOP_DEFAULT,
  			    input_token,
  			    NULL,
  			    output_token);
  	
  	if (GSS_ERROR(maj_stat)) {
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  	    sasl_gss_free_context_contents(text);
  	    return SASL_FAIL;
  	}
  	
  	
  	if (serveroutlen)
  	    *serveroutlen = output_token->length;
  	if (output_token->value) {
  	    if (serverout) {
  		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
  				      &(text->out_buf_len), *serveroutlen);
  		if(ret != SASL_OK) {
  		    gss_release_buffer(&min_stat, output_token);
  		    return ret;
  		}
  		memcpy(text->out_buf, output_token->value, *serveroutlen);
  		*serverout = text->out_buf;
  	    }
  	    
  	    gss_release_buffer(&min_stat, output_token);
  	}
  	
  	/* Wait for ssf request and authid */
  	text->state = SASL_GSSAPI_STATE_SSFREQ; 
  	
  	return SASL_CONTINUE;
      }
  
      case SASL_GSSAPI_STATE_SSFREQ: {
  	int layerchoice;
  	
  	real_input_token.value = (void *)clientin;
  	real_input_token.length = clientinlen;
  	
  	maj_stat = gss_unwrap(&min_stat,
  			      text->gss_ctx,
  			      input_token,
  			      output_token,
  			      NULL,
  			      NULL);
  	
  	if (GSS_ERROR(maj_stat)) {
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    sasl_gss_free_context_contents(text);
  	    return SASL_FAIL;
  	}
  	
  	layerchoice = (int)(((char *)(output_token->value))[0]);
  	if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */
  	    oparams->encode = NULL;
  	    oparams->decode = NULL;
  	    oparams->mech_ssf = 0;
  	} else if (layerchoice == 2 && text->requiressf <= 1 &&
  		   text->limitssf >= 1) { /* integrity */
  	    oparams->encode=&gssapi_integrity_encode;
  	    oparams->decode=&gssapi_decode;
  	    oparams->mech_ssf=1;
  	} else if (layerchoice == 4 && text->requiressf <= 56 &&
  		   text->limitssf >= 56) { /* privacy */
  	    oparams->encode = &gssapi_privacy_encode;
  	    oparams->decode = &gssapi_decode;
  	    oparams->mech_ssf = 56;
  	} else {
  	    /* not a supported encryption layer */
  #ifdef _SUN_SDK_
  	    text->utils->log(text->utils->conn, SASL_LOG_ERR,
  		"protocol violation: client requested invalid layer");
  #else
  	    SETERROR(text->utils,
  		     "protocol violation: client requested invalid layer");
  #endif /* _SUN_SDK_ */
  	    /* Mark that we attempted negotiation */
  	    oparams->mech_ssf = 2;
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  	    sasl_gss_free_context_contents(text);
  	    return SASL_FAIL;
  	}
  	
  	if (output_token->length > 4) {
  	    int ret;
  	    
  	    ret = params->canon_user(params->utils->conn,
  				     ((char *) output_token->value) + 4,
  				     (output_token->length - 4) * sizeof(char),
  				     SASL_CU_AUTHZID, oparams);
  	    
  	    if (ret != SASL_OK) {
  		sasl_gss_free_context_contents(text);
  		return ret;
  	    }
  	    
  	    ret = params->canon_user(params->utils->conn,
  				     text->authid,
  				     0, /* strlen(text->authid) */
  				     SASL_CU_AUTHID, oparams);
  	    if (ret != SASL_OK) {
  		sasl_gss_free_context_contents(text);
  		return ret;
  	    }
  	} else if(output_token->length == 4) {
  	    /* null authzid */
  	    int ret;
  	    
  	    ret = params->canon_user(params->utils->conn,
  				     text->authid,
  				     0, /* strlen(text->authid) */
  				     SASL_CU_AUTHZID | SASL_CU_AUTHID,
  				     oparams);
  	    
  	    if (ret != SASL_OK) {
  		sasl_gss_free_context_contents(text);
  		return ret;
  	    }	    
  	} else {
  #ifdef _SUN_SDK_
  	    text->utils->log(text->utils->conn, SASL_LOG_ERR,
  	    		     "token too short");
  #else
  	    SETERROR(text->utils,
  		     "token too short");
  #endif /* _SUN_SDK_ */
  	    gss_release_buffer(&min_stat, output_token);
  	    sasl_gss_free_context_contents(text);
  	    return SASL_FAIL;
  	}	
  	
  	/* No matter what, set the rest of the oparams */
          oparams->maxoutbuf =
  	    (((unsigned char *) output_token->value)[1] << 16) |
              (((unsigned char *) output_token->value)[2] << 8) |
              (((unsigned char *) output_token->value)[3] << 0);
  
  #ifdef _SUN_SDK_
  	if (oparams->mech_ssf) {
  	    oparams->maxoutbuf -= 4;	/* Allow for 4 byte tag */
  	    maj_stat = gss_wrap_size_limit(&min_stat,
  					text->gss_ctx,
  					oparams->mech_ssf > 1,
  					GSS_C_QOP_DEFAULT,
  					oparams->maxoutbuf,
  					&max_input_size);
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		(void) gss_release_buffer(&min_stat, output_token);
  		sasl_gss_free_context_contents(text);
  		return (SASL_FAIL);
  	    }
  
  	    /*
  	     * gss_wrap_size_limit will return very big sizes for
  	     * small input values
  	     */
  	    if (max_input_size < oparams->maxoutbuf)
   		oparams->maxoutbuf = max_input_size;
  	    else {
  		oparams->maxoutbuf = 0;
  	    }
  	}
  #else
  	if (oparams->mech_ssf) {
  	    /* xxx this is probably too big */
  	    oparams->maxoutbuf -= 50;
  	}
  #endif /* _SUN_SDK_ */
  	
  	gss_release_buffer(&min_stat, output_token);
  	
  	text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
  	
  	oparams->doneflag = 1;
  	
  	return SASL_OK;
      }
      
      default:
  #ifdef _SUN_SDK_
  	params->utils->log(text->utils->conn, SASL_LOG_ERR,
  			   "Invalid GSSAPI server step %d", text->state);
  #else
  	params->utils->log(NULL, SASL_LOG_ERR,
  			   "Invalid GSSAPI server step %d\n", text->state);
  #endif /* _SUN_SDK_ */
  	return SASL_FAIL;
      }
      
  #ifndef _SUN_SDK_
      return SASL_FAIL; /* should never get here */
  #endif /* !_SUN_SDK_ */
  }
  
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  static int 
  _gssapi_server_mech_step(void *conn_context,
  			sasl_server_params_t *params,
  			const char *clientin,
  			unsigned clientinlen,
  			const char **serverout,
  			unsigned *serveroutlen,
  			sasl_out_params_t *oparams)
  {
      int ret;
  
      if (LOCK_MUTEX(&global_mutex) < 0)
  	return (SASL_FAIL);
  
      ret = gssapi_server_mech_step(conn_context, params, clientin, clientinlen,
  	serverout, serveroutlen, oparams);
  
      UNLOCK_MUTEX(&global_mutex);
      return (ret);
  }
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  
  static sasl_server_plug_t gssapi_server_plugins[] = 
  {
      {
  	"GSSAPI",			/* mech_name */
  	56,				/* max_ssf */
  	SASL_SEC_NOPLAINTEXT
  	| SASL_SEC_NOACTIVE
  	| SASL_SEC_NOANONYMOUS
  	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
  	SASL_FEAT_WANT_CLIENT_FIRST
  	| SASL_FEAT_ALLOWS_PROXY,	/* features */
  	NULL,				/* glob_context */
  	&gssapi_server_mech_new,	/* mech_new */
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	&_gssapi_server_mech_step,	/* mech_step */
  #else
  	&gssapi_server_mech_step,	/* mech_step */
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  	&gssapi_common_mech_dispose,	/* mech_dispose */
  	NULL,				/* mech_free */
  	NULL,				/* setpass */
  	NULL,				/* user_query */
  	NULL,				/* idle */
  	NULL,				/* mech_avail */
  	NULL				/* spare */
      }
  };
  
  int gssapiv2_server_plug_init(
  #ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
      const sasl_utils_t *utils __attribute__((unused)),
  #else
      const sasl_utils_t *utils,
  #endif 
      int maxversion,
      int *out_version,
      sasl_server_plug_t **pluglist,
      int *plugcount)
  {
  #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
      const char *keytab = NULL;
      char keytab_path[1024];
      unsigned int rl;
  #endif
      
      if (maxversion < SASL_SERVER_PLUG_VERSION) {
  	return SASL_BADVERS;
      }
      
  #ifndef _SUN_SDK_
  #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
      /* unfortunately, we don't check for readability of keytab if it's
         the standard one, since we don't know where it is */
      
      /* FIXME: This code is broken */
      
      utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl);
      if (keytab != NULL) {
  	if (access(keytab, R_OK) != 0) {
  	    utils->log(NULL, SASL_LOG_ERR,
  		       "Could not find keytab file: %s: %m",
  		       keytab, errno);
  	    return SASL_FAIL;
  	}
  	
  	if(strlen(keytab) > 1024) {
  	    utils->log(NULL, SASL_LOG_ERR,
  		       "path to keytab is > 1024 characters");
  	    return SASL_BUFOVER;
  	}
  	
  	strncpy(keytab_path, keytab, 1024);
  	
  	gsskrb5_register_acceptor_identity(keytab_path);
      }
  #endif
  #endif /* !_SUN_SDK_ */
      
  #ifdef _INTEGRATED_SOLARIS_
      /*
       * Let libsasl know that we are a "Sun" plugin so that privacy
       * and integrity will be allowed.
       */
      REG_PLUG("GSSAPI", gssapi_server_plugins);
  #endif /* _INTEGRATED_SOLARIS_ */
  
      *out_version = SASL_SERVER_PLUG_VERSION;
      *pluglist = gssapi_server_plugins;
      *plugcount = 1;  
      
      return SASL_OK;
  }
  
  /*****************************  Client Section  *****************************/
  
  static int gssapi_client_mech_new(void *glob_context __attribute__((unused)), 
  				  sasl_client_params_t *params,
  				  void **conn_context)
  {
      context_t *text;
  #ifdef _SUN_SDK_
      const char *use_authid = NULL;
  #endif /* _SUN_SDK_ */
      
      /* holds state are in */
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      if (LOCK_MUTEX(&global_mutex) < 0)
  	return (SASL_FAIL);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      text = gss_new_context(params->utils);
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
      UNLOCK_MUTEX(&global_mutex);
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
      if (text == NULL) {
  #ifndef _SUN_SDK_
  	MEMERROR(params->utils);
  #endif /* !_SUN_SDK_ */
  	return SASL_NOMEM;
      }
      
      text->state = SASL_GSSAPI_STATE_AUTHNEG;
      text->gss_ctx = GSS_C_NO_CONTEXT;
      text->client_name = GSS_C_NO_NAME;
      text->server_creds = GSS_C_NO_CREDENTIAL;
      
  #ifdef _SUN_SDK_
      params->utils->getopt(params->utils->getopt_context,
  			  "GSSAPI", "use_authid", &use_authid, NULL);
      text->use_authid = (use_authid != NULL) &&
  	(*use_authid == 'y' || *use_authid == 'Y' || *use_authid == '1');
  #endif /* _SUN_SDK_ */
      
      *conn_context = text;
      
      return SASL_OK;
  }
  
  static int gssapi_client_mech_step(void *conn_context,
  				   sasl_client_params_t *params,
  				   const char *serverin,
  				   unsigned serverinlen,
  				   sasl_interact_t **prompt_need,
  				   const char **clientout,
  				   unsigned *clientoutlen,
  				   sasl_out_params_t *oparams)
  {
      context_t *text = (context_t *)conn_context;
      gss_buffer_t input_token, output_token;
      gss_buffer_desc real_input_token, real_output_token;
      OM_uint32 maj_stat, min_stat;
  #ifdef _SUN_SDK_
      OM_uint32 max_input_size;
  #endif /* _SUN_SDK_ */
      gss_buffer_desc name_token;
      int ret;
      OM_uint32 req_flags, out_req_flags;
      input_token = &real_input_token;
      output_token = &real_output_token;
      output_token->value = NULL;
      input_token->value = NULL; 
      input_token->length = 0;
      
      *clientout = NULL;
      *clientoutlen = 0;
      
      switch (text->state) {
  
      case SASL_GSSAPI_STATE_AUTHNEG:
  	/* try to get the userid */
  #ifdef _SUN_SDK_
  	if (text->user == NULL ||
  		(text->use_authid && text->client_authid == NULL)) {
  	    int auth_result = SASL_OK;
  	    int user_result = SASL_OK;
  
  	    if (text->use_authid && text->client_authid == NULL) {
  		auth_result = _plug_get_authid(params->utils,
  					       &text->client_authid,
  					       prompt_need);
  	
  		if ((auth_result != SASL_OK) &&
  			(auth_result != SASL_INTERACT)) {
  		    sasl_gss_free_context_contents(text);
  		    return auth_result;
  		}
  	    }
  	    if (text->user == NULL) {
  		user_result = _plug_get_userid(params->utils, &text->user,
  					       prompt_need);
  	    
  		if ((user_result != SASL_OK) &&
  			(user_result != SASL_INTERACT)) {
  		    sasl_gss_free_context_contents(text);
  		    return user_result;
  		}
  	    }
  #else
  	if (text->user == NULL) {
  	    int user_result = SASL_OK;
  	    
  	    user_result = _plug_get_userid(params->utils, &text->user,
  					   prompt_need);
  	    
  	    if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
  		sasl_gss_free_context_contents(text);
  		return user_result;
  	    }
  #endif /* _SUN_SDK_ */
  		    
  	    /* free prompts we got */
  	    if (prompt_need && *prompt_need) {
  		params->utils->free(*prompt_need);
  		*prompt_need = NULL;
  	    }
  		    
  	    /* if there are prompts not filled in */
  #ifdef _SUN_SDK_
  	    if ((user_result == SASL_INTERACT) ||
  			(auth_result == SASL_INTERACT)) {
  		/* make the prompt list */
  #ifdef _INTEGRATED_SOLARIS_
  		int result = _plug_make_prompts(params->utils, &text->h,
  			   prompt_need,
  			   user_result == SASL_INTERACT ?
  			   convert_prompt(params->utils, &text->h,
  			    gettext("Please enter your authorization name"))
  				: NULL, NULL,
  			   auth_result == SASL_INTERACT ?
  			   convert_prompt(params->utils, &text->h,
  			    gettext("Please enter your authentication name"))
  				: NULL, NULL,
  			   NULL, NULL,
  			   NULL, NULL, NULL,
  			   NULL, NULL, NULL);
  #else
  		int result = _plug_make_prompts(params->utils, prompt_need,
  			   user_result == SASL_INTERACT ?
  			   	"Please enter your authorization name"
  				: NULL, NULL,
  			   auth_result == SASL_INTERACT ?
  			   	"Please enter your authentication name"
  				: NULL, NULL,
  			   NULL, NULL,
  			   NULL, NULL, NULL,
  			   NULL, NULL, NULL);
  #endif /* _INTEGRATED_SOLARIS_ */
  	
  		if (result != SASL_OK) return result;
  
  		return SASL_INTERACT;
  	    }
  #else
  	    if (user_result == SASL_INTERACT) {
  		/* make the prompt list */
  		int result =
  		    _plug_make_prompts(params->utils, prompt_need,
  				       user_result == SASL_INTERACT ?
  				       "Please enter your authorization name" : NULL, NULL,
  				       NULL, NULL,
  				       NULL, NULL,
  				       NULL, NULL, NULL,
  				       NULL, NULL, NULL);
  		if (result != SASL_OK) return result;
  		
  		return SASL_INTERACT;
  	    }
  #endif /* _SUN_SDK_ */
  	}
  	    
  	if (text->server_name == GSS_C_NO_NAME) { /* only once */
  	    name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
  	    name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
  	    if (name_token.value == NULL) {
  		sasl_gss_free_context_contents(text);
  		return SASL_NOMEM;
  	    }
  	    if (params->serverFQDN == NULL
  		|| strlen(params->serverFQDN) == 0) {
  #ifdef _SUN_SDK_
  		text->utils->log(text->utils->conn, SASL_LOG_ERR,
  				 "GSSAPI Failure: no serverFQDN");
  #else
  		SETERROR(text->utils, "GSSAPI Failure: no serverFQDN");
  #endif /* _SUN_SDK_ */
  		return SASL_FAIL;
  	    }
  	    
  #ifdef _SUN_SDK_
  	    snprintf(name_token.value, name_token.length + 1,
  		"%s@%s", params->service, params->serverFQDN);
  #else
  	    sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
  #endif /* _SUN_SDK_ */
  	    
  	    maj_stat = gss_import_name (&min_stat,
  					&name_token,
  					GSS_C_NT_HOSTBASED_SERVICE,
  					&text->server_name);
  	    
  	    params->utils->free(name_token.value);
  	    name_token.value = NULL;
  	    
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  	}
  	    
  	if (serverinlen == 0)
  	    input_token = GSS_C_NO_BUFFER;
  
  	if (serverinlen) {
  	    real_input_token.value = (void *)serverin;
  	    real_input_token.length = serverinlen;
  	}
  	else if (text->gss_ctx != GSS_C_NO_CONTEXT ) {
  	    /* This can't happen under GSSAPI: we have a non-null context
  	     * and no input from the server.  However, thanks to Imap,
  	     * which discards our first output, this happens all the time.
  	     * Throw away the context and try again. */
  	    maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
  	    text->gss_ctx = GSS_C_NO_CONTEXT;
  	}
  	    
  	/* Setup req_flags properly */
  	req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
  	if(params->props.max_ssf > params->external_ssf) {
  	    /* We are requesting a security layer */
  	    req_flags |= GSS_C_INTEG_FLAG;
  	    if(params->props.max_ssf - params->external_ssf > 56) {
  		/* We want to try for privacy */
  		req_flags |= GSS_C_CONF_FLAG;
  	    }
  	}
  	
  #ifdef _SUN_SDK_
  	if (text->use_authid && text->client_creds == GSS_C_NO_CREDENTIAL) {
  	    gss_OID_set desired_mechs = GSS_C_NULL_OID_SET;
  	    gss_buffer_desc name_token;
  
  	    name_token.length = strlen(text->client_authid);
  	    name_token.value = (char *)text->client_authid;
  
  	    maj_stat = gss_import_name (&min_stat,
  					&name_token,
  #ifdef HAVE_GSS_C_NT_USER_NAME
  					GSS_C_NT_USER_NAME,
  #else
  					GSS_C_NULL_OID,
  #endif
  					&text->client_name);
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  
  	    if (text->mech_oid != GSS_C_NULL_OID) {
  		ret = add_mech_to_set(text, &desired_mechs);
  		if (ret != SASL_OK)
  		    return (ret);
  	    }
  
  	    maj_stat = gss_acquire_cred(&min_stat, 
  					text->client_name,
  					GSS_C_INDEFINITE, 
  					desired_mechs,
  					GSS_C_INITIATE,
  					&text->client_creds, 
  					NULL, 
  					NULL);
  
  	    if (desired_mechs != GSS_C_NULL_OID_SET) {
  		OM_uint32 min_stat2;
  		(void) gss_release_oid_set(&min_stat2, &desired_mechs);
  	    }
  
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  	}
  #endif /* _SUN_SDK_ */
  
  	maj_stat = gss_init_sec_context(&min_stat,
  #ifdef _SUN_SDK_
  					text->client_creds,
  #else
  					GSS_C_NO_CREDENTIAL,
  #endif /* _SUN_SDK_ */
  					&text->gss_ctx,
  					text->server_name,
  #ifdef _SUN_SDK_
  					text->mech_oid,
  #else
  					GSS_C_NO_OID,
  #endif /* _SUN_SDK_ */
  					req_flags,
  					0,
  					GSS_C_NO_CHANNEL_BINDINGS,
  					input_token,
  					NULL,
  					output_token,
  					&out_req_flags,
  					NULL);
  	
  	if (GSS_ERROR(maj_stat)) {
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  	    sasl_gss_free_context_contents(text);
  	    return SASL_FAIL;
  	}
  	    
  	*clientoutlen = output_token->length;
  	    
  	if (output_token->value) {
  	    if (clientout) {
  		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
  				      &(text->out_buf_len), *clientoutlen);
  		if(ret != SASL_OK) {
  		    gss_release_buffer(&min_stat, output_token);
  		    return ret;
  		}
  		memcpy(text->out_buf, output_token->value, *clientoutlen);
  		*clientout = text->out_buf;
  	    }
  	    
  	    gss_release_buffer(&min_stat, output_token);
  	}
  	
  	if (maj_stat == GSS_S_COMPLETE) {
  	    maj_stat = gss_inquire_context(&min_stat,
  					   text->gss_ctx,
  					   &text->client_name,
  					   NULL,       /* targ_name */
  					   NULL,       /* lifetime */
  					   NULL,       /* mech */
  					   NULL,       /* flags */
  					   NULL,       /* local init */
  					   NULL);      /* open */
  	    
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  	    
  	    name_token.length = 0;
  	    maj_stat = gss_display_name(&min_stat,
  					text->client_name,
  					&name_token,
  					NULL);
  	    
  	    if (GSS_ERROR(maj_stat)) {
  		if (name_token.value)
  		    gss_release_buffer(&min_stat, &name_token);
  #ifdef _INTEGRATED_SOLARIS_
  		SETERROR(text->utils, gettext("GSSAPI Failure"));
  #else
  		SETERROR(text->utils, "GSSAPI Failure");
  #endif /* _INTEGRATED_SOLARIS_ */
  		sasl_gss_free_context_contents(text);
  		return SASL_FAIL;
  	    }
  	    
  	    if (text->user && text->user[0]) {
  		ret = params->canon_user(params->utils->conn,
  					 text->user, 0,
  					 SASL_CU_AUTHZID, oparams);
  		if (ret == SASL_OK) 
  		    ret = params->canon_user(params->utils->conn,
  					     name_token.value, 0,
  					     SASL_CU_AUTHID, oparams);
  	    } else {
  		ret = params->canon_user(params->utils->conn,
  					 name_token.value, 0,
  					 SASL_CU_AUTHID | SASL_CU_AUTHZID,
  					 oparams);
  	    }
  	    gss_release_buffer(&min_stat, &name_token);
  	    
  	    if (ret != SASL_OK) return ret;
  	    
  	    /* Switch to ssf negotiation */
  	    text->state = SASL_GSSAPI_STATE_SSFCAP;
  	}
  	
  	return SASL_CONTINUE;
  
      case SASL_GSSAPI_STATE_SSFCAP: {
  	sasl_security_properties_t *secprops = &(params->props);
  	unsigned int alen, external = params->external_ssf;
  	sasl_ssf_t need, allowed;
  	char serverhas, mychoice;
  	
  	real_input_token.value = (void *) serverin;
  	real_input_token.length = serverinlen;
  	
  	maj_stat = gss_unwrap(&min_stat,
  			      text->gss_ctx,
  			      input_token,
  			      output_token,
  			      NULL,
  			      NULL);
  	
  	if (GSS_ERROR(maj_stat)) {
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    sasl_gss_free_context_contents(text);
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  	    return SASL_FAIL;
  	}
  	
  	/* taken from kerberos.c */
  	if (secprops->min_ssf > (56 + external)) {
  	    return SASL_TOOWEAK;
  	} else if (secprops->min_ssf > secprops->max_ssf) {
  	    return SASL_BADPARAM;
  	}
  	
  	/* need bits of layer -- sasl_ssf_t is unsigned so be careful */
  	if (secprops->max_ssf >= external) {
  	    allowed = secprops->max_ssf - external;
  	} else {
  	    allowed = 0;
  	}
  	if (secprops->min_ssf >= external) {
  	    need = secprops->min_ssf - external;
  	} else {
  	    /* good to go */
  	    need = 0;
  	}
  	
  	/* bit mask of server support */
  	serverhas = ((char *)output_token->value)[0];
  	
  	/* if client didn't set use strongest layer available */
  	if (allowed >= 56 && need <= 56 && (serverhas & 4)) {
  	    /* encryption */
  	    oparams->encode = &gssapi_privacy_encode;
  	    oparams->decode = &gssapi_decode;
  	    oparams->mech_ssf = 56;
  	    mychoice = 4;
  	} else if (allowed >= 1 && need <= 1 && (serverhas & 2)) {
  	    /* integrity */
  	    oparams->encode = &gssapi_integrity_encode;
  	    oparams->decode = &gssapi_decode;
  	    oparams->mech_ssf = 1;
  	    mychoice = 2;
  #ifdef _SUN_SDK_
  	} else if (need == 0 && (serverhas & 1)) {
  #else
  	} else if (need <= 0 && (serverhas & 1)) {
  #endif /* _SUN_SDK_ */
  	    /* no layer */
  	    oparams->encode = NULL;
  	    oparams->decode = NULL;
  	    oparams->mech_ssf = 0;
  	    mychoice = 1;
  	} else {
  	    /* there's no appropriate layering for us! */
  	    sasl_gss_free_context_contents(text);
  	    return SASL_TOOWEAK;
  	}
  	
          oparams->maxoutbuf =
  	    (((unsigned char *) output_token->value)[1] << 16) |
              (((unsigned char *) output_token->value)[2] << 8) |
              (((unsigned char *) output_token->value)[3] << 0);
  
  #ifdef _SUN_SDK_
  	if (oparams->mech_ssf > 0) {
  	    oparams->maxoutbuf -= 4;	/* Space for 4 byte length header */
  	    maj_stat = gss_wrap_size_limit(&min_stat,
  					text->gss_ctx,
  					oparams->mech_ssf > 1,
  					GSS_C_QOP_DEFAULT,
  					oparams->maxoutbuf,
  					&max_input_size);
  	    if (GSS_ERROR(maj_stat)) {
  		sasl_gss_seterror(text->utils, maj_stat, min_stat);
  		(void) gss_release_buffer(&min_stat, output_token);
  		sasl_gss_free_context_contents(text);
  		return (SASL_FAIL);
  	    }
  
  	/*
  	 * This is a workaround for a Solaris bug where
  	 * gss_wrap_size_limit may return very big sizes for
  	 * small input values
  	 */
  	    if (max_input_size < oparams->maxoutbuf)
   		oparams->maxoutbuf = max_input_size;
  	    else {
  		oparams->maxoutbuf = 0;
  	    }
  	}
  #else
  	if(oparams->mech_ssf) {
  	    /* xxx probably too large */
  	    oparams->maxoutbuf -= 50;
  	}
  #endif /* _SUN_SDK_ */
  	
  	gss_release_buffer(&min_stat, output_token);
  	
  	/* oparams->user is always set, due to canon_user requirements.
  	 * Make sure the client actually requested it though, by checking
  	 * if our context was set.
  	 */
  	if (text->user && text->user[0])
  	    alen = strlen(oparams->user);
  	else
  	    alen = 0;
  	
  	input_token->length = 4 + alen;
  	input_token->value =
  	    (char *)params->utils->malloc((input_token->length + 1)*sizeof(char));
  	if (input_token->value == NULL) {
  	    sasl_gss_free_context_contents(text);
  	    return SASL_NOMEM;
  	}
  	
  	if (alen)
  	    memcpy((char *)input_token->value+4,oparams->user,alen);
  
  	/* build up our security properties token */
          if (params->props.maxbufsize > 0xFFFFFF) {
              /* make sure maxbufsize isn't too large */
              /* maxbufsize = 0xFFFFFF */
              ((unsigned char *)input_token->value)[1] = 0xFF;
              ((unsigned char *)input_token->value)[2] = 0xFF;
              ((unsigned char *)input_token->value)[3] = 0xFF;
          } else {
              ((unsigned char *)input_token->value)[1] = 
                  (params->props.maxbufsize >> 16) & 0xFF;
              ((unsigned char *)input_token->value)[2] = 
                  (params->props.maxbufsize >> 8) & 0xFF;
              ((unsigned char *)input_token->value)[3] = 
                  (params->props.maxbufsize >> 0) & 0xFF;
          }
  	((unsigned char *)input_token->value)[0] = mychoice;
  	
  	maj_stat = gss_wrap (&min_stat,
  			     text->gss_ctx,
  			     0, /* Just integrity checking here */
  			     GSS_C_QOP_DEFAULT,
  			     input_token,
  			     NULL,
  			     output_token);
  	
  	params->utils->free(input_token->value);
  	input_token->value = NULL;
  	
  	if (GSS_ERROR(maj_stat)) {
  	    sasl_gss_seterror(text->utils, maj_stat, min_stat);
  	    if (output_token->value)
  		gss_release_buffer(&min_stat, output_token);
  	    sasl_gss_free_context_contents(text);
  	    return SASL_FAIL;
  	}
  	
  	if (clientoutlen)
  	    *clientoutlen = output_token->length;
  	if (output_token->value) {
  	    if (clientout) {
  		ret = _plug_buf_alloc(text->utils, &(text->out_buf),
  				      &(text->out_buf_len), *clientoutlen);
  		if (ret != SASL_OK) {
  		    gss_release_buffer(&min_stat, output_token);
  		    return ret;
  		}
  		memcpy(text->out_buf, output_token->value, *clientoutlen);
  		*clientout = text->out_buf;
  	    }
  	    
  	    gss_release_buffer(&min_stat, output_token);
  	}
  	
  	text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
  	
  	oparams->doneflag = 1;
  	
  	return SASL_OK;
      }
  	
      default:
  #ifdef _SUN_SDK_
  	params->utils->log(params->utils->conn, SASL_LOG_ERR,
  			   "Invalid GSSAPI client step %d", text->state);
  #else
  	params->utils->log(NULL, SASL_LOG_ERR,
  			   "Invalid GSSAPI client step %d\n", text->state);
  #endif /* _SUN_SDK_ */
  	return SASL_FAIL;
      }
      
  #ifndef _SUN_SDK_
      return SASL_FAIL; /* should never get here */
  #endif /* !_SUN_SDK_ */
  }
  
  #ifdef _SUN_SDK_
  static const unsigned long gssapi_required_prompts[] = {
  #else
  static const long gssapi_required_prompts[] = {
  #endif /* _SUN_SDK_ */
      SASL_CB_LIST_END
  };  
  
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  static int _gssapi_client_mech_step(void *conn_context,
  				   sasl_client_params_t *params,
  				   const char *serverin,
  				   unsigned serverinlen,
  				   sasl_interact_t **prompt_need,
  				   const char **clientout,
  				   unsigned *clientoutlen,
  				   sasl_out_params_t *oparams)
  {
      int ret;
  
      if (LOCK_MUTEX(&global_mutex) < 0)
  	return (SASL_FAIL);
  
      ret = gssapi_client_mech_step(conn_context, params, serverin, serverinlen,
  	prompt_need, clientout, clientoutlen, oparams);
  
      UNLOCK_MUTEX(&global_mutex);
      return (ret);
  }
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  
  static sasl_client_plug_t gssapi_client_plugins[] = 
  {
      {
  	"GSSAPI",			/* mech_name */
  	56,				/* max_ssf */
  	SASL_SEC_NOPLAINTEXT
  	| SASL_SEC_NOACTIVE
  	| SASL_SEC_NOANONYMOUS
  	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
  	SASL_FEAT_WANT_CLIENT_FIRST
  	| SASL_FEAT_ALLOWS_PROXY,	/* features */
  	gssapi_required_prompts,	/* required_prompts */
  	NULL,				/* glob_context */
  	&gssapi_client_mech_new,	/* mech_new */
  #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
  	&_gssapi_client_mech_step,	/* mech_step */
  #else
  	&gssapi_client_mech_step,	/* mech_step */
  #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
  	&gssapi_common_mech_dispose,	/* mech_dispose */
  	NULL,				/* mech_free */
  	NULL,				/* idle */
  	NULL,				/* spare */
  	NULL				/* spare */
      }
  };
  
  int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)), 
  			      int maxversion,
  			      int *out_version, 
  			      sasl_client_plug_t **pluglist,
  			      int *plugcount)
  {
      if (maxversion < SASL_CLIENT_PLUG_VERSION) {
  	SETERROR(utils, "Version mismatch in GSSAPI");
  	return SASL_BADVERS;
      }
      
  #ifdef _INTEGRATED_SOLARIS_
      /*
       * Let libsasl know that we are a "Sun" plugin so that privacy
       * and integrity will be allowed.
       */
      REG_PLUG("GSSAPI", gssapi_client_plugins);
  #endif /* _INTEGRATED_SOLARIS_ */
  
      *out_version = SASL_CLIENT_PLUG_VERSION;
      *pluglist = gssapi_client_plugins;
      *plugcount = 1;
      
      return SASL_OK;
  }
  