1 /*
2 *
3 * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
4 * All rights reserved.
5 *
6 */
7
8 #pragma ident "%Z%%M% %I% %E% SMI"
9
10 /*
11 * Copyright (c) 1990 Regents of the University of Michigan.
12 * All rights reserved.
13 *
14 * search.c
15 */
16
17 #ifndef lint
18 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
19 #endif
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <stdlib.h> /* free() for Solaris */
25
26 #ifdef MACOS
27 #include <stdlib.h>
28 #include "macos.h"
29 #endif /* MACOS */
30
31 #if defined(DOS) || defined(_WIN32)
32 #include "msdos.h"
33 #endif /* DOS */
34
35 #if !defined(MACOS) && !defined(DOS) && !defined(_WIN32)
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #endif
40 #include "lber.h"
41 #include "ldap.h"
42 #include "ldap-private.h"
43 #include "ldap-int.h"
44
45 #ifdef NEEDPROTOS
46 static char *find_right_paren(char *s);
47 static char *put_complex_filter(BerElement *ber, char *str,
48 unsigned int tag, int not);
49 static int put_filter(BerElement *ber, char *str);
50 static int put_simple_filter(BerElement *ber, char *str);
51 static int put_substring_filter(BerElement *ber, char *type, char *str);
52 static int put_filter_list(BerElement *ber, char *str);
53 static char *star_search(char *str);
54 static int hex_char2int(char c);
55 static int decode_value(char *str);
56 #else
57 static char *find_right_paren();
58 static char *put_complex_filter();
59 static int put_filter();
60 static int put_simple_filter();
61 static int put_substring_filter();
62 static int put_filter_list();
63 static char *star_search();
64 static int hex_char2int();
65 static int decode_value();
66 #endif /* NEEDPROTOS */
67
68
69 BerElement *
ldap_build_search_req(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,struct timeval * timeoutp,int sizelimit)70 ldap_build_search_req(LDAP *ld, char *base, int scope, char *filter,
71 char **attrs, int attrsonly, LDAPControl ** serverctrls,
72 struct timeval *timeoutp, int sizelimit)
73 {
74 BerElement *ber;
75 int err;
76 int theSizeLimit, theTimeLimit;
77 char *theFilter;
78
79 /*
80 * Create the search request. It looks like this:
81 * SearchRequest := [APPLICATION 3] SEQUENCE {
82 * baseObject DistinguishedName,
83 * scope ENUMERATED {
84 * baseObject (0),
85 * singleLevel (1),
86 * wholeSubtree (2)
87 * },
88 * derefAliases ENUMERATED {
89 * neverDerefaliases (0),
90 * derefInSearching (1),
91 * derefFindingBaseObj (2),
92 * alwaysDerefAliases (3)
93 * },
94 * sizelimit INTEGER (0 .. 65535),
95 * timelimit INTEGER (0 .. 65535),
96 * attrsOnly BOOLEAN,
97 * filter Filter,
98 * attributes SEQUENCE OF AttributeType
99 * }
100 * wrapped in an ldap message.
101 */
102
103 if (filter == NULL || *filter == '\0') {
104 ld->ld_errno = LDAP_PARAM_ERROR;
105 return (NULLBER);
106 }
107
108 /* create a message to send */
109 if ((ber = alloc_ber_with_options(ld)) == NULLBER) {
110 return (NULLBER);
111 }
112
113 if (base == NULL) {
114 base = "";
115 }
116
117 if (timeoutp != NULL) {
118 if (timeoutp->tv_sec > 0) {
119 theTimeLimit = (int)(timeoutp->tv_sec +
120 (timeoutp->tv_usec / 1000000));
121 } else if (timeoutp->tv_usec > 0) {
122 theTimeLimit = 1; /* minimum we can express in LDAP */
123 } else {
124 theTimeLimit = 0; /* no limit */
125 }
126 } else {
127 theTimeLimit = ld->ld_timelimit;
128 }
129
130 #ifdef CLDAP
131 if (ld->ld_sb.sb_naddr > 0) {
132 err = ber_printf(ber, "{ist{seeiib", ++ld->ld_msgid,
133 ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
134 sizelimit == -1 ? ld->ld_sizelimit : sizelimit, theTimeLimit,
135 attrsonly);
136 } else {
137 #endif /* CLDAP */
138 err = ber_printf(ber, "{it{seeiib", ++ld->ld_msgid,
139 LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
140 sizelimit == -1 ? ld->ld_sizelimit : sizelimit,
141 theTimeLimit, attrsonly);
142 #ifdef CLDAP
143 }
144 #endif /* CLDAP */
145
146 if (err == -1) {
147 ld->ld_errno = LDAP_ENCODING_ERROR;
148 ber_free(ber, 1);
149 return (NULLBER);
150 }
151
152 theFilter = filter;
153 while (*theFilter == ' ') theFilter++;
154 if ((*theFilter == '&') || (*theFilter == '|') || (*theFilter == '!')) {
155 char *ptr = theFilter;
156 theFilter = (char *)calloc(1, strlen(ptr) + 3);
157 sprintf(theFilter, "(%s)", ptr);
158 } else {
159 theFilter = strdup(filter);
160 }
161 err = put_filter(ber, theFilter);
162 free(theFilter);
163
164 if (err == -1) {
165 ld->ld_errno = LDAP_FILTER_ERROR;
166 ber_free(ber, 1);
167 return (NULLBER);
168 }
169
170 if (ber_printf(ber, "{v}}", attrs) == -1) {
171 ld->ld_errno = LDAP_ENCODING_ERROR;
172 ber_free(ber, 1);
173 return (NULLBER);
174 }
175
176 /* LDAPv3 */
177 /* Code controls if any */
178 if (serverctrls && serverctrls[0]) {
179 if (ldap_controls_code(ber, serverctrls) != LDAP_SUCCESS) {
180 ld->ld_errno = LDAP_ENCODING_ERROR;
181 ber_free(ber, 1);
182 return (NULLBER);
183 }
184 } else if (ld->ld_srvctrls && ld->ld_srvctrls[0]) {
185 /* Otherwise, is there any global server ctrls ? */
186 if (ldap_controls_code(ber, ld->ld_srvctrls) != LDAP_SUCCESS) {
187 ld->ld_errno = LDAP_ENCODING_ERROR;
188 ber_free(ber, 1);
189 return (NULLBER);
190 }
191 }
192
193 if (ber_printf(ber, "}") == -1) {
194 ld->ld_errno = LDAP_ENCODING_ERROR;
195 ber_free(ber, 1);
196 return (NULLBER);
197 }
198
199 return (ber);
200 }
201
202 /*
203 * ldap_search - initiate an ldap (and X.500) search operation. Parameters:
204 *
205 * ld LDAP descriptor
206 * base DN of the base object
207 * scope the search scope - one of LDAP_SCOPE_BASE,
208 * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
209 * filter a string containing the search filter
210 * (e.g., "(|(cn=bob)(sn=bob))")
211 * attrs list of attribute types to return for matches
212 * attrsonly 1 => attributes only 0 => attributes and values
213 *
214 * Example:
215 * char *attrs[] = { "mail", "title", 0 };
216 * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
217 * attrs, attrsonly );
218 */
219 int
ldap_search(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly)220 ldap_search(LDAP *ld, char *base, int scope, char *filter,
221 char **attrs, int attrsonly)
222 {
223 BerElement *ber;
224
225 #if defined(SUN) && defined(_REENTRANT)
226 int rv;
227
228 LOCK_LDAP(ld);
229 #endif
230 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 242, "ldap_search\n"),
231 0, 0, 0);
232
233 if ((ber = ldap_build_search_req(ld, base, scope, filter, attrs,
234 attrsonly, NULL, NULL, -1)) == NULLBER) {
235 #if defined(SUN) && defined(_REENTRANT)
236 UNLOCK_LDAP(ld);
237 #endif
238 return (-1);
239 }
240
241 #ifndef NO_CACHE
242 if (ld->ld_cache != NULL) {
243 if (check_cache(ld, LDAP_REQ_SEARCH, ber) == 0) {
244 ber_free(ber, 1);
245 ld->ld_errno = LDAP_SUCCESS;
246 rv = ld->ld_msgid;
247 #ifdef _REENTRANT
248 UNLOCK_LDAP(ld);
249 #endif
250 return (rv);
251 }
252 add_request_to_cache(ld, LDAP_REQ_SEARCH, ber);
253 }
254 #endif /* NO_CACHE */
255
256 /* send the message */
257 rv = send_initial_request(ld, LDAP_REQ_SEARCH, base, ber);
258 #ifdef _REENTRANT
259 UNLOCK_LDAP(ld);
260 #endif
261 return (rv);
262 }
263
264
265 static char *
find_right_paren(char * s)266 find_right_paren(char *s)
267 {
268 int balance, escape;
269
270 balance = 1;
271 escape = 0;
272 while (*s && balance) {
273 if (escape == 0) {
274 if (*s == '(')
275 balance++;
276 else if (*s == ')')
277 balance--;
278 }
279 if (*s == '\\' && ! escape)
280 escape = 1;
281 else
282 escape = 0;
283 if (balance)
284 s++;
285 }
286
287 return (*s ? s : NULL);
288 }
289
290 static char *
put_complex_filter(BerElement * ber,char * str,unsigned int tag,int not)291 put_complex_filter(BerElement *ber, char *str, unsigned int 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 if (!not && ber_printf(ber, "{") == -1)
307 return (NULL);
308 */
309
310 str++;
311 if ((next = find_right_paren(str)) == NULL)
312 return (NULL);
313
314 *next = '\0';
315 if (put_filter_list(ber, str) == -1)
316 return (NULL);
317 *next++ = ')';
318
319 /* flush explicit tagged thang */
320 if (ber_printf(ber, "}") == -1)
321 return (NULL);
322 /*
323 if (!not && ber_printf(ber, "}") == -1)
324 return (NULL);
325 */
326
327 return (next);
328 }
329
330 static int
put_filter(BerElement * ber,char * str)331 put_filter(BerElement *ber, char *str)
332 {
333 char *next, *tmp, *s, *d;
334 int parens, balance, escape;
335 int multipleparen = 0;
336
337 /*
338 * A Filter looks like this:
339 * Filter ::= CHOICE {
340 * and [0] SET OF Filter,
341 * or [1] SET OF Filter,
342 * not [2] Filter,
343 * equalityMatch [3] AttributeValueAssertion,
344 * substrings [4] SubstringFilter,
345 * greaterOrEqual [5] AttributeValueAssertion,
346 * lessOrEqual [6] AttributeValueAssertion,
347 * present [7] AttributeType,
348 * approxMatch [8] AttributeValueAssertion,
349 * extensibleMatch [9] MatchingRuleAssertion
350 * }
351 *
352 * SubstringFilter ::= SEQUENCE {
353 * type AttributeType,
354 * SEQUENCE OF CHOICE {
355 * initial [0] IA5String,
356 * any [1] IA5String,
357 * final [2] IA5String
358 * }
359 * }
360 * MatchingRuleAssertion ::= SEQUENCE {
361 * matchingRule [1] MatchingRuleId OPTIONAL,
362 * type [2] AttributeDescription OPTIONAL,
363 * matchValue [3] AssertionValue,
364 * dnAttributes [4] BOOLEAN DEFAULT FALSE
365 * }
366 *
367 * Note: tags in a choice are always explicit
368 */
369
370 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 243,
371 "put_filter \"%s\"\n"), str, 0, 0);
372
373 parens = 0;
374 while (*str) {
375 switch (*str) {
376 case '(':
377 str++;
378 parens++;
379 switch (*str) {
380 case '&':
381 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
382 244, "put_filter: AND\n"), 0, 0, 0);
383
384 if ((str = put_complex_filter(ber, str,
385 LDAP_FILTER_AND, 0)) == NULL)
386 return (-1);
387
388 parens--;
389 break;
390
391 case '|':
392 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
393 245, "put_filter: OR\n"), 0, 0, 0);
394
395 if ((str = put_complex_filter(ber, str,
396 LDAP_FILTER_OR, 0)) == NULL)
397 return (-1);
398
399 parens--;
400 break;
401
402 case '!':
403 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
404 246, "put_filter: NOT\n"), 0, 0, 0);
405
406 if ((str = put_complex_filter(ber, str,
407 LDAP_FILTER_NOT, 1)) == NULL)
408 return (-1);
409
410 parens--;
411 break;
412
413 case '(':
414 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
415 402, "put_filter: Double Parentheses\n"),
416 0, 0, 0);
417 multipleparen++;
418 continue;
419
420 default:
421 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
422 247, "put_filter: simple\n"), 0, 0, 0);
423
424 balance = 1;
425 escape = 0;
426 next = str;
427 while (*next && balance) {
428 if (escape == 0) {
429 if (*next == '(')
430 balance++;
431 else if (*next == ')')
432 balance--;
433 }
434 if (*next == '\\' && ! escape)
435 escape = 1;
436 else
437 escape = 0;
438 if (balance)
439 next++;
440 }
441 if (balance != 0)
442 return (-1);
443
444 *next = '\0';
445 if (put_simple_filter(ber, str) == -1)
446 return (-1);
447 *next++ = ')';
448 str = next;
449 parens--;
450 break;
451 }
452 break;
453
454 case ')':
455 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 248,
456 "put_filter: end\n"), 0, 0, 0);
457 if (multipleparen) {
458 multipleparen--;
459 } else {
460 if (ber_printf(ber, "]") == -1)
461 return (-1);
462 }
463
464 str++;
465 parens--;
466 break;
467
468 case ' ':
469 str++;
470 break;
471
472 default: /* assume it's a simple type=value filter */
473 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 249,
474 "put_filter: default\n"), 0, 0, 0);
475 next = strchr(str, '\0');
476 if (put_simple_filter(ber, str) == -1) {
477 return (-1);
478 }
479 str = next;
480 break;
481 }
482 }
483
484 return (parens ? -1 : 0);
485 }
486
487 /*
488 * Put a list of filters like this "(filter1)(filter2)..."
489 */
490
491 static int
put_filter_list(BerElement * ber,char * str)492 put_filter_list(BerElement *ber, char *str)
493 {
494 char *next;
495 char save;
496
497 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 250,
498 "put_filter_list \"%s\"\n"), str, 0, 0);
499
500 while (*str) {
501 while (*str && isspace(*str))
502 str++;
503 if (*str == '\0')
504 break;
505
506 if ((next = find_right_paren(str + 1)) == NULL)
507 return (-1);
508 save = *++next;
509
510 /* now we have "(filter)" with str pointing to it */
511 *next = '\0';
512 if (put_filter(ber, str) == -1)
513 return (-1);
514 *next = save;
515
516 str = next;
517 }
518
519 return (0);
520 }
521
522 static int
put_simple_filter(BerElement * ber,char * str)523 put_simple_filter(BerElement *ber, char *str)
524 {
525 char *s;
526 char *value, savechar;
527 unsigned int ftype;
528 int rc;
529 int len;
530
531 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 251,
532 "put_simple_filter \"%s\"\n"), str, 0, 0);
533
534 if ((s = strchr(str, '=')) == NULL)
535 return (-1);
536 value = s + 1;
537 *s-- = '\0';
538 savechar = *s;
539
540 switch (*s) {
541 case '<':
542 ftype = LDAP_FILTER_LE;
543 *s = '\0';
544 break;
545 case '>':
546 ftype = LDAP_FILTER_GE;
547 *s = '\0';
548 break;
549 case '~':
550 ftype = LDAP_FILTER_APPROX;
551 *s = '\0';
552 break;
553 /* LDAP V3 : New extensible matching */
554 case ':':
555 rc = put_extensible_filter(ber, str, value);
556 *(value -1) = '=';
557 return (rc);
558 default:
559 if (star_search(value) == NULL) {
560 ftype = LDAP_FILTER_EQUALITY;
561 } else if (strcmp(value, "*") == 0) {
562 ftype = LDAP_FILTER_PRESENT;
563 } else {
564 rc = put_substring_filter(ber, str, value);
565 *(value-1) = '=';
566 return (rc);
567 }
568 break;
569 }
570
571 if (*(value -1) == '=')
572 return (rc);
573 if (ftype == LDAP_FILTER_PRESENT) {
574 rc = ber_printf(ber, "ts", ftype, str);
575 } else {
576 if ((len = decode_value(value)) >= 0)
577 rc = ber_printf(ber, "t{so}", ftype, str, value, len);
578 }
579
580 *s = savechar;
581 *(value-1) = '=';
582 return (rc == -1 ? rc : 0);
583 }
584
585 static int
put_substring_filter(BerElement * ber,char * type,char * val)586 put_substring_filter(BerElement *ber, char *type, char *val)
587 {
588 char *nextstar, gotstar = 0;
589 unsigned int ftype;
590 int len;
591
592 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 252,
593 "put_substring_filter \"%1$s=%2$s\"\n"), type, val, 0);
594
595 if (ber_printf(ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type) == -1)
596 return (-1);
597
598 while (val != NULL) {
599 if ((nextstar = star_search(val)) != NULL)
600 *nextstar++ = '\0';
601
602 if (gotstar == 0) {
603 ftype = LDAP_SUBSTRING_INITIAL;
604 } else if (nextstar == NULL) {
605 ftype = LDAP_SUBSTRING_FINAL;
606 } else {
607 ftype = LDAP_SUBSTRING_ANY;
608 }
609 if (*val != '\0') {
610 if ((len = decode_value(val)) == -1 ||
611 ber_printf(ber, "to", ftype, val, len) == -1)
612 return (-1);
613 }
614
615 gotstar = 1;
616 if (nextstar != NULL)
617 *(nextstar-1) = '*';
618 val = nextstar;
619 }
620
621 if (ber_printf(ber, "}}") == -1)
622 return (-1);
623
624 return (0);
625 }
626
627 static int
put_extensible_filter(BerElement * ber,char * type,char * val)628 put_extensible_filter(BerElement *ber, char *type, char *val)
629 {
630 char *ptr, *ptype;
631 char *dn, *rule;
632 int len;
633
634 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 252,
635 "put_extensible_filter \"%1$s=%2$s\"\n"), type, val, 0);
636
637 /* type is off form : attr:dn:matchingrule: or :dn:matchingrule: */
638 /* type ends with ':', suppress it */
639 ptr = strdup(type);
640 ptype = ptr;
641 while (*ptype) {
642 *ptype = tolower(*ptype);
643 ptype++;
644 }
645
646 len = strlen(ptr);
647 if (len > 0 && ptr[len -1] == ':')
648 ptr [len - 1] = '\0';
649 else {
650 return (-1);
651 }
652
653 ptype = ptr;
654 /* Search first ':dn' */
655 if ((dn = strstr(ptype, ":dn")) == NULL) {
656 /* No dn */
657 /* if there's a : its separating type and matching rule */
658 rule = strchr(ptype, ':');
659 if (rule == ptype) {
660 ptype = NULL;
661 }
662 } else {
663 if (dn == ptype) {
664 ptype = NULL;
665 } else {
666 *dn = '\0';
667 }
668
669 rule = dn + 3;
670 }
671
672 if (rule && rule[0] == ':') {
673 rule[0] = '\0';
674 rule++;
675 } else {
676 rule = NULL;
677 }
678
679 if ((ptype == NULL || *ptype == '\0') && rule == NULL) {
680 free(ptr);
681 return (-1);
682 }
683
684 if (ber_printf(ber, "t{", LDAP_FILTER_EXTENSIBLE) == -1) {
685 free(ptr);
686 return (-1);
687 }
688
689 if (rule && *rule && (ber_printf(ber, "ts",
690 LDAP_TAG_FEXT_RULE, rule) == -1)) {
691 free(ptr);
692 return (-1);
693 }
694
695 if (ptype && *ptype && (ber_printf(ber, "ts",
696 LDAP_TAG_FEXT_TYPE, ptype) == -1)) {
697 free(ptr);
698 return (-1);
699 }
700
701 /* Code value */
702 if ((len = decode_value(val)) == -1 ||
703 ber_printf(ber, "to", LDAP_TAG_FEXT_VAL, val, len) == -1) {
704 free(ptr);
705 return (-1);
706 }
707
708 if (dn && (ber_printf(ber, "tb", LDAP_TAG_FEXT_DN, 1) == -1)) {
709 free(ptr);
710 return (-1);
711 }
712
713 free(ptr);
714
715 if (ber_printf(ber, "}") == -1)
716 return (-1);
717
718 return (0);
719 }
720
721 int
ldap_search_st(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,struct timeval * timeout,LDAPMessage ** res)722 ldap_search_st(LDAP *ld, char *base, int scope, char *filter, char **attrs,
723 int attrsonly, struct timeval *timeout, LDAPMessage **res)
724 {
725 int msgid;
726
727 if ((msgid = ldap_search(ld, base, scope, filter, attrs, attrsonly))
728 == -1)
729 return (ld->ld_errno);
730
731 if (ldap_result(ld, msgid, 1, timeout, res) == -1)
732 return (ld->ld_errno);
733
734 if (ld->ld_errno == LDAP_TIMEOUT) {
735 (void) ldap_abandon(ld, msgid);
736 ld->ld_errno = LDAP_TIMEOUT;
737 return (ld->ld_errno);
738 }
739
740 return (ldap_result2error(ld, *res, 0));
741 }
742
743 int
ldap_search_s(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,LDAPMessage ** res)744 ldap_search_s(LDAP *ld, char *base, int scope, char *filter, char **attrs,
745 int attrsonly, LDAPMessage **res)
746 {
747 int msgid;
748
749 if ((msgid = ldap_search(ld, base, scope, filter, attrs, attrsonly))
750 == -1)
751 return (ld->ld_errno);
752
753 if (ldap_result(ld, msgid, 1, (struct timeval *)NULL, res) == -1)
754 return (ld->ld_errno);
755
756 return (ldap_result2error(ld, *res, 0));
757 }
758
759 /* LDAPv3 API EXTENSIONS */
ldap_search_ext(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,struct timeval * timeoutp,int sizelimit,int * msgidp)760 int ldap_search_ext(LDAP *ld, char *base, int scope, char *filter,
761 char **attrs, int attrsonly, LDAPControl **serverctrls,
762 LDAPControl **clientctrls, struct timeval *timeoutp, int sizelimit,
763 int *msgidp)
764 {
765 BerElement *ber;
766 int rv;
767
768 if (timeoutp != NULL && timeoutp->tv_sec == 0 &&
769 timeoutp->tv_usec == 0) {
770 timeoutp = NULL;
771 }
772
773 #ifdef _REENTRANT
774 LOCK_LDAP(ld);
775 #endif
776 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 242,
777 "ldap_search\n"), 0, 0, 0);
778
779 if ((ber = ldap_build_search_req(ld, base, scope, filter, attrs,
780 attrsonly, serverctrls, timeoutp, sizelimit)) == NULLBER) {
781 rv = ld->ld_errno;
782 if (rv == LDAP_SUCCESS)
783 rv = LDAP_OTHER;
784 #ifdef _REENTRANT
785 UNLOCK_LDAP(ld);
786 #endif
787 return (rv);
788 }
789
790 #ifndef NO_CACHE
791 if (ld->ld_cache != NULL) {
792 if (check_cache(ld, LDAP_REQ_SEARCH, ber) == 0) {
793 ber_free(ber, 1);
794 ld->ld_errno = LDAP_SUCCESS;
795 *msgidp = ld->ld_msgid;
796 #ifdef _REENTRANT
797 UNLOCK_LDAP(ld);
798 #endif
799 return (LDAP_SUCCESS);
800 }
801 add_request_to_cache(ld, LDAP_REQ_SEARCH, ber);
802 }
803 #endif /* NO_CACHE */
804
805 /* send the message */
806 rv = send_initial_request(ld, LDAP_REQ_SEARCH, base, ber);
807 if (rv == -1) {
808 rv = ld->ld_errno;
809 if (rv == LDAP_SUCCESS) {
810 rv = LDAP_OTHER;
811 }
812 #ifdef _REENTRANT
813 UNLOCK_LDAP(ld);
814 #endif
815 return (rv);
816 }
817
818 *msgidp = rv;
819 #if _REENTRANT
820 UNLOCK_LDAP(ld);
821 #endif
822 return (LDAP_SUCCESS);
823 }
824
825
ldap_search_ext_s(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,LDAPControl ** serverctrls,LDAPControl ** clientctrls,struct timeval * timeoutp,int sizelimit,LDAPMessage ** res)826 int ldap_search_ext_s(LDAP *ld, char *base, int scope, char *filter,
827 char **attrs, int attrsonly, LDAPControl **serverctrls,
828 LDAPControl **clientctrls, struct timeval *timeoutp, int sizelimit,
829 LDAPMessage **res)
830 {
831 int msgid;
832 int retcode = LDAP_SUCCESS;
833
834 if ((retcode = ldap_search_ext(ld, base, scope, filter, attrs,
835 attrsonly, serverctrls, clientctrls, timeoutp, sizelimit,
836 &msgid)) != LDAP_SUCCESS)
837 return (retcode);
838 if (ldap_result(ld, msgid, 1, timeoutp, res) == -1)
839 return (ld->ld_errno);
840
841
842 #if _REENTRANT
843 LOCK_LDAP(ld);
844 #endif
845 retcode = ldap_parse_result(ld, *res, &ld->ld_errno, &ld->ld_matched,
846 &ld->ld_error, &ld->ld_referrals, &ld->ld_ret_ctrls, 0);
847 if (retcode == LDAP_SUCCESS)
848 retcode = ld->ld_errno;
849 #if _REENTRANT
850 UNLOCK_LDAP(ld);
851 #endif
852 return (retcode);
853 }
854
855 /*
856 * Search string for ascii '*' (asterisk) character.
857 * RFC 1960 permits an escaped asterisk to pass through.
858 * RFC 2254 adds the definition of encoded characters:
859 *
860 * Character ASCII value
861 * ---------------------------
862 * * 0x2a
863 * ( 0x28
864 * ) 0x29
865 * \ 0x5c
866 * NUL 0x00
867 *
868 * No distinction of escaped characters is made here.
869 */
870 static char *
star_search(char * str)871 star_search(char *str)
872 {
873 for (; *str; str++) {
874 switch (*str) {
875 case '*':
876 return (str);
877 case '\\':
878 if (str[1] == '\0')
879 break; /* input string exahausted */
880 ++str; /* Assume RFC 1960 escaped character */
881 /* Check for RFC 2254 hex encoding */
882 if (hex_char2int(str[0]) >= 0 &&
883 hex_char2int(str[1]) >= 0) {
884 str++; /* skip over RFC 2254 hex encoding */
885 }
886 default:
887 break;
888 }
889 }
890 return (NULL);
891 }
892
893 /*
894 * Return integer value of hexadecimal character or (-1) if character is
895 * not a hexadecimal digit [0-9A-Fa-f].
896 */
897 static int
hex_char2int(char c)898 hex_char2int(char c)
899 {
900 if (c >= '0' && c <= '9') {
901 return (c-'0');
902 } else if (c >= 'A' && c <= 'F') {
903 return (c-'A'+10);
904 } else if (c >= 'a' && c <= 'f') {
905 return (c-'a'+10);
906 }
907 return (-1);
908 }
909
910 /*
911 * Modifys passed string converting escaped hexadecimal characters as
912 * per RFC 2254 and un-escapes escaped characters. Returns length of
913 * modified string as it may contain null characters as per RFC 2254.
914 */
915 static int
decode_value(char * start)916 decode_value(char *start)
917 {
918 char *read, *write;
919 int hn, ln;
920
921 for (read = write = start; *read; *write++ = *read++) {
922 if (*read == '\\') {
923 if (*++read == '\0')
924 break; /* input string exahausted */
925 /*
926 * Assume *read is simple RFC 1960 escaped character.
927 * However check for RFC 2254 hex encoding.
928 */
929 if ((hn = hex_char2int(read[0])) >= 0 &&
930 (ln = hex_char2int(read[1])) >= 0) {
931 read++;
932 *read = (hn<<4)+ln;
933 }
934 }
935 }
936 *write = '\0';
937 return (write-start);
938 }
939
940