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