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