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