OpenSolaris_b135/lib/libsasl/lib/common.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/* common.c - Functions that are common to server and clinet
 * Rob Siemborski
 * Tim Martin
 * $Id: common.c,v 1.92 2003/04/16 19:36:00 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>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#ifdef HAVE_SYSLOG
#include <syslog.h>
#endif
#include <stdarg.h>
#include <ctype.h>

#include <sasl.h>
#include <saslutil.h>
#include <saslplug.h>
#include "saslint.h"

#ifdef _SUN_SDK_
#include "md5_private.h"
#include "hmac-md5.h"
#include "plugin_common.h"
#endif


#ifdef WIN32
/* need to handle the fact that errno has been defined as a function
   in a dll, not an extern int */
# ifdef errno
#  undef errno
# endif /* errno */
#endif /* WIN32 */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

static int _sasl_getpath(void *context __attribute__((unused)), const char **path);

#ifdef _SUN_SDK_
DEFINE_STATIC_MUTEX(global_mutex);
DEFINE_STATIC_MUTEX(malloc_global_mutex);
static void _sasl_dispose_context(_sasl_global_context_t *ctx);
static int _sasl_getconf(void *context, const char **conf);

#ifdef _INTEGRATED_SOLARIS_
static pthread_key_t errstring_key = PTHREAD_ONCE_KEY_NP;
#endif /* _INTEGRATED_SOLARIS_ */
#else
static const char build_ident[] = "$Build: libsasl " PACKAGE "-" VERSION " $";

/* It turns out to be conveinent to have a shared sasl_utils_t */
LIBSASL_VAR const sasl_utils_t *sasl_global_utils = NULL;

/* Should be a null-terminated array that lists the available mechanisms */
static char **global_mech_list = NULL;

void *free_mutex = NULL;

int (*_sasl_client_cleanup_hook)(void) = NULL;
int (*_sasl_server_cleanup_hook)(void) = NULL;
int (*_sasl_client_idle_hook)(sasl_conn_t *conn) = NULL;
int (*_sasl_server_idle_hook)(sasl_conn_t *conn) = NULL;

sasl_allocation_utils_t _sasl_allocation_utils={
  (sasl_malloc_t *)  &malloc,
  (sasl_calloc_t *)  &calloc,
  (sasl_realloc_t *) &realloc,
  (sasl_free_t *) &free
};
#endif /* _SUN_SDK_ */

#ifdef USE_PTHREADS
static void *sasl_mutex_alloc(void)
{
    pthread_mutex_t *mutex =
	(pthread_mutex_t *)malloc(sizeof (pthread_mutex_t));

    if (mutex != NULL) {
	if (pthread_mutex_init(mutex, NULL) != 0) {
	    free(mutex);
	    mutex = NULL;
	}
    }
    return (mutex);
}

static int sasl_mutex_lock(void *mutex)
{
    int ret = SASL_BADPARAM;

    if (mutex != NULL)
	ret = pthread_mutex_lock((pthread_mutex_t *)mutex);
	
    return ret;
}

static int sasl_mutex_unlock(void *mutex)
{
    int ret = SASL_BADPARAM;

    if (mutex != NULL)
	ret = pthread_mutex_unlock((pthread_mutex_t *)mutex);
	
    return ret;
}

static void sasl_mutex_free(void *mutex __attribute__((unused)))
{
  if (mutex != NULL) {
     pthread_mutex_destroy((pthread_mutex_t *)mutex);
     free(mutex);
  }
}
#else
/* Intenal mutex functions do as little as possible (no thread protection) */
static void *sasl_mutex_alloc(void)
{
  return (void *)0x1;
}

static int sasl_mutex_lock(void *mutex __attribute__((unused)))
{
    return SASL_OK;
}

static int sasl_mutex_unlock(void *mutex __attribute__((unused)))
{
    return SASL_OK;
}

static void sasl_mutex_free(void *mutex __attribute__((unused)))
{
    return;
}
#endif /* USE_PTHREADS */

#ifndef _SUN_SDK_
sasl_mutex_utils_t _sasl_mutex_utils={
  &sasl_mutex_alloc,
  &sasl_mutex_lock,
  &sasl_mutex_unlock,
  &sasl_mutex_free
};
#endif /* !_SUN_SDK_ */

void sasl_set_mutex(sasl_mutex_alloc_t *n, sasl_mutex_lock_t *l,
		    sasl_mutex_unlock_t *u, sasl_mutex_free_t *d)
{
#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx =  _sasl_gbl_ctx();

  gctx->sasl_mutex_utils.alloc=n;
  gctx->sasl_mutex_utils.lock=l;
  gctx->sasl_mutex_utils.unlock=u;
  gctx->sasl_mutex_utils.free=d;
#else
  _sasl_mutex_utils.alloc=n;
  _sasl_mutex_utils.lock=l;
  _sasl_mutex_utils.unlock=u;
  _sasl_mutex_utils.free=d;
#endif
}

/* copy a string to malloced memory */
#ifdef _SUN_SDK_
int __sasl_strdup(const _sasl_global_context_t *gctx, const char *in,
	char **out, size_t *outlen)
#else
int _sasl_strdup(const char *in, char **out, size_t *outlen)
#endif /* _SUN_SDK_ */
{
  size_t len = strlen(in);
  if (outlen) *outlen = len;
  *out=sasl_ALLOC(len + 1);
  if (! *out) return SASL_NOMEM;
  strcpy((char *) *out, in);
  return SASL_OK;
}

/* adds a string to the buffer; reallocing if need be */
#ifdef _SUN_SDK_
int __sasl_add_string(const _sasl_global_context_t *gctx, char **out,
		     size_t *alloclen, size_t *outlen,
		     const char *add)
#else
int _sasl_add_string(char **out, size_t *alloclen,
		     size_t *outlen, const char *add)
#endif /* _SUN_SDK_ */
{
  size_t addlen;

  if (add==NULL) add = "(null)";

  addlen=strlen(add); /* only compute once */
  if (_buf_alloc(out, alloclen, (*outlen)+addlen)!=SASL_OK)
    return SASL_NOMEM;

  strncpy(*out + *outlen, add, addlen);
  *outlen += addlen;

  return SASL_OK;
}

/* return the version of the cyrus sasl library as compiled,
 * using 32 bits: high byte is major version, second byte is minor version,
 * low 16 bits are step # */
void sasl_version(const char **implementation, int *version) 
{
#ifdef _SUN_SDK_
    const char *implementation_string = "Sun SASL";
#else
    const char *implementation_string = "Cyrus SASL";
#endif /* _SUN_SDK_ */
    if(implementation) *implementation = implementation_string;
    if(version) *version = (SASL_VERSION_MAJOR << 24) | 
		           (SASL_VERSION_MINOR << 16) |
		           (SASL_VERSION_STEP);
}

/* security-encode a regular string.  Mostly a wrapper for sasl_encodev */
/* output is only valid until next call to sasl_encode or sasl_encodev */
int sasl_encode(sasl_conn_t *conn, const char *input,
		unsigned inputlen,
		const char **output, unsigned *outputlen)
{
    int result;
    struct iovec tmp;

    if(!conn) return SASL_BADPARAM;
    if(!input || !inputlen || !output || !outputlen)
	PARAMERROR(conn);
    
    /* maxoutbuf checking is done in sasl_encodev */

    /* Note: We are casting a const pointer here, but it's okay
     * because we believe people downstream of us are well-behaved, and the
     * alternative is an absolute mess, performance-wise. */
    tmp.iov_base = (void *)input;
    tmp.iov_len = inputlen;
    
    result = sasl_encodev(conn, &tmp, 1, output, outputlen);

    RETURN(conn, result);
}

/* security-encode an iovec */
/* output is only valid until next call to sasl_encode or sasl_encodev */
int sasl_encodev(sasl_conn_t *conn,
		 const struct iovec *invec, unsigned numiov,
		 const char **output, unsigned *outputlen)
{
#ifdef _SUN_SDK_
    int result = SASL_FAIL;
#else
    int result;
#endif /* _SUN_SDK_ */
    unsigned i;
    size_t total_size = 0;

    /* EXPORT DELETE START */
    if (!conn) return SASL_BADPARAM;
    if (! invec || ! output || ! outputlen || numiov < 1)
	PARAMERROR(conn);

    if(!conn->props.maxbufsize) {
#ifdef _SUN_SDK_
	_sasl_log(conn, SASL_LOG_ERR,
		  "called sasl_encode[v] with application that does not support security layers");
#else
	sasl_seterror(conn, 0,
		      "called sasl_encode[v] with application that does not support security layers");
#endif /* _SUN_SDK_ */
	return SASL_TOOWEAK;
    }

    /* This might be better to check on a per-plugin basis, but I think
     * it's cleaner and more effective here.  It also encourages plugins
     * to be honest about what they accept */

    for(i=0; i<numiov;i++) {
#ifdef _SUN_SDK_
	if (invec[i].iov_base == NULL)
	    PARAMERROR(conn);
#endif /* _SUN_SDK_ */
	total_size += invec[i].iov_len;
    }    
    if(total_size > conn->oparams.maxoutbuf)
	PARAMERROR(conn);

    if(conn->oparams.encode == NULL)  {
#ifdef _SUN_SDK_
	result = _iovec_to_buf(conn->gctx, invec, numiov, &conn->encode_buf);
#else
	result = _iovec_to_buf(invec, numiov, &conn->encode_buf);
#endif /* _SUN_SDK_ */
	if(result != SASL_OK) INTERROR(conn, result);
       
	*output = conn->encode_buf->data;
	*outputlen = conn->encode_buf->curlen;

    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    } else if (!conn->sun_reg) {
	    INTERROR(conn, SASL_FAIL);
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    } else {
	result = conn->oparams.encode(conn->context, invec, numiov,
				      output, outputlen);
    }
    /* EXPORT DELETE END */

    RETURN(conn, result);
}
 
/* output is only valid until next call to sasl_decode */
int sasl_decode(sasl_conn_t *conn,
		const char *input, unsigned inputlen,
		const char **output, unsigned *outputlen)
{
    int result;
    /* EXPORT DELETE START */
#ifdef _SUN_SDK_
    const _sasl_global_context_t *gctx;
#endif /* _SUN_SDK_ */

    if(!conn) return SASL_BADPARAM;
    if(!input || !output || !outputlen)
	PARAMERROR(conn);

#ifdef _SUN_SDK_
    gctx = conn->gctx;
#endif /* _SUN_SDK_ */

    if(!conn->props.maxbufsize) {
#ifdef _SUN_SDK_
	_sasl_log(conn, SASL_LOG_ERR,
		  "called sasl_decode with application that does not support security layers");
#else
	sasl_seterror(conn, 0,
		      "called sasl_decode with application that does not support security layers");
#endif /* _SUN_SDK_ */
	RETURN(conn, SASL_TOOWEAK);
    }

    if(conn->oparams.decode == NULL)
    {
	/* Since we know how long the output is maximally, we can
	 * just allocate it to begin with, and never need another
         * allocation! */

	/* However, if they pass us more than they actually can take,
	 * we cannot help them... */
	if(inputlen > conn->props.maxbufsize) {
#ifdef _SUN_SDK_
	    _sasl_log(conn, SASL_LOG_ERR,
		      "input too large for default sasl_decode");
#else
	    sasl_seterror(conn, 0,
			  "input too large for default sasl_decode");
#endif /* _SUN_SDK_ */
	    RETURN(conn,SASL_BUFOVER);
	}

	if(!conn->decode_buf)
	    conn->decode_buf = sasl_ALLOC(conn->props.maxbufsize + 1);
	if(!conn->decode_buf)	
	    MEMERROR(conn);
	
	memcpy(conn->decode_buf, input, inputlen);
	conn->decode_buf[inputlen] = '\0';
	*output = conn->decode_buf;
	*outputlen = inputlen;
	
        return SASL_OK;
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    } else if (!conn->sun_reg) {
	    INTERROR(conn, SASL_FAIL);
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    } else {
        result = conn->oparams.decode(conn->context, input, inputlen,
                                      output, outputlen);

	/* NULL an empty buffer (for misbehaved applications) */
	if (*outputlen == 0) *output = NULL;

        RETURN(conn, result);
    }

    /* EXPORT DELETE END */
#ifdef _SUN_SDK_
    return SASL_FAIL;
#else
    INTERROR(conn, SASL_FAIL);
#endif	/* _SUN_SDK_ */
}


