1 /* $NetBSD: search.c,v 1.4 2025/09/05 21:16:31 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2024 The OpenLDAP Foundation. 7 * Portions Copyright 1999 Dmitry Kovalev. 8 * Portions Copyright 2002 Pierangelo Masarati. 9 * Portions Copyright 2004 Mark Adamson. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted only as authorized by the OpenLDAP 14 * Public License. 15 * 16 * A copy of this license is available in the file LICENSE in the 17 * top-level directory of the distribution or, alternatively, at 18 * <http://www.OpenLDAP.org/license.html>. 19 */ 20 /* ACKNOWLEDGEMENTS: 21 * This work was initially developed by Dmitry Kovalev for inclusion 22 * by OpenLDAP Software. Additional significant contributors include 23 * Pierangelo Masarati and Mark Adamson. 24 */ 25 26 #include <sys/cdefs.h> 27 __RCSID("$NetBSD: search.c,v 1.4 2025/09/05 21:16:31 christos Exp $"); 28 29 #include "portable.h" 30 31 #include <stdio.h> 32 #include <sys/types.h> 33 #include "ac/string.h" 34 #include "ac/ctype.h" 35 36 #include "lutil.h" 37 #include "slap.h" 38 #include "proto-sql.h" 39 40 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f ); 41 static int backsql_process_filter_eq( backsql_srch_info *bsi, 42 backsql_at_map_rec *at, 43 int casefold, struct berval *filter_value ); 44 static int backsql_process_filter_like( backsql_srch_info *bsi, 45 backsql_at_map_rec *at, 46 int casefold, struct berval *filter_value ); 47 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, 48 backsql_at_map_rec *at ); 49 50 /* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of 51 the state of paged results. The ldap_entries.id and oc_map_id values of the 52 last entry returned are used as the cookie, so 6 bits are used for the OC id 53 and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more 54 than 63, you will need to steal more bits from ldap_entries ID number and 55 put them into the OC ID part of the cookie. */ 56 57 /* NOTE: not supported when BACKSQL_ARBITRARY_KEY is defined */ 58 #ifndef BACKSQL_ARBITRARY_KEY 59 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F)) 60 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6) 61 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F) 62 63 static int parse_paged_cookie( Operation *op, SlapReply *rs ); 64 65 static void send_paged_response( 66 Operation *op, 67 SlapReply *rs, 68 ID *lastid ); 69 #endif /* ! BACKSQL_ARBITRARY_KEY */ 70 71 /* Look for chars that need to be escaped, return count of them. 72 * If out is non-NULL, copy escape'd val to it. 73 */ 74 static int 75 backsql_val_escape( Operation *op, struct berval *in, struct berval *out ) 76 { 77 char *ptr, *end; 78 int q = 0; 79 80 ptr = in->bv_val; 81 end = ptr + in->bv_len; 82 while (ptr < end) { 83 if ( *ptr == '\'' ) 84 q++; 85 ptr++; 86 } 87 if ( q && out ) { 88 char *dst; 89 out->bv_len = in->bv_len + q; 90 out->bv_val = op->o_tmpalloc( out->bv_len + 1, op->o_tmpmemctx ); 91 ptr = in->bv_val; 92 dst = out->bv_val; 93 while (ptr < end ) { 94 if ( *ptr == '\'' ) 95 *dst++ = '\''; 96 *dst++ = *ptr++; 97 } 98 *dst = '\0'; 99 } 100 return q; 101 } 102 103 static int 104 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad ) 105 { 106 int n_attrs = 0; 107 AttributeName *an = NULL; 108 109 if ( bsi->bsi_attrs == NULL ) { 110 return 1; 111 } 112 113 /* 114 * clear the list (retrieve all attrs) 115 */ 116 if ( ad == NULL ) { 117 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx ); 118 bsi->bsi_attrs = NULL; 119 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS; 120 return 1; 121 } 122 123 /* strip ';binary' */ 124 if ( slap_ad_is_binary( ad ) ) { 125 ad = ad->ad_type->sat_ad; 126 } 127 128 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) { 129 an = &bsi->bsi_attrs[ n_attrs ]; 130 131 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): " 132 "attribute \"%s\" is in list\n", 133 an->an_name.bv_val ); 134 /* 135 * We can live with strcmp because the attribute 136 * list has been normalized before calling be_search 137 */ 138 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) { 139 return 1; 140 } 141 } 142 143 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): " 144 "adding \"%s\" to list\n", ad->ad_cname.bv_val ); 145 146 an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs, 147 sizeof( AttributeName ) * ( n_attrs + 2 ), 148 bsi->bsi_op->o_tmpmemctx ); 149 if ( an == NULL ) { 150 return -1; 151 } 152 153 an[ n_attrs ].an_name = ad->ad_cname; 154 an[ n_attrs ].an_desc = ad; 155 BER_BVZERO( &an[ n_attrs + 1 ].an_name ); 156 157 bsi->bsi_attrs = an; 158 159 return 1; 160 } 161 162 /* 163 * Initializes the search structure. 164 * 165 * If get_base_id != 0, the field bsi_base_id is filled 166 * with the entryID of bsi_base_ndn; it must be freed 167 * by backsql_free_entryID() when no longer required. 168 * 169 * NOTE: base must be normalized 170 */ 171 int 172 backsql_init_search( 173 backsql_srch_info *bsi, 174 struct berval *nbase, 175 int scope, 176 time_t stoptime, 177 Filter *filter, 178 SQLHDBC dbh, 179 Operation *op, 180 SlapReply *rs, 181 AttributeName *attrs, 182 unsigned flags ) 183 { 184 backsql_info *bi = (backsql_info *)op->o_bd->be_private; 185 int rc = LDAP_SUCCESS; 186 187 bsi->bsi_base_ndn = nbase; 188 bsi->bsi_use_subtree_shortcut = 0; 189 BER_BVZERO( &bsi->bsi_base_id.eid_dn ); 190 BER_BVZERO( &bsi->bsi_base_id.eid_ndn ); 191 bsi->bsi_scope = scope; 192 bsi->bsi_filter = filter; 193 bsi->bsi_dbh = dbh; 194 bsi->bsi_op = op; 195 bsi->bsi_rs = rs; 196 bsi->bsi_flags = BSQL_SF_NONE; 197 198 bsi->bsi_attrs = NULL; 199 200 if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) { 201 /* 202 * if requested, simply try to fetch all attributes 203 */ 204 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS; 205 206 } else { 207 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) { 208 bsi->bsi_flags |= BSQL_SF_ALL_USER; 209 210 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) { 211 bsi->bsi_flags |= BSQL_SF_ALL_OPER; 212 } 213 214 if ( attrs == NULL ) { 215 /* NULL means all user attributes */ 216 bsi->bsi_flags |= BSQL_SF_ALL_USER; 217 218 } else { 219 AttributeName *p; 220 int got_oc = 0; 221 222 bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc( 223 sizeof( AttributeName ), 224 bsi->bsi_op->o_tmpmemctx ); 225 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name ); 226 227 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) { 228 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) { 229 /* handle "*" */ 230 bsi->bsi_flags |= BSQL_SF_ALL_USER; 231 232 /* if all attrs are requested, there's 233 * no need to continue */ 234 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 235 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 236 bsi->bsi_op->o_tmpmemctx ); 237 bsi->bsi_attrs = NULL; 238 break; 239 } 240 continue; 241 242 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) { 243 /* handle "+" */ 244 bsi->bsi_flags |= BSQL_SF_ALL_OPER; 245 246 /* if all attrs are requested, there's 247 * no need to continue */ 248 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 249 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 250 bsi->bsi_op->o_tmpmemctx ); 251 bsi->bsi_attrs = NULL; 252 break; 253 } 254 continue; 255 256 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) { 257 /* ignore "1.1" */ 258 continue; 259 260 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) { 261 got_oc = 1; 262 } 263 264 backsql_attrlist_add( bsi, p->an_desc ); 265 } 266 267 if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) { 268 /* add objectClass if not present, 269 * because it is required to understand 270 * if an entry is a referral, an alias 271 * or so... */ 272 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass ); 273 } 274 } 275 276 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) { 277 AttributeName *p; 278 279 /* use hints if available */ 280 for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) { 281 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) { 282 /* handle "*" */ 283 bsi->bsi_flags |= BSQL_SF_ALL_USER; 284 285 /* if all attrs are requested, there's 286 * no need to continue */ 287 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 288 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 289 bsi->bsi_op->o_tmpmemctx ); 290 bsi->bsi_attrs = NULL; 291 break; 292 } 293 continue; 294 295 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) { 296 /* handle "+" */ 297 bsi->bsi_flags |= BSQL_SF_ALL_OPER; 298 299 /* if all attrs are requested, there's 300 * no need to continue */ 301 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 302 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 303 bsi->bsi_op->o_tmpmemctx ); 304 bsi->bsi_attrs = NULL; 305 break; 306 } 307 continue; 308 } 309 310 backsql_attrlist_add( bsi, p->an_desc ); 311 } 312 313 } 314 } 315 316 bsi->bsi_id_list = NULL; 317 bsi->bsi_id_listtail = &bsi->bsi_id_list; 318 bsi->bsi_n_candidates = 0; 319 bsi->bsi_stoptime = stoptime; 320 BER_BVZERO( &bsi->bsi_sel.bb_val ); 321 bsi->bsi_sel.bb_len = 0; 322 BER_BVZERO( &bsi->bsi_from.bb_val ); 323 bsi->bsi_from.bb_len = 0; 324 BER_BVZERO( &bsi->bsi_join_where.bb_val ); 325 bsi->bsi_join_where.bb_len = 0; 326 BER_BVZERO( &bsi->bsi_flt_where.bb_val ); 327 bsi->bsi_flt_where.bb_len = 0; 328 bsi->bsi_filter_oc = NULL; 329 330 if ( BACKSQL_IS_GET_ID( flags ) ) { 331 int matched = BACKSQL_IS_MATCHED( flags ); 332 int getentry = BACKSQL_IS_GET_ENTRY( flags ); 333 int gotit = 0; 334 335 assert( op->o_bd->be_private != NULL ); 336 337 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id, 338 matched, 1 ); 339 340 /* the entry is collected either if requested for by getentry 341 * or if get noSuchObject and requested to climb the tree, 342 * so that a matchedDN or a referral can be returned */ 343 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) { 344 if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) { 345 assert( bsi->bsi_e != NULL ); 346 347 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) ) 348 { 349 gotit = 1; 350 } 351 352 /* 353 * let's see if it is a referral and, in case, get it 354 */ 355 backsql_attrlist_add( bsi, slap_schema.si_ad_ref ); 356 rc = backsql_id2entry( bsi, &bsi->bsi_base_id ); 357 if ( rc == LDAP_SUCCESS ) { 358 if ( is_entry_referral( bsi->bsi_e ) ) 359 { 360 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e ); 361 if ( erefs ) { 362 rc = rs->sr_err = LDAP_REFERRAL; 363 rs->sr_ref = referral_rewrite( erefs, 364 &bsi->bsi_e->e_nname, 365 &op->o_req_dn, 366 scope ); 367 ber_bvarray_free( erefs ); 368 369 } else { 370 rc = rs->sr_err = LDAP_OTHER; 371 rs->sr_text = "bad referral object"; 372 } 373 374 } else if ( !gotit ) { 375 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT; 376 } 377 } 378 379 } else { 380 rs->sr_err = rc; 381 } 382 } 383 384 if ( gotit && BACKSQL_IS_GET_OC( flags ) ) { 385 bsi->bsi_base_id.eid_oc = backsql_id2oc( bi, 386 bsi->bsi_base_id.eid_oc_id ); 387 if ( bsi->bsi_base_id.eid_oc == NULL ) { 388 /* error? */ 389 backsql_free_entryID( &bsi->bsi_base_id, 1, 390 op->o_tmpmemctx ); 391 rc = rs->sr_err = LDAP_OTHER; 392 } 393 } 394 } 395 396 bsi->bsi_status = rc; 397 398 switch ( rc ) { 399 case LDAP_SUCCESS: 400 case LDAP_REFERRAL: 401 break; 402 403 default: 404 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 405 bsi->bsi_op->o_tmpmemctx ); 406 break; 407 } 408 409 return rc; 410 } 411 412 static int 413 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op ) 414 { 415 int res; 416 417 if ( !f ) { 418 return 0; 419 } 420 421 backsql_strfcat_x( &bsi->bsi_flt_where, 422 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ ); 423 424 while ( 1 ) { 425 res = backsql_process_filter( bsi, f ); 426 if ( res < 0 ) { 427 /* 428 * TimesTen : If the query has no answers, 429 * don't bother to run the query. 430 */ 431 return -1; 432 } 433 434 f = f->f_next; 435 if ( f == NULL ) { 436 break; 437 } 438 439 switch ( op ) { 440 case LDAP_FILTER_AND: 441 backsql_strfcat_x( &bsi->bsi_flt_where, 442 bsi->bsi_op->o_tmpmemctx, "l", 443 (ber_len_t)STRLENOF( " AND " ), 444 " AND " ); 445 break; 446 447 case LDAP_FILTER_OR: 448 backsql_strfcat_x( &bsi->bsi_flt_where, 449 bsi->bsi_op->o_tmpmemctx, "l", 450 (ber_len_t)STRLENOF( " OR " ), 451 " OR " ); 452 break; 453 } 454 } 455 456 backsql_strfcat_x( &bsi->bsi_flt_where, 457 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' ); 458 459 return 1; 460 } 461 462 static int 463 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f, 464 backsql_at_map_rec *at ) 465 { 466 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 467 int i; 468 int casefold = 0; 469 int escaped = 0; 470 struct berval escval, *fvalue; 471 472 if ( !f ) { 473 return 0; 474 } 475 476 /* always uppercase strings by now */ 477 #ifdef BACKSQL_UPPERCASE_FILTER 478 if ( f->f_sub_desc->ad_type->sat_substr && 479 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr, 480 bi->sql_caseIgnoreMatch ) ) 481 #endif /* BACKSQL_UPPERCASE_FILTER */ 482 { 483 casefold = 1; 484 } 485 486 if ( f->f_sub_desc->ad_type->sat_substr && 487 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr, 488 bi->sql_telephoneNumberMatch ) ) 489 { 490 491 struct berval bv; 492 ber_len_t i, s, a; 493 494 /* 495 * to check for matching telephone numbers 496 * with intermixed chars, e.g. val='1234' 497 * use 498 * 499 * val LIKE '%1%2%3%4%' 500 */ 501 502 BER_BVZERO( &bv ); 503 if ( f->f_sub_initial.bv_val ) { 504 bv.bv_len += f->f_sub_initial.bv_len + backsql_val_escape( NULL, &f->f_sub_initial, NULL ); 505 } 506 if ( f->f_sub_any != NULL ) { 507 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) { 508 bv.bv_len += f->f_sub_any[ a ].bv_len + backsql_val_escape( NULL, &f->f_sub_any[ a ], NULL ); 509 } 510 } 511 if ( f->f_sub_final.bv_val ) { 512 bv.bv_len += f->f_sub_final.bv_len + backsql_val_escape( NULL, &f->f_sub_final, NULL ); 513 } 514 bv.bv_len = 2 * bv.bv_len - 1; 515 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 516 517 s = 0; 518 if ( !BER_BVISNULL( &f->f_sub_initial ) ) { 519 fvalue = &f->f_sub_initial; 520 escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval ); 521 if ( escaped ) 522 fvalue = &escval; 523 bv.bv_val[ s ] = fvalue->bv_val[ 0 ]; 524 for ( i = 1; i < fvalue->bv_len; i++ ) { 525 bv.bv_val[ s + 2 * i - 1 ] = '%'; 526 bv.bv_val[ s + 2 * i ] = fvalue->bv_val[ i ]; 527 } 528 bv.bv_val[ s + 2 * i - 1 ] = '%'; 529 s += 2 * i; 530 if ( escaped ) 531 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 532 } 533 534 if ( f->f_sub_any != NULL ) { 535 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) { 536 fvalue = &f->f_sub_any[ a ]; 537 escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval ); 538 if ( escaped ) 539 fvalue = &escval; 540 bv.bv_val[ s ] = fvalue->bv_val[ 0 ]; 541 for ( i = 1; i < fvalue->bv_len; i++ ) { 542 bv.bv_val[ s + 2 * i - 1 ] = '%'; 543 bv.bv_val[ s + 2 * i ] = fvalue->bv_val[ i ]; 544 } 545 bv.bv_val[ s + 2 * i - 1 ] = '%'; 546 s += 2 * i; 547 if ( escaped ) 548 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 549 } 550 } 551 552 if ( !BER_BVISNULL( &f->f_sub_final ) ) { 553 fvalue = &f->f_sub_final; 554 escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval ); 555 if ( escaped ) 556 fvalue = &escval; 557 bv.bv_val[ s ] = fvalue->bv_val[ 0 ]; 558 for ( i = 1; i < fvalue->bv_len; i++ ) { 559 bv.bv_val[ s + 2 * i - 1 ] = '%'; 560 bv.bv_val[ s + 2 * i ] = fvalue->bv_val[ i ]; 561 } 562 bv.bv_val[ s + 2 * i - 1 ] = '%'; 563 s += 2 * i; 564 if ( escaped ) 565 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 566 } 567 568 bv.bv_val[ s - 1 ] = '\0'; 569 570 (void)backsql_process_filter_like( bsi, at, casefold, &bv ); 571 ch_free( bv.bv_val ); 572 573 return 1; 574 } 575 576 /* 577 * When dealing with case-sensitive strings 578 * we may omit normalization; however, normalized 579 * SQL filters are more liberal. 580 */ 581 582 backsql_strfcat_x( &bsi->bsi_flt_where, 583 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ ); 584 585 /* TimesTen */ 586 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n", 587 at->bam_ad->ad_cname.bv_val ); 588 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val, 589 at->bam_sel_expr_u.bv_val ? "' '" : "", 590 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" ); 591 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 592 /* 593 * If a pre-upper-cased version of the column 594 * or a precompiled upper function exists, use it 595 */ 596 backsql_strfcat_x( &bsi->bsi_flt_where, 597 bsi->bsi_op->o_tmpmemctx, 598 "bl", 599 &at->bam_sel_expr_u, 600 (ber_len_t)STRLENOF( " LIKE '" ), 601 " LIKE '" ); 602 603 } else { 604 backsql_strfcat_x( &bsi->bsi_flt_where, 605 bsi->bsi_op->o_tmpmemctx, 606 "bl", 607 &at->bam_sel_expr, 608 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" ); 609 } 610 611 if ( !BER_BVISNULL( &f->f_sub_initial ) ) { 612 ber_len_t start; 613 614 #ifdef BACKSQL_TRACE 615 Debug( LDAP_DEBUG_TRACE, 616 "==>backsql_process_sub_filter(%s): " 617 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 618 f->f_sub_initial.bv_val ); 619 #endif /* BACKSQL_TRACE */ 620 621 fvalue = &f->f_sub_initial; 622 escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval ); 623 if ( escaped ) 624 fvalue = &escval; 625 start = bsi->bsi_flt_where.bb_val.bv_len; 626 backsql_strfcat_x( &bsi->bsi_flt_where, 627 bsi->bsi_op->o_tmpmemctx, 628 "b", 629 fvalue ); 630 if ( escaped ) 631 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 632 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 633 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 634 } 635 } 636 637 backsql_strfcat_x( &bsi->bsi_flt_where, 638 bsi->bsi_op->o_tmpmemctx, 639 "c", '%' ); 640 641 if ( f->f_sub_any != NULL ) { 642 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) { 643 ber_len_t start; 644 645 #ifdef BACKSQL_TRACE 646 Debug( LDAP_DEBUG_TRACE, 647 "==>backsql_process_sub_filter(%s): " 648 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 649 i, f->f_sub_any[ i ].bv_val ); 650 #endif /* BACKSQL_TRACE */ 651 652 fvalue = &f->f_sub_any[ i ]; 653 escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval ); 654 if ( escaped ) 655 fvalue = &escval; 656 start = bsi->bsi_flt_where.bb_val.bv_len; 657 backsql_strfcat_x( &bsi->bsi_flt_where, 658 bsi->bsi_op->o_tmpmemctx, 659 "bc", 660 fvalue, 661 '%' ); 662 if ( escaped ) 663 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 664 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 665 /* 666 * Note: toupper('%') = '%' 667 */ 668 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 669 } 670 } 671 } 672 673 if ( !BER_BVISNULL( &f->f_sub_final ) ) { 674 ber_len_t start; 675 676 #ifdef BACKSQL_TRACE 677 Debug( LDAP_DEBUG_TRACE, 678 "==>backsql_process_sub_filter(%s): " 679 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 680 f->f_sub_final.bv_val ); 681 #endif /* BACKSQL_TRACE */ 682 683 fvalue = &f->f_sub_final; 684 escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval ); 685 if ( escaped ) 686 fvalue = &escval; 687 start = bsi->bsi_flt_where.bb_val.bv_len; 688 backsql_strfcat_x( &bsi->bsi_flt_where, 689 bsi->bsi_op->o_tmpmemctx, 690 "b", 691 fvalue ); 692 if ( escaped ) 693 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 694 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 695 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 696 } 697 } 698 699 backsql_strfcat_x( &bsi->bsi_flt_where, 700 bsi->bsi_op->o_tmpmemctx, 701 "l", 702 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" ); 703 704 return 1; 705 } 706 707 static int 708 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls ) 709 { 710 if ( BER_BVISNULL( from_tbls ) ) { 711 return LDAP_SUCCESS; 712 } 713 714 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) { 715 char *start, *end; 716 struct berval tmp; 717 718 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx ); 719 720 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) { 721 if ( end ) { 722 end[0] = '\0'; 723 } 724 725 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL ) 726 { 727 backsql_strfcat_x( &bsi->bsi_from, 728 bsi->bsi_op->o_tmpmemctx, 729 "cs", ',', start ); 730 } 731 732 if ( end ) { 733 /* in case there are spaces after the comma... */ 734 for ( start = &end[1]; isspace( start[0] ); start++ ); 735 if ( start[0] ) { 736 end = strchr( start, ',' ); 737 } else { 738 start = NULL; 739 } 740 } else { 741 start = NULL; 742 } 743 } 744 745 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx ); 746 747 } else { 748 backsql_strfcat_x( &bsi->bsi_from, 749 bsi->bsi_op->o_tmpmemctx, 750 "b", from_tbls ); 751 } 752 753 return LDAP_SUCCESS; 754 } 755 756 static int 757 backsql_process_filter( backsql_srch_info *bsi, Filter *f ) 758 { 759 backsql_at_map_rec **vat = NULL; 760 AttributeDescription *ad = NULL; 761 unsigned i; 762 int done = 0; 763 int rc = 0; 764 765 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n" ); 766 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) { 767 struct berval flt; 768 char *msg = NULL; 769 770 switch ( f->f_result ) { 771 case LDAP_COMPARE_TRUE: 772 BER_BVSTR( &flt, "10=10" ); 773 msg = "TRUE"; 774 break; 775 776 case LDAP_COMPARE_FALSE: 777 BER_BVSTR( &flt, "11=0" ); 778 msg = "FALSE"; 779 break; 780 781 case SLAPD_COMPARE_UNDEFINED: 782 BER_BVSTR( &flt, "12=0" ); 783 msg = "UNDEFINED"; 784 break; 785 786 default: 787 rc = -1; 788 goto done; 789 } 790 791 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): " 792 "filter computed (%s)\n", msg ); 793 backsql_strfcat_x( &bsi->bsi_flt_where, 794 bsi->bsi_op->o_tmpmemctx, "b", &flt ); 795 rc = 1; 796 goto done; 797 } 798 799 if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) { 800 backsql_strfcat_x( &bsi->bsi_flt_where, 801 bsi->bsi_op->o_tmpmemctx, 802 "l", 803 (ber_len_t)STRLENOF( "1=0" ), "1=0" ); 804 done = 1; 805 rc = 1; 806 goto done; 807 } 808 809 switch( f->f_choice ) { 810 case LDAP_FILTER_OR: 811 rc = backsql_process_filter_list( bsi, f->f_or, 812 LDAP_FILTER_OR ); 813 done = 1; 814 break; 815 816 case LDAP_FILTER_AND: 817 rc = backsql_process_filter_list( bsi, f->f_and, 818 LDAP_FILTER_AND ); 819 done = 1; 820 break; 821 822 case LDAP_FILTER_NOT: 823 backsql_strfcat_x( &bsi->bsi_flt_where, 824 bsi->bsi_op->o_tmpmemctx, 825 "l", 826 (ber_len_t)STRLENOF( "NOT (" /* ) */ ), 827 "NOT (" /* ) */ ); 828 rc = backsql_process_filter( bsi, f->f_not ); 829 backsql_strfcat_x( &bsi->bsi_flt_where, 830 bsi->bsi_op->o_tmpmemctx, 831 "c", /* ( */ ')' ); 832 done = 1; 833 break; 834 835 case LDAP_FILTER_PRESENT: 836 ad = f->f_desc; 837 break; 838 839 case LDAP_FILTER_EXT: 840 ad = f->f_mra->ma_desc; 841 if ( f->f_mr_dnattrs ) { 842 /* 843 * if dn attrs filtering is requested, better return 844 * success and let test_filter() deal with candidate 845 * selection; otherwise we'd need to set conditions 846 * on the contents of the DN, e.g. "SELECT ... FROM 847 * ldap_entries AS attributeName WHERE attributeName.dn 848 * like '%attributeName=value%'" 849 */ 850 backsql_strfcat_x( &bsi->bsi_flt_where, 851 bsi->bsi_op->o_tmpmemctx, 852 "l", 853 (ber_len_t)STRLENOF( "1=1" ), "1=1" ); 854 bsi->bsi_status = LDAP_SUCCESS; 855 rc = 1; 856 goto done; 857 } 858 break; 859 860 default: 861 ad = f->f_av_desc; 862 break; 863 } 864 865 if ( rc == -1 ) { 866 goto done; 867 } 868 869 if ( done ) { 870 rc = 1; 871 goto done; 872 } 873 874 /* 875 * Turn structuralObjectClass into objectClass 876 */ 877 if ( ad == slap_schema.si_ad_objectClass 878 || ad == slap_schema.si_ad_structuralObjectClass ) 879 { 880 /* 881 * If the filter is LDAP_FILTER_PRESENT, then it's done; 882 * otherwise, let's see if we are lucky: filtering 883 * for "structural" objectclass or ancestor... 884 */ 885 switch ( f->f_choice ) { 886 case LDAP_FILTER_EQUALITY: 887 { 888 ObjectClass *oc = oc_bvfind( &f->f_av_value ); 889 890 if ( oc == NULL ) { 891 Debug( LDAP_DEBUG_TRACE, 892 "backsql_process_filter(): " 893 "unknown objectClass \"%s\" " 894 "in filter\n", 895 f->f_av_value.bv_val ); 896 bsi->bsi_status = LDAP_OTHER; 897 rc = -1; 898 goto done; 899 } 900 901 /* 902 * "structural" objectClass inheritance: 903 * - a search for "person" will also return 904 * "inetOrgPerson" 905 * - a search for "top" will return everything 906 */ 907 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) { 908 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" ); 909 910 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses ); 911 912 backsql_strfcat_x( &bsi->bsi_flt_where, 913 bsi->bsi_op->o_tmpmemctx, 914 "lbl", 915 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ), 916 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */, 917 &bsi->bsi_oc->bom_oc->soc_cname, 918 (ber_len_t)STRLENOF( /* ((' */ "'))" ), 919 /* ((' */ "'))" ); 920 bsi->bsi_status = LDAP_SUCCESS; 921 rc = 1; 922 goto done; 923 } 924 925 break; 926 } 927 928 case LDAP_FILTER_PRESENT: 929 backsql_strfcat_x( &bsi->bsi_flt_where, 930 bsi->bsi_op->o_tmpmemctx, 931 "l", 932 (ber_len_t)STRLENOF( "3=3" ), "3=3" ); 933 bsi->bsi_status = LDAP_SUCCESS; 934 rc = 1; 935 goto done; 936 937 /* FIXME: LDAP_FILTER_EXT? */ 938 939 default: 940 Debug( LDAP_DEBUG_TRACE, 941 "backsql_process_filter(): " 942 "illegal/unhandled filter " 943 "on objectClass attribute" ); 944 bsi->bsi_status = LDAP_OTHER; 945 rc = -1; 946 goto done; 947 } 948 949 } else if ( ad == slap_schema.si_ad_entryUUID ) { 950 unsigned long oc_id; 951 #ifdef BACKSQL_ARBITRARY_KEY 952 struct berval keyval; 953 #else /* ! BACKSQL_ARBITRARY_KEY */ 954 unsigned long keyval; 955 char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; 956 #endif /* ! BACKSQL_ARBITRARY_KEY */ 957 958 switch ( f->f_choice ) { 959 case LDAP_FILTER_EQUALITY: 960 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval ); 961 962 if ( oc_id != bsi->bsi_oc->bom_id ) { 963 bsi->bsi_status = LDAP_SUCCESS; 964 rc = -1; 965 goto done; 966 } 967 968 #ifdef BACKSQL_ARBITRARY_KEY 969 backsql_strfcat_x( &bsi->bsi_flt_where, 970 bsi->bsi_op->o_tmpmemctx, 971 "bcblbc", 972 &bsi->bsi_oc->bom_keytbl, '.', 973 &bsi->bsi_oc->bom_keycol, 974 STRLENOF( " LIKE '" ), " LIKE '", 975 &keyval, '\'' ); 976 #else /* ! BACKSQL_ARBITRARY_KEY */ 977 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval ); 978 backsql_strfcat_x( &bsi->bsi_flt_where, 979 bsi->bsi_op->o_tmpmemctx, 980 "bcbcs", 981 &bsi->bsi_oc->bom_keytbl, '.', 982 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf ); 983 #endif /* ! BACKSQL_ARBITRARY_KEY */ 984 break; 985 986 case LDAP_FILTER_PRESENT: 987 backsql_strfcat_x( &bsi->bsi_flt_where, 988 bsi->bsi_op->o_tmpmemctx, 989 "l", 990 (ber_len_t)STRLENOF( "4=4" ), "4=4" ); 991 break; 992 993 default: 994 rc = -1; 995 goto done; 996 } 997 998 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID; 999 rc = 1; 1000 goto done; 1001 1002 #ifdef BACKSQL_SYNCPROV 1003 } else if ( ad == slap_schema.si_ad_entryCSN ) { 1004 /* 1005 * support for syncrepl as provider... 1006 */ 1007 #if 0 1008 if ( !bsi->bsi_op->o_sync ) { 1009 /* unsupported at present... */ 1010 bsi->bsi_status = LDAP_OTHER; 1011 rc = -1; 1012 goto done; 1013 } 1014 #endif 1015 1016 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID); 1017 1018 /* if doing a syncrepl, try to return as much as possible, 1019 * and always match the filter */ 1020 backsql_strfcat_x( &bsi->bsi_flt_where, 1021 bsi->bsi_op->o_tmpmemctx, 1022 "l", 1023 (ber_len_t)STRLENOF( "5=5" ), "5=5" ); 1024 1025 /* save for later use in operational attributes */ 1026 /* FIXME: saves only the first occurrence, because 1027 * the filter during updates is written as 1028 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))" 1029 * so we want our fake entryCSN to match the greatest 1030 * value 1031 */ 1032 if ( bsi->bsi_op->o_private == NULL ) { 1033 bsi->bsi_op->o_private = &f->f_av_value; 1034 } 1035 bsi->bsi_status = LDAP_SUCCESS; 1036 1037 rc = 1; 1038 goto done; 1039 #endif /* BACKSQL_SYNCPROV */ 1040 1041 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) { 1042 /* 1043 * FIXME: this is not robust; e.g. a filter 1044 * '(!(hasSubordinates=TRUE))' fails because 1045 * in SQL it would read 'NOT (1=1)' instead 1046 * of no condition. 1047 * Note however that hasSubordinates is boolean, 1048 * so a more appropriate filter would be 1049 * '(hasSubordinates=FALSE)' 1050 * 1051 * A more robust search for hasSubordinates 1052 * would * require joining the ldap_entries table 1053 * selecting if there are descendants of the 1054 * candidate. 1055 */ 1056 backsql_strfcat_x( &bsi->bsi_flt_where, 1057 bsi->bsi_op->o_tmpmemctx, 1058 "l", 1059 (ber_len_t)STRLENOF( "6=6" ), "6=6" ); 1060 if ( ad == slap_schema.si_ad_hasSubordinates ) { 1061 /* 1062 * instruct candidate selection algorithm 1063 * and attribute list to try to detect 1064 * if an entry has subordinates 1065 */ 1066 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE; 1067 1068 } else { 1069 /* 1070 * clear attributes to fetch, to require ALL 1071 * and try extended match on all attributes 1072 */ 1073 backsql_attrlist_add( bsi, NULL ); 1074 } 1075 rc = 1; 1076 goto done; 1077 } 1078 1079 /* 1080 * attribute inheritance: 1081 */ 1082 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) { 1083 bsi->bsi_status = LDAP_OTHER; 1084 rc = -1; 1085 goto done; 1086 } 1087 1088 if ( vat == NULL ) { 1089 /* search anyway; other parts of the filter 1090 * may succeed */ 1091 backsql_strfcat_x( &bsi->bsi_flt_where, 1092 bsi->bsi_op->o_tmpmemctx, 1093 "l", 1094 (ber_len_t)STRLENOF( "7=7" ), "7=7" ); 1095 bsi->bsi_status = LDAP_SUCCESS; 1096 rc = 1; 1097 goto done; 1098 } 1099 1100 /* if required, open extra level of parens */ 1101 done = 0; 1102 if ( vat[0]->bam_next || vat[1] ) { 1103 backsql_strfcat_x( &bsi->bsi_flt_where, 1104 bsi->bsi_op->o_tmpmemctx, 1105 "c", '(' ); 1106 done = 1; 1107 } 1108 1109 i = 0; 1110 next:; 1111 /* apply attr */ 1112 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) { 1113 return -1; 1114 } 1115 1116 /* if more definitions of the same attr, apply */ 1117 if ( vat[i]->bam_next ) { 1118 backsql_strfcat_x( &bsi->bsi_flt_where, 1119 bsi->bsi_op->o_tmpmemctx, 1120 "l", 1121 STRLENOF( " OR " ), " OR " ); 1122 vat[i] = vat[i]->bam_next; 1123 goto next; 1124 } 1125 1126 /* if more descendants of the same attr, apply */ 1127 i++; 1128 if ( vat[i] ) { 1129 backsql_strfcat_x( &bsi->bsi_flt_where, 1130 bsi->bsi_op->o_tmpmemctx, 1131 "l", 1132 STRLENOF( " OR " ), " OR " ); 1133 goto next; 1134 } 1135 1136 /* if needed, close extra level of parens */ 1137 if ( done ) { 1138 backsql_strfcat_x( &bsi->bsi_flt_where, 1139 bsi->bsi_op->o_tmpmemctx, 1140 "c", ')' ); 1141 } 1142 1143 rc = 1; 1144 1145 done:; 1146 if ( vat ) { 1147 ch_free( vat ); 1148 } 1149 1150 Debug( LDAP_DEBUG_TRACE, 1151 "<==backsql_process_filter() %s\n", 1152 rc == 1 ? "succeeded" : "failed" ); 1153 1154 return rc; 1155 } 1156 1157 static int 1158 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at, 1159 int casefold, struct berval *filter_value ) 1160 { 1161 /* 1162 * maybe we should check type of at->sel_expr here somehow, 1163 * to know whether upper_func is applicable, but for now 1164 * upper_func stuff is made for Oracle, where UPPER is 1165 * safely applicable to NUMBER etc. 1166 */ 1167 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 1168 ber_len_t start; 1169 1170 backsql_strfcat_x( &bsi->bsi_flt_where, 1171 bsi->bsi_op->o_tmpmemctx, 1172 "cbl", 1173 '(', /* ) */ 1174 &at->bam_sel_expr_u, 1175 (ber_len_t)STRLENOF( "='" ), 1176 "='" ); 1177 1178 start = bsi->bsi_flt_where.bb_val.bv_len; 1179 1180 backsql_strfcat_x( &bsi->bsi_flt_where, 1181 bsi->bsi_op->o_tmpmemctx, 1182 "bl", 1183 filter_value, 1184 (ber_len_t)STRLENOF( /* (' */ "')" ), 1185 /* (' */ "')" ); 1186 1187 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 1188 1189 } else { 1190 backsql_strfcat_x( &bsi->bsi_flt_where, 1191 bsi->bsi_op->o_tmpmemctx, 1192 "cblbl", 1193 '(', /* ) */ 1194 &at->bam_sel_expr, 1195 (ber_len_t)STRLENOF( "='" ), "='", 1196 filter_value, 1197 (ber_len_t)STRLENOF( /* (' */ "')" ), 1198 /* (' */ "')" ); 1199 } 1200 1201 return 1; 1202 } 1203 1204 static int 1205 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at, 1206 int casefold, struct berval *filter_value ) 1207 { 1208 /* 1209 * maybe we should check type of at->sel_expr here somehow, 1210 * to know whether upper_func is applicable, but for now 1211 * upper_func stuff is made for Oracle, where UPPER is 1212 * safely applicable to NUMBER etc. 1213 */ 1214 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 1215 ber_len_t start; 1216 1217 backsql_strfcat_x( &bsi->bsi_flt_where, 1218 bsi->bsi_op->o_tmpmemctx, 1219 "cbl", 1220 '(', /* ) */ 1221 &at->bam_sel_expr_u, 1222 (ber_len_t)STRLENOF( " LIKE '%" ), 1223 " LIKE '%" ); 1224 1225 start = bsi->bsi_flt_where.bb_val.bv_len; 1226 1227 backsql_strfcat_x( &bsi->bsi_flt_where, 1228 bsi->bsi_op->o_tmpmemctx, 1229 "bl", 1230 filter_value, 1231 (ber_len_t)STRLENOF( /* (' */ "%')" ), 1232 /* (' */ "%')" ); 1233 1234 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 1235 1236 } else { 1237 backsql_strfcat_x( &bsi->bsi_flt_where, 1238 bsi->bsi_op->o_tmpmemctx, 1239 "cblbl", 1240 '(', /* ) */ 1241 &at->bam_sel_expr, 1242 (ber_len_t)STRLENOF( " LIKE '%" ), 1243 " LIKE '%", 1244 filter_value, 1245 (ber_len_t)STRLENOF( /* (' */ "%')" ), 1246 /* (' */ "%')" ); 1247 } 1248 1249 return 1; 1250 } 1251 1252 static int 1253 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at ) 1254 { 1255 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 1256 int casefold = 0; 1257 struct berval *filter_value = NULL; 1258 MatchingRule *matching_rule = NULL; 1259 struct berval ordering = BER_BVC("<="); 1260 struct berval escval; 1261 int escaped = 0; 1262 1263 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n", 1264 at->bam_ad->ad_cname.bv_val ); 1265 1266 /* 1267 * need to add this attribute to list of attrs to load, 1268 * so that we can do test_filter() later 1269 */ 1270 backsql_attrlist_add( bsi, at->bam_ad ); 1271 1272 backsql_merge_from_tbls( bsi, &at->bam_from_tbls ); 1273 1274 if ( !BER_BVISNULL( &at->bam_join_where ) 1275 && strstr( bsi->bsi_join_where.bb_val.bv_val, 1276 at->bam_join_where.bv_val ) == NULL ) 1277 { 1278 backsql_strfcat_x( &bsi->bsi_join_where, 1279 bsi->bsi_op->o_tmpmemctx, 1280 "lb", 1281 (ber_len_t)STRLENOF( " AND " ), " AND ", 1282 &at->bam_join_where ); 1283 } 1284 1285 if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) { 1286 backsql_strfcat_x( &bsi->bsi_flt_where, 1287 bsi->bsi_op->o_tmpmemctx, 1288 "l", 1289 (ber_len_t)STRLENOF( "1=0" ), "1=0" ); 1290 return 1; 1291 } 1292 1293 switch ( f->f_choice ) { 1294 case LDAP_FILTER_EQUALITY: 1295 filter_value = &f->f_av_value; 1296 matching_rule = at->bam_ad->ad_type->sat_equality; 1297 1298 goto equality_match; 1299 1300 /* fail over into next case */ 1301 1302 case LDAP_FILTER_EXT: 1303 filter_value = &f->f_mra->ma_value; 1304 matching_rule = f->f_mr_rule; 1305 1306 equality_match:; 1307 /* always uppercase strings by now */ 1308 #ifdef BACKSQL_UPPERCASE_FILTER 1309 if ( SLAP_MR_ASSOCIATED( matching_rule, 1310 bi->sql_caseIgnoreMatch ) ) 1311 #endif /* BACKSQL_UPPERCASE_FILTER */ 1312 { 1313 casefold = 1; 1314 } 1315 1316 escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval ); 1317 if ( escaped ) 1318 filter_value = &escval; 1319 1320 /* FIXME: directoryString filtering should use a similar 1321 * approach to deal with non-prettified values like 1322 * " A non prettified value ", by using a LIKE 1323 * filter with all whitespaces collapsed to a single '%' */ 1324 if ( SLAP_MR_ASSOCIATED( matching_rule, 1325 bi->sql_telephoneNumberMatch ) ) 1326 { 1327 struct berval bv; 1328 ber_len_t i; 1329 1330 /* 1331 * to check for matching telephone numbers 1332 * with intermized chars, e.g. val='1234' 1333 * use 1334 * 1335 * val LIKE '%1%2%3%4%' 1336 */ 1337 1338 bv.bv_len = 2 * filter_value->bv_len - 1; 1339 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 1340 1341 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ]; 1342 for ( i = 1; i < filter_value->bv_len; i++ ) { 1343 bv.bv_val[ 2 * i - 1 ] = '%'; 1344 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ]; 1345 } 1346 bv.bv_val[ 2 * i - 1 ] = '\0'; 1347 1348 (void)backsql_process_filter_like( bsi, at, casefold, &bv ); 1349 ch_free( bv.bv_val ); 1350 1351 break; 1352 } 1353 1354 /* NOTE: this is required by objectClass inheritance 1355 * and auxiliary objectClass use in filters for slightly 1356 * more efficient candidate selection. */ 1357 /* FIXME: a bit too many specializations to deal with 1358 * very specific cases... */ 1359 if ( at->bam_ad == slap_schema.si_ad_objectClass 1360 || at->bam_ad == slap_schema.si_ad_structuralObjectClass ) 1361 { 1362 backsql_strfcat_x( &bsi->bsi_flt_where, 1363 bsi->bsi_op->o_tmpmemctx, 1364 "lbl", 1365 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ), 1366 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */, 1367 filter_value, 1368 (ber_len_t)STRLENOF( /* (' */ "')" ), 1369 /* (' */ "')" ); 1370 break; 1371 } 1372 1373 /* 1374 * maybe we should check type of at->sel_expr here somehow, 1375 * to know whether upper_func is applicable, but for now 1376 * upper_func stuff is made for Oracle, where UPPER is 1377 * safely applicable to NUMBER etc. 1378 */ 1379 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value ); 1380 break; 1381 1382 case LDAP_FILTER_GE: 1383 ordering.bv_val = ">="; 1384 1385 /* fall thru to next case */ 1386 1387 case LDAP_FILTER_LE: 1388 filter_value = &f->f_av_value; 1389 1390 /* always uppercase strings by now */ 1391 #ifdef BACKSQL_UPPERCASE_FILTER 1392 if ( at->bam_ad->ad_type->sat_ordering && 1393 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering, 1394 bi->sql_caseIgnoreMatch ) ) 1395 #endif /* BACKSQL_UPPERCASE_FILTER */ 1396 { 1397 casefold = 1; 1398 } 1399 1400 escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval ); 1401 if ( escaped ) 1402 filter_value = &escval; 1403 1404 /* 1405 * FIXME: should we uppercase the operands? 1406 */ 1407 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 1408 ber_len_t start; 1409 1410 backsql_strfcat_x( &bsi->bsi_flt_where, 1411 bsi->bsi_op->o_tmpmemctx, 1412 "cbbc", 1413 '(', /* ) */ 1414 &at->bam_sel_expr_u, 1415 &ordering, 1416 '\'' ); 1417 1418 start = bsi->bsi_flt_where.bb_val.bv_len; 1419 1420 backsql_strfcat_x( &bsi->bsi_flt_where, 1421 bsi->bsi_op->o_tmpmemctx, 1422 "bl", 1423 filter_value, 1424 (ber_len_t)STRLENOF( /* (' */ "')" ), 1425 /* (' */ "')" ); 1426 1427 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 1428 1429 } else { 1430 backsql_strfcat_x( &bsi->bsi_flt_where, 1431 bsi->bsi_op->o_tmpmemctx, 1432 "cbbcbl", 1433 '(' /* ) */ , 1434 &at->bam_sel_expr, 1435 &ordering, 1436 '\'', 1437 filter_value, 1438 (ber_len_t)STRLENOF( /* (' */ "')" ), 1439 /* ( */ "')" ); 1440 } 1441 break; 1442 1443 case LDAP_FILTER_PRESENT: 1444 backsql_strfcat_x( &bsi->bsi_flt_where, 1445 bsi->bsi_op->o_tmpmemctx, 1446 "lbl", 1447 (ber_len_t)STRLENOF( "NOT (" /* ) */), 1448 "NOT (", /* ) */ 1449 &at->bam_sel_expr, 1450 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ), 1451 /* ( */ " IS NULL)" ); 1452 break; 1453 1454 case LDAP_FILTER_SUBSTRINGS: 1455 backsql_process_sub_filter( bsi, f, at ); 1456 break; 1457 1458 case LDAP_FILTER_APPROX: 1459 /* we do our best */ 1460 1461 filter_value = &f->f_av_value; 1462 escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval ); 1463 if ( escaped ) 1464 filter_value = &escval; 1465 /* 1466 * maybe we should check type of at->sel_expr here somehow, 1467 * to know whether upper_func is applicable, but for now 1468 * upper_func stuff is made for Oracle, where UPPER is 1469 * safely applicable to NUMBER etc. 1470 */ 1471 (void)backsql_process_filter_like( bsi, at, 1, filter_value ); 1472 break; 1473 1474 default: 1475 /* unhandled filter type; should not happen */ 1476 assert( 0 ); 1477 backsql_strfcat_x( &bsi->bsi_flt_where, 1478 bsi->bsi_op->o_tmpmemctx, 1479 "l", 1480 (ber_len_t)STRLENOF( "8=8" ), "8=8" ); 1481 break; 1482 1483 } 1484 1485 if ( escaped ) 1486 bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx ); 1487 1488 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n", 1489 at->bam_ad->ad_cname.bv_val ); 1490 1491 return 1; 1492 } 1493 1494 static int 1495 backsql_srch_query( backsql_srch_info *bsi, struct berval *query ) 1496 { 1497 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 1498 int rc; 1499 1500 assert( query != NULL ); 1501 BER_BVZERO( query ); 1502 1503 bsi->bsi_use_subtree_shortcut = 0; 1504 1505 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n" ); 1506 BER_BVZERO( &bsi->bsi_sel.bb_val ); 1507 BER_BVZERO( &bsi->bsi_sel.bb_val ); 1508 bsi->bsi_sel.bb_len = 0; 1509 BER_BVZERO( &bsi->bsi_from.bb_val ); 1510 bsi->bsi_from.bb_len = 0; 1511 BER_BVZERO( &bsi->bsi_join_where.bb_val ); 1512 bsi->bsi_join_where.bb_len = 0; 1513 BER_BVZERO( &bsi->bsi_flt_where.bb_val ); 1514 bsi->bsi_flt_where.bb_len = 0; 1515 1516 backsql_strfcat_x( &bsi->bsi_sel, 1517 bsi->bsi_op->o_tmpmemctx, 1518 "lbcbc", 1519 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ), 1520 "SELECT DISTINCT ldap_entries.id,", 1521 &bsi->bsi_oc->bom_keytbl, 1522 '.', 1523 &bsi->bsi_oc->bom_keycol, 1524 ',' ); 1525 1526 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) { 1527 backsql_strfcat_x( &bsi->bsi_sel, 1528 bsi->bsi_op->o_tmpmemctx, 1529 "blbl", 1530 &bi->sql_strcast_func, 1531 (ber_len_t)STRLENOF( "('" /* ') */ ), 1532 "('" /* ') */ , 1533 &bsi->bsi_oc->bom_oc->soc_cname, 1534 (ber_len_t)STRLENOF( /* (' */ "')" ), 1535 /* (' */ "')" ); 1536 } else { 1537 backsql_strfcat_x( &bsi->bsi_sel, 1538 bsi->bsi_op->o_tmpmemctx, 1539 "cbc", 1540 '\'', 1541 &bsi->bsi_oc->bom_oc->soc_cname, 1542 '\'' ); 1543 } 1544 1545 backsql_strfcat_x( &bsi->bsi_sel, 1546 bsi->bsi_op->o_tmpmemctx, 1547 "b", 1548 &bi->sql_dn_oc_aliasing ); 1549 backsql_strfcat_x( &bsi->bsi_from, 1550 bsi->bsi_op->o_tmpmemctx, 1551 "lb", 1552 (ber_len_t)STRLENOF( " FROM ldap_entries," ), 1553 " FROM ldap_entries,", 1554 &bsi->bsi_oc->bom_keytbl ); 1555 1556 backsql_strfcat_x( &bsi->bsi_join_where, 1557 bsi->bsi_op->o_tmpmemctx, 1558 "lbcbl", 1559 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ", 1560 &bsi->bsi_oc->bom_keytbl, 1561 '.', 1562 &bsi->bsi_oc->bom_keycol, 1563 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ), 1564 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ); 1565 1566 switch ( bsi->bsi_scope ) { 1567 case LDAP_SCOPE_BASE: 1568 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1569 backsql_strfcat_x( &bsi->bsi_join_where, 1570 bsi->bsi_op->o_tmpmemctx, 1571 "bl", 1572 &bi->sql_upper_func, 1573 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ), 1574 "(ldap_entries.dn)=?" ); 1575 } else { 1576 backsql_strfcat_x( &bsi->bsi_join_where, 1577 bsi->bsi_op->o_tmpmemctx, 1578 "l", 1579 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ), 1580 "ldap_entries.dn=?" ); 1581 } 1582 break; 1583 1584 case BACKSQL_SCOPE_BASE_LIKE: 1585 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1586 backsql_strfcat_x( &bsi->bsi_join_where, 1587 bsi->bsi_op->o_tmpmemctx, 1588 "bl", 1589 &bi->sql_upper_func, 1590 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ), 1591 "(ldap_entries.dn) LIKE ?" ); 1592 } else { 1593 backsql_strfcat_x( &bsi->bsi_join_where, 1594 bsi->bsi_op->o_tmpmemctx, 1595 "l", 1596 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ), 1597 "ldap_entries.dn LIKE ?" ); 1598 } 1599 break; 1600 1601 case LDAP_SCOPE_ONELEVEL: 1602 backsql_strfcat_x( &bsi->bsi_join_where, 1603 bsi->bsi_op->o_tmpmemctx, 1604 "l", 1605 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ), 1606 "ldap_entries.parent=?" ); 1607 break; 1608 1609 case LDAP_SCOPE_SUBORDINATE: 1610 case LDAP_SCOPE_SUBTREE: 1611 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) { 1612 int i; 1613 BackendDB *bd = bsi->bsi_op->o_bd; 1614 1615 assert( bd->be_nsuffix != NULL ); 1616 1617 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ ) 1618 { 1619 if ( dn_match( &bd->be_nsuffix[ i ], 1620 bsi->bsi_base_ndn ) ) 1621 { 1622 /* pass this to the candidate selection 1623 * routine so that the DN is not bound 1624 * to the select statement */ 1625 bsi->bsi_use_subtree_shortcut = 1; 1626 break; 1627 } 1628 } 1629 } 1630 1631 if ( bsi->bsi_use_subtree_shortcut ) { 1632 /* Skip the base DN filter, as every entry will match it */ 1633 backsql_strfcat_x( &bsi->bsi_join_where, 1634 bsi->bsi_op->o_tmpmemctx, 1635 "l", 1636 (ber_len_t)STRLENOF( "9=9"), "9=9"); 1637 1638 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) { 1639 /* This should always be true... */ 1640 backsql_strfcat_x( &bsi->bsi_join_where, 1641 bsi->bsi_op->o_tmpmemctx, 1642 "b", 1643 &bi->sql_subtree_cond ); 1644 1645 } else if ( BACKSQL_CANUPPERCASE( bi ) ) { 1646 backsql_strfcat_x( &bsi->bsi_join_where, 1647 bsi->bsi_op->o_tmpmemctx, 1648 "bl", 1649 &bi->sql_upper_func, 1650 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ), 1651 "(ldap_entries.dn) LIKE ?" ); 1652 1653 } else { 1654 backsql_strfcat_x( &bsi->bsi_join_where, 1655 bsi->bsi_op->o_tmpmemctx, 1656 "l", 1657 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ), 1658 "ldap_entries.dn LIKE ?" ); 1659 } 1660 1661 break; 1662 1663 default: 1664 assert( 0 ); 1665 } 1666 1667 #ifndef BACKSQL_ARBITRARY_KEY 1668 /* If paged results are in effect, ignore low ldap_entries.id numbers */ 1669 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) { 1670 unsigned long lowid = 0; 1671 1672 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */ 1673 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) ) 1674 { 1675 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ); 1676 } 1677 1678 if ( lowid ) { 1679 char lowidstring[48]; 1680 int lowidlen; 1681 1682 lowidlen = snprintf( lowidstring, sizeof( lowidstring ), 1683 " AND ldap_entries.id>%lu", lowid ); 1684 backsql_strfcat_x( &bsi->bsi_join_where, 1685 bsi->bsi_op->o_tmpmemctx, 1686 "l", 1687 (ber_len_t)lowidlen, 1688 lowidstring ); 1689 } 1690 } 1691 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1692 1693 rc = backsql_process_filter( bsi, bsi->bsi_filter ); 1694 if ( rc > 0 ) { 1695 struct berbuf bb = BB_NULL; 1696 1697 backsql_strfcat_x( &bb, 1698 bsi->bsi_op->o_tmpmemctx, 1699 "bbblb", 1700 &bsi->bsi_sel.bb_val, 1701 &bsi->bsi_from.bb_val, 1702 &bsi->bsi_join_where.bb_val, 1703 (ber_len_t)STRLENOF( " AND " ), " AND ", 1704 &bsi->bsi_flt_where.bb_val ); 1705 1706 *query = bb.bb_val; 1707 1708 } else if ( rc < 0 ) { 1709 /* 1710 * Indicates that there's no possible way the filter matches 1711 * anything. No need to issue the query 1712 */ 1713 free( query->bv_val ); 1714 BER_BVZERO( query ); 1715 } 1716 1717 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1718 BER_BVZERO( &bsi->bsi_sel.bb_val ); 1719 bsi->bsi_sel.bb_len = 0; 1720 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1721 BER_BVZERO( &bsi->bsi_from.bb_val ); 1722 bsi->bsi_from.bb_len = 0; 1723 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1724 BER_BVZERO( &bsi->bsi_join_where.bb_val ); 1725 bsi->bsi_join_where.bb_len = 0; 1726 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1727 BER_BVZERO( &bsi->bsi_flt_where.bb_val ); 1728 bsi->bsi_flt_where.bb_len = 0; 1729 1730 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n", 1731 query->bv_val ? query->bv_val : "NULL" ); 1732 1733 return ( rc <= 0 ? 1 : 0 ); 1734 } 1735 1736 static int 1737 backsql_oc_get_candidates( void *v_oc, void *v_bsi ) 1738 { 1739 backsql_oc_map_rec *oc = v_oc; 1740 backsql_srch_info *bsi = v_bsi; 1741 Operation *op = bsi->bsi_op; 1742 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 1743 struct berval query; 1744 SQLHSTMT sth = SQL_NULL_HSTMT; 1745 RETCODE rc; 1746 int res; 1747 BACKSQL_ROW_NTS row; 1748 int i; 1749 int j; 1750 int n_candidates = bsi->bsi_n_candidates; 1751 1752 /* 1753 * + 1 because we need room for '%'; 1754 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE; 1755 * this makes a subtree 1756 * search for a DN BACKSQL_MAX_DN_LEN long legal 1757 * if it returns that DN only 1758 */ 1759 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ]; 1760 1761 bsi->bsi_status = LDAP_SUCCESS; 1762 1763 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n", 1764 BACKSQL_OC_NAME( oc ) ); 1765 1766 /* check for abandon */ 1767 if ( op->o_abandon ) { 1768 bsi->bsi_status = SLAPD_ABANDON; 1769 return BACKSQL_AVL_STOP; 1770 } 1771 1772 #ifndef BACKSQL_ARBITRARY_KEY 1773 /* If paged results have already completed this objectClass, skip it */ 1774 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 1775 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) ) 1776 { 1777 return BACKSQL_AVL_CONTINUE; 1778 } 1779 } 1780 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1781 1782 if ( bsi->bsi_n_candidates == -1 ) { 1783 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1784 "unchecked limit has been overcome\n" ); 1785 /* should never get here */ 1786 assert( 0 ); 1787 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED; 1788 return BACKSQL_AVL_STOP; 1789 } 1790 1791 bsi->bsi_oc = oc; 1792 res = backsql_srch_query( bsi, &query ); 1793 if ( res ) { 1794 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1795 "error while constructing query for objectclass \"%s\"\n", 1796 oc->bom_oc->soc_cname.bv_val ); 1797 /* 1798 * FIXME: need to separate errors from legally 1799 * impossible filters 1800 */ 1801 switch ( bsi->bsi_status ) { 1802 case LDAP_SUCCESS: 1803 case LDAP_UNDEFINED_TYPE: 1804 case LDAP_NO_SUCH_OBJECT: 1805 /* we are conservative... */ 1806 default: 1807 bsi->bsi_status = LDAP_SUCCESS; 1808 /* try next */ 1809 return BACKSQL_AVL_CONTINUE; 1810 1811 case LDAP_ADMINLIMIT_EXCEEDED: 1812 case LDAP_OTHER: 1813 /* don't try any more */ 1814 return BACKSQL_AVL_STOP; 1815 } 1816 } 1817 1818 if ( BER_BVISNULL( &query ) ) { 1819 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1820 "could not construct query for objectclass \"%s\"\n", 1821 oc->bom_oc->soc_cname.bv_val ); 1822 bsi->bsi_status = LDAP_SUCCESS; 1823 return BACKSQL_AVL_CONTINUE; 1824 } 1825 1826 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 1827 query.bv_val ); 1828 1829 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 ); 1830 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx ); 1831 BER_BVZERO( &query ); 1832 if ( rc != SQL_SUCCESS ) { 1833 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1834 "error preparing query\n" ); 1835 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc ); 1836 bsi->bsi_status = LDAP_OTHER; 1837 return BACKSQL_AVL_CONTINUE; 1838 } 1839 1840 Debug( LDAP_DEBUG_TRACE, "id: '" BACKSQL_IDNUMFMT "'\n", 1841 bsi->bsi_oc->bom_id ); 1842 1843 rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_INPUT, 1844 &bsi->bsi_oc->bom_id ); 1845 if ( rc != SQL_SUCCESS ) { 1846 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1847 "error binding objectclass id parameter\n" ); 1848 bsi->bsi_status = LDAP_OTHER; 1849 return BACKSQL_AVL_CONTINUE; 1850 } 1851 1852 switch ( bsi->bsi_scope ) { 1853 case LDAP_SCOPE_BASE: 1854 case BACKSQL_SCOPE_BASE_LIKE: 1855 /* 1856 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN; 1857 * however this should be handled earlier 1858 */ 1859 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) { 1860 bsi->bsi_status = LDAP_OTHER; 1861 return BACKSQL_AVL_CONTINUE; 1862 } 1863 1864 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val, 1865 bsi->bsi_base_ndn->bv_len + 1 ); 1866 1867 /* uppercase DN only if the stored DN can be uppercased 1868 * for comparison */ 1869 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1870 ldap_pvt_str2upper( tmp_base_ndn ); 1871 } 1872 1873 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n", 1874 tmp_base_ndn ); 1875 1876 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT, 1877 tmp_base_ndn, BACKSQL_MAX_DN_LEN ); 1878 if ( rc != SQL_SUCCESS ) { 1879 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1880 "error binding base_ndn parameter\n" ); 1881 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 1882 sth, rc ); 1883 bsi->bsi_status = LDAP_OTHER; 1884 return BACKSQL_AVL_CONTINUE; 1885 } 1886 break; 1887 1888 case LDAP_SCOPE_SUBORDINATE: 1889 case LDAP_SCOPE_SUBTREE: 1890 { 1891 /* if short-cutting the search base, 1892 * don't bind any parameter */ 1893 if ( bsi->bsi_use_subtree_shortcut ) { 1894 break; 1895 } 1896 1897 /* 1898 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN; 1899 * however this should be handled earlier 1900 */ 1901 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) { 1902 bsi->bsi_status = LDAP_OTHER; 1903 return BACKSQL_AVL_CONTINUE; 1904 } 1905 1906 /* 1907 * Sets the parameters for the SQL built earlier 1908 * NOTE that all the databases could actually use 1909 * the TimesTen version, which would be cleaner 1910 * and would also eliminate the need for the 1911 * subtree_cond line in the configuration file. 1912 * For now, I'm leaving it the way it is, 1913 * so non-TimesTen databases use the original code. 1914 * But at some point this should get cleaned up. 1915 * 1916 * If "dn" is being used, do a suffix search. 1917 * If "dn_ru" is being used, do a prefix search. 1918 */ 1919 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) { 1920 tmp_base_ndn[ 0 ] = '\0'; 1921 1922 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1; 1923 j >= 0; i++, j--) { 1924 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ]; 1925 } 1926 1927 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) { 1928 tmp_base_ndn[ i++ ] = ','; 1929 } 1930 1931 tmp_base_ndn[ i ] = '%'; 1932 tmp_base_ndn[ i + 1 ] = '\0'; 1933 1934 } else { 1935 i = 0; 1936 1937 tmp_base_ndn[ i++ ] = '%'; 1938 1939 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) { 1940 tmp_base_ndn[ i++ ] = ','; 1941 } 1942 1943 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val, 1944 bsi->bsi_base_ndn->bv_len + 1 ); 1945 } 1946 1947 /* uppercase DN only if the stored DN can be uppercased 1948 * for comparison */ 1949 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1950 ldap_pvt_str2upper( tmp_base_ndn ); 1951 } 1952 1953 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) { 1954 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n", 1955 tmp_base_ndn ); 1956 } else { 1957 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n", 1958 tmp_base_ndn ); 1959 } 1960 1961 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT, 1962 tmp_base_ndn, BACKSQL_MAX_DN_LEN ); 1963 if ( rc != SQL_SUCCESS ) { 1964 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1965 "error binding base_ndn parameter (2)\n" ); 1966 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 1967 sth, rc ); 1968 bsi->bsi_status = LDAP_OTHER; 1969 return BACKSQL_AVL_CONTINUE; 1970 } 1971 break; 1972 } 1973 1974 case LDAP_SCOPE_ONELEVEL: 1975 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ); 1976 1977 Debug( LDAP_DEBUG_TRACE, "(one)id=" BACKSQL_IDFMT "\n", 1978 BACKSQL_IDARG(bsi->bsi_base_id.eid_id) ); 1979 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT, 1980 &bsi->bsi_base_id.eid_id ); 1981 if ( rc != SQL_SUCCESS ) { 1982 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1983 "error binding base id parameter\n" ); 1984 bsi->bsi_status = LDAP_OTHER; 1985 return BACKSQL_AVL_CONTINUE; 1986 } 1987 break; 1988 } 1989 1990 rc = SQLExecute( sth ); 1991 if ( !BACKSQL_SUCCESS( rc ) ) { 1992 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1993 "error executing query\n" ); 1994 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc ); 1995 SQLFreeStmt( sth, SQL_DROP ); 1996 bsi->bsi_status = LDAP_OTHER; 1997 return BACKSQL_AVL_CONTINUE; 1998 } 1999 2000 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx ); 2001 rc = SQLFetch( sth ); 2002 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) { 2003 struct berval dn, pdn, ndn; 2004 backsql_entryID *c_id = NULL; 2005 int ret; 2006 2007 ber_str2bv( row.cols[ 3 ], 0, 0, &dn ); 2008 2009 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) { 2010 continue; 2011 } 2012 2013 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); 2014 if ( dn.bv_val != row.cols[ 3 ] ) { 2015 free( dn.bv_val ); 2016 } 2017 2018 if ( ret != LDAP_SUCCESS ) { 2019 continue; 2020 } 2021 2022 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) { 2023 goto cleanup; 2024 } 2025 2026 c_id = (backsql_entryID *)op->o_tmpcalloc( 1, 2027 sizeof( backsql_entryID ), op->o_tmpmemctx ); 2028 #ifdef BACKSQL_ARBITRARY_KEY 2029 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id, 2030 op->o_tmpmemctx ); 2031 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval, 2032 op->o_tmpmemctx ); 2033 #else /* ! BACKSQL_ARBITRARY_KEY */ 2034 if ( BACKSQL_STR2ID( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) { 2035 goto cleanup; 2036 } 2037 if ( BACKSQL_STR2ID( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) { 2038 goto cleanup; 2039 } 2040 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2041 c_id->eid_oc = bsi->bsi_oc; 2042 c_id->eid_oc_id = bsi->bsi_oc->bom_id; 2043 2044 c_id->eid_dn = pdn; 2045 c_id->eid_ndn = ndn; 2046 2047 /* append at end of list ... */ 2048 c_id->eid_next = NULL; 2049 *bsi->bsi_id_listtail = c_id; 2050 bsi->bsi_id_listtail = &c_id->eid_next; 2051 2052 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 2053 "added entry id=" BACKSQL_IDFMT " keyval=" BACKSQL_IDFMT " dn=\"%s\"\n", 2054 BACKSQL_IDARG(c_id->eid_id), 2055 BACKSQL_IDARG(c_id->eid_keyval), 2056 row.cols[ 3 ] ); 2057 2058 /* count candidates, for unchecked limit */ 2059 bsi->bsi_n_candidates--; 2060 if ( bsi->bsi_n_candidates == -1 ) { 2061 break; 2062 } 2063 continue; 2064 2065 cleanup:; 2066 if ( !BER_BVISNULL( &pdn ) ) { 2067 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); 2068 } 2069 if ( !BER_BVISNULL( &ndn ) ) { 2070 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 2071 } 2072 if ( c_id != NULL ) { 2073 ch_free( c_id ); 2074 } 2075 } 2076 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx ); 2077 SQLFreeStmt( sth, SQL_DROP ); 2078 2079 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n", 2080 n_candidates - bsi->bsi_n_candidates ); 2081 2082 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE ); 2083 } 2084 2085 int 2086 backsql_search( Operation *op, SlapReply *rs ) 2087 { 2088 backsql_info *bi = (backsql_info *)op->o_bd->be_private; 2089 SQLHDBC dbh = SQL_NULL_HDBC; 2090 int sres; 2091 Entry user_entry = { 0 }, 2092 base_entry = { 0 }; 2093 int manageDSAit = get_manageDSAit( op ); 2094 time_t stoptime = 0; 2095 backsql_srch_info bsi = { 0 }; 2096 backsql_entryID *eid = NULL; 2097 struct berval nbase = BER_BVNULL; 2098 #ifndef BACKSQL_ARBITRARY_KEY 2099 ID lastid = 0; 2100 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2101 2102 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): " 2103 "base=\"%s\", filter=\"%s\", scope=%d,", 2104 op->o_req_ndn.bv_val, 2105 op->ors_filterstr.bv_val, 2106 op->ors_scope ); 2107 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, " 2108 "attributes to load: %s\n", 2109 op->ors_deref, 2110 op->ors_attrsonly, 2111 op->ors_attrs == NULL ? "all" : "custom list" ); 2112 2113 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) { 2114 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2115 "search base length (%ld) exceeds max length (%d)\n", 2116 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN ); 2117 /* 2118 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate 2119 * since it is impossible that such a long DN exists 2120 * in the backend 2121 */ 2122 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 2123 send_ldap_result( op, rs ); 2124 return 1; 2125 } 2126 2127 sres = backsql_get_db_conn( op, &dbh ); 2128 if ( sres != LDAP_SUCCESS ) { 2129 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2130 "could not get connection handle - exiting\n" ); 2131 rs->sr_err = sres; 2132 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL; 2133 send_ldap_result( op, rs ); 2134 return 1; 2135 } 2136 2137 /* compute it anyway; root does not use it */ 2138 stoptime = op->o_time + op->ors_tlimit; 2139 2140 /* init search */ 2141 bsi.bsi_e = &base_entry; 2142 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn, 2143 op->ors_scope, 2144 stoptime, op->ors_filter, 2145 dbh, op, rs, op->ors_attrs, 2146 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); 2147 switch ( rs->sr_err ) { 2148 case LDAP_SUCCESS: 2149 break; 2150 2151 case LDAP_REFERRAL: 2152 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) && 2153 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) ) 2154 { 2155 rs->sr_err = LDAP_SUCCESS; 2156 rs->sr_text = NULL; 2157 rs->sr_matched = NULL; 2158 if ( rs->sr_ref ) { 2159 ber_bvarray_free( rs->sr_ref ); 2160 rs->sr_ref = NULL; 2161 } 2162 break; 2163 } 2164 2165 /* an entry was created; free it */ 2166 entry_clean( bsi.bsi_e ); 2167 2168 /* fall thru */ 2169 2170 default: 2171 if ( !BER_BVISNULL( &base_entry.e_nname ) 2172 && !access_allowed( op, &base_entry, 2173 slap_schema.si_ad_entry, NULL, 2174 ACL_DISCLOSE, NULL ) ) 2175 { 2176 rs->sr_err = LDAP_NO_SUCH_OBJECT; 2177 if ( rs->sr_ref ) { 2178 ber_bvarray_free( rs->sr_ref ); 2179 rs->sr_ref = NULL; 2180 } 2181 rs->sr_matched = NULL; 2182 rs->sr_text = NULL; 2183 } 2184 2185 send_ldap_result( op, rs ); 2186 2187 if ( rs->sr_ref ) { 2188 ber_bvarray_free( rs->sr_ref ); 2189 rs->sr_ref = NULL; 2190 } 2191 2192 if ( !BER_BVISNULL( &base_entry.e_nname ) ) { 2193 entry_clean( &base_entry ); 2194 } 2195 2196 goto done; 2197 } 2198 /* NOTE: __NEW__ "search" access is required 2199 * on searchBase object */ 2200 { 2201 slap_mask_t mask; 2202 2203 if ( get_assert( op ) && 2204 ( test_filter( op, &base_entry, get_assertion( op ) ) 2205 != LDAP_COMPARE_TRUE ) ) 2206 { 2207 rs->sr_err = LDAP_ASSERTION_FAILED; 2208 2209 } 2210 if ( ! access_allowed_mask( op, &base_entry, 2211 slap_schema.si_ad_entry, 2212 NULL, ACL_SEARCH, NULL, &mask ) ) 2213 { 2214 if ( rs->sr_err == LDAP_SUCCESS ) { 2215 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 2216 } 2217 } 2218 2219 if ( rs->sr_err != LDAP_SUCCESS ) { 2220 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) { 2221 rs->sr_err = LDAP_NO_SUCH_OBJECT; 2222 rs->sr_text = NULL; 2223 } 2224 send_ldap_result( op, rs ); 2225 goto done; 2226 } 2227 } 2228 2229 bsi.bsi_e = NULL; 2230 2231 bsi.bsi_n_candidates = 2232 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 2233 ( op->ors_limit->lms_s_unchecked == -1 ? -2 : 2234 ( op->ors_limit->lms_s_unchecked ) ) ); 2235 2236 #ifndef BACKSQL_ARBITRARY_KEY 2237 /* If paged results are in effect, check the paging cookie */ 2238 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 2239 rs->sr_err = parse_paged_cookie( op, rs ); 2240 if ( rs->sr_err != LDAP_SUCCESS ) { 2241 send_ldap_result( op, rs ); 2242 goto done; 2243 } 2244 } 2245 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2246 2247 switch ( bsi.bsi_scope ) { 2248 case LDAP_SCOPE_BASE: 2249 case BACKSQL_SCOPE_BASE_LIKE: 2250 /* 2251 * probably already found... 2252 */ 2253 bsi.bsi_id_list = &bsi.bsi_base_id; 2254 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next; 2255 break; 2256 2257 case LDAP_SCOPE_SUBTREE: 2258 /* 2259 * if baseObject is defined, and if it is the root 2260 * of the search, add it to the candidate list 2261 */ 2262 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) ) 2263 { 2264 bsi.bsi_id_list = &bsi.bsi_base_id; 2265 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next; 2266 } 2267 2268 /* FALLTHRU */ 2269 default: 2270 2271 /* 2272 * for each objectclass we try to construct query which gets IDs 2273 * of entries matching LDAP query filter and scope (or at least 2274 * candidates), and get the IDs. Do this in ID order for paging. 2275 */ 2276 ldap_avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates, 2277 &bsi, BACKSQL_AVL_STOP, AVL_INORDER ); 2278 2279 /* check for abandon */ 2280 if ( op->o_abandon ) { 2281 eid = bsi.bsi_id_list; 2282 rs->sr_err = SLAPD_ABANDON; 2283 goto send_results; 2284 } 2285 } 2286 2287 if ( op->ors_limit != NULL /* isroot == FALSE */ 2288 && op->ors_limit->lms_s_unchecked != -1 2289 && bsi.bsi_n_candidates == -1 ) 2290 { 2291 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 2292 send_ldap_result( op, rs ); 2293 goto done; 2294 } 2295 2296 /* 2297 * now we load candidate entries (only those attributes 2298 * mentioned in attrs and filter), test it against full filter 2299 * and then send to client; don't free entry_id if baseObject... 2300 */ 2301 for ( eid = bsi.bsi_id_list; 2302 eid != NULL; 2303 eid = backsql_free_entryID( 2304 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) ) 2305 { 2306 int rc; 2307 Attribute *a_hasSubordinate = NULL, 2308 *a_entryUUID = NULL, 2309 *a_entryCSN = NULL, 2310 **ap = NULL; 2311 Entry *e = NULL; 2312 2313 /* check for abandon */ 2314 if ( op->o_abandon ) { 2315 rs->sr_err = SLAPD_ABANDON; 2316 goto send_results; 2317 } 2318 2319 /* check time limit */ 2320 if ( op->ors_tlimit != SLAP_NO_LIMIT 2321 && slap_get_time() > stoptime ) 2322 { 2323 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; 2324 rs->sr_ctrls = NULL; 2325 rs->sr_ref = rs->sr_v2ref; 2326 goto send_results; 2327 } 2328 2329 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data " 2330 "for entry id=" BACKSQL_IDFMT " oc_id=" BACKSQL_IDNUMFMT ", keyval=" BACKSQL_IDFMT "\n", 2331 BACKSQL_IDARG(eid->eid_id), 2332 eid->eid_oc_id, 2333 BACKSQL_IDARG(eid->eid_keyval) ); 2334 2335 /* check scope */ 2336 switch ( op->ors_scope ) { 2337 case LDAP_SCOPE_BASE: 2338 case BACKSQL_SCOPE_BASE_LIKE: 2339 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) { 2340 goto next_entry2; 2341 } 2342 break; 2343 2344 case LDAP_SCOPE_ONE: 2345 { 2346 struct berval rdn = eid->eid_ndn; 2347 2348 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," ); 2349 if ( !dnIsOneLevelRDN( &rdn ) ) { 2350 goto next_entry2; 2351 } 2352 /* fall thru */ 2353 } 2354 2355 case LDAP_SCOPE_SUBORDINATE: 2356 /* discard the baseObject entry */ 2357 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) { 2358 goto next_entry2; 2359 } 2360 /* FALLTHRU */ 2361 case LDAP_SCOPE_SUBTREE: 2362 /* FIXME: this should never fail... */ 2363 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) { 2364 goto next_entry2; 2365 } 2366 break; 2367 } 2368 2369 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) { 2370 /* don't recollect baseObject... */ 2371 e = bi->sql_baseObject; 2372 2373 } else if ( eid == &bsi.bsi_base_id ) { 2374 /* don't recollect searchBase object... */ 2375 e = &base_entry; 2376 2377 } else { 2378 bsi.bsi_e = &user_entry; 2379 rc = backsql_id2entry( &bsi, eid ); 2380 if ( rc != LDAP_SUCCESS ) { 2381 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2382 "error %d in backsql_id2entry() " 2383 "- skipping\n", rc ); 2384 continue; 2385 } 2386 e = &user_entry; 2387 } 2388 2389 if ( !manageDSAit && 2390 op->ors_scope != LDAP_SCOPE_BASE && 2391 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE && 2392 is_entry_referral( e ) ) 2393 { 2394 BerVarray refs; 2395 2396 refs = get_entry_referrals( op, e ); 2397 if ( !refs ) { 2398 backsql_srch_info bsi2 = { 0 }; 2399 Entry user_entry2 = { 0 }; 2400 2401 /* retry with the full entry... */ 2402 bsi2.bsi_e = &user_entry2; 2403 rc = backsql_init_search( &bsi2, 2404 &e->e_nname, 2405 LDAP_SCOPE_BASE, 2406 (time_t)(-1), NULL, 2407 dbh, op, rs, NULL, 2408 BACKSQL_ISF_GET_ENTRY ); 2409 if ( rc == LDAP_SUCCESS ) { 2410 if ( is_entry_referral( &user_entry2 ) ) 2411 { 2412 refs = get_entry_referrals( op, 2413 &user_entry2 ); 2414 } else { 2415 rs->sr_err = LDAP_OTHER; 2416 } 2417 backsql_entry_clean( op, &user_entry2 ); 2418 } 2419 if ( bsi2.bsi_attrs != NULL ) { 2420 op->o_tmpfree( bsi2.bsi_attrs, 2421 op->o_tmpmemctx ); 2422 } 2423 } 2424 2425 if ( refs ) { 2426 rs->sr_ref = referral_rewrite( refs, 2427 &e->e_name, 2428 &op->o_req_dn, 2429 op->ors_scope ); 2430 ber_bvarray_free( refs ); 2431 } 2432 2433 if ( rs->sr_ref ) { 2434 rs->sr_err = LDAP_REFERRAL; 2435 2436 } else { 2437 rs->sr_text = "bad referral object"; 2438 } 2439 2440 rs->sr_entry = e; 2441 rs->sr_matched = user_entry.e_name.bv_val; 2442 send_search_reference( op, rs ); 2443 2444 ber_bvarray_free( rs->sr_ref ); 2445 rs->sr_ref = NULL; 2446 rs->sr_matched = NULL; 2447 rs->sr_entry = NULL; 2448 if ( rs->sr_err == LDAP_REFERRAL ) { 2449 rs->sr_err = LDAP_SUCCESS; 2450 } 2451 2452 goto next_entry; 2453 } 2454 2455 /* 2456 * We use this flag since we need to parse the filter 2457 * anyway; we should have used the frontend API function 2458 * filter_has_subordinates() 2459 */ 2460 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) { 2461 rc = backsql_has_children( op, dbh, &e->e_nname ); 2462 2463 switch ( rc ) { 2464 case LDAP_COMPARE_TRUE: 2465 case LDAP_COMPARE_FALSE: 2466 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE ); 2467 if ( a_hasSubordinate != NULL ) { 2468 for ( ap = &user_entry.e_attrs; 2469 *ap; 2470 ap = &(*ap)->a_next ); 2471 2472 *ap = a_hasSubordinate; 2473 } 2474 rc = 0; 2475 break; 2476 2477 default: 2478 Debug(LDAP_DEBUG_TRACE, 2479 "backsql_search(): " 2480 "has_children failed( %d)\n", 2481 rc ); 2482 rc = 1; 2483 goto next_entry; 2484 } 2485 } 2486 2487 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) { 2488 a_entryUUID = backsql_operational_entryUUID( bi, eid ); 2489 if ( a_entryUUID != NULL ) { 2490 if ( ap == NULL ) { 2491 ap = &user_entry.e_attrs; 2492 } 2493 2494 for ( ; *ap; ap = &(*ap)->a_next ); 2495 2496 *ap = a_entryUUID; 2497 } 2498 } 2499 2500 #ifdef BACKSQL_SYNCPROV 2501 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) { 2502 a_entryCSN = backsql_operational_entryCSN( op ); 2503 if ( a_entryCSN != NULL ) { 2504 if ( ap == NULL ) { 2505 ap = &user_entry.e_attrs; 2506 } 2507 2508 for ( ; *ap; ap = &(*ap)->a_next ); 2509 2510 *ap = a_entryCSN; 2511 } 2512 } 2513 #endif /* BACKSQL_SYNCPROV */ 2514 2515 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) 2516 { 2517 #ifndef BACKSQL_ARBITRARY_KEY 2518 /* If paged results are in effect, see if the page limit was exceeded */ 2519 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 2520 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) 2521 { 2522 e = NULL; 2523 send_paged_response( op, rs, &lastid ); 2524 goto done; 2525 } 2526 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id ); 2527 } 2528 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2529 rs->sr_attrs = op->ors_attrs; 2530 rs->sr_operational_attrs = NULL; 2531 rs->sr_entry = e; 2532 e->e_private = (void *)eid; 2533 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0; 2534 /* FIXME: need the whole entry (ITS#3480) */ 2535 rs->sr_err = send_search_entry( op, rs ); 2536 e->e_private = NULL; 2537 rs->sr_entry = NULL; 2538 rs->sr_attrs = NULL; 2539 rs->sr_operational_attrs = NULL; 2540 2541 switch ( rs->sr_err ) { 2542 case LDAP_UNAVAILABLE: 2543 /* 2544 * FIXME: send_search_entry failed; 2545 * better stop 2546 */ 2547 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2548 "connection lost\n" ); 2549 goto end_of_search; 2550 2551 case LDAP_SIZELIMIT_EXCEEDED: 2552 case LDAP_BUSY: 2553 goto send_results; 2554 } 2555 } 2556 2557 next_entry:; 2558 if ( e == &user_entry ) { 2559 backsql_entry_clean( op, &user_entry ); 2560 } 2561 2562 next_entry2:; 2563 } 2564 2565 end_of_search:; 2566 if ( rs->sr_nentries > 0 ) { 2567 rs->sr_ref = rs->sr_v2ref; 2568 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS 2569 : LDAP_REFERRAL; 2570 2571 } else { 2572 rs->sr_err = bsi.bsi_status; 2573 } 2574 2575 send_results:; 2576 if ( rs->sr_err != SLAPD_ABANDON ) { 2577 #ifndef BACKSQL_ARBITRARY_KEY 2578 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 2579 send_paged_response( op, rs, NULL ); 2580 } else 2581 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2582 { 2583 send_ldap_result( op, rs ); 2584 } 2585 } 2586 2587 /* cleanup in case of abandon */ 2588 for ( ; eid != NULL; 2589 eid = backsql_free_entryID( 2590 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) ) 2591 ; 2592 2593 backsql_entry_clean( op, &base_entry ); 2594 2595 /* in case we got here accidentally */ 2596 backsql_entry_clean( op, &user_entry ); 2597 2598 if ( rs->sr_v2ref ) { 2599 ber_bvarray_free( rs->sr_v2ref ); 2600 rs->sr_v2ref = NULL; 2601 } 2602 2603 #ifdef BACKSQL_SYNCPROV 2604 if ( op->o_sync ) { 2605 Operation op2 = *op; 2606 SlapReply rs2 = { REP_RESULT }; 2607 Entry *e = entry_alloc(); 2608 slap_callback cb = { 0 }; 2609 2610 op2.o_tag = LDAP_REQ_ADD; 2611 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 ); 2612 op2.ora_e = e; 2613 op2.o_callback = &cb; 2614 2615 ber_dupbv( &e->e_name, op->o_bd->be_suffix ); 2616 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix ); 2617 2618 cb.sc_response = slap_null_cb; 2619 2620 op2.o_bd->be_add( &op2, &rs2 ); 2621 2622 if ( op2.ora_e == e ) 2623 entry_free( e ); 2624 } 2625 #endif /* BACKSQL_SYNCPROV */ 2626 2627 done:; 2628 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx ); 2629 2630 if ( bsi.bsi_attrs != NULL ) { 2631 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); 2632 } 2633 2634 if ( !BER_BVISNULL( &nbase ) 2635 && nbase.bv_val != op->o_req_ndn.bv_val ) 2636 { 2637 ch_free( nbase.bv_val ); 2638 } 2639 2640 /* restore scope ... FIXME: this should be done before ANY 2641 * frontend call that uses op */ 2642 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) { 2643 op->ors_scope = LDAP_SCOPE_BASE; 2644 } 2645 2646 SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK ); 2647 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n" ); 2648 2649 return rs->sr_err; 2650 } 2651 2652 /* return LDAP_SUCCESS IFF we can retrieve the specified entry. 2653 */ 2654 int 2655 backsql_entry_get( 2656 Operation *op, 2657 struct berval *ndn, 2658 ObjectClass *oc, 2659 AttributeDescription *at, 2660 int rw, 2661 Entry **ent ) 2662 { 2663 backsql_srch_info bsi = { 0 }; 2664 SQLHDBC dbh = SQL_NULL_HDBC; 2665 int rc; 2666 SlapReply rs = { 0 }; 2667 AttributeName anlist[ 2 ]; 2668 2669 *ent = NULL; 2670 2671 rc = backsql_get_db_conn( op, &dbh ); 2672 if ( rc != LDAP_SUCCESS ) { 2673 return rc; 2674 } 2675 2676 if ( at ) { 2677 anlist[ 0 ].an_name = at->ad_cname; 2678 anlist[ 0 ].an_desc = at; 2679 BER_BVZERO( &anlist[ 1 ].an_name ); 2680 } 2681 2682 bsi.bsi_e = entry_alloc(); 2683 rc = backsql_init_search( &bsi, 2684 ndn, 2685 LDAP_SCOPE_BASE, 2686 (time_t)(-1), NULL, 2687 dbh, op, &rs, at ? anlist : NULL, 2688 BACKSQL_ISF_GET_ENTRY ); 2689 2690 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { 2691 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx ); 2692 } 2693 2694 if ( rc == LDAP_SUCCESS ) { 2695 2696 #if 0 /* not supported at present */ 2697 /* find attribute values */ 2698 if ( is_entry_alias( bsi.bsi_e ) ) { 2699 Debug( LDAP_DEBUG_ACL, 2700 "<= backsql_entry_get: entry is an alias\n" ); 2701 rc = LDAP_ALIAS_PROBLEM; 2702 goto return_results; 2703 } 2704 #endif 2705 2706 if ( is_entry_referral( bsi.bsi_e ) ) { 2707 Debug( LDAP_DEBUG_ACL, 2708 "<= backsql_entry_get: entry is a referral\n" ); 2709 rc = LDAP_REFERRAL; 2710 goto return_results; 2711 } 2712 2713 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) { 2714 Debug( LDAP_DEBUG_ACL, 2715 "<= backsql_entry_get: " 2716 "failed to find objectClass\n" ); 2717 rc = LDAP_NO_SUCH_ATTRIBUTE; 2718 goto return_results; 2719 } 2720 2721 *ent = bsi.bsi_e; 2722 } 2723 2724 return_results:; 2725 if ( bsi.bsi_attrs != NULL ) { 2726 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); 2727 } 2728 2729 if ( rc != LDAP_SUCCESS ) { 2730 if ( bsi.bsi_e ) { 2731 entry_free( bsi.bsi_e ); 2732 } 2733 } 2734 2735 return rc; 2736 } 2737 2738 void 2739 backsql_entry_clean( 2740 Operation *op, 2741 Entry *e ) 2742 { 2743 void *ctx; 2744 2745 ctx = ldap_pvt_thread_pool_context(); 2746 2747 if ( ctx == NULL || ctx != op->o_tmpmemctx ) { 2748 if ( !BER_BVISNULL( &e->e_name ) ) { 2749 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx ); 2750 BER_BVZERO( &e->e_name ); 2751 } 2752 2753 if ( !BER_BVISNULL( &e->e_nname ) ) { 2754 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx ); 2755 BER_BVZERO( &e->e_nname ); 2756 } 2757 } 2758 2759 entry_clean( e ); 2760 } 2761 2762 int 2763 backsql_entry_release( 2764 Operation *op, 2765 Entry *e, 2766 int rw ) 2767 { 2768 backsql_entry_clean( op, e ); 2769 2770 entry_free( e ); 2771 2772 return 0; 2773 } 2774 2775 #ifndef BACKSQL_ARBITRARY_KEY 2776 /* This function is copied verbatim from back-bdb/search.c */ 2777 static int 2778 parse_paged_cookie( Operation *op, SlapReply *rs ) 2779 { 2780 int rc = LDAP_SUCCESS; 2781 PagedResultsState *ps = op->o_pagedresults_state; 2782 2783 /* this function must be invoked only if the pagedResults 2784 * control has been detected, parsed and partially checked 2785 * by the frontend */ 2786 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ); 2787 2788 /* cookie decoding/checks deferred to backend... */ 2789 if ( ps->ps_cookieval.bv_len ) { 2790 PagedResultsCookie reqcookie; 2791 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) { 2792 /* bad cookie */ 2793 rs->sr_text = "paged results cookie is invalid"; 2794 rc = LDAP_PROTOCOL_ERROR; 2795 goto done; 2796 } 2797 2798 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie )); 2799 2800 if ( reqcookie > ps->ps_cookie ) { 2801 /* bad cookie */ 2802 rs->sr_text = "paged results cookie is invalid"; 2803 rc = LDAP_PROTOCOL_ERROR; 2804 goto done; 2805 2806 } else if ( reqcookie < ps->ps_cookie ) { 2807 rs->sr_text = "paged results cookie is invalid or old"; 2808 rc = LDAP_UNWILLING_TO_PERFORM; 2809 goto done; 2810 } 2811 2812 } else { 2813 /* Initial request. Initialize state. */ 2814 ps->ps_cookie = 0; 2815 ps->ps_count = 0; 2816 } 2817 2818 done:; 2819 2820 return rc; 2821 } 2822 2823 /* This function is copied nearly verbatim from back-bdb/search.c */ 2824 static void 2825 send_paged_response( 2826 Operation *op, 2827 SlapReply *rs, 2828 ID *lastid ) 2829 { 2830 LDAPControl ctrl, *ctrls[2]; 2831 BerElementBuffer berbuf; 2832 BerElement *ber = (BerElement *)&berbuf; 2833 PagedResultsCookie respcookie; 2834 struct berval cookie; 2835 2836 Debug(LDAP_DEBUG_ARGS, 2837 "send_paged_response: lastid=0x%08lx nentries=%d\n", 2838 lastid ? *lastid : 0, rs->sr_nentries ); 2839 2840 BER_BVZERO( &ctrl.ldctl_value ); 2841 ctrls[0] = &ctrl; 2842 ctrls[1] = NULL; 2843 2844 ber_init2( ber, NULL, LBER_USE_DER ); 2845 2846 if ( lastid ) { 2847 respcookie = ( PagedResultsCookie )(*lastid); 2848 cookie.bv_len = sizeof( respcookie ); 2849 cookie.bv_val = (char *)&respcookie; 2850 2851 } else { 2852 respcookie = ( PagedResultsCookie )0; 2853 BER_BVSTR( &cookie, "" ); 2854 } 2855 2856 op->o_conn->c_pagedresults_state.ps_cookie = respcookie; 2857 op->o_conn->c_pagedresults_state.ps_count = 2858 ((PagedResultsState *)op->o_pagedresults_state)->ps_count + 2859 rs->sr_nentries; 2860 2861 /* return size of 0 -- no estimate */ 2862 ber_printf( ber, "{iO}", 0, &cookie ); 2863 2864 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) { 2865 goto done; 2866 } 2867 2868 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; 2869 ctrls[0]->ldctl_iscritical = 0; 2870 2871 rs->sr_ctrls = ctrls; 2872 rs->sr_err = LDAP_SUCCESS; 2873 send_ldap_result( op, rs ); 2874 rs->sr_ctrls = NULL; 2875 2876 done: 2877 (void) ber_free_buf( ber ); 2878 } 2879 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2880