1 /*	$NetBSD: unique.c,v 1.1.1.4 2010/12/12 15:23:47 adam Exp $	*/
2 
3 /* unique.c - attribute uniqueness module */
4 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/unique.c,v 1.20.2.18 2010/04/13 20:23:47 kurt Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2004-2010 The OpenLDAP Foundation.
8  * Portions Copyright 2004,2006-2007 Symas Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Symas Corporation for
21  * inclusion in OpenLDAP Software, with subsequent enhancements by
22  * Matthew Backes at Symas Corporation.  This work was sponsored by
23  * Hewlett-Packard.
24  */
25 
26 #include "portable.h"
27 
28 #ifdef SLAPD_OVER_UNIQUE
29 
30 #include <stdio.h>
31 
32 #include <ac/string.h>
33 #include <ac/socket.h>
34 
35 #include "slap.h"
36 #include "config.h"
37 
38 #define UNIQUE_DEFAULT_URI ("ldap:///??sub")
39 
40 static slap_overinst unique;
41 
42 typedef struct unique_attrs_s {
43 	struct unique_attrs_s *next;	      /* list of attrs */
44 	AttributeDescription *attr;
45 } unique_attrs;
46 
47 typedef struct unique_domain_uri_s {
48 	struct unique_domain_uri_s *next;
49 	struct berval dn;
50 	struct berval ndn;
51 	struct berval filter;
52 	Filter *f;
53 	struct unique_attrs_s *attrs;
54 	int scope;
55 } unique_domain_uri;
56 
57 typedef struct unique_domain_s {
58 	struct unique_domain_s *next;
59 	struct berval domain_spec;
60 	struct unique_domain_uri_s *uri;
61 	char ignore;                          /* polarity of attributes */
62 	char strict;                          /* null considered unique too */
63 } unique_domain;
64 
65 typedef struct unique_data_s {
66 	struct unique_domain_s *domains;
67 	struct unique_domain_s *legacy;
68 	char legacy_strict_set;
69 } unique_data;
70 
71 typedef struct unique_counter_s {
72 	struct berval *ndn;
73 	int count;
74 } unique_counter;
75 
76 enum {
77 	UNIQUE_BASE = 1,
78 	UNIQUE_IGNORE,
79 	UNIQUE_ATTR,
80 	UNIQUE_STRICT,
81 	UNIQUE_URI
82 };
83 
84 static ConfigDriver unique_cf_base;
85 static ConfigDriver unique_cf_attrs;
86 static ConfigDriver unique_cf_strict;
87 static ConfigDriver unique_cf_uri;
88 
89 static ConfigTable uniquecfg[] = {
90 	{ "unique_base", "basedn", 2, 2, 0, ARG_DN|ARG_MAGIC|UNIQUE_BASE,
91 	  unique_cf_base, "( OLcfgOvAt:10.1 NAME 'olcUniqueBase' "
92 	  "DESC 'Subtree for uniqueness searches' "
93 	  "EQUALITY distinguishedNameMatch "
94 	  "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
95 	{ "unique_ignore", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_IGNORE,
96 	  unique_cf_attrs, "( OLcfgOvAt:10.2 NAME 'olcUniqueIgnore' "
97 	  "DESC 'Attributes for which uniqueness shall not be enforced' "
98 	  "EQUALITY caseIgnoreMatch "
99 	  "ORDERING caseIgnoreOrderingMatch "
100 	  "SUBSTR caseIgnoreSubstringsMatch "
101 	  "SYNTAX OMsDirectoryString )", NULL, NULL },
102 	{ "unique_attributes", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_ATTR,
103 	  unique_cf_attrs, "( OLcfgOvAt:10.3 NAME 'olcUniqueAttribute' "
104 	  "DESC 'Attributes for which uniqueness shall be enforced' "
105 	  "EQUALITY caseIgnoreMatch "
106 	  "ORDERING caseIgnoreOrderingMatch "
107 	  "SUBSTR caseIgnoreSubstringsMatch "
108 	  "SYNTAX OMsDirectoryString )", NULL, NULL },
109 	{ "unique_strict", "on|off", 1, 2, 0, ARG_MAGIC|UNIQUE_STRICT,
110 	  unique_cf_strict, "( OLcfgOvAt:10.4 NAME 'olcUniqueStrict' "
111 	  "DESC 'Enforce uniqueness of null values' "
112 	  "EQUALITY booleanMatch "
113 	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
114 	{ "unique_uri", "ldapuri", 2, 3, 0, ARG_MAGIC|UNIQUE_URI,
115 	  unique_cf_uri, "( OLcfgOvAt:10.5 NAME 'olcUniqueURI' "
116 	  "DESC 'List of keywords and LDAP URIs for a uniqueness domain' "
117 	  "EQUALITY caseExactMatch "
118 	  "ORDERING caseExactOrderingMatch "
119 	  "SUBSTR caseExactSubstringsMatch "
120 	  "SYNTAX OMsDirectoryString )", NULL, NULL },
121 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
122 };
123 
124 static ConfigOCs uniqueocs[] = {
125 	{ "( OLcfgOvOc:10.1 "
126 	  "NAME 'olcUniqueConfig' "
127 	  "DESC 'Attribute value uniqueness configuration' "
128 	  "SUP olcOverlayConfig "
129 	  "MAY ( olcUniqueBase $ olcUniqueIgnore $ "
130 	  "olcUniqueAttribute $ olcUniqueStrict $ "
131 	  "olcUniqueURI ) )",
132 	  Cft_Overlay, uniquecfg },
133 	{ NULL, 0, NULL }
134 };
135 
136 static void
137 unique_free_domain_uri ( unique_domain_uri *uri )
138 {
139 	unique_domain_uri *next_uri = NULL;
140 	unique_attrs *attr, *next_attr = NULL;
141 
142 	while ( uri ) {
143 		next_uri = uri->next;
144 		ch_free ( uri->dn.bv_val );
145 		ch_free ( uri->ndn.bv_val );
146 		ch_free ( uri->filter.bv_val );
147 		filter_free( uri->f );
148 		attr = uri->attrs;
149 		while ( attr ) {
150 			next_attr = attr->next;
151 			ch_free (attr);
152 			attr = next_attr;
153 		}
154 		ch_free ( uri );
155 		uri = next_uri;
156 	}
157 }
158 
159 /* free an entire stack of domains */
160 static void
161 unique_free_domain ( unique_domain *domain )
162 {
163 	unique_domain *next_domain = NULL;
164 
165 	while ( domain ) {
166 		next_domain = domain->next;
167 		ch_free ( domain->domain_spec.bv_val );
168 		unique_free_domain_uri ( domain->uri );
169 		ch_free ( domain );
170 		domain = next_domain;
171 	}
172 }
173 
174 static int
175 unique_new_domain_uri ( unique_domain_uri **urip,
176 			const LDAPURLDesc *url_desc,
177 			ConfigArgs *c )
178 {
179 	int i, rc = LDAP_SUCCESS;
180 	unique_domain_uri *uri;
181 	struct berval bv = {0, NULL};
182 	BackendDB *be = (BackendDB *)c->be;
183 	char ** attr_str;
184 	AttributeDescription * ad;
185 	const char * text;
186 
187 	uri = ch_calloc ( 1, sizeof ( unique_domain_uri ) );
188 
189 	if ( url_desc->lud_dn && url_desc->lud_dn[0] ) {
190 		ber_str2bv( url_desc->lud_dn, 0, 0, &bv );
191 		rc = dnPrettyNormal( NULL,
192 				     &bv,
193 				     &uri->dn,
194 				     &uri->ndn,
195 				     NULL );
196 		if ( rc != LDAP_SUCCESS ) {
197 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
198 				  "<%s> invalid DN %d (%s)",
199 				  url_desc->lud_dn, rc, ldap_err2string( rc ));
200 			rc = ARG_BAD_CONF;
201 			goto exit;
202 		}
203 
204 		if ( be->be_nsuffix == NULL ) {
205 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
206 				  "suffix must be set" );
207 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
208 				c->cr_msg, NULL, NULL );
209 			rc = ARG_BAD_CONF;
210 			goto exit;
211 		}
212 
213 		if ( !dnIsSuffix ( &uri->ndn, &be->be_nsuffix[0] ) ) {
214 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
215 				  "dn <%s> is not a suffix of backend base dn <%s>",
216 				  uri->dn.bv_val,
217 				  be->be_nsuffix[0].bv_val );
218 			rc = ARG_BAD_CONF;
219 			goto exit;
220 		}
221 
222 		if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
223 			Debug( LDAP_DEBUG_ANY,
224 				"slapo-unique needs a rootdn; "
225 				"backend <%s> has none, YMMV.\n",
226 				be->be_nsuffix[0].bv_val, 0, 0 );
227 		}
228 	}
229 
230 	attr_str = url_desc->lud_attrs;
231 	if ( attr_str ) {
232 		for ( i=0; attr_str[i]; ++i ) {
233 			unique_attrs * attr;
234 			ad = NULL;
235 			if ( slap_str2ad ( attr_str[i], &ad, &text )
236 			     == LDAP_SUCCESS) {
237 				attr = ch_calloc ( 1,
238 						   sizeof ( unique_attrs ) );
239 				attr->attr = ad;
240 				attr->next = uri->attrs;
241 				uri->attrs = attr;
242 			} else {
243 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
244 					  "unique: attribute: %s: %s",
245 					  attr_str[i], text );
246 				rc = ARG_BAD_CONF;
247 				goto exit;
248 			}
249 		}
250 	}
251 
252 	uri->scope = url_desc->lud_scope;
253 	if ( !uri->scope ) {
254 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
255 			  "unique: uri with base scope will always be unique");
256 		rc = ARG_BAD_CONF;
257 		goto exit;
258 	}
259 
260 	if (url_desc->lud_filter) {
261 		char *ptr;
262 		uri->f = str2filter( url_desc->lud_filter );
263 		if ( !uri->f ) {
264 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
265 				  "unique: bad filter");
266 			rc = ARG_BAD_CONF;
267 			goto exit;
268 		}
269 		/* make sure the strfilter is in normal form (ITS#5581) */
270 		filter2bv( uri->f, &uri->filter );
271 		ptr = strstr( uri->filter.bv_val, "(?=" /*)*/ );
272 		if ( ptr != NULL && ptr <= ( uri->filter.bv_val - STRLENOF( "(?=" /*)*/ ) + uri->filter.bv_len ) )
273 		{
274 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
275 				  "unique: bad filter");
276 			rc = ARG_BAD_CONF;
277 			goto exit;
278 		}
279 	}
280 exit:
281 	uri->next = *urip;
282 	*urip = uri;
283 	if ( rc ) {
284 		Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
285 			"%s: %s\n", c->log, c->cr_msg, 0 );
286 		unique_free_domain_uri ( uri );
287 		*urip = NULL;
288 	}
289 	return rc;
290 }
291 
292 static int
293 unique_new_domain_uri_basic ( unique_domain_uri **urip,
294 			      ConfigArgs *c )
295 {
296 	LDAPURLDesc *url_desc = NULL;
297 	int rc;
298 
299 	rc = ldap_url_parse ( UNIQUE_DEFAULT_URI, &url_desc );
300 	if ( rc ) return rc;
301 	rc = unique_new_domain_uri ( urip, url_desc, c );
302 	ldap_free_urldesc ( url_desc );
303 	return rc;
304 }
305 
306 /* if *domain is non-null, it's pushed down the stack.
307  * note that the entire stack is freed if there is an error,
308  * so build added domains in a separate stack before adding them
309  *
310  * domain_specs look like
311  *
312  * [strict ][ignore ]uri[[ uri]...]
313  * e.g. "ldap:///ou=foo,o=bar?uid?sub ldap:///ou=baz,o=bar?uid?sub"
314  *      "strict ldap:///ou=accounts,o=bar?uid,uidNumber?one"
315  *      etc
316  *
317  * so finally strictness is per-domain
318  * but so is ignore-state, and that would be better as a per-url thing
319  */
320 static int
321 unique_new_domain ( unique_domain **domainp,
322 		    char *domain_spec,
323 		    ConfigArgs *c )
324 {
325 	char *uri_start;
326 	int rc = LDAP_SUCCESS;
327 	int uri_err = 0;
328 	unique_domain * domain;
329 	LDAPURLDesc *url_desc, *url_descs = NULL;
330 
331 	Debug(LDAP_DEBUG_TRACE, "==> unique_new_domain <%s>\n",
332 	      domain_spec, 0, 0);
333 
334 	domain = ch_calloc ( 1, sizeof (unique_domain) );
335 	ber_str2bv( domain_spec, 0, 1, &domain->domain_spec );
336 
337 	uri_start = domain_spec;
338 	if ( strncasecmp ( uri_start, "ignore ",
339 			   STRLENOF( "ignore " ) ) == 0 ) {
340 		domain->ignore = 1;
341 		uri_start += STRLENOF( "ignore " );
342 	}
343 	if ( strncasecmp ( uri_start, "strict ",
344 			   STRLENOF( "strict " ) ) == 0 ) {
345 		domain->strict = 1;
346 		uri_start += STRLENOF( "strict " );
347 		if ( !domain->ignore
348 		     && strncasecmp ( uri_start, "ignore ",
349 				      STRLENOF( "ignore " ) ) == 0 ) {
350 			domain->ignore = 1;
351 			uri_start += STRLENOF( "ignore " );
352 		}
353 	}
354 	rc = ldap_url_parselist_ext ( &url_descs, uri_start, " ", 0 );
355 	if ( rc ) {
356 		snprintf( c->cr_msg, sizeof( c->cr_msg ),
357 			  "<%s> invalid ldap urilist",
358 			  uri_start );
359 		rc = ARG_BAD_CONF;
360 		goto exit;
361 	}
362 
363 	for ( url_desc = url_descs;
364 	      url_desc;
365 	      url_desc = url_descs->lud_next ) {
366 		rc = unique_new_domain_uri ( &domain->uri,
367 					     url_desc,
368 					     c );
369 		if ( rc ) {
370 			rc = ARG_BAD_CONF;
371 			uri_err = 1;
372 			goto exit;
373 		}
374 	}
375 
376 exit:
377 	if ( url_descs ) ldap_free_urldesc ( url_descs );
378 	domain->next = *domainp;
379 	*domainp = domain;
380 	if ( rc ) {
381 		Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
382 			"%s: %s\n", c->log, c->cr_msg, 0 );
383 		unique_free_domain ( domain );
384 		*domainp = NULL;
385 	}
386 	return rc;
387 }
388 
389 static int
390 unique_cf_base( ConfigArgs *c )
391 {
392 	BackendDB *be = (BackendDB *)c->be;
393 	slap_overinst *on = (slap_overinst *)c->bi;
394 	unique_data *private = (unique_data *) on->on_bi.bi_private;
395 	unique_domain *domains = private->domains;
396 	unique_domain *legacy = private->legacy;
397 	int rc = ARG_BAD_CONF;
398 
399 	switch ( c->op ) {
400 	case SLAP_CONFIG_EMIT:
401 		rc = 0;
402 		if ( legacy && legacy->uri && legacy->uri->dn.bv_val ) {
403 			rc = value_add_one ( &c->rvalue_vals,
404 					     &legacy->uri->dn );
405 			if ( rc ) return rc;
406 			rc = value_add_one ( &c->rvalue_nvals,
407 					     &legacy->uri->ndn );
408 			if ( rc ) return rc;
409 		}
410 		break;
411 	case LDAP_MOD_DELETE:
412 		assert ( legacy && legacy->uri && legacy->uri->dn.bv_val );
413 		rc = 0;
414 		ch_free ( legacy->uri->dn.bv_val );
415 		ch_free ( legacy->uri->ndn.bv_val );
416 		BER_BVZERO( &legacy->uri->dn );
417 		BER_BVZERO( &legacy->uri->ndn );
418 		if ( !legacy->uri->attrs ) {
419 			unique_free_domain_uri ( legacy->uri );
420 			legacy->uri = NULL;
421 		}
422 		if ( !legacy->uri && !private->legacy_strict_set ) {
423 			unique_free_domain ( legacy );
424 			private->legacy = legacy = NULL;
425 		}
426 		break;
427 	case LDAP_MOD_ADD:
428 	case SLAP_CONFIG_ADD:
429 		if ( domains ) {
430 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
431 				  "cannot set legacy attrs when URIs are present" );
432 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
433 				c->cr_msg, NULL, NULL );
434 			rc = ARG_BAD_CONF;
435 			break;
436 		}
437 		if ( be->be_nsuffix == NULL ) {
438 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
439 				  "suffix must be set" );
440 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
441 				c->cr_msg, NULL, NULL );
442 			rc = ARG_BAD_CONF;
443 			break;
444 		}
445 		if ( !dnIsSuffix ( &c->value_ndn,
446 				   &be->be_nsuffix[0] ) ) {
447 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
448 				  "dn is not a suffix of backend base" );
449 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
450 				c->cr_msg, NULL, NULL );
451 			rc = ARG_BAD_CONF;
452 			break;
453 		}
454 		if ( !legacy ) {
455 			unique_new_domain ( &private->legacy,
456 					    UNIQUE_DEFAULT_URI,
457 					    c );
458 			legacy = private->legacy;
459 		}
460 		if ( !legacy->uri )
461 			unique_new_domain_uri_basic ( &legacy->uri, c );
462 		ch_free ( legacy->uri->dn.bv_val );
463 		ch_free ( legacy->uri->ndn.bv_val );
464 		legacy->uri->dn = c->value_dn;
465 		legacy->uri->ndn = c->value_ndn;
466 		rc = 0;
467 		break;
468 	default:
469 		abort();
470 	}
471 
472 	if ( rc ) {
473 		ch_free( c->value_dn.bv_val );
474 		BER_BVZERO( &c->value_dn );
475 		ch_free( c->value_ndn.bv_val );
476 		BER_BVZERO( &c->value_ndn );
477 	}
478 
479 	return rc;
480 }
481 
482 static int
483 unique_cf_attrs( ConfigArgs *c )
484 {
485 	slap_overinst *on = (slap_overinst *)c->bi;
486 	unique_data *private = (unique_data *) on->on_bi.bi_private;
487 	unique_domain *domains = private->domains;
488 	unique_domain *legacy = private->legacy;
489 	unique_attrs *new_attrs = NULL;
490 	unique_attrs *attr, *next_attr, *reverse_attrs;
491 	unique_attrs **attrp;
492 	int rc = ARG_BAD_CONF;
493 	int i;
494 
495 	switch ( c->op ) {
496 	case SLAP_CONFIG_EMIT:
497 		if ( legacy
498 		     && (c->type == UNIQUE_IGNORE) == legacy->ignore
499 		     && legacy->uri )
500 			for ( attr = legacy->uri->attrs;
501 			      attr;
502 			      attr = attr->next )
503 				value_add_one( &c->rvalue_vals,
504 					       &attr->attr->ad_cname );
505 		rc = 0;
506 		break;
507 	case LDAP_MOD_DELETE:
508 		if ( legacy
509 		     && (c->type == UNIQUE_IGNORE) == legacy->ignore
510 		     && legacy->uri
511 		     && legacy->uri->attrs) {
512 			if ( c->valx < 0 ) { /* delete all */
513 				for ( attr = legacy->uri->attrs;
514 				      attr;
515 				      attr = next_attr ) {
516 					next_attr = attr->next;
517 					ch_free ( attr );
518 				}
519 				legacy->uri->attrs = NULL;
520 			} else { /* delete by index */
521 				attrp = &legacy->uri->attrs;
522 				for ( i=0; i < c->valx; ++i )
523 					attrp = &(*attrp)->next;
524 				attr = *attrp;
525 				*attrp = attr->next;
526 				ch_free (attr);
527 			}
528 			if ( !legacy->uri->attrs
529 			     && !legacy->uri->dn.bv_val ) {
530 				unique_free_domain_uri ( legacy->uri );
531 				legacy->uri = NULL;
532 			}
533 			if ( !legacy->uri && !private->legacy_strict_set ) {
534 				unique_free_domain ( legacy );
535 				private->legacy = legacy = NULL;
536 			}
537 		}
538 		rc = 0;
539 		break;
540 	case LDAP_MOD_ADD:
541 	case SLAP_CONFIG_ADD:
542 		if ( domains ) {
543 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
544 				  "cannot set legacy attrs when URIs are present" );
545 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
546 				c->cr_msg, NULL, NULL );
547 			rc = ARG_BAD_CONF;
548 			break;
549 		}
550 		if ( legacy
551 		     && legacy->uri
552 		     && legacy->uri->attrs
553 		     && (c->type == UNIQUE_IGNORE) != legacy->ignore ) {
554 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
555 				  "cannot set both attrs and ignore-attrs" );
556 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
557 				c->cr_msg, NULL, NULL );
558 			rc = ARG_BAD_CONF;
559 			break;
560 		}
561 		if ( !legacy ) {
562 			unique_new_domain ( &private->legacy,
563 					    UNIQUE_DEFAULT_URI,
564 					    c );
565 			legacy = private->legacy;
566 		}
567 		if ( !legacy->uri )
568 			unique_new_domain_uri_basic ( &legacy->uri, c );
569 		rc = 0;
570 		for ( i=1; c->argv[i]; ++i ) {
571 			AttributeDescription * ad = NULL;
572 			const char * text;
573 			if ( slap_str2ad ( c->argv[i], &ad, &text )
574 			     == LDAP_SUCCESS) {
575 
576 				attr = ch_calloc ( 1,
577 					sizeof ( unique_attrs ) );
578 				attr->attr = ad;
579 				attr->next = new_attrs;
580 				new_attrs = attr;
581 			} else {
582 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
583 					  "unique: attribute: %s: %s",
584 					  c->argv[i], text );
585 				for ( attr = new_attrs;
586 				      attr;
587 				      attr=next_attr ) {
588 					next_attr = attr->next;
589 					ch_free ( attr );
590 				}
591 				rc = ARG_BAD_CONF;
592 				break;
593 			}
594 		}
595 		if ( rc ) break;
596 
597 		/* (nconc legacy->uri->attrs (nreverse new_attrs)) */
598 		reverse_attrs = NULL;
599 		for ( attr = new_attrs;
600 		      attr;
601 		      attr = next_attr ) {
602 			next_attr = attr->next;
603 			attr->next = reverse_attrs;
604 			reverse_attrs = attr;
605 		}
606 		for ( attrp = &legacy->uri->attrs;
607 		      *attrp;
608 		      attrp = &(*attrp)->next ) ;
609 		*attrp = reverse_attrs;
610 
611 		legacy->ignore = ( c->type == UNIQUE_IGNORE );
612 		break;
613 	default:
614 		abort();
615 	}
616 
617 	if ( rc ) {
618 		Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
619 			"%s: %s\n", c->log, c->cr_msg, 0 );
620 	}
621 	return rc;
622 }
623 
624 static int
625 unique_cf_strict( ConfigArgs *c )
626 {
627 	slap_overinst *on = (slap_overinst *)c->bi;
628 	unique_data *private = (unique_data *) on->on_bi.bi_private;
629 	unique_domain *domains = private->domains;
630 	unique_domain *legacy = private->legacy;
631 	int rc = ARG_BAD_CONF;
632 
633 	switch ( c->op ) {
634 	case SLAP_CONFIG_EMIT:
635 		/* We process the boolean manually instead of using
636 		 * ARG_ON_OFF so that we can three-state it;
637 		 * olcUniqueStrict is either TRUE, FALSE, or missing,
638 		 * and missing is necessary to add olcUniqueURIs...
639 		 */
640 		if ( private->legacy_strict_set ) {
641 			struct berval bv;
642 			bv.bv_val = legacy->strict ? "TRUE" : "FALSE";
643 			bv.bv_len = legacy->strict ?
644 				STRLENOF("TRUE") :
645 				STRLENOF("FALSE");
646 			value_add_one ( &c->rvalue_vals, &bv );
647 		}
648 		rc = 0;
649 		break;
650 	case LDAP_MOD_DELETE:
651 		if ( legacy ) {
652 			legacy->strict = 0;
653 			if ( ! legacy->uri ) {
654 				unique_free_domain ( legacy );
655 				private->legacy = NULL;
656 			}
657 		}
658 		private->legacy_strict_set = 0;
659 		rc = 0;
660 		break;
661 	case LDAP_MOD_ADD:
662 	case SLAP_CONFIG_ADD:
663 		if ( domains ) {
664 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
665 				  "cannot set legacy attrs when URIs are present" );
666 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
667 				c->cr_msg, NULL, NULL );
668 			rc = ARG_BAD_CONF;
669 			break;
670 		}
671 		if ( ! legacy ) {
672 			unique_new_domain ( &private->legacy,
673 					    UNIQUE_DEFAULT_URI,
674 					    c );
675 			legacy = private->legacy;
676 		}
677 		/* ... not using ARG_ON_OFF makes this necessary too */
678 		assert ( c->argc == 2 );
679 		legacy->strict = (strcasecmp ( c->argv[1], "TRUE" ) == 0);
680 		private->legacy_strict_set = 1;
681 		rc = 0;
682 		break;
683 	default:
684 		abort();
685 	}
686 
687 	return rc;
688 }
689 
690 static int
691 unique_cf_uri( ConfigArgs *c )
692 {
693 	slap_overinst *on = (slap_overinst *)c->bi;
694 	unique_data *private = (unique_data *) on->on_bi.bi_private;
695 	unique_domain *domains = private->domains;
696 	unique_domain *legacy = private->legacy;
697 	unique_domain *domain = NULL, **domainp = NULL;
698 	int rc = ARG_BAD_CONF;
699 	int i;
700 
701 	switch ( c->op ) {
702 	case SLAP_CONFIG_EMIT:
703 		for ( domain = domains;
704 		      domain;
705 		      domain = domain->next ) {
706 			rc = value_add_one ( &c->rvalue_vals,
707 					     &domain->domain_spec );
708 			if ( rc ) break;
709 		}
710 		break;
711 	case LDAP_MOD_DELETE:
712 		if ( c->valx < 0 ) { /* delete them all! */
713 			unique_free_domain ( domains );
714 			private->domains = NULL;
715 		} else { /* delete just one */
716 			domainp = &private->domains;
717 			for ( i=0; i < c->valx && *domainp; ++i )
718 				domainp = &(*domainp)->next;
719 
720 			/* If *domainp is null, we walked off the end
721 			 * of the list.  This happens when back-config
722 			 * and the overlay are out-of-sync, like when
723 			 * rejecting changes before ITS#4752 gets
724 			 * fixed.
725 			 *
726 			 * This should never happen, but will appear
727 			 * if you backport this version of
728 			 * slapo-unique without the config-undo fixes
729 			 *
730 			 * test024 Will hit this case in such a
731 			 * situation.
732 			 */
733 			assert (*domainp != NULL);
734 
735 			domain = *domainp;
736 			*domainp = domain->next;
737 			domain->next = NULL;
738 			unique_free_domain ( domain );
739 		}
740 		rc = 0;
741 		break;
742 
743 	case SLAP_CONFIG_ADD: /* fallthrough */
744 	case LDAP_MOD_ADD:
745 		if ( legacy ) {
746 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
747 				  "cannot set Uri when legacy attrs are present" );
748 			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
749 				c->cr_msg, NULL, NULL );
750 			rc = ARG_BAD_CONF;
751 			break;
752 		}
753 		rc = 0;
754 		if ( c->line ) rc = unique_new_domain ( &domain, c->line, c );
755 		else rc = unique_new_domain ( &domain, c->argv[1], c );
756 		if ( rc ) break;
757 		assert ( domain->next == NULL );
758 		for ( domainp = &private->domains;
759 		      *domainp;
760 		      domainp = &(*domainp)->next ) ;
761 		*domainp = domain;
762 
763 		break;
764 
765 	default:
766 		abort ();
767 	}
768 
769 	return rc;
770 }
771 
772 /*
773 ** allocate new unique_data;
774 ** initialize, copy basedn;
775 ** store in on_bi.bi_private;
776 **
777 */
778 
779 static int
780 unique_db_init(
781 	BackendDB	*be,
782 	ConfigReply	*cr
783 )
784 {
785 	slap_overinst *on = (slap_overinst *)be->bd_info;
786 	unique_data **privatep = (unique_data **) &on->on_bi.bi_private;
787 
788 	Debug(LDAP_DEBUG_TRACE, "==> unique_db_init\n", 0, 0, 0);
789 
790 	*privatep = ch_calloc ( 1, sizeof ( unique_data ) );
791 
792 	return 0;
793 }
794 
795 static int
796 unique_db_destroy(
797 	BackendDB	*be,
798 	ConfigReply	*cr
799 )
800 {
801 	slap_overinst *on = (slap_overinst *)be->bd_info;
802 	unique_data **privatep = (unique_data **) &on->on_bi.bi_private;
803 	unique_data *private = *privatep;
804 
805 	Debug(LDAP_DEBUG_TRACE, "==> unique_db_destroy\n", 0, 0, 0);
806 
807 	if ( private ) {
808 		unique_domain *domains = private->domains;
809 		unique_domain *legacy = private->legacy;
810 
811 		unique_free_domain ( domains );
812 		unique_free_domain ( legacy );
813 		ch_free ( private );
814 		*privatep = NULL;
815 	}
816 
817 	return 0;
818 }
819 
820 static int
821 unique_open(
822 	BackendDB *be,
823 	ConfigReply *cr
824 )
825 {
826 	Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
827 
828 	return 0;
829 }
830 
831 
832 /*
833 ** Leave unique_data but wipe out config
834 **
835 */
836 
837 static int
838 unique_close(
839 	BackendDB *be,
840 	ConfigReply *cr
841 )
842 {
843 	slap_overinst *on	= (slap_overinst *) be->bd_info;
844 	unique_data **privatep = (unique_data **) &on->on_bi.bi_private;
845 	unique_data *private = *privatep;
846 
847 	Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
848 
849 	if ( private ) {
850 		unique_domain *domains = private->domains;
851 		unique_domain *legacy = private->legacy;
852 
853 		unique_free_domain ( domains );
854 		unique_free_domain ( legacy );
855 		memset ( private, 0, sizeof ( unique_data ) );
856 	}
857 
858 	return ( 0 );
859 }
860 
861 
862 /*
863 ** search callback
864 **	if this is a REP_SEARCH, count++;
865 **
866 */
867 
868 static int count_attr_cb(
869 	Operation *op,
870 	SlapReply *rs
871 )
872 {
873 	unique_counter *uc;
874 
875 	/* because you never know */
876 	if(!op || !rs) return(0);
877 
878 	/* Only search entries are interesting */
879 	if(rs->sr_type != REP_SEARCH) return(0);
880 
881 	uc = op->o_callback->sc_private;
882 
883 	/* Ignore the current entry */
884 	if ( dn_match( uc->ndn, &rs->sr_entry->e_nname )) return(0);
885 
886 	Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
887 		rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
888 
889 	uc->count++;
890 
891 	return(0);
892 }
893 
894 /* count the length of one attribute ad
895  * (and all of its values b)
896  * in the proposed filter
897  */
898 static int
899 count_filter_len(
900 	unique_domain *domain,
901 	unique_domain_uri *uri,
902 	AttributeDescription *ad,
903 	BerVarray b
904 )
905 {
906 	unique_attrs *attr;
907 	int i;
908 	int ks = 0;
909 
910 	while ( !is_at_operational( ad->ad_type ) ) {
911 		if ( uri->attrs ) {
912 			for ( attr = uri->attrs; attr; attr = attr->next ) {
913 				if ( ad == attr->attr ) {
914 					break;
915 				}
916 			}
917 			if ( ( domain->ignore && attr )
918 			     || (!domain->ignore && !attr )) {
919 				break;
920 			}
921 		}
922 		if ( b && b[0].bv_val ) {
923 			for (i = 0; b[i].bv_val; i++ ) {
924 				/* note: make room for filter escaping... */
925 				ks += ( 3 * b[i].bv_len ) + ad->ad_cname.bv_len + STRLENOF( "(=)" );
926 			}
927 		} else if ( domain->strict ) {
928 			ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" );	/* (attr=*) */
929 		}
930 		break;
931 	}
932 
933 	return ks;
934 }
935 
936 static char *
937 build_filter(
938 	unique_domain *domain,
939 	unique_domain_uri *uri,
940 	AttributeDescription *ad,
941 	BerVarray b,
942 	char *kp,
943 	int ks,
944 	void *ctx
945 )
946 {
947 	unique_attrs *attr;
948 	int i;
949 
950 	while ( !is_at_operational( ad->ad_type ) ) {
951 		if ( uri->attrs ) {
952 			for ( attr = uri->attrs; attr; attr = attr->next ) {
953 				if ( ad == attr->attr ) {
954 					break;
955 				}
956 			}
957 			if ( ( domain->ignore && attr )
958 			     || (!domain->ignore && !attr )) {
959 				break;
960 			}
961 		}
962 		if ( b && b[0].bv_val ) {
963 			for ( i = 0; b[i].bv_val; i++ ) {
964 				struct berval	bv;
965 				int len;
966 
967 				ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx );
968 				len = snprintf( kp, ks, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val );
969 				assert( len >= 0 && len < ks );
970 				kp += len;
971 				if ( bv.bv_val != b[i].bv_val ) {
972 					ber_memfree_x( bv.bv_val, ctx );
973 				}
974 			}
975 		} else if ( domain->strict ) {
976 			int len;
977 			len = snprintf( kp, ks, "(%s=*)", ad->ad_cname.bv_val );
978 			assert( len >= 0 && len < ks );
979 			kp += len;
980 		}
981 		break;
982 	}
983 	return kp;
984 }
985 
986 static int
987 unique_search(
988 	Operation *op,
989 	Operation *nop,
990 	struct berval * dn,
991 	int scope,
992 	SlapReply *rs,
993 	struct berval *key
994 )
995 {
996 	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
997 	SlapReply nrs = { REP_RESULT };
998 	slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
999 	unique_counter uq = { NULL, 0 };
1000 	int rc;
1001 
1002 	Debug(LDAP_DEBUG_TRACE, "==> unique_search %s\n", key->bv_val, 0, 0);
1003 
1004 	nop->ors_filter = str2filter_x(nop, key->bv_val);
1005 	if(nop->ors_filter == NULL) {
1006 		op->o_bd->bd_info = (BackendInfo *) on->on_info;
1007 		send_ldap_error(op, rs, LDAP_OTHER,
1008 			"unique_search invalid filter");
1009 		return(rs->sr_err);
1010 	}
1011 
1012 	nop->ors_filterstr = *key;
1013 
1014 	cb.sc_response	= (slap_response*)count_attr_cb;
1015 	cb.sc_private	= &uq;
1016 	nop->o_callback	= &cb;
1017 	nop->o_tag	= LDAP_REQ_SEARCH;
1018 	nop->ors_scope	= scope;
1019 	nop->ors_deref	= LDAP_DEREF_NEVER;
1020 	nop->ors_limit	= NULL;
1021 	nop->ors_slimit	= SLAP_NO_LIMIT;
1022 	nop->ors_tlimit	= SLAP_NO_LIMIT;
1023 	nop->ors_attrs	= slap_anlist_no_attrs;
1024 	nop->ors_attrsonly = 1;
1025 
1026 	uq.ndn = &op->o_req_ndn;
1027 
1028 	nop->o_req_ndn = *dn;
1029 	nop->o_ndn = op->o_bd->be_rootndn;
1030 
1031 	nop->o_bd = on->on_info->oi_origdb;
1032 	rc = nop->o_bd->be_search(nop, &nrs);
1033 	filter_free_x(nop, nop->ors_filter, 1);
1034 	op->o_tmpfree( key->bv_val, op->o_tmpmemctx );
1035 
1036 	if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
1037 		op->o_bd->bd_info = (BackendInfo *) on->on_info;
1038 		send_ldap_error(op, rs, rc, "unique_search failed");
1039 		return(rs->sr_err);
1040 	}
1041 
1042 	Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count, 0, 0);
1043 
1044 	if(uq.count) {
1045 		op->o_bd->bd_info = (BackendInfo *) on->on_info;
1046 		send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
1047 			"some attributes not unique");
1048 		return(rs->sr_err);
1049 	}
1050 
1051 	return(SLAP_CB_CONTINUE);
1052 }
1053 
1054 static int
1055 unique_add(
1056 	Operation *op,
1057 	SlapReply *rs
1058 )
1059 {
1060 	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
1061 	unique_data *private = (unique_data *) on->on_bi.bi_private;
1062 	unique_domain *domains = private->domains;
1063 	unique_domain *legacy = private->legacy;
1064 	unique_domain *domain;
1065 	Operation nop = *op;
1066 	Attribute *a;
1067 	char *key, *kp;
1068 	struct berval bvkey;
1069 	int rc = SLAP_CB_CONTINUE;
1070 
1071 	Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n",
1072 	      op->o_req_dn.bv_val, 0, 0);
1073 
1074 	for ( domain = legacy ? legacy : domains;
1075 	      domain;
1076 	      domain = domain->next )
1077 	{
1078 		unique_domain_uri *uri;
1079 
1080 		for ( uri = domain->uri;
1081 		      uri;
1082 		      uri = uri->next )
1083 		{
1084 			int len;
1085 			int ks = 0;
1086 
1087 			if ( uri->ndn.bv_val
1088 			     && !dnIsSuffix( &op->o_req_ndn, &uri->ndn ))
1089 				continue;
1090 
1091 			if ( uri->f ) {
1092 				if ( test_filter( NULL, op->ora_e, uri->f )
1093 					== LDAP_COMPARE_FALSE )
1094 				{
1095 					Debug( LDAP_DEBUG_TRACE,
1096 						"==> unique_add_skip<%s>\n",
1097 						op->o_req_dn.bv_val, 0, 0 );
1098 					continue;
1099 				}
1100 			}
1101 
1102 			if(!(a = op->ora_e->e_attrs)) {
1103 				op->o_bd->bd_info = (BackendInfo *) on->on_info;
1104 				send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
1105 						"unique_add() got null op.ora_e.e_attrs");
1106 				rc = rs->sr_err;
1107 				break;
1108 
1109 			} else {
1110 				for(; a; a = a->a_next) {
1111 					ks += count_filter_len ( domain,
1112 								 uri,
1113 								 a->a_desc,
1114 								 a->a_vals);
1115 				}
1116 			}
1117 
1118 			/* skip this domain-uri if it isn't involved */
1119 			if ( !ks ) continue;
1120 
1121 			/* terminating NUL */
1122 			ks += sizeof("(|)");
1123 
1124 			if ( uri->filter.bv_val && uri->filter.bv_len )
1125 				ks += uri->filter.bv_len + STRLENOF ("(&)");
1126 			kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx);
1127 
1128 			if ( uri->filter.bv_val && uri->filter.bv_len ) {
1129 				len = snprintf (kp, ks, "(&%s", uri->filter.bv_val);
1130 				assert( len >= 0 && len < ks );
1131 				kp += len;
1132 			}
1133 			len = snprintf(kp, ks - (kp - key), "(|");
1134 			assert( len >= 0 && len < ks - (kp - key) );
1135 			kp += len;
1136 
1137 			for(a = op->ora_e->e_attrs; a; a = a->a_next)
1138 				kp = build_filter(domain,
1139 						  uri,
1140 						  a->a_desc,
1141 						  a->a_vals,
1142 						  kp,
1143 						  ks - ( kp - key ),
1144 						  op->o_tmpmemctx);
1145 
1146 			len = snprintf(kp, ks - (kp - key), ")");
1147 			assert( len >= 0 && len < ks - (kp - key) );
1148 			kp += len;
1149 			if ( uri->filter.bv_val && uri->filter.bv_len ) {
1150 				len = snprintf(kp, ks - (kp - key), ")");
1151 				assert( len >= 0 && len < ks - (kp - key) );
1152 				kp += len;
1153 			}
1154 			bvkey.bv_val = key;
1155 			bvkey.bv_len = kp - key;
1156 
1157 			rc = unique_search ( op,
1158 					     &nop,
1159 					     uri->ndn.bv_val ?
1160 					     &uri->ndn :
1161 					     &op->o_bd->be_nsuffix[0],
1162 					     uri->scope,
1163 					     rs,
1164 					     &bvkey);
1165 
1166 			if ( rc != SLAP_CB_CONTINUE ) break;
1167 		}
1168 		if ( rc != SLAP_CB_CONTINUE ) break;
1169 	}
1170 
1171 	return rc;
1172 }
1173 
1174 
1175 static int
1176 unique_modify(
1177 	Operation *op,
1178 	SlapReply *rs
1179 )
1180 {
1181 	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
1182 	unique_data *private = (unique_data *) on->on_bi.bi_private;
1183 	unique_domain *domains = private->domains;
1184 	unique_domain *legacy = private->legacy;
1185 	unique_domain *domain;
1186 	Operation nop = *op;
1187 	Modifications *m;
1188 	char *key, *kp;
1189 	struct berval bvkey;
1190 	int rc = SLAP_CB_CONTINUE;
1191 
1192 	Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n",
1193 	      op->o_req_dn.bv_val, 0, 0);
1194 
1195 	for ( domain = legacy ? legacy : domains;
1196 	      domain;
1197 	      domain = domain->next )
1198 	{
1199 		unique_domain_uri *uri;
1200 
1201 		for ( uri = domain->uri;
1202 		      uri;
1203 		      uri = uri->next )
1204 		{
1205 			int len;
1206 			int ks = 0;
1207 
1208 			if ( uri->ndn.bv_val
1209 			     && !dnIsSuffix( &op->o_req_ndn, &uri->ndn ))
1210 				continue;
1211 
1212 			if ( !(m = op->orm_modlist) ) {
1213 				op->o_bd->bd_info = (BackendInfo *) on->on_info;
1214 				send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
1215 						"unique_modify() got null op.orm_modlist");
1216 				rc = rs->sr_err;
1217 				break;
1218 
1219 			} else
1220 				for ( ; m; m = m->sml_next)
1221 					if ( (m->sml_op & LDAP_MOD_OP)
1222 					     != LDAP_MOD_DELETE )
1223 						ks += count_filter_len
1224 							( domain,
1225 							  uri,
1226 							  m->sml_desc,
1227 							  m->sml_values);
1228 
1229 			/* skip this domain-uri if it isn't involved */
1230 			if ( !ks ) continue;
1231 
1232 			/* terminating NUL */
1233 			ks += sizeof("(|)");
1234 
1235 			if ( uri->filter.bv_val && uri->filter.bv_len )
1236 				ks += uri->filter.bv_len + STRLENOF ("(&)");
1237 			kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx);
1238 
1239 			if ( uri->filter.bv_val && uri->filter.bv_len ) {
1240 				len = snprintf(kp, ks, "(&%s", uri->filter.bv_val);
1241 				assert( len >= 0 && len < ks );
1242 				kp += len;
1243 			}
1244 			len = snprintf(kp, ks - (kp - key), "(|");
1245 			assert( len >= 0 && len < ks - (kp - key) );
1246 			kp += len;
1247 
1248 			for(m = op->orm_modlist; m; m = m->sml_next)
1249 				if ( (m->sml_op & LDAP_MOD_OP)
1250 				     != LDAP_MOD_DELETE )
1251 					kp = build_filter ( domain,
1252 							    uri,
1253 							    m->sml_desc,
1254 							    m->sml_values,
1255 							    kp,
1256 							    ks - (kp - key),
1257 							    op->o_tmpmemctx );
1258 
1259 			len = snprintf(kp, ks - (kp - key), ")");
1260 			assert( len >= 0 && len < ks - (kp - key) );
1261 			kp += len;
1262 			if ( uri->filter.bv_val && uri->filter.bv_len ) {
1263 				len = snprintf (kp, ks - (kp - key), ")");
1264 				assert( len >= 0 && len < ks - (kp - key) );
1265 				kp += len;
1266 			}
1267 			bvkey.bv_val = key;
1268 			bvkey.bv_len = kp - key;
1269 
1270 			rc = unique_search ( op,
1271 					     &nop,
1272 					     uri->ndn.bv_val ?
1273 					     &uri->ndn :
1274 					     &op->o_bd->be_nsuffix[0],
1275 					     uri->scope,
1276 					     rs,
1277 					     &bvkey);
1278 
1279 			if ( rc != SLAP_CB_CONTINUE ) break;
1280 		}
1281 		if ( rc != SLAP_CB_CONTINUE ) break;
1282 	}
1283 
1284 	return rc;
1285 }
1286 
1287 
1288 static int
1289 unique_modrdn(
1290 	Operation *op,
1291 	SlapReply *rs
1292 )
1293 {
1294 	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
1295 	unique_data *private = (unique_data *) on->on_bi.bi_private;
1296 	unique_domain *domains = private->domains;
1297 	unique_domain *legacy = private->legacy;
1298 	unique_domain *domain;
1299 	Operation nop = *op;
1300 	char *key, *kp;
1301 	struct berval bvkey;
1302 	LDAPRDN	newrdn;
1303 	struct berval bv[2];
1304 	int rc = SLAP_CB_CONTINUE;
1305 
1306 	Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
1307 		op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
1308 
1309 	for ( domain = legacy ? legacy : domains;
1310 	      domain;
1311 	      domain = domain->next )
1312 	{
1313 		unique_domain_uri *uri;
1314 
1315 		for ( uri = domain->uri;
1316 		      uri;
1317 		      uri = uri->next )
1318 		{
1319 			int i, len;
1320 			int ks = 0;
1321 
1322 			if ( uri->ndn.bv_val
1323 			     && !dnIsSuffix( &op->o_req_ndn, &uri->ndn )
1324 			     && (!op->orr_nnewSup
1325 				 || !dnIsSuffix( op->orr_nnewSup, &uri->ndn )))
1326 				continue;
1327 
1328 			if ( ldap_bv2rdn_x ( &op->oq_modrdn.rs_newrdn,
1329 					     &newrdn,
1330 					     (char **)&rs->sr_text,
1331 					     LDAP_DN_FORMAT_LDAP,
1332 					     op->o_tmpmemctx ) ) {
1333 				op->o_bd->bd_info = (BackendInfo *) on->on_info;
1334 				send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
1335 						"unknown type(s) used in RDN");
1336 				rc = rs->sr_err;
1337 				break;
1338 			}
1339 
1340 			rc = SLAP_CB_CONTINUE;
1341 			for ( i=0; newrdn[i]; i++) {
1342 				AttributeDescription *ad = NULL;
1343 				if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
1344 					ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
1345 					rs->sr_err = LDAP_INVALID_SYNTAX;
1346 					send_ldap_result( op, rs );
1347 					rc = rs->sr_err;
1348 					break;
1349 				}
1350 				newrdn[i]->la_private = ad;
1351 			}
1352 			if ( rc != SLAP_CB_CONTINUE ) break;
1353 
1354 			bv[1].bv_val = NULL;
1355 			bv[1].bv_len = 0;
1356 
1357 			for ( i=0; newrdn[i]; i++ ) {
1358 				bv[0] = newrdn[i]->la_value;
1359 				ks += count_filter_len ( domain,
1360 							 uri,
1361 							 newrdn[i]->la_private,
1362 							 bv);
1363 			}
1364 
1365 			/* skip this domain if it isn't involved */
1366 			if ( !ks ) continue;
1367 
1368 			/* terminating NUL */
1369 			ks += sizeof("(|)");
1370 
1371 			if ( uri->filter.bv_val && uri->filter.bv_len )
1372 				ks += uri->filter.bv_len + STRLENOF ("(&)");
1373 			kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx);
1374 
1375 			if ( uri->filter.bv_val && uri->filter.bv_len ) {
1376 				len = snprintf(kp, ks, "(&%s", uri->filter.bv_val);
1377 				assert( len >= 0 && len < ks );
1378 				kp += len;
1379 			}
1380 			len = snprintf(kp, ks - (kp - key), "(|");
1381 			assert( len >= 0 && len < ks - (kp - key) );
1382 			kp += len;
1383 
1384 			for ( i=0; newrdn[i]; i++) {
1385 				bv[0] = newrdn[i]->la_value;
1386 				kp = build_filter ( domain,
1387 						    uri,
1388 						    newrdn[i]->la_private,
1389 						    bv,
1390 						    kp,
1391 						    ks - (kp - key ),
1392 						    op->o_tmpmemctx);
1393 			}
1394 
1395 			len = snprintf(kp, ks - (kp - key), ")");
1396 			assert( len >= 0 && len < ks - (kp - key) );
1397 			kp += len;
1398 			if ( uri->filter.bv_val && uri->filter.bv_len ) {
1399 				len = snprintf (kp, ks - (kp - key), ")");
1400 				assert( len >= 0 && len < ks - (kp - key) );
1401 				kp += len;
1402 			}
1403 			bvkey.bv_val = key;
1404 			bvkey.bv_len = kp - key;
1405 
1406 			rc = unique_search ( op,
1407 					     &nop,
1408 					     uri->ndn.bv_val ?
1409 					     &uri->ndn :
1410 					     &op->o_bd->be_nsuffix[0],
1411 					     uri->scope,
1412 					     rs,
1413 					     &bvkey);
1414 
1415 			if ( rc != SLAP_CB_CONTINUE ) break;
1416 		}
1417 		if ( rc != SLAP_CB_CONTINUE ) break;
1418 	}
1419 
1420 	return rc;
1421 }
1422 
1423 /*
1424 ** init_module is last so the symbols resolve "for free" --
1425 ** it expects to be called automagically during dynamic module initialization
1426 */
1427 
1428 int
1429 unique_initialize()
1430 {
1431 	int rc;
1432 
1433 	/* statically declared just after the #includes at top */
1434 	memset (&unique, 0, sizeof(unique));
1435 
1436 	unique.on_bi.bi_type = "unique";
1437 	unique.on_bi.bi_db_init = unique_db_init;
1438 	unique.on_bi.bi_db_destroy = unique_db_destroy;
1439 	unique.on_bi.bi_db_open = unique_open;
1440 	unique.on_bi.bi_db_close = unique_close;
1441 	unique.on_bi.bi_op_add = unique_add;
1442 	unique.on_bi.bi_op_modify = unique_modify;
1443 	unique.on_bi.bi_op_modrdn = unique_modrdn;
1444 
1445 	unique.on_bi.bi_cf_ocs = uniqueocs;
1446 	rc = config_register_schema( uniquecfg, uniqueocs );
1447 	if ( rc ) return rc;
1448 
1449 	return(overlay_register(&unique));
1450 }
1451 
1452 #if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
1453 int init_module(int argc, char *argv[]) {
1454 	return unique_initialize();
1455 }
1456 #endif
1457 
1458 #endif /* SLAPD_OVER_UNIQUE */
1459