unique.c 37.2 KB
Newer Older
1
2
3
4
/* unique.c - attribute uniqueness module */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2004-2021 The OpenLDAP Foundation.
Howard Chu's avatar
Howard Chu committed
6
 * Portions Copyright 2004,2006-2007 Symas Corporation.
7
8
9
10
11
12
13
14
15
16
 * 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>.
 */
Howard Chu's avatar
Howard Chu committed
17
18
19
/* ACKNOWLEDGEMENTS: 
 * This work was initially developed by Symas Corporation for
 * inclusion in OpenLDAP Software, with subsequent enhancements by
Emily Backes's avatar
Emily Backes committed
20
 * Emily Backes at Symas Corporation.  This work was sponsored by
Howard Chu's avatar
Howard Chu committed
21
 * Hewlett-Packard.
22
23
24
25
26
27
28
29
30
31
32
33
 */

#include "portable.h"

#ifdef SLAPD_OVER_UNIQUE

#include <stdio.h>

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

#include "slap.h"
34
#include "config.h"
35

Howard Chu's avatar
Howard Chu committed
36
37
#define UNIQUE_DEFAULT_URI ("ldap:///??sub")

38
39
40
static slap_overinst unique;

typedef struct unique_attrs_s {
Howard Chu's avatar
Howard Chu committed
41
	struct unique_attrs_s *next;	      /* list of attrs */
42
43
44
	AttributeDescription *attr;
} unique_attrs;

Howard Chu's avatar
Howard Chu committed
45
46
typedef struct unique_domain_uri_s {
	struct unique_domain_uri_s *next;
Howard Chu's avatar
Howard Chu committed
47
48
49
	struct berval dn;
	struct berval ndn;
	struct berval filter;
50
	Filter *f;
Howard Chu's avatar
Howard Chu committed
51
52
53
54
55
56
	struct unique_attrs_s *attrs;
	int scope;
} unique_domain_uri;

typedef struct unique_domain_s {
	struct unique_domain_s *next;
Howard Chu's avatar
Howard Chu committed
57
	struct berval domain_spec;
Howard Chu's avatar
Howard Chu committed
58
59
60
	struct unique_domain_uri_s *uri;
	char ignore;                          /* polarity of attributes */
	char strict;                          /* null considered unique too */
61
	char serial;						/* serialize execution */
Howard Chu's avatar
Howard Chu committed
62
63
} unique_domain;

64
typedef struct unique_data_s {
Howard Chu's avatar
Howard Chu committed
65
66
67
	struct unique_domain_s *domains;
	struct unique_domain_s *legacy;
	char legacy_strict_set;
68
	ldap_pvt_thread_mutex_t	serial_mutex;
69
70
71
} unique_data;

typedef struct unique_counter_s {
72
	struct berval *ndn;
73
74
75
	int count;
} unique_counter;

76
77
78
79
enum {
	UNIQUE_BASE = 1,
	UNIQUE_IGNORE,
	UNIQUE_ATTR,
Howard Chu's avatar
Howard Chu committed
80
	UNIQUE_STRICT,
81
	UNIQUE_URI,
82
83
};

Howard Chu's avatar
Howard Chu committed
84
85
86
87
static ConfigDriver unique_cf_base;
static ConfigDriver unique_cf_attrs;
static ConfigDriver unique_cf_strict;
static ConfigDriver unique_cf_uri;
88
89
90

