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