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