OpenSolaris_b135/lib/libsasl/lib/server.c

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

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
#pragma ident	"%Z%%M%	%I%	%E% SMI"

/* SASL server API implementation
 * Rob Siemborski
 * Tim Martin
 * $Id: server.c,v 1.123 2003/04/16 19:36:01 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.
 */

/* local functions/structs don't start with sasl
 */
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#ifndef macintosh
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <fcntl.h>
#include <string.h>
#include <ctype.h>

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

#ifndef _SUN_SDK_
#ifdef sun
/* gotta define gethostname ourselves on suns */
extern int gethostname(char *, int);
#endif
#endif /* !_SUN_SDK_ */

#define DEFAULT_CHECKPASS_MECH "auxprop"

/* Contains functions:
 * 
 * sasl_server_init
 * sasl_server_new
 * sasl_listmech
 * sasl_server_start
 * sasl_server_step
 * sasl_checkpass
 * sasl_checkapop
 * sasl_user_exists
 * sasl_setpass
 */

#ifdef _SUN_SDK_
int _is_sasl_server_active(_sasl_global_context_t *gctx)
{
    return gctx->sasl_server_active;
}

DEFINE_STATIC_MUTEX(init_server_mutex);
DEFINE_STATIC_MUTEX(server_active_mutex);
/*
 * server_plug_mutex ensures only one server plugin is init'ed at a time
 * If a plugin is loaded more than once, the glob_context may be overwritten
 * which may lead to a memory leak. We keep glob_context with each mech
 * to avoid this problem.
 */
DEFINE_STATIC_MUTEX(server_plug_mutex);
#else
/* if we've initialized the server sucessfully */
static int _sasl_server_active = 0;

/* For access by other modules */
int _is_sasl_server_active(void) { return _sasl_server_active; }
#endif /* _SUN_SDK_ */

static int _sasl_checkpass(sasl_conn_t *conn, 
			   const char *user, unsigned userlen,
			   const char *pass, unsigned passlen);

#ifndef _SUN_SDK_
static mech_list_t *mechlist = NULL; /* global var which holds the list */

static sasl_global_callbacks_t global_callbacks;
#endif /* !_SUN_SDK_ */

/* set the password for a user
 *  conn        -- SASL connection
 *  user        -- user name
 *  pass        -- plaintext password, may be NULL to remove user
 *  passlen     -- length of password, 0 = strlen(pass)
 *  oldpass     -- NULL will sometimes work
 *  oldpasslen  -- length of password, 0 = strlen(oldpass)
 *  flags       -- see flags below
 * 
 * returns:
 *  SASL_NOCHANGE  -- proper entry already exists
 *  SASL_NOMECH    -- no authdb supports password setting as configured
 *  SASL_NOVERIFY  -- user exists, but no settable password present
 *  SASL_DISABLED  -- account disabled
 *  SASL_PWLOCK    -- password locked
 *  SASL_WEAKPASS  -- password too weak for security policy
 *  SASL_NOUSERPASS -- user-supplied passwords not permitted
 *  SASL_FAIL      -- OS error
 *  SASL_BADPARAM  -- password too long
 *  SASL_OK        -- successful
 */

int sasl_setpass(sasl_conn_t *conn,
		 const char *user,
		 const char *pass, unsigned passlen,
		 const char *oldpass,
		 unsigned oldpasslen,
		 unsigned flags)
{
    int result=SASL_OK, tmpresult;
    sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
    sasl_server_userdb_setpass_t *setpass_cb = NULL;
    void *context = NULL;
    mechanism_t *m;
     
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx =
		 (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
    mech_list_t *mechlist = gctx == NULL ? NULL : gctx->mechlist;
 
    if (!gctx->sasl_server_active || !mechlist) return SASL_NOTINIT;
#else
    if (!_sasl_server_active || !mechlist) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */

    /* check params */
    if (!conn) return SASL_BADPARAM;
    if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
     
    if ((!(flags & SASL_SET_DISABLE) && passlen == 0)
        || ((flags & SASL_SET_CREATE) && (flags & SASL_SET_DISABLE)))
	PARAMERROR(conn);

    /* call userdb callback function */
    result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS,
			       &setpass_cb, &context);
    if(result == SASL_OK && setpass_cb) {
	tmpresult = setpass_cb(conn, context, user, pass, passlen,
			    s_conn->sparams->propctx, flags);
	if(tmpresult != SASL_OK) {
	    _sasl_log(conn, SASL_LOG_ERR,
		      "setpass callback failed for %s: %z",
		      user, tmpresult);
	} else {
	    _sasl_log(conn, SASL_LOG_NOTE,
		      "setpass callback succeeded for %s", user);
	}
    } else {
	result = SASL_OK;
    }

    /* now we let the mechanisms set their secrets */
    for (m = mechlist->mech_list; m; m = m->next) {
	if (!m->plug->setpass) {
	    /* can't set pass for this mech */
	    continue;
	}
#ifdef _SUN_SDK_
	tmpresult = m->plug->setpass(m->glob_context,
#else
	tmpresult = m->plug->setpass(m->plug->glob_context,
#endif /* _SUN_SDK_ */
				     ((sasl_server_conn_t *)conn)->sparams,
				     user,
				     pass,
				     passlen,
				     oldpass, oldpasslen,
				     flags);
	if (tmpresult == SASL_OK) {
	    _sasl_log(conn, SASL_LOG_NOTE,
		      "%s: set secret for %s", m->plug->mech_name, user);

	    m->condition = SASL_OK; /* if we previously thought the
				       mechanism didn't have any user secrets 
				       we now think it does */

	} else if (tmpresult == SASL_NOCHANGE) {
	    _sasl_log(conn, SASL_LOG_NOTE,
		      "%s: secret not changed for %s", m->plug->mech_name, user);
	} else {
	    result = tmpresult;
	    _sasl_log(conn, SASL_LOG_ERR,
		      "%s: failed to set secret for %s: %z (%m)",
		      m->plug->mech_name, user, tmpresult,
#ifndef WIN32
		      errno
#else
		      GetLastError()
#endif
		      );
	}
    }

    RETURN(conn, result);
}

#ifdef _SUN_SDK_
static void
server_dispose_mech_contexts(sasl_conn_t *pconn)
{
  sasl_server_conn_t *s_conn=  (sasl_server_conn_t *) pconn;
  context_list_t *cur, *cur_next;
  _sasl_global_context_t *gctx = pconn->gctx;

  for(cur = s_conn->mech_contexts; cur; cur=cur_next) {
      cur_next = cur->next;
      if(cur->context)
	  cur->mech->plug->mech_dispose(cur->context, s_conn->sparams->utils);
      sasl_FREE(cur);
  }  
  s_conn->mech_contexts = NULL;
}
#endif /* _SUN_SDK_ */

/* local mechanism which disposes of server */
static void server_dispose(sasl_conn_t *pconn)
{
  sasl_server_conn_t *s_conn=  (sasl_server_conn_t *) pconn;
#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx = pconn->gctx;
#else
  context_list_t *cur, *cur_next;
#endif /* _SUN_SDK_ */
  
  if (s_conn->mech
      && s_conn->mech->plug->mech_dispose) {
    s_conn->mech->plug->mech_dispose(pconn->context,
				     s_conn->sparams->utils);
  }
  pconn->context = NULL;

#ifdef _SUN_SDK_
  server_dispose_mech_contexts(pconn);
#else
  for(cur = s_conn->mech_contexts; cur; cur=cur_next) {
      cur_next = cur->next;
      if(cur->context)
	  cur->mech->plug->mech_dispose(cur->context, s_conn->sparams->utils);
      sasl_FREE(cur);
  }  
  s_conn->mech_contexts = NULL;
#endif /* _SUN_SDK_ */
  
  _sasl_free_utils(&s_conn->sparams->utils);

  if (s_conn->sparams->propctx)
      prop_dispose(&s_conn->sparams->propctx);

  if (s_conn->user_realm)
      sasl_FREE(s_conn->user_realm);

  if (s_conn->sparams)
      sasl_FREE(s_conn->sparams);

  _sasl_conn_dispose(pconn);
}

#ifdef _SUN_SDK_
static int init_mechlist(_sasl_global_context_t *gctx)
{
    mech_list_t *mechlist = gctx->mechlist;
#else
static int init_mechlist(void)
{
#endif /* _SUN_SDK_ */
    sasl_utils_t *newutils = NULL;

    mechlist->mutex = sasl_MUTEX_ALLOC();
    if(!mechlist->mutex) return SASL_FAIL;

    /* set util functions - need to do rest */
#ifdef _SUN_SDK_
    newutils = _sasl_alloc_utils(gctx, NULL, &gctx->server_global_callbacks);
#else
    newutils = _sasl_alloc_utils(NULL, &global_callbacks);
#endif /* _SUN_SDK_ */
    if (newutils == NULL)
	return SASL_NOMEM;

    newutils->checkpass = &_sasl_checkpass;

    mechlist->utils = newutils;
    mechlist->mech_list=NULL;
    mechlist->mech_length=0;

    return SASL_OK;
}

#ifdef _SUN_SDK_
static int load_mech(_sasl_global_context_t *gctx, const char *mechname)
{
    sasl_getopt_t *getopt;
    void *context;
    const char *mlist = NULL;
    const char *cp;
    size_t len;

    /* No sasl_conn_t was given to getcallback, so we provide the
     * global callbacks structure */
    if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) == SASL_OK)
	(void)getopt(&gctx->server_global_callbacks, NULL,
		"server_load_mech_list", &mlist, NULL);

    if (mlist == NULL)
	return (1);

    len = strlen(mechname);
    while (*mlist && isspace((int) *mlist)) mlist++;

    while (*mlist) {
	for (cp = mlist; *cp && !isspace((int) *cp); cp++);
	if (((size_t) (cp - mlist) == len) &&
		!strncasecmp(mlist, mechname, len))
	    break;
	mlist = cp;
	while (*mlist && isspace((int) *mlist)) mlist++;
    }
    return (*mlist != '\0');
}
#endif /* _SUN_SDK_ */

