#include <config.h>
#include <sasl.h>
#include <prop.h>
#include <ctype.h>
#include "saslint.h"
struct proppool
{
struct proppool *next;
size_t size;
size_t unused;
char data[1];
};
struct propctx {
struct propval *values;
struct propval *prev_val;
unsigned used_values, allocated_values;
char *data_end;
char **list_end;
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;
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;
}
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;
}
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;
}
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;
pool = src_ctx->mem_base;
while(pool) {
total_size += (unsigned) pool->size;
pool = pool->next;
}
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);
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;
}
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;
}
int prop_request(struct propctx *ctx, const char **names)
{
unsigned i, new_values, total_values;
if(!ctx || !names) return SASL_BADPARAM;
for(new_values=0; names[new_values]; new_values++);
if(!new_values) return SASL_OK;
total_values = new_values + ctx->used_values + 1;
if(total_values > ctx->allocated_values) {
unsigned max_in_pool;
max_in_pool = (unsigned) (ctx->mem_base->size / sizeof(struct propval));
if(total_values <= max_in_pool) {
ctx->allocated_values = total_values;
ctx->mem_base->unused =
ctx->mem_base->size - (sizeof(struct propval)
* ctx->allocated_values);
} else {
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;
}
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;
}
memset(&(ctx->values[ctx->used_values]), 0,
sizeof(struct propval) * (ctx->allocated_values - ctx->used_values));
ctx->list_end = (char **)(ctx->values + total_values);
}
for(i=0;i<new_values;i++) {
unsigned j, flag;
flag = 0;
for(j=0;j<ctx->used_values;j++) {
if(!strcmp(ctx->values[j].name, names[i])) {
flag = 1;
break;
}
}
if(flag) continue;
ctx->values[ctx->used_values++].name = names[i];
}
prop_clear(ctx, 0);
return SASL_OK;
}
const struct propval *prop_get(struct propctx *ctx)
{
if(!ctx) return NULL;
return ctx->values;
}
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;
}
}
memset(cur, 0, sizeof(struct propval));
next:
cur++;
}
return found_names;
}
void prop_clear(struct propctx *ctx, int requests)
{
struct proppool *new_pool, *tmp;
unsigned i;
new_pool = alloc_proppool(ctx->mem_base->size +
(ctx->used_values+1) * sizeof(struct propval));
if(requests) {
ctx->used_values = 0;
} else {
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);
}
ctx->allocated_values = ctx->used_values+1;
new_pool->unused =
new_pool->size - (ctx->allocated_values * sizeof(struct propval));
ctx->values = (struct propval *)new_pool->data;
ctx->prev_val = NULL;
ctx->mem_base = ctx->mem_cur = new_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;
}
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;
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;
}
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 < 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);
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;
}
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;
}
}
if(!ctx->prev_val) return SASL_BADPARAM;
}
cur = ctx->prev_val;
if(name) {
unsigned nvalues = 1;
const char **old_values = NULL;
char **tmp, **tmp2;
size_t size;
if(cur->values) {
if(!value) {
return SASL_OK;
}
old_values = cur->values;
tmp = (char **)cur->values;
while(*tmp) {
nvalues++;
tmp++;
}
}
if(value) {
nvalues++;
}
size = nvalues * sizeof(char*);
if(size > ctx->mem_cur->unused) {
size_t needed;
for(needed = ctx->mem_cur->size * 2; needed < size; needed *= 2);
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;
}
ctx->mem_cur->unused -= size;
cur->values = (const char **)ctx->list_end;
cur->values[nvalues - 1] = NULL;
ctx->list_end = (char **)(cur->values + nvalues);
if(!value)
return SASL_OK;
tmp2 = (char **)cur->values;
if(old_values) {
tmp = (char **)old_values;
while(*tmp) {
*tmp2 = *tmp;
tmp++; tmp2++;
}
}
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;
}
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;
}
ctx->data_end -= size;
ctx->mem_cur->unused -= size;
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 {
char **tmp;
size_t size;
if(!value) return SASL_OK;
size = sizeof(char*);
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)) {
return prop_set(ctx, cur->name, value, vallen);
}
ctx->mem_cur->unused -= size;
ctx->list_end++;
*(ctx->list_end - 1) = NULL;
tmp = (ctx->list_end - 2);
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;
}
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;
}
ctx->data_end -= size;
ctx->mem_cur->unused -= size;
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;
}
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(!values) return SASL_OK;
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;
}
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);
}
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;
}
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);
if(result != SASL_OK) {
_sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %i\n",result);
return result;
}
if(!plug->auxprop_lookup) return SASL_BADPROT;
new_item = sasl_ALLOC(sizeof(auxprop_plug_list_t));
if(!new_item) return SASL_NOMEM;
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;
}
void _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;
if(_sasl_getcallback(sparams->utils->conn,
SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL);
if(ret != SASL_OK) plist = NULL;
}
if(!plist) {
for(ptr = auxprop_head; ptr; ptr = ptr->next) {
found=1;
ptr->plug->auxprop_lookup(ptr->plug->glob_context,
sparams, flags, user, ulen);
}
} else {
char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL;
if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return;
thisplugin = freeptr = pluginlist;
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) {
if(!ptr->plug->name
|| strcasecmp(ptr->plug->name, thisplugin))
continue;
found=1;
ptr->plug->auxprop_lookup(ptr->plug->glob_context,
sparams, flags, user, ulen);
}
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]");
}
int sasl_auxprop_store(sasl_conn_t *conn,
struct propctx *ctx, const char *user)
{
sasl_getopt_t *getopt;
int ret, found = 0;
void *context;
const char *plist = NULL;
auxprop_plug_list_t *ptr;
sasl_server_params_t *sparams = NULL;
unsigned userlen = 0;
if (ctx) {
if (!conn || !user)
return SASL_BADPARAM;
sparams = ((sasl_server_conn_t *) conn)->sparams;
userlen = (unsigned) strlen(user);
}
if(_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL);
if(ret != SASL_OK) plist = NULL;
}
ret = SASL_OK;
if(!plist) {
for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) {
found=1;
if (ptr->plug->auxprop_store)
ret = ptr->plug->auxprop_store(ptr->plug->glob_context,
sparams, ctx, user, userlen);
}
} else {
char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL;
if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_FAIL;
thisplugin = freeptr = pluginlist;
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) {
if((!ptr->plug->name
|| strcasecmp(ptr->plug->name, thisplugin)))
continue;
found=1;
if (ptr->plug->auxprop_store)
ret = ptr->plug->auxprop_store(ptr->plug->glob_context,
sparams, ctx, user, userlen);
}
if(last) break;
thisplugin = p+1;
}
sasl_FREE(freeptr);
}
if(!found) {
_sasl_log(NULL, SASL_LOG_ERR,
"could not find auxprop plugin, was searching for %s",
plist ? plist : "[all]");
return SASL_FAIL;
}
return ret;
}