xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/compare.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: compare.c,v 1.1.1.3 2010/12/12 15:22:24 adam Exp $	*/
2 
3 /* OpenLDAP: pkg/ldap/servers/slapd/compare.c,v 1.136.2.10 2010/04/13 20:23:12 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 #include <ac/socket.h>
32 #include <ac/string.h>
33 
34 #include "slap.h"
35 
36 static int compare_entry(
37 	Operation *op,
38 	Entry *e,
39 	AttributeAssertion *ava );
40 
41 int
42 do_compare(
43     Operation	*op,
44     SlapReply	*rs )
45 {
46 	struct berval dn = BER_BVNULL;
47 	struct berval desc = BER_BVNULL;
48 	struct berval value = BER_BVNULL;
49 	AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
50 
51 	Debug( LDAP_DEBUG_TRACE, "%s do_compare\n",
52 		op->o_log_prefix, 0, 0 );
53 	/*
54 	 * Parse the compare request.  It looks like this:
55 	 *
56 	 *	CompareRequest := [APPLICATION 14] SEQUENCE {
57 	 *		entry	DistinguishedName,
58 	 *		ava	SEQUENCE {
59 	 *			type	AttributeType,
60 	 *			value	AttributeValue
61 	 *		}
62 	 *	}
63 	 */
64 
65 	if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
66 		Debug( LDAP_DEBUG_ANY, "%s do_compare: ber_scanf failed\n",
67 			op->o_log_prefix, 0, 0 );
68 		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
69 		return SLAPD_DISCONNECT;
70 	}
71 
72 	if ( ber_scanf( op->o_ber, "{mm}", &desc, &value ) == LBER_ERROR ) {
73 		Debug( LDAP_DEBUG_ANY, "%s do_compare: get ava failed\n",
74 			op->o_log_prefix, 0, 0 );
75 		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
76 		return SLAPD_DISCONNECT;
77 	}
78 
79 	if ( ber_scanf( op->o_ber, /*{*/ "}" ) == LBER_ERROR ) {
80 		Debug( LDAP_DEBUG_ANY, "%s do_compare: ber_scanf failed\n",
81 			op->o_log_prefix, 0, 0 );
82 		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
83 		return SLAPD_DISCONNECT;
84 	}
85 
86 	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
87 		Debug( LDAP_DEBUG_ANY, "%s do_compare: get_ctrls failed\n",
88 			op->o_log_prefix, 0, 0 );
89 		goto cleanup;
90 	}
91 
92 	rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
93 		op->o_tmpmemctx );
94 	if( rs->sr_err != LDAP_SUCCESS ) {
95 		Debug( LDAP_DEBUG_ANY, "%s do_compare: invalid dn (%s)\n",
96 			op->o_log_prefix, dn.bv_val, 0 );
97 		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
98 		goto cleanup;
99 	}
100 
101 	Statslog( LDAP_DEBUG_STATS,
102 		"%s CMP dn=\"%s\" attr=\"%s\"\n",
103 		op->o_log_prefix, op->o_req_dn.bv_val,
104 		desc.bv_val, 0, 0 );
105 
106 	rs->sr_err = slap_bv2ad( &desc, &ava.aa_desc, &rs->sr_text );
107 	if( rs->sr_err != LDAP_SUCCESS ) {
108 		rs->sr_err = slap_bv2undef_ad( &desc, &ava.aa_desc,
109 				&rs->sr_text,
110 				SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
111 		if( rs->sr_err != LDAP_SUCCESS ) {
112 			send_ldap_result( op, rs );
113 			goto cleanup;
114 		}
115 	}
116 
117 	rs->sr_err = asserted_value_validate_normalize( ava.aa_desc,
118 		ava.aa_desc->ad_type->sat_equality,
119 		SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
120 		&value, &ava.aa_value, &rs->sr_text, op->o_tmpmemctx );
121 	if( rs->sr_err != LDAP_SUCCESS ) {
122 		send_ldap_result( op, rs );
123 		goto cleanup;
124 	}
125 
126 	op->orc_ava = &ava;
127 
128 	Debug( LDAP_DEBUG_ARGS,
129 		"do_compare: dn (%s) attr (%s) value (%s)\n",
130 		op->o_req_dn.bv_val,
131 		ava.aa_desc->ad_cname.bv_val, ava.aa_value.bv_val );
132 
133 	op->o_bd = frontendDB;
134 	rs->sr_err = frontendDB->be_compare( op, rs );
135 
136 cleanup:;
137 	op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
138 	op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
139 	if ( !BER_BVISNULL( &ava.aa_value ) ) {
140 		op->o_tmpfree( ava.aa_value.bv_val, op->o_tmpmemctx );
141 	}
142 
143 	return rs->sr_err;
144 }
145 
146 int
147 fe_op_compare( Operation *op, SlapReply *rs )
148 {
149 	Entry			*entry = NULL;
150 	AttributeAssertion	*ava = op->orc_ava;
151 	BackendDB		*bd = op->o_bd;
152 
153 	if( strcasecmp( op->o_req_ndn.bv_val, LDAP_ROOT_DSE ) == 0 ) {
154 		if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
155 			send_ldap_result( op, rs );
156 			goto cleanup;
157 		}
158 
159 		rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text );
160 		if( rs->sr_err != LDAP_SUCCESS ) {
161 			send_ldap_result( op, rs );
162 			goto cleanup;
163 		}
164 
165 	} else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
166 		if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
167 			send_ldap_result( op, rs );
168 			rs->sr_err = 0;
169 			goto cleanup;
170 		}
171 
172 		rs->sr_err = schema_info( &entry, &rs->sr_text );
173 		if( rs->sr_err != LDAP_SUCCESS ) {
174 			send_ldap_result( op, rs );
175 			rs->sr_err = 0;
176 			goto cleanup;
177 		}
178 	}
179 
180 	if( entry ) {
181 		rs->sr_err = compare_entry( op, entry, ava );
182 		entry_free( entry );
183 
184 		send_ldap_result( op, rs );
185 
186 		if( rs->sr_err == LDAP_COMPARE_TRUE ||
187 			rs->sr_err == LDAP_COMPARE_FALSE )
188 		{
189 			rs->sr_err = LDAP_SUCCESS;
190 		}
191 
192 		goto cleanup;
193 	}
194 
195 	/*
196 	 * We could be serving multiple database backends.  Select the
197 	 * appropriate one, or send a referral to our "referral server"
198 	 * if we don't hold it.
199 	 */
200 	op->o_bd = select_backend( &op->o_req_ndn, 0 );
201 	if ( op->o_bd == NULL ) {
202 		rs->sr_ref = referral_rewrite( default_referral,
203 			NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
204 
205 		rs->sr_err = LDAP_REFERRAL;
206 		if (!rs->sr_ref) rs->sr_ref = default_referral;
207 		op->o_bd = bd;
208 		send_ldap_result( op, rs );
209 
210 		if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
211 		rs->sr_err = 0;
212 		goto cleanup;
213 	}
214 
215 	/* check restrictions */
216 	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
217 		send_ldap_result( op, rs );
218 		goto cleanup;
219 	}
220 
221 	/* check for referrals */
222 	if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
223 		goto cleanup;
224 	}
225 
226 	if ( SLAP_SHADOW(op->o_bd) && get_dontUseCopy(op) ) {
227 		/* don't use shadow copy */
228 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
229 			"copy not used" );
230 
231 	} else if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
232 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
233 			"entryDN compare not supported" );
234 
235 	} else if ( ava->aa_desc == slap_schema.si_ad_subschemaSubentry ) {
236 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
237 			"subschemaSubentry compare not supported" );
238 
239 #ifndef SLAP_COMPARE_IN_FRONTEND
240 	} else if ( ava->aa_desc == slap_schema.si_ad_hasSubordinates
241 		&& op->o_bd->be_has_subordinates )
242 	{
243 		int	rc, hasSubordinates = LDAP_SUCCESS;
244 
245 		rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &entry );
246 		if ( rc == 0 && entry ) {
247 			if ( ! access_allowed( op, entry,
248 				ava->aa_desc, &ava->aa_value, ACL_COMPARE, NULL ) )
249 			{
250 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
251 
252 			} else {
253 				rc = rs->sr_err = op->o_bd->be_has_subordinates( op,
254 						entry, &hasSubordinates );
255 				be_entry_release_r( op, entry );
256 			}
257 		}
258 
259 		if ( rc == 0 ) {
260 			int	asserted;
261 
262 			asserted = bvmatch( &ava->aa_value, &slap_true_bv )
263 				? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
264 			if ( hasSubordinates == asserted ) {
265 				rs->sr_err = LDAP_COMPARE_TRUE;
266 
267 			} else {
268 				rs->sr_err = LDAP_COMPARE_FALSE;
269 			}
270 
271 		} else {
272 			/* return error only if "disclose"
273 			 * is granted on the object */
274 			if ( backend_access( op, NULL, &op->o_req_ndn,
275 					slap_schema.si_ad_entry,
276 					NULL, ACL_DISCLOSE, NULL ) == LDAP_INSUFFICIENT_ACCESS )
277 			{
278 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
279 			}
280 		}
281 
282 		send_ldap_result( op, rs );
283 
284 		if ( rc == 0 ) {
285 			rs->sr_err = LDAP_SUCCESS;
286 		}
287 
288 	} else if ( op->o_bd->be_compare ) {
289 		rs->sr_err = op->o_bd->be_compare( op, rs );
290 
291 #endif /* ! SLAP_COMPARE_IN_FRONTEND */
292 	} else {
293 		rs->sr_err = SLAP_CB_CONTINUE;
294 	}
295 
296 	if ( rs->sr_err == SLAP_CB_CONTINUE ) {
297 		/* do our best to compare that AVA
298 		 *
299 		 * NOTE: this code is used only
300 		 * if SLAP_COMPARE_IN_FRONTEND
301 		 * is #define'd (it's not by default)
302 		 * or if op->o_bd->be_compare is NULL.
303 		 *
304 		 * FIXME: one potential issue is that
305 		 * if SLAP_COMPARE_IN_FRONTEND overlays
306 		 * are not executed for compare. */
307 		BerVarray	vals = NULL;
308 		int		rc = LDAP_OTHER;
309 
310 		rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
311 				ava->aa_desc, &vals, ACL_COMPARE );
312 		switch ( rs->sr_err ) {
313 		default:
314 			/* return error only if "disclose"
315 			 * is granted on the object */
316 			if ( backend_access( op, NULL, &op->o_req_ndn,
317 					slap_schema.si_ad_entry,
318 					NULL, ACL_DISCLOSE, NULL )
319 					== LDAP_INSUFFICIENT_ACCESS )
320 			{
321 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
322 			}
323 			break;
324 
325 		case LDAP_SUCCESS:
326 			if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
327 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
328 					SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
329 				vals, &ava->aa_value, op->o_tmpmemctx ) == 0 )
330 			{
331 				rs->sr_err = LDAP_COMPARE_TRUE;
332 				break;
333 
334 			} else {
335 				rs->sr_err = LDAP_COMPARE_FALSE;
336 			}
337 			rc = LDAP_SUCCESS;
338 			break;
339 		}
340 
341 		send_ldap_result( op, rs );
342 
343 		if ( rc == 0 ) {
344 			rs->sr_err = LDAP_SUCCESS;
345 		}
346 
347 		if ( vals ) {
348 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
349 		}
350 	}
351 
352 cleanup:;
353 	op->o_bd = bd;
354 	return rs->sr_err;
355 }
356 
357 static int compare_entry(
358 	Operation *op,
359 	Entry *e,
360 	AttributeAssertion *ava )
361 {
362 	int rc = LDAP_COMPARE_FALSE;
363 	Attribute *a;
364 
365 	if ( ! access_allowed( op, e,
366 		ava->aa_desc, &ava->aa_value, ACL_COMPARE, NULL ) )
367 	{
368 		rc = LDAP_INSUFFICIENT_ACCESS;
369 		goto done;
370 	}
371 
372 	a = attrs_find( e->e_attrs, ava->aa_desc );
373 	if( a == NULL ) {
374 		rc = LDAP_NO_SUCH_ATTRIBUTE;
375 		goto done;
376 	}
377 
378 	for(a = attrs_find( e->e_attrs, ava->aa_desc );
379 		a != NULL;
380 		a = attrs_find( a->a_next, ava->aa_desc ))
381 	{
382 		if (( ava->aa_desc != a->a_desc ) && ! access_allowed( op,
383 			e, a->a_desc, &ava->aa_value, ACL_COMPARE, NULL ) )
384 		{
385 			rc = LDAP_INSUFFICIENT_ACCESS;
386 			break;
387 		}
388 
389 		if ( attr_valfind( a,
390 			SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
391 				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
392 			&ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
393 		{
394 			rc = LDAP_COMPARE_TRUE;
395 			break;
396 		}
397 	}
398 
399 done:
400 	if( rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE ) {
401 		if ( ! access_allowed( op, e,
402 			slap_schema.si_ad_entry, NULL, ACL_DISCLOSE, NULL ) )
403 		{
404 			rc = LDAP_NO_SUCH_OBJECT;
405 		}
406 	}
407 
408 	return rc;
409 }
410