/*
 * parameters:
 *  p - entry point
 */
int sasl_server_add_plugin(const char *plugname,
			   sasl_server_plug_init_t *p)
#ifdef _SUN_SDK_
{
    return (_sasl_server_add_plugin(_sasl_gbl_ctx(), plugname, p));
}

int _sasl_server_add_plugin(void *ctx,
			    const char *plugname,
			    sasl_server_plug_init_t *p)
{
    int nplug = 0;
    int i;
    mechanism_t *m;
    _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
    mech_list_t *mechlist = gctx->mechlist;

    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    int sun_reg;
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
#else
{
#endif /* _SUN_SDK_ */
    int plugcount;
    sasl_server_plug_t *pluglist;
    mechanism_t *mech;
    sasl_server_plug_init_t *entry_point;
    int result;
    int version;
    int lupe;

    if(!plugname || !p) return SASL_BADPARAM;

#ifdef _SUN_SDK_
    if (mechlist == NULL) return SASL_BADPARAM;

    /* Check to see if this plugin has already been registered */
    m = mechlist->mech_list;
    for (i = 0; i < mechlist->mech_length; i++) {
	if (strcmp(plugname, m->plugname) == 0)
		return SASL_OK;
	m = m->next;
    }

    result = LOCK_MUTEX(&server_plug_mutex);
    if (result != SASL_OK)
	return result;

#endif /* _SUN_SDK_ */
    entry_point = (sasl_server_plug_init_t *)p;

    /* call into the shared library asking for information about it */
    /* version is filled in with the version of the plugin */
    result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, &version,
			 &pluglist, &plugcount);

    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    sun_reg = _is_sun_reg(pluglist);
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */

#ifdef _SUN_SDK_
    if (result != SASL_OK) {
	UNLOCK_MUTEX(&server_plug_mutex);
	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
		   SASL_LOG_DEBUG,
		   "server add_plugin entry_point error %z", result);
#else
    if ((result != SASL_OK) && (result != SASL_NOUSER)) {
	_sasl_log(NULL, SASL_LOG_DEBUG,
		  "server add_plugin entry_point error %z\n", result);
#endif /* _SUN_SDK_ */
	return result;
    }

    /* Make sure plugin is using the same SASL version as us */
    if (version != SASL_SERVER_PLUG_VERSION)
    {
#ifdef _SUN_SDK_
	UNLOCK_MUTEX(&server_plug_mutex);
	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
		   SASL_LOG_ERR, "version mismatch on plugin");
#else
	_sasl_log(NULL, SASL_LOG_ERR,
		  "version mismatch on plugin");
#endif /* _SUN_SDK_ */
	return SASL_BADVERS;
    }
#ifdef _SUN_SDK_
    /* Check plugins to make sure mech_name is non-NULL */
    for (lupe=0;lupe < plugcount ;lupe++) {
	if (pluglist[lupe].mech_name == NULL)
	     break;
    }
    if (lupe < plugcount) {
#ifdef _SUN_SDK_
	UNLOCK_MUTEX(&server_plug_mutex);
	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
		   SASL_LOG_ERR, "invalid server plugin %s", plugname);
#else
	_sasl_log(NULL, SASL_LOG_ERR, "invalid server plugin %s", plugname);
#endif /* _SUN_SDK_ */
	return SASL_BADPROT;
    }
#endif /* _SUN_SDK_ */

    for (lupe=0;lupe < plugcount ;lupe++)
    {
#ifdef _SUN_SDK_
	if (!load_mech(gctx, pluglist->mech_name)) {
	     pluglist++;
	     continue;
	}
	nplug++;
#endif /* _SUN_SDK_ */
	mech = sasl_ALLOC(sizeof(mechanism_t));
#ifdef _SUN_SDK_
	if (! mech) {
	    UNLOCK_MUTEX(&server_plug_mutex);
	    return SASL_NOMEM;
	}

	mech->glob_context = pluglist->glob_context;
#else
	if (! mech) return SASL_NOMEM;
#endif /* _SUN_SDK_ */

	mech->plug=pluglist++;
	if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) {
#ifdef _SUN_SDK_
	    UNLOCK_MUTEX(&server_plug_mutex);
#endif /* _SUN_SDK_ */
	    sasl_FREE(mech);
	    return SASL_NOMEM;
	}
	mech->version = version;
#ifdef _SUN_SDK_
	/* EXPORT DELETE START */
	/* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
	mech->sun_reg = sun_reg;
#endif /* _INTEGRATED_SOLARIS_ */
	/* CRYPT DELETE END */
	/* EXPORT DELETE END */

	/* whether this mech actually has any users in it's db */
	mech->condition = SASL_OK;
#else
	/* whether this mech actually has any users in it's db */
	mech->condition = result; /* SASL_OK or SASL_NOUSER */
#endif /* _SUN_SDK_ */

	mech->next = mechlist->mech_list;
	mechlist->mech_list = mech;
	mechlist->mech_length++;
    }

#ifdef _SUN_SDK_
    UNLOCK_MUTEX(&server_plug_mutex);
    return (nplug == 0) ? SASL_NOMECH : SASL_OK;
#else
    return SASL_OK;
#endif /* _SUN_SDK_ */
}

