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