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