#ifdef _SUN_SDK_
static int server_done(_sasl_global_context_t *gctx) {
  mech_list_t *mechlist = gctx->mechlist;
  _sasl_path_info_t *path_info, *p;
#else
static int server_done(void) {
#endif /* _SUN_SDK_ */
  mechanism_t *m;
  mechanism_t *prevm;

#ifdef _SUN_SDK_
  if(!gctx->sasl_server_active)
      return SASL_NOTINIT;

  if (LOCK_MUTEX(&server_active_mutex) < 0) {
	return (SASL_FAIL);
  }
  gctx->sasl_server_active--;
  
  if(gctx->sasl_server_active) {
      /* Don't de-init yet! Our refcount is nonzero. */
      UNLOCK_MUTEX(&server_active_mutex);
      return SASL_CONTINUE;
  }
#else
  if(!_sasl_server_active)
      return SASL_NOTINIT;
  else
      _sasl_server_active--;
  
  if(_sasl_server_active) {
      /* Don't de-init yet! Our refcount is nonzero. */
      return SASL_CONTINUE;
  }
#endif /* _SUN_SDK_ */

  if (mechlist != NULL)
  {
      m=mechlist->mech_list; /* m point to beginning of the list */

      while (m!=NULL)
      {
	  prevm=m;
	  m=m->next;
    
	  if (prevm->plug->mech_free) {
#ifdef _SUN_SDK_
	      prevm->plug->mech_free(prevm->glob_context,
#else
	      prevm->plug->mech_free(prevm->plug->glob_context,
#endif /* _SUN_SDK_ */
				     mechlist->utils);
	  }

	  sasl_FREE(prevm->plugname);	  	  
	  sasl_FREE(prevm);    
      }
      _sasl_free_utils(&mechlist->utils);
      sasl_MUTEX_FREE(mechlist->mutex);
      sasl_FREE(mechlist);
#ifdef _SUN_SDK_
      gctx->mechlist = NULL;
#else
      mechlist = NULL;
#endif /* _SUN_SDK_ */
  }

  /* Free the auxprop plugins */
#ifdef _SUN_SDK_
  _sasl_auxprop_free(gctx);

  gctx->server_global_callbacks.callbacks = NULL;
  gctx->server_global_callbacks.appname = NULL;

  p = gctx->splug_path_info;
  while((path_info = p) != NULL) {
    sasl_FREE(path_info->path);
    p = path_info->next;
    sasl_FREE(path_info);
  }
  gctx->splug_path_info = NULL;
  UNLOCK_MUTEX(&server_active_mutex);
#else
  _sasl_auxprop_free();

  global_callbacks.callbacks = NULL;
  global_callbacks.appname = NULL;
#endif /* _SUN_SDK_ */

  return SASL_OK;
}

static int server_idle(sasl_conn_t *conn)
{
    mechanism_t *m;
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx;
    mech_list_t *mechlist;

    if (conn == NULL)
        gctx = _sasl_gbl_ctx();
    else
        gctx = conn->gctx;
  mechlist = gctx->mechlist;
#endif /* _SUN_SDK_ */
    if (! mechlist)
	return 0;
    
    for (m = mechlist->mech_list;
	 m!=NULL;
	 m = m->next)
	if (m->plug->idle
#ifdef _SUN_SDK_
	    &&  m->plug->idle(m->glob_context,
#else
	    &&  m->plug->idle(m->plug->glob_context,
#endif /* _SUN_SDK_ */
			      conn,
			      conn ? ((sasl_server_conn_t *)conn)->sparams : NULL))
	    return 1;

    return 0;
}

#ifdef _SUN_SDK_
static int load_config(_sasl_global_context_t *gctx,
		       const sasl_callback_t *verifyfile_cb)
{
  int result;
  const char *conf_to_config = NULL;
  const char *conf_file = NULL;
  int conf_len;
  sasl_global_callbacks_t global_callbacks = gctx->server_global_callbacks;
  char *alloc_file_name=NULL;
  int len;
  const sasl_callback_t *getconf_cb=NULL;
  struct stat buf;
  int full_file = 0;
  int file_exists = 0;

  /* get the path to the plugins; for now the config file will reside there */
  getconf_cb = _sasl_find_getconf_callback(global_callbacks.callbacks);
  if (getconf_cb==NULL) return SASL_BADPARAM;

  result = ((sasl_getpath_t *)(getconf_cb->proc))(getconf_cb->context,
						  &conf_to_config);
  if (result!=SASL_OK) goto done;
  if (conf_to_config == NULL) conf_to_config = "";
  else {
	if (stat(conf_to_config, &buf))
		goto process_file;
	full_file = !S_ISDIR(buf.st_mode);
  }

  if (!full_file) {
    conf_len = strlen(conf_to_config);
    len = strlen(conf_to_config)+2+ strlen(global_callbacks.appname)+5+1;

    if (len > PATH_MAX ) {
      result = SASL_FAIL;
      goto done;
    }

    /* construct the filename for the config file */
    alloc_file_name = sasl_ALLOC(len);
    if (! alloc_file_name) {
        result = SASL_NOMEM;
        goto done;
    }

    snprintf(alloc_file_name, len, "%.*s/%s.conf", conf_len, conf_to_config, 
	   global_callbacks.appname);

  }
  conf_file = full_file ? conf_to_config : alloc_file_name;

  if (full_file || stat(conf_file, &buf) == 0)
	file_exists = S_ISREG(buf.st_mode);

process_file:
  /* Check to see if anything has changed */
  if (file_exists && gctx->config_path != NULL &&
	strcmp(conf_file, gctx->config_path) == 0 &&
	gctx->config_last_read == buf.st_mtime) {
    /* File has not changed */
    goto done;
  } else if (gctx->config_path == NULL) {
    /* No new file, nothing has changed  */
    if (!file_exists)
	goto done;
  } else {
    sasl_config_free(gctx);
    if (!file_exists) {
	gctx->config_path = NULL;
	goto done;
    }
  }
  gctx->config_last_read = buf.st_mtime;

  /* Ask the application if it's safe to use this file */
  result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context,
		conf_file, SASL_VRFY_CONF);

  /* returns continue if this file is to be skipped */
  
  /* returns SASL_CONTINUE if doesn't exist
   * if doesn't exist we can continue using default behavior
   */
  if (result==SASL_OK)
    result=sasl_config_init(gctx, conf_file);

 done:
  if (alloc_file_name) sasl_FREE(alloc_file_name);

  return result;
}
#else
static int load_config(const sasl_callback_t *verifyfile_cb)
{
  int result;
  const char *path_to_config=NULL;
  const char *c;
  unsigned path_len;

  char *config_filename=NULL;
  int len;
  const sasl_callback_t *getpath_cb=NULL;

  /* get the path to the plugins; for now the config file will reside there */
  getpath_cb=_sasl_find_getpath_callback( global_callbacks.callbacks );
  if (getpath_cb==NULL) return SASL_BADPARAM;

  /* getpath_cb->proc MUST be a sasl_getpath_t; if only c had a type
     system */
  result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context,
						  &path_to_config);
  if (result!=SASL_OK) goto done;
  if (path_to_config == NULL) path_to_config = "";

  c = strchr(path_to_config, PATHS_DELIMITER);

  /* length = length of path + '/' + length of appname + ".conf" + 1
     for '\0' */

  if(c != NULL)
    path_len = c - path_to_config;
  else
    path_len = strlen(path_to_config);

  len = path_len + 2 + strlen(global_callbacks.appname) + 5 + 1;

  if (len > PATH_MAX ) {
      result = SASL_FAIL;
      goto done;
  }

  /* construct the filename for the config file */
  config_filename = sasl_ALLOC(len);
  if (! config_filename) {
      result = SASL_NOMEM;
      goto done;
  }

  snprintf(config_filename, len, "%.*s/%s.conf", path_len, path_to_config, 
	   global_callbacks.appname);

  /* Ask the application if it's safe to use this file */
  result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context,
					config_filename, SASL_VRFY_CONF);

  /* returns continue if this file is to be skipped */
  
  /* returns SASL_CONTINUE if doesn't exist
   * if doesn't exist we can continue using default behavior
   */
  if (result==SASL_OK)
    result=sasl_config_init(config_filename);

 done:
  if (config_filename) sasl_FREE(config_filename);

  return result;
}
#endif /* _SUN_SDK_ */

/*
 * Verify that all the callbacks are valid
 */
static int verify_server_callbacks(const sasl_callback_t *callbacks)
{
    if (callbacks == NULL) return SASL_OK;

    while (callbacks->id != SASL_CB_LIST_END) {
	if (callbacks->proc==NULL) return SASL_FAIL;

	callbacks++;
    }

    return SASL_OK;
}

#ifndef _SUN_SDK_
static char *grab_field(char *line, char **eofield)
{
    int d = 0;
    char *field;

    while (isspace((int) *line)) line++;

    /* find end of field */
    while (line[d] && !isspace(((int) line[d]))) d++;
    field = sasl_ALLOC(d + 1);
    if (!field) { return NULL; }
    memcpy(field, line, d);
    field[d] = '\0';
    *eofield = line + d;
    
    return field;
}

struct secflag_map_s {
    char *name;
    int value;
};

struct secflag_map_s secflag_map[] = {
    { "noplaintext", SASL_SEC_NOPLAINTEXT },
    { "noactive", SASL_SEC_NOACTIVE },
    { "nodictionary", SASL_SEC_NODICTIONARY },
    { "forward_secrecy", SASL_SEC_FORWARD_SECRECY },
    { "noanonymous", SASL_SEC_NOANONYMOUS },
    { "pass_credentials", SASL_SEC_PASS_CREDENTIALS },
    { "mutual_auth", SASL_SEC_MUTUAL_AUTH },
    { NULL, 0x0 }
};

static int parse_mechlist_file(const char *mechlistfile)
{
    FILE *f;
    char buf[1024];
    char *t, *ptr;
    int r = 0;

    f = fopen(mechlistfile, "rF");
    if (!f) return SASL_FAIL;

    r = SASL_OK;
    while (fgets(buf, sizeof(buf), f) != NULL) {
	mechanism_t *n = sasl_ALLOC(sizeof(mechanism_t));
	sasl_server_plug_t *nplug;

	if (n == NULL) { r = SASL_NOMEM; break; }
	n->version = SASL_SERVER_PLUG_VERSION;
	n->condition = SASL_CONTINUE;
	nplug = sasl_ALLOC(sizeof(sasl_server_plug_t));
	if (nplug == NULL) { r = SASL_NOMEM; break; }
	memset(nplug, 0, sizeof(sasl_server_plug_t));

	/* each line is:
	   plugin-file WS mech_name WS max_ssf *(WS security_flag) RET
	*/
	
	/* grab file */
	n->f = grab_field(buf, &ptr);

	/* grab mech_name */
	nplug->mech_name = grab_field(ptr, &ptr);

	/* grab max_ssf */
	nplug->max_ssf = strtol(ptr, &ptr, 10);

	/* grab security flags */
	while (*ptr != '\n') {
	    struct secflag_map_s *map;

	    /* read security flag */
	    t = grab_field(ptr, &ptr);
	    map = secflag_map;
	    while (map->name) {
		if (!strcasecmp(t, map->name)) {
		    nplug->security_flags |= map->value;
		    break;
		}
		map++;
	    }
	    if (!map->name) {
		_sasl_log(NULL, SASL_LOG_ERR,
			  "%s: couldn't identify flag '%s'",
			  nplug->mech_name, t);
	    }
	    free(t);
	}

	/* insert mechanism into mechlist */
	n->plug = nplug;
	n->next = mechlist->mech_list;
	mechlist->mech_list = n;
	mechlist->mech_length++;
    }

    fclose(f);
    return r;
}
#endif /* !_SUN_SDK_ */