static ConfigTable uniquecfg[] = {
	{ "unique_base", "basedn", 2, 2, 0, ARG_DN|ARG_MAGIC|UNIQUE_BASE,
Howard Chu's avatar
Howard Chu committed
91
	  unique_cf_base, "( OLcfgOvAt:10.1 NAME 'olcUniqueBase' "
92
	  "DESC 'Subtree for uniqueness searches' "
Howard Chu's avatar
Howard Chu committed
93
	  "EQUALITY distinguishedNameMatch "
94
95
	  "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
	{ "unique_ignore", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_IGNORE,
Howard Chu's avatar
Howard Chu committed
96
	  unique_cf_attrs, "( OLcfgOvAt:10.2 NAME 'olcUniqueIgnore' "
97
	  "DESC 'Attributes for which uniqueness shall not be enforced' "
Howard Chu's avatar
Howard Chu committed
98
99
100
	  "EQUALITY caseIgnoreMatch "
	  "ORDERING caseIgnoreOrderingMatch "
	  "SUBSTR caseIgnoreSubstringsMatch "
101
102
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
	{ "unique_attributes", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_ATTR,
Howard Chu's avatar
Howard Chu committed
103
	  unique_cf_attrs, "( OLcfgOvAt:10.3 NAME 'olcUniqueAttribute' "
104
	  "DESC 'Attributes for which uniqueness shall be enforced' "
105
	  "EQUALITY caseIgnoreMatch "
Howard Chu's avatar
Howard Chu committed
106
107
	  "ORDERING caseIgnoreOrderingMatch "
	  "SUBSTR caseIgnoreSubstringsMatch "
108
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
Howard Chu's avatar
Howard Chu committed
109
110
	{ "unique_strict", "on|off", 1, 2, 0, ARG_MAGIC|UNIQUE_STRICT,
	  unique_cf_strict, "( OLcfgOvAt:10.4 NAME 'olcUniqueStrict' "
111
	  "DESC 'Enforce uniqueness of null values' "
Howard Chu's avatar
Howard Chu committed
112
	  "EQUALITY booleanMatch "
113
	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
Howard Chu's avatar
Howard Chu committed
114
115
116
117
118
119
120
	{ "unique_uri", "ldapuri", 2, 3, 0, ARG_MAGIC|UNIQUE_URI,
	  unique_cf_uri, "( OLcfgOvAt:10.5 NAME 'olcUniqueURI' "
	  "DESC 'List of keywords and LDAP URIs for a uniqueness domain' "
	  "EQUALITY caseExactMatch "
	  "ORDERING caseExactOrderingMatch "
	  "SUBSTR caseExactSubstringsMatch "
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
121
122
123
124
125
126
127
128
129
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs uniqueocs[] = {
	{ "( OLcfgOvOc:10.1 "
	  "NAME 'olcUniqueConfig' "
	  "DESC 'Attribute value uniqueness configuration' "
	  "SUP olcOverlayConfig "
	  "MAY ( olcUniqueBase $ olcUniqueIgnore $ "
Howard Chu's avatar
Howard Chu committed
130
131
	  "olcUniqueAttribute $ olcUniqueStrict $ "
	  "olcUniqueURI ) )",
132
133
134
135
	  Cft_Overlay, uniquecfg },
	{ NULL, 0, NULL }
};

Howard Chu's avatar
Howard Chu committed
136
137
138
139
140
141
142
143
static void
unique_free_domain_uri ( unique_domain_uri *uri )
{
	unique_domain_uri *next_uri = NULL;
	unique_attrs *attr, *next_attr = NULL;

	while ( uri ) {
		next_uri = uri->next;
Howard Chu's avatar
Howard Chu committed
144
145
146
		ch_free ( uri->dn.bv_val );
		ch_free ( uri->ndn.bv_val );
		ch_free ( uri->filter.bv_val );
147
		filter_free( uri->f );
Howard Chu's avatar
Howard Chu committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
		attr = uri->attrs;
		while ( attr ) {
			next_attr = attr->next;
			ch_free (attr);
			attr = next_attr;
		}
		ch_free ( uri );
		uri = next_uri;
	}
}

/* free an entire stack of domains */
static void
unique_free_domain ( unique_domain *domain )
{
	unique_domain *next_domain = NULL;

	while ( domain ) {
		next_domain = domain->next;
Howard Chu's avatar
Howard Chu committed
167
		ch_free ( domain->domain_spec.bv_val );
Howard Chu's avatar
Howard Chu committed
168
169
170
171
172
173
		unique_free_domain_uri ( domain->uri );
		ch_free ( domain );
		domain = next_domain;
	}
}

174
static int
Howard Chu's avatar
Howard Chu committed
175
176
177
unique_new_domain_uri ( unique_domain_uri **urip,
			const LDAPURLDesc *url_desc,
			ConfigArgs *c )
178
{
Howard Chu's avatar
Howard Chu committed
179
180
181
	int i, rc = LDAP_SUCCESS;
	unique_domain_uri *uri;
	struct berval bv = {0, NULL};
182
	BackendDB *be = (BackendDB *)c->be;
Howard Chu's avatar
Howard Chu committed
183
184
185
186
187
188
	char ** attr_str;
	AttributeDescription * ad;
	const char * text;

	uri = ch_calloc ( 1, sizeof ( unique_domain_uri ) );

189
190
191
192
193
194
195
196
	if ( url_desc->lud_host && url_desc->lud_host[0] ) {
		snprintf( c->cr_msg, sizeof( c->cr_msg ),
			  "host <%s> not allowed in URI",
			  url_desc->lud_host );
		rc = ARG_BAD_CONF;
		goto exit;
	}

Howard Chu's avatar
Howard Chu committed
197
	if ( url_desc->lud_dn && url_desc->lud_dn[0] ) {
Howard Chu's avatar
Howard Chu committed
198
		ber_str2bv( url_desc->lud_dn, 0, 0, &bv );
Howard Chu's avatar
Howard Chu committed
199
200
		rc = dnPrettyNormal( NULL,
				     &bv,
Howard Chu's avatar
Howard Chu committed
201
202
				     &uri->dn,
				     &uri->ndn,
Howard Chu's avatar
Howard Chu committed
203
204
				     NULL );
		if ( rc != LDAP_SUCCESS ) {
205
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
206
207
208
209
210
211
				  "<%s> invalid DN %d (%s)",
				  url_desc->lud_dn, rc, ldap_err2string( rc ));
			rc = ARG_BAD_CONF;
			goto exit;
		}

212
213
214
215
		if ( be->be_nsuffix == NULL ) {
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				  "suffix must be set" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
216
				c->cr_msg );
217
218
219
220
			rc = ARG_BAD_CONF;
			goto exit;
		}

Howard Chu's avatar
Howard Chu committed
221
		if ( !dnIsSuffix ( &uri->ndn, &be->be_nsuffix[0] ) ) {
222
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
223
				  "dn <%s> is not a suffix of backend base dn <%s>",
Howard Chu's avatar
Howard Chu committed
224
				  uri->dn.bv_val,
Howard Chu's avatar
Howard Chu committed
225
226
227
228
				  be->be_nsuffix[0].bv_val );
			rc = ARG_BAD_CONF;
			goto exit;
		}
229
230
231
232
233

		if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
			Debug( LDAP_DEBUG_ANY,
				"slapo-unique needs a rootdn; "
				"backend <%s> has none, YMMV.\n",
234
				be->be_nsuffix[0].bv_val );
235
		}
Howard Chu's avatar
Howard Chu committed
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
	}

	attr_str = url_desc->lud_attrs;
	if ( attr_str ) {
		for ( i=0; attr_str[i]; ++i ) {
			unique_attrs * attr;
			ad = NULL;
			if ( slap_str2ad ( attr_str[i], &ad, &text )
			     == LDAP_SUCCESS) {
				attr = ch_calloc ( 1,
						   sizeof ( unique_attrs ) );
				attr->attr = ad;
				attr->next = uri->attrs;
				uri->attrs = attr;
			} else {
251
				snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
252
253
254
255
256
257
258
259
260
261
					  "unique: attribute: %s: %s",
					  attr_str[i], text );
				rc = ARG_BAD_CONF;
				goto exit;
			}
		}
	}

	uri->scope = url_desc->lud_scope;
	if ( !uri->scope ) {
262
		snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
263
264
265
266
267
268
			  "unique: uri with base scope will always be unique");
		rc = ARG_BAD_CONF;
		goto exit;
	}

	if (url_desc->lud_filter) {
269
		char *ptr;
270
		uri->f = str2filter( url_desc->lud_filter );
271
		if ( !uri->f ) {
272
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
273
274
275
276
				  "unique: bad filter");
			rc = ARG_BAD_CONF;
			goto exit;
		}
277
		/* make sure the strfilter is in normal form (ITS#5581) */
278
		filter2bv( uri->f, &uri->filter );
279
280
281
282
283
284
285
286
		ptr = strstr( uri->filter.bv_val, "(?=" /*)*/ );
		if ( ptr != NULL && ptr <= ( uri->filter.bv_val - STRLENOF( "(?=" /*)*/ ) + uri->filter.bv_len ) )
		{
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				  "unique: bad filter");
			rc = ARG_BAD_CONF;
			goto exit;
		}
