diff options
author | molotkov-and <molotkov-and@ydb.tech> | 2023-08-18 17:20:47 +0300 |
---|---|---|
committer | molotkov-and <molotkov-and@ydb.tech> | 2023-08-18 19:42:07 +0300 |
commit | 73215359bc33e76f5b94d1832a377072bf245cfc (patch) | |
tree | 9cb8ad61d8c3cd107353d42951560ff3cf1b966d /contrib/libs/sasl/lib | |
parent | 1cbfd34a55732f7b1d407986b45e40853f01f2c2 (diff) | |
download | ydb-73215359bc33e76f5b94d1832a377072bf245cfc.tar.gz |
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')
-rw-r--r-- | contrib/libs/sasl/lib/auxprop.c | 1202 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/canonusr.c | 465 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/checkpw.c | 1110 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/client.c | 1317 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/common.c | 2674 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/config.c | 168 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/dlopen.c | 569 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/external.c | 407 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/md5.c | 527 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/saslint.h | 544 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/saslutil.c | 790 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/server.c | 2406 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/seterror.c | 263 | ||||
-rw-r--r-- | contrib/libs/sasl/lib/staticopen.h | 188 |
14 files changed, 12630 insertions, 0 deletions
diff --git a/contrib/libs/sasl/lib/auxprop.c b/contrib/libs/sasl/lib/auxprop.c new file mode 100644 index 0000000000..1b0162db74 --- /dev/null +++ b/contrib/libs/sasl/lib/auxprop.c @@ -0,0 +1,1202 @@ +/* auxprop.c - auxilliary property support + * Rob Siemborski + */ +/* + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <sasl.h> +#include <prop.h> +#include <ctype.h> +#include <stdio.h> +#include "saslint.h" + +struct proppool +{ + struct proppool *next; + + size_t size; /* Size of Block */ + size_t unused; /* Space unused in this pool between end + * of char** area and beginning of char* area */ + + char data[1]; /* Variable Sized */ +}; + +struct propctx { + struct propval *values; + struct propval *prev_val; /* Previous value used by set/setvalues */ + + unsigned used_values, allocated_values; + + char *data_end; /* Bottom of string area in current pool */ + char **list_end; /* Top of list area in current pool */ + + struct proppool *mem_base; + struct proppool *mem_cur; +}; + +typedef struct auxprop_plug_list +{ + struct auxprop_plug_list *next; + const sasl_auxprop_plug_t *plug; +} auxprop_plug_list_t; + +static auxprop_plug_list_t *auxprop_head = NULL; + +static struct proppool *alloc_proppool(size_t size) +{ + struct proppool *ret; + /* minus 1 for the one that is already a part of the array + * in the struct */ + size_t total_size = sizeof(struct proppool) + size - 1; + ret = sasl_ALLOC(total_size); + if(!ret) return NULL; + + memset(ret, 0, total_size); + + ret->size = ret->unused = size; + + return ret; +} + +/* Resize a proppool. Invalidates the unused value for this pool */ +static struct proppool *resize_proppool(struct proppool *pool, size_t size) +{ + struct proppool *ret; + + if(pool->size >= size) return pool; + ret = sasl_REALLOC(pool, sizeof(struct proppool) + size); + if(!ret) return NULL; + + ret->size = size; + + return ret; +} + +static int prop_init(struct propctx *ctx, unsigned estimate) +{ + const unsigned VALUES_SIZE = PROP_DEFAULT * sizeof(struct propval); + + ctx->mem_base = alloc_proppool(VALUES_SIZE + estimate); + if(!ctx->mem_base) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_base; + + ctx->values = (struct propval *)ctx->mem_base->data; + ctx->mem_base->unused = ctx->mem_base->size - VALUES_SIZE; + ctx->allocated_values = PROP_DEFAULT; + ctx->used_values = 0; + + ctx->data_end = ctx->mem_base->data + ctx->mem_base->size; + ctx->list_end = (char **)(ctx->mem_base->data + VALUES_SIZE); + + ctx->prev_val = NULL; + + return SASL_OK; +} + +/* create a property context + * estimate -- an estimate of the storage needed for requests & responses + * 0 will use module default + * returns NULL on error + */ +struct propctx *prop_new(unsigned estimate) +{ + struct propctx *new_ctx; + + if(!estimate) estimate = PROP_DEFAULT * 255; + + new_ctx = sasl_ALLOC(sizeof(struct propctx)); + if(!new_ctx) return NULL; + + if(prop_init(new_ctx, estimate) != SASL_OK) { + prop_dispose(&new_ctx); + } + + return new_ctx; +} + +/* create new propctx which duplicates the contents of an existing propctx + * returns -1 on error + */ +int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx) +{ + struct proppool *pool; + struct propctx *retval = NULL; + unsigned i; + int result; + unsigned total_size = 0; + size_t values_size; + + if(!src_ctx || !dst_ctx) return SASL_BADPARAM; + + /* What is the total allocated size of src_ctx? */ + pool = src_ctx->mem_base; + while(pool) { + total_size += (unsigned) pool->size; + pool = pool->next; + } + + /* allocate the new context */ + retval = prop_new(total_size); + if(!retval) return SASL_NOMEM; + + retval->used_values = src_ctx->used_values; + retval->allocated_values = src_ctx->used_values + 1; + + values_size = (retval->allocated_values * sizeof(struct propval)); + + retval->mem_base->unused = retval->mem_base->size - values_size; + + retval->list_end = (char **)(retval->mem_base->data + values_size); + /* data_end should still be OK */ + + /* Now dup the values */ + for(i=0; i<src_ctx->used_values; i++) { + retval->values[i].name = src_ctx->values[i].name; + result = prop_setvals(retval, retval->values[i].name, + src_ctx->values[i].values); + if(result != SASL_OK) + goto fail; + } + + retval->prev_val = src_ctx->prev_val; + + *dst_ctx = retval; + return SASL_OK; + + fail: + if(retval) prop_dispose(&retval); + return result; +} + +/* + * dispose of property context + * ctx -- is disposed and set to NULL; noop if ctx or *ctx is NULL + */ +void prop_dispose(struct propctx **ctx) +{ + struct proppool *tmp; + + if(!ctx || !*ctx) return; + + while((*ctx)->mem_base) { + tmp = (*ctx)->mem_base; + (*ctx)->mem_base = tmp->next; + sasl_FREE(tmp); + } + + sasl_FREE(*ctx); + *ctx = NULL; + + return; +} + +/* Add property names to request + * ctx -- context from prop_new() + * names -- list of property names; must persist until context freed + * or requests cleared + * + * NOTE: may clear values from context as side-effect + * returns -1 on error + */ +int prop_request(struct propctx *ctx, const char **names) +{ + unsigned i, new_values, total_values; + + if(!ctx || !names) return SASL_BADPARAM; + + /* Count how many we need to add */ + for(new_values=0; names[new_values]; new_values++); + + /* Do we need to add ANY? */ + if(!new_values) return SASL_OK; + + /* We always want at least one extra to mark the end of the array */ + total_values = new_values + ctx->used_values + 1; + + /* Do we need to increase the size of our propval table? */ + if(total_values > ctx->allocated_values) { + unsigned max_in_pool; + + /* Do we need a larger base pool? */ + max_in_pool = (unsigned) (ctx->mem_base->size / sizeof(struct propval)); + + if(total_values <= max_in_pool) { + /* Don't increase the size of the base pool, just use what + we need */ + ctx->allocated_values = total_values; + ctx->mem_base->unused = + ctx->mem_base->size - (sizeof(struct propval) + * ctx->allocated_values); + } else { + /* We need to allocate more! */ + unsigned new_alloc_length; + size_t new_size; + + new_alloc_length = 2 * ctx->allocated_values; + while(total_values > new_alloc_length) { + new_alloc_length *= 2; + } + + new_size = new_alloc_length * sizeof(struct propval); + ctx->mem_base = resize_proppool(ctx->mem_base, new_size); + + if(!ctx->mem_base) { + ctx->values = NULL; + ctx->allocated_values = ctx->used_values = 0; + return SASL_NOMEM; + } + + /* It worked! Update the structure! */ + ctx->values = (struct propval *)ctx->mem_base->data; + ctx->allocated_values = new_alloc_length; + ctx->mem_base->unused = ctx->mem_base->size + - sizeof(struct propval) * ctx->allocated_values; + } + + /* Clear out new propvals */ + memset(&(ctx->values[ctx->used_values]), 0, + sizeof(struct propval) * (ctx->allocated_values - ctx->used_values)); + + /* Finish updating the context -- we've extended the list! */ + /* ctx->list_end = (char **)(ctx->values + ctx->allocated_values); */ + /* xxx test here */ + ctx->list_end = (char **)(ctx->values + total_values); + } + + /* Now do the copy, or referencing rather */ + for(i=0;i<new_values;i++) { + unsigned j, flag; + + flag = 0; + + /* Check for dups */ + for(j=0;j<ctx->used_values;j++) { + if(!strcmp(ctx->values[j].name, names[i])) { + flag = 1; + break; + } + } + + /* We already have it... skip! */ + if(flag) continue; + + ctx->values[ctx->used_values++].name = names[i]; + } + + prop_clear(ctx, 0); + + return SASL_OK; +} + +/* return array of struct propval from the context + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + */ +const struct propval *prop_get(struct propctx *ctx) +{ + if(!ctx) return NULL; + + return ctx->values; +} + +/* Fill in an array of struct propval based on a list of property names + * return value persists until next call to + * prop_request, prop_clear or prop_dispose on context + * returns -1 on error (no properties ever requested, ctx NULL, etc) + * returns number of matching properties which were found (values != NULL) + * if a name requested here was never requested by a prop_request, then + * the name field of the associated vals entry will be set to NULL + */ +int prop_getnames(struct propctx *ctx, const char **names, + struct propval *vals) +{ + int found_names = 0; + + struct propval *cur = vals; + const char **curname; + + if(!ctx || !names || !vals) return SASL_BADPARAM; + + for(curname = names; *curname; curname++) { + struct propval *val; + for(val = ctx->values; val->name; val++) { + if(!strcmp(*curname,val->name)) { + found_names++; + memcpy(cur, val, sizeof(struct propval)); + goto next; + } + } + + /* If we are here, we didn't find it */ + memset(cur, 0, sizeof(struct propval)); + + next: + cur++; + } + + return found_names; +} + + +/* clear values and optionally requests from property context + * ctx -- property context + * requests -- 0 = don't clear requests, 1 = clear requests + */ +void prop_clear(struct propctx *ctx, int requests) +{ + struct proppool *new_pool, *tmp; + unsigned i; + + /* We're going to need a new proppool once we reset things */ + new_pool = alloc_proppool(ctx->mem_base->size + + (ctx->used_values+1) * sizeof(struct propval)); + if (new_pool == NULL) { + _sasl_log(NULL, SASL_LOG_ERR, "failed to allocate memory\n"); + exit(1); + } + + if(requests) { + /* We're wiping the whole shebang */ + ctx->used_values = 0; + } else { + /* Need to keep around old requets */ + struct propval *new_values = (struct propval *)new_pool->data; + for(i=0; i<ctx->used_values; i++) { + new_values[i].name = ctx->values[i].name; + } + } + + while(ctx->mem_base) { + tmp = ctx->mem_base; + ctx->mem_base = tmp->next; + sasl_FREE(tmp); + } + + /* Update allocation-related metadata */ + ctx->allocated_values = ctx->used_values+1; + new_pool->unused = + new_pool->size - (ctx->allocated_values * sizeof(struct propval)); + + /* Setup pointers for the values array */ + ctx->values = (struct propval *)new_pool->data; + ctx->prev_val = NULL; + + /* Setup the pools */ + ctx->mem_base = ctx->mem_cur = new_pool; + + /* Reset list_end and data_end for the new memory pool */ + ctx->list_end = + (char **)((char *)ctx->mem_base->data + ctx->allocated_values * sizeof(struct propval)); + ctx->data_end = (char *)ctx->mem_base->data + ctx->mem_base->size; + + return; +} + +/* + * erase the value of a property + */ +void prop_erase(struct propctx *ctx, const char *name) +{ + struct propval *val; + int i; + + if(!ctx || !name) return; + + for(val = ctx->values; val->name; val++) { + if(!strcmp(name,val->name)) { + if(!val->values) break; + + /* + * Yes, this is casting away the const, but + * we should be okay because the only place this + * memory should be is in the proppool's + */ + for(i=0;val->values[i];i++) { + memset((void *)(val->values[i]),0,strlen(val->values[i])); + val->values[i] = NULL; + } + + val->values = NULL; + val->nvalues = 0; + val->valsize = 0; + break; + } + } + + return; +} + +/****fetcher interfaces****/ + +/* format the requested property names into a string + * ctx -- context from prop_new()/prop_request() + * sep -- separator between property names (unused if none requested) + * seplen -- length of separator, if < 0 then strlen(sep) will be used + * outbuf -- output buffer + * outmax -- maximum length of output buffer including NUL terminator + * outlen -- set to length of output string excluding NUL terminator + * returns 0 on success and amount of additional space needed on failure + */ +int prop_format(struct propctx *ctx, const char *sep, int seplen, + char *outbuf, unsigned outmax, unsigned *outlen) +{ + unsigned needed, flag = 0; + struct propval *val; + + if (!ctx || !outbuf) return SASL_BADPARAM; + + if (!sep) seplen = 0; + if (seplen < 0) seplen = (int) strlen(sep); +/* If seplen is negative now we have overflow. + But if you have a string longer than 2Gb, you are an idiot anyway */ + if (seplen < 0) return SASL_BADPARAM; + + needed = seplen * (ctx->used_values - 1); + for(val = ctx->values; val->name; val++) { + needed += (unsigned) strlen(val->name); + } + + if(!outmax) return (needed + 1); /* Because of unsigned funkiness */ + if(needed > (outmax - 1)) return (needed - (outmax - 1)); + + *outbuf = '\0'; + if(outlen) *outlen = needed; + + if(needed == 0) return SASL_OK; + + for(val = ctx->values; val->name; val++) { + if(seplen && flag) { + strncat(outbuf, sep, seplen); + } else { + flag = 1; + } + strcat(outbuf, val->name); + } + + return SASL_OK; +} + +/* add a property value to the context + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * value -- a value for the property; will be copied into context + * if NULL, remove existing values + * vallen -- length of value, if <= 0 then strlen(value) will be used + */ +int prop_set(struct propctx *ctx, const char *name, + const char *value, int vallen) +{ + struct propval *cur; + + if(!ctx) return SASL_BADPARAM; + if(!name && !ctx->prev_val) return SASL_BADPARAM; + + if(name) { + struct propval *val; + + ctx->prev_val = NULL; + + for(val = ctx->values; val->name; val++) { + if(!strcmp(name,val->name)){ + ctx->prev_val = val; + break; + } + } + + /* Couldn't find it! */ + if(!ctx->prev_val) return SASL_BADPARAM; + } + + cur = ctx->prev_val; + + if(name) /* New Entry */ { + unsigned nvalues = 1; /* 1 for NULL entry */ + const char **old_values = NULL; + char **tmp, **tmp2; + size_t size; + + if(cur->values) { + + if(!value) { + /* If we would be adding a null value, then we are done */ + return SASL_OK; + } + + old_values = cur->values; + tmp = (char **)cur->values; + while(*tmp) { + nvalues++; + tmp++; + } + + } + + if(value) { + nvalues++; /* for the new value */ + } + + size = nvalues * sizeof(char*); + + if(size > ctx->mem_cur->unused) { + size_t needed; + + for(needed = ctx->mem_cur->size * 2; needed < size; needed *= 2); + + /* Allocate a new proppool */ + ctx->mem_cur->next = alloc_proppool(needed); + if(!ctx->mem_cur->next) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_cur->next; + + ctx->list_end = (char **)ctx->mem_cur->data; + ctx->data_end = ctx->mem_cur->data + needed; + } + + /* Grab the memory */ + ctx->mem_cur->unused -= size; + cur->values = (const char **)ctx->list_end; + cur->values[nvalues - 1] = NULL; + + /* Finish updating the context */ + ctx->list_end = (char **)(cur->values + nvalues); + + /* If we don't have an actual value to fill in, we are done */ + if(!value) + return SASL_OK; + + tmp2 = (char **)cur->values; + if(old_values) { + tmp = (char **)old_values; + + while(*tmp) { + *tmp2 = *tmp; + tmp++; tmp2++; + } + } + + /* Now allocate the last entry */ + if(vallen <= 0) + size = (size_t)(strlen(value) + 1); + else + size = (size_t)(vallen + 1); + + if(size > ctx->mem_cur->unused) { + size_t needed; + + needed = ctx->mem_cur->size * 2; + + while(needed < size) { + needed *= 2; + } + + /* Allocate a new proppool */ + ctx->mem_cur->next = alloc_proppool(needed); + if(!ctx->mem_cur->next) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_cur->next; + ctx->list_end = (char **)ctx->mem_cur->data; + ctx->data_end = ctx->mem_cur->data + needed; + } + + /* Update the data_end pointer */ + ctx->data_end -= size; + ctx->mem_cur->unused -= size; + + /* Copy and setup the new value! */ + memcpy(ctx->data_end, value, size-1); + ctx->data_end[size - 1] = '\0'; + cur->values[nvalues - 2] = ctx->data_end; + + cur->nvalues++; + cur->valsize += ((unsigned) size - 1); + } else /* Appending an entry */ { + char **tmp; + size_t size; + + /* If we are setting it to be NULL, we are done */ + if(!value) return SASL_OK; + + size = sizeof(char*); + + /* Is it in the current pool, and will it fit in the unused space? */ + if(size > ctx->mem_cur->unused && + (void *)cur->values > (void *)(ctx->mem_cur->data) && + (void *)cur->values < (void *)(ctx->mem_cur->data + ctx->mem_cur->size)) { + /* recursively call the not-fast way */ + return prop_set(ctx, cur->name, value, vallen); + } + + /* Note the invariant: the previous value list must be + at the top of the CURRENT pool at this point */ + + /* Grab the memory */ + ctx->mem_cur->unused -= size; + ctx->list_end++; + + *(ctx->list_end - 1) = NULL; + tmp = (ctx->list_end - 2); + + /* Now allocate the last entry */ + if(vallen <= 0) + size = strlen(value) + 1; + else + size = vallen + 1; + + if(size > ctx->mem_cur->unused) { + size_t needed; + + needed = ctx->mem_cur->size * 2; + + while(needed < size) { + needed *= 2; + } + + /* Allocate a new proppool */ + ctx->mem_cur->next = alloc_proppool(needed); + if(!ctx->mem_cur->next) return SASL_NOMEM; + + ctx->mem_cur = ctx->mem_cur->next; + ctx->list_end = (char **)ctx->mem_cur->data; + ctx->data_end = ctx->mem_cur->data + needed; + } + + /* Update the data_end pointer */ + ctx->data_end -= size; + ctx->mem_cur->unused -= size; + + /* Copy and setup the new value! */ + memcpy(ctx->data_end, value, size-1); + ctx->data_end[size - 1] = '\0'; + *tmp = ctx->data_end; + + cur->nvalues++; + cur->valsize += ((unsigned) size - 1); + } + + return SASL_OK; +} + + +/* set the values for a property + * ctx -- context from prop_new()/prop_request() + * name -- name of property to which value will be added + * if NULL, add to the same name as previous prop_set/setvals call + * values -- array of values, ending in NULL. Each value is a NUL terminated + * string + */ +int prop_setvals(struct propctx *ctx, const char *name, + const char **values) +{ + const char **val = values; + int result = SASL_OK; + + if(!ctx) return SASL_BADPARAM; + + /* If they want us to add no values, we can do that */ + if(!values) return SASL_OK; + + /* Basically, use prop_set to do all our dirty work for us */ + if(name) { + result = prop_set(ctx, name, *val, 0); + val++; + } + + for(;*val;val++) { + if(result != SASL_OK) return result; + result = prop_set(ctx, NULL, *val,0); + } + + return result; +} + +/* Request a set of auxiliary properties + * conn connection context + * propnames list of auxiliary property names to request ending with + * NULL. + * + * Subsequent calls will add items to the request list. Call with NULL + * to clear the request list. + * + * errors + * SASL_OK -- success + * SASL_BADPARAM -- bad count/conn parameter + * SASL_NOMEM -- out of memory + */ +int sasl_auxprop_request(sasl_conn_t *conn, const char **propnames) +{ + int result; + sasl_server_conn_t *sconn; + + if(!conn) return SASL_BADPARAM; + if(conn->type != SASL_CONN_SERVER) + PARAMERROR(conn); + + sconn = (sasl_server_conn_t *)conn; + + if(!propnames) { + prop_clear(sconn->sparams->propctx,1); + return SASL_OK; + } + + result = prop_request(sconn->sparams->propctx, propnames); + RETURN(conn, result); +} + + +/* Returns current auxiliary property context. + * Use functions in prop.h to access content + * + * if authentication hasn't completed, property values may be empty/NULL + * + * properties not recognized by active plug-ins will be left empty/NULL + * + * returns NULL if conn is invalid. + */ +struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn) +{ + sasl_server_conn_t *sconn; + + if(!conn || conn->type != SASL_CONN_SERVER) return NULL; + + sconn = (sasl_server_conn_t *)conn; + + return sconn->sparams->propctx; +} + +/* add an auxiliary property plugin */ +int sasl_auxprop_add_plugin(const char *plugname, + sasl_auxprop_init_t *auxpropfunc) +{ + int result, out_version; + auxprop_plug_list_t *new_item; + sasl_auxprop_plug_t *plug; + + result = auxpropfunc(sasl_global_utils, SASL_AUXPROP_PLUG_VERSION, + &out_version, &plug, plugname); + + /* Check if out_version is too old. + We only support the current at the moment */ + if (result == SASL_OK && out_version < SASL_AUXPROP_PLUG_VERSION) { + result = SASL_BADVERS; + } + + if(result != SASL_OK) { + _sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %s\n", + sasl_errstring(result, NULL, NULL)); + return result; + } + + /* We require that this function is implemented */ + if(!plug->auxprop_lookup) return SASL_BADPROT; + + new_item = sasl_ALLOC(sizeof(auxprop_plug_list_t)); + if(!new_item) return SASL_NOMEM; + + /* These will load from least-important to most important */ + new_item->plug = plug; + new_item->next = auxprop_head; + auxprop_head = new_item; + + return SASL_OK; +} + +void _sasl_auxprop_free() +{ + auxprop_plug_list_t *ptr, *ptr_next; + + for(ptr = auxprop_head; ptr; ptr = ptr_next) { + ptr_next = ptr->next; + if(ptr->plug->auxprop_free) + ptr->plug->auxprop_free(ptr->plug->glob_context, + sasl_global_utils); + sasl_FREE(ptr); + } + + auxprop_head = NULL; +} + +/* Return the updated account status based on the current ("so far") and + the specific status returned by the latest auxprop call */ +static int +_sasl_account_status (int current_status, + int specific_status) +{ + switch (specific_status) { + case SASL_NOVERIFY: + specific_status = SASL_OK; + /* fall through */ + case SASL_OK: + if (current_status == SASL_NOMECH || + current_status == SASL_NOUSER) { + current_status = specific_status; + } + break; + + case SASL_NOUSER: + if (current_status == SASL_NOMECH) { + current_status = specific_status; + } + break; + + /* NOTE: The disabled flag sticks, unless we hit an error */ + case SASL_DISABLED: + if (current_status == SASL_NOMECH || + current_status == SASL_NOUSER || + current_status == SASL_OK) { + current_status = specific_status; + } + break; + + case SASL_NOMECH: + /* ignore */ + break; + + /* SASL_UNAVAIL overrides everything */ + case SASL_UNAVAIL: + current_status = specific_status; + break; + + default: + current_status = specific_status; + break; + } + return (current_status); +} + +/* Do the callbacks for auxprop lookups */ +int _sasl_auxprop_lookup(sasl_server_params_t *sparams, + unsigned flags, + const char *user, unsigned ulen) +{ + sasl_getopt_t *getopt; + int ret, found = 0; + void *context; + const char *plist = NULL; + auxprop_plug_list_t *ptr; + int result = SASL_NOMECH; + + if(_sasl_getcallback(sparams->utils->conn, + SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, + &context) == SASL_OK) { + ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL); + if(ret != SASL_OK) plist = NULL; + } + + if(!plist) { + /* Do lookup in all plugins */ + + /* TODO: Ideally, each auxprop plugin should be marked if its failure + should be ignored or treated as a fatal error of the whole lookup. */ + for(ptr = auxprop_head; ptr; ptr = ptr->next) { + found=1; + ret = ptr->plug->auxprop_lookup(ptr->plug->glob_context, + sparams, flags, user, ulen); + result = _sasl_account_status (result, ret); + } + } else { + char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL; + + if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_NOMEM; + thisplugin = freeptr = pluginlist; + + /* Do lookup in all *specified* plugins, in order */ + while(*thisplugin) { + char *p; + int last=0; + + while(*thisplugin && isspace((int)*thisplugin)) thisplugin++; + if(!(*thisplugin)) break; + + for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++); + if(*p == '\0') last = 1; + else *p='\0'; + + for(ptr = auxprop_head; ptr; ptr = ptr->next) { + /* Skip non-matching plugins */ + if(!ptr->plug->name + || strcasecmp(ptr->plug->name, thisplugin)) + continue; + + found=1; + ret = ptr->plug->auxprop_lookup(ptr->plug->glob_context, + sparams, flags, user, ulen); + result = _sasl_account_status (result, ret); + } + + if(last) break; + + thisplugin = p+1; + } + + sasl_FREE(freeptr); + } + + if(!found) { + _sasl_log(sparams->utils->conn, SASL_LOG_DEBUG, + "could not find auxprop plugin, was searching for '%s'", + plist ? plist : "[all]"); + } + + return result; +} + +/* Do the callbacks for auxprop stores */ +int sasl_auxprop_store(sasl_conn_t *conn, + struct propctx *ctx, const char *user) +{ + sasl_getopt_t *getopt; + int ret; + void *context; + const char *plist = NULL; + auxprop_plug_list_t *ptr; + sasl_server_params_t *sparams = NULL; + unsigned userlen = 0; + int num_constraint_violations = 0; + int total_plugins = 0; + + if (ctx) { + if (!conn || !user) + return SASL_BADPARAM; + + sparams = ((sasl_server_conn_t *) conn)->sparams; + userlen = (unsigned) strlen(user); + } + + /* Pickup getopt callback from the connection, if conn is not NULL */ + if(_sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL); + if(ret != SASL_OK) plist = NULL; + } + + ret = SASL_OK; + if(!plist) { + /* Do store in all plugins */ + for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) { + total_plugins++; + if (ptr->plug->auxprop_store) { + ret = ptr->plug->auxprop_store(ptr->plug->glob_context, + sparams, ctx, user, userlen); + if (ret == SASL_CONSTRAINT_VIOLAT) { + ret = SASL_OK; + num_constraint_violations++; + } + } + } + } else { + char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL; + + if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_FAIL; + thisplugin = freeptr = pluginlist; + + /* Do store in all *specified* plugins, in order */ + while(*thisplugin) { + char *p; + int last=0; + + while(*thisplugin && isspace((int)*thisplugin)) thisplugin++; + if(!(*thisplugin)) break; + + for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++); + if(*p == '\0') last = 1; + else *p='\0'; + + for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) { + /* Skip non-matching plugins */ + if((!ptr->plug->name + || strcasecmp(ptr->plug->name, thisplugin))) + continue; + + total_plugins++; + if (ptr->plug->auxprop_store) { + ret = ptr->plug->auxprop_store(ptr->plug->glob_context, + sparams, ctx, user, userlen); + if (ret == SASL_CONSTRAINT_VIOLAT) { + ret = SASL_OK; + num_constraint_violations++; + } + } + } + + if(last) break; + + thisplugin = p+1; + } + + sasl_FREE(freeptr); + } + + if(total_plugins == 0) { + _sasl_log(NULL, SASL_LOG_ERR, + "could not find auxprop plugin, was searching for %s", + plist ? plist : "[all]"); + return SASL_FAIL; + } else if (total_plugins == num_constraint_violations) { + ret = SASL_CONSTRAINT_VIOLAT; + } + + return ret; +} + +/* It would be nice if we can show other information like Author, Company, Year, plugin version */ +static void +_sasl_print_mechanism (sasl_auxprop_plug_t *m, + sasl_info_callback_stage_t stage, + void *rock __attribute__((unused)) +) +{ + if (stage == SASL_INFO_LIST_START) { + printf ("List of auxprop plugins follows\n"); + return; + } else if (stage == SASL_INFO_LIST_END) { + return; + } + + /* Process the mechanism */ + printf ("Plugin \"%s\" ", m->name); + +#ifdef NOT_YET + switch (m->condition) { + case SASL_OK: + printf ("[loaded]"); + break; + + case SASL_CONTINUE: + printf ("[delayed]"); + break; + + case SASL_NOUSER: + printf ("[no users]"); + break; + + default: + printf ("[unknown]"); + break; + } +#endif + + printf (", \tAPI version: %d\n", /* m->version */ SASL_AUXPROP_PLUG_VERSION); + + /* TODO - Update for auxprop_export, etc. */ + printf ("\tsupports store: %s\n", + (m->auxprop_store != NULL) ? "yes" : "no" + ); + + /* No features defined yet */ +#ifdef NOT_YET + printf ("\n\tfeatures:"); +#endif + + printf ("\n"); +} + +/* Dump information about available auxprop plugins (separate functions are + used for canon and server authentication plugins) */ +int auxprop_plugin_info ( + const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ + auxprop_info_callback_t *info_cb, + void *info_cb_rock +) +{ + auxprop_plug_list_t *m; + sasl_auxprop_plug_t plug_data; + char * cur_mech; + char *mech_list = NULL; + char * p; + + if (info_cb == NULL) { + info_cb = _sasl_print_mechanism; + } + + if (auxprop_head != NULL) { + info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); + + if (c_mech_list == NULL) { + m = auxprop_head; /* m point to beginning of the list */ + + while (m != NULL) { + /* TODO: Need to be careful when dealing with auxprop_export, etc. */ + memcpy (&plug_data, m->plug, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + + m = m->next; + } + } else { + mech_list = strdup(c_mech_list); + + cur_mech = mech_list; + + while (cur_mech != NULL) { + p = strchr (cur_mech, ' '); + if (p != NULL) { + *p = '\0'; + p++; + } + + m = auxprop_head; /* m point to beginning of the list */ + + while (m != NULL) { + if (strcasecmp (cur_mech, m->plug->name) == 0) { + memcpy (&plug_data, m->plug, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + } + + m = m->next; + } + + cur_mech = p; + } + + free (mech_list); + } + + info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); + + return (SASL_OK); + } + + return (SASL_NOTINIT); +} diff --git a/contrib/libs/sasl/lib/canonusr.c b/contrib/libs/sasl/lib/canonusr.c new file mode 100644 index 0000000000..66f7e112a6 --- /dev/null +++ b/contrib/libs/sasl/lib/canonusr.c @@ -0,0 +1,465 @@ +/* canonusr.c - user canonicalization support + * Rob Siemborski + */ +/* + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <sasl.h> +#include <string.h> +#include <ctype.h> +#include <prop.h> +#include <stdio.h> + +#include "saslint.h" + +typedef struct canonuser_plug_list +{ + struct canonuser_plug_list *next; + char name[PATH_MAX]; + const sasl_canonuser_plug_t *plug; +} canonuser_plug_list_t; + +static canonuser_plug_list_t *canonuser_head = NULL; + +/* default behavior: + * eliminate leading & trailing whitespace, + * null-terminate, and get into the outparams + * (handled by INTERNAL plugin) */ +/* a zero ulen or alen indicates that it is strlen(value) */ +int _sasl_canon_user(sasl_conn_t *conn, + const char *user, unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams) +{ + canonuser_plug_list_t *ptr; + sasl_server_conn_t *sconn = NULL; + sasl_client_conn_t *cconn = NULL; + sasl_canon_user_t *cuser_cb; + sasl_getopt_t *getopt; + void *context; + int result; + const char *plugin_name = NULL; + char *user_buf; + unsigned *lenp; + + if(!conn) return SASL_BADPARAM; + if(!user || !oparams) return SASL_BADPARAM; + + if(flags & SASL_CU_AUTHID) { + user_buf = conn->authid_buf; + lenp = &(oparams->alen); + } else if (flags & SASL_CU_AUTHZID) { + user_buf = conn->user_buf; + lenp = &(oparams->ulen); + } else { + return SASL_BADPARAM; + } + + if (conn->type == SASL_CONN_SERVER) + sconn = (sasl_server_conn_t *)conn; + else if (conn->type == SASL_CONN_CLIENT) + cconn = (sasl_client_conn_t *)conn; + else return SASL_FAIL; + + if(!ulen) ulen = (unsigned int)strlen(user); + + /* check to see if we have a callback to make*/ + result = _sasl_getcallback(conn, + SASL_CB_CANON_USER, + (sasl_callback_ft *)&cuser_cb, + &context); + if(result == SASL_OK && cuser_cb) { + result = cuser_cb(conn, + context, + user, + ulen, + flags, + (sconn ? + sconn->user_realm : + NULL), + user_buf, + CANON_BUF_SIZE, + lenp); + + + if (result != SASL_OK) return result; + + /* Point the input copy at the stored buffer */ + user = user_buf; + ulen = *lenp; + } + + /* which plugin are we supposed to use? */ + result = _sasl_getcallback(conn, + SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, + &context); + if (result == SASL_OK && getopt) { + getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL); + } + + if (!plugin_name) { + /* Use Default */ + plugin_name = "INTERNAL"; + } + + for (ptr = canonuser_head; ptr; ptr = ptr->next) { + /* A match is if we match the internal name of the plugin, or if + * we match the filename (old-style) */ + if ((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name)) + || !strcmp(plugin_name, ptr->name)) break; + } + + /* We clearly don't have this one! */ + if (!ptr) { + sasl_seterror(conn, 0, "desired canon_user plugin %s not found", + plugin_name); + return SASL_NOMECH; + } + + if (sconn) { + /* we're a server */ + result = ptr->plug->canon_user_server(ptr->plug->glob_context, + sconn->sparams, + user, ulen, + flags, + user_buf, + CANON_BUF_SIZE, lenp); + } else { + /* we're a client */ + result = ptr->plug->canon_user_client(ptr->plug->glob_context, + cconn->cparams, + user, ulen, + flags, + user_buf, + CANON_BUF_SIZE, lenp); + } + + if (result != SASL_OK) return result; + + if ((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) { + /* We did both, so we need to copy the result into + * the buffer for the authzid from the buffer for the authid */ + memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE); + oparams->ulen = oparams->alen; + } + + /* Set the appropriate oparams (lengths have already been set by lenp) */ + if (flags & SASL_CU_AUTHID) { + oparams->authid = conn->authid_buf; + } + + if (flags & SASL_CU_AUTHZID) { + oparams->user = conn->user_buf; + } + + RETURN(conn, result); +} + +/* Lookup all properties for authentication and/or authorization identity. */ +static int _sasl_auxprop_lookup_user_props (sasl_conn_t *conn, + unsigned flags, + sasl_out_params_t *oparams) +{ + sasl_server_conn_t *sconn = NULL; + int result = SASL_OK; + + if (!conn) return SASL_BADPARAM; + if (!oparams) return SASL_BADPARAM; + +#ifndef macintosh + if (conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn; + + /* do auxprop lookups (server only) */ + if (sconn) { + int authz_result; + unsigned auxprop_lookup_flags = flags & SASL_CU_ASIS_MASK; + + if (flags & SASL_CU_OVERRIDE) { + auxprop_lookup_flags |= SASL_AUXPROP_OVERRIDE; + } + + if (flags & SASL_CU_AUTHID) { + result = _sasl_auxprop_lookup(sconn->sparams, + auxprop_lookup_flags, + oparams->authid, + oparams->alen); + } else { + result = SASL_CONTINUE; + } + if (flags & SASL_CU_AUTHZID) { + authz_result = _sasl_auxprop_lookup(sconn->sparams, + auxprop_lookup_flags | SASL_AUXPROP_AUTHZID, + oparams->user, + oparams->ulen); + + if (result == SASL_CONTINUE) { + /* Only SASL_CU_AUTHZID was requested. + The authz_result value is authoritative. */ + result = authz_result; + } else if (result == SASL_OK && authz_result != SASL_NOUSER) { + /* Use the authz_result value, unless "result" + already contains an error */ + result = authz_result; + } + } + + if ((flags & SASL_CU_EXTERNALLY_VERIFIED) && (result == SASL_NOUSER || result == SASL_NOMECH)) { + /* The called has explicitly told us that the authentication identity + was already verified or will be verified independently. + So a failure to retrieve any associated properties + is not an error. For example the caller is using Kerberos to verify user, + but the LDAPDB/SASLDB auxprop plugin doesn't contain any auxprops for + the user. + Another case is PLAIN/LOGIN not using auxprop to verify user passwords. */ + result = SASL_OK; + } + } +#endif + + RETURN(conn, result); +} + +/* default behavior: + * Eliminate leading & trailing whitespace, + * null-terminate, and get into the outparams + * (handled by INTERNAL plugin). + * + * Server only: Also does auxprop lookups once username + * is canonicalized. */ +int _sasl_canon_user_lookup (sasl_conn_t *conn, + const char *user, + unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams) +{ + int result; + + result = _sasl_canon_user (conn, + user, + ulen, + flags, + oparams); + if (result == SASL_OK) { + result = _sasl_auxprop_lookup_user_props (conn, + flags, + oparams); + } + + RETURN(conn, result); +} + +void _sasl_canonuser_free() +{ + canonuser_plug_list_t *ptr, *ptr_next; + + for(ptr = canonuser_head; ptr; ptr = ptr_next) { + ptr_next = ptr->next; + if(ptr->plug->canon_user_free) + ptr->plug->canon_user_free(ptr->plug->glob_context, + sasl_global_utils); + sasl_FREE(ptr); + } + + canonuser_head = NULL; +} + +int sasl_canonuser_add_plugin(const char *plugname, + sasl_canonuser_init_t *canonuserfunc) +{ + int result, out_version; + canonuser_plug_list_t *new_item; + sasl_canonuser_plug_t *plug; + + if(!plugname || strlen(plugname) > (PATH_MAX - 1)) { + sasl_seterror(NULL, 0, + "bad plugname passed to sasl_canonuser_add_plugin\n"); + return SASL_BADPARAM; + } + + result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION, + &out_version, &plug, plugname); + + if(result != SASL_OK) { + _sasl_log(NULL, SASL_LOG_ERR, "%s_canonuser_plug_init() failed in sasl_canonuser_add_plugin(): %z\n", + plugname, result); + return result; + } + + if(!plug->canon_user_server && !plug->canon_user_client) { + /* We need at least one of these implemented */ + _sasl_log(NULL, SASL_LOG_ERR, + "canonuser plugin '%s' without either client or server side", plugname); + return SASL_BADPROT; + } + + new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t)); + if(!new_item) return SASL_NOMEM; + + strncpy(new_item->name, plugname, PATH_MAX - 1); + new_item->name[strlen(plugname)] = '\0'; + + new_item->plug = plug; + new_item->next = canonuser_head; + canonuser_head = new_item; + + return SASL_OK; +} + +#ifdef MIN +#undef MIN +#endif +#define MIN(a,b) (((a) < (b))? (a):(b)) + +static int _canonuser_internal(const sasl_utils_t *utils, + const char *user, unsigned ulen, + unsigned flags __attribute__((unused)), + char *out_user, + unsigned out_umax, unsigned *out_ulen) +{ + unsigned i; + char *in_buf, *userin; + const char *begin_u; + unsigned u_apprealm = 0; + sasl_server_conn_t *sconn = NULL; + + if(!utils || !user) return SASL_BADPARAM; + + in_buf = sasl_ALLOC((ulen + 2) * sizeof(char)); + if(!in_buf) return SASL_NOMEM; + + userin = in_buf; + + memcpy(userin, user, ulen); + userin[ulen] = '\0'; + + /* Strip User ID */ + for(i=0;isspace((int)userin[i]) && i<ulen;i++); + begin_u = &(userin[i]); + if(i>0) ulen -= i; + + for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--); + if(begin_u == &(userin[ulen])) { + sasl_FREE(in_buf); + utils->seterror(utils->conn, 0, "All-whitespace username."); + return SASL_FAIL; + } + + if(utils->conn && utils->conn->type == SASL_CONN_SERVER) + sconn = (sasl_server_conn_t *)utils->conn; + + /* Need to append realm if necessary (see sasl.h) */ + if(sconn && sconn->user_realm && !strchr(user, '@')) { + u_apprealm = (unsigned) strlen(sconn->user_realm) + 1; + } + + /* Now Copy */ + memcpy(out_user, begin_u, MIN(ulen, out_umax)); + if(sconn && u_apprealm) { + if(ulen >= out_umax) return SASL_BUFOVER; + out_user[ulen] = '@'; + memcpy(&(out_user[ulen+1]), sconn->user_realm, + MIN(u_apprealm-1, out_umax-ulen-1)); + } + out_user[MIN(ulen + u_apprealm,out_umax)] = '\0'; + + if(ulen + u_apprealm > out_umax) return SASL_BUFOVER; + + if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax); + + sasl_FREE(in_buf); + return SASL_OK; +} + +static int _cu_internal_server(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *user, unsigned ulen, + unsigned flags, + char *out_user, + unsigned out_umax, unsigned *out_ulen) +{ + return _canonuser_internal(sparams->utils, + user, ulen, + flags, out_user, out_umax, out_ulen); +} + +static int _cu_internal_client(void *glob_context __attribute__((unused)), + sasl_client_params_t *cparams, + const char *user, unsigned ulen, + unsigned flags, + char *out_user, + unsigned out_umax, unsigned *out_ulen) +{ + return _canonuser_internal(cparams->utils, + user, ulen, + flags, out_user, out_umax, out_ulen); +} + +static sasl_canonuser_plug_t canonuser_internal_plugin = { + 0, /* features */ + 0, /* spare */ + NULL, /* glob_context */ + "INTERNAL", /* name */ + NULL, /* canon_user_free */ + _cu_internal_server, + _cu_internal_client, + NULL, + NULL, + NULL +}; + +int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)), + int max_version, + int *out_version, + sasl_canonuser_plug_t **plug, + const char *plugname __attribute__((unused))) +{ + if(!out_version || !plug) return SASL_BADPARAM; + + if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS; + + *out_version = SASL_CANONUSER_PLUG_VERSION; + + *plug = &canonuser_internal_plugin; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/lib/checkpw.c b/contrib/libs/sasl/lib/checkpw.c new file mode 100644 index 0000000000..a13f526b7c --- /dev/null +++ b/contrib/libs/sasl/lib/checkpw.c @@ -0,0 +1,1110 @@ +/* SASL server API implementation + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> + +/* checkpw stuff */ + +#include <stdio.h> +#include "sasl.h" +#include "saslutil.h" +#include "saslplug.h" +#include "saslint.h" + +#include <assert.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <fcntl.h> +#ifdef USE_DOORS +#include <sys/mman.h> +#error #include <door.h> +#endif + +#include <stdlib.h> + +#ifndef WIN32 +#include <strings.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/un.h> +#else +#include <string.h> +#endif + +#include <limits.h> +#include <sys/types.h> +#include <ctype.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif /* HAVE_PWD_H */ +#ifdef HAVE_SHADOW_H +#include <shadow.h> +#endif /* HAVE_SHADOW_H */ + +#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON) +# include <errno.h> +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/un.h> +# ifdef HAVE_UNISTD_H +# include <unistd.h> +# endif +#endif + + +/* we store the following secret to check plaintext passwords: + * + * <salt> \0 <secret> + * + * where <secret> = MD5(<salt>, "sasldb", <pass>) + */ +static int _sasl_make_plain_secret(const char *salt, + const char *passwd, size_t passlen, + sasl_secret_t **secret) +{ + MD5_CTX ctx; + unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */ + + *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) + + sec_len * sizeof(char)); + if (*secret == NULL) { + return SASL_NOMEM; + } + + _sasl_MD5Init(&ctx); + _sasl_MD5Update(&ctx, (const unsigned char *) salt, 16); + _sasl_MD5Update(&ctx, (const unsigned char *) "sasldb", 6); + _sasl_MD5Update(&ctx, (const unsigned char *) passwd, (unsigned int) passlen); + memcpy((*secret)->data, salt, 16); + (*secret)->data[16] = '\0'; + _sasl_MD5Final((*secret)->data + 17, &ctx); + (*secret)->len = sec_len; + + return SASL_OK; +} + +/* verify user password using auxprop plugins + */ +static int auxprop_verify_password(sasl_conn_t *conn, + const char *userstr, + const char *passwd, + const char *service __attribute__((unused)), + const char *user_realm __attribute__((unused))) +{ + int ret = SASL_FAIL; + int result = SASL_OK; + sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; + const char *password_request[] = { SASL_AUX_PASSWORD, + "*cmusaslsecretPLAIN", + NULL }; + struct propval auxprop_values[3]; + + if (!conn || !userstr) + return SASL_BADPARAM; + + /* We need to clear any previous results and re-canonify to + * ensure correctness */ + + prop_clear (sconn->sparams->propctx, 0); + + /* ensure its requested */ + result = prop_request(sconn->sparams->propctx, password_request); + + if(result != SASL_OK) return result; + + result = _sasl_canon_user_lookup (conn, + userstr, + 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + &(conn->oparams)); + if(result != SASL_OK) return result; + + result = prop_getnames(sconn->sparams->propctx, password_request, + auxprop_values); + if (result < 0) { + return result; + } + + /* Verify that the returned <name>s are correct. + But we defer checking for NULL values till after we verify + that a passwd is specified. */ + if (!auxprop_values[0].name && !auxprop_values[1].name) { + return SASL_NOUSER; + } + + /* It is possible for us to get useful information out of just + * the lookup, so we won't check that we have a password until now */ + if(!passwd) { + ret = SASL_BADPARAM; + goto done; + } + + if ((!auxprop_values[0].values || !auxprop_values[0].values[0]) + && (!auxprop_values[1].values || !auxprop_values[1].values[0])) { + return SASL_NOUSER; + } + + /* At the point this has been called, the username has been canonified + * and we've done the auxprop lookup. This should be easy. */ + if(auxprop_values[0].name + && auxprop_values[0].values + && auxprop_values[0].values[0] + && !strcmp(auxprop_values[0].values[0], passwd)) { + /* We have a plaintext version and it matched! */ + return SASL_OK; + } else if(auxprop_values[1].name + && auxprop_values[1].values + && auxprop_values[1].values[0]) { + const char *db_secret = auxprop_values[1].values[0]; + sasl_secret_t *construct; + + ret = _sasl_make_plain_secret(db_secret, passwd, + strlen(passwd), + &construct); + if (ret != SASL_OK) { + goto done; + } + + if (!memcmp(db_secret, construct->data, construct->len)) { + /* password verified! */ + ret = SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + sasl_FREE(construct); + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + /* erase the plaintext password */ + sconn->sparams->utils->prop_erase(sconn->sparams->propctx, + password_request[0]); + + done: + /* We're not going to erase the property here because other people + * may want it */ + return ret; +} + +#if 0 +/* Verify user password using auxprop plugins. Allow verification against a hashed password, + * or non-retrievable password. Don't use cmusaslsecretPLAIN attribute. + * + * This function is similar to auxprop_verify_password(). + */ +static int auxprop_verify_password_hashed(sasl_conn_t *conn, + const char *userstr, + const char *passwd, + const char *service __attribute__((unused)), + const char *user_realm __attribute__((unused))) +{ + int ret = SASL_FAIL; + int result = SASL_OK; + sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; + const char *password_request[] = { SASL_AUX_PASSWORD, + NULL }; + struct propval auxprop_values[2]; + unsigned extra_cu_flags = 0; + + if (!conn || !userstr) + return SASL_BADPARAM; + + /* We need to clear any previous results and re-canonify to + * ensure correctness */ + + prop_clear(sconn->sparams->propctx, 0); + + /* ensure its requested */ + result = prop_request(sconn->sparams->propctx, password_request); + + if (result != SASL_OK) return result; + + /* We need to pass "password" down to the auxprop_lookup */ + /* NB: We don't support binary passwords */ + if (passwd != NULL) { + prop_set (sconn->sparams->propctx, + SASL_AUX_PASSWORD, + passwd, + -1); + extra_cu_flags = SASL_CU_VERIFY_AGAINST_HASH; + } + + result = _sasl_canon_user_lookup (conn, + userstr, + 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID | extra_cu_flags, + &(conn->oparams)); + + if (result != SASL_OK) return result; + + result = prop_getnames(sconn->sparams->propctx, password_request, + auxprop_values); + if (result < 0) { + return result; + } + + /* Verify that the returned <name>s are correct. + But we defer checking for NULL values till after we verify + that a passwd is specified. */ + if (!auxprop_values[0].name && !auxprop_values[1].name) { + return SASL_NOUSER; + } + + /* It is possible for us to get useful information out of just + * the lookup, so we won't check that we have a password until now */ + if (!passwd) { + ret = SASL_BADPARAM; + goto done; + } + + if ((!auxprop_values[0].values || !auxprop_values[0].values[0])) { + return SASL_NOUSER; + } + + /* At the point this has been called, the username has been canonified + * and we've done the auxprop lookup. This should be easy. */ + + /* NB: Note that if auxprop_lookup failed to verify the password, + then the userPassword property value would be NULL */ + if (auxprop_values[0].name + && auxprop_values[0].values + && auxprop_values[0].values[0] + && !strcmp(auxprop_values[0].values[0], passwd)) { + /* We have a plaintext version and it matched! */ + return SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + done: + /* We're not going to erase the property here because other people + * may want it */ + return ret; +} +#endif + +#ifdef DO_SASL_CHECKAPOP +int _sasl_auxprop_verify_apop(sasl_conn_t *conn, + const char *userstr, + const char *challenge, + const char *response, + const char *user_realm __attribute__((unused))) +{ + int ret = SASL_BADAUTH; + char *userid = NULL; + char *realm = NULL; + unsigned char digest[16]; + char digeststr[33]; + const char *password_request[] = { SASL_AUX_PASSWORD, NULL }; + struct propval auxprop_values[2]; + sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn; + MD5_CTX ctx; + int i; + + if (!conn || !userstr || !challenge || !response) + PARAMERROR(conn) + + /* We've done the auxprop lookup already (in our caller) */ + /* sadly, APOP has no provision for storing secrets */ + ret = prop_getnames(sconn->sparams->propctx, password_request, + auxprop_values); + if(ret < 0) { + sasl_seterror(conn, 0, "could not perform password lookup"); + goto done; + } + + if(!auxprop_values[0].name || + !auxprop_values[0].values || + !auxprop_values[0].values[0]) { + sasl_seterror(conn, 0, "could not find password"); + ret = SASL_NOUSER; + goto done; + } + + _sasl_MD5Init(&ctx); + _sasl_MD5Update(&ctx, (const unsigned char *) challenge, strlen(challenge)); + _sasl_MD5Update(&ctx, (const unsigned char *) auxprop_values[0].values[0], + strlen(auxprop_values[0].values[0])); + _sasl_MD5Final(digest, &ctx); + + /* erase the plaintext password */ + sconn->sparams->utils->prop_erase(sconn->sparams->propctx, + password_request[0]); + + /* convert digest from binary to ASCII hex */ + for (i = 0; i < 16; i++) + sprintf(digeststr + (i*2), "%02x", digest[i]); + + if (!strncasecmp(digeststr, response, 32)) { + /* password verified! */ + ret = SASL_OK; + } else { + /* passwords do not match */ + ret = SASL_BADAUTH; + } + + done: + if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG, + "login incorrect"); + if (userid) sasl_FREE(userid); + if (realm) sasl_FREE(realm); + + return ret; +} +#endif /* DO_SASL_CHECKAPOP */ + +#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON) +/* + * Wait for file descriptor to be writable. Return with error if timeout. + */ +static int write_wait(int fd, unsigned delta) +{ + fd_set wfds; + fd_set efds; + struct timeval tv; + + /* + * Wait for file descriptor fd to be writable. Retry on + * interruptions. Return with error upon timeout. + */ + while (1) { + FD_ZERO(&wfds); + FD_ZERO(&efds); + FD_SET(fd, &wfds); + FD_SET(fd, &efds); + tv.tv_sec = (long) delta; + tv.tv_usec = 0; + switch(select(fd + 1, 0, &wfds, &efds, &tv)) { + case 0: + /* Timeout. */ + errno = ETIMEDOUT; + return -1; + case +1: + if (FD_ISSET(fd, &wfds)) { + /* Success, file descriptor is writable. */ + return 0; + } + return -1; + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + default: + /* Error catch-all. */ + return -1; + } + } + /* Not reached. */ + return -1; +} + +/* + * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt' + * until all the data is written out or an error/timeout occurs. + */ +static int retry_writev(int fd, struct iovec *iov, int iovcnt, unsigned delta) +{ + int n; + int i; + int written = 0; + static int iov_max = +#ifdef MAXIOV + MAXIOV +#else +#ifdef IOV_MAX + IOV_MAX +#else + 8192 +#endif +#endif + ; + + for (;;) { + while (iovcnt && iov[0].iov_len == 0) { + iov++; + iovcnt--; + } + + if (!iovcnt) return written; + + if (delta > 0) { + if (write_wait(fd, delta)) + return -1; + } + n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt); + if (n == -1) { + if (errno == EINVAL && iov_max > 10) { + iov_max /= 2; + continue; + } + if (errno == EINTR) continue; + return -1; + } + + written += n; + + for (i = 0; i < iovcnt; i++) { + if ((int) iov[i].iov_len > n) { + iov[i].iov_base = (char *)iov[i].iov_base + n; + iov[i].iov_len -= n; + break; + } + n -= iov[i].iov_len; + iov[i].iov_len = 0; + } + + if (i == iovcnt) return written; + } +} + +#endif + +#ifdef HAVE_PWCHECK +/* pwcheck daemon-authenticated login */ +static int pwcheck_verify_password(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service __attribute__((unused)), + const char *user_realm + __attribute__((unused))) +{ + int s; + struct sockaddr_un srvaddr; + int r; + struct iovec iov[10]; + static char response[1024]; + unsigned start, n; + char pwpath[1024]; + + if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL; + + strcpy(pwpath, PWCHECKDIR); + strcat(pwpath, "/pwcheck"); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) return errno; + + memset((char *)&srvaddr, 0, sizeof(srvaddr)); + srvaddr.sun_family = AF_UNIX; + strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path)); + r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr)); + if (r == -1) { + sasl_seterror(conn,0,"cannot connect to pwcheck server"); + return SASL_FAIL; + } + + iov[0].iov_base = (char *)userid; + iov[0].iov_len = strlen(userid)+1; + iov[1].iov_base = (char *)passwd; + iov[1].iov_len = strlen(passwd)+1; + + retry_writev(s, iov, 2, 0); + + start = 0; + while (start < sizeof(response) - 1) { + n = read(s, response+start, sizeof(response) - 1 - start); + if (n < 1) break; + start += n; + } + + close(s); + + if (start > 1 && !strncmp(response, "OK", 2)) { + return SASL_OK; + } + + response[start] = '\0'; + sasl_seterror(conn,0,response); + return SASL_BADAUTH; +} + +#endif + +#if defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON) +static int read_wait(int fd, unsigned delta) +{ + fd_set rfds; + fd_set efds; + struct timeval tv; + /* + * Wait for file descriptor fd to be readable. Retry on + * interruptions. Return with error upon timeout. + */ + while (1) { + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(fd, &rfds); + FD_SET(fd, &efds); + tv.tv_sec = (long) delta; + tv.tv_usec = 0; + switch(select(fd + 1, &rfds, 0, &efds, &tv)) { + case 0: + /* Timeout. */ + errno = ETIMEDOUT; + return -1; + case +1: + case +2: + if (FD_ISSET(fd, &rfds)) { + /* Success, file descriptor is readable. */ + return 0; + } + return -1; + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + default: + /* Error catch-all. */ + return -1; + } + } + /* Not reached. */ + return -1; +} + +/* + * Keep calling the read() system call until all the data is read in, + * timeout, EOF, or an error occurs. This function returns the number + * of useful bytes, or -1 if timeout/error. + */ +static int retry_read(int fd, void *buf0, unsigned nbyte, unsigned delta) +{ + int nr; + unsigned nleft = nbyte; + char *buf = (char*) buf0; + + while (nleft >= 1) { + if (delta > 0) { + if (read_wait(fd, delta)) + return -1; + } + nr = read(fd, buf, nleft); + if (nr < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } else if (nr == 0) { + break; + } + buf += nr; + nleft -= nr; + } + return nbyte - nleft; +} +#endif + +#ifdef HAVE_SASLAUTHD +/* saslauthd-authenticated login */ +static int saslauthd_verify_password(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service, + const char *user_realm) +{ + char response[1024]; + char query[8192]; + char *query_end = query; + int s; + struct sockaddr_un srvaddr; + sasl_getopt_t *getopt; + void *context; + char pwpath[sizeof(srvaddr.sun_path)]; + const char *p = NULL; + char *freeme = NULL; +#ifdef USE_DOORS + door_arg_t arg; +#endif + + /* check to see if the user configured a rundir */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "saslauthd_path", &p, NULL); + } + if (p) { + if (strlen(p) >= sizeof(pwpath)) + return SASL_FAIL; + + strncpy(pwpath, p, sizeof(pwpath) - 1); + pwpath[strlen(p)] = '\0'; + } else { + if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath)) + return SASL_FAIL; + + strcpy(pwpath, PATH_SASLAUTHD_RUNDIR "/mux"); + } + + /* Split out username/realm if necessary */ + if(strrchr(userid,'@') != NULL) { + char *rtmp; + + if(_sasl_strdup(userid, &freeme, NULL) != SASL_OK) + goto fail; + + userid = freeme; + rtmp = strrchr(userid,'@'); + *rtmp = '\0'; + user_realm = rtmp + 1; + } + + /* + * build request of the form: + * + * count authid count password count service count realm + */ + { + unsigned short max_len, req_len, u_len, p_len, s_len, r_len; + + max_len = (unsigned short) sizeof(query); + + /* prevent buffer overflow */ + if ((strlen(userid) > USHRT_MAX) || + (strlen(passwd) > USHRT_MAX) || + (strlen(service) > USHRT_MAX) || + (user_realm && (strlen(user_realm) > USHRT_MAX))) { + goto toobig; + } + + u_len = (strlen(userid)); + p_len = (strlen(passwd)); + s_len = (strlen(service)); + r_len = ((user_realm ? strlen(user_realm) : 0)); + + /* prevent buffer overflow */ + req_len = 30; + if (max_len - req_len < u_len) goto toobig; + req_len += u_len; + if (max_len - req_len < p_len) goto toobig; + req_len += p_len; + if (max_len - req_len < s_len) goto toobig; + req_len += s_len; + if (max_len - req_len < r_len) goto toobig; + + u_len = htons(u_len); + p_len = htons(p_len); + s_len = htons(s_len); + r_len = htons(r_len); + + memcpy(query_end, &u_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + while (*userid) *query_end++ = *userid++; + + memcpy(query_end, &p_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + while (*passwd) *query_end++ = *passwd++; + + memcpy(query_end, &s_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + while (*service) *query_end++ = *service++; + + memcpy(query_end, &r_len, sizeof(unsigned short)); + query_end += sizeof(unsigned short); + if (user_realm) while (*user_realm) *query_end++ = *user_realm++; + } + +#ifdef USE_DOORS + s = open(pwpath, O_RDONLY); + if (s < 0) { + sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno); + goto fail; + } + + arg.data_ptr = query; + arg.data_size = query_end - query; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = response; + arg.rsize = sizeof(response); + + if (door_call(s, &arg) < 0) { + /* Parameters are undefined */ + close(s); + sasl_seterror(conn, 0, "door call to saslauthd server failed: %m", errno); + goto fail; + } + + if (arg.data_ptr != response || arg.data_size >= sizeof(response)) { + /* oh damn, we got back a really long response */ + munmap(arg.rbuf, arg.rsize); + close(s); + sasl_seterror(conn, 0, "saslauthd sent an overly long response"); + goto fail; + } + response[arg.data_size] = '\0'; + + close(s); +#else + /* unix sockets */ + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno); + goto fail; + } + + memset((char *)&srvaddr, 0, sizeof(srvaddr)); + srvaddr.sun_family = AF_UNIX; + strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path) - 1); + srvaddr.sun_path[strlen(pwpath)] = '\0'; + + { + int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr)); + if (r == -1) { + close(s); + sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno); + goto fail; + } + } + + { + struct iovec iov[8]; + + iov[0].iov_len = query_end - query; + iov[0].iov_base = query; + + if (retry_writev(s, iov, 1, 0) == -1) { + close(s); + sasl_seterror(conn, 0, "write failed"); + goto fail; + } + } + + { + unsigned short count = 0; + + /* + * read response of the form: + * + * count result + */ + if (retry_read(s, &count, sizeof(count), 0) < (int) sizeof(count)) { + sasl_seterror(conn, 0, "size read failed"); + goto fail; + } + + count = ntohs(count); + if (count < 2) { /* MUST have at least "OK" or "NO" */ + close(s); + sasl_seterror(conn, 0, "bad response from saslauthd"); + goto fail; + } + + count = (int)sizeof(response) <= count ? sizeof(response) - 1 : count; + if (retry_read(s, response, count, 0) < count) { + close(s); + sasl_seterror(conn, 0, "read failed"); + goto fail; + } + response[count] = '\0'; + } + + close(s); +#endif /* USE_DOORS */ + + if(freeme) free(freeme); + + if (!strncmp(response, "OK", 2)) { + return SASL_OK; + } + + sasl_seterror(conn, SASL_NOLOG, "authentication failed"); + return SASL_BADAUTH; + + toobig: + /* request just too damn big */ + sasl_seterror(conn, 0, "saslauthd request too large"); + + fail: + if (freeme) free(freeme); + return SASL_FAIL; +} + +#endif + +#ifdef HAVE_AUTHDAEMON +/* + * Preliminary support for Courier's authdaemond. + */ +#define AUTHDAEMON_IO_TIMEOUT 30 + +static int authdaemon_blocking(int fd, int block) +{ + int f, r; + + /* Get the fd's blocking bit. */ + f = fcntl(fd, F_GETFL, 0); + if (f == -1) + return -1; + + /* Adjust the bitmap accordingly. */ +#ifndef O_NONBLOCK +#define NB_BITMASK FNDELAY +#else +#define NB_BITMASK O_NONBLOCK +#endif + if (block) + f &= ~NB_BITMASK; + else + f |= NB_BITMASK; +#undef NB_BITMASK + + /* Adjust the fd's blocking bit. */ + r = fcntl(fd, F_SETFL, f); + if (r) + return -1; + + /* Success. */ + return 0; +} + +static int authdaemon_connect(sasl_conn_t *conn, const char *path) +{ + int r, s = -1; + struct sockaddr_un srvaddr; + + if (strlen(path) >= sizeof(srvaddr.sun_path)) { + sasl_seterror(conn, 0, "unix socket path too large", errno); + goto fail; + } + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + sasl_seterror(conn, 0, "cannot create socket for connection to Courier authdaemond: %m", errno); + goto fail; + } + + memset((char *)&srvaddr, 0, sizeof(srvaddr)); + srvaddr.sun_family = AF_UNIX; + strncpy(srvaddr.sun_path, path, sizeof(srvaddr.sun_path) - 1); + + /* Use nonblocking unix socket connect(2). */ + if (authdaemon_blocking(s, 0)) { + sasl_seterror(conn, 0, "cannot set nonblocking bit: %m", errno); + goto fail; + } + + r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr)); + if (r == -1) { + sasl_seterror(conn, 0, "cannot connect to Courier authdaemond: %m", errno); + goto fail; + } + + if (authdaemon_blocking(s, 1)) { + sasl_seterror(conn, 0, "cannot clear nonblocking bit: %m", errno); + goto fail; + } + + return s; +fail: + if (s >= 0) + close(s); + return -1; +} + +static char *authdaemon_build_query(const char *service, + const char *authtype, + const char *user, + const char *passwd) +{ + int sz; + int l = strlen(service) + + 1 + + strlen(authtype) + + 1 + + strlen(user) + + 1 + + strlen(passwd) + + 1; + char *buf, n[5]; + if (snprintf(n, sizeof(n), "%d", l) >= (int)sizeof(n)) + return NULL; + sz = strlen(n) + l + 20; + if (!(buf = sasl_ALLOC(sz))) + return NULL; + snprintf(buf, + sz, + "AUTH %s\n%s\n%s\n%s\n%s\n\n", + n, + service, + authtype, + user, + passwd); + return buf; +} + +static int authdaemon_read(int fd, void *buf0, unsigned sz) +{ + int nr; + char *buf = (char*) buf0; + if (sz <= 1) + return -1; + if ((nr = retry_read(fd, buf0, sz - 1, AUTHDAEMON_IO_TIMEOUT)) < 0) + return -1; + /* We need a null-terminated buffer. */ + buf[nr] = 0; + /* Check for overflow condition. */ + return nr + 1 < (int)sz ? 0 : -1; +} + +static int authdaemon_write(int fd, void *buf0, unsigned sz) +{ + int nw; + struct iovec io; + io.iov_len = sz; + io.iov_base = buf0; + nw = retry_writev(fd, &io, 1, AUTHDAEMON_IO_TIMEOUT); + return nw == (int)sz ? 0 : -1; +} + +static int authdaemon_talk(sasl_conn_t *conn, int sock, char *authreq) +{ + char *str; + char buf[8192]; + + if (authdaemon_write(sock, authreq, strlen(authreq))) + goto _err_out; + if (authdaemon_read(sock, buf, sizeof(buf))) + goto _err_out; + for (str = buf; *str; ) { + char *sub; + + for (sub = str; *str; ++str) { + if (*str == '\n') { + *str++ = 0; + break; + } + } + if (strcmp(sub, ".") == 0) { + /* success */ + return SASL_OK; + } + if (strcmp(sub, "FAIL") == 0) { + /* passwords do not match */ + sasl_seterror(conn, SASL_NOLOG, "authentication failed"); + return SASL_BADAUTH; + } + } +_err_out: + /* catchall: authentication error */ + sasl_seterror(conn, 0, "could not verify password"); + return SASL_FAIL; +} + +static int authdaemon_verify_password(sasl_conn_t *conn, + const char *userid, + const char *passwd, + const char *service, + const char *user_realm __attribute__((unused))) +{ + const char *p = NULL; + sasl_getopt_t *getopt; + void *context; + int result = SASL_FAIL; + char *query = NULL; + int sock = -1; + + /* check to see if the user configured a rundir */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "authdaemond_path", &p, NULL); + } + if (!p) { + /* + * XXX should we peek at Courier's build-time config ? + */ + p = PATH_AUTHDAEMON_SOCKET; + } + + if ((sock = authdaemon_connect(conn, p)) < 0) + goto out; + if (!(query = authdaemon_build_query(service, "login", userid, passwd))) + goto out; + result = authdaemon_talk(conn, sock, query); +out: + if (sock >= 0) + close(sock), sock = -1; + if (query) + sasl_FREE(query), query = 0; + return result; +} +#endif + +#ifdef HAVE_ALWAYSTRUE +static int always_true(sasl_conn_t *conn, + const char *userstr, + const char *passwd __attribute__((unused)), + const char *service __attribute__((unused)), + const char *user_realm __attribute__((unused))) +{ + _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s", + userstr); + return SASL_OK; +} +#endif + +struct sasl_verify_password_s _sasl_verify_password[] = { + { "auxprop", &auxprop_verify_password }, +#if 0 /* totally undocumented. wtf is this? */ + { "auxprop-hashed", &auxprop_verify_password_hashed }, +#endif +#ifdef HAVE_PWCHECK + { "pwcheck", &pwcheck_verify_password }, +#endif +#ifdef HAVE_SASLAUTHD + { "saslauthd", &saslauthd_verify_password }, +#endif +#ifdef HAVE_AUTHDAEMON + { "authdaemond", &authdaemon_verify_password }, +#endif +#ifdef HAVE_ALWAYSTRUE + { "alwaystrue", &always_true }, +#endif + { NULL, NULL } +}; diff --git a/contrib/libs/sasl/lib/client.c b/contrib/libs/sasl/lib/client.c new file mode 100644 index 0000000000..3784bb0e42 --- /dev/null +++ b/contrib/libs/sasl/lib/client.c @@ -0,0 +1,1317 @@ +/* SASL client API implementation + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <ctype.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* SASL Headers */ +#include "sasl.h" +#include "saslplug.h" +#include "saslutil.h" +#include "saslint.h" + +static cmech_list_t *cmechlist; /* global var which holds the list */ +static sasl_global_callbacks_t global_callbacks_client; +static int _sasl_client_active = 0; + +static int init_mechlist() +{ + cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks_client); + if (cmechlist->utils==NULL) + return SASL_NOMEM; + + cmechlist->mech_list=NULL; + cmechlist->mech_length=0; + + return SASL_OK; +} + +int sasl_client_done(void) +{ + int result = SASL_CONTINUE; + + if (_sasl_server_cleanup_hook == NULL && _sasl_client_cleanup_hook == NULL) { + return SASL_NOTINIT; + } + + if (_sasl_client_cleanup_hook) { + result = _sasl_client_cleanup_hook(); + + if (result == SASL_OK) { + _sasl_client_idle_hook = NULL; + _sasl_client_cleanup_hook = NULL; + } else { + return result; + } + } + + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return result; + } + + sasl_common_done(); + + return SASL_OK; +} + +static int client_done(void) { + cmechanism_t *cm; + cmechanism_t *cprevm; + + if (!_sasl_client_active) { + return SASL_NOTINIT; + } else { + _sasl_client_active--; + } + + if(_sasl_client_active) { + /* Don't de-init yet! Our refcount is nonzero. */ + return SASL_CONTINUE; + } + + cm = cmechlist->mech_list; /* m point to beginning of the list */ + while (cm != NULL) { + cprevm = cm; + cm = cm->next; + + if (cprevm->m.plug->mech_free) { + cprevm->m.plug->mech_free(cprevm->m.plug->glob_context, + cmechlist->utils); + } + + sasl_FREE(cprevm->m.plugname); + sasl_FREE(cprevm); + } + _sasl_free_utils(&cmechlist->utils); + sasl_FREE(cmechlist); + + cmechlist = NULL; + + return SASL_OK; +} + +/* This is nearly identical to the version in server.c. + Keep in sync. */ +static int mech_compare(const sasl_client_plug_t *a, + const sasl_client_plug_t *b) +{ + unsigned sec_diff; + unsigned features_diff; + + /* XXX the following is fairly arbitrary, but its independent + of the order in which the plugins are loaded + */ +#ifdef PREFER_MECH + if (!strcasecmp(a->mech_name, PREFER_MECH)) return 1; + if (!strcasecmp(b->mech_name, PREFER_MECH)) return -1; +#endif + + sec_diff = a->security_flags ^ b->security_flags; + if (sec_diff & a->security_flags & SASL_SEC_NOANONYMOUS) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOANONYMOUS) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOPLAINTEXT) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOPLAINTEXT) return -1; + if (sec_diff & a->security_flags & SASL_SEC_MUTUAL_AUTH) return 1; + if (sec_diff & b->security_flags & SASL_SEC_MUTUAL_AUTH) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOACTIVE) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOACTIVE) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NODICTIONARY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NODICTIONARY) return -1; + if (sec_diff & a->security_flags & SASL_SEC_FORWARD_SECRECY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_FORWARD_SECRECY) return -1; + + features_diff = a->features ^ b->features; + if (features_diff & a->features & SASL_FEAT_CHANNEL_BINDING) return 1; + if (features_diff & b->features & SASL_FEAT_CHANNEL_BINDING) return -1; + + if (a->max_ssf > b->max_ssf) return 1; + if (a->max_ssf < b->max_ssf) return -1; + + if (SASL_GET_HASH_STRENGTH(a->security_flags) > SASL_GET_HASH_STRENGTH(b->security_flags)) return 1; + if (SASL_GET_HASH_STRENGTH(a->security_flags) < SASL_GET_HASH_STRENGTH(b->security_flags)) return -1; + + return 0; +} + +int sasl_client_add_plugin(const char *plugname, + sasl_client_plug_init_t *entry_point) +{ + int plugcount; + sasl_client_plug_t *pluglist; + cmechanism_t *mech, *mp; + int result; + int version; + int lupe; + + if (!plugname || !entry_point) return SASL_BADPARAM; + + result = entry_point(cmechlist->utils, + SASL_CLIENT_PLUG_VERSION, + &version, + &pluglist, + &plugcount); + + if (result != SASL_OK) + { + _sasl_log(NULL, SASL_LOG_WARN, + "sasl_client_add_plugin(): entry_point(): failed for plugname %s: %z", + plugname, result); + return result; + } + + if (version != SASL_CLIENT_PLUG_VERSION) + { + _sasl_log(NULL, SASL_LOG_WARN, + "version conflict in sasl_client_add_plugin for %s", plugname); + return SASL_BADVERS; + } + + for (lupe=0; lupe < plugcount; lupe++, pluglist++) + { + mech = sasl_ALLOC(sizeof(cmechanism_t)); + if (!mech) return SASL_NOMEM; + + mech->m.plug = pluglist; + if (_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) { + sasl_FREE(mech); + return SASL_NOMEM; + } + mech->m.version = version; + + /* sort mech_list by relative "strength" */ + mp = cmechlist->mech_list; + if (!mp || mech_compare(pluglist, mp->m.plug) >= 0) { + /* add mech to head of list */ + mech->next = cmechlist->mech_list; + cmechlist->mech_list = mech; + } else { + /* find where to insert mech into list */ + while (mp->next && + mech_compare(pluglist, mp->next->m.plug) <= 0) mp = mp->next; + mech->next = mp->next; + mp->next = mech; + } + + cmechlist->mech_length++; + } + + return SASL_OK; +} + +static int +client_idle(sasl_conn_t *conn) +{ + cmechanism_t *m; + if (! cmechlist) + return 0; + + for (m = cmechlist->mech_list; + m; + m = m->next) + if (m->m.plug->idle + && m->m.plug->idle(m->m.plug->glob_context, + conn, + conn ? ((sasl_client_conn_t *)conn)->cparams : NULL)) + return 1; + return 0; +} + +/* initialize the SASL client drivers + * callbacks -- base callbacks for all client connections + * returns: + * SASL_OK -- Success + * SASL_NOMEM -- Not enough memory + * SASL_BADVERS -- Mechanism version mismatch + * SASL_BADPARAM -- error in config file + * SASL_NOMECH -- No mechanisms available + * ... + */ + +int sasl_client_init(const sasl_callback_t *callbacks) +{ + int ret; + const add_plugin_list_t ep_list[] = { + { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin }, + { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, + { NULL, NULL } + }; + + /* lock allocation type */ + _sasl_allocation_locked++; + + if(_sasl_client_active) { + /* We're already active, just increase our refcount */ + /* xxx do something with the callback structure? */ + _sasl_client_active++; + return SASL_OK; + } + + global_callbacks_client.callbacks = callbacks; + global_callbacks_client.appname = NULL; + + cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); + if (cmechlist==NULL) return SASL_NOMEM; + + /* We need to call client_done if we fail now */ + _sasl_client_active = 1; + + /* load plugins */ + ret=init_mechlist(); + if (ret!=SASL_OK) { + client_done(); + return ret; + } + + sasl_client_add_plugin("EXTERNAL", &external_client_plug_init); + + ret = _sasl_common_init(&global_callbacks_client); + + if (ret == SASL_OK) + ret = _sasl_load_plugins(ep_list, + _sasl_find_getpath_callback(callbacks), + _sasl_find_verifyfile_callback(callbacks)); + + if (ret == SASL_OK) { + _sasl_client_cleanup_hook = &client_done; + _sasl_client_idle_hook = &client_idle; + + ret = _sasl_build_mechlist(); + } else { + client_done(); + } + + return ret; +} + +static void client_dispose(sasl_conn_t *pconn) +{ + sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn; + + if (c_conn->mech && c_conn->mech->m.plug->mech_dispose) { + c_conn->mech->m.plug->mech_dispose(pconn->context, + c_conn->cparams->utils); + } + + pconn->context = NULL; + + if (c_conn->clientFQDN) + sasl_FREE(c_conn->clientFQDN); + + if (c_conn->cparams) { + _sasl_free_utils(&(c_conn->cparams->utils)); + sasl_FREE(c_conn->cparams); + } + + if (c_conn->mech_list != cmechlist->mech_list) { + /* free connection-specific mech_list */ + cmechanism_t *m, *prevm; + + m = c_conn->mech_list; /* m point to beginning of the list */ + + while (m) { + prevm = m; + m = m->next; + sasl_FREE(prevm); + } + } + + _sasl_conn_dispose(pconn); +} + +/* initialize a client exchange based on the specified mechanism + * service -- registered name of the service using SASL (e.g. "imap") + * serverFQDN -- the fully qualified domain name of the server + * iplocalport -- client IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * ipremoteport -- server IPv4/IPv6 domain literal string with port + * (if NULL, then mechanisms requiring IPaddr are disabled) + * prompt_supp -- list of client interactions supported + * may also include sasl_getopt_t context & call + * NULL prompt_supp = user/pass via SASL_INTERACT only + * NULL proc = interaction supported via SASL_INTERACT + * secflags -- security flags (see above) + * in/out: + * pconn -- connection negotiation structure + * pointer to NULL => allocate new + * non-NULL => recycle storage and go for next available mech + * + * Returns: + * SASL_OK -- success + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_NOMEM -- not enough memory + */ +int sasl_client_new(const char *service, + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const sasl_callback_t *prompt_supp, + unsigned flags, + sasl_conn_t **pconn) +{ + int result; + char name[MAXFQDNLEN]; + sasl_client_conn_t *conn; + sasl_utils_t *utils; + sasl_getopt_t *getopt; + void *context; + const char *mlist = NULL; + int plus = 0; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + + /* Remember, serverFQDN, iplocalport and ipremoteport can be NULL and be valid! */ + if (!pconn || !service) + return SASL_BADPARAM; + + *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t)); + if (*pconn==NULL) { + _sasl_log(NULL, SASL_LOG_ERR, + "Out of memory allocating connection context"); + return SASL_NOMEM; + } + memset(*pconn, 0, sizeof(sasl_client_conn_t)); + + (*pconn)->destroy_conn = &client_dispose; + + conn = (sasl_client_conn_t *)*pconn; + + conn->mech = NULL; + + conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t)); + if (conn->cparams==NULL) + MEMERROR(*pconn); + memset(conn->cparams,0,sizeof(sasl_client_params_t)); + + result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT, + &client_idle, serverFQDN, + iplocalport, ipremoteport, + prompt_supp, &global_callbacks_client); + if (result != SASL_OK) RETURN(*pconn, result); + + utils = _sasl_alloc_utils(*pconn, &global_callbacks_client); + if (utils == NULL) { + MEMERROR(*pconn); + } + + utils->conn= *pconn; + conn->cparams->utils = utils; + + if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "client_mech_list", &mlist, NULL); + } + + /* if we have a client_mech_list, create ordered list of + available mechanisms for this conn */ + if (mlist) { + const char *cp; + cmechanism_t *mptr, *tail = NULL; + cmechanism_t *new; + + while (*mlist) { + /* find end of current mech name */ + for (cp = mlist; *cp && !isspace((int) *cp); cp++); + + /* search for mech name in loaded plugins */ + for (mptr = cmechlist->mech_list; mptr; mptr = mptr->next) { + const sasl_client_plug_t *plug = mptr->m.plug; + + if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) { + /* found a match */ + break; + } + } + if (mptr) { + new = sasl_ALLOC(sizeof(cmechanism_t)); + if (!new) { + result = SASL_NOMEM; + goto failed_client_new; + } + memcpy(&new->m, &mptr->m, sizeof(client_sasl_mechanism_t)); + new->next = NULL; + + if (!conn->mech_list) { + conn->mech_list = new; + tail = conn->mech_list; + } else { + if (tail) + tail->next = new; + tail = new; + } + conn->mech_length++; + } + + /* find next mech name */ + mlist = cp; + while (*mlist && isspace((int) *mlist)) mlist++; + } + } else { + conn->mech_list = cmechlist->mech_list; + conn->mech_length = cmechlist->mech_length; + } + + if (conn->mech_list == NULL) { + sasl_seterror(*pconn, 0, "No worthy mechs found"); + result = SASL_NOMECH; + goto failed_client_new; + } + + /* Setup the non-lazy parts of cparams, the rest is done in + * sasl_client_start */ + conn->cparams->canon_user = &_sasl_canon_user_lookup; + conn->cparams->flags = flags; + conn->cparams->prompt_supp = (*pconn)->callbacks; + + /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */ + memset(name, 0, sizeof(name)); + if (get_fqhostname (name, MAXFQDNLEN, 0) != 0) { + return (SASL_FAIL); + } + + result = _sasl_strdup(name, &conn->clientFQDN, NULL); + + if (result == SASL_OK) return SASL_OK; + +failed_client_new: + /* result isn't SASL_OK */ + _sasl_conn_dispose(*pconn); + sasl_FREE(*pconn); + *pconn = NULL; + _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new"); + return result; +} + +static int have_prompts(sasl_conn_t *conn, + const sasl_client_plug_t *mech) +{ + static const unsigned long default_prompts[] = { + SASL_CB_AUTHNAME, + SASL_CB_PASS, + SASL_CB_LIST_END + }; + + const unsigned long *prompt; + sasl_callback_ft pproc; + void *pcontext; + int result; + + for (prompt = (mech->required_prompts + ? mech->required_prompts : + default_prompts); + *prompt != SASL_CB_LIST_END; + prompt++) { + result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext); + if (result != SASL_OK && result != SASL_INTERACT) + return 0; /* we don't have this required prompt */ + } + + return 1; /* we have all the prompts */ +} + +static int +_mech_plus_p(const char *mech, size_t len) +{ + return (len > 5 && strncasecmp(&mech[len - 5], "-PLUS", 5) == 0); +} + +/* + * Order PLUS mechanisms first. Returns NUL separated list of + * *count items. + */ +static int +_sasl_client_order_mechs(const sasl_utils_t *utils, + const char *mechs, + int has_cb_data, + char **ordered_mechs, + size_t *count, + int *server_can_cb) +{ + char *list, *listp; + size_t i, mechslen, start; + + *count = 0; + *server_can_cb = 0; + + if (mechs == NULL || mechs[0] == '\0') + return SASL_NOMECH; + + mechslen = strlen(mechs); + + listp = list = utils->malloc(mechslen + 1); + if (list == NULL) + return SASL_NOMEM; + + /* As per RFC 4422: + * SASL mechanism allowable characters are "AZ-_" + * separators can be any other characters and of any length + * even variable lengths between. + * + * But for convenience we accept lowercase ASCII. + * + * Apps should be encouraged to simply use space or comma space + * though + */ +#define ismechchar(c) (isalnum((c)) || (c) == '_' || (c) == '-') + do { + for (i = start = 0; i <= mechslen; i++) { + if (!ismechchar(mechs[i])) { + const char *mechp = &mechs[start]; + size_t len = i - start; + + if (len != 0 && + _mech_plus_p(mechp, len) == has_cb_data) { + memcpy(listp, mechp, len); + listp[len] = '\0'; + listp += len + 1; + (*count)++; + if (*server_can_cb == 0 && has_cb_data) + *server_can_cb = 1; + } + start = i+1; + } + } + if (has_cb_data) + has_cb_data = 0; + else + break; + } while (1); + + if (*count == 0) { + utils->free(list); + return SASL_NOMECH; + } + + *ordered_mechs = list; + + return SASL_OK; +} + +static INLINE int +_sasl_cbinding_disp(sasl_client_params_t *cparams, + int mech_nego, + int server_can_cb, + sasl_cbinding_disp_t *cbindingdisp) +{ + /* + * If negotiating mechanisms, then we fail immediately if the + * client requires channel binding and the server does not + * advertise support. Otherwise we send "y" (which later will + * become "p" if we select a supporting mechanism). + * + * If the client explicitly selected a mechanism, then we only + * send channel bindings if they're marked critical. + */ + + *cbindingdisp = SASL_CB_DISP_NONE; + + if (SASL_CB_PRESENT(cparams)) { + if (mech_nego) { + if (!server_can_cb && SASL_CB_CRITICAL(cparams)) { + return SASL_NOMECH; + } else { + *cbindingdisp = SASL_CB_DISP_WANT; + } + } else if (SASL_CB_CRITICAL(cparams)) { + *cbindingdisp = SASL_CB_DISP_USED; + } + } + + return SASL_OK; +} + +/* select a mechanism for a connection + * mechlist -- mechanisms server has available (punctuation ignored) + * secret -- optional secret from previous session + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * clientout -- the initial client response to send to the server + * mech -- set to mechanism name + * + * Returns: + * SASL_OK -- success + * SASL_NOMEM -- not enough memory + * SASL_NOMECH -- no mechanism meets requested properties + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + */ + +/* + * SASL mechanism allowable characters are "AZ-_" + * separators can be any other characters and of any length + * even variable lengths between. + * + * But for convenience we accept lowercase ASCII. + * + * Apps should be encouraged to simply use space or comma space + * though + */ +int sasl_client_start(sasl_conn_t *conn, + const char *mechlist, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + const char **mech) +{ + sasl_client_conn_t *c_conn = (sasl_client_conn_t *) conn; + char *ordered_mechs = NULL, *name; + cmechanism_t *m = NULL, *bestm = NULL; + size_t i, list_len, name_len; + sasl_ssf_t minssf = 0; + int result, server_can_cb = 0; + sasl_cbinding_disp_t cbindingdisp; + sasl_cbinding_disp_t cur_cbindingdisp; + sasl_cbinding_disp_t best_cbindingdisp = SASL_CB_DISP_NONE; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + + if (!conn) return SASL_BADPARAM; + + /* verify parameters */ + if (mechlist == NULL) { + PARAMERROR(conn); + } + + /* if prompt_need != NULL we've already been here + and just need to do the continue step again */ + + /* do a step */ + /* FIXME: Hopefully they only give us our own prompt_need back */ + if (prompt_need && *prompt_need != NULL) { + goto dostep; + } + + if (conn->props.min_ssf < conn->external.ssf) { + minssf = 0; + } else { + minssf = conn->props.min_ssf - conn->external.ssf; + } + + /* Order mechanisms so -PLUS are preferred */ + result = _sasl_client_order_mechs(c_conn->cparams->utils, + mechlist, + SASL_CB_PRESENT(c_conn->cparams), + &ordered_mechs, + &list_len, + &server_can_cb); + if (result != 0) + goto done; + + /* + * Determine channel binding disposition based on whether we + * are doing mechanism negotiation and whether server supports + * channel bindings. + */ + result = _sasl_cbinding_disp(c_conn->cparams, + (list_len > 1), + server_can_cb, + &cbindingdisp); + if (result != 0) + goto done; + + /* for each mechanism in client's list */ + for (m = c_conn->mech_list; !bestm && m != NULL; m = m->next) { + + for (i = 0, name = ordered_mechs; i < list_len; i++, name += name_len + 1) { + unsigned myflags; + int plus; + + name_len = strlen(name); + + if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, name_len, &plus)) { + continue; + } + + /* Do we have the prompts for it? */ + if (!have_prompts(conn, m->m.plug)) + break; + + /* Is it strong enough? */ + if (minssf > m->m.plug->max_ssf) + break; + + myflags = conn->props.security_flags; + + /* if there's an external layer with a better SSF then this is no + * longer considered a plaintext mechanism + */ + if ((conn->props.min_ssf <= conn->external.ssf) && + (conn->external.ssf > 1)) { + myflags &= ~SASL_SEC_NOPLAINTEXT; + } + + /* Does it meet our security properties? */ + if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) { + break; + } + + /* Can we meet it's features? */ + if (cbindingdisp == SASL_CB_DISP_USED && + !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) { + break; + } + + if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN) + && !conn->serverFQDN) { + break; + } + + /* Can it meet our features? */ + if ((conn->flags & SASL_NEED_PROXY) && + !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) { + break; + } + + if ((conn->flags & SASL_NEED_HTTP) && + !(m->m.plug->features & SASL_FEAT_SUPPORTS_HTTP)) { + break; + } + + if (SASL_CB_PRESENT(c_conn->cparams) && plus) { + cur_cbindingdisp = SASL_CB_DISP_USED; + } else { + cur_cbindingdisp = cbindingdisp; + } + + if (mech) { + *mech = m->m.plug->mech_name; + } + + /* Since the list of client mechs is ordered by preference/strength, + the first mech in our list that is available on the server and + meets our security properties and features is the "best" */ + best_cbindingdisp = cur_cbindingdisp; + bestm = m; + break; + } + } + + if (bestm == NULL) { + sasl_seterror(conn, 0, "No worthy mechs found"); + result = SASL_NOMECH; + goto done; + } + + /* make (the rest of) cparams */ + c_conn->cparams->service = conn->service; + c_conn->cparams->servicelen = (unsigned) strlen(conn->service); + + if (conn->serverFQDN) { + c_conn->cparams->serverFQDN = conn->serverFQDN; + c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN); + } + + c_conn->cparams->clientFQDN = c_conn->clientFQDN; + c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN); + + c_conn->cparams->external_ssf = conn->external.ssf; + c_conn->cparams->props = conn->props; + c_conn->cparams->cbindingdisp = best_cbindingdisp; + c_conn->mech = bestm; + + /* init that plugin */ + result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context, + c_conn->cparams, + &(conn->context)); + if (result != SASL_OK) goto done; + + /* do a step -- but only if we can do a client-send-first */ + dostep: + if(clientout) { + if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) { + *clientout = NULL; + *clientoutlen = 0; + result = SASL_CONTINUE; + } else { + result = sasl_client_step(conn, NULL, 0, prompt_need, + clientout, clientoutlen); + } + } + else + result = SASL_CONTINUE; + + done: + if (ordered_mechs != NULL) + c_conn->cparams->utils->free(ordered_mechs); + RETURN(conn, result); +} + +/* do a single authentication step. + * serverin -- the server message received by the client, MUST have a NUL + * sentinel, not counted by serverinlen + * output: + * prompt_need -- on SASL_INTERACT, list of prompts needed to continue + * clientout -- the client response to send to the server + * + * returns: + * SASL_OK -- success + * SASL_INTERACT -- user interaction needed to fill in prompt_need list + * SASL_BADPROT -- server protocol incorrect/cancelled + * SASL_BADSERV -- server failed mutual auth + */ + +int sasl_client_step(sasl_conn_t *conn, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen) +{ + sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; + int result; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + + /* check parameters */ + if ((serverin==NULL) && (serverinlen>0)) + PARAMERROR(conn); + + /* 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 client step after doneflag"); + return SASL_FAIL; + } + + if(clientout) *clientout = NULL; + if(clientoutlen) *clientoutlen = 0; + + /* do a step */ + result = c_conn->mech->m.plug->mech_step(conn->context, + c_conn->cparams, + serverin, + serverinlen, + prompt_need, + clientout, clientoutlen, + &conn->oparams); + + if (result == SASL_OK) { + /* So we're done on this end, but if both + * 1. the mech does server-send-last + * 2. the protocol does not + * we need to return no data */ + if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) { + *clientout = ""; + *clientoutlen = 0; + } + + 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"); + result = SASL_BADPROT; + } + } + + RETURN(conn,result); +} + +/* returns the length of all the mechanisms + * added up + */ + +static unsigned mech_names_len(cmechanism_t *mech_list) +{ + cmechanism_t *listptr; + unsigned result = 0; + + for (listptr = mech_list; + listptr; + listptr = listptr->next) + result += (unsigned) strlen(listptr->m.plug->mech_name); + + return result; +} + + +int _sasl_client_listmech(sasl_conn_t *conn, + const char *prefix, + const char *sep, + const char *suffix, + const char **result, + unsigned *plen, + int *pcount) +{ + sasl_client_conn_t *c_conn = (sasl_client_conn_t *)conn; + cmechanism_t *m = NULL; + sasl_ssf_t minssf = 0; + int ret; + size_t resultlen; + int flag; + const char *mysep; + + if (_sasl_client_active == 0) return SASL_NOTINIT; + if (!conn) return SASL_BADPARAM; + if (conn->type != SASL_CONN_CLIENT) PARAMERROR(conn); + + if (! result) + PARAMERROR(conn); + + if (plen != NULL) + *plen = 0; + if (pcount != NULL) + *pcount = 0; + + if (sep) { + mysep = sep; + } else { + mysep = " "; + } + + if (conn->props.min_ssf < conn->external.ssf) { + minssf = 0; + } else { + minssf = conn->props.min_ssf - conn->external.ssf; + } + + if (!c_conn->mech_list || c_conn->mech_length <= 0) { + INTERROR(conn, SASL_NOMECH); + } + + resultlen = (prefix ? strlen(prefix) : 0) + + (strlen(mysep) * (c_conn->mech_length - 1)) + + mech_names_len(c_conn->mech_list) + + (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'; + } + + flag = 0; + for (m = c_conn->mech_list; m != NULL; m = m->next) { + /* do we have the prompts for it? */ + if (!have_prompts(conn, m->m.plug)) { + continue; + } + + /* is it strong enough? */ + if (minssf > m->m.plug->max_ssf) { + continue; + } + + /* does it meet our security properties? */ + if (((conn->props.security_flags ^ m->m.plug->security_flags) + & conn->props.security_flags) != 0) { + continue; + } + + /* Can we meet it's features? */ + if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN) + && !conn->serverFQDN) { + continue; + } + + /* Can it meet our features? */ + if ((conn->flags & SASL_NEED_PROXY) && + !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) { + continue; + } + + /* Okay, we like it, add it to the list! */ + + 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, m->m.plug->mech_name); + } + + if (suffix) + strcat(conn->mechlist_buf,suffix); + + if (plen!=NULL) + *plen = (unsigned) strlen(conn->mechlist_buf); + + *result = conn->mechlist_buf; + + return SASL_OK; +} + +sasl_string_list_t *_sasl_client_mechs(void) +{ + cmechanism_t *listptr; + sasl_string_list_t *retval = NULL, *next=NULL; + + if(!_sasl_client_active) return NULL; + + /* make list */ + for (listptr = cmechlist->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->m.plug->mech_name; + + if(!retval) { + next->next = NULL; + retval = next; + } else { + next->next = retval; + retval = next; + } + } + + return retval; +} + + + + +/* It would be nice if we can show other information like Author, Company, Year, plugin version */ +static void +_sasl_print_mechanism ( + client_sasl_mechanism_t *m, + sasl_info_callback_stage_t stage, + void *rock __attribute__((unused)) +) +{ + char delimiter; + + if (stage == SASL_INFO_LIST_START) { + printf ("List of client plugins follows\n"); + return; + } else if (stage == SASL_INFO_LIST_END) { + return; + } + + /* Process the mechanism */ + printf ("Plugin \"%s\" ", m->plugname); + + /* There is no delay loading for client side plugins */ + printf ("[loaded]"); + + printf (", \tAPI version: %d\n", m->version); + + if (m->plug != NULL) { + printf ("\tSASL mechanism: %s, best SSF: %d\n", + m->plug->mech_name, + m->plug->max_ssf); + + printf ("\tsecurity flags:"); + + delimiter = ' '; + if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) { + printf ("%cNO_ANONYMOUS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) { + printf ("%cNO_PLAINTEXT", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOACTIVE) { + printf ("%cNO_ACTIVE", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NODICTIONARY) { + printf ("%cNO_DICTIONARY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) { + printf ("%cFORWARD_SECRECY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) { + printf ("%cPASS_CREDENTIALS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) { + printf ("%cMUTUAL_AUTH", delimiter); + delimiter = '|'; + } + + + + printf ("\n\tfeatures:"); + + delimiter = ' '; + if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { + printf ("%cWANT_CLIENT_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SERVER_FIRST) { + printf ("%cSERVER_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) { + printf ("%cPROXY_AUTHENTICATION", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) { + printf ("%cNEED_SERVER_FQDN", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_GSS_FRAMING) { + printf ("%cGSS_FRAMING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) { + printf ("%cCHANNEL_BINDING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SUPPORTS_HTTP) { + printf ("%cSUPPORTS_HTTP", delimiter); + delimiter = '|'; + } + } + +/* Delay loading is not supported for the client side plugins: + if (m->f) { + printf ("\n\twill be loaded from \"%s\"", m->f); + } + */ + + printf ("\n"); +} + + +/* Dump information about available client plugins */ +int sasl_client_plugin_info ( + const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ + sasl_client_info_callback_t *info_cb, + void *info_cb_rock +) +{ + cmechanism_t *m; + client_sasl_mechanism_t plug_data; + char * cur_mech; + char * mech_list = NULL; + char * p; + + if (info_cb == NULL) { + info_cb = _sasl_print_mechanism; + } + + if (cmechlist != NULL) { + info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); + + if (c_mech_list == NULL) { + m = cmechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + + m = m->next; + } + } else { + mech_list = strdup (c_mech_list); + + cur_mech = mech_list; + + while (cur_mech != NULL) { + p = strchr (cur_mech, ' '); + if (p != NULL) { + *p = '\0'; + p++; + } + + m = cmechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + } + + m = m->next; + } + + cur_mech = p; + } + + free (mech_list); + } + + info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); + + return (SASL_OK); + } + + return (SASL_NOTINIT); +} diff --git a/contrib/libs/sasl/lib/common.c b/contrib/libs/sasl/lib/common.c new file mode 100644 index 0000000000..d9104c8956 --- /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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#ifdef HAVE_SYSLOG +#include <syslog.h> +#endif +#include <stdarg.h> +#include <ctype.h> +#include <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*/ diff --git a/contrib/libs/sasl/lib/config.c b/contrib/libs/sasl/lib/config.c new file mode 100644 index 0000000000..d1a5b973de --- /dev/null +++ b/contrib/libs/sasl/lib/config.c @@ -0,0 +1,168 @@ +/* SASL Config file API + * Rob Siemborski + * Tim Martin (originally in Cyrus distribution) + */ +/* + * 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 + * innovation@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 <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +#include "sasl.h" +#include "saslint.h" + +struct configlist { + char *key; + char *value; +}; + +static struct configlist *configlist = NULL; +static int nconfiglist = 0; + +#define CONFIGLISTGROWSIZE 100 + +int sasl_config_init(const char *filename) +{ + FILE *infile; + int lineno = 0; + int alloced = 0; + char buf[4096]; + char *p, *key; + char *tail; + int result; + + nconfiglist=0; + + infile = fopen(filename, "r"); + if (!infile) { + return SASL_CONTINUE; + } + + while (fgets(buf, sizeof(buf), infile)) { + lineno++; + + if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; + for (p = buf; *p && isspace((int) *p); p++); + if (!*p || *p == '#') continue; + + key = p; + while (*p && (isalnum((int) *p) || *p == '-' || *p == '_')) { + if (isupper((int) *p)) *p = (char) tolower(*p); + p++; + } + if (*p != ':') { + fclose(infile); + return SASL_CONFIGERR; + } + *p++ = '\0'; + + while (*p && isspace((int) *p)) p++; + + if (!*p) { + fclose(infile); + return SASL_CONFIGERR; + } + + /* Now strip trailing spaces, if any */ + tail = p + strlen(p) - 1; + while (tail > p && isspace((int) *tail)) { + *tail = '\0'; + tail--; + } + + if (nconfiglist == alloced) { + alloced += CONFIGLISTGROWSIZE; + configlist=sasl_REALLOC((char *)configlist, + alloced * sizeof(struct configlist)); + if (configlist == NULL) { + fclose(infile); + return SASL_NOMEM; + } + } + + result = _sasl_strdup(key, + &(configlist[nconfiglist].key), + NULL); + if (result != SASL_OK) { + fclose(infile); + return result; + } + result = _sasl_strdup(p, + &(configlist[nconfiglist].value), + NULL); + if (result != SASL_OK) { + fclose(infile); + return result; + } + + nconfiglist++; + } + fclose(infile); + + return SASL_OK; +} + +const char *sasl_config_getstring(const char *key,const char *def) +{ + int opt; + + for (opt = 0; opt < nconfiglist; opt++) { + if (*key == configlist[opt].key[0] && + !strcmp(key, configlist[opt].key)) + return configlist[opt].value; + } + return def; +} + +void sasl_config_done(void) +{ + int opt; + + for (opt = 0; opt < nconfiglist; opt++) { + if (configlist[opt].key) sasl_FREE(configlist[opt].key); + if (configlist[opt].value) sasl_FREE(configlist[opt].value); + } + + sasl_FREE(configlist); + configlist = NULL; + nconfiglist = 0; +} diff --git a/contrib/libs/sasl/lib/dlopen.c b/contrib/libs/sasl/lib/dlopen.c new file mode 100644 index 0000000000..8284cd8700 --- /dev/null +++ b/contrib/libs/sasl/lib/dlopen.c @@ -0,0 +1,569 @@ +/* dlopen.c--Unix dlopen() dynamic loader interface + * Rob Siemborski + * Rob Earhart + */ +/* + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> +#include <limits.h> + +#include <sasl.h> +#include "saslint.h" + +#ifndef PIC +#include <saslplug.h> +#include "staticopen.h" +#endif + +#ifdef DO_DLOPEN +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else /* HAVE_DIRENT_H */ +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif /* ! HAVE_DIRENT_H */ + +#ifndef NAME_MAX +# ifdef _POSIX_NAME_MAX +# define NAME_MAX _POSIX_NAME_MAX +# else +# define NAME_MAX 16 +# endif +#endif + +#if NAME_MAX < 8 +# define NAME_MAX 8 +#endif + +#ifdef __hpux +#ifndef HAVE_DLFCN_H +#include <dl.h> + +typedef shl_t * dll_handle; +typedef void * dll_func; + +dll_handle +dlopen(char *fname, int mode) +{ + shl_t h = shl_load(fname, BIND_DEFERRED, 0L); + shl_t *hp = NULL; + + if (h) { + hp = (shl_t *)malloc(sizeof (shl_t)); + if (!hp) { + shl_unload(h); + } else { + *hp = h; + } + } + + return (dll_handle)hp; +} + +int +dlclose(dll_handle hp) +{ + shl_t h; + + if (hp != NULL) { + h = *((shl_t *)hp); + free(hp); + return shl_unload(h); + } else { + /* Return error */ + return -1; + } +} + +dll_func +dlsym(dll_handle h, char *n) +{ + dll_func handle; + + if (shl_findsym ((shl_t *)h, n, TYPE_PROCEDURE, &handle)) + return NULL; + + return (dll_func)handle; +} + +char *dlerror() +{ + if (errno != 0) { + return strerror(errno); + } + return "Generic shared library error"; +} + +#endif /* HAVE_DLFCN_H */ + +#ifdef __ia64 +#define SO_SUFFIX ".so" +#else +#define SO_SUFFIX ".sl" +#endif /* __ia64 */ + +#else /* __hpux */ +#define SO_SUFFIX ".so" +#endif + +#define LA_SUFFIX ".la" + +typedef struct lib_list +{ + struct lib_list *next; + void *library; +} lib_list_t; + +static lib_list_t *lib_list_head = NULL; + +#endif /* DO_DLOPEN */ + +int _sasl_locate_entry(void *library, const char *entryname, + void **entry_point) +{ +#ifdef DO_DLOPEN +/* note that we still check for known problem systems in + * case we are cross-compiling */ +#if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__)) + char adj_entryname[1024]; +#else +#define adj_entryname entryname +#endif + + if(!entryname) { + _sasl_log(NULL, SASL_LOG_ERR, + "no entryname in _sasl_locate_entry"); + return SASL_BADPARAM; + } + + if(!library) { + _sasl_log(NULL, SASL_LOG_ERR, + "no library in _sasl_locate_entry"); + return SASL_BADPARAM; + } + + if(!entry_point) { + _sasl_log(NULL, SASL_LOG_ERR, + "no entrypoint output pointer in _sasl_locate_entry"); + return SASL_BADPARAM; + } + +#if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__)) + snprintf(adj_entryname, sizeof adj_entryname, "_%s", entryname); +#endif + + *entry_point = NULL; + *entry_point = dlsym(library, adj_entryname); + if (*entry_point == NULL) { +#if 0 /* This message appears to confuse people */ + _sasl_log(NULL, SASL_LOG_DEBUG, + "unable to get entry point %s: %s", adj_entryname, + dlerror()); +#endif + return SASL_FAIL; + } + + return SASL_OK; +#else + return SASL_FAIL; +#endif /* DO_DLOPEN */ +} + +#ifdef DO_DLOPEN + +static int _sasl_plugin_load(char *plugin, void *library, + const char *entryname, + int (*add_plugin)(const char *, void *)) +{ + void *entry_point; + int result; + + result = _sasl_locate_entry(library, entryname, &entry_point); + if(result == SASL_OK) { + result = add_plugin(plugin, entry_point); + if(result != SASL_OK) + _sasl_log(NULL, SASL_LOG_DEBUG, + "_sasl_plugin_load failed on %s for plugin: %s\n", + entryname, plugin); + } + + return result; +} + +/* this returns the file to actually open. + * out should be a buffer of size PATH_MAX + * and may be the same as in. */ + +/* We'll use a static buffer for speed unless someone complains */ +#define MAX_LINE 2048 + +static int _parse_la(const char *prefix, const char *in, char *out) +{ + FILE *file; + size_t length; + char line[MAX_LINE]; + char *ntmp = NULL; + + if(!in || !out || !prefix || out == in) return SASL_BADPARAM; + + /* Set this so we can detect failure */ + *out = '\0'; + + length = strlen(in); + + if (strcmp(in + (length - strlen(LA_SUFFIX)), LA_SUFFIX)) { + if(!strcmp(in + (length - strlen(SO_SUFFIX)),SO_SUFFIX)) { + /* check for a .la file */ + if (strlen(prefix) + strlen(in) + strlen(LA_SUFFIX) + 1 >= MAX_LINE) + return SASL_BADPARAM; + strcpy(line, prefix); + strcat(line, in); + length = strlen(line); + *(line + (length - strlen(SO_SUFFIX))) = '\0'; + strcat(line, LA_SUFFIX); + file = fopen(line, "r"); + if(file) { + /* We'll get it on the .la open */ + fclose(file); + return SASL_FAIL; + } + } + if (strlen(prefix) + strlen(in) + 1 >= PATH_MAX) + return SASL_BADPARAM; + strcpy(out, prefix); + strcat(out, in); + return SASL_OK; + } + + if (strlen(prefix) + strlen(in) + 1 >= MAX_LINE) + return SASL_BADPARAM; + strcpy(line, prefix); + strcat(line, in); + + file = fopen(line, "r"); + if(!file) { + _sasl_log(NULL, SASL_LOG_WARN, + "unable to open LA file: %s", line); + return SASL_FAIL; + } + + while(!feof(file)) { + if(!fgets(line, MAX_LINE, file)) break; + if(line[strlen(line) - 1] != '\n') { + _sasl_log(NULL, SASL_LOG_WARN, + "LA file has too long of a line: %s", in); + fclose(file); + return SASL_BUFOVER; + } + if(line[0] == '\n' || line[0] == '#') continue; + if(!strncmp(line, "dlname=", sizeof("dlname=") - 1)) { + /* We found the line with the name in it */ + char *end; + char *start; + size_t len; + end = strrchr(line, '\''); + if(!end) continue; + start = &line[sizeof("dlname=")-1]; + len = strlen(start); + if(len > 3 && start[0] == '\'') { + ntmp=&start[1]; + *end='\0'; + /* Do we have dlname="" ? */ + if(ntmp == end) { + _sasl_log(NULL, SASL_LOG_DEBUG, + "dlname is empty in .la file: %s", in); + fclose(file); + return SASL_FAIL; + } + strcpy(out, prefix); + strcat(out, ntmp); + } + break; + } + } + if(ferror(file) || feof(file)) { + _sasl_log(NULL, SASL_LOG_WARN, + "Error reading .la: %s\n", in); + fclose(file); + return SASL_FAIL; + } + fclose(file); + + if(!(*out)) { + _sasl_log(NULL, SASL_LOG_WARN, + "Could not find a dlname line in .la file: %s", in); + return SASL_FAIL; + } + + return SASL_OK; +} +#endif /* DO_DLOPEN */ + +/* loads a plugin library */ +int _sasl_get_plugin(const char *file, + const sasl_callback_t *verifyfile_cb, + void **libraryptr) +{ +#ifdef DO_DLOPEN + int r = 0; + int flag; + void *library; + lib_list_t *newhead; + + r = ((sasl_verifyfile_t *)(verifyfile_cb->proc)) + (verifyfile_cb->context, file, SASL_VRFY_PLUGIN); + if (r != SASL_OK) return r; + +#ifdef RTLD_NOW + flag = RTLD_NOW; +#else + flag = 0; +#endif + + newhead = sasl_ALLOC(sizeof(lib_list_t)); + if(!newhead) return SASL_NOMEM; + + if (!(library = dlopen(file, flag))) { + _sasl_log(NULL, SASL_LOG_ERR, + "unable to dlopen %s: %s", file, dlerror()); + sasl_FREE(newhead); + return SASL_FAIL; + } + + newhead->library = library; + newhead->next = lib_list_head; + lib_list_head = newhead; + + *libraryptr = library; + return SASL_OK; +#else + return SASL_FAIL; +#endif /* DO_DLOPEN */ +} + +/* gets the list of mechanisms */ +int _sasl_load_plugins(const add_plugin_list_t *entrypoints, + const sasl_callback_t *getpath_cb, + const sasl_callback_t *verifyfile_cb) +{ + int result; + const add_plugin_list_t *cur_ep; +#ifdef DO_DLOPEN + char str[PATH_MAX], tmp[PATH_MAX+2], prefix[PATH_MAX+2]; + /* 1 for '/' 1 for trailing '\0' */ + char c; + int pos; + const char *path=NULL; + int position; + DIR *dp; + struct dirent *dir; +#endif +#ifndef PIC + add_plugin_t *add_plugin; + _sasl_plug_type type; + _sasl_plug_rec *p; +#endif + + if (! entrypoints + || ! getpath_cb + || getpath_cb->id != SASL_CB_GETPATH + || ! getpath_cb->proc + || ! verifyfile_cb + || verifyfile_cb->id != SASL_CB_VERIFYFILE + || ! verifyfile_cb->proc) + return SASL_BADPARAM; + +#ifndef PIC + /* do all the static plugins first */ + + for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { + + /* What type of plugin are we looking for? */ + if(!strcmp(cur_ep->entryname, "sasl_server_plug_init")) { + type = SERVER; + add_plugin = (add_plugin_t *)sasl_server_add_plugin; + } else if (!strcmp(cur_ep->entryname, "sasl_client_plug_init")) { + type = CLIENT; + add_plugin = (add_plugin_t *)sasl_client_add_plugin; + } else if (!strcmp(cur_ep->entryname, "sasl_auxprop_plug_init")) { + type = AUXPROP; + add_plugin = (add_plugin_t *)sasl_auxprop_add_plugin; + } else if (!strcmp(cur_ep->entryname, "sasl_canonuser_init")) { + type = CANONUSER; + add_plugin = (add_plugin_t *)sasl_canonuser_add_plugin; + } else { + /* What are we looking for then? */ + return SASL_FAIL; + } + for (p=_sasl_static_plugins; p->type; p++) { + if(type == p->type) + result = add_plugin(p->name, p->plug); + } + } +#endif /* !PIC */ + +/* only do the following if: + * + * we support dlopen() + * AND we are not staticly compiled + * OR we are staticly compiled and TRY_DLOPEN_WHEN_STATIC is defined + */ +#if defined(DO_DLOPEN) && (defined(PIC) || (!defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) + /* get the path to the plugins */ + result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context, + &path); + if (result != SASL_OK) return result; + if (! path) return SASL_FAIL; + + if (strlen(path) >= PATH_MAX) { /* no you can't buffer overrun */ + return SASL_FAIL; + } + + position=0; + do { + pos=0; + do { + c=path[position]; + position++; + str[pos]=c; + pos++; + } while ((c!=':') && (c!='=') && (c!=0)); + str[pos-1]='\0'; + + strcpy(prefix,str); + strcat(prefix,"/"); + + if ((dp=opendir(str)) !=NULL) /* ignore errors */ + { + while ((dir=readdir(dp)) != NULL) + { + size_t length; + void *library; + char *c; + char plugname[PATH_MAX]; + char name[PATH_MAX]; + + length = NAMLEN(dir); + if (length < 4) + continue; /* can not possibly be what we're looking for */ + + if (length + pos>=PATH_MAX) continue; /* too big */ + + if (strcmp(dir->d_name + (length - strlen(SO_SUFFIX)), + SO_SUFFIX) + && strcmp(dir->d_name + (length - strlen(LA_SUFFIX)), + LA_SUFFIX)) + continue; + + memcpy(name,dir->d_name,length); + name[length]='\0'; + + result = _parse_la(prefix, name, tmp); + if(result != SASL_OK) + continue; + + /* skip "lib" and cut off suffix -- + this only need be approximate */ + strcpy(plugname, name + 3); + c = strchr(plugname, (int)'.'); + if(c) *c = '\0'; + + result = _sasl_get_plugin(tmp, verifyfile_cb, &library); + + if(result != SASL_OK) + continue; + + for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { + _sasl_plugin_load(plugname, library, cur_ep->entryname, + cur_ep->add_plugin); + /* If this fails, it's not the end of the world */ + } + } + + closedir(dp); + } else { + _sasl_log(NULL, SASL_LOG_DEBUG, + "looking for plugins in '%s', failed to open directory, error: %s", + str, + strerror(errno)); + } + + } while ((c!='=') && (c!=0)); +#endif /* defined(DO_DLOPEN) && (!defined(PIC) || (defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) */ + + return SASL_OK; +} + +int +_sasl_done_with_plugins(void) +{ +#ifdef DO_DLOPEN + lib_list_t *libptr, *libptr_next; + + for(libptr = lib_list_head; libptr; libptr = libptr_next) { + libptr_next = libptr->next; + if(libptr->library) + dlclose(libptr->library); + sasl_FREE(libptr); + } + + lib_list_head = NULL; +#endif /* DO_DLOPEN */ + return SASL_OK; +} diff --git a/contrib/libs/sasl/lib/external.c b/contrib/libs/sasl/lib/external.c new file mode 100644 index 0000000000..4650838bf2 --- /dev/null +++ b/contrib/libs/sasl/lib/external.c @@ -0,0 +1,407 @@ +/* SASL server API implementation + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <ctype.h> +#include <string.h> +#include <sasl.h> +#include <saslplug.h> +#include "saslint.h" + +#include "../common/plugin_common.h" + +/***************************** Common Section *****************************/ + +/***************************** Server Section *****************************/ + +static int +external_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + if (!conn_context + || !sparams + || !sparams->utils + || !sparams->utils->conn) + return SASL_BADPARAM; + + if (!sparams->utils->conn->external.auth_id) + return SASL_NOMECH; + + *conn_context = NULL; + + return SASL_OK; +} + +static int +external_server_mech_step(void *conn_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + int result; + + if (!sparams + || !sparams->utils + || !sparams->utils->conn + || !sparams->utils->getcallback + || !serverout + || !serveroutlen + || !oparams) + return SASL_BADPARAM; + + if (!sparams->utils->conn->external.auth_id) + return SASL_BADPROT; + + /* xxx arbitrary limit here */ + if (clientinlen > 16384) return SASL_BADPROT; + + if ((sparams->props.security_flags & SASL_SEC_NOANONYMOUS) && + (!strcmp(sparams->utils->conn->external.auth_id, "anonymous"))) { + sasl_seterror(sparams->utils->conn,0,"anonymous login not allowed"); + return SASL_NOAUTHZ; + } + + *serverout = NULL; + *serveroutlen = 0; + + if (!clientin) { + /* No initial data; we're in a protocol which doesn't support it. + * So we let the server app know that we need some... */ + return SASL_CONTINUE; + } + + if (clientinlen) { /* if we have a non-zero authorization id */ + /* The user's trying to authorize as someone they didn't + * authenticate as */ + result = sparams->canon_user(sparams->utils->conn, + clientin, clientinlen, + SASL_CU_AUTHZID, oparams); + if(result != SASL_OK) return result; + + result = sparams->canon_user(sparams->utils->conn, + sparams->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED, oparams); + } else { + result = sparams->canon_user(sparams->utils->conn, + sparams->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIFIED | SASL_CU_AUTHZID, oparams); + } + + if (result != SASL_OK) return result; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static int +external_server_mech_avail(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + void **conn_context __attribute__((unused))) +{ + if (!sparams->utils->conn->external.auth_id) { + /* Return Temporary Failure */ + return SASL_NOTDONE; + } + + return SASL_OK; +} + +static sasl_server_plug_t external_server_plugins[] = +{ + { + "EXTERNAL", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NODICTIONARY, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* glob_context */ + &external_server_mech_new, /* mech_new */ + &external_server_mech_step, /* mech_step */ + NULL, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + &external_server_mech_avail, /* mech_avail */ + NULL /* spare */ + } +}; + +int external_server_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (!out_version || !pluglist || !plugcount) + return SASL_BADPARAM; + + if (max_version != SASL_SERVER_PLUG_VERSION) { + SETERROR( utils, "EXTERNAL version mismatch" ); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = external_server_plugins; + *plugcount = 1; + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context +{ + char *out_buf; + size_t out_buf_len; +} client_context_t; + +static int external_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + if (!params + || !params->utils + || !params->utils->conn + || !conn_context) + return SASL_BADPARAM; + + if (!params->utils->conn->external.auth_id) + return SASL_NOMECH; + + text = sasl_ALLOC(sizeof(client_context_t)); + if(!text) return SASL_NOMEM; + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static int +external_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *)conn_context; + const char *user = NULL; + int user_result = SASL_OK; + int result; + + if (!params + || !params->utils + || !params->utils->conn + || !params->utils->getcallback + || !clientout + || !clientoutlen + || !oparams) + return SASL_BADPARAM; + + if (!params->utils->conn->external.auth_id) + return SASL_BADPROT; + + if (serverinlen != 0) + return SASL_BADPROT; + + *clientout = NULL; + *clientoutlen = 0; + + /* try to get the userid */ + if (user == NULL) { + user_result = _plug_get_userid(params->utils, &user, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) + return user_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if (user_result == SASL_INTERACT) { + /* make the prompt list */ + int result = + _plug_make_prompts(params->utils, prompt_need, + "Please enter your authorization name", + "", + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } + + *clientoutlen = user ? (unsigned) strlen(user) : 0; + + result = _buf_alloc(&text->out_buf, &text->out_buf_len, *clientoutlen + 1); + + if (result != SASL_OK) return result; + + if (user && *user) { + result = params->canon_user(params->utils->conn, + user, 0, SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, + params->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID, oparams); + if (result != SASL_OK) return result; + + memcpy(text->out_buf, user, *clientoutlen); + } else { + result = params->canon_user(params->utils->conn, + params->utils->conn->external.auth_id, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + } + + text->out_buf[*clientoutlen] = '\0'; + + *clientout = text->out_buf; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static void +external_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils __attribute__((unused))) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + if(text->out_buf) sasl_FREE(text->out_buf); + + sasl_FREE(text); +} + +static const unsigned long external_required_prompts[] = { + SASL_CB_LIST_END +}; + +static sasl_client_plug_t external_client_plugins[] = +{ + { + "EXTERNAL", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | SASL_SEC_NODICTIONARY, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + external_required_prompts, /* required_prompts */ + NULL, /* glob_context */ + &external_client_mech_new, /* mech_new */ + &external_client_mech_step, /* mech_step */ + &external_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int external_client_plug_init(const sasl_utils_t *utils, + int max_version, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (!utils || !out_version || !pluglist || !plugcount) + return SASL_BADPARAM; + + if (max_version != SASL_CLIENT_PLUG_VERSION) { + SETERROR( utils, "EXTERNAL version mismatch" ); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = external_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/contrib/libs/sasl/lib/md5.c b/contrib/libs/sasl/lib/md5.c new file mode 100644 index 0000000000..c79f4f7d77 --- /dev/null +++ b/contrib/libs/sasl/lib/md5.c @@ -0,0 +1,527 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Function names changed to avoid namespace collisions: Rob Siemborski */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include <config.h> +#include "md5global.h" +#include "md5.h" +#include "hmac-md5.h" + +#ifndef WIN32 +# include <arpa/inet.h> +#endif + +/* Constants for MD5Transform routine. +*/ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((SASL_UINT4 [4], const unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, SASL_UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((SASL_UINT4 *, const unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + + */ +#ifdef I +/* This might be defined via NANA */ +#undef I +#endif + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + + */ + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. +*/ + +#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (SASL_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. +*/ + +void _sasl_MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the context. +*/ + +void _sasl_MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +const unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((SASL_UINT4)inputLen << 3)) + < ((SASL_UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((SASL_UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform + (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); + +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. +*/ + +void _sasl_MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + _sasl_MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + _sasl_MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ + +static void MD5Transform (state, block) +SASL_UINT4 state[4]; +const unsigned char block[64]; +{ + SASL_UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (SASL_UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + + */ + +static void Encode (output, input, len) +unsigned char *output; +SASL_UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (SASL_UINT4). Assumes len is + a multiple of 4. + + */ + +static void Decode (output, input, len) +SASL_UINT4 *output; +const unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((SASL_UINT4)input[j]) | (((SASL_UINT4)input[j+1]) << 8) | (((SASL_UINT4)input[j+2]) << 16) + | (((SASL_UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. +*/ + +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + +void _sasl_hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, + int key_len) +{ + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + _sasl_MD5Init(&tctx); + _sasl_MD5Update(&tctx, key, key_len); + _sasl_MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset((POINTER)k_ipad, '\0', sizeof k_ipad); + MD5_memset((POINTER)k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + _sasl_MD5Init(&hmac->ictx); /* init inner context */ + _sasl_MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */ + + _sasl_MD5Init(&hmac->octx); /* init outer context */ + _sasl_MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ + + /* scrub the pads and key context (if used) */ + MD5_memset((POINTER)&k_ipad, 0, sizeof(k_ipad)); + MD5_memset((POINTER)&k_opad, 0, sizeof(k_opad)); + MD5_memset((POINTER)&tk, 0, sizeof(tk)); + + /* and we're done. */ +} + +/* The precalc and import routines here rely on the fact that we pad + * the key out to 64 bytes and use that to initialize the md5 + * contexts, and that updating an md5 context with 64 bytes of data + * leaves nothing left over; all of the interesting state is contained + * in the state field, and none of it is left over in the count and + * buffer fields. So all we have to do is save the state field; we + * can zero the others when we reload it. Which is why the decision + * was made to pad the key out to 64 bytes in the first place. */ +void _sasl_hmac_md5_precalc(HMAC_MD5_STATE *state, + const unsigned char *key, + int key_len) +{ + HMAC_MD5_CTX hmac; + unsigned lupe; + + _sasl_hmac_md5_init(&hmac, key, key_len); + for (lupe = 0; lupe < 4; lupe++) { + state->istate[lupe] = htonl(hmac.ictx.state[lupe]); + state->ostate[lupe] = htonl(hmac.octx.state[lupe]); + } + MD5_memset((POINTER)&hmac, 0, sizeof(hmac)); +} + + +void _sasl_hmac_md5_import(HMAC_MD5_CTX *hmac, + HMAC_MD5_STATE *state) +{ + unsigned lupe; + MD5_memset((POINTER)hmac, 0, sizeof(HMAC_MD5_CTX)); + for (lupe = 0; lupe < 4; lupe++) { + hmac->ictx.state[lupe] = ntohl(state->istate[lupe]); + hmac->octx.state[lupe] = ntohl(state->ostate[lupe]); + } + /* Init the counts to account for our having applied + * 64 bytes of key; this works out to 0x200 (64 << 3; see + * MD5Update above...) */ + hmac->ictx.count[0] = hmac->octx.count[0] = 0x200; +} + +void _sasl_hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac) +{ + _sasl_MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */ + _sasl_MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */ + _sasl_MD5Final(digest, &hmac->octx); /* Finalize outer md5 */ +} + + +void _sasl_hmac_md5(text, text_len, key, key_len, digest) +const unsigned char* text; /* pointer to data stream */ +int text_len; /* length of data stream */ +const unsigned char* key; /* pointer to authentication key */ +int key_len; /* length of authentication key */ +unsigned char *digest; /* caller digest to be filled in */ +{ + MD5_CTX context; + + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + _sasl_MD5Init(&tctx); + _sasl_MD5Update(&tctx, key, key_len); + _sasl_MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset(k_ipad, '\0', sizeof k_ipad); + MD5_memset(k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + + _sasl_MD5Init(&context); /* init context for 1st + * pass */ + _sasl_MD5Update(&context, k_ipad, 64); /* start with inner pad */ + _sasl_MD5Update(&context, text, text_len); /* then text of datagram */ + _sasl_MD5Final(digest, &context); /* finish up 1st pass */ + + /* + * perform outer MD5 + */ + _sasl_MD5Init(&context); /* init context for 2nd + * pass */ + _sasl_MD5Update(&context, k_opad, 64); /* start with outer pad */ + _sasl_MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + _sasl_MD5Final(digest, &context); /* finish up 2nd pass */ + +} diff --git a/contrib/libs/sasl/lib/saslint.h b/contrib/libs/sasl/lib/saslint.h new file mode 100644 index 0000000000..ebade78f3a --- /dev/null +++ b/contrib/libs/sasl/lib/saslint.h @@ -0,0 +1,544 @@ +/* saslint.h - internal SASL library definitions + * 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 + * innovation@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 <config.h> +#include "sasl.h" +#include "saslplug.h" +#include "saslutil.h" +#include "prop.h" + +#ifndef INLINE +#if defined (WIN32) +/* Visual Studio: "inline" keyword is not available in C, only in C++ */ +#define INLINE __inline +#else +#define INLINE inline +#endif +#endif + +/* #define'd constants */ +#define CANON_BUF_SIZE 1024 + +/* 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), + * though "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/SASL_CONF_PATH variables */ +#ifdef WIN32 +#define PATHS_DELIMITER ';' +#else +#define PATHS_DELIMITER ':' +#endif + +/* A FQDN max len is 255 per RFC 1035, + * this means 253 chars max, we add one more for zero terminator */ +#define MAXFQDNLEN 254 + +/* 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]; + + /* Allocated by sasl_encodev if the output contains multiple SASL packet. */ + buffer_info_t multipacket_encoded_data; +}; + +/* Server Conn Type Information */ + +typedef struct mechanism +{ + server_sasl_mechanism_t m; + struct mechanism *next; +} 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 loaded mechanisms */ + int mech_length; /* number of loaded 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 *appname; /* application name buffer (for sparams) */ + 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; + mechanism_t *mech_list; /* list of available mechanisms */ + int mech_length; /* number of available mechanisms */ +} sasl_server_conn_t; + +/* Client Conn Type Information */ + +typedef struct cmechanism +{ + client_sasl_mechanism_t m; + 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; + + cmechanism_t *mech_list; /* list of available mechanisms */ + int mech_length; /* number of available mechanisms */ +} 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 int (*_sasl_client_idle_hook)(sasl_conn_t *conn); +extern int (*_sasl_server_idle_hook)(sasl_conn_t *conn); + +/* These return SASL_OK if we've actually finished cleanup, + * SASL_NOTINIT if that part of the library isn't initialized, and + * SASL_CONTINUE if we need to call them again */ +extern int (*_sasl_client_cleanup_hook)(void); +extern int (*_sasl_server_cleanup_hook)(void); + +extern sasl_allocation_utils_t _sasl_allocation_utils; +extern sasl_mutex_utils_t _sasl_mutex_utils; +extern int _sasl_allocation_locked; + +void sasl_common_done(void); + +extern int _sasl_is_equal_mech(const char *req_mech, + const char *plug_mech, + size_t req_mech_len, + int *plus); + +/* + * 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_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(sasl_global_callbacks_t *global_callbacks); + +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, + sasl_callback_ft * 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 const char *sasl_config_getstring(const char *key,const char *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 (sasldb.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 int _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); +int _sasl_canon_user_lookup (sasl_conn_t *conn, + const char *user, + unsigned ulen, + unsigned flags, + sasl_out_params_t *oparams); + +/* + * saslutil.c + */ +int get_fqhostname( + char *name, + int namelen, + int abort_if_no_fqdn + ); + +#ifndef HAVE_GETHOSTNAME +#ifdef sun +/* gotta define gethostname ourselves on suns */ +extern int gethostname(char *, int); +#endif +#endif /* HAVE_GETHOSTNAME */ + +#ifdef WIN32 +char* _sasl_wchar_to_utf8(WCHAR *str); +WCHAR* _sasl_utf8_to_wchar(const char *str); +#endif + +#endif /* SASLINT_H */ diff --git a/contrib/libs/sasl/lib/saslutil.c b/contrib/libs/sasl/lib/saslutil.c new file mode 100644 index 0000000000..46c628c7f5 --- /dev/null +++ b/contrib/libs/sasl/lib/saslutil.c @@ -0,0 +1,790 @@ +/* saslutil.c + * 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 + * innovation@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. + */ + +#if defined(WIN32) +#define _CRT_RAND_S +#endif + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#include "saslint.h" +#include <saslutil.h> + +/* Contains: + * + * sasl_decode64 + * sasl_encode64 + * sasl_mkchal + * sasl_utf8verify + * sasl_randcreate + * sasl_randfree + * sasl_randseed + * sasl_rand + * sasl_churn + * sasl_erasebuffer + */ + +char *encode_table; +char *decode_table; + +#define RPOOL_SIZE 3 +struct sasl_rand_s { + unsigned short pool[RPOOL_SIZE]; + /* since the init time might be really bad let's make this lazy */ + int initialized; +}; + +#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) + +static char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????"; + +static signed char index_64[128] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +}; + +/* base64 encode + * in -- input data + * inlen -- input data length + * out -- output buffer (will be NUL terminated) + * outmax -- max size of output buffer + * result: + * outlen -- gets actual length of output buffer (optional) + * + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ + +int sasl_encode64(const char *_in, + unsigned inlen, + char *_out, + unsigned outmax, + unsigned *outlen) +{ + const unsigned char *in = (const unsigned char *)_in; + unsigned char *out = (unsigned char *)_out; + unsigned char oval; + unsigned olen; + + /* check params */ + if ((inlen > 0) && (in == NULL)) return SASL_BADPARAM; + + /* Will it fit? */ + olen = (inlen + 2) / 3 * 4; + if (outlen) { + *outlen = olen; + } + if (outmax <= olen) { + return SASL_BUFOVER; + } + + /* Do the work... */ + while (inlen >= 3) { + /* user provided max buffer size; make sure we don't go over it */ + *out++ = basis_64[in[0] >> 2]; + *out++ = basis_64[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = basis_64[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = basis_64[in[2] & 0x3f]; + in += 3; + inlen -= 3; + } + if (inlen > 0) { + /* user provided max buffer size; make sure we don't go over it */ + *out++ = basis_64[in[0] >> 2]; + oval = (in[0] << 4) & 0x30; + if (inlen > 1) oval |= in[1] >> 4; + *out++ = basis_64[oval]; + *out++ = (inlen < 2) ? '=' : basis_64[(in[1] << 2) & 0x3c]; + *out++ = '='; + } + + *out = '\0'; + + return SASL_OK; +} + +/* base64 decode + * in -- input data + * inlen -- length of input data + * out -- output data (may be same as in, must have enough space) + * outmax -- max size of output buffer + * result: + * outlen -- actual output length + * + * returns: + * SASL_BADPROT on bad base64, + * SASL_BUFOVER if result won't fit, + * SASL_CONTINUE on a partial block, + * SASL_OK on success + */ + +int sasl_decode64(const char *in, + unsigned inlen, + char *out, + unsigned outmax, /* size of the buffer, not counting the NUL */ + unsigned *outlen) +{ + unsigned len = 0; + unsigned j; + int c[4]; + int saw_equal = 0; + + /* check parameters */ + if (out == NULL) return SASL_FAIL; + + if (inlen > 0 && *in == '\r') return SASL_FAIL; + + while (inlen > 3) { + /* No data is valid after an '=' character */ + if (saw_equal) { + return SASL_BADPROT; + } + + for (j = 0; j < 4; j++) { + c[j] = in[0]; + in++; + inlen--; + } + + if (CHAR64(c[0]) == -1 || CHAR64(c[1]) == -1) return SASL_BADPROT; + if (c[2] != '=' && CHAR64(c[2]) == -1) return SASL_BADPROT; + if (c[3] != '=' && CHAR64(c[3]) == -1) return SASL_BADPROT; + /* No data is valid after a '=' character, unless it is another '=' */ + if (c[2] == '=' && c[3] != '=') return SASL_BADPROT; + if (c[2] == '=' || c[3] == '=') { + saw_equal = 1; + } + + *out++ = (CHAR64(c[0]) << 2) | (CHAR64(c[1]) >> 4); + if (++len >= outmax) return SASL_BUFOVER; + if (c[2] != '=') { + *out++ = ((CHAR64(c[1]) << 4) & 0xf0) | (CHAR64(c[2]) >> 2); + if (++len >= outmax) return SASL_BUFOVER; + if (c[3] != '=') { + *out++ = ((CHAR64(c[2]) << 6) & 0xc0) | CHAR64(c[3]); + if (++len >= outmax) return SASL_BUFOVER; + } + } + } + + *out = '\0'; /* NUL terminate the output string */ + + if (outlen) *outlen = len; + + if (inlen != 0) { + if (saw_equal) { + /* Unless there is CRLF at the end? */ + return SASL_BADPROT; + } else { + return (SASL_CONTINUE); + } + } + + return SASL_OK; +} + +/* make a challenge string (NUL terminated) + * buf -- buffer for result + * maxlen -- max length of result + * hostflag -- 0 = don't include hostname, 1 = include hostname + * returns final length or 0 if not enough space + */ + +int sasl_mkchal(sasl_conn_t *conn, + char *buf, + unsigned maxlen, + unsigned hostflag) +{ + sasl_rand_t *pool = NULL; + unsigned long randnum; + int ret; + time_t now; + unsigned len; + + len = 4 /* <.>\0 */ + + (2 * 20); /* 2 numbers, 20 => max size of 64bit + * ulong in base 10 */ + if (hostflag && conn->serverFQDN) + len += (unsigned) strlen(conn->serverFQDN) + 1 /* for the @ */; + + if (maxlen < len) + return 0; + + ret = sasl_randcreate(&pool); + if(ret != SASL_OK) return 0; /* xxx sasl return code? */ + + sasl_rand(pool, (char *)&randnum, sizeof(randnum)); + sasl_randfree(&pool); + + time(&now); + + if (hostflag && conn->serverFQDN) + snprintf(buf,maxlen, "<%lu.%lu@%s>", randnum, (unsigned long)now, conn->serverFQDN); /* don't care much about time 32bit overlap */ + else + snprintf(buf,maxlen, "<%lu.%lu>", randnum, (unsigned long)now); + + return (int) strlen(buf); +} + + /* borrowed from larry. probably works :) + * probably is also in acap server somewhere + */ +int sasl_utf8verify(const char *str, unsigned len) +{ + unsigned i; + for (i = 0; i < len; i++) { + /* how many octets? */ + int seqlen = 0; + while (str[i] & (0x80 >> seqlen)) ++seqlen; + if (seqlen == 0) continue; /* this is a valid US-ASCII char */ + if (seqlen == 1) return SASL_BADPROT; /* this shouldn't happen here */ + if (seqlen > 6) return SASL_BADPROT; /* illegal */ + while (--seqlen) + if ((str[++i] & 0xC0) != 0x80) return SASL_BADPROT; /* needed a 10 octet */ + } + return SASL_OK; +} + +/* + * To see why this is really bad see RFC 1750 + * + * unfortunatly there currently is no way to make + * cryptographically secure pseudo random numbers + * without specialized hardware etc... + * thus, this is for nonce use only + */ +void getranddata(unsigned short ret[RPOOL_SIZE]) +{ + long curtime; + + memset(ret, 0, RPOOL_SIZE*sizeof(unsigned short)); + +#ifdef DEV_RANDOM + { + int fd; + + fd = open(DEV_RANDOM, O_RDONLY); + if(fd != -1) { + unsigned char *buf = (unsigned char *)ret; + ssize_t bytesread = 0; + size_t bytesleft = RPOOL_SIZE*sizeof(unsigned short); + + do { + bytesread = read(fd, buf, bytesleft); + if(bytesread == -1 && errno == EINTR) continue; + else if(bytesread <= 0) break; + bytesleft -= bytesread; + buf += bytesread; + } while(bytesleft != 0); + + close(fd); + } + } +#endif + +#ifdef HAVE_GETPID + ret[0] ^= (unsigned short) getpid(); +#endif + +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval tv; + + /* xxx autoconf macro */ +#ifdef _SVID_GETTOD + if (!gettimeofday(&tv)) +#else + if (!gettimeofday(&tv, NULL)) +#endif + { + /* longs are guaranteed to be at least 32 bits; we need + 16 bits in each short */ + ret[0] ^= (unsigned short) (tv.tv_sec & 0xFFFF); + ret[1] ^= (unsigned short) (clock() & 0xFFFF); + ret[1] ^= (unsigned short) (tv.tv_usec >> 16); + ret[2] ^= (unsigned short) (tv.tv_usec & 0xFFFF); + return; + } + } +#endif /* HAVE_GETTIMEOFDAY */ + + /* if all else fails just use time() */ + curtime = (long) time(NULL); /* better be at least 32 bits */ + + ret[0] ^= (unsigned short) (curtime >> 16); + ret[1] ^= (unsigned short) (curtime & 0xFFFF); + ret[2] ^= (unsigned short) (clock() & 0xFFFF); + + return; +} + +int sasl_randcreate(sasl_rand_t **rpool) +{ + (*rpool)=sasl_ALLOC(sizeof(sasl_rand_t)); + if ((*rpool) == NULL) return SASL_NOMEM; + + /* init is lazy */ + (*rpool)->initialized = 0; + + return SASL_OK; +} + +void sasl_randfree(sasl_rand_t **rpool) +{ + sasl_FREE(*rpool); +} + +void sasl_randseed (sasl_rand_t *rpool, const char *seed, unsigned len) +{ + /* is it acceptable to just use the 1st 3 char's given??? */ + unsigned int lup; + + /* check params */ + if (seed == NULL) return; + if (rpool == NULL) return; + + rpool->initialized = 1; + + if (len > sizeof(unsigned short)*RPOOL_SIZE) + len = sizeof(unsigned short)*RPOOL_SIZE; + + for (lup = 0; lup < len; lup += 2) + rpool->pool[lup/2] = (seed[lup] << 8) + seed[lup + 1]; +} + +static void randinit(sasl_rand_t *rpool) +{ + if (!rpool) return; + + if (!rpool->initialized) { + getranddata(rpool->pool); + rpool->initialized = 1; +#if !(defined(WIN32)||defined(macintosh)) +#ifndef HAVE_JRAND48 + { + /* xxx varies by platform */ + unsigned int *foo = (unsigned int *)rpool->pool; + srandom(*foo); + } +#endif /* HAVE_JRAND48 */ +#elif defined(WIN32) + { + unsigned int *foo = (unsigned int *)rpool->pool; + srand(*foo); + } +#endif /* WIN32 */ + } + +} + +void sasl_rand (sasl_rand_t *rpool, char *buf, unsigned len) +{ + unsigned int lup; +#if defined(WIN32) && !defined(__MINGW32__) + unsigned int randomValue; +#endif + + /* check params */ + if (!rpool || !buf) return; + + /* init if necessary */ + randinit(rpool); + + for (lup = 0; lup < len; lup++) { +#if defined(__MINGW32__) + buf[lup] = (char) (rand() >> 8); +#elif defined(WIN32) + if (rand_s(&randomValue) != 0) { + randomValue = rand(); + } + + buf[lup] = (char) (randomValue >> 8); +#elif defined(macintosh) + buf[lup] = (char) (rand() >> 8); +#else /* !WIN32 && !macintosh */ +#ifdef HAVE_JRAND48 + buf[lup] = (char) (jrand48(rpool->pool) >> 8); +#else + buf[lup] = (char) (random() >> 8); +#endif /* HAVE_JRAND48 */ +#endif /* WIN32 */ + } +} + +/* this function is just a bad idea all around, since we're not trying to + implement a true random number generator */ +void sasl_churn (sasl_rand_t *rpool, const char *data, unsigned len) +{ + unsigned int lup; + + /* check params */ + if (!rpool || !data) return; + + /* init if necessary */ + randinit(rpool); + + for (lup=0; lup<len; lup++) + rpool->pool[lup % RPOOL_SIZE] ^= data[lup]; +} + +void sasl_erasebuffer(char *buf, unsigned len) { + memset(buf, 0, len); +} + +/* Lowercase string in place */ +char *sasl_strlower ( + char *val +) +{ + int i; + + if (val == NULL) { + return (NULL); + } + +/* don't use tolower(), as it is locale dependent */ + + for (i = 0; val[i] != '\0'; i++) { + if (val[i] >= 'A' && val[i] <= 'Z') { + val[i] = val[i] - 'A' + 'a'; + } + } + + return (val); +} + +/* A version of gethostname that tries hard to return a FQDN */ +int get_fqhostname( + char *name, + int namelen, + int abort_if_no_fqdn +) +{ + int return_value; + struct addrinfo hints; + struct addrinfo *result; + + return_value = gethostname (name, namelen); + name[namelen-1] = '\0'; /* insure string is always 0 terminated*/ + if (return_value != 0) { + return (return_value); + } + + if (strchr (name, '.') != NULL) { + goto LOWERCASE; + } + +/* gethostname hasn't returned a FQDN, we have to canonify it ourselves */ + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; /* TCP only */ +/* A value of zero for ai_protocol indicates the caller will accept any protocol. or IPPROTO_TCP? */ + hints.ai_protocol = 0; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + hints.ai_addrlen = 0; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if (getaddrinfo(name, + NULL, /* don't care abour service/port */ + &hints, + &result) != 0) { + if (abort_if_no_fqdn) { + /* errno on Unix, WSASetLastError on Windows are already done by the function */ + return (-1); + } else { + goto LOWERCASE; + } + } + + if (result == NULL || result->ai_canonname == NULL + || strchr (result->ai_canonname, '.') == NULL + || strlen (result->ai_canonname) > namelen -1) { + freeaddrinfo (result); + if (abort_if_no_fqdn) { +#ifdef WIN32 + WSASetLastError (WSANO_DATA); +#elif defined(ENODATA) + errno = ENODATA; +#elif defined(EADDRNOTAVAIL) + errno = EADDRNOTAVAIL; +#endif + return (-1); + } else { + goto LOWERCASE; + } + } + + strncpy (name, result->ai_canonname, namelen); + name[namelen-1] = '\0'; /* insure string is always 0 terminated*/ + freeaddrinfo (result); + +LOWERCASE: + sasl_strlower (name); + return (0); +} + +#if defined(WIN32) && !defined(__MINGW64_VERSION_MAJOR) +/***************************************************************************** + * + * MODULE NAME : GETOPT.C + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +/****************************************************************************** + * getopt() + * + * The getopt() function is a command line parser. It returns the next + * option character in argv that matches an option character in opstring. + * + * The argv argument points to an array of argc+1 elements containing argc + * pointers to character strings followed by a null pointer. + * + * The opstring argument points to a string of option characters; if an + * option character is followed by a colon, the option is expected to have + * an argument that may or may not be separated from it by white space. + * The external variable optarg is set to point to the start of the option + * argument on return from getopt(). + * + * The getopt() function places in optind the argv index of the next argument + * to be processed. The system initializes the external variable optind to + * 1 before the first call to getopt(). + * + * When all options have been processed (that is, up to the first nonoption + * argument), getopt() returns EOF. The special option "--" may be used to + * delimit the end of the options; EOF will be returned, and "--" will be + * skipped. + * + * The getopt() function returns a question mark (?) when it encounters an + * option character not included in opstring. This error message can be + * disabled by setting opterr to zero. Otherwise, it returns the option + * character that was detected. + * + * If the special option "--" is detected, or all options have been + * processed, EOF is returned. + * + * Options are marked by either a minus sign (-) or a slash (/). + * + * No errors are defined. + *****************************************************************************/ + +#include <string.h> /* for strchr() */ + +/* static (global) variables that are specified as exported by getopt() */ +__declspec(dllexport) char *optarg = NULL; /* pointer to the start of the option argument */ +__declspec(dllexport) int optind = 1; /* number of the next argv[] to be evaluated */ +__declspec(dllexport) int opterr = 1; /* non-zero if a question mark should be returned */ + + +/* handle possible future character set concerns by putting this in a macro */ +#define _next_char(string) (char)(*(string+1)) + +int getopt(int argc, char *argv[], char *opstring) +{ + static char *pIndexPosition = NULL; /* place inside current argv string */ + char *pArgString = NULL; /* where to start from next */ + char *pOptString; /* the string in our program */ + + + if (pIndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++pIndexPosition)) { + /* there is more to come in the most recent argv */ + pArgString = pIndexPosition; + } + } + + if (pArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + pArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *pArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(pArgString, "-") == 0) || + (strcmp(pArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + pArgString++; /* look past the / or - */ + } + + if (':' == *pArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((pOptString = strchr(opstring, *pArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*pArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ + optarg = &pArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*pArgString); + } + } + pIndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = pArgString; /* point to the letter we're on */ + } + return (int)*pArgString; /* return the letter that matched */ + } +} + +#ifndef PASSWORD_MAX +# define PASSWORD_MAX 255 +#endif + +#include <conio.h> +char * +getpass(prompt) +const char *prompt; +{ + register char *p; + register int c; + static char pbuf[PASSWORD_MAX]; + + fprintf(stderr, "%s", prompt); (void) fflush(stderr); + for (p=pbuf; (c = _getch())!=13 && c!=EOF;) { + if (p < &pbuf[sizeof(pbuf)-1]) + *p++ = (char) c; + } + *p = '\0'; + fprintf(stderr, "\n"); (void) fflush(stderr); + return(pbuf); +} + + + +#endif /* WIN32 */ diff --git a/contrib/libs/sasl/lib/server.c b/contrib/libs/sasl/lib/server.c new file mode 100644 index 0000000000..bff461f825 --- /dev/null +++ b/contrib/libs/sasl/lib/server.c @@ -0,0 +1,2406 @@ +/* SASL server API implementation + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* local functions/structs don't start with sasl + */ +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#ifndef macintosh +#include <sys/types.h> +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <string.h> +#include <ctype.h> + +#include "sasl.h" +#include "saslint.h" +#include "saslplug.h" +#include "saslutil.h" + +#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 *user, unsigned userlen, + const char *pass, unsigned passlen); + +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; + const char *password_request[] = { SASL_AUX_PASSWORD_PROP, NULL }; + const char *user_delete_request[] = { SASL_AUX_PASSWORD_PROP, SASL_AUX_ALL, NULL }; + sasl_server_userdb_setpass_t *setpass_cb = NULL; + void *context = NULL; + int tried_setpass = 0; + int failed = 0; + mechanism_t *sm; + server_sasl_mechanism_t *m; + char *current_mech; + + 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); + + /* Check that we have an active SASL mechanism */ + if (sasl_getprop (conn, + SASL_MECHNAME, + (const void **) ¤t_mech) != SASL_OK) { + current_mech = NULL; + } + + if ( (flags & SASL_SET_CURMECH_ONLY) && + (current_mech == NULL) ) { + sasl_seterror( conn, SASL_NOLOG, + "No current SASL mechanism available"); + RETURN(conn, SASL_BADPARAM); + } + + /* Do we want to store SASL_AUX_PASSWORD_PROP (plain text)? and + * Do we have an auxprop backend that can store properties? + */ + if ((flags & SASL_SET_DISABLE || !(flags & SASL_SET_NOPLAIN)) && + sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) { + + tried_setpass++; + + if (flags & SASL_SET_DISABLE) { + pass = NULL; + passlen = 0; + result = prop_request(s_conn->sparams->propctx, user_delete_request); + } else { + result = prop_request(s_conn->sparams->propctx, password_request); + } + if (result == SASL_OK) { + /* NOTE: When deleting users, this will work in a backward compatible way */ + result = prop_set(s_conn->sparams->propctx, SASL_AUX_PASSWORD_PROP, + pass, passlen); + } + if (result == SASL_OK && flags & SASL_SET_DISABLE) { + result = prop_set(s_conn->sparams->propctx, SASL_AUX_ALL, + NULL, 0); + } + if (result == SASL_OK) { + result = sasl_auxprop_store(conn, s_conn->sparams->propctx, user); + } + if (result != SASL_OK) { + _sasl_log(conn, SASL_LOG_ERR, + "setpass failed for %s: %z", + user, result); + failed++; + } else { + _sasl_log(conn, SASL_LOG_NOTE, + "setpass succeeded for %s", user); + } + } + + /* We want to preserve the current value of result, so we use tmpresult below */ + + /* call userdb callback function */ + tmpresult = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS, + (sasl_callback_ft *)&setpass_cb, &context); + if (tmpresult == SASL_OK && setpass_cb) { + + tried_setpass++; + + tmpresult = setpass_cb(conn, context, user, pass, passlen, + s_conn->sparams->propctx, flags); + if(tmpresult != SASL_OK) { + if (tmpresult == SASL_CONSTRAINT_VIOLAT) { + if (result == SASL_OK) { + result = tmpresult; + } + } else { + result = tmpresult; + } + _sasl_log(conn, SASL_LOG_ERR, + "setpass callback failed for %s: %z", + user, tmpresult); + failed++; + } else { + _sasl_log(conn, SASL_LOG_NOTE, + "setpass callback succeeded for %s", user); + } + } + + /* now we let the mechanisms set their secrets */ + for (sm = s_conn->mech_list; sm; sm = sm->next) { + m = &sm->m; + + if (!m->plug->setpass) { + /* can't set pass for this mech */ + continue; + } + + /* Invoke only one setpass for the currently selected mechanism, + if SASL_SET_CURMECH_ONLY is specified */ + if ((flags & SASL_SET_CURMECH_ONLY) && + (strcmp(current_mech, m->plug->mech_name) != 0)) { + continue; + } + + tried_setpass++; + + 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 if (tmpresult == SASL_CONSTRAINT_VIOLAT) { + _sasl_log(conn, SASL_LOG_ERR, + "%s: failed to set secret for %s: constrain violation", + m->plug->mech_name, user); + if (result == SASL_OK) { + result = tmpresult; + } + failed++; + } 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 + ); + failed++; + } + } + + if (!tried_setpass) { + _sasl_log(conn, SASL_LOG_WARN, + "secret not changed for %s: " + "no writable auxprop plugin or setpass callback found", + user); + } else if (result == SASL_CONSTRAINT_VIOLAT) { + /* If not all setpass failed with SASL_CONSTRAINT_VIOLAT - + ignore SASL_CONSTRAINT_VIOLAT */ + if (failed < tried_setpass) { + result = SASL_OK; + } + } + + 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; + + /* Just sanity check that sasl_server_done wasn't called yet */ + if (_sasl_server_active != 0) { + if (s_conn->mech) { + void (*mech_dispose)(void *conn_context, const sasl_utils_t *utils); + + mech_dispose = s_conn->mech->m.plug->mech_dispose; + + if (mech_dispose) { + 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->m.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->appname) { + sasl_FREE(s_conn->appname); + } + + if (s_conn->user_realm) { + sasl_FREE(s_conn->user_realm); + } + + if (s_conn->sparams) { + sasl_FREE(s_conn->sparams); + } + + if (s_conn->mech_list != mechlist->mech_list) { + /* free connection-specific mech_list */ + mechanism_t *m, *prevm; + + m = s_conn->mech_list; /* m point to beginning of the list */ + + while (m) { + prevm = m; + m = m->next; + sasl_FREE(prevm); + } + } + + _sasl_conn_dispose(pconn); +} + +static int init_mechlist(void) +{ + sasl_utils_t *newutils = NULL; + + /* 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; +} + +static int mech_compare(const sasl_server_plug_t *a, + const sasl_server_plug_t *b) +{ + unsigned sec_diff; + unsigned features_diff; + + /* XXX the following is fairly arbitrary, but its independent + of the order in which the plugins are loaded + */ + sec_diff = a->security_flags ^ b->security_flags; + if (sec_diff & a->security_flags & SASL_SEC_NOANONYMOUS) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOANONYMOUS) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOPLAINTEXT) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOPLAINTEXT) return -1; + if (sec_diff & a->security_flags & SASL_SEC_MUTUAL_AUTH) return 1; + if (sec_diff & b->security_flags & SASL_SEC_MUTUAL_AUTH) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NOACTIVE) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NOACTIVE) return -1; + if (sec_diff & a->security_flags & SASL_SEC_NODICTIONARY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_NODICTIONARY) return -1; + if (sec_diff & a->security_flags & SASL_SEC_FORWARD_SECRECY) return 1; + if (sec_diff & b->security_flags & SASL_SEC_FORWARD_SECRECY) return -1; + + features_diff = a->features ^ b->features; + if (features_diff & a->features & SASL_FEAT_CHANNEL_BINDING) return 1; + if (features_diff & b->features & SASL_FEAT_CHANNEL_BINDING) return -1; + + if (a->max_ssf > b->max_ssf) return 1; + if (a->max_ssf < b->max_ssf) return -1; + + if (SASL_GET_HASH_STRENGTH(a->security_flags) > SASL_GET_HASH_STRENGTH(b->security_flags)) return 1; + if (SASL_GET_HASH_STRENGTH(a->security_flags) < SASL_GET_HASH_STRENGTH(b->security_flags)) return -1; + + return 0; +} + +/* + * 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 = NULL; + sasl_server_plug_init_t *entry_point = NULL; + 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) + && (result != SASL_CONTINUE)) { + _sasl_log(NULL, SASL_LOG_DEBUG, + "%s_client_plug_init() failed in sasl_server_add_plugin(): %z\n", + plugname, 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 sasl_server_add_plugin for '%s': %d expected, but %d reported", + plugname, + SASL_SERVER_PLUG_VERSION, + version); + return SASL_BADVERS; + } + + for (lupe=0;lupe < plugcount ;lupe++, pluglist++) + { + mechanism_t *mech, *mp; + + mech = sasl_ALLOC(sizeof(mechanism_t)); + if (! mech) return SASL_NOMEM; + memset (mech, 0, sizeof(mechanism_t)); + + mech->m.plug = pluglist; + if(_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) { + sasl_FREE(mech); + return SASL_NOMEM; + } + mech->m.version = version; + + /* whether this mech actually has any users in it's db */ + mech->m.condition = result; /* SASL_OK, SASL_CONTINUE or SASL_NOUSER */ + + /* mech->m.f = NULL; */ + + /* sort mech_list by relative "strength" */ + mp = mechlist->mech_list; + if (!mp || mech_compare(pluglist, mp->m.plug) >= 0) { + /* add mech to head of list */ + mech->next = mechlist->mech_list; + mechlist->mech_list = mech; + } else { + /* find where to insert mech into list */ + while (mp->next && + mech_compare(pluglist, mp->next->m.plug) <= 0) mp = mp->next; + mech->next = mp->next; + mp->next = mech; + } + mechlist->mech_length++; + } + + return SASL_OK; +} + +int sasl_server_done(void) +{ + int result = SASL_CONTINUE; + + if (_sasl_server_cleanup_hook == NULL && _sasl_client_cleanup_hook == NULL) { + return SASL_NOTINIT; + } + + if (_sasl_server_cleanup_hook) { + result = _sasl_server_cleanup_hook(); + + if (result == SASL_OK) { + _sasl_server_idle_hook = NULL; + _sasl_server_cleanup_hook = NULL; + } else { + return result; + } + } + + if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) { + return result; + } + + sasl_common_done(); + + return SASL_OK; +} + +static int server_done(void) { + mechanism_t *m; + mechanism_t *prevm; + + if(_sasl_server_active == 0) + return SASL_NOTINIT; + else + _sasl_server_active--; + + if(_sasl_server_active) { + /* Don't de-init yet! Our refcount is nonzero. */ + return SASL_CONTINUE; + } + + if (mechlist != NULL) + { + m=mechlist->mech_list; /* m point to beginning of the list */ + + while (m!=NULL) + { + prevm=m; + m=m->next; + + if (prevm->m.plug->mech_free) { + prevm->m.plug->mech_free(prevm->m.plug->glob_context, + mechlist->utils); + } + + sasl_FREE(prevm->m.plugname); + sasl_FREE(prevm); + } + _sasl_free_utils(&mechlist->utils); + sasl_FREE(mechlist); + mechlist = NULL; + } + + /* Free the auxprop plugins */ + _sasl_auxprop_free(); + + global_callbacks.callbacks = NULL; + global_callbacks.appname = NULL; + + sasl_config_done(); + + return SASL_OK; +} + +static int server_idle(sasl_conn_t *conn) +{ + sasl_server_conn_t *s_conn = NULL; + mechanism_t *m; + + if (! mechlist) { + return 0; + } + + if (!conn) + return 1; + s_conn = (sasl_server_conn_t *) conn; + + for (m = s_conn->mech_list; + m != NULL; + m = m->next) { + if (m->m.plug->idle + && m->m.plug->idle(m->m.plug->glob_context, + conn, + s_conn->sparams)) { + return 1; + } + } + + return 0; +} + +static int load_config(const sasl_callback_t *verifyfile_cb) +{ + int result; + const char *path_to_config = NULL; + size_t path_len; + char *config_filename = NULL; + size_t len; + const sasl_callback_t *getconfpath_cb = NULL; + const char * next; + + /* If appname was not provided, behave as if there is no config file + (see also sasl_config_init() */ + if (global_callbacks.appname == NULL) { + return SASL_CONTINUE; + } + + /* get the path to the config file */ + getconfpath_cb = _sasl_find_getconfpath_callback( global_callbacks.callbacks ); + if (getconfpath_cb == NULL) return SASL_BADPARAM; + + /* getconfpath_cb->proc MUST be a sasl_getconfpath_t; if only C had a type + system */ + result = ((sasl_getconfpath_t *)(getconfpath_cb->proc))(getconfpath_cb->context, + (char **) &path_to_config); + if (result != SASL_OK) goto done; + if (path_to_config == NULL) path_to_config = ""; + + next = path_to_config; + + while (next != NULL) { + next = strchr(path_to_config, PATHS_DELIMITER); + + /* length = length of path + '/' + length of appname + ".conf" + 1 + for '\0' */ + + if (next != NULL) { + path_len = next - path_to_config; + next++; /* Skip to the next path */ + } else { + path_len = strlen(path_to_config); + } + + len = path_len + 2 + strlen(global_callbacks.appname) + 5 + 1; + + if (len > PATH_MAX ) { + result = SASL_FAIL; + goto done; + } + + /* construct the filename for the config file */ + config_filename = sasl_ALLOC((unsigned)len); + if (! config_filename) { + result = SASL_NOMEM; + goto done; + } + + snprintf(config_filename, len, "%.*s%c%s.conf", (int)path_len, path_to_config, + HIER_DELIMITER, 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 SASL_CONTINUE if the config file doesn't exist */ + if (result == SASL_OK) { + result = sasl_config_init(config_filename); + + if (result != SASL_CONTINUE) { + /* We are done */ + break; + } + } + + if (config_filename) { + sasl_FREE(config_filename); + config_filename = NULL; + } + + path_to_config = next; + } + + 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->m.version = SASL_SERVER_PLUG_VERSION; + n->m.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->m.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->m.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 and reading of the configuration file) + * 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 } + }; + + /* lock allocation type */ + _sasl_allocation_locked++; + + /* we require the appname (if present) to be short enough to be a path */ + if (appname != NULL && strlen(appname) >= PATH_MAX) + return SASL_BADPARAM; + + if (_sasl_server_active) { + /* We're already active, just increase our refcount */ + /* xxx do something with the callback structure? */ + _sasl_server_active++; + return SASL_OK; + } + + ret = _sasl_common_init(&global_callbacks); + if (ret != SASL_OK) + return ret; + + /* verify that the callbacks look ok */ + ret = verify_server_callbacks(callbacks); + if (ret != SASL_OK) + return ret; + + global_callbacks.callbacks = callbacks; + + /* A shared library calling sasl_server_init will pass NULL as appname. + This should retain the original appname. */ + if (appname != NULL) { + global_callbacks.appname = appname; + } + + /* If we fail now, we have to call server_done */ + _sasl_server_active = 1; + + /* allocate mechlist and set it to empty */ + mechlist = sasl_ALLOC(sizeof(mech_list_t)); + if (mechlist == NULL) { + server_done(); + return SASL_NOMEM; + } + + ret = init_mechlist(); + if (ret != SASL_OK) { + server_done(); + return ret; + } + + vf = _sasl_find_verifyfile_callback(callbacks); + + /* load config file if applicable */ + ret = load_config(vf); + if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) { + server_done(); + 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, (sasl_callback_ft *)&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_cleanup_hook = &server_done; + _sasl_server_idle_hook = &server_idle; + + ret = _sasl_build_mechlist(); + } else { + server_done(); + } + + 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; + unsigned flags = 0; + + 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, (sasl_callback_ft *)&getopt, &context) == SASL_OK) + { + getopt(context, NULL, "auto_transition", &dotrans, NULL); + if (dotrans == NULL) dotrans = "n"; + } + + + if (!strcmp(dotrans, "noplain")) flags |= SASL_SET_NOPLAIN; + + if (flags || *dotrans == '1' || *dotrans == 'y' || + (*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') { + /* ok, it's on! */ + _sasl_log(conn, SASL_LOG_NOTE, + "transitioning user %s to auxprop database", + conn->oparams.authid); + result = sasl_setpass(conn, + conn->oparams.authid, + pass, + passlen, + NULL, 0, SASL_SET_CREATE | flags); + } + + 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; + sasl_getopt_t *getopt; + void *context; + const char *log_level, *auto_trans; + const char *mlist = NULL; + int plus = 0; + + 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 = (unsigned) strlen((*pconn)->service); + + if (global_callbacks.appname && global_callbacks.appname[0] != '\0') { + result = _sasl_strdup (global_callbacks.appname, + &serverconn->appname, + NULL); + if (result != SASL_OK) { + result = SASL_NOMEM; + goto done_error; + } + serverconn->sparams->appname = serverconn->appname; + serverconn->sparams->applen = (unsigned) strlen(serverconn->sparams->appname); + } else { + serverconn->appname = NULL; + serverconn->sparams->appname = NULL; + serverconn->sparams->applen = 0; + } + + serverconn->sparams->serverFQDN = (*pconn)->serverFQDN; + serverconn->sparams->slen = (unsigned) strlen((*pconn)->serverFQDN); + + if (user_realm) { + result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL); + serverconn->sparams->urlen = (unsigned) strlen(user_realm); + serverconn->sparams->user_realm = serverconn->user_realm; + } else { + serverconn->user_realm = NULL; + /* the sparams is already zeroed */ + } + + serverconn->sparams->callbacks = callbacks; + + log_level = auto_trans = NULL; + if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) { + getopt(context, NULL, "log_level", &log_level, NULL); + getopt(context, NULL, "auto_transition", &auto_trans, NULL); + getopt(context, NULL, "mech_list", &mlist, NULL); + } + serverconn->sparams->log_level = log_level ? atoi(log_level) : SASL_LOG_ERR; + + serverconn->sparams->utils = utils; + + if (auto_trans && + (*auto_trans == '1' || *auto_trans == 'y' || *auto_trans == 't' || + (*auto_trans == 'o' && auto_trans[1] == 'n') || + !strcmp(auto_trans, "noplain")) && + sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) { + serverconn->sparams->transition = &_sasl_transition; + } + + /* if we have a mech_list, create ordered list of avail mechs for this conn */ + if (mlist) { + const char *cp; + mechanism_t *mptr, *tail = NULL; + + while (*mlist) { + /* find end of current mech name */ + for (cp = mlist; *cp && !isspace((int) *cp); cp++); + + /* search for mech name in loaded plugins */ + for (mptr = mechlist->mech_list; mptr; mptr = mptr->next) { + const sasl_server_plug_t *plug = mptr->m.plug; + + if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) { + /* found a match */ + break; + } + } + if (mptr) { + mechanism_t *new = sasl_ALLOC(sizeof(mechanism_t)); + if (!new) return SASL_NOMEM; + + memcpy(&new->m, &mptr->m, sizeof(server_sasl_mechanism_t)); + new->next = NULL; + + if (!serverconn->mech_list) { + serverconn->mech_list = new; + tail = serverconn->mech_list; + } + else { + if (tail) + tail->next = new; + tail = new; + } + serverconn->mech_length++; + } + + /* find next mech name */ + mlist = cp; + while (*mlist && isspace((int) *mlist)) mlist++; + } + } + else { + serverconn->mech_list = mechlist->mech_list; + serverconn->mech_length = mechlist->mech_length; + } + + serverconn->sparams->canon_user = &_sasl_canon_user_lookup; + 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 ret; + int myflags; + context_list_t *cur; + context_list_t *mech_context_list_entry = NULL; + void *context = NULL; + sasl_ssf_t minssf = 0; + + if(!conn) return SASL_NOMECH; + + if(! mech || ! mech->m.plug) { + PARAMERROR(conn); + return SASL_NOMECH; + } + + plug = mech->m.plug; + + /* 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 SASL_NOMECH; + } else { + context = cur->context; + mech_context_list_entry = cur; + } + break; + } + } + + 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 SASL_TOOWEAK; /* too weak */ + } + + if (plug->mech_avail + && (ret = plug->mech_avail(plug->glob_context, + s_conn->sparams, + (void **)&context)) != SASL_OK ) { + if (ret == SASL_NOMECH) { + /* Mark this mech as no good for this connection */ + cur = sasl_ALLOC(sizeof(context_list_t)); + if (!cur) { + MEMERROR(conn); + return SASL_NOMECH; + } + cur->context = NULL; + cur->mech = mech; + cur->next = s_conn->mech_contexts; + s_conn->mech_contexts = cur; + } + + /* SASL_NOTDONE might also get us here */ + + /* Error should be set by mech_avail call */ + return SASL_NOMECH; + } else if (context) { + if (mech_context_list_entry != NULL) { + /* Update the context. It shouldn't have changed, but who knows */ + mech_context_list_entry->context = context; + } else { + /* Save this context */ + cur = sasl_ALLOC(sizeof(context_list_t)); + if (!cur) { + MEMERROR(conn); + return SASL_NOMECH; + } + 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 SASL_TOOWEAK; /* too weak */ + } + + /* if there are no users in the secrets database we can't use this + mechanism */ + if (mech->m.condition == SASL_NOUSER) { + sasl_seterror(conn, 0, "no users in secrets db"); + return SASL_NOMECH; + } + + /* Can it meet our features? */ + if ((conn->flags & SASL_NEED_PROXY) && + !(plug->features & SASL_FEAT_ALLOWS_PROXY)) { + return SASL_NOMECH; + } + if ((conn->flags & SASL_NEED_HTTP) && + !(plug->features & SASL_FEAT_SUPPORTS_HTTP)) { + return SASL_NOMECH; + } + + /* 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 &= (myflags ^ plug->security_flags)) != 0) { + sasl_seterror(conn, SASL_NOLOG, + "security flags do not match required"); + return (myflags & SASL_SEC_NOPLAINTEXT) ? SASL_ENCRYPT : SASL_NOMECH; + } + + /* 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 SASL_NOMECH; + } + + return SASL_OK; +} + +/* + * 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, + (sasl_callback_ft *)&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 ? (unsigned) 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; + size_t mech_len; + int plus = 0; + + if (_sasl_server_active==0) return SASL_NOTINIT; + + /* check parameters */ + if(!conn) return SASL_BADPARAM; + + if (!mech || ((clientin == NULL) && (clientinlen > 0))) + PARAMERROR(conn); + + if (serverout) *serverout = NULL; + if (serveroutlen) *serveroutlen = 0; + + /* make sure mech is valid mechanism + if not return appropriate error */ + m = s_conn->mech_list; + mech_len = strlen(mech); + + while (m != NULL) { + if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, mech_len, &plus)) { + 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 ((result = mech_permitted(conn, m)) != SASL_OK) { + goto done; + } + + if (m->m.condition == SASL_CONTINUE) { + sasl_server_plug_init_t *entry_point = NULL; + void *library = NULL; + sasl_server_plug_t *pluglist = NULL; + int version, plugcount; + int l = 0; + + /* need to load this plugin */ + result = _sasl_get_plugin(m->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->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->m.plug->max_ssf) || + (pluglist[l].security_flags != m->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->m.plug); + m->m.plug = &pluglist[l]; + m->m.condition = SASL_OK; + } + + if (result != SASL_OK) { + /* The library will eventually be freed, don't sweat it */ + RETURN(conn, result); + } + } + + if (conn->context) { + s_conn->mech->m.plug->mech_dispose(conn->context, + s_conn->sparams->utils); + conn->context = NULL; + } + + /* 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 = cur->next; + conn->context = cur->context; + sasl_FREE(cur); + break; + } + } + + s_conn->mech = m; + + if (!conn->context) { + /* Note that we don't hand over a new challenge */ + result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.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->m.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->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { + /* Mech wants client first anyway, so we should do that */ + if (serverout) *serverout = ""; + if (serveroutlen) *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->m.plug->mech_dispose(conn->context, + s_conn->sparams->utils); + conn->context = NULL; + } + conn->oparams.doneflag = 0; + } + + RETURN(conn,result); +} + + +/* perform one step of the SASL exchange + * clientinlen & clientin -- client data + * NULL on first step if no optional client step + * serveroutlen & serverout -- 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->m.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; + } + + /* Validate channel bindings */ + switch (conn->oparams.cbindingdisp) { + case SASL_CB_DISP_NONE: + if (SASL_CB_CRITICAL(s_conn->sparams)) { + sasl_seterror(conn, 0, + "server requires channel binding but client provided none"); + ret = SASL_BADBINDING; + } + break; + case SASL_CB_DISP_WANT: + if (SASL_CB_PRESENT(s_conn->sparams)) { + sasl_seterror(conn, 0, + "client incorrectly assumed server had no channel binding"); + ret = SASL_BADAUTH; + } + break; + case SASL_CB_DISP_USED: + if (!SASL_CB_PRESENT(s_conn->sparams)) { + sasl_seterror(conn, 0, + "client provided channel binding but server had none"); + ret = SASL_BADBINDING; + } else if (strcmp(conn->oparams.cbindingname, + s_conn->sparams->cbinding->name) != 0) { + sasl_seterror(conn, 0, + "client channel binding %s does not match server %s", + conn->oparams.cbindingname, s_conn->sparams->cbinding->name); + ret = SASL_BADBINDING; + } + break; + } + + if (ret == SASL_OK && + (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->m.plug->mech_dispose(conn->context, + s_conn->sparams->utils); + conn->context = NULL; + } + conn->oparams.doneflag = 0; + } + + RETURN(conn, ret); +} + +/* returns the length of all the mechanisms + * added up + */ + +static unsigned mech_names_len(mechanism_t *mech_list) +{ + mechanism_t *listptr; + unsigned result = 0; + + for (listptr = mech_list; + listptr; + listptr = listptr->next) + result += (unsigned) strlen(listptr->m.plug->mech_name); + + return result; +} + +/* This returns a list of mechanisms in a NUL-terminated string + * + * The default behavior is to separate 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) +{ + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */ + int lup; + mechanism_t *listptr; + int ret; + size_t 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 (!s_conn->mech_list || s_conn->mech_length <= 0) + INTERROR(conn, SASL_NOMECH); + + resultlen = (prefix ? strlen(prefix) : 0) + + (strlen(mysep) * (s_conn->mech_length - 1) * 2) + + (mech_names_len(s_conn->mech_list) * 2) /* including -PLUS variant */ + + (s_conn->mech_length * (sizeof("-PLUS") - 1)) + + (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 = s_conn->mech_list; + + flag = 0; + /* make list */ + for (lup = 0; lup < s_conn->mech_length; lup++) { + /* currently, we don't use the "user" parameter for anything */ + if (mech_permitted(conn, listptr) == SASL_OK) { + + /* + * If the server would never succeed in the authentication of + * the non-PLUS-variant due to policy reasons, it MUST advertise + * only the PLUS-variant. + */ + if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) && + SASL_CB_PRESENT(s_conn->sparams)) { + if (pcount != NULL) { + (*pcount)++; + } + if (flag) { + strcat(conn->mechlist_buf, mysep); + } else { + flag = 1; + } + strcat(conn->mechlist_buf, listptr->m.plug->mech_name); + strcat(conn->mechlist_buf, "-PLUS"); + } + + /* + * If the server cannot support channel binding, it SHOULD + * advertise only the non-PLUS-variant. Here, supporting channel + * binding means the underlying SASL mechanism supports it and + * the application has set some channel binding data. + */ + if (!SASL_CB_PRESENT(s_conn->sparams) || + !SASL_CB_CRITICAL(s_conn->sparams)) { + if (pcount != NULL) { + (*pcount)++; + } + if (flag) { + strcat(conn->mechlist_buf, mysep); + } else { + flag = 1; + } + strcat(conn->mechlist_buf, listptr->m.plug->mech_name); + } + } + + listptr = listptr->next; + } + + if (suffix) + strcat(conn->mechlist_buf,suffix); + + if (plen!=NULL) + *plen = (unsigned) 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->m.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) +{ + size_t sl = strlen(m); + return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl)); +} + +/* returns OK if it's valid */ +static int _sasl_checkpass(sasl_conn_t *conn, + const char *user, + unsigned userlen, + const char *pass, + unsigned passlen) +{ + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; + int result; + sasl_getopt_t *getopt; + sasl_server_userdb_checkpass_t *checkpass_cb; + void *context; + const char *mlist = NULL, *mech = NULL; + struct sasl_verify_password_s *v; + const char *service = conn->service; + + if (!userlen) userlen = (unsigned) strlen(user); + if (!passlen) passlen = (unsigned) strlen(pass); + + /* call userdb callback function, if available */ + result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS, + (sasl_callback_ft *)&checkpass_cb, &context); + if(result == SASL_OK && checkpass_cb) { + result = checkpass_cb(conn, context, user, pass, passlen, + 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, (sasl_callback_ft *)&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++; + } + else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) { + s_conn->sparams->transition(conn, pass, passlen); + } + } + + if (result == SASL_NOMECH) { + /* no mechanism available ?!? */ + _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier(s) %s", mlist); + } + + 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, + 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); + + /* canonicalize the username */ + result = _sasl_canon_user(conn, user, userlen, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + &(conn->oparams)); + if(result != SASL_OK) RETURN(conn, result); + user = conn->oparams.user; + + /* Check the password and lookup additional properties */ + result = _sasl_checkpass(conn, user, userlen, pass, passlen); + + /* Do authorization */ + if(result == SASL_OK) { + result = do_authorization((sasl_server_conn_t *)conn); + } + + 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 + * SASL_UNAVAIL -- remote authentication server unavailable, try again later + */ +int sasl_user_exists(sasl_conn_t *conn, + const char *service, + const char *user_realm, + const char *user) +{ + int result=SASL_NOMECH; + const char *mlist = NULL, *mech = NULL; + void *context; + sasl_getopt_t *getopt; + struct sasl_verify_password_s *v; + + /* 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, (sasl_callback_ft *)&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, "<user> <digest>" (RFC 1939) + * resplen -- length of response, 0 = strlen(response) + * returns + * SASL_OK -- success + * SASL_BADAUTH -- authentication failed + * SASL_BADPARAM -- missing challenge + * SASL_BADPROT -- protocol error (e.g., response in wrong format) + * SASL_NOVERIFY -- user found, but no verifier + * SASL_NOMECH -- mechanism not supported + * SASL_NOUSER -- user not found + */ +int sasl_checkapop(sasl_conn_t *conn, +#ifdef DO_SASL_CHECKAPOP + const char *challenge, + unsigned challen __attribute__((unused)), + const char *response, + unsigned resplen __attribute__((unused))) +#else + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + const char *response __attribute__((unused)), + unsigned resplen __attribute__((unused))) +#endif +{ +#ifdef DO_SASL_CHECKAPOP + sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; + char *user, *user_end; + const char *password_request[] = { SASL_AUX_PASSWORD, NULL }; + size_t user_len; + int result; + + 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 "<user> <digest>", where + * <digest> is a 16-octet value which is sent in hexadecimal + * format, using lower-case ASCII characters. + */ + user_end = strrchr(response, ' '); + if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) + { + 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); + } + + /* erase the plaintext password */ + s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx, + password_request[0]); + + /* canonicalize the username and lookup any associated properties */ + result = _sasl_canon_user_lookup (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); + + /* Do authorization */ + if(result == SASL_OK) { + result = do_authorization((sasl_server_conn_t *)conn); + } else { + /* If verification failed, we don't want to encourage getprop to work */ + 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 */ +} + +/* It would be nice if we can show other information like Author, Company, Year, plugin version */ +static void +_sasl_print_mechanism ( + server_sasl_mechanism_t *m, + sasl_info_callback_stage_t stage, + void *rock __attribute__((unused)) +) +{ + char delimiter; + + if (stage == SASL_INFO_LIST_START) { + printf ("List of server plugins follows\n"); + return; + } else if (stage == SASL_INFO_LIST_END) { + return; + } + + /* Process the mechanism */ + printf ("Plugin \"%s\" ", m->plugname); + + switch (m->condition) { + case SASL_OK: + printf ("[loaded]"); + break; + + case SASL_CONTINUE: + printf ("[delayed]"); + break; + + case SASL_NOUSER: + printf ("[no users]"); + break; + + default: + printf ("[unknown]"); + break; + } + + printf (", \tAPI version: %d\n", m->version); + + if (m->plug != NULL) { + printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n", + m->plug->mech_name, + m->plug->max_ssf, + (m->plug->setpass != NULL) ? "yes" : "no" + ); + + + printf ("\tsecurity flags:"); + + delimiter = ' '; + if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) { + printf ("%cNO_ANONYMOUS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) { + printf ("%cNO_PLAINTEXT", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NOACTIVE) { + printf ("%cNO_ACTIVE", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_NODICTIONARY) { + printf ("%cNO_DICTIONARY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) { + printf ("%cFORWARD_SECRECY", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) { + printf ("%cPASS_CREDENTIALS", delimiter); + delimiter = '|'; + } + + if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) { + printf ("%cMUTUAL_AUTH", delimiter); + delimiter = '|'; + } + + + + printf ("\n\tfeatures:"); + + delimiter = ' '; + if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { + printf ("%cWANT_CLIENT_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SERVER_FIRST) { + printf ("%cSERVER_FIRST", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) { + printf ("%cPROXY_AUTHENTICATION", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_DONTUSE_USERPASSWD) { + printf ("%cDONTUSE_USERPASSWD", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) { + printf ("%cNEED_SERVER_FQDN", delimiter); + delimiter = '|'; + } + + /* Is this one used? */ + if (m->plug->features & SASL_FEAT_SERVICE) { + printf ("%cSERVICE", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_GETSECRET) { + printf ("%cNEED_GETSECRET", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_GSS_FRAMING) { + printf ("%cGSS_FRAMING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) { + printf ("%cCHANNEL_BINDING", delimiter); + delimiter = '|'; + } + + if (m->plug->features & SASL_FEAT_SUPPORTS_HTTP) { + printf ("%cSUPPORTS_HTTP", delimiter); + delimiter = '|'; + } + } + + if (m->f) { + printf ("\n\twill be loaded from \"%s\"", m->f); + } + + printf ("\n"); +} + +/* Dump information about available server plugins (separate functions should be + used for canon and auxprop plugins */ +int sasl_server_plugin_info ( + const char *c_mech_list, /* space separated mechanism list or NULL for ALL */ + sasl_server_info_callback_t *info_cb, + void *info_cb_rock +) +{ + mechanism_t *m; + server_sasl_mechanism_t plug_data; + char * cur_mech; + char *mech_list = NULL; + char * p; + + if (info_cb == NULL) { + info_cb = _sasl_print_mechanism; + } + + if (mechlist != NULL) { + info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock); + + if (c_mech_list == NULL) { + m = mechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + + m = m->next; + } + } else { + mech_list = strdup(c_mech_list); + + cur_mech = mech_list; + + while (cur_mech != NULL) { + p = strchr (cur_mech, ' '); + if (p != NULL) { + *p = '\0'; + p++; + } + + m = mechlist->mech_list; /* m point to beginning of the list */ + + while (m != NULL) { + if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) { + memcpy (&plug_data, &m->m, sizeof(plug_data)); + + info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock); + } + + m = m->next; + } + + cur_mech = p; + } + + free (mech_list); + } + + info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock); + + return (SASL_OK); + } + + return (SASL_NOTINIT); +} diff --git a/contrib/libs/sasl/lib/seterror.c b/contrib/libs/sasl/lib/seterror.c new file mode 100644 index 0000000000..05eec9a8e2 --- /dev/null +++ b/contrib/libs/sasl/lib/seterror.c @@ -0,0 +1,263 @@ +/* seterror.c - sasl_seterror split out because glue libraries + * can't pass varargs lists + * Rob Siemborski + * Tim Martin + * split from common.c by Rolf Braun + */ + +/* + * 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 + * innovation@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#ifdef HAVE_SYSLOG +#include <syslog.h> +#endif +#include <stdarg.h> +#include <ctype.h> + +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> +#include "saslint.h" + +#ifdef WIN32 +/* need to handle the fact that errno has been defined as a function + in a dll, not an extern int */ +# ifdef errno +# undef errno +# endif /* errno */ +#endif /* WIN32 */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/* this is apparently no longer a user function */ +static int _sasl_seterror_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; +} + +/* set the error string which will be returned by sasl_errdetail() using + * syslog()-style formatting (e.g. printf-style with %m as the string form + * of an errno error) + * + * primarily for use by server callbacks such as the sasl_authorize_t + * callback and internally to plug-ins + * + * This will also trigger a call to the SASL logging callback (if any) + * with a level of SASL_LOG_FAIL unless the SASL_NOLOG flag is set. + * + * Messages should be sensitive to the current language setting. If there + * is no SASL_CB_LANGUAGE callback messages MUST be US-ASCII otherwise UTF-8 + * is used and use of RFC 2482 for mixed-language text is encouraged. + * + * if conn is NULL, function does nothing + */ +void sasl_seterror(sasl_conn_t *conn, + unsigned flags, + const char *fmt, ...) +{ + size_t outlen=0; /* current length of output buffer */ + size_t pos = 0; /* current position in format string */ + size_t formatlen; + int result; + sasl_log_t *log_cb = NULL; + void *log_ctx; + int ival; + char *cval; + va_list ap; /* varargs thing */ + char **error_buf; + size_t *error_buf_len; + + if(!conn) { +#ifndef SASL_OSX_CFMGLUE + if(!(flags & SASL_NOLOG)) { + /* See if we have a logging callback... */ + result = _sasl_getcallback(NULL, SASL_CB_LOG, (sasl_callback_ft *)&log_cb, &log_ctx); + if (result == SASL_OK && ! log_cb) + result = SASL_FAIL; + if (result != SASL_OK) + return; + + log_cb(log_ctx, SASL_LOG_FAIL, + "No sasl_conn_t passed to sasl_seterror"); + } +#endif /* SASL_OSX_CFMGLUE */ + return; + } else if(!fmt) return; + +/* we need to use a back end function to get the buffer because the + cfm glue can't be rooting around in the internal structs */ + _sasl_get_errorbuf(conn, &error_buf, &error_buf_len); + + formatlen = strlen(fmt); + + va_start(ap, fmt); /* start varargs */ + + while(pos<formatlen) + { + if (fmt[pos]!='%') /* regular character */ + { + result = _buf_alloc(error_buf, error_buf_len, outlen+1); + if (result != SASL_OK) + goto done; + (*error_buf)[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(error_buf, error_buf_len, + &outlen, cval); + + if (result != SASL_OK) /* add the string */ + goto done; + + done=1; + break; + + case '%': /* double % output the '%' character */ + result = _buf_alloc(error_buf, error_buf_len, outlen+1); + if (result != SASL_OK) + goto done; + (*error_buf)[outlen]='%'; + outlen++; + done=1; + break; + + case 'm': /* insert the errno string */ + result = _sasl_add_string(error_buf, error_buf_len, + &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(error_buf, error_buf_len, &outlen, + (char *)sasl_errstring(_sasl_seterror_usererr( + 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(error_buf, error_buf_len, + &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(error_buf, error_buf_len, + &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; + } + + } + } + + (*error_buf)[outlen]='\0'; /* put 0 at end */ + +#ifndef SASL_OSX_CFMGLUE + if(!(flags & SASL_NOLOG)) { + /* 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; + + result = log_cb(log_ctx, SASL_LOG_FAIL, conn->error_buf); + } +#endif /* SASL_OSX_CFMGLUE */ +done: + va_end(ap); +} diff --git a/contrib/libs/sasl/lib/staticopen.h b/contrib/libs/sasl/lib/staticopen.h new file mode 100644 index 0000000000..d1983163d9 --- /dev/null +++ b/contrib/libs/sasl/lib/staticopen.h @@ -0,0 +1,188 @@ +/* staticopen.h + * Rob Siemborski + * Howard Chu + */ +/* + * 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 + * innovation@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. + */ + +typedef enum { + UNKNOWN = 0, SERVER = 1, CLIENT = 2, AUXPROP = 3, CANONUSER = 4 +} _sasl_plug_type; + +typedef struct { + _sasl_plug_type type; + char *name; + sasl_client_plug_init_t *plug; +} _sasl_plug_rec; + +/* For static linking */ +#define SPECIFIC_CLIENT_PLUG_INIT_PROTO( x ) \ +sasl_client_plug_init_t x##_client_plug_init + +#define SPECIFIC_SERVER_PLUG_INIT_PROTO( x ) \ +sasl_server_plug_init_t x##_server_plug_init + +#define SPECIFIC_AUXPROP_PLUG_INIT_PROTO( x ) \ +sasl_auxprop_init_t x##_auxprop_plug_init + +#define SPECIFIC_CANONUSER_PLUG_INIT_PROTO( x ) \ +sasl_canonuser_init_t x##_canonuser_plug_init + +/* Static Compillation Foo */ +#define SPECIFIC_CLIENT_PLUG_INIT( x, n )\ + { CLIENT, n, x##_client_plug_init } +#define SPECIFIC_SERVER_PLUG_INIT( x, n )\ + { SERVER, n, (sasl_client_plug_init_t *)x##_server_plug_init } +#define SPECIFIC_AUXPROP_PLUG_INIT( x, n )\ + { AUXPROP, n, (sasl_client_plug_init_t *)x##_auxprop_plug_init } +#define SPECIFIC_CANONUSER_PLUG_INIT( x, n )\ + { CANONUSER, n, (sasl_client_plug_init_t *)x##_canonuser_plug_init } + +#ifdef STATIC_ANONYMOUS +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( anonymous ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( anonymous ); +#endif +#ifdef STATIC_CRAMMD5 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( crammd5 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( crammd5 ); +#endif +#ifdef STATIC_DIGESTMD5 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( digestmd5 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( digestmd5 ); +#endif +#ifdef STATIC_SCRAM +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( scram ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( scram ); +#endif +#ifdef STATIC_GSSAPIV2 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( gssapiv2 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( gssapiv2 ); +#endif +#ifdef STATIC_KERBEROS4 +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( kerberos4 ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( kerberos4 ); +#endif +#ifdef STATIC_LOGIN +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( login ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( login ); +#endif +#ifdef STATIC_NTLM +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( ntlm ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( ntlm ); +#endif +#ifdef STATIC_OTP +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( otp ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( otp ); +#endif +#ifdef STATIC_PLAIN +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( plain ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( plain ); +#endif +#ifdef STATIC_SRP +extern SPECIFIC_SERVER_PLUG_INIT_PROTO( srp ); +extern SPECIFIC_CLIENT_PLUG_INIT_PROTO( srp ); +#endif +#ifdef STATIC_SASLDB +extern SPECIFIC_AUXPROP_PLUG_INIT_PROTO( sasldb ); +#endif +#ifdef STATIC_SQL +extern SPECIFIC_AUXPROP_PLUG_INIT_PROTO( sql ); +#endif +#ifdef STATIC_LDAPDB +extern SPECIFIC_AUXPROP_PLUG_INIT_PROTO( ldapdb ); +#endif + +_sasl_plug_rec _sasl_static_plugins[] = { +#ifdef STATIC_ANONYMOUS + SPECIFIC_SERVER_PLUG_INIT( anonymous, "ANONYMOUS" ), + SPECIFIC_CLIENT_PLUG_INIT( anonymous, "ANONYMOUS" ), +#endif +#ifdef STATIC_CRAMMD5 + SPECIFIC_SERVER_PLUG_INIT( crammd5, "CRAM-MD5" ), + SPECIFIC_CLIENT_PLUG_INIT( crammd5, "CRAM-MD5" ), +#endif +#ifdef STATIC_DIGESTMD5 + SPECIFIC_SERVER_PLUG_INIT( digestmd5, "DIGEST-MD5" ), + SPECIFIC_CLIENT_PLUG_INIT( digestmd5, "DIGEST-MD5" ), +#endif +#ifdef STATIC_GSSAPIV2 + SPECIFIC_SERVER_PLUG_INIT( gssapiv2, "GSSAPI" ), + SPECIFIC_CLIENT_PLUG_INIT( gssapiv2, "GSSAPI" ), +#endif +#ifdef STATIC_KERBEROS4 + SPECIFIC_SERVER_PLUG_INIT( kerberos4, "KERBEROS_V4" ), + SPECIFIC_CLIENT_PLUG_INIT( kerberos4, "KERBEROS_V4" ), +#endif +#ifdef STATIC_LOGIN + SPECIFIC_SERVER_PLUG_INIT( login, "LOGIN" ), + SPECIFIC_CLIENT_PLUG_INIT( login, "LOGIN" ), +#endif +#ifdef STATIC_NTLM + SPECIFIC_SERVER_PLUG_INIT( ntlm, "NTLM" ), + SPECIFIC_CLIENT_PLUG_INIT( ntlm, "NTLM" ), +#endif +#ifdef STATIC_OTP + SPECIFIC_SERVER_PLUG_INIT( otp, "OTP" ), + SPECIFIC_CLIENT_PLUG_INIT( otp, "OTP" ), +#endif +#ifdef STATIC_PLAIN + SPECIFIC_SERVER_PLUG_INIT( plain, "PLAIN" ), + SPECIFIC_CLIENT_PLUG_INIT( plain, "PLAIN" ), +#endif +#ifdef STATIC_SCRAM + SPECIFIC_SERVER_PLUG_INIT( scram, "SCRAM" ), + SPECIFIC_CLIENT_PLUG_INIT( scram, "SCRAM" ), +#endif +#ifdef STATIC_SRP + SPECIFIC_SERVER_PLUG_INIT( srp, "SRP" ), + SPECIFIC_CLIENT_PLUG_INIT( srp, "SRP" ), +#endif +#ifdef STATIC_SASLDB + SPECIFIC_AUXPROP_PLUG_INIT( sasldb, "SASLDB" ), +#endif +#ifdef STATIC_SQL + SPECIFIC_AUXPROP_PLUG_INIT( sql, "SQL" ), +#endif +#ifdef STATIC_LDAPDB + SPECIFIC_AUXPROP_PLUG_INIT( ldapdb, "LDAPDB" ), +#endif + { UNKNOWN, NULL, NULL } +}; |