xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/schema_init.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: schema_init.c,v 1.2 2020/08/11 13:15:39 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-2020 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.2 2020/08/11 13:15:39 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, unless the result would be empty */
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 *q;
2244 	ber_len_t c;
2245 
2246 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
2247 
2248 	/* Ensure q is big enough, though validator should have caught this */
2249 	if ( BER_BVISEMPTY( val )) {
2250 		BER_BVZERO( normalized );
2251 		return LDAP_INVALID_SYNTAX;
2252 	}
2253 
2254 	q = normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
2255 
2256 	for( c = 0; c < val->bv_len; c++ ) {
2257 		if ( ! ( ASCII_SPACE( val->bv_val[c] ) || val->bv_val[c] == '-' )) {
2258 			*q++ = val->bv_val[c];
2259 		}
2260 	}
2261 	if ( q == normalized->bv_val ) {
2262 		*q++ = ' ';
2263 	}
2264 	*q = '\0';
2265 
2266 	normalized->bv_len = q - normalized->bv_val;
2267 
2268 	return LDAP_SUCCESS;
2269 }
2270 
2271 static int
2272 postalAddressValidate(
2273 	Syntax *syntax,
2274 	struct berval *in )
2275 {
2276 	struct berval bv = *in;
2277 	ber_len_t c;
2278 
2279 	for ( c = 0; c < in->bv_len; c++ ) {
2280 		if ( in->bv_val[c] == '\\' ) {
2281 			c++;
2282 			if ( strncasecmp( &in->bv_val[c], "24", STRLENOF( "24" ) ) != 0
2283 				&& strncasecmp( &in->bv_val[c], "5C", STRLENOF( "5C" ) ) != 0 )
2284 			{
2285 				return LDAP_INVALID_SYNTAX;
2286 			}
2287 			continue;
2288 		}
2289 
2290 		if ( in->bv_val[c] == '$' ) {
2291 			bv.bv_len = &in->bv_val[c] - bv.bv_val;
2292 			if ( UTF8StringValidate( NULL, &bv ) != LDAP_SUCCESS ) {
2293 				return LDAP_INVALID_SYNTAX;
2294 			}
2295 			bv.bv_val = &in->bv_val[c] + 1;
2296 		}
2297 	}
2298 
2299 	bv.bv_len = &in->bv_val[c] - bv.bv_val;
2300 	return UTF8StringValidate( NULL, &bv );
2301 }
2302 
2303 static int
2304 postalAddressNormalize(
2305 	slap_mask_t usage,
2306 	Syntax *syntax,
2307 	MatchingRule *mr,
2308 	struct berval *val,
2309 	struct berval *normalized,
2310 	void *ctx )
2311 {
2312 	BerVarray lines = NULL, nlines = NULL;
2313 	ber_len_t l, c;
2314 	int rc = LDAP_SUCCESS;
2315 	MatchingRule *xmr = NULL;
2316 	char *p;
2317 
2318 	if ( SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseIgnoreListMatch ) ) {
2319 		xmr = slap_schema.si_mr_caseIgnoreMatch;
2320 
2321 	} else {
2322 		xmr = slap_schema.si_mr_caseExactMatch;
2323 	}
2324 
2325 	for ( l = 0, c = 0; c < val->bv_len; c++ ) {
2326 		if ( val->bv_val[c] == '$' ) {
2327 			l++;
2328 		}
2329 	}
2330 
2331 	lines = slap_sl_calloc( sizeof( struct berval ), 2 * ( l + 2 ), ctx );
2332 	nlines = &lines[l + 2];
2333 
2334 	lines[0].bv_val = val->bv_val;
2335 	for ( l = 0, c = 0; c < val->bv_len; c++ ) {
2336 		if ( val->bv_val[c] == '$' ) {
2337 			lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
2338 			l++;
2339 			lines[l].bv_val = &val->bv_val[c + 1];
2340 		}
2341 	}
2342 	lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
2343 
2344 	normalized->bv_len = c = l;
2345 
2346 	for ( l = 0; l <= c; l++ ) {
2347 		/* NOTE: we directly normalize each line,
2348 		 * without unescaping the values, since the special
2349 		 * values '\24' ('$') and '\5C' ('\') are not affected
2350 		 * by normalization */
2351 		if ( !lines[l].bv_len ) {
2352 			nlines[l].bv_len = 0;
2353 			nlines[l].bv_val = NULL;
2354 			continue;
2355 		}
2356 		rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx );
2357 		if ( rc != LDAP_SUCCESS ) {
2358 			rc = LDAP_INVALID_SYNTAX;
2359 			goto done;
2360 		}
2361 
2362 		normalized->bv_len += nlines[l].bv_len;
2363 	}
2364 
2365 	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2366 
2367 	p = normalized->bv_val;
2368 	for ( l = 0; l <= c ; l++ ) {
2369 		p = lutil_strbvcopy( p, &nlines[l] );
2370 		*p++ = '$';
2371 	}
2372 	*--p = '\0';
2373 
2374 	assert( p == &normalized->bv_val[normalized->bv_len] );
2375 
2376 done:;
2377 	if ( nlines != NULL ) {
2378 		for ( l = 0; !BER_BVISNULL( &nlines[ l ] ); l++ ) {
2379 			slap_sl_free( nlines[l].bv_val, ctx );
2380 		}
2381 
2382 		slap_sl_free( lines, ctx );
2383 	}
2384 
2385 	return rc;
2386 }
2387 
2388 int
2389 numericoidValidate(
2390 	Syntax *syntax,
2391 	struct berval *in )
2392 {
2393 	struct berval val = *in;
2394 
2395 	if( BER_BVISEMPTY( &val ) ) {
2396 		/* disallow empty strings */
2397 		return LDAP_INVALID_SYNTAX;
2398 	}
2399 
2400 	while( OID_LEADCHAR( val.bv_val[0] ) ) {
2401 		if ( val.bv_len == 1 ) {
2402 			return LDAP_SUCCESS;
2403 		}
2404 
2405 		if ( val.bv_val[0] == '0' && !OID_SEPARATOR( val.bv_val[1] )) {
2406 			break;
2407 		}
2408 
2409 		val.bv_val++;
2410 		val.bv_len--;
2411 
2412 		while ( OID_LEADCHAR( val.bv_val[0] )) {
2413 			val.bv_val++;
2414 			val.bv_len--;
2415 
2416 			if ( val.bv_len == 0 ) {
2417 				return LDAP_SUCCESS;
2418 			}
2419 		}
2420 
2421 		if( !OID_SEPARATOR( val.bv_val[0] )) {
2422 			break;
2423 		}
2424 
2425 		val.bv_val++;
2426 		val.bv_len--;
2427 	}
2428 
2429 	return LDAP_INVALID_SYNTAX;
2430 }
2431 
2432 static int
2433 integerValidate(
2434 	Syntax *syntax,
2435 	struct berval *in )
2436 {
2437 	ber_len_t i;
2438 	struct berval val = *in;
2439 
2440 	if ( BER_BVISEMPTY( &val ) ) return LDAP_INVALID_SYNTAX;
2441 
2442 	if ( val.bv_val[0] == '-' ) {
2443 		val.bv_len--;
2444 		val.bv_val++;
2445 
2446 		if( BER_BVISEMPTY( &val ) ) { /* bare "-" */
2447 			return LDAP_INVALID_SYNTAX;
2448 		}
2449 
2450 		if( val.bv_val[0] == '0' ) { /* "-0" */
2451 			return LDAP_INVALID_SYNTAX;
2452 		}
2453 
2454 	} else if ( val.bv_val[0] == '0' ) {
2455 		if( val.bv_len > 1 ) { /* "0<more>" */
2456 			return LDAP_INVALID_SYNTAX;
2457 		}
2458 
2459 		return LDAP_SUCCESS;
2460 	}
2461 
2462 	for( i=0; i < val.bv_len; i++ ) {
2463 		if( !ASCII_DIGIT(val.bv_val[i]) ) {
2464 			return LDAP_INVALID_SYNTAX;
2465 		}
2466 	}
2467 
2468 	return LDAP_SUCCESS;
2469 }
2470 
2471 static int
2472 integerMatch(
2473 	int *matchp,
2474 	slap_mask_t flags,
2475 	Syntax *syntax,
2476 	MatchingRule *mr,
2477 	struct berval *value,
2478 	void *assertedValue )
2479 {
2480 	struct berval *asserted = (struct berval *) assertedValue;
2481 	int vsign = 1, asign = 1;	/* default sign = '+' */
2482 	struct berval v, a;
2483 	int match;
2484 
2485 	v = *value;
2486 	if( v.bv_val[0] == '-' ) {
2487 		vsign = -1;
2488 		v.bv_val++;
2489 		v.bv_len--;
2490 	}
2491 
2492 	if( BER_BVISEMPTY( &v ) ) vsign = 0;
2493 
2494 	a = *asserted;
2495 	if( a.bv_val[0] == '-' ) {
2496 		asign = -1;
2497 		a.bv_val++;
2498 		a.bv_len--;
2499 	}
2500 
2501 	if( BER_BVISEMPTY( &a ) ) vsign = 0;
2502 
2503 	match = vsign - asign;
2504 	if( match == 0 ) {
2505 		match = ( v.bv_len != a.bv_len
2506 			? ( v.bv_len < a.bv_len ? -1 : 1 )
2507 			: memcmp( v.bv_val, a.bv_val, v.bv_len ));
2508 		if( vsign < 0 ) match = -match;
2509 	}
2510 
2511 	/* Ordering rule used in extensible match filter? */
2512 	if ( (flags & SLAP_MR_EXT) && (mr->smr_usage & SLAP_MR_ORDERING) )
2513 		match = (match >= 0);
2514 
2515 	*matchp = match;
2516 	return LDAP_SUCCESS;
2517 }
2518 
2519 /* 10**Chop < 256**Chopbytes and Chop > Chopbytes<<1 (for sign bit and itmp) */
2520 #define INDEX_INTLEN_CHOP 7
2521 #define INDEX_INTLEN_CHOPBYTES 3
2522 
2523 static int
2524 integerVal2Key(
2525 	struct berval *in,
2526 	struct berval *key,
2527 	struct berval *tmp,
2528 	void *ctx )
2529 {
2530 	/* Integer index key format, designed for memcmp to collate correctly:
2531 	 * if too large: one's complement sign*<approx exponent=chopped bytes>,
2532 	 * two's complement value (sign-extended or chopped as needed),
2533 	 * however in first byte above, the top <number of exponent-bytes + 1>
2534 	 * bits are the inverse sign and next bit is the sign as delimiter.
2535 	 */
2536 	ber_slen_t k = index_intlen_strlen;
2537 	ber_len_t chop = 0;
2538 	unsigned signmask = ~0x7fU;
2539 	unsigned char lenbuf[sizeof(k) + 2], *lenp, neg = 0xff;
2540 	struct berval val = *in, itmp = *tmp;
2541 
2542 	if ( val.bv_val[0] != '-' ) {
2543 		neg = 0;
2544 		--k;
2545 	}
2546 
2547 	/* Chop least significant digits, increase length instead */
2548 	if ( val.bv_len > (ber_len_t) k ) {
2549 		chop = (val.bv_len-k+2)/INDEX_INTLEN_CHOP; /* 2 fewer digits */
2550 		val.bv_len -= chop * INDEX_INTLEN_CHOP;	/* #digits chopped */
2551 		chop *= INDEX_INTLEN_CHOPBYTES;		/* #bytes added */
2552 	}
2553 
2554 	if ( lutil_str2bin( &val, &itmp, ctx )) {
2555 		return LDAP_INVALID_SYNTAX;
2556 	}
2557 
2558 	/* Omit leading sign byte */
2559 	if ( itmp.bv_val[0] == neg ) {
2560 		itmp.bv_val++;
2561 		itmp.bv_len--;
2562 	}
2563 
2564 	k = (ber_slen_t) index_intlen - (ber_slen_t) (itmp.bv_len + chop);
2565 	if ( k > 0 ) {
2566 		assert( chop == 0 );
2567 		memset( key->bv_val, neg, k );	/* sign-extend */
2568 	} else if ( k != 0 || ((itmp.bv_val[0] ^ neg) & 0xc0) ) {
2569 		/* Got exponent -k, or no room for 2 sign bits */
2570 		lenp = lenbuf + sizeof(lenbuf);
2571 		chop = - (ber_len_t) k;
2572 		do {
2573 			*--lenp = ((unsigned char) chop & 0xff) ^ neg;
2574 			signmask >>= 1;
2575 		} while ( (chop >>= 8) != 0 || (signmask >> 1) & (*lenp ^ neg) );
2576 		/* With n bytes in lenbuf, the top n+1 bits of (signmask&0xff)
2577 		 * are 1, and the top n+2 bits of lenp[0] are the sign bit. */
2578 		k = (lenbuf + sizeof(lenbuf)) - lenp;
2579 		if ( k > (ber_slen_t) index_intlen )
2580 			k = index_intlen;
2581 		memcpy( key->bv_val, lenp, k );
2582 		itmp.bv_len = index_intlen - k;
2583 	}
2584 	memcpy( key->bv_val + k, itmp.bv_val, itmp.bv_len );
2585 	key->bv_val[0] ^= (unsigned char) signmask & 0xff; /* invert sign */
2586 	return 0;
2587 }
2588 
2589 /* Index generation function: Ordered index */
2590 static int
2591 integerIndexer(
2592 	slap_mask_t use,
2593 	slap_mask_t flags,
2594 	Syntax *syntax,
2595 	MatchingRule *mr,
2596 	struct berval *prefix,
2597 	BerVarray values,
2598 	BerVarray *keysp,
2599 	void *ctx )
2600 {
2601 	char ibuf[64];
2602 	struct berval itmp;
2603 	BerVarray keys;
2604 	ber_len_t vlen;
2605 	int i, rc;
2606 	unsigned maxstrlen = index_intlen_strlen + INDEX_INTLEN_CHOP-1;
2607 
2608 	/* count the values and find max needed length */
2609 	vlen = 0;
2610 	for( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
2611 		if ( vlen < values[i].bv_len )
2612 			vlen = values[i].bv_len;
2613 	}
2614 	if ( vlen > maxstrlen )
2615 		vlen = maxstrlen;
2616 
2617 	/* we should have at least one value at this point */
2618 	assert( i > 0 );
2619 
2620 	keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
2621 	for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
2622 		keys[i].bv_len = index_intlen;
2623 		keys[i].bv_val = slap_sl_malloc( index_intlen, ctx );
2624 	}
2625 	keys[i].bv_len = 0;
2626 	keys[i].bv_val = NULL;
2627 
2628 	if ( vlen > sizeof(ibuf) ) {
2629 		itmp.bv_val = slap_sl_malloc( vlen, ctx );
2630 	} else {
2631 		itmp.bv_val = ibuf;
2632 	}
2633 	itmp.bv_len = sizeof(ibuf);
2634 
2635 	for ( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
2636 		if ( itmp.bv_val != ibuf ) {
2637 			itmp.bv_len = values[i].bv_len;
2638 			if ( itmp.bv_len <= sizeof(ibuf) )
2639 				itmp.bv_len = sizeof(ibuf);
2640 			else if ( itmp.bv_len > maxstrlen )
2641 				itmp.bv_len = maxstrlen;
2642 		}
2643 		rc = integerVal2Key( &values[i], &keys[i], &itmp, ctx );
2644 		if ( rc ) {
2645 			slap_sl_free( keys, ctx );
2646 			goto func_leave;
2647 		}
2648 	}
2649 	*keysp = keys;
2650 func_leave:
2651 	if ( itmp.bv_val != ibuf ) {
2652 		slap_sl_free( itmp.bv_val, ctx );
2653 	}
2654 	return rc;
2655 }
2656 
2657 /* Index generation function: Ordered index */
2658 static int
2659 integerFilter(
2660 	slap_mask_t use,
2661 	slap_mask_t flags,
2662 	Syntax *syntax,
2663 	MatchingRule *mr,
2664 	struct berval *prefix,
2665 	void * assertedValue,
2666 	BerVarray *keysp,
2667 	void *ctx )
2668 {
2669 	char ibuf[64];
2670 	struct berval iv;
2671 	BerVarray keys;
2672 	struct berval *value;
2673 	int rc;
2674 
2675 	value = (struct berval *) assertedValue;
2676 
2677 	keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
2678 
2679 	keys[0].bv_len = index_intlen;
2680 	keys[0].bv_val = slap_sl_malloc( index_intlen, ctx );
2681 	keys[1].bv_len = 0;
2682 	keys[1].bv_val = NULL;
2683 
2684 	iv.bv_len = value->bv_len < index_intlen_strlen + INDEX_INTLEN_CHOP-1
2685 		? value->bv_len : index_intlen_strlen + INDEX_INTLEN_CHOP-1;
2686 	if ( iv.bv_len > (int) sizeof(ibuf) ) {
2687 		iv.bv_val = slap_sl_malloc( iv.bv_len, ctx );
2688 	} else {
2689 		iv.bv_val = ibuf;
2690 		iv.bv_len = sizeof(ibuf);
2691 	}
2692 
2693 	rc = integerVal2Key( value, keys, &iv, ctx );
2694 
2695 	if ( iv.bv_val != ibuf ) {
2696 		slap_sl_free( iv.bv_val, ctx );
2697 	}
2698 
2699 	if ( rc == 0 )
2700 		*keysp = keys;
2701 	else
2702 		slap_sl_free( keys, ctx );
2703 
2704 	return rc;
2705 }
2706 
2707 static int
2708 countryStringValidate(
2709 	Syntax *syntax,
2710 	struct berval *val )
2711 {
2712 	if( val->bv_len != 2 ) return LDAP_INVALID_SYNTAX;
2713 
2714 	if( !SLAP_PRINTABLE(val->bv_val[0]) ) {
2715 		return LDAP_INVALID_SYNTAX;
2716 	}
2717 	if( !SLAP_PRINTABLE(val->bv_val[1]) ) {
2718 		return LDAP_INVALID_SYNTAX;
2719 	}
2720 
2721 	return LDAP_SUCCESS;
2722 }
2723 
2724 static int
2725 printableStringValidate(
2726 	Syntax *syntax,
2727 	struct berval *val )
2728 {
2729 	ber_len_t i;
2730 
2731 	if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
2732 
2733 	for(i=0; i < val->bv_len; i++) {
2734 		if( !SLAP_PRINTABLE(val->bv_val[i]) ) {
2735 			return LDAP_INVALID_SYNTAX;
2736 		}
2737 	}
2738 
2739 	return LDAP_SUCCESS;
2740 }
2741 
2742 static int
2743 printablesStringValidate(
2744 	Syntax *syntax,
2745 	struct berval *val )
2746 {
2747 	ber_len_t i, len;
2748 
2749 	if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
2750 
2751 	for(i=0,len=0; i < val->bv_len; i++) {
2752 		int c = val->bv_val[i];
2753 
2754 		if( c == '$' ) {
2755 			if( len == 0 ) {
2756 				return LDAP_INVALID_SYNTAX;
2757 			}
2758 			len = 0;
2759 
2760 		} else if ( SLAP_PRINTABLE(c) ) {
2761 			len++;
2762 		} else {
2763 			return LDAP_INVALID_SYNTAX;
2764 		}
2765 	}
2766 
2767 	if( len == 0 ) {
2768 		return LDAP_INVALID_SYNTAX;
2769 	}
2770 
2771 	return LDAP_SUCCESS;
2772 }
2773 
2774 static int
2775 IA5StringValidate(
2776 	Syntax *syntax,
2777 	struct berval *val )
2778 {
2779 	ber_len_t i;
2780 
2781 	for(i=0; i < val->bv_len; i++) {
2782 		if( !LDAP_ASCII(val->bv_val[i]) ) {
2783 			return LDAP_INVALID_SYNTAX;
2784 		}
2785 	}
2786 
2787 	return LDAP_SUCCESS;
2788 }
2789 
2790 static int
2791 IA5StringNormalize(
2792 	slap_mask_t use,
2793 	Syntax *syntax,
2794 	MatchingRule *mr,
2795 	struct berval *val,
2796 	struct berval *normalized,
2797 	void *ctx )
2798 {
2799 	char *p, *q, *end;
2800 	int casefold = !SLAP_MR_ASSOCIATED( mr,
2801 		slap_schema.si_mr_caseExactIA5Match );
2802 
2803 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
2804 
2805 	p = val->bv_val;
2806 	end = val->bv_val + val->bv_len;
2807 
2808 	/* Ignore initial whitespace */
2809 	while ( p < end && ASCII_SPACE( *p ) ) p++;
2810 
2811 	normalized->bv_len = p < end ? (val->bv_len - ( p - val->bv_val )) : 0;
2812 	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2813 	AC_MEMCPY( normalized->bv_val, p, normalized->bv_len );
2814 	normalized->bv_val[normalized->bv_len] = '\0';
2815 
2816 	p = q = normalized->bv_val;
2817 
2818 	while ( *p ) {
2819 		if ( ASCII_SPACE( *p ) ) {
2820 			*q++ = *p++;
2821 
2822 			/* Ignore the extra whitespace */
2823 			while ( ASCII_SPACE( *p ) ) {
2824 				p++;
2825 			}
2826 
2827 		} else if ( casefold ) {
2828 			/* Most IA5 rules require casefolding */
2829 			*q++ = TOLOWER(*p); p++;
2830 
2831 		} else {
2832 			*q++ = *p++;
2833 		}
2834 	}
2835 
2836 	assert( normalized->bv_val <= p );
2837 	assert( q <= p );
2838 
2839 	/*
2840 	 * If the string ended in space, backup the pointer one
2841 	 * position.  One is enough because the above loop collapsed
2842 	 * all whitespace to a single space.
2843 	 */
2844 	if ( q > normalized->bv_val && ASCII_SPACE( q[-1] ) ) --q;
2845 
2846 	/* null terminate */
2847 	*q = '\0';
2848 
2849 	normalized->bv_len = q - normalized->bv_val;
2850 
2851 	return LDAP_SUCCESS;
2852 }
2853 
2854 static int
2855 UUIDValidate(
2856 	Syntax *syntax,
2857 	struct berval *in )
2858 {
2859 	int i;
2860 	if( in->bv_len != 36 ) {
2861 		return LDAP_INVALID_SYNTAX;
2862 	}
2863 
2864 	for( i=0; i<36; i++ ) {
2865 		switch(i) {
2866 			case 8:
2867 			case 13:
2868 			case 18:
2869 			case 23:
2870 				if( in->bv_val[i] != '-' ) {
2871 					return LDAP_INVALID_SYNTAX;
2872 				}
2873 				break;
2874 			default:
2875 				if( !ASCII_HEX( in->bv_val[i]) ) {
2876 					return LDAP_INVALID_SYNTAX;
2877 				}
2878 		}
2879 	}
2880 
2881 	return LDAP_SUCCESS;
2882 }
2883 
2884 static int
2885 UUIDPretty(
2886 	Syntax *syntax,
2887 	struct berval *in,
2888 	struct berval *out,
2889 	void *ctx )
2890 {
2891 	int i;
2892 	int rc=LDAP_INVALID_SYNTAX;
2893 
2894 	assert( in != NULL );
2895 	assert( out != NULL );
2896 
2897 	if( in->bv_len != 36 ) return LDAP_INVALID_SYNTAX;
2898 
2899 	out->bv_len = 36;
2900 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
2901 
2902 	for( i=0; i<36; i++ ) {
2903 		switch(i) {
2904 			case 8:
2905 			case 13:
2906 			case 18:
2907 			case 23:
2908 				if( in->bv_val[i] != '-' ) {
2909 					goto handle_error;
2910 				}
2911 				out->bv_val[i] = '-';
2912 				break;
2913 
2914 			default:
2915 				if( !ASCII_HEX( in->bv_val[i]) ) {
2916 					goto handle_error;
2917 				}
2918 				out->bv_val[i] = TOLOWER( in->bv_val[i] );
2919 		}
2920 	}
2921 
2922 	rc = LDAP_SUCCESS;
2923 	out->bv_val[ out->bv_len ] = '\0';
2924 
2925 	if( 0 ) {
2926 handle_error:
2927 		slap_sl_free( out->bv_val, ctx );
2928 		out->bv_val = NULL;
2929 	}
2930 
2931 	return rc;
2932 }
2933 
2934 int
2935 UUIDNormalize(
2936 	slap_mask_t usage,
2937 	Syntax *syntax,
2938 	MatchingRule *mr,
2939 	struct berval *val,
2940 	struct berval *normalized,
2941 	void *ctx )
2942 {
2943 	unsigned char octet = '\0';
2944 	int i;
2945 	int j;
2946 
2947 	if ( SLAP_MR_IS_DENORMALIZE( usage ) ) {
2948 		/* NOTE: must be a normalized UUID */
2949 		assert( val->bv_len == 16 );
2950 
2951 		normalized->bv_val = slap_sl_malloc( LDAP_LUTIL_UUIDSTR_BUFSIZE, ctx );
2952 		normalized->bv_len = lutil_uuidstr_from_normalized( val->bv_val,
2953 			val->bv_len, normalized->bv_val, LDAP_LUTIL_UUIDSTR_BUFSIZE );
2954 		assert( normalized->bv_len == STRLENOF( "BADBADBA-DBAD-0123-4567-BADBADBADBAD" ) );
2955 
2956 		return LDAP_SUCCESS;
2957 	}
2958 
2959 	normalized->bv_len = 16;
2960 	normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2961 
2962 	for( i=0, j=0; i<36; i++ ) {
2963 		unsigned char nibble;
2964 		if( val->bv_val[i] == '-' ) {
2965 			continue;
2966 
2967 		} else if( ASCII_DIGIT( val->bv_val[i] ) ) {
2968 			nibble = val->bv_val[i] - '0';
2969 
2970 		} else if( ASCII_HEXLOWER( val->bv_val[i] ) ) {
2971 			nibble = val->bv_val[i] - ('a'-10);
2972 
2973 		} else if( ASCII_HEXUPPER( val->bv_val[i] ) ) {
2974 			nibble = val->bv_val[i] - ('A'-10);
2975 
2976 		} else {
2977 			slap_sl_free( normalized->bv_val, ctx );
2978 			BER_BVZERO( normalized );
2979 			return LDAP_INVALID_SYNTAX;
2980 		}
2981 
2982 		if( j & 1 ) {
2983 			octet |= nibble;
2984 			normalized->bv_val[j>>1] = octet;
2985 		} else {
2986 			octet = nibble << 4;
2987 		}
2988 		j++;
2989 	}
2990 
2991 	normalized->bv_val[normalized->bv_len] = 0;
2992 	return LDAP_SUCCESS;
2993 }
2994 
2995 
2996 
2997 int
2998 numericStringValidate(
2999 	Syntax *syntax,
3000 	struct berval *in )
3001 {
3002 	ber_len_t i;
3003 
3004 	if( BER_BVISEMPTY( in ) ) return LDAP_INVALID_SYNTAX;
3005 
3006 	for(i=0; i < in->bv_len; i++) {
3007 		if( !SLAP_NUMERIC(in->bv_val[i]) ) {
3008 			return LDAP_INVALID_SYNTAX;
3009 		}
3010 	}
3011 
3012 	return LDAP_SUCCESS;
3013 }
3014 
3015 static int
3016 numericStringNormalize(
3017 	slap_mask_t usage,
3018 	Syntax *syntax,
3019 	MatchingRule *mr,
3020 	struct berval *val,
3021 	struct berval *normalized,
3022 	void *ctx )
3023 {
3024 	/* removal all spaces */
3025 	char *p, *q;
3026 
3027 	assert( !BER_BVISEMPTY( val ) );
3028 
3029 	normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
3030 
3031 	p = val->bv_val;
3032 	q = normalized->bv_val;
3033 
3034 	while ( *p ) {
3035 		if ( ASCII_SPACE( *p ) ) {
3036 			/* Ignore whitespace */
3037 			p++;
3038 		} else {
3039 			*q++ = *p++;
3040 		}
3041 	}
3042 
3043 	/* we should have copied no more than is in val */
3044 	assert( (q - normalized->bv_val) <= (p - val->bv_val) );
3045 
3046 	/* null terminate */
3047 	*q = '\0';
3048 
3049 	normalized->bv_len = q - normalized->bv_val;
3050 
3051 	if( BER_BVISEMPTY( normalized ) ) {
3052 		normalized->bv_val = slap_sl_realloc( normalized->bv_val, 2, ctx );
3053 		normalized->bv_val[0] = ' ';
3054 		normalized->bv_val[1] = '\0';
3055 		normalized->bv_len = 1;
3056 	}
3057 
3058 	return LDAP_SUCCESS;
3059 }
3060 
3061 /*
3062  * Integer conversion macros that will use the largest available
3063  * type.
3064  */
3065 #if defined(HAVE_STRTOLL) && defined(HAVE_LONG_LONG)
3066 # define SLAP_STRTOL(n,e,b)  strtoll(n,e,b)
3067 # define SLAP_LONG           long long
3068 #else
3069 # define SLAP_STRTOL(n,e,b)  strtol(n,e,b)
3070 # define SLAP_LONG           long
3071 #endif /* HAVE_STRTOLL ... */
3072 
3073 static int
3074 integerBitAndMatch(
3075 	int *matchp,
3076 	slap_mask_t flags,
3077 	Syntax *syntax,
3078 	MatchingRule *mr,
3079 	struct berval *value,
3080 	void *assertedValue )
3081 {
3082 	SLAP_LONG lValue, lAssertedValue;
3083 
3084 	errno = 0;
3085 	/* safe to assume integers are NUL terminated? */
3086 	lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
3087 	if( errno == ERANGE )
3088 	{
3089 		return LDAP_CONSTRAINT_VIOLATION;
3090 	}
3091 
3092 	lAssertedValue = SLAP_STRTOL(((struct berval *)assertedValue)->bv_val,
3093 		NULL, 10);
3094 	if( errno == ERANGE )
3095 	{
3096 		return LDAP_CONSTRAINT_VIOLATION;
3097 	}
3098 
3099 	*matchp = ((lValue & lAssertedValue) == lAssertedValue) ? 0 : 1;
3100 	return LDAP_SUCCESS;
3101 }
3102 
3103 static int
3104 integerBitOrMatch(
3105 	int *matchp,
3106 	slap_mask_t flags,
3107 	Syntax *syntax,
3108 	MatchingRule *mr,
3109 	struct berval *value,
3110 	void *assertedValue )
3111 {
3112 	SLAP_LONG lValue, lAssertedValue;
3113 
3114 	errno = 0;
3115 	/* safe to assume integers are NUL terminated? */
3116 	lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
3117 	if( errno == ERANGE )
3118 	{
3119 		return LDAP_CONSTRAINT_VIOLATION;
3120 	}
3121 
3122 	lAssertedValue = SLAP_STRTOL( ((struct berval *)assertedValue)->bv_val,
3123 		NULL, 10);
3124 	if( errno == ERANGE )
3125 	{
3126 		return LDAP_CONSTRAINT_VIOLATION;
3127 	}
3128 
3129 	*matchp = ((lValue & lAssertedValue) != 0) ? 0 : -1;
3130 	return LDAP_SUCCESS;
3131 }
3132 
3133 static int
3134 checkNum( struct berval *in, struct berval *out )
3135 {
3136 	/* parse serialNumber */
3137 	ber_len_t neg = 0, extra = 0;
3138 	char first = '\0';
3139 
3140 	out->bv_val = in->bv_val;
3141 	out->bv_len = 0;
3142 
3143 	if ( out->bv_val[0] == '-' ) {
3144 		neg++;
3145 		out->bv_len++;
3146 	}
3147 
3148 	if ( strncasecmp( out->bv_val, "0x", STRLENOF("0x") ) == 0 ) {
3149 		first = out->bv_val[2];
3150 		extra = 2;
3151 
3152 		out->bv_len += STRLENOF("0x");
3153 		for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
3154 			if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break;
3155 		}
3156 
3157 	} else if ( out->bv_val[0] == '\'' ) {
3158 		first = out->bv_val[1];
3159 		extra = 3;
3160 
3161 		out->bv_len += STRLENOF("'");
3162 
3163 		for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
3164 			if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break;
3165 		}
3166 		if ( strncmp( &out->bv_val[out->bv_len], "'H", STRLENOF("'H") ) != 0 ) {
3167 			return -1;
3168 		}
3169 		out->bv_len += STRLENOF("'H");
3170 
3171 	} else {
3172 		first = out->bv_val[0];
3173 		for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
3174 			if ( !ASCII_DIGIT( out->bv_val[out->bv_len] ) ) break;
3175 		}
3176 	}
3177 
3178 	if ( !( out->bv_len > neg ) ) {
3179 		return -1;
3180 	}
3181 
3182 	if ( ( out->bv_len > extra + 1 + neg ) && ( first == '0' ) ) {
3183 		return -1;
3184 	}
3185 
3186 	return 0;
3187 }
3188 
3189 static int
3190 serialNumberAndIssuerCheck(
3191 	struct berval *in,
3192 	struct berval *sn,
3193 	struct berval *is,
3194 	void *ctx )
3195 {
3196 	ber_len_t n;
3197 
3198 	if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
3199 
3200 	if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
3201 		/* Parse old format */
3202 		is->bv_val = ber_bvchr( in, '$' );
3203 		if( BER_BVISNULL( is ) ) return LDAP_INVALID_SYNTAX;
3204 
3205 		sn->bv_val = in->bv_val;
3206 		sn->bv_len = is->bv_val - in->bv_val;
3207 
3208 		is->bv_val++;
3209 		is->bv_len = in->bv_len - (sn->bv_len + 1);
3210 
3211 		/* eat leading zeros */
3212 		for( n=0; n < (sn->bv_len-1); n++ ) {
3213 			if( sn->bv_val[n] != '0' ) break;
3214 		}
3215 		sn->bv_val += n;
3216 		sn->bv_len -= n;
3217 
3218 		for( n=0; n < sn->bv_len; n++ ) {
3219 			if( !ASCII_DIGIT(sn->bv_val[n]) ) return LDAP_INVALID_SYNTAX;
3220 		}
3221 
3222 	} else {
3223 		/* Parse GSER format */
3224 		enum {
3225 			HAVE_NONE = 0x0,
3226 			HAVE_ISSUER = 0x1,
3227 			HAVE_SN = 0x2,
3228 			HAVE_ALL = ( HAVE_ISSUER | HAVE_SN )
3229 		} have = HAVE_NONE;
3230 
3231 		int numdquotes = 0;
3232 		struct berval x = *in;
3233 		struct berval ni;
3234 		x.bv_val++;
3235 		x.bv_len -= 2;
3236 
3237 		do {
3238 			/* eat leading spaces */
3239 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3240 				/* empty */;
3241 			}
3242 
3243 			/* should be at issuer or serialNumber NamedValue */
3244 			if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
3245 				if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX;
3246 
3247 				/* parse issuer */
3248 				x.bv_val += STRLENOF("issuer");
3249 				x.bv_len -= STRLENOF("issuer");
3250 
3251 				if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3252 				x.bv_val++;
3253 				x.bv_len--;
3254 
3255 				/* eat leading spaces */
3256 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3257 					/* empty */;
3258 				}
3259 
3260 				/* For backward compatibility, this part is optional */
3261 				if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) == 0 ) {
3262 					x.bv_val += STRLENOF("rdnSequence:");
3263 					x.bv_len -= STRLENOF("rdnSequence:");
3264 				}
3265 
3266 				if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3267 				x.bv_val++;
3268 				x.bv_len--;
3269 
3270 				is->bv_val = x.bv_val;
3271 				is->bv_len = 0;
3272 
3273 				for ( ; is->bv_len < x.bv_len; ) {
3274 					if ( is->bv_val[is->bv_len] != '"' ) {
3275 						is->bv_len++;
3276 						continue;
3277 					}
3278 					if ( is->bv_val[is->bv_len+1] == '"' ) {
3279 						/* double dquote */
3280 						numdquotes++;
3281 						is->bv_len += 2;
3282 						continue;
3283 					}
3284 					break;
3285 				}
3286 				x.bv_val += is->bv_len + 1;
3287 				x.bv_len -= is->bv_len + 1;
3288 
3289 				have |= HAVE_ISSUER;
3290 
3291 			} else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 )
3292 			{
3293 				if ( have & HAVE_SN ) return LDAP_INVALID_SYNTAX;
3294 
3295 				/* parse serialNumber */
3296 				x.bv_val += STRLENOF("serialNumber");
3297 				x.bv_len -= STRLENOF("serialNumber");
3298 
3299 				if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3300 				x.bv_val++;
3301 				x.bv_len--;
3302 
3303 				/* eat leading spaces */
3304 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3305 					/* empty */;
3306 				}
3307 
3308 				if ( checkNum( &x, sn ) ) {
3309 					return LDAP_INVALID_SYNTAX;
3310 				}
3311 
3312 				x.bv_val += sn->bv_len;
3313 				x.bv_len -= sn->bv_len;
3314 
3315 				have |= HAVE_SN;
3316 
3317 			} else {
3318 				return LDAP_INVALID_SYNTAX;
3319 			}
3320 
3321 			/* eat leading spaces */
3322 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3323 				/* empty */;
3324 			}
3325 
3326 			if ( have == HAVE_ALL ) {
3327 				break;
3328 			}
3329 
3330 			if ( x.bv_val[0] != ',' ) {
3331 				return LDAP_INVALID_SYNTAX;
3332 			}
3333 
3334 			x.bv_val++;
3335 			x.bv_len--;
3336 		} while ( 1 );
3337 
3338 		/* should have no characters left... */
3339 		if ( x.bv_len ) return LDAP_INVALID_SYNTAX;
3340 
3341 		if ( numdquotes == 0 ) {
3342 			ber_dupbv_x( &ni, is, ctx );
3343 
3344 		} else {
3345 			ber_len_t src, dst;
3346 
3347 			ni.bv_len = is->bv_len - numdquotes;
3348 			ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx );
3349 			for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
3350 				if ( is->bv_val[src] == '"' ) {
3351 					src++;
3352 				}
3353 				ni.bv_val[dst] = is->bv_val[src];
3354 			}
3355 			ni.bv_val[dst] = '\0';
3356 		}
3357 
3358 		*is = ni;
3359 	}
3360 
3361 	return 0;
3362 }
3363 
3364 static int
3365 serialNumberAndIssuerValidate(
3366 	Syntax *syntax,
3367 	struct berval *in )
3368 {
3369 	int rc;
3370 	struct berval sn, i;
3371 
3372 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n",
3373 		in->bv_val, 0, 0 );
3374 
3375 	rc = serialNumberAndIssuerCheck( in, &sn, &i, NULL );
3376 	if ( rc ) {
3377 		goto done;
3378 	}
3379 
3380 	/* validate DN -- doesn't handle double dquote */
3381 	rc = dnValidate( NULL, &i );
3382 	if ( rc ) {
3383 		rc = LDAP_INVALID_SYNTAX;
3384 	}
3385 
3386 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3387 		slap_sl_free( i.bv_val, NULL );
3388 	}
3389 
3390 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: <%s> err=%d\n",
3391 		in->bv_val, rc, 0 );
3392 
3393 done:;
3394 	return rc;
3395 }
3396 
3397 static int
3398 serialNumberAndIssuerPretty(
3399 	Syntax *syntax,
3400 	struct berval *in,
3401 	struct berval *out,
3402 	void *ctx )
3403 {
3404 	int rc;
3405 	struct berval sn, i, ni = BER_BVNULL;
3406 	char *p;
3407 
3408 	assert( in != NULL );
3409 	assert( out != NULL );
3410 
3411 	BER_BVZERO( out );
3412 
3413 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerPretty: <%s>\n",
3414 		in->bv_val, 0, 0 );
3415 
3416 	rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx );
3417 	if ( rc ) {
3418 		goto done;
3419 	}
3420 
3421 	rc = dnPretty( syntax, &i, &ni, ctx );
3422 
3423 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3424 		slap_sl_free( i.bv_val, ctx );
3425 	}
3426 
3427 	if ( rc ) {
3428 		rc = LDAP_INVALID_SYNTAX;
3429 		goto done;
3430 	}
3431 
3432 	/* make room from sn + "$" */
3433 	out->bv_len = STRLENOF("{ serialNumber , issuer rdnSequence:\"\" }")
3434 		+ sn.bv_len + ni.bv_len;
3435 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
3436 
3437 	if ( out->bv_val == NULL ) {
3438 		out->bv_len = 0;
3439 		rc = LDAP_OTHER;
3440 		goto done;
3441 	}
3442 
3443 	p = out->bv_val;
3444 	p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
3445 	p = lutil_strbvcopy( p, &sn );
3446 	p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
3447 	p = lutil_strbvcopy( p, &ni );
3448 	p = lutil_strcopy( p, /*{*/ "\" }" );
3449 
3450 	assert( p == &out->bv_val[out->bv_len] );
3451 
3452 done:;
3453 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s> => <%s>\n",
3454 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
3455 
3456 	slap_sl_free( ni.bv_val, ctx );
3457 
3458 	return LDAP_SUCCESS;
3459 }
3460 
3461 static int
3462 slap_bin2hex(
3463 	struct berval *in,
3464 	struct berval *out,
3465 	void *ctx )
3466 
3467 {
3468 	/* Use hex format. '123456789abcdef'H */
3469 	unsigned char *ptr, zero = '\0';
3470 	char *sptr;
3471 	int first;
3472 	ber_len_t i, len, nlen;
3473 
3474 	assert( in != NULL );
3475 	assert( !BER_BVISNULL( in ) );
3476 	assert( out != NULL );
3477 	assert( !BER_BVISNULL( out ) );
3478 
3479 	ptr = (unsigned char *)in->bv_val;
3480 	len = in->bv_len;
3481 
3482 	/* Check for minimal encodings */
3483 	if ( len > 1 ) {
3484 		if ( ptr[0] & 0x80 ) {
3485 			if ( ( ptr[0] == 0xff ) && ( ptr[1] & 0x80 ) ) {
3486 				return -1;
3487 			}
3488 
3489 		} else if ( ptr[0] == 0 ) {
3490 			if ( !( ptr[1] & 0x80 ) ) {
3491 				return -1;
3492 			}
3493 			len--;
3494 			ptr++;
3495 		}
3496 
3497 	} else if ( len == 0 ) {
3498 		/* FIXME: this should not be possible,
3499 		 * since a value of zero would have length 1 */
3500 		len = 1;
3501 		ptr = &zero;
3502 	}
3503 
3504 	first = !( ptr[0] & 0xf0U );
3505 	nlen = len * 2 - first + STRLENOF("''H"); /* quotes, H */
3506 	if ( nlen >= out->bv_len ) {
3507 		out->bv_val = slap_sl_malloc( nlen + 1, ctx );
3508 	}
3509 	sptr = out->bv_val;
3510 	*sptr++ = '\'';
3511 	i = 0;
3512 	if ( first ) {
3513 		sprintf( sptr, "%01X", ( ptr[0] & 0x0fU ) );
3514 		sptr++;
3515 		i = 1;
3516 	}
3517 	for ( ; i < len; i++ ) {
3518 		sprintf( sptr, "%02X", ptr[i] );
3519 		sptr += 2;
3520 	}
3521 	*sptr++ = '\'';
3522 	*sptr++ = 'H';
3523 	*sptr = '\0';
3524 
3525 	assert( sptr == &out->bv_val[nlen] );
3526 
3527 	out->bv_len = nlen;
3528 
3529 	return 0;
3530 }
3531 
3532 #define SLAP_SN_BUFLEN	(64)
3533 
3534 /*
3535  * This routine is called by certificateExactNormalize when
3536  * certificateExactNormalize receives a search string instead of
3537  * a certificate. This routine checks if the search value is valid
3538  * and then returns the normalized value
3539  */
3540 static int
3541 serialNumberAndIssuerNormalize(
3542 	slap_mask_t usage,
3543 	Syntax *syntax,
3544 	MatchingRule *mr,
3545 	struct berval *in,
3546 	struct berval *out,
3547 	void *ctx )
3548 {
3549 	struct berval sn, sn2, sn3, i, ni;
3550 	char sbuf2[SLAP_SN_BUFLEN];
3551 	char sbuf3[SLAP_SN_BUFLEN];
3552 	char *p;
3553 	int rc;
3554 
3555 	assert( in != NULL );
3556 	assert( out != NULL );
3557 
3558 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n",
3559 		in->bv_val, 0, 0 );
3560 
3561 	rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx );
3562 	if ( rc ) {
3563 		return rc;
3564 	}
3565 
3566 	rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
3567 
3568 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3569 		slap_sl_free( i.bv_val, ctx );
3570 	}
3571 
3572 	if ( rc ) {
3573 		return LDAP_INVALID_SYNTAX;
3574 	}
3575 
3576 	/* Convert sn to canonical hex */
3577 	sn2.bv_val = sbuf2;
3578 	if ( sn.bv_len > sizeof( sbuf2 ) ) {
3579 		sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
3580 	}
3581 	sn2.bv_len = sn.bv_len;
3582 	sn3.bv_val = sbuf3;
3583 	sn3.bv_len = sizeof(sbuf3);
3584 	if ( lutil_str2bin( &sn, &sn2, ctx ) || slap_bin2hex( &sn2, &sn3, ctx ) ) {
3585 		rc = LDAP_INVALID_SYNTAX;
3586 		goto func_leave;
3587 	}
3588 
3589 	out->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
3590 		+ sn3.bv_len + ni.bv_len;
3591 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
3592 	if ( out->bv_val == NULL ) {
3593 		out->bv_len = 0;
3594 		rc = LDAP_OTHER;
3595 		goto func_leave;
3596 	}
3597 
3598 	p = out->bv_val;
3599 
3600 	p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
3601 	p = lutil_strbvcopy( p, &sn3 );
3602 	p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
3603 	p = lutil_strbvcopy( p, &ni );
3604 	p = lutil_strcopy( p, /*{*/ "\" }" );
3605 
3606 	assert( p == &out->bv_val[out->bv_len] );
3607 
3608 func_leave:
3609 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s> => <%s>\n",
3610 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
3611 
3612 	if ( sn2.bv_val != sbuf2 ) {
3613 		slap_sl_free( sn2.bv_val, ctx );
3614 	}
3615 
3616 	if ( sn3.bv_val != sbuf3 ) {
3617 		slap_sl_free( sn3.bv_val, ctx );
3618 	}
3619 
3620 	slap_sl_free( ni.bv_val, ctx );
3621 
3622 	return rc;
3623 }
3624 
3625 static int
3626 certificateExactNormalize(
3627 	slap_mask_t usage,
3628 	Syntax *syntax,
3629 	MatchingRule *mr,
3630 	struct berval *val,
3631 	struct berval *normalized,
3632 	void *ctx )
3633 {
3634 	BerElementBuffer berbuf;
3635 	BerElement *ber = (BerElement *)&berbuf;
3636 	ber_tag_t tag;
3637 	ber_len_t len;
3638 	ber_int_t i;
3639 	char serialbuf2[SLAP_SN_BUFLEN];
3640 	struct berval sn, sn2 = BER_BVNULL;
3641 	struct berval issuer_dn = BER_BVNULL, bvdn;
3642 	char *p;
3643 	int rc = LDAP_INVALID_SYNTAX;
3644 
3645 	assert( val != NULL );
3646 
3647 	Debug( LDAP_DEBUG_TRACE, ">>> certificateExactNormalize: <%p, %lu>\n",
3648 		val->bv_val, val->bv_len, 0 );
3649 
3650 	if ( BER_BVISEMPTY( val ) ) goto done;
3651 
3652 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
3653 		return serialNumberAndIssuerNormalize( 0, NULL, NULL, val, normalized, ctx );
3654 	}
3655 
3656 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
3657 
3658 	ber_init2( ber, val, LBER_USE_DER );
3659 	tag = ber_skip_tag( ber, &len );	/* Signed Sequence */
3660 	tag = ber_skip_tag( ber, &len );	/* Sequence */
3661 	tag = ber_peek_tag( ber, &len );	/* Optional version? */
3662 	if ( tag == SLAP_X509_OPT_C_VERSION ) {
3663 		tag = ber_skip_tag( ber, &len );
3664 		tag = ber_get_int( ber, &i );	/* version */
3665 	}
3666 
3667 	/* NOTE: move the test here from certificateValidate,
3668 	 * so that we can validate certs with serial longer
3669 	 * than sizeof(ber_int_t) */
3670 	tag = ber_skip_tag( ber, &len );	/* serial */
3671 	sn.bv_len = len;
3672 	sn.bv_val = (char *)ber->ber_ptr;
3673 	sn2.bv_val = serialbuf2;
3674 	sn2.bv_len = sizeof(serialbuf2);
3675 	if ( slap_bin2hex( &sn, &sn2, ctx ) ) {
3676 		rc = LDAP_INVALID_SYNTAX;
3677 		goto done;
3678 	}
3679 	ber_skip_data( ber, len );
3680 
3681 	tag = ber_skip_tag( ber, &len );	/* SignatureAlg */
3682 	ber_skip_data( ber, len );
3683 	tag = ber_peek_tag( ber, &len );	/* IssuerDN */
3684 	if ( len ) {
3685 		len = ber_ptrlen( ber );
3686 		bvdn.bv_val = val->bv_val + len;
3687 		bvdn.bv_len = val->bv_len - len;
3688 
3689 		rc = dnX509normalize( &bvdn, &issuer_dn );
3690 		if ( rc != LDAP_SUCCESS ) {
3691 			rc = LDAP_INVALID_SYNTAX;
3692 			goto done;
3693 		}
3694 	}
3695 
3696 	normalized->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
3697 		+ sn2.bv_len + issuer_dn.bv_len;
3698 	normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
3699 
3700 	p = normalized->bv_val;
3701 
3702 	p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
3703 	p = lutil_strbvcopy( p, &sn2 );
3704 	p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
3705 	p = lutil_strbvcopy( p, &issuer_dn );
3706 	p = lutil_strcopy( p, /*{*/ "\" }" );
3707 
3708 	rc = LDAP_SUCCESS;
3709 
3710 done:
3711 	Debug( LDAP_DEBUG_TRACE, "<<< certificateExactNormalize: <%p, %lu> => <%s>\n",
3712 		val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
3713 
3714 	if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
3715 	if ( sn2.bv_val != serialbuf2 ) ber_memfree_x( sn2.bv_val, ctx );
3716 
3717 	return rc;
3718 }
3719 
3720 /* X.509 PKI certificateList stuff */
3721 static int
3722 checkTime( struct berval *in, struct berval *out )
3723 {
3724 	int rc;
3725 	ber_len_t i;
3726 	char buf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
3727 	struct berval bv;
3728 
3729 	assert( in != NULL );
3730 	assert( !BER_BVISNULL( in ) );
3731 	assert( !BER_BVISEMPTY( in ) );
3732 
3733 	if ( in->bv_len < STRLENOF( "YYmmddHHMMSSZ" ) ) {
3734 		return -1;
3735 	}
3736 
3737 	if ( out != NULL ) {
3738 		assert( !BER_BVISNULL( out ) );
3739 		assert( out->bv_len >= sizeof( buf ) );
3740 		bv.bv_val = out->bv_val;
3741 
3742 	} else {
3743 		bv.bv_val = buf;
3744 	}
3745 
3746 	for ( i = 0; i < STRLENOF( "YYYYmmddHHMMSS" ); i++ ) {
3747 		if ( !ASCII_DIGIT( in->bv_val[i] ) ) break;
3748 	}
3749 
3750 	if ( in->bv_val[i] != 'Z' ) {
3751 		return -1;
3752 	}
3753 	i++;
3754 
3755 	if ( i != in->bv_len ) {
3756 		return -1;
3757 	}
3758 
3759 	if ( i == STRLENOF( "YYYYmmddHHMMSSZ" ) ) {
3760 		lutil_strncopy( bv.bv_val, in->bv_val, i );
3761 		bv.bv_len = i;
3762 
3763 	} else if ( i == STRLENOF( "YYmmddHHMMSSZ" ) ) {
3764 		char *p = bv.bv_val;
3765 		if ( in->bv_val[0] < '7' ) {
3766 			p = lutil_strcopy( p, "20" );
3767 
3768 		} else {
3769 			p = lutil_strcopy( p, "19" );
3770 		}
3771 		lutil_strncopy( p, in->bv_val, i );
3772 		bv.bv_len = 2 + i;
3773 
3774 	} else {
3775 		return -1;
3776 	}
3777 
3778 	rc = generalizedTimeValidate( NULL, &bv );
3779 	if ( rc == LDAP_SUCCESS && out != NULL ) {
3780 		if ( out->bv_len > bv.bv_len ) {
3781 			out->bv_val[ bv.bv_len ] = '\0';
3782 		}
3783 		out->bv_len = bv.bv_len;
3784 	}
3785 
3786 	return rc != LDAP_SUCCESS;
3787 }
3788 
3789 static int
3790 issuerAndThisUpdateCheck(
3791 	struct berval *in,
3792 	struct berval *is,
3793 	struct berval *tu,
3794 	void *ctx )
3795 {
3796 	int numdquotes = 0;
3797 	struct berval x = *in;
3798 	struct berval ni = BER_BVNULL;
3799 	/* Parse GSER format */
3800 	enum {
3801 		HAVE_NONE = 0x0,
3802 		HAVE_ISSUER = 0x1,
3803 		HAVE_THISUPDATE = 0x2,
3804 		HAVE_ALL = ( HAVE_ISSUER | HAVE_THISUPDATE )
3805 	} have = HAVE_NONE;
3806 
3807 
3808 	if ( in->bv_len < STRLENOF( "{issuer \"\",thisUpdate \"YYMMDDhhmmssZ\"}" ) ) return LDAP_INVALID_SYNTAX;
3809 
3810 	if ( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
3811 		return LDAP_INVALID_SYNTAX;
3812 	}
3813 
3814 	x.bv_val++;
3815 	x.bv_len -= STRLENOF("{}");
3816 
3817 	do {
3818 		/* eat leading spaces */
3819 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3820 			/* empty */;
3821 		}
3822 
3823 		/* should be at issuer or thisUpdate */
3824 		if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
3825 			if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX;
3826 
3827 			/* parse issuer */
3828 			x.bv_val += STRLENOF("issuer");
3829 			x.bv_len -= STRLENOF("issuer");
3830 
3831 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3832 			x.bv_val++;
3833 			x.bv_len--;
3834 
3835 			/* eat leading spaces */
3836 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3837 				/* empty */;
3838 			}
3839 
3840 			/* For backward compatibility, this part is optional */
3841 			if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) != 0 ) {
3842 				return LDAP_INVALID_SYNTAX;
3843 			}
3844 			x.bv_val += STRLENOF("rdnSequence:");
3845 			x.bv_len -= STRLENOF("rdnSequence:");
3846 
3847 			if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3848 			x.bv_val++;
3849 			x.bv_len--;
3850 
3851 			is->bv_val = x.bv_val;
3852 			is->bv_len = 0;
3853 
3854 			for ( ; is->bv_len < x.bv_len; ) {
3855 				if ( is->bv_val[is->bv_len] != '"' ) {
3856 					is->bv_len++;
3857 					continue;
3858 				}
3859 				if ( is->bv_val[is->bv_len+1] == '"' ) {
3860 					/* double dquote */
3861 					numdquotes++;
3862 					is->bv_len += 2;
3863 					continue;
3864 				}
3865 				break;
3866 			}
3867 			x.bv_val += is->bv_len + 1;
3868 			x.bv_len -= is->bv_len + 1;
3869 
3870 			have |= HAVE_ISSUER;
3871 
3872 		} else if ( strncasecmp( x.bv_val, "thisUpdate", STRLENOF("thisUpdate") ) == 0 )
3873 		{
3874 			if ( have & HAVE_THISUPDATE ) return LDAP_INVALID_SYNTAX;
3875 
3876 			/* parse thisUpdate */
3877 			x.bv_val += STRLENOF("thisUpdate");
3878 			x.bv_len -= STRLENOF("thisUpdate");
3879 
3880 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3881 			x.bv_val++;
3882 			x.bv_len--;
3883 
3884 			/* eat leading spaces */
3885 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3886 				/* empty */;
3887 			}
3888 
3889 			if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3890 			x.bv_val++;
3891 			x.bv_len--;
3892 
3893 			tu->bv_val = x.bv_val;
3894 			tu->bv_len = 0;
3895 
3896 			for ( ; tu->bv_len < x.bv_len; tu->bv_len++ ) {
3897 				if ( tu->bv_val[tu->bv_len] == '"' ) {
3898 					break;
3899 				}
3900 			}
3901 			x.bv_val += tu->bv_len + 1;
3902 			x.bv_len -= tu->bv_len + 1;
3903 
3904 			have |= HAVE_THISUPDATE;
3905 
3906 		} else {
3907 			return LDAP_INVALID_SYNTAX;
3908 		}
3909 
3910 		/* eat leading spaces */
3911 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
3912 			/* empty */;
3913 		}
3914 
3915 		if ( have == HAVE_ALL ) {
3916 			break;
3917 		}
3918 
3919 		if ( x.bv_val[0] != ',' ) {
3920 			return LDAP_INVALID_SYNTAX;
3921 		}
3922 
3923 		x.bv_val++;
3924 		x.bv_len--;
3925 	} while ( 1 );
3926 
3927 	/* should have no characters left... */
3928 	if ( x.bv_len ) return LDAP_INVALID_SYNTAX;
3929 
3930 	if ( numdquotes == 0 ) {
3931 		ber_dupbv_x( &ni, is, ctx );
3932 
3933 	} else {
3934 		ber_len_t src, dst;
3935 
3936 		ni.bv_len = is->bv_len - numdquotes;
3937 		ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx );
3938 		for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
3939 			if ( is->bv_val[src] == '"' ) {
3940 				src++;
3941 			}
3942 			ni.bv_val[dst] = is->bv_val[src];
3943 		}
3944 		ni.bv_val[dst] = '\0';
3945 	}
3946 
3947 	*is = ni;
3948 
3949 	return 0;
3950 }
3951 
3952 static int
3953 issuerAndThisUpdateValidate(
3954 	Syntax *syntax,
3955 	struct berval *in )
3956 {
3957 	int rc;
3958 	struct berval i, tu;
3959 
3960 	Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateValidate: <%s>\n",
3961 		in->bv_val, 0, 0 );
3962 
3963 	rc = issuerAndThisUpdateCheck( in, &i, &tu, NULL );
3964 	if ( rc ) {
3965 		goto done;
3966 	}
3967 
3968 	/* validate DN -- doesn't handle double dquote */
3969 	rc = dnValidate( NULL, &i );
3970 	if ( rc ) {
3971 		rc = LDAP_INVALID_SYNTAX;
3972 
3973 	} else if ( checkTime( &tu, NULL ) ) {
3974 		rc = LDAP_INVALID_SYNTAX;
3975 	}
3976 
3977 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3978 		slap_sl_free( i.bv_val, NULL );
3979 	}
3980 
3981 	Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateValidate: <%s> err=%d\n",
3982 		in->bv_val, rc, 0 );
3983 
3984 done:;
3985 	return rc;
3986 }
3987 
3988 static int
3989 issuerAndThisUpdatePretty(
3990 	Syntax *syntax,
3991 	struct berval *in,
3992 	struct berval *out,
3993 	void *ctx )
3994 {
3995 	int rc;
3996 	struct berval i, tu, ni = BER_BVNULL;
3997 	char *p;
3998 
3999 	assert( in != NULL );
4000 	assert( out != NULL );
4001 
4002 	BER_BVZERO( out );
4003 
4004 	Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdatePretty: <%s>\n",
4005 		in->bv_val, 0, 0 );
4006 
4007 	rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx );
4008 	if ( rc ) {
4009 		goto done;
4010 	}
4011 
4012 	rc = dnPretty( syntax, &i, &ni, ctx );
4013 
4014 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4015 		slap_sl_free( i.bv_val, ctx );
4016 	}
4017 
4018 	if ( rc || checkTime( &tu, NULL ) ) {
4019 		rc = LDAP_INVALID_SYNTAX;
4020 		goto done;
4021 	}
4022 
4023 	/* make room */
4024 	out->bv_len = STRLENOF("{ issuer rdnSequence:\"\", thisUpdate \"\" }")
4025 		+ ni.bv_len + tu.bv_len;
4026 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4027 
4028 	if ( out->bv_val == NULL ) {
4029 		out->bv_len = 0;
4030 		rc = LDAP_OTHER;
4031 		goto done;
4032 	}
4033 
4034 	p = out->bv_val;
4035 	p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
4036 	p = lutil_strbvcopy( p, &ni );
4037 	p = lutil_strcopy( p, "\", thisUpdate \"" );
4038 	p = lutil_strbvcopy( p, &tu );
4039 	p = lutil_strcopy( p, /*{*/ "\" }" );
4040 
4041 	assert( p == &out->bv_val[out->bv_len] );
4042 
4043 done:;
4044 	Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdatePretty: <%s> => <%s>\n",
4045 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4046 
4047 	slap_sl_free( ni.bv_val, ctx );
4048 
4049 	return rc;
4050 }
4051 
4052 static int
4053 issuerAndThisUpdateNormalize(
4054 	slap_mask_t usage,
4055 	Syntax *syntax,
4056 	MatchingRule *mr,
4057 	struct berval *in,
4058 	struct berval *out,
4059 	void *ctx )
4060 {
4061 	struct berval i, ni, tu, tu2;
4062 	char sbuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
4063 	char *p;
4064 	int rc;
4065 
4066 	assert( in != NULL );
4067 	assert( out != NULL );
4068 
4069 	Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateNormalize: <%s>\n",
4070 		in->bv_val, 0, 0 );
4071 
4072 	rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx );
4073 	if ( rc ) {
4074 		return rc;
4075 	}
4076 
4077 	rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
4078 
4079 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4080 		slap_sl_free( i.bv_val, ctx );
4081 	}
4082 
4083 	tu2.bv_val = sbuf;
4084 	tu2.bv_len = sizeof( sbuf );
4085 	if ( rc || checkTime( &tu, &tu2 ) ) {
4086 		return LDAP_INVALID_SYNTAX;
4087 	}
4088 
4089 	out->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
4090 		+ ni.bv_len + tu2.bv_len;
4091 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4092 
4093 	if ( out->bv_val == NULL ) {
4094 		out->bv_len = 0;
4095 		rc = LDAP_OTHER;
4096 		goto func_leave;
4097 	}
4098 
4099 	p = out->bv_val;
4100 
4101 	p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
4102 	p = lutil_strbvcopy( p, &ni );
4103 	p = lutil_strcopy( p, "\", thisUpdate \"" );
4104 	p = lutil_strbvcopy( p, &tu2 );
4105 	p = lutil_strcopy( p, /*{*/ "\" }" );
4106 
4107 	assert( p == &out->bv_val[out->bv_len] );
4108 
4109 func_leave:
4110 	Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateNormalize: <%s> => <%s>\n",
4111 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4112 
4113 	slap_sl_free( ni.bv_val, ctx );
4114 
4115 	return rc;
4116 }
4117 
4118 static int
4119 certificateListExactNormalize(
4120 	slap_mask_t usage,
4121 	Syntax *syntax,
4122 	MatchingRule *mr,
4123 	struct berval *val,
4124 	struct berval *normalized,
4125 	void *ctx )
4126 {
4127 	BerElementBuffer berbuf;
4128 	BerElement *ber = (BerElement *)&berbuf;
4129 	ber_tag_t tag;
4130 	ber_len_t len;
4131 	ber_int_t version;
4132 	struct berval issuer_dn = BER_BVNULL, bvdn,
4133 		thisUpdate, bvtu;
4134 	char *p, tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
4135 	int rc = LDAP_INVALID_SYNTAX;
4136 
4137 	assert( val != NULL );
4138 
4139 	Debug( LDAP_DEBUG_TRACE, ">>> certificateListExactNormalize: <%p, %lu>\n",
4140 		val->bv_val, val->bv_len, 0 );
4141 
4142 	if ( BER_BVISEMPTY( val ) ) goto done;
4143 
4144 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
4145 		return issuerAndThisUpdateNormalize( 0, NULL, NULL, val, normalized, ctx );
4146 	}
4147 
4148 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
4149 
4150 	ber_init2( ber, val, LBER_USE_DER );
4151 	tag = ber_skip_tag( ber, &len );	/* Signed wrapper */
4152 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4153 	tag = ber_skip_tag( ber, &len );	/* Sequence */
4154 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4155 	tag = ber_peek_tag( ber, &len );
4156 	/* Optional version */
4157 	if ( tag == LBER_INTEGER ) {
4158 		tag = ber_get_int( ber, &version );
4159 		assert( tag == LBER_INTEGER );
4160 		if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
4161 	}
4162 	tag = ber_skip_tag( ber, &len );	/* Signature Algorithm */
4163 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4164 	ber_skip_data( ber, len );
4165 
4166 	tag = ber_peek_tag( ber, &len );	/* IssuerDN */
4167 	if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
4168 	len = ber_ptrlen( ber );
4169 	bvdn.bv_val = val->bv_val + len;
4170 	bvdn.bv_len = val->bv_len - len;
4171 	tag = ber_skip_tag( ber, &len );
4172 	ber_skip_data( ber, len );
4173 
4174 	tag = ber_skip_tag( ber, &len );	/* thisUpdate */
4175 	/* Time is a CHOICE { UTCTime, GeneralizedTime } */
4176 	if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX;
4177 	bvtu.bv_val = (char *)ber->ber_ptr;
4178 	bvtu.bv_len = len;
4179 
4180 	rc = dnX509normalize( &bvdn, &issuer_dn );
4181 	if ( rc != LDAP_SUCCESS ) {
4182 		rc = LDAP_INVALID_SYNTAX;
4183 		goto done;
4184 	}
4185 
4186 	thisUpdate.bv_val = tubuf;
4187 	thisUpdate.bv_len = sizeof(tubuf);
4188 	if ( checkTime( &bvtu, &thisUpdate ) ) {
4189 		rc = LDAP_INVALID_SYNTAX;
4190 		goto done;
4191 	}
4192 
4193 	normalized->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
4194 		+ issuer_dn.bv_len + thisUpdate.bv_len;
4195 	normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
4196 
4197 	p = normalized->bv_val;
4198 
4199 	p = lutil_strcopy( p, "{ issuer rdnSequence:\"" );
4200 	p = lutil_strbvcopy( p, &issuer_dn );
4201 	p = lutil_strcopy( p, "\", thisUpdate \"" );
4202 	p = lutil_strbvcopy( p, &thisUpdate );
4203 	p = lutil_strcopy( p, /*{*/ "\" }" );
4204 
4205 	rc = LDAP_SUCCESS;
4206 
4207 done:
4208 	Debug( LDAP_DEBUG_TRACE, "<<< certificateListExactNormalize: <%p, %lu> => <%s>\n",
4209 		val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
4210 
4211 	if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
4212 
4213 	return rc;
4214 }
4215 
4216 /* X.509 PMI serialNumberAndIssuerSerialCheck
4217 
4218 AttributeCertificateExactAssertion     ::= SEQUENCE {
4219    serialNumber              CertificateSerialNumber,
4220    issuer                    AttCertIssuer }
4221 
4222 CertificateSerialNumber ::= INTEGER
4223 
4224 AttCertIssuer ::=    [0] SEQUENCE {
4225 issuerName                     GeneralNames OPTIONAL,
4226 baseCertificateID         [0] IssuerSerial OPTIONAL,
4227 objectDigestInfo          [1] ObjectDigestInfo OPTIONAL }
4228 -- At least one component shall be present
4229 
4230 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
4231 
4232 GeneralName ::= CHOICE {
4233   otherName                 [0] INSTANCE OF OTHER-NAME,
4234   rfc822Name                [1] IA5String,
4235   dNSName                   [2] IA5String,
4236   x400Address               [3] ORAddress,
4237   directoryName             [4] Name,
4238   ediPartyName              [5] EDIPartyName,
4239   uniformResourceIdentifier [6] IA5String,
4240   iPAddress                 [7] OCTET STRING,
4241   registeredID              [8] OBJECT IDENTIFIER }
4242 
4243 IssuerSerial ::= SEQUENCE {
4244    issuer       GeneralNames,
4245    serial       CertificateSerialNumber,
4246    issuerUID UniqueIdentifier OPTIONAL }
4247 
4248 ObjectDigestInfo ::= SEQUENCE {
4249    digestedObjectType ENUMERATED {
4250       publicKey           (0),
4251       publicKeyCert       (1),
4252       otherObjectTypes    (2) },
4253    otherObjectTypeID      OBJECT IDENTIFIER OPTIONAL,
4254    digestAlgorithm        AlgorithmIdentifier,
4255    objectDigest           BIT STRING }
4256 
4257  * The way I interpret it, an assertion should look like
4258 
4259  { serialNumber 'dd'H,
4260    issuer { issuerName { directoryName:rdnSequence:"cn=yyy" }, -- optional
4261             baseCertificateID { serial '1d'H,
4262                                 issuer { directoryName:rdnSequence:"cn=zzz" },
4263                                 issuerUID <value>              -- optional
4264                               },                               -- optional
4265             objectDigestInfo { ... }                           -- optional
4266           }
4267  }
4268 
4269  * with issuerName, baseCertificateID and objectDigestInfo optional,
4270  * at least one present; the way it's currently implemented, it is
4271 
4272  { serialNumber 'dd'H,
4273    issuer { baseCertificateID { serial '1d'H,
4274                                 issuer { directoryName:rdnSequence:"cn=zzz" }
4275                               }
4276           }
4277  }
4278 
4279  * with all the above parts mandatory.
4280  */
4281 static int
4282 serialNumberAndIssuerSerialCheck(
4283 	struct berval *in,
4284 	struct berval *sn,
4285 	struct berval *is,
4286 	struct berval *i_sn,	/* contain serial of baseCertificateID */
4287 	void *ctx )
4288 {
4289 	/* Parse GSER format */
4290 	enum {
4291 		HAVE_NONE = 0x0,
4292 		HAVE_SN = 0x1,
4293 		HAVE_ISSUER = 0x2,
4294 		HAVE_ALL = ( HAVE_SN | HAVE_ISSUER )
4295 	} have = HAVE_NONE, have2 = HAVE_NONE;
4296 	int numdquotes = 0;
4297 	struct berval x = *in;
4298 	struct berval ni;
4299 
4300 	if ( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
4301 
4302 	/* no old format */
4303 	if ( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) return LDAP_INVALID_SYNTAX;
4304 
4305 	x.bv_val++;
4306 	x.bv_len -= 2;
4307 
4308 	do {
4309 
4310 		/* eat leading spaces */
4311 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4312 			/* empty */;
4313 		}
4314 
4315 		/* should be at issuer or serialNumber NamedValue */
4316 		if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
4317 			if ( have & HAVE_ISSUER ) {
4318 				return LDAP_INVALID_SYNTAX;
4319 			}
4320 
4321 			/* parse IssuerSerial */
4322 			x.bv_val += STRLENOF("issuer");
4323 			x.bv_len -= STRLENOF("issuer");
4324 
4325 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
4326 			x.bv_val++;
4327 			x.bv_len--;
4328 
4329 			/* eat leading spaces */
4330 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4331 				/* empty */;
4332 			}
4333 
4334 			if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
4335 			x.bv_val++;
4336 			x.bv_len--;
4337 
4338 			/* eat leading spaces */
4339 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4340 				/* empty */;
4341 			}
4342 
4343 			if ( strncasecmp( x.bv_val, "baseCertificateID ", STRLENOF("baseCertificateID ") ) != 0 ) {
4344 				return LDAP_INVALID_SYNTAX;
4345 			}
4346 			x.bv_val += STRLENOF("baseCertificateID ");
4347 			x.bv_len -= STRLENOF("baseCertificateID ");
4348 
4349 			/* eat leading spaces */
4350 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4351 				/* empty */;
4352 			}
4353 
4354 			if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
4355 			x.bv_val++;
4356 			x.bv_len--;
4357 
4358 			do {
4359 				/* eat leading spaces */
4360 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4361 					/* empty */;
4362 				}
4363 
4364 				/* parse issuer of baseCertificateID */
4365 				if ( strncasecmp( x.bv_val, "issuer ", STRLENOF("issuer ") ) == 0 ) {
4366 					if ( have2 & HAVE_ISSUER ) {
4367 						return LDAP_INVALID_SYNTAX;
4368 					}
4369 
4370 					x.bv_val += STRLENOF("issuer ");
4371 					x.bv_len -= STRLENOF("issuer ");
4372 
4373 					/* eat leading spaces */
4374 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4375 						/* empty */;
4376 					}
4377 
4378 					if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
4379 					x.bv_val++;
4380 					x.bv_len--;
4381 
4382 					/* eat leading spaces */
4383 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4384 						/* empty */;
4385 					}
4386 
4387 					if ( strncasecmp( x.bv_val, "directoryName:rdnSequence:", STRLENOF("directoryName:rdnSequence:") ) != 0 ) {
4388 						return LDAP_INVALID_SYNTAX;
4389 					}
4390 					x.bv_val += STRLENOF("directoryName:rdnSequence:");
4391 					x.bv_len -= STRLENOF("directoryName:rdnSequence:");
4392 
4393 					if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
4394 					x.bv_val++;
4395 					x.bv_len--;
4396 
4397 					is->bv_val = x.bv_val;
4398 					is->bv_len = 0;
4399 
4400 					for ( ; is->bv_len < x.bv_len; ) {
4401 						if ( is->bv_val[is->bv_len] != '"' ) {
4402 							is->bv_len++;
4403 							continue;
4404 						}
4405 						if ( is->bv_val[is->bv_len + 1] == '"' ) {
4406 							/* double dquote */
4407 							numdquotes++;
4408 							is->bv_len += 2;
4409 							continue;
4410 						}
4411 						break;
4412 					}
4413 					x.bv_val += is->bv_len + 1;
4414 					x.bv_len -= is->bv_len + 1;
4415 
4416 					/* eat leading spaces */
4417 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4418 						/* empty */;
4419 					}
4420 
4421 					if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
4422 					x.bv_val++;
4423 					x.bv_len--;
4424 
4425 					have2 |= HAVE_ISSUER;
4426 
4427 				} else if ( strncasecmp( x.bv_val, "serial ", STRLENOF("serial ") ) == 0 ) {
4428 					if ( have2 & HAVE_SN ) {
4429 						return LDAP_INVALID_SYNTAX;
4430 					}
4431 
4432 					x.bv_val += STRLENOF("serial ");
4433 					x.bv_len -= STRLENOF("serial ");
4434 
4435 					/* eat leading spaces */
4436 					for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
4437 						/* empty */;
4438 					}
4439 
4440 					if ( checkNum( &x, i_sn ) ) {
4441 						return LDAP_INVALID_SYNTAX;
4442 					}
4443 
4444 					x.bv_val += i_sn->bv_len;
4445 					x.bv_len -= i_sn->bv_len;
4446 
4447 					have2 |= HAVE_SN;
4448 
4449 				} else {
4450 					return LDAP_INVALID_SYNTAX;
4451 				}
4452 
4453 				/* eat leading spaces */
4454 				for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4455 					/* empty */;
4456 				}
4457 
4458 				if ( have2 == HAVE_ALL ) {
4459 					break;
4460 				}
4461 
4462 				if ( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX;
4463 				x.bv_val++;
4464 				x.bv_len--;
4465 			} while ( 1 );
4466 
4467 			if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
4468 			x.bv_val++;
4469 			x.bv_len--;
4470 
4471 			/* eat leading spaces */
4472 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4473 				/* empty */;
4474 			}
4475 
4476 			if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
4477 			x.bv_val++;
4478 			x.bv_len--;
4479 
4480 			have |= HAVE_ISSUER;
4481 
4482 		} else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) {
4483 			if ( have & HAVE_SN ) {
4484 				return LDAP_INVALID_SYNTAX;
4485 			}
4486 
4487 			/* parse serialNumber */
4488 			x.bv_val += STRLENOF("serialNumber");
4489 			x.bv_len -= STRLENOF("serialNumber");
4490 
4491 			if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
4492 			x.bv_val++;
4493 			x.bv_len--;
4494 
4495 			/* eat leading spaces */
4496 			for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4497 				/* empty */;
4498 			}
4499 
4500 			if ( checkNum( &x, sn ) ) {
4501 				return LDAP_INVALID_SYNTAX;
4502 			}
4503 
4504 			x.bv_val += sn->bv_len;
4505 			x.bv_len -= sn->bv_len;
4506 
4507 			have |= HAVE_SN;
4508 
4509 		} else {
4510 			return LDAP_INVALID_SYNTAX;
4511 		}
4512 
4513 		/* eat spaces */
4514 		for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
4515 			/* empty */;
4516 		}
4517 
4518 		if ( have == HAVE_ALL ) {
4519 			break;
4520 		}
4521 
4522 		if ( x.bv_val[0] != ',' ) {
4523 			return LDAP_INVALID_SYNTAX;
4524 		}
4525 		x.bv_val++ ;
4526 		x.bv_len--;
4527 	} while ( 1 );
4528 
4529 	/* should have no characters left... */
4530 	if( x.bv_len ) return LDAP_INVALID_SYNTAX;
4531 
4532 	if ( numdquotes == 0 ) {
4533 		ber_dupbv_x( &ni, is, ctx );
4534 
4535 	} else {
4536 		ber_len_t src, dst;
4537 
4538 		ni.bv_len = is->bv_len - numdquotes;
4539 		ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx );
4540 		for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
4541 			if ( is->bv_val[src] == '"' ) {
4542 				src++;
4543 			}
4544 			ni.bv_val[dst] = is->bv_val[src];
4545 		}
4546 		ni.bv_val[dst] = '\0';
4547 	}
4548 
4549 	*is = ni;
4550 
4551 	/* need to handle double dquotes here */
4552 	return 0;
4553 }
4554 
4555 /* X.509 PMI serialNumberAndIssuerSerialValidate */
4556 static int
4557 serialNumberAndIssuerSerialValidate(
4558 	Syntax *syntax,
4559 	struct berval *in )
4560 {
4561 	int rc;
4562 	struct berval sn, i, i_sn;
4563 
4564 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialValidate: <%s>\n",
4565 		in->bv_val, 0, 0 );
4566 
4567 	rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, NULL );
4568 	if ( rc ) {
4569 		goto done;
4570 	}
4571 
4572 	/* validate DN -- doesn't handle double dquote */
4573 	rc = dnValidate( NULL, &i );
4574 	if ( rc ) {
4575 		rc = LDAP_INVALID_SYNTAX;
4576 	}
4577 
4578 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4579 		slap_sl_free( i.bv_val, NULL );
4580 	}
4581 
4582 done:;
4583 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialValidate: <%s> err=%d\n",
4584 		in->bv_val, rc, 0 );
4585 
4586 	return rc;
4587 }
4588 
4589 /* X.509 PMI serialNumberAndIssuerSerialPretty */
4590 static int
4591 serialNumberAndIssuerSerialPretty(
4592 	Syntax *syntax,
4593 	struct berval *in,
4594 	struct berval *out,
4595 	void *ctx )
4596 {
4597 	struct berval sn, i, i_sn, ni = BER_BVNULL;
4598 	char *p;
4599 	int rc;
4600 
4601 	assert( in != NULL );
4602 	assert( out != NULL );
4603 
4604 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialPretty: <%s>\n",
4605 		in->bv_val, 0, 0 );
4606 
4607 	rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx );
4608 	if ( rc ) {
4609 		goto done;
4610 	}
4611 
4612 	rc = dnPretty( syntax, &i, &ni, ctx );
4613 
4614 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4615 		slap_sl_free( i.bv_val, ctx );
4616 	}
4617 
4618 	if ( rc ) {
4619 		rc = LDAP_INVALID_SYNTAX;
4620 		goto done;
4621 	}
4622 
4623 	/* make room from sn + "$" */
4624 	out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial  } } }")
4625 		+ sn.bv_len + ni.bv_len + i_sn.bv_len;
4626 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4627 
4628 	if ( out->bv_val == NULL ) {
4629 		out->bv_len = 0;
4630 		rc = LDAP_OTHER;
4631 		goto done;
4632 	}
4633 
4634 	p = out->bv_val;
4635 	p = lutil_strcopy( p, "{ serialNumber " );
4636 	p = lutil_strbvcopy( p, &sn );
4637 	p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
4638 	p = lutil_strbvcopy( p, &ni );
4639 	p = lutil_strcopy( p, "\" }, serial " );
4640 	p = lutil_strbvcopy( p, &i_sn );
4641 	p = lutil_strcopy( p, " } } }" );
4642 
4643 	assert( p == &out->bv_val[out->bv_len] );
4644 
4645 done:;
4646 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialPretty: <%s> => <%s>\n",
4647 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4648 
4649 	slap_sl_free( ni.bv_val, ctx );
4650 
4651 	return rc;
4652 }
4653 
4654 /* X.509 PMI serialNumberAndIssuerSerialNormalize */
4655 /*
4656  * This routine is called by attributeCertificateExactNormalize
4657  * when attributeCertificateExactNormalize receives a search
4658  * string instead of a attribute certificate. This routine
4659  * checks if the search value is valid and then returns the
4660  * normalized value
4661  */
4662 static int
4663 serialNumberAndIssuerSerialNormalize(
4664 	slap_mask_t usage,
4665 	Syntax *syntax,
4666 	MatchingRule *mr,
4667 	struct berval *in,
4668 	struct berval *out,
4669 	void *ctx )
4670 {
4671 	struct berval i, ni = BER_BVNULL,
4672 		sn, sn2 = BER_BVNULL, sn3 = BER_BVNULL,
4673 		i_sn, i_sn2 = BER_BVNULL, i_sn3 = BER_BVNULL;
4674 	char sbuf2[SLAP_SN_BUFLEN], i_sbuf2[SLAP_SN_BUFLEN],
4675 		sbuf3[SLAP_SN_BUFLEN], i_sbuf3[SLAP_SN_BUFLEN];
4676 	char *p;
4677 	int rc;
4678 
4679 	assert( in != NULL );
4680 	assert( out != NULL );
4681 
4682 	Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialNormalize: <%s>\n",
4683 		in->bv_val, 0, 0 );
4684 
4685 	rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx );
4686 	if ( rc ) {
4687 		goto func_leave;
4688 	}
4689 
4690 	rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
4691 
4692 	if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
4693 		slap_sl_free( i.bv_val, ctx );
4694 	}
4695 
4696 	if ( rc ) {
4697 		rc = LDAP_INVALID_SYNTAX;
4698 		goto func_leave;
4699 	}
4700 
4701 	/* Convert sn to canonical hex */
4702 	sn2.bv_val = sbuf2;
4703 	sn2.bv_len = sn.bv_len;
4704 	if ( sn.bv_len > sizeof( sbuf2 ) ) {
4705 		sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
4706 	}
4707 	if ( lutil_str2bin( &sn, &sn2, ctx ) ) {
4708 		rc = LDAP_INVALID_SYNTAX;
4709 		goto func_leave;
4710 	}
4711 
4712         /* Convert i_sn to canonical hex */
4713 	i_sn2.bv_val = i_sbuf2;
4714 	i_sn2.bv_len = i_sn.bv_len;
4715 	if ( i_sn.bv_len > sizeof( i_sbuf2 ) ) {
4716 		i_sn2.bv_val = slap_sl_malloc( i_sn.bv_len, ctx );
4717 	}
4718 	if ( lutil_str2bin( &i_sn, &i_sn2, ctx ) ) {
4719 		rc = LDAP_INVALID_SYNTAX;
4720 		goto func_leave;
4721 	}
4722 
4723 	sn3.bv_val = sbuf3;
4724 	sn3.bv_len = sizeof(sbuf3);
4725 	if ( slap_bin2hex( &sn2, &sn3, ctx ) ) {
4726 		rc = LDAP_INVALID_SYNTAX;
4727 		goto func_leave;
4728 	}
4729 
4730 	i_sn3.bv_val = i_sbuf3;
4731 	i_sn3.bv_len = sizeof(i_sbuf3);
4732 	if ( slap_bin2hex( &i_sn2, &i_sn3, ctx ) ) {
4733 		rc = LDAP_INVALID_SYNTAX;
4734 		goto func_leave;
4735 	}
4736 
4737 	out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial  } } }")
4738 		+ sn3.bv_len + ni.bv_len + i_sn3.bv_len;
4739 	out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
4740 
4741 	if ( out->bv_val == NULL ) {
4742 		out->bv_len = 0;
4743 		rc = LDAP_OTHER;
4744 		goto func_leave;
4745 	}
4746 
4747 	p = out->bv_val;
4748 
4749 	p = lutil_strcopy( p, "{ serialNumber " );
4750 	p = lutil_strbvcopy( p, &sn3 );
4751 	p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
4752 	p = lutil_strbvcopy( p, &ni );
4753 	p = lutil_strcopy( p, "\" }, serial " );
4754 	p = lutil_strbvcopy( p, &i_sn3 );
4755 	p = lutil_strcopy( p, " } } }" );
4756 
4757 	assert( p == &out->bv_val[out->bv_len] );
4758 
4759 func_leave:
4760 	Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialNormalize: <%s> => <%s>\n",
4761 		in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
4762 
4763 	if ( sn2.bv_val != sbuf2 ) {
4764 		slap_sl_free( sn2.bv_val, ctx );
4765 	}
4766 
4767 	if ( i_sn2.bv_val != i_sbuf2 ) {
4768 		slap_sl_free( i_sn2.bv_val, ctx );
4769 	}
4770 
4771 	if ( sn3.bv_val != sbuf3 ) {
4772 		slap_sl_free( sn3.bv_val, ctx );
4773 	}
4774 
4775 	if ( i_sn3.bv_val != i_sbuf3 ) {
4776 		slap_sl_free( i_sn3.bv_val, ctx );
4777 	}
4778 
4779 	slap_sl_free( ni.bv_val, ctx );
4780 
4781 	return rc;
4782 }
4783 
4784 /* X.509 PMI attributeCertificateExactNormalize */
4785 static int
4786 attributeCertificateExactNormalize(
4787 	slap_mask_t usage,
4788 	Syntax *syntax,
4789 	MatchingRule *mr,
4790 	struct berval *val,
4791 	struct berval *normalized,
4792 	void *ctx )
4793 {
4794 	BerElementBuffer berbuf;
4795 	BerElement *ber = (BerElement *)&berbuf;
4796 	ber_tag_t tag;
4797 	ber_len_t len;
4798 	char issuer_serialbuf[SLAP_SN_BUFLEN], serialbuf[SLAP_SN_BUFLEN];
4799 	struct berval sn, i_sn, sn2 = BER_BVNULL, i_sn2 = BER_BVNULL;
4800 	struct berval issuer_dn = BER_BVNULL, bvdn;
4801 	char *p;
4802 	int rc = LDAP_INVALID_SYNTAX;
4803 
4804 	if ( BER_BVISEMPTY( val ) ) {
4805 		return rc;
4806 	}
4807 
4808 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
4809 		return serialNumberAndIssuerSerialNormalize( 0, NULL, NULL, val, normalized, ctx );
4810 	}
4811 
4812 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
4813 
4814 	ber_init2( ber, val, LBER_USE_DER );
4815 	tag = ber_skip_tag( ber, &len );	/* Signed Sequence */
4816 	tag = ber_skip_tag( ber, &len );	/* Sequence */
4817 	tag = ber_skip_tag( ber, &len );	/* (Mandatory) version; must be v2(1) */
4818 	ber_skip_data( ber, len );
4819 	tag = ber_skip_tag( ber, &len );	/* Holder Sequence */
4820 	ber_skip_data( ber, len );
4821 
4822 	/* Issuer */
4823 	tag = ber_skip_tag( ber, &len );	/* Sequence */
4824 						/* issuerName (GeneralNames sequence; optional)? */
4825 	tag = ber_skip_tag( ber, &len );	/* baseCertificateID (sequence; optional)? */
4826 	tag = ber_skip_tag( ber, &len );	/* GeneralNames (sequence) */
4827 	tag = ber_skip_tag( ber, &len );	/* directoryName (we only accept this form of GeneralName) */
4828 	if ( tag != SLAP_X509_GN_DIRECTORYNAME ) {
4829 		return LDAP_INVALID_SYNTAX;
4830 	}
4831 	tag = ber_peek_tag( ber, &len );	/* sequence of RDN */
4832 	len = ber_ptrlen( ber );
4833 	bvdn.bv_val = val->bv_val + len;
4834 	bvdn.bv_len = val->bv_len - len;
4835 	rc = dnX509normalize( &bvdn, &issuer_dn );
4836 	if ( rc != LDAP_SUCCESS ) {
4837 		rc = LDAP_INVALID_SYNTAX;
4838 		goto done;
4839 	}
4840 
4841 	tag = ber_skip_tag( ber, &len );	/* sequence of RDN */
4842 	ber_skip_data( ber, len );
4843 	tag = ber_skip_tag( ber, &len );	/* serial number */
4844 	if ( tag != LBER_INTEGER ) {
4845 		rc = LDAP_INVALID_SYNTAX;
4846 		goto done;
4847 	}
4848 	i_sn.bv_val = (char *)ber->ber_ptr;
4849 	i_sn.bv_len = len;
4850 	i_sn2.bv_val = issuer_serialbuf;
4851 	i_sn2.bv_len = sizeof(issuer_serialbuf);
4852 	if ( slap_bin2hex( &i_sn, &i_sn2, ctx ) ) {
4853 		rc = LDAP_INVALID_SYNTAX;
4854 		goto done;
4855 	}
4856 	ber_skip_data( ber, len );
4857 
4858 						/* issuerUID (bitstring; optional)? */
4859 						/* objectDigestInfo (sequence; optional)? */
4860 
4861 	tag = ber_skip_tag( ber, &len );	/* Signature (sequence) */
4862 	ber_skip_data( ber, len );
4863 	tag = ber_skip_tag( ber, &len );	/* serial number */
4864 	if ( tag != LBER_INTEGER ) {
4865 		rc = LDAP_INVALID_SYNTAX;
4866 		goto done;
4867 	}
4868 	sn.bv_val = (char *)ber->ber_ptr;
4869 	sn.bv_len = len;
4870 	sn2.bv_val = serialbuf;
4871 	sn2.bv_len = sizeof(serialbuf);
4872 	if ( slap_bin2hex( &sn, &sn2, ctx ) ) {
4873 		rc = LDAP_INVALID_SYNTAX;
4874 		goto done;
4875 	}
4876 	ber_skip_data( ber, len );
4877 
4878 	normalized->bv_len = STRLENOF( "{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial  } } }" )
4879 		+ sn2.bv_len + issuer_dn.bv_len + i_sn2.bv_len;
4880 	normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
4881 
4882 	p = normalized->bv_val;
4883 
4884 	p = lutil_strcopy( p, "{ serialNumber " );
4885 	p = lutil_strbvcopy( p, &sn2 );
4886 	p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
4887 	p = lutil_strbvcopy( p, &issuer_dn );
4888 	p = lutil_strcopy( p, "\" }, serial " );
4889 	p = lutil_strbvcopy( p, &i_sn2 );
4890 	p = lutil_strcopy( p, " } } }" );
4891 
4892 	Debug( LDAP_DEBUG_TRACE, "attributeCertificateExactNormalize: %s\n",
4893 		normalized->bv_val, NULL, NULL );
4894 
4895 	rc = LDAP_SUCCESS;
4896 
4897 done:
4898 	if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
4899 	if ( i_sn2.bv_val != issuer_serialbuf ) ber_memfree_x( i_sn2.bv_val, ctx );
4900 	if ( sn2.bv_val != serialbuf ) ber_memfree_x( sn2.bv_val, ctx );
4901 
4902 	return rc;
4903 }
4904 
4905 
4906 static int
4907 hexValidate(
4908 	Syntax *syntax,
4909 	struct berval *in )
4910 {
4911 	ber_len_t	i;
4912 
4913 	assert( in != NULL );
4914 	assert( !BER_BVISNULL( in ) );
4915 
4916 	for ( i = 0; i < in->bv_len; i++ ) {
4917 		if ( !ASCII_HEX( in->bv_val[ i ] ) ) {
4918 			return LDAP_INVALID_SYNTAX;
4919 		}
4920 	}
4921 
4922 	return LDAP_SUCCESS;
4923 }
4924 
4925 /* Normalize a SID as used inside a CSN:
4926  * three-digit numeric string */
4927 static int
4928 hexNormalize(
4929 	slap_mask_t usage,
4930 	Syntax *syntax,
4931 	MatchingRule *mr,
4932 	struct berval *val,
4933 	struct berval *normalized,
4934 	void *ctx )
4935 {
4936 	ber_len_t	i;
4937 
4938 	assert( val != NULL );
4939 	assert( normalized != NULL );
4940 
4941 	ber_dupbv_x( normalized, val, ctx );
4942 
4943 	for ( i = 0; i < normalized->bv_len; i++ ) {
4944 		if ( !ASCII_HEX( normalized->bv_val[ i ] ) ) {
4945 			ber_memfree_x( normalized->bv_val, ctx );
4946 			BER_BVZERO( normalized );
4947 			return LDAP_INVALID_SYNTAX;
4948 		}
4949 
4950 		normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] );
4951 	}
4952 
4953 	return LDAP_SUCCESS;
4954 }
4955 
4956 static int
4957 sidValidate (
4958 	Syntax *syntax,
4959 	struct berval *in )
4960 {
4961 	assert( in != NULL );
4962 	assert( !BER_BVISNULL( in ) );
4963 
4964 	if ( in->bv_len != 3 ) {
4965 		return LDAP_INVALID_SYNTAX;
4966 	}
4967 
4968 	return hexValidate( NULL, in );
4969 }
4970 
4971 /* Normalize a SID as used inside a CSN:
4972  * three-digit numeric string */
4973 static int
4974 sidNormalize(
4975 	slap_mask_t usage,
4976 	Syntax *syntax,
4977 	MatchingRule *mr,
4978 	struct berval *val,
4979 	struct berval *normalized,
4980 	void *ctx )
4981 {
4982 	if ( val->bv_len != 3 ) {
4983 		return LDAP_INVALID_SYNTAX;
4984 	}
4985 
4986 	return hexNormalize( 0, NULL, NULL, val, normalized, ctx );
4987 }
4988 
4989 static int
4990 sidPretty(
4991 	Syntax *syntax,
4992 	struct berval *val,
4993 	struct berval *out,
4994 	void *ctx )
4995 {
4996 	return sidNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx );
4997 }
4998 
4999 /* Normalize a SID as used inside a CSN, either as-is
5000  * (assertion value) or extracted from the CSN
5001  * (attribute value) */
5002 static int
5003 csnSidNormalize(
5004 	slap_mask_t usage,
5005 	Syntax *syntax,
5006 	MatchingRule *mr,
5007 	struct berval *val,
5008 	struct berval *normalized,
5009 	void *ctx )
5010 {
5011 	struct berval	bv;
5012 	char		*ptr,
5013 			buf[ 4 ];
5014 
5015 
5016 	if ( BER_BVISEMPTY( val ) ) {
5017 		return LDAP_INVALID_SYNTAX;
5018 	}
5019 
5020 	if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
5021 		return sidNormalize( 0, NULL, NULL, val, normalized, ctx );
5022 	}
5023 
5024 	assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
5025 
5026 	ptr = ber_bvchr( val, '#' );
5027 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5028 		return LDAP_INVALID_SYNTAX;
5029 	}
5030 
5031 	bv.bv_val = ptr + 1;
5032 	bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val );
5033 
5034 	ptr = ber_bvchr( &bv, '#' );
5035 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5036 		return LDAP_INVALID_SYNTAX;
5037 	}
5038 
5039 	bv.bv_val = ptr + 1;
5040 	bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val );
5041 
5042 	ptr = ber_bvchr( &bv, '#' );
5043 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5044 		return LDAP_INVALID_SYNTAX;
5045 	}
5046 
5047 	bv.bv_len = ptr - bv.bv_val;
5048 
5049 	if ( bv.bv_len == 2 ) {
5050 		/* OpenLDAP 2.3 SID */
5051 		buf[ 0 ] = '0';
5052 		buf[ 1 ] = bv.bv_val[ 0 ];
5053 		buf[ 2 ] = bv.bv_val[ 1 ];
5054 		buf[ 3 ] = '\0';
5055 
5056 		bv.bv_val = buf;
5057 		bv.bv_len = 3;
5058 	}
5059 
5060 	return sidNormalize( 0, NULL, NULL, &bv, normalized, ctx );
5061 }
5062 
5063 static int
5064 csnValidate(
5065 	Syntax *syntax,
5066 	struct berval *in )
5067 {
5068 	struct berval	bv;
5069 	char		*ptr;
5070 	int		rc;
5071 
5072 	assert( in != NULL );
5073 	assert( !BER_BVISNULL( in ) );
5074 
5075 	if ( BER_BVISEMPTY( in ) ) {
5076 		return LDAP_INVALID_SYNTAX;
5077 	}
5078 
5079 	bv = *in;
5080 
5081 	ptr = ber_bvchr( &bv, '#' );
5082 	if ( ptr == NULL || ptr == &bv.bv_val[bv.bv_len] ) {
5083 		return LDAP_INVALID_SYNTAX;
5084 	}
5085 
5086 	bv.bv_len = ptr - bv.bv_val;
5087 	if ( bv.bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) &&
5088 		bv.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) )
5089 	{
5090 		return LDAP_INVALID_SYNTAX;
5091 	}
5092 
5093 	rc = generalizedTimeValidate( NULL, &bv );
5094 	if ( rc != LDAP_SUCCESS ) {
5095 		return rc;
5096 	}
5097 
5098 	bv.bv_val = ptr + 1;
5099 	bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
5100 
5101 	ptr = ber_bvchr( &bv, '#' );
5102 	if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) {
5103 		return LDAP_INVALID_SYNTAX;
5104 	}
5105 
5106 	bv.bv_len = ptr - bv.bv_val;
5107 	if ( bv.bv_len != 6 ) {
5108 		return LDAP_INVALID_SYNTAX;
5109 	}
5110 
5111 	rc = hexValidate( NULL, &bv );
5112 	if ( rc != LDAP_SUCCESS ) {
5113 		return rc;
5114 	}
5115 
5116 	bv.bv_val = ptr + 1;
5117 	bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
5118 
5119 	ptr = ber_bvchr( &bv, '#' );
5120 	if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) {
5121 		return LDAP_INVALID_SYNTAX;
5122 	}
5123 
5124 	bv.bv_len = ptr - bv.bv_val;
5125 	if ( bv.bv_len == 2 ) {
5126 		/* tolerate old 2-digit replica-id */
5127 		rc = hexValidate( NULL, &bv );
5128 
5129 	} else {
5130 		rc = sidValidate( NULL, &bv );
5131 	}
5132 	if ( rc != LDAP_SUCCESS ) {
5133 		return rc;
5134 	}
5135 
5136 	bv.bv_val = ptr + 1;
5137 	bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
5138 
5139 	if ( bv.bv_len != 6 ) {
5140 		return LDAP_INVALID_SYNTAX;
5141 	}
5142 
5143 	return hexValidate( NULL, &bv );
5144 }
5145 
5146 /* Normalize a CSN in OpenLDAP 2.1 format */
5147 static int
5148 csnNormalize21(
5149 	slap_mask_t usage,
5150 	Syntax *syntax,
5151 	MatchingRule *mr,
5152 	struct berval *val,
5153 	struct berval *normalized,
5154 	void *ctx )
5155 {
5156 	struct berval	gt, cnt, sid, mod;
5157 	struct berval	bv;
5158 	char		buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ];
5159 	char		*ptr;
5160 	ber_len_t	i;
5161 
5162 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
5163 	assert( !BER_BVISEMPTY( val ) );
5164 
5165 	gt = *val;
5166 
5167 	ptr = ber_bvchr( &gt, '#' );
5168 	if ( ptr == NULL || ptr == &gt.bv_val[gt.bv_len] ) {
5169 		return LDAP_INVALID_SYNTAX;
5170 	}
5171 
5172 	gt.bv_len = ptr - gt.bv_val;
5173 	if ( gt.bv_len != STRLENOF( "YYYYmmddHH:MM:SSZ" ) ) {
5174 		return LDAP_INVALID_SYNTAX;
5175 	}
5176 
5177 	if ( gt.bv_val[ 10 ] != ':' || gt.bv_val[ 13 ] != ':' ) {
5178 		return LDAP_INVALID_SYNTAX;
5179 	}
5180 
5181 	cnt.bv_val = ptr + 1;
5182 	cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
5183 
5184 	ptr = ber_bvchr( &cnt, '#' );
5185 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5186 		return LDAP_INVALID_SYNTAX;
5187 	}
5188 
5189 	cnt.bv_len = ptr - cnt.bv_val;
5190 	if ( cnt.bv_len != STRLENOF( "0x0000" ) ) {
5191 		return LDAP_INVALID_SYNTAX;
5192 	}
5193 
5194 	if ( strncmp( cnt.bv_val, "0x", STRLENOF( "0x" ) ) != 0 ) {
5195 		return LDAP_INVALID_SYNTAX;
5196 	}
5197 
5198 	cnt.bv_val += STRLENOF( "0x" );
5199 	cnt.bv_len -= STRLENOF( "0x" );
5200 
5201 	sid.bv_val = ptr + 1;
5202 	sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
5203 
5204 	ptr = ber_bvchr( &sid, '#' );
5205 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5206 		return LDAP_INVALID_SYNTAX;
5207 	}
5208 
5209 	sid.bv_len = ptr - sid.bv_val;
5210 	if ( sid.bv_len != STRLENOF( "0" ) ) {
5211 		return LDAP_INVALID_SYNTAX;
5212 	}
5213 
5214 	mod.bv_val = ptr + 1;
5215 	mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
5216 	if ( mod.bv_len != STRLENOF( "0000" ) ) {
5217 		return LDAP_INVALID_SYNTAX;
5218 	}
5219 
5220 	bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" );
5221 	bv.bv_val = buf;
5222 
5223 	ptr = bv.bv_val;
5224 	ptr = lutil_strncopy( ptr, gt.bv_val, STRLENOF( "YYYYmmddHH" ) );
5225 	ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:" ) ],
5226 		STRLENOF( "MM" ) );
5227 	ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:MM:" ) ],
5228 		STRLENOF( "SS" ) );
5229 	ptr = lutil_strcopy( ptr, ".000000Z#00" );
5230 	ptr = lutil_strbvcopy( ptr, &cnt );
5231 	*ptr++ = '#';
5232 	*ptr++ = '0';
5233 	*ptr++ = '0';
5234 	*ptr++ = sid.bv_val[ 0 ];
5235 	*ptr++ = '#';
5236 	*ptr++ = '0';
5237 	*ptr++ = '0';
5238 	for ( i = 0; i < mod.bv_len; i++ ) {
5239 		*ptr++ = TOLOWER( mod.bv_val[ i ] );
5240 	}
5241 	*ptr = '\0';
5242 
5243 	assert( ptr == &bv.bv_val[bv.bv_len] );
5244 
5245 	if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) {
5246 		return LDAP_INVALID_SYNTAX;
5247 	}
5248 
5249 	ber_dupbv_x( normalized, &bv, ctx );
5250 
5251 	return LDAP_SUCCESS;
5252 }
5253 
5254 /* Normalize a CSN in OpenLDAP 2.3 format */
5255 static int
5256 csnNormalize23(
5257 	slap_mask_t usage,
5258 	Syntax *syntax,
5259 	MatchingRule *mr,
5260 	struct berval *val,
5261 	struct berval *normalized,
5262 	void *ctx )
5263 {
5264 	struct berval	gt, cnt, sid, mod;
5265 	struct berval	bv;
5266 	char		buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ];
5267 	char		*ptr;
5268 	ber_len_t	i;
5269 
5270 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
5271 	assert( !BER_BVISEMPTY( val ) );
5272 
5273 	gt = *val;
5274 
5275 	ptr = ber_bvchr( &gt, '#' );
5276 	if ( ptr == NULL || ptr == &gt.bv_val[gt.bv_len] ) {
5277 		return LDAP_INVALID_SYNTAX;
5278 	}
5279 
5280 	gt.bv_len = ptr - gt.bv_val;
5281 	if ( gt.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) {
5282 		return LDAP_INVALID_SYNTAX;
5283 	}
5284 
5285 	cnt.bv_val = ptr + 1;
5286 	cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
5287 
5288 	ptr = ber_bvchr( &cnt, '#' );
5289 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5290 		return LDAP_INVALID_SYNTAX;
5291 	}
5292 
5293 	cnt.bv_len = ptr - cnt.bv_val;
5294 	if ( cnt.bv_len != STRLENOF( "000000" ) ) {
5295 		return LDAP_INVALID_SYNTAX;
5296 	}
5297 
5298 	sid.bv_val = ptr + 1;
5299 	sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
5300 
5301 	ptr = ber_bvchr( &sid, '#' );
5302 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5303 		return LDAP_INVALID_SYNTAX;
5304 	}
5305 
5306 	sid.bv_len = ptr - sid.bv_val;
5307 	if ( sid.bv_len != STRLENOF( "00" ) ) {
5308 		return LDAP_INVALID_SYNTAX;
5309 	}
5310 
5311 	mod.bv_val = ptr + 1;
5312 	mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
5313 	if ( mod.bv_len != STRLENOF( "000000" ) ) {
5314 		return LDAP_INVALID_SYNTAX;
5315 	}
5316 
5317 	bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" );
5318 	bv.bv_val = buf;
5319 
5320 	ptr = bv.bv_val;
5321 	ptr = lutil_strncopy( ptr, gt.bv_val, gt.bv_len - 1 );
5322 	ptr = lutil_strcopy( ptr, ".000000Z#" );
5323 	ptr = lutil_strbvcopy( ptr, &cnt );
5324 	*ptr++ = '#';
5325 	*ptr++ = '0';
5326 	for ( i = 0; i < sid.bv_len; i++ ) {
5327 		*ptr++ = TOLOWER( sid.bv_val[ i ] );
5328 	}
5329 	*ptr++ = '#';
5330 	for ( i = 0; i < mod.bv_len; i++ ) {
5331 		*ptr++ = TOLOWER( mod.bv_val[ i ] );
5332 	}
5333 	*ptr = '\0';
5334 
5335 	assert( ptr == &bv.bv_val[bv.bv_len] );
5336 	if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) {
5337 		return LDAP_INVALID_SYNTAX;
5338 	}
5339 
5340 	ber_dupbv_x( normalized, &bv, ctx );
5341 
5342 	return LDAP_SUCCESS;
5343 }
5344 
5345 /* Normalize a CSN */
5346 static int
5347 csnNormalize(
5348 	slap_mask_t usage,
5349 	Syntax *syntax,
5350 	MatchingRule *mr,
5351 	struct berval *val,
5352 	struct berval *normalized,
5353 	void *ctx )
5354 {
5355 	struct berval	cnt, sid, mod;
5356 	char		*ptr;
5357 	ber_len_t	i;
5358 
5359 	assert( val != NULL );
5360 	assert( normalized != NULL );
5361 
5362 	assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
5363 
5364 	if ( BER_BVISEMPTY( val ) ) {
5365 		return LDAP_INVALID_SYNTAX;
5366 	}
5367 
5368 	if ( val->bv_len == STRLENOF( "YYYYmmddHHMMSSZ#SSSSSS#ID#ssssss" ) ) {
5369 		/* Openldap <= 2.3 */
5370 
5371 		return csnNormalize23( usage, syntax, mr, val, normalized, ctx );
5372 	}
5373 
5374 	if ( val->bv_len == STRLENOF( "YYYYmmddHH:MM:SSZ#0xSSSS#I#ssss" ) ) {
5375 		/* Openldap 2.1 */
5376 
5377 		return csnNormalize21( usage, syntax, mr, val, normalized, ctx );
5378 	}
5379 
5380 	if ( val->bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) ) {
5381 		return LDAP_INVALID_SYNTAX;
5382 	}
5383 
5384 	ptr = ber_bvchr( val, '#' );
5385 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5386 		return LDAP_INVALID_SYNTAX;
5387 	}
5388 
5389 	if ( ptr - val->bv_val != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) ) {
5390 		return LDAP_INVALID_SYNTAX;
5391 	}
5392 
5393 	cnt.bv_val = ptr + 1;
5394 	cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
5395 
5396 	ptr = ber_bvchr( &cnt, '#' );
5397 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5398 		return LDAP_INVALID_SYNTAX;
5399 	}
5400 
5401 	if ( ptr - cnt.bv_val != STRLENOF( "000000" ) ) {
5402 		return LDAP_INVALID_SYNTAX;
5403 	}
5404 
5405 	sid.bv_val = ptr + 1;
5406 	sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
5407 
5408 	ptr = ber_bvchr( &sid, '#' );
5409 	if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
5410 		return LDAP_INVALID_SYNTAX;
5411 	}
5412 
5413 	sid.bv_len = ptr - sid.bv_val;
5414 	if ( sid.bv_len != STRLENOF( "000" ) ) {
5415 		return LDAP_INVALID_SYNTAX;
5416 	}
5417 
5418 	mod.bv_val = ptr + 1;
5419 	mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
5420 
5421 	if ( mod.bv_len != STRLENOF( "000000" ) ) {
5422 		return LDAP_INVALID_SYNTAX;
5423 	}
5424 
5425 	ber_dupbv_x( normalized, val, ctx );
5426 
5427 	for ( i = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#" );
5428 		i < normalized->bv_len; i++ )
5429 	{
5430 		/* assume it's already validated that's all hex digits */
5431 		normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] );
5432 	}
5433 
5434 	return LDAP_SUCCESS;
5435 }
5436 
5437 static int
5438 csnPretty(
5439 	Syntax *syntax,
5440 	struct berval *val,
5441 	struct berval *out,
5442 	void *ctx )
5443 {
5444 	return csnNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx );
5445 }
5446 
5447 #ifndef SUPPORT_OBSOLETE_UTC_SYNTAX
5448 /* slight optimization - does not need the start parameter */
5449 #define check_time_syntax(v, start, p, f) (check_time_syntax)(v, p, f)
5450 enum { start = 0 };
5451 #endif
5452 
5453 static int
5454 check_time_syntax (struct berval *val,
5455 	int start,
5456 	int *parts,
5457 	struct berval *fraction)
5458 {
5459 	/*
5460 	 * start=0 GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM])
5461 	 * start=1 UTCTime         YYmmddHHMM[SS][Z|(+/-)HHMM]
5462 	 * GeneralizedTime supports leap seconds, UTCTime does not.
5463 	 */
5464 	static const int ceiling[9] = { 100, 100, 12, 31, 24, 60, 60, 24, 60 };
5465 	static const int mdays[2][12] = {
5466 		/* non-leap years */
5467 		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
5468 		/* leap years */
5469 		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
5470 	};
5471 	char *p, *e;
5472 	int part, c, c1, c2, tzoffset, leapyear = 0;
5473 
5474 	p = val->bv_val;
5475 	e = p + val->bv_len;
5476 
5477 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
5478 	parts[0] = 20; /* century - any multiple of 4 from 04 to 96 */
5479 #endif
5480 	for (part = start; part < 7 && p < e; part++) {
5481 		c1 = *p;
5482 		if (!ASCII_DIGIT(c1)) {
5483 			break;
5484 		}
5485 		p++;
5486 		if (p == e) {
5487 			return LDAP_INVALID_SYNTAX;
5488 		}
5489 		c = *p++;
5490 		if (!ASCII_DIGIT(c)) {
5491 			return LDAP_INVALID_SYNTAX;
5492 		}
5493 		c += c1 * 10 - '0' * 11;
5494 		if ((part | 1) == 3) {
5495 			--c;
5496 			if (c < 0) {
5497 				return LDAP_INVALID_SYNTAX;
5498 			}
5499 		}
5500 		if (c >= ceiling[part]) {
5501 			if (! (c == 60 && part == 6 && start == 0))
5502 				return LDAP_INVALID_SYNTAX;
5503 		}
5504 		parts[part] = c;
5505 	}
5506 	if (part < 5 + start) {
5507 		return LDAP_INVALID_SYNTAX;
5508 	}
5509 	for (; part < 9; part++) {
5510 		parts[part] = 0;
5511 	}
5512 
5513 	/* leapyear check for the Gregorian calendar (year>1581) */
5514 	if (parts[parts[1] == 0 ? 0 : 1] % 4 == 0) {
5515 		leapyear = 1;
5516 	}
5517 
5518 	if (parts[3] >= mdays[leapyear][parts[2]]) {
5519 		return LDAP_INVALID_SYNTAX;
5520 	}
5521 
5522 	if (start == 0) {
5523 		fraction->bv_val = p;
5524 		fraction->bv_len = 0;
5525 		if (p < e && (*p == '.' || *p == ',')) {
5526 			char *end_num;
5527 			while (++p < e && ASCII_DIGIT(*p)) {
5528 				/* EMTPY */;
5529 			}
5530 			if (p - fraction->bv_val == 1) {
5531 				return LDAP_INVALID_SYNTAX;
5532 			}
5533 			for (end_num = p; end_num[-1] == '0'; --end_num) {
5534 				/* EMPTY */;
5535 			}
5536 			c = end_num - fraction->bv_val;
5537 			if (c != 1) fraction->bv_len = c;
5538 		}
5539 	}
5540 
5541 	if (p == e) {
5542 		/* no time zone */
5543 		return start == 0 ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
5544 	}
5545 
5546 	tzoffset = *p++;
5547 	switch (tzoffset) {
5548 	default:
5549 		return LDAP_INVALID_SYNTAX;
5550 	case 'Z':
5551 		/* UTC */
5552 		break;
5553 	case '+':
5554 	case '-':
5555 		for (part = 7; part < 9 && p < e; part++) {
5556 			c1 = *p;
5557 			if (!ASCII_DIGIT(c1)) {
5558 				break;
5559 			}
5560 			p++;
5561 			if (p == e) {
5562 				return LDAP_INVALID_SYNTAX;
5563 			}
5564 			c2 = *p++;
5565 			if (!ASCII_DIGIT(c2)) {
5566 				return LDAP_INVALID_SYNTAX;
5567 			}
5568 			parts[part] = c1 * 10 + c2 - '0' * 11;
5569 			if (parts[part] >= ceiling[part]) {
5570 				return LDAP_INVALID_SYNTAX;
5571 			}
5572 		}
5573 		if (part < 8 + start) {
5574 			return LDAP_INVALID_SYNTAX;
5575 		}
5576 
5577 		if (tzoffset == '-') {
5578 			/* negative offset to UTC, ie west of Greenwich */
5579 			parts[4] += parts[7];
5580 			parts[5] += parts[8];
5581 			/* offset is just hhmm, no seconds */
5582 			for (part = 6; --part >= 0; ) {
5583 				if (part != 3) {
5584 					c = ceiling[part];
5585 				} else {
5586 					c = mdays[leapyear][parts[2]];
5587 				}
5588 				if (parts[part] >= c) {
5589 					if (part == 0) {
5590 						return LDAP_INVALID_SYNTAX;
5591 					}
5592 					parts[part] -= c;
5593 					parts[part - 1]++;
5594 					continue;
5595 				} else if (part != 5) {
5596 					break;
5597 				}
5598 			}
5599 		} else {
5600 			/* positive offset to UTC, ie east of Greenwich */
5601 			parts[4] -= parts[7];
5602 			parts[5] -= parts[8];
5603 			for (part = 6; --part >= 0; ) {
5604 				if (parts[part] < 0) {
5605 					if (part == 0) {
5606 						return LDAP_INVALID_SYNTAX;
5607 					}
5608 					if (part != 3) {
5609 						c = ceiling[part];
5610 					} else {
5611 						/* make first arg to % non-negative */
5612 						c = mdays[leapyear][(parts[2] - 1 + 12) % 12];
5613 					}
5614 					parts[part] += c;
5615 					parts[part - 1]--;
5616 					continue;
5617 				} else if (part != 5) {
5618 					break;
5619 				}
5620 			}
5621 		}
5622 	}
5623 
5624 	return p != e ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
5625 }
5626 
5627 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
5628 
5629 #if 0
5630 static int
5631 xutcTimeNormalize(
5632 	Syntax *syntax,
5633 	struct berval *val,
5634 	struct berval *normalized )
5635 {
5636 	int parts[9], rc;
5637 
5638 	rc = check_time_syntax(val, 1, parts, NULL);
5639 	if (rc != LDAP_SUCCESS) {
5640 		return rc;
5641 	}
5642 
5643 	normalized->bv_val = ch_malloc( 14 );
5644 	if ( normalized->bv_val == NULL ) {
5645 		return LBER_ERROR_MEMORY;
5646 	}
5647 
5648 	sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02dZ",
5649 		parts[1], parts[2] + 1, parts[3] + 1,
5650 		parts[4], parts[5], parts[6] );
5651 	normalized->bv_len = 13;
5652 
5653 	return LDAP_SUCCESS;
5654 }
5655 #endif /* 0 */
5656 
5657 static int
5658 utcTimeValidate(
5659 	Syntax *syntax,
5660 	struct berval *in )
5661 {
5662 	int parts[9];
5663 	return check_time_syntax(in, 1, parts, NULL);
5664 }
5665 
5666 #endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */
5667 
5668 static int
5669 generalizedTimeValidate(
5670 	Syntax *syntax,
5671 	struct berval *in )
5672 {
5673 	int parts[9];
5674 	struct berval fraction;
5675 	return check_time_syntax(in, 0, parts, &fraction);
5676 }
5677 
5678 static int
5679 generalizedTimeNormalize(
5680 	slap_mask_t usage,
5681 	Syntax *syntax,
5682 	MatchingRule *mr,
5683 	struct berval *val,
5684 	struct berval *normalized,
5685 	void *ctx )
5686 {
5687 	int parts[9], rc;
5688 	unsigned int len;
5689 	struct berval fraction;
5690 
5691 	rc = check_time_syntax(val, 0, parts, &fraction);
5692 	if (rc != LDAP_SUCCESS) {
5693 		return rc;
5694 	}
5695 
5696 	len = STRLENOF("YYYYmmddHHMMSSZ") + fraction.bv_len;
5697 	normalized->bv_val = slap_sl_malloc( len + 1, ctx );
5698 	if ( BER_BVISNULL( normalized ) ) {
5699 		return LBER_ERROR_MEMORY;
5700 	}
5701 
5702 	sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02d%02d",
5703 		parts[0], parts[1], parts[2] + 1, parts[3] + 1,
5704 		parts[4], parts[5], parts[6] );
5705 	if ( !BER_BVISEMPTY( &fraction ) ) {
5706 		memcpy( normalized->bv_val + STRLENOF("YYYYmmddHHMMSSZ")-1,
5707 			fraction.bv_val, fraction.bv_len );
5708 		normalized->bv_val[STRLENOF("YYYYmmddHHMMSSZ")-1] = '.';
5709 	}
5710 	strcpy( normalized->bv_val + len-1, "Z" );
5711 	normalized->bv_len = len;
5712 
5713 	return LDAP_SUCCESS;
5714 }
5715 
5716 static int
5717 generalizedTimeOrderingMatch(
5718 	int *matchp,
5719 	slap_mask_t flags,
5720 	Syntax *syntax,
5721 	MatchingRule *mr,
5722 	struct berval *value,
5723 	void *assertedValue )
5724 {
5725 	struct berval *asserted = (struct berval *) assertedValue;
5726 	ber_len_t v_len  = value->bv_len;
5727 	ber_len_t av_len = asserted->bv_len;
5728 
5729 	/* ignore trailing 'Z' when comparing */
5730 	int match = memcmp( value->bv_val, asserted->bv_val,
5731 		(v_len < av_len ? v_len : av_len) - 1 );
5732 	if ( match == 0 ) match = v_len - av_len;
5733 
5734 	/* If used in extensible match filter, match if value < asserted */
5735 	if ( flags & SLAP_MR_EXT )
5736 		match = (match >= 0);
5737 
5738 	*matchp = match;
5739 	return LDAP_SUCCESS;
5740 }
5741 
5742 /* Index generation function: Ordered index */
5743 int generalizedTimeIndexer(
5744 	slap_mask_t use,
5745 	slap_mask_t flags,
5746 	Syntax *syntax,
5747 	MatchingRule *mr,
5748 	struct berval *prefix,
5749 	BerVarray values,
5750 	BerVarray *keysp,
5751 	void *ctx )
5752 {
5753 	int i, j;
5754 	BerVarray keys;
5755 	char tmp[5];
5756 	BerValue bvtmp; /* 40 bit index */
5757 	struct lutil_tm tm;
5758 	struct lutil_timet tt;
5759 
5760 	bvtmp.bv_len = sizeof(tmp);
5761 	bvtmp.bv_val = tmp;
5762 	for( i=0; values[i].bv_val != NULL; i++ ) {
5763 		/* just count them */
5764 	}
5765 
5766 	/* we should have at least one value at this point */
5767 	assert( i > 0 );
5768 
5769 	keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
5770 
5771 	/* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
5772 	for( i=0, j=0; values[i].bv_val != NULL; i++ ) {
5773 		assert(values[i].bv_val != NULL && values[i].bv_len >= 10);
5774 		/* Use 40 bits of time for key */
5775 		if ( lutil_parsetime( values[i].bv_val, &tm ) == 0 ) {
5776 			lutil_tm2time( &tm, &tt );
5777 			tmp[0] = tt.tt_gsec & 0xff;
5778 			tmp[4] = tt.tt_sec & 0xff;
5779 			tt.tt_sec >>= 8;
5780 			tmp[3] = tt.tt_sec & 0xff;
5781 			tt.tt_sec >>= 8;
5782 			tmp[2] = tt.tt_sec & 0xff;
5783 			tt.tt_sec >>= 8;
5784 			tmp[1] = tt.tt_sec & 0xff;
5785 
5786 			ber_dupbv_x(&keys[j++], &bvtmp, ctx );
5787 		}
5788 	}
5789 
5790 	keys[j].bv_val = NULL;
5791 	keys[j].bv_len = 0;
5792 
5793 	*keysp = keys;
5794 
5795 	return LDAP_SUCCESS;
5796 }
5797 
5798 /* Index generation function: Ordered index */
5799 int generalizedTimeFilter(
5800 	slap_mask_t use,
5801 	slap_mask_t flags,
5802 	Syntax *syntax,
5803 	MatchingRule *mr,
5804 	struct berval *prefix,
5805 	void * assertedValue,
5806 	BerVarray *keysp,
5807 	void *ctx )
5808 {
5809 	BerVarray keys;
5810 	char tmp[5];
5811 	BerValue bvtmp; /* 40 bit index */
5812 	BerValue *value = (BerValue *) assertedValue;
5813 	struct lutil_tm tm;
5814 	struct lutil_timet tt;
5815 
5816 	bvtmp.bv_len = sizeof(tmp);
5817 	bvtmp.bv_val = tmp;
5818 	/* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
5819 	/* Use 40 bits of time for key */
5820 	if ( value->bv_val && value->bv_len >= 10 &&
5821 		lutil_parsetime( value->bv_val, &tm ) == 0 ) {
5822 
5823 		lutil_tm2time( &tm, &tt );
5824 		tmp[0] = tt.tt_gsec & 0xff;
5825 		tmp[4] = tt.tt_sec & 0xff;
5826 		tt.tt_sec >>= 8;
5827 		tmp[3] = tt.tt_sec & 0xff;
5828 		tt.tt_sec >>= 8;
5829 		tmp[2] = tt.tt_sec & 0xff;
5830 		tt.tt_sec >>= 8;
5831 		tmp[1] = tt.tt_sec & 0xff;
5832 
5833 		keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
5834 		ber_dupbv_x(keys, &bvtmp, ctx );
5835 		keys[1].bv_val = NULL;
5836 		keys[1].bv_len = 0;
5837 	} else {
5838 		keys = NULL;
5839 	}
5840 
5841 	*keysp = keys;
5842 
5843 	return LDAP_SUCCESS;
5844 }
5845 
5846 static int
5847 deliveryMethodValidate(
5848 	Syntax *syntax,
5849 	struct berval *val )
5850 {
5851 #undef LENOF
5852 #define LENOF(s) (sizeof(s)-1)
5853 	struct berval tmp = *val;
5854 	/*
5855      *	DeliveryMethod = pdm *( WSP DOLLAR WSP DeliveryMethod )
5856 	 *	pdm = "any" / "mhs" / "physical" / "telex" / "teletex" /
5857 	 *		"g3fax" / "g4fax" / "ia5" / "videotex" / "telephone"
5858 	 */
5859 again:
5860 	if( tmp.bv_len < 3 ) return LDAP_INVALID_SYNTAX;
5861 
5862 	switch( tmp.bv_val[0] ) {
5863 	case 'a':
5864 	case 'A':
5865 		if(( tmp.bv_len >= LENOF("any") ) &&
5866 			( strncasecmp(tmp.bv_val, "any", LENOF("any")) == 0 ))
5867 		{
5868 			tmp.bv_len -= LENOF("any");
5869 			tmp.bv_val += LENOF("any");
5870 			break;
5871 		}
5872 		return LDAP_INVALID_SYNTAX;
5873 
5874 	case 'm':
5875 	case 'M':
5876 		if(( tmp.bv_len >= LENOF("mhs") ) &&
5877 			( strncasecmp(tmp.bv_val, "mhs", LENOF("mhs")) == 0 ))
5878 		{
5879 			tmp.bv_len -= LENOF("mhs");
5880 			tmp.bv_val += LENOF("mhs");
5881 			break;
5882 		}
5883 		return LDAP_INVALID_SYNTAX;
5884 
5885 	case 'p':
5886 	case 'P':
5887 		if(( tmp.bv_len >= LENOF("physical") ) &&
5888 			( strncasecmp(tmp.bv_val, "physical", LENOF("physical")) == 0 ))
5889 		{
5890 			tmp.bv_len -= LENOF("physical");
5891 			tmp.bv_val += LENOF("physical");
5892 			break;
5893 		}
5894 		return LDAP_INVALID_SYNTAX;
5895 
5896 	case 't':
5897 	case 'T': /* telex or teletex or telephone */
5898 		if(( tmp.bv_len >= LENOF("telex") ) &&
5899 			( strncasecmp(tmp.bv_val, "telex", LENOF("telex")) == 0 ))
5900 		{
5901 			tmp.bv_len -= LENOF("telex");
5902 			tmp.bv_val += LENOF("telex");
5903 			break;
5904 		}
5905 		if(( tmp.bv_len >= LENOF("teletex") ) &&
5906 			( strncasecmp(tmp.bv_val, "teletex", LENOF("teletex")) == 0 ))
5907 		{
5908 			tmp.bv_len -= LENOF("teletex");
5909 			tmp.bv_val += LENOF("teletex");
5910 			break;
5911 		}
5912 		if(( tmp.bv_len >= LENOF("telephone") ) &&
5913 			( strncasecmp(tmp.bv_val, "telephone", LENOF("telephone")) == 0 ))
5914 		{
5915 			tmp.bv_len -= LENOF("telephone");
5916 			tmp.bv_val += LENOF("telephone");
5917 			break;
5918 		}
5919 		return LDAP_INVALID_SYNTAX;
5920 
5921 	case 'g':
5922 	case 'G': /* g3fax or g4fax */
5923 		if(( tmp.bv_len >= LENOF("g3fax") ) && (
5924 			( strncasecmp(tmp.bv_val, "g3fax", LENOF("g3fax")) == 0 ) ||
5925 			( strncasecmp(tmp.bv_val, "g4fax", LENOF("g4fax")) == 0 )))
5926 		{
5927 			tmp.bv_len -= LENOF("g3fax");
5928 			tmp.bv_val += LENOF("g3fax");
5929 			break;
5930 		}
5931 		return LDAP_INVALID_SYNTAX;
5932 
5933 	case 'i':
5934 	case 'I':
5935 		if(( tmp.bv_len >= LENOF("ia5") ) &&
5936 			( strncasecmp(tmp.bv_val, "ia5", LENOF("ia5")) == 0 ))
5937 		{
5938 			tmp.bv_len -= LENOF("ia5");
5939 			tmp.bv_val += LENOF("ia5");
5940 			break;
5941 		}
5942 		return LDAP_INVALID_SYNTAX;
5943 
5944 	case 'v':
5945 	case 'V':
5946 		if(( tmp.bv_len >= LENOF("videotex") ) &&
5947 			( strncasecmp(tmp.bv_val, "videotex", LENOF("videotex")) == 0 ))
5948 		{
5949 			tmp.bv_len -= LENOF("videotex");
5950 			tmp.bv_val += LENOF("videotex");
5951 			break;
5952 		}
5953 		return LDAP_INVALID_SYNTAX;
5954 
5955 	default:
5956 		return LDAP_INVALID_SYNTAX;
5957 	}
5958 
5959 	if( BER_BVISEMPTY( &tmp ) ) return LDAP_SUCCESS;
5960 
5961 	while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
5962 		tmp.bv_len--;
5963 		tmp.bv_val++;
5964 	}
5965 	if( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == '$' ) ) {
5966 		tmp.bv_len--;
5967 		tmp.bv_val++;
5968 	} else {
5969 		return LDAP_INVALID_SYNTAX;
5970 	}
5971 	while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
5972 		tmp.bv_len--;
5973 		tmp.bv_val++;
5974 	}
5975 
5976 	goto again;
5977 }
5978 
5979 static int
5980 nisNetgroupTripleValidate(
5981 	Syntax *syntax,
5982 	struct berval *val )
5983 {
5984 	char *p, *e;
5985 	int commas = 0;
5986 
5987 	if ( BER_BVISEMPTY( val ) ) {
5988 		return LDAP_INVALID_SYNTAX;
5989 	}
5990 
5991 	p = (char *)val->bv_val;
5992 	e = p + val->bv_len;
5993 
5994 	if ( *p != '(' /*')'*/ ) {
5995 		return LDAP_INVALID_SYNTAX;
5996 	}
5997 
5998 	for ( p++; ( p < e ) && ( *p != /*'('*/ ')' ); p++ ) {
5999 		if ( *p == ',' ) {
6000 			commas++;
6001 			if ( commas > 2 ) {
6002 				return LDAP_INVALID_SYNTAX;
6003 			}
6004 
6005 		} else if ( !AD_CHAR( *p ) ) {
6006 			return LDAP_INVALID_SYNTAX;
6007 		}
6008 	}
6009 
6010 	if ( ( commas != 2 ) || ( *p != /*'('*/ ')' ) ) {
6011 		return LDAP_INVALID_SYNTAX;
6012 	}
6013 
6014 	p++;
6015 
6016 	if (p != e) {
6017 		return LDAP_INVALID_SYNTAX;
6018 	}
6019 
6020 	return LDAP_SUCCESS;
6021 }
6022 
6023 static int
6024 bootParameterValidate(
6025 	Syntax *syntax,
6026 	struct berval *val )
6027 {
6028 	char *p, *e;
6029 
6030 	if ( BER_BVISEMPTY( val ) ) {
6031 		return LDAP_INVALID_SYNTAX;
6032 	}
6033 
6034 	p = (char *)val->bv_val;
6035 	e = p + val->bv_len;
6036 
6037 	/* key */
6038 	for (; ( 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 	/* server */
6049 	for ( p++; ( p < e ) && ( *p != ':' ); p++ ) {
6050 		if ( !AD_CHAR( *p ) ) {
6051 			return LDAP_INVALID_SYNTAX;
6052 		}
6053 	}
6054 
6055 	if ( *p != ':' ) {
6056 		return LDAP_INVALID_SYNTAX;
6057 	}
6058 
6059 	/* path */
6060 	for ( p++; p < e; p++ ) {
6061 		if ( !SLAP_PRINTABLE( *p ) ) {
6062 			return LDAP_INVALID_SYNTAX;
6063 		}
6064 	}
6065 
6066 	return LDAP_SUCCESS;
6067 }
6068 
6069 static int
6070 firstComponentNormalize(
6071 	slap_mask_t usage,
6072 	Syntax *syntax,
6073 	MatchingRule *mr,
6074 	struct berval *val,
6075 	struct berval *normalized,
6076 	void *ctx )
6077 {
6078 	int rc;
6079 	struct berval comp;
6080 	ber_len_t len;
6081 
6082 	if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( usage )) {
6083 		ber_dupbv_x( normalized, val, ctx );
6084 		return LDAP_SUCCESS;
6085 	}
6086 
6087 	if( val->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
6088 
6089 	if( ! ( val->bv_val[0] == '(' /*')'*/
6090 			&& val->bv_val[val->bv_len - 1] == /*'('*/ ')' )
6091 		&& ! ( val->bv_val[0] == '{' /*'}'*/
6092 			&& val->bv_val[val->bv_len - 1] == /*'('*/ '}' ) )
6093 	{
6094 		return LDAP_INVALID_SYNTAX;
6095 	}
6096 
6097 	/* trim leading white space */
6098 	for( len=1;
6099 		len < val->bv_len && ASCII_SPACE(val->bv_val[len]);
6100 		len++ )
6101 	{
6102 		/* empty */
6103 	}
6104 
6105 	/* grab next word */
6106 	comp.bv_val = &val->bv_val[len];
6107 	len = val->bv_len - len - STRLENOF(/*"{"*/ "}");
6108 	for( comp.bv_len = 0;
6109 		!ASCII_SPACE(comp.bv_val[comp.bv_len]) && comp.bv_len < len;
6110 		comp.bv_len++ )
6111 	{
6112 		/* empty */
6113 	}
6114 
6115 	if( mr == slap_schema.si_mr_objectIdentifierFirstComponentMatch ) {
6116 		rc = numericoidValidate( NULL, &comp );
6117 	} else if( mr == slap_schema.si_mr_integerFirstComponentMatch ) {
6118 		rc = integerValidate( NULL, &comp );
6119 	} else {
6120 		rc = LDAP_INVALID_SYNTAX;
6121 	}
6122 
6123 
6124 	if( rc == LDAP_SUCCESS ) {
6125 		ber_dupbv_x( normalized, &comp, ctx );
6126 	}
6127 
6128 	return rc;
6129 }
6130 
6131 static char *country_gen_syn[] = {
6132 	"1.3.6.1.4.1.1466.115.121.1.15",	/* Directory String */
6133 	"1.3.6.1.4.1.1466.115.121.1.26",	/* IA5 String */
6134 	"1.3.6.1.4.1.1466.115.121.1.44",	/* Printable String */
6135 	NULL
6136 };
6137 
6138 #define X_BINARY "X-BINARY-TRANSFER-REQUIRED 'TRUE' "
6139 #define X_NOT_H_R "X-NOT-HUMAN-READABLE 'TRUE' "
6140 
6141 static slap_syntax_defs_rec syntax_defs[] = {
6142 	{"( 1.3.6.1.4.1.1466.115.121.1.1 DESC 'ACI Item' "
6143 		X_BINARY X_NOT_H_R ")",
6144 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL, NULL},
6145 	{"( 1.3.6.1.4.1.1466.115.121.1.2 DESC 'Access Point' " X_NOT_H_R ")",
6146 		0, NULL, NULL, NULL},
6147 	{"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )",
6148 		0, NULL, NULL, NULL},
6149 	{"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' "
6150 		X_NOT_H_R ")",
6151 		SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
6152 	{"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' "
6153 		X_NOT_H_R ")",
6154 		SLAP_SYNTAX_BER, NULL, berValidate, NULL},
6155 	{"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )",
6156 		0, NULL, bitStringValidate, NULL },
6157 	{"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )",
6158 		0, NULL, booleanValidate, NULL},
6159 	{"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' "
6160 		X_BINARY X_NOT_H_R ")",
6161 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6162 		NULL, certificateValidate, NULL},
6163 	{"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' "
6164 		X_BINARY X_NOT_H_R ")",
6165 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6166 		NULL, certificateListValidate, NULL},
6167 	{"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' "
6168 		X_BINARY X_NOT_H_R ")",
6169 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6170 		NULL, sequenceValidate, NULL},
6171 	{"( " attributeCertificateSyntaxOID " DESC 'X.509 AttributeCertificate' "
6172 		X_BINARY X_NOT_H_R ")",
6173 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
6174 		NULL, attributeCertificateValidate, NULL},
6175 #if 0	/* need to go __after__ printableString */
6176 	{"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
6177 		0, "1.3.6.1.4.1.1466.115.121.1.44",
6178 		countryStringValidate, NULL},
6179 #endif
6180 	{"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )",
6181 		SLAP_SYNTAX_DN, NULL, dnValidate, dnPretty},
6182 	{"( 1.2.36.79672281.1.5.0 DESC 'RDN' )",
6183 		0, NULL, rdnValidate, rdnPretty},
6184 #ifdef LDAP_COMP_MATCH
6185 	{"( 1.2.36.79672281.1.5.3 DESC 'allComponents' )",
6186 		0, NULL, allComponentsValidate, NULL},
6187  	{"( 1.2.36.79672281.1.5.2 DESC 'componentFilterMatch assertion') ",
6188 		0, NULL, componentFilterValidate, NULL},
6189 #endif
6190 	{"( 1.3.6.1.4.1.1466.115.121.1.13 DESC 'Data Quality' )",
6191 		0, NULL, NULL, NULL},
6192 	{"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )",
6193 		0, NULL, deliveryMethodValidate, NULL},
6194 	{"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )",
6195 		0, NULL, UTF8StringValidate, NULL},
6196 	{"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )",
6197 		0, NULL, NULL, NULL},
6198 	{"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )",
6199 		0, NULL, NULL, NULL},
6200 	{"( 1.3.6.1.4.1.1466.115.121.1.19 DESC 'DSA Quality' )",
6201 		0, NULL, NULL, NULL},
6202 	{"( 1.3.6.1.4.1.1466.115.121.1.20 DESC 'DSE Type' )",
6203 		0, NULL, NULL, NULL},
6204 	{"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )",
6205 		0, NULL, NULL, NULL},
6206 	{"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )",
6207 		0, NULL, printablesStringValidate, NULL},
6208 	{"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' " X_NOT_H_R ")",
6209 		SLAP_SYNTAX_BLOB, NULL, NULL, NULL},
6210 	{"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )",
6211 		0, NULL, generalizedTimeValidate, NULL},
6212 	{"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )",
6213 		0, NULL, NULL, NULL},
6214 	{"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )",
6215 		0, NULL, IA5StringValidate, NULL},
6216 	{"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )",
6217 		0, NULL, integerValidate, NULL},
6218 	{"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' " X_NOT_H_R ")",
6219 		SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
6220 	{"( 1.3.6.1.4.1.1466.115.121.1.29 DESC 'Master And Shadow Access Points' )",
6221 		0, NULL, NULL, NULL},
6222 	{"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )",
6223 		0, NULL, NULL, NULL},
6224 	{"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )",
6225 		0, NULL, NULL, NULL},
6226 	{"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )",
6227 		0, NULL, NULL, NULL},
6228 	{"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )",
6229 		0, NULL, NULL, NULL},
6230 	{"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )",
6231 		SLAP_SYNTAX_DN, NULL, nameUIDValidate, nameUIDPretty },
6232 	{"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )",
6233 		0, NULL, NULL, NULL},
6234 	{"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )",
6235 		0, NULL, numericStringValidate, NULL},
6236 	{"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )",
6237 		0, NULL, NULL, NULL},
6238 	{"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )",
6239 		0, NULL, numericoidValidate, NULL},
6240 	{"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )",
6241 		0, NULL, IA5StringValidate, NULL},
6242 	{"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )",
6243 		0, NULL, blobValidate, NULL},
6244 	{"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )",
6245 		0, NULL, postalAddressValidate, NULL},
6246 	{"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )",
6247 		0, NULL, NULL, NULL},
6248 	{"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )",
6249 		0, NULL, NULL, NULL},
6250 	{"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )",
6251 		0, NULL, printableStringValidate, NULL},
6252 	/* moved here because now depends on Directory String, IA5 String
6253 	 * and Printable String */
6254 	{"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
6255 		0, country_gen_syn, countryStringValidate, NULL},
6256 	{"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )",
6257 #define subtreeSpecificationValidate UTF8StringValidate /* FIXME */
6258 		0, NULL, subtreeSpecificationValidate, NULL},
6259 	{"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' "
6260 		X_BINARY X_NOT_H_R ")",
6261 		SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, berValidate, NULL},
6262 	{"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )",
6263 		0, NULL, printableStringValidate, NULL},
6264 	{"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )",
6265 		0, NULL, NULL, NULL},
6266 	{"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )",
6267 		0, NULL, printablesStringValidate, NULL},
6268 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
6269 	{"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )",
6270 		0, NULL, utcTimeValidate, NULL},
6271 #endif
6272 	{"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )",
6273 		0, NULL, NULL, NULL},
6274 	{"( 1.3.6.1.4.1.1466.115.121.1.55 DESC 'Modify Rights' )",
6275 		0, NULL, NULL, NULL},
6276 	{"( 1.3.6.1.4.1.1466.115.121.1.56 DESC 'LDAP Schema Definition' )",
6277 		0, NULL, NULL, NULL},
6278 	{"( 1.3.6.1.4.1.1466.115.121.1.57 DESC 'LDAP Schema Description' )",
6279 		0, NULL, NULL, NULL},
6280 	{"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )",
6281 		0, NULL, NULL, NULL},
6282 
6283 	/* RFC 2307 NIS Syntaxes */
6284 	{"( 1.3.6.1.1.1.0.0  DESC 'RFC2307 NIS Netgroup Triple' )",
6285 		0, NULL, nisNetgroupTripleValidate, NULL},
6286 	{"( 1.3.6.1.1.1.0.1  DESC 'RFC2307 Boot Parameter' )",
6287 		0, NULL, bootParameterValidate, NULL},
6288 
6289 	/* draft-zeilenga-ldap-x509 */
6290 	{"( 1.3.6.1.1.15.1 DESC 'Certificate Exact Assertion' )",
6291 		SLAP_SYNTAX_HIDE, NULL,
6292 		serialNumberAndIssuerValidate,
6293 		serialNumberAndIssuerPretty},
6294 	{"( 1.3.6.1.1.15.2 DESC 'Certificate Assertion' )",
6295 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6296 	{"( 1.3.6.1.1.15.3 DESC 'Certificate Pair Exact Assertion' )",
6297 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6298 	{"( 1.3.6.1.1.15.4 DESC 'Certificate Pair Assertion' )",
6299 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6300 	{"( 1.3.6.1.1.15.5 DESC 'Certificate List Exact Assertion' )",
6301 		SLAP_SYNTAX_HIDE, NULL,
6302 		issuerAndThisUpdateValidate,
6303 		issuerAndThisUpdatePretty},
6304 	{"( 1.3.6.1.1.15.6 DESC 'Certificate List Assertion' )",
6305 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6306 	{"( 1.3.6.1.1.15.7 DESC 'Algorithm Identifier' )",
6307 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6308 	{"( " attributeCertificateExactAssertionSyntaxOID " DESC 'AttributeCertificate Exact Assertion' )",
6309 		SLAP_SYNTAX_HIDE, NULL,
6310 		serialNumberAndIssuerSerialValidate,
6311 		serialNumberAndIssuerSerialPretty},
6312 	{"( " attributeCertificateAssertionSyntaxOID " DESC 'AttributeCertificate Assertion' )",
6313 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6314 
6315 #ifdef SLAPD_AUTHPASSWD
6316 	/* needs updating */
6317 	{"( 1.3.6.1.4.1.4203.666.2.2 DESC 'OpenLDAP authPassword' )",
6318 		SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
6319 #endif
6320 
6321 	{"( 1.3.6.1.1.16.1 DESC 'UUID' )",
6322 		0, NULL, UUIDValidate, UUIDPretty},
6323 
6324 	{"( 1.3.6.1.4.1.4203.666.11.2.1 DESC 'CSN' )",
6325 		SLAP_SYNTAX_HIDE, NULL, csnValidate, csnPretty },
6326 
6327 	{"( 1.3.6.1.4.1.4203.666.11.2.4 DESC 'CSN SID' )",
6328 		SLAP_SYNTAX_HIDE, NULL, sidValidate, sidPretty },
6329 
6330 	/* OpenLDAP Void Syntax */
6331 	{"( 1.3.6.1.4.1.4203.1.1.1 DESC 'OpenLDAP void' )" ,
6332 		SLAP_SYNTAX_HIDE, NULL, inValidate, NULL},
6333 
6334 	/* FIXME: OID is unused, but not registered yet */
6335 	{"( 1.3.6.1.4.1.4203.666.2.7 DESC 'OpenLDAP authz' )",
6336 		SLAP_SYNTAX_HIDE, NULL, authzValidate, authzPretty},
6337 
6338 	{NULL, 0, NULL, NULL, NULL}
6339 };
6340 
6341 char *csnSIDMatchSyntaxes[] = {
6342 	"1.3.6.1.4.1.4203.666.11.2.1" /* csn */,
6343 	NULL
6344 };
6345 char *certificateExactMatchSyntaxes[] = {
6346 	"1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
6347 	NULL
6348 };
6349 char *certificateListExactMatchSyntaxes[] = {
6350 	"1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */,
6351 	NULL
6352 };
6353 char *attributeCertificateExactMatchSyntaxes[] = {
6354 	attributeCertificateSyntaxOID  /* attributeCertificate */,
6355 	NULL
6356 };
6357 
6358 #ifdef LDAP_COMP_MATCH
6359 char *componentFilterMatchSyntaxes[] = {
6360 	"1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
6361 	"1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */,
6362 	attributeCertificateSyntaxOID /* attributeCertificate */,
6363 	NULL
6364 };
6365 #endif
6366 
6367 char *directoryStringSyntaxes[] = {
6368 	"1.3.6.1.4.1.1466.115.121.1.11" /* countryString */,
6369 	"1.3.6.1.4.1.1466.115.121.1.44" /* printableString */,
6370 	"1.3.6.1.4.1.1466.115.121.1.50" /* telephoneNumber */,
6371 	NULL
6372 };
6373 char *integerFirstComponentMatchSyntaxes[] = {
6374 	"1.3.6.1.4.1.1466.115.121.1.27" /* INTEGER */,
6375 	"1.3.6.1.4.1.1466.115.121.1.17" /* dITStructureRuleDescription */,
6376 	NULL
6377 };
6378 char *objectIdentifierFirstComponentMatchSyntaxes[] = {
6379 	"1.3.6.1.4.1.1466.115.121.1.38" /* OID */,
6380 	"1.3.6.1.4.1.1466.115.121.1.3"  /* attributeTypeDescription */,
6381 	"1.3.6.1.4.1.1466.115.121.1.16" /* dITContentRuleDescription */,
6382 	"1.3.6.1.4.1.1466.115.121.1.54" /* ldapSyntaxDescription */,
6383 	"1.3.6.1.4.1.1466.115.121.1.30" /* matchingRuleDescription */,
6384 	"1.3.6.1.4.1.1466.115.121.1.31" /* matchingRuleUseDescription */,
6385 	"1.3.6.1.4.1.1466.115.121.1.35" /* nameFormDescription */,
6386 	"1.3.6.1.4.1.1466.115.121.1.37" /* objectClassDescription */,
6387 	NULL
6388 };
6389 
6390 /*
6391  * Other matching rules in X.520 that we do not use (yet):
6392  *
6393  * 2.5.13.25	uTCTimeMatch
6394  * 2.5.13.26	uTCTimeOrderingMatch
6395  * 2.5.13.31*	directoryStringFirstComponentMatch
6396  * 2.5.13.32*	wordMatch
6397  * 2.5.13.33*	keywordMatch
6398  * 2.5.13.36+	certificatePairExactMatch
6399  * 2.5.13.37+	certificatePairMatch
6400  * 2.5.13.40+	algorithmIdentifierMatch
6401  * 2.5.13.41*	storedPrefixMatch
6402  * 2.5.13.42	attributeCertificateMatch
6403  * 2.5.13.43	readerAndKeyIDMatch
6404  * 2.5.13.44	attributeIntegrityMatch
6405  *
6406  * (*) described in RFC 3698 (LDAP: Additional Matching Rules)
6407  * (+) described in draft-zeilenga-ldap-x509
6408  */
6409 static slap_mrule_defs_rec mrule_defs[] = {
6410 	/*
6411 	 * EQUALITY matching rules must be listed after associated APPROX
6412 	 * matching rules.  So, we list all APPROX matching rules first.
6413 	 */
6414 	{"( " directoryStringApproxMatchOID " NAME 'directoryStringApproxMatch' "
6415 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6416 		SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
6417 		NULL, NULL, directoryStringApproxMatch,
6418 		directoryStringApproxIndexer, directoryStringApproxFilter,
6419 		NULL},
6420 
6421 	{"( " IA5StringApproxMatchOID " NAME 'IA5StringApproxMatch' "
6422 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6423 		SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
6424 		NULL, NULL, IA5StringApproxMatch,
6425 		IA5StringApproxIndexer, IA5StringApproxFilter,
6426 		NULL},
6427 
6428 	/*
6429 	 * Other matching rules
6430 	 */
6431 
6432 	{"( 2.5.13.0 NAME 'objectIdentifierMatch' "
6433 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
6434 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6435 		NULL, NULL, octetStringMatch,
6436 		octetStringIndexer, octetStringFilter,
6437 		NULL },
6438 
6439 	{"( 2.5.13.1 NAME 'distinguishedNameMatch' "
6440 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6441 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6442 		NULL, dnNormalize, dnMatch,
6443 		octetStringIndexer, octetStringFilter,
6444 		NULL },
6445 
6446 	{"( 1.3.6.1.4.1.4203.666.4.9 NAME 'dnSubtreeMatch' "
6447 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6448 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6449 		NULL, dnNormalize, dnRelativeMatch,
6450 		NULL, NULL,
6451 		NULL },
6452 
6453 	{"( 1.3.6.1.4.1.4203.666.4.8 NAME 'dnOneLevelMatch' "
6454 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6455 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6456 		NULL, dnNormalize, dnRelativeMatch,
6457 		NULL, NULL,
6458 		NULL },
6459 
6460 	{"( 1.3.6.1.4.1.4203.666.4.10 NAME 'dnSubordinateMatch' "
6461 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6462 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6463 		NULL, dnNormalize, dnRelativeMatch,
6464 		NULL, NULL,
6465 		NULL },
6466 
6467 	{"( 1.3.6.1.4.1.4203.666.4.11 NAME 'dnSuperiorMatch' "
6468 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
6469 		SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
6470 		NULL, dnNormalize, dnRelativeMatch,
6471 		NULL, NULL,
6472 		NULL },
6473 
6474 	{"( 1.2.36.79672281.1.13.3 NAME 'rdnMatch' "
6475 		"SYNTAX 1.2.36.79672281.1.5.0 )",
6476 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6477 		NULL, rdnNormalize, rdnMatch,
6478 		octetStringIndexer, octetStringFilter,
6479 		NULL },
6480 
6481 #ifdef LDAP_COMP_MATCH
6482 	{"( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch' "
6483 		"SYNTAX 1.2.36.79672281.1.5.2 )", /* componentFilterMatch assertion */
6484 		SLAP_MR_EXT|SLAP_MR_COMPONENT, componentFilterMatchSyntaxes,
6485 		NULL, NULL , componentFilterMatch,
6486 		octetStringIndexer, octetStringFilter,
6487 		NULL },
6488 
6489         {"( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch' "
6490                 "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
6491                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
6492                 NULL, NULL , allComponentsMatch,
6493                 octetStringIndexer, octetStringFilter,
6494                 NULL },
6495 
6496         {"( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch' "
6497                 "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
6498                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
6499                 NULL, NULL , directoryComponentsMatch,
6500                 octetStringIndexer, octetStringFilter,
6501                 NULL },
6502 #endif
6503 
6504 	{"( 2.5.13.2 NAME 'caseIgnoreMatch' "
6505 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6506 		SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
6507 		NULL, UTF8StringNormalize, octetStringMatch,
6508 		octetStringIndexer, octetStringFilter,
6509 		directoryStringApproxMatchOID },
6510 
6511 	{"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' "
6512 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6513 		SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
6514 		NULL, UTF8StringNormalize, octetStringOrderingMatch,
6515 		NULL, NULL,
6516 		"caseIgnoreMatch" },
6517 
6518 	{"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' "
6519 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6520 		SLAP_MR_SUBSTR, directoryStringSyntaxes,
6521 		NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
6522 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6523 		"caseIgnoreMatch" },
6524 
6525 	{"( 2.5.13.5 NAME 'caseExactMatch' "
6526 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6527 		SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
6528 		NULL, UTF8StringNormalize, octetStringMatch,
6529 		octetStringIndexer, octetStringFilter,
6530 		directoryStringApproxMatchOID },
6531 
6532 	{"( 2.5.13.6 NAME 'caseExactOrderingMatch' "
6533 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
6534 		SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
6535 		NULL, UTF8StringNormalize, octetStringOrderingMatch,
6536 		NULL, NULL,
6537 		"caseExactMatch" },
6538 
6539 	{"( 2.5.13.7 NAME 'caseExactSubstringsMatch' "
6540 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6541 		SLAP_MR_SUBSTR, directoryStringSyntaxes,
6542 		NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
6543 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6544 		"caseExactMatch" },
6545 
6546 	{"( 2.5.13.8 NAME 'numericStringMatch' "
6547 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
6548 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6549 		NULL, numericStringNormalize, octetStringMatch,
6550 		octetStringIndexer, octetStringFilter,
6551 		NULL },
6552 
6553 	{"( 2.5.13.9 NAME 'numericStringOrderingMatch' "
6554 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
6555 		SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
6556 		NULL, numericStringNormalize, octetStringOrderingMatch,
6557 		NULL, NULL,
6558 		"numericStringMatch" },
6559 
6560 	{"( 2.5.13.10 NAME 'numericStringSubstringsMatch' "
6561 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6562 		SLAP_MR_SUBSTR, NULL,
6563 		NULL, numericStringNormalize, octetStringSubstringsMatch,
6564 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6565 		"numericStringMatch" },
6566 
6567 	{"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
6568 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", /* Postal Address */
6569 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6570 		NULL, postalAddressNormalize, octetStringMatch,
6571 		octetStringIndexer, octetStringFilter,
6572 		NULL },
6573 
6574 	{"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' "
6575 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6576 		SLAP_MR_SUBSTR, NULL,
6577 		NULL, NULL, NULL, NULL, NULL,
6578 		"caseIgnoreListMatch" },
6579 
6580 	{"( 2.5.13.13 NAME 'booleanMatch' "
6581 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )",
6582 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6583 		NULL, NULL, booleanMatch,
6584 		octetStringIndexer, octetStringFilter,
6585 		NULL },
6586 
6587 	{"( 2.5.13.14 NAME 'integerMatch' "
6588 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
6589 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6590 		NULL, NULL, integerMatch,
6591 		integerIndexer, integerFilter,
6592 		NULL },
6593 
6594 	{"( 2.5.13.15 NAME 'integerOrderingMatch' "
6595 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
6596 		SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6597 		NULL, NULL, integerMatch,
6598 		NULL, NULL,
6599 		"integerMatch" },
6600 
6601 	{"( 2.5.13.16 NAME 'bitStringMatch' "
6602 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )",
6603 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6604 		NULL, NULL, octetStringMatch,
6605 		octetStringIndexer, octetStringFilter,
6606 		NULL },
6607 
6608 	{"( 2.5.13.17 NAME 'octetStringMatch' "
6609 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
6610 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6611 		NULL, NULL, octetStringMatch,
6612 		octetStringIndexer, octetStringFilter,
6613 		NULL },
6614 
6615 	{"( 2.5.13.18 NAME 'octetStringOrderingMatch' "
6616 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
6617 		SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
6618 		NULL, NULL, octetStringOrderingMatch,
6619 		NULL, NULL,
6620 		"octetStringMatch" },
6621 
6622 	{"( 2.5.13.19 NAME 'octetStringSubstringsMatch' "
6623 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
6624 		SLAP_MR_SUBSTR, NULL,
6625 		NULL, NULL, octetStringSubstringsMatch,
6626 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6627 		"octetStringMatch" },
6628 
6629 	{"( 2.5.13.20 NAME 'telephoneNumberMatch' "
6630 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )",
6631 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6632 		NULL,
6633 		telephoneNumberNormalize, octetStringMatch,
6634 		octetStringIndexer, octetStringFilter,
6635 		NULL },
6636 
6637 	{"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' "
6638 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
6639 		SLAP_MR_SUBSTR, NULL,
6640 		NULL, telephoneNumberNormalize, octetStringSubstringsMatch,
6641 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6642 		"telephoneNumberMatch" },
6643 
6644 	{"( 2.5.13.22 NAME 'presentationAddressMatch' "
6645 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )",
6646 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6647 		NULL, NULL, NULL, NULL, NULL, NULL },
6648 
6649 	{"( 2.5.13.23 NAME 'uniqueMemberMatch' "
6650 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )", /* Name And Optional UID */
6651 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6652 		NULL, uniqueMemberNormalize, uniqueMemberMatch,
6653 		uniqueMemberIndexer, uniqueMemberFilter,
6654 		NULL },
6655 
6656 	{"( 2.5.13.24 NAME 'protocolInformationMatch' "
6657 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )",
6658 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6659 		NULL, NULL, NULL, NULL, NULL, NULL },
6660 
6661 	{"( 2.5.13.27 NAME 'generalizedTimeMatch' "
6662 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
6663 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6664 		NULL, generalizedTimeNormalize, octetStringMatch,
6665 		generalizedTimeIndexer, generalizedTimeFilter,
6666 		NULL },
6667 
6668 	{"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' "
6669 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
6670 		SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6671 		NULL, generalizedTimeNormalize, generalizedTimeOrderingMatch,
6672 		NULL, NULL,
6673 		"generalizedTimeMatch" },
6674 
6675 	{"( 2.5.13.29 NAME 'integerFirstComponentMatch' "
6676 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
6677 		SLAP_MR_EQUALITY | SLAP_MR_EXT,
6678 			integerFirstComponentMatchSyntaxes,
6679 		NULL, firstComponentNormalize, integerMatch,
6680 		octetStringIndexer, octetStringFilter,
6681 		NULL },
6682 
6683 	{"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' "
6684 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", /* OID */
6685 		SLAP_MR_EQUALITY | SLAP_MR_EXT,
6686 			objectIdentifierFirstComponentMatchSyntaxes,
6687 		NULL, firstComponentNormalize, octetStringMatch,
6688 		octetStringIndexer, octetStringFilter,
6689 		NULL },
6690 
6691 	{"( 2.5.13.34 NAME 'certificateExactMatch' "
6692 		"SYNTAX 1.3.6.1.1.15.1 )", /* Certificate Exact Assertion */
6693 		SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes,
6694 		NULL, certificateExactNormalize, octetStringMatch,
6695 		octetStringIndexer, octetStringFilter,
6696 		NULL },
6697 
6698 	{"( 2.5.13.35 NAME 'certificateMatch' "
6699 		"SYNTAX 1.3.6.1.1.15.2 )", /* Certificate Assertion */
6700 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6701 		NULL, NULL, NULL, NULL, NULL,
6702 		NULL },
6703 
6704 	{"( 2.5.13.38 NAME 'certificateListExactMatch' "
6705 		"SYNTAX 1.3.6.1.1.15.5 )", /* Certificate List Exact Assertion */
6706 		SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateListExactMatchSyntaxes,
6707 		NULL, certificateListExactNormalize, octetStringMatch,
6708 		octetStringIndexer, octetStringFilter,
6709 		NULL },
6710 
6711 	{"( 2.5.13.39 NAME 'certificateListMatch' "
6712 		"SYNTAX 1.3.6.1.1.15.6 )", /* Certificate List Assertion */
6713 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6714 		NULL, NULL, NULL, NULL, NULL,
6715 		NULL },
6716 
6717 	{"( 2.5.13.45 NAME 'attributeCertificateExactMatch' "
6718 		"SYNTAX " attributeCertificateExactAssertionSyntaxOID " )",
6719 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, attributeCertificateExactMatchSyntaxes,
6720 		NULL, attributeCertificateExactNormalize, octetStringMatch,
6721 		octetStringIndexer, octetStringFilter,
6722 		NULL },
6723 
6724 	{"( 2.5.13.46 NAME 'attributeCertificateMatch' "
6725 		"SYNTAX " attributeCertificateAssertionSyntaxOID " )",
6726 		SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, NULL,
6727 		NULL, NULL, NULL, NULL, NULL,
6728 		NULL },
6729 
6730 	{"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' "
6731 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6732 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6733 		NULL, IA5StringNormalize, octetStringMatch,
6734 		octetStringIndexer, octetStringFilter,
6735 		IA5StringApproxMatchOID },
6736 
6737 	{"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' "
6738 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6739 		SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
6740 		NULL, IA5StringNormalize, octetStringMatch,
6741 		octetStringIndexer, octetStringFilter,
6742 		IA5StringApproxMatchOID },
6743 
6744 	{"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' "
6745 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6746 		SLAP_MR_SUBSTR, NULL,
6747 		NULL, IA5StringNormalize, directoryStringSubstringsMatch,
6748 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6749 		"caseIgnoreIA5Match" },
6750 
6751 	{"( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch' "
6752 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
6753 		SLAP_MR_SUBSTR, NULL,
6754 		NULL, IA5StringNormalize, directoryStringSubstringsMatch,
6755 		octetStringSubstringsIndexer, octetStringSubstringsFilter,
6756 		"caseExactIA5Match" },
6757 
6758 #ifdef SLAPD_AUTHPASSWD
6759 	/* needs updating */
6760 	{"( 1.3.6.1.4.1.4203.666.4.1 NAME 'authPasswordMatch' "
6761 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", /* Octet String */
6762 		SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
6763 		NULL, NULL, authPasswordMatch,
6764 		NULL, NULL,
6765 		NULL},
6766 #endif
6767 
6768 	{"( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch' "
6769 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
6770 		SLAP_MR_EXT, NULL,
6771 		NULL, NULL, integerBitAndMatch,
6772 		NULL, NULL,
6773 		"integerMatch" },
6774 
6775 	{"( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch' "
6776 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
6777 		SLAP_MR_EXT, NULL,
6778 		NULL, NULL, integerBitOrMatch,
6779 		NULL, NULL,
6780 		"integerMatch" },
6781 
6782 	{"( 1.3.6.1.1.16.2 NAME 'UUIDMatch' "
6783 		"SYNTAX 1.3.6.1.1.16.1 )",
6784 		SLAP_MR_EQUALITY | SLAP_MR_MUTATION_NORMALIZER, NULL,
6785 		NULL, UUIDNormalize, octetStringMatch,
6786 		octetStringIndexer, octetStringFilter,
6787 		NULL},
6788 
6789 	{"( 1.3.6.1.1.16.3 NAME 'UUIDOrderingMatch' "
6790 		"SYNTAX 1.3.6.1.1.16.1 )",
6791 		SLAP_MR_ORDERING | SLAP_MR_MUTATION_NORMALIZER, NULL,
6792 		NULL, UUIDNormalize, octetStringOrderingMatch,
6793 		octetStringIndexer, octetStringFilter,
6794 		"UUIDMatch"},
6795 
6796 	{"( 1.3.6.1.4.1.4203.666.11.2.2 NAME 'CSNMatch' "
6797 		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
6798 		SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_ORDERED_INDEX, NULL,
6799 		NULL, csnNormalize, csnMatch,
6800 		csnIndexer, csnFilter,
6801 		NULL},
6802 
6803 	{"( 1.3.6.1.4.1.4203.666.11.2.3 NAME 'CSNOrderingMatch' "
6804 		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
6805 		SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
6806 		NULL, csnNormalize, csnOrderingMatch,
6807 		NULL, NULL,
6808 		"CSNMatch" },
6809 
6810 	{"( 1.3.6.1.4.1.4203.666.11.2.5 NAME 'CSNSIDMatch' "
6811 		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.4 )",
6812 		SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_EXT, csnSIDMatchSyntaxes,
6813 		NULL, csnSidNormalize, octetStringMatch,
6814 		octetStringIndexer, octetStringFilter,
6815 		NULL },
6816 
6817 	/* FIXME: OID is unused, but not registered yet */
6818 	{"( 1.3.6.1.4.1.4203.666.4.12 NAME 'authzMatch' "
6819 		"SYNTAX 1.3.6.1.4.1.4203.666.2.7 )", /* OpenLDAP authz */
6820 		SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
6821 		NULL, authzNormalize, authzMatch,
6822 		NULL, NULL,
6823 		NULL},
6824 
6825 	{NULL, SLAP_MR_NONE, NULL,
6826 		NULL, NULL, NULL, NULL, NULL,
6827 		NULL }
6828 };
6829 
6830 int
6831 slap_schema_init( void )
6832 {
6833 	int		res;
6834 	int		i;
6835 
6836 	/* we should only be called once (from main) */
6837 	assert( schema_init_done == 0 );
6838 
6839 	for ( i=0; syntax_defs[i].sd_desc != NULL; i++ ) {
6840 		res = register_syntax( &syntax_defs[i] );
6841 
6842 		if ( res ) {
6843 			fprintf( stderr, "slap_schema_init: Error registering syntax %s\n",
6844 				 syntax_defs[i].sd_desc );
6845 			return LDAP_OTHER;
6846 		}
6847 	}
6848 
6849 	for ( i=0; mrule_defs[i].mrd_desc != NULL; i++ ) {
6850 		if( mrule_defs[i].mrd_usage == SLAP_MR_NONE &&
6851 			mrule_defs[i].mrd_compat_syntaxes == NULL )
6852 		{
6853 			fprintf( stderr,
6854 				"slap_schema_init: Ignoring unusable matching rule %s\n",
6855 				 mrule_defs[i].mrd_desc );
6856 			continue;
6857 		}
6858 
6859 		res = register_matching_rule( &mrule_defs[i] );
6860 
6861 		if ( res ) {
6862 			fprintf( stderr,
6863 				"slap_schema_init: Error registering matching rule %s\n",
6864 				 mrule_defs[i].mrd_desc );
6865 			return LDAP_OTHER;
6866 		}
6867 	}
6868 
6869 	res = slap_schema_load();
6870 	schema_init_done = 1;
6871 	return res;
6872 }
6873 
6874 void
6875 schema_destroy( void )
6876 {
6877 	oidm_destroy();
6878 	oc_destroy();
6879 	at_destroy();
6880 	mr_destroy();
6881 	mru_destroy();
6882 	syn_destroy();
6883 
6884 	if( schema_init_done ) {
6885 		ldap_pvt_thread_mutex_destroy( &ad_index_mutex );
6886 		ldap_pvt_thread_mutex_destroy( &ad_undef_mutex );
6887 		ldap_pvt_thread_mutex_destroy( &oc_undef_mutex );
6888 	}
6889 }
6890