Howard Chu's avatar
Howard Chu committed
287
288
289
290
291
292
	}
exit:
	uri->next = *urip;
	*urip = uri;
	if ( rc ) {
		Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
293
			"%s: %s\n", c->log, c->cr_msg );
Howard Chu's avatar
Howard Chu committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
		unique_free_domain_uri ( uri );
		*urip = NULL;
	}
	return rc;
}

static int
unique_new_domain_uri_basic ( unique_domain_uri **urip,
			      ConfigArgs *c )
{
	LDAPURLDesc *url_desc = NULL;
	int rc;

	rc = ldap_url_parse ( UNIQUE_DEFAULT_URI, &url_desc );
	if ( rc ) return rc;
	rc = unique_new_domain_uri ( urip, url_desc, c );
	ldap_free_urldesc ( url_desc );
	return rc;
}

/* if *domain is non-null, it's pushed down the stack.
 * note that the entire stack is freed if there is an error,
 * so build added domains in a separate stack before adding them
 *
 * domain_specs look like
 *
320
 * [strict ][ignore ][serialize ]uri[[ uri]...]
Howard Chu's avatar
Howard Chu committed
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
 * e.g. "ldap:///ou=foo,o=bar?uid?sub ldap:///ou=baz,o=bar?uid?sub"
 *      "strict ldap:///ou=accounts,o=bar?uid,uidNumber?one"
 *      etc
 *
 * so finally strictness is per-domain
 * but so is ignore-state, and that would be better as a per-url thing
 */
