/* auxprop.c - auxilliary property support * Rob Siemborski * $Id: auxprop.c,v 1.2 2002/05/22 17:56:55 snsimon Exp $ */ /* * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "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; size_t total_size = 0, 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 += 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; iused_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 atleast on 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 = 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)); } /* Now do the copy, or referencing rather */ for(i=0;iused_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) { unsigned i; if(requests) { memset(ctx->values, 0, sizeof(struct propval) * ctx->allocated_values); ctx->prev_val = NULL; ctx->used_values = 0; } else { for(i=0; iused_values; i++) { ctx->values[i].values = NULL; ctx->values[i].nvalues = 0; ctx->values[i].valsize = 0; } } ctx->mem_cur = ctx->mem_base; 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 = strlen(sep); needed = seplen * (ctx->used_values - 1); for(val = ctx->values; val->name; val++) { needed += 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) 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 += (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) 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 += (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); if(result != SASL_OK) { _sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %i\n",result); 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; } /* Do the callbacks for auxprop lookups */ 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 *pluginname = NULL; auxprop_plug_list_t *ptr; /* xxx support more than one at the same time? */ if(_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) { ret = getopt(context, NULL, "auxprop_plugin", &pluginname, NULL); if(ret != SASL_OK) pluginname = NULL; } for(ptr = auxprop_head; ptr; ptr = ptr->next) { /* Skip non-matching plugins */ if(pluginname && (!ptr->plug->name || strcasecmp(ptr->plug->name, pluginname))) continue; found=1; ptr->plug->auxprop_lookup(ptr->plug->glob_context, sparams, flags, user, ulen); } if(!found) _sasl_log(NULL, SASL_LOG_ERR, "could not find auxprop plugin, was searching for %s", pluginname ? pluginname : "[any]"); }