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