static int
unique_new_domain ( unique_domain **domainp,
		    char *domain_spec,
		    ConfigArgs *c )
{
	char *uri_start;
	int rc = LDAP_SUCCESS;
	int uri_err = 0;
	unique_domain * domain;
	LDAPURLDesc *url_desc, *url_descs = NULL;

	Debug(LDAP_DEBUG_TRACE, "==> unique_new_domain <%s>\n",
340
	      domain_spec );
Howard Chu's avatar
Howard Chu committed
341
342

	domain = ch_calloc ( 1, sizeof (unique_domain) );
Howard Chu's avatar
Howard Chu committed
343
	ber_str2bv( domain_spec, 0, 1, &domain->domain_spec );
Howard Chu's avatar
Howard Chu committed
344
345
346
347
348
349
350

	uri_start = domain_spec;
	if ( strncasecmp ( uri_start, "ignore ",
			   STRLENOF( "ignore " ) ) == 0 ) {
		domain->ignore = 1;
		uri_start += STRLENOF( "ignore " );
	}
351
352
353
354
355
	if ( strncasecmp ( uri_start, "serialize ",
			   STRLENOF( "serialize " ) ) == 0 ) {
		domain->serial = 1;
		uri_start += STRLENOF( "serialize " );
	}
Howard Chu's avatar
Howard Chu committed
356
357
358
359
360
361
362
363
364
365
366
367
368
	if ( strncasecmp ( uri_start, "strict ",
			   STRLENOF( "strict " ) ) == 0 ) {
		domain->strict = 1;
		uri_start += STRLENOF( "strict " );
		if ( !domain->ignore
		     && strncasecmp ( uri_start, "ignore ",
				      STRLENOF( "ignore " ) ) == 0 ) {
			domain->ignore = 1;
			uri_start += STRLENOF( "ignore " );
		}
	}
	rc = ldap_url_parselist_ext ( &url_descs, uri_start, " ", 0 );
	if ( rc ) {
369
		snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
370
371
372
373
374
375
376
377
			  "<%s> invalid ldap urilist",
			  uri_start );
		rc = ARG_BAD_CONF;
		goto exit;
	}

	for ( url_desc = url_descs;
	      url_desc;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
378
	      url_desc = url_desc->lud_next ) {
Howard Chu's avatar
Howard Chu committed
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
		rc = unique_new_domain_uri ( &domain->uri,
					     url_desc,
					     c );
		if ( rc ) {
			rc = ARG_BAD_CONF;
			uri_err = 1;
			goto exit;
		}
	}

exit:
	if ( url_descs ) ldap_free_urldesc ( url_descs );
	domain->next = *domainp;
	*domainp = domain;
	if ( rc ) {
		Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
395
			"%s: %s\n", c->log, c->cr_msg );
Howard Chu's avatar
Howard Chu committed
396
397
398
399
400
401
402
403
404
405
406
407
408
409
		unique_free_domain ( domain );
		*domainp = NULL;
	}
	return rc;
}

static int
unique_cf_base( ConfigArgs *c )
{
	BackendDB *be = (BackendDB *)c->be;
	slap_overinst *on = (slap_overinst *)c->bi;
	unique_data *private = (unique_data *) on->on_bi.bi_private;
	unique_domain *domains = private->domains;
	unique_domain *legacy = private->legacy;
410
411
412
413
	int rc = ARG_BAD_CONF;

	switch ( c->op ) {
	case SLAP_CONFIG_EMIT:
Howard Chu's avatar
Howard Chu committed
414
		rc = 0;
Howard Chu's avatar
Howard Chu committed
415
		if ( legacy && legacy->uri && legacy->uri->dn.bv_val ) {
Howard Chu's avatar
Howard Chu committed
416
			rc = value_add_one ( &c->rvalue_vals,
Howard Chu's avatar
Howard Chu committed
417
					     &legacy->uri->dn );
Howard Chu's avatar
Howard Chu committed
418
419
			if ( rc ) return rc;
			rc = value_add_one ( &c->rvalue_nvals,
Howard Chu's avatar
Howard Chu committed
420
					     &legacy->uri->ndn );
Howard Chu's avatar
Howard Chu committed
421
422
423
424
			if ( rc ) return rc;
		}
		break;
	case LDAP_MOD_DELETE:
Howard Chu's avatar
Howard Chu committed
425
		assert ( legacy && legacy->uri && legacy->uri->dn.bv_val );
Howard Chu's avatar
Howard Chu committed
426
		rc = 0;
Howard Chu's avatar
Howard Chu committed
427
428
429
430
431
		ch_free ( legacy->uri->dn.bv_val );
		ch_free ( legacy->uri->ndn.bv_val );
		BER_BVZERO( &legacy->uri->dn );
		BER_BVZERO( &legacy->uri->ndn );
		if ( !legacy->uri->attrs ) {
Howard Chu's avatar
Howard Chu committed
432
433
434
435
436
437
438
439
440
441
442
			unique_free_domain_uri ( legacy->uri );
			legacy->uri = NULL;
		}
		if ( !legacy->uri && !private->legacy_strict_set ) {
			unique_free_domain ( legacy );
			private->legacy = legacy = NULL;
		}
		break;
	case LDAP_MOD_ADD:
	case SLAP_CONFIG_ADD:
		if ( domains ) {
443
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
444
445
				  "cannot set legacy attrs when URIs are present" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
446
				c->cr_msg );
Howard Chu's avatar
Howard Chu committed
447
			rc = ARG_BAD_CONF;
448
			break;
Howard Chu's avatar
Howard Chu committed
449
		}
450
451
452
453
		if ( be->be_nsuffix == NULL ) {
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				  "suffix must be set" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
454
				c->cr_msg );
455
456
457
			rc = ARG_BAD_CONF;
			break;
		}
Howard Chu's avatar
Howard Chu committed
458
459
		if ( !dnIsSuffix ( &c->value_ndn,
				   &be->be_nsuffix[0] ) ) {
460
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
461
462
				  "dn is not a suffix of backend base" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
463
				c->cr_msg );
Howard Chu's avatar
Howard Chu committed
464
			rc = ARG_BAD_CONF;