#ifdef _SUN_SDK_
static int _load_server_plugins(_sasl_global_context_t *gctx)
{
    int ret;
    const add_plugin_list_t _ep_list[] = {
	{ "sasl_server_plug_init", (add_plugin_t *)_sasl_server_add_plugin },
	{ "sasl_auxprop_plug_init", (add_plugin_t *)_sasl_auxprop_add_plugin },
	{ "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin },
	{ NULL, NULL }
    };
    const sasl_callback_t *callbacks = gctx->server_global_callbacks.callbacks;

    ret = _sasl_load_plugins(gctx, 1, _ep_list,
			     _sasl_find_getpath_callback(callbacks),
			     _sasl_find_verifyfile_callback(callbacks));
    return (ret);
}
#endif /* _SUN_SDK_ */

/* initialize server drivers, done once per process
#ifdef _SUN_SDK_
 *  callbacks      -- callbacks for all server connections
 *  appname        -- name of calling application (for config)
#else
 *  callbacks      -- callbacks for all server connections; must include
 *                    getopt callback
 *  appname        -- name of calling application (for lower level logging)
 * results:
 *  state          -- server state
#endif
 * returns:
 *  SASL_OK        -- success
 *  SASL_BADPARAM  -- error in config file
 *  SASL_NOMEM     -- memory failure
#ifndef _SUN_SDK_
 *  SASL_BADVERS   -- Mechanism version mismatch
#endif
 */

int sasl_server_init(const sasl_callback_t *callbacks,
		     const char *appname)
#ifdef _SUN_SDK_
{
	return _sasl_server_init(NULL, callbacks, appname);
}

int _sasl_server_init(void *ctx, const sasl_callback_t *callbacks,
		     const char *appname)
#endif /* _SUN_SDK_ */
{
    int ret;
    const sasl_callback_t *vf;
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
#else
    const char *pluginfile = NULL;
#ifdef PIC
    sasl_getopt_t *getopt;
    void *context;
#endif

    const add_plugin_list_t ep_list[] = {
	{ "sasl_server_plug_init", (add_plugin_t *)sasl_server_add_plugin },
	{ "sasl_auxprop_plug_init", (add_plugin_t *)sasl_auxprop_add_plugin },
	{ "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
	{ NULL, NULL }
    };
#endif /* _SUN_SDK_ */

    /* we require the appname to be non-null and short enough to be a path */
    if (!appname || strlen(appname) >= PATH_MAX)
	return SASL_BADPARAM;

#ifdef _SUN_SDK_
    /* Process only one _sasl_server_init() at a time */
    if (LOCK_MUTEX(&init_server_mutex) < 0)
	return (SASL_FAIL);
    if (LOCK_MUTEX(&server_active_mutex) < 0)
	return (SASL_FAIL);

    if (gctx->sasl_server_active) {
	/* We're already active, just increase our refcount */
	/* xxx do something with the callback structure? */
	gctx->sasl_server_active++;
	UNLOCK_MUTEX(&server_active_mutex);
  	UNLOCK_MUTEX(&init_server_mutex);
	return SASL_OK;
    }
    
    ret = _sasl_common_init(gctx, &gctx->server_global_callbacks, 1);
    if (ret != SASL_OK) {
	UNLOCK_MUTEX(&server_active_mutex);
  	UNLOCK_MUTEX(&init_server_mutex);
	return ret;
    }
#else
    if (_sasl_server_active) {
	/* We're already active, just increase our refcount */
	/* xxx do something with the callback structure? */
	_sasl_server_active++;
	return SASL_OK;
    }
    
    ret = _sasl_common_init(&global_callbacks);
    if (ret != SASL_OK)
	return ret;
#endif /* _SUN_SDK_ */
 
    /* verify that the callbacks look ok */
    ret = verify_server_callbacks(callbacks);
#ifdef _SUN_SDK_
    if (ret != SASL_OK) {
	UNLOCK_MUTEX(&server_active_mutex);
  	UNLOCK_MUTEX(&init_server_mutex);
	return ret;
    }

    gctx->server_global_callbacks.callbacks = callbacks;
    gctx->server_global_callbacks.appname = appname;

    /* If we fail now, we have to call server_done */
    gctx->sasl_server_active = 1;
    UNLOCK_MUTEX(&server_active_mutex);

    /* allocate mechlist and set it to empty */
    gctx->mechlist = sasl_ALLOC(sizeof(mech_list_t));
    if (gctx->mechlist == NULL) {
	server_done(gctx);
  	UNLOCK_MUTEX(&init_server_mutex);
	return SASL_NOMEM;
    }

    ret = init_mechlist(gctx);

    if (ret != SASL_OK) {
	server_done(gctx);
  	UNLOCK_MUTEX(&init_server_mutex);
	return ret;
    }
#else
    if (ret != SASL_OK)
	return ret;

    global_callbacks.callbacks = callbacks;
    global_callbacks.appname = appname;

    /* If we fail now, we have to call server_done */
    _sasl_server_active = 1;

    /* allocate mechlist and set it to empty */
    mechlist = sasl_ALLOC(sizeof(mech_list_t));
    if (mechlist == NULL) {
	server_done();
	return SASL_NOMEM;
    }

    ret = init_mechlist();
    if (ret != SASL_OK) {
	server_done();
	return ret;
    }
#endif /* _SUN_SDK_ */

    vf = _sasl_find_verifyfile_callback(callbacks);

    /* load config file if applicable */
#ifdef _SUN_SDK_
    ret = load_config(gctx, vf);
    if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) {
	server_done(gctx);
  	UNLOCK_MUTEX(&init_server_mutex);
#else
    ret = load_config(vf);
    if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) {
	server_done();
#endif /* _SUN_SDK_ */
	return ret;
    }

    /* load internal plugins */
#ifdef _SUN_SDK_
    _sasl_server_add_plugin(gctx, "EXTERNAL", &external_server_plug_init);

/* NOTE: plugin_list option not supported in SUN SDK */
    {
#else
    sasl_server_add_plugin("EXTERNAL", &external_server_plug_init);

#ifdef PIC
    /* delayed loading of plugins? (DSO only, as it doesn't
     * make much [any] sense to delay in the static library case) */
    if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) 
	   == SASL_OK) {
	/* No sasl_conn_t was given to getcallback, so we provide the
	 * global callbacks structure */
	ret = getopt(&global_callbacks, NULL, "plugin_list", &pluginfile, NULL);
    }
#endif
    
    if (pluginfile != NULL) {
	/* this file should contain a list of plugins available.
	   we'll load on demand. */

	/* Ask the application if it's safe to use this file */
	ret = ((sasl_verifyfile_t *)(vf->proc))(vf->context,
						pluginfile,
						SASL_VRFY_CONF);
	if (ret != SASL_OK) {
	    _sasl_log(NULL, SASL_LOG_ERR,
		      "unable to load plugin list %s: %z", pluginfile, ret);
	}
	
	if (ret == SASL_OK) {
	    ret = parse_mechlist_file(pluginfile);
	}
    } else {
#endif /* _SUN_SDK_ */
	/* load all plugins now */
#ifdef _SUN_SDK_
	ret = _load_server_plugins(gctx);
#else
	ret = _sasl_load_plugins(ep_list,
				 _sasl_find_getpath_callback(callbacks),
				 _sasl_find_verifyfile_callback(callbacks));
#endif /* _SUN_SDK_ */
    }

#ifdef _SUN_SDK_
    if (ret == SASL_OK)
	ret = _sasl_build_mechlist(gctx);
    if (ret == SASL_OK) {
	gctx->sasl_server_cleanup_hook = &server_done;
	gctx->sasl_server_idle_hook = &server_idle;
    } else {
	server_done(gctx);
    }
    UNLOCK_MUTEX(&init_server_mutex);
#else
    if (ret == SASL_OK) {
	_sasl_server_cleanup_hook = &server_done;
	_sasl_server_idle_hook = &server_idle;

	ret = _sasl_build_mechlist();
    } else {
	server_done();
    }
#endif /* _SUN_SDK_ */

    return ret;
}

/*
 * Once we have the users plaintext password we 
 * may want to transition them. That is put entries
 * for them in the passwd database for other
 * stronger mechanism
 *
 * for example PLAIN -> CRAM-MD5
 */
static int
_sasl_transition(sasl_conn_t * conn,
		 const char * pass,
		 unsigned passlen)
{
    const char *dotrans = "n";
    sasl_getopt_t *getopt;
    int result = SASL_OK;
    void *context;

    if (! conn)
	return SASL_BADPARAM;

    if (! conn->oparams.authid)
	PARAMERROR(conn);

    /* check if this is enabled: default to false */
    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK)
    {
	getopt(context, NULL, "auto_transition", &dotrans, NULL);
	if (dotrans == NULL) dotrans = "n";
    }

    if (*dotrans == '1' || *dotrans == 'y' ||
	(*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') {
	/* ok, it's on! */
	result = sasl_setpass(conn,
			      conn->oparams.authid,
			      pass,
			      passlen,
			      NULL, 0, 0);
    }

    RETURN(conn,result);
}


/* create context for a single SASL connection
 *  service        -- registered name of the service using SASL (e.g. "imap")
 *  serverFQDN     -- Fully qualified domain name of server.  NULL means use
 *                    gethostname() or equivalent.
 *                    Useful for multi-homed servers.
 *  user_realm     -- permits multiple user realms on server, NULL = default
 *  iplocalport    -- server IPv4/IPv6 domain literal string with port
 *                    (if NULL, then mechanisms requiring IPaddr are disabled)
 *  ipremoteport   -- client IPv4/IPv6 domain literal string with port
 *                    (if NULL, then mechanisms requiring IPaddr are disabled)
 *  callbacks      -- callbacks (e.g., authorization, lang, new getopt context)
 *  flags          -- usage flags (see above)
 * returns:
 *  pconn          -- new connection context
 *
 * returns:
 *  SASL_OK        -- success
 *  SASL_NOMEM     -- not enough memory
 */

int sasl_server_new(const char *service,
		    const char *serverFQDN,
		    const char *user_realm,
		    const char *iplocalport,
		    const char *ipremoteport,
		    const sasl_callback_t *callbacks,
		    unsigned flags,
		    sasl_conn_t **pconn)
#ifdef _SUN_SDK_
{
    return _sasl_server_new(NULL, service, serverFQDN, user_realm, iplocalport,
			   ipremoteport, callbacks, flags, pconn);
}

int _sasl_server_new(void *ctx,
		    const char *service,
		    const char *serverFQDN,
		    const char *user_realm,
		    const char *iplocalport,
		    const char *ipremoteport,
		    const sasl_callback_t *callbacks,
		    unsigned flags,
		    sasl_conn_t **pconn)