void
sasl_set_alloc(sasl_malloc_t *m,
	       sasl_calloc_t *c,
	       sasl_realloc_t *r,
	       sasl_free_t *f)
{
#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx =  _sasl_gbl_ctx();

  LOCK_MUTEX(&malloc_global_mutex);
  gctx->sasl_allocation_utils.malloc=m;
  gctx->sasl_allocation_utils.calloc=c;
  gctx->sasl_allocation_utils.realloc=r;
  gctx->sasl_allocation_utils.free=f;
  UNLOCK_MUTEX(&malloc_global_mutex);
#else
  _sasl_allocation_utils.malloc=m;
  _sasl_allocation_utils.calloc=c;
  _sasl_allocation_utils.realloc=r;
  _sasl_allocation_utils.free=f;
#endif /* _SUN_SDK_ */
}

void sasl_done(void)
{
#ifdef _SUN_SDK_
   _sasl_dispose_context(_sasl_gbl_ctx());
#else
    if (_sasl_server_cleanup_hook && _sasl_server_cleanup_hook() == SASL_OK) {
	_sasl_server_idle_hook = NULL;
	_sasl_server_cleanup_hook = NULL;
    }
    
    if (_sasl_client_cleanup_hook && _sasl_client_cleanup_hook() == SASL_OK) {
	_sasl_client_idle_hook = NULL;	
	_sasl_client_cleanup_hook = NULL;
    }
    
    if(_sasl_server_cleanup_hook || _sasl_client_cleanup_hook)
	return;
  
    
    _sasl_canonuser_free();
    _sasl_done_with_plugins();
    
#ifdef _SUN_SDK_
    sasl_config_free();
#endif /* _SUN_SDK_ */

    sasl_MUTEX_FREE(free_mutex);
    free_mutex = NULL;
    
    _sasl_free_utils(&sasl_global_utils);
    
    if(global_mech_list) sasl_FREE(global_mech_list);
    global_mech_list = NULL;
#endif /* _SUN_SDK_ */
}

/* fills in the base sasl_conn_t info */
int _sasl_conn_init(sasl_conn_t *conn,
		    const char *service,
		    unsigned int flags,
		    enum Sasl_conn_type type,
		    int (*idle_hook)(sasl_conn_t *conn),
		    const char *serverFQDN,
		    const char *iplocalport,
		    const char *ipremoteport,
		    const sasl_callback_t *callbacks,
		    const sasl_global_callbacks_t *global_callbacks) {
  int result = SASL_OK;
#ifdef _SUN_SDK_
  const _sasl_global_context_t *gctx = conn->gctx;
#endif /* _SUN_SDK_ */

  conn->type = type;

  result = _sasl_strdup(service, &conn->service, NULL);
  if (result != SASL_OK) 
      MEMERROR(conn);

  memset(&conn->oparams, 0, sizeof(sasl_out_params_t));
  memset(&conn->external, 0, sizeof(_sasl_external_properties_t));

  conn->flags = flags;

  result = sasl_setprop(conn, SASL_IPLOCALPORT, iplocalport);
  if(result != SASL_OK)
      RETURN(conn, result);
  
  result = sasl_setprop(conn, SASL_IPREMOTEPORT, ipremoteport);
  if(result != SASL_OK)
      RETURN(conn, result);
  
  conn->encode_buf = NULL;
  conn->context = NULL;
#ifndef _SUN_SDK_
  conn->secret = NULL;
#endif /* !_SUN_SDK_ */
  conn->idle_hook = idle_hook;
  conn->callbacks = callbacks;
  conn->global_callbacks = global_callbacks;

  memset(&conn->props, 0, sizeof(conn->props));

  /* Start this buffer out as an empty string */
  conn->error_code = SASL_OK;
  conn->errdetail_buf = conn->error_buf = NULL;
  conn->errdetail_buf_len = conn->error_buf_len = 150;

  result = _buf_alloc(&conn->error_buf, &conn->error_buf_len, 150);     
  if(result != SASL_OK) MEMERROR(conn);
  result = _buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, 150);
  if(result != SASL_OK) MEMERROR(conn);
  
  conn->error_buf[0] = '\0';
  conn->errdetail_buf[0] = '\0';
  
  conn->decode_buf = NULL;

  if(serverFQDN) {
      result = _sasl_strdup(serverFQDN, &conn->serverFQDN, NULL);
  } else if (conn->type == SASL_CONN_SERVER) {
      /* We can fake it because we *are* the server */
      char name[MAXHOSTNAMELEN];
      memset(name, 0, sizeof(name));
      gethostname(name, MAXHOSTNAMELEN);
      
      result = _sasl_strdup(name, &conn->serverFQDN, NULL);
  } else {
      conn->serverFQDN = NULL;
  }
  

  if(result != SASL_OK) MEMERROR( conn );

#ifdef _SUN_SDK_
  return (SASL_OK);
#else
  RETURN(conn, SASL_OK);
#endif /* _SUN_SDK_ */
}

#ifdef _SUN_SDK_
int _sasl_common_init(_sasl_global_context_t *gctx,
		      sasl_global_callbacks_t *global_callbacks,
		      int server)
{
    int result;
    sasl_utils_t *sasl_global_utils;

    sasl_global_utils = (sasl_utils_t *)gctx->sasl_canonusr_global_utils;

    if(!sasl_global_utils) {
        sasl_global_utils = _sasl_alloc_utils(gctx, NULL, global_callbacks);
        if(sasl_global_utils == NULL) return SASL_NOMEM;
	gctx->sasl_canonusr_global_utils = sasl_global_utils;
    }

    if (server) {
	sasl_global_utils = (sasl_utils_t *)gctx->sasl_server_global_utils;

	if(!sasl_global_utils) {
            sasl_global_utils = _sasl_alloc_utils(gctx, NULL, global_callbacks);
            if(sasl_global_utils == NULL) return SASL_NOMEM;
	    gctx->sasl_server_global_utils = sasl_global_utils;
	}
    }

    /* Init the canon_user plugin */
    result = _sasl_canonuser_add_plugin(gctx, "INTERNAL",
	internal_canonuser_init);
    if(result != SASL_OK) return result;

    if (!gctx->free_mutex)
        gctx->free_mutex = sasl_MUTEX_ALLOC();
    if (!gctx->free_mutex) return SASL_FAIL;

    return SASL_OK;
}
#else
int _sasl_common_init(sasl_global_callbacks_t *global_callbacks)
{
    int result;
    
    /* Setup the global utilities */
    if(!sasl_global_utils) {
	sasl_global_utils = _sasl_alloc_utils(NULL, global_callbacks);
	if(sasl_global_utils == NULL) return SASL_NOMEM;
    }

    /* Init the canon_user plugin */
    result = sasl_canonuser_add_plugin("INTERNAL", internal_canonuser_init);
    if(result != SASL_OK) return result;    

    if (!free_mutex)
	free_mutex = sasl_MUTEX_ALLOC();
    if (!free_mutex) return SASL_FAIL;

    return SASL_OK;
}
#endif /* _SUN_SDK_ */

/* dispose connection state, sets it to NULL
 *  checks for pointer to NULL
 */
void sasl_dispose(sasl_conn_t **pconn)
{
  int result;
#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx;
  void *free_mutex;
#endif /* _SUN_SDK_ */

  if (! pconn) return;
  if (! *pconn) return;

  /* serialize disposes. this is necessary because we can't
     dispose of conn->mutex if someone else is locked on it */
#ifdef _SUN_SDK_
  gctx = (*pconn)->gctx;
  free_mutex = gctx->free_mutex;
#endif /* _SUN_SDK_ */
  result = sasl_MUTEX_LOCK(free_mutex);
  if (result!=SASL_OK) return;
  
  /* *pconn might have become NULL by now */
#ifdef _SUN_SDK_
  if (! (*pconn)) {
	sasl_MUTEX_UNLOCK(free_mutex);
	return;
  }
#else
  if (! (*pconn)) return;
#endif /* _SUN_SDK_ */

  (*pconn)->destroy_conn(*pconn);
  sasl_FREE(*pconn);
  *pconn=NULL;

  sasl_MUTEX_UNLOCK(free_mutex);
}

void _sasl_conn_dispose(sasl_conn_t *conn) {
#ifdef _SUN_SDK_
  const _sasl_global_context_t *gctx = conn->gctx;
#endif /* _SUN_SDK_ */

  if (conn->serverFQDN)
      sasl_FREE(conn->serverFQDN);

  if (conn->external.auth_id)
      sasl_FREE(conn->external.auth_id);

  if(conn->encode_buf) {
      if(conn->encode_buf->data) sasl_FREE(conn->encode_buf->data);
      sasl_FREE(conn->encode_buf);
  }

  if(conn->error_buf)
      sasl_FREE(conn->error_buf);
  
  if(conn->errdetail_buf)
      sasl_FREE(conn->errdetail_buf);

  if(conn->decode_buf)
      sasl_FREE(conn->decode_buf);

  if(conn->mechlist_buf)
      sasl_FREE(conn->mechlist_buf);

  if(conn->service)
      sasl_FREE(conn->service);

  /* oparams sub-members should be freed by the plugin, in so much
   * as they were allocated by the plugin */
}


/* get property from SASL connection state
 *  propnum       -- property number
 *  pvalue        -- pointer to value
 * returns:
 *  SASL_OK       -- no error
 *  SASL_NOTDONE  -- property not available yet
 *  SASL_BADPARAM -- bad property number
 */
