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