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