unique.c 13.6 KB
Newer Older
1
2
3
4
/* unique.c - attribute uniqueness module */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2004-2005 The OpenLDAP Foundation.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 * Portions Copyright 2004 Symas Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Symas Corp. for inclusion in
 * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
 */

#include "portable.h"

#ifdef SLAPD_OVER_UNIQUE

#include <stdio.h>

#include <ac/string.h>
#include <ac/socket.h>

#include "slap.h"

static slap_overinst unique;

typedef struct unique_attrs_s {
	struct unique_attrs_s *next;		/* list of attrs */
	AttributeDescription *attr;
} unique_attrs;

typedef struct unique_data_s {
	const char *message;			/* breadcrumbs */
	struct unique_attrs_s *attrs;		/* list of known attrs */
	struct unique_attrs_s *ignore;		/* list of ignored attrs */
	BerValue dn;				/* base of "unique tree" */
	char strict;				/* null considered unique too */
} unique_data;

typedef struct unique_counter_s {
49
	struct berval *ndn;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
	int count;
} unique_counter;

/*
** allocate new unique_data;
** initialize, copy basedn;
** store in on_bi.bi_private;
**
*/

static int unique_db_init(
	BackendDB	*be
)
{
	slap_overinst *on = (slap_overinst *)be->bd_info;
	unique_data *ud   = ch_malloc(sizeof(unique_data));

	/* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */

	ud->message	= "_init";
	ud->attrs	= NULL;
	ud->ignore	= NULL;
	ud->strict	= 0;

	/* default to the base of our configured database */
	ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
	on->on_bi.bi_private = ud;
77
78

	return 0;
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
}


/*
** if command = attributes:
**	foreach argument:
**		convert to attribute;
**		add to configured attribute list;
** elseif command = base:
**	set our basedn to argument;
** else complain about invalid directive;
**
*/

static int unique_config(
	BackendDB	*be,
	const char	*fname,
	int		lineno,
	int		argc,
	char		**argv
)
{
	slap_overinst *on = (slap_overinst *) be->bd_info;
	unique_data *ud	  = on->on_bi.bi_private;
	unique_attrs *up;
	const char *text;
	AttributeDescription *ad;
	int i;

	ud->message = "_config";
	Debug(LDAP_DEBUG_TRACE, "==> unique_config\n", 0, 0, 0);

	if(!strcasecmp(*argv, "unique_attributes") ||
	   !strcasecmp(*argv, "unique_ignore")) {
		for(i = 1; i < argc; i++) {
			for(up = ud->attrs; up; up = up->next)
			    if(!strcmp(argv[i], up->attr->ad_cname.bv_val)) {
				Debug(LDAP_DEBUG_ANY,
Howard Chu's avatar
Howard Chu committed
117
					"%s: line %d: duplicate attribute <%s>, ignored\n",
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
					fname, lineno, argv[i]);
				continue;
			}
			ad = NULL;
			if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
				Debug(LDAP_DEBUG_ANY,
					"%s: line %d: bad attribute <%s>, ignored\n",
					fname, lineno, text);
				continue;		/* XXX */
			} else if(ad->ad_next) {
				Debug(LDAP_DEBUG_ANY,
					"%s: line %d: multiple attributes match <%s>, ignored\n",
					fname, lineno, argv[i]);
				continue;
			}
			up = ch_malloc(sizeof(unique_attrs));
			up->attr = ad;
			if(!strcasecmp(*argv, "unique_ignore")) {
				up->next = ud->ignore;
				ud->ignore = up;
			} else {
				up->next = ud->attrs;
				ud->attrs = up;
			}
142
			Debug(LDAP_DEBUG_CONFIG, "%s: line %d: new attribute <%s>\n",
143
144
145
146
147
148
149
150
151
				fname, lineno, argv[i]);
		}
	} else if(!strcasecmp(*argv, "unique_strict")) {
		ud->strict = 1;
	} else if(!strcasecmp(*argv, "unique_base")) {
		struct berval bv;
		ber_str2bv( argv[1], 0, 0, &bv );
		ch_free(ud->dn.bv_val);
		dnNormalize(0, NULL, NULL, &bv, &ud->dn, NULL);
152
		Debug(LDAP_DEBUG_CONFIG, "%s: line %d: new base dn <%s>\n",
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
			fname, lineno, argv[1]);
	} else {
		return(SLAP_CONF_UNKNOWN);
	}

	return(0);
}


