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