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