1/* $NetBSD: filter.c,v 1.3 2021/08/14 16:14:56 christos Exp $ */ 2 3/* search.c */ 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/* 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.3 2021/08/14 16:14:56 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 37static int put_simple_vrFilter LDAP_P(( 38 BerElement *ber, 39 char *str )); 40 41static int put_vrFilter_list LDAP_P(( 42 BerElement *ber, 43 char *str )); 44 45static char *put_complex_filter LDAP_P(( 46 BerElement *ber, 47 char *str, 48 ber_tag_t tag, 49 int not )); 50 51static int put_simple_filter LDAP_P(( 52 BerElement *ber, 53 char *str )); 54 55static int put_substring_filter LDAP_P(( 56 BerElement *ber, 57 char *type, 58 char *str, 59 char *nextstar )); 60 61static int put_filter_list LDAP_P(( 62 BerElement *ber, 63 char *str, 64 ber_tag_t tag )); 65 66static 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 97static 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 138options: 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 154static char * 155find_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 178static 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 195char * 196ldap_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! */ 233ber_slen_t 234ldap_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 290static char * 291put_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 328int 329ldap_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 505done: 506 LDAP_FREE( freeme ); 507 return rc; 508} 509 510/* 511 * Put a list of filters like this "(filter1)(filter2)..." 512 */ 513 514static int 515put_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 550static int 551put_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 str = LDAP_STRDUP( str ); 564 if( str == NULL ) return -1; 565 566 if ( (s = strchr( str, '=' )) == NULL ) { 567 goto done; 568 } 569 570 value = s + 1; 571 *s-- = '\0'; 572 573 switch ( *s ) { 574 case '<': 575 ftype = LDAP_FILTER_LE; 576 *s = '\0'; 577 break; 578 579 case '>': 580 ftype = LDAP_FILTER_GE; 581 *s = '\0'; 582 break; 583 584 case '~': 585 ftype = LDAP_FILTER_APPROX; 586 *s = '\0'; 587 break; 588 589 case ':': 590 /* RFC 4515 extensible filters are off the form: 591 * type [:dn] [:rule] := value 592 * or [:dn]:rule := value 593 */ 594 ftype = LDAP_FILTER_EXT; 595 *s = '\0'; 596 597 { 598 char *dn = strchr( str, ':' ); 599 char *rule = NULL; 600 601 if( dn != NULL ) { 602 *dn++ = '\0'; 603 rule = strchr( dn, ':' ); 604 605 if( rule == NULL ) { 606 /* one colon */ 607 if ( strcasecmp(dn, "dn") == 0 ) { 608 /* must have attribute */ 609 if( !ldap_is_desc( str ) ) { 610 goto done; 611 } 612 613 rule = ""; 614 615 } else { 616 rule = dn; 617 dn = NULL; 618 } 619 620 } else { 621 /* two colons */ 622 *rule++ = '\0'; 623 624 if ( strcasecmp(dn, "dn") != 0 ) { 625 /* must have "dn" */ 626 goto done; 627 } 628 } 629 630 } 631 632 if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { 633 /* must have either type or rule */ 634 goto done; 635 } 636 637 if ( *str != '\0' && !ldap_is_desc( str ) ) { 638 goto done; 639 } 640 641 if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { 642 goto done; 643 } 644 645 rc = ber_printf( ber, "t{" /*"}"*/, ftype ); 646 647 if( rc != -1 && rule && *rule != '\0' ) { 648 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); 649 } 650 651 if( rc != -1 && *str != '\0' ) { 652 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); 653 } 654 655 if( rc != -1 ) { 656 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 657 658 if( len >= 0 ) { 659 rc = ber_printf( ber, "to", 660 LDAP_FILTER_EXT_VALUE, value, len ); 661 } else { 662 rc = -1; 663 } 664 } 665 666 if( rc != -1 && dn ) { 667 rc = ber_printf( ber, "tb", 668 LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 ); 669 } 670 671 if( rc != -1 ) { 672 rc = ber_printf( ber, /*"{"*/ "N}" ); 673 } 674 } 675 goto done; 676 677 default: 678 if( !ldap_is_desc( str ) ) { 679 goto done; 680 681 } else { 682 char *nextstar = ldap_pvt_find_wildcard( value ); 683 684 if ( nextstar == NULL ) { 685 goto done; 686 687 } else if ( *nextstar == '\0' ) { 688 ftype = LDAP_FILTER_EQUALITY; 689 690 } else if ( strcmp( value, "*" ) == 0 ) { 691 ftype = LDAP_FILTER_PRESENT; 692 693 } else { 694 rc = put_substring_filter( ber, str, value, nextstar ); 695 goto done; 696 } 697 } break; 698 } 699 700 if( !ldap_is_desc( str ) ) goto done; 701 702 if ( ftype == LDAP_FILTER_PRESENT ) { 703 rc = ber_printf( ber, "ts", ftype, str ); 704 705 } else { 706 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 707 708 if( len >= 0 ) { 709 rc = ber_printf( ber, "t{soN}", 710 ftype, str, value, len ); 711 } 712 } 713 714done: 715 if( rc != -1 ) rc = 0; 716 LDAP_FREE( str ); 717 return rc; 718} 719 720static int 721put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar ) 722{ 723 int gotstar = 0; 724 ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS; 725 726 Debug2( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", 727 type, val ); 728 729 if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) { 730 return -1; 731 } 732 733 for( ; *val; val=nextstar ) { 734 if ( gotstar ) 735 nextstar = ldap_pvt_find_wildcard( val ); 736 737 if ( nextstar == NULL ) { 738 return -1; 739 } 740 741 if ( *nextstar == '\0' ) { 742 ftype = LDAP_SUBSTRING_FINAL; 743 } else { 744 *nextstar++ = '\0'; 745 if ( gotstar++ == 0 ) { 746 ftype = LDAP_SUBSTRING_INITIAL; 747 } else { 748 ftype = LDAP_SUBSTRING_ANY; 749 } 750 } 751 752 if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) { 753 ber_slen_t len = ldap_pvt_filter_value_unescape( val ); 754 755 if ( len <= 0 ) { 756 return -1; 757 } 758 759 if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) { 760 return -1; 761 } 762 } 763 } 764 765 if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) { 766 return -1; 767 } 768 769 return 0; 770} 771 772static int 773put_vrFilter( BerElement *ber, const char *str_in ) 774{ 775 int rc; 776 char *freeme; 777 char *str; 778 char *next; 779 int parens, balance, escape; 780 781 /* 782 * A ValuesReturnFilter looks like this: 783 * 784 * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem 785 * SimpleFilterItem ::= CHOICE { 786 * equalityMatch [3] AttributeValueAssertion, 787 * substrings [4] SubstringFilter, 788 * greaterOrEqual [5] AttributeValueAssertion, 789 * lessOrEqual [6] AttributeValueAssertion, 790 * present [7] AttributeType, 791 * approxMatch [8] AttributeValueAssertion, 792 * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3 793 * } 794 * 795 * SubstringFilter ::= SEQUENCE { 796 * type AttributeType, 797 * SEQUENCE OF CHOICE { 798 * initial [0] IA5String, 799 * any [1] IA5String, 800 * final [2] IA5String 801 * } 802 * } 803 * 804 * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3 805 * matchingRule [1] MatchingRuleId OPTIONAL, 806 * type [2] AttributeDescription OPTIONAL, 807 * matchValue [3] AssertionValue } 808 * 809 * (Source: RFC 3876) 810 */ 811 812 Debug1( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in ); 813 814 freeme = LDAP_STRDUP( str_in ); 815 if( freeme == NULL ) return LDAP_NO_MEMORY; 816 str = freeme; 817 818 parens = 0; 819 while ( *str ) { 820 switch ( *str ) { 821 case '(': /*')'*/ 822 str++; 823 parens++; 824 825 /* skip spaces */ 826 while( LDAP_SPACE( *str ) ) str++; 827 828 switch ( *str ) { 829 case '(': 830 if ( (next = find_right_paren( str )) == NULL ) { 831 rc = -1; 832 goto done; 833 } 834 835 *next = '\0'; 836 837 if ( put_vrFilter_list( ber, str ) == -1 ) { 838 rc = -1; 839 goto done; 840 } 841 842 /* close the '(' */ 843 *next++ = ')'; 844 845 str = next; 846 847 parens--; 848 break; 849 850 851 default: 852 Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n" ); 853 854 balance = 1; 855 escape = 0; 856 next = str; 857 858 while ( *next && balance ) { 859 if ( escape == 0 ) { 860 if ( *next == '(' ) { 861 balance++; 862 } else if ( *next == ')' ) { 863 balance--; 864 } 865 } 866 867 if ( *next == '\\' && ! escape ) { 868 escape = 1; 869 } else { 870 escape = 0; 871 } 872 873 if ( balance ) next++; 874 } 875 876 if ( balance != 0 ) { 877 rc = -1; 878 goto done; 879 } 880 881 *next = '\0'; 882 883 if ( put_simple_vrFilter( ber, str ) == -1 ) { 884 rc = -1; 885 goto done; 886 } 887 888 *next++ = /*'('*/ ')'; 889 890 str = next; 891 parens--; 892 break; 893 } 894 break; 895 896 case /*'('*/ ')': 897 Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: end\n" ); 898 if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { 899 rc = -1; 900 goto done; 901 } 902 str++; 903 parens--; 904 break; 905 906 case ' ': 907 str++; 908 break; 909 910 default: /* assume it's a simple type=value filter */ 911 Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: default\n" ); 912 next = strchr( str, '\0' ); 913 if ( put_simple_vrFilter( ber, str ) == -1 ) { 914 rc = -1; 915 goto done; 916 } 917 str = next; 918 break; 919 } 920 } 921 922 rc = parens ? -1 : 0; 923 924done: 925 LDAP_FREE( freeme ); 926 return rc; 927} 928 929int 930ldap_put_vrFilter( BerElement *ber, const char *str_in ) 931{ 932 int rc =0; 933 934 if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { 935 return -1; 936 } 937 938 rc = put_vrFilter( ber, str_in ); 939 940 if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { 941 rc = -1; 942 } 943 944 return rc; 945} 946 947static int 948put_vrFilter_list( BerElement *ber, char *str ) 949{ 950 char *next = NULL; 951 char save; 952 953 Debug1( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", 954 str ); 955 956 while ( *str ) { 957 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { 958 str++; 959 } 960 if ( *str == '\0' ) break; 961 962 if ( (next = find_right_paren( str + 1 )) == NULL ) { 963 return -1; 964 } 965 save = *++next; 966 967 /* now we have "(filter)" with str pointing to it */ 968 *next = '\0'; 969 if ( put_vrFilter( ber, str ) == -1 ) return -1; 970 *next = save; 971 str = next; 972 } 973 974 return 0; 975} 976 977static int 978put_simple_vrFilter( 979 BerElement *ber, 980 char *str ) 981{ 982 char *s; 983 char *value; 984 ber_tag_t ftype; 985 int rc = -1; 986 987 Debug1( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", 988 str ); 989 990 str = LDAP_STRDUP( str ); 991 if( str == NULL ) return -1; 992 993 if ( (s = strchr( str, '=' )) == NULL ) { 994 goto done; 995 } 996 997 value = s + 1; 998 *s-- = '\0'; 999 1000 switch ( *s ) { 1001 case '<': 1002 ftype = LDAP_FILTER_LE; 1003 *s = '\0'; 1004 break; 1005 1006 case '>': 1007 ftype = LDAP_FILTER_GE; 1008 *s = '\0'; 1009 break; 1010 1011 case '~': 1012 ftype = LDAP_FILTER_APPROX; 1013 *s = '\0'; 1014 break; 1015 1016 case ':': 1017 /* According to ValuesReturnFilter control definition 1018 * extensible filters are off the form: 1019 * type [:rule] := value 1020 * or :rule := value 1021 */ 1022 ftype = LDAP_FILTER_EXT; 1023 *s = '\0'; 1024 1025 { 1026 char *rule = strchr( str, ':' ); 1027 1028 if( rule == NULL ) { 1029 /* must have attribute */ 1030 if( !ldap_is_desc( str ) ) { 1031 goto done; 1032 } 1033 rule = ""; 1034 } else { 1035 *rule++ = '\0'; 1036 } 1037 1038 if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { 1039 /* must have either type or rule */ 1040 goto done; 1041 } 1042 1043 if ( *str != '\0' && !ldap_is_desc( str ) ) { 1044 goto done; 1045 } 1046 1047 if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { 1048 goto done; 1049 } 1050 1051 rc = ber_printf( ber, "t{" /*"}"*/, ftype ); 1052 1053 if( rc != -1 && rule && *rule != '\0' ) { 1054 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); 1055 } 1056 1057 if( rc != -1 && *str != '\0' ) { 1058 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); 1059 } 1060 1061 if( rc != -1 ) { 1062 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 1063 1064 if( len >= 0 ) { 1065 rc = ber_printf( ber, "to", 1066 LDAP_FILTER_EXT_VALUE, value, len ); 1067 } else { 1068 rc = -1; 1069 } 1070 } 1071 1072 if( rc != -1 ) { 1073 rc = ber_printf( ber, /*"{"*/ "N}" ); 1074 } 1075 } 1076 goto done; 1077 1078 default: 1079 if( !ldap_is_desc( str ) ) { 1080 goto done; 1081 1082 } else { 1083 char *nextstar = ldap_pvt_find_wildcard( value ); 1084 1085 if ( nextstar == NULL ) { 1086 goto done; 1087 1088 } else if ( *nextstar == '\0' ) { 1089 ftype = LDAP_FILTER_EQUALITY; 1090 1091 } else if ( strcmp( value, "*" ) == 0 ) { 1092 ftype = LDAP_FILTER_PRESENT; 1093 1094 } else { 1095 rc = put_substring_filter( ber, str, value, nextstar ); 1096 goto done; 1097 } 1098 } break; 1099 } 1100 1101 if( !ldap_is_desc( str ) ) goto done; 1102 1103 if ( ftype == LDAP_FILTER_PRESENT ) { 1104 rc = ber_printf( ber, "ts", ftype, str ); 1105 1106 } else { 1107 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 1108 1109 if( len >= 0 ) { 1110 rc = ber_printf( ber, "t{soN}", 1111 ftype, str, value, len ); 1112 } 1113 } 1114 1115done: 1116 if( rc != -1 ) rc = 0; 1117 LDAP_FREE( str ); 1118 return rc; 1119} 1120 1121