465
466
			break;
		}
Howard Chu's avatar
Howard Chu committed
467
468
469
470
471
472
473
474
		if ( !legacy ) {
			unique_new_domain ( &private->legacy,
					    UNIQUE_DEFAULT_URI,
					    c );
			legacy = private->legacy;
		}
		if ( !legacy->uri )
			unique_new_domain_uri_basic ( &legacy->uri, c );
Howard Chu's avatar
Howard Chu committed
475
476
477
478
		ch_free ( legacy->uri->dn.bv_val );
		ch_free ( legacy->uri->ndn.bv_val );
		legacy->uri->dn = c->value_dn;
		legacy->uri->ndn = c->value_ndn;
Howard Chu's avatar
Howard Chu committed
479
480
481
482
483
484
		rc = 0;
		break;
	default:
		abort();
	}

485
486
487
488
489
490
491
	if ( rc ) {
		ch_free( c->value_dn.bv_val );
		BER_BVZERO( &c->value_dn );
		ch_free( c->value_ndn.bv_val );
		BER_BVZERO( &c->value_ndn );
	}

Howard Chu's avatar
Howard Chu committed
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
	return rc;
}

static int
unique_cf_attrs( ConfigArgs *c )
{
	slap_overinst *on = (slap_overinst *)c->bi;
	unique_data *private = (unique_data *) on->on_bi.bi_private;
	unique_domain *domains = private->domains;
	unique_domain *legacy = private->legacy;
	unique_attrs *new_attrs = NULL;
	unique_attrs *attr, *next_attr, *reverse_attrs;
	unique_attrs **attrp;
	int rc = ARG_BAD_CONF;
	int i;

	switch ( c->op ) {
	case SLAP_CONFIG_EMIT:
		if ( legacy
		     && (c->type == UNIQUE_IGNORE) == legacy->ignore
		     && legacy->uri )
			for ( attr = legacy->uri->attrs;
			      attr;
			      attr = attr->next )
				value_add_one( &c->rvalue_vals,
					       &attr->attr->ad_cname );
		rc = 0;
519
520
		break;
	case LDAP_MOD_DELETE:
Howard Chu's avatar
Howard Chu committed
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
		if ( legacy
		     && (c->type == UNIQUE_IGNORE) == legacy->ignore
		     && legacy->uri
		     && legacy->uri->attrs) {
			if ( c->valx < 0 ) { /* delete all */
				for ( attr = legacy->uri->attrs;
				      attr;
				      attr = next_attr ) {
					next_attr = attr->next;
					ch_free ( attr );
				}
				legacy->uri->attrs = NULL;
			} else { /* delete by index */
				attrp = &legacy->uri->attrs;
				for ( i=0; i < c->valx; ++i )
					attrp = &(*attrp)->next;
				attr = *attrp;
				*attrp = attr->next;
				ch_free (attr);
			}
			if ( !legacy->uri->attrs
Howard Chu's avatar
Howard Chu committed
542
			     && !legacy->uri->dn.bv_val ) {
Howard Chu's avatar
Howard Chu committed
543
544
545
546
547
548
549
550
551
552
553
554
555
				unique_free_domain_uri ( legacy->uri );
				legacy->uri = NULL;
			}
			if ( !legacy->uri && !private->legacy_strict_set ) {
				unique_free_domain ( legacy );
				private->legacy = legacy = NULL;
			}
		}
		rc = 0;
		break;
	case LDAP_MOD_ADD:
	case SLAP_CONFIG_ADD:
		if ( domains ) {
556
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
557
558
				  "cannot set legacy attrs when URIs are present" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
559
				c->cr_msg );
Howard Chu's avatar
Howard Chu committed
560
561
562
563
564
565
566
			rc = ARG_BAD_CONF;
			break;
		}
		if ( legacy
		     && legacy->uri
		     && legacy->uri->attrs
		     && (c->type == UNIQUE_IGNORE) != legacy->ignore ) {
567
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
568
569
				  "cannot set both attrs and ignore-attrs" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
570
				c->cr_msg );
Howard Chu's avatar
Howard Chu committed
571
			rc = ARG_BAD_CONF;
572
			break;
Howard Chu's avatar
Howard Chu committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
		}
		if ( !legacy ) {
			unique_new_domain ( &private->legacy,
					    UNIQUE_DEFAULT_URI,
					    c );
			legacy = private->legacy;
		}
		if ( !legacy->uri )
			unique_new_domain_uri_basic ( &legacy->uri, c );
		rc = 0;
		for ( i=1; c->argv[i]; ++i ) {
			AttributeDescription * ad = NULL;
			const char * text;
			if ( slap_str2ad ( c->argv[i], &ad, &text )
			     == LDAP_SUCCESS) {

				attr = ch_calloc ( 1,
					sizeof ( unique_attrs ) );
				attr->attr = ad;
				attr->next = new_attrs;
				new_attrs = attr;
			} else {
595
				snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
596
597
598
599
600
601
602
					  "unique: attribute: %s: %s",
					  c->argv[i], text );
				for ( attr = new_attrs;
				      attr;
				      attr=next_attr ) {
					next_attr = attr->next;
					ch_free ( attr );
603
				}
Howard Chu's avatar
Howard Chu committed
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
				rc = ARG_BAD_CONF;
				break;
			}
		}
		if ( rc ) break;

		/* (nconc legacy->uri->attrs (nreverse new_attrs)) */
		reverse_attrs = NULL;
		for ( attr = new_attrs;
		      attr;
		      attr = next_attr ) {
			next_attr = attr->next;
			attr->next = reverse_attrs;
			reverse_attrs = attr;
		}
		for ( attrp = &legacy->uri->attrs;
		      *attrp;
		      attrp = &(*attrp)->next ) ;
		*attrp = reverse_attrs;
