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