1 /*        $NetBSD: pcache.c,v 1.3 2021/08/14 16:15:02 christos Exp $  */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2003-2021 The OpenLDAP Foundation.
7  * Portions Copyright 2003 IBM Corporation.
8  * Portions Copyright 2003-2009 Symas Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Apurva Kumar for inclusion
21  * in OpenLDAP Software and subsequently rewritten by Howard Chu.
22  */
23 
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: pcache.c,v 1.3 2021/08/14 16:15:02 christos Exp $");
26 
27 #include "portable.h"
28 
29 #ifdef SLAPD_OVER_PROXYCACHE
30 
31 #include <stdio.h>
32 
33 #include <ac/string.h>
34 #include <ac/time.h>
35 
36 #include "slap.h"
37 #include "lutil.h"
38 #include "ldap_rq.h"
39 #include "ldap_avl.h"
40 
41 #include "../back-monitor/back-monitor.h"
42 
43 #include "slap-config.h"
44 
45 /*
46  * Control that allows to access the private DB
47  * instead of the public one
48  */
49 #define   PCACHE_CONTROL_PRIVDB                   "1.3.6.1.4.1.4203.666.11.9.5.1"
50 
51 /*
52  * Extended Operation that allows to remove a query from the cache
53  */
54 #define PCACHE_EXOP_QUERY_DELETE        "1.3.6.1.4.1.4203.666.11.9.6.1"
55 
56 /*
57  * Monitoring
58  */
59 #define PCACHE_MONITOR
60 
61 /* query cache structs */
62 /* query */
63 
64 typedef struct Query_s {
65           Filter*   filter;   /* Search Filter */
66           struct berval       base;               /* Search Base */
67           int                 scope;              /* Search scope */
68 } Query;
69 
70 struct query_template_s;
71 
72 typedef struct Qbase_s {
73           TAvlnode *scopes[4];                    /* threaded AVL trees of cached queries */
74           struct berval base;
75           int queries;
76 } Qbase;
77 
78 /* struct representing a cached query */
79 typedef struct cached_query_s {
80           Filter                                            *filter;
81           Filter                                            *first;
82           Qbase                                             *qbase;
83           int                                                         scope;
84           struct berval                           q_uuid;             /* query identifier */
85           int                                                         q_sizelimit;
86           struct query_template_s                 *qtemp;   /* template of the query */
87           time_t                                                      expiry_time;        /* time till the query is considered invalid */
88           time_t                                                      refresh_time;       /* time till the query is refreshed */
89           time_t                                                      bindref_time;       /* time till the bind is refreshed */
90           int                                                         bind_refcnt;        /* number of bind operation referencing this query */
91           unsigned long                           answerable_cnt; /* how many times it was answerable */
92           int                                                         refcnt;   /* references since last refresh */
93           int                                                         in_lru;   /* query is in LRU list */
94           ldap_pvt_thread_mutex_t                 answerable_cnt_mutex;
95           struct cached_query_s                   *next;    /* next query in the template */
96           struct cached_query_s                   *prev;    /* previous query in the template */
97           struct cached_query_s                   *lru_up;  /* previous query in the LRU list */
98           struct cached_query_s                   *lru_down;          /* next query in the LRU list */
99           ldap_pvt_thread_rdwr_t                  rwlock;
100 } CachedQuery;
101 
102 /*
103  * URL representation:
104  *
105  * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>,x-refresh=<refresh>
106  *
107  * <base> ::= CachedQuery.qbase->base
108  * <scope> ::= CachedQuery.scope
109  * <filter> ::= filter2bv(CachedQuery.filter)
110  * <uuid> ::= CachedQuery.q_uuid
111  * <attrset> ::= CachedQuery.qtemp->attr_set_index
112  * <expiry> ::= CachedQuery.expiry_time
113  * <refresh> ::= CachedQuery.refresh_time
114  *
115  * quick hack: parse URI, call add_query() and then fix
116  * CachedQuery.expiry_time and CachedQuery.q_uuid
117  *
118  * NOTE: if the <attrset> changes, all stored URLs will be invalidated.
119  */
120 
121 /*
122  * Represents a set of projected attributes.
123  */
124 
125 struct attr_set {
126           struct query_template_s *templates;
127           AttributeName*      attrs;              /* specifies the set */
128           unsigned  flags;
129 #define   PC_CONFIGURED       (0x1)
130 #define   PC_REFERENCED       (0x2)
131 #define   PC_GOT_OC           (0x4)
132           int                 count;              /* number of attributes */
133 };
134 
135 /* struct representing a query template
136  * e.g. template string = &(cn=)(mail=)
137  */
138 typedef struct query_template_s {
139           struct query_template_s *qtnext;
140           struct query_template_s *qmnext;
141 
142           Avlnode*            qbase;
143           CachedQuery*        query;            /* most recent query cached for the template */
144           CachedQuery*        query_last;     /* oldest query cached for the template */
145           ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */
146           struct berval       querystr; /* Filter string corresponding to the QT */
147           struct berval       bindbase; /* base DN for Bind request */
148           struct berval       bindfilterstr;      /* Filter string for Bind request */
149           struct berval       bindftemp;          /* bind filter template */
150           Filter              *bindfilter;
151           AttributeDescription **bindfattrs;      /* attrs to substitute in ftemp */
152 
153           int                           bindnattrs;                   /* number of bindfattrs */
154           int                           bindscope;
155           int                 attr_set_index; /* determines the projected attributes */
156           int                 no_of_queries;  /* Total number of queries in the template */
157           time_t              ttl;                /* TTL for the queries of this template */
158           time_t              negttl;             /* TTL for negative results */
159           time_t              limitttl; /* TTL for sizelimit exceeding results */
160           time_t              ttr;      /* time to refresh */
161           time_t              bindttr;  /* TTR for cached binds */
162           struct attr_set t_attrs;      /* filter attrs + attr_set */
163 } QueryTemplate;
164 
165 typedef enum {
166           PC_IGNORE = 0,
167           PC_POSITIVE,
168           PC_NEGATIVE,
169           PC_SIZELIMIT
170 } pc_caching_reason_t;
171 
172 static const char *pc_caching_reason_str[] = {
173           "IGNORE",
174           "POSITIVE",
175           "NEGATIVE",
176           "SIZELIMIT",
177 
178           NULL
179 };
180 
181 struct query_manager_s;
182 
183 /* prototypes for functions for 1) query containment
184  * 2) query addition, 3) cache replacement
185  */
186 typedef CachedQuery *(QCfunc)(Operation *op, struct query_manager_s*,
187           Query*, QueryTemplate*);
188 typedef CachedQuery *(AddQueryfunc)(Operation *op, struct query_manager_s*,
189           Query*, QueryTemplate*, pc_caching_reason_t, int wlock);
190 typedef void (CRfunc)(struct query_manager_s*, struct berval*);
191 
192 /* LDAP query cache */
193 typedef struct query_manager_s {
194           struct attr_set*    attr_sets;                    /* possible sets of projected attributes */
195           QueryTemplate*                templates;                    /* cacheable templates */
196 
197           CachedQuery*                  lru_top;            /* top and bottom of LRU list */
198           CachedQuery*                  lru_bottom;
199 
200           ldap_pvt_thread_mutex_t                 lru_mutex;          /* mutex for accessing LRU list */
201 
202           /* Query cache methods */
203           QCfunc                        *qcfunc;                      /* Query containment*/
204           CRfunc                        *crfunc;                      /* cache replacement */
205           AddQueryfunc        *addfunc;                     /* add query */
206 } query_manager;
207 
208 /* LDAP query cache manager */
209 typedef struct cache_manager_s {
210           BackendDB db;       /* underlying database */
211           unsigned long       num_cached_queries;                     /* total number of cached queries */
212           unsigned long   max_queries;                      /* upper bound on # of cached queries */
213           int                 save_queries;                           /* save cached queries across restarts */
214           int       check_cacheability;           /* check whether a query is cacheable */
215           int       numattrsets;                            /* number of attribute sets */
216           int       cur_entries;                            /* current number of entries cached */
217           int       max_entries;                            /* max number of entries cached */
218           int       num_entries_limit;            /* max # of entries in a cacheable query */
219 
220           char      response_cb;                            /* install the response callback
221                                                              * at the tail of the callback list */
222 #define PCACHE_RESPONSE_CB_HEAD         0
223 #define PCACHE_RESPONSE_CB_TAIL         1
224           char      defer_db_open;                          /* defer open for online add */
225           char      cache_binds;                            /* cache binds or just passthru */
226 
227           time_t    cc_period;                    /* interval between successive consistency checks (sec) */
228 #define PCACHE_CC_PAUSED      1
229 #define PCACHE_CC_OFFLINE     2
230           int       cc_paused;
231           void      *cc_arg;
232 
233           ldap_pvt_thread_mutex_t                 cache_mutex;
234 
235           query_manager*   qm;          /* query cache managed by the cache manager */
236 
237 #ifdef PCACHE_MONITOR
238           void                *monitor_cb;
239           struct berval       monitor_ndn;
240 #endif /* PCACHE_MONITOR */
241 } cache_manager;
242 
243 #ifdef PCACHE_MONITOR
244 static int pcache_monitor_db_init( BackendDB *be );
245 static int pcache_monitor_db_open( BackendDB *be );
246 static int pcache_monitor_db_close( BackendDB *be );
247 static int pcache_monitor_db_destroy( BackendDB *be );
248 #endif /* PCACHE_MONITOR */
249 
250 static int pcache_debug;
251 
252 #ifdef PCACHE_CONTROL_PRIVDB
253 static int privDB_cid;
254 #endif /* PCACHE_CONTROL_PRIVDB */
255 
256 static AttributeDescription   *ad_queryId, *ad_cachedQueryURL;
257 
258 #ifdef PCACHE_MONITOR
259 static AttributeDescription   *ad_numQueries, *ad_numEntries;
260 static ObjectClass            *oc_olmPCache;
261 #endif /* PCACHE_MONITOR */
262 
263 static struct {
264           char                          *name;
265           char                          *oid;
266 }                   s_oid[] = {
267           { "PCacheOID",                          "1.3.6.1.4.1.4203.666.11.9.1" },
268           { "PCacheAttributes",                   "PCacheOID:1" },
269           { "PCacheObjectClasses",      "PCacheOID:2" },
270 
271           { NULL }
272 };
273 
274 static struct {
275           char      *desc;
276           AttributeDescription **adp;
277 } s_ad[] = {
278           { "( PCacheAttributes:1 "
279                     "NAME 'pcacheQueryID' "
280                     "DESC 'ID of query the entry belongs to, formatted as a UUID' "
281                     "EQUALITY octetStringMatch "
282                     "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
283                     "NO-USER-MODIFICATION "
284                     "USAGE directoryOperation )",
285                     &ad_queryId },
286           { "( PCacheAttributes:2 "
287                     "NAME 'pcacheQueryURL' "
288                     "DESC 'URI describing a cached query' "
289                     "EQUALITY caseExactMatch "
290                     "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
291                     "NO-USER-MODIFICATION "
292                     "USAGE directoryOperation )",
293                     &ad_cachedQueryURL },
294 #ifdef PCACHE_MONITOR
295           { "( PCacheAttributes:3 "
296                     "NAME 'pcacheNumQueries' "
297                     "DESC 'Number of cached queries' "
298                     "EQUALITY integerMatch "
299                     "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
300                     "NO-USER-MODIFICATION "
301                     "USAGE directoryOperation )",
302                     &ad_numQueries },
303           { "( PCacheAttributes:4 "
304                     "NAME 'pcacheNumEntries' "
305                     "DESC 'Number of cached entries' "
306                     "EQUALITY integerMatch "
307                     "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
308                     "NO-USER-MODIFICATION "
309                     "USAGE directoryOperation )",
310                     &ad_numEntries },
311 #endif /* PCACHE_MONITOR */
312 
313           { NULL }
314 };
315 
316 static struct {
317           char                *desc;
318           ObjectClass         **ocp;
319 }                   s_oc[] = {
320 #ifdef PCACHE_MONITOR
321           /* augments an existing object, so it must be AUXILIARY */
322           { "( PCacheObjectClasses:1 "
323                     "NAME ( 'olmPCache' ) "
324                     "SUP top AUXILIARY "
325                     "MAY ( "
326                               "pcacheQueryURL "
327                               "$ pcacheNumQueries "
328                               "$ pcacheNumEntries "
329                               " ) )",
330                     &oc_olmPCache },
331 #endif /* PCACHE_MONITOR */
332 
333           { NULL }
334 };
335 
336 static int
337 filter2template(
338           Operation           *op,
339           Filter                        *f,
340           struct                        berval *fstr );
341 
342 static CachedQuery *
343 add_query(
344           Operation *op,
345           query_manager* qm,
346           Query* query,
347           QueryTemplate *templ,
348           pc_caching_reason_t why,
349           int wlock);
350 
351 static int
352 remove_query_data(
353           Operation *op,
354           struct berval       *query_uuid );
355 
356 /*
357  * Turn a cached query into its URL representation
358  */
359 static int
query2url(Operation * op,CachedQuery * q,struct berval * urlbv,int dolock)360 query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock )
361 {
362           struct berval       bv_scope,
363                               bv_filter;
364           char                attrset_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
365                               expiry_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
366                               refresh_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
367                               answerable_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
368                               *ptr;
369           ber_len_t attrset_len,
370                               expiry_len,
371                               refresh_len,
372                               answerable_len;
373 
374           if ( dolock ) {
375                     ldap_pvt_thread_rdwr_rlock( &q->rwlock );
376           }
377 
378           ldap_pvt_scope2bv( q->scope, &bv_scope );
379           filter2bv_x( op, q->filter, &bv_filter );
380           attrset_len = sprintf( attrset_buf,
381                     "%lu", (unsigned long)q->qtemp->attr_set_index );
382           expiry_len = sprintf( expiry_buf,
383                     "%lu", (unsigned long)q->expiry_time );
384           answerable_len = snprintf( answerable_buf, sizeof( answerable_buf ),
385                     "%lu", q->answerable_cnt );
386           if ( q->refresh_time )
387                     refresh_len = sprintf( refresh_buf,
388                               "%lu", (unsigned long)q->refresh_time );
389           else
390                     refresh_len = 0;
391 
392           urlbv->bv_len = STRLENOF( "ldap:///" )
393                     + q->qbase->base.bv_len
394                     + STRLENOF( "??" )
395                     + bv_scope.bv_len
396                     + STRLENOF( "?" )
397                     + bv_filter.bv_len
398                     + STRLENOF( "?x-uuid=" )
399                     + q->q_uuid.bv_len
400                     + STRLENOF( ",x-attrset=" )
401                     + attrset_len
402                     + STRLENOF( ",x-expiry=" )
403                     + expiry_len
404                     + STRLENOF( ",x-answerable=" )
405                     + answerable_len;
406           if ( refresh_len )
407                     urlbv->bv_len += STRLENOF( ",x-refresh=" )
408                     + refresh_len;
409 
410           ptr = urlbv->bv_val = ber_memalloc_x( urlbv->bv_len + 1, op->o_tmpmemctx );
411           ptr = lutil_strcopy( ptr, "ldap:///" );
412           ptr = lutil_strcopy( ptr, q->qbase->base.bv_val );
413           ptr = lutil_strcopy( ptr, "??" );
414           ptr = lutil_strcopy( ptr, bv_scope.bv_val );
415           ptr = lutil_strcopy( ptr, "?" );
416           ptr = lutil_strcopy( ptr, bv_filter.bv_val );
417           ptr = lutil_strcopy( ptr, "?x-uuid=" );
418           ptr = lutil_strcopy( ptr, q->q_uuid.bv_val );
419           ptr = lutil_strcopy( ptr, ",x-attrset=" );
420           ptr = lutil_strcopy( ptr, attrset_buf );
421           ptr = lutil_strcopy( ptr, ",x-expiry=" );
422           ptr = lutil_strcopy( ptr, expiry_buf );
423           ptr = lutil_strcopy( ptr, ",x-answerable=" );
424           ptr = lutil_strcopy( ptr, answerable_buf );
425           if ( refresh_len ) {
426                     ptr = lutil_strcopy( ptr, ",x-refresh=" );
427                     ptr = lutil_strcopy( ptr, refresh_buf );
428           }
429 
430           ber_memfree_x( bv_filter.bv_val, op->o_tmpmemctx );
431 
432           if ( dolock ) {
433                     ldap_pvt_thread_rdwr_runlock( &q->rwlock );
434           }
435 
436           return 0;
437 }
438 
439 /* Find and record the empty filter clauses */
440 
441 static int
ftemp_attrs(struct berval * ftemp,struct berval * template,AttributeDescription *** ret,const char ** text)442 ftemp_attrs( struct berval *ftemp, struct berval *template,
443           AttributeDescription ***ret, const char **text )
444 {
445           int i;
446           int attr_cnt=0;
447           struct berval bv;
448           char *p1, *p2, *t1;
449           AttributeDescription *ad;
450           AttributeDescription **descs = NULL;
451           char *temp2;
452 
453           temp2 = ch_malloc( ftemp->bv_len + 1 );
454           p1 = ftemp->bv_val;
455           t1 = temp2;
456 
457           *ret = NULL;
458 
459           for (;;) {
460                     while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' )
461                               *t1++ = *p1++;
462 
463                     p2 = strchr( p1, '=' );
464                     if ( !p2 ) {
465                               if ( !descs ) {
466                                         ch_free( temp2 );
467                                         return -1;
468                               }
469                               break;
470                     }
471                     i = p2 - p1;
472                     AC_MEMCPY( t1, p1, i );
473                     t1 += i;
474                     *t1++ = '=';
475 
476                     if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
477                     bv.bv_val = p1;
478                     bv.bv_len = p2 - p1;
479                     ad = NULL;
480                     i = slap_bv2ad( &bv, &ad, text );
481                     if ( i ) {
482                               ch_free( temp2 );
483                               ch_free( descs );
484                               return -1;
485                     }
486                     if ( *p2 == '<' || *p2 == '>' ) p2++;
487                     if ( p2[1] != ')' ) {
488                               p2++;
489                               while ( *p2 != ')' ) p2++;
490                               p1 = p2;
491                               continue;
492                     }
493 
494                     descs = (AttributeDescription **)ch_realloc(descs,
495                                         (attr_cnt + 2)*sizeof(AttributeDescription *));
496 
497                     descs[attr_cnt++] = ad;
498 
499                     p1 = p2+1;
500           }
501           *t1 = '\0';
502           descs[attr_cnt] = NULL;
503           *ret = descs;
504           template->bv_val = temp2;
505           template->bv_len = t1 - temp2;
506           return attr_cnt;
507 }
508 
509 static int
template_attrs(char * template,struct attr_set * set,AttributeName ** ret,const char ** text)510 template_attrs( char *template, struct attr_set *set, AttributeName **ret,
511           const char **text )
512 {
513           int got_oc = 0;
514           int alluser = 0;
515           int allop = 0;
516           int i;
517           int attr_cnt;
518           int t_cnt = 0;
519           struct berval bv;
520           char *p1, *p2;
521           AttributeDescription *ad;
522           AttributeName *attrs;
523 
524           p1 = template;
525 
526           *ret = NULL;
527 
528           attrs = ch_calloc( set->count + 1, sizeof(AttributeName) );
529           for ( i=0; i < set->count; i++ )
530                     attrs[i] = set->attrs[i];
531           attr_cnt = i;
532           alluser = an_find( attrs, slap_bv_all_user_attrs );
533           allop = an_find( attrs, slap_bv_all_operational_attrs );
534 
535           for (;;) {
536                     while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) p1++;
537                     p2 = strchr( p1, '=' );
538                     if ( !p2 )
539                               break;
540                     if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
541                     bv.bv_val = p1;
542                     bv.bv_len = p2 - p1;
543                     ad = NULL;
544                     i = slap_bv2ad( &bv, &ad, text );
545                     if ( i ) {
546                               ch_free( attrs );
547                               return -1;
548                     }
549                     t_cnt++;
550 
551                     if ( ad == slap_schema.si_ad_objectClass )
552                               got_oc = 1;
553 
554                     if ( is_at_operational(ad->ad_type)) {
555                               if ( allop ) {
556                                         goto bottom;
557                               }
558                     } else if ( alluser ) {
559                               goto bottom;
560                     }
561                     if ( !ad_inlist( ad, attrs )) {
562                               attrs = (AttributeName *)ch_realloc(attrs,
563                                                   (attr_cnt + 2)*sizeof(AttributeName));
564 
565                               attrs[attr_cnt].an_desc = ad;
566                               attrs[attr_cnt].an_name = ad->ad_cname;
567                               attrs[attr_cnt].an_oc = NULL;
568                               attrs[attr_cnt].an_flags = 0;
569                               BER_BVZERO( &attrs[attr_cnt+1].an_name );
570                               attr_cnt++;
571                     }
572 
573 bottom:
574                     p1 = p2+2;
575           }
576           if ( !t_cnt ) {
577                     *text = "couldn't parse template";
578                     ch_free(attrs);
579                     return -1;
580           }
581           if ( !got_oc && !( set->flags & PC_GOT_OC )) {
582                     attrs = (AttributeName *)ch_realloc(attrs,
583                                         (attr_cnt + 2)*sizeof(AttributeName));
584 
585                     ad = slap_schema.si_ad_objectClass;
586                     attrs[attr_cnt].an_desc = ad;
587                     attrs[attr_cnt].an_name = ad->ad_cname;
588                     attrs[attr_cnt].an_oc = NULL;
589                     attrs[attr_cnt].an_flags = 0;
590                     BER_BVZERO( &attrs[attr_cnt+1].an_name );
591                     attr_cnt++;
592           }
593           *ret = attrs;
594           return attr_cnt;
595 }
596 
597 /*
598  * Turn an URL representing a formerly cached query into a cached query,
599  * and try to cache it
600  */
601 static int
url2query(char * url,Operation * op,query_manager * qm)602 url2query(
603           char                *url,
604           Operation *op,
605           query_manager       *qm )
606 {
607           Query               query = { 0 };
608           QueryTemplate       *qt;
609           CachedQuery         *cq;
610           LDAPURLDesc         *lud = NULL;
611           struct berval       base,
612                               tempstr = BER_BVNULL,
613                               uuid = BER_BVNULL;
614           int                 attrset;
615           time_t              expiry_time;
616           time_t              refresh_time;
617           unsigned long       answerable_cnt;
618           int                 i,
619                               got = 0,
620 #define GOT_UUID    0x1U
621 #define GOT_ATTRSET 0x2U
622 #define GOT_EXPIRY  0x4U
623 #define GOT_ANSWERABLE        0x8U
624 #define GOT_REFRESH 0x10U
625 #define GOT_ALL               (GOT_UUID|GOT_ATTRSET|GOT_EXPIRY|GOT_ANSWERABLE)
626                               rc = 0;
627 
628           rc = ldap_url_parse( url, &lud );
629           if ( rc != LDAP_URL_SUCCESS ) {
630                     return -1;
631           }
632 
633           /* non-allowed fields */
634           if ( lud->lud_host != NULL ) {
635                     rc = 1;
636                     goto error;
637           }
638 
639           if ( lud->lud_attrs != NULL ) {
640                     rc = 1;
641                     goto error;
642           }
643 
644           /* be pedantic */
645           if ( strcmp( lud->lud_scheme, "ldap" ) != 0 ) {
646                     rc = 1;
647                     goto error;
648           }
649 
650           /* required fields */
651           if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
652                     rc = 1;
653                     goto error;
654           }
655 
656           switch ( lud->lud_scope ) {
657           case LDAP_SCOPE_BASE:
658           case LDAP_SCOPE_ONELEVEL:
659           case LDAP_SCOPE_SUBTREE:
660           case LDAP_SCOPE_SUBORDINATE:
661                     break;
662 
663           default:
664                     rc = 1;
665                     goto error;
666           }
667 
668           if ( lud->lud_filter == NULL || lud->lud_filter[ 0 ] == '\0' ) {
669                     rc = 1;
670                     goto error;
671           }
672 
673           if ( lud->lud_exts == NULL ) {
674                     rc = 1;
675                     goto error;
676           }
677 
678           for ( i = 0; lud->lud_exts[ i ] != NULL; i++ ) {
679                     if ( strncmp( lud->lud_exts[ i ], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) {
680                               struct berval       tmpUUID;
681                               Syntax              *syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax;
682 
683                               if ( got & GOT_UUID ) {
684                                         rc = 1;
685                                         goto error;
686                               }
687 
688                               ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID );
689                               if ( !BER_BVISEMPTY( &tmpUUID ) ) {
690                                         rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
691                                         if ( rc != LDAP_SUCCESS ) {
692                                                   goto error;
693                                         }
694                               }
695                               got |= GOT_UUID;
696 
697                     } else if ( strncmp( lud->lud_exts[ i ], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) {
698                               if ( got & GOT_ATTRSET ) {
699                                         rc = 1;
700                                         goto error;
701                               }
702 
703                               rc = lutil_atoi( &attrset, &lud->lud_exts[ i ][ STRLENOF( "x-attrset=" ) ] );
704                               if ( rc ) {
705                                         goto error;
706                               }
707                               got |= GOT_ATTRSET;
708 
709                     } else if ( strncmp( lud->lud_exts[ i ], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) {
710                               unsigned long l;
711 
712                               if ( got & GOT_EXPIRY ) {
713                                         rc = 1;
714                                         goto error;
715                               }
716 
717                               rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-expiry=" ) ] );
718                               if ( rc ) {
719                                         goto error;
720                               }
721                               expiry_time = (time_t)l;
722                               got |= GOT_EXPIRY;
723 
724                     } else if ( strncmp( lud->lud_exts[ i ], "x-answerable=", STRLENOF( "x-answerable=" ) ) == 0 ) {
725                               if ( got & GOT_ANSWERABLE ) {
726                                         rc = 1;
727                                         goto error;
728                               }
729 
730                               rc = lutil_atoul( &answerable_cnt, &lud->lud_exts[ i ][ STRLENOF( "x-answerable=" ) ] );
731                               if ( rc ) {
732                                         goto error;
733                               }
734                               got |= GOT_ANSWERABLE;
735 
736                     } else if ( strncmp( lud->lud_exts[ i ], "x-refresh=", STRLENOF( "x-refresh=" ) ) == 0 ) {
737                               unsigned long l;
738 
739                               if ( got & GOT_REFRESH ) {
740                                         rc = 1;
741                                         goto error;
742                               }
743 
744                               rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-refresh=" ) ] );
745                               if ( rc ) {
746                                         goto error;
747                               }
748                               refresh_time = (time_t)l;
749                               got |= GOT_REFRESH;
750 
751                     } else {
752                               rc = -1;
753                               goto error;
754                     }
755           }
756 
757           if ( got != GOT_ALL ) {
758                     rc = 1;
759                     goto error;
760           }
761 
762           if ( !(got & GOT_REFRESH ))
763                     refresh_time = 0;
764 
765           /* ignore expired queries */
766           if ( expiry_time <= slap_get_time()) {
767                     Operation op2 = *op;
768 
769                     memset( &op2.oq_search, 0, sizeof( op2.oq_search ) );
770 
771                     (void)remove_query_data( &op2, &uuid );
772 
773                     rc = 0;
774 
775           } else {
776                     ber_str2bv( lud->lud_dn, 0, 0, &base );
777                     rc = dnNormalize( 0, NULL, NULL, &base, &query.base, NULL );
778                     if ( rc != LDAP_SUCCESS ) {
779                               goto error;
780                     }
781                     query.scope = lud->lud_scope;
782                     query.filter = str2filter( lud->lud_filter );
783                     if ( query.filter == NULL ) {
784                               rc = -1;
785                               goto error;
786                     }
787 
788                     tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 );
789                     tempstr.bv_len = 0;
790                     if ( filter2template( op, query.filter, &tempstr ) ) {
791                               ch_free( tempstr.bv_val );
792                               rc = -1;
793                               goto error;
794                     }
795 
796                     /* check for query containment */
797                     qt = qm->attr_sets[attrset].templates;
798                     for ( ; qt; qt = qt->qtnext ) {
799                               /* find if template i can potentially answer tempstr */
800                               if ( bvmatch( &qt->querystr, &tempstr ) ) {
801                                         break;
802                               }
803                     }
804 
805                     if ( qt == NULL ) {
806                               rc = 1;
807                               goto error;
808                     }
809 
810                     cq = add_query( op, qm, &query, qt, PC_POSITIVE, 0 );
811                     if ( cq != NULL ) {
812                               cq->expiry_time = expiry_time;
813                               cq->refresh_time = refresh_time;
814                               cq->q_uuid = uuid;
815                               cq->answerable_cnt = answerable_cnt;
816                               cq->refcnt = 0;
817 
818                               /* it's now into cq->filter */
819                               BER_BVZERO( &uuid );
820                               query.filter = NULL;
821 
822                     } else {
823                               rc = 1;
824                     }
825           }
826 
827 error:;
828           if ( query.filter != NULL ) filter_free( query.filter );
829           if ( !BER_BVISNULL( &tempstr ) ) ch_free( tempstr.bv_val );
830           if ( !BER_BVISNULL( &query.base ) ) ch_free( query.base.bv_val );
831           if ( !BER_BVISNULL( &uuid ) ) ch_free( uuid.bv_val );
832           if ( lud != NULL ) ldap_free_urldesc( lud );
833 
834           return rc;
835 }
836 
837 /* Return 1 for an added entry, else 0 */
838 static int
merge_entry(Operation * op,Entry * e,int dup,struct berval * query_uuid)839 merge_entry(
840           Operation           *op,
841           Entry                         *e,
842           int                           dup,
843           struct berval*                query_uuid )
844 {
845           int                 rc;
846           Modifications* modlist = NULL;
847           const char*         text = NULL;
848           Attribute           *attr;
849           char                          textbuf[SLAP_TEXT_BUFLEN];
850           size_t                        textlen = sizeof(textbuf);
851 
852           SlapReply sreply = {REP_RESULT};
853 
854           slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
855 
856           if ( dup )
857                     e = entry_dup( e );
858           attr = e->e_attrs;
859           e->e_attrs = NULL;
860 
861           /* add queryId attribute */
862           attr_merge_one( e, ad_queryId, query_uuid, NULL );
863 
864           /* append the attribute list from the fetched entry */
865           e->e_attrs->a_next = attr;
866 
867           op->o_tag = LDAP_REQ_ADD;
868           op->o_protocol = LDAP_VERSION3;
869           op->o_callback = &cb;
870           op->o_time = slap_get_time();
871           op->o_do_not_cache = 1;
872 
873           op->ora_e = e;
874           op->o_req_dn = e->e_name;
875           op->o_req_ndn = e->e_nname;
876           rc = op->o_bd->be_add( op, &sreply );
877 
878           if ( rc != LDAP_SUCCESS ) {
879                     if ( rc == LDAP_ALREADY_EXISTS ) {
880                               rs_reinit( &sreply, REP_RESULT );
881                               slap_entry2mods( e, &modlist, &text, textbuf, textlen );
882                               modlist->sml_op = LDAP_MOD_ADD;
883                               op->o_tag = LDAP_REQ_MODIFY;
884                               op->orm_modlist = modlist;
885                               op->o_managedsait = SLAP_CONTROL_CRITICAL;
886                               op->o_bd->be_modify( op, &sreply );
887                               slap_mods_free( modlist, 1 );
888                     } else if ( rc == LDAP_REFERRAL ||
889                                                   rc == LDAP_NO_SUCH_OBJECT ) {
890                               syncrepl_add_glue( op, e );
891                               e = NULL;
892                               rc = 1;
893                     }
894                     if ( e ) {
895                               entry_free( e );
896                               rc = 0;
897                     }
898           } else {
899                     if ( op->ora_e == e )
900                               entry_free( e );
901                     rc = 1;
902           }
903 
904           return rc;
905 }
906 
907 /* Length-ordered sort on normalized DNs */
pcache_dn_cmp(const void * v1,const void * v2)908 static int pcache_dn_cmp( const void *v1, const void *v2 )
909 {
910           const Qbase *q1 = v1, *q2 = v2;
911 
912           int rc = q1->base.bv_len - q2->base.bv_len;
913           if ( rc == 0 )
914                     rc = strncmp( q1->base.bv_val, q2->base.bv_val, q1->base.bv_len );
915           return rc;
916 }
917 
lex_bvcmp(struct berval * bv1,struct berval * bv2)918 static int lex_bvcmp( struct berval *bv1, struct berval *bv2 )
919 {
920           int len, dif;
921           dif = bv1->bv_len - bv2->bv_len;
922           len = bv1->bv_len;
923           if ( dif > 0 ) len -= dif;
924           len = memcmp( bv1->bv_val, bv2->bv_val, len );
925           if ( !len )
926                     len = dif;
927           return len;
928 }
929 
930 /* compare the current value in each filter */
pcache_filter_cmp(Filter * f1,Filter * f2)931 static int pcache_filter_cmp( Filter *f1, Filter *f2 )
932 {
933           int rc, weight1, weight2;
934 
935           switch( f1->f_choice ) {
936           case LDAP_FILTER_AND:
937           case LDAP_FILTER_OR:
938                     weight1 = 0;
939                     break;
940           case LDAP_FILTER_PRESENT:
941                     weight1 = 1;
942                     break;
943           case LDAP_FILTER_EQUALITY:
944           case LDAP_FILTER_GE:
945           case LDAP_FILTER_LE:
946                     weight1 = 2;
947                     break;
948           default:
949                     weight1 = 3;
950           }
951           switch( f2->f_choice ) {
952           case LDAP_FILTER_AND:
953           case LDAP_FILTER_OR:
954                     weight2 = 0;
955                     break;
956           case LDAP_FILTER_PRESENT:
957                     weight2 = 1;
958                     break;
959           case LDAP_FILTER_EQUALITY:
960           case LDAP_FILTER_GE:
961           case LDAP_FILTER_LE:
962                     weight2 = 2;
963                     break;
964           default:
965                     weight2 = 3;
966           }
967           rc = weight1 - weight2;
968           if ( !rc ) {
969                     switch( weight1 ) {
970                     case 0:
971                               rc = pcache_filter_cmp( f1->f_and, f2->f_and );
972                               break;
973                     case 1:
974                               break;
975                     case 2:
976                               rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value );
977                               break;
978                     case 3:
979                               if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) {
980                                         rc = 0;
981                                         if ( !BER_BVISNULL( &f1->f_sub_initial )) {
982                                                   if ( !BER_BVISNULL( &f2->f_sub_initial )) {
983                                                             rc = lex_bvcmp( &f1->f_sub_initial,
984                                                                       &f2->f_sub_initial );
985                                                   } else {
986                                                             rc = 1;
987                                                   }
988                                         } else if ( !BER_BVISNULL( &f2->f_sub_initial )) {
989                                                   rc = -1;
990                                         }
991                                         if ( rc ) break;
992                                         if ( f1->f_sub_any ) {
993                                                   if ( f2->f_sub_any ) {
994                                                             rc = lex_bvcmp( f1->f_sub_any,
995                                                                       f2->f_sub_any );
996                                                   } else {
997                                                             rc = 1;
998                                                   }
999                                         } else if ( f2->f_sub_any ) {
1000                                                   rc = -1;
1001                                         }
1002                                         if ( rc ) break;
1003                                         if ( !BER_BVISNULL( &f1->f_sub_final )) {
1004                                                   if ( !BER_BVISNULL( &f2->f_sub_final )) {
1005                                                             rc = lex_bvcmp( &f1->f_sub_final,
1006                                                                       &f2->f_sub_final );
1007                                                   } else {
1008                                                             rc = 1;
1009                                                   }
1010                                         } else if ( !BER_BVISNULL( &f2->f_sub_final )) {
1011                                                   rc = -1;
1012                                         }
1013                               } else {
1014                                         rc = lex_bvcmp( &f1->f_mr_value,
1015                                                   &f2->f_mr_value );
1016                               }
1017                               break;
1018                     }
1019                     while ( !rc ) {
1020                               f1 = f1->f_next;
1021                               f2 = f2->f_next;
1022                               if ( f1 || f2 ) {
1023                                         if ( !f1 )
1024                                                   rc = -1;
1025                                         else if ( !f2 )
1026                                                   rc = 1;
1027                                         else {
1028                                                   rc = pcache_filter_cmp( f1, f2 );
1029                                         }
1030                               } else {
1031                                         break;
1032                               }
1033                     }
1034           }
1035           return rc;
1036 }
1037 
1038 /* compare filters in each query */
pcache_query_cmp(const void * v1,const void * v2)1039 static int pcache_query_cmp( const void *v1, const void *v2 )
1040 {
1041           const CachedQuery *q1 = v1, *q2 =v2;
1042           return pcache_filter_cmp( q1->filter, q2->filter );
1043 }
1044 
1045 /* add query on top of LRU list */
1046 static void
add_query_on_top(query_manager * qm,CachedQuery * qc)1047 add_query_on_top (query_manager* qm, CachedQuery* qc)
1048 {
1049           CachedQuery* top = qm->lru_top;
1050 
1051           qc->in_lru = 1;
1052           qm->lru_top = qc;
1053 
1054           if (top)
1055                     top->lru_up = qc;
1056           else
1057                     qm->lru_bottom = qc;
1058 
1059           qc->lru_down = top;
1060           qc->lru_up = NULL;
1061           Debug( pcache_debug, "Base of added query = %s\n",
1062                               qc->qbase->base.bv_val );
1063 }
1064 
1065 /* remove_query from LRU list */
1066 
1067 static void
remove_query(query_manager * qm,CachedQuery * qc)1068 remove_query (query_manager* qm, CachedQuery* qc)
1069 {
1070           CachedQuery* up;
1071           CachedQuery* down;
1072 
1073           if (!qc || !qc->in_lru)
1074                     return;
1075 
1076           qc->in_lru = 0;
1077           up = qc->lru_up;
1078           down = qc->lru_down;
1079 
1080           if (!up)
1081                     qm->lru_top = down;
1082 
1083           if (!down)
1084                     qm->lru_bottom = up;
1085 
1086           if (down)
1087                     down->lru_up = up;
1088 
1089           if (up)
1090                     up->lru_down = down;
1091 
1092           qc->lru_up = qc->lru_down = NULL;
1093 }
1094 
1095 /* find and remove string2 from string1
1096  * from start if position = 1,
1097  * from end if position = 3,
1098  * from anywhere if position = 2
1099  * string1 is overwritten if position = 2.
1100  */
1101 
1102 static int
find_and_remove(struct berval * ber1,struct berval * ber2,int position)1103 find_and_remove(struct berval* ber1, struct berval* ber2, int position)
1104 {
1105           int ret=0;
1106 
1107           if ( !ber2->bv_val )
1108                     return 1;
1109           if ( !ber1->bv_val )
1110                     return 0;
1111 
1112           switch( position ) {
1113           case 1:
1114                     if ( ber1->bv_len >= ber2->bv_len && !memcmp( ber1->bv_val,
1115                               ber2->bv_val, ber2->bv_len )) {
1116                               ret = 1;
1117                               ber1->bv_val += ber2->bv_len;
1118                               ber1->bv_len -= ber2->bv_len;
1119                     }
1120                     break;
1121           case 2: {
1122                     char *temp;
1123                     ber1->bv_val[ber1->bv_len] = '\0';
1124                     temp = strstr( ber1->bv_val, ber2->bv_val );
1125                     if ( temp ) {
1126                               strcpy( temp, temp+ber2->bv_len );
1127                               ber1->bv_len -= ber2->bv_len;
1128                               ret = 1;
1129                     }
1130                     break;
1131                     }
1132           case 3:
1133                     if ( ber1->bv_len >= ber2->bv_len &&
1134                               !memcmp( ber1->bv_val+ber1->bv_len-ber2->bv_len, ber2->bv_val,
1135                                         ber2->bv_len )) {
1136                               ret = 1;
1137                               ber1->bv_len -= ber2->bv_len;
1138                     }
1139                     break;
1140           }
1141           return ret;
1142 }
1143 
1144 
1145 static struct berval*
merge_init_final(Operation * op,struct berval * init,struct berval * any,struct berval * final)1146 merge_init_final(Operation *op, struct berval* init, struct berval* any,
1147           struct berval* final)
1148 {
1149           struct berval* merged, *temp;
1150           int i, any_count, count;
1151 
1152           for (any_count=0; any && any[any_count].bv_val; any_count++)
1153                     ;
1154 
1155           count = any_count;
1156 
1157           if (init->bv_val)
1158                     count++;
1159           if (final->bv_val)
1160                     count++;
1161 
1162           merged = (struct berval*)op->o_tmpalloc( (count+1)*sizeof(struct berval),
1163                     op->o_tmpmemctx );
1164           temp = merged;
1165 
1166           if (init->bv_val) {
1167                     ber_dupbv_x( temp, init, op->o_tmpmemctx );
1168                     temp++;
1169           }
1170 
1171           for (i=0; i<any_count; i++) {
1172                     ber_dupbv_x( temp, any, op->o_tmpmemctx );
1173                     temp++; any++;
1174           }
1175 
1176           if (final->bv_val){
1177                     ber_dupbv_x( temp, final, op->o_tmpmemctx );
1178                     temp++;
1179           }
1180           BER_BVZERO( temp );
1181           return merged;
1182 }
1183 
1184 /* Each element in stored must be found in incoming. Incoming is overwritten.
1185  */
1186 static int
strings_containment(struct berval * stored,struct berval * incoming)1187 strings_containment(struct berval* stored, struct berval* incoming)
1188 {
1189           struct berval* element;
1190           int k=0;
1191           int j, rc = 0;
1192 
1193           for ( element=stored; element->bv_val != NULL; element++ ) {
1194                     for (j = k; incoming[j].bv_val != NULL; j++) {
1195                               if (find_and_remove(&(incoming[j]), element, 2)) {
1196                                         k = j;
1197                                         rc = 1;
1198                                         break;
1199                               }
1200                               rc = 0;
1201                     }
1202                     if ( rc ) {
1203                               continue;
1204                     } else {
1205                               return 0;
1206                     }
1207           }
1208           return 1;
1209 }
1210 
1211 static int
substr_containment_substr(Operation * op,Filter * stored,Filter * incoming)1212 substr_containment_substr(Operation *op, Filter* stored, Filter* incoming)
1213 {
1214           int rc = 0;
1215 
1216           struct berval init_incoming;
1217           struct berval final_incoming;
1218           struct berval *remaining_incoming = NULL;
1219 
1220           if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val))
1221              || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val)))
1222                     return 0;
1223 
1224           init_incoming = incoming->f_sub_initial;
1225           final_incoming =  incoming->f_sub_final;
1226 
1227           if (find_and_remove(&init_incoming,
1228                               &(stored->f_sub_initial), 1) && find_and_remove(&final_incoming,
1229                               &(stored->f_sub_final), 3))
1230           {
1231                     if (stored->f_sub_any == NULL) {
1232                               rc = 1;
1233                               goto final;
1234                     }
1235                     remaining_incoming = merge_init_final(op, &init_incoming,
1236                                                             incoming->f_sub_any, &final_incoming);
1237                     rc = strings_containment(stored->f_sub_any, remaining_incoming);
1238                     ber_bvarray_free_x( remaining_incoming, op->o_tmpmemctx );
1239           }
1240 final:
1241           return rc;
1242 }
1243 
1244 static int
substr_containment_equality(Operation * op,Filter * stored,Filter * incoming)1245 substr_containment_equality(Operation *op, Filter* stored, Filter* incoming)
1246 {
1247           struct berval incoming_val[2];
1248           int rc = 0;
1249 
1250           incoming_val[1] = incoming->f_av_value;
1251 
1252           if (find_and_remove(incoming_val+1,
1253                               &(stored->f_sub_initial), 1) && find_and_remove(incoming_val+1,
1254                               &(stored->f_sub_final), 3)) {
1255                     if (stored->f_sub_any == NULL){
1256                               rc = 1;
1257                               goto final;
1258                     }
1259                     ber_dupbv_x( incoming_val, incoming_val+1, op->o_tmpmemctx );
1260                     BER_BVZERO( incoming_val+1 );
1261                     rc = strings_containment(stored->f_sub_any, incoming_val);
1262                     op->o_tmpfree( incoming_val[0].bv_val, op->o_tmpmemctx );
1263           }
1264 final:
1265           return rc;
1266 }
1267 
1268 static Filter *
filter_first(Filter * f)1269 filter_first( Filter *f )
1270 {
1271           while ( f->f_choice == LDAP_FILTER_OR || f->f_choice == LDAP_FILTER_AND )
1272                     f = f->f_and;
1273           return f;
1274 }
1275 
1276 typedef struct fstack {
1277           struct fstack *fs_next;
1278           Filter *fs_fs;
1279           Filter *fs_fi;
1280 } fstack;
1281 
1282 static CachedQuery *
find_filter(Operation * op,TAvlnode * root,Filter * inputf,Filter * first)1283 find_filter( Operation *op, TAvlnode *root, Filter *inputf, Filter *first )
1284 {
1285           Filter* fs;
1286           Filter* fi;
1287           MatchingRule* mrule = NULL;
1288           int res=0, eqpass= 0;
1289           int ret, rc, dir;
1290           TAvlnode *ptr;
1291           CachedQuery cq, *qc;
1292           fstack *stack = NULL, *fsp;
1293 
1294           cq.filter = inputf;
1295           cq.first = first;
1296 
1297           /* substring matches sort to the end, and we just have to
1298            * walk the entire list.
1299            */
1300           if ( first->f_choice == LDAP_FILTER_SUBSTRINGS ) {
1301                     ptr = ldap_tavl_end( root, 1 );
1302                     dir = TAVL_DIR_LEFT;
1303           } else {
1304                     ptr = ldap_tavl_find3( root, &cq, pcache_query_cmp, &ret );
1305                     dir = (first->f_choice == LDAP_FILTER_GE) ? TAVL_DIR_LEFT :
1306                               TAVL_DIR_RIGHT;
1307           }
1308 
1309           while (ptr) {
1310                     qc = ptr->avl_data;
1311                     fi = inputf;
1312                     fs = qc->filter;
1313 
1314                     /* an incoming substr query can only be satisfied by a cached
1315                      * substr query.
1316                      */
1317                     if ( first->f_choice == LDAP_FILTER_SUBSTRINGS &&
1318                               qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
1319                               break;
1320 
1321                     /* an incoming eq query can be satisfied by a cached eq or substr
1322                      * query
1323                      */
1324                     if ( first->f_choice == LDAP_FILTER_EQUALITY ) {
1325                               if ( eqpass == 0 ) {
1326                                         if ( qc->first->f_choice != LDAP_FILTER_EQUALITY ) {
1327 nextpass:                     eqpass = 1;
1328                                                   ptr = ldap_tavl_end( root, 1 );
1329                                                   dir = TAVL_DIR_LEFT;
1330                                                   continue;
1331                                         }
1332                               } else {
1333                                         if ( qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
1334                                                   break;
1335                               }
1336                     }
1337                     do {
1338                               res=0;
1339                               switch (fs->f_choice) {
1340                               case LDAP_FILTER_EQUALITY:
1341                                         if (fi->f_choice == LDAP_FILTER_EQUALITY)
1342                                                   mrule = fs->f_ava->aa_desc->ad_type->sat_equality;
1343                                         else
1344                                                   ret = 1;
1345                                         break;
1346                               case LDAP_FILTER_GE:
1347                               case LDAP_FILTER_LE:
1348                                         mrule = fs->f_ava->aa_desc->ad_type->sat_ordering;
1349                                         break;
1350                               default:
1351                                         mrule = NULL;
1352                               }
1353                               if (mrule) {
1354                                         const char *text;
1355                                         rc = value_match(&ret, fs->f_ava->aa_desc, mrule,
1356                                                   SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
1357                                                   &(fi->f_ava->aa_value),
1358                                                   &(fs->f_ava->aa_value), &text);
1359                                         if (rc != LDAP_SUCCESS) {
1360                                                   return NULL;
1361                                         }
1362                                         if ( fi==first && fi->f_choice==LDAP_FILTER_EQUALITY && ret )
1363                                                   goto nextpass;
1364                               }
1365                               switch (fs->f_choice) {
1366                               case LDAP_FILTER_OR:
1367                               case LDAP_FILTER_AND:
1368                                         if ( fs->f_next ) {
1369                                                   /* save our stack position */
1370                                                   fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx);
1371                                                   fsp->fs_next = stack;
1372                                                   fsp->fs_fs = fs->f_next;
1373                                                   fsp->fs_fi = fi->f_next;
1374                                                   stack = fsp;
1375                                         }
1376                                         fs = fs->f_and;
1377                                         fi = fi->f_and;
1378                                         res=1;
1379                                         break;
1380                               case LDAP_FILTER_SUBSTRINGS:
1381                                         /* check if the equality query can be
1382                                         * answered with cached substring query */
1383                                         if ((fi->f_choice == LDAP_FILTER_EQUALITY)
1384                                                   && substr_containment_equality( op,
1385                                                   fs, fi))
1386                                                   res=1;
1387                                         /* check if the substring query can be
1388                                         * answered with cached substring query */
1389                                         if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS
1390                                                   ) && substr_containment_substr( op,
1391                                                   fs, fi))
1392                                                   res= 1;
1393                                         fs=fs->f_next;
1394                                         fi=fi->f_next;
1395                                         break;
1396                               case LDAP_FILTER_PRESENT:
1397                                         res=1;
1398                                         fs=fs->f_next;
1399                                         fi=fi->f_next;
1400                                         break;
1401                               case LDAP_FILTER_EQUALITY:
1402                                         if (ret == 0)
1403                                                   res = 1;
1404                                         fs=fs->f_next;
1405                                         fi=fi->f_next;
1406                                         break;
1407                               case LDAP_FILTER_GE:
1408                                         if (mrule && ret >= 0)
1409                                                   res = 1;
1410                                         fs=fs->f_next;
1411                                         fi=fi->f_next;
1412                                         break;
1413                               case LDAP_FILTER_LE:
1414                                         if (mrule && ret <= 0)
1415                                                   res = 1;
1416                                         fs=fs->f_next;
1417                                         fi=fi->f_next;
1418                                         break;
1419                               case LDAP_FILTER_NOT:
1420                                         res=0;
1421                                         break;
1422                               default:
1423                                         break;
1424                               }
1425                               if (!fs && !fi && stack) {
1426                                         /* pop the stack */
1427                                         fsp = stack;
1428                                         stack = fsp->fs_next;
1429                                         fs = fsp->fs_fs;
1430                                         fi = fsp->fs_fi;
1431                                         op->o_tmpfree(fsp, op->o_tmpmemctx);
1432                               }
1433                     } while((res) && (fi != NULL) && (fs != NULL));
1434 
1435                     if ( res )
1436                               return qc;
1437                     ptr = ldap_tavl_next( ptr, dir );
1438           }
1439           return NULL;
1440 }
1441 
1442 /* check whether query is contained in any of
1443  * the cached queries in template
1444  */
1445 static CachedQuery *
query_containment(Operation * op,query_manager * qm,Query * query,QueryTemplate * templa)1446 query_containment(Operation *op, query_manager *qm,
1447                       Query *query,
1448                       QueryTemplate *templa)
1449 {
1450           CachedQuery* qc;
1451           int depth = 0, tscope;
1452           Qbase qbase, *qbptr = NULL;
1453           struct berval pdn;
1454 
1455           if (query->filter != NULL) {
1456                     Filter *first;
1457 
1458                     Debug( pcache_debug, "Lock QC index = %p\n",
1459                                         (void *) templa );
1460                     qbase.base = query->base;
1461 
1462                     first = filter_first( query->filter );
1463 
1464                     ldap_pvt_thread_rdwr_rlock(&templa->t_rwlock);
1465                     for( ;; ) {
1466                               /* Find the base */
1467                               qbptr = ldap_avl_find( templa->qbase, &qbase, pcache_dn_cmp );
1468                               if ( qbptr ) {
1469                                         tscope = query->scope;
1470                                         /* Find a matching scope:
1471                                          * match at depth 0 OK
1472                                          * scope is BASE,
1473                                          *        one at depth 1 OK
1474                                          *  subord at depth > 0 OK
1475                                          *        subtree at any depth OK
1476                                          * scope is ONE,
1477                                          *  subtree or subord at any depth OK
1478                                          * scope is SUBORD,
1479                                          *  subtree or subord at any depth OK
1480                                          * scope is SUBTREE,
1481                                          *  subord at depth > 0 OK
1482                                          *  subtree at any depth OK
1483                                          */
1484                                         for ( tscope = 0 ; tscope <= LDAP_SCOPE_CHILDREN; tscope++ ) {
1485                                                   switch ( query->scope ) {
1486                                                   case LDAP_SCOPE_BASE:
1487                                                             if ( tscope == LDAP_SCOPE_BASE && depth ) continue;
1488                                                             if ( tscope == LDAP_SCOPE_ONE && depth != 1) continue;
1489                                                             if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
1490                                                             break;
1491                                                   case LDAP_SCOPE_ONE:
1492                                                             if ( tscope == LDAP_SCOPE_BASE )
1493                                                                       tscope = LDAP_SCOPE_ONE;
1494                                                             if ( tscope == LDAP_SCOPE_ONE && depth ) continue;
1495                                                             if ( !depth ) break;
1496                                                             if ( tscope < LDAP_SCOPE_SUBTREE )
1497                                                                       tscope = LDAP_SCOPE_SUBTREE;
1498                                                             break;
1499                                                   case LDAP_SCOPE_SUBTREE:
1500                                                             if ( tscope < LDAP_SCOPE_SUBTREE )
1501                                                                       tscope = LDAP_SCOPE_SUBTREE;
1502                                                             if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
1503                                                             break;
1504                                                   case LDAP_SCOPE_CHILDREN:
1505                                                             if ( tscope < LDAP_SCOPE_SUBTREE )
1506                                                                       tscope = LDAP_SCOPE_SUBTREE;
1507                                                             break;
1508                                                   }
1509                                                   if ( !qbptr->scopes[tscope] ) continue;
1510 
1511                                                   /* Find filter */
1512                                                   qc = find_filter( op, qbptr->scopes[tscope],
1513                                                                       query->filter, first );
1514                                                   if ( qc ) {
1515                                                             if ( qc->q_sizelimit ) {
1516                                                                       ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
1517                                                                       return NULL;
1518                                                             }
1519                                                             ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1520                                                             if (qm->lru_top != qc) {
1521                                                                       remove_query(qm, qc);
1522                                                                       add_query_on_top(qm, qc);
1523                                                             }
1524                                                             ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1525                                                             return qc;
1526                                                   }
1527                                         }
1528                               }
1529                               if ( be_issuffix( op->o_bd, &qbase.base ))
1530                                         break;
1531                               /* Up a level */
1532                               dnParent( &qbase.base, &pdn );
1533                               qbase.base = pdn;
1534                               depth++;
1535                     }
1536 
1537                     Debug( pcache_debug,
1538                               "Not answerable: Unlock QC index=%p\n",
1539                               (void *) templa );
1540                     ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
1541           }
1542           return NULL;
1543 }
1544 
1545 static void
free_query(CachedQuery * qc)1546 free_query (CachedQuery* qc)
1547 {
1548           free(qc->q_uuid.bv_val);
1549           filter_free(qc->filter);
1550           ldap_pvt_thread_mutex_destroy(&qc->answerable_cnt_mutex);
1551           ldap_pvt_thread_rdwr_destroy( &qc->rwlock );
1552           memset(qc, 0, sizeof(*qc));
1553           free(qc);
1554 }
1555 
1556 
1557 /* Add query to query cache, the returned Query is locked for writing */
1558 static CachedQuery *
add_query(Operation * op,query_manager * qm,Query * query,QueryTemplate * templ,pc_caching_reason_t why,int wlock)1559 add_query(
1560           Operation *op,
1561           query_manager* qm,
1562           Query* query,
1563           QueryTemplate *templ,
1564           pc_caching_reason_t why,
1565           int wlock)
1566 {
1567           CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
1568           Qbase *qbase, qb;
1569           Filter *first;
1570           int rc;
1571           time_t ttl = 0, ttr = 0;
1572           time_t now;
1573 
1574           new_cached_query->qtemp = templ;
1575           BER_BVZERO( &new_cached_query->q_uuid );
1576           new_cached_query->q_sizelimit = 0;
1577 
1578           now = slap_get_time();
1579           switch ( why ) {
1580           case PC_POSITIVE:
1581                     ttl = templ->ttl;
1582                     if ( templ->ttr )
1583                               ttr = now + templ->ttr;
1584                     break;
1585 
1586           case PC_NEGATIVE:
1587                     ttl = templ->negttl;
1588                     break;
1589 
1590           case PC_SIZELIMIT:
1591                     ttl = templ->limitttl;
1592                     break;
1593 
1594           default:
1595                     assert( 0 );
1596                     break;
1597           }
1598           new_cached_query->expiry_time = now + ttl;
1599           new_cached_query->refresh_time = ttr;
1600           new_cached_query->bindref_time = 0;
1601 
1602           new_cached_query->bind_refcnt = 0;
1603           new_cached_query->answerable_cnt = 0;
1604           new_cached_query->refcnt = 1;
1605           ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex);
1606 
1607           new_cached_query->lru_up = NULL;
1608           new_cached_query->lru_down = NULL;
1609           Debug( pcache_debug, "Added query expires at %ld (%s)\n",
1610                               (long) new_cached_query->expiry_time,
1611                               pc_caching_reason_str[ why ] );
1612 
1613           new_cached_query->scope = query->scope;
1614           new_cached_query->filter = query->filter;
1615           new_cached_query->first = first = filter_first( query->filter );
1616 
1617           ldap_pvt_thread_rdwr_init(&new_cached_query->rwlock);
1618           if (wlock)
1619                     ldap_pvt_thread_rdwr_wlock(&new_cached_query->rwlock);
1620 
1621           qb.base = query->base;
1622 
1623           /* Adding a query    */
1624           Debug( pcache_debug, "Lock AQ index = %p\n",
1625                               (void *) templ );
1626           ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
1627           qbase = ldap_avl_find( templ->qbase, &qb, pcache_dn_cmp );
1628           if ( !qbase ) {
1629                     qbase = ch_calloc( 1, sizeof(Qbase) + qb.base.bv_len + 1 );
1630                     qbase->base.bv_len = qb.base.bv_len;
1631                     qbase->base.bv_val = (char *)(qbase+1);
1632                     memcpy( qbase->base.bv_val, qb.base.bv_val, qb.base.bv_len );
1633                     qbase->base.bv_val[qbase->base.bv_len] = '\0';
1634                     ldap_avl_insert( &templ->qbase, qbase, pcache_dn_cmp, ldap_avl_dup_error );
1635           }
1636           new_cached_query->next = templ->query;
1637           new_cached_query->prev = NULL;
1638           new_cached_query->qbase = qbase;
1639           rc = ldap_tavl_insert( &qbase->scopes[query->scope], new_cached_query,
1640                     pcache_query_cmp, ldap_avl_dup_error );
1641           if ( rc == 0 ) {
1642                     qbase->queries++;
1643                     if (templ->query == NULL)
1644                               templ->query_last = new_cached_query;
1645                     else
1646                               templ->query->prev = new_cached_query;
1647                     templ->query = new_cached_query;
1648                     templ->no_of_queries++;
1649           } else {
1650                     ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex);
1651                     if (wlock)
1652                               ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock);
1653                     ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock );
1654                     ch_free( new_cached_query );
1655                     new_cached_query = find_filter( op, qbase->scopes[query->scope],
1656                                                                       query->filter, first );
1657                     filter_free( query->filter );
1658                     query->filter = NULL;
1659           }
1660           Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n",
1661                               (void *) templ, templ->no_of_queries );
1662 
1663           /* Adding on top of LRU list  */
1664           if ( rc == 0 ) {
1665                     ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1666                     add_query_on_top(qm, new_cached_query);
1667                     ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1668           }
1669           Debug( pcache_debug, "Unlock AQ index = %p \n",
1670                               (void *) templ );
1671           ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
1672 
1673           return rc == 0 ? new_cached_query : NULL;
1674 }
1675 
1676 static void
remove_from_template(CachedQuery * qc,QueryTemplate * template)1677 remove_from_template (CachedQuery* qc, QueryTemplate* template)
1678 {
1679           if (!qc->prev && !qc->next) {
1680                     template->query_last = template->query = NULL;
1681           } else if (qc->prev == NULL) {
1682                     qc->next->prev = NULL;
1683                     template->query = qc->next;
1684           } else if (qc->next == NULL) {
1685                     qc->prev->next = NULL;
1686                     template->query_last = qc->prev;
1687           } else {
1688                     qc->next->prev = qc->prev;
1689                     qc->prev->next = qc->next;
1690           }
1691           ldap_tavl_delete( &qc->qbase->scopes[qc->scope], qc, pcache_query_cmp );
1692           qc->qbase->queries--;
1693           if ( qc->qbase->queries == 0 ) {
1694                     ldap_avl_delete( &template->qbase, qc->qbase, pcache_dn_cmp );
1695                     ch_free( qc->qbase );
1696                     qc->qbase = NULL;
1697           }
1698 
1699           template->no_of_queries--;
1700 }
1701 
1702 /* remove bottom query of LRU list from the query cache */
1703 /*
1704  * NOTE: slight change in functionality.
1705  *
1706  * - if result->bv_val is NULL, the query at the bottom of the LRU
1707  *   is removed
1708  * - otherwise, the query whose UUID is *result is removed
1709  *        - if not found, result->bv_val is zeroed
1710  */
1711 static void
cache_replacement(query_manager * qm,struct berval * result)1712 cache_replacement(query_manager* qm, struct berval *result)
1713 {
1714           CachedQuery* bottom;
1715           QueryTemplate *temp;
1716 
1717           ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1718           if ( BER_BVISNULL( result ) ) {
1719                     bottom = qm->lru_bottom;
1720 
1721                     if (!bottom) {
1722                               Debug ( pcache_debug,
1723                                         "Cache replacement invoked without "
1724                                         "any query in LRU list\n" );
1725                               ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1726                               return;
1727                     }
1728 
1729           } else {
1730                     for ( bottom = qm->lru_bottom;
1731                               bottom != NULL;
1732                               bottom = bottom->lru_up )
1733                     {
1734                               if ( bvmatch( result, &bottom->q_uuid ) ) {
1735                                         break;
1736                               }
1737                     }
1738 
1739                     if ( !bottom ) {
1740                               Debug ( pcache_debug,
1741                                         "Could not find query with uuid=\"%s\""
1742                                         "in LRU list\n", result->bv_val );
1743                               ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1744                               BER_BVZERO( result );
1745                               return;
1746                     }
1747           }
1748 
1749           temp = bottom->qtemp;
1750           remove_query(qm, bottom);
1751           ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1752 
1753           *result = bottom->q_uuid;
1754           BER_BVZERO( &bottom->q_uuid );
1755 
1756           Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp );
1757           ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock);
1758           remove_from_template(bottom, temp);
1759           Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
1760                     (void *) temp, temp->no_of_queries );
1761           Debug( pcache_debug, "Unlock CR index = %p\n", (void *) temp );
1762           ldap_pvt_thread_rdwr_wunlock(&temp->t_rwlock);
1763           free_query(bottom);
1764 }
1765 
1766 struct query_info {
1767           struct query_info *next;
1768           struct berval xdn;
1769           int del;
1770 };
1771 
1772 static int
remove_func(Operation * op,SlapReply * rs)1773 remove_func (
1774           Operation *op,
1775           SlapReply *rs
1776 )
1777 {
1778           Attribute *attr;
1779           struct query_info *qi;
1780           int count = 0;
1781 
1782           if ( rs->sr_type != REP_SEARCH ) return 0;
1783 
1784           attr = attr_find( rs->sr_entry->e_attrs,  ad_queryId );
1785           if ( attr == NULL ) return 0;
1786 
1787           count = attr->a_numvals;
1788           assert( count > 0 );
1789           qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx );
1790           qi->next = op->o_callback->sc_private;
1791           op->o_callback->sc_private = qi;
1792           ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1793           qi->del = ( count == 1 );
1794 
1795           return 0;
1796 }
1797 
1798 static int
remove_query_data(Operation * op,struct berval * query_uuid)1799 remove_query_data(
1800           Operation *op,
1801           struct berval       *query_uuid )
1802 {
1803           struct query_info   *qi, *qnext;
1804           char                          filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
1805           AttributeAssertion  ava = ATTRIBUTEASSERTION_INIT;
1806           Filter                        filter = {LDAP_FILTER_EQUALITY};
1807           SlapReply                     sreply = {REP_RESULT};
1808           slap_callback cb = { NULL, remove_func, NULL, NULL };
1809           int deleted = 0;
1810 
1811           op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
1812                     "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val);
1813           filter.f_ava = &ava;
1814           filter.f_av_desc = ad_queryId;
1815           filter.f_av_value = *query_uuid;
1816 
1817           op->o_tag = LDAP_REQ_SEARCH;
1818           op->o_protocol = LDAP_VERSION3;
1819           op->o_callback = &cb;
1820           op->o_time = slap_get_time();
1821           op->o_do_not_cache = 1;
1822 
1823           op->o_req_dn = op->o_bd->be_suffix[0];
1824           op->o_req_ndn = op->o_bd->be_nsuffix[0];
1825           op->ors_scope = LDAP_SCOPE_SUBTREE;
1826           op->ors_deref = LDAP_DEREF_NEVER;
1827           op->ors_slimit = SLAP_NO_LIMIT;
1828           op->ors_tlimit = SLAP_NO_LIMIT;
1829           op->ors_limit = NULL;
1830           op->ors_filter = &filter;
1831           op->ors_filterstr.bv_val = filter_str;
1832           op->ors_filterstr.bv_len = strlen(filter_str);
1833           op->ors_attrs = NULL;
1834           op->ors_attrsonly = 0;
1835 
1836           op->o_bd->be_search( op, &sreply );
1837 
1838           for ( qi=cb.sc_private; qi; qi=qnext ) {
1839                     qnext = qi->next;
1840 
1841                     op->o_req_dn = qi->xdn;
1842                     op->o_req_ndn = qi->xdn;
1843                     rs_reinit( &sreply, REP_RESULT );
1844 
1845                     if ( qi->del ) {
1846                               Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n",
1847                                         query_uuid->bv_val );
1848 
1849                               op->o_tag = LDAP_REQ_DELETE;
1850 
1851                               if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) {
1852                                         deleted++;
1853                               }
1854 
1855                     } else {
1856                               Modifications mod;
1857                               struct berval vals[2];
1858 
1859                               vals[0] = *query_uuid;
1860                               vals[1].bv_val = NULL;
1861                               vals[1].bv_len = 0;
1862                               mod.sml_op = LDAP_MOD_DELETE;
1863                               mod.sml_flags = 0;
1864                               mod.sml_desc = ad_queryId;
1865                               mod.sml_type = ad_queryId->ad_cname;
1866                               mod.sml_values = vals;
1867                               mod.sml_nvalues = NULL;
1868                         mod.sml_numvals = 1;
1869                               mod.sml_next = NULL;
1870                               Debug( pcache_debug,
1871                                         "REMOVING TEMP ATTR : TEMPLATE=%s\n",
1872                                         query_uuid->bv_val );
1873 
1874                               op->orm_modlist = &mod;
1875 
1876                               op->o_bd->be_modify( op, &sreply );
1877                     }
1878                     op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx );
1879                     op->o_tmpfree( qi, op->o_tmpmemctx );
1880           }
1881           return deleted;
1882 }
1883 
1884 static int
1885 get_attr_set(
1886           AttributeName* attrs,
1887           query_manager* qm,
1888           int num
1889 );
1890 
1891 static int
filter2template(Operation * op,Filter * f,struct berval * fstr)1892 filter2template(
1893           Operation           *op,
1894           Filter                        *f,
1895           struct                        berval *fstr )
1896 {
1897           AttributeDescription *ad;
1898           int len, ret;
1899 
1900           switch ( f->f_choice ) {
1901           case LDAP_FILTER_EQUALITY:
1902                     ad = f->f_av_desc;
1903                     len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
1904                     ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
1905                     assert( ret == len );
1906                     fstr->bv_len += len;
1907                     break;
1908 
1909           case LDAP_FILTER_GE:
1910                     ad = f->f_av_desc;
1911                     len = STRLENOF( "(>=)" ) + ad->ad_cname.bv_len;
1912                     ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s>=)", ad->ad_cname.bv_val);
1913                     assert( ret == len );
1914                     fstr->bv_len += len;
1915                     break;
1916 
1917           case LDAP_FILTER_LE:
1918                     ad = f->f_av_desc;
1919                     len = STRLENOF( "(<=)" ) + ad->ad_cname.bv_len;
1920                     ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s<=)", ad->ad_cname.bv_val);
1921                     assert( ret == len );
1922                     fstr->bv_len += len;
1923                     break;
1924 
1925           case LDAP_FILTER_APPROX:
1926                     ad = f->f_av_desc;
1927                     len = STRLENOF( "(~=)" ) + ad->ad_cname.bv_len;
1928                     ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s~=)", ad->ad_cname.bv_val);
1929                     assert( ret == len );
1930                     fstr->bv_len += len;
1931                     break;
1932 
1933           case LDAP_FILTER_SUBSTRINGS:
1934                     ad = f->f_sub_desc;
1935                     len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
1936                     ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
1937                     assert( ret == len );
1938                     fstr->bv_len += len;
1939                     break;
1940 
1941           case LDAP_FILTER_PRESENT:
1942                     ad = f->f_desc;
1943                     len = STRLENOF( "(=*)" ) + ad->ad_cname.bv_len;
1944                     ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=*)", ad->ad_cname.bv_val );
1945                     assert( ret == len );
1946                     fstr->bv_len += len;
1947                     break;
1948 
1949           case LDAP_FILTER_AND:
1950           case LDAP_FILTER_OR:
1951           case LDAP_FILTER_NOT: {
1952                     int rc = 0;
1953                     fstr->bv_val[fstr->bv_len++] = '(';
1954                     switch ( f->f_choice ) {
1955                     case LDAP_FILTER_AND:
1956                               fstr->bv_val[fstr->bv_len] = '&';
1957                               break;
1958                     case LDAP_FILTER_OR:
1959                               fstr->bv_val[fstr->bv_len] = '|';
1960                               break;
1961                     case LDAP_FILTER_NOT:
1962                               fstr->bv_val[fstr->bv_len] = '!';
1963                               break;
1964                     }
1965                     fstr->bv_len++;
1966 
1967                     for ( f = f->f_list; f != NULL; f = f->f_next ) {
1968                               rc = filter2template( op, f, fstr );
1969                               if ( rc ) break;
1970                     }
1971                     fstr->bv_val[fstr->bv_len++] = ')';
1972                     fstr->bv_val[fstr->bv_len] = '\0';
1973 
1974                     return rc;
1975                     }
1976 
1977           default:
1978                     /* a filter should at least have room for "()",
1979                      * an "=" and for a 1-char attr */
1980                     strcpy( fstr->bv_val, "(?=)" );
1981                     fstr->bv_len += STRLENOF("(?=)");
1982                     return -1;
1983           }
1984 
1985           return 0;
1986 }
1987 
1988 #define   BI_HASHED 0x01
1989 #define   BI_DIDCB  0x02
1990 #define   BI_LOOKUP 0x04
1991 
1992 struct search_info;
1993 
1994 typedef struct bindinfo {
1995           cache_manager *bi_cm;
1996           CachedQuery *bi_cq;
1997           QueryTemplate *bi_templ;
1998           struct search_info *bi_si;
1999           int bi_flags;
2000           slap_callback bi_cb;
2001 } bindinfo;
2002 
2003 struct search_info {
2004           slap_overinst *on;
2005           Query query;
2006           QueryTemplate *qtemp;
2007           AttributeName*  save_attrs;   /* original attributes, saved for response */
2008           int swap_saved_attrs;
2009           int max;
2010           int over;
2011           int count;
2012           int slimit;
2013           int slimit_exceeded;
2014           pc_caching_reason_t caching_reason;
2015           Entry *head, *tail;
2016           bindinfo *pbi;
2017 };
2018 
2019 static void
remove_query_and_data(Operation * op,cache_manager * cm,struct berval * uuid)2020 remove_query_and_data(
2021           Operation *op,
2022           cache_manager       *cm,
2023           struct berval       *uuid )
2024 {
2025           query_manager*                qm = cm->qm;
2026 
2027           qm->crfunc( qm, uuid );
2028           if ( !BER_BVISNULL( uuid ) ) {
2029                     int       return_val;
2030 
2031                     Debug( pcache_debug,
2032                               "Removing query UUID %s\n",
2033                               uuid->bv_val );
2034                     return_val = remove_query_data( op, uuid );
2035                     Debug( pcache_debug,
2036                               "QUERY REMOVED, SIZE=%d\n",
2037                               return_val );
2038                     ldap_pvt_thread_mutex_lock( &cm->cache_mutex );
2039                     cm->cur_entries -= return_val;
2040                     cm->num_cached_queries--;
2041                     Debug( pcache_debug,
2042                               "STORED QUERIES = %lu\n",
2043                               cm->num_cached_queries );
2044                     ldap_pvt_thread_mutex_unlock( &cm->cache_mutex );
2045                     Debug( pcache_debug,
2046                               "QUERY REMOVED, CACHE ="
2047                               "%d entries\n",
2048                               cm->cur_entries );
2049           }
2050 }
2051 
2052 /*
2053  * Callback used to fetch queryId values based on entryUUID;
2054  * used by pcache_remove_entries_from_cache()
2055  */
2056 static int
fetch_queryId_cb(Operation * op,SlapReply * rs)2057 fetch_queryId_cb( Operation *op, SlapReply *rs )
2058 {
2059           int                 rc = 0;
2060 
2061           /* only care about searchEntry responses */
2062           if ( rs->sr_type != REP_SEARCH ) {
2063                     return 0;
2064           }
2065 
2066           /* allow only one response per entryUUID */
2067           if ( op->o_callback->sc_private != NULL ) {
2068                     rc = 1;
2069 
2070           } else {
2071                     Attribute *a;
2072 
2073                     /* copy all queryId values into callback's private data */
2074                     a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
2075                     if ( a != NULL ) {
2076                               BerVarray vals = NULL;
2077 
2078                               ber_bvarray_dup_x( &vals, a->a_nvals, op->o_tmpmemctx );
2079                               op->o_callback->sc_private = (void *)vals;
2080                     }
2081           }
2082 
2083           /* clear entry if required */
2084           rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info );
2085 
2086           return rc;
2087 }
2088 
2089 /*
2090  * Call that allows to remove a set of entries from the cache,
2091  * by forcing the removal of all the related queries.
2092  */
2093 int
pcache_remove_entries_from_cache(Operation * op,cache_manager * cm,BerVarray entryUUIDs)2094 pcache_remove_entries_from_cache(
2095           Operation *op,
2096           cache_manager       *cm,
2097           BerVarray entryUUIDs )
2098 {
2099           Connection          conn = { 0 };
2100           OperationBuffer opbuf;
2101           Operation op2;
2102           slap_callback       sc = { 0 };
2103           Filter              f = { 0 };
2104           char                filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
2105           AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
2106           AttributeName       attrs[ 2 ] = {{{ 0 }}};
2107           int                 s, rc;
2108 
2109           if ( op == NULL ) {
2110                     void      *thrctx = ldap_pvt_thread_pool_context();
2111 
2112                     connection_fake_init( &conn, &opbuf, thrctx );
2113                     op = &opbuf.ob_op;
2114 
2115           } else {
2116                     op2 = *op;
2117                     op = &op2;
2118           }
2119 
2120           memset( &op->oq_search, 0, sizeof( op->oq_search ) );
2121           op->ors_scope = LDAP_SCOPE_SUBTREE;
2122           op->ors_deref = LDAP_DEREF_NEVER;
2123           f.f_choice = LDAP_FILTER_EQUALITY;
2124           f.f_ava = &ava;
2125           ava.aa_desc = slap_schema.si_ad_entryUUID;
2126           op->ors_filter = &f;
2127           op->ors_slimit = 1;
2128           op->ors_tlimit = SLAP_NO_LIMIT;
2129           op->ors_limit = NULL;
2130           attrs[ 0 ].an_desc = ad_queryId;
2131           attrs[ 0 ].an_name = ad_queryId->ad_cname;
2132           op->ors_attrs = attrs;
2133           op->ors_attrsonly = 0;
2134 
2135           op->o_req_dn = cm->db.be_suffix[ 0 ];
2136           op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
2137 
2138           op->o_tag = LDAP_REQ_SEARCH;
2139           op->o_protocol = LDAP_VERSION3;
2140           op->o_managedsait = SLAP_CONTROL_CRITICAL;
2141           op->o_bd = &cm->db;
2142           op->o_dn = op->o_bd->be_rootdn;
2143           op->o_ndn = op->o_bd->be_rootndn;
2144           sc.sc_response = fetch_queryId_cb;
2145           op->o_callback = &sc;
2146 
2147           for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) {
2148                     BerVarray vals = NULL;
2149                     SlapReply rs = { REP_RESULT };
2150 
2151                     op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ),
2152                               "(entryUUID=%s)", entryUUIDs[ s ].bv_val );
2153                     op->ors_filterstr.bv_val = filtbuf;
2154                     ava.aa_value = entryUUIDs[ s ];
2155 
2156                     rc = op->o_bd->be_search( op, &rs );
2157                     if ( rc != LDAP_SUCCESS ) {
2158                               continue;
2159                     }
2160 
2161                     vals = (BerVarray)op->o_callback->sc_private;
2162                     if ( vals != NULL ) {
2163                               int                 i;
2164 
2165                               for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
2166                                         struct berval       val = vals[ i ];
2167 
2168                                         remove_query_and_data( op, cm, &val );
2169 
2170                                         if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
2171                                                   ch_free( val.bv_val );
2172                                         }
2173                               }
2174 
2175                               ber_bvarray_free_x( vals, op->o_tmpmemctx );
2176                               op->o_callback->sc_private = NULL;
2177                     }
2178           }
2179 
2180           return 0;
2181 }
2182 
2183 /*
2184  * Call that allows to remove a query from the cache.
2185  */
2186 int
pcache_remove_query_from_cache(Operation * op,cache_manager * cm,struct berval * queryid)2187 pcache_remove_query_from_cache(
2188           Operation *op,
2189           cache_manager       *cm,
2190           struct berval       *queryid )
2191 {
2192           Operation op2 = *op;
2193 
2194           op2.o_bd = &cm->db;
2195 
2196           /* remove the selected query */
2197           remove_query_and_data( &op2, cm, queryid );
2198 
2199           return LDAP_SUCCESS;
2200 }
2201 
2202 /*
2203  * Call that allows to remove a set of queries related to an entry
2204  * from the cache; if queryid is not null, the entry must belong to
2205  * the query indicated by queryid.
2206  */
2207 int
pcache_remove_entry_queries_from_cache(Operation * op,cache_manager * cm,struct berval * ndn,struct berval * queryid)2208 pcache_remove_entry_queries_from_cache(
2209           Operation *op,
2210           cache_manager       *cm,
2211           struct berval       *ndn,
2212           struct berval       *queryid )
2213 {
2214           Connection                    conn = { 0 };
2215           OperationBuffer     opbuf;
2216           Operation           op2;
2217           slap_callback                 sc = { 0 };
2218           SlapReply           rs = { REP_RESULT };
2219           Filter                        f = { 0 };
2220           char                          filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
2221           AttributeAssertion  ava = ATTRIBUTEASSERTION_INIT;
2222           AttributeName                 attrs[ 2 ] = {{{ 0 }}};
2223           int                           rc;
2224 
2225           BerVarray           vals = NULL;
2226 
2227           if ( op == NULL ) {
2228                     void      *thrctx = ldap_pvt_thread_pool_context();
2229 
2230                     connection_fake_init( &conn, &opbuf, thrctx );
2231                     op = &opbuf.ob_op;
2232 
2233           } else {
2234                     op2 = *op;
2235                     op = &op2;
2236           }
2237 
2238           memset( &op->oq_search, 0, sizeof( op->oq_search ) );
2239           op->ors_scope = LDAP_SCOPE_BASE;
2240           op->ors_deref = LDAP_DEREF_NEVER;
2241           if ( queryid == NULL || BER_BVISNULL( queryid ) ) {
2242                     BER_BVSTR( &op->ors_filterstr, "(objectClass=*)" );
2243                     f.f_choice = LDAP_FILTER_PRESENT;
2244                     f.f_desc = slap_schema.si_ad_objectClass;
2245 
2246           } else {
2247                     op->ors_filterstr.bv_len = snprintf( filter_str,
2248                               sizeof( filter_str ), "(%s=%s)",
2249                               ad_queryId->ad_cname.bv_val, queryid->bv_val );
2250                     f.f_choice = LDAP_FILTER_EQUALITY;
2251                     f.f_ava = &ava;
2252                     f.f_av_desc = ad_queryId;
2253                     f.f_av_value = *queryid;
2254           }
2255           op->ors_filter = &f;
2256           op->ors_slimit = 1;
2257           op->ors_tlimit = SLAP_NO_LIMIT;
2258           op->ors_limit = NULL;
2259           attrs[ 0 ].an_desc = ad_queryId;
2260           attrs[ 0 ].an_name = ad_queryId->ad_cname;
2261           op->ors_attrs = attrs;
2262           op->ors_attrsonly = 0;
2263 
2264           op->o_req_dn = *ndn;
2265           op->o_req_ndn = *ndn;
2266 
2267           op->o_tag = LDAP_REQ_SEARCH;
2268           op->o_protocol = LDAP_VERSION3;
2269           op->o_managedsait = SLAP_CONTROL_CRITICAL;
2270           op->o_bd = &cm->db;
2271           op->o_dn = op->o_bd->be_rootdn;
2272           op->o_ndn = op->o_bd->be_rootndn;
2273           sc.sc_response = fetch_queryId_cb;
2274           op->o_callback = &sc;
2275 
2276           rc = op->o_bd->be_search( op, &rs );
2277           if ( rc != LDAP_SUCCESS ) {
2278                     return rc;
2279           }
2280 
2281           vals = (BerVarray)op->o_callback->sc_private;
2282           if ( vals != NULL ) {
2283                     int                 i;
2284 
2285                     for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
2286                               struct berval       val = vals[ i ];
2287 
2288                               remove_query_and_data( op, cm, &val );
2289 
2290                               if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
2291                                         ch_free( val.bv_val );
2292                               }
2293                     }
2294 
2295                     ber_bvarray_free_x( vals, op->o_tmpmemctx );
2296           }
2297 
2298           return LDAP_SUCCESS;
2299 }
2300 
2301 static int
cache_entries(Operation * op,struct berval * query_uuid)2302 cache_entries(
2303           Operation *op,
2304           struct berval *query_uuid )
2305 {
2306           struct search_info *si = op->o_callback->sc_private;
2307           slap_overinst *on = si->on;
2308           cache_manager *cm = on->on_bi.bi_private;
2309           int                 return_val = 0;
2310           Entry               *e;
2311           struct berval       crp_uuid;
2312           char                uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
2313           Operation *op_tmp;
2314           Connection          conn = {0};
2315           OperationBuffer opbuf;
2316           void                *thrctx = ldap_pvt_thread_pool_context();
2317 
2318           query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
2319           ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
2320 
2321           connection_fake_init2( &conn, &opbuf, thrctx, 0 );
2322           op_tmp = &opbuf.ob_op;
2323           op_tmp->o_bd = &cm->db;
2324           op_tmp->o_dn = cm->db.be_rootdn;
2325           op_tmp->o_ndn = cm->db.be_rootndn;
2326 
2327           Debug( pcache_debug, "UUID for query being added = %s\n",
2328                               uuidbuf );
2329 
2330           for ( e=si->head; e; e=si->head ) {
2331                     si->head = e->e_private;
2332                     e->e_private = NULL;
2333                     while ( cm->cur_entries > (cm->max_entries) ) {
2334                               BER_BVZERO( &crp_uuid );
2335                               remove_query_and_data( op_tmp, cm, &crp_uuid );
2336                     }
2337 
2338                     return_val = merge_entry(op_tmp, e, 0, query_uuid);
2339                     ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
2340                     cm->cur_entries += return_val;
2341                     Debug( pcache_debug,
2342                               "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
2343                               cm->cur_entries );
2344                     return_val = 0;
2345                     ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
2346           }
2347 
2348           return return_val;
2349 }
2350 
2351 static int
pcache_op_cleanup(Operation * op,SlapReply * rs)2352 pcache_op_cleanup( Operation *op, SlapReply *rs ) {
2353           slap_callback       *cb = op->o_callback;
2354           struct search_info *si = cb->sc_private;
2355           slap_overinst *on = si->on;
2356           cache_manager *cm = on->on_bi.bi_private;
2357           query_manager*                qm = cm->qm;
2358 
2359           if ( rs->sr_type == REP_RESULT ||
2360                     op->o_abandon || rs->sr_err == SLAPD_ABANDON )
2361           {
2362                     if ( si->swap_saved_attrs ) {
2363                               rs->sr_attrs = si->save_attrs;
2364                               op->ors_attrs = si->save_attrs;
2365                     }
2366                     if ( (op->o_abandon || rs->sr_err == SLAPD_ABANDON) &&
2367                                         si->caching_reason == PC_IGNORE )
2368                     {
2369                               filter_free( si->query.filter );
2370                               if ( si->count ) {
2371                                         /* duplicate query, free it */
2372                                         Entry *e;
2373                                         for (;si->head; si->head=e) {
2374                                                   e = si->head->e_private;
2375                                                   si->head->e_private = NULL;
2376                                                   entry_free(si->head);
2377                                         }
2378                               }
2379 
2380                     } else if ( si->caching_reason != PC_IGNORE ) {
2381                               CachedQuery *qc = qm->addfunc(op, qm, &si->query,
2382                                         si->qtemp, si->caching_reason, 1 );
2383 
2384                               if ( qc != NULL ) {
2385                                         switch ( si->caching_reason ) {
2386                                         case PC_POSITIVE:
2387                                                   cache_entries( op, &qc->q_uuid );
2388                                                   if ( si->pbi ) {
2389                                                             qc->bind_refcnt++;
2390                                                             si->pbi->bi_cq = qc;
2391                                                   }
2392                                                   break;
2393 
2394                                         case PC_SIZELIMIT:
2395                                                   qc->q_sizelimit = rs->sr_nentries;
2396                                                   break;
2397 
2398                                         case PC_NEGATIVE:
2399                                                   break;
2400 
2401                                         default:
2402                                                   assert( 0 );
2403                                                   break;
2404                                         }
2405                                         ldap_pvt_thread_rdwr_wunlock(&qc->rwlock);
2406                                         ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
2407                                         cm->num_cached_queries++;
2408                                         Debug( pcache_debug, "STORED QUERIES = %lu\n",
2409                                                             cm->num_cached_queries );
2410                                         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
2411 
2412                                         /* If the consistency checker suspended itself,
2413                                          * wake it back up
2414                                          */
2415                                         if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
2416                                                   ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
2417                                                   if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
2418                                                             cm->cc_paused = 0;
2419                                                             ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
2420                                                   }
2421                                                   ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
2422                                         }
2423 
2424                               } else if ( si->count ) {
2425                                         /* duplicate query, free it */
2426                                         Entry *e;
2427                                         for (;si->head; si->head=e) {
2428                                                   e = si->head->e_private;
2429                                                   si->head->e_private = NULL;
2430                                                   entry_free(si->head);
2431                                         }
2432                               }
2433 
2434                     } else {
2435                               filter_free( si->query.filter );
2436                     }
2437 
2438                     op->o_callback = op->o_callback->sc_next;
2439                     op->o_tmpfree( cb, op->o_tmpmemctx );
2440           }
2441 
2442           return SLAP_CB_CONTINUE;
2443 }
2444 
2445 static int
pcache_response(Operation * op,SlapReply * rs)2446 pcache_response(
2447           Operation *op,
2448           SlapReply *rs )
2449 {
2450           struct search_info *si = op->o_callback->sc_private;
2451 
2452           if ( si->swap_saved_attrs ) {
2453                     rs->sr_attrs = si->save_attrs;
2454                     rs->sr_attr_flags = slap_attr_flags( si->save_attrs );
2455                     op->ors_attrs = si->save_attrs;
2456           }
2457 
2458           if ( rs->sr_type == REP_SEARCH ) {
2459                     Entry *e;
2460 
2461                     /* don't return more entries than requested by the client */
2462                     if ( si->slimit > 0 && rs->sr_nentries >= si->slimit ) {
2463                               si->slimit_exceeded = 1;
2464                     }
2465 
2466                     /* If we haven't exceeded the limit for this query,
2467                      * build a chain of answers to store. If we hit the
2468                      * limit, empty the chain and ignore the rest.
2469                      */
2470                     if ( !si->over ) {
2471                               slap_overinst *on = si->on;
2472                               cache_manager *cm = on->on_bi.bi_private;
2473 
2474                               /* check if the entry contains undefined
2475                                * attributes/objectClasses (ITS#5680) */
2476                               if ( cm->check_cacheability && test_filter( op, rs->sr_entry, si->query.filter ) != LDAP_COMPARE_TRUE ) {
2477                                         Debug( pcache_debug, "%s: query not cacheable because of schema issues in DN \"%s\"\n",
2478                                                   op->o_log_prefix, rs->sr_entry->e_name.bv_val );
2479                                         goto over;
2480                               }
2481 
2482                               /* check for malformed entries: attrs with no values */
2483                               {
2484                                         Attribute *a = rs->sr_entry->e_attrs;
2485                                         for (; a; a=a->a_next) {
2486                                                   if ( !a->a_numvals ) {
2487                                                             Debug( pcache_debug, "%s: query not cacheable because of attrs without values in DN \"%s\" (%s)\n",
2488                                                             op->o_log_prefix, rs->sr_entry->e_name.bv_val,
2489                                                             a->a_desc->ad_cname.bv_val );
2490                                                             goto over;
2491                                                   }
2492                                         }
2493                               }
2494 
2495                               if ( si->count < si->max ) {
2496                                         si->count++;
2497                                         e = entry_dup( rs->sr_entry );
2498                                         if ( !si->head ) si->head = e;
2499                                         if ( si->tail ) si->tail->e_private = e;
2500                                         si->tail = e;
2501 
2502                               } else {
2503 over:;
2504                                         si->over = 1;
2505                                         si->count = 0;
2506                                         for (;si->head; si->head=e) {
2507                                                   e = si->head->e_private;
2508                                                   si->head->e_private = NULL;
2509                                                   entry_free(si->head);
2510                                         }
2511                                         si->tail = NULL;
2512                               }
2513                     }
2514                     if ( si->slimit_exceeded ) {
2515                               return 0;
2516                     }
2517           } else if ( rs->sr_type == REP_RESULT ) {
2518 
2519                     if ( si->count ) {
2520                               if ( rs->sr_err == LDAP_SUCCESS ) {
2521                                         si->caching_reason = PC_POSITIVE;
2522 
2523                               } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED
2524                                         && si->qtemp->limitttl )
2525                               {
2526                                         Entry *e;
2527 
2528                                         si->caching_reason = PC_SIZELIMIT;
2529                                         for (;si->head; si->head=e) {
2530                                                   e = si->head->e_private;
2531                                                   si->head->e_private = NULL;
2532                                                   entry_free(si->head);
2533                                         }
2534                               }
2535 
2536                     } else if ( si->qtemp->negttl && !si->count && !si->over &&
2537                                         rs->sr_err == LDAP_SUCCESS )
2538                     {
2539                               si->caching_reason = PC_NEGATIVE;
2540                     }
2541 
2542 
2543                     if ( si->slimit_exceeded ) {
2544                               rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
2545                     }
2546           }
2547 
2548           return SLAP_CB_CONTINUE;
2549 }
2550 
2551 /* NOTE: this is a quick workaround to let pcache minimally interact
2552  * with pagedResults.  A more articulated solutions would be to
2553  * perform the remote query without control and cache all results,
2554  * performing the pagedResults search only within the client
2555  * and the proxy.  This requires pcache to understand pagedResults. */
2556 static int
pcache_chk_controls(Operation * op,SlapReply * rs)2557 pcache_chk_controls(
2558           Operation *op,
2559           SlapReply *rs )
2560 {
2561           const char          *non = "";
2562           const char          *stripped = "";
2563 
2564           switch( op->o_pagedresults ) {
2565           case SLAP_CONTROL_NONCRITICAL:
2566                     non = "non-";
2567                     stripped = "; stripped";
2568                     /* fallthru */
2569 
2570           case SLAP_CONTROL_CRITICAL:
2571                     Debug( pcache_debug, "%s: "
2572                               "%scritical pagedResults control "
2573                               "disabled with proxy cache%s.\n",
2574                               op->o_log_prefix, non, stripped );
2575 
2576                     slap_remove_control( op, rs, slap_cids.sc_pagedResults, NULL );
2577                     break;
2578 
2579           default:
2580                     rs->sr_err = SLAP_CB_CONTINUE;
2581                     break;
2582           }
2583 
2584           return rs->sr_err;
2585 }
2586 
2587 static int
pc_setpw(Operation * op,struct berval * pwd,cache_manager * cm)2588 pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm )
2589 {
2590           struct berval vals[2];
2591 
2592           {
2593                     const char *text = NULL;
2594                     BER_BVZERO( &vals[0] );
2595                     slap_passwd_hash( pwd, &vals[0], &text );
2596                     if ( BER_BVISEMPTY( &vals[0] )) {
2597                               Debug( pcache_debug, "pc_setpw: hash failed %s\n",
2598                                         text );
2599                               return LDAP_OTHER;
2600                     }
2601           }
2602 
2603           BER_BVZERO( &vals[1] );
2604 
2605           {
2606                     Modifications mod;
2607                     SlapReply sr = { REP_RESULT };
2608                     slap_callback cb = { 0, slap_null_cb, 0, 0 };
2609                     int rc;
2610 
2611                     mod.sml_op = LDAP_MOD_REPLACE;
2612                     mod.sml_flags = 0;
2613                     mod.sml_desc = slap_schema.si_ad_userPassword;
2614                     mod.sml_type = mod.sml_desc->ad_cname;
2615                     mod.sml_values = vals;
2616                     mod.sml_nvalues = NULL;
2617                     mod.sml_numvals = 1;
2618                     mod.sml_next = NULL;
2619 
2620                     op->o_tag = LDAP_REQ_MODIFY;
2621                     op->orm_modlist = &mod;
2622                     op->o_bd = &cm->db;
2623                     op->o_dn = op->o_bd->be_rootdn;
2624                     op->o_ndn = op->o_bd->be_rootndn;
2625                     op->o_callback = &cb;
2626                     Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n",
2627                               op->o_req_dn.bv_val );
2628                     rc = op->o_bd->be_modify( op, &sr );
2629                     ch_free( vals[0].bv_val );
2630                     return rc;
2631           }
2632 }
2633 
2634 typedef struct bindcacheinfo {
2635           slap_overinst *on;
2636           CachedQuery *qc;
2637 } bindcacheinfo;
2638 
2639 static int
pc_bind_save(Operation * op,SlapReply * rs)2640 pc_bind_save( Operation *op, SlapReply *rs )
2641 {
2642           if ( rs->sr_err == LDAP_SUCCESS ) {
2643                     bindcacheinfo *bci = op->o_callback->sc_private;
2644                     slap_overinst *on = bci->on;
2645                     cache_manager *cm = on->on_bi.bi_private;
2646                     CachedQuery *qc = bci->qc;
2647                     int delete = 0;
2648 
2649                     ldap_pvt_thread_rdwr_wlock( &qc->rwlock );
2650                     if ( qc->bind_refcnt-- ) {
2651                               Operation op2 = *op;
2652                               if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS )
2653                                         bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr;
2654                     } else {
2655                               bci->qc = NULL;
2656                               delete = 1;
2657                     }
2658                     ldap_pvt_thread_rdwr_wunlock( &qc->rwlock );
2659                     if ( delete ) free_query(qc);
2660           }
2661           return SLAP_CB_CONTINUE;
2662 }
2663 
2664 static Filter *
pc_bind_attrs(Operation * op,Entry * e,QueryTemplate * temp,struct berval * fbv)2665 pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp,
2666           struct berval *fbv )
2667 {
2668           int i, len = 0;
2669           struct berval *vals, pres = BER_BVC("*");
2670           char *p1, *p2;
2671           Attribute *a;
2672 
2673           vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ),
2674                     op->o_tmpmemctx );
2675 
2676           for ( i=0; i<temp->bindnattrs; i++ ) {
2677                     a = attr_find( e->e_attrs, temp->bindfattrs[i] );
2678                     if ( a && a->a_vals ) {
2679                               vals[i] = a->a_vals[0];
2680                               len += a->a_vals[0].bv_len;
2681                     } else {
2682                               vals[i] = pres;
2683                     }
2684           }
2685           fbv->bv_len = len + temp->bindftemp.bv_len;
2686           fbv->bv_val = op->o_tmpalloc( fbv->bv_len + 1, op->o_tmpmemctx );
2687 
2688           p1 = temp->bindftemp.bv_val;
2689           p2 = fbv->bv_val;
2690           i = 0;
2691           while ( *p1 ) {
2692                     *p2++ = *p1;
2693                     if ( p1[0] == '=' && p1[1] == ')' ) {
2694                               AC_MEMCPY( p2, vals[i].bv_val, vals[i].bv_len );
2695                               p2 += vals[i].bv_len;
2696                               i++;
2697                     }
2698                     p1++;
2699           }
2700           *p2 = '\0';
2701           op->o_tmpfree( vals, op->o_tmpmemctx );
2702 
2703           /* FIXME: are we sure str2filter_x can't fail?
2704            * caller needs to check */
2705           {
2706                     Filter *f = str2filter_x( op, fbv->bv_val );
2707                     assert( f != NULL );
2708                     return f;
2709           }
2710 }
2711 
2712 /* Check if the requested entry is from the cache and has a valid
2713  * ttr and password hash
2714  */
2715 static int
pc_bind_search(Operation * op,SlapReply * rs)2716 pc_bind_search( Operation *op, SlapReply *rs )
2717 {
2718           if ( rs->sr_type == REP_SEARCH ) {
2719                     bindinfo *pbi = op->o_callback->sc_private;
2720 
2721                     /* We only care if this is an already cached result and we're
2722                      * below the refresh time, or we're offline.
2723                      */
2724                     if ( pbi->bi_cq ) {
2725                               if (( pbi->bi_cm->cc_paused & PCACHE_CC_OFFLINE ) ||
2726                                         op->o_time < pbi->bi_cq->bindref_time ) {
2727                                         Attribute *a;
2728 
2729                                         /* See if a recognized password is hashed here */
2730                                         a = attr_find( rs->sr_entry->e_attrs,
2731                                                   slap_schema.si_ad_userPassword );
2732                                         if ( a && a->a_vals[0].bv_val[0] == '{' &&
2733                                                   lutil_passwd_scheme( a->a_vals[0].bv_val ))
2734                                                   pbi->bi_flags |= BI_HASHED;
2735                               } else {
2736                                         Debug( pcache_debug, "pc_bind_search: cache is stale, "
2737                                                   "reftime: %ld, current time: %ld\n",
2738                                                   pbi->bi_cq->bindref_time, op->o_time );
2739                               }
2740                     } else if ( pbi->bi_si ) {
2741                               /* This search result is going into the cache */
2742                               struct berval fbv;
2743                               Filter *f;
2744 
2745                               filter_free( pbi->bi_si->query.filter );
2746                               f = pc_bind_attrs( op, rs->sr_entry, pbi->bi_templ, &fbv );
2747                               op->o_tmpfree( fbv.bv_val, op->o_tmpmemctx );
2748                               pbi->bi_si->query.filter = filter_dup( f, NULL );
2749                               filter_free_x( op, f, 1 );
2750                     }
2751           }
2752           return 0;
2753 }
2754 
2755 /* We always want pc_bind_search to run after the search handlers */
2756 static int
pc_bind_resp(Operation * op,SlapReply * rs)2757 pc_bind_resp( Operation *op, SlapReply *rs )
2758 {
2759           bindinfo *pbi = op->o_callback->sc_private;
2760           if ( !( pbi->bi_flags & BI_DIDCB )) {
2761                     slap_callback *sc = op->o_callback;
2762                     while ( sc && sc->sc_response != pcache_response )
2763                               sc = sc->sc_next;
2764                     if ( !sc )
2765                               sc = op->o_callback;
2766                     pbi->bi_cb.sc_next = sc->sc_next;
2767                     sc->sc_next = &pbi->bi_cb;
2768                     pbi->bi_flags |= BI_DIDCB;
2769           }
2770           return SLAP_CB_CONTINUE;
2771 }
2772 
2773 #ifdef PCACHE_CONTROL_PRIVDB
2774 static int
pcache_op_privdb(Operation * op,SlapReply * rs)2775 pcache_op_privdb(
2776           Operation           *op,
2777           SlapReply           *rs )
2778 {
2779           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
2780           cache_manager       *cm = on->on_bi.bi_private;
2781           slap_callback       *save_cb;
2782           slap_op_t type;
2783 
2784           /* skip if control is unset */
2785           if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
2786                     return SLAP_CB_CONTINUE;
2787           }
2788 
2789           /* The cache DB isn't open yet */
2790           if ( cm->defer_db_open ) {
2791                     send_ldap_error( op, rs, LDAP_UNAVAILABLE,
2792                               "pcachePrivDB: cacheDB not available" );
2793                     return rs->sr_err;
2794           }
2795 
2796           /* FIXME: might be a little bit exaggerated... */
2797           if ( !be_isroot( op ) ) {
2798                     save_cb = op->o_callback;
2799                     op->o_callback = NULL;
2800                     send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
2801                               "pcachePrivDB: operation not allowed" );
2802                     op->o_callback = save_cb;
2803 
2804                     return rs->sr_err;
2805           }
2806 
2807           /* map tag to operation */
2808           type = slap_req2op( op->o_tag );
2809           if ( type != SLAP_OP_LAST ) {
2810                     BackendInfo         *bi = cm->db.bd_info;
2811                     int                 rc;
2812 
2813                     /* execute, if possible */
2814                     if ( (&bi->bi_op_bind)[ type ] ) {
2815                               Operation op2 = *op;
2816 
2817                               op2.o_bd = &cm->db;
2818 
2819                               rc = (&bi->bi_op_bind)[ type ]( &op2, rs );
2820                               if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) {
2821                                         op->o_conn->c_authz_cookie = cm->db.be_private;
2822                               }
2823 
2824                               return rs->sr_err;
2825                     }
2826           }
2827 
2828           /* otherwise fall back to error */
2829           save_cb = op->o_callback;
2830           op->o_callback = NULL;
2831           send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
2832                     "operation not supported with pcachePrivDB control" );
2833           op->o_callback = save_cb;
2834 
2835           return rs->sr_err;
2836 }
2837 #endif /* PCACHE_CONTROL_PRIVDB */
2838 
2839 static int
pcache_op_bind(Operation * op,SlapReply * rs)2840 pcache_op_bind(
2841           Operation           *op,
2842           SlapReply           *rs )
2843 {
2844           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
2845           cache_manager       *cm = on->on_bi.bi_private;
2846           QueryTemplate *temp;
2847           Entry *e;
2848           slap_callback       cb = { 0 }, *sc;
2849           bindinfo bi = { 0 };
2850           bindcacheinfo *bci;
2851           Operation op2;
2852           int rc;
2853 
2854 #ifdef PCACHE_CONTROL_PRIVDB
2855           if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL )
2856                     return pcache_op_privdb( op, rs );
2857 #endif /* PCACHE_CONTROL_PRIVDB */
2858 
2859           /* Skip if we're not configured for Binds, or cache DB isn't open yet */
2860           if ( !cm->cache_binds || cm->defer_db_open )
2861                     return SLAP_CB_CONTINUE;
2862 
2863           /* First find a matching template with Bind info */
2864           for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) {
2865                     if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase ))
2866                               break;
2867           }
2868           /* Didn't find a suitable template, just passthru */
2869           if ( !temp )
2870                     return SLAP_CB_CONTINUE;
2871 
2872           /* See if the entry is already locally cached. If so, we can
2873            * populate the query filter to retrieve the cached query. We
2874            * need to check the bindrefresh time in the query.
2875            */
2876           op2 = *op;
2877           op2.o_dn = op->o_bd->be_rootdn;
2878           op2.o_ndn = op->o_bd->be_rootndn;
2879 
2880           op2.o_bd = &cm->db;
2881           e = NULL;
2882           rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e );
2883           if ( rc == LDAP_SUCCESS && e ) {
2884                     bi.bi_flags |= BI_LOOKUP;
2885                     op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr );
2886                     be_entry_release_r( &op2, e );
2887           } else {
2888                     op2.ors_filter = temp->bindfilter;
2889                     op2.ors_filterstr = temp->bindfilterstr;
2890           }
2891 
2892           op2.o_bd = op->o_bd;
2893           op2.o_tag = LDAP_REQ_SEARCH;
2894           op2.ors_scope = LDAP_SCOPE_BASE;
2895           op2.ors_deref = LDAP_DEREF_NEVER;
2896           op2.ors_slimit = 1;
2897           op2.ors_tlimit = SLAP_NO_LIMIT;
2898           op2.ors_limit = NULL;
2899           op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs;
2900           op2.ors_attrsonly = 0;
2901 
2902           /* We want to invoke search at the same level of the stack
2903            * as we're already at...
2904            */
2905           bi.bi_cm = cm;
2906           bi.bi_templ = temp;
2907 
2908           bi.bi_cb.sc_response = pc_bind_search;
2909           bi.bi_cb.sc_private = &bi;
2910           cb.sc_private = &bi;
2911           cb.sc_response = pc_bind_resp;
2912           op2.o_callback = &cb;
2913           overlay_op_walk( &op2, rs, op_search, on->on_info, on );
2914 
2915           /* OK, just bind locally */
2916           if ( bi.bi_flags & BI_HASHED ) {
2917                     int delete = 0;
2918                     BackendDB *be = op->o_bd;
2919                     op->o_bd = &cm->db;
2920 
2921                     Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n",
2922                               op->o_req_dn.bv_val );
2923 
2924                     if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) {
2925                               op->o_conn->c_authz_cookie = cm->db.be_private;
2926                     }
2927                     op->o_bd = be;
2928                     ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock );
2929                     if ( !bi.bi_cq->bind_refcnt-- ) {
2930                               delete = 1;
2931                     }
2932                     ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock );
2933                     if ( delete ) free_query( bi.bi_cq );
2934                     return rs->sr_err;
2935           }
2936 
2937           /* We have a cached query to work with */
2938           if ( bi.bi_cq ) {
2939                     sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo),
2940                               op->o_tmpmemctx );
2941                     sc->sc_response = pc_bind_save;
2942                     sc->sc_cleanup = NULL;
2943                     sc->sc_private = sc+1;
2944                     sc->sc_writewait = NULL;
2945                     bci = sc->sc_private;
2946                     sc->sc_next = op->o_callback;
2947                     op->o_callback = sc;
2948                     bci->on = on;
2949                     bci->qc = bi.bi_cq;
2950           }
2951           return SLAP_CB_CONTINUE;
2952 }
2953 
2954 static slap_response refresh_merge;
2955 
2956 static int
pcache_op_search(Operation * op,SlapReply * rs)2957 pcache_op_search(
2958           Operation *op,
2959           SlapReply *rs )
2960 {
2961           slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2962           cache_manager *cm = on->on_bi.bi_private;
2963           query_manager*                qm = cm->qm;
2964 
2965           int i = -1;
2966 
2967           Query               query;
2968           QueryTemplate       *qtemp = NULL;
2969           bindinfo *pbi = NULL;
2970 
2971           int                 attr_set = -1;
2972           CachedQuery         *answerable = NULL;
2973           int                 cacheable = 0;
2974 
2975           struct berval       tempstr;
2976 
2977 #ifdef PCACHE_CONTROL_PRIVDB
2978           if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
2979                     return pcache_op_privdb( op, rs );
2980           }
2981 #endif /* PCACHE_CONTROL_PRIVDB */
2982 
2983           /* The cache DB isn't open yet */
2984           if ( cm->defer_db_open ) {
2985                     send_ldap_error( op, rs, LDAP_UNAVAILABLE,
2986                               "pcachePrivDB: cacheDB not available" );
2987                     return rs->sr_err;
2988           }
2989 
2990           /* pickup runtime ACL changes */
2991           cm->db.be_acl = op->o_bd->be_acl;
2992 
2993           {
2994                     /* See if we're processing a Bind request
2995                      * or a cache refresh */
2996                     slap_callback *cb = op->o_callback;
2997 
2998                     for ( ; cb; cb=cb->sc_next ) {
2999                               if ( cb->sc_response == pc_bind_resp ) {
3000                                         pbi = cb->sc_private;
3001                                         break;
3002                               }
3003                               if ( cb->sc_response == refresh_merge ) {
3004                                         /* This is a refresh, do not search the cache */
3005                                         return SLAP_CB_CONTINUE;
3006                               }
3007                     }
3008           }
3009 
3010           /* FIXME: cannot cache/answer requests with pagedResults control */
3011 
3012           query.filter = op->ors_filter;
3013 
3014           if ( pbi ) {
3015                     query.base = pbi->bi_templ->bindbase;
3016                     query.scope = pbi->bi_templ->bindscope;
3017                     attr_set = pbi->bi_templ->attr_set_index;
3018                     cacheable = 1;
3019                     qtemp = pbi->bi_templ;
3020                     if ( pbi->bi_flags & BI_LOOKUP )
3021                               answerable = qm->qcfunc(op, qm, &query, qtemp);
3022 
3023           } else {
3024                     tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1,
3025                               op->o_tmpmemctx );
3026                     tempstr.bv_len = 0;
3027                     if ( filter2template( op, op->ors_filter, &tempstr ))
3028                     {
3029                               op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
3030                               return SLAP_CB_CONTINUE;
3031                     }
3032 
3033                     Debug( pcache_debug, "query template of incoming query = %s\n",
3034                                                             tempstr.bv_val );
3035 
3036                     /* find attr set */
3037                     attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
3038 
3039                     query.base = op->o_req_ndn;
3040                     query.scope = op->ors_scope;
3041 
3042                     /* check for query containment */
3043                     if (attr_set > -1) {
3044                               QueryTemplate *qt = qm->attr_sets[attr_set].templates;
3045                               for (; qt; qt = qt->qtnext ) {
3046                                         /* find if template i can potentially answer tempstr */
3047                                         if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 )
3048                                                   continue;
3049                                         cacheable = 1;
3050                                         qtemp = qt;
3051                                         Debug( pcache_debug, "Entering QC, querystr = %s\n",
3052                                                             op->ors_filterstr.bv_val );
3053                                         answerable = qm->qcfunc(op, qm, &query, qt);
3054 
3055                                         /* if != NULL, rlocks qtemp->t_rwlock */
3056                                         if (answerable)
3057                                                   break;
3058                               }
3059                     }
3060                     op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
3061           }
3062 
3063           if (answerable) {
3064                     BackendDB *save_bd = op->o_bd;
3065 
3066                     ldap_pvt_thread_mutex_lock( &answerable->answerable_cnt_mutex );
3067                     answerable->answerable_cnt++;
3068                     /* we only care about refcnts if we're refreshing */
3069                     if ( answerable->refresh_time )
3070                               answerable->refcnt++;
3071                     Debug( pcache_debug, "QUERY ANSWERABLE (answered %lu times)\n",
3072                               answerable->answerable_cnt );
3073                     ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex );
3074 
3075                     ldap_pvt_thread_rdwr_wlock(&answerable->rwlock);
3076                     if ( BER_BVISNULL( &answerable->q_uuid )) {
3077                               /* No entries cached, just an empty result set */
3078                               i = rs->sr_err = 0;
3079                               send_ldap_result( op, rs );
3080                     } else {
3081                               /* Let Bind know we used a cached query */
3082                               if ( pbi ) {
3083                                         answerable->bind_refcnt++;
3084                                         pbi->bi_cq = answerable;
3085                               }
3086 
3087                               op->o_bd = &cm->db;
3088                               if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) {
3089                                         slap_callback cb;
3090                                         /* The cached entry was already processed by any
3091                                          * other overlays, so don't let it get processed again.
3092                                          *
3093                                          * This loop removes over_back_response from the stack.
3094                                          */
3095                                         if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) {
3096                                                   slap_callback **scp;
3097                                                   for ( scp = &op->o_callback; *scp != NULL;
3098                                                             scp = &(*scp)->sc_next ) {
3099                                                             if ( (*scp)->sc_next == &cb ) {
3100                                                                       *scp = cb.sc_next;
3101                                                                       break;
3102                                                             }
3103                                                   }
3104                                         }
3105                               }
3106                               i = cm->db.bd_info->bi_op_search( op, rs );
3107                     }
3108                     ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock);
3109                     /* locked by qtemp->qcfunc (query_containment) */
3110                     ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock);
3111                     op->o_bd = save_bd;
3112                     return i;
3113           }
3114 
3115           Debug( pcache_debug, "QUERY NOT ANSWERABLE\n" );
3116 
3117           ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
3118           if (cm->num_cached_queries >= cm->max_queries) {
3119                     cacheable = 0;
3120           }
3121           ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
3122 
3123           if (op->ors_attrsonly)
3124                     cacheable = 0;
3125 
3126           if (cacheable) {
3127                     slap_callback                 *cb;
3128                     struct search_info  *si;
3129 
3130                     Debug( pcache_debug, "QUERY CACHEABLE\n" );
3131                     query.filter = filter_dup(op->ors_filter, NULL);
3132 
3133                     cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx );
3134                     cb->sc_response = pcache_response;
3135                     cb->sc_cleanup = pcache_op_cleanup;
3136                     cb->sc_private = (cb+1);
3137                     cb->sc_writewait = 0;
3138                     si = cb->sc_private;
3139                     si->on = on;
3140                     si->query = query;
3141                     si->qtemp = qtemp;
3142                     si->max = cm->num_entries_limit ;
3143                     si->over = 0;
3144                     si->count = 0;
3145                     si->slimit = 0;
3146                     si->slimit_exceeded = 0;
3147                     si->caching_reason = PC_IGNORE;
3148                     if ( op->ors_slimit > 0 && op->ors_slimit < cm->num_entries_limit ) {
3149                               si->slimit = op->ors_slimit;
3150                               op->ors_slimit = cm->num_entries_limit;
3151                     }
3152                     si->head = NULL;
3153                     si->tail = NULL;
3154                     si->swap_saved_attrs = 1;
3155                     si->save_attrs = op->ors_attrs;
3156                     si->pbi = pbi;
3157                     if ( pbi )
3158                               pbi->bi_si = si;
3159 
3160                     op->ors_attrs = qtemp->t_attrs.attrs;
3161 
3162                     if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
3163                               cb->sc_next = op->o_callback;
3164                               op->o_callback = cb;
3165 
3166                     } else {
3167                               slap_callback                 **pcb;
3168 
3169                               /* need to move the callback at the end, in case other
3170                                * overlays are present, so that the final entry is
3171                                * actually cached */
3172                               cb->sc_next = NULL;
3173                               for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next );
3174                               *pcb = cb;
3175                     }
3176 
3177           } else {
3178                     Debug( pcache_debug, "QUERY NOT CACHEABLE\n" );
3179           }
3180 
3181           return SLAP_CB_CONTINUE;
3182 }
3183 
3184 static int
get_attr_set(AttributeName * attrs,query_manager * qm,int num)3185 get_attr_set(
3186           AttributeName* attrs,
3187           query_manager* qm,
3188           int num )
3189 {
3190           int i = 0;
3191           int count = 0;
3192 
3193           if ( attrs ) {
3194                     for ( ; attrs[i].an_name.bv_val; i++ ) {
3195                               /* only count valid attribute names
3196                                * (searches ignore others, this overlay does the same) */
3197                               if ( attrs[i].an_desc ) {
3198                                         count++;
3199                               }
3200                     }
3201           }
3202 
3203           /* recognize default or explicit single "*" */
3204           if ( ! attrs ||
3205                     ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) )
3206           {
3207                     count = 1;
3208                     attrs = slap_anlist_all_user_attributes;
3209 
3210           /* recognize implicit (no valid attributes) or explicit single "1.1" */
3211           } else if ( count == 0 ||
3212                     ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) )
3213           {
3214                     count = 0;
3215                     attrs = NULL;
3216           }
3217 
3218           for ( i = 0; i < num; i++ ) {
3219                     AttributeName *a2;
3220                     int found = 1;
3221 
3222                     if ( count > qm->attr_sets[i].count ) {
3223                               if ( qm->attr_sets[i].count &&
3224                                         bvmatch( &qm->attr_sets[i].attrs[0].an_name, slap_bv_all_user_attrs )) {
3225                                         break;
3226                               }
3227                               continue;
3228                     }
3229 
3230                     if ( !count ) {
3231                               if ( !qm->attr_sets[i].count ) {
3232                                         break;
3233                               }
3234                               continue;
3235                     }
3236 
3237                     for ( a2 = attrs; a2->an_name.bv_val; a2++ ) {
3238                               if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue;
3239 
3240                               if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) {
3241                                         found = 0;
3242                                         break;
3243                               }
3244                     }
3245 
3246                     if ( found ) {
3247                               break;
3248                     }
3249           }
3250 
3251           if ( i == num ) {
3252                     i = -1;
3253           }
3254 
3255           return i;
3256 }
3257 
3258 /* Refresh a cached query:
3259  * 1: Replay the query on the remote DB and merge each entry into
3260  * the local DB. Remember the DNs of each remote entry.
3261  * 2: Search the local DB for all entries matching this queryID.
3262  * Delete any entry whose DN is not in the list from (1).
3263  */
3264 typedef struct dnlist {
3265           struct dnlist *next;
3266           struct berval dn;
3267           char delete;
3268 } dnlist;
3269 
3270 typedef struct refresh_info {
3271           dnlist *ri_dns;
3272           dnlist *ri_tail;
3273           dnlist *ri_dels;
3274           BackendDB *ri_be;
3275           CachedQuery *ri_q;
3276 } refresh_info;
3277 
dnl_alloc(Operation * op,struct berval * bvdn)3278 static dnlist *dnl_alloc( Operation *op, struct berval *bvdn )
3279 {
3280           dnlist *dn = op->o_tmpalloc( sizeof(dnlist) + bvdn->bv_len + 1,
3281                               op->o_tmpmemctx );
3282           dn->dn.bv_len = bvdn->bv_len;
3283           dn->dn.bv_val = (char *)(dn+1);
3284           AC_MEMCPY( dn->dn.bv_val, bvdn->bv_val, dn->dn.bv_len );
3285           dn->dn.bv_val[dn->dn.bv_len] = '\0';
3286           return dn;
3287 }
3288 
3289 static int
refresh_merge(Operation * op,SlapReply * rs)3290 refresh_merge( Operation *op, SlapReply *rs )
3291 {
3292           if ( rs->sr_type == REP_SEARCH ) {
3293                     refresh_info *ri = op->o_callback->sc_private;
3294                     Entry *e;
3295                     dnlist *dnl;
3296                     slap_callback *ocb;
3297                     int rc;
3298 
3299                     ocb = op->o_callback;
3300                     /* Find local entry, merge */
3301                     op->o_bd = ri->ri_be;
3302                     rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e );
3303                     if ( rc != LDAP_SUCCESS || e == NULL ) {
3304                               /* No local entry, just add it. FIXME: we are not checking
3305                                * the cache entry limit here
3306                                */
3307                                merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid );
3308                     } else {
3309                               /* Entry exists, update it */
3310                               Entry ne;
3311                               Attribute *a, **b;
3312                               Modifications *modlist, *mods = NULL;
3313                               const char*         text = NULL;
3314                               char                          textbuf[SLAP_TEXT_BUFLEN];
3315                               size_t                        textlen = sizeof(textbuf);
3316                               slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
3317 
3318                               ne = *e;
3319                               b = &ne.e_attrs;
3320                               /* Get a copy of only the attrs we requested */
3321                               for ( a=e->e_attrs; a; a=a->a_next ) {
3322                                         if ( ad_inlist( a->a_desc, rs->sr_attrs )) {
3323                                                   *b = attr_alloc( a->a_desc );
3324                                                   *(*b) = *a;
3325                                                   /* The actual values still belong to e */
3326                                                   (*b)->a_flags |= SLAP_ATTR_DONT_FREE_VALS |
3327                                                             SLAP_ATTR_DONT_FREE_DATA;
3328                                                   b = &((*b)->a_next);
3329                                         }
3330                               }
3331                               *b = NULL;
3332                               slap_entry2mods( rs->sr_entry, &modlist, &text, textbuf, textlen );
3333                               syncrepl_diff_entry( op, ne.e_attrs, rs->sr_entry->e_attrs,
3334                                         &mods, &modlist, 0 );
3335                               be_entry_release_r( op, e );
3336                               attrs_free( ne.e_attrs );
3337                               slap_mods_free( modlist, 1 );
3338                               /* mods is NULL if there are no changes */
3339                               if ( mods ) {
3340                                         SlapReply rs2 = { REP_RESULT };
3341                                         struct berval dn = op->o_req_dn;
3342                                         struct berval ndn = op->o_req_ndn;
3343                                         op->o_tag = LDAP_REQ_MODIFY;
3344                                         op->orm_modlist = mods;
3345                                         op->o_req_dn = rs->sr_entry->e_name;
3346                                         op->o_req_ndn = rs->sr_entry->e_nname;
3347                                         op->o_callback = &cb;
3348                                         op->o_bd->be_modify( op, &rs2 );
3349                                         rs->sr_err = rs2.sr_err;
3350                                         rs_assert_done( &rs2 );
3351                                         slap_mods_free( mods, 1 );
3352                                         op->o_req_dn = dn;
3353                                         op->o_req_ndn = ndn;
3354                               }
3355                     }
3356 
3357                     /* Add DN to list */
3358                     dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
3359                     dnl->next = NULL;
3360                     if ( ri->ri_tail ) {
3361                               ri->ri_tail->next = dnl;
3362                     } else {
3363                               ri->ri_dns = dnl;
3364                     }
3365                     ri->ri_tail = dnl;
3366                     op->o_callback = ocb;
3367           }
3368           return 0;
3369 }
3370 
3371 static int
refresh_purge(Operation * op,SlapReply * rs)3372 refresh_purge( Operation *op, SlapReply *rs )
3373 {
3374           if ( rs->sr_type == REP_SEARCH ) {
3375                     refresh_info *ri = op->o_callback->sc_private;
3376                     dnlist **dn;
3377                     int del = 1;
3378 
3379                     /* Did the entry exist on the remote? */
3380                     for ( dn=&ri->ri_dns; *dn; dn = &(*dn)->next ) {
3381                               if ( dn_match( &(*dn)->dn, &rs->sr_entry->e_nname )) {
3382                                         dnlist *dnext = (*dn)->next;
3383                                         op->o_tmpfree( *dn, op->o_tmpmemctx );
3384                                         *dn = dnext;
3385                                         del = 0;
3386                                         break;
3387                               }
3388                     }
3389                     /* No, so put it on the list to delete */
3390                     if ( del ) {
3391                               Attribute *a;
3392                               dnlist *dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
3393                               dnl->next = ri->ri_dels;
3394                               ri->ri_dels = dnl;
3395                               a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
3396                               /* If ours is the only queryId, delete entry */
3397                               dnl->delete = ( a->a_numvals == 1 );
3398                     }
3399           }
3400           return 0;
3401 }
3402 
3403 static int
refresh_query(Operation * op,CachedQuery * query,slap_overinst * on)3404 refresh_query( Operation *op, CachedQuery *query, slap_overinst *on )
3405 {
3406           SlapReply rs = {REP_RESULT};
3407           slap_callback cb = { 0 };
3408           refresh_info ri = { 0 };
3409           char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
3410           AttributeAssertion  ava = ATTRIBUTEASSERTION_INIT;
3411           Filter filter = {LDAP_FILTER_EQUALITY};
3412           AttributeName attrs[ 2 ] = {{{ 0 }}};
3413           dnlist *dn;
3414           int rc;
3415 
3416           ldap_pvt_thread_mutex_lock( &query->answerable_cnt_mutex );
3417           query->refcnt = 0;
3418           ldap_pvt_thread_mutex_unlock( &query->answerable_cnt_mutex );
3419 
3420           cb.sc_response = refresh_merge;
3421           cb.sc_private = &ri;
3422 
3423           /* cache DB */
3424           ri.ri_be = op->o_bd;
3425           ri.ri_q = query;
3426 
3427           op->o_tag = LDAP_REQ_SEARCH;
3428           op->o_protocol = LDAP_VERSION3;
3429           op->o_callback = &cb;
3430           op->o_do_not_cache = 1;
3431 
3432           op->o_req_dn = query->qbase->base;
3433           op->o_req_ndn = query->qbase->base;
3434           op->ors_scope = query->scope;
3435           op->ors_deref = LDAP_DEREF_NEVER;
3436           op->ors_slimit = SLAP_NO_LIMIT;
3437           op->ors_tlimit = SLAP_NO_LIMIT;
3438           op->ors_limit = NULL;
3439           op->ors_filter = query->filter;
3440           filter2bv_x( op, query->filter, &op->ors_filterstr );
3441           op->ors_attrs = query->qtemp->t_attrs.attrs;
3442           op->ors_attrsonly = 0;
3443 
3444           op->o_bd = on->on_info->oi_origdb;
3445           rc = op->o_bd->be_search( op, &rs );
3446           if ( rc ) {
3447                     op->o_bd = ri.ri_be;
3448                     goto leave;
3449           }
3450 
3451           /* Get the DNs of all entries matching this query */
3452           cb.sc_response = refresh_purge;
3453 
3454           op->o_bd = ri.ri_be;
3455           op->o_req_dn = op->o_bd->be_suffix[0];
3456           op->o_req_ndn = op->o_bd->be_nsuffix[0];
3457           op->ors_scope = LDAP_SCOPE_SUBTREE;
3458           op->ors_deref = LDAP_DEREF_NEVER;
3459           op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
3460                     "(%s=%s)", ad_queryId->ad_cname.bv_val, query->q_uuid.bv_val);
3461           filter.f_ava = &ava;
3462           filter.f_av_desc = ad_queryId;
3463           filter.f_av_value = query->q_uuid;
3464           attrs[ 0 ].an_desc = ad_queryId;
3465           attrs[ 0 ].an_name = ad_queryId->ad_cname;
3466           op->ors_attrs = attrs;
3467           op->ors_attrsonly = 0;
3468           rs_reinit( &rs, REP_RESULT );
3469           rc = op->o_bd->be_search( op, &rs );
3470           if ( rc ) goto leave;
3471 
3472           while (( dn = ri.ri_dels )) {
3473                     op->o_req_dn = dn->dn;
3474                     op->o_req_ndn = dn->dn;
3475                     rs_reinit( &rs, REP_RESULT );
3476                     if ( dn->delete ) {
3477                               op->o_tag = LDAP_REQ_DELETE;
3478                               op->o_bd->be_delete( op, &rs );
3479                     } else {
3480                               Modifications mod;
3481                               struct berval vals[2];
3482 
3483                               vals[0] = query->q_uuid;
3484                               BER_BVZERO( &vals[1] );
3485                               mod.sml_op = LDAP_MOD_DELETE;
3486                               mod.sml_flags = 0;
3487                               mod.sml_desc = ad_queryId;
3488                               mod.sml_type = ad_queryId->ad_cname;
3489                               mod.sml_values = vals;
3490                               mod.sml_nvalues = NULL;
3491                               mod.sml_numvals = 1;
3492                               mod.sml_next = NULL;
3493 
3494                               op->o_tag = LDAP_REQ_MODIFY;
3495                               op->orm_modlist = &mod;
3496                               op->o_bd->be_modify( op, &rs );
3497                     }
3498                     ri.ri_dels = dn->next;
3499                     op->o_tmpfree( dn, op->o_tmpmemctx );
3500           }
3501 
3502 leave:
3503           /* reset our local heap, we're done with it */
3504           slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, op->o_threadctx, 1 );
3505           return rc;
3506 }
3507 
3508 static void*
consistency_check(void * ctx,void * arg)3509 consistency_check(
3510           void *ctx,
3511           void *arg )
3512 {
3513           struct re_s *rtask = arg;
3514           slap_overinst *on = rtask->arg;
3515           cache_manager *cm = on->on_bi.bi_private;
3516           query_manager *qm = cm->qm;
3517           Connection conn = {0};
3518           OperationBuffer opbuf;
3519           Operation *op;
3520 
3521           CachedQuery *query, *qprev;
3522           CachedQuery *expires = NULL;
3523           int return_val, pause = PCACHE_CC_PAUSED;
3524           QueryTemplate *templ;
3525 
3526           /* Don't expire anything when we're offline */
3527           if ( cm->cc_paused & PCACHE_CC_OFFLINE ) {
3528                     pause = PCACHE_CC_OFFLINE;
3529                     goto leave;
3530           }
3531 
3532           connection_fake_init( &conn, &opbuf, ctx );
3533           op = &opbuf.ob_op;
3534 
3535           op->o_bd = &cm->db;
3536           op->o_dn = cm->db.be_rootdn;
3537           op->o_ndn = cm->db.be_rootndn;
3538 
3539           cm->cc_arg = arg;
3540 
3541           for (templ = qm->templates; templ; templ=templ->qmnext) {
3542                     time_t ttl;
3543                     if ( !templ->query_last ) continue;
3544                     pause = 0;
3545                     op->o_time = slap_get_time();
3546                     if ( !templ->ttr ) {
3547                               ttl = templ->ttl;
3548                               if ( templ->negttl && templ->negttl < ttl )
3549                                         ttl = templ->negttl;
3550                               if ( templ->limitttl && templ->limitttl < ttl )
3551                                         ttl = templ->limitttl;
3552                               /* The oldest timestamp that needs expiration checking */
3553                               ttl += op->o_time;
3554                     }
3555 
3556                     Debug( pcache_debug, "Lock CR index = %p\n",
3557                                         (void *) templ );
3558                     ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
3559                     for ( query=templ->query_last; query; query=qprev ) {
3560                               qprev = query->prev;
3561                               if ( query->refresh_time && query->refresh_time < op->o_time ) {
3562                                         /* A refresh will extend the expiry if the query has been
3563                                          * referenced, but not if it's unreferenced. If the
3564                                          * expiration has been hit, then skip the refresh since
3565                                          * we're just going to discard the result anyway.
3566                                          */
3567                                         if ( query->refcnt )
3568                                                   query->expiry_time = op->o_time + templ->ttl;
3569                                         if ( query->expiry_time > op->o_time ) {
3570                                                   /* perform actual refresh below */
3571                                                   continue;
3572                                         }
3573                               }
3574 
3575                               if (query->expiry_time < op->o_time) {
3576                                         int rem = 0;
3577                                         if ( query != templ->query_last )
3578                                                   continue;
3579                                         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
3580                                         if (query->in_lru) {
3581                                                   remove_query(qm, query);
3582                                                   rem = 1;
3583                                         }
3584                                         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
3585                                         if (!rem)
3586                                                   continue;
3587                                         remove_from_template(query, templ);
3588                                         Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
3589                                                             (void *) templ, templ->no_of_queries );
3590                                         query->prev = expires;
3591                                         expires = query;
3592                                         query->qtemp = NULL;
3593                               } else if ( !templ->ttr && query->expiry_time > ttl ) {
3594                                         /* We don't need to check for refreshes, and this
3595                                          * query's expiry is too new, and all subsequent queries
3596                                          * will be newer yet. So stop looking.
3597                                          *
3598                                          * If we have refreshes, then we always have to walk the
3599                                          * entire query list.
3600                                          */
3601                                         break;
3602                               }
3603                     }
3604                     Debug( pcache_debug, "Unlock CR index = %p\n",
3605                                         (void *) templ );
3606                     ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
3607                     for ( query=expires; query; query=qprev ) {
3608                               int rem;
3609                               qprev = query->prev;
3610                               if ( BER_BVISNULL( &query->q_uuid ))
3611                                         return_val = 0;
3612                               else
3613                                         return_val = remove_query_data(op, &query->q_uuid);
3614                               Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n",
3615                                                             return_val );
3616                               ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
3617                               cm->cur_entries -= return_val;
3618                               cm->num_cached_queries--;
3619                               Debug( pcache_debug, "STORED QUERIES = %lu\n",
3620                                                   cm->num_cached_queries );
3621                               ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
3622                               Debug( pcache_debug,
3623                                         "STALE QUERY REMOVED, CACHE ="
3624                                         "%d entries\n",
3625                                         cm->cur_entries );
3626                               ldap_pvt_thread_rdwr_wlock( &query->rwlock );
3627                               if ( query->bind_refcnt-- ) {
3628                                         rem = 0;
3629                               } else {
3630                                         rem = 1;
3631                               }
3632                               ldap_pvt_thread_rdwr_wunlock( &query->rwlock );
3633                               if ( rem ) free_query(query);
3634                     }
3635 
3636                     /* handle refreshes that we skipped earlier */
3637                     if ( templ->ttr ) {
3638                               ldap_pvt_thread_rdwr_rlock(&templ->t_rwlock);
3639                               for ( query=templ->query_last; query; query=qprev ) {
3640                                         qprev = query->prev;
3641                                         if ( query->refresh_time && query->refresh_time < op->o_time ) {
3642                                                   /* A refresh will extend the expiry if the query has been
3643                                                    * referenced, but not if it's unreferenced. If the
3644                                                    * expiration has been hit, then skip the refresh since
3645                                                    * we're just going to discard the result anyway.
3646                                                    */
3647                                                   if ( query->expiry_time > op->o_time ) {
3648                                                             refresh_query( op, query, on );
3649                                                             query->refresh_time = op->o_time + templ->ttr;
3650                                                   }
3651                                         }
3652                               }
3653                               ldap_pvt_thread_rdwr_runlock(&templ->t_rwlock);
3654                     }
3655           }
3656 
3657 leave:
3658           ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
3659           if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
3660                     ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
3661           }
3662           /* If there were no queries, defer processing for a while */
3663           if ( cm->cc_paused != pause )
3664                     cm->cc_paused = pause;
3665           ldap_pvt_runqueue_resched( &slapd_rq, rtask, pause );
3666 
3667           ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
3668           return NULL;
3669 }
3670 
3671 
3672 #define MAX_ATTR_SETS 500
3673 
3674 enum {
3675           PC_MAIN = 1,
3676           PC_ATTR,
3677           PC_TEMP,
3678           PC_RESP,
3679           PC_QUERIES,
3680           PC_OFFLINE,
3681           PC_BIND,
3682           PC_PRIVATE_DB
3683 };
3684 
3685 static ConfigDriver pc_cf_gen;
3686 static ConfigLDAPadd pc_ldadd;
3687 static ConfigCfAdd pc_cfadd;
3688 
3689 static ConfigTable pccfg[] = {
3690           { "pcache", "backend> <max_entries> <numattrsets> <entry limit> "
3691                                         "<cycle_time",
3692                     6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
3693                     "( OLcfgOvAt:2.1 NAME ( 'olcPcache' 'olcProxyCache' ) "
3694                               "DESC 'Proxy Cache basic parameters' "
3695                               "EQUALITY caseIgnoreMatch "
3696                               "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
3697           { "pcacheAttrset", "index> <attributes...",
3698                     2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
3699                     "( OLcfgOvAt:2.2 NAME ( 'olcPcacheAttrset' 'olcProxyAttrset' ) "
3700                               "DESC 'A set of attributes to cache' "
3701                               "EQUALITY caseIgnoreMatch "
3702                               "SYNTAX OMsDirectoryString )", NULL, NULL },
3703           { "pcacheTemplate", "filter> <attrset-index> <TTL> <negTTL> "
3704                               "<limitTTL> <TTR",
3705                     4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
3706                     "( OLcfgOvAt:2.3 NAME ( 'olcPcacheTemplate' 'olcProxyCacheTemplate' ) "
3707                               "DESC 'Filter template, attrset, cache TTL, "
3708                                         "optional negative TTL, optional sizelimit TTL, "
3709                                         "optional TTR' "
3710                               "EQUALITY caseIgnoreMatch "
3711                               "SYNTAX OMsDirectoryString )", NULL, NULL },
3712           { "pcachePosition", "head|tail(default)",
3713                     2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
3714                     "( OLcfgOvAt:2.4 NAME 'olcPcachePosition' "
3715                               "DESC 'Response callback position in overlay stack' "
3716                               "EQUALITY caseIgnoreMatch "
3717                               "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
3718           { "pcacheMaxQueries", "queries",
3719                     2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
3720                     "( OLcfgOvAt:2.5 NAME ( 'olcPcacheMaxQueries' 'olcProxyCacheQueries' ) "
3721                               "DESC 'Maximum number of queries to cache' "
3722                               "EQUALITY integerMatch "
3723                               "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
3724           { "pcachePersist", "TRUE|FALSE",
3725                     2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
3726                     "( OLcfgOvAt:2.6 NAME ( 'olcPcachePersist' 'olcProxySaveQueries' ) "
3727                               "DESC 'Save cached queries for hot restart' "
3728                               "EQUALITY booleanMatch "
3729                               "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
3730           { "pcacheValidate", "TRUE|FALSE",
3731                     2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
3732                     "( OLcfgOvAt:2.7 NAME ( 'olcPcacheValidate' 'olcProxyCheckCacheability' ) "
3733                               "DESC 'Check whether the results of a query are cacheable, e.g. for schema issues' "
3734                               "EQUALITY booleanMatch "
3735                               "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
3736           { "pcacheOffline", "TRUE|FALSE",
3737                     2, 2, 0, ARG_ON_OFF|ARG_MAGIC|PC_OFFLINE, pc_cf_gen,
3738                     "( OLcfgOvAt:2.8 NAME 'olcPcacheOffline' "
3739                               "DESC 'Set cache to offline mode and disable expiration' "
3740                               "EQUALITY booleanMatch "
3741                               "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
3742           { "pcacheBind", "filter> <attrset-index> <TTR> <scope> <base",
3743                     6, 6, 0, ARG_MAGIC|PC_BIND, pc_cf_gen,
3744                     "( OLcfgOvAt:2.9 NAME 'olcPcacheBind' "
3745                               "DESC 'Parameters for caching Binds' "
3746                               "EQUALITY caseIgnoreMatch "
3747                               "SYNTAX OMsDirectoryString )", NULL, NULL },
3748           { "pcache-", "private database args",
3749                     1, 0, STRLENOF("pcache-"), ARG_MAGIC|PC_PRIVATE_DB, pc_cf_gen,
3750                     NULL, NULL, NULL },
3751 
3752           /* Legacy keywords */
3753           { "proxycache", "backend> <max_entries> <numattrsets> <entry limit> "
3754                                         "<cycle_time",
3755                     6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
3756                     NULL, NULL, NULL },
3757           { "proxyattrset", "index> <attributes...",
3758                     2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
3759                     NULL, NULL, NULL },
3760           { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
3761                     4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
3762                     NULL, NULL, NULL },
3763           { "response-callback", "head|tail(default)",
3764                     2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
3765                     NULL, NULL, NULL },
3766           { "proxyCacheQueries", "queries",
3767                     2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
3768                     NULL, NULL, NULL },
3769           { "proxySaveQueries", "TRUE|FALSE",
3770                     2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
3771                     NULL, NULL, NULL },
3772           { "proxyCheckCacheability", "TRUE|FALSE",
3773                     2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
3774                     NULL, NULL, NULL },
3775 
3776           { NULL, NULL, 0, 0, 0, ARG_IGNORED }
3777 };
3778 
3779 static ConfigOCs pcocs[] = {
3780           { "( OLcfgOvOc:2.1 "
3781                     "NAME 'olcPcacheConfig' "
3782                     "DESC 'ProxyCache configuration' "
3783                     "SUP olcOverlayConfig "
3784                     "MUST ( olcPcache $ olcPcacheAttrset $ olcPcacheTemplate ) "
3785                     "MAY ( olcPcachePosition $ olcPcacheMaxQueries $ olcPcachePersist $ "
3786                               "olcPcacheValidate $ olcPcacheOffline $ olcPcacheBind ) )",
3787                     Cft_Overlay, pccfg, NULL, pc_cfadd },
3788           { "( OLcfgOvOc:2.2 "
3789                     "NAME 'olcPcacheDatabase' "
3790                     "DESC 'Cache database configuration' "
3791                     /* co_table is initialized in pcache_initialize */
3792                     "AUXILIARY )", Cft_Misc, NULL, pc_ldadd },
3793           { NULL, 0, NULL }
3794 };
3795 
3796 static int pcache_db_open2( slap_overinst *on, ConfigReply *cr );
3797 
3798 static int
pc_ldadd_cleanup(ConfigArgs * c)3799 pc_ldadd_cleanup( ConfigArgs *c )
3800 {
3801           slap_overinst *on = c->ca_private;
3802           return pcache_db_open2( on, &c->reply );
3803 }
3804 
3805 static int
pc_ldadd(CfEntryInfo * p,Entry * e,ConfigArgs * ca)3806 pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
3807 {
3808           slap_overinst *on;
3809           cache_manager *cm;
3810 
3811           if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
3812                     p->ce_bi->bi_cf_ocs != pcocs )
3813                     return LDAP_CONSTRAINT_VIOLATION;
3814 
3815           on = (slap_overinst *)p->ce_bi;
3816           cm = on->on_bi.bi_private;
3817           ca->be = &cm->db;
3818           /* Defer open if this is an LDAPadd */
3819           if ( CONFIG_ONLINE_ADD( ca ))
3820                     config_push_cleanup( ca, pc_ldadd_cleanup );
3821           else
3822                     cm->defer_db_open = 0;
3823           ca->ca_private = on;
3824           return LDAP_SUCCESS;
3825 }
3826 
3827 static int
pc_cfadd(Operation * op,SlapReply * rs,Entry * p,ConfigArgs * ca)3828 pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
3829 {
3830           CfEntryInfo *pe = p->e_private;
3831           slap_overinst *on = (slap_overinst *)pe->ce_bi;
3832           cache_manager *cm = on->on_bi.bi_private;
3833           struct berval bv;
3834 
3835           /* FIXME: should not hardcode "olcDatabase" here */
3836           bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
3837                     "olcDatabase=" SLAP_X_ORDERED_FMT "%s",
3838                     0, cm->db.bd_info->bi_type );
3839           if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
3840                     return -1;
3841           }
3842           bv.bv_val = ca->cr_msg;
3843           ca->be = &cm->db;
3844           cm->defer_db_open = 0;
3845 
3846           /* We can only create this entry if the database is table-driven
3847            */
3848           if ( cm->db.bd_info->bi_cf_ocs )
3849                     config_build_entry( op, rs, pe, ca, &bv, cm->db.bd_info->bi_cf_ocs,
3850                               &pcocs[1] );
3851 
3852           return 0;
3853 }
3854 
3855 static int
pc_cf_gen(ConfigArgs * c)3856 pc_cf_gen( ConfigArgs *c )
3857 {
3858           slap_overinst       *on = (slap_overinst *)c->bi;
3859           cache_manager*      cm = on->on_bi.bi_private;
3860           query_manager*  qm = cm->qm;
3861           QueryTemplate*      temp;
3862           AttributeName*  attr_name;
3863           AttributeName*      attrarray;
3864           const char*         text=NULL;
3865           int                 i, num, rc = 0;
3866           char                *ptr;
3867           unsigned long       t;
3868 
3869           if ( c->op == SLAP_CONFIG_EMIT ) {
3870                     struct berval bv;
3871                     switch( c->type ) {
3872                     case PC_MAIN:
3873                               bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %d %d %d %ld",
3874                                         cm->db.bd_info->bi_type, cm->max_entries, cm->numattrsets,
3875                                         cm->num_entries_limit, cm->cc_period );
3876                               bv.bv_val = c->cr_msg;
3877                               value_add_one( &c->rvalue_vals, &bv );
3878                               break;
3879                     case PC_ATTR:
3880                               for (i=0; i<cm->numattrsets; i++) {
3881                                         if ( !qm->attr_sets[i].count ) continue;
3882 
3883                                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%d", i );
3884 
3885                                         /* count the attr length */
3886                                         for ( attr_name = qm->attr_sets[i].attrs;
3887                                                   attr_name->an_name.bv_val; attr_name++ )
3888                                         {
3889                                                   bv.bv_len += attr_name->an_name.bv_len + 1;
3890                                                   if ( attr_name->an_desc &&
3891                                                                       ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
3892                                                             bv.bv_len += STRLENOF("undef:");
3893                                                   }
3894                                         }
3895 
3896                                         bv.bv_val = ch_malloc( bv.bv_len+1 );
3897                                         ptr = lutil_strcopy( bv.bv_val, c->cr_msg );
3898                                         for ( attr_name = qm->attr_sets[i].attrs;
3899                                                   attr_name->an_name.bv_val; attr_name++ ) {
3900                                                   *ptr++ = ' ';
3901                                                   if ( attr_name->an_desc &&
3902                                                                       ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
3903                                                             ptr = lutil_strcopy( ptr, "undef:" );
3904                                                   }
3905                                                   ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val );
3906                                         }
3907                                         ber_bvarray_add( &c->rvalue_vals, &bv );
3908                               }
3909                               if ( !c->rvalue_vals )
3910                                         rc = 1;
3911                               break;
3912                     case PC_TEMP:
3913                               for (temp=qm->templates; temp; temp=temp->qmnext) {
3914                                         /* HEADS-UP: always print all;
3915                                          * if optional == 0, ignore */
3916                                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
3917                                                   " %d %ld %ld %ld %ld",
3918                                                   temp->attr_set_index,
3919                                                   temp->ttl,
3920                                                   temp->negttl,
3921                                                   temp->limitttl,
3922                                                   temp->ttr );
3923                                         bv.bv_len += temp->querystr.bv_len + 2;
3924                                         bv.bv_val = ch_malloc( bv.bv_len+1 );
3925                                         ptr = bv.bv_val;
3926                                         *ptr++ = '"';
3927                                         ptr = lutil_strcopy( ptr, temp->querystr.bv_val );
3928                                         *ptr++ = '"';
3929                                         strcpy( ptr, c->cr_msg );
3930                                         ber_bvarray_add( &c->rvalue_vals, &bv );
3931                               }
3932                               if ( !c->rvalue_vals )
3933                                         rc = 1;
3934                               break;
3935                     case PC_BIND:
3936                               for (temp=qm->templates; temp; temp=temp->qmnext) {
3937                                         if ( !temp->bindttr ) continue;
3938                                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
3939                                                   " %d %ld %s ",
3940                                                   temp->attr_set_index,
3941                                                   temp->bindttr,
3942                                                   ldap_pvt_scope2str( temp->bindscope ));
3943                                         bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4;
3944                                         bv.bv_val = ch_malloc( bv.bv_len + 1 );
3945                                         ptr = bv.bv_val;
3946                                         *ptr++ = '"';
3947                                         ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val );
3948                                         *ptr++ = '"';
3949                                         ptr = lutil_strcopy( ptr, c->cr_msg );
3950                                         *ptr++ = '"';
3951                                         ptr = lutil_strcopy( ptr, temp->bindbase.bv_val );
3952                                         *ptr++ = '"';
3953                                         *ptr = '\0';
3954                                         ber_bvarray_add( &c->rvalue_vals, &bv );
3955                               }
3956                               if ( !c->rvalue_vals )
3957                                         rc = 1;
3958                               break;
3959                     case PC_RESP:
3960                               if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
3961                                         BER_BVSTR( &bv, "head" );
3962                               } else {
3963                                         BER_BVSTR( &bv, "tail" );
3964                               }
3965                               value_add_one( &c->rvalue_vals, &bv );
3966                               break;
3967                     case PC_QUERIES:
3968                               c->value_int = cm->max_queries;
3969                               break;
3970                     case PC_OFFLINE:
3971                               c->value_int = (cm->cc_paused & PCACHE_CC_OFFLINE) != 0;
3972                               break;
3973                     }
3974                     return rc;
3975           } else if ( c->op == LDAP_MOD_DELETE ) {
3976                     rc = 1;
3977                     switch( c->type ) {
3978                     case PC_ATTR: /* FIXME */
3979                     case PC_TEMP:
3980                     case PC_BIND:
3981                               break;
3982                     case PC_OFFLINE:
3983                               cm->cc_paused &= ~PCACHE_CC_OFFLINE;
3984                               /* If there were cached queries when we went offline,
3985                                * restart the checker now.
3986                                */
3987                               if ( cm->num_cached_queries ) {
3988                                         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
3989                                         cm->cc_paused = 0;
3990                                         ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
3991                                         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
3992                               }
3993                               rc = 0;
3994                               break;
3995                     }
3996                     return rc;
3997           }
3998 
3999           switch( c->type ) {
4000           case PC_MAIN:
4001                     if ( cm->numattrsets > 0 ) {
4002                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive already provided" );
4003                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4004                               return( 1 );
4005                     }
4006 
4007                     if ( lutil_atoi( &cm->numattrsets, c->argv[3] ) != 0 ) {
4008                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse num attrsets=\"%s\" (arg #3)",
4009                                         c->argv[3] );
4010                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4011                               return( 1 );
4012                     }
4013                     if ( cm->numattrsets <= 0 ) {
4014                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be positive" );
4015                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4016                               return( 1 );
4017                     }
4018                     if ( cm->numattrsets > MAX_ATTR_SETS ) {
4019                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS );
4020                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4021                               return( 1 );
4022                     }
4023 
4024                     if ( !backend_db_init( c->argv[1], &cm->db, -1, NULL )) {
4025                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown backend type (arg #1)" );
4026                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4027                               return( 1 );
4028                     }
4029 
4030                     if ( lutil_atoi( &cm->max_entries, c->argv[2] ) != 0 ) {
4031                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse max entries=\"%s\" (arg #2)",
4032                                         c->argv[2] );
4033                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4034                               return( 1 );
4035                     }
4036                     if ( cm->max_entries <= 0 ) {
4037                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "max entries (arg #2) must be positive.\n" );
4038                               Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->cr_msg );
4039                               return( 1 );
4040                     }
4041 
4042                     if ( lutil_atoi( &cm->num_entries_limit, c->argv[4] ) != 0 ) {
4043                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse entry limit=\"%s\" (arg #4)",
4044                                         c->argv[4] );
4045                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4046                               return( 1 );
4047                     }
4048                     if ( cm->num_entries_limit <= 0 ) {
4049                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be positive" );
4050                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4051                               return( 1 );
4052                     }
4053                     if ( cm->num_entries_limit > cm->max_entries ) {
4054                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm->max_entries );
4055                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4056                               return( 1 );
4057                     }
4058 
4059                     if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
4060                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse period=\"%s\" (arg #5)",
4061                                         c->argv[5] );
4062                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4063                               return( 1 );
4064                     }
4065 
4066                     cm->cc_period = (time_t)t;
4067                     Debug( pcache_debug,
4068                                         "Total # of attribute sets to be cached = %d.\n",
4069                                         cm->numattrsets );
4070                     qm->attr_sets = ( struct attr_set * )ch_calloc( cm->numattrsets,
4071                                                             sizeof( struct attr_set ) );
4072                     break;
4073           case PC_ATTR:
4074                     if ( cm->numattrsets == 0 ) {
4075                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
4076                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4077                               return( 1 );
4078                     }
4079                     if ( lutil_atoi( &num, c->argv[1] ) != 0 ) {
4080                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse attrset #=\"%s\"",
4081                                         c->argv[1] );
4082                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4083                               return( 1 );
4084                     }
4085 
4086                     if ( num < 0 || num >= cm->numattrsets ) {
4087                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "attrset index %d out of bounds (must be %s%d)",
4088                                         num, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
4089                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4090                               return 1;
4091                     }
4092                     qm->attr_sets[num].flags |= PC_CONFIGURED;
4093                     if ( c->argc == 2 ) {
4094                               /* assume "1.1" */
4095                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
4096                                         "need an explicit attr in attrlist; use \"*\" to indicate all attrs" );
4097                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4098                               return 1;
4099 
4100                     } else if ( c->argc == 3 ) {
4101                               if ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
4102                                         qm->attr_sets[num].count = 1;
4103                                         qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
4104                                                   sizeof( AttributeName ) );
4105                                         BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
4106                                         break;
4107 
4108                               } else if ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
4109                                         qm->attr_sets[num].count = 1;
4110                                         qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
4111                                                   sizeof( AttributeName ) );
4112                                         BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
4113                                         break;
4114 
4115                               } else if ( strcmp( c->argv[2], LDAP_NO_ATTRS ) == 0 ) {
4116                                         break;
4117                               }
4118                               /* else: fallthru */
4119 
4120                     } else if ( c->argc == 4 ) {
4121                               if ( ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 )
4122                                         || ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) )
4123                               {
4124                                         qm->attr_sets[num].count = 2;
4125                                         qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 3,
4126                                                   sizeof( AttributeName ) );
4127                                         BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
4128                                         BER_BVSTR( &qm->attr_sets[num].attrs[1].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
4129                                         break;
4130                               }
4131                               /* else: fallthru */
4132                     }
4133 
4134                     if ( c->argc > 2 ) {
4135                               int all_user = 0, all_op = 0;
4136 
4137                               qm->attr_sets[num].count = c->argc - 2;
4138                               qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( c->argc - 1,
4139                                         sizeof( AttributeName ) );
4140                               attr_name = qm->attr_sets[num].attrs;
4141                               for ( i = 2; i < c->argc; i++ ) {
4142                                         attr_name->an_desc = NULL;
4143                                         if ( strcmp( c->argv[i], LDAP_NO_ATTRS ) == 0 ) {
4144                                                   snprintf( c->cr_msg, sizeof( c->cr_msg ),
4145                                                             "invalid attr #%d \"%s\" in attrlist",
4146                                                             i - 2, c->argv[i] );
4147                                                   Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4148                                                   ch_free( qm->attr_sets[num].attrs );
4149                                                   qm->attr_sets[num].attrs = NULL;
4150                                                   qm->attr_sets[num].count = 0;
4151                                                   return 1;
4152                                         }
4153                                         if ( strcmp( c->argv[i], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
4154                                                   all_user = 1;
4155                                                   BER_BVSTR( &attr_name->an_name, LDAP_ALL_USER_ATTRIBUTES );
4156                                         } else if ( strcmp( c->argv[i], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
4157                                                   all_op = 1;
4158                                                   BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
4159                                         } else {
4160                                                   if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) {
4161                                                             struct berval bv;
4162                                                             ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv );
4163                                                             attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL );
4164 
4165                                                   } else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
4166                                                             strcpy( c->cr_msg, text );
4167                                                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4168                                                             ch_free( qm->attr_sets[num].attrs );
4169                                                             qm->attr_sets[num].attrs = NULL;
4170                                                             qm->attr_sets[num].count = 0;
4171                                                             return 1;
4172                                                   }
4173                                                   attr_name->an_name = attr_name->an_desc->ad_cname;
4174                                         }
4175                                         attr_name->an_oc = NULL;
4176                                         attr_name->an_flags = 0;
4177                                         if ( attr_name->an_desc == slap_schema.si_ad_objectClass )
4178                                                   qm->attr_sets[num].flags |= PC_GOT_OC;
4179                                         attr_name++;
4180                                         BER_BVZERO( &attr_name->an_name );
4181                               }
4182 
4183                               /* warn if list contains both "*" and "+" */
4184                               if ( i > 4 && all_user && all_op ) {
4185                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4186                                                   "warning: attribute list contains \"*\" and \"+\"" );
4187                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4188                               }
4189                     }
4190                     break;
4191           case PC_TEMP:
4192                     if ( cm->numattrsets == 0 ) {
4193                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
4194                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4195                               return( 1 );
4196                     }
4197                     if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
4198                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template #=\"%s\"",
4199                                         c->argv[2] );
4200                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4201                               return( 1 );
4202                     }
4203 
4204                     if ( i < 0 || i >= cm->numattrsets ||
4205                               !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
4206                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "template index %d invalid (%s%d)",
4207                                         i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
4208                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4209                               return 1;
4210                     }
4211                     {
4212                               AttributeName *attrs;
4213                               int cnt;
4214                               cnt = template_attrs( c->argv[1], &qm->attr_sets[i], &attrs, &text );
4215                               if ( cnt < 0 ) {
4216                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
4217                                                   text );
4218                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4219                                         return 1;
4220                               }
4221                               temp = ch_calloc( 1, sizeof( QueryTemplate ));
4222                               temp->qmnext = qm->templates;
4223                               qm->templates = temp;
4224                               temp->t_attrs.attrs = attrs;
4225                               temp->t_attrs.count = cnt;
4226                     }
4227                     ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
4228                     temp->query = temp->query_last = NULL;
4229                     if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
4230                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
4231                                         "unable to parse template ttl=\"%s\"",
4232                                         c->argv[3] );
4233                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4234 pc_temp_fail:
4235                               ch_free( temp->t_attrs.attrs );
4236                               ch_free( temp );
4237                               return( 1 );
4238                     }
4239                     temp->ttl = (time_t)t;
4240                     temp->negttl = (time_t)0;
4241                     temp->limitttl = (time_t)0;
4242                     temp->ttr = (time_t)0;
4243                     switch ( c->argc ) {
4244                     case 7:
4245                               if ( lutil_parse_time( c->argv[6], &t ) != 0 ) {
4246                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4247                                                   "unable to parse template ttr=\"%s\"",
4248                                                   c->argv[6] );
4249                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4250                                         goto pc_temp_fail;
4251                               }
4252                               temp->ttr = (time_t)t;
4253                               /* fallthru */
4254 
4255                     case 6:
4256                               if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
4257                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4258                                                   "unable to parse template sizelimit ttl=\"%s\"",
4259                                                   c->argv[5] );
4260                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4261                                         goto pc_temp_fail;
4262                               }
4263                               temp->limitttl = (time_t)t;
4264                               /* fallthru */
4265 
4266                     case 5:
4267                               if ( lutil_parse_time( c->argv[4], &t ) != 0 ) {
4268                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4269                                                   "unable to parse template negative ttl=\"%s\"",
4270                                                   c->argv[4] );
4271                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4272                                         goto pc_temp_fail;
4273                               }
4274                               temp->negttl = (time_t)t;
4275                               break;
4276                     }
4277 
4278                     temp->no_of_queries = 0;
4279 
4280                     ber_str2bv( c->argv[1], 0, 1, &temp->querystr );
4281                     Debug( pcache_debug, "Template:\n" );
4282                     Debug( pcache_debug, "  query template: %s\n",
4283                                         temp->querystr.bv_val );
4284                     temp->attr_set_index = i;
4285                     qm->attr_sets[i].flags |= PC_REFERENCED;
4286                     temp->qtnext = qm->attr_sets[i].templates;
4287                     qm->attr_sets[i].templates = temp;
4288                     Debug( pcache_debug, "  attributes: \n" );
4289                     if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) {
4290                               for ( i=0; attrarray[i].an_name.bv_val; i++ )
4291                                         Debug( pcache_debug, "\t%s\n",
4292                                                   attrarray[i].an_name.bv_val );
4293                     }
4294                     break;
4295           case PC_BIND:
4296                     if ( !qm->templates ) {
4297                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcacheTemplate\" directive not provided yet" );
4298                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4299                               return( 1 );
4300                     }
4301                     if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
4302                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse Bind index #=\"%s\"",
4303                                         c->argv[2] );
4304                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4305                               return( 1 );
4306                     }
4307 
4308                     if ( i < 0 || i >= cm->numattrsets ||
4309                               !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
4310                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind index %d invalid (%s%d)",
4311                                         i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
4312                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4313                               return 1;
4314                     }
4315                     {         struct berval bv, tempbv;
4316                               AttributeDescription **descs;
4317                               int ndescs;
4318                               ber_str2bv( c->argv[1], 0, 0, &bv );
4319                               ndescs = ftemp_attrs( &bv, &tempbv, &descs, &text );
4320                               if ( ndescs < 0 ) {
4321                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
4322                                                   text );
4323                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4324                                         return 1;
4325                               }
4326                               for ( temp = qm->templates; temp; temp=temp->qmnext ) {
4327                                         if ( temp->attr_set_index == i && bvmatch( &tempbv,
4328                                                   &temp->querystr ))
4329                                                   break;
4330                               }
4331                               ch_free( tempbv.bv_val );
4332                               if ( !temp ) {
4333                                         ch_free( descs );
4334                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid",
4335                                                   c->argv[1], i );
4336                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4337                                         return 1;
4338                               }
4339                               ber_dupbv( &temp->bindftemp, &bv );
4340                               temp->bindfattrs = descs;
4341                               temp->bindnattrs = ndescs;
4342                     }
4343                     if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
4344                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
4345                                         "unable to parse bind ttr=\"%s\"",
4346                                         c->argv[3] );
4347                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4348 pc_bind_fail:
4349                               ch_free( temp->bindfattrs );
4350                               temp->bindfattrs = NULL;
4351                               ch_free( temp->bindftemp.bv_val );
4352                               BER_BVZERO( &temp->bindftemp );
4353                               return( 1 );
4354                     }
4355                     num = ldap_pvt_str2scope( c->argv[4] );
4356                     if ( num < 0 ) {
4357                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
4358                                         "unable to parse bind scope=\"%s\"",
4359                                         c->argv[4] );
4360                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4361                               goto pc_bind_fail;
4362                     }
4363                     {
4364                               struct berval dn, ndn;
4365                               ber_str2bv( c->argv[5], 0, 0, &dn );
4366                               rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
4367                               if ( rc ) {
4368                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4369                                                   "invalid bind baseDN=\"%s\"",
4370                                                   c->argv[5] );
4371                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4372                                         goto pc_bind_fail;
4373                               }
4374                               if ( temp->bindbase.bv_val )
4375                                         ch_free( temp->bindbase.bv_val );
4376                               temp->bindbase = ndn;
4377                     }
4378                     {
4379                               /* convert the template into dummy filter */
4380                               struct berval bv;
4381                               char *eq = temp->bindftemp.bv_val, *e2;
4382                               Filter *f;
4383                               i = 0;
4384                               while ((eq = strchr(eq, '=' ))) {
4385                                         eq++;
4386                                         if ( eq[0] == ')' )
4387                                                   i++;
4388                               }
4389                               bv.bv_len = temp->bindftemp.bv_len + i;
4390                               bv.bv_val = ch_malloc( bv.bv_len + 1 );
4391                               for ( e2 = bv.bv_val, eq = temp->bindftemp.bv_val;
4392                                         *eq; eq++ ) {
4393                                         if ( *eq == '=' ) {
4394                                                   *e2++ = '=';
4395                                                   if ( eq[1] == ')' )
4396                                                             *e2++ = '*';
4397                                         } else {
4398                                                   *e2++ = *eq;
4399                                         }
4400                               }
4401                               *e2 = '\0';
4402                               f = str2filter( bv.bv_val );
4403                               if ( !f ) {
4404                                         ch_free( bv.bv_val );
4405                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4406                                                   "unable to parse bindfilter=\"%s\"", bv.bv_val );
4407                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4408                                         ch_free( temp->bindbase.bv_val );
4409                                         BER_BVZERO( &temp->bindbase );
4410                                         goto pc_bind_fail;
4411                               }
4412                               if ( temp->bindfilter )
4413                                         filter_free( temp->bindfilter );
4414                               if ( temp->bindfilterstr.bv_val )
4415                                         ch_free( temp->bindfilterstr.bv_val );
4416                               temp->bindfilterstr = bv;
4417                               temp->bindfilter = f;
4418                     }
4419                     temp->bindttr = (time_t)t;
4420                     temp->bindscope = num;
4421                     cm->cache_binds = 1;
4422                     break;
4423 
4424           case PC_RESP:
4425                     if ( strcasecmp( c->argv[1], "head" ) == 0 ) {
4426                               cm->response_cb = PCACHE_RESPONSE_CB_HEAD;
4427 
4428                     } else if ( strcasecmp( c->argv[1], "tail" ) == 0 ) {
4429                               cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
4430 
4431                     } else {
4432                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown specifier" );
4433                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4434                               return 1;
4435                     }
4436                     break;
4437           case PC_QUERIES:
4438                     if ( c->value_int <= 0 ) {
4439                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "max queries must be positive" );
4440                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4441                               return( 1 );
4442                     }
4443                     cm->max_queries = c->value_int;
4444                     break;
4445           case PC_OFFLINE:
4446                     if ( c->value_int )
4447                               cm->cc_paused |= PCACHE_CC_OFFLINE;
4448                     else
4449                               cm->cc_paused &= ~PCACHE_CC_OFFLINE;
4450                     break;
4451           case PC_PRIVATE_DB:
4452                     if ( cm->db.be_private == NULL ) {
4453                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
4454                                         "private database must be defined before setting database specific options" );
4455                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4456                               return( 1 );
4457                     }
4458 
4459                     if ( cm->db.bd_info->bi_cf_ocs ) {
4460                               ConfigTable         *ct;
4461                               ConfigArgs          c2 = *c;
4462                               char                *argv0 = c->argv[ 0 ];
4463 
4464                               c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
4465 
4466                               ct = config_find_keyword( cm->db.bd_info->bi_cf_ocs->co_table, c );
4467                               if ( ct == NULL ) {
4468                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
4469                                                   "private database does not recognize specific option '%s'",
4470                                                   c->argv[ 0 ] );
4471                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4472                                         rc = 1;
4473 
4474                               } else {
4475                                         c->table = cm->db.bd_info->bi_cf_ocs->co_type;
4476                                         c->be = &cm->db;
4477                                         c->bi = c->be->bd_info;
4478 
4479                                         rc = config_add_vals( ct, c );
4480 
4481                                         c->bi = c2.bi;
4482                                         c->be = c2.be;
4483                                         c->table = c2.table;
4484                               }
4485 
4486                               c->argv[ 0 ] = argv0;
4487 
4488                     } else if ( cm->db.be_config != NULL ) {
4489                               char      *argv0 = c->argv[ 0 ];
4490 
4491                               c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
4492                               rc = cm->db.be_config( &cm->db, c->fname, c->lineno, c->argc, c->argv );
4493                               c->argv[ 0 ] = argv0;
4494 
4495                     } else {
4496                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
4497                                         "no means to set private database specific options" );
4498                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
4499                               return 1;
4500                     }
4501                     break;
4502           default:
4503                     rc = SLAP_CONF_UNKNOWN;
4504                     break;
4505           }
4506 
4507           return rc;
4508 }
4509 
4510 static int
pcache_db_config(BackendDB * be,const char * fname,int lineno,int argc,char ** argv)4511 pcache_db_config(
4512           BackendDB *be,
4513           const char          *fname,
4514           int                 lineno,
4515           int                 argc,
4516           char                **argv
4517 )
4518 {
4519           slap_overinst       *on = (slap_overinst *)be->bd_info;
4520           cache_manager*      cm = on->on_bi.bi_private;
4521 
4522           /* Something for the cache database? */
4523           if ( cm->db.bd_info && cm->db.bd_info->bi_db_config )
4524                     return cm->db.bd_info->bi_db_config( &cm->db, fname, lineno,
4525                               argc, argv );
4526           return SLAP_CONF_UNKNOWN;
4527 }
4528 
4529 static int
pcache_db_init(BackendDB * be,ConfigReply * cr)4530 pcache_db_init(
4531           BackendDB *be,
4532           ConfigReply *cr)
4533 {
4534           slap_overinst *on = (slap_overinst *)be->bd_info;
4535           cache_manager *cm;
4536           query_manager *qm;
4537 
4538           cm = (cache_manager *)ch_malloc(sizeof(cache_manager));
4539           on->on_bi.bi_private = cm;
4540 
4541           qm = (query_manager*)ch_malloc(sizeof(query_manager));
4542 
4543           cm->db = *be;
4544           cm->db.bd_info = NULL;
4545           SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
4546           cm->db.be_private = NULL;
4547           cm->db.bd_self = &cm->db;
4548           cm->db.be_pending_csn_list = NULL;
4549           cm->qm = qm;
4550           cm->numattrsets = 0;
4551           cm->num_entries_limit = 5;
4552           cm->num_cached_queries = 0;
4553           cm->max_entries = 0;
4554           cm->cur_entries = 0;
4555           cm->max_queries = 10000;
4556           cm->save_queries = 0;
4557           cm->check_cacheability = 0;
4558           cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
4559           cm->defer_db_open = 1;
4560           cm->cache_binds = 0;
4561           cm->cc_period = 1000;
4562           cm->cc_paused = 0;
4563           cm->cc_arg = NULL;
4564 #ifdef PCACHE_MONITOR
4565           cm->monitor_cb = NULL;
4566 #endif /* PCACHE_MONITOR */
4567 
4568           qm->attr_sets = NULL;
4569           qm->templates = NULL;
4570           qm->lru_top = NULL;
4571           qm->lru_bottom = NULL;
4572 
4573           qm->qcfunc = query_containment;
4574           qm->crfunc = cache_replacement;
4575           qm->addfunc = add_query;
4576           ldap_pvt_thread_mutex_init(&qm->lru_mutex);
4577 
4578           ldap_pvt_thread_mutex_init(&cm->cache_mutex);
4579 
4580 #ifndef PCACHE_MONITOR
4581           return 0;
4582 #else /* PCACHE_MONITOR */
4583           return pcache_monitor_db_init( be );
4584 #endif /* PCACHE_MONITOR */
4585 }
4586 
4587 static int
pcache_cachedquery_open_cb(Operation * op,SlapReply * rs)4588 pcache_cachedquery_open_cb( Operation *op, SlapReply *rs )
4589 {
4590           assert( op->o_tag == LDAP_REQ_SEARCH );
4591 
4592           if ( rs->sr_type == REP_SEARCH ) {
4593                     Attribute *a;
4594 
4595                     a = attr_find( rs->sr_entry->e_attrs, ad_cachedQueryURL );
4596                     if ( a != NULL ) {
4597                               BerVarray *valsp;
4598 
4599                               assert( a->a_nvals != NULL );
4600 
4601                               valsp = op->o_callback->sc_private;
4602                               assert( *valsp == NULL );
4603 
4604                               ber_bvarray_dup_x( valsp, a->a_nvals, op->o_tmpmemctx );
4605                     }
4606           }
4607 
4608           return 0;
4609 }
4610 
4611 static int
pcache_cachedquery_count_cb(Operation * op,SlapReply * rs)4612 pcache_cachedquery_count_cb( Operation *op, SlapReply *rs )
4613 {
4614           assert( op->o_tag == LDAP_REQ_SEARCH );
4615 
4616           if ( rs->sr_type == REP_SEARCH ) {
4617                     int       *countp = (int *)op->o_callback->sc_private;
4618 
4619                     (*countp)++;
4620           }
4621 
4622           return 0;
4623 }
4624 
4625 static int
pcache_db_open2(slap_overinst * on,ConfigReply * cr)4626 pcache_db_open2(
4627           slap_overinst *on,
4628           ConfigReply *cr )
4629 {
4630           cache_manager       *cm = on->on_bi.bi_private;
4631           query_manager*  qm = cm->qm;
4632           int rc;
4633 
4634           rc = backend_startup_one( &cm->db, cr );
4635           if ( rc == 0 ) {
4636                     cm->defer_db_open = 0;
4637           }
4638 
4639           /* There is no runqueue in TOOL mode */
4640           if (( slapMode & SLAP_SERVER_MODE ) && rc == 0 ) {
4641                     ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
4642                     ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period,
4643                               consistency_check, on,
4644                               "pcache_consistency", cm->db.be_suffix[0].bv_val );
4645                     ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
4646 
4647                     /* Cached database must have the rootdn */
4648                     if ( BER_BVISNULL( &cm->db.be_rootndn )
4649                                         || BER_BVISEMPTY( &cm->db.be_rootndn ) )
4650                     {
4651                               Debug( LDAP_DEBUG_ANY, "pcache_db_open(): "
4652                                         "underlying database of type \"%s\"\n"
4653                                         "    serving naming context \"%s\"\n"
4654                                         "    has no \"rootdn\", required by \"pcache\".\n",
4655                                         on->on_info->oi_orig->bi_type,
4656                                         cm->db.be_suffix[0].bv_val );
4657                               return 1;
4658                     }
4659 
4660                     if ( cm->save_queries ) {
4661                               void                *thrctx = ldap_pvt_thread_pool_context();
4662                               Connection          conn = { 0 };
4663                               OperationBuffer     opbuf;
4664                               Operation *op;
4665                               slap_callback       cb = { 0 };
4666                               SlapReply rs = { REP_RESULT };
4667                               BerVarray vals = NULL;
4668                               Filter              f = { 0 }, f2 = { 0 };
4669                               AttributeAssertion  ava = ATTRIBUTEASSERTION_INIT;
4670                               AttributeName       attrs[ 2 ] = {{{ 0 }}};
4671 
4672                               connection_fake_init2( &conn, &opbuf, thrctx, 0 );
4673                               op = &opbuf.ob_op;
4674 
4675                               op->o_bd = &cm->db;
4676 
4677                               op->o_tag = LDAP_REQ_SEARCH;
4678                               op->o_protocol = LDAP_VERSION3;
4679                               cb.sc_response = pcache_cachedquery_open_cb;
4680                               cb.sc_private = &vals;
4681                               op->o_callback = &cb;
4682                               op->o_time = slap_get_time();
4683                               op->o_do_not_cache = 1;
4684                               op->o_managedsait = SLAP_CONTROL_CRITICAL;
4685 
4686                               op->o_dn = cm->db.be_rootdn;
4687                               op->o_ndn = cm->db.be_rootndn;
4688                               op->o_req_dn = cm->db.be_suffix[ 0 ];
4689                               op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
4690 
4691                               op->ors_scope = LDAP_SCOPE_BASE;
4692                               op->ors_deref = LDAP_DEREF_NEVER;
4693                               op->ors_slimit = 1;
4694                               op->ors_tlimit = SLAP_NO_LIMIT;
4695                               op->ors_limit = NULL;
4696                               ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr );
4697                               f.f_choice = LDAP_FILTER_PRESENT;
4698                               f.f_desc = ad_cachedQueryURL;
4699                               op->ors_filter = &f;
4700                               attrs[ 0 ].an_desc = ad_cachedQueryURL;
4701                               attrs[ 0 ].an_name = ad_cachedQueryURL->ad_cname;
4702                               op->ors_attrs = attrs;
4703                               op->ors_attrsonly = 0;
4704 
4705                               rc = op->o_bd->be_search( op, &rs );
4706                               if ( rc == LDAP_SUCCESS && vals != NULL ) {
4707                                         int       i;
4708 
4709                                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
4710                                                   if ( url2query( vals[ i ].bv_val, op, qm ) == 0 ) {
4711                                                             cm->num_cached_queries++;
4712                                                   }
4713                                         }
4714 
4715                                         ber_bvarray_free_x( vals, op->o_tmpmemctx );
4716                               }
4717 
4718                               /* count cached entries */
4719                               f.f_choice = LDAP_FILTER_NOT;
4720                               f.f_not = &f2;
4721                               f2.f_choice = LDAP_FILTER_EQUALITY;
4722                               f2.f_ava = &ava;
4723                               f2.f_av_desc = slap_schema.si_ad_objectClass;
4724                               BER_BVSTR( &f2.f_av_value, "glue" );
4725                               ber_str2bv( "(!(objectClass=glue))", 0, 0, &op->ors_filterstr );
4726 
4727                               op->ors_slimit = SLAP_NO_LIMIT;
4728                               op->ors_scope = LDAP_SCOPE_SUBTREE;
4729                               op->ors_attrs = slap_anlist_no_attrs;
4730 
4731                               rs_reinit( &rs, REP_RESULT );
4732                               op->o_callback->sc_response = pcache_cachedquery_count_cb;
4733                               op->o_callback->sc_private = &rs.sr_nentries;
4734 
4735                               rc = op->o_bd->be_search( op, &rs );
4736 
4737                               cm->cur_entries = rs.sr_nentries;
4738 
4739                               /* ignore errors */
4740                               rc = 0;
4741                     }
4742           }
4743           return rc;
4744 }
4745 
4746 static int
pcache_db_open(BackendDB * be,ConfigReply * cr)4747 pcache_db_open(
4748           BackendDB *be,
4749           ConfigReply *cr )
4750 {
4751           slap_overinst       *on = (slap_overinst *)be->bd_info;
4752           cache_manager       *cm = on->on_bi.bi_private;
4753           query_manager*  qm = cm->qm;
4754           int                 i, ncf = 0, rf = 0, nrf = 0, rc = 0;
4755 
4756           /* check attr sets */
4757           for ( i = 0; i < cm->numattrsets; i++) {
4758                     if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
4759                               if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
4760                                         Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i );
4761                                         rf++;
4762 
4763                               } else {
4764                                         Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i );
4765                               }
4766                               ncf++;
4767 
4768                     } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
4769                               Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i );
4770                               nrf++;
4771                     }
4772           }
4773 
4774           if ( ncf || rf || nrf ) {
4775                     Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf );
4776                     Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf );
4777                     Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf );
4778 
4779                     if ( rf > 0 ) {
4780                               return 1;
4781                     }
4782           }
4783 
4784           /* need to inherit something from the original database... */
4785           cm->db.be_def_limit = be->be_def_limit;
4786           cm->db.be_limits = be->be_limits;
4787           cm->db.be_acl = be->be_acl;
4788           cm->db.be_dfltaccess = be->be_dfltaccess;
4789 
4790           if ( SLAP_DBMONITORING( be ) ) {
4791                     SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
4792 
4793           } else {
4794                     SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
4795           }
4796 
4797           if ( !cm->defer_db_open ) {
4798                     rc = pcache_db_open2( on, cr );
4799           }
4800 
4801 #ifdef PCACHE_MONITOR
4802           if ( rc == LDAP_SUCCESS ) {
4803                     rc = pcache_monitor_db_open( be );
4804           }
4805 #endif /* PCACHE_MONITOR */
4806 
4807           return rc;
4808 }
4809 
4810 static void
pcache_free_qbase(void * v)4811 pcache_free_qbase( void *v )
4812 {
4813           Qbase *qb = v;
4814           int i;
4815 
4816           for (i=0; i<3; i++)
4817                     ldap_tavl_free( qb->scopes[i], NULL );
4818           ch_free( qb );
4819 }
4820 
4821 static int
pcache_db_close(BackendDB * be,ConfigReply * cr)4822 pcache_db_close(
4823           BackendDB *be,
4824           ConfigReply *cr
4825 )
4826 {
4827           slap_overinst *on = (slap_overinst *)be->bd_info;
4828           cache_manager *cm = on->on_bi.bi_private;
4829           query_manager *qm = cm->qm;
4830           QueryTemplate *tm;
4831           int rc = 0;
4832 
4833           /* stop the thread ... */
4834           if ( cm->cc_arg ) {
4835                     ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
4836                     if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
4837                               ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
4838                     }
4839                     ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
4840                     ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
4841                     cm->cc_arg = NULL;
4842           }
4843 
4844           if ( cm->save_queries ) {
4845                     CachedQuery         *qc;
4846                     BerVarray vals = NULL;
4847 
4848                     void                *thrctx;
4849                     Connection          conn = { 0 };
4850                     OperationBuffer     opbuf;
4851                     Operation *op;
4852                     slap_callback       cb = { 0 };
4853 
4854                     SlapReply rs = { REP_RESULT };
4855                     Modifications       mod = {{ 0 }};
4856 
4857                     thrctx = ldap_pvt_thread_pool_context();
4858 
4859                     connection_fake_init2( &conn, &opbuf, thrctx, 0 );
4860                     op = &opbuf.ob_op;
4861 
4862                 mod.sml_numvals = 0;
4863                     if ( qm->templates != NULL ) {
4864                               for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
4865                                         for ( qc = tm->query; qc; qc = qc->next ) {
4866                                                   struct berval       bv;
4867 
4868                                                   if ( query2url( op, qc, &bv, 0 ) == 0 ) {
4869                                                             ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
4870                                                   mod.sml_numvals++;
4871                                                   }
4872                                         }
4873                               }
4874                     }
4875 
4876                     op->o_bd = &cm->db;
4877                     op->o_dn = cm->db.be_rootdn;
4878                     op->o_ndn = cm->db.be_rootndn;
4879 
4880                     op->o_tag = LDAP_REQ_MODIFY;
4881                     op->o_protocol = LDAP_VERSION3;
4882                     cb.sc_response = slap_null_cb;
4883                     op->o_callback = &cb;
4884                     op->o_time = slap_get_time();
4885                     op->o_do_not_cache = 1;
4886                     op->o_managedsait = SLAP_CONTROL_CRITICAL;
4887 
4888                     op->o_req_dn = op->o_bd->be_suffix[0];
4889                     op->o_req_ndn = op->o_bd->be_nsuffix[0];
4890 
4891                     mod.sml_op = LDAP_MOD_REPLACE;
4892                     mod.sml_flags = 0;
4893                     mod.sml_desc = ad_cachedQueryURL;
4894                     mod.sml_type = ad_cachedQueryURL->ad_cname;
4895                     mod.sml_values = vals;
4896                     mod.sml_nvalues = NULL;
4897                     mod.sml_next = NULL;
4898                     Debug( pcache_debug,
4899                               "%sSETTING CACHED QUERY URLS\n",
4900                               vals == NULL ? "RE" : "" );
4901 
4902                     op->orm_modlist = &mod;
4903 
4904                     op->o_bd->be_modify( op, &rs );
4905 
4906                     ber_bvarray_free_x( vals, op->o_tmpmemctx );
4907           }
4908 
4909           /* cleanup stuff inherited from the original database... */
4910           cm->db.be_limits = NULL;
4911           cm->db.be_acl = NULL;
4912 
4913           if ( cm->db.bd_info->bi_db_close ) {
4914                     rc = cm->db.bd_info->bi_db_close( &cm->db, NULL );
4915           }
4916 
4917 #ifdef PCACHE_MONITOR
4918           if ( rc == LDAP_SUCCESS ) {
4919                     rc = pcache_monitor_db_close( be );
4920           }
4921 #endif /* PCACHE_MONITOR */
4922 
4923           return rc;
4924 }
4925 
4926 static int
pcache_db_destroy(BackendDB * be,ConfigReply * cr)4927 pcache_db_destroy(
4928           BackendDB *be,
4929           ConfigReply *cr
4930 )
4931 {
4932           slap_overinst *on = (slap_overinst *)be->bd_info;
4933           cache_manager *cm = on->on_bi.bi_private;
4934           query_manager *qm = cm->qm;
4935           QueryTemplate *tm;
4936           int i;
4937 
4938           if ( cm->db.be_private != NULL ) {
4939                     backend_stopdown_one( &cm->db );
4940           }
4941 
4942           while ( (tm = qm->templates) != NULL ) {
4943                     CachedQuery *qc, *qn;
4944                     qm->templates = tm->qmnext;
4945                     for ( qc = tm->query; qc; qc = qn ) {
4946                               qn = qc->next;
4947                               free_query( qc );
4948                     }
4949                     ldap_avl_free( tm->qbase, pcache_free_qbase );
4950                     free( tm->querystr.bv_val );
4951                     free( tm->bindfattrs );
4952                     free( tm->bindftemp.bv_val );
4953                     free( tm->bindfilterstr.bv_val );
4954                     free( tm->bindbase.bv_val );
4955                     filter_free( tm->bindfilter );
4956                     ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock );
4957                     free( tm->t_attrs.attrs );
4958                     free( tm );
4959           }
4960 
4961           for ( i = 0; i < cm->numattrsets; i++ ) {
4962                     int j;
4963 
4964                     /* Account of LDAP_NO_ATTRS */
4965                     if ( !qm->attr_sets[i].count ) continue;
4966 
4967                     for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) {
4968                               if ( qm->attr_sets[i].attrs[j].an_desc &&
4969                                                   ( qm->attr_sets[i].attrs[j].an_desc->ad_flags &
4970                                                     SLAP_DESC_TEMPORARY ) ) {
4971                                         slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL );
4972                               }
4973                     }
4974                     free( qm->attr_sets[i].attrs );
4975           }
4976           free( qm->attr_sets );
4977           qm->attr_sets = NULL;
4978 
4979           ldap_pvt_thread_mutex_destroy( &qm->lru_mutex );
4980           ldap_pvt_thread_mutex_destroy( &cm->cache_mutex );
4981           free( qm );
4982           free( cm );
4983 
4984 #ifdef PCACHE_MONITOR
4985           pcache_monitor_db_destroy( be );
4986 #endif /* PCACHE_MONITOR */
4987 
4988           return 0;
4989 }
4990 
4991 #ifdef PCACHE_CONTROL_PRIVDB
4992 /*
4993         Control ::= SEQUENCE {
4994              controlType             LDAPOID,
4995              criticality             BOOLEAN DEFAULT FALSE,
4996              controlValue            OCTET STRING OPTIONAL }
4997 
4998         controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1
4999 
5000  * criticality must be TRUE; controlValue must be absent.
5001  */
5002 static int
parse_privdb_ctrl(Operation * op,SlapReply * rs,LDAPControl * ctrl)5003 parse_privdb_ctrl(
5004           Operation *op,
5005           SlapReply *rs,
5006           LDAPControl         *ctrl )
5007 {
5008           if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_NONE ) {
5009                     rs->sr_text = "privateDB control specified multiple times";
5010                     return LDAP_PROTOCOL_ERROR;
5011           }
5012 
5013           if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
5014                     rs->sr_text = "privateDB control value not absent";
5015                     return LDAP_PROTOCOL_ERROR;
5016           }
5017 
5018           if ( !ctrl->ldctl_iscritical ) {
5019                     rs->sr_text = "privateDB control criticality required";
5020                     return LDAP_PROTOCOL_ERROR;
5021           }
5022 
5023           op->o_ctrlflag[ privDB_cid ] = SLAP_CONTROL_CRITICAL;
5024 
5025           return LDAP_SUCCESS;
5026 }
5027 
5028 static char *extops[] = {
5029           LDAP_EXOP_MODIFY_PASSWD,
5030           NULL
5031 };
5032 #endif /* PCACHE_CONTROL_PRIVDB */
5033 
5034 static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD );
5035 #ifdef PCACHE_EXOP_QUERY_DELETE
5036 static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE );
5037 
5038 #define   LDAP_TAG_EXOP_QUERY_DELETE_BASE         ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0)
5039 #define   LDAP_TAG_EXOP_QUERY_DELETE_DN ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1)
5040 #define   LDAP_TAG_EXOP_QUERY_DELETE_UUID         ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2)
5041 
5042 /*
5043         ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
5044              requestName      [0] LDAPOID,
5045              requestValue     [1] OCTET STRING OPTIONAL }
5046 
5047         requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1
5048 
5049         requestValue ::= SEQUENCE { CHOICE {
5050                   baseDN           [0] LDAPDN
5051                   entryDN          [1] LDAPDN },
5052              queryID          [2] OCTET STRING (SIZE(16))
5053                   -- constrained to UUID }
5054 
5055  * Either baseDN or entryDN must be present, to allow database selection.
5056  *
5057  * 1. if baseDN and queryID are present, then the query corresponding
5058  *    to queryID is deleted;
5059  * 2. if baseDN is present and queryID is absent, then all queries
5060  *    are deleted;
5061  * 3. if entryDN is present and queryID is absent, then all queries
5062  *    corresponding to the queryID values present in entryDN are deleted;
5063  * 4. if entryDN and queryID are present, then all queries
5064  *    corresponding to the queryID values present in entryDN are deleted,
5065  *    but only if the value of queryID is contained in the entry;
5066  *
5067  * Currently, only 1, 3 and 4 are implemented.  2 can be obtained by either
5068  * recursively deleting the database (ldapdelete -r) with PRIVDB control,
5069  * or by removing the database files.
5070 
5071         ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
5072              COMPONENTS OF LDAPResult,
5073              responseName     [10] LDAPOID OPTIONAL,
5074              responseValue    [11] OCTET STRING OPTIONAL }
5075 
5076  * responseName and responseValue must be absent.
5077  */
5078 
5079 /*
5080  * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE
5081  *   or LDAP_TAG_EXOP_QUERY_DELETE_DN.
5082  * - if ndn != NULL, it is set to the normalized DN in the request
5083  *   corresponding to either the baseDN or the entryDN, according
5084  *   to *tagp; memory is malloc'ed on the Operation's slab, and must
5085  *   be freed by the caller.
5086  * - if uuid != NULL, it is set to point to the normalized UUID;
5087  *   memory is malloc'ed on the Operation's slab, and must
5088  *   be freed by the caller.
5089  */
5090 static int
pcache_parse_query_delete(struct berval * in,ber_tag_t * tagp,struct berval * ndn,struct berval * uuid,const char ** text,void * ctx)5091 pcache_parse_query_delete(
5092           struct berval       *in,
5093           ber_tag_t *tagp,
5094           struct berval       *ndn,
5095           struct berval       *uuid,
5096           const char          **text,
5097           void                *ctx )
5098 {
5099           int                           rc = LDAP_SUCCESS;
5100           ber_tag_t           tag;
5101           ber_len_t           len = -1;
5102           BerElementBuffer    berbuf;
5103           BerElement                    *ber = (BerElement *)&berbuf;
5104           struct berval                 reqdata = BER_BVNULL;
5105 
5106           *text = NULL;
5107 
5108           if ( ndn ) {
5109                     BER_BVZERO( ndn );
5110           }
5111 
5112           if ( uuid ) {
5113                     BER_BVZERO( uuid );
5114           }
5115 
5116           if ( in == NULL || in->bv_len == 0 ) {
5117                     *text = "empty request data field in queryDelete exop";
5118                     return LDAP_PROTOCOL_ERROR;
5119           }
5120 
5121           ber_dupbv_x( &reqdata, in, ctx );
5122 
5123           /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
5124           ber_init2( ber, &reqdata, 0 );
5125 
5126           tag = ber_scanf( ber, "{" /*}*/ );
5127 
5128           if ( tag == LBER_ERROR ) {
5129                     Debug( LDAP_DEBUG_TRACE,
5130                               "pcache_parse_query_delete: decoding error.\n" );
5131                     goto decoding_error;
5132           }
5133 
5134           tag = ber_peek_tag( ber, &len );
5135           if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE
5136                     || tag == LDAP_TAG_EXOP_QUERY_DELETE_DN )
5137           {
5138                     *tagp = tag;
5139 
5140                     if ( ndn != NULL ) {
5141                               struct berval       dn;
5142 
5143                               tag = ber_scanf( ber, "m", &dn );
5144                               if ( tag == LBER_ERROR ) {
5145                                         Debug( LDAP_DEBUG_TRACE,
5146                                                   "pcache_parse_query_delete: DN parse failed.\n" );
5147                                         goto decoding_error;
5148                               }
5149 
5150                               rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
5151                               if ( rc != LDAP_SUCCESS ) {
5152                                         *text = "invalid DN in queryDelete exop request data";
5153                                         goto done;
5154                               }
5155 
5156                     } else {
5157                               tag = ber_scanf( ber, "x" /* "m" */ );
5158                               if ( tag == LBER_DEFAULT ) {
5159                                         goto decoding_error;
5160                               }
5161                     }
5162 
5163                     tag = ber_peek_tag( ber, &len );
5164           }
5165 
5166           if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_UUID ) {
5167                     if ( uuid != NULL ) {
5168                               struct berval       bv;
5169                               char                uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
5170 
5171                               tag = ber_scanf( ber, "m", &bv );
5172                               if ( tag == LBER_ERROR ) {
5173                                         Debug( LDAP_DEBUG_TRACE,
5174                                                   "pcache_parse_query_delete: UUID parse failed.\n" );
5175                                         goto decoding_error;
5176                               }
5177 
5178                               if ( bv.bv_len != 16 ) {
5179                                         Debug( LDAP_DEBUG_TRACE,
5180                                                   "pcache_parse_query_delete: invalid UUID length %lu.\n",
5181                                                   (unsigned long)bv.bv_len );
5182                                         goto decoding_error;
5183                               }
5184 
5185                               rc = lutil_uuidstr_from_normalized(
5186                                         bv.bv_val, bv.bv_len,
5187                                         uuidbuf, sizeof( uuidbuf ) );
5188                               if ( rc == -1 ) {
5189                                         goto decoding_error;
5190                               }
5191                               ber_str2bv( uuidbuf, rc, 1, uuid );
5192                               rc = LDAP_SUCCESS;
5193 
5194                     } else {
5195                               tag = ber_skip_tag( ber, &len );
5196                               if ( tag == LBER_DEFAULT ) {
5197                                         goto decoding_error;
5198                               }
5199 
5200                               if ( len != 16 ) {
5201                                         Debug( LDAP_DEBUG_TRACE,
5202                                                   "pcache_parse_query_delete: invalid UUID length %lu.\n",
5203                                                   (unsigned long)len );
5204                                         goto decoding_error;
5205                               }
5206                     }
5207 
5208                     tag = ber_peek_tag( ber, &len );
5209           }
5210 
5211           if ( tag != LBER_DEFAULT || len != 0 ) {
5212 decoding_error:;
5213                     Debug( LDAP_DEBUG_TRACE,
5214                               "pcache_parse_query_delete: decoding error\n" );
5215                     rc = LDAP_PROTOCOL_ERROR;
5216                     *text = "queryDelete data decoding error";
5217 
5218 done:;
5219                     if ( ndn && !BER_BVISNULL( ndn ) ) {
5220                               slap_sl_free( ndn->bv_val, ctx );
5221                               BER_BVZERO( ndn );
5222                     }
5223 
5224                     if ( uuid && !BER_BVISNULL( uuid ) ) {
5225                               slap_sl_free( uuid->bv_val, ctx );
5226                               BER_BVZERO( uuid );
5227                     }
5228           }
5229 
5230           if ( !BER_BVISNULL( &reqdata ) ) {
5231                     ber_memfree_x( reqdata.bv_val, ctx );
5232           }
5233 
5234           return rc;
5235 }
5236 
5237 static int
pcache_exop_query_delete(Operation * op,SlapReply * rs)5238 pcache_exop_query_delete(
5239           Operation *op,
5240           SlapReply *rs )
5241 {
5242           BackendDB *bd = op->o_bd;
5243 
5244           struct berval       uuid = BER_BVNULL,
5245                               *uuidp = NULL;
5246           char                buf[ SLAP_TEXT_BUFLEN ];
5247           unsigned  len;
5248           ber_tag_t tag = LBER_DEFAULT;
5249 
5250           if ( LogTest( LDAP_DEBUG_STATS ) ) {
5251                     uuidp = &uuid;
5252           }
5253 
5254           rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
5255                     &tag, &op->o_req_ndn, uuidp,
5256                     &rs->sr_text, op->o_tmpmemctx );
5257           if ( rs->sr_err != LDAP_SUCCESS ) {
5258                     return rs->sr_err;
5259           }
5260 
5261           if ( LogTest( LDAP_DEBUG_STATS ) ) {
5262                     assert( !BER_BVISNULL( &op->o_req_ndn ) );
5263                     len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
5264 
5265                     if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) {
5266                               snprintf( &buf[ len ], sizeof( buf ) - len, " pcacheQueryId=\"%s\"", uuid.bv_val );
5267                     }
5268 
5269                     Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n",
5270                               op->o_log_prefix, buf );
5271           }
5272           op->o_req_dn = op->o_req_ndn;
5273 
5274           op->o_bd = select_backend( &op->o_req_ndn, 0 );
5275           if ( op->o_bd == NULL ) {
5276                     send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
5277                               "no global superior knowledge" );
5278           }
5279           rs->sr_err = backend_check_restrictions( op, rs,
5280                     (struct berval *)&pcache_exop_QUERY_DELETE );
5281           if ( rs->sr_err != LDAP_SUCCESS ) {
5282                     goto done;
5283           }
5284 
5285           if ( op->o_bd->be_extended == NULL ) {
5286                     send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
5287                               "backend does not support extended operations" );
5288                     goto done;
5289           }
5290 
5291           op->o_bd->be_extended( op, rs );
5292 
5293 done:;
5294           if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
5295                     op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
5296                     BER_BVZERO( &op->o_req_ndn );
5297                     BER_BVZERO( &op->o_req_dn );
5298           }
5299 
5300           if ( !BER_BVISNULL( &uuid ) ) {
5301                     op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
5302           }
5303 
5304           op->o_bd = bd;
5305 
5306         return rs->sr_err;
5307 }
5308 #endif /* PCACHE_EXOP_QUERY_DELETE */
5309 
5310 static int
pcache_op_extended(Operation * op,SlapReply * rs)5311 pcache_op_extended( Operation *op, SlapReply *rs )
5312 {
5313           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
5314           cache_manager       *cm = on->on_bi.bi_private;
5315 
5316 #ifdef PCACHE_CONTROL_PRIVDB
5317           if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
5318                     return pcache_op_privdb( op, rs );
5319           }
5320 #endif /* PCACHE_CONTROL_PRIVDB */
5321 
5322 #ifdef PCACHE_EXOP_QUERY_DELETE
5323           if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) {
5324                     struct berval       uuid = BER_BVNULL;
5325                     ber_tag_t tag = LBER_DEFAULT;
5326 
5327                     rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
5328                               &tag, NULL, &uuid, &rs->sr_text, op->o_tmpmemctx );
5329                     assert( rs->sr_err == LDAP_SUCCESS );
5330 
5331                     if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) {
5332                               /* remove all queries related to the selected entry */
5333                               rs->sr_err = pcache_remove_entry_queries_from_cache( op,
5334                                         cm, &op->o_req_ndn, &uuid );
5335 
5336                     } else if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE ) {
5337                               if ( !BER_BVISNULL( &uuid ) ) {
5338                                         /* remove the selected query */
5339                                         rs->sr_err = pcache_remove_query_from_cache( op,
5340                                                   cm, &uuid );
5341 
5342                               } else {
5343                                         /* TODO: remove all queries */
5344                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
5345                                         rs->sr_text = "deletion of all queries not implemented";
5346                               }
5347                     }
5348 
5349                     op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
5350                     return rs->sr_err;
5351           }
5352 #endif /* PCACHE_EXOP_QUERY_DELETE */
5353 
5354           /* We only care if we're configured for Bind caching */
5355           if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) &&
5356                     cm->cache_binds ) {
5357                     /* See if the local entry exists and has a password.
5358                      * It's too much work to find the matching query, so
5359                      * we just see if there's a hashed password to update.
5360                      */
5361                     Operation op2 = *op;
5362                     Entry *e = NULL;
5363                     int rc;
5364                     int doit = 0;
5365 
5366                     op2.o_bd = &cm->db;
5367                     op2.o_dn = op->o_bd->be_rootdn;
5368                     op2.o_ndn = op->o_bd->be_rootndn;
5369                     rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL,
5370                               slap_schema.si_ad_userPassword, 0, &e );
5371                     if ( rc == LDAP_SUCCESS && e ) {
5372                               /* See if a recognized password is hashed here */
5373                               Attribute *a = attr_find( e->e_attrs,
5374                                         slap_schema.si_ad_userPassword );
5375                               if ( a && a->a_vals[0].bv_val[0] == '{' &&
5376                                         lutil_passwd_scheme( a->a_vals[0].bv_val )) {
5377                                         doit = 1;
5378                               }
5379                               be_entry_release_r( &op2, e );
5380                     }
5381 
5382                     if ( doit ) {
5383                               rc = overlay_op_walk( op, rs, op_extended, on->on_info,
5384                                         on->on_next );
5385                               if ( rc == LDAP_SUCCESS ) {
5386                                         req_pwdexop_s *qpw = &op->oq_pwdexop;
5387 
5388                                         /* We don't care if it succeeds or not */
5389                                         pc_setpw( &op2, &qpw->rs_new, cm );
5390                               }
5391                               return rc;
5392                     }
5393           }
5394           return SLAP_CB_CONTINUE;
5395 }
5396 
5397 static int
pcache_entry_release(Operation * op,Entry * e,int rw)5398 pcache_entry_release( Operation  *op, Entry *e, int rw )
5399 {
5400           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
5401           cache_manager       *cm = on->on_bi.bi_private;
5402           BackendDB *db = op->o_bd;
5403           int rc;
5404 
5405           op->o_bd = &cm->db;
5406           rc = be_entry_release_rw( op, e, rw );
5407           op->o_bd = db;
5408           return rc;
5409 }
5410 
5411 #ifdef PCACHE_MONITOR
5412 
5413 static int
pcache_monitor_update(Operation * op,SlapReply * rs,Entry * e,void * priv)5414 pcache_monitor_update(
5415           Operation *op,
5416           SlapReply *rs,
5417           Entry               *e,
5418           void                *priv )
5419 {
5420           cache_manager       *cm = (cache_manager *) priv;
5421           query_manager       *qm = cm->qm;
5422 
5423           CachedQuery         *qc;
5424           BerVarray vals = NULL;
5425 
5426           attr_delete( &e->e_attrs, ad_cachedQueryURL );
5427           if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) )
5428                     && qm->templates != NULL )
5429           {
5430                     QueryTemplate *tm;
5431 
5432                     for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
5433                               for ( qc = tm->query; qc; qc = qc->next ) {
5434                                         struct berval       bv;
5435 
5436                                         if ( query2url( op, qc, &bv, 1 ) == 0 ) {
5437                                                   ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
5438                                         }
5439                               }
5440                     }
5441 
5442 
5443                     if ( vals != NULL ) {
5444                               attr_merge_normalize( e, ad_cachedQueryURL, vals, NULL );
5445                               ber_bvarray_free_x( vals, op->o_tmpmemctx );
5446                     }
5447           }
5448 
5449           {
5450                     Attribute *a;
5451                     char                buf[ SLAP_TEXT_BUFLEN ];
5452                     struct berval       bv;
5453 
5454                     /* number of cached queries */
5455                     a = attr_find( e->e_attrs, ad_numQueries );
5456                     assert( a != NULL );
5457 
5458                     bv.bv_val = buf;
5459                     bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries );
5460 
5461                     if ( a->a_nvals != a->a_vals ) {
5462                               ber_bvreplace( &a->a_nvals[ 0 ], &bv );
5463                     }
5464                     ber_bvreplace( &a->a_vals[ 0 ], &bv );
5465 
5466                     /* number of cached entries */
5467                     a = attr_find( e->e_attrs, ad_numEntries );
5468                     assert( a != NULL );
5469 
5470                     bv.bv_val = buf;
5471                     bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries );
5472 
5473                     if ( a->a_nvals != a->a_vals ) {
5474                               ber_bvreplace( &a->a_nvals[ 0 ], &bv );
5475                     }
5476                     ber_bvreplace( &a->a_vals[ 0 ], &bv );
5477           }
5478 
5479           return SLAP_CB_CONTINUE;
5480 }
5481 
5482 static int
pcache_monitor_free(Entry * e,void ** priv)5483 pcache_monitor_free(
5484           Entry               *e,
5485           void                **priv )
5486 {
5487           struct berval       values[ 2 ];
5488           Modification        mod = { 0 };
5489 
5490           const char          *text;
5491           char                textbuf[ SLAP_TEXT_BUFLEN ];
5492 
5493           int                 rc;
5494 
5495           /* NOTE: if slap_shutdown != 0, priv might have already been freed */
5496           *priv = NULL;
5497 
5498           /* Remove objectClass */
5499           mod.sm_op = LDAP_MOD_DELETE;
5500           mod.sm_desc = slap_schema.si_ad_objectClass;
5501           mod.sm_values = values;
5502           mod.sm_numvals = 1;
5503           values[ 0 ] = oc_olmPCache->soc_cname;
5504           BER_BVZERO( &values[ 1 ] );
5505 
5506           rc = modify_delete_values( e, &mod, 1, &text,
5507                     textbuf, sizeof( textbuf ) );
5508           /* don't care too much about return code... */
5509 
5510           /* remove attrs */
5511           mod.sm_values = NULL;
5512           mod.sm_desc = ad_cachedQueryURL;
5513           mod.sm_numvals = 0;
5514           rc = modify_delete_values( e, &mod, 1, &text,
5515                     textbuf, sizeof( textbuf ) );
5516           /* don't care too much about return code... */
5517 
5518           /* remove attrs */
5519           mod.sm_values = NULL;
5520           mod.sm_desc = ad_numQueries;
5521           mod.sm_numvals = 0;
5522           rc = modify_delete_values( e, &mod, 1, &text,
5523                     textbuf, sizeof( textbuf ) );
5524           /* don't care too much about return code... */
5525 
5526           /* remove attrs */
5527           mod.sm_values = NULL;
5528           mod.sm_desc = ad_numEntries;
5529           mod.sm_numvals = 0;
5530           rc = modify_delete_values( e, &mod, 1, &text,
5531                     textbuf, sizeof( textbuf ) );
5532           /* don't care too much about return code... */
5533 
5534           return SLAP_CB_CONTINUE;
5535 }
5536 
5537 /*
5538  * call from within pcache_initialize()
5539  */
5540 static int
pcache_monitor_initialize(void)5541 pcache_monitor_initialize( void )
5542 {
5543           static int          pcache_monitor_initialized = 0;
5544 
5545           if ( backend_info( "monitor" ) == NULL ) {
5546                     return -1;
5547           }
5548 
5549           if ( pcache_monitor_initialized++ ) {
5550                     return 0;
5551           }
5552 
5553           return 0;
5554 }
5555 
5556 static int
pcache_monitor_db_init(BackendDB * be)5557 pcache_monitor_db_init( BackendDB *be )
5558 {
5559           if ( pcache_monitor_initialize() == LDAP_SUCCESS ) {
5560                     SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
5561           }
5562 
5563           return 0;
5564 }
5565 
5566 static int
pcache_monitor_db_open(BackendDB * be)5567 pcache_monitor_db_open( BackendDB *be )
5568 {
5569           slap_overinst                 *on = (slap_overinst *)be->bd_info;
5570           cache_manager                 *cm = on->on_bi.bi_private;
5571           Attribute           *a, *next;
5572           monitor_callback_t  *cb = NULL;
5573           int                           rc = 0;
5574           BackendInfo                   *mi;
5575           monitor_extra_t               *mbe;
5576 
5577           if ( !SLAP_DBMONITORING( be ) ) {
5578                     return 0;
5579           }
5580 
5581           mi = backend_info( "monitor" );
5582           if ( !mi || !mi->bi_extra ) {
5583                     SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
5584                     return 0;
5585           }
5586           mbe = mi->bi_extra;
5587 
5588           /* don't bother if monitor is not configured */
5589           if ( !mbe->is_configured() ) {
5590                     static int warning = 0;
5591 
5592                     if ( warning++ == 0 ) {
5593                               Debug( LDAP_DEBUG_CONFIG, "pcache_monitor_db_open: "
5594                                         "monitoring disabled; "
5595                                         "configure monitor database to enable\n" );
5596                     }
5597 
5598                     return 0;
5599           }
5600 
5601           /* alloc as many as required (plus 1 for objectClass) */
5602           a = attrs_alloc( 1 + 2 );
5603           if ( a == NULL ) {
5604                     rc = 1;
5605                     goto cleanup;
5606           }
5607 
5608           a->a_desc = slap_schema.si_ad_objectClass;
5609           attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 );
5610           next = a->a_next;
5611 
5612           {
5613                     struct berval       bv = BER_BVC( "0" );
5614 
5615                     next->a_desc = ad_numQueries;
5616                     attr_valadd( next, &bv, NULL, 1 );
5617                     next = next->a_next;
5618 
5619                     next->a_desc = ad_numEntries;
5620                     attr_valadd( next, &bv, NULL, 1 );
5621                     next = next->a_next;
5622           }
5623 
5624           cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
5625           cb->mc_update = pcache_monitor_update;
5626           cb->mc_free = pcache_monitor_free;
5627           cb->mc_private = (void *)cm;
5628 
5629           /* make sure the database is registered; then add monitor attributes */
5630           BER_BVZERO( &cm->monitor_ndn );
5631           rc = mbe->register_overlay( be, on, &cm->monitor_ndn );
5632           if ( rc == 0 ) {
5633                     rc = mbe->register_entry_attrs( &cm->monitor_ndn, a, cb,
5634                               NULL, -1, NULL);
5635           }
5636 
5637 cleanup:;
5638           if ( rc != 0 ) {
5639                     if ( cb != NULL ) {
5640                               ch_free( cb );
5641                               cb = NULL;
5642                     }
5643 
5644                     if ( a != NULL ) {
5645                               attrs_free( a );
5646                               a = NULL;
5647                     }
5648           }
5649 
5650           /* store for cleanup */
5651           cm->monitor_cb = (void *)cb;
5652 
5653           /* we don't need to keep track of the attributes, because
5654            * mdb_monitor_free() takes care of everything */
5655           if ( a != NULL ) {
5656                     attrs_free( a );
5657           }
5658 
5659           return rc;
5660 }
5661 
5662 static int
pcache_monitor_db_close(BackendDB * be)5663 pcache_monitor_db_close( BackendDB *be )
5664 {
5665           slap_overinst *on = (slap_overinst *)be->bd_info;
5666           cache_manager *cm = on->on_bi.bi_private;
5667 
5668           if ( cm->monitor_cb != NULL ) {
5669                     BackendInfo                   *mi = backend_info( "monitor" );
5670                     monitor_extra_t               *mbe;
5671 
5672                     if ( mi && mi->bi_extra ) {
5673                               mbe = mi->bi_extra;
5674                               mbe->unregister_entry_callback( &cm->monitor_ndn,
5675                                         (monitor_callback_t *)cm->monitor_cb,
5676                                         NULL, 0, NULL );
5677                     }
5678           }
5679 
5680           return 0;
5681 }
5682 
5683 static int
pcache_monitor_db_destroy(BackendDB * be)5684 pcache_monitor_db_destroy( BackendDB *be )
5685 {
5686           return 0;
5687 }
5688 
5689 #endif /* PCACHE_MONITOR */
5690 
5691 static slap_overinst pcache;
5692 
5693 static char *obsolete_names[] = {
5694           "proxycache",
5695           NULL
5696 };
5697 
5698 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
5699 static
5700 #endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */
5701 int
pcache_initialize()5702 pcache_initialize()
5703 {
5704           int i, code;
5705           struct berval debugbv = BER_BVC("pcache");
5706           ConfigArgs c;
5707           char *argv[ 4 ];
5708 
5709         /* olcDatabaseDummy is defined in slapd, and Windows
5710            will not let us initialize a struct element with a data pointer
5711            from another library, so we have to initialize this element
5712            "by hand".  */
5713         pcocs[1].co_table = olcDatabaseDummy;
5714 
5715 
5716           code = slap_loglevel_get( &debugbv, &pcache_debug );
5717           if ( code ) {
5718                     return code;
5719           }
5720 
5721 #ifdef PCACHE_CONTROL_PRIVDB
5722           code = register_supported_control( PCACHE_CONTROL_PRIVDB,
5723                     SLAP_CTRL_BIND|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, extops,
5724                     parse_privdb_ctrl, &privDB_cid );
5725           if ( code != LDAP_SUCCESS ) {
5726                     Debug( LDAP_DEBUG_ANY,
5727                               "pcache_initialize: failed to register control %s (%d)\n",
5728                               PCACHE_CONTROL_PRIVDB, code );
5729                     return code;
5730           }
5731 #endif /* PCACHE_CONTROL_PRIVDB */
5732 
5733 #ifdef PCACHE_EXOP_QUERY_DELETE
5734           code = load_extop2( (struct berval *)&pcache_exop_QUERY_DELETE,
5735                     SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, pcache_exop_query_delete,
5736                     0 );
5737           if ( code != LDAP_SUCCESS ) {
5738                     Debug( LDAP_DEBUG_ANY,
5739                               "pcache_initialize: unable to register queryDelete exop: %d.\n",
5740                               code );
5741                     return code;
5742           }
5743 #endif /* PCACHE_EXOP_QUERY_DELETE */
5744 
5745           argv[ 0 ] = "back-mdb monitor";
5746           c.argv = argv;
5747           c.argc = 3;
5748           c.fname = argv[0];
5749 
5750           for ( i = 0; s_oid[ i ].name; i++ ) {
5751                     c.lineno = i;
5752                     argv[ 1 ] = s_oid[ i ].name;
5753                     argv[ 2 ] = s_oid[ i ].oid;
5754 
5755                     if ( parse_oidm( &c, 0, NULL ) != 0 ) {
5756                               Debug( LDAP_DEBUG_ANY, "pcache_initialize: "
5757                                         "unable to add objectIdentifier \"%s=%s\"\n",
5758                                         s_oid[ i ].name, s_oid[ i ].oid );
5759                               return 1;
5760                     }
5761           }
5762 
5763           for ( i = 0; s_ad[i].desc != NULL; i++ ) {
5764                     code = register_at( s_ad[i].desc, s_ad[i].adp, 0 );
5765                     if ( code ) {
5766                               Debug( LDAP_DEBUG_ANY,
5767                                         "pcache_initialize: register_at #%d failed\n", i );
5768                               return code;
5769                     }
5770                     (*s_ad[i].adp)->ad_type->sat_flags |= SLAP_AT_HIDE;
5771           }
5772 
5773           for ( i = 0; s_oc[i].desc != NULL; i++ ) {
5774                     code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 );
5775                     if ( code ) {
5776                               Debug( LDAP_DEBUG_ANY,
5777                                         "pcache_initialize: register_oc #%d failed\n", i );
5778                               return code;
5779                     }
5780                     (*s_oc[i].ocp)->soc_flags |= SLAP_OC_HIDE;
5781           }
5782 
5783           pcache.on_bi.bi_type = "pcache";
5784           pcache.on_bi.bi_obsolete_names = obsolete_names;
5785           pcache.on_bi.bi_db_init = pcache_db_init;
5786           pcache.on_bi.bi_db_config = pcache_db_config;
5787           pcache.on_bi.bi_db_open = pcache_db_open;
5788           pcache.on_bi.bi_db_close = pcache_db_close;
5789           pcache.on_bi.bi_db_destroy = pcache_db_destroy;
5790 
5791           pcache.on_bi.bi_op_search = pcache_op_search;
5792           pcache.on_bi.bi_op_bind = pcache_op_bind;
5793 #ifdef PCACHE_CONTROL_PRIVDB
5794           pcache.on_bi.bi_op_compare = pcache_op_privdb;
5795           pcache.on_bi.bi_op_modrdn = pcache_op_privdb;
5796           pcache.on_bi.bi_op_modify = pcache_op_privdb;
5797           pcache.on_bi.bi_op_add = pcache_op_privdb;
5798           pcache.on_bi.bi_op_delete = pcache_op_privdb;
5799 #endif /* PCACHE_CONTROL_PRIVDB */
5800           pcache.on_bi.bi_extended = pcache_op_extended;
5801 
5802           pcache.on_bi.bi_entry_release_rw = pcache_entry_release;
5803           pcache.on_bi.bi_chk_controls = pcache_chk_controls;
5804 
5805           pcache.on_bi.bi_cf_ocs = pcocs;
5806 
5807           code = config_register_schema( pccfg, pcocs );
5808           if ( code ) return code;
5809 
5810           return overlay_register( &pcache );
5811 }
5812 
5813 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
init_module(int argc,char * argv[])5814 int init_module(int argc, char *argv[]) {
5815           return pcache_initialize();
5816 }
5817 #endif
5818 
5819 #endif    /* defined(SLAPD_OVER_PROXYCACHE) */
5820