unique.c 13.3 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

266
267
268
269
270
271
272
273
274
275
	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;
			}
Howard Chu's avatar
Howard Chu committed
276
		}
277
278
279
280
281
282
283
284
285
		if ( ud->attrs ) {
			for ( up = ud->attrs; up; up = up->next ) {
				if ( ad == up->attr ) {
					break;
				}
			}
			if ( !up ) {
				break;
			}
Howard Chu's avatar
Howard Chu committed
286
		}
287
288
289
290
291
292
		if ( b && b[0].bv_val ) {
			for (i = 0; b[i].bv_val; i++ ) {
				/* note: make room for filter escaping... */
				ks += ( 3 * b[i].bv_len ) + ad->ad_cname.bv_len + STRLENOF( "(=)" );
			}
		} else if ( ud->strict ) {
Howard Chu's avatar
Howard Chu committed
293
			ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" );	/* (attr=*) */
294
		}
Howard Chu's avatar
Howard Chu committed
295
296
297
298
299
300
301
302
303
		break;
	}
	return ks;
}

static char *build_filter(
	unique_data *ud,
	AttributeDescription *ad,
	BerVarray b,
304
305
	char *kp,
	void *ctx
Howard Chu's avatar
Howard Chu committed
306
307
308
309
310
)
{
	unique_attrs *up;
	int i;

311
312
313
314
315
316
317
318
319
320
	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;
			}
Howard Chu's avatar
Howard Chu committed
321
		}
322
323
324
325
326
327
328
329
330
331
332
333
334
335
		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++ ) {
				struct berval	bv;

336
				ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx );
337
				kp += sprintf( kp, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val );
338
339
340
				if ( bv.bv_val != b[i].bv_val ) {
					ber_memfree_x( bv.bv_val, ctx );
				}
341
342
343
			}
		} else if ( ud->strict ) {
			kp += sprintf( kp, "(%s=*)", ad->ad_cname.bv_val );
Howard Chu's avatar
Howard Chu committed
344
345
346
347
348
349
350
		}
		break;
	}
	return kp;
}

static int unique_search(
351
	Operation *op,
Howard Chu's avatar
Howard Chu committed
352
353
354
	Operation *nop,
	SlapReply *rs,
	char *key
355
356
)
{
Howard Chu's avatar
Howard Chu committed
357
358
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
	unique_data *ud = on->on_bi.bi_private;
359
360
	SlapReply nrs = { REP_RESULT };
	slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
361
	unique_counter uq = { NULL, 0 };
Howard Chu's avatar
Howard Chu committed
362
363
364
365
366
367
368
369
370
371
372
	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
373
	nop->ors_limit	= NULL;
Howard Chu's avatar
Howard Chu committed
374
375
376
377
378
	nop->ors_slimit	= SLAP_NO_LIMIT;
	nop->ors_tlimit	= SLAP_NO_LIMIT;
	nop->ors_attrs	= slap_anlist_no_attrs;
	nop->ors_attrsonly = 1;

379
380
	uq.ndn = &op->o_req_ndn;

Howard Chu's avatar
Howard Chu committed
381
382
383
	nop->o_req_ndn	= ud->dn;
	nop->o_ndn = op->o_bd->be_rootndn;

384
	nop->o_bd = on->on_info->oi_origdb;
Howard Chu's avatar
Howard Chu committed
385
386
	rc = nop->o_bd->be_search(nop, &nrs);
	filter_free_x(nop, nop->ors_filter);
387
	op->o_tmpfree( key, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

	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);
}

407
408
#define ALLOC_EXTRA	16	/* extra slop */

Howard Chu's avatar
Howard Chu committed
409
410
411
412
413
static int unique_add(
	Operation *op,
	SlapReply *rs
)
{
414
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
415
416
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
417
418

	Attribute *a;
419
	char *key, *kp;
420
	int ks = 0;
421
422
423

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

424
425
	if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
		return SLAP_CB_CONTINUE;
426
427
428
429
430
431
432
433
434
435
436
437
438
439

/*
** 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
440
		ks = count_filter_len(ud, a->a_desc, a->a_vals, ks);
441
442
	}

443
444
445
446
447
	if ( !ks )
		return SLAP_CB_CONTINUE;

	ks += ALLOC_EXTRA;
	key = op->o_tmpalloc(ks, op->o_tmpmemctx);
448
449
450
451

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

	for(a = op->ora_e->e_attrs; a; a = a->a_next) {
452
		kp = build_filter(ud, a->a_desc, a->a_vals, kp, op->o_tmpmemctx);
453
454
	}

Howard Chu's avatar
Howard Chu committed
455
	sprintf(kp, ")");
456
457
458

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

Howard Chu's avatar
Howard Chu committed
459
	return unique_search(op, &nop, rs, key);
460
461
462
463
464
465
466
467
468
}


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
469
470
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
471
472

	Modifications *m;
473
	char *key, *kp;
474
	int ks = 0;
475
476
477

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

478
479
	if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
		return SLAP_CB_CONTINUE;
480
481
482
483
484
485
486
487
488
489
490
491
492
493

/*
** 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
494
495
		if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
		ks = count_filter_len(ud, m->sml_desc, m->sml_values, ks);
496
497
	}

498
499
500
501
502
	if ( !ks )
		return SLAP_CB_CONTINUE;

	ks += ALLOC_EXTRA;
	key = op->o_tmpalloc(ks, op->o_tmpmemctx);
503
504
505
506

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

	for(m = op->orm_modlist; m; m = m->sml_next) {
Howard Chu's avatar
Howard Chu committed
507
 		if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
508
 		kp = build_filter(ud, m->sml_desc, m->sml_values, kp, op->o_tmpmemctx);
509
510
	}

Howard Chu's avatar
Howard Chu committed
511
	sprintf(kp, ")");
512
513
514

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

Howard Chu's avatar
Howard Chu committed
515
	return unique_search(op, &nop, rs, key);
516
517
518
519
520
521
522
523
524
}


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
525
526
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
527

528
	char *key, *kp;
529
	int i, ks = 0;
530
	LDAPRDN	newrdn;
Howard Chu's avatar
Howard Chu committed
531
	struct berval bv[2];
532
533
534
535

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

536
	if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ) && 
Howard Chu's avatar
Howard Chu committed
537
		(!op->orr_nnewSup || !dnIsSuffix( op->orr_nnewSup, &ud->dn )))
538
		return SLAP_CB_CONTINUE;
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557

	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
558
559
560
	bv[1].bv_val = NULL;
	bv[1].bv_len = 0;

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

566
567
568
569
570
	if ( !ks )
		return SLAP_CB_CONTINUE;

	ks += ALLOC_EXTRA;
	key = op->o_tmpalloc(ks, op->o_tmpmemctx);
571
572
573
	kp = key + sprintf(key, "(|");

	for(i = 0; newrdn[i]; i++) {
Howard Chu's avatar
Howard Chu committed
574
		bv[0] = newrdn[i]->la_value;
575
		kp = build_filter(ud, newrdn[i]->la_private, bv, kp, op->o_tmpmemctx);
576
577
	}

Howard Chu's avatar
Howard Chu committed
578
	sprintf(kp, ")");
579
580
581

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

Howard Chu's avatar
Howard Chu committed
582
	return unique_search(op, &nop, rs, key);
583
584
585
586
587
588
589
}

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

590
int unique_initialize() {
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607

	/* 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[]) {
608
	return unique_initialize();
609
610
611
612
}
#endif

#endif /* SLAPD_OVER_UNIQUE */