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