1 /* $NetBSD: compare.c,v 1.1.1.6 2018/02/06 01:53:15 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2017 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.1.1.6 2018/02/06 01:53:15 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 39 int 40 do_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, 0, 0 ); 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, 0, 0 ); 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, 0, 0 ); 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, 0, 0 ); 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, 0, 0 ); 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, 0 ); 95 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" ); 96 goto cleanup; 97 } 98 99 Statslog( 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, 0, 0 ); 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 134 cleanup:; 135 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx ); 136 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 137 if ( !BER_BVISNULL( &ava.aa_value ) ) { 138 op->o_tmpfree( ava.aa_value.bv_val, op->o_tmpmemctx ); 139 } 140 141 return rs->sr_err; 142 } 143 144 int 145 fe_op_compare( Operation *op, SlapReply *rs ) 146 { 147 Entry *entry = NULL; 148 AttributeAssertion *ava = op->orc_ava; 149 BackendDB *bd = op->o_bd; 150 151 if( strcasecmp( op->o_req_ndn.bv_val, LDAP_ROOT_DSE ) == 0 ) { 152 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 153 send_ldap_result( op, rs ); 154 goto cleanup; 155 } 156 157 rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text ); 158 if( rs->sr_err != LDAP_SUCCESS ) { 159 send_ldap_result( op, rs ); 160 goto cleanup; 161 } 162 163 } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) { 164 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 165 send_ldap_result( op, rs ); 166 rs->sr_err = 0; 167 goto cleanup; 168 } 169 170 rs->sr_err = schema_info( &entry, &rs->sr_text ); 171 if( rs->sr_err != LDAP_SUCCESS ) { 172 send_ldap_result( op, rs ); 173 rs->sr_err = 0; 174 goto cleanup; 175 } 176 } 177 178 if( entry ) { 179 rs->sr_err = slap_compare_entry( op, entry, ava ); 180 entry_free( entry ); 181 182 send_ldap_result( op, rs ); 183 184 if( rs->sr_err == LDAP_COMPARE_TRUE || 185 rs->sr_err == LDAP_COMPARE_FALSE ) 186 { 187 rs->sr_err = LDAP_SUCCESS; 188 } 189 190 goto cleanup; 191 } 192 193 /* 194 * We could be serving multiple database backends. Select the 195 * appropriate one, or send a referral to our "referral server" 196 * if we don't hold it. 197 */ 198 op->o_bd = select_backend( &op->o_req_ndn, 0 ); 199 if ( op->o_bd == NULL ) { 200 rs->sr_ref = referral_rewrite( default_referral, 201 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 202 203 rs->sr_err = LDAP_REFERRAL; 204 if (!rs->sr_ref) rs->sr_ref = default_referral; 205 op->o_bd = bd; 206 send_ldap_result( op, rs ); 207 208 if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref ); 209 rs->sr_err = 0; 210 goto cleanup; 211 } 212 213 /* check restrictions */ 214 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 215 send_ldap_result( op, rs ); 216 goto cleanup; 217 } 218 219 /* check for referrals */ 220 if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) { 221 goto cleanup; 222 } 223 224 if ( SLAP_SHADOW(op->o_bd) && get_dontUseCopy(op) ) { 225 /* don't use shadow copy */ 226 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 227 "copy not used" ); 228 229 } else if ( ava->aa_desc == slap_schema.si_ad_entryDN ) { 230 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 231 "entryDN compare not supported" ); 232 233 } else if ( ava->aa_desc == slap_schema.si_ad_subschemaSubentry ) { 234 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 235 "subschemaSubentry compare not supported" ); 236 237 #ifndef SLAP_COMPARE_IN_FRONTEND 238 } else if ( ava->aa_desc == slap_schema.si_ad_hasSubordinates 239 && op->o_bd->be_has_subordinates ) 240 { 241 int rc, hasSubordinates = LDAP_SUCCESS; 242 243 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &entry ); 244 if ( rc == 0 && entry ) { 245 if ( ! access_allowed( op, entry, 246 ava->aa_desc, &ava->aa_value, ACL_COMPARE, NULL ) ) 247 { 248 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 249 250 } else { 251 rc = rs->sr_err = op->o_bd->be_has_subordinates( op, 252 entry, &hasSubordinates ); 253 be_entry_release_r( op, entry ); 254 } 255 } 256 257 if ( rc == 0 ) { 258 int asserted; 259 260 asserted = bvmatch( &ava->aa_value, &slap_true_bv ) 261 ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE; 262 if ( hasSubordinates == asserted ) { 263 rs->sr_err = LDAP_COMPARE_TRUE; 264 265 } else { 266 rs->sr_err = LDAP_COMPARE_FALSE; 267 } 268 269 } else { 270 /* return error only if "disclose" 271 * is granted on the object */ 272 if ( backend_access( op, NULL, &op->o_req_ndn, 273 slap_schema.si_ad_entry, 274 NULL, ACL_DISCLOSE, NULL ) == LDAP_INSUFFICIENT_ACCESS ) 275 { 276 rs->sr_err = LDAP_NO_SUCH_OBJECT; 277 } 278 } 279 280 send_ldap_result( op, rs ); 281 282 if ( rc == 0 ) { 283 rs->sr_err = LDAP_SUCCESS; 284 } 285 286 } else if ( op->o_bd->be_compare ) { 287 rs->sr_err = op->o_bd->be_compare( op, rs ); 288 289 #endif /* ! SLAP_COMPARE_IN_FRONTEND */ 290 } else { 291 rs->sr_err = SLAP_CB_CONTINUE; 292 } 293 294 if ( rs->sr_err == SLAP_CB_CONTINUE ) { 295 /* do our best to compare that AVA 296 * 297 * NOTE: this code is used only 298 * if SLAP_COMPARE_IN_FRONTEND 299 * is #define'd (it's not by default) 300 * or if op->o_bd->be_compare is NULL. 301 * 302 * FIXME: one potential issue is that 303 * if SLAP_COMPARE_IN_FRONTEND overlays 304 * are not executed for compare. */ 305 BerVarray vals = NULL; 306 int rc = LDAP_OTHER; 307 308 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 309 ava->aa_desc, &vals, ACL_COMPARE ); 310 switch ( rs->sr_err ) { 311 default: 312 /* return error only if "disclose" 313 * is granted on the object */ 314 if ( backend_access( op, NULL, &op->o_req_ndn, 315 slap_schema.si_ad_entry, 316 NULL, ACL_DISCLOSE, NULL ) 317 == LDAP_INSUFFICIENT_ACCESS ) 318 { 319 rs->sr_err = LDAP_NO_SUCH_OBJECT; 320 } 321 break; 322 323 case LDAP_SUCCESS: 324 if ( value_find_ex( op->oq_compare.rs_ava->aa_desc, 325 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 326 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 327 vals, &ava->aa_value, op->o_tmpmemctx ) == 0 ) 328 { 329 rs->sr_err = LDAP_COMPARE_TRUE; 330 break; 331 332 } else { 333 rs->sr_err = LDAP_COMPARE_FALSE; 334 } 335 rc = LDAP_SUCCESS; 336 break; 337 } 338 339 send_ldap_result( op, rs ); 340 341 if ( rc == 0 ) { 342 rs->sr_err = LDAP_SUCCESS; 343 } 344 345 if ( vals ) { 346 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 347 } 348 } 349 350 cleanup:; 351 op->o_bd = bd; 352 return rs->sr_err; 353 } 354 355 int slap_compare_entry( 356 Operation *op, 357 Entry *e, 358 AttributeAssertion *ava ) 359 { 360 int rc = LDAP_COMPARE_FALSE; 361 Attribute *a; 362 363 if ( ! access_allowed( op, e, 364 ava->aa_desc, &ava->aa_value, ACL_COMPARE, NULL ) ) 365 { 366 rc = LDAP_INSUFFICIENT_ACCESS; 367 goto done; 368 } 369 370 if ( get_assert( op ) && 371 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE )) 372 { 373 rc = LDAP_ASSERTION_FAILED; 374 goto done; 375 } 376 377 a = attrs_find( e->e_attrs, ava->aa_desc ); 378 if( a == NULL ) { 379 rc = LDAP_NO_SUCH_ATTRIBUTE; 380 goto done; 381 } 382 383 for(; 384 a != NULL; 385 a = attrs_find( a->a_next, ava->aa_desc )) 386 { 387 if (( ava->aa_desc != a->a_desc ) && ! access_allowed( op, 388 e, a->a_desc, &ava->aa_value, ACL_COMPARE, NULL ) ) 389 { 390 rc = LDAP_INSUFFICIENT_ACCESS; 391 break; 392 } 393 394 if ( attr_valfind( a, 395 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 396 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 397 &ava->aa_value, NULL, op->o_tmpmemctx ) == 0 ) 398 { 399 rc = LDAP_COMPARE_TRUE; 400 break; 401 } 402 } 403 404 done: 405 if( rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE ) { 406 if ( ! access_allowed( op, e, 407 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE, NULL ) ) 408 { 409 rc = LDAP_NO_SUCH_OBJECT; 410 } 411 } 412 413 return rc; 414 } 415