623

Howard Chu's avatar
Howard Chu committed
624
625
626
627
628
		legacy->ignore = ( c->type == UNIQUE_IGNORE );
		break;
	default:
		abort();
	}
629

Howard Chu's avatar
Howard Chu committed
630
631
	if ( rc ) {
		Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
632
			"%s: %s\n", c->log, c->cr_msg );
Howard Chu's avatar
Howard Chu committed
633
634
635
636
637
638
639
640
641
642
643
644
	}
	return rc;
}

static int
unique_cf_strict( ConfigArgs *c )
{
	slap_overinst *on = (slap_overinst *)c->bi;
	unique_data *private = (unique_data *) on->on_bi.bi_private;
	unique_domain *domains = private->domains;
	unique_domain *legacy = private->legacy;
	int rc = ARG_BAD_CONF;
645

Howard Chu's avatar
Howard Chu committed
646
647
648
649
650
651
652
653
	switch ( c->op ) {
	case SLAP_CONFIG_EMIT:
		/* We process the boolean manually instead of using
		 * ARG_ON_OFF so that we can three-state it;
		 * olcUniqueStrict is either TRUE, FALSE, or missing,
		 * and missing is necessary to add olcUniqueURIs...
		 */
		if ( private->legacy_strict_set ) {
654
			struct berval bv = legacy->strict ? slap_true_bv : slap_false_bv;
Howard Chu's avatar
Howard Chu committed
655
656
657
658
659
660
661
662
663
664
			value_add_one ( &c->rvalue_vals, &bv );
		}
		rc = 0;
		break;
	case LDAP_MOD_DELETE:
		if ( legacy ) {
			legacy->strict = 0;
			if ( ! legacy->uri ) {
				unique_free_domain ( legacy );
				private->legacy = NULL;
665
666
			}
		}
Howard Chu's avatar
Howard Chu committed
667
668
		private->legacy_strict_set = 0;
		rc = 0;
669
670
		break;
	case LDAP_MOD_ADD:
Howard Chu's avatar
Howard Chu committed
671
672
	case SLAP_CONFIG_ADD:
		if ( domains ) {
673
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
674
675
				  "cannot set legacy attrs when URIs are present" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
676
				c->cr_msg );
Howard Chu's avatar
Howard Chu committed
677
			rc = ARG_BAD_CONF;
678
			break;
Howard Chu's avatar
Howard Chu committed
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
		}
		if ( ! legacy ) {
			unique_new_domain ( &private->legacy,
					    UNIQUE_DEFAULT_URI,
					    c );
			legacy = private->legacy;
		}
		/* ... not using ARG_ON_OFF makes this necessary too */
		assert ( c->argc == 2 );
		legacy->strict = (strcasecmp ( c->argv[1], "TRUE" ) == 0);
		private->legacy_strict_set = 1;
		rc = 0;
		break;
	default:
		abort();
	}

	return rc;
}

static int
unique_cf_uri( ConfigArgs *c )
{
	slap_overinst *on = (slap_overinst *)c->bi;
	unique_data *private = (unique_data *) on->on_bi.bi_private;
	unique_domain *domains = private->domains;
	unique_domain *legacy = private->legacy;
	unique_domain *domain = NULL, **domainp = NULL;
	int rc = ARG_BAD_CONF;
	int i;

	switch ( c->op ) {
	case SLAP_CONFIG_EMIT:
		for ( domain = domains;
		      domain;
		      domain = domain->next ) {
			rc = value_add_one ( &c->rvalue_vals,
Howard Chu's avatar
Howard Chu committed
716
					     &domain->domain_spec );
Howard Chu's avatar
Howard Chu committed
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
			if ( rc ) break;
		}
		break;
	case LDAP_MOD_DELETE:
		if ( c->valx < 0 ) { /* delete them all! */
			unique_free_domain ( domains );
			private->domains = NULL;
		} else { /* delete just one */
			domainp = &private->domains;
			for ( i=0; i < c->valx && *domainp; ++i )
				domainp = &(*domainp)->next;

			/* If *domainp is null, we walked off the end
			 * of the list.  This happens when back-config
			 * and the overlay are out-of-sync, like when
			 * rejecting changes before ITS#4752 gets
			 * fixed.
			 *
			 * This should never happen, but will appear
			 * if you backport this version of
			 * slapo-unique without the config-undo fixes
			 *
			 * test024 Will hit this case in such a
			 * situation.
			 */
742
			assert (*domainp != NULL);
Howard Chu's avatar
Howard Chu committed
743
744
745
746
747
748
749
750
751

			domain = *domainp;
			*domainp = domain->next;
			domain->next = NULL;
			unique_free_domain ( domain );
		}
		rc = 0;
		break;

Josh Soref's avatar
Josh Soref committed
752
	case SLAP_CONFIG_ADD: /* fallthru */
Howard Chu's avatar
Howard Chu committed
753
754
	case LDAP_MOD_ADD:
		if ( legacy ) {
755
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
Howard Chu's avatar
Howard Chu committed
756
757
				  "cannot set Uri when legacy attrs are present" );
			Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
758
				c->cr_msg );
Howard Chu's avatar
Howard Chu committed
759
			rc = ARG_BAD_CONF;
760
761
			break;
		}