/*
** mostly, just print the init message;
**
*/

static int
unique_open(
	BackendDB *be
)
{
	slap_overinst *on	= (slap_overinst *)be->bd_info;
	unique_data *ud		= on->on_bi.bi_private;
	ud->message		= "_open";

	Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);

	return(0);
}


/*
** foreach configured attribute:
**	free it;
** free our basedn;
** (do not) free ud->message;
** reset on_bi.bi_private;
** free our config data;
**
*/

static int
unique_close(
	BackendDB *be
)
{
	slap_overinst *on	= (slap_overinst *) be->bd_info;
	unique_data *ud		= on->on_bi.bi_private;
	unique_attrs *ii, *ij;
	ud->message		= "_close";

	Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);

	for(ii = ud->attrs; ii; ii = ij) {
		ij = ii->next;
		ch_free(ii);
	}

	for(ii = ud->ignore; ii; ii = ij) {
		ij = ii->next;
		ch_free(ii);
	}

	ch_free(ud->dn.bv_val);

	on->on_bi.bi_private = NULL;	/* XXX */

	ch_free(ud);

	return(0);
}


/*
** search callback
**	if this is a REP_SEARCH, count++;
**
*/

static int count_attr_cb(
	Operation *op,
	SlapReply *rs
)
{
235
236
	unique_counter *uc;

237
238
239
240
241
242
	/* because you never know */
	if(!op || !rs) return(0);

	/* Only search entries are interesting */
	if(rs->sr_type != REP_SEARCH) return(0);

243
244
245
246
247
	uc = op->o_callback->sc_private;

	/* Ignore the current entry */
	if ( dn_match( uc->ndn, &rs->sr_entry->e_nname )) return(0);

248
249
250
	Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
		rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);

251
	uc->count++;
252
253
254
255

	return(0);
}

Howard Chu's avatar
Howard Chu committed
256
257
258
259
260
261
262
263
264
static int count_filter_len(
	unique_data *ud,
	AttributeDescription *ad,
	BerVarray b,
	int ks
)
{
	unique_attrs *up;
	int i;
265

Howard Chu's avatar
Howard Chu committed
266
267
268
269
270
271
272
273
274
275
276
277
	while(!is_at_operational(ad->ad_type)) {
		if(ud->ignore) {
			for(up = ud->ignore; up; up = up->next)
				if(ad == up->attr) break;
			if(up) break;
		}
		if(ud->attrs) {
			for(up = ud->attrs; up; up = up->next)
				if(ad == up->attr) break;
			if(!up) break;
		}
		if(b && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
Howard Chu's avatar
Howard Chu committed
278
			ks += b[i].bv_len + ad->ad_cname.bv_len + STRLENOF( "(=)" );
Howard Chu's avatar
Howard Chu committed
279
		else if(ud->strict)
Howard Chu's avatar
Howard Chu committed
280
			ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" );	/* (attr=*) */
Howard Chu's avatar
Howard Chu committed
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
		break;
	}
	return ks;
}

static char *build_filter(
	unique_data *ud,
	AttributeDescription *ad,
	BerVarray b,
	char *kp
)
{
	unique_attrs *up;
	int i;

	while(!is_at_operational(ad->ad_type)) {
		if(ud->ignore) {
			for(up = ud->ignore; up; up = up->next)
				if(ad == up->attr) break;
			if(up) break;
		}
		if(ud->attrs) {
			for(up = ud->attrs; up; up = up->next)
				if(ad == up->attr) break;
			if(!up) break;
		}
		if(b && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
			kp += sprintf(kp, "(%s=%s)", ad->ad_cname.bv_val, b[i].bv_val);
		else if(ud->strict)
			kp += sprintf(kp, "(%s=*)", ad->ad_cname.bv_val);
		break;
	}
	return kp;
}

static int unique_search(
317
	Operation *op,
Howard Chu's avatar
Howard Chu committed
318
319
320
	Operation *nop,
	SlapReply *rs,
	char *key
321
322
)
{
Howard Chu's avatar
Howard Chu committed
323
324
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
	unique_data *ud = on->on_bi.bi_private;
325
326
	SlapReply nrs = { REP_RESULT };
	slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
327
	unique_counter uq = { NULL, 0 };
Howard Chu's avatar
Howard Chu committed
328
329
330
331
332
333
334
335
336
337
338
	int rc;

	nop->ors_filter = str2filter_x(nop, key);
	ber_str2bv(key, 0, 0, &nop->ors_filterstr);

	cb.sc_response	= (slap_response*)count_attr_cb;
	cb.sc_private	= &uq;
	nop->o_callback	= &cb;
	nop->o_tag	= LDAP_REQ_SEARCH;
	nop->ors_scope	= LDAP_SCOPE_SUBTREE;
	nop->ors_deref	= LDAP_DEREF_NEVER;
Howard Chu's avatar
Howard Chu committed
339
	nop->ors_limit	= NULL;
Howard Chu's avatar
Howard Chu committed
340
341
342
343
344
	nop->ors_slimit	= SLAP_NO_LIMIT;
	nop->ors_tlimit	= SLAP_NO_LIMIT;
	nop->ors_attrs	= slap_anlist_no_attrs;
	nop->ors_attrsonly = 1;

345
346
	uq.ndn = &op->o_req_ndn;

Howard Chu's avatar
Howard Chu committed
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
	nop->o_req_ndn	= ud->dn;
	nop->o_ndn = op->o_bd->be_rootndn;

	rc = nop->o_bd->be_search(nop, &nrs);
	filter_free_x(nop, nop->ors_filter);
	ch_free( key );

	if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, rc, "unique_search failed");
		return(rs->sr_err);
	}

	Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count, 0, 0);

	if(uq.count) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
			"some attributes not unique");
		return(rs->sr_err);
	}

	return(SLAP_CB_CONTINUE);
}

