Home | History | Annotate | Line # | Download | only in overlays
unique.c revision 1.1.1.4.24.1
      1 /*	$NetBSD: unique.c,v 1.1.1.4.24.1 2014/08/10 07:09:51 tls 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