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