diff -durN cyrus-sasl-2.1.10.orig/acconfig.h cyrus-sasl-2.1.10/acconfig.h --- cyrus-sasl-2.1.10.orig/acconfig.h Tue Sep 10 19:17:32 2002 +++ cyrus-sasl-2.1.10/acconfig.h Thu Jan 9 11:42:29 2003 @@ -78,6 +78,9 @@ /* This is where plugins will live at runtime */ #undef PLUGINDIR +/* This is where config files will live at runtime */ +#undef CONFIGDIR + /* Make autoheader happy */ #undef WITH_SYMBOL_UNDERSCORE @@ -247,6 +250,7 @@ #endif #define SASL_PATH_ENV_VAR "SASL_PATH" +#define SASL_CONF_PATH_ENV_VAR "SASL_CONF_PATH" #include #include diff -durN cyrus-sasl-2.1.10.orig/configure.in cyrus-sasl-2.1.10/configure.in --- cyrus-sasl-2.1.10.orig/configure.in Fri Dec 6 17:23:56 2002 +++ cyrus-sasl-2.1.10/configure.in Thu Jan 9 11:42:29 2003 @@ -664,6 +664,13 @@ AC_DEFINE_UNQUOTED(PLUGINDIR, "$plugindir") AC_SUBST(plugindir) +AC_ARG_WITH(configdir, [ --with-configdir=DIR set the directory where config files will + be found [/etc/sasl] ], + configdir=$withval, + configdir=/etc/sasl) +AC_DEFINE_UNQUOTED(CONFIGDIR, "$configdir") +AC_SUBST(configdir) + dnl look for rc4 libraries. we accept the CMU one or one from openSSL AC_ARG_WITH(rc4, [ --with-rc4 use internal rc4 routines [yes] ], with_rc4=$withval, diff -durN cyrus-sasl-2.1.10.orig/include/sasl.h cyrus-sasl-2.1.10/include/sasl.h --- cyrus-sasl-2.1.10.orig/include/sasl.h Fri Dec 6 17:23:59 2002 +++ cyrus-sasl-2.1.10/include/sasl.h Thu Jan 9 11:44:00 2003 @@ -467,6 +467,24 @@ const char *file, sasl_verify_type_t type); #define SASL_CB_VERIFYFILE 4 +/* getconfpath callback -- this allows applications to specify the + * colon-separated path to search for config files (by default, + * taken from the SASL_CONF_PATH environment variable). + * inputs: + * context -- getconfpath context from the callback record + * outputs: + * path -- colon seperated path (allocated on the heap; the + * library will free it using the sasl_free_t * + * passed to sasl_set_callback, or the standard free() + * library call). + * returns: + * SASL_OK -- no error + * SASL_FAIL -- error + */ +typedef int sasl_getconfpath_t(void * context, + char ** path); + +#define SASL_CB_GETCONFPATH (5) /* client/user interaction callbacks: */ diff -durN cyrus-sasl-2.1.10.orig/lib/common.c cyrus-sasl-2.1.10/lib/common.c --- cyrus-sasl-2.1.10.orig/lib/common.c Thu Dec 5 15:00:38 2002 +++ cyrus-sasl-2.1.10/lib/common.c Thu Jan 9 11:42:29 2003 @@ -1040,6 +1040,20 @@ } static int +_sasl_getconfpath(void *context __attribute__((unused)), + char ** path_dest) +{ + char *path; + + if (! path_dest) + return SASL_BADPARAM; + path = getenv(SASL_CONF_PATH_ENV_VAR); + if (! path) + path = CONFIGDIR; + return _sasl_strdup(path, path_dest, NULL); +} + +static int _sasl_verifyfile(void *context __attribute__((unused)), char *file __attribute__((unused)), int type __attribute__((unused))) @@ -1147,6 +1161,10 @@ *pproc = (int (*)()) &_sasl_getpath; *pcontext = NULL; return SASL_OK; + case SASL_CB_GETCONFPATH: + *pproc = (int (*)()) &_sasl_getconfpath; + *pcontext = NULL; + return SASL_OK; case SASL_CB_AUTHNAME: *pproc = (int (*)()) &_sasl_getsimple; *pcontext = conn; @@ -1475,6 +1493,30 @@ } const sasl_callback_t * +_sasl_find_getconfpath_callback(const sasl_callback_t *callbacks) +{ + static const sasl_callback_t default_getconfpath_cb = { + SASL_CB_GETCONFPATH, + &_sasl_getconfpath, + NULL + }; + + if (callbacks) + while (callbacks->id != SASL_CB_LIST_END) + { + if (callbacks->id == SASL_CB_GETCONFPATH) + { + return callbacks; + } else { + ++callbacks; + } + } + + return &default_getconfpath_cb; +} + + +const sasl_callback_t * _sasl_find_verifyfile_callback(const sasl_callback_t *callbacks) { static const sasl_callback_t default_verifyfile_cb = { diff -durN cyrus-sasl-2.1.10.orig/lib/common.c.orig cyrus-sasl-2.1.10/lib/common.c.orig --- cyrus-sasl-2.1.10.orig/lib/common.c.orig Thu Jan 1 01:00:00 1970 +++ cyrus-sasl-2.1.10/lib/common.c.orig Thu Dec 5 15:00:38 2002 @@ -0,0 +1,1921 @@ +/* common.c - Functions that are common to server and clinet + * Rob Siemborski + * Tim Martin + * $Id$ + */ +/* + * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_SYSLOG +#include +#endif +#include +#include + +#include +#include +#include +#include "saslint.h" + +#ifdef WIN32 +/* need to handle the fact that errno has been defined as a function + in a dll, not an extern int */ +# ifdef errno +# undef errno +# endif /* errno */ +#endif /* WIN32 */ +#ifdef HAVE_UNISTD_H +#include +#endif + +static int _sasl_getpath(void *context __attribute__((unused)), const char **path); + +static const char build_ident[] = "$Build: libsasl " PACKAGE "-" VERSION " $"; + +/* It turns out to be conveinent to have a shared sasl_utils_t */ +LIBSASL_VAR const sasl_utils_t *sasl_global_utils = NULL; + +/* Should be a null-terminated array that lists the available mechanisms */ +static char **global_mech_list = NULL; + +void *free_mutex = NULL; + +void (*_sasl_client_cleanup_hook)(void) = NULL; +void (*_sasl_server_cleanup_hook)(void) = NULL; +int (*_sasl_client_idle_hook)(sasl_conn_t *conn) = NULL; +int (*_sasl_server_idle_hook)(sasl_conn_t *conn) = NULL; + +sasl_allocation_utils_t _sasl_allocation_utils={ + (sasl_malloc_t *) &malloc, + (sasl_calloc_t *) &calloc, + (sasl_realloc_t *) &realloc, + (sasl_free_t *) &free +}; + +/* Intenal mutex functions do as little as possible (no thread protection) */ +static void *sasl_mutex_alloc(void) +{ + return (void *)0x1; +} + +static int sasl_mutex_lock(void *mutex __attribute__((unused))) +{ + return SASL_OK; +} + +static int sasl_mutex_unlock(void *mutex __attribute__((unused))) +{ + return SASL_OK; +} + +static void sasl_mutex_free(void *mutex __attribute__((unused))) +{ + return; +} + +sasl_mutex_utils_t _sasl_mutex_utils={ + &sasl_mutex_alloc, + &sasl_mutex_lock, + &sasl_mutex_unlock, + &sasl_mutex_free +}; + +void sasl_set_mutex(sasl_mutex_alloc_t *n, sasl_mutex_lock_t *l, + sasl_mutex_unlock_t *u, sasl_mutex_free_t *d) +{ + _sasl_mutex_utils.alloc=n; + _sasl_mutex_utils.lock=l; + _sasl_mutex_utils.unlock=u; + _sasl_mutex_utils.free=d; +} + +/* copy a string to malloced memory */ +int _sasl_strdup(const char *in, char **out, size_t *outlen) +{ + size_t len = strlen(in); + if (outlen) *outlen = len; + *out=sasl_ALLOC(len + 1); + if (! *out) return SASL_NOMEM; + strcpy((char *) *out, in); + return SASL_OK; +} + +/* adds a string to the buffer; reallocing if need be */ +int _sasl_add_string(char **out, size_t *alloclen, + size_t *outlen, const char *add) +{ + size_t addlen; + + if (add==NULL) add = "(null)"; + + addlen=strlen(add); /* only compute once */ + if (_buf_alloc(out, alloclen, (*outlen)+addlen)!=SASL_OK) + return SASL_NOMEM; + + strncpy(*out + *outlen, add, addlen); + *outlen += addlen; + + return SASL_OK; +} + +/* return the version of the cyrus sasl library as compiled, + * using 32 bits: high byte is major version, second byte is minor version, + * low 16 bits are step # */ +void sasl_version(const char **implementation, int *version) +{ + const char *implementation_string = "Cyrus SASL"; + if(implementation) *implementation = implementation_string; + if(version) *version = (SASL_VERSION_MAJOR << 24) | + (SASL_VERSION_MINOR << 16) | + (SASL_VERSION_STEP); +} + +/* security-encode a regular string. Mostly a wrapper for sasl_encodev */ +/* output is only valid until next call to sasl_encode or sasl_encodev */ +int sasl_encode(sasl_conn_t *conn, const char *input, + unsigned inputlen, + const char **output, unsigned *outputlen) +{ + int result; + struct iovec tmp; + + if(!conn) return SASL_BADPARAM; + if(!input || !inputlen || !output || !outputlen) + PARAMERROR(conn); + + /* maxoutbuf checking is done in sasl_encodev */ + + /* Note: We are casting a const pointer here, but it's okay + * because we believe people downstream of us are well-behaved, and the + * alternative is an absolute mess, performance-wise. */ + tmp.iov_base = (void *)input; + tmp.iov_len = inputlen; + + result = sasl_encodev(conn, &tmp, 1, output, outputlen); + + RETURN(conn, result); +} + +/* security-encode an iovec */ +/* output is only valid until next call to sasl_encode or sasl_encodev */ +int sasl_encodev(sasl_conn_t *conn, + const struct iovec *invec, unsigned numiov, + const char **output, unsigned *outputlen) +{ + int result; + unsigned i; + size_t total_size = 0; + + if (!conn) return SASL_BADPARAM; + if (! invec || ! output || ! outputlen || numiov < 1) + PARAMERROR(conn); + + if(!conn->props.maxbufsize) { + sasl_seterror(conn, 0, + "called sasl_encode[v] with application that does not support security layers"); + return SASL_TOOWEAK; + } + + /* This might be better to check on a per-plugin basis, but I think + * it's cleaner and more effective here. It also encourages plugins + * to be honest about what they accept */ + + for(i=0; i conn->oparams.maxoutbuf) + PARAMERROR(conn); + + if(conn->oparams.encode == NULL) { + result = _iovec_to_buf(invec, numiov, &conn->encode_buf); + if(result != SASL_OK) INTERROR(conn, result); + + *output = conn->encode_buf->data; + *outputlen = conn->encode_buf->curlen; + + } else { + result = conn->oparams.encode(conn->context, invec, numiov, + output, outputlen); + } + + RETURN(conn, result); +} + +/* output is only valid until next call to sasl_decode */ +int sasl_decode(sasl_conn_t *conn, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen) +{ + int result; + + if(!conn) return SASL_BADPARAM; + if(!input || !output || !outputlen) + PARAMERROR(conn); + + if(!conn->props.maxbufsize) { + sasl_seterror(conn, 0, + "called sasl_decode with application that does not support security layers"); + RETURN(conn, SASL_TOOWEAK); + } + + if(conn->oparams.decode == NULL) + { + /* Since we know how long the output is maximally, we can + * just allocate it to begin with, and never need another + * allocation! */ + + /* However, if they pass us more than they actually can take, + * we cannot help them... */ + if(inputlen > conn->props.maxbufsize) { + sasl_seterror(conn, 0, + "input too large for default sasl_decode"); + RETURN(conn,SASL_BUFOVER); + } + + if(!conn->decode_buf) + conn->decode_buf = sasl_ALLOC(conn->props.maxbufsize + 1); + if(!conn->decode_buf) + MEMERROR(conn); + + memcpy(conn->decode_buf, input, inputlen); + conn->decode_buf[inputlen] = '\0'; + *output = conn->decode_buf; + *outputlen = inputlen; + + return SASL_OK; + } else { + result = conn->oparams.decode(conn->context, input, inputlen, + output, outputlen); + + /* NULL an empty buffer (for misbehaved applications) */ + if (*outputlen == 0) *output = NULL; + + RETURN(conn, result); + } + + INTERROR(conn, SASL_FAIL); +} + + +void +sasl_set_alloc(sasl_malloc_t *m, + sasl_calloc_t *c, + sasl_realloc_t *r, + sasl_free_t *f) +{ + _sasl_allocation_utils.malloc=m; + _sasl_allocation_utils.calloc=c; + _sasl_allocation_utils.realloc=r; + _sasl_allocation_utils.free=f; +} + +void sasl_done(void) +{ + if (_sasl_server_cleanup_hook) + _sasl_server_cleanup_hook(); + + if (_sasl_client_cleanup_hook) + _sasl_client_cleanup_hook(); + + _sasl_canonuser_free(); + _sasl_done_with_plugins(); + + sasl_MUTEX_FREE(free_mutex); + free_mutex = NULL; + + _sasl_free_utils(&sasl_global_utils); + + sasl_FREE(global_mech_list); + global_mech_list = NULL; + + /* in case of another init/done */ + _sasl_server_cleanup_hook = NULL; + _sasl_client_cleanup_hook = NULL; + + _sasl_client_idle_hook = NULL; + _sasl_server_idle_hook = NULL; +} + +/* fills in the base sasl_conn_t info */ +int _sasl_conn_init(sasl_conn_t *conn, + const char *service, + unsigned int flags, + enum Sasl_conn_type type, + int (*idle_hook)(sasl_conn_t *conn), + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + const sasl_global_callbacks_t *global_callbacks) { + int result = SASL_OK; + + conn->type = type; + + result = _sasl_strdup(service, &conn->service, NULL); + if (result != SASL_OK) + MEMERROR(conn); + + memset(&conn->oparams, 0, sizeof(sasl_out_params_t)); + memset(&conn->external, 0, sizeof(_sasl_external_properties_t)); + + conn->flags = flags; + + result = sasl_setprop(conn, SASL_IPLOCALPORT, iplocalport); + if(result != SASL_OK) + RETURN(conn, result); + + result = sasl_setprop(conn, SASL_IPREMOTEPORT, ipremoteport); + if(result != SASL_OK) + RETURN(conn, result); + + conn->encode_buf = NULL; + conn->context = NULL; + conn->secret = NULL; + conn->idle_hook = idle_hook; + conn->callbacks = callbacks; + conn->global_callbacks = global_callbacks; + + memset(&conn->props, 0, sizeof(conn->props)); + + /* Start this buffer out as an empty string */ + conn->error_code = SASL_OK; + conn->errdetail_buf = conn->error_buf = NULL; + conn->errdetail_buf_len = conn->error_buf_len = 150; + + result = _buf_alloc(&conn->error_buf, &conn->error_buf_len, 150); + if(result != SASL_OK) MEMERROR(conn); + result = _buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, 150); + if(result != SASL_OK) MEMERROR(conn); + + conn->error_buf[0] = '\0'; + conn->errdetail_buf[0] = '\0'; + + conn->decode_buf = NULL; + + if(serverFQDN) { + result = _sasl_strdup(serverFQDN, &conn->serverFQDN, NULL); + } else if (conn->type == SASL_CONN_SERVER) { + /* We can fake it because we *are* the server */ + char name[MAXHOSTNAMELEN]; + memset(name, 0, sizeof(name)); + gethostname(name, MAXHOSTNAMELEN); + + result = _sasl_strdup(name, &conn->serverFQDN, NULL); + } else { + conn->serverFQDN = NULL; + } + + + if(result != SASL_OK) MEMERROR( conn ); + + RETURN(conn, SASL_OK); +} + +int _sasl_common_init(void) +{ + int result; + + /* Setup the global utilities */ + if(!sasl_global_utils) { + sasl_global_utils = _sasl_alloc_utils(NULL, NULL); + if(sasl_global_utils == NULL) return SASL_NOMEM; + } + + /* Init the canon_user plugin */ + result = sasl_canonuser_add_plugin("INTERNAL", internal_canonuser_init); + if(result != SASL_OK) return result; + + if (!free_mutex) + free_mutex = sasl_MUTEX_ALLOC(); + if (!free_mutex) return SASL_FAIL; + + return SASL_OK; +} + +/* dispose connection state, sets it to NULL + * checks for pointer to NULL + */ +void sasl_dispose(sasl_conn_t **pconn) +{ + int result; + + if (! pconn) return; + if (! *pconn) return; + + /* serialize disposes. this is necessary because we can't + dispose of conn->mutex if someone else is locked on it */ + result = sasl_MUTEX_LOCK(free_mutex); + if (result!=SASL_OK) return; + + /* *pconn might have become NULL by now */ + if (! (*pconn)) return; + + (*pconn)->destroy_conn(*pconn); + sasl_FREE(*pconn); + *pconn=NULL; + + sasl_MUTEX_UNLOCK(free_mutex); +} + +void _sasl_conn_dispose(sasl_conn_t *conn) { + if (conn->serverFQDN) + sasl_FREE(conn->serverFQDN); + + if (conn->external.auth_id) + sasl_FREE(conn->external.auth_id); + + if(conn->encode_buf) { + if(conn->encode_buf->data) sasl_FREE(conn->encode_buf->data); + sasl_FREE(conn->encode_buf); + } + + if(conn->error_buf) + sasl_FREE(conn->error_buf); + + if(conn->errdetail_buf) + sasl_FREE(conn->errdetail_buf); + + if(conn->decode_buf) + sasl_FREE(conn->decode_buf); + + if(conn->mechlist_buf) + sasl_FREE(conn->mechlist_buf); + + if(conn->service) + sasl_FREE(conn->service); + + /* oparams sub-members should be freed by the plugin, in so much + * as they were allocated by the plugin */ +} + + +/* get property from SASL connection state + * propnum -- property number + * pvalue -- pointer to value + * returns: + * SASL_OK -- no error + * SASL_NOTDONE -- property not available yet + * SASL_BADPARAM -- bad property number + */ +int sasl_getprop(sasl_conn_t *conn, int propnum, const void **pvalue) +{ + int result = SASL_OK; + sasl_getopt_t *getopt; + void *context; + + if (! conn) return SASL_BADPARAM; + if (! pvalue) PARAMERROR(conn); + + switch(propnum) + { + case SASL_SSF: + *(sasl_ssf_t **)pvalue= &conn->oparams.mech_ssf; + break; + case SASL_MAXOUTBUF: + *(unsigned **)pvalue = &conn->oparams.maxoutbuf; + break; + case SASL_GETOPTCTX: + result = _sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context); + if(result != SASL_OK) break; + + *(void **)pvalue = context; + break; + case SASL_CALLBACK: + *(const sasl_callback_t **)pvalue = conn->callbacks; + break; + case SASL_IPLOCALPORT: + if(conn->got_ip_local) + *(const char **)pvalue = conn->iplocalport; + else { + *(const char **)pvalue = NULL; + result = SASL_NOTDONE; + } + break; + case SASL_IPREMOTEPORT: + if(conn->got_ip_remote) + *(const char **)pvalue = conn->ipremoteport; + else { + *(const char **)pvalue = NULL; + result = SASL_NOTDONE; + } + break; + case SASL_USERNAME: + if(! conn->oparams.user) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.user; + break; + case SASL_AUTHUSER: + if(! conn->oparams.authid) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.authid; + break; + case SASL_SERVERFQDN: + *((const char **)pvalue) = conn->serverFQDN; + break; + case SASL_DEFUSERREALM: + if(conn->type != SASL_CONN_SERVER) result = SASL_BADPROT; + else + *((const char **)pvalue) = ((sasl_server_conn_t *)conn)->user_realm; + break; + case SASL_SERVICE: + *((const char **)pvalue) = conn->service; + break; + case SASL_AUTHSOURCE: /* name of plugin (not name of mech) */ + if(conn->type == SASL_CONN_CLIENT) { + if(!((sasl_client_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_client_conn_t *)conn)->mech->plugname; + } else if (conn->type == SASL_CONN_SERVER) { + if(!((sasl_server_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_server_conn_t *)conn)->mech->plugname; + } else { + result = SASL_BADPARAM; + } + break; + case SASL_MECHNAME: /* name of mech */ + if(conn->type == SASL_CONN_CLIENT) { + if(!((sasl_client_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_client_conn_t *)conn)->mech->plug->mech_name; + } else if (conn->type == SASL_CONN_SERVER) { + if(!((sasl_server_conn_t *)conn)->mech) { + result = SASL_NOTDONE; + break; + } + *((const char **)pvalue) = + ((sasl_server_conn_t *)conn)->mech->plug->mech_name; + } else { + result = SASL_BADPARAM; + } + + if(!(*pvalue) && result == SASL_OK) result = SASL_NOTDONE; + break; + case SASL_PLUGERR: + *((const char **)pvalue) = conn->error_buf; + break; + case SASL_SSF_EXTERNAL: + *((const sasl_ssf_t **)pvalue) = &conn->external.ssf; + break; + case SASL_AUTH_EXTERNAL: + *((const char **)pvalue) = conn->external.auth_id; + break; + case SASL_SEC_PROPS: + *((const sasl_security_properties_t **)pvalue) = &conn->props; + break; + default: + result = SASL_BADPARAM; + } + + if(result == SASL_BADPARAM) { + PARAMERROR(conn); + } else if(result == SASL_NOTDONE) { + sasl_seterror(conn, SASL_NOLOG, + "Information that was requested is not yet available."); + RETURN(conn, result); + } else if(result != SASL_OK) { + INTERROR(conn, result); + } else + RETURN(conn, result); +} + +/* set property in SASL connection state + * returns: + * SASL_OK -- value set + * SASL_BADPARAM -- invalid property or value + */ +int sasl_setprop(sasl_conn_t *conn, int propnum, const void *value) +{ + int result = SASL_OK; + char *str; + + /* make sure the sasl context is valid */ + if (!conn) + return SASL_BADPARAM; + + switch(propnum) + { + case SASL_SSF_EXTERNAL: + conn->external.ssf = *((sasl_ssf_t *)value); + break; + + case SASL_AUTH_EXTERNAL: + if(value && strlen(value)) { + result = _sasl_strdup(value, &str, NULL); + if(result != SASL_OK) MEMERROR(conn); + } else { + str = NULL; + } + + if(conn->external.auth_id) + sasl_FREE(conn->external.auth_id); + + conn->external.auth_id = str; + + break; + + case SASL_DEFUSERREALM: + if(conn->type != SASL_CONN_SERVER) { + sasl_seterror(conn, 0, "Tried to set realm on non-server connection"); + result = SASL_BADPROT; + break; + } + + if(value && strlen(value)) { + result = _sasl_strdup(value, &str, NULL); + if(result != SASL_OK) MEMERROR(conn); + } else { + PARAMERROR(conn); + } + + if(((sasl_server_conn_t *)conn)->user_realm) + sasl_FREE(((sasl_server_conn_t *)conn)->user_realm); + + ((sasl_server_conn_t *)conn)->user_realm = str; + ((sasl_server_conn_t *)conn)->sparams->user_realm = str; + + break; + + case SASL_SEC_PROPS: + { + sasl_security_properties_t *props = (sasl_security_properties_t *)value; + + if(props->maxbufsize == 0 && props->min_ssf != 0) { + sasl_seterror(conn, 0, + "Attempt to disable security layers (maxoutbuf == 0) with min_ssf > 0"); + RETURN(conn, SASL_TOOWEAK); + } + + memcpy(&(conn->props), props, + sizeof(sasl_security_properties_t)); + + break; + } + + case SASL_IPREMOTEPORT: + { + const char *ipremoteport = (const char *)value; + if(!value) { + conn->got_ip_remote = 0; + } else if (_sasl_ipfromstring(ipremoteport, NULL, 0) + != SASL_OK) { + sasl_seterror(conn, 0, "Bad IPREMOTEPORT value"); + RETURN(conn, SASL_BADPARAM); + } else { + strcpy(conn->ipremoteport, ipremoteport); + conn->got_ip_remote = 1; + } + + if(conn->got_ip_remote) { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->ipremoteport + = conn->ipremoteport; + ((sasl_client_conn_t *)conn)->cparams->ipremlen = + strlen(conn->ipremoteport); + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->ipremoteport + = conn->ipremoteport; + ((sasl_server_conn_t *)conn)->sparams->ipremlen = + strlen(conn->ipremoteport); + } + } else { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->ipremoteport + = NULL; + ((sasl_client_conn_t *)conn)->cparams->ipremlen = 0; + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->ipremoteport + = NULL; + ((sasl_server_conn_t *)conn)->sparams->ipremlen = 0; + } + } + + break; + } + + case SASL_IPLOCALPORT: + { + const char *iplocalport = (const char *)value; + if(!value) { + conn->got_ip_local = 0; + } else if (_sasl_ipfromstring(iplocalport, NULL, 0) + != SASL_OK) { + sasl_seterror(conn, 0, "Bad IPLOCALPORT value"); + RETURN(conn, SASL_BADPARAM); + } else { + strcpy(conn->iplocalport, iplocalport); + conn->got_ip_local = 1; + } + + if(conn->got_ip_local) { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->iplocalport + = conn->iplocalport; + ((sasl_client_conn_t *)conn)->cparams->iploclen + = strlen(conn->iplocalport); + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->iplocalport + = conn->iplocalport; + ((sasl_server_conn_t *)conn)->sparams->iploclen + = strlen(conn->iplocalport); + } + } else { + if(conn->type == SASL_CONN_CLIENT) { + ((sasl_client_conn_t *)conn)->cparams->iplocalport + = NULL; + ((sasl_client_conn_t *)conn)->cparams->iploclen = 0; + } else if (conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t *)conn)->sparams->iplocalport + = NULL; + ((sasl_server_conn_t *)conn)->sparams->iploclen = 0; + } + } + break; + } + + default: + sasl_seterror(conn, 0, "Unknown parameter type"); + result = SASL_BADPARAM; + } + + RETURN(conn, result); +} + +/* this is apparently no longer a user function */ +static int sasl_usererr(int saslerr) +{ + /* Hide the difference in a username failure and a password failure */ + if (saslerr == SASL_NOUSER) + return SASL_BADAUTH; + + /* otherwise return the error given; no transform necessary */ + return saslerr; +} + +const char *sasl_errstring(int saslerr, + const char *langlist __attribute__((unused)), + const char **outlang) +{ + if (outlang) *outlang="en-us"; + + switch(saslerr) + { + case SASL_CONTINUE: return "another step is needed in authentication"; + case SASL_OK: return "successful result"; + case SASL_FAIL: return "generic failure"; + case SASL_NOMEM: return "no memory available"; + case SASL_BUFOVER: return "overflowed buffer"; + case SASL_NOMECH: return "no mechanism available"; + case SASL_BADPROT: return "bad protocol / cancel"; + case SASL_NOTDONE: return "can't request info until later in exchange"; + case SASL_BADPARAM: return "invalid parameter supplied"; + case SASL_TRYAGAIN: return "transient failure (e.g., weak key)"; + case SASL_BADMAC: return "integrity check failed"; + case SASL_NOTINIT: return "SASL library not initialized"; + /* -- client only codes -- */ + case SASL_INTERACT: return "needs user interaction"; + case SASL_BADSERV: return "server failed mutual authentication step"; + case SASL_WRONGMECH: return "mechanism doesn't support requested feature"; + /* -- server only codes -- */ + case SASL_BADAUTH: return "authentication failure"; + case SASL_NOAUTHZ: return "authorization failure"; + case SASL_TOOWEAK: return "mechanism too weak for this user"; + case SASL_ENCRYPT: return "encryption needed to use mechanism"; + case SASL_TRANS: return "One time use of a plaintext password will enable requested mechanism for user"; + case SASL_EXPIRED: return "passphrase expired, has to be reset"; + case SASL_DISABLED: return "account disabled"; + case SASL_NOUSER: return "user not found"; + case SASL_BADVERS: return "version mismatch with plug-in"; + case SASL_UNAVAIL: return "remote authentication server unavailable"; + case SASL_NOVERIFY: return "user exists, but no verifier for user"; + case SASL_PWLOCK: return "passphrase locked"; + case SASL_NOCHANGE: return "requested change was not needed"; + case SASL_WEAKPASS: return "passphrase is too weak for security policy"; + case SASL_NOUSERPASS: return "user supplied passwords are not permitted"; + + default: return "undefined error!"; + } + +} + +/* Return the sanitized error detail about the last error that occured for + * a connection */ +const char *sasl_errdetail(sasl_conn_t *conn) +{ + unsigned need_len; + const char *errstr; + char leader[128]; + + if(!conn) return NULL; + + errstr = sasl_errstring(conn->error_code, NULL, NULL); + snprintf(leader,128,"SASL(%d): %s: ", + sasl_usererr(conn->error_code), errstr); + + need_len = strlen(leader) + strlen(conn->error_buf) + 12; + _buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, need_len); + + snprintf(conn->errdetail_buf, need_len, "%s%s", leader, conn->error_buf); + + return conn->errdetail_buf; +} + + +/* Note that this needs the global callbacks, so if you don't give getcallbacks + * a sasl_conn_t, you're going to need to pass it yourself (or else we couldn't + * have client and server at the same time */ +static int _sasl_global_getopt(void *context, + const char *plugin_name, + const char *option, + const char ** result, + unsigned *len) +{ + const sasl_global_callbacks_t * global_callbacks; + const sasl_callback_t *callback; + + global_callbacks = (const sasl_global_callbacks_t *) context; + + if (global_callbacks && global_callbacks->callbacks) { + for (callback = global_callbacks->callbacks; + callback->id != SASL_CB_LIST_END; + callback++) { + if (callback->id == SASL_CB_GETOPT + && (((sasl_getopt_t *)(callback->proc))(callback->context, + plugin_name, + option, + result, + len) + == SASL_OK)) + return SASL_OK; + } + } + + /* look it up in our configuration file */ + *result = sasl_config_getstring(option, NULL); + if (*result != NULL) { + if (len) { *len = strlen(*result); } + return SASL_OK; + } + + return SASL_FAIL; +} + +static int +_sasl_conn_getopt(void *context, + const char *plugin_name, + const char *option, + const char ** result, + unsigned *len) +{ + sasl_conn_t * conn; + const sasl_callback_t *callback; + + if (! context) + return SASL_BADPARAM; + + conn = (sasl_conn_t *) context; + + if (conn->callbacks) + for (callback = conn->callbacks; + callback->id != SASL_CB_LIST_END; + callback++) + if (callback->id == SASL_CB_GETOPT + && (((sasl_getopt_t *)(callback->proc))(callback->context, + plugin_name, + option, + result, + len) + == SASL_OK)) + return SASL_OK; + + /* If we made it here, we didn't find an appropriate callback + * in the connection's callback list, or the callback we did + * find didn't return SASL_OK. So we attempt to use the + * global callback for this connection... */ + return _sasl_global_getopt((void *)conn->global_callbacks, + plugin_name, + option, + result, + len); +} + +#ifdef HAVE_SYSLOG +/* this is the default logging */ +static int _sasl_syslog(void *context __attribute__((unused)), + int priority, + const char *message) +{ + int syslog_priority; + + /* set syslog priority */ + switch(priority) { + case SASL_LOG_NONE: + return SASL_OK; + break; + case SASL_LOG_ERR: + syslog_priority = LOG_ERR; + break; + case SASL_LOG_WARN: + syslog_priority = LOG_WARNING; + break; + case SASL_LOG_NOTE: + case SASL_LOG_FAIL: + syslog_priority = LOG_NOTICE; + break; + case SASL_LOG_PASS: + case SASL_LOG_TRACE: + case SASL_LOG_DEBUG: + default: + syslog_priority = LOG_DEBUG; + break; + } + + /* do the syslog call. do not need to call openlog */ + syslog(syslog_priority | LOG_AUTH, "%s", message); + + return SASL_OK; +} +#endif /* HAVE_SYSLOG */ + +static int +_sasl_getsimple(void *context, + int id, + const char ** result, + size_t *len) +{ + const char *userid; + sasl_conn_t *conn; + + if (! context || ! result) return SASL_BADPARAM; + + conn = (sasl_conn_t *)context; + + switch(id) { + case SASL_CB_AUTHNAME: + userid = getenv("USER"); + if (userid != NULL) { + *result = userid; + if (len) *len = strlen(userid); + return SASL_OK; + } + userid = getenv("USERNAME"); + if (userid != NULL) { + *result = userid; + if (len) *len = strlen(userid); + return SASL_OK; + } +#ifdef WIN32 + /* for win32, try using the GetUserName standard call */ + { + DWORD i; + BOOL rval; + static char sender[128]; + + i = sizeof(sender); + rval = GetUserName(sender, &i); + if ( rval) { /* got a userid */ + *result = sender; + if (len) *len = strlen(sender); + return SASL_OK; + } + } +#endif /* WIN32 */ + return SASL_FAIL; + default: + return SASL_BADPARAM; + } +} + +static int +_sasl_verifyfile(void *context __attribute__((unused)), + char *file __attribute__((unused)), + int type __attribute__((unused))) +{ + /* always say ok */ + return SASL_OK; +} + + +static int +_sasl_proxy_policy(sasl_conn_t *conn, + void *context __attribute__((unused)), + const char *requested_user, unsigned rlen, + const char *auth_identity, unsigned alen, + const char *def_realm __attribute__((unused)), + unsigned urlen __attribute__((unused)), + struct propctx *propctx __attribute__((unused))) +{ + if (!conn) + return SASL_BADPARAM; + + if (!requested_user || *requested_user == '\0') + return SASL_OK; + + if (!auth_identity || !requested_user || rlen != alen || + (memcmp(auth_identity, requested_user, rlen) != 0)) { + sasl_seterror(conn, 0, + "Requested identity not authenticated identity"); + RETURN(conn, SASL_BADAUTH); + } + + return SASL_OK; +} + +int _sasl_getcallback(sasl_conn_t * conn, + unsigned long callbackid, + int (**pproc)(), + void **pcontext) +{ + const sasl_callback_t *callback; + + if (!pproc || !pcontext) + PARAMERROR(conn); + + /* Some callbacks are always provided by the library */ + switch (callbackid) { + case SASL_CB_LIST_END: + /* Nothing ever gets to provide this */ + INTERROR(conn, SASL_FAIL); + case SASL_CB_GETOPT: + if (conn) { + *pproc = &_sasl_conn_getopt; + *pcontext = conn; + } else { + *pproc = &_sasl_global_getopt; + *pcontext = NULL; + } + return SASL_OK; + } + + /* If it's not always provided by the library, see if there's + * a version provided by the application for this connection... */ + if (conn && conn->callbacks) { + for (callback = conn->callbacks; callback->id != SASL_CB_LIST_END; + callback++) { + if (callback->id == callbackid) { + *pproc = callback->proc; + *pcontext = callback->context; + if (callback->proc) { + return SASL_OK; + } else { + return SASL_INTERACT; + } + } + } + } + + /* And, if not for this connection, see if there's one + * for all {server,client} connections... */ + if (conn && conn->global_callbacks && conn->global_callbacks->callbacks) { + for (callback = conn->global_callbacks->callbacks; + callback->id != SASL_CB_LIST_END; + callback++) { + if (callback->id == callbackid) { + *pproc = callback->proc; + *pcontext = callback->context; + if (callback->proc) { + return SASL_OK; + } else { + return SASL_INTERACT; + } + } + } + } + + /* Otherwise, see if the library provides a default callback. */ + switch (callbackid) { +#ifdef HAVE_SYSLOG + case SASL_CB_LOG: + *pproc = (int (*)()) &_sasl_syslog; + *pcontext = NULL; + return SASL_OK; +#endif /* HAVE_SYSLOG */ + case SASL_CB_GETPATH: + *pproc = (int (*)()) &_sasl_getpath; + *pcontext = NULL; + return SASL_OK; + case SASL_CB_AUTHNAME: + *pproc = (int (*)()) &_sasl_getsimple; + *pcontext = conn; + return SASL_OK; + case SASL_CB_VERIFYFILE: + *pproc = & _sasl_verifyfile; + *pcontext = NULL; + return SASL_OK; + case SASL_CB_PROXY_POLICY: + *pproc = (int (*)()) &_sasl_proxy_policy; + *pcontext = NULL; + return SASL_OK; + } + + /* Unable to find a callback... */ + *pproc = NULL; + *pcontext = NULL; + sasl_seterror(conn, SASL_NOLOG, "Unable to find a callback: %d", callbackid); + RETURN(conn,SASL_FAIL); +} + + +/* + * This function is typically called from a plugin. + * It creates a string from the formatting and varargs given + * and calls the logging callback (syslog by default) + * + * %m will parse the value in the next argument as an errno string + * %z will parse the next argument as a SASL error code. + */ + +void +_sasl_log (sasl_conn_t *conn, + int level, + const char *fmt, + ...) +{ + char *out=(char *) sasl_ALLOC(250); + size_t alloclen=100; /* current allocated length */ + size_t outlen=0; /* current length of output buffer */ + size_t formatlen; + size_t pos=0; /* current position in format string */ + int result; + sasl_log_t *log_cb; + void *log_ctx; + + int ival; + char *cval; + va_list ap; /* varargs thing */ + + if(!fmt) goto done; + if(!out) return; + + formatlen = strlen(fmt); + + /* See if we have a logging callback... */ + result = _sasl_getcallback(conn, SASL_CB_LOG, &log_cb, &log_ctx); + if (result == SASL_OK && ! log_cb) + result = SASL_FAIL; + if (result != SASL_OK) goto done; + + va_start(ap, fmt); /* start varargs */ + + while(pos9) + done=1; + } + pos++; + if (pos>formatlen) + done=1; + } + + } + } + + /* put 0 at end */ + result = _buf_alloc(&out, &alloclen, outlen+1); + if (result != SASL_OK) goto done; + out[outlen]=0; + + va_end(ap); + + /* send log message */ + result = log_cb(log_ctx, level, out); + + done: + if(out) sasl_FREE(out); +} + + + +/* Allocate and Init a sasl_utils_t structure */ +sasl_utils_t * +_sasl_alloc_utils(sasl_conn_t *conn, + sasl_global_callbacks_t *global_callbacks) +{ + sasl_utils_t *utils; + /* set util functions - need to do rest*/ + utils=sasl_ALLOC(sizeof(sasl_utils_t)); + if (utils==NULL) + return NULL; + + utils->conn = conn; + + sasl_randcreate(&utils->rpool); + + if (conn) { + utils->getopt = &_sasl_conn_getopt; + utils->getopt_context = conn; + } else { + utils->getopt = &_sasl_global_getopt; + utils->getopt_context = global_callbacks; + } + + utils->malloc=_sasl_allocation_utils.malloc; + utils->calloc=_sasl_allocation_utils.calloc; + utils->realloc=_sasl_allocation_utils.realloc; + utils->free=_sasl_allocation_utils.free; + + utils->mutex_alloc = _sasl_mutex_utils.alloc; + utils->mutex_lock = _sasl_mutex_utils.lock; + utils->mutex_unlock = _sasl_mutex_utils.unlock; + utils->mutex_free = _sasl_mutex_utils.free; + + utils->MD5Init = &_sasl_MD5Init; + utils->MD5Update= &_sasl_MD5Update; + utils->MD5Final = &_sasl_MD5Final; + utils->hmac_md5 = &_sasl_hmac_md5; + utils->hmac_md5_init = &_sasl_hmac_md5_init; + utils->hmac_md5_final = &_sasl_hmac_md5_final; + utils->hmac_md5_precalc = &_sasl_hmac_md5_precalc; + utils->hmac_md5_import = &_sasl_hmac_md5_import; + utils->mkchal = &sasl_mkchal; + utils->utf8verify = &sasl_utf8verify; + utils->rand=&sasl_rand; + utils->churn=&sasl_churn; + utils->checkpass=NULL; + + utils->encode64=&sasl_encode64; + utils->decode64=&sasl_decode64; + + utils->erasebuffer=&sasl_erasebuffer; + + utils->getprop=&sasl_getprop; + utils->setprop=&sasl_setprop; + + utils->getcallback=&_sasl_getcallback; + + utils->log=&_sasl_log; + + utils->seterror=&sasl_seterror; + +#ifndef macintosh + /* Aux Property Utilities */ + utils->prop_new=&prop_new; + utils->prop_dup=&prop_dup; + utils->prop_request=&prop_request; + utils->prop_get=&prop_get; + utils->prop_getnames=&prop_getnames; + utils->prop_clear=&prop_clear; + utils->prop_dispose=&prop_dispose; + utils->prop_format=&prop_format; + utils->prop_set=&prop_set; + utils->prop_setvals=&prop_setvals; + utils->prop_erase=&prop_erase; +#endif + + /* Spares */ + utils->spare_fptr = NULL; + utils->spare_fptr1 = utils->spare_fptr2 = + utils->spare_fptr3 = NULL; + + return utils; +} + +int +_sasl_free_utils(const sasl_utils_t ** utils) +{ + sasl_utils_t *nonconst; + + if(!utils) return SASL_BADPARAM; + if(!*utils) return SASL_OK; + + /* I wish we could avoid this cast, it's pretty gratuitous but it + * does make life easier to have it const everywhere else. */ + nonconst = (sasl_utils_t *)(*utils); + + sasl_randfree(&(nonconst->rpool)); + sasl_FREE(nonconst); + + *utils = NULL; + return SASL_OK; +} + +int sasl_idle(sasl_conn_t *conn) +{ + if (! conn) { + if (_sasl_server_idle_hook + && _sasl_server_idle_hook(NULL)) + return 1; + if (_sasl_client_idle_hook + && _sasl_client_idle_hook(NULL)) + return 1; + return 0; + } + + if (conn->idle_hook) + return conn->idle_hook(conn); + + return 0; +} + +const sasl_callback_t * +_sasl_find_getpath_callback(const sasl_callback_t *callbacks) +{ + static const sasl_callback_t default_getpath_cb = { + SASL_CB_GETPATH, + &_sasl_getpath, + NULL + }; + + if (callbacks) + while (callbacks->id != SASL_CB_LIST_END) + { + if (callbacks->id == SASL_CB_GETPATH) + { + return callbacks; + } else { + ++callbacks; + } + } + + return &default_getpath_cb; +} + +const sasl_callback_t * +_sasl_find_verifyfile_callback(const sasl_callback_t *callbacks) +{ + static const sasl_callback_t default_verifyfile_cb = { + SASL_CB_VERIFYFILE, + &_sasl_verifyfile, + NULL + }; + + if (callbacks) + while (callbacks->id != SASL_CB_LIST_END) + { + if (callbacks->id == SASL_CB_VERIFYFILE) + { + return callbacks; + } else { + ++callbacks; + } + } + + return &default_verifyfile_cb; +} + +/* Basically a conditional call to realloc(), if we need more */ +int _buf_alloc(char **rwbuf, size_t *curlen, size_t newlen) +{ + if(!(*rwbuf)) { + *rwbuf = sasl_ALLOC(newlen); + if (*rwbuf == NULL) { + *curlen = 0; + return SASL_NOMEM; + } + *curlen = newlen; + } else if(*rwbuf && *curlen < newlen) { + size_t needed = 2*(*curlen); + + while(needed < newlen) + needed *= 2; + + *rwbuf = sasl_REALLOC(*rwbuf, needed); + + if (*rwbuf == NULL) { + *curlen = 0; + return SASL_NOMEM; + } + *curlen = needed; + } + + return SASL_OK; +} + +/* for the mac os x cfm glue: this lets the calling function + get pointers to the error buffer without having to touch the sasl_conn_t struct */ +void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, size_t **lenhdl) +{ + *bufhdl = &conn->error_buf; + *lenhdl = &conn->error_buf_len; +} + +/* convert an iovec to a single buffer */ +int _iovec_to_buf(const struct iovec *vec, + unsigned numiov, buffer_info_t **output) +{ + unsigned i; + int ret; + buffer_info_t *out; + char *pos; + + if(!vec || !output) return SASL_BADPARAM; + + if(!(*output)) { + *output = sasl_ALLOC(sizeof(buffer_info_t)); + if(!*output) return SASL_NOMEM; + memset(*output,0,sizeof(buffer_info_t)); + } + + out = *output; + + out->curlen = 0; + for(i=0; icurlen += vec[i].iov_len; + + ret = _buf_alloc(&out->data, &out->reallen, out->curlen); + + if(ret != SASL_OK) return SASL_NOMEM; + + memset(out->data, 0, out->reallen); + pos = out->data; + + for(i=0; i= NI_MAXHOST) + return SASL_BADPARAM; + hbuf[i] = addr[i]; + } + hbuf[i] = '\0'; + + if (addr[i] == ';') + i++; + /* XXX: Do we need this check? */ + for (j = i; addr[j] != '\0'; j++) + if (!isdigit((int)(addr[j]))) + return SASL_BADPARAM; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) + return SASL_BADPARAM; + + if (out) { + if (outlen < (socklen_t)ai->ai_addrlen) { + freeaddrinfo(ai); + return SASL_BUFOVER; + } + memcpy(out, ai->ai_addr, ai->ai_addrlen); + } + + freeaddrinfo(ai); + + return SASL_OK; +} + +int _sasl_build_mechlist(void) +{ + int count = 0; + sasl_string_list_t *clist = NULL, *slist = NULL, *olist = NULL; + sasl_string_list_t *p, *q, **last, *p_next; + + clist = _sasl_client_mechs(); + slist = _sasl_server_mechs(); + + if(!clist) { + olist = slist; + } else { + int flag; + + /* append slist to clist, and set olist to clist */ + for(p = slist; p; p = p_next) { + flag = 0; + p_next = p->next; + + last = &clist; + for(q = clist; q; q = q->next) { + if(!strcmp(q->d, p->d)) { + /* They match, set the flag */ + flag = 1; + break; + } + last = &(q->next); + } + + if(!flag) { + (*last)->next = p; + p->next = NULL; + } else { + sasl_FREE(p); + } + } + + olist = clist; + } + + if(!olist) { + printf ("no olist"); + return SASL_FAIL; + } + + for (p = olist; p; p = p->next) count++; + + if(global_mech_list) { + sasl_FREE(global_mech_list); + global_mech_list = NULL; + } + + global_mech_list = sasl_ALLOC((count + 1) * sizeof(char *)); + if(!global_mech_list) return SASL_NOMEM; + + memset(global_mech_list, 0, (count + 1) * sizeof(char *)); + + count = 0; + for (p = olist; p; p = p_next) { + p_next = p->next; + + global_mech_list[count++] = (char *) p->d; + + sasl_FREE(p); + } + + return SASL_OK; +} + +const char ** sasl_global_listmech(void) +{ + return global_mech_list; +} + +int sasl_listmech(sasl_conn_t *conn, + const char *user, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount) +{ + if(!conn) { + return SASL_BADPARAM; + } else if(conn->type == SASL_CONN_SERVER) { + RETURN(conn, _sasl_server_listmech(conn, user, prefix, sep, suffix, + result, plen, pcount)); + } else if (conn->type == SASL_CONN_CLIENT) { + RETURN(conn, _sasl_client_listmech(conn, prefix, sep, suffix, + result, plen, pcount)); + } + + PARAMERROR(conn); +} + + +#ifndef WIN32 +static int +_sasl_getpath(void *context __attribute__((unused)), + const char **path) +{ + if (! path) + return SASL_BADPARAM; + + *path = getenv(SASL_PATH_ENV_VAR); + if (! *path) + *path = PLUGINDIR; + + return SASL_OK; +} + +#else +/* Return NULL on failure */ +static int +_sasl_getpath(void *context __attribute__((unused)), const char **path) +{ + /* Open registry entry, and find all registered SASL libraries. + * + * Registry location: + * + * SOFTWARE\\Carnegie Mellon\\Project Cyrus\\SASL Library + * + * Key - value: + * + * "SearchPath" - value: PATH like (';' delimited) list + * of directories where to search for plugins + * The list may contain references to environment + * variables (e.g. %PATH%). + * + */ + HKEY hKey; + DWORD ret; + DWORD ValueType; /* value type */ + DWORD cbData; /* value size */ + BYTE * ValueData; /* value */ + DWORD cbExpandedData; /* "expanded" value size */ + BYTE * ExpandedValueData; /* "expanded" value */ + char * return_value; /* function return value */ + char * tmp; + + /* Initialization */ + ExpandedValueData = NULL; + ValueData = NULL; + return_value = NULL; + + /* Open the registry */ + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + SASL_ROOT_KEY, + 0, + KEY_READ, + &hKey); + + if (ret != ERROR_SUCCESS) { + /* no registry entry */ + *path = PLUGINDIR; + return SASL_OK; + } + + /* figure out value type and required buffer size */ + /* the size will include space for terminating NUL if required */ + RegQueryValueEx (hKey, + SASL_PATH_SUBKEY, + NULL, /* reserved */ + &ValueType, + NULL, + &cbData); + + /* Only accept string related types */ + if (ValueType != REG_EXPAND_SZ && + ValueType != REG_MULTI_SZ && + ValueType != REG_SZ) { + return_value = NULL; + goto CLEANUP; + } + + /* Any high water mark? */ + ValueData = sasl_ALLOC(cbData); + if (ValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + RegQueryValueEx (hKey, + SASL_PATH_SUBKEY, + NULL, /* reserved */ + &ValueType, + ValueData, + &cbData); + + switch (ValueType) { + case REG_EXPAND_SZ: + /* : A random starting guess */ + cbExpandedData = cbData + 1024; + ExpandedValueData = sasl_ALLOC(cbExpandedData); + if (ExpandedValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + cbExpandedData = ExpandEnvironmentStrings( + ValueData, + ExpandedValueData, + cbExpandedData); + + if (cbExpandedData == 0) { + /* : GetLastError() contains the reason for failure */ + return_value = NULL; + goto CLEANUP; + } + + /* : Must retry expansion with the bigger buffer */ + if (cbExpandedData > cbData + 1024) { + /* : Memory leak here if can't realloc */ + ExpandedValueData = sasl_REALLOC(ExpandedValueData, cbExpandedData); + if (ExpandedValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + cbExpandedData = ExpandEnvironmentStrings( + ValueData, + ExpandedValueData, + cbExpandedData); + + /* : This should not happen */ + if (cbExpandedData == 0) { + /* : GetLastError() contains the reason for failure */ + return_value = NULL; + goto CLEANUP; + } + } + + sasl_FREE(ValueData); + ValueData = ExpandedValueData; + /* : This is to prevent automatical freeing of this block on cleanup */ + ExpandedValueData = NULL; + + break; + + case REG_MULTI_SZ: + tmp = ValueData; + + /* : We shouldn't overflow here, as the buffer is guarantied + : to contain at least two consequent NULs */ + while (1) { + if (tmp[0] == '\0') { + /* : Stop the process if we found the end of the string (two consequent NULs) */ + if (tmp[1] == '\0') { + break; + } + + /* : Replace delimiting NUL with our delimiter characted */ + tmp[0] = PATHS_DELIMITER; + } + tmp += strlen(tmp); + } + break; + + case REG_SZ: + /* Do nothing, it is good as is */ + break; + + default: + return_value = NULL; + goto CLEANUP; + } + + return_value = ValueData; + + CLEANUP: + RegCloseKey(hKey); + if (ExpandedValueData != NULL) sasl_FREE(ExpandedValueData); + if (return_value == NULL) { + if (ValueData != NULL) sasl_FREE(ValueData); + } + *path = return_value; + + return SASL_OK; +} + +#endif diff -durN cyrus-sasl-2.1.10.orig/lib/saslint.h cyrus-sasl-2.1.10/lib/saslint.h --- cyrus-sasl-2.1.10.orig/lib/saslint.h Thu Dec 5 05:16:59 2002 +++ cyrus-sasl-2.1.10/lib/saslint.h Thu Jan 9 11:42:29 2003 @@ -356,6 +356,9 @@ _sasl_find_getpath_callback(const sasl_callback_t *callbacks); extern const sasl_callback_t * +_sasl_find_getconfpath_callback(const sasl_callback_t *callbacks); + +extern const sasl_callback_t * _sasl_find_verifyfile_callback(const sasl_callback_t *callbacks); extern int _sasl_common_init(void); diff -durN cyrus-sasl-2.1.10.orig/lib/saslint.h.orig cyrus-sasl-2.1.10/lib/saslint.h.orig --- cyrus-sasl-2.1.10.orig/lib/saslint.h.orig Thu Jan 1 01:00:00 1970 +++ cyrus-sasl-2.1.10/lib/saslint.h.orig Thu Dec 5 05:16:59 2002 @@ -0,0 +1,494 @@ +/* saslint.h - internal SASL library definitions + * Rob Siemborski + * Tim Martin + * $Id$ + */ +/* + * Copyright (c) 2001 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. + */ + +#ifndef SASLINT_H +#define SASLINT_H + +#include +#include "sasl.h" +#include "saslplug.h" +#include "saslutil.h" +#include "prop.h" + +/* #define'd constants */ +#define CANON_BUF_SIZE 256 + +/* Error Handling Foo */ +/* Helpful Hints: + * -Error strings are set as soon as possible (first function in stack trace + * with a pointer to the sasl_conn_t. + * -Error codes are set as late as possible (only in the sasl api functions), + * thoug "as often as possible" also comes to mind to ensure correctness + * -Errors from calls to _buf_alloc, _sasl_strdup, etc are assumed to be + * memory errors. + * -Only errors (error codes < SASL_OK) should be remembered + */ +#define RETURN(conn, val) { if(conn && (val) < SASL_OK) \ + (conn)->error_code = (val); \ + return (val); } +#define MEMERROR(conn) {\ + if(conn) sasl_seterror( (conn), 0, \ + "Out of Memory in " __FILE__ " near line %d", __LINE__ ); \ + RETURN(conn, SASL_NOMEM) } +#define PARAMERROR(conn) {\ + if(conn) sasl_seterror( (conn), SASL_NOLOG, \ + "Parameter error in " __FILE__ " near line %d", __LINE__ ); \ + RETURN(conn, SASL_BADPARAM) } +#define INTERROR(conn, val) {\ + if(conn) sasl_seterror( (conn), 0, \ + "Internal Error %d in " __FILE__ " near line %d", (val),\ + __LINE__ ); \ + RETURN(conn, (val)) } + +#ifndef PATH_MAX +# ifdef WIN32 +# define PATH_MAX MAX_PATH +# else +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# else +# define PATH_MAX 1024 /* arbitrary; probably big enough will + * probably only be 256+64 on + * pre-posix machines */ +# endif /* _POSIX_PATH_MAX */ +# endif /* WIN32 */ +#endif + +/* : Define directory delimiter in SASL_PATH variable */ +#ifdef WIN32 +#define PATHS_DELIMITER ';' +#else +#define PATHS_DELIMITER ':' +#endif + +/* Datatype Definitions */ +typedef struct { + const sasl_callback_t *callbacks; + const char *appname; +} sasl_global_callbacks_t; + +typedef struct _sasl_external_properties +{ + sasl_ssf_t ssf; + char *auth_id; +} _sasl_external_properties_t; + +typedef struct sasl_string_list +{ + const char *d; + struct sasl_string_list *next; +} sasl_string_list_t; + +typedef struct buffer_info +{ + char *data; + size_t curlen; + size_t reallen; +} buffer_info_t; + +typedef int add_plugin_t(const char *, void *); + +typedef struct add_plugin_list +{ + const char *entryname; + add_plugin_t *add_plugin; +} add_plugin_list_t; + +enum Sasl_conn_type { SASL_CONN_UNKNOWN = 0, + SASL_CONN_SERVER = 1, + SASL_CONN_CLIENT = 2 }; + +struct sasl_conn { + enum Sasl_conn_type type; + + void (*destroy_conn)(sasl_conn_t *); /* destroy function */ + + char *service; + + unsigned int flags; /* flags passed to sasl_*_new */ + + /* IP information. A buffer of size 52 is adequate for this in its + longest format (see sasl.h) */ + int got_ip_local, got_ip_remote; + char iplocalport[NI_MAXHOST + NI_MAXSERV]; + char ipremoteport[NI_MAXHOST + NI_MAXSERV]; + + void *context; + sasl_out_params_t oparams; + + sasl_security_properties_t props; + _sasl_external_properties_t external; + + sasl_secret_t *secret; + + int (*idle_hook)(sasl_conn_t *conn); + const sasl_callback_t *callbacks; + const sasl_global_callbacks_t *global_callbacks; /* global callbacks + * connection */ + char *serverFQDN; + + /* Pointers to memory that we are responsible for */ + buffer_info_t *encode_buf; + + int error_code; + char *error_buf, *errdetail_buf; + size_t error_buf_len, errdetail_buf_len; + char *mechlist_buf; + size_t mechlist_buf_len; + + char *decode_buf; + + char user_buf[CANON_BUF_SIZE+1], authid_buf[CANON_BUF_SIZE+1]; +}; + +/* Server Conn Type Information */ + +typedef struct mechanism +{ + int version; + int condition; /* set to SASL_NOUSER if no available users; + set to SASL_CONTINUE if delayed plugn loading */ + char *plugname; /* for AUTHSOURCE tracking */ + const sasl_server_plug_t *plug; + struct mechanism *next; + char *f; /* where should i load the mechanism from? */ +} mechanism_t; + +typedef struct mech_list { + const sasl_utils_t *utils; /* gotten from plug_init */ + + void *mutex; /* mutex for this data */ + mechanism_t *mech_list; /* list of mechanisms */ + int mech_length; /* number of mechanisms */ +} mech_list_t; + +typedef struct context_list +{ + mechanism_t *mech; + void *context; /* if NULL, this mech is disabled for this connection + * otherwise, use this context instead of a call + * to mech_new */ + struct context_list *next; +} context_list_t; + +typedef struct sasl_server_conn { + sasl_conn_t base; /* parts common to server + client */ + + char *user_realm; /* domain the user authenticating is in */ + int sent_last; /* Have we already done the last send? */ + int authenticated; + mechanism_t *mech; /* mechanism trying to use */ + sasl_server_params_t *sparams; + context_list_t *mech_contexts; +} sasl_server_conn_t; + +/* Client Conn Type Information */ + +typedef struct cmechanism +{ + int version; + + char *plugname; + const sasl_client_plug_t *plug; + + struct cmechanism *next; +} cmechanism_t; + +typedef struct cmech_list { + const sasl_utils_t *utils; + + void *mutex; /* mutex for this data */ + cmechanism_t *mech_list; /* list of mechanisms */ + int mech_length; /* number of mechanisms */ + +} cmech_list_t; + +typedef struct sasl_client_conn { + sasl_conn_t base; /* parts common to server + client */ + + cmechanism_t *mech; + sasl_client_params_t *cparams; + + char *clientFQDN; + +} sasl_client_conn_t; + +typedef struct sasl_allocation_utils { + sasl_malloc_t *malloc; + sasl_calloc_t *calloc; + sasl_realloc_t *realloc; + sasl_free_t *free; +} sasl_allocation_utils_t; + +typedef struct sasl_mutex_utils { + sasl_mutex_alloc_t *alloc; + sasl_mutex_lock_t *lock; + sasl_mutex_unlock_t *unlock; + sasl_mutex_free_t *free; +} sasl_mutex_utils_t; + +typedef struct sasl_log_utils_s { + sasl_log_t *log; +} sasl_log_utils_t; + +typedef int sasl_plaintext_verifier(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service, + const char *user_realm); + +struct sasl_verify_password_s { + char *name; + sasl_plaintext_verifier *verify; +}; + +/* + * globals & constants + */ +/* + * common.c + */ +LIBSASL_API const sasl_utils_t *sasl_global_utils; + +extern void (*_sasl_client_cleanup_hook)(void); +extern void (*_sasl_server_cleanup_hook)(void); +extern int (*_sasl_client_idle_hook)(sasl_conn_t *conn); +extern int (*_sasl_server_idle_hook)(sasl_conn_t *conn); + +extern sasl_allocation_utils_t _sasl_allocation_utils; +extern sasl_mutex_utils_t _sasl_mutex_utils; + +/* + * checkpw.c + */ +extern struct sasl_verify_password_s _sasl_verify_password[]; + +/* + * server.c + */ +/* (this is a function call to ensure this is read-only to the outside) */ +extern int _is_sasl_server_active(void); + +/* + * Allocation and Mutex utility macros + */ +#define sasl_ALLOC(__size__) (_sasl_allocation_utils.malloc((__size__))) +#define sasl_CALLOC(__nelem__, __size__) \ + (_sasl_allocation_utils.calloc((__nelem__), (__size__))) +#define sasl_REALLOC(__ptr__, __size__) \ + (_sasl_allocation_utils.realloc((__ptr__), (__size__))) +#define sasl_FREE(__ptr__) (_sasl_allocation_utils.free((__ptr__))) + +#define sasl_MUTEX_ALLOC() (_sasl_mutex_utils.alloc()) +#define sasl_MUTEX_LOCK(__mutex__) (_sasl_mutex_utils.lock((__mutex__))) +#define sasl_MUTEX_UNLOCK(__mutex__) (_sasl_mutex_utils.unlock((__mutex__))) +#define sasl_MUTEX_FREE(__mutex__) \ + (_sasl_mutex_utils.free((__mutex__))) + +/* function prototypes */ +/* + * dlopen.c and staticopen.c + */ +/* + * The differences here are: + * _sasl_load_plugins loads all plugins from all files + * _sasl_get_plugin loads the LIBRARY for an individual file + * _sasl_done_with_plugins frees the LIBRARIES loaded by the above 2 + * _sasl_locate_entry locates an entrypoint in a given library + */ +extern int _sasl_load_plugins(const add_plugin_list_t *entrypoints, + const sasl_callback_t *getpath_callback, + const sasl_callback_t *verifyfile_callback); +extern int _sasl_get_plugin(const char *file, + const sasl_callback_t *verifyfile_cb, + void **libraryptr); +extern int _sasl_locate_entry(void *library, const char *entryname, + void **entry_point); +extern int _sasl_done_with_plugins(); + + +/* + * common.c + */ +extern const sasl_callback_t * +_sasl_find_getpath_callback(const sasl_callback_t *callbacks); + +extern const sasl_callback_t * +_sasl_find_verifyfile_callback(const sasl_callback_t *callbacks); + +extern int _sasl_common_init(void); + +extern int _sasl_conn_init(sasl_conn_t *conn, + const char *service, + unsigned int flags, + enum Sasl_conn_type type, + int (*idle_hook)(sasl_conn_t *conn), + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *callbacks, + const sasl_global_callbacks_t *global_callbacks); +extern void _sasl_conn_dispose(sasl_conn_t *conn); + +extern sasl_utils_t * +_sasl_alloc_utils(sasl_conn_t *conn, + sasl_global_callbacks_t *global_callbacks); +extern int _sasl_free_utils(const sasl_utils_t ** utils); + +extern int +_sasl_getcallback(sasl_conn_t * conn, + unsigned long callbackid, + int (**pproc)(), + void **pcontext); + +extern void +_sasl_log(sasl_conn_t *conn, + int level, + const char *fmt, + ...); + +void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, size_t **lenhdl); +int _sasl_add_string(char **out, size_t *alloclen, + size_t *outlen, const char *add); + +/* More Generic Utilities in common.c */ +extern int _sasl_strdup(const char *in, char **out, size_t *outlen); + +/* Basically a conditional call to realloc(), if we need more */ +int _buf_alloc(char **rwbuf, size_t *curlen, size_t newlen); + +/* convert an iovec to a single buffer */ +int _iovec_to_buf(const struct iovec *vec, + unsigned numiov, buffer_info_t **output); + +/* Convert between string formats and sockaddr formats */ +int _sasl_iptostring(const struct sockaddr *addr, socklen_t addrlen, + char *out, unsigned outlen); +int _sasl_ipfromstring(const char *addr, struct sockaddr *out, + socklen_t outlen); + +/* + * external plugin (external.c) + */ +int external_client_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount); +int external_server_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount); + +/* Mech Listing Functions */ +int _sasl_build_mechlist(void); +int _sasl_server_listmech(sasl_conn_t *conn, + const char *user, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount); +int _sasl_client_listmech(sasl_conn_t *conn, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount); +/* Just create a straight list of them */ +sasl_string_list_t *_sasl_client_mechs(void); +sasl_string_list_t *_sasl_server_mechs(void); + +/* + * config file declarations (config.c) + */ +extern int sasl_config_init(const char *filename); +extern const char *sasl_config_getstring(const char *key,const char *def); +extern int sasl_config_getint(const char *key,int def); +extern int sasl_config_getswitch(const char *key,int def); + +/* checkpw.c */ +#ifdef DO_SASL_CHECKAPOP +extern int _sasl_auxprop_verify_apop(sasl_conn_t *conn, + const char *userstr, + const char *challenge, + const char *response, + const char *user_realm); +#endif /* DO_SASL_CHECKAPOP */ + +/* Auxprop Plugin (checkpw.c) */ +extern int sasldb_auxprop_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_auxprop_plug_t **plug, + const char *plugname); + +/* + * auxprop.c + */ +extern int _sasl_auxprop_add_plugin(void *p, void *library); +extern void _sasl_auxprop_free(void); +extern void _sasl_auxprop_lookup(sasl_server_params_t *sparams, + unsigned flags, + const char *user, unsigned ulen); + +/* + * canonusr.c + */ +void _sasl_canonuser_free(); +extern int internal_canonuser_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_canonuser_plug_t **plug, + const char *plugname); +extern int _sasl_canon_user(sasl_conn_t *conn, + const char *user, unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams); + +#endif /* SASLINT_H */ diff -durN cyrus-sasl-2.1.10.orig/lib/server.c cyrus-sasl-2.1.10/lib/server.c --- cyrus-sasl-2.1.10.orig/lib/server.c Thu Dec 5 05:16:59 2002 +++ cyrus-sasl-2.1.10/lib/server.c Thu Jan 9 11:42:29 2003 @@ -379,15 +379,15 @@ char *c; char *config_filename=NULL; int len; - const sasl_callback_t *getpath_cb=NULL; + const sasl_callback_t *getconfpath_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; + getconfpath_cb=_sasl_find_getconfpath_callback( global_callbacks.callbacks ); + if (getconfpath_cb==NULL) return SASL_BADPARAM; - /* getpath_cb->proc MUST be a sasl_getpath_t; if only c had a type + /* getconfpath_cb->proc MUST be a sasl_getconfpath_t; if only c had a type system */ - result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context, + result = ((sasl_getconfpath_t *)(getconfpath_cb->proc))(getconfpath_cb->context, &path_to_config); if (result!=SASL_OK) goto done; if (path_to_config == NULL) path_to_config = ""; diff -durN cyrus-sasl-2.1.10.orig/lib/server.c.orig cyrus-sasl-2.1.10/lib/server.c.orig --- cyrus-sasl-2.1.10.orig/lib/server.c.orig Thu Jan 1 01:00:00 1970 +++ cyrus-sasl-2.1.10/lib/server.c.orig Thu Dec 5 05:16:59 2002 @@ -0,0 +1,1734 @@ +/* SASL server API implementation + * Rob Siemborski + * Tim Martin + * $Id$ + */ +/* + * Copyright (c) 2001 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 +#include +#include +#include +#include +#ifndef macintosh +#include +#include +#endif +#include +#include +#include + +#include "sasl.h" +#include "saslint.h" +#include "saslplug.h" +#include "saslutil.h" + +#ifdef sun +/* gotta define gethostname ourselves on suns */ +extern int gethostname(char *, int); +#endif + +#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 + */ + +/* 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; } + +static int _sasl_checkpass(sasl_conn_t *conn, const char *service, + const char *user, const char *pass); + +static mech_list_t *mechlist = NULL; /* global var which holds the list */ + +static sasl_global_callbacks_t global_callbacks; + +/* 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; + + if (!_sasl_server_active || !mechlist) return SASL_NOTINIT; + + /* 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; + } + tmpresult = m->plug->setpass(m->plug->glob_context, + ((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); +} + +/* 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; + context_list_t *cur, *cur_next; + + 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; + + 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; + + _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); +} + +static int init_mechlist(void) +{ + sasl_utils_t *newutils = NULL; + + mechlist->mutex = sasl_MUTEX_ALLOC(); + if(!mechlist->mutex) return SASL_FAIL; + + /* set util functions - need to do rest */ + newutils = _sasl_alloc_utils(NULL, &global_callbacks); + if (newutils == NULL) + return SASL_NOMEM; + + newutils->checkpass = &sasl_checkpass; + + mechlist->utils = newutils; + mechlist->mech_list=NULL; + mechlist->mech_length=0; + + return SASL_OK; +} + +/* + * parameters: + * p - entry point + */ +int sasl_server_add_plugin(const char *plugname, + sasl_server_plug_init_t *p) +{ + 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; + + 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); + + if ((result != SASL_OK) && (result != SASL_NOUSER)) { + _sasl_log(NULL, SASL_LOG_DEBUG, + "server add_plugin entry_point error %z\n", result); + return result; + } + + /* Make sure plugin is using the same SASL version as us */ + if (version != SASL_SERVER_PLUG_VERSION) + { + _sasl_log(NULL, SASL_LOG_ERR, + "version mismatch on plugin"); + return SASL_BADVERS; + } + + for (lupe=0;lupe < plugcount ;lupe++) + { + mech = sasl_ALLOC(sizeof(mechanism_t)); + if (! mech) return SASL_NOMEM; + + mech->plug=pluglist++; + if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) { + sasl_FREE(mech); + return SASL_NOMEM; + } + mech->version = version; + + /* wheather this mech actually has any users in it's db */ + mech->condition = result; /* SASL_OK or SASL_NOUSER */ + + mech->next = mechlist->mech_list; + mechlist->mech_list = mech; + mechlist->mech_length++; + } + + return SASL_OK; +} + +static void server_done(void) { + mechanism_t *m; + mechanism_t *prevm; + + 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) { + prevm->plug->mech_free(prevm->plug->glob_context, + mechlist->utils); + } + + sasl_FREE(prevm->plugname); + sasl_FREE(prevm); + } + _sasl_free_utils(&mechlist->utils); + sasl_MUTEX_FREE(mechlist->mutex); + sasl_FREE(mechlist); + mechlist = NULL; + } + + /* Free the auxprop plugins */ + _sasl_auxprop_free(); + + global_callbacks.callbacks = NULL; + global_callbacks.appname = NULL; + + /* no longer active. fail on listmech's etc. */ + _sasl_server_active = 0; +} + +static int +server_idle(sasl_conn_t *conn) +{ + mechanism_t *m; + if (! mechlist) + return 0; + + for (m = mechlist->mech_list; + m!=NULL; + m = m->next) + if (m->plug->idle + && m->plug->idle(m->plug->glob_context, + conn, + conn ? ((sasl_server_conn_t *)conn)->sparams : NULL)) + return 1; + return 0; +} + +static int load_config(const sasl_callback_t *verifyfile_cb) +{ + int result; + const char *path_to_config=NULL; + char *c; + 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 = ""; + + if ((c = strchr(path_to_config, PATHS_DELIMITER))) { + *c = '\0'; + } + + /* length = length of path + '/' + length of appname + ".conf" + 1 + for '\0' */ + len = strlen(path_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 */ + config_filename = sasl_ALLOC(len); + if (! config_filename) { + result = SASL_NOMEM; + goto done; + } + + snprintf(config_filename, len, "%s/%s.conf", 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; +} + +/* + * 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; +} + +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, "r"); + 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; +} + +/* initialize server drivers, done once per process + * callbacks -- callbacks for all server connections; must include + * getopt callback + * appname -- name of calling application (for lower level logging) + * results: + * state -- server state + * returns: + * SASL_OK -- success + * SASL_BADPARAM -- error in config file + * SASL_NOMEM -- memory failure + * SASL_BADVERS -- Mechanism version mismatch + */ + +int sasl_server_init(const sasl_callback_t *callbacks, + const char *appname) +{ + int ret; + const sasl_callback_t *vf; + 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 } + }; + + /* we require the appname to be non-null */ + if (appname==NULL) return SASL_BADPARAM; + + ret = _sasl_common_init(); + if (ret != SASL_OK) + return ret; + + _sasl_server_cleanup_hook = &server_done; + + /* verify that the callbacks look ok */ + ret = verify_server_callbacks(callbacks); + if (ret != SASL_OK) return ret; + + global_callbacks.callbacks = callbacks; + global_callbacks.appname = appname; + + /* allocate mechlist and set it to empty */ + mechlist = sasl_ALLOC(sizeof(mech_list_t)); + if (mechlist == NULL) return SASL_NOMEM; + + ret = init_mechlist(); + if (ret != SASL_OK) return ret; + + vf = _sasl_find_verifyfile_callback(callbacks); + + /* load config file if applicable */ + ret = load_config(vf); + if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) { + return ret; + } + + /* load internal plugins */ + 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 { + /* load all plugins now */ + ret = _sasl_load_plugins(ep_list, + _sasl_find_getpath_callback(callbacks), + _sasl_find_verifyfile_callback(callbacks)); + } + + if (ret == SASL_OK) { + /* _sasl_server_active shows if we're active or not. + server_done() sets it back to 0 */ + _sasl_server_active = 1; + _sasl_server_idle_hook = &server_idle; + + ret = _sasl_build_mechlist(); + } + + 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) +{ + int result; + sasl_server_conn_t *serverconn; + sasl_utils_t *utils; + + if (_sasl_server_active==0) return SASL_NOTINIT; + 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)); + + 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, + callbacks, &global_callbacks); + if (result != SASL_OK) + goto done_error; + + + /* set util functions - need to do rest */ + utils=_sasl_alloc_utils(*pconn, &global_callbacks); + if (!utils) { + result = SASL_NOMEM; + goto done_error; + } + + utils->checkpass = &sasl_checkpass; + + /* 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); + + serverconn->sparams->appname = global_callbacks.appname; + serverconn->sparams->applen = strlen(global_callbacks.appname); + + 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 */ + } + + 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; + + if(!conn) return 0; + + if(! mech || ! mech->plug) { + PARAMERROR(conn); + 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; + + 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; + } + } + + if (!strcasecmp(plug->mech_name, "EXTERNAL")) { + /* Special case for the external mechanism */ + if (conn->props.min_ssf > conn->external.ssf + || ! conn->external.auth_id) { + sasl_seterror(conn, SASL_NOLOG, + "External SSF not good enough"); + return 0; + } + } else { + if (conn->props.min_ssf < conn->external.ssf) { + minssf = 0; + } else { + minssf = conn->props.min_ssf - conn->external.ssf; + } + + /* Generic mechanism */ + if (plug->max_ssf < minssf) { + sasl_seterror(conn, SASL_NOLOG, + "mech %s is too weak", plug->mech_name); + return 0; /* too weak */ + } + + } + + context = NULL; + if(plug->mech_avail + && plug->mech_avail(plug->glob_context, + 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) { + MEMERROR(conn); + 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) { + MEMERROR(conn); + return 0; + } + cur->context = context; + cur->mech = mech; + cur->next = s_conn->mech_contexts; + s_conn->mech_contexts = cur; + } + + /* Generic mechanism */ + if (plug->max_ssf < minssf) { + sasl_seterror(conn, SASL_NOLOG, "too weak"); + return 0; /* too weak */ + } + + /* 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; + } + + /* 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) { + sasl_seterror(conn, SASL_NOLOG, + "security flags do not match required"); + return 0; + } + + /* Check Features */ + if(plug->features & SASL_FEAT_GETSECRET) { + /* We no longer support sasl_server_{get,put}secret */ + sasl_seterror(conn, 0, + "mech %s requires unprovided secret facility", + plug->mech_name); + 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 + * output: + * pconn -- the connection negotiation state on success + * + * 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; + + 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; + + 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) { + sasl_seterror(conn, 0, "Couldn't find mech %s", mech); + result = SASL_NOMECH; + goto done; + } + + /* Make sure that we're willing to use this mech */ + if (! mech_permitted(conn, m)) { + result = SASL_NOMECH; + goto done; + } + + 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); + } + } + + /* 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) { + sasl_seterror(conn, 0, + "Got past mech_permitted with a disallowed mech!"); + 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 */ + result = s_conn->mech->plug->mech_new(s_conn->mech->plug->glob_context, + 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. */ + sasl_seterror(conn, 0, + "Remote sent first but mech does not allow it."); + 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 */ + + /* check parameters */ + if (_sasl_server_active==0) return SASL_NOTINIT; + 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) { + sasl_seterror(conn, 0, + "mech did not call canon_user for both authzid " \ + "and authid"); + 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 + */ + +static unsigned mech_names_len() +{ + 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; + + /* if there hasn't been a sasl_sever_init() fail */ + if (_sasl_server_active==0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + 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)) + + mech_names_len() + + (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; + +} + +sasl_string_list_t *_sasl_server_mechs(void) +{ + mechanism_t *listptr; + sasl_string_list_t *retval = NULL, *next=NULL; + + if(!_sasl_server_active) return NULL; + + /* 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 *service, + const char *user, const char *pass) +{ + 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, *mech; + struct sasl_verify_password_s *v; + + /* 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) + sasl_seterror(conn, SASL_NOLOG, "checkpass failed"); + + 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, + unsigned userlen __attribute__((unused)), + const char *pass, + unsigned passlen) +{ + int result; + + 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; + + /* check params */ + if (pass == NULL) + PARAMERROR(conn); + + result = _sasl_checkpass(conn, conn->service, user, pass); + + if (result == SASL_OK) { + strncpy(conn->authid_buf, user, CANON_BUF_SIZE); + conn->oparams.authid = conn->authid_buf; + + 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, *mech; + void *context; + sasl_getopt_t *getopt; + struct sasl_verify_password_s *v; + + /* check params */ + if (_sasl_server_active==0) return SASL_NOTINIT; + 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?"); + sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?"); + } + + 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, " " (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; + + if (_sasl_server_active==0) + return SASL_NOTINIT; + + /* 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 " ", where + * 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) + { + sasl_seterror(conn, 0, "Bad Digest"); + 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 */ +} + diff -durN cyrus-sasl-2.1.10.orig/man/sasl_getconfpath_t.3 cyrus-sasl-2.1.10/man/sasl_getconfpath_t.3 --- cyrus-sasl-2.1.10.orig/man/sasl_getconfpath_t.3 Thu Jan 1 01:00:00 1970 +++ cyrus-sasl-2.1.10/man/sasl_getconfpath_t.3 Thu Jan 9 11:42:29 2003 @@ -0,0 +1,47 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" +.\" This manpage is Copyright (C) 1999 Tim Martin +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of this +.\" manual under the conditions for verbatim copying, provided that the +.\" entire resulting derived work is distributed under the terms of a +.\" permission notice identical to this one +.\" +.\" Formatted or processed versions of this manual, if unaccompanied by +.\" the source, must acknowledge the copyright and authors of this work. +.\" +.\" +.TH sasl_getpath_t "26 March 2000" SASL "SASL man pages" +.SH NAME +sasl_getconfpath_t \- The SASL callback to indicate location of the config files + + +.SH SYNOPSIS +.nf +.B #include + +.sp +.BI "int sasl_getconfpath_t(void " *context ", " +.BI " char ** " path ")"; + +.fi +.SH DESCRIPTION + +.B sasl_getconfpath_t +is used if the application wishes to use a different location for the SASL cofiguration files. If this callback is not used SASL will either use the location in the enviornment variable SASL_CONF_PATH or /etc/sasl by default. +.PP + +.SH "RETURN VALUE" + +SASL callback functions should return SASL return codes. See sasl.h for a complete list. SASL_OK indicates success. + +.SH "CONFORMING TO" +RFC 2222 +.SH "SEE ALSO" +.BR other sasl stuff +.BR +.BR \ No newline at end of file diff -durN cyrus-sasl-2.1.10.orig/win32/include/config.h cyrus-sasl-2.1.10/win32/include/config.h --- cyrus-sasl-2.1.10.orig/win32/include/config.h Fri Dec 6 17:24:48 2002 +++ cyrus-sasl-2.1.10/win32/include/config.h Thu Jan 9 11:44:41 2003 @@ -96,7 +96,9 @@ #define HAVE_MEMCPY 1 #define SASL_PATH_ENV_VAR "SASL_PATH" +#define SASL_CONF_PATH_ENV_VAR "SASL_CONF_PATH" #define PLUGINDIR "C:\\SASL-PLUGINS" +#define CONFIGDIR "\\sasl-configs" /* Windows calls these functions something else */