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