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