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