1 /* $NetBSD: filter.c,v 1.4 2025/09/05 21:16:21 christos Exp $ */ 2 3 /* search.c */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2024 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 /* Portions Copyright (c) 1990 Regents of the University of Michigan. 19 * All rights reserved. 20 */ 21 22 #include <sys/cdefs.h> 23 __RCSID("$NetBSD: filter.c,v 1.4 2025/09/05 21:16:21 christos Exp $"); 24 25 #include "portable.h" 26 27 #include <stdio.h> 28 29 #include <ac/stdlib.h> 30 31 #include <ac/socket.h> 32 #include <ac/string.h> 33 #include <ac/time.h> 34 35 #include "ldap-int.h" 36 37 static int put_simple_vrFilter LDAP_P(( 38 BerElement *ber, 39 char *str )); 40 41 static int put_vrFilter_list LDAP_P(( 42 BerElement *ber, 43 char *str )); 44 45 static char *put_complex_filter LDAP_P(( 46 BerElement *ber, 47 char *str, 48 ber_tag_t tag, 49 int not )); 50 51 static int put_simple_filter LDAP_P(( 52 BerElement *ber, 53 char *str )); 54 55 static int put_substring_filter LDAP_P(( 56 BerElement *ber, 57 char *type, 58 char *str, 59 char *nextstar )); 60 61 static int put_filter_list LDAP_P(( 62 BerElement *ber, 63 char *str, 64 ber_tag_t tag )); 65 66 static int ldap_is_oid ( const char *str ) 67 { 68 int i; 69 70 if( LDAP_ALPHA( str[0] )) { 71 for( i=1; str[i]; i++ ) { 72 if( !LDAP_LDH( str[i] )) { 73 return 0; 74 } 75 } 76 return 1; 77 78 } else if LDAP_DIGIT( str[0] ) { 79 int dot=0; 80 for( i=1; str[i]; i++ ) { 81 if( LDAP_DIGIT( str[i] )) { 82 dot=0; 83 84 } else if ( str[i] == '.' ) { 85 if( ++dot > 1 ) return 0; 86 87 } else { 88 return 0; 89 } 90 } 91 return !dot; 92 } 93 94 return 0; 95 } 96 97 static int ldap_is_desc ( const char *str ) 98 { 99 int i; 100 101 if( LDAP_ALPHA( str[0] )) { 102 for( i=1; str[i]; i++ ) { 103 if( str[i] == ';' ) { 104 str = &str[i+1]; 105 goto options; 106 } 107 108 if( !LDAP_LDH( str[i] )) { 109 return 0; 110 } 111 } 112 return 1; 113 114 } else if LDAP_DIGIT( str[0] ) { 115 int dot=0; 116 for( i=1; str[i]; i++ ) { 117 if( str[i] == ';' ) { 118 if( dot ) return 0; 119 str = &str[i+1]; 120 goto options; 121 } 122 123 if( LDAP_DIGIT( str[i] )) { 124 dot=0; 125 126 } else if ( str[i] == '.' ) { 127 if( ++dot > 1 ) return 0; 128 129 } else { 130 return 0; 131 } 132 } 133 return !dot; 134 } 135 136 return 0; 137 138 options: 139 if( !LDAP_LDH( str[0] )) { 140 return 0; 141 } 142 for( i=1; str[i]; i++ ) { 143 if( str[i] == ';' ) { 144 str = &str[i+1]; 145 goto options; 146 } 147 if( !LDAP_LDH( str[i] )) { 148 return 0; 149 } 150 } 151 return 1; 152 } 153 154 static char * 155 find_right_paren( char *s ) 156 { 157 int balance, escape; 158 159 balance = 1; 160 escape = 0; 161 while ( *s && balance ) { 162 if ( !escape ) { 163 if ( *s == '(' ) { 164 balance++; 165 } else if ( *s == ')' ) { 166 balance--; 167 } 168 } 169 170 escape = ( *s == '\\' && !escape ); 171 172 if ( balance ) s++; 173 } 174 175 return *s ? s : NULL; 176 } 177 178 static int hex2value( int c ) 179 { 180 if( c >= '0' && c <= '9' ) { 181 return c - '0'; 182 } 183 184 if( c >= 'A' && c <= 'F' ) { 185 return c + (10 - (int) 'A'); 186 } 187 188 if( c >= 'a' && c <= 'f' ) { 189 return c + (10 - (int) 'a'); 190 } 191 192 return -1; 193 } 194 195 char * 196 ldap_pvt_find_wildcard( const char *s ) 197 { 198 for( ; *s; s++ ) { 199 switch( *s ) { 200 case '*': /* found wildcard */ 201 return (char *) s; 202 203 case '(': 204 case ')': 205 return NULL; 206 207 case '\\': 208 if( s[1] == '\0' ) return NULL; 209 210 if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) { 211 s+=2; 212 213 } else switch( s[1] ) { 214 default: 215 return NULL; 216 217 /* allow RFC 1960 escapes */ 218 case '*': 219 case '(': 220 case ')': 221 case '\\': 222 s++; 223 } 224 } 225 } 226 227 return (char *) s; 228 } 229 230 /* unescape filter value */ 231 /* support both LDAP v2 and v3 escapes */ 232 /* output can include nul characters! */ 233 ber_slen_t 234 ldap_pvt_filter_value_unescape( char *fval ) 235 { 236 ber_slen_t r, v; 237 int v1, v2; 238 239 for( r=v=0; fval[v] != '\0'; v++ ) { 240 switch( fval[v] ) { 241 case '(': 242 case ')': 243 case '*': 244 return -1; 245 246 case '\\': 247 /* escape */ 248 v++; 249 250 if ( fval[v] == '\0' ) { 251 /* escape at end of string */ 252 return -1; 253 } 254 255 if (( v1 = hex2value( fval[v] )) >= 0 ) { 256 /* LDAPv3 escape */ 257 if (( v2 = hex2value( fval[v+1] )) < 0 ) { 258 /* must be two digit code */ 259 return -1; 260 } 261 262 fval[r++] = v1 * 16 + v2; 263 v++; 264 265 } else { 266 /* LDAPv2 escape */ 267 switch( fval[v] ) { 268 case '(': 269 case ')': 270 case '*': 271 case '\\': 272 fval[r++] = fval[v]; 273 break; 274 default: 275 /* illegal escape */ 276 return -1; 277 } 278 } 279 break; 280 281 default: 282 fval[r++] = fval[v]; 283 } 284 } 285 286 fval[r] = '\0'; 287 return r; 288 } 289 290 static char * 291 put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not ) 292 { 293 char *next; 294 295 /* 296 * We have (x(filter)...) with str sitting on 297 * the x. We have to find the paren matching 298 * the one before the x and put the intervening 299 * filters by calling put_filter_list(). 300 */ 301 302 /* put explicit tag */ 303 if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) { 304 return NULL; 305 } 306 307 str++; 308 if ( (next = find_right_paren( str )) == NULL ) { 309 return NULL; 310 } 311 312 *next = '\0'; 313 if ( put_filter_list( ber, str, tag ) == -1 ) { 314 return NULL; 315 } 316 317 /* close the '(' */ 318 *next++ = ')'; 319 320 /* flush explicit tagged thang */ 321 if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { 322 return NULL; 323 } 324 325 return next; 326 } 327 328 int 329 ldap_pvt_put_filter( BerElement *ber, const char *str_in ) 330 { 331 int rc; 332 char *freeme; 333 char *str; 334 char *next; 335 int parens, balance, escape; 336 337 /* 338 * A Filter looks like this (RFC 4511 as extended by RFC 4526): 339 * Filter ::= CHOICE { 340 * and [0] SET SIZE (0..MAX) OF filter Filter, 341 * or [1] SET SIZE (0..MAX) OF filter Filter, 342 * not [2] Filter, 343 * equalityMatch [3] AttributeValueAssertion, 344 * substrings [4] SubstringFilter, 345 * greaterOrEqual [5] AttributeValueAssertion, 346 * lessOrEqual [6] AttributeValueAssertion, 347 * present [7] AttributeDescription, 348 * approxMatch [8] AttributeValueAssertion, 349 * extensibleMatch [9] MatchingRuleAssertion, 350 * ... } 351 * 352 * SubstringFilter ::= SEQUENCE { 353 * type AttributeDescription, 354 * substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { 355 * initial [0] AssertionValue, -- only once 356 * any [1] AssertionValue, 357 * final [2] AssertionValue -- only once 358 * } 359 * } 360 * 361 * MatchingRuleAssertion ::= SEQUENCE { 362 * matchingRule [1] MatchingRuleId OPTIONAL, 363 * type [2] AttributeDescription OPTIONAL, 364 * matchValue [3] AssertionValue, 365 * dnAttributes [4] BOOLEAN DEFAULT FALSE } 366 * 367 * Note: tags in a CHOICE are always explicit 368 */ 369 370 Debug1( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in ); 371 372 freeme = LDAP_STRDUP( str_in ); 373 if( freeme == NULL ) return LDAP_NO_MEMORY; 374 str = freeme; 375 376 parens = 0; 377 while ( *str ) { 378 switch ( *str ) { 379 case '(': /*')'*/ 380 str++; 381 parens++; 382 383 /* skip spaces */ 384 while( LDAP_SPACE( *str ) ) str++; 385 386 switch ( *str ) { 387 case '&': 388 Debug0( LDAP_DEBUG_TRACE, "put_filter: AND\n" ); 389 390 str = put_complex_filter( ber, str, 391 LDAP_FILTER_AND, 0 ); 392 if( str == NULL ) { 393 rc = -1; 394 goto done; 395 } 396 397 parens--; 398 break; 399 400 case '|': 401 Debug0( LDAP_DEBUG_TRACE, "put_filter: OR\n" ); 402 403 str = put_complex_filter( ber, str, 404 LDAP_FILTER_OR, 0 ); 405 if( str == NULL ) { 406 rc = -1; 407 goto done; 408 } 409 410 parens--; 411 break; 412 413 case '!': 414 Debug0( LDAP_DEBUG_TRACE, "put_filter: NOT\n" ); 415 416 str = put_complex_filter( ber, str, 417 LDAP_FILTER_NOT, 0 ); 418 if( str == NULL ) { 419 rc = -1; 420 goto done; 421 } 422 423 parens--; 424 break; 425 426 case '(': 427 rc = -1; 428 goto done; 429 430 default: 431 Debug0( LDAP_DEBUG_TRACE, "put_filter: simple\n" ); 432 433 balance = 1; 434 escape = 0; 435 next = str; 436 437 while ( *next && balance ) { 438 if ( escape == 0 ) { 439 if ( *next == '(' ) { 440 balance++; 441 } else if ( *next == ')' ) { 442 balance--; 443 } 444 } 445 446 if ( *next == '\\' && ! escape ) { 447 escape = 1; 448 } else { 449 escape = 0; 450 } 451 452 if ( balance ) next++; 453 } 454 455 if ( balance != 0 ) { 456 rc = -1; 457 goto done; 458 } 459 460 *next = '\0'; 461 462 if ( put_simple_filter( ber, str ) == -1 ) { 463 rc = -1; 464 goto done; 465 } 466 467 *next++ = /*'('*/ ')'; 468 469 str = next; 470 parens--; 471 break; 472 } 473 break; 474 475 case /*'('*/ ')': 476 Debug0( LDAP_DEBUG_TRACE, "put_filter: end\n" ); 477 if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { 478 rc = -1; 479 goto done; 480 } 481 str++; 482 parens--; 483 break; 484 485 case ' ': 486 str++; 487 break; 488 489 default: /* assume it's a simple type=value filter */ 490 Debug0( LDAP_DEBUG_TRACE, "put_filter: default\n" ); 491 next = strchr( str, '\0' ); 492 if ( put_simple_filter( ber, str ) == -1 ) { 493 rc = -1; 494 goto done; 495 } 496 str = next; 497 break; 498 } 499 if ( !parens ) 500 break; 501 } 502 503 rc = ( parens || *str ) ? -1 : 0; 504 505 done: 506 LDAP_FREE( freeme ); 507 return rc; 508 } 509 510 /* 511 * Put a list of filters like this "(filter1)(filter2)..." 512 */ 513 514 static int 515 put_filter_list( BerElement *ber, char *str, ber_tag_t tag ) 516 { 517 char *next = NULL; 518 char save; 519 520 Debug1( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", 521 str ); 522 523 while ( *str ) { 524 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { 525 str++; 526 } 527 if ( *str == '\0' ) break; 528 529 if ( (next = find_right_paren( str + 1 )) == NULL ) { 530 return -1; 531 } 532 save = *++next; 533 534 /* now we have "(filter)" with str pointing to it */ 535 *next = '\0'; 536 if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1; 537 *next = save; 538 str = next; 539 540 if( tag == LDAP_FILTER_NOT ) break; 541 } 542 543 if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) { 544 return -1; 545 } 546 547 return 0; 548 } 549 550 static int 551 put_simple_filter( 552 BerElement *ber, 553 char *str ) 554 { 555 char *s; 556 char *value; 557 ber_tag_t ftype; 558 int rc = -1; 559 560 Debug1( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n", 561 str ); 562 563 if ( str[0] == '=' ) return -1; 564 565 str = LDAP_STRDUP( str ); 566 if( str == NULL ) return -1; 567 568 if ( (s = strchr( str, '=' )) == NULL ) { 569 goto done; 570 } 571 572 value = s + 1; 573 *s-- = '\0'; 574 575 switch ( *s ) { 576 case '<': 577 ftype = LDAP_FILTER_LE; 578 *s = '\0'; 579 break; 580 581 case '>': 582 ftype = LDAP_FILTER_GE; 583 *s = '\0'; 584 break; 585 586 case '~': 587 ftype = LDAP_FILTER_APPROX; 588 *s = '\0'; 589 break; 590 591 case ':': 592 /* RFC 4515 extensible filters are off the form: 593 * type [:dn] [:rule] := value 594 * or [:dn]:rule := value 595 */ 596 ftype = LDAP_FILTER_EXT; 597 *s = '\0'; 598 599 { 600 char *dn = strchr( str, ':' ); 601 char *rule = NULL; 602 603 if( dn != NULL ) { 604 *dn++ = '\0'; 605 rule = strchr( dn, ':' ); 606 607 if( rule == NULL ) { 608 /* one colon */ 609 if ( strcasecmp(dn, "dn") == 0 ) { 610 /* must have attribute */ 611 if( !ldap_is_desc( str ) ) { 612 goto done; 613 } 614 615 rule = ""; 616 617 } else { 618 rule = dn; 619 dn = NULL; 620 } 621 622 } else { 623 /* two colons */ 624 *rule++ = '\0'; 625 626 if ( strcasecmp(dn, "dn") != 0 ) { 627 /* must have "dn" */ 628 goto done; 629 } 630 } 631 632 } 633 634 if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { 635 /* must have either type or rule */ 636 goto done; 637 } 638 639 if ( *str != '\0' && !ldap_is_desc( str ) ) { 640 goto done; 641 } 642 643 if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { 644 goto done; 645 } 646 647 rc = ber_printf( ber, "t{" /*"}"*/, ftype ); 648 649 if( rc != -1 && rule && *rule != '\0' ) { 650 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); 651 } 652 653 if( rc != -1 && *str != '\0' ) { 654 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); 655 } 656 657 if( rc != -1 ) { 658 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 659 660 if( len >= 0 ) { 661 rc = ber_printf( ber, "to", 662 LDAP_FILTER_EXT_VALUE, value, len ); 663 } else { 664 rc = -1; 665 } 666 } 667 668 if( rc != -1 && dn ) { 669 rc = ber_printf( ber, "tb", 670 LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 ); 671 } 672 673 if( rc != -1 ) { 674 rc = ber_printf( ber, /*"{"*/ "N}" ); 675 } 676 } 677 goto done; 678 679 default: 680 if( !ldap_is_desc( str ) ) { 681 goto done; 682 683 } else { 684 char *nextstar = ldap_pvt_find_wildcard( value ); 685 686 if ( nextstar == NULL ) { 687 goto done; 688 689 } else if ( *nextstar == '\0' ) { 690 ftype = LDAP_FILTER_EQUALITY; 691 692 } else if ( strcmp( value, "*" ) == 0 ) { 693 ftype = LDAP_FILTER_PRESENT; 694 695 } else { 696 rc = put_substring_filter( ber, str, value, nextstar ); 697 goto done; 698 } 699 } break; 700 } 701 702 if( !ldap_is_desc( str ) ) goto done; 703 704 if ( ftype == LDAP_FILTER_PRESENT ) { 705 rc = ber_printf( ber, "ts", ftype, str ); 706 707 } else { 708 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 709 710 if( len >= 0 ) { 711 rc = ber_printf( ber, "t{soN}", 712 ftype, str, value, len ); 713 } 714 } 715 716 done: 717 if( rc != -1 ) rc = 0; 718 LDAP_FREE( str ); 719 return rc; 720 } 721 722 static int 723 put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar ) 724 { 725 int gotstar = 0; 726 ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS; 727 728 Debug2( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", 729 type, val ); 730 731 if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) { 732 return -1; 733 } 734 735 for( ; *val; val=nextstar ) { 736 if ( gotstar ) 737 nextstar = ldap_pvt_find_wildcard( val ); 738 739 if ( nextstar == NULL ) { 740 return -1; 741 } 742 743 if ( *nextstar == '\0' ) { 744 ftype = LDAP_SUBSTRING_FINAL; 745 } else { 746 *nextstar++ = '\0'; 747 if ( gotstar++ == 0 ) { 748 ftype = LDAP_SUBSTRING_INITIAL; 749 } else { 750 ftype = LDAP_SUBSTRING_ANY; 751 } 752 } 753 754 if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) { 755 ber_slen_t len = ldap_pvt_filter_value_unescape( val ); 756 757 if ( len <= 0 ) { 758 return -1; 759 } 760 761 if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) { 762 return -1; 763 } 764 } 765 } 766 767 if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) { 768 return -1; 769 } 770 771 return 0; 772 } 773 774 static int 775 put_vrFilter( BerElement *ber, const char *str_in ) 776 { 777 int rc; 778 char *freeme; 779 char *str; 780 char *next; 781 int parens, balance, escape; 782 783 /* 784 * A ValuesReturnFilter looks like this: 785 * 786 * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem 787 * SimpleFilterItem ::= CHOICE { 788 * equalityMatch [3] AttributeValueAssertion, 789 * substrings [4] SubstringFilter, 790 * greaterOrEqual [5] AttributeValueAssertion, 791 * lessOrEqual [6] AttributeValueAssertion, 792 * present [7] AttributeType, 793 * approxMatch [8] AttributeValueAssertion, 794 * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3 795 * } 796 * 797 * SubstringFilter ::= SEQUENCE { 798 * type AttributeType, 799 * SEQUENCE OF CHOICE { 800 * initial [0] IA5String, 801 * any [1] IA5String, 802 * final [2] IA5String 803 * } 804 * } 805 * 806 * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3 807 * matchingRule [1] MatchingRuleId OPTIONAL, 808 * type [2] AttributeDescription OPTIONAL, 809 * matchValue [3] AssertionValue } 810 * 811 * (Source: RFC 3876) 812 */ 813 814 Debug1( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in ); 815 816 freeme = LDAP_STRDUP( str_in ); 817 if( freeme == NULL ) return LDAP_NO_MEMORY; 818 str = freeme; 819 820 parens = 0; 821 while ( *str ) { 822 switch ( *str ) { 823 case '(': /*')'*/ 824 str++; 825 parens++; 826 827 /* skip spaces */ 828 while( LDAP_SPACE( *str ) ) str++; 829 830 switch ( *str ) { 831 case '(': 832 if ( (next = find_right_paren( str )) == NULL ) { 833 rc = -1; 834 goto done; 835 } 836 837 *next = '\0'; 838 839 if ( put_vrFilter_list( ber, str ) == -1 ) { 840 rc = -1; 841 goto done; 842 } 843 844 /* close the '(' */ 845 *next++ = ')'; 846 847 str = next; 848 849 parens--; 850 break; 851 852 853 default: 854 Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n" ); 855 856 balance = 1; 857 escape = 0; 858 next = str; 859 860 while ( *next && balance ) { 861 if ( escape == 0 ) { 862 if ( *next == '(' ) { 863 balance++; 864 } else if ( *next == ')' ) { 865 balance--; 866 } 867 } 868 869 if ( *next == '\\' && ! escape ) { 870 escape = 1; 871 } else { 872 escape = 0; 873 } 874 875 if ( balance ) next++; 876 } 877 878 if ( balance != 0 ) { 879 rc = -1; 880 goto done; 881 } 882 883 *next = '\0'; 884 885 if ( put_simple_vrFilter( ber, str ) == -1 ) { 886 rc = -1; 887 goto done; 888 } 889 890 *next++ = /*'('*/ ')'; 891 892 str = next; 893 parens--; 894 break; 895 } 896 break; 897 898 case /*'('*/ ')': 899 Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: end\n" ); 900 if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { 901 rc = -1; 902 goto done; 903 } 904 str++; 905 parens--; 906 break; 907 908 case ' ': 909 str++; 910 break; 911 912 default: /* assume it's a simple type=value filter */ 913 Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: default\n" ); 914 next = strchr( str, '\0' ); 915 if ( put_simple_vrFilter( ber, str ) == -1 ) { 916 rc = -1; 917 goto done; 918 } 919 str = next; 920 break; 921 } 922 } 923 924 rc = parens ? -1 : 0; 925 926 done: 927 LDAP_FREE( freeme ); 928 return rc; 929 } 930 931 int 932 ldap_put_vrFilter( BerElement *ber, const char *str_in ) 933 { 934 int rc =0; 935 936 if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { 937 return -1; 938 } 939 940 rc = put_vrFilter( ber, str_in ); 941 942 if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { 943 rc = -1; 944 } 945 946 return rc; 947 } 948 949 static int 950 put_vrFilter_list( BerElement *ber, char *str ) 951 { 952 char *next = NULL; 953 char save; 954 955 Debug1( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", 956 str ); 957 958 while ( *str ) { 959 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { 960 str++; 961 } 962 if ( *str == '\0' ) break; 963 964 if ( (next = find_right_paren( str + 1 )) == NULL ) { 965 return -1; 966 } 967 save = *++next; 968 969 /* now we have "(filter)" with str pointing to it */ 970 *next = '\0'; 971 if ( put_vrFilter( ber, str ) == -1 ) return -1; 972 *next = save; 973 str = next; 974 } 975 976 return 0; 977 } 978 979 static int 980 put_simple_vrFilter( 981 BerElement *ber, 982 char *str ) 983 { 984 char *s; 985 char *value; 986 ber_tag_t ftype; 987 int rc = -1; 988 989 Debug1( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", 990 str ); 991 992 str = LDAP_STRDUP( str ); 993 if( str == NULL ) return -1; 994 995 if ( (s = strchr( str, '=' )) == NULL ) { 996 goto done; 997 } 998 999 value = s + 1; 1000 *s-- = '\0'; 1001 1002 switch ( *s ) { 1003 case '<': 1004 ftype = LDAP_FILTER_LE; 1005 *s = '\0'; 1006 break; 1007 1008 case '>': 1009 ftype = LDAP_FILTER_GE; 1010 *s = '\0'; 1011 break; 1012 1013 case '~': 1014 ftype = LDAP_FILTER_APPROX; 1015 *s = '\0'; 1016 break; 1017 1018 case ':': 1019 /* According to ValuesReturnFilter control definition 1020 * extensible filters are off the form: 1021 * type [:rule] := value 1022 * or :rule := value 1023 */ 1024 ftype = LDAP_FILTER_EXT; 1025 *s = '\0'; 1026 1027 { 1028 char *rule = strchr( str, ':' ); 1029 1030 if( rule == NULL ) { 1031 /* must have attribute */ 1032 if( !ldap_is_desc( str ) ) { 1033 goto done; 1034 } 1035 rule = ""; 1036 } else { 1037 *rule++ = '\0'; 1038 } 1039 1040 if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { 1041 /* must have either type or rule */ 1042 goto done; 1043 } 1044 1045 if ( *str != '\0' && !ldap_is_desc( str ) ) { 1046 goto done; 1047 } 1048 1049 if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { 1050 goto done; 1051 } 1052 1053 rc = ber_printf( ber, "t{" /*"}"*/, ftype ); 1054 1055 if( rc != -1 && rule && *rule != '\0' ) { 1056 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); 1057 } 1058 1059 if( rc != -1 && *str != '\0' ) { 1060 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); 1061 } 1062 1063 if( rc != -1 ) { 1064 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 1065 1066 if( len >= 0 ) { 1067 rc = ber_printf( ber, "to", 1068 LDAP_FILTER_EXT_VALUE, value, len ); 1069 } else { 1070 rc = -1; 1071 } 1072 } 1073 1074 if( rc != -1 ) { 1075 rc = ber_printf( ber, /*"{"*/ "N}" ); 1076 } 1077 } 1078 goto done; 1079 1080 default: 1081 if( !ldap_is_desc( str ) ) { 1082 goto done; 1083 1084 } else { 1085 char *nextstar = ldap_pvt_find_wildcard( value ); 1086 1087 if ( nextstar == NULL ) { 1088 goto done; 1089 1090 } else if ( *nextstar == '\0' ) { 1091 ftype = LDAP_FILTER_EQUALITY; 1092 1093 } else if ( strcmp( value, "*" ) == 0 ) { 1094 ftype = LDAP_FILTER_PRESENT; 1095 1096 } else { 1097 rc = put_substring_filter( ber, str, value, nextstar ); 1098 goto done; 1099 } 1100 } break; 1101 } 1102 1103 if( !ldap_is_desc( str ) ) goto done; 1104 1105 if ( ftype == LDAP_FILTER_PRESENT ) { 1106 rc = ber_printf( ber, "ts", ftype, str ); 1107 1108 } else { 1109 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 1110 1111 if( len >= 0 ) { 1112 rc = ber_printf( ber, "t{soN}", 1113 ftype, str, value, len ); 1114 } 1115 } 1116 1117 done: 1118 if( rc != -1 ) rc = 0; 1119 LDAP_FREE( str ); 1120 return rc; 1121 } 1122 1123