1/*	$NetBSD: search.c,v 1.1.1.3 2010/12/12 15:22:45 adam Exp $	*/
2
3/* OpenLDAP: pkg/ldap/servers/slapd/search.c,v 1.181.2.11 2010/04/13 20:23:19 kurt 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) 1995 Regents of the University of Michigan.
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms are permitted
21 * provided that this notice is preserved and that due credit is given
22 * to the University of Michigan at Ann Arbor. The name of the University
23 * may not be used to endorse or promote products derived from this
24 * software without specific prior written permission. This software
25 * is provided ``as is'' without express or implied warranty.
26 */
27
28#include "portable.h"
29
30#include <stdio.h>
31
32#include <ac/string.h>
33#include <ac/socket.h>
34
35#include "lutil.h"
36#include "slap.h"
37
38int
39do_search(
40    Operation	*op,	/* info about the op to which we're responding */
41    SlapReply	*rs	/* all the response data we'll send */ )
42{
43	struct berval base = BER_BVNULL;
44	ber_len_t	siz, off, i;
45
46	Debug( LDAP_DEBUG_TRACE, "%s do_search\n",
47		op->o_log_prefix, 0, 0 );
48	/*
49	 * Parse the search request.  It looks like this:
50	 *
51	 *	SearchRequest := [APPLICATION 3] SEQUENCE {
52	 *		baseObject	DistinguishedName,
53	 *		scope		ENUMERATED {
54	 *			baseObject	(0),
55	 *			singleLevel	(1),
56	 *			wholeSubtree (2),
57	 *          subordinate (3)  -- OpenLDAP extension
58	 *		},
59	 *		derefAliases	ENUMERATED {
60	 *			neverDerefaliases	(0),
61	 *			derefInSearching	(1),
62	 *			derefFindingBaseObj	(2),
63	 *			alwaysDerefAliases	(3)
64	 *		},
65	 *		sizelimit	INTEGER (0 .. 65535),
66	 *		timelimit	INTEGER (0 .. 65535),
67	 *		attrsOnly	BOOLEAN,
68	 *		filter		Filter,
69	 *		attributes	SEQUENCE OF AttributeType
70	 *	}
71	 */
72
73	/* baseObject, scope, derefAliases, sizelimit, timelimit, attrsOnly */
74	if ( ber_scanf( op->o_ber, "{miiiib" /*}*/,
75		&base, &op->ors_scope, &op->ors_deref, &op->ors_slimit,
76	    &op->ors_tlimit, &op->ors_attrsonly ) == LBER_ERROR )
77	{
78		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
79		rs->sr_err = SLAPD_DISCONNECT;
80		goto return_results;
81	}
82
83	if ( op->ors_tlimit < 0 || op->ors_tlimit > SLAP_MAX_LIMIT ) {
84		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid time limit" );
85		goto return_results;
86	}
87
88	if ( op->ors_slimit < 0 || op->ors_slimit > SLAP_MAX_LIMIT ) {
89		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid size limit" );
90		goto return_results;
91	}
92
93	switch( op->ors_scope ) {
94	case LDAP_SCOPE_BASE:
95	case LDAP_SCOPE_ONELEVEL:
96	case LDAP_SCOPE_SUBTREE:
97	case LDAP_SCOPE_SUBORDINATE:
98		break;
99	default:
100		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid scope" );
101		goto return_results;
102	}
103
104	switch( op->ors_deref ) {
105	case LDAP_DEREF_NEVER:
106	case LDAP_DEREF_FINDING:
107	case LDAP_DEREF_SEARCHING:
108	case LDAP_DEREF_ALWAYS:
109		break;
110	default:
111		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid deref" );
112		goto return_results;
113	}
114
115	rs->sr_err = dnPrettyNormal( NULL, &base, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
116	if( rs->sr_err != LDAP_SUCCESS ) {
117		Debug( LDAP_DEBUG_ANY, "%s do_search: invalid dn: \"%s\"\n",
118			op->o_log_prefix, base.bv_val, 0 );
119		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
120		goto return_results;
121	}
122
123	Debug( LDAP_DEBUG_ARGS, "SRCH \"%s\" %d %d",
124		base.bv_val, op->ors_scope, op->ors_deref );
125	Debug( LDAP_DEBUG_ARGS, "    %d %d %d\n",
126		op->ors_slimit, op->ors_tlimit, op->ors_attrsonly);
127
128	/* filter - returns a "normalized" version */
129	rs->sr_err = get_filter( op, op->o_ber, &op->ors_filter, &rs->sr_text );
130	if( rs->sr_err != LDAP_SUCCESS ) {
131		if( rs->sr_err == SLAPD_DISCONNECT ) {
132			rs->sr_err = LDAP_PROTOCOL_ERROR;
133			send_ldap_disconnect( op, rs );
134			rs->sr_err = SLAPD_DISCONNECT;
135		} else {
136			send_ldap_result( op, rs );
137		}
138		goto return_results;
139	}
140	filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
141
142	Debug( LDAP_DEBUG_ARGS, "    filter: %s\n",
143		!BER_BVISEMPTY( &op->ors_filterstr ) ? op->ors_filterstr.bv_val : "empty", 0, 0 );
144
145	/* attributes */
146	siz = sizeof(AttributeName);
147	off = offsetof(AttributeName,an_name);
148	if ( ber_scanf( op->o_ber, "{M}}", &op->ors_attrs, &siz, off ) == LBER_ERROR ) {
149		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding attrs error" );
150		rs->sr_err = SLAPD_DISCONNECT;
151		goto return_results;
152	}
153	for ( i=0; i<siz; i++ ) {
154		const char *dummy;	/* ignore msgs from bv2ad */
155		op->ors_attrs[i].an_desc = NULL;
156		op->ors_attrs[i].an_oc = NULL;
157		op->ors_attrs[i].an_flags = 0;
158		if ( slap_bv2ad( &op->ors_attrs[i].an_name,
159			&op->ors_attrs[i].an_desc, &dummy ) != LDAP_SUCCESS )
160		{
161			slap_bv2undef_ad( &op->ors_attrs[i].an_name,
162				&op->ors_attrs[i].an_desc, &dummy,
163				SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
164		};
165	}
166
167	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
168		Debug( LDAP_DEBUG_ANY, "%s do_search: get_ctrls failed\n",
169			op->o_log_prefix, 0, 0 );
170		goto return_results;
171	}
172
173	Debug( LDAP_DEBUG_ARGS, "    attrs:", 0, 0, 0 );
174
175	if ( siz != 0 ) {
176		for ( i = 0; i<siz; i++ ) {
177			Debug( LDAP_DEBUG_ARGS, " %s", op->ors_attrs[i].an_name.bv_val, 0, 0 );
178		}
179	}
180
181	Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
182
183	if ( StatslogTest( LDAP_DEBUG_STATS ) ) {
184		char abuf[BUFSIZ/2], *ptr = abuf;
185		unsigned len = 0, alen;
186
187		sprintf(abuf, "scope=%d deref=%d", op->ors_scope, op->ors_deref);
188		Statslog( LDAP_DEBUG_STATS,
189		        "%s SRCH base=\"%s\" %s filter=\"%s\"\n",
190		        op->o_log_prefix, op->o_req_dn.bv_val, abuf,
191		        op->ors_filterstr.bv_val, 0 );
192
193		for ( i = 0; i<siz; i++ ) {
194			alen = op->ors_attrs[i].an_name.bv_len;
195			if (alen >= sizeof(abuf)) {
196				alen = sizeof(abuf)-1;
197			}
198			if (len && (len + 1 + alen >= sizeof(abuf))) {
199				Statslog( LDAP_DEBUG_STATS, "%s SRCH attr=%s\n",
200				    op->o_log_prefix, abuf, 0, 0, 0 );
201				len = 0;
202				ptr = abuf;
203			}
204			if (len) {
205				*ptr++ = ' ';
206				len++;
207			}
208			ptr = lutil_strncopy(ptr, op->ors_attrs[i].an_name.bv_val, alen);
209			len += alen;
210			*ptr = '\0';
211		}
212		if (len) {
213			Statslog( LDAP_DEBUG_STATS, "%s SRCH attr=%s\n",
214	    			op->o_log_prefix, abuf, 0, 0, 0 );
215		}
216	}
217
218	op->o_bd = frontendDB;
219	rs->sr_err = frontendDB->be_search( op, rs );
220
221return_results:;
222	if ( !BER_BVISNULL( &op->o_req_dn ) ) {
223		slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
224	}
225	if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
226		slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
227	}
228	if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
229		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
230	}
231	if ( op->ors_filter != NULL) {
232		filter_free_x( op, op->ors_filter, 1 );
233	}
234	if ( op->ors_attrs != NULL ) {
235		op->o_tmpfree( op->ors_attrs, op->o_tmpmemctx );
236	}
237
238	return rs->sr_err;
239}
240
241int
242fe_op_search( Operation *op, SlapReply *rs )
243{
244	BackendDB		*bd = op->o_bd;
245
246	if ( op->ors_scope == LDAP_SCOPE_BASE ) {
247		Entry *entry = NULL;
248
249		if ( BER_BVISEMPTY( &op->o_req_ndn ) ) {
250#ifdef LDAP_CONNECTIONLESS
251			/* Ignore LDAPv2 CLDAP Root DSE queries */
252			if (op->o_protocol == LDAP_VERSION2 && op->o_conn->c_is_udp) {
253				goto return_results;
254			}
255#endif
256			/* check restrictions */
257			if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
258				send_ldap_result( op, rs );
259				goto return_results;
260			}
261
262			rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text );
263
264		} else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
265			/* check restrictions */
266			if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
267				send_ldap_result( op, rs );
268				goto return_results;
269			}
270
271			rs->sr_err = schema_info( &entry, &rs->sr_text );
272		}
273
274		if( rs->sr_err != LDAP_SUCCESS ) {
275			send_ldap_result( op, rs );
276			goto return_results;
277
278		} else if ( entry != NULL ) {
279			rs->sr_err = test_filter( op, entry, op->ors_filter );
280
281			if( rs->sr_err == LDAP_COMPARE_TRUE ) {
282				/* note: we set no limits because either
283				 * no limit is specified, or at least 1
284				 * is specified, and we're going to return
285				 * at most one entry */
286				op->ors_slimit = SLAP_NO_LIMIT;
287				op->ors_tlimit = SLAP_NO_LIMIT;
288
289				rs->sr_entry = entry;
290				rs->sr_attrs = op->ors_attrs;
291				rs->sr_operational_attrs = NULL;
292				rs->sr_flags = 0;
293				send_search_entry( op, rs );
294				rs->sr_entry = NULL;
295				rs->sr_operational_attrs = NULL;
296			}
297			entry_free( entry );
298
299			rs->sr_err = LDAP_SUCCESS;
300			send_ldap_result( op, rs );
301			goto return_results;
302		}
303	}
304
305	if( BER_BVISEMPTY( &op->o_req_ndn ) && !BER_BVISEMPTY( &default_search_nbase ) ) {
306		slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
307		slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
308
309		ber_dupbv_x( &op->o_req_dn, &default_search_base, op->o_tmpmemctx );
310		ber_dupbv_x( &op->o_req_ndn, &default_search_nbase, op->o_tmpmemctx );
311	}
312
313	/*
314	 * We could be serving multiple database backends.  Select the
315	 * appropriate one, or send a referral to our "referral server"
316	 * if we don't hold it.
317	 */
318
319	op->o_bd = select_backend( &op->o_req_ndn, 1 );
320	if ( op->o_bd == NULL ) {
321		rs->sr_ref = referral_rewrite( default_referral,
322			NULL, &op->o_req_dn, op->ors_scope );
323
324		if (!rs->sr_ref) rs->sr_ref = default_referral;
325		rs->sr_err = LDAP_REFERRAL;
326		op->o_bd = bd;
327		send_ldap_result( op, rs );
328
329		if (rs->sr_ref != default_referral)
330		ber_bvarray_free( rs->sr_ref );
331		rs->sr_ref = NULL;
332		goto return_results;
333	}
334
335	/* check restrictions */
336	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
337		send_ldap_result( op, rs );
338		goto return_results;
339	}
340
341	/* check for referrals */
342	if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
343		goto return_results;
344	}
345
346	if ( SLAP_SHADOW(op->o_bd) && get_dontUseCopy(op) ) {
347		/* don't use shadow copy */
348		BerVarray defref = op->o_bd->be_update_refs
349			? op->o_bd->be_update_refs : default_referral;
350
351		if( defref != NULL ) {
352			rs->sr_ref = referral_rewrite( defref,
353				NULL, &op->o_req_dn, op->ors_scope );
354			if( !rs->sr_ref) rs->sr_ref = defref;
355			rs->sr_err = LDAP_REFERRAL;
356			send_ldap_result( op, rs );
357
358			if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref );
359
360		} else {
361			send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
362				"copy not used; no referral information available" );
363		}
364
365	} else if ( op->o_bd->be_search ) {
366		if ( limits_check( op, rs ) == 0 ) {
367			/* actually do the search and send the result(s) */
368			(op->o_bd->be_search)( op, rs );
369		}
370		/* else limits_check() sends error */
371
372	} else {
373		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
374			"operation not supported within namingContext" );
375	}
376
377return_results:;
378	op->o_bd = bd;
379	return rs->sr_err;
380}
381
382