Howard Chu's avatar
Howard Chu committed
762
763
764
765
766
767
768
769
770
771
		rc = 0;
		if ( c->line ) rc = unique_new_domain ( &domain, c->line, c );
		else rc = unique_new_domain ( &domain, c->argv[1], c );
		if ( rc ) break;
		assert ( domain->next == NULL );
		for ( domainp = &private->domains;
		      *domainp;
		      domainp = &(*domainp)->next ) ;
		*domainp = domain;

772
		break;
Howard Chu's avatar
Howard Chu committed
773

774
775
776
777
778
779
780
	default:
		abort ();
	}

	return rc;
}

781
782
783
784
785
786
787
/*
** allocate new unique_data;
** initialize, copy basedn;
** store in on_bi.bi_private;
**
*/

Howard Chu's avatar
Howard Chu committed
788
789
static int
unique_db_init(
790
	BackendDB	*be,
791
	ConfigReply	*cr
792
793
794
)
{
	slap_overinst *on = (slap_overinst *)be->bd_info;
795
	unique_data *private;
796

797
	Debug(LDAP_DEBUG_TRACE, "==> unique_db_init\n" );
Howard Chu's avatar
Howard Chu committed
798

799
800
801
	private = ch_calloc ( 1, sizeof ( unique_data ) );
	ldap_pvt_thread_mutex_init( &private->serial_mutex );
	on->on_bi.bi_private = private;
802

803
	return 0;
804
805
}

Howard Chu's avatar
Howard Chu committed
806
807
static int
unique_db_destroy(
808
	BackendDB	*be,
809
	ConfigReply	*cr
810
811
)
{
812
	slap_overinst *on = (slap_overinst *)be->bd_info;
813
	unique_data *private = on->on_bi.bi_private;
Howard Chu's avatar
Howard Chu committed
814

815
	Debug(LDAP_DEBUG_TRACE, "==> unique_db_destroy\n" );
Howard Chu's avatar
Howard Chu committed
816
817
818
819

	if ( private ) {
		unique_domain *domains = private->domains;
		unique_domain *legacy = private->legacy;
820

Howard Chu's avatar
Howard Chu committed
821
822
		unique_free_domain ( domains );
		unique_free_domain ( legacy );
823
		ldap_pvt_thread_mutex_destroy( &private->serial_mutex );
Howard Chu's avatar
Howard Chu committed
824
		ch_free ( private );
825
		on->on_bi.bi_private = NULL;
826
	}
Howard Chu's avatar
Howard Chu committed
827

828
	return 0;
829
830
831
832
833
834
835
836
837
838
839
840
841
842
}


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

static int count_attr_cb(
	Operation *op,
	SlapReply *rs
)
{
843
844
	unique_counter *uc;

845
846
847
848
849
850
	/* because you never know */
	if(!op || !rs) return(0);

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

851
852
853
854
855
	uc = op->o_callback->sc_private;

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

856
	Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
857
		rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
858

859
	uc->count++;
860
861
862
863

	return(0);
}

Howard Chu's avatar
Howard Chu committed
864
865
866
867
868
869
870
871
/* count the length of one attribute ad
 * (and all of its values b)
 * in the proposed filter
 */
static int
count_filter_len(
	unique_domain *domain,
	unique_domain_uri *uri,
Howard Chu's avatar
Howard Chu committed
872
	AttributeDescription *ad,
Howard Chu's avatar
Howard Chu committed
873
	BerVarray b
Howard Chu's avatar
Howard Chu committed
874
875
)
{
Howard Chu's avatar
Howard Chu committed
876
	unique_attrs *attr;
Howard Chu's avatar
Howard Chu committed
877
	int i;
Howard Chu's avatar
Howard Chu committed
878
	int ks = 0;
879

880
	while ( !is_at_operational( ad->ad_type ) ) {
Howard Chu's avatar
Howard Chu committed
881
882
883
		if ( uri->attrs ) {
			for ( attr = uri->attrs; attr; attr = attr->next ) {
				if ( ad == attr->attr ) {
884
885
886
					break;
				}
			}
Howard Chu's avatar
Howard Chu committed
887
888
			if ( ( domain->ignore && attr )
			     || (!domain->ignore && !attr )) {
889
890
				break;
			}
Howard Chu's avatar
Howard Chu committed
891
		}
892
893
894
895
896
		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( "(=)" );
			}
Howard Chu's avatar
Howard Chu committed
897
		} else if ( domain->strict ) {
Howard Chu's avatar
Howard Chu committed
898
			ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" );	/* (attr=*) */
899
		}