static int unique_add(
	Operation *op,
	SlapReply *rs
)
{
377
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
378
379
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
380
381

	Attribute *a;
382
	char *key, *kp;
Howard Chu's avatar
Howard Chu committed
383
	int ks = 16;
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415

	Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);

	/* validate backend. Should have already been done, but whatever */
	nop.o_bd = select_backend(&ud->dn, 0, 1);
	if(nop.o_bd) {
		if (!nop.o_bd->be_search) {
			op->o_bd->bd_info = (BackendInfo *) on->on_info;
			send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
			"backend missing search function");
			return(rs->sr_err);
		}
	} else {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_OTHER,
			"no known backend? this shouldn't be happening!");
		return(rs->sr_err);
	}

/*
** count everything first;
** allocate some memory;
** write the search key;
**
*/

	if(!(a = op->ora_e->e_attrs)) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
			"unique_add() got null op.ora_e.e_attrs");
		return(rs->sr_err);
	} else for(; a; a = a->a_next) {
Howard Chu's avatar
Howard Chu committed
416
		ks = count_filter_len(ud, a->a_desc, a->a_vals, ks);
417
418
419
420
421
422
423
	}

	key = ch_malloc(ks);

	kp = key + sprintf(key, "(|");

	for(a = op->ora_e->e_attrs; a; a = a->a_next) {
Howard Chu's avatar
Howard Chu committed
424
		kp = build_filter(ud, a->a_desc, a->a_vals, kp);
425
426
	}

Howard Chu's avatar
Howard Chu committed
427
	sprintf(kp, ")");
428
429
430

	Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);

Howard Chu's avatar
Howard Chu committed
431
	return unique_search(op, &nop, rs, key);
432
433
434
435
436
437
438
439
440
}


static int unique_modify(
	Operation *op,
	SlapReply *rs
)
{
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
441
442
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
443
444

	Modifications *m;
445
	char *key, *kp;
Howard Chu's avatar
Howard Chu committed
446
	int ks = 16;		/* a handful of extra bytes */
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

	Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);

	nop.o_bd = select_backend(&ud->dn, 0, 1);
	if(nop.o_bd) {
		if (!nop.o_bd->be_search) {
			op->o_bd->bd_info = (BackendInfo *) on->on_info;
			send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
			"backend missing search function");
			return(rs->sr_err);
		}
	} else {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_OTHER,
			"no known backend? this shouldn't be happening!");
		return(rs->sr_err);
	}

