1 /*        $NetBSD: search.c,v 1.3 2021/08/14 16:14:56 christos Exp $  */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
18  * All rights reserved.
19  */
20 
21 #include <sys/cdefs.h>
22 __RCSID("$NetBSD: search.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
23 
24 #include "portable.h"
25 
26 #include <stdio.h>
27 
28 #include <ac/stdlib.h>
29 
30 #include <ac/socket.h>
31 #include <ac/string.h>
32 #include <ac/time.h>
33 
34 #include "ldap-int.h"
35 #include "ldap_log.h"
36 
37 /*
38  * ldap_search_ext - initiate an ldap search operation.
39  *
40  * Parameters:
41  *
42  *        ld                  LDAP descriptor
43  *        base                DN of the base object
44  *        scope               the search scope - one of
45  *                                      LDAP_SCOPE_BASE (baseObject),
46  *                                LDAP_SCOPE_ONELEVEL (oneLevel),
47  *                                      LDAP_SCOPE_SUBTREE (subtree), or
48  *                                      LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
49  *        filter              a string containing the search filter
50  *                            (e.g., "(|(cn=bob)(sn=bob))")
51  *        attrs               list of attribute types to return for matches
52  *        attrsonly 1 => attributes only 0 => attributes and values
53  *
54  * Example:
55  *        char      *attrs[] = { "mail", "title", 0 };
56  *        ldap_search_ext( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
57  *            attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
58  *                  &msgid );
59  */
60 int
ldap_search_ext(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,LDAPControl ** sctrls,LDAPControl ** cctrls,struct timeval * timeout,int sizelimit,int * msgidp)61 ldap_search_ext(
62           LDAP *ld,
63           LDAP_CONST char *base,
64           int scope,
65           LDAP_CONST char *filter,
66           char **attrs,
67           int attrsonly,
68           LDAPControl **sctrls,
69           LDAPControl **cctrls,
70           struct timeval *timeout,
71           int sizelimit,
72           int *msgidp )
73 {
74           return ldap_pvt_search( ld, base, scope, filter, attrs,
75                     attrsonly, sctrls, cctrls, timeout, sizelimit, -1, msgidp );
76 }
77 
78 int
ldap_pvt_search(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,LDAPControl ** sctrls,LDAPControl ** cctrls,struct timeval * timeout,int sizelimit,int deref,int * msgidp)79 ldap_pvt_search(
80           LDAP *ld,
81           LDAP_CONST char *base,
82           int scope,
83           LDAP_CONST char *filter,
84           char **attrs,
85           int attrsonly,
86           LDAPControl **sctrls,
87           LDAPControl **cctrls,
88           struct timeval *timeout,
89           int sizelimit,
90           int deref,
91           int *msgidp )
92 {
93           int rc;
94           BerElement          *ber;
95           int timelimit;
96           ber_int_t id;
97 
98           Debug0( LDAP_DEBUG_TRACE, "ldap_search_ext\n" );
99 
100           assert( ld != NULL );
101           assert( LDAP_VALID( ld ) );
102 
103           /* check client controls */
104           rc = ldap_int_client_controls( ld, cctrls );
105           if( rc != LDAP_SUCCESS ) return rc;
106 
107           /*
108            * if timeout is provided, both tv_sec and tv_usec must
109            * not be zero
110            */
111           if( timeout != NULL ) {
112                     if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
113                               return LDAP_PARAM_ERROR;
114                     }
115 
116                     /* timelimit must be non-zero if timeout is provided */
117                     timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
118 
119           } else {
120                     /* no timeout, no timelimit */
121                     timelimit = -1;
122           }
123 
124           ber = ldap_build_search_req( ld, base, scope, filter, attrs,
125               attrsonly, sctrls, cctrls, timelimit, sizelimit, deref, &id );
126 
127           if ( ber == NULL ) {
128                     return ld->ld_errno;
129           }
130 
131 
132           /* send the message */
133           *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id );
134 
135           if( *msgidp < 0 )
136                     return ld->ld_errno;
137 
138           return LDAP_SUCCESS;
139 }
140 
141 int
ldap_search_ext_s(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,LDAPControl ** sctrls,LDAPControl ** cctrls,struct timeval * timeout,int sizelimit,LDAPMessage ** res)142 ldap_search_ext_s(
143           LDAP *ld,
144           LDAP_CONST char *base,
145           int scope,
146           LDAP_CONST char *filter,
147           char **attrs,
148           int attrsonly,
149           LDAPControl **sctrls,
150           LDAPControl **cctrls,
151           struct timeval *timeout,
152           int sizelimit,
153           LDAPMessage **res )
154 {
155           return ldap_pvt_search_s( ld, base, scope, filter, attrs,
156                     attrsonly, sctrls, cctrls, timeout, sizelimit, -1, res );
157 }
158 
159 int
ldap_pvt_search_s(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,LDAPControl ** sctrls,LDAPControl ** cctrls,struct timeval * timeout,int sizelimit,int deref,LDAPMessage ** res)160 ldap_pvt_search_s(
161           LDAP *ld,
162           LDAP_CONST char *base,
163           int scope,
164           LDAP_CONST char *filter,
165           char **attrs,
166           int attrsonly,
167           LDAPControl **sctrls,
168           LDAPControl **cctrls,
169           struct timeval *timeout,
170           int sizelimit,
171           int deref,
172           LDAPMessage **res )
173 {
174           int rc;
175           int       msgid;
176 
177     *res = NULL;
178 
179           rc = ldap_pvt_search( ld, base, scope, filter, attrs, attrsonly,
180                     sctrls, cctrls, timeout, sizelimit, deref, &msgid );
181 
182           if ( rc != LDAP_SUCCESS ) {
183                     return( rc );
184           }
185 
186           rc = ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res );
187 
188           if( rc <= 0 ) {
189                     /* error(-1) or timeout(0) */
190                     if ( ld->ld_errno == LDAP_TIMEOUT ) {
191                               /* cleanup request */
192                               (void) ldap_abandon( ld, msgid );
193                               ld->ld_errno = LDAP_TIMEOUT;
194                     }
195                     return( ld->ld_errno );
196           }
197 
198           if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_INTERMEDIATE ) {
199                     return( ld->ld_errno );
200           }
201 
202           return( ldap_result2error( ld, *res, 0 ) );
203 }
204 
205 /*
206  * ldap_search - initiate an ldap search operation.
207  *
208  * Parameters:
209  *
210  *        ld                  LDAP descriptor
211  *        base                DN of the base object
212  *        scope               the search scope - one of
213  *                                      LDAP_SCOPE_BASE (baseObject),
214  *                                LDAP_SCOPE_ONELEVEL (oneLevel),
215  *                                      LDAP_SCOPE_SUBTREE (subtree), or
216  *                                      LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
217  *        filter              a string containing the search filter
218  *                            (e.g., "(|(cn=bob)(sn=bob))")
219  *        attrs               list of attribute types to return for matches
220  *        attrsonly 1 => attributes only 0 => attributes and values
221  *
222  * Example:
223  *        char      *attrs[] = { "mail", "title", 0 };
224  *        msgid = ldap_search( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
225  *            attrs, attrsonly );
226  */
227 int
ldap_search(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly)228 ldap_search(
229           LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
230           char **attrs, int attrsonly )
231 {
232           BerElement          *ber;
233           ber_int_t id;
234 
235           Debug0( LDAP_DEBUG_TRACE, "ldap_search\n" );
236 
237           assert( ld != NULL );
238           assert( LDAP_VALID( ld ) );
239 
240           ber = ldap_build_search_req( ld, base, scope, filter, attrs,
241               attrsonly, NULL, NULL, -1, -1, -1, &id );
242 
243           if ( ber == NULL ) {
244                     return( -1 );
245           }
246 
247 
248           /* send the message */
249           return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id ));
250 }
251 
252 
253 BerElement *
ldap_build_search_req(LDAP * ld,LDAP_CONST char * base,ber_int_t scope,LDAP_CONST char * filter,char ** attrs,ber_int_t attrsonly,LDAPControl ** sctrls,LDAPControl ** cctrls,ber_int_t timelimit,ber_int_t sizelimit,ber_int_t deref,ber_int_t * idp)254 ldap_build_search_req(
255           LDAP *ld,
256           LDAP_CONST char *base,
257           ber_int_t scope,
258           LDAP_CONST char *filter,
259           char **attrs,
260           ber_int_t attrsonly,
261           LDAPControl **sctrls,
262           LDAPControl **cctrls,
263           ber_int_t timelimit,
264           ber_int_t sizelimit,
265           ber_int_t deref,
266           ber_int_t *idp)
267 {
268           BerElement          *ber;
269           int                 err;
270 
271           /*
272            * Create the search request.  It looks like this:
273            *        SearchRequest := [APPLICATION 3] SEQUENCE {
274            *                  baseObject          DistinguishedName,
275            *                  scope               ENUMERATED {
276            *                            baseObject          (0),
277            *                            singleLevel         (1),
278            *                            wholeSubtree        (2)
279            *                  },
280            *                  derefAliases        ENUMERATED {
281            *                            neverDerefaliases   (0),
282            *                            derefInSearching    (1),
283            *                            derefFindingBaseObj (2),
284            *                            alwaysDerefAliases  (3)
285            *                  },
286            *                  sizelimit INTEGER (0 .. 65535),
287            *                  timelimit INTEGER (0 .. 65535),
288            *                  attrsOnly BOOLEAN,
289            *                  filter              Filter,
290            *                  attributes          SEQUENCE OF AttributeType
291            *        }
292            * wrapped in an ldap message.
293            */
294 
295           /* create a message to send */
296           if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
297                     return( NULL );
298           }
299 
300           if ( base == NULL ) {
301                     /* no base provided, use session default base */
302                     base = ld->ld_options.ldo_defbase;
303 
304                     if ( base == NULL ) {
305                               /* no session default base, use top */
306                               base = "";
307                     }
308           }
309 
310           LDAP_NEXT_MSGID( ld, *idp );
311 #ifdef LDAP_CONNECTIONLESS
312           if ( LDAP_IS_UDP(ld) ) {
313                     struct sockaddr_storage sa = {0};
314                     /* dummy, filled with ldo_peer in request.c */
315               err = ber_write( ber, (char *) &sa, sizeof( sa ), 0 );
316           }
317           if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2) {
318               char *dn = ld->ld_options.ldo_cldapdn;
319               if (!dn) dn = "";
320               err = ber_printf( ber, "{ist{seeiib", *idp, dn,
321                     LDAP_REQ_SEARCH, base, (ber_int_t) scope,
322                     (deref < 0) ? ld->ld_deref : deref,
323                     (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
324                     (timelimit < 0) ? ld->ld_timelimit : timelimit,
325                     attrsonly );
326           } else
327 #endif
328           {
329               err = ber_printf( ber, "{it{seeiib", *idp,
330                     LDAP_REQ_SEARCH, base, (ber_int_t) scope,
331                     (deref < 0) ? ld->ld_deref : deref,
332                     (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
333                     (timelimit < 0) ? ld->ld_timelimit : timelimit,
334                     attrsonly );
335           }
336 
337           if ( err == -1 ) {
338                     ld->ld_errno = LDAP_ENCODING_ERROR;
339                     ber_free( ber, 1 );
340                     return( NULL );
341           }
342 
343           if( filter == NULL ) {
344                     filter = "(objectclass=*)";
345           }
346 
347           err = ldap_pvt_put_filter( ber, filter );
348 
349           if ( err  == -1 ) {
350                     ld->ld_errno = LDAP_FILTER_ERROR;
351                     ber_free( ber, 1 );
352                     return( NULL );
353           }
354 
355 #ifdef LDAP_DEBUG
356           if ( ldap_debug & LDAP_DEBUG_ARGS ) {
357                     char      buf[ BUFSIZ ], *ptr = " *";
358 
359                     if ( attrs != NULL ) {
360                               int       i, len, rest = sizeof( buf );
361 
362                               for ( i = 0; attrs[ i ] != NULL && rest > 0; i++ ) {
363                                         ptr = &buf[ sizeof( buf ) - rest ];
364                                         len = snprintf( ptr, rest, " %s", attrs[ i ] );
365                                         rest -= (len >= 0 ? len : (int) sizeof( buf ));
366                               }
367 
368                               if ( rest <= 0 ) {
369                                         AC_MEMCPY( &buf[ sizeof( buf ) - STRLENOF( "...(truncated)" ) - 1 ],
370                                                   "...(truncated)", STRLENOF( "...(truncated)" ) + 1 );
371                               }
372                               ptr = buf;
373                     }
374 
375                     Debug1( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr );
376           }
377 #endif /* LDAP_DEBUG */
378 
379           if ( ber_printf( ber, /*{*/ "{v}N}", attrs ) == -1 ) {
380                     ld->ld_errno = LDAP_ENCODING_ERROR;
381                     ber_free( ber, 1 );
382                     return( NULL );
383           }
384 
385           /* Put Server Controls */
386           if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
387                     ber_free( ber, 1 );
388                     return( NULL );
389           }
390 
391           if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
392                     ld->ld_errno = LDAP_ENCODING_ERROR;
393                     ber_free( ber, 1 );
394                     return( NULL );
395           }
396 
397           return( ber );
398 }
399 
400 int
ldap_search_st(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,struct timeval * timeout,LDAPMessage ** res)401 ldap_search_st(
402           LDAP *ld, LDAP_CONST char *base, int scope,
403           LDAP_CONST char *filter, char **attrs,
404           int attrsonly, struct timeval *timeout, LDAPMessage **res )
405 {
406           int       msgid;
407 
408     *res = NULL;
409 
410           if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
411               == -1 )
412                     return( ld->ld_errno );
413 
414           if ( ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ) == -1 || !*res )
415                     return( ld->ld_errno );
416 
417           if ( ld->ld_errno == LDAP_TIMEOUT ) {
418                     (void) ldap_abandon( ld, msgid );
419                     ld->ld_errno = LDAP_TIMEOUT;
420                     return( ld->ld_errno );
421           }
422 
423           return( ldap_result2error( ld, *res, 0 ) );
424 }
425 
426 int
ldap_search_s(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,LDAPMessage ** res)427 ldap_search_s(
428           LDAP *ld,
429           LDAP_CONST char *base,
430           int scope,
431           LDAP_CONST char *filter,
432           char **attrs,
433           int attrsonly,
434           LDAPMessage **res )
435 {
436           int       msgid;
437 
438     *res = NULL;
439 
440           if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
441               == -1 )
442                     return( ld->ld_errno );
443 
444           if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, res ) == -1 || !*res )
445                     return( ld->ld_errno );
446 
447           return( ldap_result2error( ld, *res, 0 ) );
448 }
449 
450 static char escape[128] = {
451           1, 1, 1, 1, 1, 1, 1, 1,
452           1, 1, 1, 1, 1, 1, 1, 1,
453           1, 1, 1, 1, 1, 1, 1, 1,
454           1, 1, 1, 1, 1, 1, 1, 1,
455 
456           0, 0, 0, 0, 0, 0, 0, 0,
457           1, 1, 1, 0, 0, 0, 0, 0,
458           0, 0, 0, 0, 0, 0, 0, 0,
459           0, 0, 0, 0, 0, 0, 0, 0,
460 
461           0, 0, 0, 0, 0, 0, 0, 0,
462           0, 0, 0, 0, 0, 0, 0, 0,
463           0, 0, 0, 0, 0, 0, 0, 0,
464           0, 0, 0, 0, 1, 0, 0, 0,
465 
466           0, 0, 0, 0, 0, 0, 0, 0,
467           0, 0, 0, 0, 0, 0, 0, 0,
468           0, 0, 0, 0, 0, 0, 0, 0,
469           0, 0, 0, 0, 0, 0, 0, 1
470 };
471 #define   NEEDFLTESCAPE(c)    ((c) & 0x80 || escape[ (unsigned)(c) ])
472 
473 /*
474  * compute the length of the escaped value
475  */
476 ber_len_t
ldap_bv2escaped_filter_value_len(struct berval * in)477 ldap_bv2escaped_filter_value_len( struct berval *in )
478 {
479           ber_len_t i, l;
480 
481           assert( in != NULL );
482 
483           if ( in->bv_len == 0 ) {
484                     return 0;
485           }
486 
487           for( l = 0, i = 0; i < in->bv_len; l++, i++ ) {
488                     char c = in->bv_val[ i ];
489                     if ( NEEDFLTESCAPE( c ) ) {
490                               l += 2;
491                     }
492           }
493 
494           return l;
495 }
496 
497 int
ldap_bv2escaped_filter_value(struct berval * in,struct berval * out)498 ldap_bv2escaped_filter_value( struct berval *in, struct berval *out )
499 {
500           return ldap_bv2escaped_filter_value_x( in, out, 0, NULL );
501 }
502 
503 int
ldap_bv2escaped_filter_value_x(struct berval * in,struct berval * out,int inplace,void * ctx)504 ldap_bv2escaped_filter_value_x( struct berval *in, struct berval *out, int inplace, void *ctx )
505 {
506           ber_len_t i, l;
507 
508           assert( in != NULL );
509           assert( out != NULL );
510 
511           BER_BVZERO( out );
512 
513           if ( in->bv_len == 0 ) {
514                     return 0;
515           }
516 
517           /* assume we'll escape everything */
518           l = ldap_bv2escaped_filter_value_len( in );
519           if ( l == in->bv_len ) {
520                     if ( inplace ) {
521                               *out = *in;
522                     } else {
523                               ber_dupbv( out, in );
524                     }
525                     return 0;
526           }
527           out->bv_val = LDAP_MALLOCX( l + 1, ctx );
528           if ( out->bv_val == NULL ) {
529                     return -1;
530           }
531 
532           for ( i = 0; i < in->bv_len; i++ ) {
533                     char c = in->bv_val[ i ];
534                     if ( NEEDFLTESCAPE( c ) ) {
535                               assert( out->bv_len < l - 2 );
536                               out->bv_val[out->bv_len++] = '\\';
537                               out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & (c>>4)];
538                               out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & c];
539 
540                     } else {
541                               assert( out->bv_len < l );
542                               out->bv_val[out->bv_len++] = c;
543                     }
544           }
545 
546           out->bv_val[out->bv_len] = '\0';
547 
548           return 0;
549 }
550 
551