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