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