int sasl_getprop(sasl_conn_t *conn, int propnum, const void **pvalue)
{
  int result = SASL_OK;
  sasl_getopt_t *getopt;
  void *context;
  
  if (! conn) return SASL_BADPARAM;
  if (! pvalue) PARAMERROR(conn);

  switch(propnum)
  {
  case SASL_SSF:
    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
      if (!conn->sun_reg)
	conn->oparams.mech_ssf = 0;
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
      *(sasl_ssf_t **)pvalue= &conn->oparams.mech_ssf;
      break;      
  case SASL_MAXOUTBUF:
      *(unsigned **)pvalue = &conn->oparams.maxoutbuf;
      break;
  case SASL_GETOPTCTX:
      result = _sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context);
      if(result != SASL_OK) break;
      
      *(void **)pvalue = context;
      break;
  case SASL_CALLBACK:
      *(const sasl_callback_t **)pvalue = conn->callbacks;
      break;
  case SASL_IPLOCALPORT:
      if(conn->got_ip_local)
	  *(const char **)pvalue = conn->iplocalport;
      else {
	  *(const char **)pvalue = NULL;
	  result = SASL_NOTDONE;
      }
      break;
  case SASL_IPREMOTEPORT:
      if(conn->got_ip_remote)
	  *(const char **)pvalue = conn->ipremoteport;
      else {
	  *(const char **)pvalue = NULL;
	  result = SASL_NOTDONE;
      }	  
      break;
  case SASL_USERNAME:
      if(! conn->oparams.user)
	  result = SASL_NOTDONE;
      else
	  *((const char **)pvalue) = conn->oparams.user;
      break;
  case SASL_AUTHUSER:
      if(! conn->oparams.authid)
	  result = SASL_NOTDONE;
      else
	  *((const char **)pvalue) = conn->oparams.authid;
      break;
  case SASL_SERVERFQDN:
      *((const char **)pvalue) = conn->serverFQDN;
      break;
  case SASL_DEFUSERREALM:
      if(conn->type != SASL_CONN_SERVER) result = SASL_BADPROT;
      else
	  *((const char **)pvalue) = ((sasl_server_conn_t *)conn)->user_realm;
      break;
  case SASL_SERVICE:
      *((const char **)pvalue) = conn->service;
      break;
  case SASL_AUTHSOURCE: /* name of plugin (not name of mech) */
      if(conn->type == SASL_CONN_CLIENT) {
	  if(!((sasl_client_conn_t *)conn)->mech) {
	      result = SASL_NOTDONE;
	      break;
	  }
	  *((const char **)pvalue) =
	      ((sasl_client_conn_t *)conn)->mech->plugname;
      } else if (conn->type == SASL_CONN_SERVER) {
	  if(!((sasl_server_conn_t *)conn)->mech) {
	      result = SASL_NOTDONE;
	      break;
	  }
	  *((const char **)pvalue) =
	      ((sasl_server_conn_t *)conn)->mech->plugname;
      } else {
	  result = SASL_BADPARAM;
      }
      break;
  case SASL_MECHNAME: /* name of mech */
      if(conn->type == SASL_CONN_CLIENT) {
	  if(!((sasl_client_conn_t *)conn)->mech) {
	      result = SASL_NOTDONE;
	      break;
	  }
	  *((const char **)pvalue) =
	      ((sasl_client_conn_t *)conn)->mech->plug->mech_name;
      } else if (conn->type == SASL_CONN_SERVER) {
	  if(!((sasl_server_conn_t *)conn)->mech) {
	      result = SASL_NOTDONE;
	      break;
	  }
	  *((const char **)pvalue) =
	      ((sasl_server_conn_t *)conn)->mech->plug->mech_name;
      } else {
	  result = SASL_BADPARAM;
      }
      
      if(!(*pvalue) && result == SASL_OK) result = SASL_NOTDONE;
      break;
  case SASL_PLUGERR:
      *((const char **)pvalue) = conn->error_buf;
      break;
  case SASL_SSF_EXTERNAL:
      *((const sasl_ssf_t **)pvalue) = &conn->external.ssf;
      break;
  case SASL_AUTH_EXTERNAL:
      *((const char **)pvalue) = conn->external.auth_id;
      break;
  case SASL_SEC_PROPS:
      *((const sasl_security_properties_t **)pvalue) = &conn->props;
      break;
  default: 
      result = SASL_BADPARAM;
  }

  if(result == SASL_BADPARAM) {
      PARAMERROR(conn);
  } else if(result == SASL_NOTDONE) {
#ifdef _SUN_SDK_
      _sasl_log(conn, SASL_LOG_NONE,
		"Information that was requested is not yet available.");
#else
      sasl_seterror(conn, SASL_NOLOG,
		    "Information that was requested is not yet available.");
#endif /* _SUN_SDK_ */
      RETURN(conn, result);
  } else if(result != SASL_OK) {
      INTERROR(conn, result);
  } else
      RETURN(conn, result); 
#ifdef _SUN_SDK_
  return SASL_OK;
#endif /* _SUN_SDK_ */
}

/* set property in SASL connection state
 * returns:
 *  SASL_OK       -- value set
 *  SASL_BADPARAM -- invalid property or value
 */
int sasl_setprop(sasl_conn_t *conn, int propnum, const void *value)
{
  int result = SASL_OK;
  char *str;
#ifdef _SUN_SDK_
  const _sasl_global_context_t *gctx;
#endif	/* _SUN_SDK_ */

  /* make sure the sasl context is valid */
  if (!conn)
    return SASL_BADPARAM;

#ifdef _SUN_SDK_
  gctx = conn->gctx;
#endif	/* _SUN_SDK_ */

  switch(propnum)
  {
  case SASL_SSF_EXTERNAL:
      conn->external.ssf = *((sasl_ssf_t *)value);
      if(conn->type == SASL_CONN_SERVER) {
	((sasl_server_conn_t*)conn)->sparams->external_ssf =
	  conn->external.ssf;
      } else {
	((sasl_client_conn_t*)conn)->cparams->external_ssf =
	  conn->external.ssf;
      }
      break;

  case SASL_AUTH_EXTERNAL:
      if(value && strlen(value)) {
	  result = _sasl_strdup(value, &str, NULL);
	  if(result != SASL_OK) MEMERROR(conn);
      } else {
	  str = NULL;
      }

      if(conn->external.auth_id)
	  sasl_FREE(conn->external.auth_id);

      conn->external.auth_id = str;

      break;

  case SASL_DEFUSERREALM:
      if(conn->type != SASL_CONN_SERVER) {
#ifdef _SUN_SDK_
	_sasl_log(conn, SASL_LOG_WARN,
		  "Tried to set realm on non-server connection");
#else
	sasl_seterror(conn, 0, "Tried to set realm on non-server connection");
#endif /* _SUN_SDK_ */
	result = SASL_BADPROT;
	break;
      }

      if(value && strlen(value)) {
	  result = _sasl_strdup(value, &str, NULL);
	  if(result != SASL_OK) MEMERROR(conn);
      } else {
	  PARAMERROR(conn);
      }

      if(((sasl_server_conn_t *)conn)->user_realm)
      	  sasl_FREE(((sasl_server_conn_t *)conn)->user_realm);

      ((sasl_server_conn_t *)conn)->user_realm = str;
      ((sasl_server_conn_t *)conn)->sparams->user_realm = str;

      break;

  case SASL_SEC_PROPS:
  {
      sasl_security_properties_t *props = (sasl_security_properties_t *)value;

      if(props->maxbufsize == 0 && props->min_ssf != 0) {
#ifdef _SUN_SDK_
	  _sasl_log(conn, SASL_LOG_ERR,
		    "Attempt to disable security layers (maxoutbuf == 0) with min_ssf > 0");
#else
	  sasl_seterror(conn, 0,
			"Attempt to disable security layers (maxoutbuf == 0) with min_ssf > 0");
#endif /* _SUN_SDK_ */
	  RETURN(conn, SASL_TOOWEAK);
      }

      conn->props = *props;

      if(conn->type == SASL_CONN_SERVER) {
	((sasl_server_conn_t*)conn)->sparams->props = *props;
      } else {
	((sasl_client_conn_t*)conn)->cparams->props = *props;
      }

      break;
  }
      
  case SASL_IPREMOTEPORT:
  {
      const char *ipremoteport = (const char *)value;
      if(!value) {
	  conn->got_ip_remote = 0; 
#ifdef _SUN_SDK_
      } else if (strlen(ipremoteport) >= sizeof (conn->ipremoteport)) {
	  RETURN(conn, SASL_BADPARAM);
#endif /* _SUN_SDK_ */
      } else if (_sasl_ipfromstring(ipremoteport, NULL, 0)
		 != SASL_OK) {
#ifdef _SUN_SDK_
	  _sasl_log(conn, SASL_LOG_ERR, "Bad IPREMOTEPORT value");
#else
	  sasl_seterror(conn, 0, "Bad IPREMOTEPORT value");
#endif /* _SUN_SDK_ */
	  RETURN(conn, SASL_BADPARAM);
      } else {
	  strcpy(conn->ipremoteport, ipremoteport);
	  conn->got_ip_remote = 1;
      }
      
      if(conn->got_ip_remote) {
	  if(conn->type == SASL_CONN_CLIENT) {
	      ((sasl_client_conn_t *)conn)->cparams->ipremoteport
		  = conn->ipremoteport;
	      ((sasl_client_conn_t *)conn)->cparams->ipremlen =
		  strlen(conn->ipremoteport);
	  } else if (conn->type == SASL_CONN_SERVER) {
	      ((sasl_server_conn_t *)conn)->sparams->ipremoteport
		  = conn->ipremoteport;
	      ((sasl_server_conn_t *)conn)->sparams->ipremlen =
		  strlen(conn->ipremoteport);
	  }
      } else {
	  if(conn->type == SASL_CONN_CLIENT) {
	      ((sasl_client_conn_t *)conn)->cparams->ipremoteport
		  = NULL;
	      ((sasl_client_conn_t *)conn)->cparams->ipremlen = 0;
	  } else if (conn->type == SASL_CONN_SERVER) {
	      ((sasl_server_conn_t *)conn)->sparams->ipremoteport
		  = NULL;	      
	      ((sasl_server_conn_t *)conn)->sparams->ipremlen = 0;
	  }
      }

      break;
  }

  case SASL_IPLOCALPORT:
  {
      const char *iplocalport = (const char *)value;
      if(!value) {
	  conn->got_ip_local = 0;	  
#ifdef _SUN_SDK_
      } else if (strlen(iplocalport) >= sizeof (conn->iplocalport)) {
	  RETURN(conn, SASL_BADPARAM);
#endif /* _SUN_SDK_ */
      } else if (_sasl_ipfromstring(iplocalport, NULL, 0)
		 != SASL_OK) {
#ifdef _SUN_SDK_
	  _sasl_log(conn, SASL_LOG_ERR, "Bad IPLOCALPORT value");
#else
	  sasl_seterror(conn, 0, "Bad IPLOCALPORT value");
#endif /* _SUN_SDK_ */
	  RETURN(conn, SASL_BADPARAM);
      } else {
	  strcpy(conn->iplocalport, iplocalport);
	  conn->got_ip_local = 1;
      }

      if(conn->got_ip_local) {
	  if(conn->type == SASL_CONN_CLIENT) {
	      ((sasl_client_conn_t *)conn)->cparams->iplocalport
		  = conn->iplocalport;
	      ((sasl_client_conn_t *)conn)->cparams->iploclen
		  = strlen(conn->iplocalport);
	  } else if (conn->type == SASL_CONN_SERVER) {
	      ((sasl_server_conn_t *)conn)->sparams->iplocalport
		  = conn->iplocalport;
	      ((sasl_server_conn_t *)conn)->sparams->iploclen
		  = strlen(conn->iplocalport);
	  }
      } else {
	  if(conn->type == SASL_CONN_CLIENT) {
	      ((sasl_client_conn_t *)conn)->cparams->iplocalport
		  = NULL;
	      ((sasl_client_conn_t *)conn)->cparams->iploclen = 0;
	  } else if (conn->type == SASL_CONN_SERVER) {
	      ((sasl_server_conn_t *)conn)->sparams->iplocalport
		  = NULL;
	      ((sasl_server_conn_t *)conn)->sparams->iploclen = 0;
	  }
      }
      break;
  }

  default:
#ifdef _SUN_SDK_
      _sasl_log(conn, SASL_LOG_WARN, "Unknown parameter type");
#else
      sasl_seterror(conn, 0, "Unknown parameter type");
#endif /* _SUN_SDK_ */
      result = SASL_BADPARAM;
  }
  
  RETURN(conn, result);
}

