1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1998-2011 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
34int
35do_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
129cleanup:;
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
139int
140fe_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
345cleanup:;
346	op->o_bd = bd;
347	return rs->sr_err;
348}
349
350int 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
399done:
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