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