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