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