#endif /* _SUN_SDK_ */
{
  int result;
  sasl_server_conn_t *serverconn;
  sasl_utils_t *utils;
  sasl_getopt_t *getopt;
  void *context;
  const char *log_level;

#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx = (ctx == NULL) ? _sasl_gbl_ctx() : ctx;
 
  if (gctx->sasl_server_active==0) return SASL_NOTINIT;
#else
  if (_sasl_server_active==0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
  if (! pconn) return SASL_FAIL;
  if (! service) return SASL_FAIL;

  *pconn=sasl_ALLOC(sizeof(sasl_server_conn_t));
  if (*pconn==NULL) return SASL_NOMEM;

  memset(*pconn, 0, sizeof(sasl_server_conn_t));

#ifdef _SUN_SDK_
  (*pconn)->gctx = gctx;
#endif /* _SUN_SDK_ */

  serverconn = (sasl_server_conn_t *)*pconn;

  /* make sparams */
  serverconn->sparams=sasl_ALLOC(sizeof(sasl_server_params_t));
  if (serverconn->sparams==NULL)
      MEMERROR(*pconn);

  memset(serverconn->sparams, 0, sizeof(sasl_server_params_t));

  (*pconn)->destroy_conn = &server_dispose;
  result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_SERVER,
			   &server_idle, serverFQDN,
			   iplocalport, ipremoteport,
#ifdef _SUN_SDK_
			   callbacks, &gctx->server_global_callbacks);
#else
			   callbacks, &global_callbacks);
#endif /* _SUN_SDK_ */
  if (result != SASL_OK)
      goto done_error;


  /* set util functions - need to do rest */
#ifdef _SUN_SDK_
  utils=_sasl_alloc_utils(gctx, *pconn, &gctx->server_global_callbacks);
#else
  utils=_sasl_alloc_utils(*pconn, &global_callbacks);
#endif /* _SUN_SDK_ */
  if (!utils) {
      result = SASL_NOMEM;
      goto done_error;
  }
  
#ifdef _SUN_SDK_
  utils->checkpass = &_sasl_checkpass;
#else /* _SUN_SDK_ */  
  utils->checkpass = &sasl_checkpass;
#endif /* _SUN_SDK_ */

  /* Setup the propctx -> We'll assume the default size */
  serverconn->sparams->propctx=prop_new(0);
  if(!serverconn->sparams->propctx) {
      result = SASL_NOMEM;
      goto done_error;
  }

  serverconn->sparams->service = (*pconn)->service;
  serverconn->sparams->servicelen = strlen((*pconn)->service);

#ifdef _SUN_SDK_
  serverconn->sparams->appname = gctx->server_global_callbacks.appname;
  serverconn->sparams->applen = strlen(gctx->server_global_callbacks.appname);
#else
  serverconn->sparams->appname = global_callbacks.appname;
  serverconn->sparams->applen = strlen(global_callbacks.appname);
#endif /* _SUN_SDK_ */

  serverconn->sparams->serverFQDN = (*pconn)->serverFQDN;
  serverconn->sparams->slen = strlen((*pconn)->serverFQDN);

  if (user_realm) {
      result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL);
      serverconn->sparams->urlen = strlen(user_realm);
      serverconn->sparams->user_realm = serverconn->user_realm;
  } else {
      serverconn->user_realm = NULL;
      /* the sparams is already zeroed */
  }

#ifdef _SUN_SDK_
  serverconn->sparams->iplocalport = (*pconn)->iplocalport;
  serverconn->sparams->iploclen = strlen((*pconn)->iplocalport);
  serverconn->sparams->ipremoteport = (*pconn)->ipremoteport;
  serverconn->sparams->ipremlen = strlen((*pconn)->ipremoteport);

  serverconn->sparams->callbacks = callbacks;
#endif /* _SUN_SDK_ */

  log_level = NULL;
  if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
    getopt(context, NULL, "log_level", &log_level, NULL);
  }
  serverconn->sparams->log_level = log_level ? atoi(log_level) : SASL_LOG_ERR;

  serverconn->sparams->utils = utils;
  serverconn->sparams->transition = &_sasl_transition;
  serverconn->sparams->canon_user = &_sasl_canon_user;
  serverconn->sparams->props = serverconn->base.props;
  serverconn->sparams->flags = flags;

  if(result == SASL_OK) return SASL_OK;

 done_error:
  _sasl_conn_dispose(*pconn);
  sasl_FREE(*pconn);
  *pconn = NULL;
  return result;
}

/*
 * The rule is:
 * IF mech strength + external strength < min ssf THEN FAIL
 * We also have to look at the security properties and make sure
 * that this mechanism has everything we want
 */
