From: Howard Chu Date: Wed, 19 Aug 2009 03:00:33 +0000 (+0000) Subject: Bind caching, work in progress X-Git-Tag: ACLCHECK_0~291 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=3de89b4201cec0d4a6b03a0b8e1e9b3ec4a5cc95;p=openldap Bind caching, work in progress --- diff --git a/servers/slapd/overlays/pcache.c b/servers/slapd/overlays/pcache.c index 5601ede571..716cd74cf4 100644 --- a/servers/slapd/overlays/pcache.c +++ b/servers/slapd/overlays/pcache.c @@ -3,7 +3,7 @@ * * Copyright 2003-2009 The OpenLDAP Foundation. * Portions Copyright 2003 IBM Corporation. - * Portions Copyright 2003 Symas Corporation. + * Portions Copyright 2003-2009 Symas Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -83,6 +83,7 @@ typedef struct cached_query_s { struct query_template_s *qtemp; /* template of the query */ time_t expiry_time; /* time till the query is considered invalid */ time_t refresh_time; /* time till the query is refreshed */ + time_t bindref_time; /* time till the bind is refreshed */ unsigned long answerable_cnt; /* how many times it was answerable */ int refcnt; /* references since last refresh */ ldap_pvt_thread_mutex_t answerable_cnt_mutex; @@ -138,13 +139,18 @@ typedef struct query_template_s { CachedQuery* query_last; /* oldest query cached for the template */ ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */ struct berval querystr; /* Filter string corresponding to the QT */ + struct berval bindbase; /* base DN for Bind request */ + struct berval bindfilterstr; /* Filter string for Bind request */ + Filter *bindfilter; + int bindscope; int attr_set_index; /* determines the projected attributes */ int no_of_queries; /* Total number of queries in the template */ time_t ttl; /* TTL for the queries of this template */ time_t negttl; /* TTL for negative results */ time_t limitttl; /* TTL for sizelimit exceeding results */ time_t ttr; /* time to refresh */ + time_t bindttr; /* TTR for cached binds */ struct attr_set t_attrs; /* filter attrs + attr_set */ } QueryTemplate; @@ -208,6 +214,7 @@ typedef struct cache_manager_s { #define PCACHE_RESPONSE_CB_HEAD 0 #define PCACHE_RESPONSE_CB_TAIL 1 char defer_db_open; /* defer open for online add */ + char cache_binds; /* cache binds or just passthru */ time_t cc_period; /* interval between successive consistency checks (sec) */ #define PCACHE_CC_PAUSED 1 @@ -260,7 +267,7 @@ static struct { AttributeDescription **adp; } s_ad[] = { { "( PCacheAttributes:1 " - "NAME 'queryId' " + "NAME 'pcacheQueryID' " "DESC 'ID of query the entry belongs to, formatted as a UUID' " "EQUALITY octetStringMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} " @@ -268,7 +275,7 @@ static struct { "USAGE directoryOperation )", &ad_queryId }, { "( PCacheAttributes:2 " - "NAME 'cachedQueryURL' " + "NAME 'pcacheQueryURL' " "DESC 'URI describing a cached query' " "EQUALITY caseExactMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " @@ -301,10 +308,7 @@ static int filter2template( Operation *op, Filter *f, - struct berval *fstr, - AttributeName** filter_attrs, - int* filter_cnt, - int* filter_got_oc ); + struct berval *fstr ); static CachedQuery * add_query( @@ -404,6 +408,93 @@ query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock ) return 0; } +static int +template_attrs( char *template, struct attr_set *set, AttributeName **ret, + const char **text ) +{ + int got_oc = 0; + int alluser = 0; + int allop = 0; + int i; + int attr_cnt; + int t_cnt = 0; + struct berval bv; + char *p1, *p2; + AttributeDescription *ad; + AttributeName *attrs; + + p1 = template; + + *ret = NULL; + + attrs = ch_malloc(( set->count+1 )*sizeof(AttributeName)); + for ( i=0; i < set->count; i++ ) + attrs[i] = set->attrs[i]; + attr_cnt = i; + alluser = an_find( attrs, slap_bv_all_user_attrs ); + allop = an_find( attrs, slap_bv_all_operational_attrs ); + + for (;;) { + while ( *p1 == '(' || *p1 == '&' || *p1 == '|' ) p1++; + p2 = strchr( p1, '=' ); + if ( !p2 ) + break; + if ( p2[-1] == '<' || p2[-1] == '>' ) p2--; + bv.bv_val = p1; + bv.bv_len = p2 - p1; + ad = NULL; + i = slap_bv2ad( &bv, &ad, text ); + if ( i ) { + ch_free( attrs ); + return -1; + } + t_cnt++; + + if ( ad == slap_schema.si_ad_objectClass ) + got_oc = 1; + + if ( is_at_operational(ad->ad_type)) { + if ( allop ) { + goto bottom; + } + } else if ( alluser ) { + goto bottom; + } + if ( !ad_inlist( ad, attrs )) { + attrs = (AttributeName *)ch_realloc(attrs, + (attr_cnt + 2)*sizeof(AttributeName)); + + attrs[attr_cnt].an_desc = ad; + attrs[attr_cnt].an_name = ad->ad_cname; + attrs[attr_cnt].an_oc = NULL; + attrs[attr_cnt].an_flags = 0; + BER_BVZERO( &attrs[attr_cnt+1].an_name ); + attr_cnt++; + } + +bottom: + p1 = p2+2; + } + if ( !t_cnt ) { + *text = "couldn't parse template"; + return -1; + } + if ( !got_oc && !( set->flags & PC_GOT_OC )) { + attrs = (AttributeName *)ch_realloc(attrs, + (attr_cnt + 2)*sizeof(AttributeName)); + + ad = slap_schema.si_ad_objectClass; + attrs[attr_cnt].an_desc = ad; + attrs[attr_cnt].an_name = ad->ad_cname; + attrs[attr_cnt].an_oc = NULL; + attrs[attr_cnt].an_flags = 0; + BER_BVZERO( &attrs[attr_cnt+1].an_name ); + attr_cnt++; + } + *ret = attrs; + return attr_cnt; +} + /* * Turn an URL representing a formerly cached query into a cached query, * and try to cache it @@ -592,7 +683,7 @@ url2query( tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 ); tempstr.bv_len = 0; - if ( filter2template( op, query.filter, &tempstr, NULL, NULL, NULL ) ) { + if ( filter2template( op, query.filter, &tempstr ) ) { ch_free( tempstr.bv_val ); rc = -1; goto error; @@ -1590,6 +1681,7 @@ remove_query_data( op->ors_deref = LDAP_DEREF_NEVER; op->ors_slimit = SLAP_NO_LIMIT; op->ors_tlimit = SLAP_NO_LIMIT; + op->ors_limit = NULL; op->ors_filter = &filter; op->ors_filterstr.bv_val = filter_str; op->ors_filterstr.bv_len = strlen(filter_str); @@ -1654,10 +1746,7 @@ static int filter2template( Operation *op, Filter *f, - struct berval *fstr, - AttributeName** filter_attrs, - int* filter_cnt, - int* filter_got_oc ) + struct berval *fstr ) { AttributeDescription *ad; int len, ret; @@ -1730,8 +1819,7 @@ filter2template( fstr->bv_len++; for ( f = f->f_list; f != NULL; f = f->f_next ) { - rc = filter2template( op, f, fstr, filter_attrs, filter_cnt, - filter_got_oc ); + rc = filter2template( op, f, fstr ); if ( rc ) break; } fstr->bv_val[fstr->bv_len++] = ')'; @@ -1748,23 +1836,20 @@ filter2template( return -1; } - if ( filter_attrs != NULL ) { - *filter_attrs = (AttributeName *)op->o_tmprealloc(*filter_attrs, - (*filter_cnt + 2)*sizeof(AttributeName), op->o_tmpmemctx); - - (*filter_attrs)[*filter_cnt].an_desc = ad; - (*filter_attrs)[*filter_cnt].an_name = ad->ad_cname; - (*filter_attrs)[*filter_cnt].an_oc = NULL; - (*filter_attrs)[*filter_cnt].an_flags = 0; - BER_BVZERO( &(*filter_attrs)[*filter_cnt+1].an_name ); - (*filter_cnt)++; - if ( ad == slap_schema.si_ad_objectClass ) - *filter_got_oc = 1; - } - return 0; } +typedef struct bindinfo { + CachedQuery *bi_query; + struct berval *bi_querystr; + struct berval *bi_base; + Filter *bi_filter; + int bi_scope; + int bi_hashed; + int bi_didcb; + slap_callback bi_cb; +} bindinfo; + struct search_info { slap_overinst *on; Query query; @@ -1778,6 +1863,7 @@ struct search_info { int slimit_exceeded; pc_caching_reason_t caching_reason; Entry *head, *tail; + bindinfo *pbi; }; static void @@ -1896,6 +1982,7 @@ pcache_remove_entries_from_cache( op->ors_filter = &f; op->ors_slimit = 1; op->ors_tlimit = SLAP_NO_LIMIT; + op->ors_limit = NULL; attrs[ 0 ].an_desc = ad_queryId; attrs[ 0 ].an_name = ad_queryId->ad_cname; op->ors_attrs = attrs; @@ -2024,6 +2111,7 @@ pcache_remove_entry_queries_from_cache( op->ors_filter = &f; op->ors_slimit = 1; op->ors_tlimit = SLAP_NO_LIMIT; + op->ors_limit = NULL; attrs[ 0 ].an_desc = ad_queryId; attrs[ 0 ].an_name = ad_queryId->ad_cname; op->ors_attrs = attrs; @@ -2211,6 +2299,8 @@ over:; switch ( si->caching_reason ) { case PC_POSITIVE: cache_entries( op, rs, &qc->q_uuid ); + if ( si->pbi ) + si->pbi->bi_query = qc; break; case PC_SIZELIMIT: @@ -2306,68 +2396,6 @@ pcache_response( return SLAP_CB_CONTINUE; } -static int -add_filter_attrs( - Operation *op, - AttributeName** new_attrs, - struct attr_set *attrs, - AttributeName* filter_attrs, - int fattr_cnt, - int fattr_got_oc) -{ - int alluser = 0; - int allop = 0; - int i, j; - int count; - int addoc = 0; - - /* duplicate attrs */ - count = attrs->count + fattr_cnt; - if ( !fattr_got_oc && !(attrs->flags & PC_GOT_OC)) { - addoc = 1; - count++; - } - - *new_attrs = (AttributeName*)ch_calloc( count + 1, - sizeof(AttributeName) ); - for (i=0; icount; i++) { - (*new_attrs)[i].an_name = attrs->attrs[i].an_name; - (*new_attrs)[i].an_desc = attrs->attrs[i].an_desc; - } - BER_BVZERO( &(*new_attrs)[i].an_name ); - alluser = an_find( *new_attrs, slap_bv_all_user_attrs ); - allop = an_find( *new_attrs, slap_bv_all_operational_attrs ); - - j = i; - for ( i=0; iad_type) ) { - if ( allop ) { - continue; - } - } else if ( alluser ) { - continue; - } - (*new_attrs)[j].an_name = filter_attrs[i].an_name; - (*new_attrs)[j].an_desc = filter_attrs[i].an_desc; - (*new_attrs)[j].an_oc = NULL; - (*new_attrs)[j].an_flags = 0; - j++; - } - if ( addoc ) { - (*new_attrs)[j].an_name = slap_schema.si_ad_objectClass->ad_cname; - (*new_attrs)[j].an_desc = slap_schema.si_ad_objectClass; - (*new_attrs)[j].an_oc = NULL; - (*new_attrs)[j].an_flags = 0; - j++; - } - BER_BVZERO( &(*new_attrs)[j].an_name ); - - return j; -} - /* NOTE: this is a quick workaround to let pcache minimally interact * with pagedResults. A more articulated solutions would be to * perform the remote query without control and cache all results, @@ -2404,6 +2432,240 @@ pcache_chk_controls( return rs->sr_err; } +typedef struct bindcacheinfo { + slap_overinst *on; + CachedQuery *qc; +} bindcacheinfo; + +static int +pc_bind_save( Operation *op, SlapReply *rs ) +{ + if ( rs->sr_err == LDAP_SUCCESS ) { + bindcacheinfo *bci = op->o_callback->sc_private; + slap_overinst *on = bci->on; + cache_manager *cm = on->on_bi.bi_private; + struct berval vals[2]; + const char *text = NULL; + + BER_BVZERO( &vals[0] ); + BER_BVZERO( &vals[1] ); + slap_passwd_hash( &op->orb_cred, &vals[0], &text ); + if ( BER_BVISEMPTY( &vals[0] )) { + Debug( pcache_debug, "pcache_bind_save: hash failed %s\n", + text, 0, 0 ); + } else { + Operation op2 = *op; + Modifications mod; + SlapReply sr = { REP_RESULT }; + slap_callback cb = { 0, slap_null_cb, 0, 0 }; + + mod.sml_op = LDAP_MOD_REPLACE; + mod.sml_flags = 0; + mod.sml_desc = slap_schema.si_ad_userPassword; + mod.sml_type = mod.sml_desc->ad_cname; + mod.sml_values = vals; + mod.sml_nvalues = NULL; + mod.sml_numvals = 1; + mod.sml_next = NULL; + + op2.o_tag = LDAP_REQ_MODIFY; + op2.orm_modlist = &mod; + op2.o_bd = &cm->db; + op2.o_dn = op2.o_bd->be_rootdn; + op2.o_ndn = op2.o_bd->be_rootndn; + op2.o_callback = &cb; + if ( op2.o_bd->be_modify( &op2, &sr ) == LDAP_SUCCESS ) + bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr; + } + } + return SLAP_CB_CONTINUE; +} + +/* Check if the requested entry is from the cache and has a valid + * ttr and password hash + */ +static int +pc_bind_search( Operation *op, SlapReply *rs ) +{ + if ( rs->sr_type == REP_SEARCH ) { + bindinfo *pbi = op->o_callback->sc_private; + + /* We only care if this is an already cached result and we're + * below the refresh time. + */ + if ( pbi->bi_query && op->o_time < pbi->bi_query->bindref_time ) { + Attribute *a; + + /* See if a recognized password is hashed here */ + a = attr_find( rs->sr_entry->e_attrs, + slap_schema.si_ad_userPassword ); + if ( a && a->a_vals[0].bv_val[0] == '{' && + lutil_passwd_scheme( a->a_vals[0].bv_val )) + pbi->bi_hashed = 1; + } + } + return 0; +} + +/* We always want pc_bind_search to run after the search handlers */ +static int +pc_bind_resp( Operation *op, SlapReply *rs ) +{ + bindinfo *pbi = op->o_callback->sc_private; + if ( !pbi->bi_didcb ) { + slap_callback *sc = op->o_callback; + while ( sc && sc->sc_response != pcache_response ) + sc = sc->sc_next; + if ( !sc ) + sc = op->o_callback; + pbi->bi_cb.sc_next = sc->sc_next; + sc->sc_next = &pbi->bi_cb; + pbi->bi_didcb = 1; + } + return SLAP_CB_CONTINUE; +} + +static int +pcache_op_bind( + Operation *op, + SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + cache_manager *cm = on->on_bi.bi_private; + QueryTemplate *temp; + slap_callback cb = { 0 }, *sc; + bindinfo bi; + bindcacheinfo *bci; + Operation op2; + int is_priv = 0; + +#ifdef PCACHE_CONTROL_PRIVDB + if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) + is_priv = 1; +#endif + + /* Skip if we're not configured for Binds, and no priv control */ + if ( !cm->cache_binds && !is_priv ) + return SLAP_CB_CONTINUE; + + /* The cache DB isn't open yet */ + if ( cm->defer_db_open ) { + if ( !is_priv ) { + /* Just passthru for now */ + return SLAP_CB_CONTINUE; + } else { + send_ldap_error( op, rs, LDAP_UNAVAILABLE, + "pcachePrivDB: cacheDB not available" ); + return rs->sr_err; + } + } + + if ( is_priv ) { + int rc; + slap_callback *save_cb; + + /* FIXME: might be a little bit exaggerated... */ + if ( !be_isroot( op ) ) { + save_cb = op->o_callback; + op->o_callback = NULL; + send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, + "pcachePrivDB: operation not allowed" ); + op->o_callback = save_cb; + + return rs->sr_err; + } + + /* execute, if possible */ + if ( cm->db.be_bind != NULL ) { + op2 = *op; + op2.o_bd = &cm->db; + + rc = op2.o_bd->be_bind( &op2, rs ); + if ( rc == LDAP_SUCCESS ) { + op->o_conn->c_authz_cookie = cm->db.be_private; + } + } else { + /* otherwise fall back to error */ + save_cb = op->o_callback; + op->o_callback = NULL; + send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, + "operation not supported with pcachePrivDB control" ); + op->o_callback = save_cb; + } + + return rs->sr_err; + } + + /* First search for the target entry and userPassword. This will + * give us a cached_query to work with. Find a matching template + * to use for the search. + */ + for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) { + if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase )) + break; + } + /* Didn't find a suitable template, just passthru */ + if ( !temp ) + return SLAP_CB_CONTINUE; + + op2 = *op; + op2.o_dn = op->o_bd->be_rootdn; + op2.o_ndn = op->o_bd->be_rootndn; + + op2.o_tag = LDAP_REQ_SEARCH; + op2.ors_scope = LDAP_SCOPE_BASE; + op2.ors_deref = LDAP_DEREF_NEVER; + op2.ors_slimit = 1; + op2.ors_tlimit = SLAP_NO_LIMIT; + op2.ors_limit = NULL; + op2.ors_filter = temp->bindfilter; + op2.ors_filterstr = temp->bindfilterstr; + op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs; + op2.ors_attrsonly = 0; + + /* We want to invoke search at the same level of the stack + * as we're already at... + */ + bi.bi_query = NULL; + bi.bi_querystr = &temp->querystr; + bi.bi_hashed = 0; + bi.bi_didcb = 0; + bi.bi_cb.sc_response = pc_bind_search; + bi.bi_cb.sc_cleanup = NULL; + bi.bi_cb.sc_private = &bi; + cb.sc_private = &bi; + cb.sc_response = pc_bind_resp; + op2.o_callback = &cb; + overlay_op_walk( &op2, rs, op_search, on->on_info, on ); + + /* OK, just bind locally */ + if ( bi.bi_hashed ) { + BackendDB *be = op->o_bd; + op->o_bd = &cm->db; + + if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) { + op->o_conn->c_authz_cookie = cm->db.be_private; + } + op->o_bd = be; + return rs->sr_err; + } + + /* We have a cached query to work with */ + if ( bi.bi_query ) { + sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo), + op->o_tmpmemctx ); + sc->sc_response = pc_bind_save; + sc->sc_cleanup = NULL; + sc->sc_private = sc+1; + bci = sc->sc_private; + sc->sc_next = op->o_callback; + op->o_callback = sc; + bci->on = on; + bci->qc = bi.bi_query; + } + return SLAP_CB_CONTINUE; +} + #ifdef PCACHE_CONTROL_PRIVDB static int pcache_op_privdb( @@ -2482,16 +2744,13 @@ pcache_op_search( int i = -1; - AttributeName *filter_attrs = NULL; - Query query; QueryTemplate *qtemp = NULL; + bindinfo *pbi = NULL; int attr_set = -1; CachedQuery *answerable = NULL; int cacheable = 0; - int fattr_cnt=0; - int fattr_got_oc = 0; struct berval tempstr; @@ -2511,13 +2770,29 @@ pcache_op_search( /* pickup runtime ACL changes */ cm->db.be_acl = op->o_bd->be_acl; - tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1, op->o_tmpmemctx ); - tempstr.bv_len = 0; - if ( filter2template( op, op->ors_filter, &tempstr, &filter_attrs, - &fattr_cnt, &fattr_got_oc )) { - op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx ); - return SLAP_CB_CONTINUE; + /* See if we're processing a Bind request */ + slap_callback *cb = op->o_callback; + + for ( ; cb; cb=cb->sc_next ) { + if ( cb->sc_response == pc_bind_resp ) { + pbi = cb->sc_private; + break; + } + } + } + + if ( pbi ) { + tempstr = *pbi->bi_querystr; + } else { + tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1, + op->o_tmpmemctx ); + tempstr.bv_len = 0; + if ( filter2template( op, op->ors_filter, &tempstr )) + { + op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx ); + return SLAP_CB_CONTINUE; + } } Debug( pcache_debug, "query template of incoming query = %s\n", @@ -2550,11 +2825,12 @@ pcache_op_search( break; } } - op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx ); + if ( !pbi ) + op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx ); + if (answerable) { BackendDB *save_bd = op->o_bd; - slap_callback *save_cb = op->o_callback; ldap_pvt_thread_mutex_lock( &answerable->answerable_cnt_mutex ); answerable->answerable_cnt++; @@ -2565,27 +2841,31 @@ pcache_op_search( answerable->answerable_cnt, 0, 0 ); ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex ); - op->o_tmpfree( filter_attrs, op->o_tmpmemctx ); ldap_pvt_thread_rdwr_rlock(&answerable->rwlock); if ( BER_BVISNULL( &answerable->q_uuid )) { /* No entries cached, just an empty result set */ i = rs->sr_err = 0; send_ldap_result( op, rs ); } else { + /* Let Bind know we used a cached query */ + if ( pbi ) + pbi->bi_query = answerable; + op->o_bd = &cm->db; +#if 0 if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) { /* The cached entry was already processed by any * other overlays, so don't let it get processed again. */ op->o_callback = NULL; } +#endif i = cm->db.bd_info->bi_op_search( op, rs ); } ldap_pvt_thread_rdwr_runlock(&answerable->rwlock); /* locked by qtemp->qcfunc (query_containment) */ ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock); op->o_bd = save_bd; - op->o_callback = save_cb; return i; } @@ -2606,14 +2886,6 @@ pcache_op_search( Debug( pcache_debug, "QUERY CACHEABLE\n", 0, 0, 0 ); query.filter = filter_dup(op->ors_filter, NULL); - ldap_pvt_thread_rdwr_wlock(&qtemp->t_rwlock); - if ( !qtemp->t_attrs.count ) { - qtemp->t_attrs.count = add_filter_attrs(op, - &qtemp->t_attrs.attrs, - &qm->attr_sets[attr_set], - filter_attrs, fattr_cnt, fattr_got_oc); - } - ldap_pvt_thread_rdwr_wunlock(&qtemp->t_rwlock); cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx ); cb->sc_response = pcache_response; @@ -2637,6 +2909,7 @@ pcache_op_search( si->tail = NULL; si->swap_saved_attrs = 1; si->save_attrs = op->ors_attrs; + si->pbi = pbi; op->ors_attrs = qtemp->t_attrs.attrs; @@ -2660,8 +2933,6 @@ pcache_op_search( 0, 0, 0); } - op->o_tmpfree( filter_attrs, op->o_tmpmemctx ); - return SLAP_CB_CONTINUE; } @@ -2899,6 +3170,7 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query, op->ors_scope = query->scope; op->ors_slimit = SLAP_NO_LIMIT; op->ors_tlimit = SLAP_NO_LIMIT; + op->ors_limit = NULL; op->ors_filter = query->filter; filter2bv_x( op, query->filter, &op->ors_filterstr ); op->ors_attrs = query->qtemp->t_attrs.attrs; @@ -3106,6 +3378,7 @@ enum { PC_RESP, PC_QUERIES, PC_OFFLINE, + PC_BIND, PC_PRIVATE_DB }; @@ -3158,6 +3431,11 @@ static ConfigTable pccfg[] = { "( OLcfgOvAt:2.8 NAME 'olcPcacheOffline' " "DESC 'Set cache to offline mode and disable expiration' " "SYNTAX OMsBoolean )", NULL, NULL }, + { "pcacheBind", "filter> rvalue_vals ) rc = 1; break; + case PC_BIND: + for (temp=qm->templates; temp; temp=temp->qmnext) { + if ( !temp->bindttr ) continue; + bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), + " %d %ld %s ", + temp->attr_set_index, + temp->bindttr, + ldap_pvt_scope2str( temp->bindscope )); + bv.bv_len += temp->bindbase.bv_len + temp->querystr.bv_len + 3; + bv.bv_val = ch_malloc( bv.bv_len+1 ); + ptr = bv.bv_val; + *ptr++ = '"'; + ptr = lutil_strcopy( ptr, temp->querystr.bv_val ); + *ptr++ = '"'; + ptr = lutil_strcopy( ptr, c->cr_msg ); + *ptr++ = '"'; + ptr = lutil_strcopy( ptr, temp->bindbase.bv_val ); + *ptr++ = '"'; + *ptr = '\0'; + ber_bvarray_add( &c->rvalue_vals, &bv ); + } + if ( !c->rvalue_vals ) + rc = 1; + break; case PC_RESP: if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) { BER_BVSTR( &bv, "head" ); @@ -3355,6 +3657,7 @@ pc_cf_gen( ConfigArgs *c ) switch( c->type ) { case PC_ATTR: /* FIXME */ case PC_TEMP: + case PC_BIND: break; case PC_OFFLINE: cm->cc_paused &= ~PCACHE_CC_OFFLINE; @@ -3376,7 +3679,7 @@ pc_cf_gen( ConfigArgs *c ) switch( c->type ) { case PC_MAIN: if ( cm->numattrsets > 0 ) { - snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"proxycache\" directive already provided" ); + snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive already provided" ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); return( 1 ); } @@ -3449,7 +3752,7 @@ pc_cf_gen( ConfigArgs *c ) break; case PC_ATTR: if ( cm->numattrsets == 0 ) { - snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"proxycache\" directive not provided yet" ); + snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); return( 1 ); } @@ -3562,7 +3865,7 @@ pc_cf_gen( ConfigArgs *c ) break; case PC_TEMP: if ( cm->numattrsets == 0 ) { - snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"proxycache\" directive not provided yet" ); + snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); return( 1 ); } @@ -3580,9 +3883,22 @@ pc_cf_gen( ConfigArgs *c ) Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); return 1; } - temp = ch_calloc( 1, sizeof( QueryTemplate )); - temp->qmnext = qm->templates; - qm->templates = temp; + { + AttributeName *attrs; + int cnt; + cnt = template_attrs( c->argv[1], &qm->attr_sets[i], &attrs, &text ); + if ( cnt < 0 ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s", + text ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return 1; + } + temp = ch_calloc( 1, sizeof( QueryTemplate )); + temp->qmnext = qm->templates; + qm->templates = temp; + temp->t_attrs.attrs = attrs; + temp->t_attrs.count = cnt; + } ldap_pvt_thread_rdwr_init( &temp->t_rwlock ); temp->query = temp->query_last = NULL; if ( lutil_parse_time( c->argv[3], &t ) != 0 ) { @@ -3590,6 +3906,9 @@ pc_cf_gen( ConfigArgs *c ) "unable to parse template ttl=\"%s\"", c->argv[3] ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); +pc_temp_fail: + ch_free( temp->t_attrs.attrs ); + ch_free( temp ); return( 1 ); } temp->ttl = (time_t)t; @@ -3603,7 +3922,7 @@ pc_cf_gen( ConfigArgs *c ) "unable to parse template ttr=\"%s\"", c->argv[6] ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); - return( 1 ); + goto pc_temp_fail; } temp->ttr = (time_t)t; /* fallthru */ @@ -3614,7 +3933,7 @@ pc_cf_gen( ConfigArgs *c ) "unable to parse template sizelimit ttl=\"%s\"", c->argv[5] ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); - return( 1 ); + goto pc_temp_fail; } temp->limitttl = (time_t)t; /* fallthru */ @@ -3625,7 +3944,7 @@ pc_cf_gen( ConfigArgs *c ) "unable to parse template negative ttl=\"%s\"", c->argv[4] ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); - return( 1 ); + goto pc_temp_fail; } temp->negttl = (time_t)t; break; @@ -3648,6 +3967,112 @@ pc_cf_gen( ConfigArgs *c ) attrarray[i].an_name.bv_val, 0, 0 ); } break; + case PC_BIND: + if ( !qm->templates ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcacheTemplate\" directive not provided yet" ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return( 1 ); + } + if ( lutil_atoi( &i, c->argv[2] ) != 0 ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse Bind index #=\"%s\"", + c->argv[2] ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return( 1 ); + } + + if ( i < 0 || i >= cm->numattrsets || + !(qm->attr_sets[i].flags & PC_CONFIGURED )) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind index %d invalid (%s%d)", + i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return 1; + } + { struct berval bv; + ber_str2bv( c->argv[1], 0, 0, &bv ); + for ( temp = qm->templates; temp; temp=temp->qmnext ) { + if ( temp->attr_set_index == i && bvmatch( &bv, + &temp->querystr )) + break; + } + if ( !temp ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid", + c->argv[1], i ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return 1; + } + } + if ( lutil_parse_time( c->argv[3], &t ) != 0 ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "unable to parse bind ttr=\"%s\"", + c->argv[3] ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return( 1 ); + } + i = ldap_pvt_str2scope( c->argv[4] ); + if ( i < 0 ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "unable to parse bind scope=\"%s\"", + c->argv[4] ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return( 1 ); + } + { + struct berval dn, ndn; + ber_str2bv( c->argv[5], 0, 0, &dn ); + rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ); + if ( rc ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "invalid bind baseDN=\"%s\"", + c->argv[5] ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return( 1 ); + } + if ( temp->bindbase.bv_val ) + ch_free( temp->bindbase.bv_val ); + temp->bindbase = ndn; + } + { + /* convert the template into all presence filters */ + struct berval bv; + char *eq = temp->querystr.bv_val, *e2; + Filter *f; + i = 0; + while ((eq = strchr(eq, '=' ))) { + eq++; + i++; + } + bv.bv_len = temp->querystr.bv_len + i; + bv.bv_val = ch_malloc( bv.bv_len + 1 ); + for ( e2 = bv.bv_val, eq = temp->querystr.bv_val; + *eq; eq++ ) { + if ( *eq == '=' ) { + *e2++ = '='; + *e2++ = '*'; + } else { + *e2++ = *eq; + } + } + *e2 = '\0'; + f = str2filter( bv.bv_val ); + if ( !f ) { + ch_free( bv.bv_val ); + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "unable to parse bindfilter=\"%s\"", bv.bv_val ); + Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); + return( 1 ); + } + if ( temp->bindfilter ) + filter_free( temp->bindfilter ); + if ( temp->bindfilterstr.bv_val ) + ch_free( temp->bindfilterstr.bv_val ); + temp->bindfilterstr = bv; + temp->bindfilter = f; + } + temp->bindttr = (time_t)t; + temp->bindscope = i; + cm->cache_binds = 1; + break; + case PC_RESP: if ( strcasecmp( c->argv[1], "head" ) == 0 ) { cm->response_cb = PCACHE_RESPONSE_CB_HEAD; @@ -3688,7 +4113,7 @@ pc_cf_gen( ConfigArgs *c ) ConfigArgs c2 = *c; char *argv0 = c->argv[ 0 ]; - c->argv[ 0 ] = &argv0[ STRLENOF( "proxycache-" ) ]; + c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ]; ct = config_find_keyword( cm->db.bd_info->bi_cf_ocs->co_table, c ); if ( ct == NULL ) { @@ -3715,7 +4140,7 @@ pc_cf_gen( ConfigArgs *c ) } else if ( cm->db.be_config != NULL ) { char *argv0 = c->argv[ 0 ]; - c->argv[ 0 ] = &argv0[ STRLENOF( "proxycache-" ) ]; + c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ]; rc = cm->db.be_config( &cm->db, c->fname, c->lineno, c->argc, c->argv ); c->argv[ 0 ] = argv0; @@ -3872,7 +4297,7 @@ pcache_db_open2( Debug( LDAP_DEBUG_ANY, "pcache_db_open(): " "underlying database of type \"%s\"\n" " serving naming context \"%s\"\n" - " has no \"rootdn\", required by \"proxycache\".\n", + " has no \"rootdn\", required by \"pcache\".\n", on->on_info->oi_orig->bi_type, cm->db.be_suffix[0].bv_val, 0 ); return 1; @@ -3913,6 +4338,7 @@ pcache_db_open2( op->ors_deref = LDAP_DEREF_NEVER; op->ors_slimit = 1; op->ors_tlimit = SLAP_NO_LIMIT; + op->ors_limit = NULL; ber_str2bv( "(cachedQueryURL=*)", 0, 0, &op->ors_filterstr ); f.f_choice = LDAP_FILTER_PRESENT; f.f_desc = ad_cachedQueryURL; @@ -4868,8 +5294,8 @@ pcache_initialize() pcache.on_bi.bi_db_destroy = pcache_db_destroy; pcache.on_bi.bi_op_search = pcache_op_search; + pcache.on_bi.bi_op_bind = pcache_op_bind; #ifdef PCACHE_CONTROL_PRIVDB - pcache.on_bi.bi_op_bind = pcache_op_privdb; pcache.on_bi.bi_op_compare = pcache_op_privdb; pcache.on_bi.bi_op_modrdn = pcache_op_privdb; pcache.on_bi.bi_op_modify = pcache_op_privdb;