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