Howard Chu's avatar
Howard Chu committed
900
901
		break;
	}
Howard Chu's avatar
Howard Chu committed
902

Howard Chu's avatar
Howard Chu committed
903
904
905
	return ks;
}

Howard Chu's avatar
Howard Chu committed
906
907
908
909
static char *
build_filter(
	unique_domain *domain,
	unique_domain_uri *uri,
Howard Chu's avatar
Howard Chu committed
910
911
	AttributeDescription *ad,
	BerVarray b,
912
	char *kp,
913
	int ks,
914
	void *ctx
Howard Chu's avatar
Howard Chu committed
915
916
)
{
Howard Chu's avatar
Howard Chu committed
917
	unique_attrs *attr;
Howard Chu's avatar
Howard Chu committed
918
919
	int i;

920
	while ( !is_at_operational( ad->ad_type ) ) {
Howard Chu's avatar
Howard Chu committed
921
922
923
		if ( uri->attrs ) {
			for ( attr = uri->attrs; attr; attr = attr->next ) {
				if ( ad == attr->attr ) {
924
925
926
					break;
				}
			}
Howard Chu's avatar
Howard Chu committed
927
928
			if ( ( domain->ignore && attr )
			     || (!domain->ignore && !attr )) {
929
930
931
932
933
934
				break;
			}
		}
		if ( b && b[0].bv_val ) {
			for ( i = 0; b[i].bv_val; i++ ) {
				struct berval	bv;
935
				int len;
936

937
				ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx );
938
939
				if (!b[i].bv_len)
					bv.bv_val = b[i].bv_val;
940
941
942
				len = snprintf( kp, ks, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val );
				assert( len >= 0 && len < ks );
				kp += len;
943
944
945
				if ( bv.bv_val != b[i].bv_val ) {
					ber_memfree_x( bv.bv_val, ctx );
				}
946
			}
Howard Chu's avatar
Howard Chu committed
947
		} else if ( domain->strict ) {
948
949
950
951
			int len;
			len = snprintf( kp, ks, "(%s=*)", ad->ad_cname.bv_val );
			assert( len >= 0 && len < ks );
			kp += len;
Howard Chu's avatar
Howard Chu committed
952
953
954
955
956
957
		}
		break;
	}
	return kp;
}

Howard Chu's avatar
Howard Chu committed
958
959
static int
unique_search(
960
	Operation *op,
Howard Chu's avatar
Howard Chu committed
961
	Operation *nop,
Howard Chu's avatar
Howard Chu committed
962
963
	struct berval * dn,
	int scope,
Howard Chu's avatar
Howard Chu committed
964
	SlapReply *rs,
Howard Chu's avatar
Howard Chu committed
965
	struct berval *key
966
967
)
{
Howard Chu's avatar
Howard Chu committed
968
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
969
970
	SlapReply nrs = { REP_RESULT };
	slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
971
	unique_counter uq = { NULL, 0 };
Howard Chu's avatar
Howard Chu committed
972
	int rc;
973
974
	char *errmsg;
	int errmsgsize;
Howard Chu's avatar
Howard Chu committed
975

976
	Debug(LDAP_DEBUG_TRACE, "==> unique_search %s\n", key->bv_val );
Howard Chu's avatar
Howard Chu committed
977

Howard Chu's avatar
Howard Chu committed
978
	nop->ors_filter = str2filter_x(nop, key->bv_val);
979
980
981
982
983
984
985
	if(nop->ors_filter == NULL) {
		op->o_bd->bd_info = (BackendInfo *) on->on_info;
		send_ldap_error(op, rs, LDAP_OTHER,
			"unique_search invalid filter");
		return(rs->sr_err);
	}

Howard Chu's avatar
Howard Chu committed
986
	nop->ors_filterstr = *key;
Howard Chu's avatar
Howard Chu committed
987
988
989
990
991

	cb.sc_response	= (slap_response*)count_attr_cb;
	cb.sc_private	= &uq;
	nop->o_callback	= &cb;
	nop->o_tag	= LDAP_REQ_SEARCH;
Howard Chu's avatar
Howard Chu committed
992
	nop->ors_scope	= scope;
Howard Chu's avatar
Howard Chu committed
993
	nop->ors_deref	= LDAP_DEREF_NEVER;
Howard Chu's avatar
Howard Chu committed
994
	nop->ors_limit	= NULL;
Howard Chu's avatar
Howard Chu committed
995
996
997
998
999
	nop->ors_slimit	= SLAP_NO_LIMIT;
	nop->ors_tlimit	= SLAP_NO_LIMIT;
	nop->ors_attrs	= slap_anlist_no_attrs;
	nop->ors_attrsonly = 1;

1000
	uq.ndn = &op->o_req_ndn;
For faster browsing, not all history is shown. View entire blame