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