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