]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/unique.c
Fix typo
[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 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
33 static slap_overinst unique;
34
35 typedef struct unique_attrs_s {
36         struct unique_attrs_s *next;            /* list of attrs */
37         AttributeDescription *attr;
38 } unique_attrs;
39
40 typedef struct unique_data_s {
41         const char *message;                    /* breadcrumbs */
42         struct unique_attrs_s *attrs;           /* list of known attrs */
43         struct unique_attrs_s *ignore;          /* list of ignored attrs */
44         BerValue dn;                            /* base of "unique tree" */
45         char strict;                            /* null considered unique too */
46 } unique_data;
47
48 typedef struct unique_counter_s {
49         int count;
50 } unique_counter;
51
52 /*
53 ** allocate new unique_data;
54 ** initialize, copy basedn;
55 ** store in on_bi.bi_private;
56 **
57 */
58
59 static int unique_db_init(
60         BackendDB       *be
61 )
62 {
63         slap_overinst *on = (slap_overinst *)be->bd_info;
64         unique_data *ud   = ch_malloc(sizeof(unique_data));
65
66         /* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */
67
68         ud->message     = "_init";
69         ud->attrs       = NULL;
70         ud->ignore      = NULL;
71         ud->strict      = 0;
72
73         /* default to the base of our configured database */
74         ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
75         on->on_bi.bi_private = ud;
76
77         return 0;
78 }
79
80
81 /*
82 ** if command = attributes:
83 **      foreach argument:
84 **              convert to attribute;
85 **              add to configured attribute list;
86 ** elseif command = base:
87 **      set our basedn to argument;
88 ** else complain about invalid directive;
89 **
90 */
91
92 static int unique_config(
93         BackendDB       *be,
94         const char      *fname,
95         int             lineno,
96         int             argc,
97         char            **argv
98 )
99 {
100         slap_overinst *on = (slap_overinst *) be->bd_info;
101         unique_data *ud   = on->on_bi.bi_private;
102         unique_attrs *up;
103         const char *text;
104         AttributeDescription *ad;
105         int i;
106
107         ud->message = "_config";
108         Debug(LDAP_DEBUG_TRACE, "==> unique_config\n", 0, 0, 0);
109
110         if(!strcasecmp(*argv, "unique_attributes") ||
111            !strcasecmp(*argv, "unique_ignore")) {
112                 for(i = 1; i < argc; i++) {
113                         for(up = ud->attrs; up; up = up->next)
114                             if(!strcmp(argv[i], up->attr->ad_cname.bv_val)) {
115                                 Debug(LDAP_DEBUG_ANY,
116                                         "%s: line %d: duplicate attribute <%s>, ignored\n",
117                                         fname, lineno, argv[i]);
118                                 continue;
119                         }
120                         ad = NULL;
121                         if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
122                                 Debug(LDAP_DEBUG_ANY,
123                                         "%s: line %d: bad attribute <%s>, ignored\n",
124                                         fname, lineno, text);
125                                 continue;               /* XXX */
126                         } else if(ad->ad_next) {
127                                 Debug(LDAP_DEBUG_ANY,
128                                         "%s: line %d: multiple attributes match <%s>, ignored\n",
129                                         fname, lineno, argv[i]);
130                                 continue;
131                         }
132                         up = ch_malloc(sizeof(unique_attrs));
133                         up->attr = ad;
134                         if(!strcasecmp(*argv, "unique_ignore")) {
135                                 up->next = ud->ignore;
136                                 ud->ignore = up;
137                         } else {
138                                 up->next = ud->attrs;
139                                 ud->attrs = up;
140                         }
141                         Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n",
142                                 fname, lineno, argv[i]);
143                 }
144         } else if(!strcasecmp(*argv, "unique_strict")) {
145                 ud->strict = 1;
146         } else if(!strcasecmp(*argv, "unique_base")) {
147                 struct berval bv;
148                 ber_str2bv( argv[1], 0, 0, &bv );
149                 ch_free(ud->dn.bv_val);
150                 dnNormalize(0, NULL, NULL, &bv, &ud->dn, NULL);
151                 Debug(LDAP_DEBUG_ANY, "%s: line %d: new base dn <%s>\n",
152                         fname, lineno, argv[1]);
153         } else {
154                 return(SLAP_CONF_UNKNOWN);
155         }
156
157         return(0);
158 }
159
160
161 /*
162 ** mostly, just print the init message;
163 **
164 */
165
166 static int
167 unique_open(
168         BackendDB *be
169 )
170 {
171         slap_overinst *on       = (slap_overinst *)be->bd_info;
172         unique_data *ud         = on->on_bi.bi_private;
173         ud->message             = "_open";
174
175         Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
176
177         return(0);
178 }
179
180
181 /*
182 ** foreach configured attribute:
183 **      free it;
184 ** free our basedn;
185 ** (do not) free ud->message;
186 ** reset on_bi.bi_private;
187 ** free our config data;
188 **
189 */
190
191 static int
192 unique_close(
193         BackendDB *be
194 )
195 {
196         slap_overinst *on       = (slap_overinst *) be->bd_info;
197         unique_data *ud         = on->on_bi.bi_private;
198         unique_attrs *ii, *ij;
199         ud->message             = "_close";
200
201         Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
202
203         for(ii = ud->attrs; ii; ii = ij) {
204                 ij = ii->next;
205                 ch_free(ii);
206         }
207
208         for(ii = ud->ignore; ii; ii = ij) {
209                 ij = ii->next;
210                 ch_free(ii);
211         }
212
213         ch_free(ud->dn.bv_val);
214
215         on->on_bi.bi_private = NULL;    /* XXX */
216
217         ch_free(ud);
218
219         return(0);
220 }
221
222
223 /*
224 ** search callback
225 **      if this is a REP_SEARCH, count++;
226 **
227 */
228
229 static int count_attr_cb(
230         Operation *op,
231         SlapReply *rs
232 )
233 {
234         /* because you never know */
235         if(!op || !rs) return(0);
236
237         /* Only search entries are interesting */
238         if(rs->sr_type != REP_SEARCH) return(0);
239
240         Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
241                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
242
243         ((unique_counter*)op->o_callback->sc_private)->count++;
244
245         return(0);
246 }
247
248 static int count_filter_len(
249         unique_data *ud,
250         AttributeDescription *ad,
251         BerVarray b,
252         int ks
253 )
254 {
255         unique_attrs *up;
256         int i;
257
258         while(!is_at_operational(ad->ad_type)) {
259                 if(ud->ignore) {
260                         for(up = ud->ignore; up; up = up->next)
261                                 if(ad == up->attr) break;
262                         if(up) break;
263                 }
264                 if(ud->attrs) {
265                         for(up = ud->attrs; up; up = up->next)
266                                 if(ad == up->attr) break;
267                         if(!up) break;
268                 }
269                 if(b && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
270                         ks += b[i].bv_len + ad->ad_cname.bv_len + STRLENOF( "(=)" );
271                 else if(ud->strict)
272                         ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" ); /* (attr=*) */
273                 break;
274         }
275         return ks;
276 }
277
278 static char *build_filter(
279         unique_data *ud,
280         AttributeDescription *ad,
281         BerVarray b,
282         char *kp
283 )
284 {
285         unique_attrs *up;
286         int i;
287
288         while(!is_at_operational(ad->ad_type)) {
289                 if(ud->ignore) {
290                         for(up = ud->ignore; up; up = up->next)
291                                 if(ad == up->attr) break;
292                         if(up) break;
293                 }
294                 if(ud->attrs) {
295                         for(up = ud->attrs; up; up = up->next)
296                                 if(ad == up->attr) break;
297                         if(!up) break;
298                 }
299                 if(b && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
300                         kp += sprintf(kp, "(%s=%s)", ad->ad_cname.bv_val, b[i].bv_val);
301                 else if(ud->strict)
302                         kp += sprintf(kp, "(%s=*)", ad->ad_cname.bv_val);
303                 break;
304         }
305         return kp;
306 }
307
308 static int unique_search(
309         Operation *op,
310         Operation *nop,
311         SlapReply *rs,
312         char *key
313 )
314 {
315         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
316         unique_data *ud = on->on_bi.bi_private;
317         SlapReply nrs = { REP_RESULT };
318         slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
319         unique_counter uq = { 0 };
320         int rc;
321
322         nop->ors_filter = str2filter_x(nop, key);
323         ber_str2bv(key, 0, 0, &nop->ors_filterstr);
324
325         cb.sc_response  = (slap_response*)count_attr_cb;
326         cb.sc_private   = &uq;
327         nop->o_callback = &cb;
328         nop->o_tag      = LDAP_REQ_SEARCH;
329         nop->ors_scope  = LDAP_SCOPE_SUBTREE;
330         nop->ors_deref  = LDAP_DEREF_NEVER;
331         nop->ors_slimit = SLAP_NO_LIMIT;
332         nop->ors_tlimit = SLAP_NO_LIMIT;
333         nop->ors_attrs  = slap_anlist_no_attrs;
334         nop->ors_attrsonly = 1;
335
336         nop->o_req_ndn  = ud->dn;
337         nop->o_ndn = op->o_bd->be_rootndn;
338
339         rc = nop->o_bd->be_search(nop, &nrs);
340         filter_free_x(nop, nop->ors_filter);
341         ch_free( key );
342
343         if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
344                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
345                 send_ldap_error(op, rs, rc, "unique_search failed");
346                 return(rs->sr_err);
347         }
348
349         Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count, 0, 0);
350
351         if(uq.count) {
352                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
353                 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
354                         "some attributes not unique");
355                 return(rs->sr_err);
356         }
357
358         return(SLAP_CB_CONTINUE);
359 }
360
361 static int unique_add(
362         Operation *op,
363         SlapReply *rs
364 )
365 {
366         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
367         unique_data *ud = on->on_bi.bi_private;
368         Operation nop = *op;
369
370         Attribute *a;
371         char *key, *kp;
372         int ks = 16;
373
374         Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);
375
376         /* validate backend. Should have already been done, but whatever */
377         nop.o_bd = select_backend(&ud->dn, 0, 1);
378         if(nop.o_bd) {
379                 if (!nop.o_bd->be_search) {
380                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
381                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
382                         "backend missing search function");
383                         return(rs->sr_err);
384                 }
385         } else {
386                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
387                 send_ldap_error(op, rs, LDAP_OTHER,
388                         "no known backend? this shouldn't be happening!");
389                 return(rs->sr_err);
390         }
391
392 /*
393 ** count everything first;
394 ** allocate some memory;
395 ** write the search key;
396 **
397 */
398
399         if(!(a = op->ora_e->e_attrs)) {
400                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
401                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
402                         "unique_add() got null op.ora_e.e_attrs");
403                 return(rs->sr_err);
404         } else for(; a; a = a->a_next) {
405                 ks = count_filter_len(ud, a->a_desc, a->a_vals, ks);
406         }
407
408         key = ch_malloc(ks);
409
410         kp = key + sprintf(key, "(|");
411
412         for(a = op->ora_e->e_attrs; a; a = a->a_next) {
413                 kp = build_filter(ud, a->a_desc, a->a_vals, kp);
414         }
415
416         sprintf(kp, ")");
417
418         Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);
419
420         return unique_search(op, &nop, rs, key);
421 }
422
423
424 static int unique_modify(
425         Operation *op,
426         SlapReply *rs
427 )
428 {
429         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
430         unique_data *ud = on->on_bi.bi_private;
431         Operation nop = *op;
432
433         Modifications *m;
434         char *key, *kp;
435         int ks = 16;            /* a handful of extra bytes */
436
437         Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
438
439         nop.o_bd = select_backend(&ud->dn, 0, 1);
440         if(nop.o_bd) {
441                 if (!nop.o_bd->be_search) {
442                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
443                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
444                         "backend missing search function");
445                         return(rs->sr_err);
446                 }
447         } else {
448                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
449                 send_ldap_error(op, rs, LDAP_OTHER,
450                         "no known backend? this shouldn't be happening!");
451                 return(rs->sr_err);
452         }
453
454 /*
455 ** count everything first;
456 ** allocate some memory;
457 ** write the search key;
458 **
459 */
460
461         if(!(m = op->orm_modlist)) {
462                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
463                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
464                         "unique_modify() got null op.orm_modlist");
465                 return(rs->sr_err);
466         } else for(; m; m = m->sml_next) {
467                 if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
468                 ks = count_filter_len(ud, m->sml_desc, m->sml_values, ks);
469         }
470
471         key = ch_malloc(ks);
472
473         kp = key + sprintf(key, "(|");
474
475         for(m = op->orm_modlist; m; m = m->sml_next) {
476                 if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
477                 kp = build_filter(ud, m->sml_desc, m->sml_values, kp);
478         }
479
480         sprintf(kp, ")");
481
482         Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
483
484         return unique_search(op, &nop, rs, key);
485 }
486
487
488 static int unique_modrdn(
489         Operation *op,
490         SlapReply *rs
491 )
492 {
493         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
494         unique_data *ud = on->on_bi.bi_private;
495         Operation nop = *op;
496
497         char *key, *kp;
498         int i, rc, ks = 16;             /* a handful of extra bytes */
499         LDAPRDN newrdn;
500         struct berval bv[2];
501
502         Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
503                 op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
504
505         nop.o_bd = select_backend(&ud->dn, 0, 1);
506         if(nop.o_bd) {
507                 if (!nop.o_bd->be_search) {
508                         op->o_bd->bd_info = (BackendInfo *) on->on_info;
509                         send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
510                         "backend missing search function");
511                         return(rs->sr_err);
512                 }
513         } else {
514                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
515                 send_ldap_error(op, rs, LDAP_OTHER,
516                         "no known backend? this shouldn't be happening!");
517                 return(rs->sr_err);
518         }
519
520         if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
521                 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
522                 op->o_bd->bd_info = (BackendInfo *) on->on_info;
523                 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
524                         "unknown type(s) used in RDN");
525                 return(rs->sr_err);
526         }
527         for(i = 0; newrdn[i]; i++) {
528                 AttributeDescription *ad = NULL;
529                 if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
530                         ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
531                         rs->sr_err = LDAP_INVALID_SYNTAX;
532                         send_ldap_result( op, rs );
533                         return(rs->sr_err);
534                 }
535                 newrdn[i]->la_private = ad;
536         }
537
538         bv[1].bv_val = NULL;
539         bv[1].bv_len = 0;
540
541         for(i = 0; newrdn[i]; i++) {
542                 bv[0] = newrdn[i]->la_value;
543                 ks = count_filter_len(ud, newrdn[i]->la_private, bv, ks);
544         }
545
546         key = ch_malloc(ks);
547         kp = key + sprintf(key, "(|");
548
549         for(i = 0; newrdn[i]; i++) {
550                 bv[0] = newrdn[i]->la_value;
551                 kp = build_filter(ud, newrdn[i]->la_private, bv, kp);
552         }
553
554         sprintf(kp, ")");
555
556         Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);
557
558         return unique_search(op, &nop, rs, key);
559 }
560
561 /*
562 ** init_module is last so the symbols resolve "for free" --
563 ** it expects to be called automagically during dynamic module initialization
564 */
565
566 int unique_init() {
567
568         /* statically declared just after the #includes at top */
569         unique.on_bi.bi_type = "unique";
570         unique.on_bi.bi_db_init = unique_db_init;
571         unique.on_bi.bi_db_config = unique_config;
572         unique.on_bi.bi_db_open = unique_open;
573         unique.on_bi.bi_db_close = unique_close;
574         unique.on_bi.bi_op_add = unique_add;
575         unique.on_bi.bi_op_modify = unique_modify;
576         unique.on_bi.bi_op_modrdn = unique_modrdn;
577         unique.on_bi.bi_op_delete = NULL;
578
579         return(overlay_register(&unique));
580 }
581
582 #if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
583 int init_module(int argc, char *argv[]) {
584         return unique_init();
585 }
586 #endif
587
588 #endif /* SLAPD_OVER_UNIQUE */