]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/refint.c
Happy New Year
[openldap] / servers / slapd / overlays / refint.c
1 /* refint.c - referential integrity module */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004-2018 The OpenLDAP Foundation.
6  * Portions Copyright 2004 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Symas Corp. for inclusion in
19  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20  */
21
22 #include "portable.h"
23
24 /* This module maintains referential integrity for a set of
25  * DN-valued attributes by searching for all references to a given
26  * DN whenever the DN is changed or its entry is deleted, and making
27  * the appropriate update.
28  *
29  * Updates are performed using the database rootdn in a separate task
30  * to allow the original operation to complete immediately.
31  */
32
33 #ifdef SLAPD_OVER_REFINT
34
35 #include <stdio.h>
36
37 #include <ac/string.h>
38 #include <ac/socket.h>
39
40 #include "slap.h"
41 #include "config.h"
42 #include "ldap_rq.h"
43
44 static slap_overinst refint;
45
46 /* The DN to use in the ModifiersName for all refint updates */
47 static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
48 static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay");
49
50 typedef struct refint_attrs_s {
51         struct refint_attrs_s   *next;
52         AttributeDescription    *attr;
53         BerVarray               old_vals;
54         BerVarray               old_nvals;
55         BerVarray               new_vals;
56         BerVarray               new_nvals;
57         int                             ra_numvals;
58         int                             dont_empty;
59 } refint_attrs;
60
61 typedef struct dependents_s {
62         struct dependents_s *next;
63         BerValue dn;                            /* target dn */
64         BerValue ndn;
65         refint_attrs *attrs;
66 } dependent_data;
67
68 typedef struct refint_q {
69         struct refint_q *next;
70         struct refint_data_s *rdata;
71         dependent_data *attrs;          /* entries and attrs returned from callback */
72         BackendDB *db;
73         BerValue olddn;
74         BerValue oldndn;
75         BerValue newdn;
76         BerValue newndn;
77         int do_sub;
78 } refint_q;
79
80 typedef struct refint_data_s {
81         struct refint_attrs_s *attrs;   /* list of known attrs */
82         BerValue dn;                            /* basedn in parent, */
83         BerValue nothing;                       /* the nothing value, if needed */
84         BerValue nnothing;                      /* normalized nothingness */
85         BerValue refint_dn;                     /* modifier's name */
86         BerValue refint_ndn;                    /* normalized modifier's name */
87         struct re_s *qtask;
88         refint_q *qhead;
89         refint_q *qtail;
90         BackendDB *db;
91         ldap_pvt_thread_mutex_t qmutex;
92 } refint_data;
93
94 typedef struct refint_pre_s {
95         slap_overinst *on;
96         int do_sub;
97 } refint_pre;
98
99 #define RUNQ_INTERVAL   36000   /* a long time */
100
101 static MatchingRule     *mr_dnSubtreeMatch;
102
103 enum {
104         REFINT_ATTRS = 1,
105         REFINT_NOTHING,
106         REFINT_MODIFIERSNAME
107 };
108
109 static ConfigDriver refint_cf_gen;
110
111 static ConfigTable refintcfg[] = {
112         { "refint_attributes", "attribute...", 2, 0, 0,
113           ARG_MAGIC|REFINT_ATTRS, refint_cf_gen,
114           "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' "
115           "DESC 'Attributes for referential integrity' "
116           "EQUALITY caseIgnoreMatch "
117           "SYNTAX OMsDirectoryString )", NULL, NULL },
118         { "refint_nothing", "string", 2, 2, 0,
119           ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen,
120           "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' "
121           "DESC 'Replacement DN to supply when needed' "
122           "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
123         { "refint_modifiersName", "DN", 2, 2, 0,
124           ARG_DN|ARG_MAGIC|REFINT_MODIFIERSNAME, refint_cf_gen,
125           "( OLcfgOvAt:11.3 NAME 'olcRefintModifiersName' "
126           "DESC 'The DN to use as modifiersName' "
127           "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
128         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
129 };
130
131 static ConfigOCs refintocs[] = {
132         { "( OLcfgOvOc:11.1 "
133           "NAME 'olcRefintConfig' "
134           "DESC 'Referential integrity configuration' "
135           "SUP olcOverlayConfig "
136           "MAY ( olcRefintAttribute "
137                 "$ olcRefintNothing "
138                 "$ olcRefintModifiersName "
139           ") )",
140           Cft_Overlay, refintcfg },
141         { NULL, 0, NULL }
142 };
143
144 static int
145 refint_cf_gen(ConfigArgs *c)
146 {
147         slap_overinst *on = (slap_overinst *)c->bi;
148         refint_data *dd = (refint_data *)on->on_bi.bi_private;
149         refint_attrs *ip, *pip, **pipp = NULL;
150         AttributeDescription *ad;
151         const char *text;
152         int rc = ARG_BAD_CONF;
153         int i;
154
155         switch ( c->op ) {
156         case SLAP_CONFIG_EMIT:
157                 switch ( c->type ) {
158                 case REFINT_ATTRS:
159                         ip = dd->attrs;
160                         while ( ip ) {
161                                 value_add_one( &c->rvalue_vals,
162                                                &ip->attr->ad_cname );
163                                 ip = ip->next;
164                         }
165                         rc = 0;
166                         break;
167                 case REFINT_NOTHING:
168                         if ( !BER_BVISEMPTY( &dd->nothing )) {
169                                 rc = value_add_one( &c->rvalue_vals,
170                                                     &dd->nothing );
171                                 if ( rc ) return rc;
172                                 rc = value_add_one( &c->rvalue_nvals,
173                                                     &dd->nnothing );
174                                 return rc;
175                         }
176                         rc = 0;
177                         break;
178                 case REFINT_MODIFIERSNAME:
179                         if ( !BER_BVISEMPTY( &dd->refint_dn )) {
180                                 rc = value_add_one( &c->rvalue_vals,
181                                                     &dd->refint_dn );
182                                 if ( rc ) return rc;
183                                 rc = value_add_one( &c->rvalue_nvals,
184                                                     &dd->refint_ndn );
185                                 return rc;
186                         }
187                         rc = 0;
188                         break;
189                 default:
190                         abort ();
191                 }
192                 break;
193         case LDAP_MOD_DELETE:
194                 switch ( c->type ) {
195                 case REFINT_ATTRS:
196                         pipp = &dd->attrs;
197                         if ( c->valx < 0 ) {
198                                 ip = *pipp;
199                                 *pipp = NULL;
200                                 while ( ip ) {
201                                         pip = ip;
202                                         ip = ip->next;
203                                         ch_free ( pip );
204                                 }
205                         } else {
206                                 /* delete from linked list */
207                                 for ( i=0; i < c->valx; ++i ) {
208                                         pipp = &(*pipp)->next;
209                                 }
210                                 ip = *pipp;
211                                 *pipp = (*pipp)->next;
212
213                                 /* AttributeDescriptions are global so
214                                  * shouldn't be freed here... */
215                                 ch_free ( ip );
216                         }
217                         rc = 0;
218                         break;
219                 case REFINT_NOTHING:
220                         ch_free( dd->nothing.bv_val );
221                         ch_free( dd->nnothing.bv_val );
222                         BER_BVZERO( &dd->nothing );
223                         BER_BVZERO( &dd->nnothing );
224                         rc = 0;
225                         break;
226                 case REFINT_MODIFIERSNAME:
227                         ch_free( dd->refint_dn.bv_val );
228                         ch_free( dd->refint_ndn.bv_val );
229                         BER_BVZERO( &dd->refint_dn );
230                         BER_BVZERO( &dd->refint_ndn );
231                         rc = 0;
232                         break;
233                 default:
234                         abort ();
235                 }
236                 break;
237         case SLAP_CONFIG_ADD:
238                 /* fallthru to LDAP_MOD_ADD */
239         case LDAP_MOD_ADD:
240                 switch ( c->type ) {
241                 case REFINT_ATTRS:
242                         rc = 0;
243                         for ( i=1; i < c->argc; ++i ) {
244                                 ad = NULL;
245                                 if ( slap_str2ad ( c->argv[i], &ad, &text )
246                                      == LDAP_SUCCESS) {
247                                         ip = ch_malloc (
248                                                 sizeof ( refint_attrs ) );
249                                         ip->attr = ad;
250                                         ip->next = dd->attrs;
251                                         dd->attrs = ip;
252                                 } else {
253                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
254                                                 "%s <%s>: %s", c->argv[0], c->argv[i], text );
255                                         Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
256                                                 "%s: %s\n", c->log, c->cr_msg, 0 );
257                                         rc = ARG_BAD_CONF;
258                                 }
259                         }
260                         break;
261                 case REFINT_NOTHING:
262                         if ( !BER_BVISNULL( &c->value_ndn )) {
263                                 ch_free ( dd->nothing.bv_val );
264                                 ch_free ( dd->nnothing.bv_val );
265                                 dd->nothing = c->value_dn;
266                                 dd->nnothing = c->value_ndn;
267                                 rc = 0;
268                         } else {
269                                 rc = ARG_BAD_CONF;
270                         }
271                         break;
272                 case REFINT_MODIFIERSNAME:
273                         if ( !BER_BVISNULL( &c->value_ndn )) {
274                                 ch_free( dd->refint_dn.bv_val );
275                                 ch_free( dd->refint_ndn.bv_val );
276                                 dd->refint_dn = c->value_dn;
277                                 dd->refint_ndn = c->value_ndn;
278                                 rc = 0;
279                         } else {
280                                 rc = ARG_BAD_CONF;
281                         }
282                         break;
283                 default:
284                         abort ();
285                 }
286                 break;
287         default:
288                 abort ();
289         }
290
291         return rc;
292 }
293
294 /*
295 ** allocate new refint_data;
296 ** store in on_bi.bi_private;
297 **
298 */
299
300 static int
301 refint_db_init(
302         BackendDB       *be,
303         ConfigReply     *cr
304 )
305 {
306         slap_overinst *on = (slap_overinst *)be->bd_info;
307         refint_data *id = ch_calloc(1,sizeof(refint_data));
308
309         on->on_bi.bi_private = id;
310         ldap_pvt_thread_mutex_init( &id->qmutex );
311         return(0);
312 }
313
314 static int
315 refint_db_destroy(
316         BackendDB       *be,
317         ConfigReply     *cr
318 )
319 {
320         slap_overinst *on = (slap_overinst *)be->bd_info;
321
322         if ( on->on_bi.bi_private ) {
323                 refint_data *id = on->on_bi.bi_private;
324                 refint_attrs *ii, *ij;
325
326                 on->on_bi.bi_private = NULL;
327                 ldap_pvt_thread_mutex_destroy( &id->qmutex );
328
329                 for(ii = id->attrs; ii; ii = ij) {
330                         ij = ii->next;
331                         ch_free(ii);
332                 }
333
334                 ch_free( id->nothing.bv_val );
335                 BER_BVZERO( &id->nothing );
336                 ch_free( id->nnothing.bv_val );
337                 BER_BVZERO( &id->nnothing );
338
339                 ch_free( id );
340         }
341         return(0);
342 }
343
344 /*
345 ** initialize, copy basedn if not already set
346 **
347 */
348
349 static int
350 refint_open(
351         BackendDB *be,
352         ConfigReply *cr
353 )
354 {
355         slap_overinst *on       = (slap_overinst *)be->bd_info;
356         refint_data *id = on->on_bi.bi_private;
357
358         if ( BER_BVISNULL( &id->dn )) {
359                 if ( BER_BVISNULL( &be->be_nsuffix[0] ))
360                         return -1;
361                 ber_dupbv( &id->dn, &be->be_nsuffix[0] );
362         }
363         if ( BER_BVISNULL( &id->refint_dn ) ) {
364                 ber_dupbv( &id->refint_dn, &refint_dn );
365                 ber_dupbv( &id->refint_ndn, &refint_ndn );
366         }
367
368         /*
369         ** find the backend that matches our configured basedn;
370         ** make sure it exists and has search and modify methods;
371         **
372         */
373
374         if ( on->on_info->oi_origdb != frontendDB ) {
375                 BackendDB *db = select_backend(&id->dn, 1);
376
377                 if ( db ) {
378                         BackendInfo *bi;
379                         if ( db == be )
380                                 bi = on->on_info->oi_orig;
381                         else
382                                 bi = db->bd_info;
383                         if ( !bi->bi_op_search || !bi->bi_op_modify ) {
384                                 Debug( LDAP_DEBUG_CONFIG,
385                                         "refint_response: backend missing search and/or modify\n",
386                                         0, 0, 0 );
387                                 return -1;
388                         }
389                         id->db = db;
390                 } else {
391                         Debug( LDAP_DEBUG_CONFIG,
392                                 "refint_response: no backend for our baseDN %s??\n",
393                                 id->dn.bv_val, 0, 0 );
394                         return -1;
395                 }
396         }
397         return(0);
398 }
399
400
401 /*
402 ** free our basedn;
403 ** free our refintdn
404 **
405 */
406
407 static int
408 refint_close(
409         BackendDB *be,
410         ConfigReply *cr
411 )
412 {
413         slap_overinst *on       = (slap_overinst *) be->bd_info;
414         refint_data *id = on->on_bi.bi_private;
415
416         ch_free( id->dn.bv_val );
417         BER_BVZERO( &id->dn );
418         ch_free( id->refint_dn.bv_val );
419         BER_BVZERO( &id->refint_dn );
420         ch_free( id->refint_ndn.bv_val );
421         BER_BVZERO( &id->refint_ndn );
422
423         return(0);
424 }
425
426 /*
427 ** search callback
428 ** generates a list of Attributes from search results
429 */
430
431 static int
432 refint_search_cb(
433         Operation *op,
434         SlapReply *rs
435 )
436 {
437         Attribute *a;
438         BerVarray b = NULL;
439         refint_q *rq = op->o_callback->sc_private;
440         refint_data *dd = rq->rdata;
441         refint_attrs *ia, *da = dd->attrs, *na;
442         dependent_data *ip;
443         int i;
444
445         Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n",
446                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
447
448         if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
449
450         /*
451         ** foreach configured attribute type:
452         **      if this attr exists in the search result,
453         **      and it has a value matching the target:
454         **              allocate an attr;
455         **              save/build DNs of any subordinate matches;
456         **              handle special case: found exact + subordinate match;
457         **              handle olcRefintNothing;
458         **
459         */
460
461         ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx );
462         ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx );
463         ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
464         ip->next = rq->attrs;
465         rq->attrs = ip;
466         ip->attrs = NULL;
467         for(ia = da; ia; ia = ia->next) {
468                 if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) {
469                         int exact = -1, is_exact;
470
471                         na = NULL;
472
473                         /* Are we doing subtree matching or simple equality? */
474                         if ( rq->do_sub ) {
475                         for(i = 0, b = a->a_nvals; b[i].bv_val; i++) {
476                                 if(dnIsSuffix(&b[i], &rq->oldndn)) {
477                                         is_exact = b[i].bv_len == rq->oldndn.bv_len;
478
479                                         /* Paranoia: skip buggy duplicate exact match,
480                                          * it would break ra_numvals
481                                          */
482                                         if ( is_exact && exact >= 0 )
483                                                 continue;
484
485                                         /* first match? create structure */
486                                         if ( na == NULL ) {
487                                                 na = op->o_tmpcalloc( 1,
488                                                         sizeof( refint_attrs ),
489                                                         op->o_tmpmemctx );
490                                                 na->next = ip->attrs;
491                                                 ip->attrs = na;
492                                                 na->attr = ia->attr;
493                                         }
494
495                                         na->ra_numvals++;
496
497                                         if ( is_exact ) {
498                                                 /* Exact match: refint_repair will deduce the DNs */
499                                                 exact = i;
500
501                                         } else {
502                                                 /* Subordinate match */
503                                                 struct berval   newsub, newdn, olddn, oldndn;
504
505                                                 /* Save old DN */
506                                                 ber_dupbv_x( &olddn, &a->a_vals[i], op->o_tmpmemctx );
507                                                 ber_bvarray_add_x( &na->old_vals, &olddn, op->o_tmpmemctx );
508
509                                                 ber_dupbv_x( &oldndn, &a->a_nvals[i], op->o_tmpmemctx );
510                                                 ber_bvarray_add_x( &na->old_nvals, &oldndn, op->o_tmpmemctx );
511
512                                                 if ( BER_BVISEMPTY( &rq->newdn ) )
513                                                         continue;
514
515                                                 /* Rename subordinate match: Build new DN */
516                                                 newsub = a->a_vals[i];
517                                                 newsub.bv_len -= rq->olddn.bv_len + 1;
518                                                 build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );
519                                                 ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );
520
521                                                 newsub = a->a_nvals[i];
522                                                 newsub.bv_len -= rq->oldndn.bv_len + 1;
523                                                 build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );
524                                                 ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
525                                         }
526                                 }
527                         }
528
529                         /* If we got both subordinate and exact match,
530                          * refint_repair won't special-case the exact match */
531                         if ( exact >= 0 && na->old_vals ) {
532                                 struct berval   dn;
533
534                                 ber_dupbv_x( &dn, &a->a_vals[exact], op->o_tmpmemctx );
535                                 ber_bvarray_add_x( &na->old_vals, &dn, op->o_tmpmemctx );
536                                 ber_dupbv_x( &dn, &a->a_nvals[exact], op->o_tmpmemctx );
537                                 ber_bvarray_add_x( &na->old_nvals, &dn, op->o_tmpmemctx );
538
539                                 if ( !BER_BVISEMPTY( &rq->newdn ) ) {
540                                         ber_dupbv_x( &dn, &rq->newdn, op->o_tmpmemctx );
541                                         ber_bvarray_add_x( &na->new_vals, &dn, op->o_tmpmemctx );
542                                         ber_dupbv_x( &dn, &rq->newndn, op->o_tmpmemctx );
543                                         ber_bvarray_add_x( &na->new_nvals, &dn, op->o_tmpmemctx );
544                                 }
545                         }
546                         } else {
547                                 /* entry has no children, just equality matching */
548                                 is_exact = attr_valfind( a,
549                                         SLAP_MR_EQUALITY|SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH|
550                                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, &rq->oldndn, &i, NULL );
551                                 if ( is_exact == LDAP_SUCCESS ) {
552                                         na = op->o_tmpcalloc( 1,
553                                                 sizeof( refint_attrs ),
554                                                 op->o_tmpmemctx );
555                                         na->next = ip->attrs;
556                                         ip->attrs = na;
557                                         na->attr = ia->attr;
558                                         na->ra_numvals = 1;
559                                 }
560                         }
561
562                         /* Deleting/replacing all values and a nothing DN is configured? */
563                         if ( na && na->ra_numvals == a->a_numvals && !BER_BVISNULL(&dd->nothing) )
564                                 na->dont_empty = 1;
565
566                         Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n",
567                                 a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, i );
568                 }
569         }
570
571         return(0);
572 }
573
574 static int
575 refint_repair(
576         Operation       *op,
577         refint_data     *id,
578         refint_q        *rq )
579 {
580         dependent_data  *dp;
581         SlapReply               rs = {REP_RESULT};
582         Operation               op2;
583         unsigned long   opid;
584         int             rc;
585         int     cache;
586
587         op->o_callback->sc_response = refint_search_cb;
588         op->o_req_dn = op->o_bd->be_suffix[ 0 ];
589         op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
590         op->o_dn = op->o_bd->be_rootdn;
591         op->o_ndn = op->o_bd->be_rootndn;
592         cache = op->o_do_not_cache;
593         op->o_do_not_cache = 1;
594
595         /* search */
596         rc = op->o_bd->be_search( op, &rs );
597         op->o_do_not_cache = cache;
598
599         if ( rc != LDAP_SUCCESS ) {
600                 Debug( LDAP_DEBUG_TRACE,
601                         "refint_repair: search failed: %d\n",
602                         rc, 0, 0 );
603                 return rc;
604         }
605
606         /* safety? paranoid just in case */
607         if ( op->o_callback->sc_private == NULL ) {
608                 Debug( LDAP_DEBUG_TRACE,
609                         "refint_repair: callback wiped out sc_private?!\n",
610                         0, 0, 0 );
611                 return 0;
612         }
613
614         /* Set up the Modify requests */
615         op->o_callback->sc_response = &slap_null_cb;
616
617         /*
618          * [our search callback builds a list of attrs]
619          * foreach attr:
620          *      make sure its dn has a backend;
621          *      build Modification* chain;
622          *      call the backend modify function;
623          *
624          */
625
626         opid = op->o_opid;
627         op2 = *op;
628         for ( dp = rq->attrs; dp; dp = dp->next ) {
629                 SlapReply       rs2 = {REP_RESULT};
630                 refint_attrs    *ra;
631                 Modifications   *m;
632
633                 if ( dp->attrs == NULL ) continue; /* TODO: Is this needed? */
634
635                 op2.o_bd = select_backend( &dp->ndn, 1 );
636                 if ( !op2.o_bd ) {
637                         Debug( LDAP_DEBUG_TRACE,
638                                 "refint_repair: no backend for DN %s!\n",
639                                 dp->dn.bv_val, 0, 0 );
640                         continue;
641                 }
642                 op2.o_tag = LDAP_REQ_MODIFY;
643                 op2.orm_modlist = NULL;
644                 op2.o_req_dn    = dp->dn;
645                 op2.o_req_ndn   = dp->ndn;
646                 /* Internal ops, never replicate these */
647                 op2.orm_no_opattrs = 1;
648                 op2.o_dont_replicate = 1;
649                 op2.o_opid = 0;
650
651                 /* Set our ModifiersName */
652                 if ( SLAP_LASTMOD( op->o_bd ) ) {
653                                 m = op2.o_tmpalloc( sizeof(Modifications) +
654                                         4*sizeof(BerValue), op2.o_tmpmemctx );
655                                 m->sml_next = op2.orm_modlist;
656                                 op2.orm_modlist = m;
657                                 m->sml_op = LDAP_MOD_REPLACE;
658                                 m->sml_flags = SLAP_MOD_INTERNAL;
659                                 m->sml_desc = slap_schema.si_ad_modifiersName;
660                                 m->sml_type = m->sml_desc->ad_cname;
661                                 m->sml_numvals = 1;
662                                 m->sml_values = (BerVarray)(m+1);
663                                 m->sml_nvalues = m->sml_values+2;
664                                 BER_BVZERO( &m->sml_values[1] );
665                                 BER_BVZERO( &m->sml_nvalues[1] );
666                                 m->sml_values[0] = id->refint_dn;
667                                 m->sml_nvalues[0] = id->refint_ndn;
668                 }
669
670                 for ( ra = dp->attrs; ra; ra = ra->next ) {
671                         size_t  len;
672
673                         /* Add values */
674                         if ( ra->dont_empty || !BER_BVISEMPTY( &rq->newdn ) ) {
675                                 len = sizeof(Modifications);
676
677                                 if ( ra->new_vals == NULL ) {
678                                         len += 4*sizeof(BerValue);
679                                 }
680
681                                 m = op2.o_tmpalloc( len, op2.o_tmpmemctx );
682                                 m->sml_next = op2.orm_modlist;
683                                 op2.orm_modlist = m;
684                                 m->sml_op = LDAP_MOD_ADD;
685                                 m->sml_flags = 0;
686                                 m->sml_desc = ra->attr;
687                                 m->sml_type = ra->attr->ad_cname;
688                                 if ( ra->new_vals == NULL ) {
689                                         m->sml_values = (BerVarray)(m+1);
690                                         m->sml_nvalues = m->sml_values+2;
691                                         BER_BVZERO( &m->sml_values[1] );
692                                         BER_BVZERO( &m->sml_nvalues[1] );
693                                         m->sml_numvals = 1;
694                                         if ( BER_BVISEMPTY( &rq->newdn ) ) {
695                                                 m->sml_values[0] = id->nothing;
696                                                 m->sml_nvalues[0] = id->nnothing;
697                                         } else {
698                                                 m->sml_values[0] = rq->newdn;
699                                                 m->sml_nvalues[0] = rq->newndn;
700                                         }
701                                 } else {
702                                         m->sml_values = ra->new_vals;
703                                         m->sml_nvalues = ra->new_nvals;
704                                         m->sml_numvals = ra->ra_numvals;
705                                 }
706                         }
707
708                         /* Delete values */
709                         len = sizeof(Modifications);
710                         if ( ra->old_vals == NULL ) {
711                                 len += 4*sizeof(BerValue);
712                         }
713                         m = op2.o_tmpalloc( len, op2.o_tmpmemctx );
714                         m->sml_next = op2.orm_modlist;
715                         op2.orm_modlist = m;
716                         m->sml_op = LDAP_MOD_DELETE;
717                         m->sml_flags = 0;
718                         m->sml_desc = ra->attr;
719                         m->sml_type = ra->attr->ad_cname;
720                         if ( ra->old_vals == NULL ) {
721                                 m->sml_numvals = 1;
722                                 m->sml_values = (BerVarray)(m+1);
723                                 m->sml_nvalues = m->sml_values+2;
724                                 m->sml_values[0] = rq->olddn;
725                                 m->sml_nvalues[0] = rq->oldndn;
726                                 BER_BVZERO( &m->sml_values[1] );
727                                 BER_BVZERO( &m->sml_nvalues[1] );
728                         } else {
729                                 m->sml_values = ra->old_vals;
730                                 m->sml_nvalues = ra->old_nvals;
731                                 m->sml_numvals = ra->ra_numvals;
732                         }
733                 }
734
735                 op2.o_dn = op2.o_bd->be_rootdn;
736                 op2.o_ndn = op2.o_bd->be_rootndn;
737                 rc = op2.o_bd->be_modify( &op2, &rs2 );
738                 if ( rc != LDAP_SUCCESS ) {
739                         Debug( LDAP_DEBUG_TRACE,
740                                 "refint_repair: dependent modify failed: %d\n",
741                                 rs2.sr_err, 0, 0 );
742                 }
743
744                 while ( ( m = op2.orm_modlist ) ) {
745                         op2.orm_modlist = m->sml_next;
746                         op2.o_tmpfree( m, op2.o_tmpmemctx );
747                 }
748         }
749         op2.o_opid = opid;
750
751         return 0;
752 }
753
754 static void *
755 refint_qtask( void *ctx, void *arg )
756 {
757         struct re_s *rtask = arg;
758         refint_data *id = rtask->arg;
759         Connection conn = {0};
760         OperationBuffer opbuf;
761         Operation *op;
762         slap_callback cb = { NULL, NULL, NULL, NULL };
763         Filter ftop, *fptr;
764         refint_q *rq;
765         refint_attrs *ip;
766         int pausing = 0, rc = 0;
767
768         connection_fake_init( &conn, &opbuf, ctx );
769         op = &opbuf.ob_op;
770
771         /*
772         ** build a search filter for all configured attributes;
773         ** populate our Operation;
774         ** pass our data (attr list, dn) to backend via sc_private;
775         ** call the backend search function;
776         ** nb: (|(one=thing)) is valid, but do smart formatting anyway;
777         ** nb: 16 is arbitrarily a dozen or so extra bytes;
778         **
779         */
780
781         ftop.f_choice = LDAP_FILTER_OR;
782         ftop.f_next = NULL;
783         ftop.f_or = NULL;
784         op->ors_filter = &ftop;
785         for(ip = id->attrs; ip; ip = ip->next) {
786                 /* this filter can be either EQUALITY or EXT */
787                 fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion),
788                         1, op->o_tmpmemctx );
789                 fptr->f_mra = (MatchingRuleAssertion *)(fptr+1);
790                 fptr->f_mr_rule = mr_dnSubtreeMatch;
791                 fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_bvoid;
792                 fptr->f_mr_desc = ip->attr;
793                 fptr->f_mr_dnattrs = 0;
794                 fptr->f_next = ftop.f_or;
795                 ftop.f_or = fptr;
796         }
797
798         for (;;) {
799                 dependent_data  *dp, *dp_next;
800                 refint_attrs *ra, *ra_next;
801
802                 if ( ldap_pvt_thread_pool_pausing( &connection_pool ) > 0 ) {
803                         pausing = 1;
804                         break;
805                 }
806
807                 /* Dequeue an op */
808                 ldap_pvt_thread_mutex_lock( &id->qmutex );
809                 rq = id->qhead;
810                 if ( rq ) {
811                         id->qhead = rq->next;
812                         if ( !id->qhead )
813                                 id->qtail = NULL;
814                 }
815                 ldap_pvt_thread_mutex_unlock( &id->qmutex );
816                 if ( !rq )
817                         break;
818
819                 for (fptr = ftop.f_or; fptr; fptr = fptr->f_next ) {
820                         fptr->f_mr_value = rq->oldndn;
821                         /* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
822                          * and subtree delete where supported */
823                         if (rq->do_sub)
824                                 fptr->f_choice = LDAP_FILTER_EXT;
825                         else
826                                 fptr->f_choice = LDAP_FILTER_EQUALITY;
827                 }
828
829                 filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
830
831                 /* callback gets the searched dn instead */
832                 cb.sc_private   = rq;
833                 cb.sc_response  = refint_search_cb;
834                 op->o_callback  = &cb;
835                 op->o_tag       = LDAP_REQ_SEARCH;
836                 op->ors_scope   = LDAP_SCOPE_SUBTREE;
837                 op->ors_deref   = LDAP_DEREF_NEVER;
838                 op->ors_limit   = NULL;
839                 op->ors_slimit  = SLAP_NO_LIMIT;
840                 op->ors_tlimit  = SLAP_NO_LIMIT;
841
842                 /* no attrs! */
843                 op->ors_attrs = slap_anlist_no_attrs;
844
845                 slap_op_time( &op->o_time, &op->o_tincr );
846
847                 if ( rq->db != NULL ) {
848                         op->o_bd = rq->db;
849                         rc = refint_repair( op, id, rq );
850
851                 } else {
852                         BackendDB       *be;
853
854                         LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
855                                 /* we may want to skip cn=config */
856                                 if ( be == LDAP_STAILQ_FIRST(&backendDB) ) {
857                                         continue;
858                                 }
859
860                                 if ( be->be_search && be->be_modify ) {
861                                         op->o_bd = be;
862                                         rc = refint_repair( op, id, rq );
863                                 }
864                         }
865                 }
866
867                 for ( dp = rq->attrs; dp; dp = dp_next ) {
868                         dp_next = dp->next;
869                         for ( ra = dp->attrs; ra; ra = ra_next ) {
870                                 ra_next = ra->next;
871                                 ber_bvarray_free_x( ra->new_nvals, op->o_tmpmemctx );
872                                 ber_bvarray_free_x( ra->new_vals, op->o_tmpmemctx );
873                                 ber_bvarray_free_x( ra->old_nvals, op->o_tmpmemctx );
874                                 ber_bvarray_free_x( ra->old_vals, op->o_tmpmemctx );
875                                 op->o_tmpfree( ra, op->o_tmpmemctx );
876                         }
877                         op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx );
878                         op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx );
879                         op->o_tmpfree( dp, op->o_tmpmemctx );
880                 }
881                 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
882                 if ( rc == LDAP_BUSY ) {
883                         pausing = 1;
884                         /* re-queue this op */
885                         ldap_pvt_thread_mutex_lock( &id->qmutex );
886                         rq->next = id->qhead;
887                         id->qhead = rq;
888                         if ( !id->qtail )
889                                 id->qtail = rq;
890                         ldap_pvt_thread_mutex_unlock( &id->qmutex );
891                         break;
892                 }
893
894                 if ( !BER_BVISNULL( &rq->newndn )) {
895                         ch_free( rq->newndn.bv_val );
896                         ch_free( rq->newdn.bv_val );
897                 }
898                 ch_free( rq->oldndn.bv_val );
899                 ch_free( rq->olddn.bv_val );
900                 ch_free( rq );
901         }
902
903         /* free filter */
904         for ( fptr = ftop.f_or; fptr; ) {
905                 Filter *f_next = fptr->f_next;
906                 op->o_tmpfree( fptr, op->o_tmpmemctx );
907                 fptr = f_next;
908         }
909
910         /* wait until we get explicitly scheduled again */
911         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
912         ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask );
913         if ( pausing ) {
914                 /* try to run again as soon as the pause is done */
915                 id->qtask->interval.tv_sec = 0;
916                 ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
917                 id->qtask->interval.tv_sec = RUNQ_INTERVAL;
918         } else {
919                 ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 );
920         }
921         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
922
923         return NULL;
924 }
925
926 /*
927 ** refint_response
928 ** search for matching records and modify them
929 */
930
931 static int
932 refint_response(
933         Operation *op,
934         SlapReply *rs
935 )
936 {
937         refint_pre *rp;
938         slap_overinst *on;
939         refint_data *id;
940         BerValue pdn;
941         refint_q *rq;
942         refint_attrs *ip;
943         int ac;
944
945         /* If the main op failed or is not a Delete or ModRdn, ignore it */
946         if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
947                 rs->sr_err != LDAP_SUCCESS )
948                 return SLAP_CB_CONTINUE;
949
950         rp = op->o_callback->sc_private;
951         on = rp->on;
952         id = on->on_bi.bi_private;
953
954         rq = ch_calloc( 1, sizeof( refint_q ));
955         ber_dupbv( &rq->olddn, &op->o_req_dn );
956         ber_dupbv( &rq->oldndn, &op->o_req_ndn );
957         rq->db = id->db;
958         rq->rdata = id;
959         rq->do_sub = rp->do_sub;
960
961         if ( op->o_tag == LDAP_REQ_MODRDN ) {
962                 if ( op->oq_modrdn.rs_newSup ) {
963                         pdn = *op->oq_modrdn.rs_newSup;
964                 } else {
965                         dnParent( &op->o_req_dn, &pdn );
966                 }
967                 build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL );
968                 if ( op->oq_modrdn.rs_nnewSup ) {
969                         pdn = *op->oq_modrdn.rs_nnewSup;
970                 } else {
971                         dnParent( &op->o_req_ndn, &pdn );
972                 }
973                 build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL );
974         }
975
976         ldap_pvt_thread_mutex_lock( &id->qmutex );
977         if ( id->qtail ) {
978                 id->qtail->next = rq;
979         } else {
980                 id->qhead = rq;
981         }
982         id->qtail = rq;
983         ldap_pvt_thread_mutex_unlock( &id->qmutex );
984
985         ac = 0;
986         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
987         if ( !id->qtask ) {
988                 id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
989                         refint_qtask, id, "refint_qtask",
990                         op->o_bd->be_suffix[0].bv_val );
991                 ac = 1;
992         } else {
993                 if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) &&
994                         !id->qtask->next_sched.tv_sec ) {
995                         id->qtask->interval.tv_sec = 0;
996                         ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
997                         id->qtask->interval.tv_sec = RUNQ_INTERVAL;
998                         ac = 1;
999                 }
1000         }
1001         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1002         if ( ac )
1003                 slap_wake_listener();
1004
1005         return SLAP_CB_CONTINUE;
1006 }
1007
1008 /* Check if the target entry exists and has children.
1009  * Do nothing if target doesn't exist.
1010  */
1011 static int
1012 refint_preop(
1013         Operation *op,
1014         SlapReply *rs
1015 )
1016 {
1017         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
1018         refint_data *id = on->on_bi.bi_private;
1019         Entry *e;
1020         int rc;
1021
1022         /* are any attrs configured? */
1023         if ( !id->attrs )
1024                 return SLAP_CB_CONTINUE;
1025
1026         rc = overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on );
1027         if ( rc == LDAP_SUCCESS ) {
1028                 slap_callback *sc = op->o_tmpcalloc( 1,
1029                         sizeof(slap_callback)+sizeof(refint_pre), op->o_tmpmemctx );
1030                 refint_pre *rp = (refint_pre *)(sc+1);
1031                 rp->on = on;
1032                 rp->do_sub = 1; /* assume there are children */
1033                 if ( op->o_bd->be_has_subordinates ) {
1034                         int has = 0;
1035                         rc = op->o_bd->be_has_subordinates( op, e, &has );
1036                         /* there definitely are not children */
1037                         if ( rc == LDAP_SUCCESS && has == LDAP_COMPARE_FALSE )
1038                                 rp->do_sub = 0;
1039                 }
1040                 overlay_entry_release_ov( op, e, 0, on );
1041                 sc->sc_response = refint_response;
1042                 sc->sc_private = rp;
1043                 sc->sc_next = op->o_callback;
1044                 op->o_callback = sc;
1045         }
1046         return SLAP_CB_CONTINUE;
1047 }
1048
1049 /*
1050 ** init_module is last so the symbols resolve "for free" --
1051 ** it expects to be called automagically during dynamic module initialization
1052 */
1053
1054 int refint_initialize() {
1055         int rc;
1056
1057         mr_dnSubtreeMatch = mr_find( "dnSubtreeMatch" );
1058         if ( mr_dnSubtreeMatch == NULL ) {
1059                 Debug( LDAP_DEBUG_ANY, "refint_initialize: "
1060                         "unable to find MatchingRule 'dnSubtreeMatch'.\n",
1061                         0, 0, 0 );
1062                 return 1;
1063         }
1064
1065         /* statically declared just after the #includes at top */
1066         refint.on_bi.bi_type = "refint";
1067         refint.on_bi.bi_db_init = refint_db_init;
1068         refint.on_bi.bi_db_destroy = refint_db_destroy;
1069         refint.on_bi.bi_db_open = refint_open;
1070         refint.on_bi.bi_db_close = refint_close;
1071         refint.on_bi.bi_op_delete = refint_preop;
1072         refint.on_bi.bi_op_modrdn = refint_preop;
1073
1074         refint.on_bi.bi_cf_ocs = refintocs;
1075         rc = config_register_schema ( refintcfg, refintocs );
1076         if ( rc ) return rc;
1077
1078         return(overlay_register(&refint));
1079 }
1080
1081 #if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
1082 int init_module(int argc, char *argv[]) {
1083         return refint_initialize();
1084 }
1085 #endif
1086
1087 #endif /* SLAPD_OVER_REFINT */