2N/A/*
2N/A * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* SASL server API implementation
2N/A * Rob Siemborski
2N/A * Tim Martin
2N/A * $Id: server.c,v 1.123 2003/04/16 19:36:01 rjs3 Exp $
2N/A */
2N/A/*
2N/A * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A *
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A *
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in
2N/A * the documentation and/or other materials provided with the
2N/A * distribution.
2N/A *
2N/A * 3. The name "Carnegie Mellon University" must not be used to
2N/A * endorse or promote products derived from this software without
2N/A * prior written permission. For permission or any other legal
2N/A * details, please contact
2N/A * Office of Technology Transfer
2N/A * Carnegie Mellon University
2N/A * 5000 Forbes Avenue
2N/A * Pittsburgh, PA 15213-3890
2N/A * (412) 268-4387, fax: (412) 268-7395
2N/A * tech-transfer@andrew.cmu.edu
2N/A *
2N/A * 4. Redistributions of any form whatsoever must retain the following
2N/A * acknowledgment:
2N/A * "This product includes software developed by Computing Services
2N/A * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
2N/A *
2N/A * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
2N/A * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2N/A * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
2N/A * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2N/A * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
2N/A * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
2N/A * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2N/A */
2N/A
2N/A/* local functions/structs don't start with sasl
2N/A */
2N/A#include <config.h>
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <limits.h>
2N/A#ifndef macintosh
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#endif
2N/A#include <fcntl.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A
2N/A#include "sasl.h"
2N/A#include "saslint.h"
2N/A#include "saslplug.h"
2N/A#include "saslutil.h"
2N/A
2N/A#ifndef _SUN_SDK_
2N/A#ifdef sun
2N/A/* gotta define gethostname ourselves on suns */
2N/Aextern int gethostname(char *, int);
2N/A#endif
2N/A#endif /* !_SUN_SDK_ */
2N/A
2N/A#define DEFAULT_CHECKPASS_MECH "auxprop"
2N/A
2N/A/* Contains functions:
2N/A *
2N/A * sasl_server_init
2N/A * sasl_server_new
2N/A * sasl_listmech
2N/A * sasl_server_start
2N/A * sasl_server_step
2N/A * sasl_checkpass
2N/A * sasl_checkapop
2N/A * sasl_user_exists
2N/A * sasl_setpass
2N/A */
2N/A
2N/A#ifdef _SUN_SDK_
2N/Aint _is_sasl_server_active(_sasl_global_context_t *gctx)
2N/A{
2N/A return gctx->sasl_server_active;
2N/A}
2N/A
2N/ADEFINE_STATIC_MUTEX(init_server_mutex);
2N/ADEFINE_STATIC_MUTEX(server_active_mutex);
2N/A/*
2N/A * server_plug_mutex ensures only one server plugin is init'ed at a time
2N/A * If a plugin is loaded more than once, the glob_context may be overwritten
2N/A * which may lead to a memory leak. We keep glob_context with each mech
2N/A * to avoid this problem.
2N/A */
2N/ADEFINE_STATIC_MUTEX(server_plug_mutex);
2N/A#else
2N/A/* if we've initialized the server sucessfully */
2N/Astatic int _sasl_server_active = 0;
2N/A
2N/A/* For access by other modules */
2N/Aint _is_sasl_server_active(void) { return _sasl_server_active; }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/Astatic int _sasl_checkpass(sasl_conn_t *conn,
2N/A const char *user, unsigned userlen,
2N/A const char *pass, unsigned passlen);
2N/A
2N/A#ifndef _SUN_SDK_
2N/Astatic mech_list_t *mechlist = NULL; /* global var which holds the list */
2N/A
2N/Astatic sasl_global_callbacks_t global_callbacks;
2N/A#endif /* !_SUN_SDK_ */
2N/A
2N/A/* set the password for a user
2N/A * conn -- SASL connection
2N/A * user -- user name
2N/A * pass -- plaintext password, may be NULL to remove user
2N/A * passlen -- length of password, 0 = strlen(pass)
2N/A * oldpass -- NULL will sometimes work
2N/A * oldpasslen -- length of password, 0 = strlen(oldpass)
2N/A * flags -- see flags below
2N/A *
2N/A * returns:
2N/A * SASL_NOCHANGE -- proper entry already exists
2N/A * SASL_NOMECH -- no authdb supports password setting as configured
2N/A * SASL_NOVERIFY -- user exists, but no settable password present
2N/A * SASL_DISABLED -- account disabled
2N/A * SASL_PWLOCK -- password locked
2N/A * SASL_WEAKPASS -- password too weak for security policy
2N/A * SASL_NOUSERPASS -- user-supplied passwords not permitted
2N/A * SASL_FAIL -- OS error
2N/A * SASL_BADPARAM -- password too long
2N/A * SASL_OK -- successful
2N/A */
2N/A
2N/Aint sasl_setpass(sasl_conn_t *conn,
2N/A const char *user,
2N/A const char *pass, unsigned passlen,
2N/A const char *oldpass,
2N/A unsigned oldpasslen,
2N/A unsigned flags)
2N/A{
2N/A int result=SASL_OK, tmpresult;
2N/A sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
2N/A sasl_server_userdb_setpass_t *setpass_cb = NULL;
2N/A void *context = NULL;
2N/A mechanism_t *m;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx =
2N/A (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
2N/A mech_list_t *mechlist = gctx == NULL ? NULL : gctx->mechlist;
2N/A
2N/A if (!gctx->sasl_server_active || !mechlist) return SASL_NOTINIT;
2N/A#else
2N/A if (!_sasl_server_active || !mechlist) return SASL_NOTINIT;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* check params */
2N/A if (!conn) return SASL_BADPARAM;
2N/A if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
2N/A
2N/A if ((!(flags & SASL_SET_DISABLE) && passlen == 0)
2N/A || ((flags & SASL_SET_CREATE) && (flags & SASL_SET_DISABLE)))
2N/A PARAMERROR(conn);
2N/A
2N/A /* call userdb callback function */
2N/A result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS,
2N/A &setpass_cb, &context);
2N/A if(result == SASL_OK && setpass_cb) {
2N/A tmpresult = setpass_cb(conn, context, user, pass, passlen,
2N/A s_conn->sparams->propctx, flags);
2N/A if(tmpresult != SASL_OK) {
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "setpass callback failed for %s: %z",
2N/A user, tmpresult);
2N/A } else {
2N/A _sasl_log(conn, SASL_LOG_NOTE,
2N/A "setpass callback succeeded for %s", user);
2N/A }
2N/A } else {
2N/A result = SASL_OK;
2N/A }
2N/A
2N/A /* now we let the mechanisms set their secrets */
2N/A for (m = mechlist->mech_list; m; m = m->next) {
2N/A if (!m->plug->setpass) {
2N/A /* can't set pass for this mech */
2N/A continue;
2N/A }
2N/A#ifdef _SUN_SDK_
2N/A tmpresult = m->plug->setpass(m->glob_context,
2N/A#else
2N/A tmpresult = m->plug->setpass(m->plug->glob_context,
2N/A#endif /* _SUN_SDK_ */
2N/A ((sasl_server_conn_t *)conn)->sparams,
2N/A user,
2N/A pass,
2N/A passlen,
2N/A oldpass, oldpasslen,
2N/A flags);
2N/A if (tmpresult == SASL_OK) {
2N/A _sasl_log(conn, SASL_LOG_NOTE,
2N/A "%s: set secret for %s", m->plug->mech_name, user);
2N/A
2N/A m->condition = SASL_OK; /* if we previously thought the
2N/A mechanism didn't have any user secrets
2N/A we now think it does */
2N/A
2N/A } else if (tmpresult == SASL_NOCHANGE) {
2N/A _sasl_log(conn, SASL_LOG_NOTE,
2N/A "%s: secret not changed for %s", m->plug->mech_name, user);
2N/A } else {
2N/A result = tmpresult;
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "%s: failed to set secret for %s: %z (%m)",
2N/A m->plug->mech_name, user, tmpresult,
2N/A#ifndef WIN32
2N/A errno
2N/A#else
2N/A GetLastError()
2N/A#endif
2N/A );
2N/A }
2N/A }
2N/A
2N/A RETURN(conn, result);
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic void
2N/Aserver_dispose_mech_contexts(sasl_conn_t *pconn)
2N/A{
2N/A sasl_server_conn_t *s_conn= (sasl_server_conn_t *) pconn;
2N/A context_list_t *cur, *cur_next;
2N/A _sasl_global_context_t *gctx = pconn->gctx;
2N/A
2N/A for(cur = s_conn->mech_contexts; cur; cur=cur_next) {
2N/A cur_next = cur->next;
2N/A if(cur->context)
2N/A cur->mech->plug->mech_dispose(cur->context, s_conn->sparams->utils);
2N/A sasl_FREE(cur);
2N/A }
2N/A s_conn->mech_contexts = NULL;
2N/A}
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A/* local mechanism which disposes of server */
2N/Astatic void server_dispose(sasl_conn_t *pconn)
2N/A{
2N/A sasl_server_conn_t *s_conn= (sasl_server_conn_t *) pconn;
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx = pconn->gctx;
2N/A#else
2N/A context_list_t *cur, *cur_next;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if (s_conn->mech
2N/A && s_conn->mech->plug->mech_dispose) {
2N/A s_conn->mech->plug->mech_dispose(pconn->context,
2N/A s_conn->sparams->utils);
2N/A }
2N/A pconn->context = NULL;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A server_dispose_mech_contexts(pconn);
2N/A#else
2N/A for(cur = s_conn->mech_contexts; cur; cur=cur_next) {
2N/A cur_next = cur->next;
2N/A if(cur->context)
2N/A cur->mech->plug->mech_dispose(cur->context, s_conn->sparams->utils);
2N/A sasl_FREE(cur);
2N/A }
2N/A s_conn->mech_contexts = NULL;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A _sasl_free_utils(&s_conn->sparams->utils);
2N/A
2N/A if (s_conn->sparams->propctx)
2N/A prop_dispose(&s_conn->sparams->propctx);
2N/A
2N/A if (s_conn->user_realm)
2N/A sasl_FREE(s_conn->user_realm);
2N/A
2N/A if (s_conn->sparams)
2N/A sasl_FREE(s_conn->sparams);
2N/A
2N/A _sasl_conn_dispose(pconn);
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic int init_mechlist(_sasl_global_context_t *gctx)
2N/A{
2N/A mech_list_t *mechlist = gctx->mechlist;
2N/A#else
2N/Astatic int init_mechlist(void)
2N/A{
2N/A#endif /* _SUN_SDK_ */
2N/A sasl_utils_t *newutils = NULL;
2N/A
2N/A mechlist->mutex = sasl_MUTEX_ALLOC();
2N/A if(!mechlist->mutex) return SASL_FAIL;
2N/A
2N/A /* set util functions - need to do rest */
2N/A#ifdef _SUN_SDK_
2N/A newutils = _sasl_alloc_utils(gctx, NULL, &gctx->server_global_callbacks);
2N/A#else
2N/A newutils = _sasl_alloc_utils(NULL, &global_callbacks);
2N/A#endif /* _SUN_SDK_ */
2N/A if (newutils == NULL)
2N/A return SASL_NOMEM;
2N/A
2N/A newutils->checkpass = &_sasl_checkpass;
2N/A
2N/A mechlist->utils = newutils;
2N/A mechlist->mech_list=NULL;
2N/A mechlist->mech_length=0;
2N/A
2N/A return SASL_OK;
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic int load_mech(_sasl_global_context_t *gctx, const char *mechname)
2N/A{
2N/A sasl_getopt_t *getopt;
2N/A void *context;
2N/A const char *mlist = NULL;
2N/A const char *cp;
2N/A size_t len;
2N/A
2N/A /* No sasl_conn_t was given to getcallback, so we provide the
2N/A * global callbacks structure */
2N/A if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) == SASL_OK)
2N/A (void)getopt(&gctx->server_global_callbacks, NULL,
2N/A "server_load_mech_list", &mlist, NULL);
2N/A
2N/A if (mlist == NULL)
2N/A return (1);
2N/A
2N/A len = strlen(mechname);
2N/A while (*mlist && isspace((int) *mlist)) mlist++;
2N/A
2N/A while (*mlist) {
2N/A for (cp = mlist; *cp && !isspace((int) *cp); cp++);
2N/A if (((size_t) (cp - mlist) == len) &&
2N/A !strncasecmp(mlist, mechname, len))
2N/A break;
2N/A mlist = cp;
2N/A while (*mlist && isspace((int) *mlist)) mlist++;
2N/A }
2N/A return (*mlist != '\0');
2N/A}
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A/*
2N/A * parameters:
2N/A * p - entry point
2N/A */
2N/Aint sasl_server_add_plugin(const char *plugname,
2N/A sasl_server_plug_init_t *p)
2N/A#ifdef _SUN_SDK_
2N/A{
2N/A return (_sasl_server_add_plugin(_sasl_gbl_ctx(), plugname, p));
2N/A}
2N/A
2N/Aint _sasl_server_add_plugin(void *ctx,
2N/A const char *plugname,
2N/A sasl_server_plug_init_t *p)
2N/A{
2N/A int nplug = 0;
2N/A int i;
2N/A mechanism_t *m;
2N/A _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
2N/A mech_list_t *mechlist = gctx->mechlist;
2N/A
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A int sun_reg;
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A#else
2N/A{
2N/A#endif /* _SUN_SDK_ */
2N/A int plugcount;
2N/A sasl_server_plug_t *pluglist;
2N/A mechanism_t *mech;
2N/A sasl_server_plug_init_t *entry_point;
2N/A int result;
2N/A int version;
2N/A int lupe;
2N/A
2N/A if(!plugname || !p) return SASL_BADPARAM;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if (mechlist == NULL) return SASL_BADPARAM;
2N/A
2N/A /* Check to see if this plugin has already been registered */
2N/A m = mechlist->mech_list;
2N/A for (i = 0; i < mechlist->mech_length; i++) {
2N/A if (strcmp(plugname, m->plugname) == 0)
2N/A return SASL_OK;
2N/A m = m->next;
2N/A }
2N/A
2N/A result = LOCK_MUTEX(&server_plug_mutex);
2N/A if (result != SASL_OK)
2N/A return result;
2N/A
2N/A#endif /* _SUN_SDK_ */
2N/A entry_point = (sasl_server_plug_init_t *)p;
2N/A
2N/A /* call into the shared library asking for information about it */
2N/A /* version is filled in with the version of the plugin */
2N/A result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, &version,
2N/A &pluglist, &plugcount);
2N/A
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sun_reg = _is_sun_reg(pluglist);
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if (result != SASL_OK) {
2N/A UNLOCK_MUTEX(&server_plug_mutex);
2N/A __sasl_log(gctx, gctx->server_global_callbacks.callbacks,
2N/A SASL_LOG_DEBUG,
2N/A "server add_plugin entry_point error %z", result);
2N/A#else
2N/A if ((result != SASL_OK) && (result != SASL_NOUSER)) {
2N/A _sasl_log(NULL, SASL_LOG_DEBUG,
2N/A "server add_plugin entry_point error %z\n", result);
2N/A#endif /* _SUN_SDK_ */
2N/A return result;
2N/A }
2N/A
2N/A /* Make sure plugin is using the same SASL version as us */
2N/A if (version != SASL_SERVER_PLUG_VERSION)
2N/A {
2N/A#ifdef _SUN_SDK_
2N/A UNLOCK_MUTEX(&server_plug_mutex);
2N/A __sasl_log(gctx, gctx->server_global_callbacks.callbacks,
2N/A SASL_LOG_ERR, "version mismatch on plugin");
2N/A#else
2N/A _sasl_log(NULL, SASL_LOG_ERR,
2N/A "version mismatch on plugin");
2N/A#endif /* _SUN_SDK_ */
2N/A return SASL_BADVERS;
2N/A }
2N/A#ifdef _SUN_SDK_
2N/A /* Check plugins to make sure mech_name is non-NULL */
2N/A for (lupe=0;lupe < plugcount ;lupe++) {
2N/A if (pluglist[lupe].mech_name == NULL)
2N/A break;
2N/A }
2N/A if (lupe < plugcount) {
2N/A#ifdef _SUN_SDK_
2N/A UNLOCK_MUTEX(&server_plug_mutex);
2N/A __sasl_log(gctx, gctx->server_global_callbacks.callbacks,
2N/A SASL_LOG_ERR, "invalid server plugin %s", plugname);
2N/A#else
2N/A _sasl_log(NULL, SASL_LOG_ERR, "invalid server plugin %s", plugname);
2N/A#endif /* _SUN_SDK_ */
2N/A return SASL_BADPROT;
2N/A }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A for (lupe=0;lupe < plugcount ;lupe++)
2N/A {
2N/A#ifdef _SUN_SDK_
2N/A if (!load_mech(gctx, pluglist->mech_name)) {
2N/A pluglist++;
2N/A continue;
2N/A }
2N/A nplug++;
2N/A#endif /* _SUN_SDK_ */
2N/A mech = sasl_ALLOC(sizeof(mechanism_t));
2N/A#ifdef _SUN_SDK_
2N/A if (! mech) {
2N/A UNLOCK_MUTEX(&server_plug_mutex);
2N/A return SASL_NOMEM;
2N/A }
2N/A
2N/A mech->glob_context = pluglist->glob_context;
2N/A#else
2N/A if (! mech) return SASL_NOMEM;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A mech->plug=pluglist++;
2N/A if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) {
2N/A#ifdef _SUN_SDK_
2N/A UNLOCK_MUTEX(&server_plug_mutex);
2N/A#endif /* _SUN_SDK_ */
2N/A sasl_FREE(mech);
2N/A return SASL_NOMEM;
2N/A }
2N/A mech->version = version;
2N/A#ifdef _SUN_SDK_
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A mech->sun_reg = sun_reg;
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A
2N/A /* whether this mech actually has any users in it's db */
2N/A mech->condition = SASL_OK;
2N/A#else
2N/A /* whether this mech actually has any users in it's db */
2N/A mech->condition = result; /* SASL_OK or SASL_NOUSER */
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A mech->next = mechlist->mech_list;
2N/A mechlist->mech_list = mech;
2N/A mechlist->mech_length++;
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A UNLOCK_MUTEX(&server_plug_mutex);
2N/A return (nplug == 0) ? SASL_NOMECH : SASL_OK;
2N/A#else
2N/A return SASL_OK;
2N/A#endif /* _SUN_SDK_ */
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic int server_done(_sasl_global_context_t *gctx) {
2N/A mech_list_t *mechlist = gctx->mechlist;
2N/A _sasl_path_info_t *path_info, *p;
2N/A#else
2N/Astatic int server_done(void) {
2N/A#endif /* _SUN_SDK_ */
2N/A mechanism_t *m;
2N/A mechanism_t *prevm;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if(!gctx->sasl_server_active)
2N/A return SASL_NOTINIT;
2N/A
2N/A if (LOCK_MUTEX(&server_active_mutex) < 0) {
2N/A return (SASL_FAIL);
2N/A }
2N/A gctx->sasl_server_active--;
2N/A
2N/A if(gctx->sasl_server_active) {
2N/A /* Don't de-init yet! Our refcount is nonzero. */
2N/A UNLOCK_MUTEX(&server_active_mutex);
2N/A return SASL_CONTINUE;
2N/A }
2N/A#else
2N/A if(!_sasl_server_active)
2N/A return SASL_NOTINIT;
2N/A else
2N/A _sasl_server_active--;
2N/A
2N/A if(_sasl_server_active) {
2N/A /* Don't de-init yet! Our refcount is nonzero. */
2N/A return SASL_CONTINUE;
2N/A }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if (mechlist != NULL)
2N/A {
2N/A m=mechlist->mech_list; /* m point to beginning of the list */
2N/A
2N/A while (m!=NULL)
2N/A {
2N/A prevm=m;
2N/A m=m->next;
2N/A
2N/A if (prevm->plug->mech_free) {
2N/A#ifdef _SUN_SDK_
2N/A prevm->plug->mech_free(prevm->glob_context,
2N/A#else
2N/A prevm->plug->mech_free(prevm->plug->glob_context,
2N/A#endif /* _SUN_SDK_ */
2N/A mechlist->utils);
2N/A }
2N/A
2N/A sasl_FREE(prevm->plugname);
2N/A sasl_FREE(prevm);
2N/A }
2N/A _sasl_free_utils(&mechlist->utils);
2N/A sasl_MUTEX_FREE(mechlist->mutex);
2N/A sasl_FREE(mechlist);
2N/A#ifdef _SUN_SDK_
2N/A gctx->mechlist = NULL;
2N/A#else
2N/A mechlist = NULL;
2N/A#endif /* _SUN_SDK_ */
2N/A }
2N/A
2N/A /* Free the auxprop plugins */
2N/A#ifdef _SUN_SDK_
2N/A _sasl_auxprop_free(gctx);
2N/A
2N/A gctx->server_global_callbacks.callbacks = NULL;
2N/A gctx->server_global_callbacks.appname = NULL;
2N/A
2N/A p = gctx->splug_path_info;
2N/A while((path_info = p) != NULL) {
2N/A sasl_FREE(path_info->path);
2N/A p = path_info->next;
2N/A sasl_FREE(path_info);
2N/A }
2N/A gctx->splug_path_info = NULL;
2N/A UNLOCK_MUTEX(&server_active_mutex);
2N/A#else
2N/A _sasl_auxprop_free();
2N/A
2N/A global_callbacks.callbacks = NULL;
2N/A global_callbacks.appname = NULL;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A return SASL_OK;
2N/A}
2N/A
2N/Astatic int server_idle(sasl_conn_t *conn)
2N/A{
2N/A mechanism_t *m;
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx;
2N/A mech_list_t *mechlist;
2N/A
2N/A if (conn == NULL)
2N/A gctx = _sasl_gbl_ctx();
2N/A else
2N/A gctx = conn->gctx;
2N/A mechlist = gctx->mechlist;
2N/A#endif /* _SUN_SDK_ */
2N/A if (! mechlist)
2N/A return 0;
2N/A
2N/A for (m = mechlist->mech_list;
2N/A m!=NULL;
2N/A m = m->next)
2N/A if (m->plug->idle
2N/A#ifdef _SUN_SDK_
2N/A && m->plug->idle(m->glob_context,
2N/A#else
2N/A && m->plug->idle(m->plug->glob_context,
2N/A#endif /* _SUN_SDK_ */
2N/A conn,
2N/A conn ? ((sasl_server_conn_t *)conn)->sparams : NULL))
2N/A return 1;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic int load_config(_sasl_global_context_t *gctx,
2N/A const sasl_callback_t *verifyfile_cb)
2N/A{
2N/A int result;
2N/A const char *conf_to_config = NULL;
2N/A const char *conf_file = NULL;
2N/A int conf_len;
2N/A sasl_global_callbacks_t global_callbacks = gctx->server_global_callbacks;
2N/A char *alloc_file_name=NULL;
2N/A int len;
2N/A const sasl_callback_t *getconf_cb=NULL;
2N/A struct stat buf;
2N/A int full_file = 0;
2N/A int file_exists = 0;
2N/A
2N/A /* get the path to the plugins; for now the config file will reside there */
2N/A getconf_cb = _sasl_find_getconf_callback(global_callbacks.callbacks);
2N/A if (getconf_cb==NULL) return SASL_BADPARAM;
2N/A
2N/A result = ((sasl_getpath_t *)(getconf_cb->proc))(getconf_cb->context,
2N/A &conf_to_config);
2N/A if (result!=SASL_OK) goto done;
2N/A if (conf_to_config == NULL) conf_to_config = "";
2N/A else {
2N/A if (stat(conf_to_config, &buf))
2N/A goto process_file;
2N/A full_file = !S_ISDIR(buf.st_mode);
2N/A }
2N/A
2N/A if (!full_file) {
2N/A conf_len = strlen(conf_to_config);
2N/A len = strlen(conf_to_config)+2+ strlen(global_callbacks.appname)+5+1;
2N/A
2N/A if (len > PATH_MAX ) {
2N/A result = SASL_FAIL;
2N/A goto done;
2N/A }
2N/A
2N/A /* construct the filename for the config file */
2N/A alloc_file_name = sasl_ALLOC(len);
2N/A if (! alloc_file_name) {
2N/A result = SASL_NOMEM;
2N/A goto done;
2N/A }
2N/A
2N/A snprintf(alloc_file_name, len, "%.*s/%s.conf", conf_len, conf_to_config,
2N/A global_callbacks.appname);
2N/A
2N/A }
2N/A conf_file = full_file ? conf_to_config : alloc_file_name;
2N/A
2N/A if (full_file || stat(conf_file, &buf) == 0)
2N/A file_exists = S_ISREG(buf.st_mode);
2N/A
2N/Aprocess_file:
2N/A /* Check to see if anything has changed */
2N/A if (file_exists && gctx->config_path != NULL &&
2N/A strcmp(conf_file, gctx->config_path) == 0 &&
2N/A gctx->config_last_read == buf.st_mtime) {
2N/A /* File has not changed */
2N/A goto done;
2N/A } else if (gctx->config_path == NULL) {
2N/A /* No new file, nothing has changed */
2N/A if (!file_exists)
2N/A goto done;
2N/A } else {
2N/A sasl_config_free(gctx);
2N/A if (!file_exists) {
2N/A gctx->config_path = NULL;
2N/A goto done;
2N/A }
2N/A }
2N/A gctx->config_last_read = buf.st_mtime;
2N/A
2N/A /* Ask the application if it's safe to use this file */
2N/A result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context,
2N/A conf_file, SASL_VRFY_CONF);
2N/A
2N/A /* returns continue if this file is to be skipped */
2N/A
2N/A /* returns SASL_CONTINUE if doesn't exist
2N/A * if doesn't exist we can continue using default behavior
2N/A */
2N/A if (result==SASL_OK)
2N/A result=sasl_config_init(gctx, conf_file);
2N/A
2N/A done:
2N/A if (alloc_file_name) sasl_FREE(alloc_file_name);
2N/A
2N/A return result;
2N/A}
2N/A#else
2N/Astatic int load_config(const sasl_callback_t *verifyfile_cb)
2N/A{
2N/A int result;
2N/A const char *path_to_config=NULL;
2N/A const char *c;
2N/A unsigned path_len;
2N/A
2N/A char *config_filename=NULL;
2N/A int len;
2N/A const sasl_callback_t *getpath_cb=NULL;
2N/A
2N/A /* get the path to the plugins; for now the config file will reside there */
2N/A getpath_cb=_sasl_find_getpath_callback( global_callbacks.callbacks );
2N/A if (getpath_cb==NULL) return SASL_BADPARAM;
2N/A
2N/A /* getpath_cb->proc MUST be a sasl_getpath_t; if only c had a type
2N/A system */
2N/A result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context,
2N/A &path_to_config);
2N/A if (result!=SASL_OK) goto done;
2N/A if (path_to_config == NULL) path_to_config = "";
2N/A
2N/A c = strchr(path_to_config, PATHS_DELIMITER);
2N/A
2N/A /* length = length of path + '/' + length of appname + ".conf" + 1
2N/A for '\0' */
2N/A
2N/A if(c != NULL)
2N/A path_len = c - path_to_config;
2N/A else
2N/A path_len = strlen(path_to_config);
2N/A
2N/A len = path_len + 2 + strlen(global_callbacks.appname) + 5 + 1;
2N/A
2N/A if (len > PATH_MAX ) {
2N/A result = SASL_FAIL;
2N/A goto done;
2N/A }
2N/A
2N/A /* construct the filename for the config file */
2N/A config_filename = sasl_ALLOC(len);
2N/A if (! config_filename) {
2N/A result = SASL_NOMEM;
2N/A goto done;
2N/A }
2N/A
2N/A snprintf(config_filename, len, "%.*s/%s.conf", path_len, path_to_config,
2N/A global_callbacks.appname);
2N/A
2N/A /* Ask the application if it's safe to use this file */
2N/A result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context,
2N/A config_filename, SASL_VRFY_CONF);
2N/A
2N/A /* returns continue if this file is to be skipped */
2N/A
2N/A /* returns SASL_CONTINUE if doesn't exist
2N/A * if doesn't exist we can continue using default behavior
2N/A */
2N/A if (result==SASL_OK)
2N/A result=sasl_config_init(config_filename);
2N/A
2N/A done:
2N/A if (config_filename) sasl_FREE(config_filename);
2N/A
2N/A return result;
2N/A}
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A/*
2N/A * Verify that all the callbacks are valid
2N/A */
2N/Astatic int verify_server_callbacks(const sasl_callback_t *callbacks)
2N/A{
2N/A if (callbacks == NULL) return SASL_OK;
2N/A
2N/A while (callbacks->id != SASL_CB_LIST_END) {
2N/A if (callbacks->proc==NULL) return SASL_FAIL;
2N/A
2N/A callbacks++;
2N/A }
2N/A
2N/A return SASL_OK;
2N/A}
2N/A
2N/A#ifndef _SUN_SDK_
2N/Astatic char *grab_field(char *line, char **eofield)
2N/A{
2N/A int d = 0;
2N/A char *field;
2N/A
2N/A while (isspace((int) *line)) line++;
2N/A
2N/A /* find end of field */
2N/A while (line[d] && !isspace(((int) line[d]))) d++;
2N/A field = sasl_ALLOC(d + 1);
2N/A if (!field) { return NULL; }
2N/A memcpy(field, line, d);
2N/A field[d] = '\0';
2N/A *eofield = line + d;
2N/A
2N/A return field;
2N/A}
2N/A
2N/Astruct secflag_map_s {
2N/A char *name;
2N/A int value;
2N/A};
2N/A
2N/Astruct secflag_map_s secflag_map[] = {
2N/A { "noplaintext", SASL_SEC_NOPLAINTEXT },
2N/A { "noactive", SASL_SEC_NOACTIVE },
2N/A { "nodictionary", SASL_SEC_NODICTIONARY },
2N/A { "forward_secrecy", SASL_SEC_FORWARD_SECRECY },
2N/A { "noanonymous", SASL_SEC_NOANONYMOUS },
2N/A { "pass_credentials", SASL_SEC_PASS_CREDENTIALS },
2N/A { "mutual_auth", SASL_SEC_MUTUAL_AUTH },
2N/A { NULL, 0x0 }
2N/A};
2N/A
2N/Astatic int parse_mechlist_file(const char *mechlistfile)
2N/A{
2N/A FILE *f;
2N/A char buf[1024];
2N/A char *t, *ptr;
2N/A int r = 0;
2N/A
2N/A f = fopen(mechlistfile, "rF");
2N/A if (!f) return SASL_FAIL;
2N/A
2N/A r = SASL_OK;
2N/A while (fgets(buf, sizeof(buf), f) != NULL) {
2N/A mechanism_t *n = sasl_ALLOC(sizeof(mechanism_t));
2N/A sasl_server_plug_t *nplug;
2N/A
2N/A if (n == NULL) { r = SASL_NOMEM; break; }
2N/A n->version = SASL_SERVER_PLUG_VERSION;
2N/A n->condition = SASL_CONTINUE;
2N/A nplug = sasl_ALLOC(sizeof(sasl_server_plug_t));
2N/A if (nplug == NULL) { r = SASL_NOMEM; break; }
2N/A memset(nplug, 0, sizeof(sasl_server_plug_t));
2N/A
2N/A /* each line is:
2N/A plugin-file WS mech_name WS max_ssf *(WS security_flag) RET
2N/A */
2N/A
2N/A /* grab file */
2N/A n->f = grab_field(buf, &ptr);
2N/A
2N/A /* grab mech_name */
2N/A nplug->mech_name = grab_field(ptr, &ptr);
2N/A
2N/A /* grab max_ssf */
2N/A nplug->max_ssf = strtol(ptr, &ptr, 10);
2N/A
2N/A /* grab security flags */
2N/A while (*ptr != '\n') {
2N/A struct secflag_map_s *map;
2N/A
2N/A /* read security flag */
2N/A t = grab_field(ptr, &ptr);
2N/A map = secflag_map;
2N/A while (map->name) {
2N/A if (!strcasecmp(t, map->name)) {
2N/A nplug->security_flags |= map->value;
2N/A break;
2N/A }
2N/A map++;
2N/A }
2N/A if (!map->name) {
2N/A _sasl_log(NULL, SASL_LOG_ERR,
2N/A "%s: couldn't identify flag '%s'",
2N/A nplug->mech_name, t);
2N/A }
2N/A free(t);
2N/A }
2N/A
2N/A /* insert mechanism into mechlist */
2N/A n->plug = nplug;
2N/A n->next = mechlist->mech_list;
2N/A mechlist->mech_list = n;
2N/A mechlist->mech_length++;
2N/A }
2N/A
2N/A fclose(f);
2N/A return r;
2N/A}
2N/A#endif /* !_SUN_SDK_ */
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic int _load_server_plugins(_sasl_global_context_t *gctx)
2N/A{
2N/A int ret;
2N/A const add_plugin_list_t _ep_list[] = {
2N/A { "sasl_server_plug_init", (add_plugin_t *)_sasl_server_add_plugin },
2N/A { "sasl_auxprop_plug_init", (add_plugin_t *)_sasl_auxprop_add_plugin },
2N/A { "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin },
2N/A { NULL, NULL }
2N/A };
2N/A const sasl_callback_t *callbacks = gctx->server_global_callbacks.callbacks;
2N/A
2N/A ret = _sasl_load_plugins(gctx, 1, _ep_list,
2N/A _sasl_find_getpath_callback(callbacks),
2N/A _sasl_find_verifyfile_callback(callbacks));
2N/A return (ret);
2N/A}
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A/* initialize server drivers, done once per process
2N/A#ifdef _SUN_SDK_
2N/A * callbacks -- callbacks for all server connections
2N/A * appname -- name of calling application (for config)
2N/A#else
2N/A * callbacks -- callbacks for all server connections; must include
2N/A * getopt callback
2N/A * appname -- name of calling application (for lower level logging)
2N/A * results:
2N/A * state -- server state
2N/A#endif
2N/A * returns:
2N/A * SASL_OK -- success
2N/A * SASL_BADPARAM -- error in config file
2N/A * SASL_NOMEM -- memory failure
2N/A#ifndef _SUN_SDK_
2N/A * SASL_BADVERS -- Mechanism version mismatch
2N/A#endif
2N/A */
2N/A
2N/Aint sasl_server_init(const sasl_callback_t *callbacks,
2N/A const char *appname)
2N/A#ifdef _SUN_SDK_
2N/A{
2N/A return _sasl_server_init(NULL, callbacks, appname);
2N/A}
2N/A
2N/Aint _sasl_server_init(void *ctx, const sasl_callback_t *callbacks,
2N/A const char *appname)
2N/A#endif /* _SUN_SDK_ */
2N/A{
2N/A int ret;
2N/A const sasl_callback_t *vf;
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
2N/A#else
2N/A const char *pluginfile = NULL;
2N/A#ifdef PIC
2N/A sasl_getopt_t *getopt;
2N/A void *context;
2N/A#endif
2N/A
2N/A const add_plugin_list_t ep_list[] = {
2N/A { "sasl_server_plug_init", (add_plugin_t *)sasl_server_add_plugin },
2N/A { "sasl_auxprop_plug_init", (add_plugin_t *)sasl_auxprop_add_plugin },
2N/A { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
2N/A { NULL, NULL }
2N/A };
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* we require the appname to be non-null and short enough to be a path */
2N/A if (!appname || strlen(appname) >= PATH_MAX)
2N/A return SASL_BADPARAM;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A /* Process only one _sasl_server_init() at a time */
2N/A if (LOCK_MUTEX(&init_server_mutex) < 0)
2N/A return (SASL_FAIL);
2N/A if (LOCK_MUTEX(&server_active_mutex) < 0)
2N/A return (SASL_FAIL);
2N/A
2N/A if (gctx->sasl_server_active) {
2N/A /* We're already active, just increase our refcount */
2N/A /* xxx do something with the callback structure? */
2N/A gctx->sasl_server_active++;
2N/A UNLOCK_MUTEX(&server_active_mutex);
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A return SASL_OK;
2N/A }
2N/A
2N/A ret = _sasl_common_init(gctx, &gctx->server_global_callbacks, 1);
2N/A if (ret != SASL_OK) {
2N/A UNLOCK_MUTEX(&server_active_mutex);
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A return ret;
2N/A }
2N/A#else
2N/A if (_sasl_server_active) {
2N/A /* We're already active, just increase our refcount */
2N/A /* xxx do something with the callback structure? */
2N/A _sasl_server_active++;
2N/A return SASL_OK;
2N/A }
2N/A
2N/A ret = _sasl_common_init(&global_callbacks);
2N/A if (ret != SASL_OK)
2N/A return ret;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* verify that the callbacks look ok */
2N/A ret = verify_server_callbacks(callbacks);
2N/A#ifdef _SUN_SDK_
2N/A if (ret != SASL_OK) {
2N/A UNLOCK_MUTEX(&server_active_mutex);
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A return ret;
2N/A }
2N/A
2N/A gctx->server_global_callbacks.callbacks = callbacks;
2N/A gctx->server_global_callbacks.appname = appname;
2N/A
2N/A /* If we fail now, we have to call server_done */
2N/A gctx->sasl_server_active = 1;
2N/A UNLOCK_MUTEX(&server_active_mutex);
2N/A
2N/A /* allocate mechlist and set it to empty */
2N/A gctx->mechlist = sasl_ALLOC(sizeof(mech_list_t));
2N/A if (gctx->mechlist == NULL) {
2N/A server_done(gctx);
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A return SASL_NOMEM;
2N/A }
2N/A
2N/A ret = init_mechlist(gctx);
2N/A
2N/A if (ret != SASL_OK) {
2N/A server_done(gctx);
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A return ret;
2N/A }
2N/A#else
2N/A if (ret != SASL_OK)
2N/A return ret;
2N/A
2N/A global_callbacks.callbacks = callbacks;
2N/A global_callbacks.appname = appname;
2N/A
2N/A /* If we fail now, we have to call server_done */
2N/A _sasl_server_active = 1;
2N/A
2N/A /* allocate mechlist and set it to empty */
2N/A mechlist = sasl_ALLOC(sizeof(mech_list_t));
2N/A if (mechlist == NULL) {
2N/A server_done();
2N/A return SASL_NOMEM;
2N/A }
2N/A
2N/A ret = init_mechlist();
2N/A if (ret != SASL_OK) {
2N/A server_done();
2N/A return ret;
2N/A }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A vf = _sasl_find_verifyfile_callback(callbacks);
2N/A
2N/A /* load config file if applicable */
2N/A#ifdef _SUN_SDK_
2N/A ret = load_config(gctx, vf);
2N/A if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) {
2N/A server_done(gctx);
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A#else
2N/A ret = load_config(vf);
2N/A if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) {
2N/A server_done();
2N/A#endif /* _SUN_SDK_ */
2N/A return ret;
2N/A }
2N/A
2N/A /* load internal plugins */
2N/A#ifdef _SUN_SDK_
2N/A _sasl_server_add_plugin(gctx, "EXTERNAL", &external_server_plug_init);
2N/A
2N/A/* NOTE: plugin_list option not supported in SUN SDK */
2N/A {
2N/A#else
2N/A sasl_server_add_plugin("EXTERNAL", &external_server_plug_init);
2N/A
2N/A#ifdef PIC
2N/A /* delayed loading of plugins? (DSO only, as it doesn't
2N/A * make much [any] sense to delay in the static library case) */
2N/A if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context)
2N/A == SASL_OK) {
2N/A /* No sasl_conn_t was given to getcallback, so we provide the
2N/A * global callbacks structure */
2N/A ret = getopt(&global_callbacks, NULL, "plugin_list", &pluginfile, NULL);
2N/A }
2N/A#endif
2N/A
2N/A if (pluginfile != NULL) {
2N/A /* this file should contain a list of plugins available.
2N/A we'll load on demand. */
2N/A
2N/A /* Ask the application if it's safe to use this file */
2N/A ret = ((sasl_verifyfile_t *)(vf->proc))(vf->context,
2N/A pluginfile,
2N/A SASL_VRFY_CONF);
2N/A if (ret != SASL_OK) {
2N/A _sasl_log(NULL, SASL_LOG_ERR,
2N/A "unable to load plugin list %s: %z", pluginfile, ret);
2N/A }
2N/A
2N/A if (ret == SASL_OK) {
2N/A ret = parse_mechlist_file(pluginfile);
2N/A }
2N/A } else {
2N/A#endif /* _SUN_SDK_ */
2N/A /* load all plugins now */
2N/A#ifdef _SUN_SDK_
2N/A ret = _load_server_plugins(gctx);
2N/A#else
2N/A ret = _sasl_load_plugins(ep_list,
2N/A _sasl_find_getpath_callback(callbacks),
2N/A _sasl_find_verifyfile_callback(callbacks));
2N/A#endif /* _SUN_SDK_ */
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if (ret == SASL_OK)
2N/A ret = _sasl_build_mechlist(gctx);
2N/A if (ret == SASL_OK) {
2N/A gctx->sasl_server_cleanup_hook = &server_done;
2N/A gctx->sasl_server_idle_hook = &server_idle;
2N/A } else {
2N/A server_done(gctx);
2N/A }
2N/A UNLOCK_MUTEX(&init_server_mutex);
2N/A#else
2N/A if (ret == SASL_OK) {
2N/A _sasl_server_cleanup_hook = &server_done;
2N/A _sasl_server_idle_hook = &server_idle;
2N/A
2N/A ret = _sasl_build_mechlist();
2N/A } else {
2N/A server_done();
2N/A }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Once we have the users plaintext password we
2N/A * may want to transition them. That is put entries
2N/A * for them in the passwd database for other
2N/A * stronger mechanism
2N/A *
2N/A * for example PLAIN -> CRAM-MD5
2N/A */
2N/Astatic int
2N/A_sasl_transition(sasl_conn_t * conn,
2N/A const char * pass,
2N/A unsigned passlen)
2N/A{
2N/A const char *dotrans = "n";
2N/A sasl_getopt_t *getopt;
2N/A int result = SASL_OK;
2N/A void *context;
2N/A
2N/A if (! conn)
2N/A return SASL_BADPARAM;
2N/A
2N/A if (! conn->oparams.authid)
2N/A PARAMERROR(conn);
2N/A
2N/A /* check if this is enabled: default to false */
2N/A if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK)
2N/A {
2N/A getopt(context, NULL, "auto_transition", &dotrans, NULL);
2N/A if (dotrans == NULL) dotrans = "n";
2N/A }
2N/A
2N/A if (*dotrans == '1' || *dotrans == 'y' ||
2N/A (*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') {
2N/A /* ok, it's on! */
2N/A result = sasl_setpass(conn,
2N/A conn->oparams.authid,
2N/A pass,
2N/A passlen,
2N/A NULL, 0, 0);
2N/A }
2N/A
2N/A RETURN(conn,result);
2N/A}
2N/A
2N/A
2N/A/* create context for a single SASL connection
2N/A * service -- registered name of the service using SASL (e.g. "imap")
2N/A * serverFQDN -- Fully qualified domain name of server. NULL means use
2N/A * gethostname() or equivalent.
2N/A * Useful for multi-homed servers.
2N/A * user_realm -- permits multiple user realms on server, NULL = default
2N/A * iplocalport -- server IPv4/IPv6 domain literal string with port
2N/A * (if NULL, then mechanisms requiring IPaddr are disabled)
2N/A * ipremoteport -- client IPv4/IPv6 domain literal string with port
2N/A * (if NULL, then mechanisms requiring IPaddr are disabled)
2N/A * callbacks -- callbacks (e.g., authorization, lang, new getopt context)
2N/A * flags -- usage flags (see above)
2N/A * returns:
2N/A * pconn -- new connection context
2N/A *
2N/A * returns:
2N/A * SASL_OK -- success
2N/A * SASL_NOMEM -- not enough memory
2N/A */
2N/A
2N/Aint sasl_server_new(const char *service,
2N/A const char *serverFQDN,
2N/A const char *user_realm,
2N/A const char *iplocalport,
2N/A const char *ipremoteport,
2N/A const sasl_callback_t *callbacks,
2N/A unsigned flags,
2N/A sasl_conn_t **pconn)
2N/A#ifdef _SUN_SDK_
2N/A{
2N/A return _sasl_server_new(NULL, service, serverFQDN, user_realm, iplocalport,
2N/A ipremoteport, callbacks, flags, pconn);
2N/A}
2N/A
2N/Aint _sasl_server_new(void *ctx,
2N/A const char *service,
2N/A const char *serverFQDN,
2N/A const char *user_realm,
2N/A const char *iplocalport,
2N/A const char *ipremoteport,
2N/A const sasl_callback_t *callbacks,
2N/A unsigned flags,
2N/A sasl_conn_t **pconn)
2N/A#endif /* _SUN_SDK_ */
2N/A{
2N/A int result;
2N/A sasl_server_conn_t *serverconn;
2N/A sasl_utils_t *utils;
2N/A sasl_getopt_t *getopt;
2N/A void *context;
2N/A const char *log_level;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx = (ctx == NULL) ? _sasl_gbl_ctx() : ctx;
2N/A
2N/A if (gctx->sasl_server_active==0) return SASL_NOTINIT;
2N/A#else
2N/A if (_sasl_server_active==0) return SASL_NOTINIT;
2N/A#endif /* _SUN_SDK_ */
2N/A if (! pconn) return SASL_FAIL;
2N/A if (! service) return SASL_FAIL;
2N/A
2N/A *pconn=sasl_ALLOC(sizeof(sasl_server_conn_t));
2N/A if (*pconn==NULL) return SASL_NOMEM;
2N/A
2N/A memset(*pconn, 0, sizeof(sasl_server_conn_t));
2N/A
2N/A#ifdef _SUN_SDK_
2N/A (*pconn)->gctx = gctx;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A serverconn = (sasl_server_conn_t *)*pconn;
2N/A
2N/A /* make sparams */
2N/A serverconn->sparams=sasl_ALLOC(sizeof(sasl_server_params_t));
2N/A if (serverconn->sparams==NULL)
2N/A MEMERROR(*pconn);
2N/A
2N/A memset(serverconn->sparams, 0, sizeof(sasl_server_params_t));
2N/A
2N/A (*pconn)->destroy_conn = &server_dispose;
2N/A result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_SERVER,
2N/A &server_idle, serverFQDN,
2N/A iplocalport, ipremoteport,
2N/A#ifdef _SUN_SDK_
2N/A callbacks, &gctx->server_global_callbacks);
2N/A#else
2N/A callbacks, &global_callbacks);
2N/A#endif /* _SUN_SDK_ */
2N/A if (result != SASL_OK)
2N/A goto done_error;
2N/A
2N/A
2N/A /* set util functions - need to do rest */
2N/A#ifdef _SUN_SDK_
2N/A utils=_sasl_alloc_utils(gctx, *pconn, &gctx->server_global_callbacks);
2N/A#else
2N/A utils=_sasl_alloc_utils(*pconn, &global_callbacks);
2N/A#endif /* _SUN_SDK_ */
2N/A if (!utils) {
2N/A result = SASL_NOMEM;
2N/A goto done_error;
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A utils->checkpass = &_sasl_checkpass;
2N/A#else /* _SUN_SDK_ */
2N/A utils->checkpass = &sasl_checkpass;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* Setup the propctx -> We'll assume the default size */
2N/A serverconn->sparams->propctx=prop_new(0);
2N/A if(!serverconn->sparams->propctx) {
2N/A result = SASL_NOMEM;
2N/A goto done_error;
2N/A }
2N/A
2N/A serverconn->sparams->service = (*pconn)->service;
2N/A serverconn->sparams->servicelen = strlen((*pconn)->service);
2N/A
2N/A#ifdef _SUN_SDK_
2N/A serverconn->sparams->appname = gctx->server_global_callbacks.appname;
2N/A serverconn->sparams->applen = strlen(gctx->server_global_callbacks.appname);
2N/A#else
2N/A serverconn->sparams->appname = global_callbacks.appname;
2N/A serverconn->sparams->applen = strlen(global_callbacks.appname);
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A serverconn->sparams->serverFQDN = (*pconn)->serverFQDN;
2N/A serverconn->sparams->slen = strlen((*pconn)->serverFQDN);
2N/A
2N/A if (user_realm) {
2N/A result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL);
2N/A serverconn->sparams->urlen = strlen(user_realm);
2N/A serverconn->sparams->user_realm = serverconn->user_realm;
2N/A } else {
2N/A serverconn->user_realm = NULL;
2N/A /* the sparams is already zeroed */
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A serverconn->sparams->iplocalport = (*pconn)->iplocalport;
2N/A serverconn->sparams->iploclen = strlen((*pconn)->iplocalport);
2N/A serverconn->sparams->ipremoteport = (*pconn)->ipremoteport;
2N/A serverconn->sparams->ipremlen = strlen((*pconn)->ipremoteport);
2N/A
2N/A serverconn->sparams->callbacks = callbacks;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A log_level = NULL;
2N/A if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
2N/A getopt(context, NULL, "log_level", &log_level, NULL);
2N/A }
2N/A serverconn->sparams->log_level = log_level ? atoi(log_level) : SASL_LOG_ERR;
2N/A
2N/A serverconn->sparams->utils = utils;
2N/A serverconn->sparams->transition = &_sasl_transition;
2N/A serverconn->sparams->canon_user = &_sasl_canon_user;
2N/A serverconn->sparams->props = serverconn->base.props;
2N/A serverconn->sparams->flags = flags;
2N/A
2N/A if(result == SASL_OK) return SASL_OK;
2N/A
2N/A done_error:
2N/A _sasl_conn_dispose(*pconn);
2N/A sasl_FREE(*pconn);
2N/A *pconn = NULL;
2N/A return result;
2N/A}
2N/A
2N/A/*
2N/A * The rule is:
2N/A * IF mech strength + external strength < min ssf THEN FAIL
2N/A * We also have to look at the security properties and make sure
2N/A * that this mechanism has everything we want
2N/A */
2N/Astatic int mech_permitted(sasl_conn_t *conn,
2N/A mechanism_t *mech)
2N/A{
2N/A sasl_server_conn_t *s_conn = (sasl_server_conn_t *)conn;
2N/A const sasl_server_plug_t *plug;
2N/A int myflags;
2N/A context_list_t *cur;
2N/A sasl_getopt_t *getopt;
2N/A void *context;
2N/A sasl_ssf_t minssf = 0;
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if(!conn) return 0;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A gctx = conn->gctx;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if(! mech || ! mech->plug) {
2N/A#ifdef _SUN_SDK_
2N/A if(conn) _sasl_log(conn, SASL_LOG_WARN, "Parameter error");
2N/A#else
2N/A PARAMERROR(conn);
2N/A#endif /* _SUN_SDK_ */
2N/A return 0;
2N/A }
2N/A
2N/A plug = mech->plug;
2N/A
2N/A /* get the list of allowed mechanisms (default = all) */
2N/A if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
2N/A == SASL_OK) {
2N/A const char *mlist = NULL;
2N/A
2N/A getopt(context, NULL, "mech_list", &mlist, NULL);
2N/A
2N/A /* if we have a list, check the plugin against it */
2N/A if (mlist) {
2N/A const char *cp;
2N/A
2N/A while (*mlist) {
2N/A for (cp = mlist; *cp && !isspace((int) *cp); cp++);
2N/A if (((size_t) (cp - mlist) == strlen(plug->mech_name)) &&
2N/A !strncasecmp(mlist, plug->mech_name,
2N/A strlen(plug->mech_name))) {
2N/A break;
2N/A }
2N/A mlist = cp;
2N/A while (*mlist && isspace((int) *mlist)) mlist++;
2N/A }
2N/A
2N/A if (!*mlist) return 0; /* reached EOS -> not in our list */
2N/A }
2N/A }
2N/A
2N/A /* setup parameters for the call to mech_avail */
2N/A s_conn->sparams->serverFQDN=conn->serverFQDN;
2N/A s_conn->sparams->service=conn->service;
2N/A s_conn->sparams->user_realm=s_conn->user_realm;
2N/A s_conn->sparams->props=conn->props;
2N/A s_conn->sparams->external_ssf=conn->external.ssf;
2N/A
2N/A /* Check if we have banished this one already */
2N/A for(cur = s_conn->mech_contexts; cur; cur=cur->next) {
2N/A if(cur->mech == mech) {
2N/A /* If it's not mech_avail'd, then stop now */
2N/A if(!cur->context) return 0;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A if (!mech->sun_reg) {
2N/A s_conn->sparams->props.min_ssf = 0;
2N/A s_conn->sparams->props.max_ssf = 0;
2N/A }
2N/A s_conn->base.sun_reg = mech->sun_reg;
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A if (conn->props.min_ssf < conn->external.ssf) {
2N/A minssf = 0;
2N/A } else {
2N/A minssf = conn->props.min_ssf - conn->external.ssf;
2N/A }
2N/A
2N/A /* Generic mechanism */
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A /* If not SUN supplied mech, it has no strength */
2N/A if (plug->max_ssf < minssf || (minssf > 0 && !mech->sun_reg)) {
2N/A#else
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A if (plug->max_ssf < minssf) {
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sasl_seterror(conn, SASL_NOLOG,
2N/A gettext("mech %s is too weak"), plug->mech_name);
2N/A#else
2N/A sasl_seterror(conn, SASL_NOLOG,
2N/A "mech %s is too weak", plug->mech_name);
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A return 0; /* too weak */
2N/A }
2N/A
2N/A context = NULL;
2N/A if(plug->mech_avail
2N/A#ifdef _SUN_SDK_
2N/A && plug->mech_avail(mech->glob_context,
2N/A#else
2N/A && plug->mech_avail(plug->glob_context,
2N/A#endif /* _SUN_SDK_ */
2N/A s_conn->sparams, (void **)&context) != SASL_OK ) {
2N/A /* Mark this mech as no good for this connection */
2N/A cur = sasl_ALLOC(sizeof(context_list_t));
2N/A if(!cur) {
2N/A#ifdef _SUN_SDK_
2N/A if(conn) _sasl_log(conn, SASL_LOG_WARN, "Out of Memory");
2N/A#else
2N/A MEMERROR(conn);
2N/A#endif /* _SUN_SDK_ */
2N/A return 0;
2N/A }
2N/A cur->context = NULL;
2N/A cur->mech = mech;
2N/A cur->next = s_conn->mech_contexts;
2N/A s_conn->mech_contexts = cur;
2N/A
2N/A /* Error should be set by mech_avail call */
2N/A return 0;
2N/A } else if(context) {
2N/A /* Save this context */
2N/A cur = sasl_ALLOC(sizeof(context_list_t));
2N/A if(!cur) {
2N/A#ifdef _SUN_SDK_
2N/A if(conn) _sasl_log(conn, SASL_LOG_WARN, "Out of Memory");
2N/A#else
2N/A MEMERROR(conn);
2N/A#endif /* _SUN_SDK_ */
2N/A return 0;
2N/A }
2N/A cur->context = context;
2N/A cur->mech = mech;
2N/A cur->next = s_conn->mech_contexts;
2N/A s_conn->mech_contexts = cur;
2N/A }
2N/A
2N/A /* Generic mechanism */
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A /* If not SUN supplied mech, it has no strength */
2N/A if (plug->max_ssf < minssf || (minssf > 0 && !mech->sun_reg)) {
2N/A#else
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A if (plug->max_ssf < minssf) {
2N/A /* EXPORT DELETE START */
2N/A /* CRYPT DELETE START */
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A /* CRYPT DELETE END */
2N/A /* EXPORT DELETE END */
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sasl_seterror(conn, SASL_NOLOG, gettext("too weak"));
2N/A#else
2N/A sasl_seterror(conn, SASL_NOLOG, "too weak");
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A return 0; /* too weak */
2N/A }
2N/A
2N/A#ifndef _SUN_SDK_
2N/A /* if there are no users in the secrets database we can't use this
2N/A mechanism */
2N/A if (mech->condition == SASL_NOUSER) {
2N/A sasl_seterror(conn, 0, "no users in secrets db");
2N/A return 0;
2N/A }
2N/A#endif /* !_SUN_SDK_ */
2N/A
2N/A /* Can it meet our features? */
2N/A if ((conn->flags & SASL_NEED_PROXY) &&
2N/A !(plug->features & SASL_FEAT_ALLOWS_PROXY)) {
2N/A return 0;
2N/A }
2N/A
2N/A /* security properties---if there are any flags that differ and are
2N/A in what the connection are requesting, then fail */
2N/A
2N/A /* special case plaintext */
2N/A myflags = conn->props.security_flags;
2N/A
2N/A /* if there's an external layer this is no longer plaintext */
2N/A if ((conn->props.min_ssf <= conn->external.ssf) &&
2N/A (conn->external.ssf > 1)) {
2N/A myflags &= ~SASL_SEC_NOPLAINTEXT;
2N/A }
2N/A
2N/A /* do we want to special case SASL_SEC_PASS_CREDENTIALS? nah.. */
2N/A if (((myflags ^ plug->security_flags) & myflags) != 0) {
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sasl_seterror(conn, SASL_NOLOG,
2N/A gettext("security flags do not match required"));
2N/A#else
2N/A sasl_seterror(conn, SASL_NOLOG,
2N/A "security flags do not match required");
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A return 0;
2N/A }
2N/A
2N/A /* Check Features */
2N/A if(plug->features & SASL_FEAT_GETSECRET) {
2N/A /* We no longer support sasl_server_{get,put}secret */
2N/A#ifdef _SUN_SDK_
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "mech %s requires unprovided secret facility",
2N/A plug->mech_name);
2N/A#else
2N/A sasl_seterror(conn, 0,
2N/A "mech %s requires unprovided secret facility",
2N/A plug->mech_name);
2N/A#endif /* _SUN_SDK_ */
2N/A return 0;
2N/A }
2N/A
2N/A return 1;
2N/A}
2N/A
2N/A/*
2N/A * make the authorization
2N/A *
2N/A */
2N/A
2N/Astatic int do_authorization(sasl_server_conn_t *s_conn)
2N/A{
2N/A int ret;
2N/A sasl_authorize_t *authproc;
2N/A void *auth_context;
2N/A
2N/A /* now let's see if authname is allowed to proxy for username! */
2N/A
2N/A /* check the proxy callback */
2N/A if (_sasl_getcallback(&s_conn->base, SASL_CB_PROXY_POLICY,
2N/A &authproc, &auth_context) != SASL_OK) {
2N/A INTERROR(&s_conn->base, SASL_NOAUTHZ);
2N/A }
2N/A
2N/A ret = authproc(&(s_conn->base), auth_context,
2N/A s_conn->base.oparams.user, s_conn->base.oparams.ulen,
2N/A s_conn->base.oparams.authid, s_conn->base.oparams.alen,
2N/A s_conn->user_realm,
2N/A (s_conn->user_realm ? strlen(s_conn->user_realm) : 0),
2N/A s_conn->sparams->propctx);
2N/A
2N/A RETURN(&s_conn->base, ret);
2N/A}
2N/A
2N/A
2N/A/* start a mechanism exchange within a connection context
2N/A * mech -- the mechanism name client requested
2N/A * clientin -- client initial response (NUL terminated), NULL if empty
2N/A * clientinlen -- length of initial response
2N/A * serverout -- initial server challenge, NULL if done
2N/A * (library handles freeing this string)
2N/A * serveroutlen -- length of initial server challenge
2N/A#ifdef _SUN_SDK_
2N/A * conn -- the sasl connection
2N/A#else
2N/A * output:
2N/A * pconn -- the connection negotiation state on success
2N/A#endif
2N/A *
2N/A * Same returns as sasl_server_step() or
2N/A * SASL_NOMECH if mechanism not available.
2N/A */
2N/Aint sasl_server_start(sasl_conn_t *conn,
2N/A const char *mech,
2N/A const char *clientin,
2N/A unsigned clientinlen,
2N/A const char **serverout,
2N/A unsigned *serveroutlen)
2N/A{
2N/A sasl_server_conn_t *s_conn=(sasl_server_conn_t *) conn;
2N/A int result;
2N/A context_list_t *cur, **prev;
2N/A mechanism_t *m;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx =
2N/A (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
2N/A mech_list_t *mechlist;
2N/A
2N/A if (gctx->sasl_server_active==0) return SASL_NOTINIT;
2N/A if (! conn)
2N/A return SASL_BADPARAM;
2N/A
2N/A (void)_load_server_plugins(gctx);
2N/A mechlist = gctx->mechlist;
2N/A m=mechlist->mech_list;
2N/A result = load_config(gctx, _sasl_find_verifyfile_callback(
2N/A gctx->server_global_callbacks.callbacks));
2N/A if (result != SASL_OK)
2N/A return (result);
2N/A#else
2N/A if (_sasl_server_active==0) return SASL_NOTINIT;
2N/A
2N/A /* make sure mech is valid mechanism
2N/A if not return appropriate error */
2N/A m=mechlist->mech_list;
2N/A
2N/A /* check parameters */
2N/A if(!conn) return SASL_BADPARAM;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if (!mech || ((clientin==NULL) && (clientinlen>0)))
2N/A PARAMERROR(conn);
2N/A
2N/A if(serverout) *serverout = NULL;
2N/A if(serveroutlen) *serveroutlen = 0;
2N/A
2N/A while (m!=NULL)
2N/A {
2N/A if ( strcasecmp(mech,m->plug->mech_name)==0)
2N/A {
2N/A break;
2N/A }
2N/A m=m->next;
2N/A }
2N/A
2N/A if (m==NULL) {
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sasl_seterror(conn, 0, gettext("Couldn't find mech %s"), mech);
2N/A#else
2N/A sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A result = SASL_NOMECH;
2N/A goto done;
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A server_dispose_mech_contexts(conn);
2N/A#endif /*_SUN_SDK_ */
2N/A
2N/A /* Make sure that we're willing to use this mech */
2N/A if (! mech_permitted(conn, m)) {
2N/A result = SASL_NOMECH;
2N/A goto done;
2N/A }
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if(conn->context) {
2N/A s_conn->mech->plug->mech_dispose(conn->context, s_conn->sparams->utils);
2N/A conn->context = NULL;
2N/A }
2N/A memset(&conn->oparams, 0, sizeof(sasl_out_params_t));
2N/A#else
2N/A if (m->condition == SASL_CONTINUE) {
2N/A sasl_server_plug_init_t *entry_point;
2N/A void *library = NULL;
2N/A sasl_server_plug_t *pluglist;
2N/A int version, plugcount;
2N/A int l = 0;
2N/A
2N/A /* need to load this plugin */
2N/A result = _sasl_get_plugin(m->f,
2N/A _sasl_find_verifyfile_callback(global_callbacks.callbacks),
2N/A &library);
2N/A
2N/A if (result == SASL_OK) {
2N/A result = _sasl_locate_entry(library, "sasl_server_plug_init",
2N/A (void **)&entry_point);
2N/A }
2N/A
2N/A if (result == SASL_OK) {
2N/A result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
2N/A &version, &pluglist, &plugcount);
2N/A }
2N/A
2N/A if (result == SASL_OK) {
2N/A /* find the correct mechanism in this plugin */
2N/A for (l = 0; l < plugcount; l++) {
2N/A if (!strcasecmp(pluglist[l].mech_name,
2N/A m->plug->mech_name)) break;
2N/A }
2N/A if (l == plugcount) {
2N/A result = SASL_NOMECH;
2N/A }
2N/A }
2N/A if (result == SASL_OK) {
2N/A /* check that the parameters are the same */
2N/A if ((pluglist[l].max_ssf != m->plug->max_ssf) ||
2N/A (pluglist[l].security_flags != m->plug->security_flags)) {
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "%s: security parameters don't match mechlist file",
2N/A pluglist[l].mech_name);
2N/A result = SASL_NOMECH;
2N/A }
2N/A }
2N/A if (result == SASL_OK) {
2N/A /* copy mechlist over */
2N/A sasl_FREE((sasl_server_plug_t *) m->plug);
2N/A m->plug = &pluglist[l];
2N/A m->condition = SASL_OK;
2N/A }
2N/A
2N/A if (result != SASL_OK) {
2N/A /* The library will eventually be freed, don't sweat it */
2N/A RETURN(conn, result);
2N/A }
2N/A }
2N/A#endif /* !_SUN_SDK_ */
2N/A
2N/A /* We used to setup sparams HERE, but now it's done
2N/A inside of mech_permitted (which is called above) */
2N/A prev = &s_conn->mech_contexts;
2N/A for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
2N/A if(cur->mech == m) {
2N/A if(!cur->context) {
2N/A#ifdef _SUN_SDK_
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "Got past mech_permitted with a disallowed mech!");
2N/A#else
2N/A sasl_seterror(conn, 0,
2N/A "Got past mech_permitted with a disallowed mech!");
2N/A#endif /* _SUN_SDK_ */
2N/A return SASL_NOMECH;
2N/A }
2N/A /* If we find it, we need to pull cur out of the
2N/A list so it won't be freed later! */
2N/A (*prev)->next = cur->next;
2N/A conn->context = cur->context;
2N/A sasl_FREE(cur);
2N/A }
2N/A }
2N/A
2N/A s_conn->mech = m;
2N/A
2N/A if(!conn->context) {
2N/A /* Note that we don't hand over a new challenge */
2N/A#ifdef _SUN_SDK_
2N/A result = s_conn->mech->plug->mech_new(s_conn->mech->glob_context,
2N/A#else
2N/A result = s_conn->mech->plug->mech_new(s_conn->mech->plug->glob_context,
2N/A#endif /* _SUN_SDK_ */
2N/A s_conn->sparams,
2N/A NULL,
2N/A 0,
2N/A &(conn->context));
2N/A } else {
2N/A /* the work was already done by mech_avail! */
2N/A result = SASL_OK;
2N/A }
2N/A
2N/A if (result == SASL_OK) {
2N/A if(clientin) {
2N/A if(s_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) {
2N/A /* Remote sent first, but mechanism does not support it.
2N/A * RFC 2222 says we fail at this point. */
2N/A#ifdef _SUN_SDK_
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "Remote sent first but mech does not allow it.");
2N/A#else
2N/A sasl_seterror(conn, 0,
2N/A "Remote sent first but mech does not allow it.");
2N/A#endif /* _SUN_SDK_ */
2N/A result = SASL_BADPROT;
2N/A } else {
2N/A /* Mech wants client-first, so let them have it */
2N/A result = sasl_server_step(conn,
2N/A clientin, clientinlen,
2N/A serverout, serveroutlen);
2N/A }
2N/A } else {
2N/A if(s_conn->mech->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2N/A /* Mech wants client first anyway, so we should do that */
2N/A *serverout = "";
2N/A *serveroutlen = 0;
2N/A result = SASL_CONTINUE;
2N/A } else {
2N/A /* Mech wants server-first, so let them have it */
2N/A result = sasl_server_step(conn,
2N/A clientin, clientinlen,
2N/A serverout, serveroutlen);
2N/A }
2N/A }
2N/A }
2N/A
2N/A done:
2N/A if( result != SASL_OK
2N/A && result != SASL_CONTINUE
2N/A && result != SASL_INTERACT) {
2N/A if(conn->context) {
2N/A s_conn->mech->plug->mech_dispose(conn->context,
2N/A s_conn->sparams->utils);
2N/A conn->context = NULL;
2N/A }
2N/A }
2N/A
2N/A RETURN(conn,result);
2N/A}
2N/A
2N/A
2N/A/* perform one step of the SASL exchange
2N/A * inputlen & input -- client data
2N/A * NULL on first step if no optional client step
2N/A * outputlen & output -- set to the server data to transmit
2N/A * to the client in the next step
2N/A * (library handles freeing this)
2N/A *
2N/A * returns:
2N/A * SASL_OK -- exchange is complete.
2N/A * SASL_CONTINUE -- indicates another step is necessary.
2N/A * SASL_TRANS -- entry for user exists, but not for mechanism
2N/A * and transition is possible
2N/A * SASL_BADPARAM -- service name needed
2N/A * SASL_BADPROT -- invalid input from client
2N/A * ...
2N/A */
2N/A
2N/Aint sasl_server_step(sasl_conn_t *conn,
2N/A const char *clientin,
2N/A unsigned clientinlen,
2N/A const char **serverout,
2N/A unsigned *serveroutlen)
2N/A{
2N/A int ret;
2N/A sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx =
2N/A (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
2N/A
2N/A /* check parameters */
2N/A if (gctx->sasl_server_active==0) return SASL_NOTINIT;
2N/A#else
2N/A /* check parameters */
2N/A if (_sasl_server_active==0) return SASL_NOTINIT;
2N/A#endif /* _SUN_SDK_ */
2N/A if (!conn) return SASL_BADPARAM;
2N/A if ((clientin==NULL) && (clientinlen>0))
2N/A PARAMERROR(conn);
2N/A
2N/A /* If we've already done the last send, return! */
2N/A if(s_conn->sent_last == 1) {
2N/A return SASL_OK;
2N/A }
2N/A
2N/A /* Don't do another step if the plugin told us that we're done */
2N/A if (conn->oparams.doneflag) {
2N/A _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
2N/A return SASL_FAIL;
2N/A }
2N/A
2N/A if(serverout) *serverout = NULL;
2N/A if(serveroutlen) *serveroutlen = 0;
2N/A
2N/A ret = s_conn->mech->plug->mech_step(conn->context,
2N/A s_conn->sparams,
2N/A clientin,
2N/A clientinlen,
2N/A serverout,
2N/A serveroutlen,
2N/A &conn->oparams);
2N/A
2N/A if (ret == SASL_OK) {
2N/A ret = do_authorization(s_conn);
2N/A }
2N/A
2N/A if (ret == SASL_OK) {
2N/A /* if we're done, we need to watch out for the following:
2N/A * 1. the mech does server-send-last
2N/A * 2. the protocol does not
2N/A *
2N/A * in this case, return SASL_CONTINUE and remember we are done.
2N/A */
2N/A if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
2N/A s_conn->sent_last = 1;
2N/A ret = SASL_CONTINUE;
2N/A }
2N/A if(!conn->oparams.maxoutbuf) {
2N/A conn->oparams.maxoutbuf = conn->props.maxbufsize;
2N/A }
2N/A
2N/A if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
2N/A#ifdef _SUN_SDK_
2N/A _sasl_log(conn, SASL_LOG_ERR,
2N/A "mech did not call canon_user for both authzid "
2N/A "and authid");
2N/A#else
2N/A sasl_seterror(conn, 0,
2N/A "mech did not call canon_user for both authzid " \
2N/A "and authid");
2N/A#endif /* _SUN_SDK_ */
2N/A ret = SASL_BADPROT;
2N/A }
2N/A }
2N/A
2N/A if( ret != SASL_OK
2N/A && ret != SASL_CONTINUE
2N/A && ret != SASL_INTERACT) {
2N/A if(conn->context) {
2N/A s_conn->mech->plug->mech_dispose(conn->context,
2N/A s_conn->sparams->utils);
2N/A conn->context = NULL;
2N/A }
2N/A }
2N/A
2N/A RETURN(conn, ret);
2N/A}
2N/A
2N/A/* returns the length of all the mechanisms
2N/A * added up
2N/A */
2N/A
2N/A#ifdef _SUN_SDK_
2N/Astatic unsigned mech_names_len(_sasl_global_context_t *gctx)
2N/A{
2N/A mech_list_t *mechlist = gctx->mechlist;
2N/A#else
2N/Astatic unsigned mech_names_len()
2N/A{
2N/A#endif /* _SUN_SDK_ */
2N/A mechanism_t *listptr;
2N/A unsigned result = 0;
2N/A
2N/A for (listptr = mechlist->mech_list;
2N/A listptr;
2N/A listptr = listptr->next)
2N/A result += strlen(listptr->plug->mech_name);
2N/A
2N/A return result;
2N/A}
2N/A
2N/A/* This returns a list of mechanisms in a NUL-terminated string
2N/A *
2N/A * The default behavior is to seperate with spaces if sep==NULL
2N/A */
2N/Aint _sasl_server_listmech(sasl_conn_t *conn,
2N/A const char *user __attribute__((unused)),
2N/A const char *prefix,
2N/A const char *sep,
2N/A const char *suffix,
2N/A const char **result,
2N/A unsigned *plen,
2N/A int *pcount)
2N/A{
2N/A int lup;
2N/A mechanism_t *listptr;
2N/A int ret;
2N/A int resultlen;
2N/A int flag;
2N/A const char *mysep;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx;
2N/A mech_list_t *mechlist;
2N/A
2N/A if (!conn) return SASL_BADPARAM;
2N/A /* if there hasn't been a sasl_sever_init() fail */
2N/A gctx = conn->gctx;
2N/A if (gctx->sasl_server_active==0) return SASL_NOTINIT;
2N/A
2N/A (void)_load_server_plugins(gctx);
2N/A mechlist = gctx->mechlist;
2N/A#else
2N/A /* if there hasn't been a sasl_sever_init() fail */
2N/A if (_sasl_server_active==0) return SASL_NOTINIT;
2N/A if (!conn) return SASL_BADPARAM;
2N/A#endif /* _SUN_SDK_ */
2N/A if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
2N/A
2N/A if (! result)
2N/A PARAMERROR(conn);
2N/A
2N/A if (plen != NULL)
2N/A *plen = 0;
2N/A if (pcount != NULL)
2N/A *pcount = 0;
2N/A
2N/A if (sep) {
2N/A mysep = sep;
2N/A } else {
2N/A mysep = " ";
2N/A }
2N/A
2N/A if (! mechlist || mechlist->mech_length <= 0)
2N/A INTERROR(conn, SASL_NOMECH);
2N/A
2N/A resultlen = (prefix ? strlen(prefix) : 0)
2N/A + (strlen(mysep) * (mechlist->mech_length - 1))
2N/A#ifdef _SUN_SDK_
2N/A + mech_names_len(gctx)
2N/A#else
2N/A + mech_names_len()
2N/A#endif /* _SUN_SDK_ */
2N/A + (suffix ? strlen(suffix) : 0)
2N/A + 1;
2N/A ret = _buf_alloc(&conn->mechlist_buf,
2N/A &conn->mechlist_buf_len, resultlen);
2N/A if(ret != SASL_OK) MEMERROR(conn);
2N/A
2N/A if (prefix)
2N/A strcpy (conn->mechlist_buf,prefix);
2N/A else
2N/A *(conn->mechlist_buf) = '\0';
2N/A
2N/A listptr = mechlist->mech_list;
2N/A
2N/A flag = 0;
2N/A /* make list */
2N/A for (lup = 0; lup < mechlist->mech_length; lup++) {
2N/A /* currently, we don't use the "user" parameter for anything */
2N/A if (mech_permitted(conn, listptr)) {
2N/A if (pcount != NULL)
2N/A (*pcount)++;
2N/A
2N/A /* print separator */
2N/A if (flag) {
2N/A strcat(conn->mechlist_buf, mysep);
2N/A } else {
2N/A flag = 1;
2N/A }
2N/A
2N/A /* now print the mechanism name */
2N/A strcat(conn->mechlist_buf, listptr->plug->mech_name);
2N/A }
2N/A
2N/A listptr = listptr->next;
2N/A }
2N/A
2N/A if (suffix)
2N/A strcat(conn->mechlist_buf,suffix);
2N/A
2N/A if (plen!=NULL)
2N/A *plen=strlen(conn->mechlist_buf);
2N/A
2N/A *result = conn->mechlist_buf;
2N/A
2N/A return SASL_OK;
2N/A}
2N/A
2N/A#ifdef _SUN_SDK_
2N/Asasl_string_list_t *_sasl_server_mechs(_sasl_global_context_t *gctx)
2N/A#else
2N/Asasl_string_list_t *_sasl_server_mechs(void)
2N/A#endif /* _SUN_SDK_ */
2N/A{
2N/A mechanism_t *listptr;
2N/A sasl_string_list_t *retval = NULL, *next=NULL;
2N/A#ifdef _SUN_SDK_
2N/A mech_list_t *mechlist = gctx->mechlist;
2N/A
2N/A if(!gctx->sasl_server_active) return NULL;
2N/A#else
2N/A if(!_sasl_server_active) return NULL;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* make list */
2N/A for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
2N/A next = sasl_ALLOC(sizeof(sasl_string_list_t));
2N/A
2N/A if(!next && !retval) return NULL;
2N/A else if(!next) {
2N/A next = retval->next;
2N/A do {
2N/A sasl_FREE(retval);
2N/A retval = next;
2N/A next = retval->next;
2N/A } while(next);
2N/A return NULL;
2N/A }
2N/A
2N/A next->d = listptr->plug->mech_name;
2N/A
2N/A if(!retval) {
2N/A next->next = NULL;
2N/A retval = next;
2N/A } else {
2N/A next->next = retval;
2N/A retval = next;
2N/A }
2N/A }
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A#define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
2N/Astatic int is_mech(const char *t, const char *m)
2N/A{
2N/A int sl = strlen(m);
2N/A return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
2N/A}
2N/A
2N/A/* returns OK if it's valid */
2N/Astatic int _sasl_checkpass(sasl_conn_t *conn,
2N/A const char *user,
2N/A unsigned userlen __attribute__((unused)),
2N/A const char *pass,
2N/A unsigned passlen __attribute__((unused)))
2N/A{
2N/A sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
2N/A int result;
2N/A sasl_getopt_t *getopt;
2N/A sasl_server_userdb_checkpass_t *checkpass_cb;
2N/A void *context;
2N/A const char *mlist = NULL, *mech = NULL;
2N/A struct sasl_verify_password_s *v;
2N/A const char *service = conn->service;
2N/A
2N/A /* call userdb callback function, if available */
2N/A result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
2N/A &checkpass_cb, &context);
2N/A if(result == SASL_OK && checkpass_cb) {
2N/A result = checkpass_cb(conn, context, user, pass, strlen(pass),
2N/A s_conn->sparams->propctx);
2N/A if(result == SASL_OK)
2N/A return SASL_OK;
2N/A }
2N/A
2N/A /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
2N/A if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
2N/A == SASL_OK) {
2N/A getopt(context, NULL, "pwcheck_method", &mlist, NULL);
2N/A }
2N/A
2N/A if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
2N/A
2N/A result = SASL_NOMECH;
2N/A
2N/A mech = mlist;
2N/A while (*mech && result != SASL_OK) {
2N/A for (v = _sasl_verify_password; v->name; v++) {
2N/A if(is_mech(mech, v->name)) {
2N/A result = v->verify(conn, user, pass, service,
2N/A s_conn->user_realm);
2N/A break;
2N/A }
2N/A }
2N/A if (result != SASL_OK) {
2N/A /* skip to next mech in list */
2N/A while (*mech && !isspace((int) *mech)) mech++;
2N/A while (*mech && isspace((int) *mech)) mech++;
2N/A }
2N/A }
2N/A
2N/A if (result == SASL_NOMECH) {
2N/A /* no mechanism available ?!? */
2N/A _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
2N/A }
2N/A
2N/A if (result != SASL_OK)
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sasl_seterror(conn, SASL_NOLOG, gettext("checkpass failed"));
2N/A#else
2N/A sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A
2N/A RETURN(conn, result);
2N/A}
2N/A
2N/A/* check if a plaintext password is valid
2N/A * if user is NULL, check if plaintext passwords are enabled
2N/A * inputs:
2N/A * user -- user to query in current user_domain
2N/A * userlen -- length of username, 0 = strlen(user)
2N/A * pass -- plaintext password to check
2N/A * passlen -- length of password, 0 = strlen(pass)
2N/A * returns
2N/A * SASL_OK -- success
2N/A * SASL_NOMECH -- mechanism not supported
2N/A * SASL_NOVERIFY -- user found, but no verifier
2N/A * SASL_NOUSER -- user not found
2N/A */
2N/Aint sasl_checkpass(sasl_conn_t *conn,
2N/A const char *user,
2N/A#ifdef _SUN_SDK_
2N/A unsigned userlen,
2N/A#else /* _SUN_SDK_ */
2N/A unsigned userlen __attribute__((unused)),
2N/A#endif /* _SUN_SDK_ */
2N/A const char *pass,
2N/A unsigned passlen)
2N/A{
2N/A int result;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx =
2N/A (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
2N/A
2N/A if (gctx->sasl_server_active==0) return SASL_NOTINIT;
2N/A
2N/A /* A NULL user means the caller is checking if plaintext authentication
2N/A * is enabled. But if no connection context is supplied, we have no
2N/A * appropriate policy to check against. So for consistant global
2N/A * behavior we always say plaintext is enabled in this case.
2N/A */
2N/A if (!user && !conn) return SASL_OK;
2N/A
2N/A if (!conn) return SASL_BADPARAM;
2N/A
2N/A /* Check connection security policy to see if plaintext password
2N/A * authentication is permitted.
2N/A *
2N/A * XXX TODO FIXME:
2N/A * This should call mech_permitted with the PLAIN mechanism,
2N/A * since all plaintext mechanisms should fall under the same
2N/A * security policy guidelines. But to keep code changes and
2N/A * risk to a minimum at this juncture, we do the minimal
2N/A * security strength and plaintext policy checks which are
2N/A * most likely to be deployed and useful in the field.
2N/A */
2N/A if (conn->props.min_ssf > conn->external.ssf)
2N/A RETURN(conn, SASL_TOOWEAK);
2N/A if ((conn->props.security_flags & SASL_SEC_NOPLAINTEXT) != 0
2N/A && conn->external.ssf == 0)
2N/A RETURN(conn, SASL_ENCRYPT);
2N/A
2N/A if (!user)
2N/A return SASL_OK;
2N/A#else
2N/A if (_sasl_server_active==0) return SASL_NOTINIT;
2N/A
2N/A /* check if it's just a query if we are enabled */
2N/A if (!user)
2N/A return SASL_OK;
2N/A
2N/A if (!conn) return SASL_BADPARAM;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* check params */
2N/A if (pass == NULL)
2N/A PARAMERROR(conn);
2N/A
2N/A /* canonicalize the username */
2N/A result = _sasl_canon_user(conn, user, 0,
2N/A SASL_CU_AUTHID | SASL_CU_AUTHZID,
2N/A &(conn->oparams));
2N/A if(result != SASL_OK) RETURN(conn, result);
2N/A user = conn->oparams.user;
2N/A
2N/A /* Check the password */
2N/A result = _sasl_checkpass(conn, user, strlen(user), pass, strlen(pass));
2N/A
2N/A#ifdef _SUN_SDK_
2N/A if (result == SASL_OK) {
2N/A result = do_authorization((sasl_server_conn_t *) conn);
2N/A }
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A if (result == SASL_OK)
2N/A result = _sasl_transition(conn, pass, passlen);
2N/A
2N/A RETURN(conn,result);
2N/A}
2N/A
2N/A/* check if a user exists on server
2N/A * conn -- connection context (may be NULL, used to hold last error)
2N/A * service -- registered name of the service using SASL (e.g. "imap")
2N/A * user_realm -- permits multiple user realms on server, NULL = default
2N/A * user -- NUL terminated user name
2N/A *
2N/A * returns:
2N/A * SASL_OK -- success
2N/A * SASL_DISABLED -- account disabled [FIXME: currently not detected]
2N/A * SASL_NOUSER -- user not found
2N/A * SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
2N/A * SASL_NOMECH -- no mechanisms enabled
2N/A */
2N/Aint sasl_user_exists(sasl_conn_t *conn,
2N/A const char *service,
2N/A const char *user_realm,
2N/A const char *user)
2N/A{
2N/A int result=SASL_NOMECH;
2N/A const char *mlist = NULL, *mech = NULL;
2N/A void *context;
2N/A sasl_getopt_t *getopt;
2N/A struct sasl_verify_password_s *v;
2N/A
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx =
2N/A (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
2N/A
2N/A /* check params */
2N/A if (gctx->sasl_server_active==0) return SASL_NOTINIT;
2N/A#else
2N/A /* check params */
2N/A if (_sasl_server_active==0) return SASL_NOTINIT;
2N/A#endif /* _SUN_SDK_ */
2N/A if (!conn) return SASL_BADPARAM;
2N/A if (!user || conn->type != SASL_CONN_SERVER)
2N/A PARAMERROR(conn);
2N/A
2N/A if(!service) service = conn->service;
2N/A
2N/A /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
2N/A if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
2N/A == SASL_OK) {
2N/A getopt(context, NULL, "pwcheck_method", &mlist, NULL);
2N/A }
2N/A
2N/A if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
2N/A
2N/A result = SASL_NOMECH;
2N/A
2N/A mech = mlist;
2N/A while (*mech && result != SASL_OK) {
2N/A for (v = _sasl_verify_password; v->name; v++) {
2N/A if(is_mech(mech, v->name)) {
2N/A result = v->verify(conn, user, NULL, service, user_realm);
2N/A break;
2N/A }
2N/A }
2N/A if (result != SASL_OK) {
2N/A /* skip to next mech in list */
2N/A while (*mech && !isspace((int) *mech)) mech++;
2N/A while (*mech && isspace((int) *mech)) mech++;
2N/A }
2N/A }
2N/A
2N/A /* Screen out the SASL_BADPARAM response
2N/A * we'll get from not giving a password */
2N/A if(result == SASL_BADPARAM) {
2N/A result = SASL_OK;
2N/A }
2N/A
2N/A if (result == SASL_NOMECH) {
2N/A /* no mechanism available ?!? */
2N/A _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
2N/A#ifndef _SUN_SDK_
2N/A sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
2N/A#endif /* !_SUN_SDK_ */
2N/A }
2N/A
2N/A RETURN(conn, result);
2N/A}
2N/A
2N/A/* check if an apop exchange is valid
2N/A * (note this is an optional part of the SASL API)
2N/A * if challenge is NULL, just check if APOP is enabled
2N/A * inputs:
2N/A * challenge -- challenge which was sent to client
2N/A * challen -- length of challenge, 0 = strlen(challenge)
2N/A * response -- client response, "<user> <digest>" (RFC 1939)
2N/A * resplen -- length of response, 0 = strlen(response)
2N/A * returns
2N/A * SASL_OK -- success
2N/A * SASL_BADAUTH -- authentication failed
2N/A * SASL_BADPARAM -- missing challenge
2N/A * SASL_BADPROT -- protocol error (e.g., response in wrong format)
2N/A * SASL_NOVERIFY -- user found, but no verifier
2N/A * SASL_NOMECH -- mechanism not supported
2N/A * SASL_NOUSER -- user not found
2N/A */
2N/Aint sasl_checkapop(sasl_conn_t *conn,
2N/A#ifdef DO_SASL_CHECKAPOP
2N/A const char *challenge,
2N/A unsigned challen __attribute__((unused)),
2N/A const char *response,
2N/A unsigned resplen __attribute__((unused)))
2N/A#else
2N/A const char *challenge __attribute__((unused)),
2N/A unsigned challen __attribute__((unused)),
2N/A const char *response __attribute__((unused)),
2N/A unsigned resplen __attribute__((unused)))
2N/A#endif
2N/A{
2N/A#ifdef DO_SASL_CHECKAPOP
2N/A sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
2N/A char *user, *user_end;
2N/A const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
2N/A size_t user_len;
2N/A int result;
2N/A#ifdef _SUN_SDK_
2N/A _sasl_global_context_t *gctx =
2N/A (conn == NULL) ? _sasl_gbl_ctx() : conn->gctx;
2N/A
2N/A if (gctx->sasl_server_active==0)
2N/A return SASL_NOTINIT;
2N/A#else
2N/A if (_sasl_server_active==0)
2N/A return SASL_NOTINIT;
2N/A#endif /* _SUN_SDK_ */
2N/A
2N/A /* check if it's just a query if we are enabled */
2N/A if(!challenge)
2N/A return SASL_OK;
2N/A
2N/A /* check params */
2N/A if (!conn) return SASL_BADPARAM;
2N/A if (!response)
2N/A PARAMERROR(conn);
2N/A
2N/A /* Parse out username and digest.
2N/A *
2N/A * Per RFC 1939, response must be "<user> <digest>", where
2N/A * <digest> is a 16-octet value which is sent in hexadecimal
2N/A * format, using lower-case ASCII characters.
2N/A */
2N/A user_end = strrchr(response, ' ');
2N/A if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32)
2N/A {
2N/A#ifdef _INTEGRATED_SOLARIS_
2N/A sasl_seterror(conn, 0, gettext("Bad Digest"));
2N/A#else
2N/A sasl_seterror(conn, 0, "Bad Digest");
2N/A#endif /* _INTEGRATED_SOLARIS_ */
2N/A RETURN(conn,SASL_BADPROT);
2N/A }
2N/A
2N/A user_len = (size_t)(user_end - response);
2N/A user = sasl_ALLOC(user_len + 1);
2N/A memcpy(user, response, user_len);
2N/A user[user_len] = '\0';
2N/A
2N/A result = prop_request(s_conn->sparams->propctx, password_request);
2N/A if(result != SASL_OK)
2N/A {
2N/A sasl_FREE(user);
2N/A RETURN(conn, result);
2N/A }
2N/A
2N/A /* Cannonify it */
2N/A result = _sasl_canon_user(conn, user, user_len,
2N/A SASL_CU_AUTHID | SASL_CU_AUTHZID,
2N/A &(conn->oparams));
2N/A sasl_FREE(user);
2N/A
2N/A if(result != SASL_OK) RETURN(conn, result);
2N/A
2N/A /* Do APOP verification */
2N/A result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
2N/A challenge, user_end + 1, s_conn->user_realm);
2N/A
2N/A /* If verification failed, we don't want to encourage getprop to work */
2N/A if(result != SASL_OK) {
2N/A conn->oparams.user = NULL;
2N/A conn->oparams.authid = NULL;
2N/A }
2N/A
2N/A RETURN(conn, result);
2N/A#else /* sasl_checkapop was disabled at compile time */
2N/A sasl_seterror(conn, SASL_NOLOG,
2N/A "sasl_checkapop called, but was disabled at compile time");
2N/A RETURN(conn, SASL_NOMECH);
2N/A#endif /* DO_SASL_CHECKAPOP */
2N/A}
2N/A