1/* $NetBSD: ad.c,v 1.1.1.3 2010/12/12 15:22:16 adam Exp $ */ 2 3/* ad.c - routines for dealing with attribute descriptions */ 4/* OpenLDAP: pkg/ldap/servers/slapd/ad.c,v 1.95.2.10 2010/04/13 20:23:10 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19#include "portable.h" 20 21#include <stdio.h> 22 23#include <ac/ctype.h> 24#include <ac/errno.h> 25#include <ac/socket.h> 26#include <ac/string.h> 27#include <ac/time.h> 28 29#include "slap.h" 30#include "lutil.h" 31 32static struct berval bv_no_attrs = BER_BVC( LDAP_NO_ATTRS ); 33static struct berval bv_all_user_attrs = BER_BVC( "*" ); 34static struct berval bv_all_operational_attrs = BER_BVC( "+" ); 35 36static AttributeName anlist_no_attrs[] = { 37 { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL }, 38 { BER_BVNULL, NULL, 0, NULL } 39}; 40 41static AttributeName anlist_all_user_attributes[] = { 42 { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL }, 43 { BER_BVNULL, NULL, 0, NULL } 44}; 45 46static AttributeName anlist_all_operational_attributes[] = { 47 { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL }, 48 { BER_BVNULL, NULL, 0, NULL } 49}; 50 51static AttributeName anlist_all_attributes[] = { 52 { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL }, 53 { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL }, 54 { BER_BVNULL, NULL, 0, NULL } 55}; 56 57AttributeName *slap_anlist_no_attrs = anlist_no_attrs; 58AttributeName *slap_anlist_all_user_attributes = anlist_all_user_attributes; 59AttributeName *slap_anlist_all_operational_attributes = anlist_all_operational_attributes; 60AttributeName *slap_anlist_all_attributes = anlist_all_attributes; 61 62struct berval * slap_bv_no_attrs = &bv_no_attrs; 63struct berval * slap_bv_all_user_attrs = &bv_all_user_attrs; 64struct berval * slap_bv_all_operational_attrs = &bv_all_operational_attrs; 65 66typedef struct Attr_option { 67 struct berval name; /* option name or prefix */ 68 int prefix; /* NAME is a tag and range prefix */ 69} Attr_option; 70 71static Attr_option lang_option = { BER_BVC("lang-"), 1 }; 72 73/* Options sorted by name, and number of options */ 74static Attr_option *options = &lang_option; 75static int option_count = 1; 76 77static int msad_range_hack = 0; 78 79static Attr_option *ad_find_option_definition( const char *opt, int optlen ); 80 81static int ad_keystring( 82 struct berval *bv ) 83{ 84 ber_len_t i; 85 86 if( !AD_LEADCHAR( bv->bv_val[0] ) ) { 87 return 1; 88 } 89 90 for( i=1; i<bv->bv_len; i++ ) { 91 if( !AD_CHAR( bv->bv_val[i] )) { 92 if ( msad_range_hack && bv->bv_val[i] == '=' ) 93 continue; 94 return 1; 95 } 96 } 97 return 0; 98} 99 100void ad_destroy( AttributeDescription *ad ) 101{ 102 AttributeDescription *n; 103 104 for (; ad != NULL; ad = n) { 105 n = ad->ad_next; 106 ldap_memfree( ad ); 107 } 108} 109 110/* Is there an AttributeDescription for this type that uses these tags? */ 111AttributeDescription * ad_find_tags( 112 AttributeType *type, 113 struct berval *tags ) 114{ 115 AttributeDescription *ad; 116 117 ldap_pvt_thread_mutex_lock( &type->sat_ad_mutex ); 118 for (ad = type->sat_ad; ad; ad=ad->ad_next) 119 { 120 if (ad->ad_tags.bv_len == tags->bv_len && 121 !strcasecmp(ad->ad_tags.bv_val, tags->bv_val)) 122 break; 123 } 124 ldap_pvt_thread_mutex_unlock( &type->sat_ad_mutex ); 125 return ad; 126} 127 128int slap_str2ad( 129 const char *str, 130 AttributeDescription **ad, 131 const char **text ) 132{ 133 struct berval bv; 134 bv.bv_val = (char *) str; 135 bv.bv_len = strlen( str ); 136 137 return slap_bv2ad( &bv, ad, text ); 138} 139 140static char *strchrlen( 141 const char *beg, 142 const char *end, 143 const char ch, 144 int *len ) 145{ 146 const char *p; 147 148 for( p=beg; *p && p < end; p++ ) { 149 if( *p == ch ) { 150 *len = p - beg; 151 return (char *) p; 152 } 153 } 154 155 *len = p - beg; 156 return NULL; 157} 158 159int slap_bv2ad( 160 struct berval *bv, 161 AttributeDescription **ad, 162 const char **text ) 163{ 164 int rtn = LDAP_UNDEFINED_TYPE; 165 AttributeDescription desc, *d2; 166 char *name, *options, *optn; 167 char *opt, *next; 168 int ntags; 169 int tagslen; 170 171 /* hardcoded limits for speed */ 172#define MAX_TAGGING_OPTIONS 128 173 struct berval tags[MAX_TAGGING_OPTIONS+1]; 174#define MAX_TAGS_LEN 1024 175 char tagbuf[MAX_TAGS_LEN]; 176 177 assert( ad != NULL ); 178 assert( *ad == NULL ); /* temporary */ 179 180 if( bv == NULL || BER_BVISNULL( bv ) || BER_BVISEMPTY( bv ) ) { 181 *text = "empty AttributeDescription"; 182 return rtn; 183 } 184 185 /* make sure description is IA5 */ 186 if( ad_keystring( bv ) ) { 187 *text = "AttributeDescription contains inappropriate characters"; 188 return rtn; 189 } 190 191 /* find valid base attribute type; parse in place */ 192 desc.ad_cname = *bv; 193 desc.ad_flags = 0; 194 BER_BVZERO( &desc.ad_tags ); 195 name = bv->bv_val; 196 options = ber_bvchr( bv, ';' ); 197 if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) { 198 /* don't go past the end of the berval! */ 199 desc.ad_cname.bv_len = options - name; 200 } else { 201 options = NULL; 202 } 203 desc.ad_type = at_bvfind( &desc.ad_cname ); 204 if( desc.ad_type == NULL ) { 205 *text = "attribute type undefined"; 206 return rtn; 207 } 208 209 if( is_at_operational( desc.ad_type ) && options != NULL ) { 210 *text = "operational attribute with options undefined"; 211 return rtn; 212 } 213 214 /* 215 * parse options in place 216 */ 217 ntags = 0; 218 tagslen = 0; 219 optn = bv->bv_val + bv->bv_len; 220 221 for( opt=options; opt != NULL; opt=next ) { 222 int optlen; 223 opt++; 224 next = strchrlen( opt, optn, ';', &optlen ); 225 226 if( optlen == 0 ) { 227 *text = "zero length option is invalid"; 228 return rtn; 229 230 } else if ( optlen == STRLENOF("binary") && 231 strncasecmp( opt, "binary", STRLENOF("binary") ) == 0 ) 232 { 233 /* binary option */ 234 if( slap_ad_is_binary( &desc ) ) { 235 *text = "option \"binary\" specified multiple times"; 236 return rtn; 237 } 238 239 if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) { 240 /* not stored in binary, disallow option */ 241 *text = "option \"binary\" not supported with type"; 242 return rtn; 243 } 244 245 desc.ad_flags |= SLAP_DESC_BINARY; 246 continue; 247 248 } else if ( ad_find_option_definition( opt, optlen ) ) { 249 int i; 250 251 if( opt[optlen-1] == '-' || 252 ( opt[optlen-1] == '=' && msad_range_hack )) { 253 desc.ad_flags |= SLAP_DESC_TAG_RANGE; 254 } 255 256 if( ntags >= MAX_TAGGING_OPTIONS ) { 257 *text = "too many tagging options"; 258 return rtn; 259 } 260 261 /* 262 * tags should be presented in sorted order, 263 * so run the array in reverse. 264 */ 265 for( i=ntags-1; i>=0; i-- ) { 266 int rc; 267 268 rc = strncasecmp( opt, tags[i].bv_val, 269 (unsigned) optlen < tags[i].bv_len 270 ? (unsigned) optlen : tags[i].bv_len ); 271 272 if( rc == 0 && (unsigned)optlen == tags[i].bv_len ) { 273 /* duplicate (ignore) */ 274 goto done; 275 276 } else if ( rc > 0 || 277 ( rc == 0 && (unsigned)optlen > tags[i].bv_len )) 278 { 279 AC_MEMCPY( &tags[i+2], &tags[i+1], 280 (ntags-i-1)*sizeof(struct berval) ); 281 tags[i+1].bv_val = opt; 282 tags[i+1].bv_len = optlen; 283 goto done; 284 } 285 } 286 287 if( ntags ) { 288 AC_MEMCPY( &tags[1], &tags[0], 289 ntags*sizeof(struct berval) ); 290 } 291 tags[0].bv_val = opt; 292 tags[0].bv_len = optlen; 293 294done:; 295 tagslen += optlen + 1; 296 ntags++; 297 298 } else { 299 *text = "unrecognized option"; 300 return rtn; 301 } 302 } 303 304 if( ntags > 0 ) { 305 int i; 306 307 if( tagslen > MAX_TAGS_LEN ) { 308 *text = "tagging options too long"; 309 return rtn; 310 } 311 312 desc.ad_tags.bv_val = tagbuf; 313 tagslen = 0; 314 315 for( i=0; i<ntags; i++ ) { 316 AC_MEMCPY( &desc.ad_tags.bv_val[tagslen], 317 tags[i].bv_val, tags[i].bv_len ); 318 319 tagslen += tags[i].bv_len; 320 desc.ad_tags.bv_val[tagslen++] = ';'; 321 } 322 323 desc.ad_tags.bv_val[--tagslen] = '\0'; 324 desc.ad_tags.bv_len = tagslen; 325 } 326 327 /* see if a matching description is already cached */ 328 for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { 329 if( d2->ad_flags != desc.ad_flags ) { 330 continue; 331 } 332 if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) { 333 continue; 334 } 335 if( d2->ad_tags.bv_len == 0 ) { 336 break; 337 } 338 if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val, 339 desc.ad_tags.bv_len ) == 0 ) 340 { 341 break; 342 } 343 } 344 345 /* Not found, add new one */ 346 while (d2 == NULL) { 347 size_t dlen = 0; 348 ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex ); 349 /* check again now that we've locked */ 350 for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { 351 if (d2->ad_flags != desc.ad_flags) 352 continue; 353 if (d2->ad_tags.bv_len != desc.ad_tags.bv_len) 354 continue; 355 if (d2->ad_tags.bv_len == 0) 356 break; 357 if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val, 358 desc.ad_tags.bv_len) == 0) 359 break; 360 } 361 if (d2) { 362 ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); 363 break; 364 } 365 366 /* Allocate a single contiguous block. If there are no 367 * options, we just need space for the AttrDesc structure. 368 * Otherwise, we need to tack on the full name length + 369 * options length, + maybe tagging options length again. 370 */ 371 if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) { 372 dlen = desc.ad_type->sat_cname.bv_len + 1; 373 if (desc.ad_tags.bv_len) { 374 dlen += 1 + desc.ad_tags.bv_len; 375 } 376 if ( slap_ad_is_binary( &desc ) ) { 377 dlen += 1 + STRLENOF(";binary") + desc.ad_tags.bv_len; 378 } 379 } 380 381 d2 = ch_malloc(sizeof(AttributeDescription) + dlen); 382 d2->ad_next = NULL; 383 d2->ad_type = desc.ad_type; 384 d2->ad_flags = desc.ad_flags; 385 d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len; 386 d2->ad_tags.bv_len = desc.ad_tags.bv_len; 387 388 if (dlen == 0) { 389 d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val; 390 d2->ad_tags.bv_val = NULL; 391 } else { 392 char *cp, *op, *lp; 393 int j; 394 d2->ad_cname.bv_val = (char *)(d2+1); 395 strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val); 396 cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len; 397 if( slap_ad_is_binary( &desc ) ) { 398 op = cp; 399 lp = NULL; 400 if( desc.ad_tags.bv_len ) { 401 lp = desc.ad_tags.bv_val; 402 while( strncasecmp(lp, "binary", STRLENOF("binary")) < 0 403 && (lp = strchr( lp, ';' )) != NULL ) 404 ++lp; 405 if( lp != desc.ad_tags.bv_val ) { 406 *cp++ = ';'; 407 j = (lp 408 ? (unsigned) (lp - desc.ad_tags.bv_val - 1) 409 : strlen( desc.ad_tags.bv_val )); 410 cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j); 411 } 412 } 413 cp = lutil_strcopy(cp, ";binary"); 414 if( lp != NULL ) { 415 *cp++ = ';'; 416 cp = lutil_strcopy(cp, lp); 417 } 418 d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val; 419 if( desc.ad_tags.bv_len ) 420 ldap_pvt_str2lower(op); 421 j = 1; 422 } else { 423 j = 0; 424 } 425 if( desc.ad_tags.bv_len ) { 426 lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j; 427 if ( j == 0 ) 428 *lp++ = ';'; 429 d2->ad_tags.bv_val = lp; 430 strcpy(lp, desc.ad_tags.bv_val); 431 ldap_pvt_str2lower(lp); 432 if( j == 0 ) 433 d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len; 434 } 435 } 436 /* Add new desc to list. We always want the bare Desc with 437 * no options to stay at the head of the list, assuming 438 * that one will be used most frequently. 439 */ 440 if (desc.ad_type->sat_ad == NULL || dlen == 0) { 441 d2->ad_next = desc.ad_type->sat_ad; 442 desc.ad_type->sat_ad = d2; 443 } else { 444 d2->ad_next = desc.ad_type->sat_ad->ad_next; 445 desc.ad_type->sat_ad->ad_next = d2; 446 } 447 ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); 448 } 449 450 if( *ad == NULL ) { 451 *ad = d2; 452 } else { 453 **ad = *d2; 454 } 455 456 return LDAP_SUCCESS; 457} 458 459static int is_ad_subtags( 460 struct berval *subtagsbv, 461 struct berval *suptagsbv ) 462{ 463 const char *suptags, *supp, *supdelimp, *supn; 464 const char *subtags, *subp, *subdelimp, *subn; 465 int suplen, sublen; 466 467 subtags =subtagsbv->bv_val; 468 suptags =suptagsbv->bv_val; 469 subn = subtags + subtagsbv->bv_len; 470 supn = suptags + suptagsbv->bv_len; 471 472 for( supp=suptags ; supp; supp=supdelimp ) { 473 supdelimp = strchrlen( supp, supn, ';', &suplen ); 474 if( supdelimp ) supdelimp++; 475 476 for( subp=subtags ; subp; subp=subdelimp ) { 477 subdelimp = strchrlen( subp, subn, ';', &sublen ); 478 if( subdelimp ) subdelimp++; 479 480 if ( suplen > sublen 481 ? ( suplen-1 == sublen && supp[suplen-1] == '-' 482 && strncmp( supp, subp, sublen ) == 0 ) 483 : ( ( suplen == sublen || supp[suplen-1] == '-' ) 484 && strncmp( supp, subp, suplen ) == 0 ) ) 485 { 486 goto match; 487 } 488 } 489 490 return 0; 491match:; 492 } 493 return 1; 494} 495 496int is_ad_subtype( 497 AttributeDescription *sub, 498 AttributeDescription *super 499) 500{ 501 AttributeType *a; 502 int lr; 503 504 for ( a = sub->ad_type; a; a=a->sat_sup ) { 505 if ( a == super->ad_type ) break; 506 } 507 if( !a ) { 508 return 0; 509 } 510 511 /* ensure sub does support all flags of super */ 512 lr = sub->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0; 513 if(( super->ad_flags & ( sub->ad_flags | lr )) != super->ad_flags ) { 514 return 0; 515 } 516 517 /* check for tagging options */ 518 if ( super->ad_tags.bv_len == 0 ) 519 return 1; 520 if ( sub->ad_tags.bv_len == 0 ) 521 return 0; 522 523 return is_ad_subtags( &sub->ad_tags, &super->ad_tags ); 524} 525 526int ad_inlist( 527 AttributeDescription *desc, 528 AttributeName *attrs ) 529{ 530 if (! attrs ) return 0; 531 532 for( ; attrs->an_name.bv_val; attrs++ ) { 533 AttributeType *a; 534 ObjectClass *oc; 535 536 if ( attrs->an_desc ) { 537 int lr; 538 539 if ( desc == attrs->an_desc ) { 540 return 1; 541 } 542 543 /* 544 * EXTENSION: if requested description is preceeded by 545 * a '-' character, do not match on subtypes. 546 */ 547 if ( attrs->an_name.bv_val[0] == '-' ) { 548 continue; 549 } 550 551 /* Is this a subtype of the requested attr? */ 552 for (a = desc->ad_type; a; a=a->sat_sup) { 553 if ( a == attrs->an_desc->ad_type ) 554 break; 555 } 556 if ( !a ) { 557 continue; 558 } 559 /* Does desc support all the requested flags? */ 560 lr = desc->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0; 561 if(( attrs->an_desc->ad_flags & (desc->ad_flags | lr)) 562 != attrs->an_desc->ad_flags ) { 563 continue; 564 } 565 /* Do the descs have compatible tags? */ 566 if ( attrs->an_desc->ad_tags.bv_len == 0 ) { 567 return 1; 568 } 569 if ( desc->ad_tags.bv_len == 0) { 570 continue; 571 } 572 if ( is_ad_subtags( &desc->ad_tags, 573 &attrs->an_desc->ad_tags ) ) { 574 return 1; 575 } 576 continue; 577 } 578 579 if ( ber_bvccmp( &attrs->an_name, '*' ) ) { 580 if ( !is_at_operational( desc->ad_type ) ) { 581 return 1; 582 } 583 continue; 584 } 585 586 if ( ber_bvccmp( &attrs->an_name, '+' ) ) { 587 if ( is_at_operational( desc->ad_type ) ) { 588 return 1; 589 } 590 continue; 591 } 592 593 /* 594 * EXTENSION: see if requested description is @objectClass 595 * if so, return attributes which the class requires/allows 596 * else if requested description is !objectClass, return 597 * attributes which the class does not require/allow 598 */ 599 if ( !( attrs->an_flags & SLAP_AN_OCINITED )) { 600 if( attrs->an_name.bv_val ) { 601 switch( attrs->an_name.bv_val[0] ) { 602 case '@': /* @objectClass */ 603 case '+': /* +objectClass (deprecated) */ 604 case '!': { /* exclude */ 605 struct berval ocname; 606 ocname.bv_len = attrs->an_name.bv_len - 1; 607 ocname.bv_val = &attrs->an_name.bv_val[1]; 608 oc = oc_bvfind( &ocname ); 609 if ( oc && attrs->an_name.bv_val[0] == '!' ) { 610 attrs->an_flags |= SLAP_AN_OCEXCLUDE; 611 } else { 612 attrs->an_flags &= ~SLAP_AN_OCEXCLUDE; 613 } 614 } break; 615 616 default: /* old (deprecated) way */ 617 oc = oc_bvfind( &attrs->an_name ); 618 } 619 attrs->an_oc = oc; 620 } 621 attrs->an_flags |= SLAP_AN_OCINITED; 622 } 623 oc = attrs->an_oc; 624 if( oc != NULL ) { 625 if ( attrs->an_flags & SLAP_AN_OCEXCLUDE ) { 626 if ( oc == slap_schema.si_oc_extensibleObject ) { 627 /* extensibleObject allows the return of anything */ 628 return 0; 629 } 630 631 if( oc->soc_required ) { 632 /* allow return of required attributes */ 633 int i; 634 635 for ( i = 0; oc->soc_required[i] != NULL; i++ ) { 636 for (a = desc->ad_type; a; a=a->sat_sup) { 637 if ( a == oc->soc_required[i] ) { 638 return 0; 639 } 640 } 641 } 642 } 643 644 if( oc->soc_allowed ) { 645 /* allow return of allowed attributes */ 646 int i; 647 for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) { 648 for (a = desc->ad_type; a; a=a->sat_sup) { 649 if ( a == oc->soc_allowed[i] ) { 650 return 0; 651 } 652 } 653 } 654 } 655 656 return 1; 657 } 658 659 if ( oc == slap_schema.si_oc_extensibleObject ) { 660 /* extensibleObject allows the return of anything */ 661 return 1; 662 } 663 664 if( oc->soc_required ) { 665 /* allow return of required attributes */ 666 int i; 667 668 for ( i = 0; oc->soc_required[i] != NULL; i++ ) { 669 for (a = desc->ad_type; a; a=a->sat_sup) { 670 if ( a == oc->soc_required[i] ) { 671 return 1; 672 } 673 } 674 } 675 } 676 677 if( oc->soc_allowed ) { 678 /* allow return of allowed attributes */ 679 int i; 680 for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) { 681 for (a = desc->ad_type; a; a=a->sat_sup) { 682 if ( a == oc->soc_allowed[i] ) { 683 return 1; 684 } 685 } 686 } 687 } 688 689 } else { 690 const char *text; 691 692 /* give it a chance of being retrieved by a proxy... */ 693 (void)slap_bv2undef_ad( &attrs->an_name, 694 &attrs->an_desc, &text, 695 SLAP_AD_PROXIED|SLAP_AD_NOINSERT ); 696 } 697 } 698 699 return 0; 700} 701 702 703int slap_str2undef_ad( 704 const char *str, 705 AttributeDescription **ad, 706 const char **text, 707 unsigned flags ) 708{ 709 struct berval bv; 710 bv.bv_val = (char *) str; 711 bv.bv_len = strlen( str ); 712 713 return slap_bv2undef_ad( &bv, ad, text, flags ); 714} 715 716int slap_bv2undef_ad( 717 struct berval *bv, 718 AttributeDescription **ad, 719 const char **text, 720 unsigned flags ) 721{ 722 AttributeDescription *desc; 723 AttributeType *at; 724 725 assert( ad != NULL ); 726 727 if( bv == NULL || bv->bv_len == 0 ) { 728 *text = "empty AttributeDescription"; 729 return LDAP_UNDEFINED_TYPE; 730 } 731 732 /* make sure description is IA5 */ 733 if( ad_keystring( bv ) ) { 734 *text = "AttributeDescription contains inappropriate characters"; 735 return LDAP_UNDEFINED_TYPE; 736 } 737 738 /* use the appropriate type */ 739 if ( flags & SLAP_AD_PROXIED ) { 740 at = slap_schema.si_at_proxied; 741 742 } else { 743 at = slap_schema.si_at_undefined; 744 } 745 746 for( desc = at->sat_ad; desc; desc=desc->ad_next ) { 747 if( desc->ad_cname.bv_len == bv->bv_len && 748 !strcasecmp( desc->ad_cname.bv_val, bv->bv_val ) ) 749 { 750 break; 751 } 752 } 753 754 if( !desc ) { 755 if ( flags & SLAP_AD_NOINSERT ) { 756 *text = NULL; 757 return LDAP_UNDEFINED_TYPE; 758 } 759 760 desc = ch_malloc(sizeof(AttributeDescription) + 1 + 761 bv->bv_len); 762 763 desc->ad_flags = SLAP_DESC_NONE; 764 BER_BVZERO( &desc->ad_tags ); 765 766 desc->ad_cname.bv_len = bv->bv_len; 767 desc->ad_cname.bv_val = (char *)(desc+1); 768 strcpy(desc->ad_cname.bv_val, bv->bv_val); 769 770 /* canonical to upper case */ 771 ldap_pvt_str2upper( desc->ad_cname.bv_val ); 772 773 /* shouldn't we protect this for concurrency? */ 774 desc->ad_type = at; 775 ldap_pvt_thread_mutex_lock( &ad_undef_mutex ); 776 desc->ad_next = desc->ad_type->sat_ad; 777 desc->ad_type->sat_ad = desc; 778 ldap_pvt_thread_mutex_unlock( &ad_undef_mutex ); 779 780 Debug( LDAP_DEBUG_ANY, 781 "%s attributeDescription \"%s\" inserted.\n", 782 ( flags & SLAP_AD_PROXIED ) ? "PROXIED" : "UNKNOWN", 783 desc->ad_cname.bv_val, 0 ); 784 } 785 786 if( !*ad ) { 787 *ad = desc; 788 } else { 789 **ad = *desc; 790 } 791 792 return LDAP_SUCCESS; 793} 794 795AttributeDescription * 796slap_bv2tmp_ad( 797 struct berval *bv, 798 void *memctx ) 799{ 800 AttributeDescription *ad = 801 slap_sl_mfuncs.bmf_malloc( sizeof(AttributeDescription) + 802 bv->bv_len + 1, memctx ); 803 804 ad->ad_cname.bv_val = (char *)(ad+1); 805 strncpy( ad->ad_cname.bv_val, bv->bv_val, bv->bv_len+1 ); 806 ad->ad_cname.bv_len = bv->bv_len; 807 ad->ad_flags = SLAP_DESC_TEMPORARY; 808 ad->ad_type = slap_schema.si_at_undefined; 809 810 return ad; 811} 812 813static int 814undef_promote( 815 AttributeType *at, 816 char *name, 817 AttributeType *nat ) 818{ 819 AttributeDescription **u_ad, **n_ad; 820 821 /* Get to last ad on the new type */ 822 for ( n_ad = &nat->sat_ad; *n_ad; n_ad = &(*n_ad)->ad_next ) ; 823 824 for ( u_ad = &at->sat_ad; *u_ad; ) { 825 struct berval bv; 826 827 ber_str2bv( name, 0, 0, &bv ); 828 829 /* remove iff undef == name or undef == name;tag */ 830 if ( (*u_ad)->ad_cname.bv_len >= bv.bv_len 831 && strncasecmp( (*u_ad)->ad_cname.bv_val, bv.bv_val, bv.bv_len ) == 0 832 && ( (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == '\0' 833 || (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == ';' ) ) 834 { 835 AttributeDescription *tmp = *u_ad; 836 837 *u_ad = (*u_ad)->ad_next; 838 839 tmp->ad_type = nat; 840 tmp->ad_next = NULL; 841 /* ad_cname was contiguous, no leak here */ 842 tmp->ad_cname = nat->sat_cname; 843 *n_ad = tmp; 844 n_ad = &tmp->ad_next; 845 } else { 846 u_ad = &(*u_ad)->ad_next; 847 } 848 } 849 850 return 0; 851} 852 853int 854slap_ad_undef_promote( 855 char *name, 856 AttributeType *at ) 857{ 858 int rc; 859 860 ldap_pvt_thread_mutex_lock( &ad_undef_mutex ); 861 862 rc = undef_promote( slap_schema.si_at_undefined, name, at ); 863 if ( rc == 0 ) { 864 rc = undef_promote( slap_schema.si_at_proxied, name, at ); 865 } 866 867 ldap_pvt_thread_mutex_unlock( &ad_undef_mutex ); 868 869 return rc; 870} 871 872int 873an_find( 874 AttributeName *a, 875 struct berval *s 876) 877{ 878 if( a == NULL ) return 0; 879 880 for ( ; a->an_name.bv_val; a++ ) { 881 if ( a->an_name.bv_len != s->bv_len) continue; 882 if ( strcasecmp( s->bv_val, a->an_name.bv_val ) == 0 ) { 883 return( 1 ); 884 } 885 } 886 887 return( 0 ); 888} 889 890/* 891 * Convert a delimited string into a list of AttributeNames; add 892 * on to an existing list if it was given. If the string is not 893 * a valid attribute name, if a '-' is prepended it is skipped 894 * and the remaining name is tried again; if a '@' (or '+') is 895 * prepended, an objectclass name is searched instead; if a '!' 896 * is prepended, the objectclass name is negated. 897 * 898 * NOTE: currently, if a valid attribute name is not found, the 899 * same string is also checked as valid objectclass name; however, 900 * this behavior is deprecated. 901 */ 902AttributeName * 903str2anlist( AttributeName *an, char *in, const char *brkstr ) 904{ 905 char *str; 906 char *s; 907 char *lasts; 908 int i, j; 909 const char *text; 910 AttributeName *anew; 911 912 /* find last element in list */ 913 i = 0; 914 if ( an != NULL ) { 915 for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ) ; i++) 916 ; 917 } 918 919 /* protect the input string from strtok */ 920 str = ch_strdup( in ); 921 922 /* Count words in string */ 923 j = 1; 924 for ( s = str; *s; s++ ) { 925 if ( strchr( brkstr, *s ) != NULL ) { 926 j++; 927 } 928 } 929 930 an = ch_realloc( an, ( i + j + 1 ) * sizeof( AttributeName ) ); 931 anew = an + i; 932 for ( s = ldap_pvt_strtok( str, brkstr, &lasts ); 933 s != NULL; 934 s = ldap_pvt_strtok( NULL, brkstr, &lasts ) ) 935 { 936 /* put a stop mark */ 937 BER_BVZERO( &anew[1].an_name ); 938 939 anew->an_desc = NULL; 940 anew->an_oc = NULL; 941 anew->an_flags = 0; 942 ber_str2bv(s, 0, 1, &anew->an_name); 943 slap_bv2ad(&anew->an_name, &anew->an_desc, &text); 944 if ( !anew->an_desc ) { 945 switch( anew->an_name.bv_val[0] ) { 946 case '-': { 947 struct berval adname; 948 adname.bv_len = anew->an_name.bv_len - 1; 949 adname.bv_val = &anew->an_name.bv_val[1]; 950 slap_bv2ad(&adname, &anew->an_desc, &text); 951 if ( !anew->an_desc ) { 952 goto reterr; 953 } 954 } break; 955 956 case '@': 957 case '+': /* (deprecated) */ 958 case '!': { 959 struct berval ocname; 960 ocname.bv_len = anew->an_name.bv_len - 1; 961 ocname.bv_val = &anew->an_name.bv_val[1]; 962 anew->an_oc = oc_bvfind( &ocname ); 963 if ( !anew->an_oc ) { 964 goto reterr; 965 } 966 967 if ( anew->an_name.bv_val[0] == '!' ) { 968 anew->an_flags |= SLAP_AN_OCEXCLUDE; 969 } 970 } break; 971 972 default: 973 /* old (deprecated) way */ 974 anew->an_oc = oc_bvfind( &anew->an_name ); 975 if ( !anew->an_oc ) { 976 goto reterr; 977 } 978 } 979 } 980 anew->an_flags |= SLAP_AN_OCINITED; 981 anew++; 982 } 983 984 BER_BVZERO( &anew->an_name ); 985 free( str ); 986 return( an ); 987 988reterr: 989 anlist_free( an, 1, NULL ); 990 991 /* 992 * overwrites input string 993 * on error! 994 */ 995 strcpy( in, s ); 996 free( str ); 997 return NULL; 998} 999 1000void 1001anlist_free( AttributeName *an, int freename, void *ctx ) 1002{ 1003 if ( an == NULL ) { 1004 return; 1005 } 1006 1007 if ( freename ) { 1008 int i; 1009 1010 for ( i = 0; an[i].an_name.bv_val; i++ ) { 1011 ber_memfree_x( an[i].an_name.bv_val, ctx ); 1012 } 1013 } 1014 1015 ber_memfree_x( an, ctx ); 1016} 1017 1018char **anlist2charray_x( AttributeName *an, int dup, void *ctx ) 1019{ 1020 char **attrs; 1021 int i; 1022 1023 if ( an != NULL ) { 1024 for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) 1025 ; 1026 attrs = (char **) slap_sl_malloc( (i + 1) * sizeof(char *), ctx ); 1027 for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) { 1028 if ( dup ) 1029 attrs[i] = ch_strdup( an[i].an_name.bv_val ); 1030 else 1031 attrs[i] = an[i].an_name.bv_val; 1032 } 1033 attrs[i] = NULL; 1034 } else { 1035 attrs = NULL; 1036 } 1037 1038 return attrs; 1039} 1040 1041char **anlist2charray( AttributeName *an, int dup ) 1042{ 1043 return anlist2charray_x( an, dup, NULL ); 1044} 1045 1046char** 1047anlist2attrs( AttributeName * anlist ) 1048{ 1049 int i, j, k = 0; 1050 int n; 1051 char **attrs; 1052 ObjectClass *oc; 1053 1054 if ( anlist == NULL ) 1055 return NULL; 1056 1057 for ( i = 0; anlist[i].an_name.bv_val; i++ ) { 1058 if ( ( oc = anlist[i].an_oc ) ) { 1059 for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) ; 1060 k += j; 1061 for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) ; 1062 k += j; 1063 } 1064 } 1065 1066 if ( i == 0 ) 1067 return NULL; 1068 1069 attrs = anlist2charray( anlist, 1 ); 1070 1071 n = i; 1072 1073 if ( k ) 1074 attrs = (char **) ch_realloc( attrs, (i + k + 1) * sizeof( char * )); 1075 1076 for ( i = 0; anlist[i].an_name.bv_val; i++ ) { 1077 if ( ( oc = anlist[i].an_oc ) ) { 1078 for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) { 1079 attrs[n++] = ch_strdup( 1080 oc->soc_required[j]->sat_cname.bv_val ); 1081 } 1082 for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) { 1083 attrs[n++] = ch_strdup( 1084 oc->soc_allowed[j]->sat_cname.bv_val ); 1085 } 1086 } 1087 } 1088 1089 if ( attrs ) 1090 attrs[n] = NULL; 1091 1092 i = 0; 1093 while ( attrs && attrs[i] ) { 1094 if ( *attrs[i] == '@' ) { 1095 ch_free( attrs[i] ); 1096 for ( j = i; attrs[j]; j++ ) { 1097 attrs[j] = attrs[j+1]; 1098 } 1099 } else { 1100 i++; 1101 } 1102 } 1103 1104 for ( i = 0; attrs && attrs[i]; i++ ) { 1105 j = i + 1; 1106 while ( attrs && attrs[j] ) { 1107 if ( !strcmp( attrs[i], attrs[j] )) { 1108 ch_free( attrs[j] ); 1109 for ( k = j; attrs && attrs[k]; k++ ) { 1110 attrs[k] = attrs[k+1]; 1111 } 1112 } else { 1113 j++; 1114 } 1115 } 1116 } 1117 1118 if ( i != n ) 1119 attrs = (char **) ch_realloc( attrs, (i+1) * sizeof( char * )); 1120 1121 return attrs; 1122} 1123 1124#define LBUFSIZ 80 1125AttributeName* 1126file2anlist( AttributeName *an, const char *fname, const char *brkstr ) 1127{ 1128 FILE *fp; 1129 char *line = NULL; 1130 char *lcur = NULL; 1131 char *c; 1132 size_t lmax = LBUFSIZ; 1133 1134 fp = fopen( fname, "r" ); 1135 if ( fp == NULL ) { 1136 Debug( LDAP_DEBUG_ANY, 1137 "get_attrs_from_file: failed to open attribute list file " 1138 "\"%s\": %s\n", fname, strerror(errno), 0 ); 1139 return NULL; 1140 } 1141 1142 lcur = line = (char *) ch_malloc( lmax ); 1143 if ( !line ) { 1144 Debug( LDAP_DEBUG_ANY, 1145 "get_attrs_from_file: could not allocate memory\n", 1146 0, 0, 0 ); 1147 fclose(fp); 1148 return NULL; 1149 } 1150 1151 while ( fgets( lcur, LBUFSIZ, fp ) != NULL ) { 1152 if ( ( c = strchr( lcur, '\n' ) ) ) { 1153 if ( c == line ) { 1154 *c = '\0'; 1155 } else if ( *(c-1) == '\r' ) { 1156 *(c-1) = '\0'; 1157 } else { 1158 *c = '\0'; 1159 } 1160 } else { 1161 lmax += LBUFSIZ; 1162 line = (char *) ch_realloc( line, lmax ); 1163 if ( !line ) { 1164 Debug( LDAP_DEBUG_ANY, 1165 "get_attrs_from_file: could not allocate memory\n", 1166 0, 0, 0 ); 1167 fclose(fp); 1168 return NULL; 1169 } 1170 lcur = line + strlen( line ); 1171 continue; 1172 } 1173 an = str2anlist( an, line, brkstr ); 1174 if ( an == NULL ) 1175 break; 1176 lcur = line; 1177 } 1178 ch_free( line ); 1179 fclose(fp); 1180 return an; 1181} 1182#undef LBUFSIZ 1183 1184/* Define an attribute option. */ 1185int 1186ad_define_option( const char *name, const char *fname, int lineno ) 1187{ 1188 int i; 1189 unsigned int optlen; 1190 1191 if ( options == &lang_option ) { 1192 options = NULL; 1193 option_count = 0; 1194 } 1195 if ( name == NULL ) 1196 return 0; 1197 1198 optlen = 0; 1199 do { 1200 if ( !DESC_CHAR( name[optlen] ) ) { 1201 /* allow trailing '=', same as '-' */ 1202 if ( name[optlen] == '=' && !name[optlen+1] ) { 1203 msad_range_hack = 1; 1204 continue; 1205 } 1206 Debug( LDAP_DEBUG_ANY, 1207 "%s: line %d: illegal option name \"%s\"\n", 1208 fname, lineno, name ); 1209 return 1; 1210 } 1211 } while ( name[++optlen] ); 1212 1213 options = ch_realloc( options, 1214 (option_count+1) * sizeof(Attr_option) ); 1215 1216 if ( strcasecmp( name, "binary" ) == 0 1217 || ad_find_option_definition( name, optlen ) ) { 1218 Debug( LDAP_DEBUG_ANY, 1219 "%s: line %d: option \"%s\" is already defined\n", 1220 fname, lineno, name ); 1221 return 1; 1222 } 1223 1224 for ( i = option_count; i; --i ) { 1225 if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 ) 1226 break; 1227 options[i] = options[i-1]; 1228 } 1229 1230 options[i].name.bv_val = ch_strdup( name ); 1231 options[i].name.bv_len = optlen; 1232 options[i].prefix = (name[optlen-1] == '-') || 1233 (name[optlen-1] == '='); 1234 1235 if ( i != option_count && 1236 options[i].prefix && 1237 optlen < options[i+1].name.bv_len && 1238 strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) { 1239 Debug( LDAP_DEBUG_ANY, 1240 "%s: line %d: option \"%s\" overrides previous option\n", 1241 fname, lineno, name ); 1242 return 1; 1243 } 1244 1245 option_count++; 1246 return 0; 1247} 1248 1249void 1250ad_unparse_options( BerVarray *res ) 1251{ 1252 int i; 1253 for ( i = 0; i < option_count; i++ ) { 1254 value_add_one( res, &options[i].name ); 1255 } 1256} 1257 1258/* Find the definition of the option name or prefix matching the arguments */ 1259static Attr_option * 1260ad_find_option_definition( const char *opt, int optlen ) 1261{ 1262 int top = 0, bot = option_count; 1263 while ( top < bot ) { 1264 int mid = (top + bot) / 2; 1265 int mlen = options[mid].name.bv_len; 1266 char *mname = options[mid].name.bv_val; 1267 int j; 1268 if ( optlen < mlen ) { 1269 j = strncasecmp( opt, mname, optlen ) - 1; 1270 } else { 1271 j = strncasecmp( opt, mname, mlen ); 1272 if ( j==0 && (optlen==mlen || options[mid].prefix) ) 1273 return &options[mid]; 1274 } 1275 if ( j < 0 ) 1276 bot = mid; 1277 else 1278 top = mid + 1; 1279 } 1280 return NULL; 1281} 1282 1283MatchingRule *ad_mr( 1284 AttributeDescription *ad, 1285 unsigned usage ) 1286{ 1287 switch( usage & SLAP_MR_TYPE_MASK ) { 1288 case SLAP_MR_NONE: 1289 case SLAP_MR_EQUALITY: 1290 return ad->ad_type->sat_equality; 1291 break; 1292 case SLAP_MR_ORDERING: 1293 return ad->ad_type->sat_ordering; 1294 break; 1295 case SLAP_MR_SUBSTR: 1296 return ad->ad_type->sat_substr; 1297 break; 1298 case SLAP_MR_EXT: 1299 default: 1300 assert( 0 /* ad_mr: bad usage */); 1301 } 1302 return NULL; 1303} 1304