/* this is apparently no longer a user function */
static int sasl_usererr(int saslerr)
{
    /* Hide the difference in a username failure and a password failure */
    if (saslerr == SASL_NOUSER)
	return SASL_BADAUTH;

    /* otherwise return the error given; no transform necessary */
    return saslerr;
}

#ifdef _INTEGRATED_SOLARIS_
static void free_err_tsd(void *key)
{
    free(key);
}
#endif /* _INTEGRATED_SOLARIS_ */

const char *sasl_errstring(int saslerr,
#ifdef _SUN_SDK_
			   const char *langlist,
#else
			   const char *langlist __attribute__((unused)),
#endif /* _SUN_SDK_ */
			   const char **outlang)
{
#ifdef _INTEGRATED_SOLARIS_
  const char *s;
  const char *s_locale;
  char *s_utf8;
  void *tsd;

  if (outlang) *outlang="i-default";
#else
  if (outlang) *outlang="en-us";
#endif /* _INTEGRATED_SOLARIS_ */

#ifdef _INTEGRATED_SOLARIS_
  switch(saslerr)
    {
    case SASL_CONTINUE: s = gettext("another step is needed in authentication");
	break;
    case SASL_OK:       s = gettext("successful result");
	break;
    case SASL_FAIL:     s = gettext("generic failure");
	break;
    case SASL_NOMEM:    s = gettext("no memory available");
	break;
    case SASL_BUFOVER:  s = gettext("overflowed buffer");
	break;
    case SASL_NOMECH:   s = gettext("no mechanism available");
	break;
    case SASL_BADPROT:  s = gettext("bad protocol / cancel");
	break;
    case SASL_NOTDONE:  s = gettext("can't request info until later in exchange");
	break;
    case SASL_BADPARAM: s = gettext("invalid parameter supplied");
	break;
    case SASL_TRYAGAIN: s = gettext("transient failure (e.g., weak key)");
	break;
    case SASL_BADMAC:   s = gettext("integrity check failed");
	break;
    case SASL_NOTINIT:  s = gettext("SASL library not initialized");
	break;
                             /* -- client only codes -- */
    case SASL_INTERACT:   s = gettext("needs user interaction");
	break;
    case SASL_BADSERV:    s = gettext("server failed mutual authentication step");
	break;
    case SASL_WRONGMECH:  s = gettext("mechanism doesn't support requested feature");
	break;
                             /* -- server only codes -- */
    case SASL_BADAUTH:    s = gettext("authentication failure");
	break;
    case SASL_NOAUTHZ:    s = gettext("authorization failure");
	break;
    case SASL_TOOWEAK:    s = gettext("mechanism too weak for this user");
	break;
    case SASL_ENCRYPT:    s = gettext("encryption needed to use mechanism");
	break;
    case SASL_TRANS:      s = gettext("One time use of a plaintext password will enable requested mechanism for user");
	break;
    case SASL_EXPIRED:    s = gettext("passphrase expired, has to be reset");
	break;
    case SASL_DISABLED:   s = gettext("account disabled");
	break;
    case SASL_NOUSER:     s = gettext("user not found");
	break;
    case SASL_BADVERS:    s = gettext("version mismatch with plug-in");
	break;
    case SASL_UNAVAIL:    s = gettext("remote authentication server unavailable");
	break;
    case SASL_NOVERIFY:   s = gettext("user exists, but no verifier for user");
	break;
    case SASL_PWLOCK:     s = gettext("passphrase locked");
	break;
    case SASL_NOCHANGE:   s = gettext("requested change was not needed");
	break;
    case SASL_WEAKPASS:   s = gettext("passphrase is too weak for security policy");
	break;
    case SASL_NOUSERPASS: s = gettext("user supplied passwords are not permitted");

	break;
    default:   s = gettext("undefined error!");
	break;
  }
 
  if (use_locale(langlist, 0))
    s_locale = dgettext(TEXT_DOMAIN, s);
  else
    s_locale = s;

  if (s == s_locale)
    return s;

  s_utf8 = local_to_utf(NULL, s_locale);
  if (s_utf8 == NULL)
    return s;

  if (pthread_key_create_once_np(&errstring_key, free_err_tsd) != 0) {
    free(s_utf8);
    return s;
  }

  tsd = pthread_getspecific(errstring_key);
  if (tsd != NULL)
    free(tsd);
  pthread_setspecific(errstring_key, s_utf8);

  if (outlang) *outlang="*";
  return s_utf8;
#else
  switch(saslerr)
    {
    case SASL_CONTINUE: return "another step is needed in authentication";
    case SASL_OK:       return "successful result";
    case SASL_FAIL:     return "generic failure";
    case SASL_NOMEM:    return "no memory available";
    case SASL_BUFOVER:  return "overflowed buffer";
    case SASL_NOMECH:   return "no mechanism available";
    case SASL_BADPROT:  return "bad protocol / cancel";
    case SASL_NOTDONE:  return "can't request info until later in exchange";
    case SASL_BADPARAM: return "invalid parameter supplied";
    case SASL_TRYAGAIN: return "transient failure (e.g., weak key)";
    case SASL_BADMAC:   return "integrity check failed";
    case SASL_NOTINIT:  return "SASL library not initialized";
                             /* -- client only codes -- */
    case SASL_INTERACT:   return "needs user interaction";
    case SASL_BADSERV:    return "server failed mutual authentication step";
    case SASL_WRONGMECH:  return "mechanism doesn't support requested feature";
                             /* -- server only codes -- */
    case SASL_BADAUTH:    return "authentication failure";
    case SASL_NOAUTHZ:    return "authorization failure";
    case SASL_TOOWEAK:    return "mechanism too weak for this user";
    case SASL_ENCRYPT:    return "encryption needed to use mechanism";
    case SASL_TRANS:      return "One time use of a plaintext password will enable requested mechanism for user";
    case SASL_EXPIRED:    return "passphrase expired, has to be reset";
    case SASL_DISABLED:   return "account disabled";
    case SASL_NOUSER:     return "user not found";
    case SASL_BADVERS:    return "version mismatch with plug-in";
    case SASL_UNAVAIL:    return "remote authentication server unavailable";
    case SASL_NOVERIFY:   return "user exists, but no verifier for user";
    case SASL_PWLOCK:     return "passphrase locked";
    case SASL_NOCHANGE:   return "requested change was not needed";
    case SASL_WEAKPASS:   return "passphrase is too weak for security policy";
    case SASL_NOUSERPASS: return "user supplied passwords are not permitted";

    default:   return "undefined error!";
    }
#endif /* _INTEGRATED_SOLARIS_ */

}

/* Return the sanitized error detail about the last error that occured for 
 * a connection */
const char *sasl_errdetail(sasl_conn_t *conn) 
{
    unsigned need_len;
    const char *errstr;
    char leader[128];
#ifdef _SUN_SDK_
    int ret;
    const _sasl_global_context_t *gctx;

    if(!conn) return "invalid parameter supplied";

    gctx = conn->gctx;
#else
    if(!conn) return NULL;
#endif /* _SUN_SDK_ */
    
    errstr = sasl_errstring(conn->error_code, NULL, NULL);
    snprintf(leader,128,"SASL(%d): %s: ",
	     sasl_usererr(conn->error_code), errstr);
    
    need_len = strlen(leader) + strlen(conn->error_buf) + 12;
#ifdef _SUN_SDK_
    ret = _buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, need_len);
    if (ret != SASL_OK)
	return "no memory available";
#else
    _buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, need_len);
#endif /* _SUN_SDK_ */

    snprintf(conn->errdetail_buf, need_len, "%s%s", leader, conn->error_buf);
   
    return conn->errdetail_buf;
}

/* EXPORT DELETE START */
/* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
DEFINE_STATIC_MUTEX(reg_mutex);
typedef struct reg_list {
	struct reg_list *next;
	void *mech;
} reg_list_t;

static reg_list_t *reg_list_base = NULL;

int _is_sun_reg(void *mech)
{
	reg_list_t *r, *prev;
	int is_reg = 0;

	LOCK_MUTEX(&reg_mutex);
	for (r = reg_list_base; r != NULL; r = r->next) {
		if (r->mech != mech) {
			prev = r;
			continue;
		}
		is_reg = 1;
		if (r == reg_list_base) {
			reg_list_base = reg_list_base->next;
		} else {
			prev->next = r->next;
		}
		free(r);
		break;
	}
	UNLOCK_MUTEX(&reg_mutex);
	return (is_reg);
}

static void
_register_plugin(void *arg)
{
	reg_list_t *r = (reg_list_t *)calloc(1, sizeof (reg_list_t));

	if (r != NULL) {
		r->mech = arg;
		LOCK_MUTEX(&reg_mutex);
		r->next = reg_list_base;
		reg_list_base = r;
		UNLOCK_MUTEX(&reg_mutex);
	}
}
#endif /* _INTEGRATED_SOLARIS_ */
/* CRYPT DELETE END */
/* EXPORT DELETE END */

/* Note that this needs the global callbacks, so if you don't give getcallbacks
 * a sasl_conn_t, you're going to need to pass it yourself (or else we couldn't
 * have client and server at the same time */
static int _sasl_global_getopt(void *context,
			       const char *plugin_name,
			       const char *option,
			       const char ** result,
			       unsigned *len)
{
  const sasl_global_callbacks_t * global_callbacks;
  const sasl_callback_t *callback;
#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx;
#endif /* _SUN_SDK_ */

  global_callbacks = (const sasl_global_callbacks_t *) context;

#ifdef _SUN_SDK_
  /* EXPORT DELETE START */
  /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
  if (strcmp("reg_sun_plug", option) == 0) {
        *result = (const char *)_register_plugin;
        *len = 0;
        return (SASL_OK);
  }
#endif /* _INTEGRATED_SOLARIS_ */
  /* CRYPT DELETE END */
  /* EXPORT DELETE END */

  if (global_callbacks)
    gctx = global_callbacks->gctx;
  else
    gctx = _sasl_gbl_ctx();
#endif /* _SUN_SDK_ */

  if (global_callbacks && global_callbacks->callbacks) {
      for (callback = global_callbacks->callbacks;
	   callback->id != SASL_CB_LIST_END;
	   callback++) {
	if (callback->id == SASL_CB_GETOPT) {
	  if (!callback->proc) return SASL_FAIL;
	  if (((sasl_getopt_t *)(callback->proc))(callback->context,
						  plugin_name,
						  option,
						  result,
						  len)
	      == SASL_OK)
	    return SASL_OK;
	}
      }
  }
  
  /* look it up in our configuration file */
#ifdef _SUN_SDK_
  *result = sasl_config_getstring(gctx, option, NULL);
#else
  *result = sasl_config_getstring(option, NULL);
#endif /* _SUN_SDK_ */
  if (*result != NULL) {
      if (len) { *len = strlen(*result); }
      return SASL_OK;
  }

  return SASL_FAIL;
}