/*
** count everything first;
** allocate some memory;
** write the search key;
**
*/

	if(!(m = op->orm_modlist)) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
			"unique_modify() got null op.orm_modlist");
		return(rs->sr_err);
	} else for(; m; m = m->sml_next) {
Howard Chu's avatar
Howard Chu committed
478
479
		if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
		ks = count_filter_len(ud, m->sml_desc, m->sml_values, ks);
480
481
482
483
484
485
486
	}

	key = ch_malloc(ks);

	kp = key + sprintf(key, "(|");

	for(m = op->orm_modlist; m; m = m->sml_next) {
Howard Chu's avatar
Howard Chu committed
487
488
 		if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
 		kp = build_filter(ud, m->sml_desc, m->sml_values, kp);
489
490
	}

Howard Chu's avatar
Howard Chu committed
491
	sprintf(kp, ")");
492
493
494

	Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);

Howard Chu's avatar
Howard Chu committed
495
	return unique_search(op, &nop, rs, key);
496
497
498
499
500
501
502
503
504
}


static int unique_modrdn(
	Operation *op,
	SlapReply *rs
)
{
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
505
506
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
507

508
	char *key, *kp;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
509
	int i, ks = 16;			/* a handful of extra bytes */
510
	LDAPRDN	newrdn;
Howard Chu's avatar
Howard Chu committed
511
	struct berval bv[2];
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548

	Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
		op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);

	nop.o_bd = select_backend(&ud->dn, 0, 1);
	if(nop.o_bd) {
		if (!nop.o_bd->be_search) {
			op->o_bd->bd_info = (BackendInfo *) on->on_info;
			send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
			"backend missing search function");
			return(rs->sr_err);
		}
	} else {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_OTHER,
			"no known backend? this shouldn't be happening!");
		return(rs->sr_err);
	}

	if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
		(char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
			"unknown type(s) used in RDN");
		return(rs->sr_err);
	}
	for(i = 0; newrdn[i]; i++) {
		AttributeDescription *ad = NULL;
		if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
			ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
			rs->sr_err = LDAP_INVALID_SYNTAX;
			send_ldap_result( op, rs );
			return(rs->sr_err);
		}
		newrdn[i]->la_private = ad;
	}

Howard Chu's avatar
Howard Chu committed
549
550
551
	bv[1].bv_val = NULL;
	bv[1].bv_len = 0;

552
	for(i = 0; newrdn[i]; i++) {
Howard Chu's avatar
Howard Chu committed
553
554
		bv[0] = newrdn[i]->la_value;
		ks = count_filter_len(ud, newrdn[i]->la_private, bv, ks);
555
556
557
558
559
560
	}

	key = ch_malloc(ks);
	kp = key + sprintf(key, "(|");

	for(i = 0; newrdn[i]; i++) {
Howard Chu's avatar
Howard Chu committed
561
562
		bv[0] = newrdn[i]->la_value;
		kp = build_filter(ud, newrdn[i]->la_private, bv, kp);
563
564
	}

Howard Chu's avatar
Howard Chu committed
565
	sprintf(kp, ")");
566
567
568

	Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);

Howard Chu's avatar
Howard Chu committed
569
	return unique_search(op, &nop, rs, key);
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
}

/*
** init_module is last so the symbols resolve "for free" --
** it expects to be called automagically during dynamic module initialization
*/

int unique_init() {

	/* statically declared just after the #includes at top */
	unique.on_bi.bi_type = "unique";
	unique.on_bi.bi_db_init = unique_db_init;
	unique.on_bi.bi_db_config = unique_config;
	unique.on_bi.bi_db_open = unique_open;
	unique.on_bi.bi_db_close = unique_close;
	unique.on_bi.bi_op_add = unique_add;
	unique.on_bi.bi_op_modify = unique_modify;
	unique.on_bi.bi_op_modrdn = unique_modrdn;
	unique.on_bi.bi_op_delete = NULL;

	return(overlay_register(&unique));
}

#if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
int init_module(int argc, char *argv[]) {
	return unique_init();
}
#endif

#endif /* SLAPD_OVER_UNIQUE */