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