static int
_sasl_conn_getopt(void *context,
		  const char *plugin_name,
		  const char *option,
		  const char ** result,
		  unsigned *len)
{
  sasl_conn_t * conn;
  const sasl_callback_t *callback;

  if (! context)
    return SASL_BADPARAM;

  conn = (sasl_conn_t *) context;

  if (conn->callbacks)
    for (callback = conn->callbacks;
	 callback->id != SASL_CB_LIST_END;
	 callback++)
      if (callback->id == SASL_CB_GETOPT
	  && (((sasl_getopt_t *)(callback->proc))(callback->context,
						  plugin_name,
						  option,
						  result,
						  len)
	      == SASL_OK))
	return SASL_OK;

  /* If we made it here, we didn't find an appropriate callback
   * in the connection's callback list, or the callback we did
   * find didn't return SASL_OK.  So we attempt to use the
   * global callback for this connection... */
  return _sasl_global_getopt((void *)conn->global_callbacks,
			     plugin_name,
			     option,
			     result,
			     len);
}

#ifdef HAVE_SYSLOG
/* this is the default logging */
static int _sasl_syslog(void *context __attribute__((unused)),
			int priority,
			const char *message)
{
    int syslog_priority;

    /* set syslog priority */
    switch(priority) {
    case SASL_LOG_NONE:
	return SASL_OK;
	break;
    case SASL_LOG_ERR:
	syslog_priority = LOG_ERR;
	break;
    case SASL_LOG_WARN:
	syslog_priority = LOG_WARNING;
	break;
    case SASL_LOG_NOTE:
    case SASL_LOG_FAIL:
	syslog_priority = LOG_NOTICE;
	break;
    case SASL_LOG_PASS:
    case SASL_LOG_TRACE:
    case SASL_LOG_DEBUG:
    default:
	syslog_priority = LOG_DEBUG;
	break;
    }
    
    /* do the syslog call. do not need to call openlog */
    syslog(syslog_priority | LOG_AUTH, "%s", message);
    
    return SASL_OK;
}
#endif				/* HAVE_SYSLOG */

static int
_sasl_getsimple(void *context,
		int id,
		const char ** result,
		size_t *len)
{
  const char *userid;
#ifndef _SUN_SDK_
  sasl_conn_t *conn;
#endif /* _SUN_SDK_ */

  if (! context || ! result) return SASL_BADPARAM;

#ifndef _SUN_SDK_
  conn = (sasl_conn_t *)context;
#endif /* _SUN_SDK_ */

  switch(id) {
  case SASL_CB_AUTHNAME:
#ifdef _INTEGRATED_SOLARIS_
    userid = getenv("LOGNAME");
    if (userid != NULL) {
	*result = userid;
	if (len) *len = strlen(userid);
	return SASL_OK;
    }
#else
    userid = getenv("USER");
    if (userid != NULL) {
	*result = userid;
	if (len) *len = strlen(userid);
	return SASL_OK;
    }
    userid = getenv("USERNAME");
    if (userid != NULL) {
	*result = userid;
	if (len) *len = strlen(userid);
	return SASL_OK;
    }
#endif /* _INTEGRATED_SOLARIS_ */
#ifdef WIN32
    /* for win32, try using the GetUserName standard call */
    {
	DWORD i;
	BOOL rval;
	static char sender[128];
	
	i = sizeof(sender);
	rval = GetUserName(sender, &i);
	if ( rval) { /* got a userid */
		*result = sender;
		if (len) *len = strlen(sender);
		return SASL_OK;
	}
    }
#endif /* WIN32 */
    return SASL_FAIL;
  default:
    return SASL_BADPARAM;
  }
}

static int
_sasl_verifyfile(void *context __attribute__((unused)),
		 char *file  __attribute__((unused)),
		 int type  __attribute__((unused)))
{
  /* always say ok */
  return SASL_OK;
}


static int
_sasl_proxy_policy(sasl_conn_t *conn,
		   void *context __attribute__((unused)),
		   const char *requested_user, unsigned rlen,
		   const char *auth_identity, unsigned alen,
		   const char *def_realm __attribute__((unused)),
		   unsigned urlen __attribute__((unused)),
		   struct propctx *propctx __attribute__((unused)))
{
    if (!conn)
	return SASL_BADPARAM;

    if (!requested_user || *requested_user == '\0')
	return SASL_OK;

    if (!auth_identity || !requested_user || rlen != alen ||
	(memcmp(auth_identity, requested_user, rlen) != 0)) {
#ifdef _INTEGRATED_SOLARIS_
	sasl_seterror(conn, 0,
		      gettext("Requested identity not authenticated identity"));
#else
	sasl_seterror(conn, 0,
		      "Requested identity not authenticated identity");
#endif /* _INTEGRATED_SOLARIS_ */
	RETURN(conn, SASL_BADAUTH);
    }

    return SASL_OK;
}

int _sasl_getcallback(sasl_conn_t * conn,
		      unsigned long callbackid,
		      int (**pproc)(),
		      void **pcontext)
{
  const sasl_callback_t *callback;

  if (!pproc || !pcontext)
      PARAMERROR(conn);

  /* Some callbacks are always provided by the library */
  switch (callbackid) {
  case SASL_CB_LIST_END:
    /* Nothing ever gets to provide this */
      INTERROR(conn, SASL_FAIL);
#ifdef _SUN_SDK_
      break;
#endif /* _SUN_SDK_ */
  case SASL_CB_GETOPT:
      if (conn) {
	  *pproc = &_sasl_conn_getopt;
	  *pcontext = conn;
      } else {
	  *pproc = &_sasl_global_getopt;
	  *pcontext = NULL;
      }
      return SASL_OK;
  }

  /* If it's not always provided by the library, see if there's
   * a version provided by the application for this connection... */
  if (conn && conn->callbacks) {
    for (callback = conn->callbacks; callback->id != SASL_CB_LIST_END;
	 callback++) {
	if (callback->id == callbackid) {
	    *pproc = callback->proc;
	    *pcontext = callback->context;
	    if (callback->proc) {
		return SASL_OK;
	    } else {
		return SASL_INTERACT;
	    }
	}
    }
  }

  /* And, if not for this connection, see if there's one
   * for all {server,client} connections... */
  if (conn && conn->global_callbacks && conn->global_callbacks->callbacks) {
      for (callback = conn->global_callbacks->callbacks;
	   callback->id != SASL_CB_LIST_END;
	   callback++) {
	  if (callback->id == callbackid) {
	      *pproc = callback->proc;
	      *pcontext = callback->context;
	      if (callback->proc) {
		  return SASL_OK;
	      } else {
		  return SASL_INTERACT;
	      }
	  }
      }
  }

  /* Otherwise, see if the library provides a default callback. */
  switch (callbackid) {
#ifdef HAVE_SYSLOG
  case SASL_CB_LOG:
    *pproc = (int (*)()) &_sasl_syslog;
    *pcontext = NULL;
    return SASL_OK;
#endif /* HAVE_SYSLOG */
  case SASL_CB_GETPATH:
    *pproc = (int (*)()) &_sasl_getpath;
    *pcontext = NULL;
    return SASL_OK;
  case SASL_CB_AUTHNAME:
    *pproc = (int (*)()) &_sasl_getsimple;
    *pcontext = conn;
    return SASL_OK;
  case SASL_CB_VERIFYFILE:
    *pproc = & _sasl_verifyfile;
    *pcontext = NULL;
    return SASL_OK;
  case SASL_CB_PROXY_POLICY:
    *pproc = (int (*)()) &_sasl_proxy_policy;
    *pcontext = NULL;
    return SASL_OK;
  }

  /* Unable to find a callback... */
  *pproc = NULL;
  *pcontext = NULL;
#ifdef _SUN_SDK_
  if (callbackid != SASL_CB_LANGUAGE)
    _sasl_log(conn, SASL_LOG_NONE, "Unable to find a callback: %d", callbackid);
#else
  sasl_seterror(conn, SASL_NOLOG, "Unable to find a callback: %d", callbackid);
#endif /* _SUN_SDK_ */
  RETURN(conn,SASL_FAIL);
}


#ifdef _SUN_SDK_
static void ___sasl_log (const _sasl_global_context_t *gctx,
			sasl_log_t *log_cb, void *log_ctx,
			int level, const char *fmt, va_list ap);
#endif /* _SUN_SDK_ */
/*
 * This function is typically called from a plugin.
 * It creates a string from the formatting and varargs given
 * and calls the logging callback (syslog by default)
 *
 * %m will parse the value in the next argument as an errno string
 * %z will parse the next argument as a SASL error code.
 */

void
_sasl_log (sasl_conn_t *conn,
	   int level,
	   const char *fmt,
	   ...)
#ifdef _SUN_SDK_
{
  _sasl_global_context_t *gctx = conn==NULL ? _sasl_gbl_ctx() : conn->gctx;
  sasl_log_t *log_cb;
  void *log_ctx;
  int result;
  va_list ap;

  /* See if we have a logging callback... */
  result = _sasl_getcallback(conn, SASL_CB_LOG, &log_cb, &log_ctx);
  if (result == SASL_OK && ! log_cb)
    return;

  va_start(ap, fmt); /* start varargs */
  ___sasl_log(gctx, log_cb, log_ctx, level, fmt, ap);
  va_end(ap);    
}

void
__sasl_log(const _sasl_global_context_t *gctx,
	   const sasl_callback_t *callbacks,
	   int level,
	   const char *fmt,
	   ...)
{
  sasl_log_t *log_cb = NULL;
  void *log_ctx = NULL;
  int result;
  va_list ap;

  if (callbacks)
    while (callbacks->id != SASL_CB_LIST_END) {
      if (callbacks->id == SASL_CB_LOG) {
	log_cb = callbacks->proc;
	log_ctx = callbacks->context;
	break;
      }
      ++callbacks;
    }

  if (log_cb == NULL) {
    result = _sasl_getcallback(NULL, SASL_CB_LOG, &log_cb, &log_ctx);
    if (result != SASL_OK || ! log_cb)
	return;
  }
  
  if (gctx == NULL)
    gctx = _sasl_gbl_ctx();

  va_start(ap, fmt); /* start varargs */
  ___sasl_log(gctx, log_cb, log_ctx, level, fmt, ap);
  va_end(ap);    
}

static void
___sasl_log(const _sasl_global_context_t *gctx,
	    sasl_log_t *log_cb,
	    void *log_ctx,
	    int level,
	    const char *fmt,
	    va_list ap)
