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