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