1 /* 2 * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 9 /* 10 * The contents of this file are subject to the Netscape Public 11 * License Version 1.1 (the "License"); you may not use this file 12 * except in compliance with the License. You may obtain a copy of 13 * the License at http://www.mozilla.org/NPL/ 14 * 15 * Software distributed under the License is distributed on an "AS 16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 17 * implied. See the License for the specific language governing 18 * rights and limitations under the License. 19 * 20 * The Original Code is Mozilla Communicator client code, released 21 * March 31, 1998. 22 * 23 * The Initial Developer of the Original Code is Netscape 24 * Communications Corporation. Portions created by Netscape are 25 * Copyright (C) 1998-1999 Netscape Communications Corporation. All 26 * Rights Reserved. 27 * 28 * Contributor(s): 29 */ 30 /* 31 * Copyright (c) 1990 Regents of the University of Michigan. 32 * All rights reserved. 33 */ 34 /* 35 * search.c 36 */ 37 38 #if 0 39 #ifndef lint 40 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; 41 #endif 42 #endif 43 44 #include "ldap-int.h" 45 46 static int nsldapi_timeval2ldaplimit( struct timeval *timeoutp, 47 int defaultvalue ); 48 static int nsldapi_search( LDAP *ld, const char *base, int scope, 49 const char *filter, char **attrs, int attrsonly, 50 LDAPControl **serverctrls, LDAPControl **clientctrls, 51 int timelimit, int sizelimit, int *msgidp ); 52 static char *find_right_paren( char *s ); 53 static char *put_complex_filter( BerElement *ber, char *str, 54 ber_tag_t tag, int not ); 55 static int unescape_filterval( char *str ); 56 static int hexchar2int( char c ); 57 static int is_valid_attr( char *a ); 58 static int put_simple_filter( BerElement *ber, char *str ); 59 static int put_substring_filter( BerElement *ber, char *type, 60 char *str ); 61 static int put_filter_list( BerElement *ber, char *str ); 62 static int nsldapi_search_s( LDAP *ld, const char *base, int scope, 63 const char *filter, char **attrs, int attrsonly, 64 LDAPControl **serverctrls, LDAPControl **clientctrls, 65 struct timeval *localtimeoutp, int timelimit, int sizelimit, 66 LDAPMessage **res ); 67 68 /* 69 * ldap_search - initiate an ldap search operation. Parameters: 70 * 71 * ld LDAP descriptor 72 * base DN of the base object 73 * scope the search scope - one of LDAP_SCOPE_BASE, 74 * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE 75 * filter a string containing the search filter 76 * (e.g., "(|(cn=bob)(sn=bob))") 77 * attrs list of attribute types to return for matches 78 * attrsonly 1 => attributes only 0 => attributes and values 79 * 80 * Example: 81 * char *attrs[] = { "mail", "title", 0 }; 82 * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob", 83 * attrs, attrsonly ); 84 */ 85 int 86 LDAP_CALL 87 ldap_search( 88 LDAP *ld, 89 const char *base, 90 int scope, 91 const char *filter, 92 char **attrs, 93 int attrsonly 94 ) 95 { 96 int msgid; 97 98 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 ); 99 100 if ( ldap_search_ext( ld, base, scope, filter, attrs, attrsonly, NULL, 101 NULL, NULL, -1, &msgid ) == LDAP_SUCCESS ) { 102 return( msgid ); 103 } else { 104 return( -1 ); /* error is in ld handle */ 105 } 106 } 107 108 109 /* 110 * LDAPv3 extended search. 111 * Returns an LDAP error code. 112 */ 113 int 114 LDAP_CALL 115 ldap_search_ext( 116 LDAP *ld, 117 const char *base, 118 int scope, 119 const char *filter, 120 char **attrs, 121 int attrsonly, 122 LDAPControl **serverctrls, 123 LDAPControl **clientctrls, 124 struct timeval *timeoutp, /* NULL means use ld->ld_timelimit */ 125 int sizelimit, 126 int *msgidp 127 ) 128 { 129 /* 130 * It is an error to pass in a zero'd timeval. 131 */ 132 if ( timeoutp != NULL && timeoutp->tv_sec == 0 && 133 timeoutp->tv_usec == 0 ) { 134 if ( ld != NULL ) { 135 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); 136 } 137 return( LDAP_PARAM_ERROR ); 138 } 139 140 return( nsldapi_search( ld, base, scope, filter, attrs, attrsonly, 141 serverctrls, clientctrls, 142 nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, msgidp )); 143 } 144 145 146 /* 147 * Like ldap_search_ext() except an integer timelimit is passed instead of 148 * using the overloaded struct timeval *timeoutp. 149 */ 150 static int 151 nsldapi_search( 152 LDAP *ld, 153 const char *base, 154 int scope, 155 const char *filter, 156 char **attrs, 157 int attrsonly, 158 LDAPControl **serverctrls, 159 LDAPControl **clientctrls, 160 int timelimit, /* -1 means use ld->ld_timelimit */ 161 int sizelimit, /* -1 means use ld->ld_sizelimit */ 162 int *msgidp 163 ) 164 { 165 BerElement *ber; 166 int rc, rc_key; 167 unsigned long key; /* XXXmcs: memcache */ 168 169 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 ); 170 171 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 172 return( LDAP_PARAM_ERROR ); 173 } 174 175 if ( base == NULL ) { 176 base = ""; 177 } 178 179 if ( filter == NULL ) { 180 filter = "(objectclass=*)"; 181 } 182 183 if ( msgidp == NULL || ( scope != LDAP_SCOPE_BASE 184 && scope != LDAP_SCOPE_ONELEVEL && scope != LDAP_SCOPE_SUBTREE ) 185 || ( sizelimit < -1 )) { 186 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); 187 return( LDAP_PARAM_ERROR ); 188 } 189 LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); 190 *msgidp = ++ld->ld_msgid; 191 LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); 192 193 /* 194 * XXXmcs: should use cache function pointers to hook in memcache 195 */ 196 if ( ld->ld_memcache == NULL ) { 197 rc_key = LDAP_NOT_SUPPORTED; 198 } else if (( rc_key = ldap_memcache_createkey( ld, base, scope, filter, 199 attrs, attrsonly, serverctrls, clientctrls, &key)) == LDAP_SUCCESS 200 && ldap_memcache_result( ld, *msgidp, key ) == LDAP_SUCCESS ) { 201 return LDAP_SUCCESS; 202 } 203 204 /* check the cache */ 205 if ( ld->ld_cache_on && ld->ld_cache_search != NULL ) { 206 LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); 207 if ( (rc = (ld->ld_cache_search)( ld, *msgidp, LDAP_REQ_SEARCH, 208 base, scope, filter, attrs, attrsonly )) != 0 ) { 209 *msgidp = rc; 210 LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); 211 return( LDAP_SUCCESS ); 212 } 213 LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); 214 } 215 216 /* caching off or did not find it in the cache - check the net */ 217 if (( rc = nsldapi_build_search_req( ld, base, scope, filter, attrs, 218 attrsonly, serverctrls, clientctrls, timelimit, sizelimit, 219 *msgidp, &ber )) != LDAP_SUCCESS ) { 220 return( rc ); 221 } 222 223 /* send the message */ 224 rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_SEARCH, 225 (char *) base, ber ); 226 227 /* 228 * XXXmcs: should use cache function pointers to hook in memcache 229 */ 230 if ( (rc_key == LDAP_SUCCESS) && (rc >= 0) ) { 231 ldap_memcache_new( ld, rc, key, base ); 232 } 233 234 *msgidp = rc; 235 return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); 236 } 237 238 239 /* 240 * Convert a non-NULL timeoutp to a value in seconds that is appropriate to 241 * send in an LDAP search request. If timeoutp is NULL, return defaultvalue. 242 */ 243 static int 244 nsldapi_timeval2ldaplimit( struct timeval *timeoutp, int defaultvalue ) 245 { 246 int timelimit; 247 248 if ( NULL == timeoutp ) { 249 timelimit = defaultvalue; 250 } else if ( timeoutp->tv_sec > 0 ) { 251 timelimit = timeoutp->tv_sec; 252 } else if ( timeoutp->tv_usec > 0 ) { 253 timelimit = 1; /* minimum we can express in LDAP */ 254 } else { 255 /* 256 * both tv_sec and tv_usec are less than one (zero?) so 257 * to maintain compatiblity with our "zero means no limit" 258 * convention we pass no limit to the server. 259 */ 260 timelimit = 0; /* no limit */ 261 } 262 263 return( timelimit ); 264 } 265 266 267 /* returns an LDAP error code and also sets it in ld */ 268 int 269 nsldapi_build_search_req( 270 LDAP *ld, 271 const char *base, 272 int scope, 273 const char *filter, 274 char **attrs, 275 int attrsonly, 276 LDAPControl **serverctrls, 277 LDAPControl **clientctrls, /* not used for anything yet */ 278 int timelimit, /* if -1, ld->ld_timelimit is used */ 279 int sizelimit, /* if -1, ld->ld_sizelimit is used */ 280 int msgid, 281 BerElement **berp 282 ) 283 { 284 BerElement *ber; 285 int err; 286 char *fdup; 287 288 /* 289 * Create the search request. It looks like this: 290 * SearchRequest := [APPLICATION 3] SEQUENCE { 291 * baseObject DistinguishedName, 292 * scope ENUMERATED { 293 * baseObject (0), 294 * singleLevel (1), 295 * wholeSubtree (2) 296 * }, 297 * derefAliases ENUMERATED { 298 * neverDerefaliases (0), 299 * derefInSearching (1), 300 * derefFindingBaseObj (2), 301 * alwaysDerefAliases (3) 302 * }, 303 * sizelimit INTEGER (0 .. 65535), 304 * timelimit INTEGER (0 .. 65535), 305 * attrsOnly BOOLEAN, 306 * filter Filter, 307 * attributes SEQUENCE OF AttributeType 308 * } 309 * wrapped in an ldap message. 310 */ 311 312 /* create a message to send */ 313 if (( err = nsldapi_alloc_ber_with_options( ld, &ber )) 314 != LDAP_SUCCESS ) { 315 return( err ); 316 } 317 318 if ( base == NULL ) { 319 base = ""; 320 } 321 322 if ( sizelimit == -1 ) { 323 sizelimit = ld->ld_sizelimit; 324 } 325 326 if ( timelimit == -1 ) { 327 timelimit = ld->ld_timelimit; 328 } 329 330 #ifdef CLDAP 331 if ( ld->ld_sbp->sb_naddr > 0 ) { 332 err = ber_printf( ber, "{ist{seeiib", msgid, 333 ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref, 334 sizelimit, timelimit, attrsonly ); 335 } else { 336 #endif /* CLDAP */ 337 err = ber_printf( ber, "{it{seeiib", msgid, 338 LDAP_REQ_SEARCH, base, scope, ld->ld_deref, 339 sizelimit, timelimit, attrsonly ); 340 #ifdef CLDAP 341 } 342 #endif /* CLDAP */ 343 344 if ( err == -1 ) { 345 LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); 346 ber_free( ber, 1 ); 347 return( LDAP_ENCODING_ERROR ); 348 } 349 350 fdup = nsldapi_strdup( filter ); 351 if (fdup == NULL) { 352 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); 353 ber_free( ber, 1 ); 354 return( LDAP_NO_MEMORY ); 355 } 356 err = ldap_put_filter( ber, fdup ); 357 NSLDAPI_FREE( fdup ); 358 359 if ( err == -1 ) { 360 LDAP_SET_LDERRNO( ld, LDAP_FILTER_ERROR, NULL, NULL ); 361 ber_free( ber, 1 ); 362 return( LDAP_FILTER_ERROR ); 363 } 364 365 if ( ber_printf( ber, "{v}}", attrs ) == -1 ) { 366 LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); 367 ber_free( ber, 1 ); 368 return( LDAP_ENCODING_ERROR ); 369 } 370 371 if ( (err = nsldapi_put_controls( ld, serverctrls, 1, ber )) 372 != LDAP_SUCCESS ) { 373 ber_free( ber, 1 ); 374 return( err ); 375 } 376 377 *berp = ber; 378 return( LDAP_SUCCESS ); 379 } 380 381 static char * 382 find_right_paren( char *s ) 383 { 384 int balance, escape; 385 386 balance = 1; 387 escape = 0; 388 while ( *s && balance ) { 389 if ( escape == 0 ) { 390 if ( *s == '(' ) 391 balance++; 392 else if ( *s == ')' ) 393 balance--; 394 } 395 if ( *s == '\\' && ! escape ) 396 escape = 1; 397 else 398 escape = 0; 399 if ( balance ) 400 s++; 401 } 402 403 return( *s ? s : NULL ); 404 } 405 406 static char * 407 put_complex_filter( 408 BerElement *ber, 409 char *str, 410 ber_tag_t tag, 411 int not 412 ) 413 { 414 char *next; 415 416 /* 417 * We have (x(filter)...) with str sitting on 418 * the x. We have to find the paren matching 419 * the one before the x and put the intervening 420 * filters by calling put_filter_list(). 421 */ 422 423 /* put explicit tag */ 424 if ( ber_printf( ber, "t{", tag ) == -1 ) 425 return( NULL ); 426 427 str++; 428 if ( (next = find_right_paren( str )) == NULL ) 429 return( NULL ); 430 431 *next = '\0'; 432 if ( put_filter_list( ber, str ) == -1 ) 433 return( NULL ); 434 *next++ = ')'; 435 436 /* flush explicit tagged thang */ 437 if ( ber_printf( ber, "}" ) == -1 ) 438 return( NULL ); 439 440 return( next ); 441 } 442 443 int 444 ldap_put_filter( BerElement *ber, char *str ) 445 { 446 char *next; 447 int parens, balance, escape; 448 449 /* 450 * A Filter looks like this: 451 * Filter ::= CHOICE { 452 * and [0] SET OF Filter, 453 * or [1] SET OF Filter, 454 * not [2] Filter, 455 * equalityMatch [3] AttributeValueAssertion, 456 * substrings [4] SubstringFilter, 457 * greaterOrEqual [5] AttributeValueAssertion, 458 * lessOrEqual [6] AttributeValueAssertion, 459 * present [7] AttributeType,, 460 * approxMatch [8] AttributeValueAssertion 461 * } 462 * 463 * SubstringFilter ::= SEQUENCE { 464 * type AttributeType, 465 * SEQUENCE OF CHOICE { 466 * initial [0] IA5String, 467 * any [1] IA5String, 468 * final [2] IA5String 469 * } 470 * } 471 * Note: tags in a choice are always explicit 472 */ 473 474 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 ); 475 476 parens = 0; 477 while ( *str ) { 478 switch ( *str ) { 479 case '(': 480 str++; 481 parens++; 482 switch ( *str ) { 483 case '&': 484 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: AND\n", 485 0, 0, 0 ); 486 487 if ( (str = put_complex_filter( ber, str, 488 LDAP_FILTER_AND, 0 )) == NULL ) 489 return( -1 ); 490 491 parens--; 492 break; 493 494 case '|': 495 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: OR\n", 496 0, 0, 0 ); 497 498 if ( (str = put_complex_filter( ber, str, 499 LDAP_FILTER_OR, 0 )) == NULL ) 500 return( -1 ); 501 502 parens--; 503 break; 504 505 case '!': 506 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: NOT\n", 507 0, 0, 0 ); 508 509 if ( (str = put_complex_filter( ber, str, 510 LDAP_FILTER_NOT, 1 )) == NULL ) 511 return( -1 ); 512 513 parens--; 514 break; 515 516 default: 517 LDAPDebug( LDAP_DEBUG_TRACE, 518 "put_filter: simple\n", 0, 0, 0 ); 519 520 balance = 1; 521 escape = 0; 522 next = str; 523 while ( *next && balance ) { 524 if ( escape == 0 ) { 525 if ( *next == '(' ) 526 balance++; 527 else if ( *next == ')' ) 528 balance--; 529 } 530 if ( *next == '\\' && ! escape ) 531 escape = 1; 532 else 533 escape = 0; 534 if ( balance ) 535 next++; 536 } 537 if ( balance != 0 ) 538 return( -1 ); 539 540 *next = '\0'; 541 if ( put_simple_filter( ber, str ) == -1 ) { 542 return( -1 ); 543 } 544 *next++ = ')'; 545 str = next; 546 parens--; 547 break; 548 } 549 break; 550 551 case ')': 552 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0, 553 0 ); 554 if ( ber_printf( ber, "]" ) == -1 ) 555 return( -1 ); 556 str++; 557 parens--; 558 break; 559 560 case ' ': 561 str++; 562 break; 563 564 default: /* assume it's a simple type=value filter */ 565 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0, 566 0 ); 567 next = strchr( str, '\0' ); 568 if ( put_simple_filter( ber, str ) == -1 ) { 569 return( -1 ); 570 } 571 str = next; 572 break; 573 } 574 } 575 576 return( parens ? -1 : 0 ); 577 } 578 579 580 /* 581 * Put a list of filters like this "(filter1)(filter2)..." 582 */ 583 584 static int 585 put_filter_list( BerElement *ber, char *str ) 586 { 587 char *next; 588 char save; 589 590 LDAPDebug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 ); 591 592 while ( *str ) { 593 while ( *str && isspace( *str ) ) 594 str++; 595 if ( *str == '\0' ) 596 break; 597 598 if ( (next = find_right_paren( str + 1 )) == NULL ) 599 return( -1 ); 600 save = *++next; 601 602 /* now we have "(filter)" with str pointing to it */ 603 *next = '\0'; 604 if ( ldap_put_filter( ber, str ) == -1 ) 605 return( -1 ); 606 *next = save; 607 608 str = next; 609 } 610 611 return( 0 ); 612 } 613 614 615 /* 616 * is_valid_attr - returns 1 if a is a syntactically valid left-hand side 617 * of a filter expression, 0 otherwise. A valid string may contain only 618 * letters, numbers, hyphens, semi-colons, colons and periods. examples: 619 * cn 620 * cn;lang-fr 621 * 1.2.3.4;binary;dynamic 622 * mail;dynamic 623 * cn:dn:1.2.3.4 624 * 625 * For compatibility with older servers, we also allow underscores in 626 * attribute types, even through they are not allowed by the LDAPv3 RFCs. 627 */ 628 static int 629 is_valid_attr( char *a ) 630 { 631 for ( ; *a; a++ ) { 632 if ( !isascii( *a ) ) { 633 return( 0 ); 634 } else if ( !isalnum( *a ) ) { 635 switch ( *a ) { 636 case '-': 637 case '.': 638 case ';': 639 case ':': 640 case '_': 641 break; /* valid */ 642 default: 643 return( 0 ); 644 } 645 } 646 } 647 648 return( 1 ); 649 } 650 651 static char * 652 find_star( char *s ) 653 { 654 for ( ; *s; ++s ) { 655 switch ( *s ) { 656 case '*': return s; 657 case '\\': 658 ++s; 659 if ( hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0 ) ++s; 660 default: break; 661 } 662 } 663 return NULL; 664 } 665 666 static int 667 put_simple_filter( BerElement *ber, char *str ) 668 { 669 char *s, *s2, *s3, filterop; 670 char *value; 671 ber_uint_t ftype; 672 int rc, len; 673 char *oid; /* for v3 extended filter */ 674 int dnattr; /* for v3 extended filter */ 675 676 LDAPDebug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 ); 677 678 rc = -1; /* pessimistic */ 679 680 if (( str = nsldapi_strdup( str )) == NULL ) { 681 return( rc ); 682 } 683 684 if ( (s = strchr( str, '=' )) == NULL ) { 685 goto free_and_return; 686 } 687 value = s + 1; 688 *s-- = '\0'; 689 filterop = *s; 690 if ( filterop == '<' || filterop == '>' || filterop == '~' || 691 filterop == ':' ) { 692 *s = '\0'; 693 } 694 695 if ( ! is_valid_attr( str ) ) { 696 goto free_and_return; 697 } 698 699 switch ( filterop ) { 700 case '<': 701 ftype = LDAP_FILTER_LE; 702 break; 703 case '>': 704 ftype = LDAP_FILTER_GE; 705 break; 706 case '~': 707 ftype = LDAP_FILTER_APPROX; 708 break; 709 case ':': /* extended filter - v3 only */ 710 /* 711 * extended filter looks like this: 712 * 713 * [type][':dn'][':'oid]':='value 714 * 715 * where one of type or :oid is required. 716 * 717 */ 718 ftype = LDAP_FILTER_EXTENDED; 719 s2 = s3 = NULL; 720 if ( (s2 = strrchr( str, ':' )) == NULL ) { 721 goto free_and_return; 722 } 723 if ( strcasecmp( s2, ":dn" ) == 0 ) { 724 oid = NULL; 725 dnattr = 1; 726 *s2 = '\0'; 727 } else { 728 oid = s2 + 1; 729 dnattr = 0; 730 *s2 = '\0'; 731 if ( (s3 = strrchr( str, ':' )) != NULL ) { 732 if ( strcasecmp( s3, ":dn" ) == 0 ) { 733 dnattr = 1; 734 } else { 735 goto free_and_return; 736 } 737 *s3 = '\0'; 738 } 739 } 740 if ( (rc = ber_printf( ber, "t{", ftype )) == -1 ) { 741 goto free_and_return; 742 } 743 if ( oid != NULL ) { 744 if ( (rc = ber_printf( ber, "ts", LDAP_TAG_MRA_OID, 745 oid )) == -1 ) { 746 goto free_and_return; 747 } 748 } 749 if ( *str != '\0' ) { 750 if ( (rc = ber_printf( ber, "ts", 751 LDAP_TAG_MRA_TYPE, str )) == -1 ) { 752 goto free_and_return; 753 } 754 } 755 if (( len = unescape_filterval( value )) < 0 || 756 ( rc = ber_printf( ber, "totb}", LDAP_TAG_MRA_VALUE, 757 value, len, LDAP_TAG_MRA_DNATTRS, dnattr )) == -1 ) { 758 goto free_and_return; 759 } 760 rc = 0; 761 goto free_and_return; 762 /* break; */ 763 default: 764 if ( find_star( value ) == NULL ) { 765 ftype = LDAP_FILTER_EQUALITY; 766 } else if ( strcmp( value, "*" ) == 0 ) { 767 ftype = LDAP_FILTER_PRESENT; 768 } else { 769 rc = put_substring_filter( ber, str, value ); 770 goto free_and_return; 771 } 772 break; 773 } 774 775 if ( ftype == LDAP_FILTER_PRESENT ) { 776 rc = ber_printf( ber, "ts", ftype, str ); 777 } else if (( len = unescape_filterval( value )) >= 0 ) { 778 rc = ber_printf( ber, "t{so}", ftype, str, value, len ); 779 } 780 if ( rc != -1 ) { 781 rc = 0; 782 } 783 784 free_and_return: 785 NSLDAPI_FREE( str ); 786 return( rc ); 787 } 788 789 790 /* 791 * Undo in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape 792 * sequences within the null-terminated string 'val'. The resulting value 793 * may contain null characters. 794 * 795 * If 'val' contains invalid escape sequences we return -1. 796 * Otherwise the length of the unescaped value is returned. 797 */ 798 static int 799 unescape_filterval( char *val ) 800 { 801 int escape, firstdigit, ival; 802 char *s, *d; 803 804 escape = 0; 805 for ( s = d = val; *s; s++ ) { 806 if ( escape ) { 807 /* 808 * first try LDAPv3 escape (hexadecimal) sequence 809 */ 810 if (( ival = hexchar2int( *s )) < 0 ) { 811 if ( firstdigit ) { 812 /* 813 * LDAPv2 (RFC1960) escape sequence 814 */ 815 *d++ = *s; 816 escape = 0; 817 } else { 818 return(-1); 819 } 820 } 821 if ( firstdigit ) { 822 *d = ( ival<<4 ); 823 firstdigit = 0; 824 } else { 825 *d++ |= ival; 826 escape = 0; 827 } 828 829 } else if ( *s != '\\' ) { 830 *d++ = *s; 831 escape = 0; 832 833 } else { 834 escape = 1; 835 firstdigit = 1; 836 } 837 } 838 839 return( d - val ); 840 } 841 842 843 /* 844 * convert character 'c' that represents a hexadecimal digit to an integer. 845 * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned. 846 * otherwise the converted value is returned. 847 */ 848 static int 849 hexchar2int( char c ) 850 { 851 if ( c >= '0' && c <= '9' ) { 852 return( c - '0' ); 853 } 854 if ( c >= 'A' && c <= 'F' ) { 855 return( c - 'A' + 10 ); 856 } 857 if ( c >= 'a' && c <= 'f' ) { 858 return( c - 'a' + 10 ); 859 } 860 return( -1 ); 861 } 862 863 static int 864 put_substring_filter( BerElement *ber, char *type, char *val ) 865 { 866 char *nextstar, gotstar = 0; 867 ber_uint_t ftype; 868 int len; 869 870 LDAPDebug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type, 871 val, 0 ); 872 873 if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 ) { 874 return( -1 ); 875 } 876 877 for ( ; val != NULL; val = nextstar ) { 878 if ( (nextstar = find_star( val )) != NULL ) { 879 *nextstar++ = '\0'; 880 } 881 882 if ( gotstar == 0 ) { 883 ftype = LDAP_SUBSTRING_INITIAL; 884 } else if ( nextstar == NULL ) { 885 ftype = LDAP_SUBSTRING_FINAL; 886 } else { 887 ftype = LDAP_SUBSTRING_ANY; 888 } 889 if ( *val != '\0' ) { 890 if (( len = unescape_filterval( val )) < 0 || 891 ber_printf( ber, "to", ftype, val, len ) == -1 ) { 892 return( -1 ); 893 } 894 } 895 896 gotstar = 1; 897 } 898 899 if ( ber_printf( ber, "}}" ) == -1 ) { 900 return( -1 ); 901 } 902 903 return( 0 ); 904 } 905 906 int 907 LDAP_CALL 908 ldap_search_st( 909 LDAP *ld, 910 const char *base, 911 int scope, 912 const char *filter, 913 char **attrs, 914 int attrsonly, 915 struct timeval *timeout, 916 LDAPMessage **res 917 ) 918 { 919 return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly, 920 NULL, NULL, timeout, -1, -1, res )); 921 } 922 923 int 924 LDAP_CALL 925 ldap_search_s( 926 LDAP *ld, 927 const char *base, 928 int scope, 929 const char *filter, 930 char **attrs, 931 int attrsonly, 932 LDAPMessage **res 933 ) 934 { 935 return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly, 936 NULL, NULL, NULL, -1, -1, res )); 937 } 938 939 int LDAP_CALL 940 ldap_search_ext_s( 941 LDAP *ld, 942 const char *base, 943 int scope, 944 const char *filter, 945 char **attrs, 946 int attrsonly, 947 LDAPControl **serverctrls, 948 LDAPControl **clientctrls, 949 struct timeval *timeoutp, 950 int sizelimit, 951 LDAPMessage **res 952 ) 953 { 954 return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly, 955 serverctrls, clientctrls, timeoutp, 956 nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, res )); 957 } 958 959 960 static int 961 nsldapi_search_s( 962 LDAP *ld, 963 const char *base, 964 int scope, 965 const char *filter, 966 char **attrs, 967 int attrsonly, 968 LDAPControl **serverctrls, 969 LDAPControl **clientctrls, 970 struct timeval *localtimeoutp, 971 int timelimit, /* -1 means use ld->ld_timelimit */ 972 int sizelimit, /* -1 means use ld->ld_sizelimit */ 973 LDAPMessage **res 974 ) 975 { 976 int err, msgid; 977 978 /* 979 * It is an error to pass in a zero'd timeval. 980 */ 981 if ( localtimeoutp != NULL && localtimeoutp->tv_sec == 0 && 982 localtimeoutp->tv_usec == 0 ) { 983 if ( ld != NULL ) { 984 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); 985 } 986 if ( res != NULL ) { 987 *res = NULL; 988 } 989 return( LDAP_PARAM_ERROR ); 990 } 991 992 if (( err = nsldapi_search( ld, base, scope, filter, attrs, attrsonly, 993 serverctrls, clientctrls, timelimit, sizelimit, &msgid )) 994 != LDAP_SUCCESS ) { 995 if ( res != NULL ) { 996 *res = NULL; 997 } 998 return( err ); 999 } 1000 1001 if ( ldap_result( ld, msgid, 1, localtimeoutp, res ) == -1 ) { 1002 /* 1003 * Error. ldap_result() sets *res to NULL for us. 1004 */ 1005 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 1006 } 1007 1008 if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) { 1009 (void) ldap_abandon( ld, msgid ); 1010 err = LDAP_TIMEOUT; 1011 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 1012 if ( res != NULL ) { 1013 *res = NULL; 1014 } 1015 return( err ); 1016 } 1017 1018 return( ldap_result2error( ld, *res, 0 ) ); 1019 } 1020