filter.c revision 1.1.1.7
1/* $NetBSD: filter.c,v 1.1.1.7 2019/08/08 13:31:15 christos Exp $ */ 2 3/* search.c */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2019 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.1.1.7 2019/08/08 13:31:15 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 Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 ); 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 Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n", 389 0, 0, 0 ); 390 391 str = put_complex_filter( ber, str, 392 LDAP_FILTER_AND, 0 ); 393 if( str == NULL ) { 394 rc = -1; 395 goto done; 396 } 397 398 parens--; 399 break; 400 401 case '|': 402 Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n", 403 0, 0, 0 ); 404 405 str = put_complex_filter( ber, str, 406 LDAP_FILTER_OR, 0 ); 407 if( str == NULL ) { 408 rc = -1; 409 goto done; 410 } 411 412 parens--; 413 break; 414 415 case '!': 416 Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n", 417 0, 0, 0 ); 418 419 str = put_complex_filter( ber, str, 420 LDAP_FILTER_NOT, 0 ); 421 if( str == NULL ) { 422 rc = -1; 423 goto done; 424 } 425 426 parens--; 427 break; 428 429 case '(': 430 rc = -1; 431 goto done; 432 433 default: 434 Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n", 435 0, 0, 0 ); 436 437 balance = 1; 438 escape = 0; 439 next = str; 440 441 while ( *next && balance ) { 442 if ( escape == 0 ) { 443 if ( *next == '(' ) { 444 balance++; 445 } else if ( *next == ')' ) { 446 balance--; 447 } 448 } 449 450 if ( *next == '\\' && ! escape ) { 451 escape = 1; 452 } else { 453 escape = 0; 454 } 455 456 if ( balance ) next++; 457 } 458 459 if ( balance != 0 ) { 460 rc = -1; 461 goto done; 462 } 463 464 *next = '\0'; 465 466 if ( put_simple_filter( ber, str ) == -1 ) { 467 rc = -1; 468 goto done; 469 } 470 471 *next++ = /*'('*/ ')'; 472 473 str = next; 474 parens--; 475 break; 476 } 477 break; 478 479 case /*'('*/ ')': 480 Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", 481 0, 0, 0 ); 482 if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { 483 rc = -1; 484 goto done; 485 } 486 str++; 487 parens--; 488 break; 489 490 case ' ': 491 str++; 492 break; 493 494 default: /* assume it's a simple type=value filter */ 495 Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", 496 0, 0, 0 ); 497 next = strchr( str, '\0' ); 498 if ( put_simple_filter( ber, str ) == -1 ) { 499 rc = -1; 500 goto done; 501 } 502 str = next; 503 break; 504 } 505 if ( !parens ) 506 break; 507 } 508 509 rc = ( parens || *str ) ? -1 : 0; 510 511done: 512 LDAP_FREE( freeme ); 513 return rc; 514} 515 516/* 517 * Put a list of filters like this "(filter1)(filter2)..." 518 */ 519 520static int 521put_filter_list( BerElement *ber, char *str, ber_tag_t tag ) 522{ 523 char *next = NULL; 524 char save; 525 526 Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", 527 str, 0, 0 ); 528 529 while ( *str ) { 530 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { 531 str++; 532 } 533 if ( *str == '\0' ) break; 534 535 if ( (next = find_right_paren( str + 1 )) == NULL ) { 536 return -1; 537 } 538 save = *++next; 539 540 /* now we have "(filter)" with str pointing to it */ 541 *next = '\0'; 542 if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1; 543 *next = save; 544 str = next; 545 546 if( tag == LDAP_FILTER_NOT ) break; 547 } 548 549 if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) { 550 return -1; 551 } 552 553 return 0; 554} 555 556static int 557put_simple_filter( 558 BerElement *ber, 559 char *str ) 560{ 561 char *s; 562 char *value; 563 ber_tag_t ftype; 564 int rc = -1; 565 566 Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n", 567 str, 0, 0 ); 568 569 str = LDAP_STRDUP( str ); 570 if( str == NULL ) return -1; 571 572 if ( (s = strchr( str, '=' )) == NULL ) { 573 goto done; 574 } 575 576 value = s + 1; 577 *s-- = '\0'; 578 579 switch ( *s ) { 580 case '<': 581 ftype = LDAP_FILTER_LE; 582 *s = '\0'; 583 break; 584 585 case '>': 586 ftype = LDAP_FILTER_GE; 587 *s = '\0'; 588 break; 589 590 case '~': 591 ftype = LDAP_FILTER_APPROX; 592 *s = '\0'; 593 break; 594 595 case ':': 596 /* RFC 4515 extensible filters are off the form: 597 * type [:dn] [:rule] := value 598 * or [:dn]:rule := value 599 */ 600 ftype = LDAP_FILTER_EXT; 601 *s = '\0'; 602 603 { 604 char *dn = strchr( str, ':' ); 605 char *rule = NULL; 606 607 if( dn != NULL ) { 608 *dn++ = '\0'; 609 rule = strchr( dn, ':' ); 610 611 if( rule == NULL ) { 612 /* one colon */ 613 if ( strcasecmp(dn, "dn") == 0 ) { 614 /* must have attribute */ 615 if( !ldap_is_desc( str ) ) { 616 goto done; 617 } 618 619 rule = ""; 620 621 } else { 622 rule = dn; 623 dn = NULL; 624 } 625 626 } else { 627 /* two colons */ 628 *rule++ = '\0'; 629 630 if ( strcasecmp(dn, "dn") != 0 ) { 631 /* must have "dn" */ 632 goto done; 633 } 634 } 635 636 } 637 638 if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { 639 /* must have either type or rule */ 640 goto done; 641 } 642 643 if ( *str != '\0' && !ldap_is_desc( str ) ) { 644 goto done; 645 } 646 647 if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { 648 goto done; 649 } 650 651 rc = ber_printf( ber, "t{" /*"}"*/, ftype ); 652 653 if( rc != -1 && rule && *rule != '\0' ) { 654 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); 655 } 656 657 if( rc != -1 && *str != '\0' ) { 658 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); 659 } 660 661 if( rc != -1 ) { 662 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 663 664 if( len >= 0 ) { 665 rc = ber_printf( ber, "to", 666 LDAP_FILTER_EXT_VALUE, value, len ); 667 } else { 668 rc = -1; 669 } 670 } 671 672 if( rc != -1 && dn ) { 673 rc = ber_printf( ber, "tb", 674 LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 ); 675 } 676 677 if( rc != -1 ) { 678 rc = ber_printf( ber, /*"{"*/ "N}" ); 679 } 680 } 681 goto done; 682 683 default: 684 if( !ldap_is_desc( str ) ) { 685 goto done; 686 687 } else { 688 char *nextstar = ldap_pvt_find_wildcard( value ); 689 690 if ( nextstar == NULL ) { 691 goto done; 692 693 } else if ( *nextstar == '\0' ) { 694 ftype = LDAP_FILTER_EQUALITY; 695 696 } else if ( strcmp( value, "*" ) == 0 ) { 697 ftype = LDAP_FILTER_PRESENT; 698 699 } else { 700 rc = put_substring_filter( ber, str, value, nextstar ); 701 goto done; 702 } 703 } break; 704 } 705 706 if( !ldap_is_desc( str ) ) goto done; 707 708 if ( ftype == LDAP_FILTER_PRESENT ) { 709 rc = ber_printf( ber, "ts", ftype, str ); 710 711 } else { 712 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 713 714 if( len >= 0 ) { 715 rc = ber_printf( ber, "t{soN}", 716 ftype, str, value, len ); 717 } 718 } 719 720done: 721 if( rc != -1 ) rc = 0; 722 LDAP_FREE( str ); 723 return rc; 724} 725 726static int 727put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar ) 728{ 729 int gotstar = 0; 730 ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS; 731 732 Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", 733 type, val, 0 ); 734 735 if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) { 736 return -1; 737 } 738 739 for( ; *val; val=nextstar ) { 740 if ( gotstar ) 741 nextstar = ldap_pvt_find_wildcard( val ); 742 743 if ( nextstar == NULL ) { 744 return -1; 745 } 746 747 if ( *nextstar == '\0' ) { 748 ftype = LDAP_SUBSTRING_FINAL; 749 } else { 750 *nextstar++ = '\0'; 751 if ( gotstar++ == 0 ) { 752 ftype = LDAP_SUBSTRING_INITIAL; 753 } else { 754 ftype = LDAP_SUBSTRING_ANY; 755 } 756 } 757 758 if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) { 759 ber_slen_t len = ldap_pvt_filter_value_unescape( val ); 760 761 if ( len <= 0 ) { 762 return -1; 763 } 764 765 if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) { 766 return -1; 767 } 768 } 769 } 770 771 if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) { 772 return -1; 773 } 774 775 return 0; 776} 777 778static int 779put_vrFilter( BerElement *ber, const char *str_in ) 780{ 781 int rc; 782 char *freeme; 783 char *str; 784 char *next; 785 int parens, balance, escape; 786 787 /* 788 * A ValuesReturnFilter looks like this: 789 * 790 * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem 791 * SimpleFilterItem ::= CHOICE { 792 * equalityMatch [3] AttributeValueAssertion, 793 * substrings [4] SubstringFilter, 794 * greaterOrEqual [5] AttributeValueAssertion, 795 * lessOrEqual [6] AttributeValueAssertion, 796 * present [7] AttributeType, 797 * approxMatch [8] AttributeValueAssertion, 798 * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3 799 * } 800 * 801 * SubstringFilter ::= SEQUENCE { 802 * type AttributeType, 803 * SEQUENCE OF CHOICE { 804 * initial [0] IA5String, 805 * any [1] IA5String, 806 * final [2] IA5String 807 * } 808 * } 809 * 810 * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3 811 * matchingRule [1] MatchingRuleId OPTIONAL, 812 * type [2] AttributeDescription OPTIONAL, 813 * matchValue [3] AssertionValue } 814 * 815 * (Source: RFC 3876) 816 */ 817 818 Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 ); 819 820 freeme = LDAP_STRDUP( str_in ); 821 if( freeme == NULL ) return LDAP_NO_MEMORY; 822 str = freeme; 823 824 parens = 0; 825 while ( *str ) { 826 switch ( *str ) { 827 case '(': /*')'*/ 828 str++; 829 parens++; 830 831 /* skip spaces */ 832 while( LDAP_SPACE( *str ) ) str++; 833 834 switch ( *str ) { 835 case '(': 836 if ( (next = find_right_paren( str )) == NULL ) { 837 rc = -1; 838 goto done; 839 } 840 841 *next = '\0'; 842 843 if ( put_vrFilter_list( ber, str ) == -1 ) { 844 rc = -1; 845 goto done; 846 } 847 848 /* close the '(' */ 849 *next++ = ')'; 850 851 str = next; 852 853 parens--; 854 break; 855 856 857 default: 858 Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n", 859 0, 0, 0 ); 860 861 balance = 1; 862 escape = 0; 863 next = str; 864 865 while ( *next && balance ) { 866 if ( escape == 0 ) { 867 if ( *next == '(' ) { 868 balance++; 869 } else if ( *next == ')' ) { 870 balance--; 871 } 872 } 873 874 if ( *next == '\\' && ! escape ) { 875 escape = 1; 876 } else { 877 escape = 0; 878 } 879 880 if ( balance ) next++; 881 } 882 883 if ( balance != 0 ) { 884 rc = -1; 885 goto done; 886 } 887 888 *next = '\0'; 889 890 if ( put_simple_vrFilter( ber, str ) == -1 ) { 891 rc = -1; 892 goto done; 893 } 894 895 *next++ = /*'('*/ ')'; 896 897 str = next; 898 parens--; 899 break; 900 } 901 break; 902 903 case /*'('*/ ')': 904 Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n", 905 0, 0, 0 ); 906 if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { 907 rc = -1; 908 goto done; 909 } 910 str++; 911 parens--; 912 break; 913 914 case ' ': 915 str++; 916 break; 917 918 default: /* assume it's a simple type=value filter */ 919 Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n", 920 0, 0, 0 ); 921 next = strchr( str, '\0' ); 922 if ( put_simple_vrFilter( ber, str ) == -1 ) { 923 rc = -1; 924 goto done; 925 } 926 str = next; 927 break; 928 } 929 } 930 931 rc = parens ? -1 : 0; 932 933done: 934 LDAP_FREE( freeme ); 935 return rc; 936} 937 938int 939ldap_put_vrFilter( BerElement *ber, const char *str_in ) 940{ 941 int rc =0; 942 943 if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { 944 return -1; 945 } 946 947 rc = put_vrFilter( ber, str_in ); 948 949 if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { 950 rc = -1; 951 } 952 953 return rc; 954} 955 956static int 957put_vrFilter_list( BerElement *ber, char *str ) 958{ 959 char *next = NULL; 960 char save; 961 962 Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", 963 str, 0, 0 ); 964 965 while ( *str ) { 966 while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { 967 str++; 968 } 969 if ( *str == '\0' ) break; 970 971 if ( (next = find_right_paren( str + 1 )) == NULL ) { 972 return -1; 973 } 974 save = *++next; 975 976 /* now we have "(filter)" with str pointing to it */ 977 *next = '\0'; 978 if ( put_vrFilter( ber, str ) == -1 ) return -1; 979 *next = save; 980 str = next; 981 } 982 983 return 0; 984} 985 986static int 987put_simple_vrFilter( 988 BerElement *ber, 989 char *str ) 990{ 991 char *s; 992 char *value; 993 ber_tag_t ftype; 994 int rc = -1; 995 996 Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", 997 str, 0, 0 ); 998 999 str = LDAP_STRDUP( str ); 1000 if( str == NULL ) return -1; 1001 1002 if ( (s = strchr( str, '=' )) == NULL ) { 1003 goto done; 1004 } 1005 1006 value = s + 1; 1007 *s-- = '\0'; 1008 1009 switch ( *s ) { 1010 case '<': 1011 ftype = LDAP_FILTER_LE; 1012 *s = '\0'; 1013 break; 1014 1015 case '>': 1016 ftype = LDAP_FILTER_GE; 1017 *s = '\0'; 1018 break; 1019 1020 case '~': 1021 ftype = LDAP_FILTER_APPROX; 1022 *s = '\0'; 1023 break; 1024 1025 case ':': 1026 /* According to ValuesReturnFilter control definition 1027 * extensible filters are off the form: 1028 * type [:rule] := value 1029 * or :rule := value 1030 */ 1031 ftype = LDAP_FILTER_EXT; 1032 *s = '\0'; 1033 1034 { 1035 char *rule = strchr( str, ':' ); 1036 1037 if( rule == NULL ) { 1038 /* must have attribute */ 1039 if( !ldap_is_desc( str ) ) { 1040 goto done; 1041 } 1042 rule = ""; 1043 } else { 1044 *rule++ = '\0'; 1045 } 1046 1047 if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { 1048 /* must have either type or rule */ 1049 goto done; 1050 } 1051 1052 if ( *str != '\0' && !ldap_is_desc( str ) ) { 1053 goto done; 1054 } 1055 1056 if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { 1057 goto done; 1058 } 1059 1060 rc = ber_printf( ber, "t{" /*"}"*/, ftype ); 1061 1062 if( rc != -1 && rule && *rule != '\0' ) { 1063 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); 1064 } 1065 1066 if( rc != -1 && *str != '\0' ) { 1067 rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); 1068 } 1069 1070 if( rc != -1 ) { 1071 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 1072 1073 if( len >= 0 ) { 1074 rc = ber_printf( ber, "to", 1075 LDAP_FILTER_EXT_VALUE, value, len ); 1076 } else { 1077 rc = -1; 1078 } 1079 } 1080 1081 if( rc != -1 ) { 1082 rc = ber_printf( ber, /*"{"*/ "N}" ); 1083 } 1084 } 1085 goto done; 1086 1087 default: 1088 if( !ldap_is_desc( str ) ) { 1089 goto done; 1090 1091 } else { 1092 char *nextstar = ldap_pvt_find_wildcard( value ); 1093 1094 if ( nextstar == NULL ) { 1095 goto done; 1096 1097 } else if ( *nextstar == '\0' ) { 1098 ftype = LDAP_FILTER_EQUALITY; 1099 1100 } else if ( strcmp( value, "*" ) == 0 ) { 1101 ftype = LDAP_FILTER_PRESENT; 1102 1103 } else { 1104 rc = put_substring_filter( ber, str, value, nextstar ); 1105 goto done; 1106 } 1107 } break; 1108 } 1109 1110 if( !ldap_is_desc( str ) ) goto done; 1111 1112 if ( ftype == LDAP_FILTER_PRESENT ) { 1113 rc = ber_printf( ber, "ts", ftype, str ); 1114 1115 } else { 1116 ber_slen_t len = ldap_pvt_filter_value_unescape( value ); 1117 1118 if( len >= 0 ) { 1119 rc = ber_printf( ber, "t{soN}", 1120 ftype, str, value, len ); 1121 } 1122 } 1123 1124done: 1125 if( rc != -1 ) rc = 0; 1126 LDAP_FREE( str ); 1127 return rc; 1128} 1129 1130