#endif /* _SUN_SDK_ */
{
  char *out=(char *) sasl_ALLOC(250);
  size_t alloclen=100; /* current allocated length */
  size_t outlen=0; /* current length of output buffer */
  size_t formatlen;
  size_t pos=0; /* current position in format string */
  int result;
#ifndef _SUN_SDK_
  sasl_log_t *log_cb;
  void *log_ctx;
#endif /* !_SUN_SDK_ */
  
  int ival;
  char *cval;
#ifndef _SUN_SDK_
  va_list ap; /* varargs thing */
#endif /* !_SUN_SDK_ */

  if(!fmt) goto done;
  if(!out) return;
  
  formatlen = strlen(fmt);

#ifndef _SUN_SDK_
  /* See if we have a logging callback... */
  result = _sasl_getcallback(conn, SASL_CB_LOG, &log_cb, &log_ctx);
  if (result == SASL_OK && ! log_cb)
    result = SASL_FAIL;
  if (result != SASL_OK) goto done;
  
  va_start(ap, fmt); /* start varargs */
#endif /* !_SUN_SDK_ */

  while(pos<formatlen)
  {
    if (fmt[pos]!='%') /* regular character */
    {
      result = _buf_alloc(&out, &alloclen, outlen+1);
      if (result != SASL_OK) goto done;
      out[outlen]=fmt[pos];
      outlen++;
      pos++;

    } else { /* formating thing */
      int done=0;
      char frmt[10];
      int frmtpos=1;
      char tempbuf[21];
      frmt[0]='%';
      pos++;

      while (done==0)
      {
	switch(fmt[pos])
	  {
	  case 's': /* need to handle this */
	    cval = va_arg(ap, char *); /* get the next arg */
	    result = _sasl_add_string(&out, &alloclen,
				&outlen, cval);
	      
	    if (result != SASL_OK) /* add the string */
		goto done;

	    done=1;
	    break;

	  case '%': /* double % output the '%' character */
	    result = _buf_alloc(&out,&alloclen,outlen+1);
	    if (result != SASL_OK)
		goto done;
	    
	    out[outlen]='%';
	    outlen++;
	    done=1;
	    break;

	  case 'm': /* insert the errno string */
	    result = _sasl_add_string(&out, &alloclen, &outlen,
				strerror(va_arg(ap, int)));
	    if (result != SASL_OK)
		goto done;
	    
	    done=1;
	    break;

	  case 'z': /* insert the sasl error string */
	    result = _sasl_add_string(&out, &alloclen, &outlen,
				(char *) sasl_errstring(va_arg(ap, int),NULL,NULL));
	    if (result != SASL_OK)
		goto done;
	    
	    done=1;
	    break;

	  case 'c':
#ifndef _SUN_SDK_
	    frmt[frmtpos++]=fmt[pos];
	    frmt[frmtpos]=0;
#endif /* !_SUN_SDK_ */
	    tempbuf[0] = (char) va_arg(ap, int); /* get the next arg */
	    tempbuf[1]='\0';
	    
	    /* now add the character */
	    result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf);
	    if (result != SASL_OK)
		goto done;
		
	    done=1;
	    break;

	  case 'd':
	  case 'i':
	    frmt[frmtpos++]=fmt[pos];
	    frmt[frmtpos]=0;
	    ival = va_arg(ap, int); /* get the next arg */

	    snprintf(tempbuf,20,frmt,ival); /* have snprintf do the work */
	    /* now add the string */
	    result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf);
	    if (result != SASL_OK)
		goto done;

	    done=1;

	    break;
	  default: 
	    frmt[frmtpos++]=fmt[pos]; /* add to the formating */
	    frmt[frmtpos]=0;	    
#ifdef _SUN_SDK_
	    if (frmtpos > sizeof (frmt) - 2) 
#else
	    if (frmtpos>9) 
#endif /* _SUN_SDK_ */
	      done=1;
	  }
	pos++;
	if (pos>formatlen)
	  done=1;
      }

    }
  }

  /* put 0 at end */
  result = _buf_alloc(&out, &alloclen, outlen+1);
  if (result != SASL_OK) goto done;
  out[outlen]=0;

  va_end(ap);    

  /* send log message */
  result = log_cb(log_ctx, level, out);

 done:
  if(out) sasl_FREE(out);
}



/* Allocate and Init a sasl_utils_t structure */
#ifdef _SUN_SDK_
sasl_utils_t *
_sasl_alloc_utils(_sasl_global_context_t *gctx, sasl_conn_t *conn,
		  sasl_global_callbacks_t *global_callbacks)
#else
sasl_utils_t *
_sasl_alloc_utils(sasl_conn_t *conn,
		  sasl_global_callbacks_t *global_callbacks)
#endif /* _SUN_SDK_ */
{
  sasl_utils_t *utils;
#ifdef _SUN_SDK_
  sasl_allocation_utils_t alloc;
  sasl_mutex_utils_t mutex;

  LOCK_MUTEX(&malloc_global_mutex);
  alloc = gctx->sasl_allocation_utils;
  mutex = gctx->sasl_mutex_utils;
  UNLOCK_MUTEX(&malloc_global_mutex);
#endif /* _SUN_SDK_ */

  /* set util functions - need to do rest*/
#ifdef _SUN_SDK_
  utils=alloc.malloc(sizeof(sasl_utils_t));
#else
  utils=sasl_ALLOC(sizeof(sasl_utils_t));
#endif /* _SUN_SDK_ */
  if (utils==NULL)
    return NULL;

  utils->conn = conn;

  sasl_randcreate(&utils->rpool);

  if (conn) {
    utils->getopt = &_sasl_conn_getopt;
    utils->getopt_context = conn;
  } else {
    utils->getopt = &_sasl_global_getopt;
    utils->getopt_context = global_callbacks;
  }

#ifdef _SUN_SDK_
  utils->malloc=alloc.malloc;
  utils->calloc=alloc.calloc;
  utils->realloc=alloc.realloc;
  utils->free=alloc.free;

  utils->mutex_alloc = mutex.alloc;
  utils->mutex_lock = mutex.lock;
  utils->mutex_unlock = mutex.unlock;
  utils->mutex_free = mutex.free;
#else
  utils->malloc=_sasl_allocation_utils.malloc;
  utils->calloc=_sasl_allocation_utils.calloc;
  utils->realloc=_sasl_allocation_utils.realloc;
  utils->free=_sasl_allocation_utils.free;

  utils->mutex_alloc = _sasl_mutex_utils.alloc;
  utils->mutex_lock = _sasl_mutex_utils.lock;
  utils->mutex_unlock = _sasl_mutex_utils.unlock;
  utils->mutex_free = _sasl_mutex_utils.free;
#endif /* _SUN_SDK_ */
  
#ifdef _SUN_SDK_
  utils->MD5Init  = (void (*)(MD5_CTX *))&MD5Init;
  utils->MD5Update= (void (*)
	(MD5_CTX *, const unsigned char *, unsigned int ))&MD5Update;
  utils->MD5Final = (void (*)(unsigned char [16], MD5_CTX *))&MD5Final;
#else
  utils->MD5Init  = &_sasl_MD5Init;
  utils->MD5Update= &_sasl_MD5Update;
  utils->MD5Final = &_sasl_MD5Final;
#endif /* _SUN_SDK_ */
  utils->hmac_md5 = &_sasl_hmac_md5;
  utils->hmac_md5_init = &_sasl_hmac_md5_init;
  utils->hmac_md5_final = &_sasl_hmac_md5_final;
  utils->hmac_md5_precalc = &_sasl_hmac_md5_precalc;
  utils->hmac_md5_import = &_sasl_hmac_md5_import;
  utils->mkchal = &sasl_mkchal;
  utils->utf8verify = &sasl_utf8verify;
  utils->rand=&sasl_rand;
  utils->churn=&sasl_churn;  
  utils->checkpass=NULL;
  
  utils->encode64=&sasl_encode64;
  utils->decode64=&sasl_decode64;
  
  utils->erasebuffer=&sasl_erasebuffer;

  utils->getprop=&sasl_getprop;
  utils->setprop=&sasl_setprop;

  utils->getcallback=&_sasl_getcallback;

  utils->log=&_sasl_log;

  utils->seterror=&sasl_seterror;

#ifndef macintosh
  /* Aux Property Utilities */
  utils->prop_new=&prop_new;
  utils->prop_dup=&prop_dup;
  utils->prop_request=&prop_request;
  utils->prop_get=&prop_get;
  utils->prop_getnames=&prop_getnames;
  utils->prop_clear=&prop_clear;
  utils->prop_dispose=&prop_dispose;
  utils->prop_format=&prop_format;
  utils->prop_set=&prop_set;
  utils->prop_setvals=&prop_setvals;
  utils->prop_erase=&prop_erase;
#endif

  /* Spares */
  utils->spare_fptr = NULL;
  utils->spare_fptr1 = utils->spare_fptr2 = 
      utils->spare_fptr3 = NULL;
  
  return utils;
}

int
_sasl_free_utils(const sasl_utils_t ** utils)
{
    sasl_utils_t *nonconst;
#ifdef _SUN_SDK_
    sasl_free_t *free_func;
#endif /* _SUN_SDK_ */

    if(!utils) return SASL_BADPARAM;
    if(!*utils) return SASL_OK;

    /* I wish we could avoid this cast, it's pretty gratuitous but it
     * does make life easier to have it const everywhere else. */
    nonconst = (sasl_utils_t *)(*utils);

    sasl_randfree(&(nonconst->rpool));
#ifdef _SUN_SDK_
    free_func = (*utils)->free;
    free_func(nonconst);
#else
    sasl_FREE(nonconst);
#endif /* _SUN_SDK_ */

    *utils = NULL;
    return SASL_OK;
}

int sasl_idle(sasl_conn_t *conn)
{
  if (! conn) {
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx = _sasl_gbl_ctx();

    if (gctx->sasl_server_idle_hook
        && gctx->sasl_server_idle_hook(NULL))
      return 1;
    if (gctx->sasl_client_idle_hook
        && gctx->sasl_client_idle_hook(NULL))
      return 1;
#else
    if (_sasl_server_idle_hook
	&& _sasl_server_idle_hook(NULL))
      return 1;
    if (_sasl_client_idle_hook
	&& _sasl_client_idle_hook(NULL))
      return 1;
#endif /* _SUN_SDK_ */
    return 0;
  }

  if (conn->idle_hook)
    return conn->idle_hook(conn);

  return 0;
}

const sasl_callback_t *
_sasl_find_getpath_callback(const sasl_callback_t *callbacks)
{
  static const sasl_callback_t default_getpath_cb = {
    SASL_CB_GETPATH,
    &_sasl_getpath,
    NULL
  };

  if (callbacks)
    while (callbacks->id != SASL_CB_LIST_END)
    {
      if (callbacks->id == SASL_CB_GETPATH)
      {
	return callbacks;
      } else {
	++callbacks;
      }
    }
  
  return &default_getpath_cb;
}

#ifdef _SUN_SDK_
extern const sasl_callback_t *
_sasl_find_getconf_callback(const sasl_callback_t *callbacks)
{
  static const sasl_callback_t default_getconf_cb = {
    SASL_CB_GETCONF,
    &_sasl_getconf,
    NULL
  };

  if (callbacks)
    while (callbacks->id != SASL_CB_LIST_END)
    {
      if (callbacks->id == SASL_CB_GETCONF)
      {
	return callbacks;
      } else {
	++callbacks;
      }
    }
  
  return &default_getconf_cb;
}
#endif /* _SUN_SDK_ */

