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