static int mech_permitted(sasl_conn_t *conn,
			  mechanism_t *mech)
{
    sasl_server_conn_t *s_conn = (sasl_server_conn_t *)conn;
    const sasl_server_plug_t *plug;
    int myflags;
    context_list_t *cur;
    sasl_getopt_t *getopt;
    void *context;
    sasl_ssf_t minssf = 0;
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx;
#endif /* _SUN_SDK_ */

    if(!conn) return 0;

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

    if(! mech || ! mech->plug) {
#ifdef _SUN_SDK_
	if(conn) _sasl_log(conn, SASL_LOG_WARN, "Parameter error");
#else
	PARAMERROR(conn);
#endif /* _SUN_SDK_ */
	return 0;
    }
    
    plug = mech->plug;

    /* get the list of allowed mechanisms (default = all) */
    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
            == SASL_OK) {
	const char *mlist = NULL;

	getopt(context, NULL, "mech_list", &mlist, NULL);

	/* if we have a list, check the plugin against it */
	if (mlist) {
	    const char *cp;

	    while (*mlist) {
		for (cp = mlist; *cp && !isspace((int) *cp); cp++);
		if (((size_t) (cp - mlist) == strlen(plug->mech_name)) &&
		    !strncasecmp(mlist, plug->mech_name,
				 strlen(plug->mech_name))) {
		    break;
		}
		mlist = cp;
		while (*mlist && isspace((int) *mlist)) mlist++;
	    }

	    if (!*mlist) return 0;  /* reached EOS -> not in our list */
	}
    }

    /* setup parameters for the call to mech_avail */
    s_conn->sparams->serverFQDN=conn->serverFQDN;
    s_conn->sparams->service=conn->service;
    s_conn->sparams->user_realm=s_conn->user_realm;
    s_conn->sparams->props=conn->props;
    s_conn->sparams->external_ssf=conn->external.ssf;

    /* Check if we have banished this one already */
    for(cur = s_conn->mech_contexts; cur; cur=cur->next) {
	if(cur->mech == mech) {
	    /* If it's not mech_avail'd, then stop now */
	    if(!cur->context) return 0;
	    break;
	}
    }
    
    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    if (!mech->sun_reg) {
	s_conn->sparams->props.min_ssf = 0;
	s_conn->sparams->props.max_ssf = 0;
    }
    s_conn->base.sun_reg = mech->sun_reg;
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
    if (conn->props.min_ssf < conn->external.ssf) {
	minssf = 0;
    } else {
	minssf = conn->props.min_ssf - conn->external.ssf;
    }
    
    /* Generic mechanism */
    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    /* If not SUN supplied mech, it has no strength */
    if (plug->max_ssf < minssf || (minssf > 0 && !mech->sun_reg)) {
#else
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
    if (plug->max_ssf < minssf) {
    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
#ifdef _INTEGRATED_SOLARIS_
	sasl_seterror(conn, SASL_NOLOG,
		      gettext("mech %s is too weak"), plug->mech_name);
#else
	sasl_seterror(conn, SASL_NOLOG,
		      "mech %s is too weak", plug->mech_name);
#endif /* _INTEGRATED_SOLARIS_ */
	return 0; /* too weak */
    }

    context = NULL;
    if(plug->mech_avail
#ifdef _SUN_SDK_
       && plug->mech_avail(mech->glob_context,
#else
       && plug->mech_avail(plug->glob_context,
#endif /* _SUN_SDK_ */
			   s_conn->sparams, (void **)&context) != SASL_OK ) {
	/* Mark this mech as no good for this connection */
	cur = sasl_ALLOC(sizeof(context_list_t));
	if(!cur) {
#ifdef _SUN_SDK_
	    if(conn) _sasl_log(conn, SASL_LOG_WARN, "Out of Memory");
#else
	    MEMERROR(conn);
#endif /* _SUN_SDK_ */
	    return 0;
	}
	cur->context = NULL;
	cur->mech = mech;
	cur->next = s_conn->mech_contexts;
	s_conn->mech_contexts = cur;

	/* Error should be set by mech_avail call */
	return 0;
    } else if(context) {
	/* Save this context */
	cur = sasl_ALLOC(sizeof(context_list_t));
	if(!cur) {
#ifdef _SUN_SDK_
	    if(conn) _sasl_log(conn, SASL_LOG_WARN, "Out of Memory");
#else
	    MEMERROR(conn);
#endif /* _SUN_SDK_ */
	    return 0;
	}
	cur->context = context;
	cur->mech = mech;
	cur->next = s_conn->mech_contexts;
	s_conn->mech_contexts = cur;
    }
    
    /* Generic mechanism */
    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
    /* If not SUN supplied mech, it has no strength */
    if (plug->max_ssf < minssf || (minssf > 0 && !mech->sun_reg)) {
#else
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
    if (plug->max_ssf < minssf) {
    /* EXPORT DELETE START */
    /* CRYPT DELETE START */
#endif /* _INTEGRATED_SOLARIS_ */
    /* CRYPT DELETE END */
    /* EXPORT DELETE END */
#ifdef _INTEGRATED_SOLARIS_
	sasl_seterror(conn, SASL_NOLOG, gettext("too weak"));
#else
	sasl_seterror(conn, SASL_NOLOG, "too weak");
#endif /* _INTEGRATED_SOLARIS_ */
	return 0; /* too weak */
    }

#ifndef _SUN_SDK_
    /* if there are no users in the secrets database we can't use this 
       mechanism */
    if (mech->condition == SASL_NOUSER) {
	sasl_seterror(conn, 0, "no users in secrets db");
	return 0;
    }
#endif /* !_SUN_SDK_ */

    /* Can it meet our features? */
    if ((conn->flags & SASL_NEED_PROXY) &&
	!(plug->features & SASL_FEAT_ALLOWS_PROXY)) {
	return 0;
    }
    
    /* security properties---if there are any flags that differ and are
       in what the connection are requesting, then fail */
    
    /* special case plaintext */
    myflags = conn->props.security_flags;

    /* if there's an external layer this is no longer plaintext */
    if ((conn->props.min_ssf <= conn->external.ssf) && 
	(conn->external.ssf > 1)) {
	myflags &= ~SASL_SEC_NOPLAINTEXT;
    }

    /* do we want to special case SASL_SEC_PASS_CREDENTIALS? nah.. */
    if (((myflags ^ plug->security_flags) & myflags) != 0) {
#ifdef _INTEGRATED_SOLARIS_
	sasl_seterror(conn, SASL_NOLOG,
		      gettext("security flags do not match required"));
#else
	sasl_seterror(conn, SASL_NOLOG,
		      "security flags do not match required");
#endif /* _INTEGRATED_SOLARIS_ */
	return 0;
    }

    /* Check Features */
    if(plug->features & SASL_FEAT_GETSECRET) {
	/* We no longer support sasl_server_{get,put}secret */
#ifdef _SUN_SDK_
	_sasl_log(conn, SASL_LOG_ERR,
		  "mech %s requires unprovided secret facility",
		  plug->mech_name);
#else
	sasl_seterror(conn, 0,
		      "mech %s requires unprovided secret facility",
		      plug->mech_name);
#endif /* _SUN_SDK_ */
	return 0;
    }

    return 1;
}

/*
 * make the authorization 
 *
 */

static int do_authorization(sasl_server_conn_t *s_conn)
{
    int ret;
    sasl_authorize_t *authproc;
    void *auth_context;
    
    /* now let's see if authname is allowed to proxy for username! */
    
    /* check the proxy callback */
    if (_sasl_getcallback(&s_conn->base, SASL_CB_PROXY_POLICY,
			  &authproc, &auth_context) != SASL_OK) {
	INTERROR(&s_conn->base, SASL_NOAUTHZ);
    }

    ret = authproc(&(s_conn->base), auth_context,
		   s_conn->base.oparams.user, s_conn->base.oparams.ulen,
		   s_conn->base.oparams.authid, s_conn->base.oparams.alen,
		   s_conn->user_realm,
		   (s_conn->user_realm ? strlen(s_conn->user_realm) : 0),
		   s_conn->sparams->propctx);

    RETURN(&s_conn->base, ret);
}


/* start a mechanism exchange within a connection context
 *  mech           -- the mechanism name client requested
 *  clientin       -- client initial response (NUL terminated), NULL if empty
 *  clientinlen    -- length of initial response
 *  serverout      -- initial server challenge, NULL if done 
 *                    (library handles freeing this string)
 *  serveroutlen   -- length of initial server challenge
#ifdef _SUN_SDK_
 * conn            -- the sasl connection
#else
 * output:
 *  pconn          -- the connection negotiation state on success
#endif
 *
 * Same returns as sasl_server_step() or
 * SASL_NOMECH if mechanism not available.
 */
int sasl_server_start(sasl_conn_t *conn,
		      const char *mech,
		      const char *clientin,
		      unsigned clientinlen,
		      const char **serverout,
		      unsigned *serveroutlen)
{
    sasl_server_conn_t *s_conn=(sasl_server_conn_t *) conn;
    int result;
    context_list_t *cur, **prev;
    mechanism_t *m;

#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx =
		 (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
    mech_list_t *mechlist;
 
    if (gctx->sasl_server_active==0) return SASL_NOTINIT;
    if (! conn)
	return SASL_BADPARAM;
 
    (void)_load_server_plugins(gctx);
    mechlist = gctx->mechlist;
    m=mechlist->mech_list;
    result = load_config(gctx, _sasl_find_verifyfile_callback(
	gctx->server_global_callbacks.callbacks));
    if (result != SASL_OK)
	return (result);
#else
    if (_sasl_server_active==0) return SASL_NOTINIT;

    /* make sure mech is valid mechanism
       if not return appropriate error */
    m=mechlist->mech_list;

    /* check parameters */
    if(!conn) return SASL_BADPARAM;
#endif /* _SUN_SDK_ */
    
    if (!mech || ((clientin==NULL) && (clientinlen>0)))
	PARAMERROR(conn);

    if(serverout) *serverout = NULL;
    if(serveroutlen) *serveroutlen = 0;

    while (m!=NULL)
    {
	if ( strcasecmp(mech,m->plug->mech_name)==0)
	{
	    break;
	}
	m=m->next;
    }
  
    if (m==NULL) {
#ifdef _INTEGRATED_SOLARIS_
	sasl_seterror(conn, 0, gettext("Couldn't find mech %s"), mech);
#else
	sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
#endif /* _INTEGRATED_SOLARIS_ */
	result = SASL_NOMECH;
	goto done;
    }

#ifdef _SUN_SDK_
    server_dispose_mech_contexts(conn);
#endif /*_SUN_SDK_ */

    /* Make sure that we're willing to use this mech */
    if (! mech_permitted(conn, m)) {
	result = SASL_NOMECH;
	goto done;
    }

#ifdef _SUN_SDK_
    if(conn->context) {
	s_conn->mech->plug->mech_dispose(conn->context, s_conn->sparams->utils);
	conn->context = NULL;
    }
    memset(&conn->oparams, 0, sizeof(sasl_out_params_t));
#else
    if (m->condition == SASL_CONTINUE) {
	sasl_server_plug_init_t *entry_point;
	void *library = NULL;
	sasl_server_plug_t *pluglist;
	int version, plugcount;
	int l = 0;

	/* need to load this plugin */
	result = _sasl_get_plugin(m->f,
		    _sasl_find_verifyfile_callback(global_callbacks.callbacks),
				  &library);

	if (result == SASL_OK) {
	    result = _sasl_locate_entry(library, "sasl_server_plug_init",
					(void **)&entry_point);
	}

	if (result == SASL_OK) {
	    result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
				 &version, &pluglist, &plugcount);
	}

	if (result == SASL_OK) {
	    /* find the correct mechanism in this plugin */
	    for (l = 0; l < plugcount; l++) {
		if (!strcasecmp(pluglist[l].mech_name, 
				m->plug->mech_name)) break;
	    }
	    if (l == plugcount) {
		result = SASL_NOMECH;
	    }
	}
	if (result == SASL_OK) {
	    /* check that the parameters are the same */
	    if ((pluglist[l].max_ssf != m->plug->max_ssf) ||
		(pluglist[l].security_flags != m->plug->security_flags)) {
		_sasl_log(conn, SASL_LOG_ERR, 
			  "%s: security parameters don't match mechlist file",
			  pluglist[l].mech_name);
		result = SASL_NOMECH;
	    }
	}
	if (result == SASL_OK) {
	    /* copy mechlist over */
	    sasl_FREE((sasl_server_plug_t *) m->plug);
	    m->plug = &pluglist[l];
	    m->condition = SASL_OK;
	}

	if (result != SASL_OK) {
	    /* The library will eventually be freed, don't sweat it */
	    RETURN(conn, result);
	}
    }
#endif /* !_SUN_SDK_ */

    /* We used to setup sparams HERE, but now it's done
       inside of mech_permitted (which is called above) */
    prev = &s_conn->mech_contexts;
    for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
	if(cur->mech == m) {
	    if(!cur->context) {
#ifdef _SUN_SDK_
		_sasl_log(conn, SASL_LOG_ERR,
			  "Got past mech_permitted with a disallowed mech!");
#else
		sasl_seterror(conn, 0,
			      "Got past mech_permitted with a disallowed mech!");
#endif /* _SUN_SDK_ */
		return SASL_NOMECH;
	    }
	    /* If we find it, we need to pull cur out of the
	       list so it won't be freed later! */
	    (*prev)->next = cur->next;
	    conn->context = cur->context;
	    sasl_FREE(cur);
	}
    }

    s_conn->mech = m;
    
    if(!conn->context) {
	/* Note that we don't hand over a new challenge */
#ifdef _SUN_SDK_
	result = s_conn->mech->plug->mech_new(s_conn->mech->glob_context,
#else
	result = s_conn->mech->plug->mech_new(s_conn->mech->plug->glob_context,
#endif /* _SUN_SDK_ */
					      s_conn->sparams,
					      NULL,
					      0,
					      &(conn->context));
    } else {
	/* the work was already done by mech_avail! */
	result = SASL_OK;
    }
    
    if (result == SASL_OK) {
         if(clientin) {
            if(s_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) {
                /* Remote sent first, but mechanism does not support it.
                 * RFC 2222 says we fail at this point. */
#ifdef _SUN_SDK_
		_sasl_log(conn, SASL_LOG_ERR,
                          "Remote sent first but mech does not allow it.");
#else
                sasl_seterror(conn, 0,
                              "Remote sent first but mech does not allow it.");
#endif /* _SUN_SDK_ */
                result = SASL_BADPROT;
            } else {
                /* Mech wants client-first, so let them have it */
                result = sasl_server_step(conn,
                                          clientin, clientinlen,
                                          serverout, serveroutlen);
            }
        } else {
            if(s_conn->mech->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
                /* Mech wants client first anyway, so we should do that */
                *serverout = "";
                *serveroutlen = 0;
                result = SASL_CONTINUE;
            } else {
                /* Mech wants server-first, so let them have it */
                result = sasl_server_step(conn,
                                          clientin, clientinlen,
                                          serverout, serveroutlen);
            }
	}
    }

 done:
    if(   result != SASL_OK
       && result != SASL_CONTINUE
       && result != SASL_INTERACT) {
	if(conn->context) {
	    s_conn->mech->plug->mech_dispose(conn->context,
					     s_conn->sparams->utils);
	    conn->context = NULL;
	}
    }
    
    RETURN(conn,result);
}


/* perform one step of the SASL exchange
 *  inputlen & input -- client data
 *                      NULL on first step if no optional client step
 *  outputlen & output -- set to the server data to transmit
 *                        to the client in the next step
 *                        (library handles freeing this)
 *
 * returns:
 *  SASL_OK        -- exchange is complete.
 *  SASL_CONTINUE  -- indicates another step is necessary.
 *  SASL_TRANS     -- entry for user exists, but not for mechanism
 *                    and transition is possible
 *  SASL_BADPARAM  -- service name needed
 *  SASL_BADPROT   -- invalid input from client
 *  ...
 */

int sasl_server_step(sasl_conn_t *conn,
		     const char *clientin,
		     unsigned clientinlen,
		     const char **serverout,
		     unsigned *serveroutlen)
{
    int ret;
    sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;  /* cast */

#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx =
		 (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
 
    /* check parameters */ 
    if (gctx->sasl_server_active==0) return SASL_NOTINIT;
#else
    /* check parameters */
    if (_sasl_server_active==0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
    if (!conn) return SASL_BADPARAM;
    if ((clientin==NULL) && (clientinlen>0))
	PARAMERROR(conn);

    /* If we've already done the last send, return! */
    if(s_conn->sent_last == 1) {
	return SASL_OK;
    }

    /* Don't do another step if the plugin told us that we're done */
    if (conn->oparams.doneflag) {
	_sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
	return SASL_FAIL;
    }

    if(serverout) *serverout = NULL;
    if(serveroutlen) *serveroutlen = 0;

    ret = s_conn->mech->plug->mech_step(conn->context,
					s_conn->sparams,
					clientin,
					clientinlen,
					serverout,
					serveroutlen,
					&conn->oparams);

    if (ret == SASL_OK) {
	ret = do_authorization(s_conn);
    }

    if (ret == SASL_OK) {
	/* if we're done, we need to watch out for the following:
	 * 1. the mech does server-send-last
	 * 2. the protocol does not
	 *
	 * in this case, return SASL_CONTINUE and remember we are done.
	 */
	if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
	    s_conn->sent_last = 1;
	    ret = SASL_CONTINUE;
	}
	if(!conn->oparams.maxoutbuf) {
	    conn->oparams.maxoutbuf = conn->props.maxbufsize;
	}

	if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
#ifdef _SUN_SDK_
	    _sasl_log(conn, SASL_LOG_ERR,
		      "mech did not call canon_user for both authzid "
		      "and authid");
#else
	    sasl_seterror(conn, 0,
			  "mech did not call canon_user for both authzid " \
			  "and authid");
#endif /* _SUN_SDK_ */
	    ret = SASL_BADPROT;
	}	
    }
    
    if(   ret != SASL_OK
       && ret != SASL_CONTINUE
       && ret != SASL_INTERACT) {
	if(conn->context) {
	    s_conn->mech->plug->mech_dispose(conn->context,
					     s_conn->sparams->utils);
	    conn->context = NULL;
	}
    }

    RETURN(conn, ret);
}

/* returns the length of all the mechanisms
 * added up 
 */

#ifdef _SUN_SDK_
static unsigned mech_names_len(_sasl_global_context_t *gctx)
{
  mech_list_t *mechlist = gctx->mechlist;
#else
static unsigned mech_names_len()
{
#endif /* _SUN_SDK_ */
  mechanism_t *listptr;
  unsigned result = 0;

  for (listptr = mechlist->mech_list;
       listptr;
       listptr = listptr->next)
    result += strlen(listptr->plug->mech_name);

  return result;
}

/* This returns a list of mechanisms in a NUL-terminated string
 *
 * The default behavior is to seperate with spaces if sep==NULL
 */
int _sasl_server_listmech(sasl_conn_t *conn,
			  const char *user __attribute__((unused)),
			  const char *prefix,
			  const char *sep,
			  const char *suffix,
			  const char **result,
			  unsigned *plen,
			  int *pcount)
{
  int lup;
  mechanism_t *listptr;
  int ret;
  int resultlen;
  int flag;
  const char *mysep;

#ifdef _SUN_SDK_
  _sasl_global_context_t *gctx;
   mech_list_t *mechlist;
 
  if (!conn) return SASL_BADPARAM;
   /* if there hasn't been a sasl_sever_init() fail */
  gctx = conn->gctx;
  if (gctx->sasl_server_active==0) return SASL_NOTINIT;
 
  (void)_load_server_plugins(gctx);
  mechlist = gctx->mechlist;
#else
  /* if there hasn't been a sasl_sever_init() fail */
  if (_sasl_server_active==0) return SASL_NOTINIT;
  if (!conn) return SASL_BADPARAM;
#endif /* _SUN_SDK_ */
  if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
  
  if (! result)
      PARAMERROR(conn);

  if (plen != NULL)
      *plen = 0;
  if (pcount != NULL)
      *pcount = 0;

  if (sep) {
      mysep = sep;
  } else {
      mysep = " ";
  }

  if (! mechlist || mechlist->mech_length <= 0)
      INTERROR(conn, SASL_NOMECH);

  resultlen = (prefix ? strlen(prefix) : 0)
            + (strlen(mysep) * (mechlist->mech_length - 1))
#ifdef _SUN_SDK_
	    + mech_names_len(gctx)
#else
	    + mech_names_len()
#endif /* _SUN_SDK_ */
            + (suffix ? strlen(suffix) : 0)
	    + 1;
  ret = _buf_alloc(&conn->mechlist_buf,
		   &conn->mechlist_buf_len, resultlen);
  if(ret != SASL_OK) MEMERROR(conn);

  if (prefix)
    strcpy (conn->mechlist_buf,prefix);
  else
    *(conn->mechlist_buf) = '\0';

  listptr = mechlist->mech_list;  
   
  flag = 0;
  /* make list */
  for (lup = 0; lup < mechlist->mech_length; lup++) {
      /* currently, we don't use the "user" parameter for anything */
      if (mech_permitted(conn, listptr)) {
	  if (pcount != NULL)
	      (*pcount)++;

	  /* print seperator */
	  if (flag) {
	      strcat(conn->mechlist_buf, mysep);
	  } else {
	      flag = 1;
	  }

	  /* now print the mechanism name */
	  strcat(conn->mechlist_buf, listptr->plug->mech_name);
      }

      listptr = listptr->next;
  }

  if (suffix)
      strcat(conn->mechlist_buf,suffix);

  if (plen!=NULL)
      *plen=strlen(conn->mechlist_buf);

  *result = conn->mechlist_buf;

  return SASL_OK;  
}

#ifdef _SUN_SDK_
sasl_string_list_t *_sasl_server_mechs(_sasl_global_context_t *gctx) 
#else
sasl_string_list_t *_sasl_server_mechs(void) 
#endif /* _SUN_SDK_ */
{
  mechanism_t *listptr;
  sasl_string_list_t *retval = NULL, *next=NULL;
#ifdef _SUN_SDK_
  mech_list_t *mechlist = gctx->mechlist;

  if(!gctx->sasl_server_active) return NULL;
#else
  if(!_sasl_server_active) return NULL;
#endif /* _SUN_SDK_ */

  /* make list */
  for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
      next = sasl_ALLOC(sizeof(sasl_string_list_t));

      if(!next && !retval) return NULL;
      else if(!next) {
	  next = retval->next;
	  do {
	      sasl_FREE(retval);
	      retval = next;
	      next = retval->next;
	  } while(next);
	  return NULL;
      }
      
      next->d = listptr->plug->mech_name;

      if(!retval) {
	  next->next = NULL;
	  retval = next;
      } else {
	  next->next = retval;
	  retval = next;
      }
  }

  return retval;
}

#define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
static int is_mech(const char *t, const char *m)
{
    int sl = strlen(m);
    return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
}

/* returns OK if it's valid */
static int _sasl_checkpass(sasl_conn_t *conn,
			   const char *user,
			   unsigned userlen __attribute__((unused)),
			   const char *pass,
			   unsigned passlen __attribute__((unused)))
{
    sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
    int result;
    sasl_getopt_t *getopt;
    sasl_server_userdb_checkpass_t *checkpass_cb;
    void *context;
    const char *mlist = NULL, *mech = NULL;
    struct sasl_verify_password_s *v;
    const char *service = conn->service;

    /* call userdb callback function, if available */
    result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
			       &checkpass_cb, &context);
    if(result == SASL_OK && checkpass_cb) {
	result = checkpass_cb(conn, context, user, pass, strlen(pass),
			      s_conn->sparams->propctx);
	if(result == SASL_OK)
	    return SASL_OK;
    }

    /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
            == SASL_OK) {
        getopt(context, NULL, "pwcheck_method", &mlist, NULL);
    }

    if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;

    result = SASL_NOMECH;

    mech = mlist;
    while (*mech && result != SASL_OK) {
	for (v = _sasl_verify_password; v->name; v++) {
	    if(is_mech(mech, v->name)) {
		result = v->verify(conn, user, pass, service,
				   s_conn->user_realm);
		break;
	    }
	}
	if (result != SASL_OK) {
	    /* skip to next mech in list */
	    while (*mech && !isspace((int) *mech)) mech++;
	    while (*mech && isspace((int) *mech)) mech++;
	}
    }

    if (result == SASL_NOMECH) {
	/* no mechanism available ?!? */
	_sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
    }

    if (result != SASL_OK)
#ifdef _INTEGRATED_SOLARIS_
	sasl_seterror(conn, SASL_NOLOG, gettext("checkpass failed"));
#else
	sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
#endif /* _INTEGRATED_SOLARIS_ */

    RETURN(conn, result);
}

