xref: /illumos-gate/usr/src/lib/libsasl/lib/auxprop.c (revision 1da57d55)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /* auxprop.c - auxilliary property support
7  * Rob Siemborski
8  * $Id: auxprop.c,v 1.10 2003/03/19 18:25:27 rjs3 Exp $
9  */
10 /*
11  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  *
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  *
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in
22  *    the documentation and/or other materials provided with the
23  *    distribution.
24  *
25  * 3. The name "Carnegie Mellon University" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For permission or any other legal
28  *    details, please contact
29  *      Office of Technology Transfer
30  *      Carnegie Mellon University
31  *      5000 Forbes Avenue
32  *      Pittsburgh, PA  15213-3890
33  *      (412) 268-4387, fax: (412) 268-7395
34  *      tech-transfer@andrew.cmu.edu
35  *
36  * 4. Redistributions of any form whatsoever must retain the following
37  *    acknowledgment:
38  *    "This product includes software developed by Computing Services
39  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
40  *
41  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
42  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
43  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
44  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
45  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
46  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
47  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48  */
49 
50 #include <config.h>
51 #include <sasl.h>
52 #include <prop.h>
53 #include <ctype.h>
54 #include "saslint.h"
55 
56 struct proppool
57 {
58     struct proppool *next;
59 
60     size_t size;          /* Size of Block */
61     size_t unused;        /* Space unused in this pool between end
62 			   * of char** area and beginning of char* area */
63 
64     char data[1];         /* Variable Sized */
65 };
66 
67 struct propctx  {
68     struct propval *values;
69     struct propval *prev_val; /* Previous value used by set/setvalues */
70 
71     unsigned used_values, allocated_values;
72 
73     char *data_end; /* Bottom of string area in current pool */
74     char **list_end; /* Top of list area in current pool */
75 
76     struct proppool *mem_base;
77     struct proppool *mem_cur;
78 };
79 
80 typedef struct auxprop_plug_list
81 {
82     struct auxprop_plug_list *next;
83     const sasl_auxprop_plug_t *plug;
84 #ifdef _SUN_SDK_
85     char *plugname;
86 #endif /* _SUN_SDK_ */
87 } auxprop_plug_list_t;
88 
89 #ifndef _SUN_SDK_
90 static auxprop_plug_list_t *auxprop_head = NULL;
91 #endif /* !_SUN_SDK_ */
92 
alloc_proppool(size_t size)93 static struct proppool *alloc_proppool(size_t size)
94 {
95     struct proppool *ret;
96     /* minus 1 for the one that is already a part of the array
97      * in the struct */
98     size_t total_size = sizeof(struct proppool) + size - 1;
99 #ifdef _SUN_SDK_
100     ret = sasl_sun_ALLOC(total_size);
101 #else
102     ret = sasl_ALLOC(total_size);
103 #endif /* _SUN_SDK_*/
104     if(!ret) return NULL;
105 
106     memset(ret, 0, total_size);
107 
108     ret->size = ret->unused = size;
109 
110     return ret;
111 }
112 
113 /* Resize a proppool.  Invalidates the unused value for this pool */
resize_proppool(struct proppool * pool,size_t size)114 static struct proppool *resize_proppool(struct proppool *pool, size_t size)
115 {
116     struct proppool *ret;
117 
118     if(pool->size >= size) return pool;
119 #ifdef _SUN_SDK_
120     ret = sasl_sun_REALLOC(pool, sizeof(struct proppool) + size);
121 #else
122     ret = sasl_REALLOC(pool, sizeof(struct proppool) + size);
123 #endif /* _SUN_SDK_*/
124     if(!ret) return NULL;
125 
126     ret->size = size;
127 
128     return ret;
129 }
130 
prop_init(struct propctx * ctx,unsigned estimate)131 static int prop_init(struct propctx *ctx, unsigned estimate)
132 {
133     const unsigned VALUES_SIZE = PROP_DEFAULT * sizeof(struct propval);
134 
135     ctx->mem_base = alloc_proppool(VALUES_SIZE + estimate);
136     if(!ctx->mem_base) return SASL_NOMEM;
137 
138     ctx->mem_cur = ctx->mem_base;
139 
140     ctx->values = (struct propval *)ctx->mem_base->data;
141     ctx->mem_base->unused = ctx->mem_base->size - VALUES_SIZE;
142     ctx->allocated_values = PROP_DEFAULT;
143     ctx->used_values = 0;
144 
145     ctx->data_end = ctx->mem_base->data + ctx->mem_base->size;
146     ctx->list_end = (char **)(ctx->mem_base->data + VALUES_SIZE);
147 
148     ctx->prev_val = NULL;
149 
150     return SASL_OK;
151 }
152 
153 /* create a property context
154  *  estimate -- an estimate of the storage needed for requests & responses
155  *              0 will use module default
156  * returns NULL on error
157  */
prop_new(unsigned estimate)158 struct propctx *prop_new(unsigned estimate)
159 {
160     struct propctx *new_ctx;
161 
162     if(!estimate) estimate = PROP_DEFAULT * 255;
163 
164 #ifdef _SUN_SDK_
165     new_ctx = sasl_sun_ALLOC(sizeof(struct propctx));
166 #else
167     new_ctx = sasl_ALLOC(sizeof(struct propctx));
168 #endif /* _SUN_SDK_*/
169     if(!new_ctx) return NULL;
170 
171     if(prop_init(new_ctx, estimate) != SASL_OK) {
172 	prop_dispose(&new_ctx);
173     }
174 
175     return new_ctx;
176 }
177 
178 /* create new propctx which duplicates the contents of an existing propctx
179  * returns -1 on error
180  */
prop_dup(struct propctx * src_ctx,struct propctx ** dst_ctx)181 int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx)
182 {
183     struct proppool *pool;
184     struct propctx *retval = NULL;
185     unsigned i;
186     int result;
187     size_t total_size = 0, values_size;
188 
189     if(!src_ctx || !dst_ctx) return SASL_BADPARAM;
190 
191     /* What is the total allocated size of src_ctx? */
192     pool = src_ctx->mem_base;
193     while(pool) {
194 	total_size += pool->size;
195 	pool = pool->next;
196     }
197 
198     /* allocate the new context */
199     retval = prop_new(total_size);
200     if(!retval) return SASL_NOMEM;
201 
202     retval->used_values = src_ctx->used_values;
203     retval->allocated_values = src_ctx->used_values + 1;
204 
205     values_size = (retval->allocated_values * sizeof(struct propval));
206 
207     retval->mem_base->unused = retval->mem_base->size - values_size;
208 
209     retval->list_end = (char **)(retval->mem_base->data + values_size);
210     /* data_end should still be OK */
211 
212     /* Now dup the values */
213     for(i=0; i<src_ctx->used_values; i++) {
214 	retval->values[i].name = src_ctx->values[i].name;
215 	result = prop_setvals(retval, retval->values[i].name,
216 			      src_ctx->values[i].values);
217 	if(result != SASL_OK)
218 	    goto fail;
219     }
220 
221     retval->prev_val = src_ctx->prev_val;
222 
223     *dst_ctx = retval;
224     return SASL_OK;
225 
226     fail:
227     if(retval) prop_dispose(&retval);
228     return result;
229 }
230 
231 /*
232  * dispose of property context
233  *  ctx      -- is disposed and set to NULL; noop if ctx or *ctx is NULL
234  */
prop_dispose(struct propctx ** ctx)235 void prop_dispose(struct propctx **ctx)
236 {
237     struct proppool *tmp;
238 
239     if(!ctx || !*ctx) return;
240 
241     while((*ctx)->mem_base) {
242 	tmp = (*ctx)->mem_base;
243 	(*ctx)->mem_base = tmp->next;
244 #ifdef _SUN_SDK_
245         sasl_sun_FREE(tmp);
246 #else
247 	sasl_FREE(tmp);
248 #endif /* _SUN_SDK_*/
249     }
250 
251 #ifdef _SUN_SDK_
252     sasl_sun_FREE(*ctx);
253 #else
254     sasl_FREE(*ctx);
255 #endif /* _SUN_SDK_*/
256     *ctx = NULL;
257 
258     return;
259 }
260 
261 /* Add property names to request
262  *  ctx       -- context from prop_new()
263  *  names     -- list of property names; must persist until context freed
264  *               or requests cleared
265  *
266  * NOTE: may clear values from context as side-effect
267  * returns -1 on error
268  */
prop_request(struct propctx * ctx,const char ** names)269 int prop_request(struct propctx *ctx, const char **names)
270 {
271     unsigned i, new_values, total_values;
272 
273     if(!ctx || !names) return SASL_BADPARAM;
274 
275     /* Count how many we need to add */
276     for(new_values=0; names[new_values]; new_values++);
277 
278     /* Do we need to add ANY? */
279     if(!new_values) return SASL_OK;
280 
281     /* We always want atleast on extra to mark the end of the array */
282     total_values = new_values + ctx->used_values + 1;
283 
284     /* Do we need to increase the size of our propval table? */
285     if(total_values > ctx->allocated_values) {
286 	unsigned max_in_pool;
287 
288 	/* Do we need a larger base pool? */
289 	max_in_pool = ctx->mem_base->size / sizeof(struct propval);
290 
291 	if(total_values <= max_in_pool) {
292 	    /* Don't increase the size of the base pool, just use what
293 	       we need */
294 	    ctx->allocated_values = total_values;
295 	    ctx->mem_base->unused =
296 		ctx->mem_base->size - (sizeof(struct propval)
297 				       * ctx->allocated_values);
298       	} else {
299 	    /* We need to allocate more! */
300 	    unsigned new_alloc_length;
301 	    size_t new_size;
302 
303 	    new_alloc_length = 2 * ctx->allocated_values;
304 	    while(total_values > new_alloc_length) {
305 		new_alloc_length *= 2;
306 	    }
307 
308 	    new_size = new_alloc_length * sizeof(struct propval);
309 	    ctx->mem_base = resize_proppool(ctx->mem_base, new_size);
310 
311 	    if(!ctx->mem_base) {
312 		ctx->values = NULL;
313 		ctx->allocated_values = ctx->used_values = 0;
314 		return SASL_NOMEM;
315 	    }
316 
317 	    /* It worked! Update the structure! */
318 	    ctx->values = (struct propval *)ctx->mem_base->data;
319 	    ctx->allocated_values = new_alloc_length;
320 	    ctx->mem_base->unused = ctx->mem_base->size
321 		- sizeof(struct propval) * ctx->allocated_values;
322 	}
323 
324 	/* Clear out new propvals */
325 	memset(&(ctx->values[ctx->used_values]), 0,
326 	       sizeof(struct propval) * (ctx->allocated_values - ctx->used_values));
327 
328         /* Finish updating the context -- we've extended the list! */
329 	/* ctx->list_end = (char **)(ctx->values + ctx->allocated_values); */
330 	/* xxx test here */
331 	ctx->list_end = (char **)(ctx->values + total_values);
332     }
333 
334     /* Now do the copy, or referencing rather */
335     for(i=0;i<new_values;i++) {
336 	unsigned j, flag;
337 
338 	flag = 0;
339 
340 	/* Check for dups */
341 	for(j=0;j<ctx->used_values;j++) {
342 	    if(!strcmp(ctx->values[j].name, names[i])) {
343 		flag = 1;
344 		break;
345 	    }
346 	}
347 
348 	/* We already have it... skip! */
349 	if(flag) continue;
350 
351 	ctx->values[ctx->used_values++].name = names[i];
352     }
353 
354     prop_clear(ctx, 0);
355 
356     return SASL_OK;
357 }
358 
359 /* return array of struct propval from the context
360  *  return value persists until next call to
361  *   prop_request, prop_clear or prop_dispose on context
362  */
prop_get(struct propctx * ctx)363 const struct propval *prop_get(struct propctx *ctx)
364 {
365     if(!ctx) return NULL;
366 
367     return ctx->values;
368 }
369 
370 /* Fill in an array of struct propval based on a list of property names
371  *  return value persists until next call to
372  *   prop_request, prop_clear or prop_dispose on context
373  *  returns -1 on error (no properties ever requested, ctx NULL, etc)
374  *  returns number of matching properties which were found (values != NULL)
375  *  if a name requested here was never requested by a prop_request, then
376  *  the name field of the associated vals entry will be set to NULL
377  */
prop_getnames(struct propctx * ctx,const char ** names,struct propval * vals)378 int prop_getnames(struct propctx *ctx, const char **names,
379 		  struct propval *vals)
380 {
381     int found_names = 0;
382 
383     struct propval *cur = vals;
384     const char **curname;
385 
386     if(!ctx || !names || !vals) return SASL_BADPARAM;
387 
388     for(curname = names; *curname; curname++) {
389 	struct propval *val;
390 	for(val = ctx->values; val->name; val++) {
391 	    if(!strcmp(*curname,val->name)) {
392 		found_names++;
393 		memcpy(cur, val, sizeof(struct propval));
394 		goto next;
395 	    }
396 	}
397 
398 	/* If we are here, we didn't find it */
399 	memset(cur, 0, sizeof(struct propval));
400 
401 	next:
402 	cur++;
403     }
404 
405     return found_names;
406 }
407 
408 
409 /* clear values and optionally requests from property context
410  *  ctx      -- property context
411  *  requests -- 0 = don't clear requests, 1 = clear requests
412  */
prop_clear(struct propctx * ctx,int requests)413 void prop_clear(struct propctx *ctx, int requests)
414 {
415     struct proppool *new_pool, *tmp;
416     unsigned i;
417 
418 #ifdef _SUN_SDK_
419     if(!ctx) return;
420 #endif /* _SUN_SDK_ */
421 
422     /* We're going to need a new proppool once we reset things */
423     new_pool = alloc_proppool(ctx->mem_base->size +
424 			      (ctx->used_values+1) * sizeof(struct propval));
425 
426     if(requests) {
427 	/* We're wiping the whole shebang */
428 	ctx->used_values = 0;
429     } else {
430 	/* Need to keep around old requets */
431 	struct propval *new_values = (struct propval *)new_pool->data;
432 	for(i=0; i<ctx->used_values; i++) {
433 	    new_values[i].name = ctx->values[i].name;
434 	}
435     }
436 
437     while(ctx->mem_base) {
438 	tmp = ctx->mem_base;
439 	ctx->mem_base = tmp->next;
440 #ifdef _SUN_SDK_
441 	sasl_sun_FREE(tmp);
442 #else
443 	sasl_FREE(tmp);
444 #endif /* _SUN_SDK_ */
445     }
446 
447     /* Update allocation-related metadata */
448     ctx->allocated_values = ctx->used_values+1;
449     new_pool->unused =
450 	new_pool->size - (ctx->allocated_values * sizeof(struct propval));
451 
452     /* Setup pointers for the values array */
453     ctx->values = (struct propval *)new_pool->data;
454     ctx->prev_val = NULL;
455 
456     /* Setup the pools */
457     ctx->mem_base = ctx->mem_cur = new_pool;
458 
459     /* Reset list_end and data_end for the new memory pool */
460     ctx->list_end =
461 	(char **)((char *)ctx->mem_base->data + ctx->allocated_values * sizeof(struct propval));
462     ctx->data_end = (char *)ctx->mem_base->data + ctx->mem_base->size;
463 
464     return;
465 }
466 
467 /*
468  * erase the value of a property
469  */
prop_erase(struct propctx * ctx,const char * name)470 void prop_erase(struct propctx *ctx, const char *name)
471 {
472     struct propval *val;
473     int i;
474 
475     if(!ctx || !name) return;
476 
477     for(val = ctx->values; val->name; val++) {
478 	if(!strcmp(name,val->name)) {
479 	    if(!val->values) break;
480 
481 	    /*
482 	     * Yes, this is casting away the const, but
483 	     * we should be okay because the only place this
484 	     * memory should be is in the proppool's
485 	     */
486 	    for(i=0;val->values[i];i++) {
487 		memset((void *)(val->values[i]),0,strlen(val->values[i]));
488 		val->values[i] = NULL;
489 	    }
490 
491 	    val->values = NULL;
492 	    val->nvalues = 0;
493 	    val->valsize = 0;
494 	    break;
495 	}
496     }
497 
498     return;
499 }
500 
501 /****fetcher interfaces****/
502 
503 /* format the requested property names into a string
504  *  ctx    -- context from prop_new()/prop_request()
505  *  sep    -- separator between property names (unused if none requested)
506  *  seplen -- length of separator, if < 0 then strlen(sep) will be used
507  *  outbuf -- output buffer
508  *  outmax -- maximum length of output buffer including NUL terminator
509  *  outlen -- set to length of output string excluding NUL terminator
510  * returns 0 on success and amount of additional space needed on failure
511  */
prop_format(struct propctx * ctx,const char * sep,int seplen,char * outbuf,unsigned outmax,unsigned * outlen)512 int prop_format(struct propctx *ctx, const char *sep, int seplen,
513 		char *outbuf, unsigned outmax, unsigned *outlen)
514 {
515     unsigned needed, flag = 0;
516     struct propval *val;
517 
518     if(!ctx || !outbuf) return SASL_BADPARAM;
519 
520     if(!sep) seplen = 0;
521     if(seplen < 0) seplen = strlen(sep);
522 
523     needed = seplen * (ctx->used_values - 1);
524     for(val = ctx->values; val->name; val++) {
525 	needed += strlen(val->name);
526     }
527 
528     if(!outmax) return (needed + 1); /* Because of unsigned funkiness */
529     if(needed > (outmax - 1)) return (needed - (outmax - 1));
530 
531     *outbuf = '\0';
532     if(outlen) *outlen = needed;
533 
534     if(needed == 0) return SASL_OK;
535 
536     for(val = ctx->values; val->name; val++) {
537 	if(seplen && flag) {
538 	    strncat(outbuf, sep, seplen);
539 	} else {
540 	    flag = 1;
541 	}
542 	strcat(outbuf, val->name);
543     }
544 
545     return SASL_OK;
546 }
547 
548 /* add a property value to the context
549  *  ctx    -- context from prop_new()/prop_request()
550  *  name   -- name of property to which value will be added
551  *            if NULL, add to the same name as previous prop_set/setvals call
552  *  value  -- a value for the property; will be copied into context
553  *            if NULL, remove existing values
554  *  vallen -- length of value, if <= 0 then strlen(value) will be used
555  */
prop_set(struct propctx * ctx,const char * name,const char * value,int vallen)556 int prop_set(struct propctx *ctx, const char *name,
557 	     const char *value, int vallen)
558 {
559     struct propval *cur;
560 
561     if(!ctx) return SASL_BADPARAM;
562     if(!name && !ctx->prev_val) return SASL_BADPARAM;
563 
564     if(name) {
565 	struct propval *val;
566 
567 	ctx->prev_val = NULL;
568 
569 	for(val = ctx->values; val->name; val++) {
570 	    if(!strcmp(name,val->name)){
571 		ctx->prev_val = val;
572 		break;
573 	    }
574 	}
575 
576 	/* Couldn't find it! */
577 	if(!ctx->prev_val) return SASL_BADPARAM;
578     }
579 
580     cur = ctx->prev_val;
581 
582     if(name) /* New Entry */ {
583 	unsigned nvalues = 1; /* 1 for NULL entry */
584 	const char **old_values = NULL;
585 	char **tmp, **tmp2;
586 	size_t size;
587 
588 	if(cur->values) {
589 
590 	    if(!value) {
591 		/* If we would be adding a null value, then we are done */
592 		return SASL_OK;
593 	    }
594 
595 	    old_values = cur->values;
596 	    tmp = (char **)cur->values;
597 	    while(*tmp) {
598 		nvalues++;
599 		tmp++;
600 	    }
601 
602 	}
603 
604 	if(value) {
605 	    nvalues++; /* for the new value */
606 	}
607 
608 	size = nvalues * sizeof(char*);
609 
610 	if(size > ctx->mem_cur->unused) {
611 	    size_t needed;
612 
613 	    for(needed = ctx->mem_cur->size * 2; needed < size; needed *= 2);
614 
615 	    /* Allocate a new proppool */
616 	    ctx->mem_cur->next = alloc_proppool(needed);
617 	    if(!ctx->mem_cur->next) return SASL_NOMEM;
618 
619 	    ctx->mem_cur = ctx->mem_cur->next;
620 
621 	    ctx->list_end = (char **)ctx->mem_cur->data;
622 	    ctx->data_end = ctx->mem_cur->data + needed;
623 	}
624 
625 	/* Grab the memory */
626 	ctx->mem_cur->unused -= size;
627 	cur->values = (const char **)ctx->list_end;
628 	cur->values[nvalues - 1] = NULL;
629 
630 	/* Finish updating the context */
631 	ctx->list_end = (char **)(cur->values + nvalues);
632 
633 	/* If we don't have an actual value to fill in, we are done */
634 	if(!value)
635 	    return SASL_OK;
636 
637 	tmp2 = (char **)cur->values;
638 	if(old_values) {
639 	    tmp = (char **)old_values;
640 
641 	    while(*tmp) {
642 		*tmp2 = *tmp;
643 		tmp++; tmp2++;
644 	    }
645 	}
646 
647 	/* Now allocate the last entry */
648 	if(vallen <= 0)
649 	    size = (size_t)(strlen(value) + 1);
650 	else
651 	    size = (size_t)(vallen + 1);
652 
653 	if(size > ctx->mem_cur->unused) {
654 	    size_t needed;
655 
656 	    needed = ctx->mem_cur->size * 2;
657 
658 	    while(needed < size) {
659 		needed *= 2;
660 	    }
661 
662 	    /* Allocate a new proppool */
663 	    ctx->mem_cur->next = alloc_proppool(needed);
664 	    if(!ctx->mem_cur->next) return SASL_NOMEM;
665 
666 	    ctx->mem_cur = ctx->mem_cur->next;
667 	    ctx->list_end = (char **)ctx->mem_cur->data;
668 	    ctx->data_end = ctx->mem_cur->data + needed;
669 	}
670 
671 	/* Update the data_end pointer */
672 	ctx->data_end -= size;
673 	ctx->mem_cur->unused -= size;
674 
675 	/* Copy and setup the new value! */
676 	memcpy(ctx->data_end, value, size-1);
677 	ctx->data_end[size - 1] = '\0';
678 	cur->values[nvalues - 2] = ctx->data_end;
679 
680 	cur->nvalues++;
681 	cur->valsize += (size - 1);
682     } else /* Appending an entry */ {
683 	char **tmp;
684 	size_t size;
685 
686 	/* If we are setting it to be NULL, we are done */
687 	if(!value) return SASL_OK;
688 
689 	size = sizeof(char*);
690 
691 	/* Is it in the current pool, and will it fit in the unused space? */
692 	if(size > ctx->mem_cur->unused &&
693 	    (void *)cur->values > (void *)(ctx->mem_cur->data) &&
694 	    (void *)cur->values < (void *)(ctx->mem_cur->data + ctx->mem_cur->size)) {
695 	    /* recursively call the not-fast way */
696 	    return prop_set(ctx, cur->name, value, vallen);
697 	}
698 
699 	/* Note the invariant: the previous value list must be
700 	   at the top of the CURRENT pool at this point */
701 
702 	/* Grab the memory */
703 	ctx->mem_cur->unused -= size;
704 	ctx->list_end++;
705 
706 	*(ctx->list_end - 1) = NULL;
707 	tmp = (ctx->list_end - 2);
708 
709 	/* Now allocate the last entry */
710 	if(vallen <= 0)
711 	    size = strlen(value) + 1;
712 	else
713 	    size = vallen + 1;
714 
715 	if(size > ctx->mem_cur->unused) {
716 	    size_t needed;
717 
718 	    needed = ctx->mem_cur->size * 2;
719 
720 	    while(needed < size) {
721 		needed *= 2;
722 	    }
723 
724 	    /* Allocate a new proppool */
725 	    ctx->mem_cur->next = alloc_proppool(needed);
726 	    if(!ctx->mem_cur->next) return SASL_NOMEM;
727 
728 	    ctx->mem_cur = ctx->mem_cur->next;
729 	    ctx->list_end = (char **)ctx->mem_cur->data;
730 	    ctx->data_end = ctx->mem_cur->data + needed;
731 	}
732 
733 	/* Update the data_end pointer */
734 	ctx->data_end -= size;
735 	ctx->mem_cur->unused -= size;
736 
737 	/* Copy and setup the new value! */
738 	memcpy(ctx->data_end, value, size-1);
739 	ctx->data_end[size - 1] = '\0';
740 	*tmp = ctx->data_end;
741 
742 	cur->nvalues++;
743 	cur->valsize += (size - 1);
744     }
745 
746     return SASL_OK;
747 }
748 
749 
750 /* set the values for a property
751  *  ctx    -- context from prop_new()/prop_request()
752  *  name   -- name of property to which value will be added
753  *            if NULL, add to the same name as previous prop_set/setvals call
754  *  values -- array of values, ending in NULL.  Each value is a NUL terminated
755  *            string
756  */
prop_setvals(struct propctx * ctx,const char * name,const char ** values)757 int prop_setvals(struct propctx *ctx, const char *name,
758 		 const char **values)
759 {
760     const char **val = values;
761     int result = SASL_OK;
762 
763     if(!ctx) return SASL_BADPARAM;
764 
765     /* If they want us to add no values, we can do that */
766     if(!values) return SASL_OK;
767 
768     /* Basically, use prop_set to do all our dirty work for us */
769     if(name) {
770 	result = prop_set(ctx, name, *val, 0);
771 	val++;
772     }
773 
774     for(;*val;val++) {
775 	if(result != SASL_OK) return result;
776 	result = prop_set(ctx, NULL, *val,0);
777     }
778 
779     return result;
780 }
781 
782 /* Request a set of auxiliary properties
783  *  conn         connection context
784  *  propnames    list of auxiliary property names to request ending with
785  *               NULL.
786  *
787  * Subsequent calls will add items to the request list.  Call with NULL
788  * to clear the request list.
789  *
790  * errors
791  *  SASL_OK       -- success
792  *  SASL_BADPARAM -- bad count/conn parameter
793  *  SASL_NOMEM    -- out of memory
794  */
sasl_auxprop_request(sasl_conn_t * conn,const char ** propnames)795 int sasl_auxprop_request(sasl_conn_t *conn, const char **propnames)
796 {
797     int result;
798     sasl_server_conn_t *sconn;
799 
800     if(!conn) return SASL_BADPARAM;
801     if(conn->type != SASL_CONN_SERVER)
802 	PARAMERROR(conn);
803 
804     sconn = (sasl_server_conn_t *)conn;
805 
806     if(!propnames) {
807 	prop_clear(sconn->sparams->propctx,1);
808 	return SASL_OK;
809     }
810 
811     result = prop_request(sconn->sparams->propctx, propnames);
812     RETURN(conn, result);
813 }
814 
815 
816 /* Returns current auxiliary property context.
817  * Use functions in prop.h to access content
818  *
819  *  if authentication hasn't completed, property values may be empty/NULL
820  *
821  *  properties not recognized by active plug-ins will be left empty/NULL
822  *
823  *  returns NULL if conn is invalid.
824  */
sasl_auxprop_getctx(sasl_conn_t * conn)825 struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn)
826 {
827     sasl_server_conn_t *sconn;
828 
829     if(!conn || conn->type != SASL_CONN_SERVER) return NULL;
830 
831     sconn = (sasl_server_conn_t *)conn;
832 
833     return sconn->sparams->propctx;
834 }
835 
836 /* add an auxiliary property plugin */
837 #ifdef _SUN_SDK_
sasl_auxprop_add_plugin(const char * plugname,sasl_auxprop_init_t * auxpropfunc)838 int sasl_auxprop_add_plugin(const char *plugname,
839                             sasl_auxprop_init_t *auxpropfunc)
840 {
841     return (_sasl_auxprop_add_plugin(_sasl_gbl_ctx(), plugname, auxpropfunc));
842 }
843 
_sasl_auxprop_add_plugin(void * ctx,const char * plugname,sasl_auxprop_init_t * auxpropfunc)844 int _sasl_auxprop_add_plugin(void *ctx,
845                              const char *plugname,
846                              sasl_auxprop_init_t *auxpropfunc)
847 #else
848 int sasl_auxprop_add_plugin(const char *plugname,
849 			    sasl_auxprop_init_t *auxpropfunc)
850 #endif /* _SUN_SDK_ */
851 {
852     int result, out_version;
853     auxprop_plug_list_t *new_item;
854     sasl_auxprop_plug_t *plug;
855 #ifdef _SUN_SDK_
856     _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
857     auxprop_plug_list_t *auxprop_head;
858     const sasl_utils_t *sasl_global_utils;
859     auxprop_plug_list_t *l;
860 
861     auxprop_head = gctx->auxprop_head;
862     sasl_global_utils = gctx->sasl_server_global_utils;
863 
864   /* Check to see if this plugin has already been registered */
865     for (l = auxprop_head; l != NULL; l = l->next) {
866 	if (strcmp(plugname, l->plugname) == 0) {
867 	    return SASL_OK;
868 	}
869     }
870 #endif /* _SUN_SDK_ */
871 
872     result = auxpropfunc(sasl_global_utils, SASL_AUXPROP_PLUG_VERSION,
873 			 &out_version, &plug, plugname);
874 
875     if(result != SASL_OK) {
876 #ifdef _SUN_SDK_
877 	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
878 		SASL_LOG_ERR, "auxpropfunc error %i\n",result);
879 #else
880 	_sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %i\n",result);
881 #endif /* _SUN_SDK_ */
882 	return result;
883     }
884 
885     /* We require that this function is implemented */
886     if(!plug->auxprop_lookup) return SASL_BADPROT;
887 
888 #ifdef _SUN_SDK_
889     /* Check plugin to make sure name is non-NULL */
890     if (plug->name == NULL) {
891 	__sasl_log(gctx, gctx->server_global_callbacks.callbacks,
892 		SASL_LOG_ERR, "invalid auxprop plugin %s", plugname);
893 	return SASL_BADPROT;
894     }
895 #endif /* _SUN_SDK_ */
896 
897     new_item = sasl_ALLOC(sizeof(auxprop_plug_list_t));
898     if(!new_item) return SASL_NOMEM;
899 
900 #ifdef _SUN_SDK_
901     if(_sasl_strdup(plugname, &new_item->plugname, NULL) != SASL_OK) {
902 	sasl_FREE(new_item);
903 	return SASL_NOMEM;
904     }
905 #endif /* _SUN_SDK_ */
906     /* These will load from least-important to most important */
907     new_item->plug = plug;
908     new_item->next = auxprop_head;
909 #ifdef _SUN_SDK_
910     gctx->auxprop_head = new_item;
911 #else
912     auxprop_head = new_item;
913 #endif /* _SUN_SDK_ */
914 
915     return SASL_OK;
916 }
917 
918 #ifdef _SUN_SDK_
_sasl_auxprop_free(_sasl_global_context_t * gctx)919 void _sasl_auxprop_free(_sasl_global_context_t *gctx)
920 #else
921 void _sasl_auxprop_free()
922 #endif /* _SUN_SDK_ */
923 {
924     auxprop_plug_list_t *ptr, *ptr_next;
925 #ifdef _SUN_SDK_
926     const sasl_utils_t *sasl_global_utils = gctx->sasl_server_global_utils;
927 
928     for(ptr = (auxprop_plug_list_t *)gctx->auxprop_head; ptr; ptr = ptr_next) {
929 #else
930 
931     for(ptr = auxprop_head; ptr; ptr = ptr_next) {
932 #endif /* _SUN_SDK_ */
933 	ptr_next = ptr->next;
934 	if(ptr->plug->auxprop_free)
935 	    ptr->plug->auxprop_free(ptr->plug->glob_context,
936 				    sasl_global_utils);
937 #ifdef _SUN_SDK_
938 	sasl_FREE(ptr->plugname);
939 #endif /* _SUN_SDK_ */
940 	sasl_FREE(ptr);
941     }
942 
943 #ifdef _SUN_SDK_
944     gctx->auxprop_head = NULL;
945 #else
946     auxprop_head = NULL;
947 #endif /* _SUN_SDK_ */
948 }
949 
950 
951 /* Do the callbacks for auxprop lookups */
952 void _sasl_auxprop_lookup(sasl_server_params_t *sparams,
953 			  unsigned flags,
954 			  const char *user, unsigned ulen)
955 {
956     sasl_getopt_t *getopt;
957     int ret, found = 0;
958     void *context;
959     const char *plist = NULL;
960     auxprop_plug_list_t *ptr;
961 #ifdef _SUN_SDK_
962     _sasl_global_context_t *gctx = sparams->utils->conn->gctx;
963     auxprop_plug_list_t *auxprop_head = gctx->auxprop_head;
964 #endif /* _SUN_SDK_ */
965 
966     if(_sasl_getcallback(sparams->utils->conn,
967 			 SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
968 	ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL);
969 	if(ret != SASL_OK) plist = NULL;
970     }
971 
972     if(!plist) {
973 	/* Do lookup in all plugins */
974 	for(ptr = auxprop_head; ptr; ptr = ptr->next) {
975 	    found=1;
976 	    ptr->plug->auxprop_lookup(ptr->plug->glob_context,
977 				      sparams, flags, user, ulen);
978 	}
979     } else {
980 	char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL;
981 
982 	if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return;
983 	thisplugin = freeptr = pluginlist;
984 
985 	/* Do lookup in all *specified* plugins, in order */
986 	while(*thisplugin) {
987 	    char *p;
988 	    int last=0;
989 
990 	    while(*thisplugin && isspace((int)*thisplugin)) thisplugin++;
991 	    if(!(*thisplugin)) break;
992 
993 	    for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++);
994 	    if(*p == '\0') last = 1;
995 	    else *p='\0';
996 
997 	    for(ptr = auxprop_head; ptr; ptr = ptr->next) {
998 		/* Skip non-matching plugins */
999 		if(!ptr->plug->name
1000 		   || strcasecmp(ptr->plug->name, thisplugin))
1001 		    continue;
1002 
1003 		found=1;
1004 		ptr->plug->auxprop_lookup(ptr->plug->glob_context,
1005 					  sparams, flags, user, ulen);
1006 	    }
1007 
1008 	    if(last) break;
1009 
1010 	    thisplugin = p+1;
1011 	}
1012 
1013 	sasl_FREE(freeptr);
1014     }
1015 
1016     if(!found)
1017 	_sasl_log(sparams->utils->conn, SASL_LOG_DEBUG,
1018 		  "could not find auxprop plugin, was searching for '%s'",
1019 		  plist ? plist : "[all]");
1020 }
1021