]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/unique.c
ITS#4744 authzTo/authzFrom patterns are supposed to allow multiple targets.
[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-2006 The OpenLDAP Foundation.
6  * Portions Copyright 2004 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 Corp. for inclusion in
19  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_UNIQUE
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/socket.h>
30
31 #include "slap.h"
32 #include "config.h"
33
34 static slap_overinst unique;
35
36 typedef struct unique_attrs_s {
37         struct unique_attrs_s *next;            /* list of attrs */
38         AttributeDescription *attr;
39 } unique_attrs;
40
41 typedef struct unique_data_s {
42         const char *message;                    /* breadcrumbs */
43         struct unique_attrs_s *attrs;           /* list of known attrs */
44         struct unique_attrs_s *ignore;          /* list of ignored attrs */
45         BerValue dn;                            /* base of "unique tree" */
46         char strict;                            /* null considered unique too */
47 } unique_data;
48
49 typedef struct unique_counter_s {
50         struct berval *ndn;
51         int count;
52 } unique_counter;
53
54 enum {
55         UNIQUE_BASE = 1,
56         UNIQUE_IGNORE,
57         UNIQUE_ATTR,
58         UNIQUE_STRICT
59 };
60
61 static ConfigDriver unique_cf_gen;
62
63 static ConfigTable uniquecfg[] = {
64         { "unique_base", "basedn", 2, 2, 0, ARG_DN|ARG_MAGIC|UNIQUE_BASE,
65           unique_cf_gen, "( OLcfgOvAt:10.1 NAME 'olcUniqueBase' "
66           "DESC 'Subtree for uniqueness searches' "
67           "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
68         { "unique_ignore", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_IGNORE,
69           unique_cf_gen, "( OLcfgOvAt:10.2 NAME 'olcUniqueIgnore' "
70           "DESC 'Attributes for which uniqueness shall not be enforced' "
71           "SYNTAX OMsDirectoryString )", NULL, NULL },
72         { "unique_attributes", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_ATTR,
73           unique_cf_gen, "( OLcfgOvAt:10.3 NAME 'olcUniqueAttribute' "
74           "DESC 'Attributes for which uniqueness shall be enforced' "
75           "SYNTAX OMsDirectoryString )", NULL, NULL },
76         { "unique_strict", "on|off", 1, 2, 0,
77           ARG_ON_OFF|ARG_OFFSET|UNIQUE_STRICT,
78           (void *)offsetof(unique_data, strict),
79           "( OLcfgOvAt:10.4 NAME 'olcUniqueStrict' "
80           "DESC 'Enforce uniqueness of null values' "
81           "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
82         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
83 };
84
85 static ConfigOCs uniqueocs[] = {
86         { "( OLcfgOvOc:10.1 "
87           "NAME 'olcUniqueConfig' "
88           "DESC 'Attribute value uniqueness configuration' "
89           "SUP olcOverlayConfig "
90           "MAY ( olcUniqueBase $ olcUniqueIgnore $ "
91           "olcUniqueAttribute $ olcUniqueStrict ) )",
92           Cft_Overlay, uniquecfg },
93         { NULL, 0, NULL }
94 };
95
96 static int
97 unique_cf_gen( ConfigArgs *c )
98 {
99         slap_overinst *on = (slap_overinst *)c->bi;
100         unique_data *ud = (unique_data *)on->on_bi.bi_private;
101         BackendDB *be = (BackendDB *)c->be;
102         unique_attrs *up, *pup, **pupp = NULL;
103         AttributeDescription *ad;
104         const char *text;
105         int rc = ARG_BAD_CONF;
106         int i;
107
108         switch ( c->op ) {
109         case SLAP_CONFIG_EMIT:
110                 switch ( c->type ) {
111                 case UNIQUE_BASE:
112                         if ( !BER_BVISEMPTY( &ud->dn )) {
113                                 rc = value_add_one( &c->rvalue_vals, &ud->dn );
114                                 if ( rc ) return rc;
115                                 rc = value_add_one( &c->rvalue_nvals, &ud->dn );
116                                 return rc;
117                         }
118                         break;
119                 case UNIQUE_IGNORE:
120                         /* fallthrough to UNIQUE_ATTR */
121                 case UNIQUE_ATTR:
122                         if ( c->type == UNIQUE_IGNORE ) up = ud->ignore;
123                         else up = ud->attrs;
124                         while ( up ) {
125                                 value_add_one( &c->rvalue_vals,
126                                                &up->attr->ad_cname );
127                                 up = up->next;
128                         }
129                         rc = 0;
130                         break;
131                 case UNIQUE_STRICT:
132                         /* handled via ARG_OFFSET */
133                         /* fallthrough to default */
134                 default:
135                         abort ();
136                 }
137                 break;
138         case LDAP_MOD_DELETE:
139                 switch ( c->type ) {
140                 case UNIQUE_BASE:
141                         /* default to the base of our configured database */
142                         if ( ud->dn.bv_val ) ber_memfree ( ud->dn.bv_val );
143                         ber_dupbv( &ud->dn, &be->be_nsuffix[0] );
144                         rc = 0;
145                         break;
146                 case UNIQUE_IGNORE:
147                         /* fallthrough to UNIQUE_ATTR */
148                 case UNIQUE_ATTR:
149                         if ( c->type == UNIQUE_IGNORE ) pupp = &ud->ignore;
150                         else pupp = &ud->attrs;
151
152                         if ( c->valx < 0 ) {
153                                 up = *pupp;
154                                 *pupp = NULL;
155                                 while ( up ) {
156                                         pup = up;
157                                         up = up->next;
158                                         ch_free ( pup );
159                                 }
160
161                         } else {
162
163                                 /* delete from linked list */
164                                 for ( i=0; i < c->valx; ++i ) {
165                                         pupp = &(*pupp)->next;
166                                 }
167                                 up = *pupp;
168                                 *pupp = (*pupp)->next;
169
170                                 /* AttributeDescriptions are global so
171                                  * shouldn't be freed here... */
172                                 ch_free ( up );
173                         }
174                         rc = 0;
175                         break;
176                 case UNIQUE_STRICT:
177                         /* handled via ARG_OFFSET */
178                         /* fallthrough to default */
179                 default:
180                         abort ();
181                 }
182                 break;
183         case SLAP_CONFIG_ADD:
184                 /* fallthrough to LDAP_MOD_ADD */
185         case LDAP_MOD_ADD:
186                 switch ( c->type ) {
187                 case UNIQUE_BASE:
188                         if ( !dnIsSuffix ( &c->value_ndn,
189                                            &be->be_nsuffix[0] ) ) {
190                                 sprintf ( c->msg, "dn is not a suffix of backend base" );
191                                 Debug ( LDAP_DEBUG_CONFIG, "unique add: %s\n",
192                                         c->msg, NULL, NULL );
193                                 rc = ARG_BAD_CONF;
194                         }
195                         if ( ud->dn.bv_val ) ber_memfree ( ud->dn.bv_val );
196                         ud->dn = c->value_ndn;
197                         rc = 0;
198                         break;
199                 case UNIQUE_IGNORE:
200                         /* fallthrough to UNIQUE_ATTR */
201                 case UNIQUE_ATTR:
202                         rc = 0;
203                         for ( i=1; i < c->argc; ++i ) {
204                                 ad = NULL;
205                                 if ( slap_str2ad ( c->argv[i], &ad, &text )
206                                      == LDAP_SUCCESS) {
207
208                                         up = ch_malloc (
209                                                 sizeof ( unique_attrs ) );
210                                         up->attr = ad;
211                                         if ( c->type == UNIQUE_IGNORE ) {
212                                                 up->next = ud->ignore;
213                                                 ud->ignore = up;
214                                         } else {
215                                                 up->next = ud->attrs;
216                                                 ud->attrs = up;
217                                         }
218                                 } else {
219                                         Debug ( LDAP_DEBUG_CONFIG,
220                                                 "unique add: <%s>: %s\n",
221                                                 c->argv[i], text, NULL );
222                                         strncpy ( c->msg,
223                                                   text,
224                                                   SLAP_TEXT_BUFLEN-1 );
225                                         c->msg[SLAP_TEXT_BUFLEN-1] = '\0';
226                                         rc = ARG_BAD_CONF;
227                                 }
228                         }
229                         break;
230                 case UNIQUE_STRICT:
231                         /* handled via ARG_OFFSET */
232                         /* fallthrough to default */
233                 default:
234                         abort ();
235                 }
236                 break;
237         default:
238                 abort ();
239         }
240
241         return rc;
242 }
243
244 /*
245 ** allocate new unique_data;
246 ** initialize, copy basedn;
247 ** store in on_bi.bi_private;
248 **
249 */
250
251 static int unique_db_init(
252         BackendDB       *be
253 )
254 {
255         slap_overinst *on = (slap_overinst *)be->bd_info;
256         unique_data *ud   = ch_calloc(1,sizeof(unique_data));
257
258         /* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */
259
260         ud->message     = "_init";
261         on->on_bi.bi_private = ud;
262         return 0;
263 }
264
265 static int unique_db_destroy(
266         BackendDB       *be
267 )
268 {
269         slap_overinst *on = (slap_overinst *)be->bd_info;
270
271         if ( on->on_bi.bi_private ) {
272                 ch_free( on->on_bi.bi_private );
273                 on->on_bi.bi_private = NULL;
274         }
275         return 0;
276 }
277
278 /*
279 ** mostly, just print the init message;
280 **
281 */
282
283 static int
284 unique_open(
285         BackendDB *be
286 )
287 {
288         slap_overinst *on       = (slap_overinst *)be->bd_info;
289         unique_data *ud         = on->on_bi.bi_private;
290         ud->message             = "_open";
291
292         Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
293
294         if ( BER_BVISNULL( &ud->dn )) {
295                 if ( BER_BVISNULL( &be->be_nsuffix[0] ))
296                         return -1;
297
298                 /* default to the base of our configured database */
299                 ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
300         }
301         return(0);
302 }
303
304
305 /*
306 ** foreach configured attribute:
307 **      free it;
308 ** free our basedn;
309 **
310 */
311
312 static int
313 unique_close(
314         BackendDB *be
315 )
316 {
317         slap_overinst *on       = (slap_overinst *) be->bd_info;
318         unique_data *ud         = on->on_bi.bi_private;
319         unique_attrs *ii, *ij;
320         ud->message             = "_close";
321
322         Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
323
324         for(ii = ud->attrs; ii; ii = ij) {
325                 ij = ii->next;
326                 ch_free(ii);
327         }
328
329         for(ii = ud->ignore; ii; ii = ij) {
330                 ij = ii->next;
331                 ch_free(ii);
332         }
333
334         ch_free(ud->dn.bv_val);
335
336         memset( ud, 0, sizeof(*ud));
337
338         return(0);
339 }
340
341
342 /*
343 ** search callback
344 **      if this is a REP_SEARCH, count++;
345 **
346 */
347
348 static int count_attr_cb(
349         Operation *op,
350         SlapReply *rs
351 )
352 {
353         unique_counter *uc;
354
355         /* because you never know */
356         if(!op || !rs) return(0);
357
358         /* Only search entries are interesting */
359         if(rs->sr_type != REP_SEARCH) return(0);
360
361         uc = op->o_callback->sc_private;
362
363         /* Ignore the current entry */
364         if ( dn_match( uc->ndn, &rs->sr_entry->e_nname )) return(0);
365
366         Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
367                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
368
369         uc->count++;
370
371         return(0);
372 }
373
374 static int count_filter_len(
375         unique_data *ud,
376         AttributeDescription *ad,
377         BerVarray b,
378         int ks
379 )
380 {
381         unique_attrs *up;
382         int i;
383
384         while ( !is_at_operational( ad->ad_type ) ) {
385                 if ( ud->ignore ) {
386                         for ( up = ud->ignore; up; up = up->next ) {
387                                 if (ad == up->attr ) {
388                                         break;
389                                 }
390                         }
391                         if ( up ) {
392                                 break;
393                         }
394                 }
395                 if ( ud->attrs ) {
396                         for ( up = ud->attrs; up; up = up->next ) {
397                                 if ( ad == up->attr ) {
398                                         break;
399                                 }
400                         }
401                         if ( !up ) {
402                                 break;
403                         }
404                 }
405                 if ( b && b[0].bv_val ) {
406                         for (i = 0; b[i].bv_val; i++ ) {
407                                 /* note: make room for filter escaping... */
408                                 ks += ( 3 * b[i].bv_len ) + ad->ad_cname.bv_len + STRLENOF( "(=)" );
409                         }
410                 } else if ( ud->strict ) {
411                         ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" ); /* (attr=*) */
412                 }
413                 break;
414         }
415         return ks;
416 }
417
418 static char *build_filter(
419         unique_data *ud,
420         AttributeDescription *ad,
421         BerVarray b,
422         char *kp,
423         void *ctx
424 )
425 {
426         unique_attrs *up;
427         int i;
428
429         while ( !is_at_operational( ad->ad_type ) ) {
430                 if ( ud->ignore ) {
431                         for ( up = ud->ignore; up; up = up->next ) {
432                                 if ( ad == up->attr ) {
433                                         break;
434                                 }
435                         }
436                         if ( up ) {
437                                 break;
438                         }
439                 }
440                 if ( ud->attrs ) {
441                         for ( up = ud->attrs; up; up = up->next ) {
442                                 if ( ad == up->attr ) {
443                                         break;
444                                 }
445                         }
446                         if ( !up ) {
447                                 break;
448                         }
449                 }
450                 if ( b && b[0].bv_val ) {
451                         for ( i = 0; b[i].bv_val; i++ ) {
452                                 struct berval   bv;
453
454                                 ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx );
455                                 kp += sprintf( kp, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val );
456                                 if ( bv.bv_val != b[i].bv_val ) {
457                                         ber_memfree_x( bv.bv_val, ctx );
458                                 }
459                         }
460                 } else if ( ud->strict ) {
461                         kp += sprintf( kp, "(%s=*)", ad->ad_cname.bv_val );
462                 }
463                 break;
464         }
465         return kp;
466 }
467
468 static int unique_search(
469         Operation *op,
470         Operation *nop,
471         SlapReply *rs,
472         char *key
473 )
474 {
475         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
476         unique_data *ud = on->on_bi.bi_private;
477         SlapReply nrs = { REP_RESULT };
478         slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
479         unique_counter uq = { NULL, 0 };
480         int rc;
481
482         nop->ors_filter = str2filter_x(nop, key);
483         ber_str2bv(key, 0, 0, &nop->ors_filterstr);
484
485         cb.sc_response  = (slap_response*)count_attr_cb;
486         cb.sc_private   = &uq;
487         nop->o_callback = &cb;
488         nop->o_tag      = LDAP_REQ_SEARCH;
489         nop->ors_scope  = LDAP_SCOPE_SUBTREE;
490         nop->ors_deref  = LDAP_DEREF_NEVER;
491         nop->ors_limit  = NULL;
492         nop->ors_slimit = SLAP_NO_LIMIT;
493         nop->ors_tlimit = SLAP_NO_LIMIT;
494         nop->ors_attrs  = slap_anlist_no_attrs;
495         nop->ors_attrsonly = 1;
496
497         uq.ndn = &op->o_req_ndn;
498
499         nop->o_req_ndn  = ud->dn;
500         nop->o_ndn = op->o_bd->be_rootndn;
501
502         nop->o_bd = on->on_info->oi_origdb;
503         rc = nop->o_bd->be_search(nop, &nrs);
504         filter_free_x(nop, nop->ors_filter);
505         op->o_tmpfree( key, op->o_tmpmemctx );
506
507         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
508                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
509                 send_ldap_error(op, rs, rc, "unique_search failed");
510                 return(rs->sr_err);
511         }
512
513         Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count, 0, 0);
514
515         if(uq.count) {
516                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
517                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
518                         "some attributes not unique");
519                 return(rs->sr_err);
520         }
521
522         return(SLAP_CB_CONTINUE);
523 }
524
525 #define ALLOC_EXTRA     16      /* extra slop */
526
527 static int unique_add(
528         Operation *op,
529         SlapReply *rs
530 )
531 {
532         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
533         unique_data *ud = on->on_bi.bi_private;
534         Operation nop = *op;
535
536         Attribute *a;
537         char *key, *kp;
538         int ks = 0;
539
540         Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);
541
542         if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
543                 return SLAP_CB_CONTINUE;
544
545 /*
546 ** count everything first;
547 ** allocate some memory;
548 ** write the search key;
549 **
550 */
551
552         if(!(a = op->ora_e->e_attrs)) {
553                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
554                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
555                         "unique_add() got null op.ora_e.e_attrs");
556                 return(rs->sr_err);
557         } else for(; a; a = a->a_next) {
558                 ks = count_filter_len(ud, a->a_desc, a->a_vals, ks);
559         }
560
561         if ( !ks )
562                 return SLAP_CB_CONTINUE;
563
564         ks += ALLOC_EXTRA;
565         key = op->o_tmpalloc(ks, op->o_tmpmemctx);
566
567         kp = key + sprintf(key, "(|");
568
569         for(a = op->ora_e->e_attrs; a; a = a->a_next) {
570                 kp = build_filter(ud, a->a_desc, a->a_vals, kp, op->o_tmpmemctx);
571         }
572
573         sprintf(kp, ")");
574
575         Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);
576
577         return unique_search(op, &nop, rs, key);
578 }
579
580
581 static int unique_modify(
582         Operation *op,
583         SlapReply *rs
584 )
585 {
586         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
587         unique_data *ud = on->on_bi.bi_private;
588         Operation nop = *op;
589
590         Modifications *m;
591         char *key, *kp;
592         int ks = 0;
593
594         Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
595
596         if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
597                 return SLAP_CB_CONTINUE;
598
599 /*
600 ** count everything first;
601 ** allocate some memory;
602 ** write the search key;
603 **
604 */
605
606         if(!(m = op->orm_modlist)) {
607                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
608                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
609                         "unique_modify() got null op.orm_modlist");
610                 return(rs->sr_err);
611         } else for(; m; m = m->sml_next) {
612                 if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
613                 ks = count_filter_len(ud, m->sml_desc, m->sml_values, ks);
614         }
615
616         if ( !ks )
617                 return SLAP_CB_CONTINUE;
618
619         ks += ALLOC_EXTRA;
620         key = op->o_tmpalloc(ks, op->o_tmpmemctx);
621
622         kp = key + sprintf(key, "(|");
623
624         for(m = op->orm_modlist; m; m = m->sml_next) {
625                 if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
626                 kp = build_filter(ud, m->sml_desc, m->sml_values, kp, op->o_tmpmemctx);
627         }
628
629         sprintf(kp, ")");
630
631         Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
632
633         return unique_search(op, &nop, rs, key);
634 }
635
636
637 static int unique_modrdn(
638         Operation *op,
639         SlapReply *rs
640 )
641 {
642         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
643         unique_data *ud = on->on_bi.bi_private;
644         Operation nop = *op;
645
646         char *key, *kp;
647         int i, ks = 0;
648         LDAPRDN newrdn;
649         struct berval bv[2];
650
651         Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
652                 op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
653
654         if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ) && 
655                 (!op->orr_nnewSup || !dnIsSuffix( op->orr_nnewSup, &ud->dn )))
656                 return SLAP_CB_CONTINUE;
657
658         if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
659                 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
660                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
661                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
662                         "unknown type(s) used in RDN");
663                 return(rs->sr_err);
664         }
665         for(i = 0; newrdn[i]; i++) {
666                 AttributeDescription *ad = NULL;
667                 if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
668                         ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
669                         rs->sr_err = LDAP_INVALID_SYNTAX;
670                         send_ldap_result( op, rs );
671                         return(rs->sr_err);
672                 }
673                 newrdn[i]->la_private = ad;
674         }
675
676         bv[1].bv_val = NULL;
677         bv[1].bv_len = 0;
678
679         for(i = 0; newrdn[i]; i++) {
680                 bv[0] = newrdn[i]->la_value;
681                 ks = count_filter_len(ud, newrdn[i]->la_private, bv, ks);
682         }
683
684         if ( !ks )
685                 return SLAP_CB_CONTINUE;
686
687         ks += ALLOC_EXTRA;
688         key = op->o_tmpalloc(ks, op->o_tmpmemctx);
689         kp = key + sprintf(key, "(|");
690
691         for(i = 0; newrdn[i]; i++) {
692                 bv[0] = newrdn[i]->la_value;
693                 kp = build_filter(ud, newrdn[i]->la_private, bv, kp, op->o_tmpmemctx);
694         }
695
696         sprintf(kp, ")");
697
698         Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);
699
700         return unique_search(op, &nop, rs, key);
701 }
702
703 /*
704 ** init_module is last so the symbols resolve "for free" --
705 ** it expects to be called automagically during dynamic module initialization
706 */
707
708 int unique_initialize() {
709         int rc;
710
711         /* statically declared just after the #includes at top */
712         unique.on_bi.bi_type = "unique";
713         unique.on_bi.bi_db_init = unique_db_init;
714         unique.on_bi.bi_db_destroy = unique_db_destroy;
715         unique.on_bi.bi_db_open = unique_open;
716         unique.on_bi.bi_db_close = unique_close;
717         unique.on_bi.bi_op_add = unique_add;
718         unique.on_bi.bi_op_modify = unique_modify;
719         unique.on_bi.bi_op_modrdn = unique_modrdn;
720         unique.on_bi.bi_op_delete = NULL;
721
722         unique.on_bi.bi_cf_ocs = uniqueocs;
723         rc = config_register_schema( uniquecfg, uniqueocs );
724         if ( rc ) return rc;
725
726         return(overlay_register(&unique));
727 }
728
729 #if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
730 int init_module(int argc, char *argv[]) {
731         return unique_initialize();
732 }
733 #endif
734
735 #endif /* SLAPD_OVER_UNIQUE */