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