const sasl_callback_t *
_sasl_find_verifyfile_callback(const sasl_callback_t *callbacks)
{
  static const sasl_callback_t default_verifyfile_cb = {
    SASL_CB_VERIFYFILE,
    &_sasl_verifyfile,
    NULL
  };

  if (callbacks)
    while (callbacks->id != SASL_CB_LIST_END)
    {
      if (callbacks->id == SASL_CB_VERIFYFILE)
      {
	return callbacks;
      } else {
	++callbacks;
      }
    }
  
  return &default_verifyfile_cb;
}

/* Basically a conditional call to realloc(), if we need more */
#ifdef _SUN_SDK_
int __buf_alloc(const _sasl_global_context_t *gctx, char **rwbuf,
		size_t *curlen, size_t newlen)
#else
int _buf_alloc(char **rwbuf, size_t *curlen, size_t newlen) 
#endif /* _SUN_SDK_ */
{
    if(!(*rwbuf)) {
	*rwbuf = sasl_ALLOC(newlen);
	if (*rwbuf == NULL) {
	    *curlen = 0;
	    return SASL_NOMEM;
	}
	*curlen = newlen;
    } else if(*rwbuf && *curlen < newlen) {
	size_t needed = 2*(*curlen);

	while(needed < newlen)
	    needed *= 2;

	*rwbuf = sasl_REALLOC(*rwbuf, needed);
	
	if (*rwbuf == NULL) {
	    *curlen = 0;
	    return SASL_NOMEM;
	}
	*curlen = needed;
    } 

    return SASL_OK;
}

/* for the mac os x cfm glue: this lets the calling function
   get pointers to the error buffer without having to touch the sasl_conn_t struct */
void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, size_t **lenhdl)
{
	*bufhdl = &conn->error_buf;
	*lenhdl = &conn->error_buf_len;
}

/* convert an iovec to a single buffer */
#ifdef _SUN_SDK_
int _iovec_to_buf(const _sasl_global_context_t *gctx, const struct iovec *vec,
		  unsigned numiov, buffer_info_t **output)
#else
int _iovec_to_buf(const struct iovec *vec,
		  unsigned numiov, buffer_info_t **output) 
#endif /* _SUN_SDK_ */
{
    unsigned i;
    int ret;
    buffer_info_t *out;
    char *pos;

    if(!vec || !output) return SASL_BADPARAM;

    if(!(*output)) {
	*output = sasl_ALLOC(sizeof(buffer_info_t));
	if(!*output) return SASL_NOMEM;
	memset(*output,0,sizeof(buffer_info_t));
    }

    out = *output;
    
    out->curlen = 0;
    for(i=0; i<numiov; i++)
	out->curlen += vec[i].iov_len;

    ret = _buf_alloc(&out->data, &out->reallen, out->curlen);

    if(ret != SASL_OK) return SASL_NOMEM;
    
    memset(out->data, 0, out->reallen);
    pos = out->data;
    
    for(i=0; i<numiov; i++) {
	memcpy(pos, vec[i].iov_base, vec[i].iov_len);
	pos += vec[i].iov_len;
    }

    return SASL_OK;
}

/* This code might be useful in the future, but it isn't now, so.... */
#if 0
int _sasl_iptostring(const struct sockaddr *addr, socklen_t addrlen,
		     char *out, unsigned outlen) {
    char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
    
    if(!addr || !out) return SASL_BADPARAM;

    getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
		NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);

    if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
	return SASL_BUFOVER;

    snprintf(out, outlen, "%s;%s", hbuf, pbuf);

    return SASL_OK;
}
#endif

#ifdef _SUN_SDK_
/* An ipv6 address will contain at least two colons */
static int can_be_ipv6(const char *addr)
{
   const char *p;

   if ((p = strchr(addr, ':')) == NULL)
	return (0);

   p = strchr(p + 1, ':');

   return (p != NULL);
}
#endif /* _SUN_SDK_ */

int _sasl_ipfromstring(const char *addr,
		       struct sockaddr *out, socklen_t outlen) 
{
    int i, j;
    struct addrinfo hints, *ai = NULL;
    char hbuf[NI_MAXHOST];
#ifdef _SUN_SDK_
    const char *start, *end, *p;
    int addr_only = 1;
#endif /* _SUN_SDK_ */
    
    /* A NULL out pointer just implies we don't do a copy, just verify it */

    if(!addr) return SASL_BADPARAM;

#ifdef _SUN_SDK_
    end = strchr(addr, ']');
    if (end != NULL) {
	/* This an rfc 2732 ipv6 address */
	start = strchr(addr, '[');
	if (start >= end || start == NULL)
	    return SASL_BADPARAM;
	for (i = 0, p = start + 1; p < end; p++) {
	    hbuf[i++] = *p;
	    if (i >= NI_MAXHOST)
		return SASL_BADPARAM;
	}
	p = strchr(end, ':');
	if (p == NULL)
		p = end + 1;
	else
		p = p + 1;
    } else if (can_be_ipv6(addr) != 0) {
	/* Parse the address */
	for (i = 0; addr[i] != '\0' && addr[i] != ';'; ) {
	    hbuf[i] = addr[i];
	    if (++i >= NI_MAXHOST)
		return SASL_BADPARAM;
	}
	if (addr[i] == ';')
	     p = &addr[i+1];
	else
	     p = &addr[i];
    } else {
	for (i = 0; addr[i] != '\0' && addr[i] != ';' && addr[i] != ':'; ) {
	    hbuf[i] = addr[i];
	    if (isalpha(addr[i]))
		addr_only = 0;
	    if (++i >= NI_MAXHOST)
		return SASL_BADPARAM;
	}
	if (addr[i] == ';' || addr[i] == ':')
	     p = &addr[i+1];
	else
	     p = &addr[i];
    }
    hbuf[i] = '\0';
    for (j = 0; p[j] != '\0'; j++)
	if (!isdigit((int)(p[j])))
	    return SASL_BADPARAM;
    if (atoi(p) == 0)
	p = NULL;
#else
    /* Parse the address */
    for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) {
	if (i >= NI_MAXHOST)
	    return SASL_BADPARAM;
	hbuf[i] = addr[i];
    }
    hbuf[i] = '\0';

    if (addr[i] == ';')
	i++;
    /* XXX: Do we need this check? */
    for (j = i; addr[j] != '\0'; j++)
	if (!isdigit((int)(addr[j])))
	    return SASL_BADPARAM;
#endif /* _SUN_SDK_ */

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
#ifdef _SUN_SDK_
    hints.ai_flags = addr_only ? AI_PASSIVE | AI_NUMERICHOST : AI_PASSIVE;
    if (getaddrinfo(hbuf, p, &hints, &ai) != 0)
#else
    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
    if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0)
#endif /* _SUN_SDK_ */
	return SASL_BADPARAM;

    if (out) {
	if (outlen < (socklen_t)ai->ai_addrlen) {
	    freeaddrinfo(ai);
	    return SASL_BUFOVER;
	}
	memcpy(out, ai->ai_addr, ai->ai_addrlen);
    }

    freeaddrinfo(ai);

    return SASL_OK;
}

#ifdef _SUN_SDK_
int _sasl_build_mechlist(_sasl_global_context_t *gctx)
#else
int _sasl_build_mechlist(void) 
#endif /* _SUN_SDK_ */
{
    int count = 0;
    sasl_string_list_t *clist = NULL, *slist = NULL, *olist = NULL;
    sasl_string_list_t *p, *q, **last, *p_next;

#ifdef _SUN_SDK_
    char **global_mech_list;

    LOCK_MUTEX(&global_mutex);

    clist = _sasl_client_mechs(gctx);
    slist = _sasl_server_mechs(gctx);

    global_mech_list = gctx->global_mech_list;
#else
    clist = _sasl_client_mechs();
    slist = _sasl_server_mechs();
#endif /* _SUN_SDK_ */

    if(!clist) {
	olist = slist;
    } else {
	int flag;
	
	/* append slist to clist, and set olist to clist */
	for(p = slist; p; p = p_next) {
	    flag = 0;
	    p_next = p->next;

	    last = &clist;
	    for(q = clist; q; q = q->next) {
		if(!strcmp(q->d, p->d)) {
		    /* They match, set the flag */
		    flag = 1;
		    break;
		}
		last = &(q->next);
	    }

	    if(!flag) {
		*last = p;
		p->next = NULL;
	    } else {
		sasl_FREE(p);
	    }
	}

	olist = clist;
    }

    if(!olist) {
#ifdef _SUN_SDK_
	UNLOCK_MUTEX(&global_mutex);
#else
	printf ("no olist");
#endif /* _SUN_SDK_ */
	return SASL_FAIL;
    }

    for (p = olist; p; p = p->next) count++;
    
    if(global_mech_list) {
	sasl_FREE(global_mech_list);
#ifdef _SUN_SDK_
	gctx->global_mech_list = NULL;
#else
	global_mech_list = NULL;
#endif /* _SUN_SDK_ */
    }
    
    global_mech_list = sasl_ALLOC((count + 1) * sizeof(char *));
    if(!global_mech_list) return SASL_NOMEM;
    
    memset(global_mech_list, 0, (count + 1) * sizeof(char *));
#ifdef _SUN_SDK_
    gctx->global_mech_list = global_mech_list;
#endif /* _SUN_SDK_ */
    
    count = 0;
    for (p = olist; p; p = p_next) {
	p_next = p->next;

	global_mech_list[count++] = (char *) p->d;

    	sasl_FREE(p);
    }

#ifdef _SUN_SDK_
    UNLOCK_MUTEX(&global_mutex);
#endif /* _SUN_SDK_ */

    return SASL_OK;
}

const char ** sasl_global_listmech(void) 
{
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx = _sasl_gbl_ctx();

    return (const char **)gctx->global_mech_list;
#else
    return (const char **)global_mech_list;
#endif /* _SUN_SDK_ */
}

int sasl_listmech(sasl_conn_t *conn,
		  const char *user,
		  const char *prefix,
		  const char *sep,
		  const char *suffix,
		  const char **result,
		  unsigned *plen,
		  int *pcount)
{
    if(!conn) {
	return SASL_BADPARAM;
    } else if(conn->type == SASL_CONN_SERVER) {
	RETURN(conn, _sasl_server_listmech(conn, user, prefix, sep, suffix,
					   result, plen, pcount));
    } else if (conn->type == SASL_CONN_CLIENT) {
	RETURN(conn, _sasl_client_listmech(conn, prefix, sep, suffix,
					   result, plen, pcount));
    }
    
    PARAMERROR(conn);
}

