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