diff options
author | molotkov-and <[email protected]> | 2023-08-18 17:20:47 +0300 |
---|---|---|
committer | molotkov-and <[email protected]> | 2023-08-18 19:42:07 +0300 |
commit | 73215359bc33e76f5b94d1832a377072bf245cfc (patch) | |
tree | 9cb8ad61d8c3cd107353d42951560ff3cf1b966d /contrib/libs/sasl/lib/common.c | |
parent | 1cbfd34a55732f7b1d407986b45e40853f01f2c2 (diff) |
KIKIMR-18220: Enrich token with groups from LDAP
Add ldap functions wrapper and separate in different files for compatibility with different OS.
Add user groups fetching from ldap server.
Limitations:
- Fixed 'memberOf' attribute
- No tests to check how filter for search created
- Fetched groups are returned in event as is.
Diffstat (limited to 'contrib/libs/sasl/lib/common.c')
-rw-r--r-- | contrib/libs/sasl/lib/common.c | 2674 |
1 files changed, 2674 insertions, 0 deletions
diff --git a/contrib/libs/sasl/lib/common.c b/contrib/libs/sasl/lib/common.c new file mode 100644 index 00000000000..d9104c89563 --- /dev/null +++ b/contrib/libs/sasl/lib/common.c @@ -0,0 +1,2674 @@ +/* common.c - Functions that are common to server and clinet + * Rob Siemborski + * Tim Martin + */ +/* + * Copyright (c) 1998-2016 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 + * Carnegie Mellon University + * Center for Technology Transfer and Enterprise Creation + * 4615 Forbes Avenue + * Suite 302 + * Pittsburgh, PA 15213 + * (412) 268-7393, fax: (412) 268-7395 + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#ifdef HAVE_SYSLOG +#include <syslog.h> +#endif +#include <stdarg.h> +#include <ctype.h> +#include <assert.h> + +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> +#include "saslint.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +static const char *implementation_string = "Cyrus SASL"; + +#define VSTR0(maj, min, step) #maj "." #min "." #step +#define VSTR(maj, min, step) VSTR0(maj, min, step) +#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \ + SASL_VERSION_STEP) + +static int _sasl_getpath(void *context __attribute__((unused)), const char **path); +static int _sasl_getpath_simple(void *context __attribute__((unused)), const char **path); +static int _sasl_getconfpath(void *context __attribute__((unused)), char ** path); +static int _sasl_getconfpath_simple(void *context __attribute__((unused)), const char **path); + +#if !defined(WIN32) +static char * _sasl_get_default_unix_path(void *context __attribute__((unused)), + char * env_var_name, char * default_value); +#else +/* NB: Always returned allocated value */ +static char * _sasl_get_default_win_path(void *context __attribute__((unused)), + TCHAR * reg_attr_name, char * default_value); +#endif + + +/* It turns out to be convenient to have a shared sasl_utils_t */ +const sasl_utils_t *sasl_global_utils = NULL; + +/* Should be a null-terminated array that lists the available mechanisms */ +static char **global_mech_list = NULL; + +void *free_mutex = NULL; + +int (*_sasl_client_cleanup_hook)(void) = NULL; +int (*_sasl_server_cleanup_hook)(void) = NULL; +int (*_sasl_client_idle_hook)(sasl_conn_t *conn) = NULL; +int (*_sasl_server_idle_hook)(sasl_conn_t *conn) = NULL; + +sasl_allocation_utils_t _sasl_allocation_utils={ + (sasl_malloc_t *) &malloc, + (sasl_calloc_t *) &calloc, + (sasl_realloc_t *) &realloc, + (sasl_free_t *) &free +}; +int _sasl_allocation_locked = 0; + +#define SASL_ENCODEV_EXTRA 4096 + +/* Default getpath/getconfpath callbacks. These can be edited by sasl_set_path(). */ +static sasl_callback_t default_getpath_cb = { + SASL_CB_GETPATH, (sasl_callback_ft)&_sasl_getpath, NULL +}; +static sasl_callback_t default_getconfpath_cb = { + SASL_CB_GETCONFPATH, (sasl_callback_ft)&_sasl_getconfpath, NULL +}; + +static char * default_plugin_path = NULL; +static char * default_conf_path = NULL; + +static int _sasl_global_getopt(void *context, + const char *plugin_name, + const char *option, + const char ** result, + unsigned *len); + +/* 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) +{ + /* Disallow mutex function changes once sasl_client_init + and/or sasl_server_init is called */ + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return; + } + + _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((unsigned) 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+1)!=SASL_OK) + return SASL_NOMEM; + + strcpy(*out + *outlen, add); + *outlen += addlen; + + return SASL_OK; +} + +/* a simpler way to set plugin path or configuration file path + * without the need to set sasl_getpath_t callback. + * + * This function can be called before sasl_server_init/sasl_client_init. + * + * Don't call this function without locking in a multithreaded application. + */ +int sasl_set_path (int path_type, char * path) +{ + int result; + + if (path == NULL) { + return (SASL_FAIL); + } + + switch (path_type) { + case SASL_PATH_TYPE_PLUGIN: + if (default_plugin_path != NULL) { + sasl_FREE (default_plugin_path); + default_plugin_path = NULL; + } + result = _sasl_strdup (path, &default_plugin_path, NULL); + if (result != SASL_OK) { + return (result); + } + + /* Update the default getpath_t callback */ + default_getpath_cb.proc = (sasl_callback_ft)&_sasl_getpath_simple; + break; + + case SASL_PATH_TYPE_CONFIG: + if (default_conf_path != NULL) { + sasl_FREE (default_conf_path); + default_conf_path = NULL; + } + result = _sasl_strdup (path, &default_conf_path, NULL); + if (result != SASL_OK) { + return (result); + } + + /* Update the default getpath_t callback */ + default_getconfpath_cb.proc = (sasl_callback_ft)&_sasl_getconfpath_simple; + break; + + default: + return (SASL_FAIL); + } + + 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 #. + * Patch version is not available using this function, + * use sasl_version_info() instead. + */ +void sasl_version(const char **implementation, int *version) +{ + if(implementation) *implementation = implementation_string; + /* NB: the format is not the same as in SASL_VERSION_FULL */ + if(version) *version = (SASL_VERSION_MAJOR << 24) | + (SASL_VERSION_MINOR << 16) | + (SASL_VERSION_STEP); +} + +/* Extended version of sasl_version above */ +void sasl_version_info (const char **implementation, const char **version_string, + int *version_major, int *version_minor, int *version_step, + int *version_patch) +{ + if (implementation) *implementation = implementation_string; + if (version_string) *version_string = SASL_VERSION_STRING; + if (version_major) *version_major = SASL_VERSION_MAJOR; + if (version_minor) *version_minor = SASL_VERSION_MINOR; + if (version_step) *version_step = SASL_VERSION_STEP; + /* Version patch is always 0 for CMU SASL */ + if (version_patch) *version_patch = 0; +} + +/* 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); +} + +/* Internal function that doesn't do any verification */ +static int +_sasl_encodev (sasl_conn_t *conn, + const struct iovec *invec, + unsigned numiov, + int * p_num_packets, /* number of packets generated so far */ + const char **output, /* previous output, if *p_num_packets > 0 */ + unsigned *outputlen) +{ + int result; + char * new_buf; + + assert (conn->oparams.encode != NULL); + + if (*p_num_packets == 1) { + /* This is the second call to this function, + so we need to allocate a new output buffer + and copy existing data there. */ + conn->multipacket_encoded_data.curlen = *outputlen; + if (conn->multipacket_encoded_data.data == NULL) { + conn->multipacket_encoded_data.reallen = + conn->multipacket_encoded_data.curlen + SASL_ENCODEV_EXTRA; + conn->multipacket_encoded_data.data = + sasl_ALLOC(conn->multipacket_encoded_data.reallen + 1); + + if (conn->multipacket_encoded_data.data == NULL) { + MEMERROR(conn); + } + } else { + /* A buffer left from a previous sasl_encodev call. + Make sure it is big enough. */ + if (conn->multipacket_encoded_data.curlen > + conn->multipacket_encoded_data.reallen) { + conn->multipacket_encoded_data.reallen = + conn->multipacket_encoded_data.curlen + SASL_ENCODEV_EXTRA; + + new_buf = sasl_REALLOC(conn->multipacket_encoded_data.data, + conn->multipacket_encoded_data.reallen + 1); + if (new_buf == NULL) { + MEMERROR(conn); + } + conn->multipacket_encoded_data.data = new_buf; + } + } + + memcpy (conn->multipacket_encoded_data.data, + *output, + *outputlen); + } + + result = conn->oparams.encode(conn->context, + invec, + numiov, + output, + outputlen); + + if (*p_num_packets > 0 && result == SASL_OK) { + /* Is the allocated buffer big enough? If not, grow it. */ + if ((conn->multipacket_encoded_data.curlen + *outputlen) > + conn->multipacket_encoded_data.reallen) { + conn->multipacket_encoded_data.reallen = + conn->multipacket_encoded_data.curlen + *outputlen; + new_buf = sasl_REALLOC(conn->multipacket_encoded_data.data, + conn->multipacket_encoded_data.reallen + 1); + if (new_buf == NULL) { + MEMERROR(conn); + } + conn->multipacket_encoded_data.data = new_buf; + } + + /* Append new data to the end of the buffer */ + memcpy (conn->multipacket_encoded_data.data + + conn->multipacket_encoded_data.curlen, + *output, + *outputlen); + conn->multipacket_encoded_data.curlen += *outputlen; + + *output = conn->multipacket_encoded_data.data; + *outputlen = (unsigned)conn->multipacket_encoded_data.curlen; + } + + (*p_num_packets)++; + + RETURN(conn, result); +} + +/* security-encode an iovec */ +/* output is only valid until the 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 = SASL_OK; + unsigned i; + unsigned j; + size_t total_size = 0; + struct iovec *cur_invec = NULL; + struct iovec last_invec; + unsigned cur_numiov; + char * next_buf = NULL; + size_t remainder_len; + unsigned index_offset; + unsigned allocated = 0; + /* Number of generated SASL packets */ + int num_packets = 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; + } + + /* If oparams.encode is NULL, this means there is no SASL security + layer in effect, so no SASL framing is needed. */ + 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 = (unsigned) conn->encode_buf->curlen; + + RETURN(conn, result); + } + + /* 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 */ + + last_invec.iov_base = NULL; + remainder_len = 0; + next_buf = NULL; + i = 0; + while (i < numiov) { + if ((total_size + invec[i].iov_len) > conn->oparams.maxoutbuf) { + + /* CLAIM: total_size < conn->oparams.maxoutbuf */ + + /* Fit as many bytes in last_invec, so that we have conn->oparams.maxoutbuf + bytes in total. */ + last_invec.iov_len = conn->oparams.maxoutbuf - total_size; + /* Point to the first byte of the current record. */ + last_invec.iov_base = invec[i].iov_base; + + /* Note that total_size < conn->oparams.maxoutbuf */ + /* The total size of the iov is bigger then the other end can accept. + So we allocate a new iov that contains just enough. */ + + /* +1 --- for the tail record */ + cur_numiov = i + 1; + + /* +1 --- just in case we need the head record */ + if ((cur_numiov + 1) > allocated) { + struct iovec *new_invec; + + allocated = cur_numiov + 1; + new_invec = sasl_REALLOC (cur_invec, sizeof(struct iovec) * allocated); + if (new_invec == NULL) { + if (cur_invec != NULL) { + sasl_FREE(cur_invec); + } + MEMERROR(conn); + } + cur_invec = new_invec; + } + + if (next_buf != NULL) { + cur_invec[0].iov_base = next_buf; + cur_invec[0].iov_len = (long)remainder_len; + cur_numiov++; + index_offset = 1; + } else { + index_offset = 0; + } + + if (i > 0) { + /* Copy all previous chunks */ + /* NOTE - The starting index in invec is always 0 */ + for (j = 0; j < i; j++) { + cur_invec[j + index_offset] = invec[j]; + } + } + + /* Initialize the last record */ + cur_invec[i + index_offset] = last_invec; + + result = _sasl_encodev (conn, + cur_invec, + cur_numiov, + &num_packets, + output, + outputlen); + + if (result != SASL_OK) { + goto cleanup; + } + + /* Point to the first byte that wouldn't fit into + the conn->oparams.maxoutbuf buffer. */ + /* Note, if next_buf points to the very end of the IOV record, + it will be reset to NULL below */ + /* Note, that some platforms define iov_base as "void *", + thus the typecase below */ + next_buf = (char *) last_invec.iov_base + last_invec.iov_len; + /* Note - remainder_len is how many bytes left to be encoded in + the current IOV slot. */ + remainder_len = (total_size + invec[i].iov_len) - conn->oparams.maxoutbuf; + + /* Skip all consumed IOV records */ + invec += i + 1; + numiov = numiov - (i + 1); + i = 0; + + while (remainder_len > conn->oparams.maxoutbuf) { + last_invec.iov_base = next_buf; + last_invec.iov_len = conn->oparams.maxoutbuf; + + /* Note, if next_buf points to the very end of the IOV record, + it will be reset to NULL below */ + /* Note, that some platforms define iov_base as "void *", + thus the typecase below */ + next_buf = (char *) last_invec.iov_base + last_invec.iov_len; + remainder_len = remainder_len - conn->oparams.maxoutbuf; + + result = _sasl_encodev (conn, + &last_invec, + 1, + &num_packets, + output, + outputlen); + if (result != SASL_OK) { + goto cleanup; + } + } + + total_size = remainder_len; + + if (remainder_len == 0) { + /* Just clear next_buf */ + next_buf = NULL; + } + } else { + total_size += invec[i].iov_len; + i++; + } + } + + /* CLAIM - The remaining data is shorter then conn->oparams.maxoutbuf. */ + + /* Force encoding of any partial buffer. Might not be optimal on the wire. */ + if (next_buf != NULL) { + last_invec.iov_base = next_buf; + last_invec.iov_len = (long)remainder_len; + + result = _sasl_encodev (conn, + &last_invec, + 1, + &num_packets, + output, + outputlen); + + if (result != SASL_OK) { + goto cleanup; + } + } + + if (numiov > 0) { + result = _sasl_encodev (conn, + invec, + numiov, + &num_packets, + output, + outputlen); + } + +cleanup: + if (cur_invec != NULL) { + sasl_FREE(cur_invec); + } + + 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) +{ + if (_sasl_allocation_locked++) return; + + _sasl_allocation_utils.malloc=m; + _sasl_allocation_utils.calloc=c; + _sasl_allocation_utils.realloc=r; + _sasl_allocation_utils.free=f; +} + +void sasl_common_done(void) +{ + /* NOTE - the caller will need to reinitialize the values, + if it is going to call sasl_client_init/sasl_server_init again. */ + if (default_plugin_path != NULL) { + sasl_FREE (default_plugin_path); + default_plugin_path = NULL; + } + if (default_conf_path != NULL) { + sasl_FREE (default_conf_path); + default_conf_path = NULL; + } + + _sasl_canonuser_free(); + _sasl_done_with_plugins(); + + sasl_MUTEX_FREE(free_mutex); + free_mutex = NULL; + + _sasl_free_utils(&sasl_global_utils); + + if (global_mech_list) { + sasl_FREE(global_mech_list); + global_mech_list = NULL; + } +} + +/* This function is for backward compatibility */ +void sasl_done(void) +{ + if (_sasl_server_cleanup_hook && _sasl_server_cleanup_hook() == SASL_OK) { + _sasl_server_idle_hook = NULL; + _sasl_server_cleanup_hook = NULL; + } + + if (_sasl_client_cleanup_hook && _sasl_client_cleanup_hook() == SASL_OK) { + _sasl_client_idle_hook = NULL; + _sasl_client_cleanup_hook = NULL; + } + + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return; + } + + sasl_common_done(); +} + +/* 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); + sasl_strlower (conn->serverFQDN); + } else if (conn->type == SASL_CONN_SERVER) { + /* We can fake it because we *are* the server */ + char name[MAXFQDNLEN]; + memset(name, 0, sizeof(name)); + if (get_fqhostname (name, MAXFQDNLEN, 0) != 0) { + return (SASL_FAIL); + } + + 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(sasl_global_callbacks_t *global_callbacks) +{ + int result; + + /* The last specified global callback always wins */ + if (sasl_global_utils != NULL) { + sasl_utils_t * global_utils = (sasl_utils_t *)sasl_global_utils; + global_utils->getopt = &_sasl_global_getopt; + global_utils->getopt_context = global_callbacks; + } + + /* Do nothing if we are already initialized */ + if (free_mutex) { + return SASL_OK; + } + + /* Setup the global utilities */ + if(!sasl_global_utils) { + sasl_global_utils = _sasl_alloc_utils(NULL, global_callbacks); + if(sasl_global_utils == NULL) return SASL_NOMEM; + } + + /* Init the canon_user plugin */ + result = sasl_canonuser_add_plugin("INTERNAL", internal_canonuser_init); + if(result != SASL_OK) return result; + + if (!free_mutex) { + free_mutex = sasl_MUTEX_ALLOC(); + } + if (!free_mutex) return SASL_FAIL; + + return SASL_OK; +} + +/* 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 */ + if (!free_mutex) { + free_mutex = sasl_MUTEX_ALLOC(); + if (!free_mutex) return; + } + + result = sasl_MUTEX_LOCK(free_mutex); + if (result!=SASL_OK) return; + + /* *pconn might have become NULL by now */ + if (*pconn) { + (*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); + + if (conn->multipacket_encoded_data.data) { + sasl_FREE(conn->multipacket_encoded_data.data); + } + + /* 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 or SASL context is NULL + */ +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, (sasl_callback_ft *)&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_APPNAME: + /* Currently we only support server side contexts, but we should + be able to extend this to support client side contexts as well */ + if(conn->type != SASL_CONN_SERVER) result = SASL_BADPROT; + else + *((const char **)pvalue) = ((sasl_server_conn_t *)conn)->sparams->appname; + 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->m.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->m.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->m.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->m.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_DELEGATEDCREDS: + /* We can't really distinguish between "no delegated credentials" + and "authentication not finished" */ + if(! conn->oparams.client_creds) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.client_creds; + break; + case SASL_GSS_PEER_NAME: + if(! conn->oparams.gss_peer_name) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.gss_peer_name; + break; + case SASL_GSS_LOCAL_NAME: + if(! conn->oparams.gss_local_name) + result = SASL_NOTDONE; + else + *((const char **)pvalue) = conn->oparams.gss_local_name; + 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; + case SASL_GSS_CREDS: + if(conn->type == SASL_CONN_CLIENT) + *(const void **)pvalue = + ((sasl_client_conn_t *)conn)->cparams->gss_creds; + else + *(const void **)pvalue = + ((sasl_server_conn_t *)conn)->sparams->gss_creds; + break; + case SASL_HTTP_REQUEST: { + if (conn->type == SASL_CONN_SERVER) + *(const sasl_http_request_t **)pvalue = + ((sasl_server_conn_t *)conn)->sparams->http_request; + else + *(const sasl_http_request_t **)pvalue = + ((sasl_client_conn_t *)conn)->cparams->http_request; + 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); + if(conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t*)conn)->sparams->external_ssf = + conn->external.ssf; + } else { + ((sasl_client_conn_t*)conn)->cparams->external_ssf = + conn->external.ssf; + } + break; + + case SASL_AUTH_EXTERNAL: + if(value && strlen(value)) { + result = _sasl_strdup(value, &str, NULL); + if(result != SASL_OK) MEMERROR(conn); + } else { + str = NULL; + } + + if(conn->external.auth_id) + sasl_FREE(conn->external.auth_id); + + conn->external.auth_id = str; + + break; + + case SASL_DEFUSERREALM: + if(conn->type != SASL_CONN_SERVER) { + 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); + } + + conn->props = *props; + + if(conn->type == SASL_CONN_SERVER) { + ((sasl_server_conn_t*)conn)->sparams->props = *props; + } else { + ((sasl_client_conn_t*)conn)->cparams->props = *props; + } + + break; + } + + case SASL_IPREMOTEPORT: + { + const char *ipremoteport = (const char *)value; + if(!value) { + conn->got_ip_remote = 0; + } 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 = + (unsigned) 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 = + (unsigned) 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 + = (unsigned) 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 + = (unsigned) 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; + } + + case SASL_APPNAME: + /* Currently we only support server side contexts, but we should + be able to extend this to support client side contexts as well */ + if(conn->type != SASL_CONN_SERVER) { + sasl_seterror(conn, 0, "Tried to set application name on non-server connection"); + result = SASL_BADPROT; + break; + } + + if(((sasl_server_conn_t *)conn)->appname) { + sasl_FREE(((sasl_server_conn_t *)conn)->appname); + ((sasl_server_conn_t *)conn)->appname = NULL; + } + + if(value && strlen(value)) { + result = _sasl_strdup(value, + &(((sasl_server_conn_t *)conn)->appname), + NULL); + if(result != SASL_OK) MEMERROR(conn); + ((sasl_server_conn_t *)conn)->sparams->appname = + ((sasl_server_conn_t *)conn)->appname; + ((sasl_server_conn_t *)conn)->sparams->applen = + (unsigned) strlen(((sasl_server_conn_t *)conn)->appname); + } else { + ((sasl_server_conn_t *)conn)->sparams->appname = NULL; + ((sasl_server_conn_t *)conn)->sparams->applen = 0; + } + break; + + case SASL_GSS_CREDS: + if(conn->type == SASL_CONN_CLIENT) + ((sasl_client_conn_t *)conn)->cparams->gss_creds = value; + else + ((sasl_server_conn_t *)conn)->sparams->gss_creds = value; + break; + + case SASL_CHANNEL_BINDING: { + const struct sasl_channel_binding *cb = (const struct sasl_channel_binding *)value; + + if (conn->type == SASL_CONN_SERVER) + ((sasl_server_conn_t *)conn)->sparams->cbinding = cb; + else + ((sasl_client_conn_t *)conn)->cparams->cbinding = cb; + break; + } + + case SASL_HTTP_REQUEST: { + const sasl_http_request_t *req = (const sasl_http_request_t *)value; + + if (conn->type == SASL_CONN_SERVER) + ((sasl_server_conn_t *)conn)->sparams->http_request = req; + else + ((sasl_client_conn_t *)conn)->cparams->http_request = req; + 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 information 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 is 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"; + case SASL_NEED_OLD_PASSWD: return "sasl_setpass needs old password in order " + "to perform password change"; + case SASL_CONSTRAINT_VIOLAT: return "sasl_setpass can't store a property because " + "of a constraint violation"; + case SASL_BADBINDING: return "channel binding failure"; + case SASL_CONFIGERR: return "error when parsing configuration file"; + + 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 = (unsigned) (strlen(leader) + strlen(conn->error_buf) + 12); + if (_buf_alloc(&conn->errdetail_buf, &conn->errdetail_buf_len, need_len) != SASL_OK) { + return NULL; + } + + 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) { + if (!callback->proc) return SASL_FAIL; + if (((sasl_getopt_t *)(callback->proc))(callback->context, + plugin_name, + option, + result, + len) + == SASL_OK) + return SASL_OK; + } + } + } + + /* look it up in our configuration file */ + *result = sasl_config_getstring(option, NULL); + if (*result != NULL) { + if (len) { *len = (unsigned) 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, + int priority, + const char *message) +{ + int syslog_priority; + sasl_server_conn_t *sconn; + + if (context) { + if (((sasl_conn_t *)context)->type == SASL_CONN_SERVER) { + sconn = (sasl_server_conn_t *)context; + if (sconn->sparams->log_level < priority) + return SASL_OK; + } + } + + /* 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; + + if (! context || ! result) return SASL_BADPARAM; + + 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]; + + TCHAR tsender[128]; + i = sizeof(tsender) / sizeof(tsender[0]); + rval = GetUserName(tsender, &i); + if ( rval) { /* got a userid */ + WideCharToMultiByte(CP_UTF8, 0, tsender, -1, sender, sizeof(sender), NULL, NULL); /* -1 ensures null-terminated utf8 */ + *result = sender; + if (len) *len = strlen(sender); + return SASL_OK; + } + } +#endif /* WIN32 */ + return SASL_FAIL; + default: + return SASL_BADPARAM; + } +} + +static int +_sasl_getpath(void *context __attribute__((unused)), + const char ** path_dest) +{ +#if !defined(WIN32) + char *path; +#endif + int res = SASL_OK; + + if (! path_dest) { + return SASL_BADPARAM; + } + + /* Only calculate the path once. */ + if (default_plugin_path == NULL) { + +#if defined(WIN32) + /* NB: On Windows platforms this value is always allocated */ + default_plugin_path = _sasl_get_default_win_path(context, + SASL_PLUGIN_PATH_ATTR, + PLUGINDIR); +#else + /* NB: On Unix platforms this value is never allocated */ + path = _sasl_get_default_unix_path(context, + SASL_PATH_ENV_VAR, + PLUGINDIR); + + res = _sasl_strdup(path, &default_plugin_path, NULL); +#endif + } + + if (res == SASL_OK) { + *path_dest = default_plugin_path; + } + + return res; +} + +static int +_sasl_getpath_simple(void *context __attribute__((unused)), + const char **path) +{ + if (! path) { + return SASL_BADPARAM; + } + + if (default_plugin_path == NULL) { + return SASL_FAIL; + } + + *path = default_plugin_path; + + return SASL_OK; +} + +static int +_sasl_getconfpath(void *context __attribute__((unused)), + char ** path_dest) +{ +#if !defined(WIN32) + char *path; +#endif + int res = SASL_OK; + + if (! path_dest) { + return SASL_BADPARAM; + } + + /* Only calculate the path once. */ + if (default_conf_path == NULL) { + +#if defined(WIN32) + /* NB: On Windows platforms this value is always allocated */ + default_conf_path = _sasl_get_default_win_path(context, + SASL_CONF_PATH_ATTR, + CONFIGDIR); +#else + /* NB: On Unix platforms this value is never allocated */ + path = _sasl_get_default_unix_path(context, + SASL_CONF_PATH_ENV_VAR, + CONFIGDIR); + + res = _sasl_strdup(path, &default_conf_path, NULL); +#endif + } + + if (res == SASL_OK) { + *path_dest = default_conf_path; + } + + return res; +} + +static int +_sasl_getconfpath_simple(void *context __attribute__((unused)), + const char **path) +{ + if (! path) { + return SASL_BADPARAM; + } + + if (default_conf_path == NULL) { + return SASL_FAIL; + } + + *path = default_conf_path; + + return SASL_OK; +} + + +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, + sasl_callback_ft *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_callback_ft)&_sasl_conn_getopt; + *pcontext = conn; + } else { + *pproc = (sasl_callback_ft)&_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 = (sasl_callback_ft)&_sasl_syslog; + *pcontext = conn; + return SASL_OK; +#endif /* HAVE_SYSLOG */ + case SASL_CB_GETPATH: + *pproc = default_getpath_cb.proc; + *pcontext = default_getpath_cb.context; + return SASL_OK; + case SASL_CB_GETCONFPATH: + *pproc = default_getconfpath_cb.proc; + *pcontext = default_getconfpath_cb.context; + return SASL_OK; + case SASL_CB_AUTHNAME: + *pproc = (sasl_callback_ft)&_sasl_getsimple; + *pcontext = conn; + return SASL_OK; + case SASL_CB_VERIFYFILE: + *pproc = (sasl_callback_ft)&_sasl_verifyfile; + *pcontext = NULL; + return SASL_OK; + case SASL_CB_PROXY_POLICY: + *pproc = (sasl_callback_ft)&_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 = NULL; + 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; + unsigned int uval; + char *cval; + va_list ap; /* varargs thing */ + + if(!fmt) return; + + out = (char *) sasl_ALLOC(250); + if(!out) return; + + formatlen = strlen(fmt); + + /* See if we have a logging callback... */ + result = _sasl_getcallback(conn, SASL_CB_LOG, (sasl_callback_ft *)&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(pos<formatlen) + { + if (fmt[pos]!='%') /* regular character */ + { + result = _buf_alloc(&out, &alloclen, outlen+1); + if (result != SASL_OK) goto done; + out[outlen]=fmt[pos]; + outlen++; + pos++; + + } else { /* formating thing */ + int done=0; + char frmt[10]; + int frmtpos=1; + char tempbuf[21]; + frmt[0]='%'; + pos++; + + while (done==0) + { + switch(fmt[pos]) + { + case 's': /* need to handle this */ + cval = va_arg(ap, char *); /* get the next arg */ + result = _sasl_add_string(&out, &alloclen, + &outlen, cval); + + if (result != SASL_OK) /* add the string */ + goto done; + + done=1; + break; + + case '%': /* double % output the '%' character */ + result = _buf_alloc(&out,&alloclen,outlen+1); + if (result != SASL_OK) + goto done; + + out[outlen]='%'; + outlen++; + done=1; + break; + + case 'm': /* insert the errno string */ + result = _sasl_add_string(&out, &alloclen, &outlen, + strerror(va_arg(ap, int))); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'z': /* insert the sasl error string */ + result = _sasl_add_string(&out, &alloclen, &outlen, + (char *) sasl_errstring(va_arg(ap, int),NULL,NULL)); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'c': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + tempbuf[0] = (char) va_arg(ap, int); /* get the next arg */ + tempbuf[1]='\0'; + + /* now add the character */ + result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'd': + case 'i': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + ival = va_arg(ap, int); /* get the next arg */ + + snprintf(tempbuf,20,frmt,ival); /* have snprintf do the work */ + /* now add the string */ + result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf); + if (result != SASL_OK) + goto done; + + done=1; + break; + + case 'o': + case 'u': + case 'x': + case 'X': + frmt[frmtpos++]=fmt[pos]; + frmt[frmtpos]=0; + uval = va_arg(ap, unsigned int); /* get the next arg */ + + snprintf(tempbuf,20,frmt,uval); /* have snprintf do the work */ + /* now add the string */ + result = _sasl_add_string(&out, &alloclen, &outlen, tempbuf); + if (result != SASL_OK) + goto done; + + done=1; + break; + + default: + frmt[frmtpos++]=fmt[pos]; /* add to the formating */ + frmt[frmtpos]=0; + if (frmtpos>9) + 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; + + /* send log message */ + result = log_cb(log_ctx, level, out); + + done: + va_end(ap); + 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; + utils->auxprop_store=&sasl_auxprop_store; +#endif + + /* Spares */ + utils->spare_fptr = NULL; + utils->spare_fptr1 = utils->spare_fptr2 = 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; +} + +static const sasl_callback_t * +_sasl_find_callback_by_type (const sasl_callback_t *callbacks, + unsigned long id) +{ + if (callbacks) { + while (callbacks->id != SASL_CB_LIST_END) { + if (callbacks->id == id) { + return callbacks; + } else { + ++callbacks; + } + } + } + return NULL; +} + +const sasl_callback_t * +_sasl_find_getpath_callback(const sasl_callback_t *callbacks) +{ + callbacks = _sasl_find_callback_by_type (callbacks, SASL_CB_GETPATH); + if (callbacks != NULL) { + return callbacks; + } else { + return &default_getpath_cb; + } +} + +const sasl_callback_t * +_sasl_find_getconfpath_callback(const sasl_callback_t *callbacks) +{ + callbacks = _sasl_find_callback_by_type (callbacks, SASL_CB_GETCONFPATH); + if (callbacks != NULL) { + return callbacks; + } else { + 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 = { + SASL_CB_VERIFYFILE, + (sasl_callback_ft)&_sasl_verifyfile, + NULL + }; + + callbacks = _sasl_find_callback_by_type (callbacks, SASL_CB_VERIFYFILE); + if (callbacks != NULL) { + return callbacks; + } else { + 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((unsigned)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; + + /* WARN - We will leak the old buffer on failure */ + *rwbuf = sasl_REALLOC(*rwbuf, (unsigned)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; i < numiov; i++) { + out->curlen += vec[i].iov_len; + } + + ret = _buf_alloc(&out->data, &out->reallen, out->curlen); + + if (ret != SASL_OK) return SASL_NOMEM; + + memset(out->data, 0, out->reallen); + pos = out->data; + + for (i = 0; i < numiov; i++) { + memcpy(pos, vec[i].iov_base, vec[i].iov_len); + pos += vec[i].iov_len; + } + + return SASL_OK; +} + +/* This code might be useful in the future, but it isn't now, so.... */ +#if 0 +int _sasl_iptostring(const struct sockaddr *addr, socklen_t addrlen, + char *out, unsigned outlen) { + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + int niflags; + + if(!addr || !out) return SASL_BADPARAM; + + niflags = (NI_NUMERICHOST | NI_NUMERICSERV); +#ifdef NI_WITHSCOPEID + if (addr->sa_family == AF_INET6) + niflags |= NI_WITHSCOPEID; +#endif + if (getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), + niflags) != 0) + return SASL_BADPARAM; + + if(outlen < strlen(hbuf) + strlen(pbuf) + 2) + return SASL_BUFOVER; + + snprintf(out, outlen, "%s;%s", hbuf, pbuf); + + return SASL_OK; +} +#endif + +int _sasl_ipfromstring(const char *addr, + struct sockaddr *out, socklen_t outlen) +{ + int i, j; + struct addrinfo hints, *ai = NULL; + char hbuf[NI_MAXHOST]; + + /* A NULL out pointer just implies we don't do a copy, just verify it */ + + if(!addr) return SASL_BADPARAM; + + /* Parse the address */ + for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) { + if (i >= NI_MAXHOST) + return SASL_BADPARAM; + hbuf[i] = addr[i]; + } + hbuf[i] = '\0'; + + if (addr[i] == ';') + i++; + /* XXX: Do we need this check? */ + for (j = i; addr[j] != '\0'; j++) + if (!isdigit((int)(addr[j]))) + return SASL_BADPARAM; + + 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 = p; + p->next = NULL; + } else { + sasl_FREE(p); + } + } + + olist = clist; + } + + if(!olist) { + /* This is not going to be very useful */ + 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 (const char **)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); +} + +int _sasl_is_equal_mech(const char *req_mech, + const char *plug_mech, + size_t req_mech_len, + int *plus) +{ + size_t n; + + if (req_mech_len > 5 && + strcasecmp(&req_mech[req_mech_len - 5], "-PLUS") == 0) { + n = req_mech_len - 5; + *plus = 1; + } else { + n = req_mech_len; + *plus = 0; + } + + if (n < strlen(plug_mech)) { + /* Don't allow arbitrary prefix match */ + return 0; + } + + return (strncasecmp(req_mech, plug_mech, n) == 0); +} + +#ifndef WIN32 +static char * +_sasl_get_default_unix_path(void *context __attribute__((unused)), + char * env_var_name, + char * default_value) +{ + char *path = NULL; + + /* Honor external variable only in a safe environment */ + if (getuid() == geteuid() && getgid() == getegid()) { + path = getenv(env_var_name); + } + if (! path) { + path = default_value; + } + + return path; +} + +#else /*WIN32*/ +/* Return NULL on failure */ +static char * +_sasl_get_default_win_path(void *context __attribute__((unused)), + TCHAR * reg_attr_name, + char * default_value) +{ + /* 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 in bytes and later number of wchars */ + TCHAR * ValueData; /* value */ + DWORD cbExpandedData; /* "expanded" value size in wchars */ + TCHAR * ExpandedValueData; /* "expanded" value */ + TCHAR * return_value; /* function return value */ + TCHAR * 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 */ + char *ret; + (void) _sasl_strdup (default_value, &ret, NULL); + return ret; + } + + /* figure out value type and required buffer size */ + /* the size will include space for terminating NUL if required */ + RegQueryValueEx (hKey, + reg_attr_name, + 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 + 2 * sizeof(TCHAR)); /* extra bytes to insert null-terminator if it's missed */ + if (ValueData == NULL) { + return_value = NULL; + goto CLEANUP; + }; + + if (RegQueryValueEx(hKey, + reg_attr_name, + NULL, /* reserved */ + &ValueType, + (LPBYTE)ValueData, + &cbData) != ERROR_SUCCESS) { + return_value = NULL; + goto CLEANUP; + } + cbData /= sizeof(TCHAR); /* covert to number of symbols */ + ValueData[cbData] = '\0'; /* MS docs say we have to to that */ + ValueData[cbData + 1] = '\0'; /* for MULTI */ + + switch (ValueType) { + case REG_EXPAND_SZ: + /* : A random starting guess */ + cbExpandedData = cbData + 1024; + ExpandedValueData = (TCHAR*)sasl_ALLOC(cbExpandedData * sizeof(TCHAR)); + 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 * sizeof(TCHAR)); + 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 += (_tcslen(tmp)); + } + break; + + case REG_SZ: + /* Do nothing, it is good as is */ + break; + + default: + return_value = NULL; + goto CLEANUP; + } + + return_value = ValueData; /* just to flag we have a result */ + +CLEANUP: + RegCloseKey(hKey); + if (ExpandedValueData != NULL) sasl_FREE(ExpandedValueData); + if (return_value == NULL) { + if (ValueData != NULL) sasl_FREE(ValueData); + return NULL; + } + if (sizeof(TCHAR) == sizeof(char)) { + return (char*)return_value; + } + + /* convert to utf-8 for compatibility with other OS' */ + { + char *tmp = _sasl_wchar_to_utf8(return_value); + sasl_FREE(return_value); + return tmp; + } +} + +char* _sasl_wchar_to_utf8(WCHAR *str) +{ + size_t bufLen = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + char *buf = sasl_ALLOC(bufLen); + if (buf) { + if (WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, bufLen, NULL, NULL) == 0) { /* -1 ensures null-terminated utf8 */ + sasl_FREE(buf); + buf = NULL; + } + } + return buf; +} + +WCHAR* _sasl_utf8_to_wchar(const char *str) +{ + size_t bufLen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR *buf = sasl_ALLOC(bufLen * sizeof(WCHAR)); + if (buf) { + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, bufLen) == 0) { /* -1 ensures null-terminated utf8 */ + sasl_FREE(buf); + buf = NULL; + } + } + return buf; +} + +#endif /*WIN32*/ |