/* check if a plaintext password is valid
 *   if user is NULL, check if plaintext passwords are enabled
 * inputs:
 *  user          -- user to query in current user_domain
 *  userlen       -- length of username, 0 = strlen(user)
 *  pass          -- plaintext password to check
 *  passlen       -- length of password, 0 = strlen(pass)
 * returns 
 *  SASL_OK       -- success
 *  SASL_NOMECH   -- mechanism not supported
 *  SASL_NOVERIFY -- user found, but no verifier
 *  SASL_NOUSER   -- user not found
 */
int sasl_checkpass(sasl_conn_t *conn,
		   const char *user,
#ifdef _SUN_SDK_
		   unsigned userlen,
#else /* _SUN_SDK_ */
		   unsigned userlen __attribute__((unused)),
#endif /* _SUN_SDK_ */
		   const char *pass,
		   unsigned passlen)
{
    int result;
    
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx =
		 (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;

    if (gctx->sasl_server_active==0) return SASL_NOTINIT;

    /* A NULL user means the caller is checking if plaintext authentication
     * is enabled.  But if no connection context is supplied, we have no
     * appropriate policy to check against.  So for consistant global
     * behavior we always say plaintext is enabled in this case.
     */
    if (!user && !conn) return SASL_OK;

    if (!conn) return SASL_BADPARAM;

    /* Check connection security policy to see if plaintext password
     * authentication is permitted.
     *
     * XXX TODO FIXME:
     * This should call mech_permitted with the PLAIN mechanism,
     * since all plaintext mechanisms should fall under the same
     * security policy guidelines.  But to keep code changes and
     * risk to a minimum at this juncture, we do the minimal
     * security strength and plaintext policy checks which are
     * most likely to be deployed and useful in the field.
     */
    if (conn->props.min_ssf > conn->external.ssf)
      RETURN(conn, SASL_TOOWEAK);
    if ((conn->props.security_flags & SASL_SEC_NOPLAINTEXT) != 0
      && conn->external.ssf == 0)
      RETURN(conn, SASL_ENCRYPT);

    if (!user)
      return SASL_OK;
#else
    if (_sasl_server_active==0) return SASL_NOTINIT;
    
    /* check if it's just a query if we are enabled */
    if (!user)
	return SASL_OK;

    if (!conn) return SASL_BADPARAM;
#endif /* _SUN_SDK_ */
    
    /* check params */
    if (pass == NULL)
	PARAMERROR(conn);

    /* canonicalize the username */
    result = _sasl_canon_user(conn, user, 0,
			      SASL_CU_AUTHID | SASL_CU_AUTHZID,
			      &(conn->oparams));
    if(result != SASL_OK) RETURN(conn, result);
    user = conn->oparams.user;

    /* Check the password */
    result = _sasl_checkpass(conn, user, strlen(user), pass, strlen(pass));

#ifdef _SUN_SDK_
    if (result == SASL_OK) {
      result = do_authorization((sasl_server_conn_t *) conn);
    }
#endif /* _SUN_SDK_ */

    if (result == SASL_OK)      
	result = _sasl_transition(conn, pass, passlen);

    RETURN(conn,result);
}

/* check if a user exists on server
 *  conn          -- connection context (may be NULL, used to hold last error)
 *  service       -- registered name of the service using SASL (e.g. "imap")
 *  user_realm    -- permits multiple user realms on server, NULL = default
 *  user          -- NUL terminated user name
 *
 * returns:
 *  SASL_OK       -- success
 *  SASL_DISABLED -- account disabled [FIXME: currently not detected]
 *  SASL_NOUSER   -- user not found
 *  SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
 *  SASL_NOMECH   -- no mechanisms enabled
 */
int sasl_user_exists(sasl_conn_t *conn,
		     const char *service,
		     const char *user_realm,
		     const char *user) 
{
    int result=SASL_NOMECH;
    const char *mlist = NULL, *mech = NULL;
    void *context;
    sasl_getopt_t *getopt;
    struct sasl_verify_password_s *v;
    
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx =
		 (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;

    /* check params */ 
    if (gctx->sasl_server_active==0) return SASL_NOTINIT;
#else
    /* check params */
    if (_sasl_server_active==0) return SASL_NOTINIT;
#endif /* _SUN_SDK_ */
    if (!conn) return SASL_BADPARAM;
    if (!user || conn->type != SASL_CONN_SERVER) 
	PARAMERROR(conn);

    if(!service) service = conn->service;
    
    /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
    if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
            == SASL_OK) {
        getopt(context, NULL, "pwcheck_method", &mlist, NULL);
    }

    if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;

    result = SASL_NOMECH;

    mech = mlist;
    while (*mech && result != SASL_OK) {
	for (v = _sasl_verify_password; v->name; v++) {
	    if(is_mech(mech, v->name)) {
		result = v->verify(conn, user, NULL, service, user_realm);
		break;
	    }
	}
	if (result != SASL_OK) {
	    /* skip to next mech in list */
	    while (*mech && !isspace((int) *mech)) mech++;
	    while (*mech && isspace((int) *mech)) mech++;
	}
    }

    /* Screen out the SASL_BADPARAM response
     * we'll get from not giving a password */
    if(result == SASL_BADPARAM) {
	result = SASL_OK;
    }

    if (result == SASL_NOMECH) {
	/* no mechanism available ?!? */
	_sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
#ifndef _SUN_SDK_
	sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
#endif /* !_SUN_SDK_ */
    }

    RETURN(conn, result);
}

/* check if an apop exchange is valid
 *  (note this is an optional part of the SASL API)
 *  if challenge is NULL, just check if APOP is enabled
 * inputs:
 *  challenge     -- challenge which was sent to client
 *  challen       -- length of challenge, 0 = strlen(challenge)
 *  response      -- client response, "<user> <digest>" (RFC 1939)
 *  resplen       -- length of response, 0 = strlen(response)
 * returns 
 *  SASL_OK       -- success
 *  SASL_BADAUTH  -- authentication failed
 *  SASL_BADPARAM -- missing challenge
 *  SASL_BADPROT  -- protocol error (e.g., response in wrong format)
 *  SASL_NOVERIFY -- user found, but no verifier
 *  SASL_NOMECH   -- mechanism not supported
 *  SASL_NOUSER   -- user not found
 */
int sasl_checkapop(sasl_conn_t *conn,
#ifdef DO_SASL_CHECKAPOP
 		   const char *challenge,
 		   unsigned challen __attribute__((unused)),
 		   const char *response,
 		   unsigned resplen __attribute__((unused)))
#else
 		   const char *challenge __attribute__((unused)),
 		   unsigned challen __attribute__((unused)),
 		   const char *response __attribute__((unused)),
 		   unsigned resplen __attribute__((unused)))
