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