#ifdef _SUN_SDK_
/*
 * Creates a context so that libraries may use libsasl independently
 * of applications using libsasl.
 * Returns NULL on failure.
 *
 * sasl_free_context frees the context
 * To use libsasl independently of the default context, use
 * _sasl_server_init()		instead of	sasl_server_init()
 * _sasl_server_new()		instead of	sasl_server_new()
 * _sasl_client_init()		instead of	sasl_client_init()
 * _sasl_client_new()		instead of	sasl_client_new()
 * _sasl_client_add_plugin()	instead of	sasl_client_add_plugin()
 * _sasl_server_add_plugin()	instead of	sasl_server_add_plugin()
 * _sasl_canonuser_add_plugin()	instead of	sasl_canonuser_add_plugin()
 * _sasl_auxprop_add_plugin()	instead of	sasl_auxprop_add_plugin()
 */

void *sasl_create_context(void)
{
  _sasl_global_context_t *gctx;

  gctx = (_sasl_global_context_t *)
	sasl_sun_ALLOC(sizeof(_sasl_global_context_t));

  if (gctx != NULL) {
    memset(gctx, 0, sizeof(_sasl_global_context_t));

    gctx->server_global_callbacks.gctx = gctx;
    gctx->client_global_callbacks.gctx = gctx;
    LOCK_MUTEX(&malloc_global_mutex);
    gctx->sasl_allocation_utils.malloc = (sasl_malloc_t *)&malloc;
    gctx->sasl_allocation_utils.calloc = (sasl_calloc_t *)&calloc;
    gctx->sasl_allocation_utils.realloc = (sasl_realloc_t *)&realloc;
    gctx->sasl_allocation_utils.free = (sasl_free_t *)&free;
    gctx->sasl_mutex_utils.alloc = sasl_mutex_alloc;
    gctx->sasl_mutex_utils.lock = sasl_mutex_lock;
    gctx->sasl_mutex_utils.unlock = sasl_mutex_unlock;
    gctx->sasl_mutex_utils.free = sasl_mutex_free;
    UNLOCK_MUTEX(&malloc_global_mutex);
  }
  return gctx;
}

/* Frees the context created by sasl_create_context() */
void sasl_free_context(void *context)
{
  _sasl_dispose_context(context);
  if (context != NULL) {
    sasl_sun_FREE(context);
  }
}

/* Used by both sasl_done() and sasl_free_context() to free context */
static void _sasl_dispose_context(_sasl_global_context_t *gctx)
{
  if (gctx == NULL)
        return;

  if (gctx->sasl_server_cleanup_hook &&
		gctx->sasl_server_cleanup_hook(gctx) == SASL_OK) {
	gctx->sasl_server_idle_hook = NULL;
	gctx->sasl_server_cleanup_hook = NULL;
  }
    
  if (gctx->sasl_client_cleanup_hook &&
		gctx->sasl_client_cleanup_hook(gctx) == SASL_OK) {
	gctx->sasl_client_idle_hook = NULL;	
	gctx->sasl_client_cleanup_hook = NULL;
  }
    
  if(gctx->sasl_server_cleanup_hook || gctx->sasl_client_cleanup_hook)
	return;

  _sasl_canonuser_free(gctx);
  _sasl_done_with_plugins(gctx);

  sasl_config_free(gctx);

  if (gctx->free_mutex != NULL)
    sasl_MUTEX_FREE(gctx->free_mutex);
  gctx->free_mutex = NULL;

  _sasl_free_utils(&(gctx->sasl_server_global_utils));
  _sasl_free_utils(&(gctx->sasl_canonusr_global_utils));

  LOCK_MUTEX(&global_mutex);
  sasl_FREE((void *)gctx->global_mech_list);
  gctx->global_mech_list = NULL;
  UNLOCK_MUTEX(&global_mutex);

  /* in case of another init/done */
  gctx->sasl_server_cleanup_hook = NULL;
  gctx->sasl_client_cleanup_hook = NULL;

  gctx->sasl_client_idle_hook = NULL;
  gctx->sasl_server_idle_hook = NULL;
}

_sasl_global_context_t *_sasl_gbl_ctx(void)
{
  static _sasl_global_context_t gbl_ctx = {
        0,                      /* sasl_server_active */
        NULL,                   /* mechlist */
	NULL,			/* splug_path_info */
        {NULL, NULL, &gbl_ctx}, /* server_global_callbacks */
        NULL,                   /* sasl_server_cleanup_hook */
        NULL,                   /* sasl_server_idle_hook */
        NULL,                   /* cmechlist */
	NULL,			/* cplug_path_info */
        {NULL, NULL, &gbl_ctx}, /* client_global_callbacks */
        0,                      /* sasl_client_active */
        NULL,                   /* sasl_client_cleanup_hook */
        NULL,                   /* sasl_client_idle_hook */
        NULL,                   /* sasl_server_global_utils */
        NULL,                   /* sasl_client_global_utils */
        NULL,                   /* configlist */
        0,                      /* nconfiglist */
	NULL,			/* config_path */
	0,			/* config_last_read */
        NULL,                   /* auxprop_head */
        NULL,                   /* canonuser_head */
        NULL,                   /* global_mech_list */
        NULL,                   /* free_mutex */
        {(sasl_malloc_t *)&malloc, (sasl_calloc_t *)&calloc,
            (sasl_realloc_t *)&realloc, (sasl_free_t *)&free},
                                /* sasl_allocation_utils */
        {&sasl_mutex_alloc, &sasl_mutex_lock, &sasl_mutex_unlock,
            &sasl_mutex_free},  /* sasl_mutex_utils */
        NULL			/* lib_list_head */
  };

  return (&gbl_ctx);
}

static int
_sasl_getconf(void *context __attribute__((unused)), const char **conf)
{
    if (! conf)
	return SASL_BADPARAM;

    *conf = SASL_CONFDIR;

    return SASL_OK;
}

/* EXPORT DELETE START */
/* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
#pragma fini(sasl_fini)
int 
sasl_fini(void) 
{ 
    reg_list_t *next;

    while (reg_list_base != NULL) {
	next = reg_list_base->next;
	free(reg_list_base);
	reg_list_base = next;
    }
    return (0);
} 
#endif /* _INTEGRATED_SOLARIS_ */
/* CRYPT DELETE END */
/* EXPORT DELETE END */

#endif /* _SUN_SDK_ */

#ifndef WIN32
static int
_sasl_getpath(void *context __attribute__((unused)),
	      const char **path)
{
  if (! path)
    return SASL_BADPARAM;

#ifdef _SUN_SDK_
/* SASL_PATH is not allowed for SUN SDK */
#else
  *path = getenv(SASL_PATH_ENV_VAR);
  if (! *path)
#endif /* _SUN_SDK_ */
    *path = PLUGINDIR;

  return SASL_OK;
}

#else
/* Return NULL on failure */
static int
_sasl_getpath(void *context __attribute__((unused)), const char **path)
{
    /* Open registry entry, and find all registered SASL libraries.
     *
     * Registry location:
     *
     *     SOFTWARE\\Carnegie Mellon\\Project Cyrus\\SASL Library
     *
     * Key - value:
     *
     *     "SearchPath" - value: PATH like (';' delimited) list
     *                    of directories where to search for plugins
     *                    The list may contain references to environment
     *                    variables (e.g. %PATH%).
     *
     */
    HKEY  hKey;
    DWORD ret;
    DWORD ValueType;		    /* value type */
    DWORD cbData;		    /* value size */
    BYTE * ValueData;		    /* value */
    DWORD cbExpandedData;	    /* "expanded" value size */
    BYTE * ExpandedValueData;	    /* "expanded" value */
    char * return_value;	    /* function return value */
    char * tmp;

    /* Initialization */
    ExpandedValueData = NULL;
    ValueData = NULL;
    return_value = NULL;

    /* Open the registry */
    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
		       SASL_ROOT_KEY,
		       0,
		       KEY_READ,
		       &hKey);

    if (ret != ERROR_SUCCESS) { 
		/* no registry entry */
		*path = PLUGINDIR;
		return SASL_OK; 
	}

    /* figure out value type and required buffer size */
    /* the size will include space for terminating NUL if required */
    RegQueryValueEx (hKey,
		     SASL_PATH_SUBKEY,
		     NULL,	    /* reserved */
		     &ValueType,
		     NULL,
		     &cbData);
 
    /* Only accept string related types */
    if (ValueType != REG_EXPAND_SZ &&
	ValueType != REG_MULTI_SZ &&
	ValueType != REG_SZ) {
	return_value = NULL;
	goto CLEANUP;
    }

    /* Any high water mark? */
    ValueData = sasl_ALLOC(cbData);
    if (ValueData == NULL) {
	return_value = NULL;
	goto CLEANUP;
    };

    RegQueryValueEx (hKey,
		     SASL_PATH_SUBKEY,
		     NULL,	    /* reserved */
		     &ValueType,
		     ValueData,
		     &cbData);

    switch (ValueType) {
    case REG_EXPAND_SZ:
        /* : A random starting guess */
        cbExpandedData = cbData + 1024;
        ExpandedValueData = sasl_ALLOC(cbExpandedData);
        if (ExpandedValueData == NULL) {
            return_value = NULL;
            goto CLEANUP;
        };

        cbExpandedData = ExpandEnvironmentStrings(
                                                  ValueData,
                                                  ExpandedValueData,
                                                  cbExpandedData);

        if (cbExpandedData == 0) {
            /* : GetLastError() contains the reason for failure */
            return_value = NULL;
            goto CLEANUP;
        }

        /* : Must retry expansion with the bigger buffer */
        if (cbExpandedData > cbData + 1024) {
            /* : Memory leak here if can't realloc */
            ExpandedValueData = sasl_REALLOC(ExpandedValueData, cbExpandedData);
            if (ExpandedValueData == NULL) {
                return_value = NULL;
                goto CLEANUP;
            };

            cbExpandedData = ExpandEnvironmentStrings(
                                                      ValueData,
                                                      ExpandedValueData,
                                                      cbExpandedData);

            /* : This should not happen */
            if (cbExpandedData == 0) {
                /* : GetLastError() contains the reason for failure */
                return_value = NULL;
                goto CLEANUP;
            }
        }

        sasl_FREE(ValueData);
        ValueData = ExpandedValueData;
        /* : This is to prevent automatical freeing of this block on cleanup */
        ExpandedValueData = NULL;

        break;

    case REG_MULTI_SZ:
        tmp = ValueData;

        /* : We shouldn't overflow here, as the buffer is guarantied
           : to contain at least two consequent NULs */
        while (1) {
            if (tmp[0] == '\0') {
                /* : Stop the process if we found the end of the string (two consequent NULs) */
                if (tmp[1] == '\0') {
                    break;
                }

                /* : Replace delimiting NUL with our delimiter characted */
                tmp[0] = PATHS_DELIMITER;
            }
            tmp += strlen(tmp);
        }
        break;

    case REG_SZ:
        /* Do nothing, it is good as is */
        break;

    default:
        return_value = NULL;
        goto CLEANUP;
    }

    return_value = ValueData;

    CLEANUP:
    RegCloseKey(hKey);
    if (ExpandedValueData != NULL) sasl_FREE(ExpandedValueData);
    if (return_value == NULL) {
	if (ValueData != NULL) sasl_FREE(ValueData);
    }
    *path = return_value;

#ifdef _SUN_SDK_
/* SASL_PATH is not allowed for SUN SDK */
  if (! *path)
    *path = PLUGINDIR;
#endif /* _SUN_SDK_ */
	return SASL_OK;
}

#endif