unique.c 13.1 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
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
	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
)
{
412
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
413
414
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
415
416

	Attribute *a;
417
	char *key, *kp;
Howard Chu's avatar
Howard Chu committed
418
	int ks = 16;
419
420
421

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

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

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

	key = ch_malloc(ks);

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

	for(a = op->ora_e->e_attrs; a; a = a->a_next) {
446
		kp = build_filter(ud, a->a_desc, a->a_vals, kp, op->o_tmpmemctx);
447
448
	}

Howard Chu's avatar
Howard Chu committed
449
	sprintf(kp, ")");
450
451
452

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

Howard Chu's avatar
Howard Chu committed
453
	return unique_search(op, &nop, rs, key);
454
455
456
457
458
459
460
461
462
}


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
463
464
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
465
466

	Modifications *m;
467
	char *key, *kp;
Howard Chu's avatar
Howard Chu committed
468
	int ks = 16;		/* a handful of extra bytes */
469
470
471

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

472
473
	if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ))
		return SLAP_CB_CONTINUE;
474
475
476
477
478
479
480
481
482
483
484
485
486
487

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

	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
497
 		if ((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) continue;
498
 		kp = build_filter(ud, m->sml_desc, m->sml_values, kp, op->o_tmpmemctx);
499
500
	}

Howard Chu's avatar
Howard Chu committed
501
	sprintf(kp, ")");
502
503
504

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

Howard Chu's avatar
Howard Chu committed
505
	return unique_search(op, &nop, rs, key);
506
507
508
509
510
511
512
513
514
}


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
515
516
	unique_data *ud = on->on_bi.bi_private;
	Operation nop = *op;
517

518
	char *key, *kp;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
519
	int i, ks = 16;			/* a handful of extra bytes */
520
	LDAPRDN	newrdn;
Howard Chu's avatar
Howard Chu committed
521
	struct berval bv[2];
522
523
524
525

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

526
	if ( !dnIsSuffix( &op->o_req_ndn, &ud->dn ) && 
Howard Chu's avatar
Howard Chu committed
527
		(!op->orr_nnewSup || !dnIsSuffix( op->orr_nnewSup, &ud->dn )))
528
		return SLAP_CB_CONTINUE;
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547

	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
548
549
550
	bv[1].bv_val = NULL;
	bv[1].bv_len = 0;

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

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

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

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

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

Howard Chu's avatar
Howard Chu committed
568
	return unique_search(op, &nop, rs, key);
569
570
571
572
573
574
575
}

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

576
int unique_initialize() {
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593

	/* 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[]) {
594
	return unique_initialize();
595
596
597
598
}
#endif

#endif /* SLAPD_OVER_UNIQUE */