#endif
{
#ifdef DO_SASL_CHECKAPOP
    sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
    char *user, *user_end;
    const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
    size_t user_len;
    int result;
#ifdef _SUN_SDK_
    _sasl_global_context_t *gctx =
		 (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;

    if (gctx->sasl_server_active==0)
        return SASL_NOTINIT;
#else
    if (_sasl_server_active==0)
	return SASL_NOTINIT;
#endif /* _SUN_SDK_ */

    /* check if it's just a query if we are enabled */
    if(!challenge)
	return SASL_OK;

    /* check params */
    if (!conn) return SASL_BADPARAM;
    if (!response)
	PARAMERROR(conn);

    /* Parse out username and digest.
     *
     * Per RFC 1939, response must be "<user> <digest>", where
     * <digest> is a 16-octet value which is sent in hexadecimal
     * format, using lower-case ASCII characters.
     */
    user_end = strrchr(response, ' ');
    if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) 
    {
#ifdef _INTEGRATED_SOLARIS_
        sasl_seterror(conn, 0, gettext("Bad Digest"));
#else
        sasl_seterror(conn, 0, "Bad Digest");
#endif /* _INTEGRATED_SOLARIS_ */
        RETURN(conn,SASL_BADPROT);
    }
 
    user_len = (size_t)(user_end - response);
    user = sasl_ALLOC(user_len + 1);
    memcpy(user, response, user_len);
    user[user_len] = '\0';

    result = prop_request(s_conn->sparams->propctx, password_request);
    if(result != SASL_OK) 
    {
        sasl_FREE(user);
        RETURN(conn, result);
    }

    /* Cannonify it */
    result = _sasl_canon_user(conn, user, user_len,
	                      SASL_CU_AUTHID | SASL_CU_AUTHZID,
	                      &(conn->oparams));
    sasl_FREE(user);

    if(result != SASL_OK) RETURN(conn, result);

    /* Do APOP verification */
    result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
	challenge, user_end + 1, s_conn->user_realm);

    /* If verification failed, we don't want to encourage getprop to work */
    if(result != SASL_OK) {
	conn->oparams.user = NULL;
	conn->oparams.authid = NULL;
    }

    RETURN(conn, result);
#else /* sasl_checkapop was disabled at compile time */
    sasl_seterror(conn, SASL_NOLOG,
	"sasl_checkapop called, but was disabled at compile time");
    RETURN(conn, SASL_NOMECH);
#endif /* DO_SASL_CHECKAPOP */
}