xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/schema_init.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: schema_init.c,v 1.1.1.7 2018/02/06 01:53:14 christos Exp $	*/
2 
3 /* schema_init.c - init builtin schema */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2017 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 
19 /*
20  * Syntaxes - implementation notes:
21  *
22  * Validate function(syntax, value):
23  *   Called before the other functions here to check if the value
24  *   is valid according to the syntax.
25  *
26  * Pretty function(syntax, input value, output prettified...):
27  *   If it exists, maps different notations of the same value to a
28  *   unique representation which can be stored in the directory and
29  *   possibly be passed to the Match/Indexer/Filter() functions.
30  *
31  *   E.g. DN "2.5.4.3 = foo\,bar, o = BAZ" -> "cn=foo\2Cbar,o=BAZ",
32  *   but unlike DN normalization, "BAZ" is not mapped to "baz".
33  */
34 
35 /*
36  * Matching rules - implementation notes:
37  *
38  * Matching rules match an attribute value (often from the directory)
39  * against an asserted value (e.g. from a filter).
40  *
41  * Invoked with validated and commonly pretty/normalized arguments, thus
42  * a number of matching rules can simply use the octetString functions.
43  *
44  * Normalize function(...input value, output normalized...):
45  *   If it exists, maps matching values to a unique representation
46  *   which is passed to the Match/Indexer/Filter() functions.
47  *
48  *   Different matching rules can normalize values of the same syntax
49  *   differently.  E.g. caseIgnore rules normalize to lowercase,
50  *   caseExact rules do not.
51  *
52  * Match function(*output matchp, ...value, asserted value):
53  *   On success, set *matchp.  0 means match.  For ORDERING/most EQUALITY,
54  *   less/greater than 0 means value less/greater than asserted.  However:
55  *
56  *   In extensible match filters, ORDERING rules match if value<asserted.
57  *
58  *   EQUALITY rules may order values differently than ORDERING rules for
59  *   speed, since EQUALITY ordering is only used for SLAP_AT_SORTED_VAL.
60  *   Some EQUALITY rules do not order values (ITS#6722).
61  *
62  * Indexer function(...attribute values, *output keysp,...):
63  *   Generates index keys for the attribute values.  Backends can store
64  *   them in an index, a {key->entry ID set} mapping, for the attribute.
65  *
66  *   A search can look up the DN/scope and asserted values in the
67  *   indexes, if any, to narrow down the number of entires to check
68  *   against the search criteria.
69  *
70  * Filter function(...asserted value, *output keysp,...):
71  *   Generates index key(s) for the asserted value, to be looked up in
72  *   the index from the Indexer function.  *keysp is an array because
73  *   substring matching rules can generate multiple lookup keys.
74  *
75  * Index keys:
76  *   A key is usually a hash of match type, attribute value and schema
77  *   info, because one index can contain keys for many filtering types.
78  *
79  *   Some indexes instead have EQUALITY keys ordered so that if
80  *   key(val1) < key(val2), then val1 < val2 by the ORDERING rule.
81  *   That way the ORDERING rule can use the EQUALITY index.
82  *
83  * Substring indexing:
84  *   This chops the attribute values up in small chunks and indexes all
85  *   possible chunks of certain sizes.  Substring filtering looks up
86  *   SOME of the asserted value's chunks, and the caller uses the
87  *   intersection of the resulting entry ID sets.
88  *   See the index_substr_* keywords in slapd.conf(5).
89  */
90 
91 #include <sys/cdefs.h>
92 __RCSID("$NetBSD: schema_init.c,v 1.1.1.7 2018/02/06 01:53:14 christos Exp $");
93 
94 #include "portable.h"
95 
96 #include <stdio.h>
97 #ifdef HAVE_LIMITS_H
98 #include <limits.h>
99 #endif
100 
101 #include <ac/ctype.h>
102 #include <ac/errno.h>
103 #include <ac/string.h>
104 #include <ac/socket.h>
105 
106 #include "slap.h"
107 #include "../../libraries/liblber/lber-int.h" /* get ber_ptrlen() */
108 
109 #include "ldap_utf8.h"
110 
111 #include "lutil.h"
112 #include "lutil_hash.h"
113 #define HASH_BYTES				LUTIL_HASH_BYTES
114 #define HASH_CONTEXT			lutil_HASH_CTX
115 #define HASH_Init(c)			lutil_HASHInit(c)
116 #define HASH_Update(c,buf,len)	lutil_HASHUpdate(c,buf,len)
117 #define HASH_Final(d,c)			lutil_HASHFinal(d,c)
118 
119 /* approx matching rules */
120 #define directoryStringApproxMatchOID	"1.3.6.1.4.1.4203.666.4.4"
121 #define directoryStringApproxMatch		approxMatch
122 #define directoryStringApproxIndexer	approxIndexer
123 #define directoryStringApproxFilter		approxFilter
124 #define IA5StringApproxMatchOID			"1.3.6.1.4.1.4203.666.4.5"
125 #define IA5StringApproxMatch			approxMatch
126 #define IA5StringApproxIndexer			approxIndexer
127 #define IA5StringApproxFilter			approxFilter
128 
129 /* Change Sequence Number (CSN) - much of this will change */
130 #define csnMatch				octetStringMatch
131 #define csnOrderingMatch		octetStringOrderingMatch
132 #define csnIndexer				generalizedTimeIndexer
133 #define csnFilter				generalizedTimeFilter
134 
135 #define authzMatch				octetStringMatch
136 
137 /* X.509 PMI ldapSyntaxes */
138 /* FIXME: need to create temporary OIDs under OpenLDAP's arc;
139  * these are currently hijacked
140  *
141  *	1.3.6.1.4.1.4203.666		OpenLDAP
142  *	1.3.6.1.4.1.4203.666.11		self-contained works
143  *	1.3.6.1.4.1.4203.666.11.10	X.509 PMI
144  *	1.3.6.1.4.1.4203.666.11.10.2	X.509 PMI ldapSyntaxes
145  *	1.3.6.1.4.1.4203.666.11.10.2.1	AttributeCertificate (supported)
146  *	1.3.6.1.4.1.4203.666.11.10.2.2	AttributeCertificateExactAssertion (supported)
147  *	1.3.6.1.4.1.4203.666.11.10.2.3	AttributeCertificateAssertion (not supported)
148  *	1.3.6.1.4.1.4203.666.11.10.2.4	AttCertPath (X-SUBST'ed right now in pmi.schema)
149  *	1.3.6.1.4.1.4203.666.11.10.2.5	PolicySyntax (X-SUBST'ed right now in pmi.schema)
150  *	1.3.6.1.4.1.4203.666.11.10.2.6	RoleSyntax (X-SUBST'ed right now in pmi.schema)
151  */
152 #if 0 /* from <draft-ietf-pkix-ldap-schema-02.txt> (expired) */
153 #define attributeCertificateSyntaxOID			"1.2.826.0.1.3344810.7.5"
154 #define attributeCertificateExactAssertionSyntaxOID	"1.2.826.0.1.3344810.7.6"
155 #define attributeCertificateAssertionSyntaxOID		"1.2.826.0.1.3344810.7.7"
156 #else /* from OpenLDAP's experimental oid arc */
157 #define X509_PMI_SyntaxOID				"1.3.6.1.4.1.4203.666.11.10.2"
158 #define attributeCertificateSyntaxOID			X509_PMI_SyntaxOID ".1"
159 #define attributeCertificateExactAssertionSyntaxOID	X509_PMI_SyntaxOID ".2"
160 #define attributeCertificateAssertionSyntaxOID		X509_PMI_SyntaxOID ".3"
161 #endif
162 
163 unsigned int index_substr_if_minlen = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT;
164 unsigned int index_substr_if_maxlen = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT;
165 unsigned int index_substr_any_len = SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT;
166 unsigned int index_substr_any_step = SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT;
167 
168 unsigned int index_intlen = SLAP_INDEX_INTLEN_DEFAULT;
169 unsigned int index_intlen_strlen = SLAP_INDEX_INTLEN_STRLEN(
170 	SLAP_INDEX_INTLEN_DEFAULT );
171 
172 ldap_pvt_thread_mutex_t	ad_index_mutex;
173 ldap_pvt_thread_mutex_t	ad_undef_mutex;
174 ldap_pvt_thread_mutex_t	oc_undef_mutex;
175 
176 static int
177 generalizedTimeValidate(
178 	Syntax *syntax,
179 	struct berval *in );
180 
181 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
182 static int
183 utcTimeValidate(
184 	Syntax *syntax,
185 	struct berval *in );
186 #endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */
187 
188 static int
189 inValidate(
190 	Syntax *syntax,
191 	struct berval *in )
192 {
193 	/* no value allowed */
194 	return LDAP_INVALID_SYNTAX;
195 }
196 
197 static int
198 blobValidate(
199 	Syntax *syntax,
200 	struct berval *in )
201 {
202 	/* any value allowed */
203 	return LDAP_SUCCESS;
204 }
205 
206 #define berValidate blobValidate
207 
208 static int
209 sequenceValidate(
210 	Syntax *syntax,
211 	struct berval *in )
212 {
213 	if ( in->bv_len < 2 ) return LDAP_INVALID_SYNTAX;
214 	if ( in->bv_val[0] != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
215 
216 	return LDAP_SUCCESS;
217 }
218 
219 /* X.509 related stuff */
220 
221 enum {
222 	SLAP_X509_V1		= 0,
223 	SLAP_X509_V2		= 1,
224 	SLAP_X509_V3		= 2
225 };
226 
227 enum {
228 	SLAP_TAG_UTCTIME		= 0x17U,
229 	SLAP_TAG_GENERALIZEDTIME	= 0x18U
230 };
231 
232 
233 #define	SLAP_X509_OPTION	(LBER_CLASS_CONTEXT|LBER_CONSTRUCTED)
234 
235 enum {
236 	SLAP_X509_OPT_C_VERSION		= SLAP_X509_OPTION + 0,
237 	SLAP_X509_OPT_C_ISSUERUNIQUEID	= LBER_CLASS_CONTEXT + 1,
238 	SLAP_X509_OPT_C_SUBJECTUNIQUEID	= LBER_CLASS_CONTEXT + 2,
239 	SLAP_X509_OPT_C_EXTENSIONS	= SLAP_X509_OPTION + 3
240 };
241 
242 enum {
243 	SLAP_X509_OPT_CL_CRLEXTENSIONS	= SLAP_X509_OPTION + 0
244 };
245 
246 /*
247 GeneralName ::= CHOICE {
248   otherName                 [0] INSTANCE OF OTHER-NAME,
249   rfc822Name                [1] IA5String,
250   dNSName                   [2] IA5String,
251   x400Address               [3] ORAddress,
252   directoryName             [4] Name,
253   ediPartyName              [5] EDIPartyName,
254   uniformResourceIdentifier [6] IA5String,
255   iPAddress                 [7] OCTET STRING,
256   registeredID              [8] OBJECT IDENTIFIER }
257 */
258 enum {
259 	SLAP_X509_GN_OTHERNAME		= SLAP_X509_OPTION + 0,
260 	SLAP_X509_GN_RFC822NAME		= SLAP_X509_OPTION + 1,
261 	SLAP_X509_GN_DNSNAME		= SLAP_X509_OPTION + 2,
262 	SLAP_X509_GN_X400ADDRESS	= SLAP_X509_OPTION + 3,
263 	SLAP_X509_GN_DIRECTORYNAME	= SLAP_X509_OPTION + 4,
264 	SLAP_X509_GN_EDIPARTYNAME	= SLAP_X509_OPTION + 5,
265 	SLAP_X509_GN_URI		= SLAP_X509_OPTION + 6,
266 	SLAP_X509_GN_IPADDRESS		= SLAP_X509_OPTION + 7,
267 	SLAP_X509_GN_REGISTEREDID	= SLAP_X509_OPTION + 8
268 };
269 
270 /* X.509 PMI related stuff */
271 enum {
272 	SLAP_X509AC_V1		= 0,
273 	SLAP_X509AC_V2		= 1
274 };
275 
276 enum {
277 	SLAP_X509AC_ISSUER	= SLAP_X509_OPTION + 0
278 };
279 
280 /* X.509 certificate validation */
281 static int
282 certificateValidate( Syntax *syntax, struct berval *in )
283 {
284 	BerElementBuffer berbuf;
285 	BerElement *ber = (BerElement *)&berbuf;
286 	ber_tag_t tag;
287 	ber_len_t len;
288 	ber_int_t version = SLAP_X509_V1;
289 
290 	ber_init2( ber, in, LBER_USE_DER );
291 	tag = ber_skip_tag( ber, &len );	/* Signed wrapper */
292 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
293 	tag = ber_skip_tag( ber, &len );	/* Sequence */
294 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
295 	tag = ber_peek_tag( ber, &len );
296 	/* Optional version */
297 	if ( tag == SLAP_X509_OPT_C_VERSION ) {
298 		tag = ber_skip_tag( ber, &len );
299 		tag = ber_get_int( ber, &version );
300 		if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
301 	}
302 	/* NOTE: don't try to parse Serial, because it might be longer
303 	 * than sizeof(ber_int_t); deferred to certificateExactNormalize() */
304 	tag = ber_skip_tag( ber, &len );	/* Serial */
305 	if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
306 	ber_skip_data( ber, len );
307 	tag = ber_skip_tag( ber, &len );	/* Signature Algorithm */
308 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
309 	ber_skip_data( ber, len );
310 	tag = ber_skip_tag( ber, &len );	/* Issuer DN */
311 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
312 	ber_skip_data( ber, len );
313 	tag = ber_skip_tag( ber, &len );	/* Validity */
314 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
315 	ber_skip_data( ber, len );
316 	tag = ber_skip_tag( ber, &len );	/* Subject DN */
317 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
318 	ber_skip_data( ber, len );
319 	tag = ber_skip_tag( ber, &len );	/* Subject PublicKeyInfo */
320 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
321 	ber_skip_data( ber, len );
322 	tag = ber_skip_tag( ber, &len );
323 	if ( tag == SLAP_X509_OPT_C_ISSUERUNIQUEID ) {	/* issuerUniqueID */
324 		if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
325 		ber_skip_data( ber, len );
326 		tag = ber_skip_tag( ber, &len );
327 	}
328 	if ( tag == SLAP_X509_OPT_C_SUBJECTUNIQUEID ) {	/* subjectUniqueID */
329 		if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
330 		ber_skip_data( ber, len );
331 		tag = ber_skip_tag( ber, &len );
332 	}
333 	if ( tag == SLAP_X509_OPT_C_EXTENSIONS ) {	/* Extensions */
334 		if ( version < SLAP_X509_V3 ) return LDAP_INVALID_SYNTAX;
335 		tag = ber_skip_tag( ber, &len );
336 		if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
337 		ber_skip_data( ber, len );
338 		tag = ber_skip_tag( ber, &len );
339 	}
340 	/* signatureAlgorithm */
341 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
342 	ber_skip_data( ber, len );
343 	tag = ber_skip_tag( ber, &len );
344 	/* Signature */
345 	if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX;
346 	ber_skip_data( ber, len );
347 	tag = ber_skip_tag( ber, &len );
348 	/* Must be at end now */
349 	if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX;
350 	return LDAP_SUCCESS;
351 }
352 
353 /* X.509 certificate list validation */
354 static int
355 checkTime( struct berval *in, struct berval *out );
356 
357 static int
358 certificateListValidate( Syntax *syntax, struct berval *in )
359 {
360 	BerElementBuffer berbuf;
361 	BerElement *ber = (BerElement *)&berbuf;
362 	ber_tag_t tag;
363 	ber_len_t len, wrapper_len;
364 	char *wrapper_start;
365 	int wrapper_ok = 0;
366 	ber_int_t version = SLAP_X509_V1;
367 	struct berval bvdn, bvtu;
368 
369 	ber_init2( ber, in, LBER_USE_DER );
370 	tag = ber_skip_tag( ber, &wrapper_len );	/* Signed wrapper */
371 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
372 	wrapper_start = ber->ber_ptr;
373 	tag = ber_skip_tag( ber, &len );	/* Sequence */
374 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
375 	tag = ber_peek_tag( ber, &len );
376 	/* Optional version */
377 	if ( tag == LBER_INTEGER ) {
378 		tag = ber_get_int( ber, &version );
379 		assert( tag == LBER_INTEGER );
380 		if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
381 	}
382 	tag = ber_skip_tag( ber, &len );	/* Signature Algorithm */
383 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
384 	ber_skip_data( ber, len );
385 	tag = ber_peek_tag( ber, &len );	/* Issuer DN */
386 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
387 	len = ber_ptrlen( ber );
388 	bvdn.bv_val = in->bv_val + len;
389 	bvdn.bv_len = in->bv_len - len;
390 	tag = ber_skip_tag( ber, &len );
391 	ber_skip_data( ber, len );
392 	tag = ber_skip_tag( ber, &len );	/* thisUpdate */
393 	/* Time is a CHOICE { UTCTime, GeneralizedTime } */
394 	if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX;
395 	bvtu.bv_val = (char *)ber->ber_ptr;
396 	bvtu.bv_len = len;
397 	ber_skip_data( ber, len );
398 	/* Optional nextUpdate */
399 	tag = ber_skip_tag( ber, &len );
400 	if ( tag == SLAP_TAG_UTCTIME || tag == SLAP_TAG_GENERALIZEDTIME ) {
401 		ber_skip_data( ber, len );
402 		tag = ber_skip_tag( ber, &len );
403 	}
404 	/* revokedCertificates - Sequence of Sequence, Optional */
405 	if ( tag == LBER_SEQUENCE ) {
406 		ber_len_t seqlen;
407 		ber_tag_t stag;
408 		stag = ber_peek_tag( ber, &seqlen );
409 		if ( stag == LBER_SEQUENCE || !len ) {
410 			/* RFC5280 requires non-empty, but X.509(2005) allows empty. */
411 			if ( len )
412 				ber_skip_data( ber, len );
413 			tag = ber_skip_tag( ber, &len );
414 		}
415 	}
416 	/* Optional Extensions - Sequence of Sequence */
417 	if ( tag == SLAP_X509_OPT_CL_CRLEXTENSIONS ) { /* ? */
418 		ber_len_t seqlen;
419 		if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
420 		tag = ber_peek_tag( ber, &seqlen );
421 		if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
422 		ber_skip_data( ber, len );
423 		tag = ber_skip_tag( ber, &len );
424 	}
425 	/* signatureAlgorithm */
426 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
427 	ber_skip_data( ber, len );
428 	tag = ber_skip_tag( ber, &len );
429 	/* Signature */
430 	if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX;
431 	ber_skip_data( ber, len );
432 	if ( ber->ber_ptr == wrapper_start + wrapper_len ) wrapper_ok = 1;
433 	tag = ber_skip_tag( ber, &len );
434 	/* Must be at end now */
435 	/* NOTE: OpenSSL tolerates CL with garbage past the end */
436 	if ( len || tag != LBER_DEFAULT ) {
437 		struct berval issuer_dn = BER_BVNULL, thisUpdate;
438 		char tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
439 		int rc;
440 
441 		if ( ! wrapper_ok ) {
442 			return LDAP_INVALID_SYNTAX;
443 		}
444 
445 		rc = dnX509normalize( &bvdn, &issuer_dn );
446 		if ( rc != LDAP_SUCCESS ) {
447 			rc = LDAP_INVALID_SYNTAX;
448 			goto done;
449 		}
450 
451 		thisUpdate.bv_val = tubuf;
452 		thisUpdate.bv_len = sizeof(tubuf);
453 		if ( checkTime( &bvtu, &thisUpdate ) ) {
454 			rc = LDAP_INVALID_SYNTAX;
455 			goto done;
456 		}
457 
458 		Debug( LDAP_DEBUG_ANY,
459 			"certificateListValidate issuer=\"%s\", thisUpdate=%s: extra cruft past end of certificateList\n",
460 			issuer_dn.bv_val, thisUpdate.bv_val, 0 );
461 
462 done:;
463 		if ( ! BER_BVISNULL( &issuer_dn ) ) {
464 			ber_memfree( issuer_dn.bv_val );
465 		}
466 
467 		return rc;
468 	}
469 
470 	return LDAP_SUCCESS;
471 }
472 
473 /* X.509 PMI Attribute Certificate Validate */
474 static int
475 attributeCertificateValidate( Syntax *syntax, struct berval *in )
476 {
477 	BerElementBuffer berbuf;
478 	BerElement *ber = (BerElement *)&berbuf;
479 	ber_tag_t tag;
480 	ber_len_t len;
481 	ber_int_t version;
482 	int cont = 0;
483 
484 	ber_init2( ber, in, LBER_USE_DER );
485 
486 	tag = ber_skip_tag( ber, &len );	/* Signed wrapper */
487 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
488 
489 	tag = ber_skip_tag( ber, &len );	/* Sequence */
490 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
491 
492 	tag = ber_peek_tag( ber, &len );	/* Version */
493 	if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
494 	tag = ber_get_int( ber, &version );	/* X.509 only allows v2 */
495 	if ( version != SLAP_X509AC_V2 ) return LDAP_INVALID_SYNTAX;
496 
497 	tag = ber_skip_tag( ber, &len );	/* Holder */
498 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
499 	ber_skip_data( ber, len );
500 
501 	tag = ber_skip_tag( ber, &len );	/* Issuer */
502 	if ( tag != SLAP_X509AC_ISSUER ) return LDAP_INVALID_SYNTAX;
503 	ber_skip_data( ber, len );
504 
505 	tag = ber_skip_tag( ber, &len );	/* Signature */
506 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
507 	ber_skip_data( ber, len );
508 
509 	tag = ber_skip_tag( ber, &len );	/* Serial number */
510 	if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
511 	ber_skip_data( ber, len );
512 
513 	tag = ber_skip_tag( ber, &len );	/* AttCertValidityPeriod */
514 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
515 	ber_skip_data( ber, len );
516 
517 	tag = ber_skip_tag( ber, &len );	/* Attributes */
518 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
519 	ber_skip_data( ber, len );
520 
521 	tag = ber_peek_tag( ber, &len );
522 
523 	if ( tag == LBER_BITSTRING ) {	/* issuerUniqueID */
524 		tag = ber_skip_tag( ber, &len );
525 		ber_skip_data( ber, len );
526 		tag = ber_peek_tag( ber, &len );
527 	}
528 
529 	if ( tag == LBER_SEQUENCE ) {	/* extensions or signatureAlgorithm */
530 		tag = ber_skip_tag( ber, &len );
531 		ber_skip_data( ber, len );
532 		cont++;
533 		tag = ber_peek_tag( ber, &len );
534 	}
535 
536 	if ( tag == LBER_SEQUENCE ) {	/* signatureAlgorithm */
537 		tag = ber_skip_tag( ber, &len );
538 		ber_skip_data( ber, len );
539 		cont++;
540 		tag = ber_peek_tag( ber, &len );
541 	}
542 
543 	if ( tag == LBER_BITSTRING ) {	/* Signature */
544 		tag = ber_skip_tag( ber, &len );
545 		ber_skip_data( ber, len );
546 		cont++;
547 		tag = ber_peek_tag( ber, &len );
548 	}
549 
550 	/* Must be at end now */
551 	if ( len != 0 || tag != LBER_DEFAULT || cont < 2 ) return LDAP_INVALID_SYNTAX;
552 
553 	return LDAP_SUCCESS;
554 }
555 
556 int
557 octetStringMatch(
558 	int *matchp,
559 	slap_mask_t flags,
560 	Syntax *syntax,
561 	MatchingRule *mr,
562 	struct berval *value,
563 	void *assertedValue )
564 {
565 	struct berval *asserted = (struct berval *) assertedValue;
566 	ber_slen_t d = (ber_slen_t) value->bv_len - (ber_slen_t) asserted->bv_len;
567 
568 	/* For speed, order first by length, then by contents */
569 	*matchp = d ? (sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1)
570 		: memcmp( value->bv_val, asserted->bv_val, value->bv_len );
571 
572 	return LDAP_SUCCESS;
573 }
574 
575 int
576 octetStringOrderingMatch(
577 	int *matchp,
578 	slap_mask_t flags,
579 	Syntax *syntax,
580 	MatchingRule *mr,
581 	struct berval *value,
582 	void *assertedValue )
583 {
584 	struct berval *asserted = (struct berval *) assertedValue;
585 	ber_len_t v_len  = value->bv_len;
586 	ber_len_t av_len = asserted->bv_len;
587 
588 	int match = memcmp( value->bv_val, asserted->bv_val,
589 		(v_len < av_len ? v_len : av_len) );
590 
591 	if( match == 0 )
592 		match = sizeof(v_len) == sizeof(int)
593 			? (int) v_len - (int) av_len
594 			: v_len < av_len ? -1 : v_len > av_len;
595 
596 	/* If used in extensible match filter, match if value < asserted */
597 	if ( flags & SLAP_MR_EXT )
598 		match = (match >= 0);
599 
600 	*matchp = match;
601 	return LDAP_SUCCESS;
602 }
603 
604 /* Initialize HASHcontext from match type and schema info */
605 static void
606 hashPreset(
607 	HASH_CONTEXT *HASHcontext,
608 	struct berval *prefix,
609 	char pre,
610 	Syntax *syntax,
611 	MatchingRule *mr)
612 {
613 	HASH_Init(HASHcontext);
614 	if(prefix && prefix->bv_len > 0) {
615 		HASH_Update(HASHcontext,
616 			(unsigned char *)prefix->bv_val, prefix->bv_len);
617 	}
618 	if(pre) HASH_Update(HASHcontext, (unsigned char*)&pre, sizeof(pre));
619 	HASH_Update(HASHcontext, (unsigned char*)syntax->ssyn_oid, syntax->ssyn_oidlen);
620 	HASH_Update(HASHcontext, (unsigned char*)mr->smr_oid, mr->smr_oidlen);
621 	return;
622 }
623 
624 /* Set HASHdigest from HASHcontext and value:len */
625 static void
626 hashIter(
627 	HASH_CONTEXT *HASHcontext,
628 	unsigned char *HASHdigest,
629 	unsigned char *value,
630 	int len)
631 {
632 	HASH_CONTEXT ctx = *HASHcontext;
633 	HASH_Update( &ctx, value, len );
634 	HASH_Final( HASHdigest, &ctx );
635 }
636 
637 /* Index generation function: Attribute values -> index hash keys */
638 int octetStringIndexer(
639 	slap_mask_t use,
640 	slap_mask_t flags,
641 	Syntax *syntax,
642 	MatchingRule *mr,
643 	struct berval *prefix,
644 	BerVarray values,
645 	BerVarray *keysp,
646 	void *ctx )
647 {
648 	int i;
649 	size_t slen, mlen;
650 	BerVarray keys;
651 	HASH_CONTEXT HASHcontext;
652 	unsigned char HASHdigest[HASH_BYTES];
653 	struct berval digest;
654 	digest.bv_val = (char *)HASHdigest;
655 	digest.bv_len = sizeof(HASHdigest);
656 
657 	for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
658 		/* just count them */
659 	}
660 
661 	/* we should have at least one value at this point */
662 	assert( i > 0 );
663 
664 	keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
665 
666 	slen = syntax->ssyn_oidlen;
667 	mlen = mr->smr_oidlen;
668 
669 	hashPreset( &HASHcontext, prefix, 0, syntax, mr);
670 	for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
671 		hashIter( &HASHcontext, HASHdigest,
672 			(unsigned char *)values[i].bv_val, values[i].bv_len );
673 		ber_dupbv_x( &keys[i], &digest, ctx );
674 	}
675 
676 	BER_BVZERO( &keys[i] );
677 
678 	*keysp = keys;
679 
680 	return LDAP_SUCCESS;
681 }
682 
683 /* Index generation function: Asserted value -> index hash key */
684 int octetStringFilter(
685 	slap_mask_t use,
686 	slap_mask_t flags,
687 	Syntax *syntax,
688 	MatchingRule *mr,
689 	struct berval *prefix,
690 	void * assertedValue,
691 	BerVarray *keysp,
692 	void *ctx )
693 {
694 	size_t slen, mlen;
695 	BerVarray keys;
696 	HASH_CONTEXT HASHcontext;
697 	unsigned char HASHdigest[HASH_BYTES];
698 	struct berval *value = (struct berval *) assertedValue;
699 	struct berval digest;
700 	digest.bv_val = (char *)HASHdigest;
701 	digest.bv_len = sizeof(HASHdigest);
702 
703 	slen = syntax->ssyn_oidlen;
704 	mlen = mr->smr_oidlen;
705 
706 	keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
707 
708 	hashPreset( &HASHcontext, prefix, 0, syntax, mr );
709 	hashIter( &HASHcontext, HASHdigest,
710 		(unsigned char *)value->bv_val, value->bv_len );
711 
712 	ber_dupbv_x( keys, &digest, ctx );
713 	BER_BVZERO( &keys[1] );
714 
715 	*keysp = keys;
716 
717 	return LDAP_SUCCESS;
718 }
719 
720 static int
721 octetStringSubstringsMatch(
722 	int *matchp,
723 	slap_mask_t flags,
724 	Syntax *syntax,
725 	MatchingRule *mr,
726 	struct berval *value,
727 	void *assertedValue )
728 {
729 	int match = 0;
730 	SubstringsAssertion *sub = assertedValue;
731 	struct berval left = *value;
732 	int i;
733 	ber_len_t inlen = 0;
734 
735 	/* Add up asserted input length */
736 	if ( !BER_BVISNULL( &sub->sa_initial ) ) {
737 		inlen += sub->sa_initial.bv_len;
738 	}
739 	if ( sub->sa_any ) {
740 		for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
741 			inlen += sub->sa_any[i].bv_len;
742 		}
743 	}
744 	if ( !BER_BVISNULL( &sub->sa_final ) ) {
745 		inlen += sub->sa_final.bv_len;
746 	}
747 
748 	if ( !BER_BVISNULL( &sub->sa_initial ) ) {
749 		if ( inlen > left.bv_len ) {
750 			match = 1;
751 			goto done;
752 		}
753 
754 		match = memcmp( sub->sa_initial.bv_val, left.bv_val,
755 			sub->sa_initial.bv_len );
756 
757 		if ( match != 0 ) {
758 			goto done;
759 		}
760 
761 		left.bv_val += sub->sa_initial.bv_len;
762 		left.bv_len -= sub->sa_initial.bv_len;
763 		inlen -= sub->sa_initial.bv_len;
764 	}
765 
766 	if ( !BER_BVISNULL( &sub->sa_final ) ) {
767 		if ( inlen > left.bv_len ) {
768 			match = 1;
769 			goto done;
770 		}
771 
772 		match = memcmp( sub->sa_final.bv_val,
773 			&left.bv_val[left.bv_len - sub->sa_final.bv_len],
774 			sub->sa_final.bv_len );
775 
776 		if ( match != 0 ) {
777 			goto done;
778 		}
779 
780 		left.bv_len -= sub->sa_final.bv_len;
781 		inlen -= sub->sa_final.bv_len;
782 	}
783 
784 	if ( sub->sa_any ) {
785 		for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
786 			ber_len_t idx;
787 			char *p;
788 
789 retry:
790 			if ( inlen > left.bv_len ) {
791 				/* not enough length */
792 				match = 1;
793 				goto done;
794 			}
795 
796 			if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) {
797 				continue;
798 			}
799 
800 			p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len );
801 
802 			if( p == NULL ) {
803 				match = 1;
804 				goto done;
805 			}
806 
807 			idx = p - left.bv_val;
808 
809 			if ( idx >= left.bv_len ) {
810 				/* this shouldn't happen */
811 				return LDAP_OTHER;
812 			}
813 
814 			left.bv_val = p;
815 			left.bv_len -= idx;
816 
817 			if ( sub->sa_any[i].bv_len > left.bv_len ) {
818 				/* not enough left */
819 				match = 1;
820 				goto done;
821 			}
822 
823 			match = memcmp( left.bv_val,
824 				sub->sa_any[i].bv_val,
825 				sub->sa_any[i].bv_len );
826 
827 			if ( match != 0 ) {
828 				left.bv_val++;
829 				left.bv_len--;
830 				goto retry;
831 			}
832 
833 			left.bv_val += sub->sa_any[i].bv_len;
834 			left.bv_len -= sub->sa_any[i].bv_len;
835 			inlen -= sub->sa_any[i].bv_len;
836 		}
837 	}
838 
839 done:
840 	*matchp = match;
841 	return LDAP_SUCCESS;
842 }
843 
844 /* Substring index generation function: Attribute values -> index hash keys */
845 static int
846 octetStringSubstringsIndexer(
847 	slap_mask_t use,
848 	slap_mask_t flags,
849 	Syntax *syntax,
850 	MatchingRule *mr,
851 	struct berval *prefix,
852 	BerVarray values,
853 	BerVarray *keysp,
854 	void *ctx )
855 {
856 	ber_len_t i, nkeys;
857 	size_t slen, mlen;
858 	BerVarray keys;
859 
860 	HASH_CONTEXT HCany, HCini, HCfin;
861 	unsigned char HASHdigest[HASH_BYTES];
862 	struct berval digest;
863 	digest.bv_val = (char *)HASHdigest;
864 	digest.bv_len = sizeof(HASHdigest);
865 
866 	nkeys = 0;
867 
868 	for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
869 		/* count number of indices to generate */
870 		if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
871 			if( values[i].bv_len >= index_substr_if_maxlen ) {
872 				nkeys += index_substr_if_maxlen -
873 					(index_substr_if_minlen - 1);
874 			} else if( values[i].bv_len >= index_substr_if_minlen ) {
875 				nkeys += values[i].bv_len - (index_substr_if_minlen - 1);
876 			}
877 		}
878 
879 		if( flags & SLAP_INDEX_SUBSTR_ANY ) {
880 			if( values[i].bv_len >= index_substr_any_len ) {
881 				nkeys += values[i].bv_len - (index_substr_any_len - 1);
882 			}
883 		}
884 
885 		if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
886 			if( values[i].bv_len >= index_substr_if_maxlen ) {
887 				nkeys += index_substr_if_maxlen -
888 					(index_substr_if_minlen - 1);
889 			} else if( values[i].bv_len >= index_substr_if_minlen ) {
890 				nkeys += values[i].bv_len - (index_substr_if_minlen - 1);
891 			}
892 		}
893 	}
894 
895 	if( nkeys == 0 ) {
896 		/* no keys to generate */
897 		*keysp = NULL;
898 		return LDAP_SUCCESS;
899 	}
900 
901 	keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx );
902 
903 	slen = syntax->ssyn_oidlen;
904 	mlen = mr->smr_oidlen;
905 
906 	if ( flags & SLAP_INDEX_SUBSTR_ANY )
907 		hashPreset( &HCany, prefix, SLAP_INDEX_SUBSTR_PREFIX, syntax, mr );
908 	if( flags & SLAP_INDEX_SUBSTR_INITIAL )
909 		hashPreset( &HCini, prefix, SLAP_INDEX_SUBSTR_INITIAL_PREFIX, syntax, mr );
910 	if( flags & SLAP_INDEX_SUBSTR_FINAL )
911 		hashPreset( &HCfin, prefix, SLAP_INDEX_SUBSTR_FINAL_PREFIX, syntax, mr );
912 
913 	nkeys = 0;
914 	for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
915 		ber_len_t j,max;
916 
917 		if( ( flags & SLAP_INDEX_SUBSTR_ANY ) &&
918 			( values[i].bv_len >= index_substr_any_len ) )
919 		{
920 			max = values[i].bv_len - (index_substr_any_len - 1);
921 
922 			for( j=0; j<max; j++ ) {
923 				hashIter( &HCany, HASHdigest,
924 					(unsigned char *)&values[i].bv_val[j],
925 					index_substr_any_len );
926 				ber_dupbv_x( &keys[nkeys++], &digest, ctx );
927 			}
928 		}
929 
930 		/* skip if too short */
931 		if( values[i].bv_len < index_substr_if_minlen ) continue;
932 
933 		max = index_substr_if_maxlen < values[i].bv_len
934 			? index_substr_if_maxlen : values[i].bv_len;
935 
936 		for( j=index_substr_if_minlen; j<=max; j++ ) {
937 
938 			if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
939 				hashIter( &HCini, HASHdigest,
940 					(unsigned char *)values[i].bv_val, j );
941 				ber_dupbv_x( &keys[nkeys++], &digest, ctx );
942 			}
943 
944 			if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
945 				hashIter( &HCfin, HASHdigest,
946 					(unsigned char *)&values[i].bv_val[values[i].bv_len-j], j );
947 				ber_dupbv_x( &keys[nkeys++], &digest, ctx );
948 			}
949 
950 		}
951 	}
952 
953 	if( nkeys > 0 ) {
954 		BER_BVZERO( &keys[nkeys] );
955 		*keysp = keys;
956 	} else {
957 		ch_free( keys );
958 		*keysp = NULL;
959 	}
960 
961 	return LDAP_SUCCESS;
962 }
963 
964 /* Substring index generation function: Assertion value -> index hash keys */
965 static int
966 octetStringSubstringsFilter (
967 	slap_mask_t use,
968 	slap_mask_t flags,
969 	Syntax *syntax,
970 	MatchingRule *mr,
971 	struct berval *prefix,
972 	void * assertedValue,
973 	BerVarray *keysp,
974 	void *ctx)
975 {
976 	SubstringsAssertion *sa;
977 	char pre;
978 	ber_len_t nkeys = 0;
979 	size_t slen, mlen, klen;
980 	BerVarray keys;
981 	HASH_CONTEXT HASHcontext;
982 	unsigned char HASHdigest[HASH_BYTES];
983 	struct berval *value;
984 	struct berval digest;
985 
986 	sa = (SubstringsAssertion *) assertedValue;
987 
988 	if( flags & SLAP_INDEX_SUBSTR_INITIAL &&
989 		!BER_BVISNULL( &sa->sa_initial ) &&
990 		sa->sa_initial.bv_len >= index_substr_if_minlen )
991 	{
992 		nkeys++;
993 		if ( sa->sa_initial.bv_len > index_substr_if_maxlen &&
994 			( flags & SLAP_INDEX_SUBSTR_ANY ))
995 		{
996 			nkeys += 1 + (sa->sa_initial.bv_len - index_substr_if_maxlen) / index_substr_any_step;
997 		}
998 	}
999 
1000 	if ( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
1001 		ber_len_t i;
1002 		for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) {
1003 			if( sa->sa_any[i].bv_len >= index_substr_any_len ) {
1004 				/* don't bother accounting with stepping */
1005 				nkeys += sa->sa_any[i].bv_len -
1006 					( index_substr_any_len - 1 );
1007 			}
1008 		}
1009 	}
1010 
1011 	if( flags & SLAP_INDEX_SUBSTR_FINAL &&
1012 		!BER_BVISNULL( &sa->sa_final ) &&
1013 		sa->sa_final.bv_len >= index_substr_if_minlen )
1014 	{
1015 		nkeys++;
1016 		if ( sa->sa_final.bv_len > index_substr_if_maxlen &&
1017 			( flags & SLAP_INDEX_SUBSTR_ANY ))
1018 		{
1019 			nkeys += 1 + (sa->sa_final.bv_len - index_substr_if_maxlen) / index_substr_any_step;
1020 		}
1021 	}
1022 
1023 	if( nkeys == 0 ) {
1024 		*keysp = NULL;
1025 		return LDAP_SUCCESS;
1026 	}
1027 
1028 	digest.bv_val = (char *)HASHdigest;
1029 	digest.bv_len = sizeof(HASHdigest);
1030 
1031 	slen = syntax->ssyn_oidlen;
1032 	mlen = mr->smr_oidlen;
1033 
1034 	keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx );
1035 	nkeys = 0;
1036 
1037 	if( flags & SLAP_INDEX_SUBSTR_INITIAL &&
1038 		!BER_BVISNULL( &sa->sa_initial ) &&
1039 		sa->sa_initial.bv_len >= index_substr_if_minlen )
1040 	{
1041 		pre = SLAP_INDEX_SUBSTR_INITIAL_PREFIX;
1042 		value = &sa->sa_initial;
1043 
1044 		klen = index_substr_if_maxlen < value->bv_len
1045 			? index_substr_if_maxlen : value->bv_len;
1046 
1047 		hashPreset( &HASHcontext, prefix, pre, syntax, mr );
1048 		hashIter( &HASHcontext, HASHdigest,
1049 			(unsigned char *)value->bv_val, klen );
1050 		ber_dupbv_x( &keys[nkeys++], &digest, ctx );
1051 
1052 		/* If initial is too long and we have subany indexed, use it
1053 		 * to match the excess...
1054 		 */
1055 		if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY))
1056 		{
1057 			ber_len_t j;
1058 			pre = SLAP_INDEX_SUBSTR_PREFIX;
1059 			hashPreset( &HASHcontext, prefix, pre, syntax, mr);
1060 			for ( j=index_substr_if_maxlen-1; j <= value->bv_len - index_substr_any_len; j+=index_substr_any_step )
1061 			{
1062 				hashIter( &HASHcontext, HASHdigest,
1063 					(unsigned char *)&value->bv_val[j], index_substr_any_len );
1064 				ber_dupbv_x( &keys[nkeys++], &digest, ctx );
1065 			}
1066 		}
1067 	}
1068 
1069 	if( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
1070 		ber_len_t i, j;
1071 		pre = SLAP_INDEX_SUBSTR_PREFIX;
1072 		klen = index_substr_any_len;
1073 
1074 		for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) {
1075 			if( sa->sa_any[i].bv_len < index_substr_any_len ) {
1076 				continue;
1077 			}
1078 
1079 			value = &sa->sa_any[i];
1080 
1081 			hashPreset( &HASHcontext, prefix, pre, syntax, mr);
1082 			for(j=0;
1083 				j <= value->bv_len - index_substr_any_len;
1084 				j += index_substr_any_step )
1085 			{
1086 				hashIter( &HASHcontext, HASHdigest,
1087 					(unsigned char *)&value->bv_val[j], klen );
1088 				ber_dupbv_x( &keys[nkeys++], &digest, ctx );
1089 			}
1090 		}
1091 	}
1092 
1093 	if( flags & SLAP_INDEX_SUBSTR_FINAL &&
1094 		!BER_BVISNULL( &sa->sa_final ) &&
1095 		sa->sa_final.bv_len >= index_substr_if_minlen )
1096 	{
1097 		pre = SLAP_INDEX_SUBSTR_FINAL_PREFIX;
1098 		value = &sa->sa_final;
1099 
1100 		klen = index_substr_if_maxlen < value->bv_len
1101 			? index_substr_if_maxlen : value->bv_len;
1102 
1103 		hashPreset( &HASHcontext, prefix, pre, syntax, mr );
1104 		hashIter( &HASHcontext, HASHdigest,
1105 			(unsigned char *)&value->bv_val[value->bv_len-klen], klen );
1106 		ber_dupbv_x( &keys[nkeys++], &digest, ctx );
1107 
1108 		/* If final is too long and we have subany indexed, use it
1109 		 * to match the excess...
1110 		 */
1111 		if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY))
1112 		{
1113 			ber_len_t j;
1114 			pre = SLAP_INDEX_SUBSTR_PREFIX;
1115 			hashPreset( &HASHcontext, prefix, pre, syntax, mr);
1116 			for ( j=0; j <= value->bv_len - index_substr_if_maxlen; j+=index_substr_any_step )
1117 			{
1118 				hashIter( &HASHcontext, HASHdigest,
1119 					(unsigned char *)&value->bv_val[j], index_substr_any_len );
1120 				ber_dupbv_x( &keys[nkeys++], &digest, ctx );
1121 			}
1122 		}
1123 	}
1124 
1125 	if( nkeys > 0 ) {
1126 		BER_BVZERO( &keys[nkeys] );
1127 		*keysp = keys;
1128 	} else {
1129 		ch_free( keys );
1130 		*keysp = NULL;
1131 	}
1132 
1133 	return LDAP_SUCCESS;
1134 }
1135 
1136 static int
1137 bitStringValidate(
1138 	Syntax *syntax,
1139 	struct berval *in )
1140 {
1141 	ber_len_t i;
1142 
1143 	/* very unforgiving validation, requires no normalization
1144 	 * before simplistic matching
1145 	 */
1146 	if( in->bv_len < 3 ) {
1147 		return LDAP_INVALID_SYNTAX;
1148 	}
1149 
1150 	/* RFC 4517 Section 3.3.2 Bit String:
1151 	 *	BitString    = SQUOTE *binary-digit SQUOTE "B"
1152 	 *	binary-digit = "0" / "1"
1153 	 *
1154 	 * where SQUOTE [RFC4512] is
1155 	 *	SQUOTE  = %x27 ; single quote ("'")
1156 	 *
1157 	 * Example: '0101111101'B
1158 	 */
1159 
1160 	if( in->bv_val[0] != '\'' ||
1161 		in->bv_val[in->bv_len - 2] != '\'' ||
1162 		in->bv_val[in->bv_len - 1] != 'B' )
1163 	{
1164 		return LDAP_INVALID_SYNTAX;
1165 	}
1166 
1167 	for( i = in->bv_len - 3; i > 0; i-- ) {
1168 		if( in->bv_val[i] != '0' && in->bv_val[i] != '1' ) {
1169 			return LDAP_INVALID_SYNTAX;
1170 		}
1171 	}
1172 
1173 	return LDAP_SUCCESS;
1174 }
1175 
1176 /*
1177  * Syntaxes from RFC 4517
1178  *
1179 
1180 3.3.2.  Bit String
1181 
1182    A value of the Bit String syntax is a sequence of binary digits.  The
1183    LDAP-specific encoding of a value of this syntax is defined by the
1184    following ABNF:
1185 
1186       BitString    = SQUOTE *binary-digit SQUOTE "B"
1187 
1188       binary-digit = "0" / "1"
1189 
1190    The <SQUOTE> rule is defined in [MODELS].
1191 
1192       Example:
1193          '0101111101'B
1194 
1195    The LDAP definition for the Bit String syntax is:
1196 
1197       ( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )
1198 
1199    This syntax corresponds to the BIT STRING ASN.1 type from [ASN.1].
1200 
1201    ...
1202 
1203 3.3.21.  Name and Optional UID
1204 
1205    A value of the Name and Optional UID syntax is the distinguished name
1206    [MODELS] of an entity optionally accompanied by a unique identifier
1207    that serves to differentiate the entity from others with an identical
1208    distinguished name.
1209 
1210    The LDAP-specific encoding of a value of this syntax is defined by
1211    the following ABNF:
1212 
1213        NameAndOptionalUID = distinguishedName [ SHARP BitString ]
1214 
1215    The <BitString> rule is defined in Section 3.3.2.  The
1216    <distinguishedName> rule is defined in [LDAPDN].  The <SHARP> rule is
1217    defined in [MODELS].
1218 
1219    Note that although the '#' character may occur in the string
1220    representation of a distinguished name, no additional escaping of
1221    this character is performed when a <distinguishedName> is encoded in
1222    a <NameAndOptionalUID>.
1223 
1224       Example:
1225          1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB#'0101'B
1226 
1227    The LDAP definition for the Name and Optional UID syntax is:
1228 
1229       ( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )
1230 
1231    This syntax corresponds to the NameAndOptionalUID ASN.1 type from
1232    [X.520].
1233 
1234  *
1235  * RFC 4512 says:
1236  *
1237 
1238 1.4. Common ABNF Productions
1239 
1240   ...
1241       SHARP   = %x23 ; octothorpe (or sharp sign) ("#")
1242   ...
1243       SQUOTE  = %x27 ; single quote ("'")
1244   ...
1245 
1246  *
1247  * Note:
1248  * RFC 4514 clarifies that SHARP, i.e. "#", doesn't have to
1249  * be escaped except when at the beginning of a value, the
1250  * definition of Name and Optional UID appears to be flawed,
1251  * because there is no clear means to determine whether the
1252  * UID part is present or not.
1253  *
1254  * Example:
1255  *
1256  * 	cn=Someone,dc=example,dc=com#'1'B
1257  *
1258  * could be either a NameAndOptionalUID with trailing UID, i.e.
1259  *
1260  * 	DN = "cn=Someone,dc=example,dc=com"
1261  * 	UID = "'1'B"
1262  *
1263  * or a NameAndOptionalUID with no trailing UID, and the AVA
1264  * in the last RDN made of
1265  *
1266  * 	attributeType = dc
1267  * 	attributeValue = com#'1'B
1268  *
1269  * in fact "com#'1'B" is a valid IA5 string.
1270  *
1271  * As a consequence, current slapd code takes the presence of
1272  * #<valid BitString> at the end of the string representation
1273  * of a NameAndOptionalUID to mean this is indeed a BitString.
1274  * This is quite arbitrary - it has changed the past and might
1275  * change in the future.
1276  */
1277 
1278 
1279 static int
1280 nameUIDValidate(
1281 	Syntax *syntax,
1282 	struct berval *in )
1283 {
1284 	int rc;
1285 	struct berval dn, uid;
1286 
1287 	if( BER_BVISEMPTY( in ) ) return LDAP_SUCCESS;
1288 
1289 	ber_dupbv( &dn, in );
1290 	if( !dn.bv_val ) return LDAP_OTHER;
1291 
1292 	/* if there's a "#", try bitStringValidate()... */
1293 	uid.bv_val = strrchr( dn.bv_val, '#' );
1294 	if ( !BER_BVISNULL( &uid ) ) {
1295 		uid.bv_val++;
1296 		uid.bv_len = dn.bv_len - ( uid.bv_val - dn.bv_val );
1297 
1298 		rc = bitStringValidate( NULL, &uid );
1299 		if ( rc == LDAP_SUCCESS ) {
1300 			/* in case of success, trim the UID,
1301 			 * otherwise treat it as part of the DN */
1302 			dn.bv_len -= uid.bv_len + 1;
1303 			uid.bv_val[-1] = '\0';
1304 		}
1305 	}
1306 
1307 	rc = dnValidate( NULL, &dn );
1308 
1309 	ber_memfree( dn.bv_val );
1310 	return rc;
1311 }
1312 
1313 int
1314 nameUIDPretty(
1315 	Syntax *syntax,
1316 	struct berval *val,
1317 	struct berval *out,
1318 	void *ctx )
1319 {
1320 	assert( val != NULL );
1321 	assert( out != NULL );
1322 
1323 
1324 	Debug( LDAP_DEBUG_TRACE, ">>> nameUIDPretty: <%s>\n", val->bv_val, 0, 0 );
1325 
1326 	if( BER_BVISEMPTY( val ) ) {
1327 		ber_dupbv_x( out, val, ctx );
1328 
1329 	} else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
1330 		return LDAP_INVALID_SYNTAX;
1331 
1332 	} else {
1333 		int		rc;
1334 		struct berval	dnval = *val;
1335 		struct berval	uidval = BER_BVNULL;
1336 
1337 		uidval.bv_val = strrchr( val->bv_val, '#' );
1338 		if ( !BER_BVISNULL( &uidval ) ) {
1339 			uidval.bv_val++;
1340 			uidval.bv_len = val->bv_len - ( uidval.bv_val - val->bv_val );
1341 
1342 			rc = bitStringValidate( NULL, &uidval );
1343 
1344 			if ( rc == LDAP_SUCCESS ) {
1345 				ber_dupbv_x( &dnval, val, ctx );
1346 				uidval.bv_val--;
1347 				dnval.bv_len -= ++uidval.bv_len;
1348 				dnval.bv_val[dnval.bv_len] = '\0';
1349 
1350 			} else {
1351 				BER_BVZERO( &uidval );
1352 			}
1353 		}
1354 
1355 		rc = dnPretty( syntax, &dnval, out, ctx );
1356 		if ( dnval.bv_val != val->bv_val ) {
1357 			slap_sl_free( dnval.bv_val, ctx );
1358 		}
1359 		if( rc != LDAP_SUCCESS ) {
1360 			return rc;
1361 		}
1362 
1363 		if( !BER_BVISNULL( &uidval ) ) {
1364 			char	*tmp;
1365 
1366 			tmp = slap_sl_realloc( out->bv_val, out->bv_len
1367 				+ uidval.bv_len + 1,
1368 				ctx );
1369 			if( tmp == NULL ) {
1370 				ber_memfree_x( out->bv_val, ctx );
1371 				return LDAP_OTHER;
1372 			}
1373 			out->bv_val = tmp;
1374 			memcpy( out->bv_val + out->bv_len, uidval.bv_val, uidval.bv_len );
1375 			out->bv_len += uidval.bv_len;
1376 			out->bv_val[out->bv_len] = '\0';
1377 		}
1378 	}
1379 
1380 	Debug( LDAP_DEBUG_TRACE, "<<< nameUIDPretty: <%s>\n", out->bv_val, 0, 0 );
1381 
1382 	return LDAP_SUCCESS;
1383 }
1384 
1385 static int
1386 uniqueMemberNormalize(
1387 	slap_mask_t usage,
1388 	Syntax *syntax,
1389 	MatchingRule *mr,
1390 	struct berval *val,
1391 	struct berval *normalized,
1392 	void *ctx )
1393 {
1394 	struct berval out;
1395 	int rc;
1396 
1397 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
1398 
1399 	ber_dupbv_x( &out, val, ctx );
1400 	if ( BER_BVISEMPTY( &out ) ) {
1401 		*normalized = out;
1402 
1403 	} else {
1404 		struct berval uid = BER_BVNULL;
1405 
1406 		uid.bv_val = strrchr( out.bv_val, '#' );
1407 		if ( !BER_BVISNULL( &uid ) ) {
1408 			uid.bv_val++;
1409 			uid.bv_len = out.bv_len - ( uid.bv_val - out.bv_val );
1410 
1411 			rc = bitStringValidate( NULL, &uid );
1412 			if ( rc == LDAP_SUCCESS ) {
1413 				uid.bv_val[-1] = '\0';
1414 				out.bv_len -= uid.bv_len + 1;
1415 			} else {
1416 				BER_BVZERO( &uid );
1417 			}
1418 		}
1419 
1420 		rc = dnNormalize( 0, NULL, NULL, &out, normalized, ctx );
1421 
1422 		if( rc != LDAP_SUCCESS ) {
1423 			slap_sl_free( out.bv_val, ctx );
1424 			return LDAP_INVALID_SYNTAX;
1425 		}
1426 
1427 		if( !BER_BVISNULL( &uid ) ) {
1428 			char	*tmp;
1429 
1430 			tmp = ch_realloc( normalized->bv_val,
1431 				normalized->bv_len + uid.bv_len
1432 				+ STRLENOF("#") + 1 );
1433 			if ( tmp == NULL ) {
1434 				ber_memfree_x( normalized->bv_val, ctx );
1435 				return LDAP_OTHER;
1436 			}
1437 
1438 			normalized->bv_val = tmp;
1439 
1440 			/* insert the separator */
1441 			normalized->bv_val[normalized->bv_len++] = '#';
1442 
1443 			/* append the UID */
1444 			AC_MEMCPY( &normalized->bv_val[normalized->bv_len],
1445 				uid.bv_val, uid.bv_len );
1446 			normalized->bv_len += uid.bv_len;
1447 
1448 			/* terminate */
1449 			normalized->bv_val[normalized->bv_len] = '\0';
1450 		}
1451 
1452 		slap_sl_free( out.bv_val, ctx );
1453 	}
1454 
1455 	return LDAP_SUCCESS;
1456 }
1457 
1458 static int
1459 uniqueMemberMatch(
1460 	int *matchp,
1461 	slap_mask_t flags,
1462 	Syntax *syntax,
1463 	MatchingRule *mr,
1464 	struct berval *value,
1465 	void *assertedValue )
1466 {
1467 	int match;
1468 	struct berval *asserted = (struct berval *) assertedValue;
1469 	struct berval assertedDN = *asserted;
1470 	struct berval assertedUID = BER_BVNULL;
1471 	struct berval valueDN = *value;
1472 	struct berval valueUID = BER_BVNULL;
1473 	int approx = ((flags & SLAP_MR_EQUALITY_APPROX) == SLAP_MR_EQUALITY_APPROX);
1474 
1475 	if ( !BER_BVISEMPTY( asserted ) ) {
1476 		assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
1477 		if ( !BER_BVISNULL( &assertedUID ) ) {
1478 			assertedUID.bv_val++;
1479 			assertedUID.bv_len = assertedDN.bv_len
1480 				- ( assertedUID.bv_val - assertedDN.bv_val );
1481 
1482 			if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
1483 				assertedDN.bv_len -= assertedUID.bv_len + 1;
1484 
1485 			} else {
1486 				BER_BVZERO( &assertedUID );
1487 			}
1488 		}
1489 	}
1490 
1491 	if ( !BER_BVISEMPTY( value ) ) {
1492 
1493 		valueUID.bv_val = strrchr( valueDN.bv_val, '#' );
1494 		if ( !BER_BVISNULL( &valueUID ) ) {
1495 			valueUID.bv_val++;
1496 			valueUID.bv_len = valueDN.bv_len
1497 				- ( valueUID.bv_val - valueDN.bv_val );
1498 
1499 			if ( bitStringValidate( NULL, &valueUID ) == LDAP_SUCCESS ) {
1500 				valueDN.bv_len -= valueUID.bv_len + 1;
1501 
1502 			} else {
1503 				BER_BVZERO( &valueUID );
1504 			}
1505 		}
1506 	}
1507 
1508 	if( valueUID.bv_len && assertedUID.bv_len ) {
1509 		ber_slen_t d;
1510 		d = (ber_slen_t) valueUID.bv_len - (ber_slen_t) assertedUID.bv_len;
1511 		if ( d ) {
1512 			*matchp = sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1;
1513 			return LDAP_SUCCESS;
1514 		}
1515 
1516 		match = memcmp( valueUID.bv_val, assertedUID.bv_val, valueUID.bv_len );
1517 		if( match ) {
1518 			*matchp = match;
1519 			return LDAP_SUCCESS;
1520 		}
1521 
1522 	} else if ( !approx && valueUID.bv_len ) {
1523 		match = -1;
1524 		*matchp = match;
1525 		return LDAP_SUCCESS;
1526 
1527 	} else if ( !approx && assertedUID.bv_len ) {
1528 		match = 1;
1529 		*matchp = match;
1530 		return LDAP_SUCCESS;
1531 	}
1532 
1533 	return dnMatch( matchp, flags, syntax, mr, &valueDN, &assertedDN );
1534 }
1535 
1536 static int
1537 uniqueMemberIndexer(
1538 	slap_mask_t use,
1539 	slap_mask_t flags,
1540 	Syntax *syntax,
1541 	MatchingRule *mr,
1542 	struct berval *prefix,
1543 	BerVarray values,
1544 	BerVarray *keysp,
1545 	void *ctx )
1546 {
1547 	BerVarray dnvalues;
1548 	int rc;
1549 	int i;
1550 	for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
1551 		/* just count them */
1552 	}
1553 	assert( i > 0 );
1554 
1555 	dnvalues = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
1556 
1557 	for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
1558 		struct berval assertedDN = values[i];
1559 		struct berval assertedUID = BER_BVNULL;
1560 
1561 		if ( !BER_BVISEMPTY( &assertedDN ) ) {
1562 			assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
1563 			if ( !BER_BVISNULL( &assertedUID ) ) {
1564 				assertedUID.bv_val++;
1565 				assertedUID.bv_len = assertedDN.bv_len
1566 					- ( assertedUID.bv_val - assertedDN.bv_val );
1567 
1568 				if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
1569 					assertedDN.bv_len -= assertedUID.bv_len + 1;
1570 
1571 				} else {
1572 					BER_BVZERO( &assertedUID );
1573 				}
1574 			}
1575 		}
1576 
1577 		dnvalues[i] = assertedDN;
1578 	}
1579 	BER_BVZERO( &dnvalues[i] );
1580 
1581 	rc = octetStringIndexer( use, flags, syntax, mr, prefix,
1582 		dnvalues, keysp, ctx );
1583 
1584 	slap_sl_free( dnvalues, ctx );
1585 	return rc;
1586 }
1587 
1588 static int
1589 uniqueMemberFilter(
1590 	slap_mask_t use,
1591 	slap_mask_t flags,
1592 	Syntax *syntax,
1593 	MatchingRule *mr,
1594 	struct berval *prefix,
1595 	void * assertedValue,
1596 	BerVarray *keysp,
1597 	void *ctx )
1598 {
1599 	struct berval *asserted = (struct berval *) assertedValue;
1600 	struct berval assertedDN = *asserted;
1601 	struct berval assertedUID = BER_BVNULL;
1602 
1603 	if ( !BER_BVISEMPTY( asserted ) ) {
1604 		assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
1605 		if ( !BER_BVISNULL( &assertedUID ) ) {
1606 			assertedUID.bv_val++;
1607 			assertedUID.bv_len = assertedDN.bv_len
1608 				- ( assertedUID.bv_val - assertedDN.bv_val );
1609 
1610 			if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
1611 				assertedDN.bv_len -= assertedUID.bv_len + 1;
1612 
1613 			} else {
1614 				BER_BVZERO( &assertedUID );
1615 			}
1616 		}
1617 	}
1618 
1619 	return octetStringFilter( use, flags, syntax, mr, prefix,
1620 		&assertedDN, keysp, ctx );
1621 }
1622 
1623 
1624 /*
1625  * Handling boolean syntax and matching is quite rigid.
1626  * A more flexible approach would be to allow a variety
1627  * of strings to be normalized and prettied into TRUE
1628  * and FALSE.
1629  */
1630 static int
1631 booleanValidate(
1632 	Syntax *syntax,
1633 	struct berval *in )
1634 {
1635 	/* very unforgiving validation, requires no normalization
1636 	 * before simplistic matching
1637 	 */
1638 
1639 	if( in->bv_len == 4 ) {
1640 		if( bvmatch( in, &slap_true_bv ) ) {
1641 			return LDAP_SUCCESS;
1642 		}
1643 	} else if( in->bv_len == 5 ) {
1644 		if( bvmatch( in, &slap_false_bv ) ) {
1645 			return LDAP_SUCCESS;
1646 		}
1647 	}
1648 
1649 	return LDAP_INVALID_SYNTAX;
1650 }
1651 
1652 static int
1653 booleanMatch(
1654 	int *matchp,
1655 	slap_mask_t flags,
1656 	Syntax *syntax,
1657 	MatchingRule *mr,
1658 	struct berval *value,
1659 	void *assertedValue )
1660 {
1661 	/* simplistic matching allowed by rigid validation */
1662 	struct berval *asserted = (struct berval *) assertedValue;
1663 	*matchp = (int) asserted->bv_len - (int) value->bv_len;
1664 	return LDAP_SUCCESS;
1665 }
1666 
1667 /*-------------------------------------------------------------------
1668 LDAP/X.500 string syntax / matching rules have a few oddities.  This
1669 comment attempts to detail how slapd(8) treats them.
1670 
1671 Summary:
1672   StringSyntax		X.500	LDAP	Matching/Comments
1673   DirectoryString	CHOICE	UTF8	i/e + ignore insignificant spaces
1674   PrintableString	subset	subset	i/e + ignore insignificant spaces
1675   PrintableString	subset	subset	i/e + ignore insignificant spaces
1676   NumericString		subset	subset	ignore all spaces
1677   IA5String			ASCII	ASCII	i/e + ignore insignificant spaces
1678   TeletexString		T.61	T.61	i/e + ignore insignificant spaces
1679 
1680   TelephoneNumber	subset	subset	i + ignore all spaces and "-"
1681 
1682   See RFC 4518 for details.
1683 
1684 
1685 Directory String -
1686   In X.500(93), a directory string can be either a PrintableString,
1687   a bmpString, or a UniversalString (e.g., UCS (a subset of Unicode)).
1688   In later versions, more CHOICEs were added.  In all cases the string
1689   must be non-empty.
1690 
1691   In LDAPv3, a directory string is a UTF-8 encoded UCS string.
1692   A directory string cannot be zero length.
1693 
1694   For matching, there are both case ignore and exact rules.  Both
1695   also require that "insignificant" spaces be ignored.
1696 	spaces before the first non-space are ignored;
1697 	spaces after the last non-space are ignored;
1698 	spaces after a space are ignored.
1699   Note: by these rules (and as clarified in X.520), a string of only
1700   spaces is to be treated as if held one space, not empty (which
1701   would be a syntax error).
1702 
1703 NumericString
1704   In ASN.1, numeric string is just a string of digits and spaces
1705   and could be empty.  However, in X.500, all attribute values of
1706   numeric string carry a non-empty constraint.  For example:
1707 
1708 	internationalISDNNumber ATTRIBUTE ::= {
1709 		WITH SYNTAX InternationalISDNNumber
1710 		EQUALITY MATCHING RULE numericStringMatch
1711 		SUBSTRINGS MATCHING RULE numericStringSubstringsMatch
1712 		ID id-at-internationalISDNNumber }
1713 	InternationalISDNNumber ::=
1714 	    NumericString (SIZE(1..ub-international-isdn-number))
1715 
1716   Unforunately, some assertion values are don't carry the same
1717   constraint (but its unclear how such an assertion could ever
1718   be true). In LDAP, there is one syntax (numericString) not two
1719   (numericString with constraint, numericString without constraint).
1720   This should be treated as numericString with non-empty constraint.
1721   Note that while someone may have no ISDN number, there are no ISDN
1722   numbers which are zero length.
1723 
1724   In matching, spaces are ignored.
1725 
1726 PrintableString
1727   In ASN.1, Printable string is just a string of printable characters
1728   and can be empty.  In X.500, semantics much like NumericString (see
1729   serialNumber for a like example) excepting uses insignificant space
1730   handling instead of ignore all spaces.  They must be non-empty.
1731 
1732 IA5String
1733   Basically same as PrintableString.  There are no examples in X.500,
1734   but same logic applies.  Empty strings are allowed.
1735 
1736 -------------------------------------------------------------------*/
1737 
1738 static int
1739 UTF8StringValidate(
1740 	Syntax *syntax,
1741 	struct berval *in )
1742 {
1743 	int len;
1744 	unsigned char *u = (unsigned char *)in->bv_val, *end = in->bv_val + in->bv_len;
1745 
1746 	if( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
1747 		/* directory strings cannot be empty */
1748 		return LDAP_INVALID_SYNTAX;
1749 	}
1750 
1751 	for( ; u < end; u += len ) {
1752 		/* get the length indicated by the first byte */
1753 		len = LDAP_UTF8_CHARLEN2( u, len );
1754 
1755 		/* very basic checks */
1756 		switch( len ) {
1757 			case 6:
1758 				if( (u[5] & 0xC0) != 0x80 ) {
1759 					return LDAP_INVALID_SYNTAX;
1760 				}
1761 			case 5:
1762 				if( (u[4] & 0xC0) != 0x80 ) {
1763 					return LDAP_INVALID_SYNTAX;
1764 				}
1765 			case 4:
1766 				if( (u[3] & 0xC0) != 0x80 ) {
1767 					return LDAP_INVALID_SYNTAX;
1768 				}
1769 			case 3:
1770 				if( (u[2] & 0xC0 )!= 0x80 ) {
1771 					return LDAP_INVALID_SYNTAX;
1772 				}
1773 			case 2:
1774 				if( (u[1] & 0xC0) != 0x80 ) {
1775 					return LDAP_INVALID_SYNTAX;
1776 				}
1777 			case 1:
1778 				/* CHARLEN already validated it */
1779 				break;
1780 			default:
1781 				return LDAP_INVALID_SYNTAX;
1782 		}
1783 
1784 		/* make sure len corresponds with the offset
1785 			to the next character */
1786 		if( LDAP_UTF8_OFFSET( (char *)u ) != len ) return LDAP_INVALID_SYNTAX;
1787 	}
1788 
1789 	if( u > end ) {
1790 		return LDAP_INVALID_SYNTAX;
1791 	}
1792 
1793 	return LDAP_SUCCESS;
1794 }
1795 
1796 static int
1797 UTF8StringNormalize(
1798 	slap_mask_t use,
1799 	Syntax *syntax,
1800 	MatchingRule *mr,
1801 	struct berval *val,
1802 	struct berval *normalized,
1803 	void *ctx )
1804 {
1805 	struct berval tmp, nvalue;
1806 	int flags, wasspace;
1807 	ber_len_t i;
1808 
1809 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
1810 
1811 	if( BER_BVISNULL( val ) ) {
1812 		/* assume we're dealing with a syntax (e.g., UTF8String)
1813 		 * which allows empty strings
1814 		 */
1815 		BER_BVZERO( normalized );
1816 		return LDAP_SUCCESS;
1817 	}
1818 
1819 	flags = SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseExactMatch )
1820 		? LDAP_UTF8_NOCASEFOLD : LDAP_UTF8_CASEFOLD;
1821 	flags |= ( ( use & SLAP_MR_EQUALITY_APPROX ) == SLAP_MR_EQUALITY_APPROX )
1822 		? LDAP_UTF8_APPROX : 0;
1823 
1824 	val = UTF8bvnormalize( val, &tmp, flags, ctx );
1825 	/* out of memory or syntax error, the former is unlikely */
1826 	if( val == NULL ) {
1827 		return LDAP_INVALID_SYNTAX;
1828 	}
1829 
1830 	/* collapse spaces (in place) */
1831 	nvalue.bv_len = 0;
1832 	nvalue.bv_val = tmp.bv_val;
1833 
1834 	/* trim leading spaces? */
1835 	wasspace = !((( use & SLAP_MR_SUBSTR_ANY ) == SLAP_MR_SUBSTR_ANY ) ||
1836 		(( use & SLAP_MR_SUBSTR_FINAL ) == SLAP_MR_SUBSTR_FINAL ));
1837 
1838 	for( i = 0; i < tmp.bv_len; i++) {
1839 		if ( ASCII_SPACE( tmp.bv_val[i] )) {
1840 			if( wasspace++ == 0 ) {
1841 				/* trim repeated spaces */
1842 				nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i];
1843 			}
1844 		} else {
1845 			wasspace = 0;
1846 			nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i];
1847 		}
1848 	}
1849 
1850 	if( !BER_BVISEMPTY( &nvalue ) ) {
1851 		/* trim trailing space? */
1852 		if( wasspace && (
1853 			(( use & SLAP_MR_SUBSTR_INITIAL ) != SLAP_MR_SUBSTR_INITIAL ) &&
1854 			( use & SLAP_MR_SUBSTR_ANY ) != SLAP_MR_SUBSTR_ANY ))
1855 		{
1856 			--nvalue.bv_len;
1857 		}
1858 		nvalue.bv_val[nvalue.bv_len] = '\0';
1859 
1860 	} else if ( tmp.bv_len )  {
1861 		/* string of all spaces is treated as one space */
1862 		nvalue.bv_val[0] = ' ';
1863 		nvalue.bv_val[1] = '\0';
1864 		nvalue.bv_len = 1;
1865 	}	/* should never be entered with 0-length val */
1866 
1867 	*normalized = nvalue;
1868 	return LDAP_SUCCESS;
1869 }
1870 
1871 static int
1872 directoryStringSubstringsMatch(
1873 	int *matchp,
1874 	slap_mask_t flags,
1875 	Syntax *syntax,
1876 	MatchingRule *mr,
1877 	struct berval *value,
1878 	void *assertedValue )
1879 {
1880 	int match = 0;
1881 	SubstringsAssertion *sub = assertedValue;
1882 	struct berval left = *value;
1883 	ber_len_t i;
1884 	int priorspace=0;
1885 
1886 	if ( !BER_BVISNULL( &sub->sa_initial ) ) {
1887 		if ( sub->sa_initial.bv_len > left.bv_len ) {
1888 			/* not enough left */
1889 			match = 1;
1890 			goto done;
1891 		}
1892 
1893 		match = memcmp( sub->sa_initial.bv_val, left.bv_val,
1894 			sub->sa_initial.bv_len );
1895 
1896 		if ( match != 0 ) {
1897 			goto done;
1898 		}
1899 
1900 		left.bv_val += sub->sa_initial.bv_len;
1901 		left.bv_len -= sub->sa_initial.bv_len;
1902 
1903 		priorspace = ASCII_SPACE(
1904 			sub->sa_initial.bv_val[sub->sa_initial.bv_len] );
1905 	}
1906 
1907 	if ( sub->sa_any ) {
1908 		for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
1909 			ber_len_t idx;
1910 			char *p;
1911 
1912 			if( priorspace && !BER_BVISEMPTY( &sub->sa_any[i] )
1913 				&& ASCII_SPACE( sub->sa_any[i].bv_val[0] ))
1914 			{
1915 				/* allow next space to match */
1916 				left.bv_val--;
1917 				left.bv_len++;
1918 			}
1919 			priorspace=0;
1920 
1921 retry:
1922 			if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) {
1923 				continue;
1924 			}
1925 
1926 			if ( sub->sa_any[i].bv_len > left.bv_len ) {
1927 				/* not enough left */
1928 				match = 1;
1929 				goto done;
1930 			}
1931 
1932 			p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len );
1933 
1934 			if( p == NULL ) {
1935 				match = 1;
1936 				goto done;
1937 			}
1938 
1939 			idx = p - left.bv_val;
1940 
1941 			if ( idx >= left.bv_len ) {
1942 				/* this shouldn't happen */
1943 				return LDAP_OTHER;
1944 			}
1945 
1946 			left.bv_val = p;
1947 			left.bv_len -= idx;
1948 
1949 			if ( sub->sa_any[i].bv_len > left.bv_len ) {
1950 				/* not enough left */
1951 				match = 1;
1952 				goto done;
1953 			}
1954 
1955 			match = memcmp( left.bv_val,
1956 				sub->sa_any[i].bv_val,
1957 				sub->sa_any[i].bv_len );
1958 
1959 			if ( match != 0 ) {
1960 				left.bv_val++;
1961 				left.bv_len--;
1962 				goto retry;
1963 			}
1964 
1965 			left.bv_val += sub->sa_any[i].bv_len;
1966 			left.bv_len -= sub->sa_any[i].bv_len;
1967 
1968 			priorspace = ASCII_SPACE(
1969 				sub->sa_any[i].bv_val[sub->sa_any[i].bv_len] );
1970 		}
1971 	}
1972 
1973 	if ( !BER_BVISNULL( &sub->sa_final ) ) {
1974 		if( priorspace && !BER_BVISEMPTY( &sub->sa_final )
1975 			&& ASCII_SPACE( sub->sa_final.bv_val[0] ))
1976 		{
1977 			/* allow next space to match */
1978 			left.bv_val--;
1979 			left.bv_len++;
1980 		}
1981 
1982 		if ( sub->sa_final.bv_len > left.bv_len ) {
1983 			/* not enough left */
1984 			match = 1;
1985 			goto done;
1986 		}
1987 
1988 		match = memcmp( sub->sa_final.bv_val,
1989 			&left.bv_val[left.bv_len - sub->sa_final.bv_len],
1990 			sub->sa_final.bv_len );
1991 
1992 		if ( match != 0 ) {
1993 			goto done;
1994 		}
1995 	}
1996 
1997 done:
1998 	*matchp = match;
1999 	return LDAP_SUCCESS;
2000 }
2001 
2002 #if defined(SLAPD_APPROX_INITIALS)
2003 #	define SLAPD_APPROX_DELIMITER "._ "
2004 #	define SLAPD_APPROX_WORDLEN 2
2005 #else
2006 #	define SLAPD_APPROX_DELIMITER " "
2007 #	define SLAPD_APPROX_WORDLEN 1
2008 #endif
2009 
2010 static int
2011 approxMatch(
2012 	int *matchp,
2013 	slap_mask_t flags,
2014 	Syntax *syntax,
2015 	MatchingRule *mr,
2016 	struct berval *value,
2017 	void *assertedValue )
2018 {
2019 	struct berval *nval, *assertv;
2020 	char *val, **values, **words, *c;
2021 	int i, count, len, nextchunk=0, nextavail=0;
2022 
2023 	/* Yes, this is necessary */
2024 	nval = UTF8bvnormalize( value, NULL, LDAP_UTF8_APPROX, NULL );
2025 	if( nval == NULL ) {
2026 		*matchp = 1;
2027 		return LDAP_SUCCESS;
2028 	}
2029 
2030 	/* Yes, this is necessary */
2031 	assertv = UTF8bvnormalize( ((struct berval *)assertedValue),
2032 		NULL, LDAP_UTF8_APPROX, NULL );
2033 	if( assertv == NULL ) {
2034 		ber_bvfree( nval );
2035 		*matchp = 1;
2036 		return LDAP_SUCCESS;
2037 	}
2038 
2039 	/* Isolate how many words there are */
2040 	for ( c = nval->bv_val, count = 1; *c; c++ ) {
2041 		c = strpbrk( c, SLAPD_APPROX_DELIMITER );
2042 		if ( c == NULL ) break;
2043 		*c = '\0';
2044 		count++;
2045 	}
2046 
2047 	/* Get a phonetic copy of each word */
2048 	words = (char **)ch_malloc( count * sizeof(char *) );
2049 	values = (char **)ch_malloc( count * sizeof(char *) );
2050 	for ( c = nval->bv_val, i = 0;  i < count; i++, c += strlen(c) + 1 ) {
2051 		words[i] = c;
2052 		values[i] = phonetic(c);
2053 	}
2054 
2055 	/* Work through the asserted value's words, to see if at least some
2056 	 * of the words are there, in the same order. */
2057 	len = 0;
2058 	while ( (ber_len_t) nextchunk < assertv->bv_len ) {
2059 		len = strcspn( assertv->bv_val + nextchunk, SLAPD_APPROX_DELIMITER);
2060 		if( len == 0 ) {
2061 			nextchunk++;
2062 			continue;
2063 		}
2064 #if defined(SLAPD_APPROX_INITIALS)
2065 		else if( len == 1 ) {
2066 			/* Single letter words need to at least match one word's initial */
2067 			for( i=nextavail; i<count; i++ )
2068 				if( !strncasecmp( assertv->bv_val + nextchunk, words[i], 1 )) {
2069 					nextavail=i+1;
2070 					break;
2071 				}
2072 		}
2073 #endif
2074 		else {
2075 			/* Isolate the next word in the asserted value and phonetic it */
2076 			assertv->bv_val[nextchunk+len] = '\0';
2077 			val = phonetic( assertv->bv_val + nextchunk );
2078 
2079 			/* See if this phonetic chunk is in the remaining words of *value */
2080 			for( i=nextavail; i<count; i++ ){
2081 				if( !strcmp( val, values[i] ) ){
2082 					nextavail = i+1;
2083 					break;
2084 				}
2085 			}
2086 			ch_free( val );
2087 		}
2088 
2089 		/* This chunk in the asserted value was NOT within the *value. */
2090 		if( i >= count ) {
2091 			nextavail=-1;
2092 			break;
2093 		}
2094 
2095 		/* Go on to the next word in the asserted value */
2096 		nextchunk += len+1;
2097 	}
2098 
2099 	/* If some of the words were seen, call it a match */
2100 	if( nextavail > 0 ) {
2101 		*matchp = 0;
2102 	}
2103 	else {
2104 		*matchp = 1;
2105 	}
2106 
2107 	/* Cleanup allocs */
2108 	ber_bvfree( assertv );
2109 	for( i=0; i<count; i++ ) {
2110 		ch_free( values[i] );
2111 	}
2112 	ch_free( values );
2113 	ch_free( words );
2114 	ber_bvfree( nval );
2115 
2116 	return LDAP_SUCCESS;
2117 }
2118 
2119 static int
2120 approxIndexer(
2121 	slap_mask_t use,
2122 	slap_mask_t flags,
2123 	Syntax *syntax,
2124 	MatchingRule *mr,
2125 	struct berval *prefix,
2126 	BerVarray values,
2127 	BerVarray *keysp,
2128 	void *ctx )
2129 {
2130 	char *c;
2131 	int i,j, len, wordcount, keycount=0;
2132 	struct berval *newkeys;
2133 	BerVarray keys=NULL;
2134 
2135 	for( j = 0; !BER_BVISNULL( &values[j] ); j++ ) {
2136 		struct berval val = BER_BVNULL;
2137 		/* Yes, this is necessary */
2138 		UTF8bvnormalize( &values[j], &val, LDAP_UTF8_APPROX, NULL );
2139 		assert( !BER_BVISNULL( &val ) );
2140 
2141 		/* Isolate how many words there are. There will be a key for each */
2142 		for( wordcount = 0, c = val.bv_val; *c; c++) {
2143 			len = strcspn(c, SLAPD_APPROX_DELIMITER);
2144 			if( len >= SLAPD_APPROX_WORDLEN ) wordcount++;
2145 			c+= len;
2146 			if (*c == '\0') break;
2147 			*c = '\0';
2148 		}
2149 
2150 		/* Allocate/increase storage to account for new keys */
2151 		newkeys = (struct berval *)ch_malloc( (keycount + wordcount + 1)
2152 			* sizeof(struct berval) );
2153 		AC_MEMCPY( newkeys, keys, keycount * sizeof(struct berval) );
2154 		if( keys ) ch_free( keys );
2155 		keys = newkeys;
2156 
2157 		/* Get a phonetic copy of each word */
2158 		for( c = val.bv_val, i = 0; i < wordcount; c += len + 1 ) {
2159 			len = strlen( c );
2160 			if( len < SLAPD_APPROX_WORDLEN ) continue;
2161 			ber_str2bv( phonetic( c ), 0, 0, &keys[keycount] );
2162 			if( keys[keycount].bv_len ) {
2163 				keycount++;
2164 			} else {
2165 				ch_free( keys[keycount].bv_val );
2166 			}
2167 			i++;
2168 		}
2169 
2170 		ber_memfree( val.bv_val );
2171 	}
2172 	BER_BVZERO( &keys[keycount] );
2173 	*keysp = keys;
2174 
2175 	return LDAP_SUCCESS;
2176 }
2177 
2178 static int
2179 approxFilter(
2180 	slap_mask_t use,
2181 	slap_mask_t flags,
2182 	Syntax *syntax,
2183 	MatchingRule *mr,
2184 	struct berval *prefix,
2185 	void * assertedValue,
2186 	BerVarray *keysp,
2187 	void *ctx )
2188 {
2189 	char *c;
2190 	int i, count, len;
2191 	struct berval *val;
2192 	BerVarray keys;
2193 
2194 	/* Yes, this is necessary */
2195 	val = UTF8bvnormalize( ((struct berval *)assertedValue),
2196 		NULL, LDAP_UTF8_APPROX, NULL );
2197 	if( val == NULL || BER_BVISNULL( val ) ) {
2198 		keys = (struct berval *)ch_malloc( sizeof(struct berval) );
2199 		BER_BVZERO( &keys[0] );
2200 		*keysp = keys;
2201 		ber_bvfree( val );
2202 		return LDAP_SUCCESS;
2203 	}
2204 
2205 	/* Isolate how many words there are. There will be a key for each */
2206 	for( count = 0,c = val->bv_val; *c; c++) {
2207 		len = strcspn(c, SLAPD_APPROX_DELIMITER);
2208 		if( len >= SLAPD_APPROX_WORDLEN ) count++;
2209 		c+= len;
2210 		if (*c == '\0') break;
2211 		*c = '\0';
2212 	}
2213 
2214 	/* Allocate storage for new keys */
2215 	keys = (struct berval *)ch_malloc( (count + 1) * sizeof(struct berval) );
2216 
2217 	/* Get a phonetic copy of each word */
2218 	for( c = val->bv_val, i = 0; i < count; c += len + 1 ) {
2219 		len = strlen(c);
2220 		if( len < SLAPD_APPROX_WORDLEN ) continue;
2221 		ber_str2bv( phonetic( c ), 0, 0, &keys[i] );
2222 		i++;
2223 	}
2224 
2225 	ber_bvfree( val );
2226 
2227 	BER_BVZERO( &keys[count] );
2228 	*keysp = keys;
2229 
2230 	return LDAP_SUCCESS;
2231 }
2232 
2233 /* Remove all spaces and '-' characters */
2234 static int
2235 telephoneNumberNormalize(
2236 	slap_mask_t usage,
2237 	Syntax *syntax,
2238 	MatchingRule *mr,
2239 	struct berval *val,
2240 	struct berval *normalized,
2241 	void *ctx )
2242 {
2243 	char *p, *q;
2244 
2245 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
2246 
2247 	/* validator should have refused an empty string */
2248 	assert( !BER_BVISEMPTY( val ) );
2249 
2250 	q = normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
2251 
2252 	for( p = val->bv_val; *p; p++ ) {
2253 		if ( ! ( ASCII_SPACE( *p ) || *p == '-' )) {
2254 			*q++ = *p;
2255 		}
2256 	}
2257 	*q = '\0';
2258 
2259 	normalized->bv_len = q - normalized->bv_val;
2260 
2261 	if( BER_BVISEMPTY( normalized ) ) {
2262 		slap_sl_free( normalized->bv_val, ctx );
2263 		BER_BVZERO( normalized );
2264 		return LDAP_INVALID_SYNTAX;
2265 	}
2266 
2267 	return LDAP_SUCCESS;
2268 }
2269 
2270 static int
2271 postalAddressValidate(
2272 	Syntax *syntax,
2273 	struct berval *in )
2274 {
2275 	struct berval bv = *in;
2276 	ber_len_t c;
2277 
2278 	for ( c = 0; c < in->bv_len; c++ ) {
2279 		if ( in->bv_val[c] == '\\' ) {
2280 			c++;
2281 			if ( strncasecmp( &in->bv_val[c], "24", STRLENOF( "24" ) ) != 0
2282 				&& strncasecmp( &in->bv_val[c], "5C", STRLENOF( "5C" ) ) != 0 )
2283 			{
2284 				return LDAP_INVALID_SYNTAX;
2285 			}
2286 			continue;
2287 		}
2288 
2289 		if ( in->bv_val[c] == '$' ) {
2290 			bv.bv_len = &in->bv_val[c] - bv.bv_val;
2291 			if ( UTF8StringValidate( NULL, &bv ) != LDAP_SUCCESS ) {
2292 				return LDAP_INVALID_SYNTAX;
2293 			}
2294 			bv.bv_val = &in->bv_val[c] + 1;
2295 		}
2296 	}
2297 
2298 	bv.bv_len = &in->bv_val[c] - bv.bv_val;
2299 	return UTF8StringValidate( NULL, &bv );
2300 }
2301 
2302 static int
2303 postalAddressNormalize(
2304 	slap_mask_t usage,
2305 	Syntax *syntax,
2306 	MatchingRule *mr,
2307 	struct berval *val,
2308 	struct berval *normalized,
2309 	void *ctx )
2310 {
2311 	BerVarray lines = NULL, nlines = NULL;
2312 	ber_len_t l, c;
2313 	int rc = LDAP_SUCCESS;
2314 	MatchingRule *xmr = NULL;
2315 	char *p;
2316 
2317 	if ( SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseIgnoreListMatch ) ) {
2318 		xmr = slap_schema.si_mr_caseIgnoreMatch;
2319 
2320 	} else {
2321 		xmr = slap_schema.si_mr_caseExactMatch;
2322 	}
2323 
2324 	for ( l = 0, c = 0; c < val->bv_len; c++ ) {
2325 		if ( val->bv_val[c] == '$' ) {
2326 			l++;
2327 		}
2328 	}
2329 
2330 	lines = slap_sl_calloc( sizeof( struct berval ), 2 * ( l + 2 ), ctx );
2331 	nlines = &lines[l + 2];
2332 
2333 	lines[0].bv_val = val->bv_val;
2334 	for ( l = 0, c = 0; c < val->bv_len; c++ ) {
2335 		if ( val->bv_val[c] == '$' ) {
2336 			lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
2337 			l++;
2338 			lines[l].bv_val = &val->bv_val[c + 1];
2339 		}
2340 	}
2341 	lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
2342 
2343 	normalized->bv_len = c = l;
2344 
2345 	for ( l = 0; l <= c; l++ ) {
2346 		/* NOTE: we directly normalize each line,
2347 		 * without unescaping the values, since the special
2348 		 * values '\24' ('$') and '\5C' ('\') are not affected
2349 		 * by normalization */
2350 		if ( !lines[l].bv_len ) {
2351 			nlines[l].bv_len = 0;
2352 			nlines[l].bv_val = NULL;
2353 			continue;
2354 		}
2355 		rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx );
2356 		if ( rc != LDAP_SUCCESS ) {
2357 			rc = LDAP_INVALID_SYNTAX;
2358 			goto done;
2359 		}
2360 
2361 		normalized->bv_len += nlines[l].bv_len;
2362 	}
2363 
2364 	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2365 
2366 	p = normalized->bv_val;
2367 	for ( l = 0; l <= c ; l++ ) {
2368 		p = lutil_strbvcopy( p, &nlines[l] );
2369 		*p++ = '$';
2370 	}
2371 	*--p = '\0';
2372 
2373 	assert( p == &normalized->bv_val[normalized->bv_len] );
2374 
2375 done:;
2376 	if ( nlines != NULL ) {
2377 		for ( l = 0; !BER_BVISNULL( &nlines[ l ] ); l++ ) {
2378 			slap_sl_free( nlines[l].bv_val, ctx );
2379 		}
2380 
2381 		slap_sl_free( lines, ctx );
2382 	}
2383 
2384 	return rc;
2385 }
2386 
2387 int
2388 numericoidValidate(
2389 	Syntax *syntax,
2390 	struct berval *in )
2391 {
2392 	struct berval val = *in;
2393 
2394 	if( BER_BVISEMPTY( &val ) ) {
2395 		/* disallow empty strings */
2396 		return LDAP_INVALID_SYNTAX;
2397 	}
2398 
2399 	while( OID_LEADCHAR( val.bv_val[0] ) ) {
2400 		if ( val.bv_len == 1 ) {
2401 			return LDAP_SUCCESS;
2402 		}
2403 
2404 		if ( val.bv_val[0] == '0' && !OID_SEPARATOR( val.bv_val[1] )) {
2405 			break;
2406 		}
2407 
2408 		val.bv_val++;
2409 		val.bv_len--;
2410 
2411 		while ( OID_LEADCHAR( val.bv_val[0] )) {
2412 			val.bv_val++;
2413 			val.bv_len--;
2414 
2415 			if ( val.bv_len == 0 ) {
2416 				return LDAP_SUCCESS;
2417 			}
2418 		}
2419 
2420 		if( !OID_SEPARATOR( val.bv_val[0] )) {
2421 			break;
2422 		}
2423 
2424 		val.bv_val++;
2425 		val.bv_len--;
2426 	}
2427 
2428 	return LDAP_INVALID_SYNTAX;
2429 }
2430 
2431 static int
2432 integerValidate(
2433 	Syntax *syntax,
2434 	struct berval *in )
2435 {
2436 	ber_len_t i;
2437 	struct berval val = *in;
2438 
2439 	if ( BER_BVISEMPTY( &val ) ) return LDAP_INVALID_SYNTAX;
2440 
2441 	if ( val.bv_val[0] == '-' ) {
2442 		val.bv_len--;
2443 		val.bv_val++;
2444 
2445 		if( BER_BVISEMPTY( &val ) ) { /* bare "-" */
2446 			return LDAP_INVALID_SYNTAX;
2447 		}
2448 
2449 		if( val.bv_val[0] == '0' ) { /* "-0" */
2450 			return LDAP_INVALID_SYNTAX;
2451 		}
2452 
2453 	} else if ( val.bv_val[0] == '0' ) {
2454 		if( val.bv_len > 1 ) { /* "0<more>" */
2455 			return LDAP_INVALID_SYNTAX;
2456 		}
2457 
2458 		return LDAP_SUCCESS;
2459 	}
2460 
2461 	for( i=0; i < val.bv_len; i++ ) {
2462 		if( !ASCII_DIGIT(val.bv_val[i]) ) {
2463 			return LDAP_INVALID_SYNTAX;
2464 		}
2465 	}
2466 
2467 	return LDAP_SUCCESS;
2468 }
2469 
2470 static int
2471 integerMatch(
2472 	int *matchp,
2473 	slap_mask_t flags,
2474 	Syntax *syntax,
2475 	MatchingRule *mr,
2476 	struct berval *value,
2477 	void *assertedValue )
2478 {
2479 	struct berval *asserted = (struct berval *) assertedValue;
2480 	int vsign = 1, asign = 1;	/* default sign = '+' */
2481 	struct berval v, a;
2482 	int match;
2483 
2484 	v = *value;
2485 	if( v.bv_val[0] == '-' ) {
2486 		vsign = -1;
2487 		v.bv_val++;
2488 		v.bv_len--;
2489 	}
2490 
2491 	if( BER_BVISEMPTY( &v ) ) vsign = 0;
2492 
2493 	a = *asserted;
2494 	if( a.bv_val[0] == '-' ) {
2495 		asign = -1;
2496 		a.bv_val++;
2497 		a.bv_len--;
2498 	}
2499 
2500 	if( BER_BVISEMPTY( &a ) ) vsign = 0;
2501 
2502 	match = vsign - asign;
2503 	if( match == 0 ) {
2504 		match = ( v.bv_len != a.bv_len
2505 			? ( v.bv_len < a.bv_len ? -1 : 1 )
2506 			: memcmp( v.bv_val, a.bv_val, v.bv_len ));
2507 		if( vsign < 0 ) match = -match;
2508 	}
2509 
2510 	/* Ordering rule used in extensible match filter? */
2511 	if ( (flags & SLAP_MR_EXT) && (mr->smr_usage & SLAP_MR_ORDERING) )
2512 		match = (match >= 0);
2513 
2514 	*matchp = match;
2515 	return LDAP_SUCCESS;
2516 }
2517 
2518 /* 10**Chop < 256**Chopbytes and Chop > Chopbytes<<1 (for sign bit and itmp) */
2519 #define INDEX_INTLEN_CHOP 7
2520 #define INDEX_INTLEN_CHOPBYTES 3
2521 
2522 static int
2523 integerVal2Key(
2524 	struct berval *in,
2525 	struct berval *key,
2526 	struct berval *tmp,
2527 	void *ctx )
2528 {
2529 	/* Integer index key format, designed for memcmp to collate correctly:
2530 	 * if too large: one's complement sign*<approx exponent=chopped bytes>,
2531 	 * two's complement value (sign-extended or chopped as needed),
2532 	 * however in first byte above, the top <number of exponent-bytes + 1>
2533 	 * bits are the inverse sign and next bit is the sign as delimiter.
2534 	 */
2535 	ber_slen_t k = index_intlen_strlen;
2536 	ber_len_t chop = 0;
2537 	unsigned signmask = ~0x7fU;
2538 	unsigned char lenbuf[sizeof(k) + 2], *lenp, neg = 0xff;
2539 	struct berval val = *in, itmp = *tmp;
2540 
2541 	if ( val.bv_val[0] != '-' ) {
2542 		neg = 0;
2543 		--k;
2544 	}
2545 
2546 	/* Chop least significant digits, increase length instead */
2547 	if ( val.bv_len > (ber_len_t) k ) {
2548 		chop = (val.bv_len-k+2)/INDEX_INTLEN_CHOP; /* 2 fewer digits */
2549 		val.bv_len -= chop * INDEX_INTLEN_CHOP;	/* #digits chopped */
2550 		chop *= INDEX_INTLEN_CHOPBYTES;		/* #bytes added */
2551 	}
2552 
2553 	if ( lutil_str2bin( &val, &itmp, ctx )) {
2554 		return LDAP_INVALID_SYNTAX;
2555 	}
2556 
2557 	/* Omit leading sign byte */
2558 	if ( itmp.bv_val[0] == neg ) {
2559 		itmp.bv_val++;
2560 		itmp.bv_len--;
2561 	}
2562 
2563 	k = (ber_slen_t) index_intlen - (ber_slen_t) (itmp.bv_len + chop);
2564 	if ( k > 0 ) {
2565 		assert( chop == 0 );
2566 		memset( key->bv_val, neg, k );	/* sign-extend */
2567 	} else if ( k != 0 || ((itmp.bv_val[0] ^ neg) & 0xc0) ) {
2568 		/* Got exponent -k, or no room for 2 sign bits */
2569 		lenp = lenbuf + sizeof(lenbuf);
2570 		chop = - (ber_len_t) k;
2571 		do {
2572 			*--lenp = ((unsigned char) chop & 0xff) ^ neg;
2573 			signmask >>= 1;
2574 		} while ( (chop >>= 8) != 0 || (signmask >> 1) & (*lenp ^ neg) );
2575 		/* With n bytes in lenbuf, the top n+1 bits of (signmask&0xff)
2576 		 * are 1, and the top n+2 bits of lenp[0] are the sign bit. */
2577 		k = (lenbuf + sizeof(lenbuf)) - lenp;
2578 		if ( k > (ber_slen_t) index_intlen )
2579 			k = index_intlen;
2580 		memcpy( key->bv_val, lenp, k );
2581 		itmp.bv_len = index_intlen - k;
2582 	}
2583 	memcpy( key->bv_val + k, itmp.bv_val, itmp.bv_len );
2584 	key->bv_val[0] ^= (unsigned char) signmask & 0xff; /* invert sign */
2585 	return 0;
2586 }
2587 
2588 /* Index generation function: Ordered index */
2589 static int
2590 integerIndexer(
2591 	slap_mask_t use,
2592 	slap_mask_t flags,
2593 	Syntax *syntax,
2594 	MatchingRule *mr,
2595 	struct berval *prefix,
2596 	BerVarray values,
2597 	BerVarray *keysp,
2598 	void *ctx )
2599 {
2600 	char ibuf[64];
2601 	struct berval itmp;
2602 	BerVarray keys;
2603 	ber_len_t vlen;
2604 	int i, rc;
2605 	unsigned maxstrlen = index_intlen_strlen + INDEX_INTLEN_CHOP-1;
2606 
2607 	/* count the values and find max needed length */
2608 	vlen = 0;
2609 	for( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
2610 		if ( vlen < values[i].bv_len )
2611 			vlen = values[i].bv_len;
2612 	}
2613 	if ( vlen > maxstrlen )
2614 		vlen = maxstrlen;
2615 
2616 	/* we should have at least one value at this point */
2617 	assert( i > 0 );
2618 
2619 	keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
2620 	for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
2621 		keys[i].bv_len = index_intlen;
2622 		keys[i].bv_val = slap_sl_malloc( index_intlen, ctx );
2623 	}
2624 	keys[i].bv_len = 0;
2625 	keys[i].bv_val = NULL;
2626 
2627 	if ( vlen > sizeof(ibuf) ) {
2628 		itmp.bv_val = slap_sl_malloc( vlen, ctx );
2629 	} else {
2630 		itmp.bv_val = ibuf;
2631 	}
2632 	itmp.bv_len = sizeof(ibuf);
2633 
2634 	for ( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
2635 		if ( itmp.bv_val != ibuf ) {
2636 			itmp.bv_len = values[i].bv_len;
2637 			if ( itmp.bv_len <= sizeof(ibuf) )
2638 				itmp.bv_len = sizeof(ibuf);
2639 			else if ( itmp.bv_len > maxstrlen )
2640 				itmp.bv_len = maxstrlen;
2641 		}
2642 		rc = integerVal2Key( &values[i], &keys[i], &itmp, ctx );
2643 		if ( rc ) {
2644 			slap_sl_free( keys, ctx );
2645 			goto func_leave;
2646 		}
2647 	}
2648 	*keysp = keys;
2649 func_leave:
2650 	if ( itmp.bv_val != ibuf ) {
2651 		slap_sl_free( itmp.bv_val, ctx );
2652 	}
2653 	return rc;
2654 }
2655 
2656 /* Index generation function: Ordered index */
2657 static int
2658 integerFilter(
2659 	slap_mask_t use,
2660 	slap_mask_t flags,
2661 	Syntax *syntax,
2662 	MatchingRule *mr,
2663 	struct berval *prefix,
2664 	void * assertedValue,
2665 	BerVarray *keysp,
2666 	void *ctx )
2667 {
2668 	char ibuf[64];
2669 	struct berval iv;
2670 	BerVarray keys;
2671 	struct berval *value;
2672 	int rc;
2673 
2674 	value = (struct berval *) assertedValue;
2675 
2676 	keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
2677 
2678 	keys[0].bv_len = index_intlen;
2679 	keys[0].bv_val = slap_sl_malloc( index_intlen, ctx );
2680 	keys[1].bv_len = 0;
2681 	keys[1].bv_val = NULL;
2682 
2683 	iv.bv_len = value->bv_len < index_intlen_strlen + INDEX_INTLEN_CHOP-1
2684 		? value->bv_len : index_intlen_strlen + INDEX_INTLEN_CHOP-1;
2685 	if ( iv.bv_len > (int) sizeof(ibuf) ) {
2686 		iv.bv_val = slap_sl_malloc( iv.bv_len, ctx );
2687 	} else {
2688 		iv.bv_val = ibuf;
2689 		iv.bv_len = sizeof(ibuf);
2690 	}
2691 
2692 	rc = integerVal2Key( value, keys, &iv, ctx );
2693 
2694 	if ( iv.bv_val != ibuf ) {
2695 		slap_sl_free( iv.bv_val, ctx );
2696 	}
2697 
2698 	if ( rc == 0 )
2699 		*keysp = keys;
2700 	else
2701 		slap_sl_free( keys, ctx );
2702 
2703 	return rc;
2704 }
2705 
2706 static int
2707 countryStringValidate(
2708 	Syntax *syntax,
2709 	struct berval *val )
2710 {
2711 	if( val->bv_len != 2 ) return LDAP_INVALID_SYNTAX;
2712 
2713 	if( !SLAP_PRINTABLE(val->bv_val[0]) ) {
2714 		return LDAP_INVALID_SYNTAX;
2715 	}
2716 	if( !SLAP_PRINTABLE(val->bv_val[1]) ) {
2717 		return LDAP_INVALID_SYNTAX;
2718 	}
2719 
2720 	return LDAP_SUCCESS;
2721 }
2722 
2723 static int
2724 printableStringValidate(
2725 	Syntax *syntax,
2726 	struct berval *val )
2727 {
2728 	ber_len_t i;
2729 
2730 	if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
2731 
2732 	for(i=0; i < val->bv_len; i++) {
2733 		if( !SLAP_PRINTABLE(val->bv_val[i]) ) {
2734 			return LDAP_INVALID_SYNTAX;
2735 		}
2736 	}
2737 
2738 	return LDAP_SUCCESS;
2739 }
2740 
2741 static int
2742 printablesStringValidate(
2743 	Syntax *syntax,
2744 	struct berval *val )
2745 {
2746 	ber_len_t i, len;
2747 
2748 	if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
2749 
2750 	for(i=0,len=0; i < val->bv_len; i++) {
2751 		int c = val->bv_val[i];
2752 
2753 		if( c == '$' ) {
2754 			if( len == 0 ) {
2755 				return LDAP_INVALID_SYNTAX;
2756 			}
2757 			len = 0;
2758 
2759 		} else if ( SLAP_PRINTABLE(c) ) {
2760 			len++;
2761 		} else {
2762 			return LDAP_INVALID_SYNTAX;
2763 		}
2764 	}
2765 
2766 	if( len == 0 ) {
2767 		return LDAP_INVALID_SYNTAX;
2768 	}
2769 
2770 	return LDAP_SUCCESS;
2771 }
2772 
2773 static int
2774 IA5StringValidate(
2775 	Syntax *syntax,
2776 	struct berval *val )
2777 {
2778 	ber_len_t i;
2779 
2780 	for(i=0; i < val->bv_len; i++) {
2781 		if( !LDAP_ASCII(val->bv_val[i]) ) {
2782 			return LDAP_INVALID_SYNTAX;
2783 		}
2784 	}
2785 
2786 	return LDAP_SUCCESS;
2787 }
2788 
2789 static int
2790 IA5StringNormalize(
2791 	slap_mask_t use,
2792 	Syntax *syntax,
2793 	MatchingRule *mr,
2794 	struct berval *val,
2795 	struct berval *normalized,
2796 	void *ctx )
2797 {
2798 	char *p, *q;
2799 	int casefold = !SLAP_MR_ASSOCIATED( mr,
2800 		slap_schema.si_mr_caseExactIA5Match );
2801 
2802 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
2803 
2804 	p = val->bv_val;
2805 
2806 	/* Ignore initial whitespace */
2807 	while ( ASCII_SPACE( *p ) ) p++;
2808 
2809 	normalized->bv_len = val->bv_len - ( p - val->bv_val );
2810 	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2811 	AC_MEMCPY( normalized->bv_val, p, normalized->bv_len );
2812 	normalized->bv_val[normalized->bv_len] = '\0';
2813 
2814 	p = q = normalized->bv_val;
2815 
2816 	while ( *p ) {
2817 		if ( ASCII_SPACE( *p ) ) {
2818 			*q++ = *p++;
2819 
2820 			/* Ignore the extra whitespace */
2821 			while ( ASCII_SPACE( *p ) ) {
2822 				p++;
2823 			}
2824 
2825 		} else if ( casefold ) {
2826 			/* Most IA5 rules require casefolding */
2827 			*q++ = TOLOWER(*p); p++;
2828 
2829 		} else {
2830 			*q++ = *p++;
2831 		}
2832 	}
2833 
2834 	assert( normalized->bv_val <= p );
2835 	assert( q <= p );
2836 
2837 	/*
2838 	 * If the string ended in space, backup the pointer one
2839 	 * position.  One is enough because the above loop collapsed
2840 	 * all whitespace to a single space.
2841 	 */
2842 	if ( q > normalized->bv_val && ASCII_SPACE( q[-1] ) ) --q;
2843 
2844 	/* null terminate */
2845 	*q = '\0';
2846 
2847 	normalized->bv_len = q - normalized->bv_val;
2848 
2849 	return LDAP_SUCCESS;
2850 }
2851 
2852 static int
2853 UUIDValidate(
2854 	Syntax *syntax,
2855 	struct berval *in )
2856 {
2857 	int i;
2858 	if( in->bv_len != 36 ) {
2859 		return LDAP_INVALID_SYNTAX;
2860 	}
2861 
2862 	for( i=0; i<36; i++ ) {
2863 		switch(i) {
2864 			case 8:
2865 			case 13:
2866 			case 18:
2867 			case 23:
2868 				if( in->bv_val[i] != '-' ) {
2869 					return LDAP_INVALID_SYNTAX;
2870 				}
2871 				break;
2872 			default:
2873 				if( !ASCII_HEX( in->bv_val[i]) ) {
2874 					return LDAP_INVALID_SYNTAX;
2875 				}
2876 		}
2877 	}
2878 
2879 	return LDAP_SUCCESS;
2880 }
2881 
2882 static int
2883 UUIDPretty(
2884 	Syntax *syntax,
2885 	struct berval *in,
2886 	struct berval *out,
2887 	void *ctx )
2888 {
2889 	int i;
2890 	int rc=LDAP_INVALID_SYNTAX;
2891 
2892 	assert( in != NULL );
2893 	assert( out != NULL );
2894 
2895 	if( in->bv_len != 36 ) return LDAP_INVALID_SYNTAX;
2896 
2897 	out->bv_len = 36;
2898 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
2899 
2900 	for( i=0; i<36; i++ ) {
2901 		switch(i) {
2902 			case 8:
2903 			case 13:
2904 			case 18:
2905 			case 23:
2906 				if( in->bv_val[i] != '-' ) {
2907 					goto handle_error;
2908 				}
2909 				out->bv_val[i] = '-';
2910 				break;
2911 
2912 			default:
2913 				if( !ASCII_HEX( in->bv_val[i]) ) {
2914 					goto handle_error;
2915 				}
2916 				out->bv_val[i] = TOLOWER( in->bv_val[i] );
2917 		}
2918 	}
2919 
2920 	rc = LDAP_SUCCESS;
2921 	out->bv_val[ out->bv_len ] = '\0';
2922 
2923 	if( 0 ) {
2924 handle_error:
2925 		slap_sl_free( out->bv_val, ctx );
2926 		out->bv_val = NULL;
2927 	}
2928 
2929 	return rc;
2930 }
2931 
2932 int
2933 UUIDNormalize(
2934 	slap_mask_t usage,
2935 	Syntax *syntax,
2936 	MatchingRule *mr,
2937 	struct berval *val,
2938 	struct berval *normalized,
2939 	void *ctx )
2940 {
2941 	unsigned char octet = '\0';
2942 	int i;
2943 	int j;
2944 
2945 	if ( SLAP_MR_IS_DENORMALIZE( usage ) ) {
2946 		/* NOTE: must be a normalized UUID */
2947 		assert( val->bv_len == 16 );
2948 
2949 		normalized->bv_val = slap_sl_malloc( LDAP_LUTIL_UUIDSTR_BUFSIZE, ctx );
2950 		normalized->bv_len = lutil_uuidstr_from_normalized( val->bv_val,
2951 			val->bv_len, normalized->bv_val, LDAP_LUTIL_UUIDSTR_BUFSIZE );
2952 		assert( normalized->bv_len == STRLENOF( "BADBADBA-DBAD-0123-4567-BADBADBADBAD" ) );
2953 
2954 		return LDAP_SUCCESS;
2955 	}
2956 
2957 	normalized->bv_len = 16;
2958 	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2959 
2960 	for( i=0, j=0; i<36; i++ ) {
2961 		unsigned char nibble;
2962 		if( val->bv_val[i] == '-' ) {
2963 			continue;
2964 
2965 		} else if( ASCII_DIGIT( val->bv_val[i] ) ) {
2966 			nibble = val->bv_val[i] - '0';
2967 
2968 		} else if( ASCII_HEXLOWER( val->bv_val[i] ) ) {
2969 			nibble = val->bv_val[i] - ('a'-10);
2970 
2971 		} else if( ASCII_HEXUPPER( val->bv_val[i] ) ) {
2972 			nibble = val->bv_val[i] - ('A'-10);
2973 
2974 		} else {
2975 			slap_sl_free( normalized->bv_val, ctx );
2976 			BER_BVZERO( normalized );
2977 			return LDAP_INVALID_SYNTAX;
2978 		}
2979 
2980 		if( j & 1 ) {
2981 			octet |= nibble;
2982 			normalized->bv_val[j>>1] = octet;
2983 		} else {
2984 			octet = nibble << 4;
2985 		}
2986 		j++;
2987 	}
2988 
2989 	normalized->bv_val[normalized->bv_len] = 0;
2990 	return LDAP_SUCCESS;
2991 }
2992 
2993 
2994 
2995 int
2996 numericStringValidate(
2997 	Syntax *syntax,
2998 	struct berval *in )
2999 {
3000 	ber_len_t i;
3001 
3002 	if( BER_BVISEMPTY( in ) ) return LDAP_INVALID_SYNTAX;
3003 
3004 	for(i=0; i < in->bv_len; i++) {
3005 		if( !SLAP_NUMERIC(in->bv_val[i]) ) {
3006 			return LDAP_INVALID_SYNTAX;
3007 		}
3008 	}
3009 
3010 	return LDAP_SUCCESS;
3011 }
3012 
3013 static int
3014 numericStringNormalize(
3015 	slap_mask_t usage,
3016 	Syntax *syntax,
3017 	MatchingRule *mr,
3018 	struct berval *val,
3019 	struct berval *normalized,
3020 	void *ctx )
3021 {
3022 	/* removal all spaces */
3023 	char *p, *q;
3024 
3025 	assert( !BER_BVISEMPTY( val ) );
3026 
3027 	normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
3028 
3029 	p = val->bv_val;
3030 	q = normalized->bv_val;
3031 
3032 	while ( *p ) {
3033 		if ( ASCII_SPACE( *p ) ) {
3034 			/* Ignore whitespace */
3035 			p++;
3036 		} else {
3037 			*q++ = *p++;
3038 		}
3039 	}
3040 
3041 	/* we should have copied no more than is in val */
3042 	assert( (q - normalized->bv_val) <= (p - val->bv_val) );
3043 
3044 	/* null terminate */
3045 	*q = '\0';
3046 
3047 	normalized->bv_len = q - normalized->bv_val;
3048 
3049 	if( BER_BVISEMPTY( normalized ) ) {
3050 		normalized->bv_val = slap_sl_realloc( normalized->bv_val, 2, ctx );
3051 		normalized->bv_val[0] = ' ';
3052 		normalized->bv_val[1] = '\0';
3053 		normalized->bv_len = 1;
3054 	}
3055 
3056 	return LDAP_SUCCESS;
3057 }
3058 
3059 /*
3060  * Integer conversion macros that will use the largest available
3061  * type.
3062  */
3063 #if defined(HAVE_STRTOLL) && defined(HAVE_LONG_LONG)
3064 # define SLAP_STRTOL(n,e,b)  strtoll(n,e,b)
3065 # define SLAP_LONG           long long
3066 #else
3067 # define SLAP_STRTOL(n,e,b)  strtol(n,e,b)
3068 # define SLAP_LONG           long
3069 #endif /* HAVE_STRTOLL ... */
3070 
3071 static int
3072 integerBitAndMatch(
3073 	int *matchp,
3074 	slap_mask_t flags,
3075 	Syntax *syntax,
3076 	MatchingRule *mr,
3077 	struct berval *value,
3078 	void *assertedValue )
3079 {
3080 	SLAP_LONG lValue, lAssertedValue;
3081 
3082 	errno = 0;
3083 	/* safe to assume integers are NUL terminated? */
3084 	lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
3085 	if( errno == ERANGE )
3086 	{
3087 		return LDAP_CONSTRAINT_VIOLATION;
3088 	}
3089 
3090 	lAssertedValue = SLAP_STRTOL(((struct berval *)assertedValue)->bv_val,
3091 		NULL, 10);
3092 	if( errno == ERANGE )
3093 	{
3094 		return LDAP_CONSTRAINT_VIOLATION;
3095 	}
3096 
3097 	*matchp = ((lValue & lAssertedValue) == lAssertedValue) ? 0 : 1;
3098 	return LDAP_SUCCESS;
3099 }
3100 
3101 static int
3102 integerBitOrMatch(
3103 	int *matchp,
3104 	slap_mask_t flags,
3105 	Syntax *syntax,
3106 	MatchingRule *mr,
3107 	struct berval *value,
3108 	void *assertedValue )
3109 {
3110 	SLAP_LONG lValue, lAssertedValue;
3111 
3112 	errno = 0;
3113 	/* safe to assume integers are NUL terminated? */
3114 	lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
3115 	if( errno == ERANGE )
3116 	{
3117 		return LDAP_CONSTRAINT_VIOLATION;
3118 	}
3119 
3120 	lAssertedValue = SLAP_STRTOL( ((struct berval *)assertedValue)->bv_val,
3121 		NULL, 10);
3122 	if( errno == ERANGE )
3123 	{
3124 		return LDAP_CONSTRAINT_VIOLATION;
3125 	}
3126 
3127 	*matchp = ((lValue & lAssertedValue) != 0) ? 0 : -1;
3128 	return LDAP_SUCCESS;
3129 }
3130 
3131 static int
3132 checkNum( struct berval *in, struct berval *out )
3133 {
3134 	/* parse serialNumber */
3135 	ber_len_t neg = 0, extra = 0;
3136 	char first = '\0';
3137 
3138 	out->bv_val = in->bv_val;
3139 	out->bv_len = 0;
3140 
3141 	if ( out->bv_val[0] == '-' ) {
3142 		neg++;
3143 		out->bv_len++;
3144 	}
3145 
3146 	if ( strncasecmp( out->bv_val, "0x", STRLENOF("0x") ) == 0 ) {
3147 		first = out->bv_val[2];
3148 		extra = 2;
3149 
3150 		out->bv_len += STRLENOF("0x");
3151 		for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
3152 			if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break;
3153 		}
3154 
3155 	} else if ( out->bv_val[0] == '\'' ) {
3156 		first = out->bv_val[1];
3157 		extra = 3;
3158 
3159 		out->bv_len += STRLENOF("'");
3160 
3161 		for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
3162 			if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break;
3163 		}
3164 		if ( strncmp( &out->bv_val[out->bv_len], "'H", STRLENOF("'H") ) != 0 ) {
3165 			return -1;
3166 		}
3167 		out->bv_len += STRLENOF("'H");
3168 
3169 	} else {
3170 		first = out->bv_val[0];
3171 		for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
3172 			if ( !ASCII_DIGIT( out->bv_val[out->bv_len] ) ) break;
3173 		}
3174 	}
3175 
3176 	if ( !( out->bv_len > neg ) ) {
3177 		return -1;
3178 	}
3179 
3180 	if ( ( out->bv_len > extra + 1 + neg ) && ( first == '0' ) ) {
3181 		return -1;
3182 	}
3183 
3184 	return 0;
3185 }
3186 
3187 static int
3188 serialNumberAndIssuerCheck(
3189 	struct berval *in,
3190 	struct berval *sn,
3191 	struct berval *is,
3192 	void *ctx )
3193 {
3194 	ber_len_t n;
3195 
3196 	if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
3197 
3198 	if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
3199 		/* Parse old format */
3200 		is->bv_val = ber_bvchr( in, '$' );
3201 		if( BER_BVISNULL( is ) ) return LDAP_INVALID_SYNTAX;
3202 
3203 		sn->bv_val = in->bv_val;
3204 		sn->bv_len = is->bv_val - in->bv_val;
3205 
3206 		is->bv_val++;
3207 		is->bv_len = in->bv_len - (sn->bv_len + 1);
3208 
3209 		/* eat leading zeros */
3210 		for( n=0; n < (sn->bv_len-1); n++ ) {
3211 			if( sn->bv_val[n] != '0' ) break;
3212 		}
3213 		sn->bv_val += n;
3214 		sn->bv_len -= n;
3215 
3216 		for( n=0; n < sn->bv_len; n++ ) {
3217 			if( !ASCII_DIGIT(sn->bv_val[n]) ) return LDAP_INVALID_SYNTAX;
3218 		}
3219 
3220 	} else {
3221 		/* Parse GSER format */
3222 		enum {
3223 			HAVE_NONE = 0x0,
3224 			HAVE_ISSUER = 0x1,
3225 			HAVE_SN = 0x2,
3226 			HAVE_ALL = ( HAVE_ISSUER | HAVE_SN )
3227 		} have = HAVE_NONE;
3228 
3229 		int numdquotes = 0;
3230 		struct berval x = *in;
3231 		struct berval ni;
3232 		x.bv_val++;
3233 		x.bv_len -= 2;
3234 
3235 		do {
3236 			/* eat leading spaces */
3237 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3238 				/* empty */;
3239 			}
3240 
3241 			/* should be at issuer or serialNumber NamedValue */
3242 			if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
3243 				if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX;
3244 
3245 				/* parse issuer */
3246 				x.bv_val += STRLENOF("issuer");
3247 				x.bv_len -= STRLENOF("issuer");
3248 
3249 				if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3250 				x.bv_val++;
3251 				x.bv_len--;
3252 
3253 				/* eat leading spaces */
3254 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3255 					/* empty */;
3256 				}
3257 
3258 				/* For backward compatibility, this part is optional */
3259 				if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) == 0 ) {
3260 					x.bv_val += STRLENOF("rdnSequence:");
3261 					x.bv_len -= STRLENOF("rdnSequence:");
3262 				}
3263 
3264 				if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3265 				x.bv_val++;
3266 				x.bv_len--;
3267 
3268 				is->bv_val = x.bv_val;
3269 				is->bv_len = 0;
3270 
3271 				for ( ; is->bv_len < x.bv_len; ) {
3272 					if ( is->bv_val[is->bv_len] != '"' ) {
3273 						is->bv_len++;
3274 						continue;
3275 					}
3276 					if ( is->bv_val[is->bv_len+1] == '"' ) {
3277 						/* double dquote */
3278 						numdquotes++;
3279 						is->bv_len += 2;
3280 						continue;
3281 					}
3282 					break;
3283 				}
3284 				x.bv_val += is->bv_len + 1;
3285 				x.bv_len -= is->bv_len + 1;
3286 
3287 				have |= HAVE_ISSUER;
3288 
3289 			} else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 )
3290 			{
3291 				if ( have & HAVE_SN ) return LDAP_INVALID_SYNTAX;
3292 
3293 				/* parse serialNumber */
3294 				x.bv_val += STRLENOF("serialNumber");
3295 				x.bv_len -= STRLENOF("serialNumber");
3296 
3297 				if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3298 				x.bv_val++;
3299 				x.bv_len--;
3300 
3301 				/* eat leading spaces */
3302 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3303 					/* empty */;
3304 				}
3305 
3306 				if ( checkNum( &x, sn ) ) {
3307 					return LDAP_INVALID_SYNTAX;
3308 				}
3309 
3310 				x.bv_val += sn->bv_len;
3311 				x.bv_len -= sn->bv_len;
3312 
3313 				have |= HAVE_SN;
3314 
3315 			} else {
3316 				return LDAP_INVALID_SYNTAX;
3317 			}
3318 
3319 			/* eat leading spaces */
3320 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3321 				/* empty */;
3322 			}
3323 
3324 			if ( have == HAVE_ALL ) {
3325 				break;
3326 			}
3327 
3328 			if ( x.bv_val[0] != ',' ) {
3329 				return LDAP_INVALID_SYNTAX;
3330 			}
3331 
3332 			x.bv_val++;
3333 			x.bv_len--;
3334 		} while ( 1 );
3335 
3336 		/* should have no characters left... */
3337 		if ( x.bv_len ) return LDAP_INVALID_SYNTAX;
3338 
3339 		if ( numdquotes == 0 ) {
3340 			ber_dupbv_x( &ni, is, ctx );
3341 
3342 		} else {
3343 			ber_len_t src, dst;
3344 
3345 			ni.bv_len = is->bv_len - numdquotes;
3346 			ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx );
3347 			for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
3348 				if ( is->bv_val[src] == '"' ) {
3349 					src++;
3350 				}
3351 				ni.bv_val[dst] = is->bv_val[src];
3352 			}
3353 			ni.bv_val[dst] = '\0';
3354 		}
3355 
3356 		*is = ni;
3357 	}
3358 
3359 	return 0;
3360 }
3361 
3362 static int
3363 serialNumberAndIssuerValidate(
3364 	Syntax *syntax,
3365 	struct berval *in )
3366 {
3367 	int rc;
3368 	struct berval sn, i;
3369 
3370 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n",
3371 		in->bv_val, 0, 0 );
3372 
3373 	rc = serialNumberAndIssuerCheck( in, &sn, &i, NULL );
3374 	if ( rc ) {
3375 		goto done;
3376 	}
3377 
3378 	/* validate DN -- doesn't handle double dquote */
3379 	rc = dnValidate( NULL, &i );
3380 	if ( rc ) {
3381 		rc = LDAP_INVALID_SYNTAX;
3382 	}
3383 
3384 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3385 		slap_sl_free( i.bv_val, NULL );
3386 	}
3387 
3388 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: <%s> err=%d\n",
3389 		in->bv_val, rc, 0 );
3390 
3391 done:;
3392 	return rc;
3393 }
3394 
3395 static int
3396 serialNumberAndIssuerPretty(
3397 	Syntax *syntax,
3398 	struct berval *in,
3399 	struct berval *out,
3400 	void *ctx )
3401 {
3402 	int rc;
3403 	struct berval sn, i, ni = BER_BVNULL;
3404 	char *p;
3405 
3406 	assert( in != NULL );
3407 	assert( out != NULL );
3408 
3409 	BER_BVZERO( out );
3410 
3411 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerPretty: <%s>\n",
3412 		in->bv_val, 0, 0 );
3413 
3414 	rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx );
3415 	if ( rc ) {
3416 		goto done;
3417 	}
3418 
3419 	rc = dnPretty( syntax, &i, &ni, ctx );
3420 
3421 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3422 		slap_sl_free( i.bv_val, ctx );
3423 	}
3424 
3425 	if ( rc ) {
3426 		rc = LDAP_INVALID_SYNTAX;
3427 		goto done;
3428 	}
3429 
3430 	/* make room from sn + "$" */
3431 	out->bv_len = STRLENOF("{ serialNumber , issuer rdnSequence:\"\" }")
3432 		+ sn.bv_len + ni.bv_len;
3433 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
3434 
3435 	if ( out->bv_val == NULL ) {
3436 		out->bv_len = 0;
3437 		rc = LDAP_OTHER;
3438 		goto done;
3439 	}
3440 
3441 	p = out->bv_val;
3442 	p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
3443 	p = lutil_strbvcopy( p, &sn );
3444 	p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
3445 	p = lutil_strbvcopy( p, &ni );
3446 	p = lutil_strcopy( p, /*{*/ "\" }" );
3447 
3448 	assert( p == &out->bv_val[out->bv_len] );
3449 
3450 done:;
3451 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s> => <%s>\n",
3452 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
3453 
3454 	slap_sl_free( ni.bv_val, ctx );
3455 
3456 	return LDAP_SUCCESS;
3457 }
3458 
3459 static int
3460 slap_bin2hex(
3461 	struct berval *in,
3462 	struct berval *out,
3463 	void *ctx )
3464 
3465 {
3466 	/* Use hex format. '123456789abcdef'H */
3467 	unsigned char *ptr, zero = '\0';
3468 	char *sptr;
3469 	int first;
3470 	ber_len_t i, len, nlen;
3471 
3472 	assert( in != NULL );
3473 	assert( !BER_BVISNULL( in ) );
3474 	assert( out != NULL );
3475 	assert( !BER_BVISNULL( out ) );
3476 
3477 	ptr = (unsigned char *)in->bv_val;
3478 	len = in->bv_len;
3479 
3480 	/* Check for minimal encodings */
3481 	if ( len > 1 ) {
3482 		if ( ptr[0] & 0x80 ) {
3483 			if ( ( ptr[0] == 0xff ) && ( ptr[1] & 0x80 ) ) {
3484 				return -1;
3485 			}
3486 
3487 		} else if ( ptr[0] == 0 ) {
3488 			if ( !( ptr[1] & 0x80 ) ) {
3489 				return -1;
3490 			}
3491 			len--;
3492 			ptr++;
3493 		}
3494 
3495 	} else if ( len == 0 ) {
3496 		/* FIXME: this should not be possible,
3497 		 * since a value of zero would have length 1 */
3498 		len = 1;
3499 		ptr = &zero;
3500 	}
3501 
3502 	first = !( ptr[0] & 0xf0U );
3503 	nlen = len * 2 - first + STRLENOF("''H"); /* quotes, H */
3504 	if ( nlen >= out->bv_len ) {
3505 		out->bv_val = slap_sl_malloc( nlen + 1, ctx );
3506 	}
3507 	sptr = out->bv_val;
3508 	*sptr++ = '\'';
3509 	i = 0;
3510 	if ( first ) {
3511 		sprintf( sptr, "%01X", ( ptr[0] & 0x0fU ) );
3512 		sptr++;
3513 		i = 1;
3514 	}
3515 	for ( ; i < len; i++ ) {
3516 		sprintf( sptr, "%02X", ptr[i] );
3517 		sptr += 2;
3518 	}
3519 	*sptr++ = '\'';
3520 	*sptr++ = 'H';
3521 	*sptr = '\0';
3522 
3523 	assert( sptr == &out->bv_val[nlen] );
3524 
3525 	out->bv_len = nlen;
3526 
3527 	return 0;
3528 }
3529 
3530 #define SLAP_SN_BUFLEN	(64)
3531 
3532 /*
3533  * This routine is called by certificateExactNormalize when
3534  * certificateExactNormalize receives a search string instead of
3535  * a certificate. This routine checks if the search value is valid
3536  * and then returns the normalized value
3537  */
3538 static int
3539 serialNumberAndIssuerNormalize(
3540 	slap_mask_t usage,
3541 	Syntax *syntax,
3542 	MatchingRule *mr,
3543 	struct berval *in,
3544 	struct berval *out,
3545 	void *ctx )
3546 {
3547 	struct berval sn, sn2, sn3, i, ni;
3548 	char sbuf2[SLAP_SN_BUFLEN];
3549 	char sbuf3[SLAP_SN_BUFLEN];
3550 	char *p;
3551 	int rc;
3552 
3553 	assert( in != NULL );
3554 	assert( out != NULL );
3555 
3556 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n",
3557 		in->bv_val, 0, 0 );
3558 
3559 	rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx );
3560 	if ( rc ) {
3561 		return rc;
3562 	}
3563 
3564 	rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
3565 
3566 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3567 		slap_sl_free( i.bv_val, ctx );
3568 	}
3569 
3570 	if ( rc ) {
3571 		return LDAP_INVALID_SYNTAX;
3572 	}
3573 
3574 	/* Convert sn to canonical hex */
3575 	sn2.bv_val = sbuf2;
3576 	if ( sn.bv_len > sizeof( sbuf2 ) ) {
3577 		sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
3578 	}
3579 	sn2.bv_len = sn.bv_len;
3580 	sn3.bv_val = sbuf3;
3581 	sn3.bv_len = sizeof(sbuf3);
3582 	if ( lutil_str2bin( &sn, &sn2, ctx ) || slap_bin2hex( &sn2, &sn3, ctx ) ) {
3583 		rc = LDAP_INVALID_SYNTAX;
3584 		goto func_leave;
3585 	}
3586 
3587 	out->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
3588 		+ sn3.bv_len + ni.bv_len;
3589 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
3590 	if ( out->bv_val == NULL ) {
3591 		out->bv_len = 0;
3592 		rc = LDAP_OTHER;
3593 		goto func_leave;
3594 	}
3595 
3596 	p = out->bv_val;
3597 
3598 	p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
3599 	p = lutil_strbvcopy( p, &sn3 );
3600 	p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
3601 	p = lutil_strbvcopy( p, &ni );
3602 	p = lutil_strcopy( p, /*{*/ "\" }" );
3603 
3604 	assert( p == &out->bv_val[out->bv_len] );
3605 
3606 func_leave:
3607 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s> => <%s>\n",
3608 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
3609 
3610 	if ( sn2.bv_val != sbuf2 ) {
3611 		slap_sl_free( sn2.bv_val, ctx );
3612 	}
3613 
3614 	if ( sn3.bv_val != sbuf3 ) {
3615 		slap_sl_free( sn3.bv_val, ctx );
3616 	}
3617 
3618 	slap_sl_free( ni.bv_val, ctx );
3619 
3620 	return rc;
3621 }
3622 
3623 static int
3624 certificateExactNormalize(
3625 	slap_mask_t usage,
3626 	Syntax *syntax,
3627 	MatchingRule *mr,
3628 	struct berval *val,
3629 	struct berval *normalized,
3630 	void *ctx )
3631 {
3632 	BerElementBuffer berbuf;
3633 	BerElement *ber = (BerElement *)&berbuf;
3634 	ber_tag_t tag;
3635 	ber_len_t len;
3636 	ber_int_t i;
3637 	char serialbuf2[SLAP_SN_BUFLEN];
3638 	struct berval sn, sn2 = BER_BVNULL;
3639 	struct berval issuer_dn = BER_BVNULL, bvdn;
3640 	char *p;
3641 	int rc = LDAP_INVALID_SYNTAX;
3642 
3643 	assert( val != NULL );
3644 
3645 	Debug( LDAP_DEBUG_TRACE, ">>> certificateExactNormalize: <%p, %lu>\n",
3646 		val->bv_val, val->bv_len, 0 );
3647 
3648 	if ( BER_BVISEMPTY( val ) ) goto done;
3649 
3650 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
3651 		return serialNumberAndIssuerNormalize( 0, NULL, NULL, val, normalized, ctx );
3652 	}
3653 
3654 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
3655 
3656 	ber_init2( ber, val, LBER_USE_DER );
3657 	tag = ber_skip_tag( ber, &len );	/* Signed Sequence */
3658 	tag = ber_skip_tag( ber, &len );	/* Sequence */
3659 	tag = ber_peek_tag( ber, &len );	/* Optional version? */
3660 	if ( tag == SLAP_X509_OPT_C_VERSION ) {
3661 		tag = ber_skip_tag( ber, &len );
3662 		tag = ber_get_int( ber, &i );	/* version */
3663 	}
3664 
3665 	/* NOTE: move the test here from certificateValidate,
3666 	 * so that we can validate certs with serial longer
3667 	 * than sizeof(ber_int_t) */
3668 	tag = ber_skip_tag( ber, &len );	/* serial */
3669 	sn.bv_len = len;
3670 	sn.bv_val = (char *)ber->ber_ptr;
3671 	sn2.bv_val = serialbuf2;
3672 	sn2.bv_len = sizeof(serialbuf2);
3673 	if ( slap_bin2hex( &sn, &sn2, ctx ) ) {
3674 		rc = LDAP_INVALID_SYNTAX;
3675 		goto done;
3676 	}
3677 	ber_skip_data( ber, len );
3678 
3679 	tag = ber_skip_tag( ber, &len );	/* SignatureAlg */
3680 	ber_skip_data( ber, len );
3681 	tag = ber_peek_tag( ber, &len );	/* IssuerDN */
3682 	if ( len ) {
3683 		len = ber_ptrlen( ber );
3684 		bvdn.bv_val = val->bv_val + len;
3685 		bvdn.bv_len = val->bv_len - len;
3686 
3687 		rc = dnX509normalize( &bvdn, &issuer_dn );
3688 		if ( rc != LDAP_SUCCESS ) goto done;
3689 	}
3690 
3691 	normalized->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
3692 		+ sn2.bv_len + issuer_dn.bv_len;
3693 	normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
3694 
3695 	p = normalized->bv_val;
3696 
3697 	p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
3698 	p = lutil_strbvcopy( p, &sn2 );
3699 	p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
3700 	p = lutil_strbvcopy( p, &issuer_dn );
3701 	p = lutil_strcopy( p, /*{*/ "\" }" );
3702 
3703 	rc = LDAP_SUCCESS;
3704 
3705 done:
3706 	Debug( LDAP_DEBUG_TRACE, "<<< certificateExactNormalize: <%p, %lu> => <%s>\n",
3707 		val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
3708 
3709 	if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
3710 	if ( sn2.bv_val != serialbuf2 ) ber_memfree_x( sn2.bv_val, ctx );
3711 
3712 	return rc;
3713 }
3714 
3715 /* X.509 PKI certificateList stuff */
3716 static int
3717 checkTime( struct berval *in, struct berval *out )
3718 {
3719 	int rc;
3720 	ber_len_t i;
3721 	char buf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
3722 	struct berval bv;
3723 
3724 	assert( in != NULL );
3725 	assert( !BER_BVISNULL( in ) );
3726 	assert( !BER_BVISEMPTY( in ) );
3727 
3728 	if ( in->bv_len < STRLENOF( "YYmmddHHMMSSZ" ) ) {
3729 		return -1;
3730 	}
3731 
3732 	if ( out != NULL ) {
3733 		assert( !BER_BVISNULL( out ) );
3734 		assert( out->bv_len >= sizeof( buf ) );
3735 		bv.bv_val = out->bv_val;
3736 
3737 	} else {
3738 		bv.bv_val = buf;
3739 	}
3740 
3741 	for ( i = 0; i < STRLENOF( "YYYYmmddHHMMSS" ); i++ ) {
3742 		if ( !ASCII_DIGIT( in->bv_val[i] ) ) break;
3743 	}
3744 
3745 	if ( in->bv_val[i] != 'Z' ) {
3746 		return -1;
3747 	}
3748 	i++;
3749 
3750 	if ( i != in->bv_len ) {
3751 		return -1;
3752 	}
3753 
3754 	if ( i == STRLENOF( "YYYYmmddHHMMSSZ" ) ) {
3755 		lutil_strncopy( bv.bv_val, in->bv_val, i );
3756 		bv.bv_len = i;
3757 
3758 	} else if ( i == STRLENOF( "YYmmddHHMMSSZ" ) ) {
3759 		char *p = bv.bv_val;
3760 		if ( in->bv_val[0] < '7' ) {
3761 			p = lutil_strcopy( p, "20" );
3762 
3763 		} else {
3764 			p = lutil_strcopy( p, "19" );
3765 		}
3766 		lutil_strncopy( p, in->bv_val, i );
3767 		bv.bv_len = 2 + i;
3768 
3769 	} else {
3770 		return -1;
3771 	}
3772 
3773 	rc = generalizedTimeValidate( NULL, &bv );
3774 	if ( rc == LDAP_SUCCESS && out != NULL ) {
3775 		if ( out->bv_len > bv.bv_len ) {
3776 			out->bv_val[ bv.bv_len ] = '\0';
3777 		}
3778 		out->bv_len = bv.bv_len;
3779 	}
3780 
3781 	return rc != LDAP_SUCCESS;
3782 }
3783 
3784 static int
3785 issuerAndThisUpdateCheck(
3786 	struct berval *in,
3787 	struct berval *is,
3788 	struct berval *tu,
3789 	void *ctx )
3790 {
3791 	int numdquotes = 0;
3792 	struct berval x = *in;
3793 	struct berval ni = BER_BVNULL;
3794 	/* Parse GSER format */
3795 	enum {
3796 		HAVE_NONE = 0x0,
3797 		HAVE_ISSUER = 0x1,
3798 		HAVE_THISUPDATE = 0x2,
3799 		HAVE_ALL = ( HAVE_ISSUER | HAVE_THISUPDATE )
3800 	} have = HAVE_NONE;
3801 
3802 
3803 	if ( in->bv_len < STRLENOF( "{issuer \"\",thisUpdate \"YYMMDDhhmmssZ\"}" ) ) return LDAP_INVALID_SYNTAX;
3804 
3805 	if ( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
3806 		return LDAP_INVALID_SYNTAX;
3807 	}
3808 
3809 	x.bv_val++;
3810 	x.bv_len -= STRLENOF("{}");
3811 
3812 	do {
3813 		/* eat leading spaces */
3814 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3815 			/* empty */;
3816 		}
3817 
3818 		/* should be at issuer or thisUpdate */
3819 		if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
3820 			if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX;
3821 
3822 			/* parse issuer */
3823 			x.bv_val += STRLENOF("issuer");
3824 			x.bv_len -= STRLENOF("issuer");
3825 
3826 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3827 			x.bv_val++;
3828 			x.bv_len--;
3829 
3830 			/* eat leading spaces */
3831 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3832 				/* empty */;
3833 			}
3834 
3835 			/* For backward compatibility, this part is optional */
3836 			if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) != 0 ) {
3837 				return LDAP_INVALID_SYNTAX;
3838 			}
3839 			x.bv_val += STRLENOF("rdnSequence:");
3840 			x.bv_len -= STRLENOF("rdnSequence:");
3841 
3842 			if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3843 			x.bv_val++;
3844 			x.bv_len--;
3845 
3846 			is->bv_val = x.bv_val;
3847 			is->bv_len = 0;
3848 
3849 			for ( ; is->bv_len < x.bv_len; ) {
3850 				if ( is->bv_val[is->bv_len] != '"' ) {
3851 					is->bv_len++;
3852 					continue;
3853 				}
3854 				if ( is->bv_val[is->bv_len+1] == '"' ) {
3855 					/* double dquote */
3856 					numdquotes++;
3857 					is->bv_len += 2;
3858 					continue;
3859 				}
3860 				break;
3861 			}
3862 			x.bv_val += is->bv_len + 1;
3863 			x.bv_len -= is->bv_len + 1;
3864 
3865 			have |= HAVE_ISSUER;
3866 
3867 		} else if ( strncasecmp( x.bv_val, "thisUpdate", STRLENOF("thisUpdate") ) == 0 )
3868 		{
3869 			if ( have & HAVE_THISUPDATE ) return LDAP_INVALID_SYNTAX;
3870 
3871 			/* parse thisUpdate */
3872 			x.bv_val += STRLENOF("thisUpdate");
3873 			x.bv_len -= STRLENOF("thisUpdate");
3874 
3875 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3876 			x.bv_val++;
3877 			x.bv_len--;
3878 
3879 			/* eat leading spaces */
3880 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3881 				/* empty */;
3882 			}
3883 
3884 			if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3885 			x.bv_val++;
3886 			x.bv_len--;
3887 
3888 			tu->bv_val = x.bv_val;
3889 			tu->bv_len = 0;
3890 
3891 			for ( ; tu->bv_len < x.bv_len; tu->bv_len++ ) {
3892 				if ( tu->bv_val[tu->bv_len] == '"' ) {
3893 					break;
3894 				}
3895 			}
3896 			x.bv_val += tu->bv_len + 1;
3897 			x.bv_len -= tu->bv_len + 1;
3898 
3899 			have |= HAVE_THISUPDATE;
3900 
3901 		} else {
3902 			return LDAP_INVALID_SYNTAX;
3903 		}
3904 
3905 		/* eat leading spaces */
3906 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3907 			/* empty */;
3908 		}
3909 
3910 		if ( have == HAVE_ALL ) {
3911 			break;
3912 		}
3913 
3914 		if ( x.bv_val[0] != ',' ) {
3915 			return LDAP_INVALID_SYNTAX;
3916 		}
3917 
3918 		x.bv_val++;
3919 		x.bv_len--;
3920 	} while ( 1 );
3921 
3922 	/* should have no characters left... */
3923 	if ( x.bv_len ) return LDAP_INVALID_SYNTAX;
3924 
3925 	if ( numdquotes == 0 ) {
3926 		ber_dupbv_x( &ni, is, ctx );
3927 
3928 	} else {
3929 		ber_len_t src, dst;
3930 
3931 		ni.bv_len = is->bv_len - numdquotes;
3932 		ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx );
3933 		for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
3934 			if ( is->bv_val[src] == '"' ) {
3935 				src++;
3936 			}
3937 			ni.bv_val[dst] = is->bv_val[src];
3938 		}
3939 		ni.bv_val[dst] = '\0';
3940 	}
3941 
3942 	*is = ni;
3943 
3944 	return 0;
3945 }
3946 
3947 static int
3948 issuerAndThisUpdateValidate(
3949 	Syntax *syntax,
3950 	struct berval *in )
3951 {
3952 	int rc;
3953 	struct berval i, tu;
3954 
3955 	Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateValidate: <%s>\n",
3956 		in->bv_val, 0, 0 );
3957 
3958 	rc = issuerAndThisUpdateCheck( in, &i, &tu, NULL );
3959 	if ( rc ) {
3960 		goto done;
3961 	}
3962 
3963 	/* validate DN -- doesn't handle double dquote */
3964 	rc = dnValidate( NULL, &i );
3965 	if ( rc ) {
3966 		rc = LDAP_INVALID_SYNTAX;
3967 
3968 	} else if ( checkTime( &tu, NULL ) ) {
3969 		rc = LDAP_INVALID_SYNTAX;
3970 	}
3971 
3972 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3973 		slap_sl_free( i.bv_val, NULL );
3974 	}
3975 
3976 	Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateValidate: <%s> err=%d\n",
3977 		in->bv_val, rc, 0 );
3978 
3979 done:;
3980 	return rc;
3981 }
3982 
3983 static int
3984 issuerAndThisUpdatePretty(
3985 	Syntax *syntax,
3986 	struct berval *in,
3987 	struct berval *out,
3988 	void *ctx )
3989 {
3990 	int rc;
3991 	struct berval i, tu, ni = BER_BVNULL;
3992 	char *p;
3993 
3994 	assert( in != NULL );
3995 	assert( out != NULL );
3996 
3997 	BER_BVZERO( out );
3998 
3999 	Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdatePretty: <%s>\n",
4000 		in->bv_val, 0, 0 );
4001 
4002 	rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx );
4003 	if ( rc ) {
4004 		goto done;
4005 	}
4006 
4007 	rc = dnPretty( syntax, &i, &ni, ctx );
4008 
4009 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4010 		slap_sl_free( i.bv_val, ctx );
4011 	}
4012 
4013 	if ( rc || checkTime( &tu, NULL ) ) {
4014 		rc = LDAP_INVALID_SYNTAX;
4015 		goto done;
4016 	}
4017 
4018 	/* make room */
4019 	out->bv_len = STRLENOF("{ issuer rdnSequence:\"\", thisUpdate \"\" }")
4020 		+ ni.bv_len + tu.bv_len;
4021 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4022 
4023 	if ( out->bv_val == NULL ) {
4024 		out->bv_len = 0;
4025 		rc = LDAP_OTHER;
4026 		goto done;
4027 	}
4028 
4029 	p = out->bv_val;
4030 	p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
4031 	p = lutil_strbvcopy( p, &ni );
4032 	p = lutil_strcopy( p, "\", thisUpdate \"" );
4033 	p = lutil_strbvcopy( p, &tu );
4034 	p = lutil_strcopy( p, /*{*/ "\" }" );
4035 
4036 	assert( p == &out->bv_val[out->bv_len] );
4037 
4038 done:;
4039 	Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdatePretty: <%s> => <%s>\n",
4040 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4041 
4042 	slap_sl_free( ni.bv_val, ctx );
4043 
4044 	return rc;
4045 }
4046 
4047 static int
4048 issuerAndThisUpdateNormalize(
4049 	slap_mask_t usage,
4050 	Syntax *syntax,
4051 	MatchingRule *mr,
4052 	struct berval *in,
4053 	struct berval *out,
4054 	void *ctx )
4055 {
4056 	struct berval i, ni, tu, tu2;
4057 	char sbuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
4058 	char *p;
4059 	int rc;
4060 
4061 	assert( in != NULL );
4062 	assert( out != NULL );
4063 
4064 	Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateNormalize: <%s>\n",
4065 		in->bv_val, 0, 0 );
4066 
4067 	rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx );
4068 	if ( rc ) {
4069 		return rc;
4070 	}
4071 
4072 	rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
4073 
4074 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4075 		slap_sl_free( i.bv_val, ctx );
4076 	}
4077 
4078 	tu2.bv_val = sbuf;
4079 	tu2.bv_len = sizeof( sbuf );
4080 	if ( rc || checkTime( &tu, &tu2 ) ) {
4081 		return LDAP_INVALID_SYNTAX;
4082 	}
4083 
4084 	out->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
4085 		+ ni.bv_len + tu2.bv_len;
4086 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4087 
4088 	if ( out->bv_val == NULL ) {
4089 		out->bv_len = 0;
4090 		rc = LDAP_OTHER;
4091 		goto func_leave;
4092 	}
4093 
4094 	p = out->bv_val;
4095 
4096 	p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
4097 	p = lutil_strbvcopy( p, &ni );
4098 	p = lutil_strcopy( p, "\", thisUpdate \"" );
4099 	p = lutil_strbvcopy( p, &tu2 );
4100 	p = lutil_strcopy( p, /*{*/ "\" }" );
4101 
4102 	assert( p == &out->bv_val[out->bv_len] );
4103 
4104 func_leave:
4105 	Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateNormalize: <%s> => <%s>\n",
4106 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4107 
4108 	slap_sl_free( ni.bv_val, ctx );
4109 
4110 	return rc;
4111 }
4112 
4113 static int
4114 certificateListExactNormalize(
4115 	slap_mask_t usage,
4116 	Syntax *syntax,
4117 	MatchingRule *mr,
4118 	struct berval *val,
4119 	struct berval *normalized,
4120 	void *ctx )
4121 {
4122 	BerElementBuffer berbuf;
4123 	BerElement *ber = (BerElement *)&berbuf;
4124 	ber_tag_t tag;
4125 	ber_len_t len;
4126 	ber_int_t version;
4127 	struct berval issuer_dn = BER_BVNULL, bvdn,
4128 		thisUpdate, bvtu;
4129 	char *p, tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
4130 	int rc = LDAP_INVALID_SYNTAX;
4131 
4132 	assert( val != NULL );
4133 
4134 	Debug( LDAP_DEBUG_TRACE, ">>> certificateListExactNormalize: <%p, %lu>\n",
4135 		val->bv_val, val->bv_len, 0 );
4136 
4137 	if ( BER_BVISEMPTY( val ) ) goto done;
4138 
4139 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
4140 		return issuerAndThisUpdateNormalize( 0, NULL, NULL, val, normalized, ctx );
4141 	}
4142 
4143 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
4144 
4145 	ber_init2( ber, val, LBER_USE_DER );
4146 	tag = ber_skip_tag( ber, &len );	/* Signed wrapper */
4147 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4148 	tag = ber_skip_tag( ber, &len );	/* Sequence */
4149 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4150 	tag = ber_peek_tag( ber, &len );
4151 	/* Optional version */
4152 	if ( tag == LBER_INTEGER ) {
4153 		tag = ber_get_int( ber, &version );
4154 		assert( tag == LBER_INTEGER );
4155 		if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
4156 	}
4157 	tag = ber_skip_tag( ber, &len );	/* Signature Algorithm */
4158 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4159 	ber_skip_data( ber, len );
4160 
4161 	tag = ber_peek_tag( ber, &len );	/* IssuerDN */
4162 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4163 	len = ber_ptrlen( ber );
4164 	bvdn.bv_val = val->bv_val + len;
4165 	bvdn.bv_len = val->bv_len - len;
4166 	tag = ber_skip_tag( ber, &len );
4167 	ber_skip_data( ber, len );
4168 
4169 	tag = ber_skip_tag( ber, &len );	/* thisUpdate */
4170 	/* Time is a CHOICE { UTCTime, GeneralizedTime } */
4171 	if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX;
4172 	bvtu.bv_val = (char *)ber->ber_ptr;
4173 	bvtu.bv_len = len;
4174 
4175 	rc = dnX509normalize( &bvdn, &issuer_dn );
4176 	if ( rc != LDAP_SUCCESS ) goto done;
4177 
4178 	thisUpdate.bv_val = tubuf;
4179 	thisUpdate.bv_len = sizeof(tubuf);
4180 	if ( checkTime( &bvtu, &thisUpdate ) ) {
4181 		rc = LDAP_INVALID_SYNTAX;
4182 		goto done;
4183 	}
4184 
4185 	normalized->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
4186 		+ issuer_dn.bv_len + thisUpdate.bv_len;
4187 	normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
4188 
4189 	p = normalized->bv_val;
4190 
4191 	p = lutil_strcopy( p, "{ issuer rdnSequence:\"" );
4192 	p = lutil_strbvcopy( p, &issuer_dn );
4193 	p = lutil_strcopy( p, "\", thisUpdate \"" );
4194 	p = lutil_strbvcopy( p, &thisUpdate );
4195 	p = lutil_strcopy( p, /*{*/ "\" }" );
4196 
4197 	rc = LDAP_SUCCESS;
4198 
4199 done:
4200 	Debug( LDAP_DEBUG_TRACE, "<<< certificateListExactNormalize: <%p, %lu> => <%s>\n",
4201 		val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
4202 
4203 	if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
4204 
4205 	return rc;
4206 }
4207 
4208 /* X.509 PMI serialNumberAndIssuerSerialCheck
4209 
4210 AttributeCertificateExactAssertion     ::= SEQUENCE {
4211    serialNumber              CertificateSerialNumber,
4212    issuer                    AttCertIssuer }
4213 
4214 CertificateSerialNumber ::= INTEGER
4215 
4216 AttCertIssuer ::=    [0] SEQUENCE {
4217 issuerName                     GeneralNames OPTIONAL,
4218 baseCertificateID         [0] IssuerSerial OPTIONAL,
4219 objectDigestInfo          [1] ObjectDigestInfo OPTIONAL }
4220 -- At least one component shall be present
4221 
4222 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
4223 
4224 GeneralName ::= CHOICE {
4225   otherName                 [0] INSTANCE OF OTHER-NAME,
4226   rfc822Name                [1] IA5String,
4227   dNSName                   [2] IA5String,
4228   x400Address               [3] ORAddress,
4229   directoryName             [4] Name,
4230   ediPartyName              [5] EDIPartyName,
4231   uniformResourceIdentifier [6] IA5String,
4232   iPAddress                 [7] OCTET STRING,
4233   registeredID              [8] OBJECT IDENTIFIER }
4234 
4235 IssuerSerial ::= SEQUENCE {
4236    issuer       GeneralNames,
4237    serial       CertificateSerialNumber,
4238    issuerUID UniqueIdentifier OPTIONAL }
4239 
4240 ObjectDigestInfo ::= SEQUENCE {
4241    digestedObjectType ENUMERATED {
4242       publicKey           (0),
4243       publicKeyCert       (1),
4244       otherObjectTypes    (2) },
4245    otherObjectTypeID      OBJECT IDENTIFIER OPTIONAL,
4246    digestAlgorithm        AlgorithmIdentifier,
4247    objectDigest           BIT STRING }
4248 
4249  * The way I interpret it, an assertion should look like
4250 
4251  { serialNumber 'dd'H,
4252    issuer { issuerName { directoryName:rdnSequence:"cn=yyy" }, -- optional
4253             baseCertificateID { serial '1d'H,
4254                                 issuer { directoryName:rdnSequence:"cn=zzz" },
4255                                 issuerUID <value>              -- optional
4256                               },                               -- optional
4257             objectDigestInfo { ... }                           -- optional
4258           }
4259  }
4260 
4261  * with issuerName, baseCertificateID and objectDigestInfo optional,
4262  * at least one present; the way it's currently implemented, it is
4263 
4264  { serialNumber 'dd'H,
4265    issuer { baseCertificateID { serial '1d'H,
4266                                 issuer { directoryName:rdnSequence:"cn=zzz" }
4267                               }
4268           }
4269  }
4270 
4271  * with all the above parts mandatory.
4272  */
4273 static int
4274 serialNumberAndIssuerSerialCheck(
4275 	struct berval *in,
4276 	struct berval *sn,
4277 	struct berval *is,
4278 	struct berval *i_sn,	/* contain serial of baseCertificateID */
4279 	void *ctx )
4280 {
4281 	/* Parse GSER format */
4282 	enum {
4283 		HAVE_NONE = 0x0,
4284 		HAVE_SN = 0x1,
4285 		HAVE_ISSUER = 0x2,
4286 		HAVE_ALL = ( HAVE_SN | HAVE_ISSUER )
4287 	} have = HAVE_NONE, have2 = HAVE_NONE;
4288 	int numdquotes = 0;
4289 	struct berval x = *in;
4290 	struct berval ni;
4291 
4292 	if ( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
4293 
4294 	/* no old format */
4295 	if ( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) return LDAP_INVALID_SYNTAX;
4296 
4297 	x.bv_val++;
4298 	x.bv_len -= 2;
4299 
4300 	do {
4301 
4302 		/* eat leading spaces */
4303 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4304 			/* empty */;
4305 		}
4306 
4307 		/* should be at issuer or serialNumber NamedValue */
4308 		if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
4309 			if ( have & HAVE_ISSUER ) {
4310 				return LDAP_INVALID_SYNTAX;
4311 			}
4312 
4313 			/* parse IssuerSerial */
4314 			x.bv_val += STRLENOF("issuer");
4315 			x.bv_len -= STRLENOF("issuer");
4316 
4317 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
4318 			x.bv_val++;
4319 			x.bv_len--;
4320 
4321 			/* eat leading spaces */
4322 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4323 				/* empty */;
4324 			}
4325 
4326 			if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
4327 			x.bv_val++;
4328 			x.bv_len--;
4329 
4330 			/* eat leading spaces */
4331 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4332 				/* empty */;
4333 			}
4334 
4335 			if ( strncasecmp( x.bv_val, "baseCertificateID ", STRLENOF("baseCertificateID ") ) != 0 ) {
4336 				return LDAP_INVALID_SYNTAX;
4337 			}
4338 			x.bv_val += STRLENOF("baseCertificateID ");
4339 			x.bv_len -= STRLENOF("baseCertificateID ");
4340 
4341 			/* eat leading spaces */
4342 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4343 				/* empty */;
4344 			}
4345 
4346 			if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
4347 			x.bv_val++;
4348 			x.bv_len--;
4349 
4350 			do {
4351 				/* eat leading spaces */
4352 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4353 					/* empty */;
4354 				}
4355 
4356 				/* parse issuer of baseCertificateID */
4357 				if ( strncasecmp( x.bv_val, "issuer ", STRLENOF("issuer ") ) == 0 ) {
4358 					if ( have2 & HAVE_ISSUER ) {
4359 						return LDAP_INVALID_SYNTAX;
4360 					}
4361 
4362 					x.bv_val += STRLENOF("issuer ");
4363 					x.bv_len -= STRLENOF("issuer ");
4364 
4365 					/* eat leading spaces */
4366 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4367 						/* empty */;
4368 					}
4369 
4370 					if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
4371 					x.bv_val++;
4372 					x.bv_len--;
4373 
4374 					/* eat leading spaces */
4375 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4376 						/* empty */;
4377 					}
4378 
4379 					if ( strncasecmp( x.bv_val, "directoryName:rdnSequence:", STRLENOF("directoryName:rdnSequence:") ) != 0 ) {
4380 						return LDAP_INVALID_SYNTAX;
4381 					}
4382 					x.bv_val += STRLENOF("directoryName:rdnSequence:");
4383 					x.bv_len -= STRLENOF("directoryName:rdnSequence:");
4384 
4385 					if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
4386 					x.bv_val++;
4387 					x.bv_len--;
4388 
4389 					is->bv_val = x.bv_val;
4390 					is->bv_len = 0;
4391 
4392 					for ( ; is->bv_len < x.bv_len; ) {
4393 						if ( is->bv_val[is->bv_len] != '"' ) {
4394 							is->bv_len++;
4395 							continue;
4396 						}
4397 						if ( is->bv_val[is->bv_len + 1] == '"' ) {
4398 							/* double dquote */
4399 							numdquotes++;
4400 							is->bv_len += 2;
4401 							continue;
4402 						}
4403 						break;
4404 					}
4405 					x.bv_val += is->bv_len + 1;
4406 					x.bv_len -= is->bv_len + 1;
4407 
4408 					/* eat leading spaces */
4409 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4410 						/* empty */;
4411 					}
4412 
4413 					if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
4414 					x.bv_val++;
4415 					x.bv_len--;
4416 
4417 					have2 |= HAVE_ISSUER;
4418 
4419 				} else if ( strncasecmp( x.bv_val, "serial ", STRLENOF("serial ") ) == 0 ) {
4420 					if ( have2 & HAVE_SN ) {
4421 						return LDAP_INVALID_SYNTAX;
4422 					}
4423 
4424 					x.bv_val += STRLENOF("serial ");
4425 					x.bv_len -= STRLENOF("serial ");
4426 
4427 					/* eat leading spaces */
4428 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
4429 						/* empty */;
4430 					}
4431 
4432 					if ( checkNum( &x, i_sn ) ) {
4433 						return LDAP_INVALID_SYNTAX;
4434 					}
4435 
4436 					x.bv_val += i_sn->bv_len;
4437 					x.bv_len -= i_sn->bv_len;
4438 
4439 					have2 |= HAVE_SN;
4440 
4441 				} else {
4442 					return LDAP_INVALID_SYNTAX;
4443 				}
4444 
4445 				/* eat leading spaces */
4446 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4447 					/* empty */;
4448 				}
4449 
4450 				if ( have2 == HAVE_ALL ) {
4451 					break;
4452 				}
4453 
4454 				if ( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX;
4455 				x.bv_val++;
4456 				x.bv_len--;
4457 			} while ( 1 );
4458 
4459 			if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
4460 			x.bv_val++;
4461 			x.bv_len--;
4462 
4463 			/* eat leading spaces */
4464 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4465 				/* empty */;
4466 			}
4467 
4468 			if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
4469 			x.bv_val++;
4470 			x.bv_len--;
4471 
4472 			have |= HAVE_ISSUER;
4473 
4474 		} else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) {
4475 			if ( have & HAVE_SN ) {
4476 				return LDAP_INVALID_SYNTAX;
4477 			}
4478 
4479 			/* parse serialNumber */
4480 			x.bv_val += STRLENOF("serialNumber");
4481 			x.bv_len -= STRLENOF("serialNumber");
4482 
4483 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
4484 			x.bv_val++;
4485 			x.bv_len--;
4486 
4487 			/* eat leading spaces */
4488 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4489 				/* empty */;
4490 			}
4491 
4492 			if ( checkNum( &x, sn ) ) {
4493 				return LDAP_INVALID_SYNTAX;
4494 			}
4495 
4496 			x.bv_val += sn->bv_len;
4497 			x.bv_len -= sn->bv_len;
4498 
4499 			have |= HAVE_SN;
4500 
4501 		} else {
4502 			return LDAP_INVALID_SYNTAX;
4503 		}
4504 
4505 		/* eat spaces */
4506 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4507 			/* empty */;
4508 		}
4509 
4510 		if ( have == HAVE_ALL ) {
4511 			break;
4512 		}
4513 
4514 		if ( x.bv_val[0] != ',' ) {
4515 			return LDAP_INVALID_SYNTAX;
4516 		}
4517 		x.bv_val++ ;
4518 		x.bv_len--;
4519 	} while ( 1 );
4520 
4521 	/* should have no characters left... */
4522 	if( x.bv_len ) return LDAP_INVALID_SYNTAX;
4523 
4524 	if ( numdquotes == 0 ) {
4525 		ber_dupbv_x( &ni, is, ctx );
4526 
4527 	} else {
4528 		ber_len_t src, dst;
4529 
4530 		ni.bv_len = is->bv_len - numdquotes;
4531 		ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx );
4532 		for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
4533 			if ( is->bv_val[src] == '"' ) {
4534 				src++;
4535 			}
4536 			ni.bv_val[dst] = is->bv_val[src];
4537 		}
4538 		ni.bv_val[dst] = '\0';
4539 	}
4540 
4541 	*is = ni;
4542 
4543 	/* need to handle double dquotes here */
4544 	return 0;
4545 }
4546 
4547 /* X.509 PMI serialNumberAndIssuerSerialValidate */
4548 static int
4549 serialNumberAndIssuerSerialValidate(
4550 	Syntax *syntax,
4551 	struct berval *in )
4552 {
4553 	int rc;
4554 	struct berval sn, i, i_sn;
4555 
4556 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialValidate: <%s>\n",
4557 		in->bv_val, 0, 0 );
4558 
4559 	rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, NULL );
4560 	if ( rc ) {
4561 		goto done;
4562 	}
4563 
4564 	/* validate DN -- doesn't handle double dquote */
4565 	rc = dnValidate( NULL, &i );
4566 	if ( rc ) {
4567 		rc = LDAP_INVALID_SYNTAX;
4568 	}
4569 
4570 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4571 		slap_sl_free( i.bv_val, NULL );
4572 	}
4573 
4574 done:;
4575 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialValidate: <%s> err=%d\n",
4576 		in->bv_val, rc, 0 );
4577 
4578 	return rc;
4579 }
4580 
4581 /* X.509 PMI serialNumberAndIssuerSerialPretty */
4582 static int
4583 serialNumberAndIssuerSerialPretty(
4584 	Syntax *syntax,
4585 	struct berval *in,
4586 	struct berval *out,
4587 	void *ctx )
4588 {
4589 	struct berval sn, i, i_sn, ni = BER_BVNULL;
4590 	char *p;
4591 	int rc;
4592 
4593 	assert( in != NULL );
4594 	assert( out != NULL );
4595 
4596 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialPretty: <%s>\n",
4597 		in->bv_val, 0, 0 );
4598 
4599 	rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx );
4600 	if ( rc ) {
4601 		goto done;
4602 	}
4603 
4604 	rc = dnPretty( syntax, &i, &ni, ctx );
4605 
4606 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4607 		slap_sl_free( i.bv_val, ctx );
4608 	}
4609 
4610 	if ( rc ) {
4611 		rc = LDAP_INVALID_SYNTAX;
4612 		goto done;
4613 	}
4614 
4615 	/* make room from sn + "$" */
4616 	out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial  } } }")
4617 		+ sn.bv_len + ni.bv_len + i_sn.bv_len;
4618 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4619 
4620 	if ( out->bv_val == NULL ) {
4621 		out->bv_len = 0;
4622 		rc = LDAP_OTHER;
4623 		goto done;
4624 	}
4625 
4626 	p = out->bv_val;
4627 	p = lutil_strcopy( p, "{ serialNumber " );
4628 	p = lutil_strbvcopy( p, &sn );
4629 	p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
4630 	p = lutil_strbvcopy( p, &ni );
4631 	p = lutil_strcopy( p, "\" }, serial " );
4632 	p = lutil_strbvcopy( p, &i_sn );
4633 	p = lutil_strcopy( p, " } } }" );
4634 
4635 	assert( p == &out->bv_val[out->bv_len] );
4636 
4637 done:;
4638 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialPretty: <%s> => <%s>\n",
4639 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4640 
4641 	slap_sl_free( ni.bv_val, ctx );
4642 
4643 	return rc;
4644 }
4645 
4646 /* X.509 PMI serialNumberAndIssuerSerialNormalize */
4647 /*
4648  * This routine is called by attributeCertificateExactNormalize
4649  * when attributeCertificateExactNormalize receives a search
4650  * string instead of a attribute certificate. This routine
4651  * checks if the search value is valid and then returns the
4652  * normalized value
4653  */
4654 static int
4655 serialNumberAndIssuerSerialNormalize(
4656 	slap_mask_t usage,
4657 	Syntax *syntax,
4658 	MatchingRule *mr,
4659 	struct berval *in,
4660 	struct berval *out,
4661 	void *ctx )
4662 {
4663 	struct berval i, ni = BER_BVNULL,
4664 		sn, sn2 = BER_BVNULL, sn3 = BER_BVNULL,
4665 		i_sn, i_sn2 = BER_BVNULL, i_sn3 = BER_BVNULL;
4666 	char sbuf2[SLAP_SN_BUFLEN], i_sbuf2[SLAP_SN_BUFLEN],
4667 		sbuf3[SLAP_SN_BUFLEN], i_sbuf3[SLAP_SN_BUFLEN];
4668 	char *p;
4669 	int rc;
4670 
4671 	assert( in != NULL );
4672 	assert( out != NULL );
4673 
4674 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialNormalize: <%s>\n",
4675 		in->bv_val, 0, 0 );
4676 
4677 	rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx );
4678 	if ( rc ) {
4679 		goto func_leave;
4680 	}
4681 
4682 	rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
4683 
4684 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4685 		slap_sl_free( i.bv_val, ctx );
4686 	}
4687 
4688 	if ( rc ) {
4689 		rc = LDAP_INVALID_SYNTAX;
4690 		goto func_leave;
4691 	}
4692 
4693 	/* Convert sn to canonical hex */
4694 	sn2.bv_val = sbuf2;
4695 	sn2.bv_len = sn.bv_len;
4696 	if ( sn.bv_len > sizeof( sbuf2 ) ) {
4697 		sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
4698 	}
4699 	if ( lutil_str2bin( &sn, &sn2, ctx ) ) {
4700 		rc = LDAP_INVALID_SYNTAX;
4701 		goto func_leave;
4702 	}
4703 
4704         /* Convert i_sn to canonical hex */
4705 	i_sn2.bv_val = i_sbuf2;
4706 	i_sn2.bv_len = i_sn.bv_len;
4707 	if ( i_sn.bv_len > sizeof( i_sbuf2 ) ) {
4708 		i_sn2.bv_val = slap_sl_malloc( i_sn.bv_len, ctx );
4709 	}
4710 	if ( lutil_str2bin( &i_sn, &i_sn2, ctx ) ) {
4711 		rc = LDAP_INVALID_SYNTAX;
4712 		goto func_leave;
4713 	}
4714 
4715 	sn3.bv_val = sbuf3;
4716 	sn3.bv_len = sizeof(sbuf3);
4717 	if ( slap_bin2hex( &sn2, &sn3, ctx ) ) {
4718 		rc = LDAP_INVALID_SYNTAX;
4719 		goto func_leave;
4720 	}
4721 
4722 	i_sn3.bv_val = i_sbuf3;
4723 	i_sn3.bv_len = sizeof(i_sbuf3);
4724 	if ( slap_bin2hex( &i_sn2, &i_sn3, ctx ) ) {
4725 		rc = LDAP_INVALID_SYNTAX;
4726 		goto func_leave;
4727 	}
4728 
4729 	out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial  } } }")
4730 		+ sn3.bv_len + ni.bv_len + i_sn3.bv_len;
4731 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4732 
4733 	if ( out->bv_val == NULL ) {
4734 		out->bv_len = 0;
4735 		rc = LDAP_OTHER;
4736 		goto func_leave;
4737 	}
4738 
4739 	p = out->bv_val;
4740 
4741 	p = lutil_strcopy( p, "{ serialNumber " );
4742 	p = lutil_strbvcopy( p, &sn3 );
4743 	p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
4744 	p = lutil_strbvcopy( p, &ni );
4745 	p = lutil_strcopy( p, "\" }, serial " );
4746 	p = lutil_strbvcopy( p, &i_sn3 );
4747 	p = lutil_strcopy( p, " } } }" );
4748 
4749 	assert( p == &out->bv_val[out->bv_len] );
4750 
4751 func_leave:
4752 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialNormalize: <%s> => <%s>\n",
4753 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4754 
4755 	if ( sn2.bv_val != sbuf2 ) {
4756 		slap_sl_free( sn2.bv_val, ctx );
4757 	}
4758 
4759 	if ( i_sn2.bv_val != i_sbuf2 ) {
4760 		slap_sl_free( i_sn2.bv_val, ctx );
4761 	}
4762 
4763 	if ( sn3.bv_val != sbuf3 ) {
4764 		slap_sl_free( sn3.bv_val, ctx );
4765 	}
4766 
4767 	if ( i_sn3.bv_val != i_sbuf3 ) {
4768 		slap_sl_free( i_sn3.bv_val, ctx );
4769 	}
4770 
4771 	slap_sl_free( ni.bv_val, ctx );
4772 
4773 	return rc;
4774 }
4775 
4776 /* X.509 PMI attributeCertificateExactNormalize */
4777 static int
4778 attributeCertificateExactNormalize(
4779 	slap_mask_t usage,
4780 	Syntax *syntax,
4781 	MatchingRule *mr,
4782 	struct berval *val,
4783 	struct berval *normalized,
4784 	void *ctx )
4785 {
4786 	BerElementBuffer berbuf;
4787 	BerElement *ber = (BerElement *)&berbuf;
4788 	ber_tag_t tag;
4789 	ber_len_t len;
4790 	char issuer_serialbuf[SLAP_SN_BUFLEN], serialbuf[SLAP_SN_BUFLEN];
4791 	struct berval sn, i_sn, sn2 = BER_BVNULL, i_sn2 = BER_BVNULL;
4792 	struct berval issuer_dn = BER_BVNULL, bvdn;
4793 	char *p;
4794 	int rc = LDAP_INVALID_SYNTAX;
4795 
4796 	if ( BER_BVISEMPTY( val ) ) {
4797 		return rc;
4798 	}
4799 
4800 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
4801 		return serialNumberAndIssuerSerialNormalize( 0, NULL, NULL, val, normalized, ctx );
4802 	}
4803 
4804 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
4805 
4806 	ber_init2( ber, val, LBER_USE_DER );
4807 	tag = ber_skip_tag( ber, &len );	/* Signed Sequence */
4808 	tag = ber_skip_tag( ber, &len );	/* Sequence */
4809 	tag = ber_skip_tag( ber, &len );	/* (Mandatory) version; must be v2(1) */
4810 	ber_skip_data( ber, len );
4811 	tag = ber_skip_tag( ber, &len );	/* Holder Sequence */
4812 	ber_skip_data( ber, len );
4813 
4814 	/* Issuer */
4815 	tag = ber_skip_tag( ber, &len );	/* Sequence */
4816 						/* issuerName (GeneralNames sequence; optional)? */
4817 	tag = ber_skip_tag( ber, &len );	/* baseCertificateID (sequence; optional)? */
4818 	tag = ber_skip_tag( ber, &len );	/* GeneralNames (sequence) */
4819 	tag = ber_skip_tag( ber, &len );	/* directoryName (we only accept this form of GeneralName) */
4820 	if ( tag != SLAP_X509_GN_DIRECTORYNAME ) {
4821 		return LDAP_INVALID_SYNTAX;
4822 	}
4823 	tag = ber_peek_tag( ber, &len );	/* sequence of RDN */
4824 	len = ber_ptrlen( ber );
4825 	bvdn.bv_val = val->bv_val + len;
4826 	bvdn.bv_len = val->bv_len - len;
4827 	rc = dnX509normalize( &bvdn, &issuer_dn );
4828 	if ( rc != LDAP_SUCCESS ) goto done;
4829 
4830 	tag = ber_skip_tag( ber, &len );	/* sequence of RDN */
4831 	ber_skip_data( ber, len );
4832 	tag = ber_skip_tag( ber, &len );	/* serial number */
4833 	if ( tag != LBER_INTEGER ) {
4834 		rc = LDAP_INVALID_SYNTAX;
4835 		goto done;
4836 	}
4837 	i_sn.bv_val = (char *)ber->ber_ptr;
4838 	i_sn.bv_len = len;
4839 	i_sn2.bv_val = issuer_serialbuf;
4840 	i_sn2.bv_len = sizeof(issuer_serialbuf);
4841 	if ( slap_bin2hex( &i_sn, &i_sn2, ctx ) ) {
4842 		rc = LDAP_INVALID_SYNTAX;
4843 		goto done;
4844 	}
4845 	ber_skip_data( ber, len );
4846 
4847 						/* issuerUID (bitstring; optional)? */
4848 						/* objectDigestInfo (sequence; optional)? */
4849 
4850 	tag = ber_skip_tag( ber, &len );	/* Signature (sequence) */
4851 	ber_skip_data( ber, len );
4852 	tag = ber_skip_tag( ber, &len );	/* serial number */
4853 	if ( tag != LBER_INTEGER ) {
4854 		rc = LDAP_INVALID_SYNTAX;
4855 		goto done;
4856 	}
4857 	sn.bv_val = (char *)ber->ber_ptr;
4858 	sn.bv_len = len;
4859 	sn2.bv_val = serialbuf;
4860 	sn2.bv_len = sizeof(serialbuf);
4861 	if ( slap_bin2hex( &sn, &sn2, ctx ) ) {
4862 		rc = LDAP_INVALID_SYNTAX;
4863 		goto done;
4864 	}
4865 	ber_skip_data( ber, len );
4866 
4867 	normalized->bv_len = STRLENOF( "{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial  } } }" )
4868 		+ sn2.bv_len + issuer_dn.bv_len + i_sn2.bv_len;
4869 	normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
4870 
4871 	p = normalized->bv_val;
4872 
4873 	p = lutil_strcopy( p, "{ serialNumber " );
4874 	p = lutil_strbvcopy( p, &sn2 );
4875 	p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
4876 	p = lutil_strbvcopy( p, &issuer_dn );
4877 	p = lutil_strcopy( p, "\" }, serial " );
4878 	p = lutil_strbvcopy( p, &i_sn2 );
4879 	p = lutil_strcopy( p, " } } }" );
4880 
4881 	Debug( LDAP_DEBUG_TRACE, "attributeCertificateExactNormalize: %s\n",
4882 		normalized->bv_val, NULL, NULL );
4883 
4884 	rc = LDAP_SUCCESS;
4885 
4886 done:
4887 	if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
4888 	if ( i_sn2.bv_val != issuer_serialbuf ) ber_memfree_x( i_sn2.bv_val, ctx );
4889 	if ( sn2.bv_val != serialbuf ) ber_memfree_x( sn2.bv_val, ctx );
4890 
4891 	return rc;
4892 }
4893 
4894 
4895 static int
4896 hexValidate(
4897 	Syntax *syntax,
4898 	struct berval *in )
4899 {
4900 	ber_len_t	i;
4901 
4902 	assert( in != NULL );
4903 	assert( !BER_BVISNULL( in ) );
4904 
4905 	for ( i = 0; i < in->bv_len; i++ ) {
4906 		if ( !ASCII_HEX( in->bv_val[ i ] ) ) {
4907 			return LDAP_INVALID_SYNTAX;
4908 		}
4909 	}
4910 
4911 	return LDAP_SUCCESS;
4912 }
4913 
4914 /* Normalize a SID as used inside a CSN:
4915  * three-digit numeric string */
4916 static int
4917 hexNormalize(
4918 	slap_mask_t usage,
4919 	Syntax *syntax,
4920 	MatchingRule *mr,
4921 	struct berval *val,
4922 	struct berval *normalized,
4923 	void *ctx )
4924 {
4925 	ber_len_t	i;
4926 
4927 	assert( val != NULL );
4928 	assert( normalized != NULL );
4929 
4930 	ber_dupbv_x( normalized, val, ctx );
4931 
4932 	for ( i = 0; i < normalized->bv_len; i++ ) {
4933 		if ( !ASCII_HEX( normalized->bv_val[ i ] ) ) {
4934 			ber_memfree_x( normalized->bv_val, ctx );
4935 			BER_BVZERO( normalized );
4936 			return LDAP_INVALID_SYNTAX;
4937 		}
4938 
4939 		normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] );
4940 	}
4941 
4942 	return LDAP_SUCCESS;
4943 }
4944 
4945 static int
4946 sidValidate (
4947 	Syntax *syntax,
4948 	struct berval *in )
4949 {
4950 	assert( in != NULL );
4951 	assert( !BER_BVISNULL( in ) );
4952 
4953 	if ( in->bv_len != 3 ) {
4954 		return LDAP_INVALID_SYNTAX;
4955 	}
4956 
4957 	return hexValidate( NULL, in );
4958 }
4959 
4960 /* Normalize a SID as used inside a CSN:
4961  * three-digit numeric string */
4962 static int
4963 sidNormalize(
4964 	slap_mask_t usage,
4965 	Syntax *syntax,
4966 	MatchingRule *mr,
4967 	struct berval *val,
4968 	struct berval *normalized,
4969 	void *ctx )
4970 {
4971 	if ( val->bv_len != 3 ) {
4972 		return LDAP_INVALID_SYNTAX;
4973 	}
4974 
4975 	return hexNormalize( 0, NULL, NULL, val, normalized, ctx );
4976 }
4977 
4978 static int
4979 sidPretty(
4980 	Syntax *syntax,
4981 	struct berval *val,
4982 	struct berval *out,
4983 	void *ctx )
4984 {
4985 	return sidNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx );
4986 }
4987 
4988 /* Normalize a SID as used inside a CSN, either as-is
4989  * (assertion value) or extracted from the CSN
4990  * (attribute value) */
4991 static int
4992 csnSidNormalize(
4993 	slap_mask_t usage,
4994 	Syntax *syntax,
4995 	MatchingRule *mr,
4996 	struct berval *val,
4997 	struct berval *normalized,
4998 	void *ctx )
4999 {
5000 	struct berval	bv;
5001 	char		*ptr,
5002 			buf[ 4 ];
5003 
5004 
5005 	if ( BER_BVISEMPTY( val ) ) {
5006 		return LDAP_INVALID_SYNTAX;
5007 	}
5008 
5009 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
5010 		return sidNormalize( 0, NULL, NULL, val, normalized, ctx );
5011 	}
5012 
5013 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
5014 
5015 	ptr = ber_bvchr( val, '#' );
5016 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5017 		return LDAP_INVALID_SYNTAX;
5018 	}
5019 
5020 	bv.bv_val = ptr + 1;
5021 	bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val );
5022 
5023 	ptr = ber_bvchr( &bv, '#' );
5024 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5025 		return LDAP_INVALID_SYNTAX;
5026 	}
5027 
5028 	bv.bv_val = ptr + 1;
5029 	bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val );
5030 
5031 	ptr = ber_bvchr( &bv, '#' );
5032 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5033 		return LDAP_INVALID_SYNTAX;
5034 	}
5035 
5036 	bv.bv_len = ptr - bv.bv_val;
5037 
5038 	if ( bv.bv_len == 2 ) {
5039 		/* OpenLDAP 2.3 SID */
5040 		buf[ 0 ] = '0';
5041 		buf[ 1 ] = bv.bv_val[ 0 ];
5042 		buf[ 2 ] = bv.bv_val[ 1 ];
5043 		buf[ 3 ] = '\0';
5044 
5045 		bv.bv_val = buf;
5046 		bv.bv_len = 3;
5047 	}
5048 
5049 	return sidNormalize( 0, NULL, NULL, &bv, normalized, ctx );
5050 }
5051 
5052 static int
5053 csnValidate(
5054 	Syntax *syntax,
5055 	struct berval *in )
5056 {
5057 	struct berval	bv;
5058 	char		*ptr;
5059 	int		rc;
5060 
5061 	assert( in != NULL );
5062 	assert( !BER_BVISNULL( in ) );
5063 
5064 	if ( BER_BVISEMPTY( in ) ) {
5065 		return LDAP_INVALID_SYNTAX;
5066 	}
5067 
5068 	bv = *in;
5069 
5070 	ptr = ber_bvchr( &bv, '#' );
5071 	if ( ptr == NULL || ptr == &bv.bv_val[bv.bv_len] ) {
5072 		return LDAP_INVALID_SYNTAX;
5073 	}
5074 
5075 	bv.bv_len = ptr - bv.bv_val;
5076 	if ( bv.bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) &&
5077 		bv.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) )
5078 	{
5079 		return LDAP_INVALID_SYNTAX;
5080 	}
5081 
5082 	rc = generalizedTimeValidate( NULL, &bv );
5083 	if ( rc != LDAP_SUCCESS ) {
5084 		return rc;
5085 	}
5086 
5087 	bv.bv_val = ptr + 1;
5088 	bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
5089 
5090 	ptr = ber_bvchr( &bv, '#' );
5091 	if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) {
5092 		return LDAP_INVALID_SYNTAX;
5093 	}
5094 
5095 	bv.bv_len = ptr - bv.bv_val;
5096 	if ( bv.bv_len != 6 ) {
5097 		return LDAP_INVALID_SYNTAX;
5098 	}
5099 
5100 	rc = hexValidate( NULL, &bv );
5101 	if ( rc != LDAP_SUCCESS ) {
5102 		return rc;
5103 	}
5104 
5105 	bv.bv_val = ptr + 1;
5106 	bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
5107 
5108 	ptr = ber_bvchr( &bv, '#' );
5109 	if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) {
5110 		return LDAP_INVALID_SYNTAX;
5111 	}
5112 
5113 	bv.bv_len = ptr - bv.bv_val;
5114 	if ( bv.bv_len == 2 ) {
5115 		/* tolerate old 2-digit replica-id */
5116 		rc = hexValidate( NULL, &bv );
5117 
5118 	} else {
5119 		rc = sidValidate( NULL, &bv );
5120 	}
5121 	if ( rc != LDAP_SUCCESS ) {
5122 		return rc;
5123 	}
5124 
5125 	bv.bv_val = ptr + 1;
5126 	bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
5127 
5128 	if ( bv.bv_len != 6 ) {
5129 		return LDAP_INVALID_SYNTAX;
5130 	}
5131 
5132 	return hexValidate( NULL, &bv );
5133 }
5134 
5135 /* Normalize a CSN in OpenLDAP 2.1 format */
5136 static int
5137 csnNormalize21(
5138 	slap_mask_t usage,
5139 	Syntax *syntax,
5140 	MatchingRule *mr,
5141 	struct berval *val,
5142 	struct berval *normalized,
5143 	void *ctx )
5144 {
5145 	struct berval	gt, cnt, sid, mod;
5146 	struct berval	bv;
5147 	char		buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ];
5148 	char		*ptr;
5149 	ber_len_t	i;
5150 
5151 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
5152 	assert( !BER_BVISEMPTY( val ) );
5153 
5154 	gt = *val;
5155 
5156 	ptr = ber_bvchr( &gt, '#' );
5157 	if ( ptr == NULL || ptr == &gt.bv_val[gt.bv_len] ) {
5158 		return LDAP_INVALID_SYNTAX;
5159 	}
5160 
5161 	gt.bv_len = ptr - gt.bv_val;
5162 	if ( gt.bv_len != STRLENOF( "YYYYmmddHH:MM:SSZ" ) ) {
5163 		return LDAP_INVALID_SYNTAX;
5164 	}
5165 
5166 	if ( gt.bv_val[ 10 ] != ':' || gt.bv_val[ 13 ] != ':' ) {
5167 		return LDAP_INVALID_SYNTAX;
5168 	}
5169 
5170 	cnt.bv_val = ptr + 1;
5171 	cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
5172 
5173 	ptr = ber_bvchr( &cnt, '#' );
5174 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5175 		return LDAP_INVALID_SYNTAX;
5176 	}
5177 
5178 	cnt.bv_len = ptr - cnt.bv_val;
5179 	if ( cnt.bv_len != STRLENOF( "0x0000" ) ) {
5180 		return LDAP_INVALID_SYNTAX;
5181 	}
5182 
5183 	if ( strncmp( cnt.bv_val, "0x", STRLENOF( "0x" ) ) != 0 ) {
5184 		return LDAP_INVALID_SYNTAX;
5185 	}
5186 
5187 	cnt.bv_val += STRLENOF( "0x" );
5188 	cnt.bv_len -= STRLENOF( "0x" );
5189 
5190 	sid.bv_val = ptr + 1;
5191 	sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
5192 
5193 	ptr = ber_bvchr( &sid, '#' );
5194 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5195 		return LDAP_INVALID_SYNTAX;
5196 	}
5197 
5198 	sid.bv_len = ptr - sid.bv_val;
5199 	if ( sid.bv_len != STRLENOF( "0" ) ) {
5200 		return LDAP_INVALID_SYNTAX;
5201 	}
5202 
5203 	mod.bv_val = ptr + 1;
5204 	mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
5205 	if ( mod.bv_len != STRLENOF( "0000" ) ) {
5206 		return LDAP_INVALID_SYNTAX;
5207 	}
5208 
5209 	bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" );
5210 	bv.bv_val = buf;
5211 
5212 	ptr = bv.bv_val;
5213 	ptr = lutil_strncopy( ptr, gt.bv_val, STRLENOF( "YYYYmmddHH" ) );
5214 	ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:" ) ],
5215 		STRLENOF( "MM" ) );
5216 	ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:MM:" ) ],
5217 		STRLENOF( "SS" ) );
5218 	ptr = lutil_strcopy( ptr, ".000000Z#00" );
5219 	ptr = lutil_strbvcopy( ptr, &cnt );
5220 	*ptr++ = '#';
5221 	*ptr++ = '0';
5222 	*ptr++ = '0';
5223 	*ptr++ = sid.bv_val[ 0 ];
5224 	*ptr++ = '#';
5225 	*ptr++ = '0';
5226 	*ptr++ = '0';
5227 	for ( i = 0; i < mod.bv_len; i++ ) {
5228 		*ptr++ = TOLOWER( mod.bv_val[ i ] );
5229 	}
5230 	*ptr = '\0';
5231 
5232 	assert( ptr == &bv.bv_val[bv.bv_len] );
5233 
5234 	if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) {
5235 		return LDAP_INVALID_SYNTAX;
5236 	}
5237 
5238 	ber_dupbv_x( normalized, &bv, ctx );
5239 
5240 	return LDAP_SUCCESS;
5241 }
5242 
5243 /* Normalize a CSN in OpenLDAP 2.3 format */
5244 static int
5245 csnNormalize23(
5246 	slap_mask_t usage,
5247 	Syntax *syntax,
5248 	MatchingRule *mr,
5249 	struct berval *val,
5250 	struct berval *normalized,
5251 	void *ctx )
5252 {
5253 	struct berval	gt, cnt, sid, mod;
5254 	struct berval	bv;
5255 	char		buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ];
5256 	char		*ptr;
5257 	ber_len_t	i;
5258 
5259 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
5260 	assert( !BER_BVISEMPTY( val ) );
5261 
5262 	gt = *val;
5263 
5264 	ptr = ber_bvchr( &gt, '#' );
5265 	if ( ptr == NULL || ptr == &gt.bv_val[gt.bv_len] ) {
5266 		return LDAP_INVALID_SYNTAX;
5267 	}
5268 
5269 	gt.bv_len = ptr - gt.bv_val;
5270 	if ( gt.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) {
5271 		return LDAP_INVALID_SYNTAX;
5272 	}
5273 
5274 	cnt.bv_val = ptr + 1;
5275 	cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
5276 
5277 	ptr = ber_bvchr( &cnt, '#' );
5278 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5279 		return LDAP_INVALID_SYNTAX;
5280 	}
5281 
5282 	cnt.bv_len = ptr - cnt.bv_val;
5283 	if ( cnt.bv_len != STRLENOF( "000000" ) ) {
5284 		return LDAP_INVALID_SYNTAX;
5285 	}
5286 
5287 	sid.bv_val = ptr + 1;
5288 	sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
5289 
5290 	ptr = ber_bvchr( &sid, '#' );
5291 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5292 		return LDAP_INVALID_SYNTAX;
5293 	}
5294 
5295 	sid.bv_len = ptr - sid.bv_val;
5296 	if ( sid.bv_len != STRLENOF( "00" ) ) {
5297 		return LDAP_INVALID_SYNTAX;
5298 	}
5299 
5300 	mod.bv_val = ptr + 1;
5301 	mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
5302 	if ( mod.bv_len != STRLENOF( "000000" ) ) {
5303 		return LDAP_INVALID_SYNTAX;
5304 	}
5305 
5306 	bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" );
5307 	bv.bv_val = buf;
5308 
5309 	ptr = bv.bv_val;
5310 	ptr = lutil_strncopy( ptr, gt.bv_val, gt.bv_len - 1 );
5311 	ptr = lutil_strcopy( ptr, ".000000Z#" );
5312 	ptr = lutil_strbvcopy( ptr, &cnt );
5313 	*ptr++ = '#';
5314 	*ptr++ = '0';
5315 	for ( i = 0; i < sid.bv_len; i++ ) {
5316 		*ptr++ = TOLOWER( sid.bv_val[ i ] );
5317 	}
5318 	*ptr++ = '#';
5319 	for ( i = 0; i < mod.bv_len; i++ ) {
5320 		*ptr++ = TOLOWER( mod.bv_val[ i ] );
5321 	}
5322 	*ptr = '\0';
5323 
5324 	assert( ptr == &bv.bv_val[bv.bv_len] );
5325 	if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) {
5326 		return LDAP_INVALID_SYNTAX;
5327 	}
5328 
5329 	ber_dupbv_x( normalized, &bv, ctx );
5330 
5331 	return LDAP_SUCCESS;
5332 }
5333 
5334 /* Normalize a CSN */
5335 static int
5336 csnNormalize(
5337 	slap_mask_t usage,
5338 	Syntax *syntax,
5339 	MatchingRule *mr,
5340 	struct berval *val,
5341 	struct berval *normalized,
5342 	void *ctx )
5343 {
5344 	struct berval	cnt, sid, mod;
5345 	char		*ptr;
5346 	ber_len_t	i;
5347 
5348 	assert( val != NULL );
5349 	assert( normalized != NULL );
5350 
5351 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
5352 
5353 	if ( BER_BVISEMPTY( val ) ) {
5354 		return LDAP_INVALID_SYNTAX;
5355 	}
5356 
5357 	if ( val->bv_len == STRLENOF( "YYYYmmddHHMMSSZ#SSSSSS#ID#ssssss" ) ) {
5358 		/* Openldap <= 2.3 */
5359 
5360 		return csnNormalize23( usage, syntax, mr, val, normalized, ctx );
5361 	}
5362 
5363 	if ( val->bv_len == STRLENOF( "YYYYmmddHH:MM:SSZ#0xSSSS#I#ssss" ) ) {
5364 		/* Openldap 2.1 */
5365 
5366 		return csnNormalize21( usage, syntax, mr, val, normalized, ctx );
5367 	}
5368 
5369 	if ( val->bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) ) {
5370 		return LDAP_INVALID_SYNTAX;
5371 	}
5372 
5373 	ptr = ber_bvchr( val, '#' );
5374 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5375 		return LDAP_INVALID_SYNTAX;
5376 	}
5377 
5378 	if ( ptr - val->bv_val != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) ) {
5379 		return LDAP_INVALID_SYNTAX;
5380 	}
5381 
5382 	cnt.bv_val = ptr + 1;
5383 	cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
5384 
5385 	ptr = ber_bvchr( &cnt, '#' );
5386 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5387 		return LDAP_INVALID_SYNTAX;
5388 	}
5389 
5390 	if ( ptr - cnt.bv_val != STRLENOF( "000000" ) ) {
5391 		return LDAP_INVALID_SYNTAX;
5392 	}
5393 
5394 	sid.bv_val = ptr + 1;
5395 	sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
5396 
5397 	ptr = ber_bvchr( &sid, '#' );
5398 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5399 		return LDAP_INVALID_SYNTAX;
5400 	}
5401 
5402 	sid.bv_len = ptr - sid.bv_val;
5403 	if ( sid.bv_len != STRLENOF( "000" ) ) {
5404 		return LDAP_INVALID_SYNTAX;
5405 	}
5406 
5407 	mod.bv_val = ptr + 1;
5408 	mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
5409 
5410 	if ( mod.bv_len != STRLENOF( "000000" ) ) {
5411 		return LDAP_INVALID_SYNTAX;
5412 	}
5413 
5414 	ber_dupbv_x( normalized, val, ctx );
5415 
5416 	for ( i = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#" );
5417 		i < normalized->bv_len; i++ )
5418 	{
5419 		/* assume it's already validated that's all hex digits */
5420 		normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] );
5421 	}
5422 
5423 	return LDAP_SUCCESS;
5424 }
5425 
5426 static int
5427 csnPretty(
5428 	Syntax *syntax,
5429 	struct berval *val,
5430 	struct berval *out,
5431 	void *ctx )
5432 {
5433 	return csnNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx );
5434 }
5435 
5436 #ifndef SUPPORT_OBSOLETE_UTC_SYNTAX
5437 /* slight optimization - does not need the start parameter */
5438 #define check_time_syntax(v, start, p, f) (check_time_syntax)(v, p, f)
5439 enum { start = 0 };
5440 #endif
5441 
5442 static int
5443 check_time_syntax (struct berval *val,
5444 	int start,
5445 	int *parts,
5446 	struct berval *fraction)
5447 {
5448 	/*
5449 	 * start=0 GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM])
5450 	 * start=1 UTCTime         YYmmddHHMM[SS][Z|(+/-)HHMM]
5451 	 * GeneralizedTime supports leap seconds, UTCTime does not.
5452 	 */
5453 	static const int ceiling[9] = { 100, 100, 12, 31, 24, 60, 60, 24, 60 };
5454 	static const int mdays[2][12] = {
5455 		/* non-leap years */
5456 		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
5457 		/* leap years */
5458 		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
5459 	};
5460 	char *p, *e;
5461 	int part, c, c1, c2, tzoffset, leapyear = 0;
5462 
5463 	p = val->bv_val;
5464 	e = p + val->bv_len;
5465 
5466 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
5467 	parts[0] = 20; /* century - any multiple of 4 from 04 to 96 */
5468 #endif
5469 	for (part = start; part < 7 && p < e; part++) {
5470 		c1 = *p;
5471 		if (!ASCII_DIGIT(c1)) {
5472 			break;
5473 		}
5474 		p++;
5475 		if (p == e) {
5476 			return LDAP_INVALID_SYNTAX;
5477 		}
5478 		c = *p++;
5479 		if (!ASCII_DIGIT(c)) {
5480 			return LDAP_INVALID_SYNTAX;
5481 		}
5482 		c += c1 * 10 - '0' * 11;
5483 		if ((part | 1) == 3) {
5484 			--c;
5485 			if (c < 0) {
5486 				return LDAP_INVALID_SYNTAX;
5487 			}
5488 		}
5489 		if (c >= ceiling[part]) {
5490 			if (! (c == 60 && part == 6 && start == 0))
5491 				return LDAP_INVALID_SYNTAX;
5492 		}
5493 		parts[part] = c;
5494 	}
5495 	if (part < 5 + start) {
5496 		return LDAP_INVALID_SYNTAX;
5497 	}
5498 	for (; part < 9; part++) {
5499 		parts[part] = 0;
5500 	}
5501 
5502 	/* leapyear check for the Gregorian calendar (year>1581) */
5503 	if (parts[parts[1] == 0 ? 0 : 1] % 4 == 0) {
5504 		leapyear = 1;
5505 	}
5506 
5507 	if (parts[3] >= mdays[leapyear][parts[2]]) {
5508 		return LDAP_INVALID_SYNTAX;
5509 	}
5510 
5511 	if (start == 0) {
5512 		fraction->bv_val = p;
5513 		fraction->bv_len = 0;
5514 		if (p < e && (*p == '.' || *p == ',')) {
5515 			char *end_num;
5516 			while (++p < e && ASCII_DIGIT(*p)) {
5517 				/* EMTPY */;
5518 			}
5519 			if (p - fraction->bv_val == 1) {
5520 				return LDAP_INVALID_SYNTAX;
5521 			}
5522 			for (end_num = p; end_num[-1] == '0'; --end_num) {
5523 				/* EMPTY */;
5524 			}
5525 			c = end_num - fraction->bv_val;
5526 			if (c != 1) fraction->bv_len = c;
5527 		}
5528 	}
5529 
5530 	if (p == e) {
5531 		/* no time zone */
5532 		return start == 0 ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
5533 	}
5534 
5535 	tzoffset = *p++;
5536 	switch (tzoffset) {
5537 	default:
5538 		return LDAP_INVALID_SYNTAX;
5539 	case 'Z':
5540 		/* UTC */
5541 		break;
5542 	case '+':
5543 	case '-':
5544 		for (part = 7; part < 9 && p < e; part++) {
5545 			c1 = *p;
5546 			if (!ASCII_DIGIT(c1)) {
5547 				break;
5548 			}
5549 			p++;
5550 			if (p == e) {
5551 				return LDAP_INVALID_SYNTAX;
5552 			}
5553 			c2 = *p++;
5554 			if (!ASCII_DIGIT(c2)) {
5555 				return LDAP_INVALID_SYNTAX;
5556 			}
5557 			parts[part] = c1 * 10 + c2 - '0' * 11;
5558 			if (parts[part] >= ceiling[part]) {
5559 				return LDAP_INVALID_SYNTAX;
5560 			}
5561 		}
5562 		if (part < 8 + start) {
5563 			return LDAP_INVALID_SYNTAX;
5564 		}
5565 
5566 		if (tzoffset == '-') {
5567 			/* negative offset to UTC, ie west of Greenwich */
5568 			parts[4] += parts[7];
5569 			parts[5] += parts[8];
5570 			/* offset is just hhmm, no seconds */
5571 			for (part = 6; --part >= 0; ) {
5572 				if (part != 3) {
5573 					c = ceiling[part];
5574 				} else {
5575 					c = mdays[leapyear][parts[2]];
5576 				}
5577 				if (parts[part] >= c) {
5578 					if (part == 0) {
5579 						return LDAP_INVALID_SYNTAX;
5580 					}
5581 					parts[part] -= c;
5582 					parts[part - 1]++;
5583 					continue;
5584 				} else if (part != 5) {
5585 					break;
5586 				}
5587 			}
5588 		} else {
5589 			/* positive offset to UTC, ie east of Greenwich */
5590 			parts[4] -= parts[7];
5591 			parts[5] -= parts[8];
5592 			for (part = 6; --part >= 0; ) {
5593 				if (parts[part] < 0) {
5594 					if (part == 0) {
5595 						return LDAP_INVALID_SYNTAX;
5596 					}
5597 					if (part != 3) {
5598 						c = ceiling[part];
5599 					} else {
5600 						/* make first arg to % non-negative */
5601 						c = mdays[leapyear][(parts[2] - 1 + 12) % 12];
5602 					}
5603 					parts[part] += c;
5604 					parts[part - 1]--;
5605 					continue;
5606 				} else if (part != 5) {
5607 					break;
5608 				}
5609 			}
5610 		}
5611 	}
5612 
5613 	return p != e ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
5614 }
5615 
5616 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
5617 
5618 #if 0
5619 static int
5620 xutcTimeNormalize(
5621 	Syntax *syntax,
5622 	struct berval *val,
5623 	struct berval *normalized )
5624 {
5625 	int parts[9], rc;
5626 
5627 	rc = check_time_syntax(val, 1, parts, NULL);
5628 	if (rc != LDAP_SUCCESS) {
5629 		return rc;
5630 	}
5631 
5632 	normalized->bv_val = ch_malloc( 14 );
5633 	if ( normalized->bv_val == NULL ) {
5634 		return LBER_ERROR_MEMORY;
5635 	}
5636 
5637 	sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02dZ",
5638 		parts[1], parts[2] + 1, parts[3] + 1,
5639 		parts[4], parts[5], parts[6] );
5640 	normalized->bv_len = 13;
5641 
5642 	return LDAP_SUCCESS;
5643 }
5644 #endif /* 0 */
5645 
5646 static int
5647 utcTimeValidate(
5648 	Syntax *syntax,
5649 	struct berval *in )
5650 {
5651 	int parts[9];
5652 	return check_time_syntax(in, 1, parts, NULL);
5653 }
5654 
5655 #endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */
5656 
5657 static int
5658 generalizedTimeValidate(
5659 	Syntax *syntax,
5660 	struct berval *in )
5661 {
5662 	int parts[9];
5663 	struct berval fraction;
5664 	return check_time_syntax(in, 0, parts, &fraction);
5665 }
5666 
5667 static int
5668 generalizedTimeNormalize(
5669 	slap_mask_t usage,
5670 	Syntax *syntax,
5671 	MatchingRule *mr,
5672 	struct berval *val,
5673 	struct berval *normalized,
5674 	void *ctx )
5675 {
5676 	int parts[9], rc;
5677 	unsigned int len;
5678 	struct berval fraction;
5679 
5680 	rc = check_time_syntax(val, 0, parts, &fraction);
5681 	if (rc != LDAP_SUCCESS) {
5682 		return rc;
5683 	}
5684 
5685 	len = STRLENOF("YYYYmmddHHMMSSZ") + fraction.bv_len;
5686 	normalized->bv_val = slap_sl_malloc( len + 1, ctx );
5687 	if ( BER_BVISNULL( normalized ) ) {
5688 		return LBER_ERROR_MEMORY;
5689 	}
5690 
5691 	sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02d%02d",
5692 		parts[0], parts[1], parts[2] + 1, parts[3] + 1,
5693 		parts[4], parts[5], parts[6] );
5694 	if ( !BER_BVISEMPTY( &fraction ) ) {
5695 		memcpy( normalized->bv_val + STRLENOF("YYYYmmddHHMMSSZ")-1,
5696 			fraction.bv_val, fraction.bv_len );
5697 		normalized->bv_val[STRLENOF("YYYYmmddHHMMSSZ")-1] = '.';
5698 	}
5699 	strcpy( normalized->bv_val + len-1, "Z" );
5700 	normalized->bv_len = len;
5701 
5702 	return LDAP_SUCCESS;
5703 }
5704 
5705 static int
5706 generalizedTimeOrderingMatch(
5707 	int *matchp,
5708 	slap_mask_t flags,
5709 	Syntax *syntax,
5710 	MatchingRule *mr,
5711 	struct berval *value,
5712 	void *assertedValue )
5713 {
5714 	struct berval *asserted = (struct berval *) assertedValue;
5715 	ber_len_t v_len  = value->bv_len;
5716 	ber_len_t av_len = asserted->bv_len;
5717 
5718 	/* ignore trailing 'Z' when comparing */
5719 	int match = memcmp( value->bv_val, asserted->bv_val,
5720 		(v_len < av_len ? v_len : av_len) - 1 );
5721 	if ( match == 0 ) match = v_len - av_len;
5722 
5723 	/* If used in extensible match filter, match if value < asserted */
5724 	if ( flags & SLAP_MR_EXT )
5725 		match = (match >= 0);
5726 
5727 	*matchp = match;
5728 	return LDAP_SUCCESS;
5729 }
5730 
5731 /* Index generation function: Ordered index */
5732 int generalizedTimeIndexer(
5733 	slap_mask_t use,
5734 	slap_mask_t flags,
5735 	Syntax *syntax,
5736 	MatchingRule *mr,
5737 	struct berval *prefix,
5738 	BerVarray values,
5739 	BerVarray *keysp,
5740 	void *ctx )
5741 {
5742 	int i, j;
5743 	BerVarray keys;
5744 	char tmp[5];
5745 	BerValue bvtmp; /* 40 bit index */
5746 	struct lutil_tm tm;
5747 	struct lutil_timet tt;
5748 
5749 	bvtmp.bv_len = sizeof(tmp);
5750 	bvtmp.bv_val = tmp;
5751 	for( i=0; values[i].bv_val != NULL; i++ ) {
5752 		/* just count them */
5753 	}
5754 
5755 	/* we should have at least one value at this point */
5756 	assert( i > 0 );
5757 
5758 	keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
5759 
5760 	/* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
5761 	for( i=0, j=0; values[i].bv_val != NULL; i++ ) {
5762 		assert(values[i].bv_val != NULL && values[i].bv_len >= 10);
5763 		/* Use 40 bits of time for key */
5764 		if ( lutil_parsetime( values[i].bv_val, &tm ) == 0 ) {
5765 			lutil_tm2time( &tm, &tt );
5766 			tmp[0] = tt.tt_gsec & 0xff;
5767 			tmp[4] = tt.tt_sec & 0xff;
5768 			tt.tt_sec >>= 8;
5769 			tmp[3] = tt.tt_sec & 0xff;
5770 			tt.tt_sec >>= 8;
5771 			tmp[2] = tt.tt_sec & 0xff;
5772 			tt.tt_sec >>= 8;
5773 			tmp[1] = tt.tt_sec & 0xff;
5774 
5775 			ber_dupbv_x(&keys[j++], &bvtmp, ctx );
5776 		}
5777 	}
5778 
5779 	keys[j].bv_val = NULL;
5780 	keys[j].bv_len = 0;
5781 
5782 	*keysp = keys;
5783 
5784 	return LDAP_SUCCESS;
5785 }
5786 
5787 /* Index generation function: Ordered index */
5788 int generalizedTimeFilter(
5789 	slap_mask_t use,
5790 	slap_mask_t flags,
5791 	Syntax *syntax,
5792 	MatchingRule *mr,
5793 	struct berval *prefix,
5794 	void * assertedValue,
5795 	BerVarray *keysp,
5796 	void *ctx )
5797 {
5798 	BerVarray keys;
5799 	char tmp[5];
5800 	BerValue bvtmp; /* 40 bit index */
5801 	BerValue *value = (BerValue *) assertedValue;
5802 	struct lutil_tm tm;
5803 	struct lutil_timet tt;
5804 
5805 	bvtmp.bv_len = sizeof(tmp);
5806 	bvtmp.bv_val = tmp;
5807 	/* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
5808 	/* Use 40 bits of time for key */
5809 	if ( value->bv_val && value->bv_len >= 10 &&
5810 		lutil_parsetime( value->bv_val, &tm ) == 0 ) {
5811 
5812 		lutil_tm2time( &tm, &tt );
5813 		tmp[0] = tt.tt_gsec & 0xff;
5814 		tmp[4] = tt.tt_sec & 0xff;
5815 		tt.tt_sec >>= 8;
5816 		tmp[3] = tt.tt_sec & 0xff;
5817 		tt.tt_sec >>= 8;
5818 		tmp[2] = tt.tt_sec & 0xff;
5819 		tt.tt_sec >>= 8;
5820 		tmp[1] = tt.tt_sec & 0xff;
5821 
5822 		keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
5823 		ber_dupbv_x(keys, &bvtmp, ctx );
5824 		keys[1].bv_val = NULL;
5825 		keys[1].bv_len = 0;
5826 	} else {
5827 		keys = NULL;
5828 	}
5829 
5830 	*keysp = keys;
5831 
5832 	return LDAP_SUCCESS;
5833 }
5834 
5835 static int
5836 deliveryMethodValidate(
5837 	Syntax *syntax,
5838 	struct berval *val )
5839 {
5840 #undef LENOF
5841 #define LENOF(s) (sizeof(s)-1)
5842 	struct berval tmp = *val;
5843 	/*
5844      *	DeliveryMethod = pdm *( WSP DOLLAR WSP DeliveryMethod )
5845 	 *	pdm = "any" / "mhs" / "physical" / "telex" / "teletex" /
5846 	 *		"g3fax" / "g4fax" / "ia5" / "videotex" / "telephone"
5847 	 */
5848 again:
5849 	if( tmp.bv_len < 3 ) return LDAP_INVALID_SYNTAX;
5850 
5851 	switch( tmp.bv_val[0] ) {
5852 	case 'a':
5853 	case 'A':
5854 		if(( tmp.bv_len >= LENOF("any") ) &&
5855 			( strncasecmp(tmp.bv_val, "any", LENOF("any")) == 0 ))
5856 		{
5857 			tmp.bv_len -= LENOF("any");
5858 			tmp.bv_val += LENOF("any");
5859 			break;
5860 		}
5861 		return LDAP_INVALID_SYNTAX;
5862 
5863 	case 'm':
5864 	case 'M':
5865 		if(( tmp.bv_len >= LENOF("mhs") ) &&
5866 			( strncasecmp(tmp.bv_val, "mhs", LENOF("mhs")) == 0 ))
5867 		{
5868 			tmp.bv_len -= LENOF("mhs");
5869 			tmp.bv_val += LENOF("mhs");
5870 			break;
5871 		}
5872 		return LDAP_INVALID_SYNTAX;
5873 
5874 	case 'p':
5875 	case 'P':
5876 		if(( tmp.bv_len >= LENOF("physical") ) &&
5877 			( strncasecmp(tmp.bv_val, "physical", LENOF("physical")) == 0 ))
5878 		{
5879 			tmp.bv_len -= LENOF("physical");
5880 			tmp.bv_val += LENOF("physical");
5881 			break;
5882 		}
5883 		return LDAP_INVALID_SYNTAX;
5884 
5885 	case 't':
5886 	case 'T': /* telex or teletex or telephone */
5887 		if(( tmp.bv_len >= LENOF("telex") ) &&
5888 			( strncasecmp(tmp.bv_val, "telex", LENOF("telex")) == 0 ))
5889 		{
5890 			tmp.bv_len -= LENOF("telex");
5891 			tmp.bv_val += LENOF("telex");
5892 			break;
5893 		}
5894 		if(( tmp.bv_len >= LENOF("teletex") ) &&
5895 			( strncasecmp(tmp.bv_val, "teletex", LENOF("teletex")) == 0 ))
5896 		{
5897 			tmp.bv_len -= LENOF("teletex");
5898 			tmp.bv_val += LENOF("teletex");
5899 			break;
5900 		}
5901 		if(( tmp.bv_len >= LENOF("telephone") ) &&
5902 			( strncasecmp(tmp.bv_val, "telephone", LENOF("telephone")) == 0 ))
5903 		{
5904 			tmp.bv_len -= LENOF("telephone");
5905 			tmp.bv_val += LENOF("telephone");
5906 			break;
5907 		}
5908 		return LDAP_INVALID_SYNTAX;
5909 
5910 	case 'g':
5911 	case 'G': /* g3fax or g4fax */
5912 		if(( tmp.bv_len >= LENOF("g3fax") ) && (
5913 			( strncasecmp(tmp.bv_val, "g3fax", LENOF("g3fax")) == 0 ) ||
5914 			( strncasecmp(tmp.bv_val, "g4fax", LENOF("g4fax")) == 0 )))
5915 		{
5916 			tmp.bv_len -= LENOF("g3fax");
5917 			tmp.bv_val += LENOF("g3fax");
5918 			break;
5919 		}
5920 		return LDAP_INVALID_SYNTAX;
5921 
5922 	case 'i':
5923 	case 'I':
5924 		if(( tmp.bv_len >= LENOF("ia5") ) &&
5925 			( strncasecmp(tmp.bv_val, "ia5", LENOF("ia5")) == 0 ))
5926 		{
5927 			tmp.bv_len -= LENOF("ia5");
5928 			tmp.bv_val += LENOF("ia5");
5929 			break;
5930 		}
5931 		return LDAP_INVALID_SYNTAX;
5932 
5933 	case 'v':
5934 	case 'V':
5935 		if(( tmp.bv_len >= LENOF("videotex") ) &&
5936 			( strncasecmp(tmp.bv_val, "videotex", LENOF("videotex")) == 0 ))
5937 		{
5938 			tmp.bv_len -= LENOF("videotex");
5939 			tmp.bv_val += LENOF("videotex");
5940 			break;
5941 		}
5942 		return LDAP_INVALID_SYNTAX;
5943 
5944 	default:
5945 		return LDAP_INVALID_SYNTAX;
5946 	}
5947 
5948 	if( BER_BVISEMPTY( &tmp ) ) return LDAP_SUCCESS;
5949 
5950 	while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
5951 		tmp.bv_len++;
5952 		tmp.bv_val--;
5953 	}
5954 	if( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == '$' ) ) {
5955 		tmp.bv_len++;
5956 		tmp.bv_val--;
5957 	} else {
5958 		return LDAP_INVALID_SYNTAX;
5959 	}
5960 	while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
5961 		tmp.bv_len++;
5962 		tmp.bv_val--;
5963 	}
5964 
5965 	goto again;
5966 }
5967 
5968 static int
5969 nisNetgroupTripleValidate(
5970 	Syntax *syntax,
5971 	struct berval *val )
5972 {
5973 	char *p, *e;
5974 	int commas = 0;
5975 
5976 	if ( BER_BVISEMPTY( val ) ) {
5977 		return LDAP_INVALID_SYNTAX;
5978 	}
5979 
5980 	p = (char *)val->bv_val;
5981 	e = p + val->bv_len;
5982 
5983 	if ( *p != '(' /*')'*/ ) {
5984 		return LDAP_INVALID_SYNTAX;
5985 	}
5986 
5987 	for ( p++; ( p < e ) && ( *p != /*'('*/ ')' ); p++ ) {
5988 		if ( *p == ',' ) {
5989 			commas++;
5990 			if ( commas > 2 ) {
5991 				return LDAP_INVALID_SYNTAX;
5992 			}
5993 
5994 		} else if ( !AD_CHAR( *p ) ) {
5995 			return LDAP_INVALID_SYNTAX;
5996 		}
5997 	}
5998 
5999 	if ( ( commas != 2 ) || ( *p != /*'('*/ ')' ) ) {
6000 		return LDAP_INVALID_SYNTAX;
6001 	}
6002 
6003 	p++;
6004 
6005 	if (p != e) {
6006 		return LDAP_INVALID_SYNTAX;
6007 	}
6008 
6009 	return LDAP_SUCCESS;
6010 }
6011 
6012 static int
6013 bootParameterValidate(
6014 	Syntax *syntax,
6015 	struct berval *val )
6016 {
6017 	char *p, *e;
6018 
6019 	if ( BER_BVISEMPTY( val ) ) {
6020 		return LDAP_INVALID_SYNTAX;
6021 	}
6022 
6023 	p = (char *)val->bv_val;
6024 	e = p + val->bv_len;
6025 
6026 	/* key */
6027 	for (; ( p < e ) && ( *p != '=' ); p++ ) {
6028 		if ( !AD_CHAR( *p ) ) {
6029 			return LDAP_INVALID_SYNTAX;
6030 		}
6031 	}
6032 
6033 	if ( *p != '=' ) {
6034 		return LDAP_INVALID_SYNTAX;
6035 	}
6036 
6037 	/* server */
6038 	for ( p++; ( p < e ) && ( *p != ':' ); p++ ) {
6039 		if ( !AD_CHAR( *p ) ) {
6040 			return LDAP_INVALID_SYNTAX;
6041 		}
6042 	}
6043 
6044 	if ( *p != ':' ) {
6045 		return LDAP_INVALID_SYNTAX;
6046 	}
6047 
6048 	/* path */
6049 	for ( p++; p < e; p++ ) {
6050 		if ( !SLAP_PRINTABLE( *p ) ) {
6051 			return LDAP_INVALID_SYNTAX;
6052 		}
6053 	}
6054 
6055 	return LDAP_SUCCESS;
6056 }
6057 
6058 static int
6059 firstComponentNormalize(
6060 	slap_mask_t usage,
6061 	Syntax *syntax,
6062 	MatchingRule *mr,
6063 	struct berval *val,
6064 	struct berval *normalized,
6065 	void *ctx )
6066 {
6067 	int rc;
6068 	struct berval comp;
6069 	ber_len_t len;
6070 
6071 	if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( usage )) {
6072 		ber_dupbv_x( normalized, val, ctx );
6073 		return LDAP_SUCCESS;
6074 	}
6075 
6076 	if( val->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
6077 
6078 	if( ! ( val->bv_val[0] == '(' /*')'*/
6079 			&& val->bv_val[val->bv_len - 1] == /*'('*/ ')' )
6080 		&& ! ( val->bv_val[0] == '{' /*'}'*/
6081 			&& val->bv_val[val->bv_len - 1] == /*'('*/ '}' ) )
6082 	{
6083 		return LDAP_INVALID_SYNTAX;
6084 	}
6085 
6086 	/* trim leading white space */
6087 	for( len=1;
6088 		len < val->bv_len && ASCII_SPACE(val->bv_val[len]);
6089 		len++ )
6090 	{
6091 		/* empty */
6092 	}
6093 
6094 	/* grab next word */
6095 	comp.bv_val = &val->bv_val[len];
6096 	len = val->bv_len - len - STRLENOF(/*"{"*/ "}");
6097 	for( comp.bv_len = 0;
6098 		!ASCII_SPACE(comp.bv_val[comp.bv_len]) && comp.bv_len < len;
6099 		comp.bv_len++ )
6100 	{
6101 		/* empty */
6102 	}
6103 
6104 	if( mr == slap_schema.si_mr_objectIdentifierFirstComponentMatch ) {
6105 		rc = numericoidValidate( NULL, &comp );
6106 	} else if( mr == slap_schema.si_mr_integerFirstComponentMatch ) {
6107 		rc = integerValidate( NULL, &comp );
6108 	} else {
6109 		rc = LDAP_INVALID_SYNTAX;
6110 	}
6111 
6112 
6113 	if( rc == LDAP_SUCCESS ) {
6114 		ber_dupbv_x( normalized, &comp, ctx );
6115 	}
6116 
6117 	return rc;
6118 }
6119 
6120 static char *country_gen_syn[] = {
6121 	"1.3.6.1.4.1.1466.115.121.1.15",	/* Directory String */
6122 	"1.3.6.1.4.1.1466.115.121.1.26",	/* IA5 String */
6123 	"1.3.6.1.4.1.1466.115.121.1.44",	/* Printable String */
6124 	NULL
6125 };
6126 
6127 #define X_BINARY "X-BINARY-TRANSFER-REQUIRED 'TRUE' "
6128 #define X_NOT_H_R "X-NOT-HUMAN-READABLE 'TRUE' "
6129 
6130 static slap_syntax_defs_rec syntax_defs[] = {
6131 	{"( 1.3.6.1.4.1.1466.115.121.1.1 DESC 'ACI Item' "
6132 		X_BINARY X_NOT_H_R ")",
6133 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL, NULL},
6134 	{"( 1.3.6.1.4.1.1466.115.121.1.2 DESC 'Access Point' " X_NOT_H_R ")",
6135 		0, NULL, NULL, NULL},
6136 	{"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )",
6137 		0, NULL, NULL, NULL},
6138 	{"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' "
6139 		X_NOT_H_R ")",
6140 		SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
6141 	{"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' "
6142 		X_NOT_H_R ")",
6143 		SLAP_SYNTAX_BER, NULL, berValidate, NULL},
6144 	{"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )",
6145 		0, NULL, bitStringValidate, NULL },
6146 	{"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )",
6147 		0, NULL, booleanValidate, NULL},
6148 	{"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' "
6149 		X_BINARY X_NOT_H_R ")",
6150 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6151 		NULL, certificateValidate, NULL},
6152 	{"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' "
6153 		X_BINARY X_NOT_H_R ")",
6154 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6155 		NULL, certificateListValidate, NULL},
6156 	{"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' "
6157 		X_BINARY X_NOT_H_R ")",
6158 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6159 		NULL, sequenceValidate, NULL},
6160 	{"( " attributeCertificateSyntaxOID " DESC 'X.509 AttributeCertificate' "
6161 		X_BINARY X_NOT_H_R ")",
6162 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6163 		NULL, attributeCertificateValidate, NULL},
6164 #if 0	/* need to go __after__ printableString */
6165 	{"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
6166 		0, "1.3.6.1.4.1.1466.115.121.1.44",
6167 		countryStringValidate, NULL},
6168 #endif
6169 	{"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )",
6170 		SLAP_SYNTAX_DN, NULL, dnValidate, dnPretty},
6171 	{"( 1.2.36.79672281.1.5.0 DESC 'RDN' )",
6172 		0, NULL, rdnValidate, rdnPretty},
6173 #ifdef LDAP_COMP_MATCH
6174 	{"( 1.2.36.79672281.1.5.3 DESC 'allComponents' )",
6175 		0, NULL, allComponentsValidate, NULL},
6176  	{"( 1.2.36.79672281.1.5.2 DESC 'componentFilterMatch assertion') ",
6177 		0, NULL, componentFilterValidate, NULL},
6178 #endif
6179 	{"( 1.3.6.1.4.1.1466.115.121.1.13 DESC 'Data Quality' )",
6180 		0, NULL, NULL, NULL},
6181 	{"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )",
6182 		0, NULL, deliveryMethodValidate, NULL},
6183 	{"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )",
6184 		0, NULL, UTF8StringValidate, NULL},
6185 	{"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )",
6186 		0, NULL, NULL, NULL},
6187 	{"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )",
6188 		0, NULL, NULL, NULL},
6189 	{"( 1.3.6.1.4.1.1466.115.121.1.19 DESC 'DSA Quality' )",
6190 		0, NULL, NULL, NULL},
6191 	{"( 1.3.6.1.4.1.1466.115.121.1.20 DESC 'DSE Type' )",
6192 		0, NULL, NULL, NULL},
6193 	{"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )",
6194 		0, NULL, NULL, NULL},
6195 	{"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )",
6196 		0, NULL, printablesStringValidate, NULL},
6197 	{"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' " X_NOT_H_R ")",
6198 		SLAP_SYNTAX_BLOB, NULL, NULL, NULL},
6199 	{"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )",
6200 		0, NULL, generalizedTimeValidate, NULL},
6201 	{"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )",
6202 		0, NULL, NULL, NULL},
6203 	{"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )",
6204 		0, NULL, IA5StringValidate, NULL},
6205 	{"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )",
6206 		0, NULL, integerValidate, NULL},
6207 	{"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' " X_NOT_H_R ")",
6208 		SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
6209 	{"( 1.3.6.1.4.1.1466.115.121.1.29 DESC 'Master And Shadow Access Points' )",
6210 		0, NULL, NULL, NULL},
6211 	{"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )",
6212 		0, NULL, NULL, NULL},
6213 	{"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )",
6214 		0, NULL, NULL, NULL},
6215 	{"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )",
6216 		0, NULL, NULL, NULL},
6217 	{"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )",
6218 		0, NULL, NULL, NULL},
6219 	{"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )",
6220 		SLAP_SYNTAX_DN, NULL, nameUIDValidate, nameUIDPretty },
6221 	{"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )",
6222 		0, NULL, NULL, NULL},
6223 	{"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )",
6224 		0, NULL, numericStringValidate, NULL},
6225 	{"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )",
6226 		0, NULL, NULL, NULL},
6227 	{"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )",
6228 		0, NULL, numericoidValidate, NULL},
6229 	{"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )",
6230 		0, NULL, IA5StringValidate, NULL},
6231 	{"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )",
6232 		0, NULL, blobValidate, NULL},
6233 	{"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )",
6234 		0, NULL, postalAddressValidate, NULL},
6235 	{"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )",
6236 		0, NULL, NULL, NULL},
6237 	{"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )",
6238 		0, NULL, NULL, NULL},
6239 	{"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )",
6240 		0, NULL, printableStringValidate, NULL},
6241 	/* moved here because now depends on Directory String, IA5 String
6242 	 * and Printable String */
6243 	{"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
6244 		0, country_gen_syn, countryStringValidate, NULL},
6245 	{"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )",
6246 #define subtreeSpecificationValidate UTF8StringValidate /* FIXME */
6247 		0, NULL, subtreeSpecificationValidate, NULL},
6248 	{"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' "
6249 		X_BINARY X_NOT_H_R ")",
6250 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, berValidate, NULL},
6251 	{"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )",
6252 		0, NULL, printableStringValidate, NULL},
6253 	{"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )",
6254 		0, NULL, NULL, NULL},
6255 	{"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )",
6256 		0, NULL, printablesStringValidate, NULL},
6257 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
6258 	{"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )",
6259 		0, NULL, utcTimeValidate, NULL},
6260 #endif
6261 	{"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )",
6262 		0, NULL, NULL, NULL},
6263 	{"( 1.3.6.1.4.1.1466.115.121.1.55 DESC 'Modify Rights' )",
6264 		0, NULL, NULL, NULL},
6265 	{"( 1.3.6.1.4.1.1466.115.121.1.56 DESC 'LDAP Schema Definition' )",
6266 		0, NULL, NULL, NULL},
6267 	{"( 1.3.6.1.4.1.1466.115.121.1.57 DESC 'LDAP Schema Description' )",
6268 		0, NULL, NULL, NULL},
6269 	{"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )",
6270 		0, NULL, NULL, NULL},
6271 
6272 	/* RFC 2307 NIS Syntaxes */
6273 	{"( 1.3.6.1.1.1.0.0  DESC 'RFC2307 NIS Netgroup Triple' )",
6274 		0, NULL, nisNetgroupTripleValidate, NULL},
6275 	{"( 1.3.6.1.1.1.0.1  DESC 'RFC2307 Boot Parameter' )",
6276 		0, NULL, bootParameterValidate, NULL},
6277 
6278 	/* draft-zeilenga-ldap-x509 */
6279 	{"( 1.3.6.1.1.15.1 DESC 'Certificate Exact Assertion' )",
6280 		SLAP_SYNTAX_HIDE, NULL,
6281 		serialNumberAndIssuerValidate,
6282 		serialNumberAndIssuerPretty},
6283 	{"( 1.3.6.1.1.15.2 DESC 'Certificate Assertion' )",
6284 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6285 	{"( 1.3.6.1.1.15.3 DESC 'Certificate Pair Exact Assertion' )",
6286 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6287 	{"( 1.3.6.1.1.15.4 DESC 'Certificate Pair Assertion' )",
6288 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6289 	{"( 1.3.6.1.1.15.5 DESC 'Certificate List Exact Assertion' )",
6290 		SLAP_SYNTAX_HIDE, NULL,
6291 		issuerAndThisUpdateValidate,
6292 		issuerAndThisUpdatePretty},
6293 	{"( 1.3.6.1.1.15.6 DESC 'Certificate List Assertion' )",
6294 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6295 	{"( 1.3.6.1.1.15.7 DESC 'Algorithm Identifier' )",
6296 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6297 	{"( " attributeCertificateExactAssertionSyntaxOID " DESC 'AttributeCertificate Exact Assertion' )",
6298 		SLAP_SYNTAX_HIDE, NULL,
6299 		serialNumberAndIssuerSerialValidate,
6300 		serialNumberAndIssuerSerialPretty},
6301 	{"( " attributeCertificateAssertionSyntaxOID " DESC 'AttributeCertificate Assertion' )",
6302 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6303 
6304 #ifdef SLAPD_AUTHPASSWD
6305 	/* needs updating */
6306 	{"( 1.3.6.1.4.1.4203.666.2.2 DESC 'OpenLDAP authPassword' )",
6307 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6308 #endif
6309 
6310 	{"( 1.3.6.1.1.16.1 DESC 'UUID' )",
6311 		0, NULL, UUIDValidate, UUIDPretty},
6312 
6313 	{"( 1.3.6.1.4.1.4203.666.11.2.1 DESC 'CSN' )",
6314 		SLAP_SYNTAX_HIDE, NULL, csnValidate, csnPretty },
6315 
6316 	{"( 1.3.6.1.4.1.4203.666.11.2.4 DESC 'CSN SID' )",
6317 		SLAP_SYNTAX_HIDE, NULL, sidValidate, sidPretty },
6318 
6319 	/* OpenLDAP Void Syntax */
6320 	{"( 1.3.6.1.4.1.4203.1.1.1 DESC 'OpenLDAP void' )" ,
6321 		SLAP_SYNTAX_HIDE, NULL, inValidate, NULL},
6322 
6323 	/* FIXME: OID is unused, but not registered yet */
6324 	{"( 1.3.6.1.4.1.4203.666.2.7 DESC 'OpenLDAP authz' )",
6325 		SLAP_SYNTAX_HIDE, NULL, authzValidate, authzPretty},
6326 
6327 	{NULL, 0, NULL, NULL, NULL}
6328 };
6329 
6330 char *csnSIDMatchSyntaxes[] = {
6331 	"1.3.6.1.4.1.4203.666.11.2.1" /* csn */,
6332 	NULL
6333 };
6334 char *certificateExactMatchSyntaxes[] = {
6335 	"1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
6336 	NULL
6337 };
6338 char *certificateListExactMatchSyntaxes[] = {
6339 	"1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */,
6340 	NULL
6341 };
6342 char *attributeCertificateExactMatchSyntaxes[] = {
6343 	attributeCertificateSyntaxOID  /* attributeCertificate */,
6344 	NULL
6345 };
6346 
6347 #ifdef LDAP_COMP_MATCH
6348 char *componentFilterMatchSyntaxes[] = {
6349 	"1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
6350 	"1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */,
6351 	attributeCertificateSyntaxOID /* attributeCertificate */,
6352 	NULL
6353 };
6354 #endif
6355 
6356 char *directoryStringSyntaxes[] = {
6357 	"1.3.6.1.4.1.1466.115.121.1.11" /* countryString */,
6358 	"1.3.6.1.4.1.1466.115.121.1.44" /* printableString */,
6359 	"1.3.6.1.4.1.1466.115.121.1.50" /* telephoneNumber */,
6360 	NULL
6361 };
6362 char *integerFirstComponentMatchSyntaxes[] = {
6363 	"1.3.6.1.4.1.1466.115.121.1.27" /* INTEGER */,
6364 	"1.3.6.1.4.1.1466.115.121.1.17" /* dITStructureRuleDescription */,
6365 	NULL
6366 };
6367 char *objectIdentifierFirstComponentMatchSyntaxes[] = {
6368 	"1.3.6.1.4.1.1466.115.121.1.38" /* OID */,
6369 	"1.3.6.1.4.1.1466.115.121.1.3"  /* attributeTypeDescription */,
6370 	"1.3.6.1.4.1.1466.115.121.1.16" /* dITContentRuleDescription */,
6371 	"1.3.6.1.4.1.1466.115.121.1.54" /* ldapSyntaxDescription */,
6372 	"1.3.6.1.4.1.1466.115.121.1.30" /* matchingRuleDescription */,
6373 	"1.3.6.1.4.1.1466.115.121.1.31" /* matchingRuleUseDescription */,
6374 	"1.3.6.1.4.1.1466.115.121.1.35" /* nameFormDescription */,
6375 	"1.3.6.1.4.1.1466.115.121.1.37" /* objectClassDescription */,
6376 	NULL
6377 };
6378 
6379 /*
6380  * Other matching rules in X.520 that we do not use (yet):
6381  *
6382  * 2.5.13.25	uTCTimeMatch
6383  * 2.5.13.26	uTCTimeOrderingMatch
6384  * 2.5.13.31*	directoryStringFirstComponentMatch
6385  * 2.5.13.32*	wordMatch
6386  * 2.5.13.33*	keywordMatch
6387  * 2.5.13.36+	certificatePairExactMatch
6388  * 2.5.13.37+	certificatePairMatch
6389  * 2.5.13.40+	algorithmIdentifierMatch
6390  * 2.5.13.41*	storedPrefixMatch
6391  * 2.5.13.42	attributeCertificateMatch
6392  * 2.5.13.43	readerAndKeyIDMatch
6393  * 2.5.13.44	attributeIntegrityMatch
6394  *
6395  * (*) described in RFC 3698 (LDAP: Additional Matching Rules)
6396  * (+) described in draft-zeilenga-ldap-x509
6397  */
6398 static slap_mrule_defs_rec mrule_defs[] = {
6399 	/*
6400 	 * EQUALITY matching rules must be listed after associated APPROX
6401 	 * matching rules.  So, we list all APPROX matching rules first.
6402 	 */
6403 	{"( " directoryStringApproxMatchOID " NAME 'directoryStringApproxMatch' "
6404 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6405 		SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
6406 		NULL, NULL, directoryStringApproxMatch,
6407 		directoryStringApproxIndexer, directoryStringApproxFilter,
6408 		NULL},
6409 
6410 	{"( " IA5StringApproxMatchOID " NAME 'IA5StringApproxMatch' "
6411 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6412 		SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
6413 		NULL, NULL, IA5StringApproxMatch,
6414 		IA5StringApproxIndexer, IA5StringApproxFilter,
6415 		NULL},
6416 
6417 	/*
6418 	 * Other matching rules
6419 	 */
6420 
6421 	{"( 2.5.13.0 NAME 'objectIdentifierMatch' "
6422 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
6423 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6424 		NULL, NULL, octetStringMatch,
6425 		octetStringIndexer, octetStringFilter,
6426 		NULL },
6427 
6428 	{"( 2.5.13.1 NAME 'distinguishedNameMatch' "
6429 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6430 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6431 		NULL, dnNormalize, dnMatch,
6432 		octetStringIndexer, octetStringFilter,
6433 		NULL },
6434 
6435 	{"( 1.3.6.1.4.1.4203.666.4.9 NAME 'dnSubtreeMatch' "
6436 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6437 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6438 		NULL, dnNormalize, dnRelativeMatch,
6439 		NULL, NULL,
6440 		NULL },
6441 
6442 	{"( 1.3.6.1.4.1.4203.666.4.8 NAME 'dnOneLevelMatch' "
6443 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6444 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6445 		NULL, dnNormalize, dnRelativeMatch,
6446 		NULL, NULL,
6447 		NULL },
6448 
6449 	{"( 1.3.6.1.4.1.4203.666.4.10 NAME 'dnSubordinateMatch' "
6450 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6451 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6452 		NULL, dnNormalize, dnRelativeMatch,
6453 		NULL, NULL,
6454 		NULL },
6455 
6456 	{"( 1.3.6.1.4.1.4203.666.4.11 NAME 'dnSuperiorMatch' "
6457 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6458 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6459 		NULL, dnNormalize, dnRelativeMatch,
6460 		NULL, NULL,
6461 		NULL },
6462 
6463 	{"( 1.2.36.79672281.1.13.3 NAME 'rdnMatch' "
6464 		"SYNTAX 1.2.36.79672281.1.5.0 )",
6465 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6466 		NULL, rdnNormalize, rdnMatch,
6467 		octetStringIndexer, octetStringFilter,
6468 		NULL },
6469 
6470 #ifdef LDAP_COMP_MATCH
6471 	{"( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch' "
6472 		"SYNTAX 1.2.36.79672281.1.5.2 )", /* componentFilterMatch assertion */
6473 		SLAP_MR_EXT|SLAP_MR_COMPONENT, componentFilterMatchSyntaxes,
6474 		NULL, NULL , componentFilterMatch,
6475 		octetStringIndexer, octetStringFilter,
6476 		NULL },
6477 
6478         {"( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch' "
6479                 "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
6480                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
6481                 NULL, NULL , allComponentsMatch,
6482                 octetStringIndexer, octetStringFilter,
6483                 NULL },
6484 
6485         {"( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch' "
6486                 "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
6487                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
6488                 NULL, NULL , directoryComponentsMatch,
6489                 octetStringIndexer, octetStringFilter,
6490                 NULL },
6491 #endif
6492 
6493 	{"( 2.5.13.2 NAME 'caseIgnoreMatch' "
6494 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6495 		SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
6496 		NULL, UTF8StringNormalize, octetStringMatch,
6497 		octetStringIndexer, octetStringFilter,
6498 		directoryStringApproxMatchOID },
6499 
6500 	{"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' "
6501 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6502 		SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
6503 		NULL, UTF8StringNormalize, octetStringOrderingMatch,
6504 		NULL, NULL,
6505 		"caseIgnoreMatch" },
6506 
6507 	{"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' "
6508 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6509 		SLAP_MR_SUBSTR, directoryStringSyntaxes,
6510 		NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
6511 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6512 		"caseIgnoreMatch" },
6513 
6514 	{"( 2.5.13.5 NAME 'caseExactMatch' "
6515 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6516 		SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
6517 		NULL, UTF8StringNormalize, octetStringMatch,
6518 		octetStringIndexer, octetStringFilter,
6519 		directoryStringApproxMatchOID },
6520 
6521 	{"( 2.5.13.6 NAME 'caseExactOrderingMatch' "
6522 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6523 		SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
6524 		NULL, UTF8StringNormalize, octetStringOrderingMatch,
6525 		NULL, NULL,
6526 		"caseExactMatch" },
6527 
6528 	{"( 2.5.13.7 NAME 'caseExactSubstringsMatch' "
6529 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6530 		SLAP_MR_SUBSTR, directoryStringSyntaxes,
6531 		NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
6532 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6533 		"caseExactMatch" },
6534 
6535 	{"( 2.5.13.8 NAME 'numericStringMatch' "
6536 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
6537 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6538 		NULL, numericStringNormalize, octetStringMatch,
6539 		octetStringIndexer, octetStringFilter,
6540 		NULL },
6541 
6542 	{"( 2.5.13.9 NAME 'numericStringOrderingMatch' "
6543 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
6544 		SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
6545 		NULL, numericStringNormalize, octetStringOrderingMatch,
6546 		NULL, NULL,
6547 		"numericStringMatch" },
6548 
6549 	{"( 2.5.13.10 NAME 'numericStringSubstringsMatch' "
6550 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6551 		SLAP_MR_SUBSTR, NULL,
6552 		NULL, numericStringNormalize, octetStringSubstringsMatch,
6553 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6554 		"numericStringMatch" },
6555 
6556 	{"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
6557 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", /* Postal Address */
6558 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6559 		NULL, postalAddressNormalize, octetStringMatch,
6560 		octetStringIndexer, octetStringFilter,
6561 		NULL },
6562 
6563 	{"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' "
6564 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6565 		SLAP_MR_SUBSTR, NULL,
6566 		NULL, NULL, NULL, NULL, NULL,
6567 		"caseIgnoreListMatch" },
6568 
6569 	{"( 2.5.13.13 NAME 'booleanMatch' "
6570 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )",
6571 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6572 		NULL, NULL, booleanMatch,
6573 		octetStringIndexer, octetStringFilter,
6574 		NULL },
6575 
6576 	{"( 2.5.13.14 NAME 'integerMatch' "
6577 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
6578 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6579 		NULL, NULL, integerMatch,
6580 		integerIndexer, integerFilter,
6581 		NULL },
6582 
6583 	{"( 2.5.13.15 NAME 'integerOrderingMatch' "
6584 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
6585 		SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6586 		NULL, NULL, integerMatch,
6587 		NULL, NULL,
6588 		"integerMatch" },
6589 
6590 	{"( 2.5.13.16 NAME 'bitStringMatch' "
6591 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )",
6592 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6593 		NULL, NULL, octetStringMatch,
6594 		octetStringIndexer, octetStringFilter,
6595 		NULL },
6596 
6597 	{"( 2.5.13.17 NAME 'octetStringMatch' "
6598 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
6599 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6600 		NULL, NULL, octetStringMatch,
6601 		octetStringIndexer, octetStringFilter,
6602 		NULL },
6603 
6604 	{"( 2.5.13.18 NAME 'octetStringOrderingMatch' "
6605 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
6606 		SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
6607 		NULL, NULL, octetStringOrderingMatch,
6608 		NULL, NULL,
6609 		"octetStringMatch" },
6610 
6611 	{"( 2.5.13.19 NAME 'octetStringSubstringsMatch' "
6612 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
6613 		SLAP_MR_SUBSTR, NULL,
6614 		NULL, NULL, octetStringSubstringsMatch,
6615 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6616 		"octetStringMatch" },
6617 
6618 	{"( 2.5.13.20 NAME 'telephoneNumberMatch' "
6619 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )",
6620 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6621 		NULL,
6622 		telephoneNumberNormalize, octetStringMatch,
6623 		octetStringIndexer, octetStringFilter,
6624 		NULL },
6625 
6626 	{"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' "
6627 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6628 		SLAP_MR_SUBSTR, NULL,
6629 		NULL, telephoneNumberNormalize, octetStringSubstringsMatch,
6630 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6631 		"telephoneNumberMatch" },
6632 
6633 	{"( 2.5.13.22 NAME 'presentationAddressMatch' "
6634 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )",
6635 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6636 		NULL, NULL, NULL, NULL, NULL, NULL },
6637 
6638 	{"( 2.5.13.23 NAME 'uniqueMemberMatch' "
6639 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )", /* Name And Optional UID */
6640 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6641 		NULL, uniqueMemberNormalize, uniqueMemberMatch,
6642 		uniqueMemberIndexer, uniqueMemberFilter,
6643 		NULL },
6644 
6645 	{"( 2.5.13.24 NAME 'protocolInformationMatch' "
6646 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )",
6647 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6648 		NULL, NULL, NULL, NULL, NULL, NULL },
6649 
6650 	{"( 2.5.13.27 NAME 'generalizedTimeMatch' "
6651 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
6652 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6653 		NULL, generalizedTimeNormalize, octetStringMatch,
6654 		generalizedTimeIndexer, generalizedTimeFilter,
6655 		NULL },
6656 
6657 	{"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' "
6658 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
6659 		SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6660 		NULL, generalizedTimeNormalize, generalizedTimeOrderingMatch,
6661 		NULL, NULL,
6662 		"generalizedTimeMatch" },
6663 
6664 	{"( 2.5.13.29 NAME 'integerFirstComponentMatch' "
6665 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
6666 		SLAP_MR_EQUALITY | SLAP_MR_EXT,
6667 			integerFirstComponentMatchSyntaxes,
6668 		NULL, firstComponentNormalize, integerMatch,
6669 		octetStringIndexer, octetStringFilter,
6670 		NULL },
6671 
6672 	{"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' "
6673 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", /* OID */
6674 		SLAP_MR_EQUALITY | SLAP_MR_EXT,
6675 			objectIdentifierFirstComponentMatchSyntaxes,
6676 		NULL, firstComponentNormalize, octetStringMatch,
6677 		octetStringIndexer, octetStringFilter,
6678 		NULL },
6679 
6680 	{"( 2.5.13.34 NAME 'certificateExactMatch' "
6681 		"SYNTAX 1.3.6.1.1.15.1 )", /* Certificate Exact Assertion */
6682 		SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes,
6683 		NULL, certificateExactNormalize, octetStringMatch,
6684 		octetStringIndexer, octetStringFilter,
6685 		NULL },
6686 
6687 	{"( 2.5.13.35 NAME 'certificateMatch' "
6688 		"SYNTAX 1.3.6.1.1.15.2 )", /* Certificate Assertion */
6689 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6690 		NULL, NULL, NULL, NULL, NULL,
6691 		NULL },
6692 
6693 	{"( 2.5.13.38 NAME 'certificateListExactMatch' "
6694 		"SYNTAX 1.3.6.1.1.15.5 )", /* Certificate List Exact Assertion */
6695 		SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateListExactMatchSyntaxes,
6696 		NULL, certificateListExactNormalize, octetStringMatch,
6697 		octetStringIndexer, octetStringFilter,
6698 		NULL },
6699 
6700 	{"( 2.5.13.39 NAME 'certificateListMatch' "
6701 		"SYNTAX 1.3.6.1.1.15.6 )", /* Certificate List Assertion */
6702 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6703 		NULL, NULL, NULL, NULL, NULL,
6704 		NULL },
6705 
6706 	{"( 2.5.13.45 NAME 'attributeCertificateExactMatch' "
6707 		"SYNTAX " attributeCertificateExactAssertionSyntaxOID " )",
6708 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, attributeCertificateExactMatchSyntaxes,
6709 		NULL, attributeCertificateExactNormalize, octetStringMatch,
6710 		octetStringIndexer, octetStringFilter,
6711 		NULL },
6712 
6713 	{"( 2.5.13.46 NAME 'attributeCertificateMatch' "
6714 		"SYNTAX " attributeCertificateAssertionSyntaxOID " )",
6715 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, NULL,
6716 		NULL, NULL, NULL, NULL, NULL,
6717 		NULL },
6718 
6719 	{"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' "
6720 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6721 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6722 		NULL, IA5StringNormalize, octetStringMatch,
6723 		octetStringIndexer, octetStringFilter,
6724 		IA5StringApproxMatchOID },
6725 
6726 	{"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' "
6727 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6728 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6729 		NULL, IA5StringNormalize, octetStringMatch,
6730 		octetStringIndexer, octetStringFilter,
6731 		IA5StringApproxMatchOID },
6732 
6733 	{"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' "
6734 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6735 		SLAP_MR_SUBSTR, NULL,
6736 		NULL, IA5StringNormalize, directoryStringSubstringsMatch,
6737 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6738 		"caseIgnoreIA5Match" },
6739 
6740 	{"( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch' "
6741 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6742 		SLAP_MR_SUBSTR, NULL,
6743 		NULL, IA5StringNormalize, directoryStringSubstringsMatch,
6744 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6745 		"caseExactIA5Match" },
6746 
6747 #ifdef SLAPD_AUTHPASSWD
6748 	/* needs updating */
6749 	{"( 1.3.6.1.4.1.4203.666.4.1 NAME 'authPasswordMatch' "
6750 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", /* Octet String */
6751 		SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
6752 		NULL, NULL, authPasswordMatch,
6753 		NULL, NULL,
6754 		NULL},
6755 #endif
6756 
6757 	{"( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch' "
6758 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
6759 		SLAP_MR_EXT, NULL,
6760 		NULL, NULL, integerBitAndMatch,
6761 		NULL, NULL,
6762 		"integerMatch" },
6763 
6764 	{"( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch' "
6765 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
6766 		SLAP_MR_EXT, NULL,
6767 		NULL, NULL, integerBitOrMatch,
6768 		NULL, NULL,
6769 		"integerMatch" },
6770 
6771 	{"( 1.3.6.1.1.16.2 NAME 'UUIDMatch' "
6772 		"SYNTAX 1.3.6.1.1.16.1 )",
6773 		SLAP_MR_EQUALITY | SLAP_MR_MUTATION_NORMALIZER, NULL,
6774 		NULL, UUIDNormalize, octetStringMatch,
6775 		octetStringIndexer, octetStringFilter,
6776 		NULL},
6777 
6778 	{"( 1.3.6.1.1.16.3 NAME 'UUIDOrderingMatch' "
6779 		"SYNTAX 1.3.6.1.1.16.1 )",
6780 		SLAP_MR_ORDERING | SLAP_MR_MUTATION_NORMALIZER, NULL,
6781 		NULL, UUIDNormalize, octetStringOrderingMatch,
6782 		octetStringIndexer, octetStringFilter,
6783 		"UUIDMatch"},
6784 
6785 	{"( 1.3.6.1.4.1.4203.666.11.2.2 NAME 'CSNMatch' "
6786 		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
6787 		SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_ORDERED_INDEX, NULL,
6788 		NULL, csnNormalize, csnMatch,
6789 		csnIndexer, csnFilter,
6790 		NULL},
6791 
6792 	{"( 1.3.6.1.4.1.4203.666.11.2.3 NAME 'CSNOrderingMatch' "
6793 		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
6794 		SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6795 		NULL, csnNormalize, csnOrderingMatch,
6796 		NULL, NULL,
6797 		"CSNMatch" },
6798 
6799 	{"( 1.3.6.1.4.1.4203.666.11.2.5 NAME 'CSNSIDMatch' "
6800 		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.4 )",
6801 		SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_EXT, csnSIDMatchSyntaxes,
6802 		NULL, csnSidNormalize, octetStringMatch,
6803 		octetStringIndexer, octetStringFilter,
6804 		NULL },
6805 
6806 	/* FIXME: OID is unused, but not registered yet */
6807 	{"( 1.3.6.1.4.1.4203.666.4.12 NAME 'authzMatch' "
6808 		"SYNTAX 1.3.6.1.4.1.4203.666.2.7 )", /* OpenLDAP authz */
6809 		SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
6810 		NULL, authzNormalize, authzMatch,
6811 		NULL, NULL,
6812 		NULL},
6813 
6814 	{NULL, SLAP_MR_NONE, NULL,
6815 		NULL, NULL, NULL, NULL, NULL,
6816 		NULL }
6817 };
6818 
6819 int
6820 slap_schema_init( void )
6821 {
6822 	int		res;
6823 	int		i;
6824 
6825 	/* we should only be called once (from main) */
6826 	assert( schema_init_done == 0 );
6827 
6828 	for ( i=0; syntax_defs[i].sd_desc != NULL; i++ ) {
6829 		res = register_syntax( &syntax_defs[i] );
6830 
6831 		if ( res ) {
6832 			fprintf( stderr, "slap_schema_init: Error registering syntax %s\n",
6833 				 syntax_defs[i].sd_desc );
6834 			return LDAP_OTHER;
6835 		}
6836 	}
6837 
6838 	for ( i=0; mrule_defs[i].mrd_desc != NULL; i++ ) {
6839 		if( mrule_defs[i].mrd_usage == SLAP_MR_NONE &&
6840 			mrule_defs[i].mrd_compat_syntaxes == NULL )
6841 		{
6842 			fprintf( stderr,
6843 				"slap_schema_init: Ignoring unusable matching rule %s\n",
6844 				 mrule_defs[i].mrd_desc );
6845 			continue;
6846 		}
6847 
6848 		res = register_matching_rule( &mrule_defs[i] );
6849 
6850 		if ( res ) {
6851 			fprintf( stderr,
6852 				"slap_schema_init: Error registering matching rule %s\n",
6853 				 mrule_defs[i].mrd_desc );
6854 			return LDAP_OTHER;
6855 		}
6856 	}
6857 
6858 	res = slap_schema_load();
6859 	schema_init_done = 1;
6860 	return res;
6861 }
6862 
6863 void
6864 schema_destroy( void )
6865 {
6866 	oidm_destroy();
6867 	oc_destroy();
6868 	at_destroy();
6869 	mr_destroy();
6870 	mru_destroy();
6871 	syn_destroy();
6872 
6873 	if( schema_init_done ) {
6874 		ldap_pvt_thread_mutex_destroy( &ad_index_mutex );
6875 		ldap_pvt_thread_mutex_destroy( &ad_undef_mutex );
6876 		ldap_pvt_thread_mutex_destroy( &oc_undef_mutex );
6877 	}
6878 }
6879