]> git.sur5r.net Git - openldap/blob - servers/slapd/schema_init.c
cleanup error messages; complete privateDB control and queryDelete exop specifications
[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-2007 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 csnValidate                             blobValidate
54 #define csnMatch                                octetStringMatch
55 #define csnOrderingMatch                octetStringOrderingMatch
56 #define csnIndexer                              generalizedTimeIndexer
57 #define csnFilter                               generalizedTimeFilter
58
59 /* FIXME: temporary */
60 #define authzMatch                              octetStringMatch
61
62 unsigned int index_substr_if_minlen = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT;
63 unsigned int index_substr_if_maxlen = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT;
64 unsigned int index_substr_any_len = SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT;
65 unsigned int index_substr_any_step = SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT;
66
67 ldap_pvt_thread_mutex_t ad_undef_mutex;
68 ldap_pvt_thread_mutex_t oc_undef_mutex;
69
70 static int
71 inValidate(
72         Syntax *syntax,
73         struct berval *in )
74 {
75         /* no value allowed */
76         return LDAP_INVALID_SYNTAX;
77 }
78
79 static int
80 blobValidate(
81         Syntax *syntax,
82         struct berval *in )
83 {
84         /* any value allowed */
85         return LDAP_SUCCESS;
86 }
87
88 #define berValidate blobValidate
89
90 static int
91 sequenceValidate(
92         Syntax *syntax,
93         struct berval *in )
94 {
95         if ( in->bv_len < 2 ) return LDAP_INVALID_SYNTAX;
96         if ( in->bv_val[0] != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
97
98         return LDAP_SUCCESS;
99 }
100
101 /* X.509 related stuff */
102
103 enum {
104         SLAP_X509_V1            = 0,
105         SLAP_X509_V2            = 1,
106         SLAP_X509_V3            = 2
107 };
108
109 #define SLAP_X509_OPTION        (LBER_CLASS_CONTEXT|LBER_CONSTRUCTED)
110
111 enum {
112         SLAP_X509_OPT_C_VERSION         = SLAP_X509_OPTION + 0,
113         SLAP_X509_OPT_C_ISSUERUNIQUEID  = SLAP_X509_OPTION + 1,
114         SLAP_X509_OPT_C_SUBJECTUNIQUEID = SLAP_X509_OPTION + 2,
115         SLAP_X509_OPT_C_EXTENSIONS      = SLAP_X509_OPTION + 3
116 };
117
118 enum {
119         SLAP_X509_OPT_CL_CRLEXTENSIONS  = SLAP_X509_OPTION + 0
120 };
121
122 /* X.509 certificate validation */
123 static int certificateValidate( Syntax *syntax, struct berval *in )
124 {
125         BerElementBuffer berbuf;
126         BerElement *ber = (BerElement *)&berbuf;
127         ber_tag_t tag;
128         ber_len_t len;
129         ber_int_t version = SLAP_X509_V1;
130
131         ber_init2( ber, in, LBER_USE_DER );
132         tag = ber_skip_tag( ber, &len );        /* Signed wrapper */
133         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
134         tag = ber_skip_tag( ber, &len );        /* Sequence */
135         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
136         tag = ber_peek_tag( ber, &len );
137         /* Optional version */
138         if ( tag == SLAP_X509_OPT_C_VERSION ) {
139                 tag = ber_skip_tag( ber, &len );
140                 tag = ber_get_int( ber, &version );
141                 if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
142         }
143         /* NOTE: don't try to parse Serial, because it might be longer
144          * than sizeof(ber_int_t); deferred to certificateExactNormalize() */
145         tag = ber_skip_tag( ber, &len );        /* Serial */
146         if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
147         ber_skip_data( ber, len );
148         tag = ber_skip_tag( ber, &len );        /* Signature Algorithm */
149         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
150         ber_skip_data( ber, len );
151         tag = ber_skip_tag( ber, &len );        /* Issuer DN */
152         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
153         ber_skip_data( ber, len );
154         tag = ber_skip_tag( ber, &len );        /* Validity */
155         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
156         ber_skip_data( ber, len );
157         tag = ber_skip_tag( ber, &len );        /* Subject DN */
158         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
159         ber_skip_data( ber, len );
160         tag = ber_skip_tag( ber, &len );        /* Subject PublicKeyInfo */
161         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
162         ber_skip_data( ber, len );
163         tag = ber_skip_tag( ber, &len );
164         if ( tag == SLAP_X509_OPT_C_ISSUERUNIQUEID ) {  /* issuerUniqueID */
165                 if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
166                 ber_skip_data( ber, len );
167                 tag = ber_skip_tag( ber, &len );
168         }
169         if ( tag == SLAP_X509_OPT_C_SUBJECTUNIQUEID ) { /* subjectUniqueID */
170                 if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
171                 ber_skip_data( ber, len );
172                 tag = ber_skip_tag( ber, &len );
173         }
174         if ( tag == SLAP_X509_OPT_C_EXTENSIONS ) {      /* Extensions */
175                 if ( version < SLAP_X509_V3 ) return LDAP_INVALID_SYNTAX;
176                 tag = ber_skip_tag( ber, &len );
177                 if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
178                 ber_skip_data( ber, len );
179                 tag = ber_skip_tag( ber, &len );
180         }
181         /* signatureAlgorithm */
182         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
183         ber_skip_data( ber, len );
184         tag = ber_skip_tag( ber, &len );
185         /* Signature */
186         if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; 
187         ber_skip_data( ber, len );
188         tag = ber_skip_tag( ber, &len );
189         /* Must be at end now */
190         if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX;
191         return LDAP_SUCCESS;
192 }
193
194 /* X.509 certificate list validation */
195 #ifdef LDAP_DEVEL
196 static int certificateListValidate( Syntax *syntax, struct berval *in )
197 {
198         BerElementBuffer berbuf;
199         BerElement *ber = (BerElement *)&berbuf;
200         ber_tag_t tag;
201         ber_len_t len;
202         ber_int_t version = SLAP_X509_V1;
203
204         ber_init2( ber, in, LBER_USE_DER );
205         tag = ber_skip_tag( ber, &len );        /* Signed wrapper */
206         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
207         tag = ber_skip_tag( ber, &len );        /* Sequence */
208         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
209         tag = ber_peek_tag( ber, &len );
210         /* Optional version */
211         if ( tag == LBER_INTEGER ) {
212                 tag = ber_get_int( ber, &version );
213                 assert( tag == LBER_INTEGER );
214                 if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
215         }
216         tag = ber_skip_tag( ber, &len );        /* Signature Algorithm */
217         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
218         ber_skip_data( ber, len );
219         tag = ber_skip_tag( ber, &len );        /* Issuer DN */
220         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
221         ber_skip_data( ber, len );
222         tag = ber_skip_tag( ber, &len );        /* thisUpdate */
223         /* NOTE: in the certificates I'm playing with, the time is UTC.
224          * maybe the tag is different from 0x17U for generalizedTime? */
225         if ( tag != 0x17U ) return LDAP_INVALID_SYNTAX;
226         ber_skip_data( ber, len );
227         /* Optional nextUpdate */
228         tag = ber_skip_tag( ber, &len );
229         if ( tag == 0x17U ) {
230                 ber_skip_data( ber, len );
231                 tag = ber_skip_tag( ber, &len );
232         }
233         /* Optional revokedCertificates */
234         if ( tag == LBER_SEQUENCE ) {
235                 /* Should NOT be empty */
236                 ber_skip_data( ber, len );
237                 tag = ber_skip_tag( ber, &len );
238         }
239         /* Optional Extensions */
240         if ( tag == SLAP_X509_OPT_CL_CRLEXTENSIONS ) { /* ? */
241                 if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
242                 tag = ber_skip_tag( ber, &len );
243                 if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
244                 ber_skip_data( ber, len );
245                 tag = ber_skip_tag( ber, &len );
246         }
247         /* signatureAlgorithm */
248         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
249         ber_skip_data( ber, len );
250         tag = ber_skip_tag( ber, &len );
251         /* Signature */
252         if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; 
253         ber_skip_data( ber, len );
254         tag = ber_skip_tag( ber, &len );
255         /* Must be at end now */
256         if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX;
257         return LDAP_SUCCESS;
258 }
259 #else /* ! LDAP_DEVEL */
260 #define certificateListValidate sequenceValidate
261 #endif /* ! LDAP_DEVEL */
262
263 int
264 octetStringMatch(
265         int *matchp,
266         slap_mask_t flags,
267         Syntax *syntax,
268         MatchingRule *mr,
269         struct berval *value,
270         void *assertedValue )
271 {
272         struct berval *asserted = (struct berval *) assertedValue;
273         int match = value->bv_len - asserted->bv_len;
274
275         if( match == 0 ) {
276                 match = memcmp( value->bv_val, asserted->bv_val, value->bv_len );
277         }
278
279         *matchp = match;
280         return LDAP_SUCCESS;
281 }
282
283 static int
284 octetStringOrderingMatch(
285         int *matchp,
286         slap_mask_t flags,
287         Syntax *syntax,
288         MatchingRule *mr,
289         struct berval *value,
290         void *assertedValue )
291 {
292         struct berval *asserted = (struct berval *) assertedValue;
293         ber_len_t v_len  = value->bv_len;
294         ber_len_t av_len = asserted->bv_len;
295
296         int match = memcmp( value->bv_val, asserted->bv_val,
297                 (v_len < av_len ? v_len : av_len) );
298
299         if( match == 0 ) match = v_len - av_len;
300
301         *matchp = match;
302         return LDAP_SUCCESS;
303 }
304
305 static void
306 hashPreset(
307         HASH_CONTEXT *HASHcontext,
308         struct berval *prefix,
309         char pre,
310         Syntax *syntax,
311         MatchingRule *mr)
312 {
313         HASH_Init(HASHcontext);
314         if(prefix && prefix->bv_len > 0) {
315                 HASH_Update(HASHcontext,
316                         (unsigned char *)prefix->bv_val, prefix->bv_len);
317         }
318         if(pre) HASH_Update(HASHcontext, (unsigned char*)&pre, sizeof(pre));
319         HASH_Update(HASHcontext, (unsigned char*)syntax->ssyn_oid, syntax->ssyn_oidlen);
320         HASH_Update(HASHcontext, (unsigned char*)mr->smr_oid, mr->smr_oidlen);
321         return;
322 }
323
324 static void
325 hashIter(
326         HASH_CONTEXT *HASHcontext,
327         unsigned char *HASHdigest,
328         unsigned char *value,
329         int len)
330 {
331         HASH_CONTEXT ctx = *HASHcontext;
332         HASH_Update( &ctx, value, len );
333         HASH_Final( HASHdigest, &ctx );
334 }
335
336 /* Index generation function */
337 int octetStringIndexer(
338         slap_mask_t use,
339         slap_mask_t flags,
340         Syntax *syntax,
341         MatchingRule *mr,
342         struct berval *prefix,
343         BerVarray values,
344         BerVarray *keysp,
345         void *ctx )
346 {
347         int i;
348         size_t slen, mlen;
349         BerVarray keys;
350         HASH_CONTEXT HASHcontext;
351         unsigned char HASHdigest[HASH_BYTES];
352         struct berval digest;
353         digest.bv_val = (char *)HASHdigest;
354         digest.bv_len = sizeof(HASHdigest);
355
356         for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
357                 /* just count them */
358         }
359
360         /* we should have at least one value at this point */
361         assert( i > 0 );
362
363         keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
364
365         slen = syntax->ssyn_oidlen;
366         mlen = mr->smr_oidlen;
367
368         hashPreset( &HASHcontext, prefix, 0, syntax, mr);
369         for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
370                 hashIter( &HASHcontext, HASHdigest,
371                         (unsigned char *)values[i].bv_val, values[i].bv_len );
372                 ber_dupbv_x( &keys[i], &digest, ctx );
373         }
374
375         BER_BVZERO( &keys[i] );
376
377         *keysp = keys;
378
379         return LDAP_SUCCESS;
380 }
381
382 /* Index generation function */
383 int octetStringFilter(
384         slap_mask_t use,
385         slap_mask_t flags,
386         Syntax *syntax,
387         MatchingRule *mr,
388         struct berval *prefix,
389         void * assertedValue,
390         BerVarray *keysp,
391         void *ctx )
392 {
393         size_t slen, mlen;
394         BerVarray keys;
395         HASH_CONTEXT HASHcontext;
396         unsigned char HASHdigest[HASH_BYTES];
397         struct berval *value = (struct berval *) assertedValue;
398         struct berval digest;
399         digest.bv_val = (char *)HASHdigest;
400         digest.bv_len = sizeof(HASHdigest);
401
402         slen = syntax->ssyn_oidlen;
403         mlen = mr->smr_oidlen;
404
405         keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
406
407         hashPreset( &HASHcontext, prefix, 0, syntax, mr );
408         hashIter( &HASHcontext, HASHdigest,
409                 (unsigned char *)value->bv_val, value->bv_len );
410
411         ber_dupbv_x( keys, &digest, ctx );
412         BER_BVZERO( &keys[1] );
413
414         *keysp = keys;
415
416         return LDAP_SUCCESS;
417 }
418
419 static int
420 octetStringSubstringsMatch(
421         int *matchp,
422         slap_mask_t flags,
423         Syntax *syntax,
424         MatchingRule *mr,
425         struct berval *value,
426         void *assertedValue )
427 {
428         int match = 0;
429         SubstringsAssertion *sub = assertedValue;
430         struct berval left = *value;
431         int i;
432         ber_len_t inlen = 0;
433
434         /* Add up asserted input length */
435         if ( !BER_BVISNULL( &sub->sa_initial ) ) {
436                 inlen += sub->sa_initial.bv_len;
437         }
438         if ( sub->sa_any ) {
439                 for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
440                         inlen += sub->sa_any[i].bv_len;
441                 }
442         }
443         if ( !BER_BVISNULL( &sub->sa_final ) ) {
444                 inlen += sub->sa_final.bv_len;
445         }
446
447         if ( !BER_BVISNULL( &sub->sa_initial ) ) {
448                 if ( inlen > left.bv_len ) {
449                         match = 1;
450                         goto done;
451                 }
452
453                 match = memcmp( sub->sa_initial.bv_val, left.bv_val,
454                         sub->sa_initial.bv_len );
455
456                 if ( match != 0 ) {
457                         goto done;
458                 }
459
460                 left.bv_val += sub->sa_initial.bv_len;
461                 left.bv_len -= sub->sa_initial.bv_len;
462                 inlen -= sub->sa_initial.bv_len;
463         }
464
465         if ( !BER_BVISNULL( &sub->sa_final ) ) {
466                 if ( inlen > left.bv_len ) {
467                         match = 1;
468                         goto done;
469                 }
470
471                 match = memcmp( sub->sa_final.bv_val,
472                         &left.bv_val[left.bv_len - sub->sa_final.bv_len],
473                         sub->sa_final.bv_len );
474
475                 if ( match != 0 ) {
476                         goto done;
477                 }
478
479                 left.bv_len -= sub->sa_final.bv_len;
480                 inlen -= sub->sa_final.bv_len;
481         }
482
483         if ( sub->sa_any ) {
484                 for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
485                         ber_len_t idx;
486                         char *p;
487
488 retry:
489                         if ( inlen > left.bv_len ) {
490                                 /* not enough length */
491                                 match = 1;
492                                 goto done;
493                         }
494
495                         if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) {
496                                 continue;
497                         }
498
499                         p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len );
500
501                         if( p == NULL ) {
502                                 match = 1;
503                                 goto done;
504                         }
505
506                         idx = p - left.bv_val;
507
508                         if ( idx >= left.bv_len ) {
509                                 /* this shouldn't happen */
510                                 return LDAP_OTHER;
511                         }
512
513                         left.bv_val = p;
514                         left.bv_len -= idx;
515
516                         if ( sub->sa_any[i].bv_len > left.bv_len ) {
517                                 /* not enough left */
518                                 match = 1;
519                                 goto done;
520                         }
521
522                         match = memcmp( left.bv_val,
523                                 sub->sa_any[i].bv_val,
524                                 sub->sa_any[i].bv_len );
525
526                         if ( match != 0 ) {
527                                 left.bv_val++;
528                                 left.bv_len--;
529                                 goto retry;
530                         }
531
532                         left.bv_val += sub->sa_any[i].bv_len;
533                         left.bv_len -= sub->sa_any[i].bv_len;
534                         inlen -= sub->sa_any[i].bv_len;
535                 }
536         }
537
538 done:
539         *matchp = match;
540         return LDAP_SUCCESS;
541 }
542
543 /* Substrings Index generation function */
544 static int
545 octetStringSubstringsIndexer(
546         slap_mask_t use,
547         slap_mask_t flags,
548         Syntax *syntax,
549         MatchingRule *mr,
550         struct berval *prefix,
551         BerVarray values,
552         BerVarray *keysp,
553         void *ctx )
554 {
555         ber_len_t i, nkeys;
556         size_t slen, mlen;
557         BerVarray keys;
558
559         HASH_CONTEXT HCany, HCini, HCfin;
560         unsigned char HASHdigest[HASH_BYTES];
561         struct berval digest;
562         digest.bv_val = (char *)HASHdigest;
563         digest.bv_len = sizeof(HASHdigest);
564
565         nkeys = 0;
566
567         for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
568                 /* count number of indices to generate */
569                 if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
570                         if( values[i].bv_len >= index_substr_if_maxlen ) {
571                                 nkeys += index_substr_if_maxlen -
572                                         (index_substr_if_minlen - 1);
573                         } else if( values[i].bv_len >= index_substr_if_minlen ) {
574                                 nkeys += values[i].bv_len - (index_substr_if_minlen - 1);
575                         }
576                 }
577
578                 if( flags & SLAP_INDEX_SUBSTR_ANY ) {
579                         if( values[i].bv_len >= index_substr_any_len ) {
580                                 nkeys += values[i].bv_len - (index_substr_any_len - 1);
581                         }
582                 }
583
584                 if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
585                         if( values[i].bv_len >= index_substr_if_maxlen ) {
586                                 nkeys += index_substr_if_maxlen -
587                                         (index_substr_if_minlen - 1);
588                         } else if( values[i].bv_len >= index_substr_if_minlen ) {
589                                 nkeys += values[i].bv_len - (index_substr_if_minlen - 1);
590                         }
591                 }
592         }
593
594         if( nkeys == 0 ) {
595                 /* no keys to generate */
596                 *keysp = NULL;
597                 return LDAP_SUCCESS;
598         }
599
600         keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx );
601
602         slen = syntax->ssyn_oidlen;
603         mlen = mr->smr_oidlen;
604
605         if ( flags & SLAP_INDEX_SUBSTR_ANY )
606                 hashPreset( &HCany, prefix, SLAP_INDEX_SUBSTR_PREFIX, syntax, mr );
607         if( flags & SLAP_INDEX_SUBSTR_INITIAL )
608                 hashPreset( &HCini, prefix, SLAP_INDEX_SUBSTR_INITIAL_PREFIX, syntax, mr );
609         if( flags & SLAP_INDEX_SUBSTR_FINAL )
610                 hashPreset( &HCfin, prefix, SLAP_INDEX_SUBSTR_FINAL_PREFIX, syntax, mr );
611
612         nkeys = 0;
613         for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
614                 ber_len_t j,max;
615
616                 if( ( flags & SLAP_INDEX_SUBSTR_ANY ) &&
617                         ( values[i].bv_len >= index_substr_any_len ) )
618                 {
619                         max = values[i].bv_len - (index_substr_any_len - 1);
620
621                         for( j=0; j<max; j++ ) {
622                                 hashIter( &HCany, HASHdigest,
623                                         (unsigned char *)&values[i].bv_val[j],
624                                         index_substr_any_len );
625                                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
626                         }
627                 }
628
629                 /* skip if too short */ 
630                 if( values[i].bv_len < index_substr_if_minlen ) continue;
631
632                 max = index_substr_if_maxlen < values[i].bv_len
633                         ? index_substr_if_maxlen : values[i].bv_len;
634
635                 for( j=index_substr_if_minlen; j<=max; j++ ) {
636
637                         if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
638                                 hashIter( &HCini, HASHdigest,
639                                         (unsigned char *)values[i].bv_val, j );
640                                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
641                         }
642
643                         if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
644                                 hashIter( &HCfin, HASHdigest,
645                                         (unsigned char *)&values[i].bv_val[values[i].bv_len-j], j );
646                                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
647                         }
648
649                 }
650         }
651
652         if( nkeys > 0 ) {
653                 BER_BVZERO( &keys[nkeys] );
654                 *keysp = keys;
655         } else {
656                 ch_free( keys );
657                 *keysp = NULL;
658         }
659
660         return LDAP_SUCCESS;
661 }
662
663 static int
664 octetStringSubstringsFilter (
665         slap_mask_t use,
666         slap_mask_t flags,
667         Syntax *syntax,
668         MatchingRule *mr,
669         struct berval *prefix,
670         void * assertedValue,
671         BerVarray *keysp,
672         void *ctx)
673 {
674         SubstringsAssertion *sa;
675         char pre;
676         ber_len_t nkeys = 0;
677         size_t slen, mlen, klen;
678         BerVarray keys;
679         HASH_CONTEXT HASHcontext;
680         unsigned char HASHdigest[HASH_BYTES];
681         struct berval *value;
682         struct berval digest;
683
684         sa = (SubstringsAssertion *) assertedValue;
685
686         if( flags & SLAP_INDEX_SUBSTR_INITIAL &&
687                 !BER_BVISNULL( &sa->sa_initial ) &&
688                 sa->sa_initial.bv_len >= index_substr_if_minlen )
689         {
690                 nkeys++;
691                 if ( sa->sa_initial.bv_len > index_substr_if_maxlen &&
692                         ( flags & SLAP_INDEX_SUBSTR_ANY ))
693                 {
694                         nkeys += 1 + (sa->sa_initial.bv_len - index_substr_if_maxlen) / index_substr_any_step;
695                 }
696         }
697
698         if ( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
699                 ber_len_t i;
700                 for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) {
701                         if( sa->sa_any[i].bv_len >= index_substr_any_len ) {
702                                 /* don't bother accounting with stepping */
703                                 nkeys += sa->sa_any[i].bv_len -
704                                         ( index_substr_any_len - 1 );
705                         }
706                 }
707         }
708
709         if( flags & SLAP_INDEX_SUBSTR_FINAL &&
710                 !BER_BVISNULL( &sa->sa_final ) &&
711                 sa->sa_final.bv_len >= index_substr_if_minlen )
712         {
713                 nkeys++;
714                 if ( sa->sa_final.bv_len > index_substr_if_maxlen &&
715                         ( flags & SLAP_INDEX_SUBSTR_ANY ))
716                 {
717                         nkeys += 1 + (sa->sa_final.bv_len - index_substr_if_maxlen) / index_substr_any_step;
718                 }
719         }
720
721         if( nkeys == 0 ) {
722                 *keysp = NULL;
723                 return LDAP_SUCCESS;
724         }
725
726         digest.bv_val = (char *)HASHdigest;
727         digest.bv_len = sizeof(HASHdigest);
728
729         slen = syntax->ssyn_oidlen;
730         mlen = mr->smr_oidlen;
731
732         keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx );
733         nkeys = 0;
734
735         if( flags & SLAP_INDEX_SUBSTR_INITIAL &&
736                 !BER_BVISNULL( &sa->sa_initial ) &&
737                 sa->sa_initial.bv_len >= index_substr_if_minlen )
738         {
739                 pre = SLAP_INDEX_SUBSTR_INITIAL_PREFIX;
740                 value = &sa->sa_initial;
741
742                 klen = index_substr_if_maxlen < value->bv_len
743                         ? index_substr_if_maxlen : value->bv_len;
744
745                 hashPreset( &HASHcontext, prefix, pre, syntax, mr );
746                 hashIter( &HASHcontext, HASHdigest,
747                         (unsigned char *)value->bv_val, klen );
748                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
749
750                 /* If initial is too long and we have subany indexed, use it
751                  * to match the excess...
752                  */
753                 if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY))
754                 {
755                         ber_len_t j;
756                         pre = SLAP_INDEX_SUBSTR_PREFIX;
757                         hashPreset( &HASHcontext, prefix, pre, syntax, mr);
758                         for ( j=index_substr_if_maxlen-1; j <= value->bv_len - index_substr_any_len; j+=index_substr_any_step )
759                         {
760                                 hashIter( &HASHcontext, HASHdigest,
761                                         (unsigned char *)&value->bv_val[j], index_substr_any_len );
762                                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
763                         }
764                 }
765         }
766
767         if( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
768                 ber_len_t i, j;
769                 pre = SLAP_INDEX_SUBSTR_PREFIX;
770                 klen = index_substr_any_len;
771
772                 for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) {
773                         if( sa->sa_any[i].bv_len < index_substr_any_len ) {
774                                 continue;
775                         }
776
777                         value = &sa->sa_any[i];
778
779                         hashPreset( &HASHcontext, prefix, pre, syntax, mr);
780                         for(j=0;
781                                 j <= value->bv_len - index_substr_any_len;
782                                 j += index_substr_any_step )
783                         {
784                                 hashIter( &HASHcontext, HASHdigest,
785                                         (unsigned char *)&value->bv_val[j], klen ); 
786                                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
787                         }
788                 }
789         }
790
791         if( flags & SLAP_INDEX_SUBSTR_FINAL &&
792                 !BER_BVISNULL( &sa->sa_final ) &&
793                 sa->sa_final.bv_len >= index_substr_if_minlen )
794         {
795                 pre = SLAP_INDEX_SUBSTR_FINAL_PREFIX;
796                 value = &sa->sa_final;
797
798                 klen = index_substr_if_maxlen < value->bv_len
799                         ? index_substr_if_maxlen : value->bv_len;
800
801                 hashPreset( &HASHcontext, prefix, pre, syntax, mr );
802                 hashIter( &HASHcontext, HASHdigest,
803                         (unsigned char *)&value->bv_val[value->bv_len-klen], klen );
804                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
805
806                 /* If final is too long and we have subany indexed, use it
807                  * to match the excess...
808                  */
809                 if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY))
810                 {
811                         ber_len_t j;
812                         pre = SLAP_INDEX_SUBSTR_PREFIX;
813                         hashPreset( &HASHcontext, prefix, pre, syntax, mr);
814                         for ( j=0; j <= value->bv_len - index_substr_if_maxlen; j+=index_substr_any_step )
815                         {
816                                 hashIter( &HASHcontext, HASHdigest,
817                                         (unsigned char *)&value->bv_val[j], index_substr_any_len );
818                                 ber_dupbv_x( &keys[nkeys++], &digest, ctx );
819                         }
820                 }
821         }
822
823         if( nkeys > 0 ) {
824                 BER_BVZERO( &keys[nkeys] );
825                 *keysp = keys;
826         } else {
827                 ch_free( keys );
828                 *keysp = NULL;
829         }
830
831         return LDAP_SUCCESS;
832 }
833
834 static int
835 bitStringValidate(
836         Syntax *syntax,
837         struct berval *in )
838 {
839         ber_len_t i;
840
841         /* very unforgiving validation, requires no normalization
842          * before simplistic matching
843          */
844         if( in->bv_len < 3 ) {
845                 return LDAP_INVALID_SYNTAX;
846         }
847
848         /* RFC 4517 Section 3.3.2 Bit String:
849      *  BitString    = SQUOTE *binary-digit SQUOTE "B"
850      *  binary-digit = "0" / "1"
851          *
852          * where SQUOTE [RFC4512] is
853          *      SQUOTE  = %x27 ; single quote ("'")
854          *
855          * Example: '0101111101'B
856          */
857         
858         if( in->bv_val[0] != '\'' ||
859                 in->bv_val[in->bv_len - 2] != '\'' ||
860                 in->bv_val[in->bv_len - 1] != 'B' )
861         {
862                 return LDAP_INVALID_SYNTAX;
863         }
864
865         for( i = in->bv_len - 3; i > 0; i-- ) {
866                 if( in->bv_val[i] != '0' && in->bv_val[i] != '1' ) {
867                         return LDAP_INVALID_SYNTAX;
868                 }
869         }
870
871         return LDAP_SUCCESS;
872 }
873
874 /*
875  * Syntaxes from RFC 4517
876  *
877
878 3.3.2.  Bit String
879
880    A value of the Bit String syntax is a sequence of binary digits.  The
881    LDAP-specific encoding of a value of this syntax is defined by the
882    following ABNF:
883
884       BitString    = SQUOTE *binary-digit SQUOTE "B"
885
886       binary-digit = "0" / "1"
887
888    The <SQUOTE> rule is defined in [MODELS].
889
890       Example:
891          '0101111101'B
892
893    The LDAP definition for the Bit String syntax is:
894
895       ( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )
896
897    This syntax corresponds to the BIT STRING ASN.1 type from [ASN.1].
898
899    ...
900
901 3.3.21.  Name and Optional UID
902
903    A value of the Name and Optional UID syntax is the distinguished name
904    [MODELS] of an entity optionally accompanied by a unique identifier
905    that serves to differentiate the entity from others with an identical
906    distinguished name.
907
908    The LDAP-specific encoding of a value of this syntax is defined by
909    the following ABNF:
910
911        NameAndOptionalUID = distinguishedName [ SHARP BitString ]
912
913    The <BitString> rule is defined in Section 3.3.2.  The
914    <distinguishedName> rule is defined in [LDAPDN].  The <SHARP> rule is
915    defined in [MODELS].
916
917    Note that although the '#' character may occur in the string
918    representation of a distinguished name, no additional escaping of
919    this character is performed when a <distinguishedName> is encoded in
920    a <NameAndOptionalUID>.
921
922       Example:
923          1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB#'0101'B
924
925    The LDAP definition for the Name and Optional UID syntax is:
926
927       ( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )
928
929    This syntax corresponds to the NameAndOptionalUID ASN.1 type from
930    [X.520].
931
932  *
933  * RFC 4512 says:
934  *
935
936 1.4. Common ABNF Productions
937
938   ...
939       SHARP   = %x23 ; octothorpe (or sharp sign) ("#")
940   ...
941       SQUOTE  = %x27 ; single quote ("'")
942   ...
943       
944  *
945  * Note: normalization strips any leading "0"s, unless the
946  * bit string is exactly "'0'B", so the normalized example,
947  * in slapd, would result in
948  * 
949  * 1.3.6.1.4.1.1466.0=#04024869,o=test,c=gb#'101'B
950  * 
951  * RFC 4514 clarifies that SHARP, i.e. "#", doesn't have to
952  * be escaped except when at the beginning of a value, the
953  * definition of Name and Optional UID appears to be flawed,
954  * because there is no clear means to determine whether the
955  * UID part is present or not.
956  *
957  * Example:
958  *
959  *      cn=Someone,dc=example,dc=com#'1'B
960  *
961  * could be either a NameAndOptionalUID with trailing UID, i.e.
962  *
963  *      DN = "cn=Someone,dc=example,dc=com"
964  *      UID = "'1'B"
965  * 
966  * or a NameAndOptionalUID with no trailing UID, and the AVA
967  * in the last RDN made of
968  *
969  *      attributeType = dc 
970  *      attributeValue = com#'1'B
971  *
972  * in fact "com#'1'B" is a valid IA5 string.
973  *
974  * As a consequence, current slapd code assumes that the
975  * presence of portions of a BitString at the end of the string 
976  * representation of a NameAndOptionalUID means a BitString
977  * is expected, and cause an error otherwise.  This is quite
978  * arbitrary, and might change in the future.
979  */
980
981
982 static int
983 nameUIDValidate(
984         Syntax *syntax,
985         struct berval *in )
986 {
987         int rc;
988         struct berval dn, uid;
989
990         if( BER_BVISEMPTY( in ) ) return LDAP_SUCCESS;
991
992         ber_dupbv( &dn, in );
993         if( !dn.bv_val ) return LDAP_OTHER;
994
995         /* if there's a "#", try bitStringValidate()... */
996         uid.bv_val = strrchr( dn.bv_val, '#' );
997         if ( !BER_BVISNULL( &uid ) ) {
998                 uid.bv_val++;
999                 uid.bv_len = dn.bv_len - ( uid.bv_val - dn.bv_val );
1000
1001                 rc = bitStringValidate( NULL, &uid );
1002                 if ( rc == LDAP_SUCCESS ) {
1003                         /* in case of success, trim the UID,
1004                          * otherwise treat it as part of the DN */
1005                         dn.bv_len -= uid.bv_len + 1;
1006                         uid.bv_val[-1] = '\0';
1007                 }
1008         }
1009
1010         rc = dnValidate( NULL, &dn );
1011
1012         ber_memfree( dn.bv_val );
1013         return rc;
1014 }
1015
1016 int
1017 nameUIDPretty(
1018         Syntax *syntax,
1019         struct berval *val,
1020         struct berval *out,
1021         void *ctx )
1022 {
1023         assert( val != NULL );
1024         assert( out != NULL );
1025
1026
1027         Debug( LDAP_DEBUG_TRACE, ">>> nameUIDPretty: <%s>\n", val->bv_val, 0, 0 );
1028
1029         if( BER_BVISEMPTY( val ) ) {
1030                 ber_dupbv_x( out, val, ctx );
1031
1032         } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
1033                 return LDAP_INVALID_SYNTAX;
1034
1035         } else {
1036                 int             rc;
1037                 struct berval   dnval = *val;
1038                 struct berval   uidval = BER_BVNULL;
1039
1040                 uidval.bv_val = strrchr( val->bv_val, '#' );
1041                 if ( !BER_BVISNULL( &uidval ) ) {
1042                         uidval.bv_val++;
1043                         uidval.bv_len = val->bv_len - ( uidval.bv_val - val->bv_val );
1044
1045                         rc = bitStringValidate( NULL, &uidval );
1046
1047                         if ( rc == LDAP_SUCCESS ) {
1048                                 ber_dupbv_x( &dnval, val, ctx );
1049                                 dnval.bv_len -= uidval.bv_len + 1;
1050                                 dnval.bv_val[dnval.bv_len] = '\0';
1051
1052                         } else {
1053                                 BER_BVZERO( &uidval );
1054                         }
1055                 }
1056
1057                 rc = dnPretty( syntax, &dnval, out, ctx );
1058                 if ( dnval.bv_val != val->bv_val ) {
1059                         slap_sl_free( dnval.bv_val, ctx );
1060                 }
1061                 if( rc != LDAP_SUCCESS ) {
1062                         return rc;
1063                 }
1064
1065                 if( !BER_BVISNULL( &uidval ) ) {
1066                         int     i, c, got1;
1067                         char    *tmp;
1068
1069                         tmp = slap_sl_realloc( out->bv_val, out->bv_len 
1070                                 + STRLENOF( "#" ) + uidval.bv_len + 1,
1071                                 ctx );
1072                         if( tmp == NULL ) {
1073                                 ber_memfree_x( out->bv_val, ctx );
1074                                 return LDAP_OTHER;
1075                         }
1076                         out->bv_val = tmp;
1077                         out->bv_val[out->bv_len++] = '#';
1078                         out->bv_val[out->bv_len++] = '\'';
1079
1080                         got1 = uidval.bv_len < sizeof("'0'B"); 
1081                         for( i = 1; i < uidval.bv_len - 2; i++ ) {
1082                                 c = uidval.bv_val[i];
1083                                 switch(c) {
1084                                         case '0':
1085                                                 if( got1 ) out->bv_val[out->bv_len++] = c;
1086                                                 break;
1087                                         case '1':
1088                                                 got1 = 1;
1089                                                 out->bv_val[out->bv_len++] = c;
1090                                                 break;
1091                                 }
1092                         }
1093
1094                         out->bv_val[out->bv_len++] = '\'';
1095                         out->bv_val[out->bv_len++] = 'B';
1096                         out->bv_val[out->bv_len] = '\0';
1097                 }
1098         }
1099
1100         Debug( LDAP_DEBUG_TRACE, "<<< nameUIDPretty: <%s>\n", out->bv_val, 0, 0 );
1101
1102         return LDAP_SUCCESS;
1103 }
1104
1105 static int
1106 uniqueMemberNormalize(
1107         slap_mask_t usage,
1108         Syntax *syntax,
1109         MatchingRule *mr,
1110         struct berval *val,
1111         struct berval *normalized,
1112         void *ctx )
1113 {
1114         struct berval out;
1115         int rc;
1116
1117         assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
1118
1119         ber_dupbv_x( &out, val, ctx );
1120         if ( BER_BVISEMPTY( &out ) ) {
1121                 *normalized = out;
1122
1123         } else {
1124                 struct berval uid = BER_BVNULL;
1125
1126                 uid.bv_val = strrchr( out.bv_val, '#' );
1127                 if ( !BER_BVISNULL( &uid ) ) {
1128                         uid.bv_val++;
1129                         uid.bv_len = out.bv_len - ( uid.bv_val - out.bv_val );
1130
1131                         rc = bitStringValidate( NULL, &uid );
1132                         if ( rc == LDAP_SUCCESS ) {
1133                                 uid.bv_val[-1] = '\0';
1134                                 out.bv_len -= uid.bv_len + 1;
1135                         } else {
1136                                 BER_BVZERO( &uid );
1137                         }
1138                 }
1139
1140                 rc = dnNormalize( 0, NULL, NULL, &out, normalized, ctx );
1141
1142                 if( rc != LDAP_SUCCESS ) {
1143                         slap_sl_free( out.bv_val, ctx );
1144                         return LDAP_INVALID_SYNTAX;
1145                 }
1146
1147                 if( !BER_BVISNULL( &uid ) ) {
1148                         char    *tmp;
1149
1150                         tmp = ch_realloc( normalized->bv_val,
1151                                 normalized->bv_len + uid.bv_len
1152                                 + STRLENOF("#") + 1 );
1153                         if ( tmp == NULL ) {
1154                                 ber_memfree_x( normalized->bv_val, ctx );
1155                                 return LDAP_OTHER;
1156                         }
1157
1158                         normalized->bv_val = tmp;
1159
1160                         /* insert the separator */
1161                         normalized->bv_val[normalized->bv_len++] = '#';
1162
1163                         /* append the UID */
1164                         AC_MEMCPY( &normalized->bv_val[normalized->bv_len],
1165                                 uid.bv_val, uid.bv_len );
1166                         normalized->bv_len += uid.bv_len;
1167
1168                         /* terminate */
1169                         normalized->bv_val[normalized->bv_len] = '\0';
1170                 }
1171
1172                 slap_sl_free( out.bv_val, ctx );
1173         }
1174
1175         return LDAP_SUCCESS;
1176 }
1177
1178 static int
1179 uniqueMemberMatch(
1180         int *matchp,
1181         slap_mask_t flags,
1182         Syntax *syntax,
1183         MatchingRule *mr,
1184         struct berval *value,
1185         void *assertedValue )
1186 {
1187         int match;
1188         struct berval *asserted = (struct berval *) assertedValue;
1189         struct berval assertedDN = *asserted;
1190         struct berval assertedUID = BER_BVNULL;
1191         struct berval valueDN = *value;
1192         struct berval valueUID = BER_BVNULL;
1193         int approx = ((flags & SLAP_MR_EQUALITY_APPROX) == SLAP_MR_EQUALITY_APPROX);
1194
1195         if ( !BER_BVISEMPTY( asserted ) ) {
1196                 assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
1197                 if ( !BER_BVISNULL( &assertedUID ) ) {
1198                         assertedUID.bv_val++;
1199                         assertedUID.bv_len = assertedDN.bv_len
1200                                 - ( assertedUID.bv_val - assertedDN.bv_val );
1201
1202                         if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
1203                                 assertedDN.bv_len -= assertedUID.bv_len + 1;
1204
1205                         } else {
1206                                 BER_BVZERO( &assertedUID );
1207                         }
1208                 }
1209         }
1210
1211         if ( !BER_BVISEMPTY( value ) ) {
1212
1213                 valueUID.bv_val = strrchr( valueDN.bv_val, '#' );
1214                 if ( !BER_BVISNULL( &valueUID ) ) {
1215                         valueUID.bv_val++;
1216                         valueUID.bv_len = valueDN.bv_len
1217                                 - ( valueUID.bv_val - valueDN.bv_val );
1218
1219                         if ( bitStringValidate( NULL, &valueUID ) == LDAP_SUCCESS ) {
1220                                 valueDN.bv_len -= valueUID.bv_len + 1;
1221
1222                         } else {
1223                                 BER_BVZERO( &valueUID );
1224                         }
1225                 }
1226         }
1227
1228         if( valueUID.bv_len && assertedUID.bv_len ) {
1229                 match = valueUID.bv_len - assertedUID.bv_len;
1230                 if ( match ) {
1231                         *matchp = match;
1232                         return LDAP_SUCCESS;
1233                 }
1234
1235                 match = memcmp( valueUID.bv_val, assertedUID.bv_val, valueUID.bv_len );
1236                 if( match ) {
1237                         *matchp = match;
1238                         return LDAP_SUCCESS;
1239                 }
1240
1241         } else if ( !approx && valueUID.bv_len ) {
1242                 match = -1;
1243                 *matchp = match;
1244                 return LDAP_SUCCESS;
1245
1246         } else if ( !approx && assertedUID.bv_len ) {
1247                 match = 1;
1248                 *matchp = match;
1249                 return LDAP_SUCCESS;
1250         }
1251
1252         return dnMatch( matchp, flags, syntax, mr, &valueDN, &assertedDN );
1253 }
1254
1255 static int 
1256 uniqueMemberIndexer(
1257         slap_mask_t use,
1258         slap_mask_t flags,
1259         Syntax *syntax,
1260         MatchingRule *mr,
1261         struct berval *prefix,
1262         BerVarray values,
1263         BerVarray *keysp,
1264         void *ctx )
1265 {
1266         BerVarray dnvalues;
1267         int rc;
1268         int i;
1269         for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
1270                 /* just count them */                 
1271         }
1272         assert( i > 0 );
1273
1274         dnvalues = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
1275
1276         for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
1277                 struct berval assertedDN = values[i];
1278                 struct berval assertedUID = BER_BVNULL;
1279
1280                 if ( !BER_BVISEMPTY( &assertedDN ) ) {
1281                         assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
1282                         if ( !BER_BVISNULL( &assertedUID ) ) {
1283                                 assertedUID.bv_val++;
1284                                 assertedUID.bv_len = assertedDN.bv_len
1285                                         - ( assertedUID.bv_val - assertedDN.bv_val );
1286         
1287                                 if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
1288                                         assertedDN.bv_len -= assertedUID.bv_len + 1;
1289
1290                                 } else {
1291                                         BER_BVZERO( &assertedUID );
1292                                 }
1293                         }
1294                 }
1295
1296                 dnvalues[i] = assertedDN;
1297         }
1298         BER_BVZERO( &dnvalues[i] );
1299
1300         rc = octetStringIndexer( use, flags, syntax, mr, prefix,
1301                 dnvalues, keysp, ctx );
1302
1303         slap_sl_free( dnvalues, ctx );
1304         return rc;
1305 }
1306
1307 static int 
1308 uniqueMemberFilter(
1309         slap_mask_t use,
1310         slap_mask_t flags,
1311         Syntax *syntax,
1312         MatchingRule *mr,
1313         struct berval *prefix,
1314         void * assertedValue,
1315         BerVarray *keysp,
1316         void *ctx )
1317 {
1318         struct berval *asserted = (struct berval *) assertedValue;
1319         struct berval assertedDN = *asserted;
1320         struct berval assertedUID = BER_BVNULL;
1321
1322         if ( !BER_BVISEMPTY( asserted ) ) {
1323                 assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
1324                 if ( !BER_BVISNULL( &assertedUID ) ) {
1325                         assertedUID.bv_val++;
1326                         assertedUID.bv_len = assertedDN.bv_len
1327                                 - ( assertedUID.bv_val - assertedDN.bv_val );
1328
1329                         if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
1330                                 assertedDN.bv_len -= assertedUID.bv_len + 1;
1331
1332                         } else {
1333                                 BER_BVZERO( &assertedUID );
1334                         }
1335                 }
1336         }
1337
1338         return octetStringFilter( use, flags, syntax, mr, prefix,
1339                 &assertedDN, keysp, ctx );
1340 }
1341
1342
1343 /*
1344  * Handling boolean syntax and matching is quite rigid.
1345  * A more flexible approach would be to allow a variety
1346  * of strings to be normalized and prettied into TRUE
1347  * and FALSE.
1348  */
1349 static int
1350 booleanValidate(
1351         Syntax *syntax,
1352         struct berval *in )
1353 {
1354         /* very unforgiving validation, requires no normalization
1355          * before simplistic matching
1356          */
1357
1358         if( in->bv_len == 4 ) {
1359                 if( bvmatch( in, &slap_true_bv ) ) {
1360                         return LDAP_SUCCESS;
1361                 }
1362         } else if( in->bv_len == 5 ) {
1363                 if( bvmatch( in, &slap_false_bv ) ) {
1364                         return LDAP_SUCCESS;
1365                 }
1366         }
1367
1368         return LDAP_INVALID_SYNTAX;
1369 }
1370
1371 static int
1372 booleanMatch(
1373         int *matchp,
1374         slap_mask_t flags,
1375         Syntax *syntax,
1376         MatchingRule *mr,
1377         struct berval *value,
1378         void *assertedValue )
1379 {
1380         /* simplistic matching allowed by rigid validation */
1381         struct berval *asserted = (struct berval *) assertedValue;
1382         *matchp = value->bv_len != asserted->bv_len;
1383         return LDAP_SUCCESS;
1384 }
1385
1386 /*-------------------------------------------------------------------
1387 LDAP/X.500 string syntax / matching rules have a few oddities.  This
1388 comment attempts to detail how slapd(8) treats them.
1389
1390 Summary:
1391   StringSyntax          X.500   LDAP    Matching/Comments
1392   DirectoryString       CHOICE  UTF8    i/e + ignore insignificant spaces
1393   PrintableString       subset  subset  i/e + ignore insignificant spaces
1394   PrintableString       subset  subset  i/e + ignore insignificant spaces
1395   NumericString         subset  subset  ignore all spaces
1396   IA5String                     ASCII   ASCII   i/e + ignore insignificant spaces
1397   TeletexString         T.61    T.61    i/e + ignore insignificant spaces
1398
1399   TelephoneNumber       subset  subset  i + ignore all spaces and "-"
1400
1401   See RFC 4518 for details.
1402
1403
1404 Directory String -
1405   In X.500(93), a directory string can be either a PrintableString,
1406   a bmpString, or a UniversalString (e.g., UCS (a subset of Unicode)).
1407   In later versions, more CHOICEs were added.  In all cases the string
1408   must be non-empty.
1409
1410   In LDAPv3, a directory string is a UTF-8 encoded UCS string.
1411   A directory string cannot be zero length.
1412
1413   For matching, there are both case ignore and exact rules.  Both
1414   also require that "insignificant" spaces be ignored.
1415         spaces before the first non-space are ignored;
1416         spaces after the last non-space are ignored;
1417         spaces after a space are ignored.
1418   Note: by these rules (and as clarified in X.520), a string of only
1419   spaces is to be treated as if held one space, not empty (which
1420   would be a syntax error).
1421
1422 NumericString
1423   In ASN.1, numeric string is just a string of digits and spaces
1424   and could be empty.  However, in X.500, all attribute values of
1425   numeric string carry a non-empty constraint.  For example:
1426
1427         internationalISDNNumber ATTRIBUTE ::= {
1428                 WITH SYNTAX InternationalISDNNumber
1429                 EQUALITY MATCHING RULE numericStringMatch
1430                 SUBSTRINGS MATCHING RULE numericStringSubstringsMatch
1431                 ID id-at-internationalISDNNumber }
1432         InternationalISDNNumber ::=
1433             NumericString (SIZE(1..ub-international-isdn-number))
1434
1435   Unforunately, some assertion values are don't carry the same
1436   constraint (but its unclear how such an assertion could ever
1437   be true). In LDAP, there is one syntax (numericString) not two
1438   (numericString with constraint, numericString without constraint).
1439   This should be treated as numericString with non-empty constraint.
1440   Note that while someone may have no ISDN number, there are no ISDN
1441   numbers which are zero length.
1442
1443   In matching, spaces are ignored.
1444
1445 PrintableString
1446   In ASN.1, Printable string is just a string of printable characters
1447   and can be empty.  In X.500, semantics much like NumericString (see
1448   serialNumber for a like example) excepting uses insignificant space
1449   handling instead of ignore all spaces.  
1450
1451 IA5String
1452   Basically same as PrintableString.  There are no examples in X.500,
1453   but same logic applies.  So we require them to be non-empty as
1454   well.
1455
1456 -------------------------------------------------------------------*/
1457
1458 static int
1459 UTF8StringValidate(
1460         Syntax *syntax,
1461         struct berval *in )
1462 {
1463         ber_len_t count;
1464         int len;
1465         unsigned char *u = (unsigned char *)in->bv_val;
1466
1467         if( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
1468                 /* directory strings cannot be empty */
1469                 return LDAP_INVALID_SYNTAX;
1470         }
1471
1472         for( count = in->bv_len; count > 0; count -= len, u += len ) {
1473                 /* get the length indicated by the first byte */
1474                 len = LDAP_UTF8_CHARLEN2( u, len );
1475
1476                 /* very basic checks */
1477                 switch( len ) {
1478                         case 6:
1479                                 if( (u[5] & 0xC0) != 0x80 ) {
1480                                         return LDAP_INVALID_SYNTAX;
1481                                 }
1482                         case 5:
1483                                 if( (u[4] & 0xC0) != 0x80 ) {
1484                                         return LDAP_INVALID_SYNTAX;
1485                                 }
1486                         case 4:
1487                                 if( (u[3] & 0xC0) != 0x80 ) {
1488                                         return LDAP_INVALID_SYNTAX;
1489                                 }
1490                         case 3:
1491                                 if( (u[2] & 0xC0 )!= 0x80 ) {
1492                                         return LDAP_INVALID_SYNTAX;
1493                                 }
1494                         case 2:
1495                                 if( (u[1] & 0xC0) != 0x80 ) {
1496                                         return LDAP_INVALID_SYNTAX;
1497                                 }
1498                         case 1:
1499                                 /* CHARLEN already validated it */
1500                                 break;
1501                         default:
1502                                 return LDAP_INVALID_SYNTAX;
1503                 }
1504
1505                 /* make sure len corresponds with the offset
1506                         to the next character */
1507                 if( LDAP_UTF8_OFFSET( (char *)u ) != len ) return LDAP_INVALID_SYNTAX;
1508         }
1509
1510         if( count != 0 ) {
1511                 return LDAP_INVALID_SYNTAX;
1512         }
1513
1514         return LDAP_SUCCESS;
1515 }
1516
1517 static int
1518 UTF8StringNormalize(
1519         slap_mask_t use,
1520         Syntax *syntax,
1521         MatchingRule *mr,
1522         struct berval *val,
1523         struct berval *normalized,
1524         void *ctx )
1525 {
1526         struct berval tmp, nvalue;
1527         int flags;
1528         int i, wasspace;
1529
1530         assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
1531
1532         if( BER_BVISNULL( val ) ) {
1533                 /* assume we're dealing with a syntax (e.g., UTF8String)
1534                  * which allows empty strings
1535                  */
1536                 BER_BVZERO( normalized );
1537                 return LDAP_SUCCESS;
1538         }
1539
1540         flags = SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseExactMatch )
1541                 ? LDAP_UTF8_NOCASEFOLD : LDAP_UTF8_CASEFOLD;
1542         flags |= ( ( use & SLAP_MR_EQUALITY_APPROX ) == SLAP_MR_EQUALITY_APPROX )
1543                 ? LDAP_UTF8_APPROX : 0;
1544
1545         val = UTF8bvnormalize( val, &tmp, flags, ctx );
1546         if( val == NULL ) {
1547                 return LDAP_OTHER;
1548         }
1549         
1550         /* collapse spaces (in place) */
1551         nvalue.bv_len = 0;
1552         nvalue.bv_val = tmp.bv_val;
1553
1554         /* trim leading spaces? */
1555         wasspace = !((( use & SLAP_MR_SUBSTR_ANY ) == SLAP_MR_SUBSTR_ANY ) ||
1556                 (( use & SLAP_MR_SUBSTR_FINAL ) == SLAP_MR_SUBSTR_FINAL ));
1557
1558         for( i = 0; i < tmp.bv_len; i++) {
1559                 if ( ASCII_SPACE( tmp.bv_val[i] )) {
1560                         if( wasspace++ == 0 ) {
1561                                 /* trim repeated spaces */
1562                                 nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i];
1563                         }
1564                 } else {
1565                         wasspace = 0;
1566                         nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i];
1567                 }
1568         }
1569
1570         if( !BER_BVISEMPTY( &nvalue ) ) {
1571                 /* trim trailing space? */
1572                 if( wasspace && (
1573                         (( use & SLAP_MR_SUBSTR_INITIAL ) != SLAP_MR_SUBSTR_INITIAL ) &&
1574                         ( use & SLAP_MR_SUBSTR_ANY ) != SLAP_MR_SUBSTR_ANY ))
1575                 {
1576                         --nvalue.bv_len;
1577                 }
1578                 nvalue.bv_val[nvalue.bv_len] = '\0';
1579
1580         } else {
1581                 /* string of all spaces is treated as one space */
1582                 nvalue.bv_val[0] = ' ';
1583                 nvalue.bv_val[1] = '\0';
1584                 nvalue.bv_len = 1;
1585         }
1586
1587         *normalized = nvalue;
1588         return LDAP_SUCCESS;
1589 }
1590
1591 static int
1592 directoryStringSubstringsMatch(
1593         int *matchp,
1594         slap_mask_t flags,
1595         Syntax *syntax,
1596         MatchingRule *mr,
1597         struct berval *value,
1598         void *assertedValue )
1599 {
1600         int match = 0;
1601         SubstringsAssertion *sub = assertedValue;
1602         struct berval left = *value;
1603         int i;
1604         int priorspace=0;
1605
1606         if ( !BER_BVISNULL( &sub->sa_initial ) ) {
1607                 if ( sub->sa_initial.bv_len > left.bv_len ) {
1608                         /* not enough left */
1609                         match = 1;
1610                         goto done;
1611                 }
1612
1613                 match = memcmp( sub->sa_initial.bv_val, left.bv_val,
1614                         sub->sa_initial.bv_len );
1615
1616                 if ( match != 0 ) {
1617                         goto done;
1618                 }
1619
1620                 left.bv_val += sub->sa_initial.bv_len;
1621                 left.bv_len -= sub->sa_initial.bv_len;
1622
1623                 priorspace = ASCII_SPACE(
1624                         sub->sa_initial.bv_val[sub->sa_initial.bv_len] );
1625         }
1626
1627         if ( sub->sa_any ) {
1628                 for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
1629                         ber_len_t idx;
1630                         char *p;
1631
1632                         if( priorspace && !BER_BVISEMPTY( &sub->sa_any[i] ) 
1633                                 && ASCII_SPACE( sub->sa_any[i].bv_val[0] ))
1634                         { 
1635                                 /* allow next space to match */
1636                                 left.bv_val--;
1637                                 left.bv_len++;
1638                         }
1639                         priorspace=0;
1640
1641 retry:
1642                         if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) {
1643                                 continue;
1644                         }
1645
1646                         if ( sub->sa_any[i].bv_len > left.bv_len ) {
1647                                 /* not enough left */
1648                                 match = 1;
1649                                 goto done;
1650                         }
1651
1652                         p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len );
1653
1654                         if( p == NULL ) {
1655                                 match = 1;
1656                                 goto done;
1657                         }
1658
1659                         idx = p - left.bv_val;
1660
1661                         if ( idx >= left.bv_len ) {
1662                                 /* this shouldn't happen */
1663                                 return LDAP_OTHER;
1664                         }
1665
1666                         left.bv_val = p;
1667                         left.bv_len -= idx;
1668
1669                         if ( sub->sa_any[i].bv_len > left.bv_len ) {
1670                                 /* not enough left */
1671                                 match = 1;
1672                                 goto done;
1673                         }
1674
1675                         match = memcmp( left.bv_val,
1676                                 sub->sa_any[i].bv_val,
1677                                 sub->sa_any[i].bv_len );
1678
1679                         if ( match != 0 ) {
1680                                 left.bv_val++;
1681                                 left.bv_len--;
1682                                 goto retry;
1683                         }
1684
1685                         left.bv_val += sub->sa_any[i].bv_len;
1686                         left.bv_len -= sub->sa_any[i].bv_len;
1687
1688                         priorspace = ASCII_SPACE(
1689                                 sub->sa_any[i].bv_val[sub->sa_any[i].bv_len] );
1690                 }
1691         }
1692
1693         if ( !BER_BVISNULL( &sub->sa_final ) ) {
1694                 if( priorspace && !BER_BVISEMPTY( &sub->sa_final ) 
1695                         && ASCII_SPACE( sub->sa_final.bv_val[0] ))
1696                 { 
1697                         /* allow next space to match */
1698                         left.bv_val--;
1699                         left.bv_len++;
1700                 }
1701
1702                 if ( sub->sa_final.bv_len > left.bv_len ) {
1703                         /* not enough left */
1704                         match = 1;
1705                         goto done;
1706                 }
1707
1708                 match = memcmp( sub->sa_final.bv_val,
1709                         &left.bv_val[left.bv_len - sub->sa_final.bv_len],
1710                         sub->sa_final.bv_len );
1711
1712                 if ( match != 0 ) {
1713                         goto done;
1714                 }
1715         }
1716
1717 done:
1718         *matchp = match;
1719         return LDAP_SUCCESS;
1720 }
1721
1722 #if defined(SLAPD_APPROX_INITIALS)
1723 #       define SLAPD_APPROX_DELIMITER "._ "
1724 #       define SLAPD_APPROX_WORDLEN 2
1725 #else
1726 #       define SLAPD_APPROX_DELIMITER " "
1727 #       define SLAPD_APPROX_WORDLEN 1
1728 #endif
1729
1730 static int
1731 approxMatch(
1732         int *matchp,
1733         slap_mask_t flags,
1734         Syntax *syntax,
1735         MatchingRule *mr,
1736         struct berval *value,
1737         void *assertedValue )
1738 {
1739         struct berval *nval, *assertv;
1740         char *val, **values, **words, *c;
1741         int i, count, len, nextchunk=0, nextavail=0;
1742
1743         /* Yes, this is necessary */
1744         nval = UTF8bvnormalize( value, NULL, LDAP_UTF8_APPROX, NULL );
1745         if( nval == NULL ) {
1746                 *matchp = 1;
1747                 return LDAP_SUCCESS;
1748         }
1749
1750         /* Yes, this is necessary */
1751         assertv = UTF8bvnormalize( ((struct berval *)assertedValue),
1752                 NULL, LDAP_UTF8_APPROX, NULL );
1753         if( assertv == NULL ) {
1754                 ber_bvfree( nval );
1755                 *matchp = 1;
1756                 return LDAP_SUCCESS;
1757         }
1758
1759         /* Isolate how many words there are */
1760         for ( c = nval->bv_val, count = 1; *c; c++ ) {
1761                 c = strpbrk( c, SLAPD_APPROX_DELIMITER );
1762                 if ( c == NULL ) break;
1763                 *c = '\0';
1764                 count++;
1765         }
1766
1767         /* Get a phonetic copy of each word */
1768         words = (char **)ch_malloc( count * sizeof(char *) );
1769         values = (char **)ch_malloc( count * sizeof(char *) );
1770         for ( c = nval->bv_val, i = 0;  i < count; i++, c += strlen(c) + 1 ) {
1771                 words[i] = c;
1772                 values[i] = phonetic(c);
1773         }
1774
1775         /* Work through the asserted value's words, to see if at least some
1776            of the words are there, in the same order. */
1777         len = 0;
1778         while ( (ber_len_t) nextchunk < assertv->bv_len ) {
1779                 len = strcspn( assertv->bv_val + nextchunk, SLAPD_APPROX_DELIMITER);
1780                 if( len == 0 ) {
1781                         nextchunk++;
1782                         continue;
1783                 }
1784 #if defined(SLAPD_APPROX_INITIALS)
1785                 else if( len == 1 ) {
1786                         /* Single letter words need to at least match one word's initial */
1787                         for( i=nextavail; i<count; i++ )
1788                                 if( !strncasecmp( assertv->bv_val + nextchunk, words[i], 1 )) {
1789                                         nextavail=i+1;
1790                                         break;
1791                                 }
1792                 }
1793 #endif
1794                 else {
1795                         /* Isolate the next word in the asserted value and phonetic it */
1796                         assertv->bv_val[nextchunk+len] = '\0';
1797                         val = phonetic( assertv->bv_val + nextchunk );
1798
1799                         /* See if this phonetic chunk is in the remaining words of *value */
1800                         for( i=nextavail; i<count; i++ ){
1801                                 if( !strcmp( val, values[i] ) ){
1802                                         nextavail = i+1;
1803                                         break;
1804                                 }
1805                         }
1806                         ch_free( val );
1807                 }
1808
1809                 /* This chunk in the asserted value was NOT within the *value. */
1810                 if( i >= count ) {
1811                         nextavail=-1;
1812                         break;
1813                 }
1814
1815                 /* Go on to the next word in the asserted value */
1816                 nextchunk += len+1;
1817         }
1818
1819         /* If some of the words were seen, call it a match */
1820         if( nextavail > 0 ) {
1821                 *matchp = 0;
1822         }
1823         else {
1824                 *matchp = 1;
1825         }
1826
1827         /* Cleanup allocs */
1828         ber_bvfree( assertv );
1829         for( i=0; i<count; i++ ) {
1830                 ch_free( values[i] );
1831         }
1832         ch_free( values );
1833         ch_free( words );
1834         ber_bvfree( nval );
1835
1836         return LDAP_SUCCESS;
1837 }
1838
1839 static int 
1840 approxIndexer(
1841         slap_mask_t use,
1842         slap_mask_t flags,
1843         Syntax *syntax,
1844         MatchingRule *mr,
1845         struct berval *prefix,
1846         BerVarray values,
1847         BerVarray *keysp,
1848         void *ctx )
1849 {
1850         char *c;
1851         int i,j, len, wordcount, keycount=0;
1852         struct berval *newkeys;
1853         BerVarray keys=NULL;
1854
1855         for( j = 0; !BER_BVISNULL( &values[j] ); j++ ) {
1856                 struct berval val = BER_BVNULL;
1857                 /* Yes, this is necessary */
1858                 UTF8bvnormalize( &values[j], &val, LDAP_UTF8_APPROX, NULL );
1859                 assert( !BER_BVISNULL( &val ) );
1860
1861                 /* Isolate how many words there are. There will be a key for each */
1862                 for( wordcount = 0, c = val.bv_val; *c; c++) {
1863                         len = strcspn(c, SLAPD_APPROX_DELIMITER);
1864                         if( len >= SLAPD_APPROX_WORDLEN ) wordcount++;
1865                         c+= len;
1866                         if (*c == '\0') break;
1867                         *c = '\0';
1868                 }
1869
1870                 /* Allocate/increase storage to account for new keys */
1871                 newkeys = (struct berval *)ch_malloc( (keycount + wordcount + 1) 
1872                         * sizeof(struct berval) );
1873                 AC_MEMCPY( newkeys, keys, keycount * sizeof(struct berval) );
1874                 if( keys ) ch_free( keys );
1875                 keys = newkeys;
1876
1877                 /* Get a phonetic copy of each word */
1878                 for( c = val.bv_val, i = 0; i < wordcount; c += len + 1 ) {
1879                         len = strlen( c );
1880                         if( len < SLAPD_APPROX_WORDLEN ) continue;
1881                         ber_str2bv( phonetic( c ), 0, 0, &keys[keycount] );
1882                         keycount++;
1883                         i++;
1884                 }
1885
1886                 ber_memfree( val.bv_val );
1887         }
1888         BER_BVZERO( &keys[keycount] );
1889         *keysp = keys;
1890
1891         return LDAP_SUCCESS;
1892 }
1893
1894 static int 
1895 approxFilter(
1896         slap_mask_t use,
1897         slap_mask_t flags,
1898         Syntax *syntax,
1899         MatchingRule *mr,
1900         struct berval *prefix,
1901         void * assertedValue,
1902         BerVarray *keysp,
1903         void *ctx )
1904 {
1905         char *c;
1906         int i, count, len;
1907         struct berval *val;
1908         BerVarray keys;
1909
1910         /* Yes, this is necessary */
1911         val = UTF8bvnormalize( ((struct berval *)assertedValue),
1912                 NULL, LDAP_UTF8_APPROX, NULL );
1913         if( val == NULL || BER_BVISNULL( val ) ) {
1914                 keys = (struct berval *)ch_malloc( sizeof(struct berval) );
1915                 BER_BVZERO( &keys[0] );
1916                 *keysp = keys;
1917                 ber_bvfree( val );
1918                 return LDAP_SUCCESS;
1919         }
1920
1921         /* Isolate how many words there are. There will be a key for each */
1922         for( count = 0,c = val->bv_val; *c; c++) {
1923                 len = strcspn(c, SLAPD_APPROX_DELIMITER);
1924                 if( len >= SLAPD_APPROX_WORDLEN ) count++;
1925                 c+= len;
1926                 if (*c == '\0') break;
1927                 *c = '\0';
1928         }
1929
1930         /* Allocate storage for new keys */
1931         keys = (struct berval *)ch_malloc( (count + 1) * sizeof(struct berval) );
1932
1933         /* Get a phonetic copy of each word */
1934         for( c = val->bv_val, i = 0; i < count; c += len + 1 ) {
1935                 len = strlen(c);
1936                 if( len < SLAPD_APPROX_WORDLEN ) continue;
1937                 ber_str2bv( phonetic( c ), 0, 0, &keys[i] );
1938                 i++;
1939         }
1940
1941         ber_bvfree( val );
1942
1943         BER_BVZERO( &keys[count] );
1944         *keysp = keys;
1945
1946         return LDAP_SUCCESS;
1947 }
1948
1949 /* Remove all spaces and '-' characters */
1950 static int
1951 telephoneNumberNormalize(
1952         slap_mask_t usage,
1953         Syntax *syntax,
1954         MatchingRule *mr,
1955         struct berval *val,
1956         struct berval *normalized,
1957         void *ctx )
1958 {
1959         char *p, *q;
1960
1961         assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
1962
1963         /* validator should have refused an empty string */
1964         assert( !BER_BVISEMPTY( val ) );
1965
1966         q = normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
1967
1968         for( p = val->bv_val; *p; p++ ) {
1969                 if ( ! ( ASCII_SPACE( *p ) || *p == '-' )) {
1970                         *q++ = *p;
1971                 }
1972         }
1973         *q = '\0';
1974
1975         normalized->bv_len = q - normalized->bv_val;
1976
1977         if( BER_BVISEMPTY( normalized ) ) {
1978                 slap_sl_free( normalized->bv_val, ctx );
1979                 BER_BVZERO( normalized );
1980                 return LDAP_INVALID_SYNTAX;
1981         }
1982
1983         return LDAP_SUCCESS;
1984 }
1985
1986 int
1987 numericoidValidate(
1988         Syntax *syntax,
1989         struct berval *in )
1990 {
1991         struct berval val = *in;
1992
1993         if( BER_BVISEMPTY( &val ) ) {
1994                 /* disallow empty strings */
1995                 return LDAP_INVALID_SYNTAX;
1996         }
1997
1998         while( OID_LEADCHAR( val.bv_val[0] ) ) {
1999                 if ( val.bv_len == 1 ) {
2000                         return LDAP_SUCCESS;
2001                 }
2002
2003                 if ( val.bv_val[0] == '0' && !OID_SEPARATOR( val.bv_val[1] )) {
2004                         break;
2005                 }
2006
2007                 val.bv_val++;
2008                 val.bv_len--;
2009
2010                 while ( OID_LEADCHAR( val.bv_val[0] )) {
2011                         val.bv_val++;
2012                         val.bv_len--;
2013
2014                         if ( val.bv_len == 0 ) {
2015                                 return LDAP_SUCCESS;
2016                         }
2017                 }
2018
2019                 if( !OID_SEPARATOR( val.bv_val[0] )) {
2020                         break;
2021                 }
2022
2023                 val.bv_val++;
2024                 val.bv_len--;
2025         }
2026
2027         return LDAP_INVALID_SYNTAX;
2028 }
2029
2030 static int
2031 integerValidate(
2032         Syntax *syntax,
2033         struct berval *in )
2034 {
2035         ber_len_t i;
2036         struct berval val = *in;
2037
2038         if ( BER_BVISEMPTY( &val ) ) return LDAP_INVALID_SYNTAX;
2039
2040         if ( val.bv_val[0] == '-' ) {
2041                 val.bv_len--;
2042                 val.bv_val++;
2043
2044                 if( BER_BVISEMPTY( &val ) ) { /* bare "-" */
2045                         return LDAP_INVALID_SYNTAX;
2046                 }
2047
2048                 if( val.bv_val[0] == '0' ) { /* "-0" */
2049                         return LDAP_INVALID_SYNTAX;
2050                 }
2051
2052         } else if ( val.bv_val[0] == '0' ) {
2053                 if( val.bv_len > 1 ) { /* "0<more>" */
2054                         return LDAP_INVALID_SYNTAX;
2055                 }
2056
2057                 return LDAP_SUCCESS;
2058         }
2059
2060         for( i=0; i < val.bv_len; i++ ) {
2061                 if( !ASCII_DIGIT(val.bv_val[i]) ) {
2062                         return LDAP_INVALID_SYNTAX;
2063                 }
2064         }
2065
2066         return LDAP_SUCCESS;
2067 }
2068
2069 static int
2070 integerMatch(
2071         int *matchp,
2072         slap_mask_t flags,
2073         Syntax *syntax,
2074         MatchingRule *mr,
2075         struct berval *value,
2076         void *assertedValue )
2077 {
2078         struct berval *asserted = (struct berval *) assertedValue;
2079         int vsign = 1, asign = 1;       /* default sign = '+' */
2080         struct berval v, a;
2081         int match;
2082
2083         v = *value;
2084         if( v.bv_val[0] == '-' ) {
2085                 vsign = -1;
2086                 v.bv_val++;
2087                 v.bv_len--;
2088         }
2089
2090         if( BER_BVISEMPTY( &v ) ) vsign = 0;
2091
2092         a = *asserted;
2093         if( a.bv_val[0] == '-' ) {
2094                 asign = -1;
2095                 a.bv_val++;
2096                 a.bv_len--;
2097         }
2098
2099         if( BER_BVISEMPTY( &a ) ) vsign = 0;
2100
2101         match = vsign - asign;
2102         if( match == 0 ) {
2103                 match = ( v.bv_len != a.bv_len
2104                         ? ( v.bv_len < a.bv_len ? -1 : 1 )
2105                         : memcmp( v.bv_val, a.bv_val, v.bv_len ));
2106                 if( vsign < 0 ) match = -match;
2107         }
2108
2109         *matchp = match;
2110         return LDAP_SUCCESS;
2111 }
2112         
2113 static int
2114 countryStringValidate(
2115         Syntax *syntax,
2116         struct berval *val )
2117 {
2118         if( val->bv_len != 2 ) return LDAP_INVALID_SYNTAX;
2119
2120         if( !SLAP_PRINTABLE(val->bv_val[0]) ) {
2121                 return LDAP_INVALID_SYNTAX;
2122         }
2123         if( !SLAP_PRINTABLE(val->bv_val[1]) ) {
2124                 return LDAP_INVALID_SYNTAX;
2125         }
2126
2127         return LDAP_SUCCESS;
2128 }
2129
2130 static int
2131 printableStringValidate(
2132         Syntax *syntax,
2133         struct berval *val )
2134 {
2135         ber_len_t i;
2136
2137         if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
2138
2139         for(i=0; i < val->bv_len; i++) {
2140                 if( !SLAP_PRINTABLE(val->bv_val[i]) ) {
2141                         return LDAP_INVALID_SYNTAX;
2142                 }
2143         }
2144
2145         return LDAP_SUCCESS;
2146 }
2147
2148 static int
2149 printablesStringValidate(
2150         Syntax *syntax,
2151         struct berval *val )
2152 {
2153         ber_len_t i, len;
2154
2155         if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
2156
2157         for(i=0,len=0; i < val->bv_len; i++) {
2158                 int c = val->bv_val[i];
2159
2160                 if( c == '$' ) {
2161                         if( len == 0 ) {
2162                                 return LDAP_INVALID_SYNTAX;
2163                         }
2164                         len = 0;
2165
2166                 } else if ( SLAP_PRINTABLE(c) ) {
2167                         len++;
2168                 } else {
2169                         return LDAP_INVALID_SYNTAX;
2170                 }
2171         }
2172
2173         if( len == 0 ) {
2174                 return LDAP_INVALID_SYNTAX;
2175         }
2176
2177         return LDAP_SUCCESS;
2178 }
2179
2180 static int
2181 IA5StringValidate(
2182         Syntax *syntax,
2183         struct berval *val )
2184 {
2185         ber_len_t i;
2186
2187         for(i=0; i < val->bv_len; i++) {
2188                 if( !LDAP_ASCII(val->bv_val[i]) ) {
2189                         return LDAP_INVALID_SYNTAX;
2190                 }
2191         }
2192
2193         return LDAP_SUCCESS;
2194 }
2195
2196 static int
2197 IA5StringNormalize(
2198         slap_mask_t use,
2199         Syntax *syntax,
2200         MatchingRule *mr,
2201         struct berval *val,
2202         struct berval *normalized,
2203         void *ctx )
2204 {
2205         char *p, *q;
2206         int casefold = !SLAP_MR_ASSOCIATED( mr,
2207                 slap_schema.si_mr_caseExactIA5Match );
2208
2209         assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
2210
2211         p = val->bv_val;
2212
2213         /* Ignore initial whitespace */
2214         while ( ASCII_SPACE( *p ) ) p++;
2215
2216         normalized->bv_len = val->bv_len - ( p - val->bv_val );
2217         normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2218         AC_MEMCPY( normalized->bv_val, p, normalized->bv_len );
2219         normalized->bv_val[normalized->bv_len] = '\0';
2220
2221         p = q = normalized->bv_val;
2222
2223         while ( *p ) {
2224                 if ( ASCII_SPACE( *p ) ) {
2225                         *q++ = *p++;
2226
2227                         /* Ignore the extra whitespace */
2228                         while ( ASCII_SPACE( *p ) ) {
2229                                 p++;
2230                         }
2231
2232                 } else if ( casefold ) {
2233                         /* Most IA5 rules require casefolding */
2234                         *q++ = TOLOWER(*p); p++;
2235
2236                 } else {
2237                         *q++ = *p++;
2238                 }
2239         }
2240
2241         assert( normalized->bv_val <= p );
2242         assert( q <= p );
2243
2244         /*
2245          * If the string ended in space, backup the pointer one
2246          * position.  One is enough because the above loop collapsed
2247          * all whitespace to a single space.
2248          */
2249         if ( q > normalized->bv_val && ASCII_SPACE( q[-1] ) ) --q;
2250
2251         /* null terminate */
2252         *q = '\0';
2253
2254         normalized->bv_len = q - normalized->bv_val;
2255
2256         return LDAP_SUCCESS;
2257 }
2258
2259 static int
2260 UUIDValidate(
2261         Syntax *syntax,
2262         struct berval *in )
2263 {
2264         int i;
2265         if( in->bv_len != 36 ) {
2266                 return LDAP_INVALID_SYNTAX;
2267         }
2268
2269         for( i=0; i<36; i++ ) {
2270                 switch(i) {
2271                         case 8:
2272                         case 13:
2273                         case 18:
2274                         case 23:
2275                                 if( in->bv_val[i] != '-' ) {
2276                                         return LDAP_INVALID_SYNTAX;
2277                                 }
2278                                 break;
2279                         default:
2280                                 if( !ASCII_HEX( in->bv_val[i]) ) {
2281                                         return LDAP_INVALID_SYNTAX;
2282                                 }
2283                 }
2284         }
2285         
2286         return LDAP_SUCCESS;
2287 }
2288
2289 static int
2290 UUIDPretty(
2291         Syntax *syntax,
2292         struct berval *in,
2293         struct berval *out,
2294         void *ctx )
2295 {
2296         int i;
2297         int rc=LDAP_INVALID_SYNTAX;
2298
2299         assert( in != NULL );
2300         assert( out != NULL );
2301
2302         if( in->bv_len != 36 ) return LDAP_INVALID_SYNTAX;
2303
2304         out->bv_len = 36;
2305         out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
2306
2307         for( i=0; i<36; i++ ) {
2308                 switch(i) {
2309                         case 8:
2310                         case 13:
2311                         case 18:
2312                         case 23:
2313                                 if( in->bv_val[i] != '-' ) {
2314                                         goto handle_error;
2315                                 }
2316                                 out->bv_val[i] = '-';
2317                                 break;
2318
2319                         default:
2320                                 if( !ASCII_HEX( in->bv_val[i]) ) {
2321                                         goto handle_error;
2322                                 }
2323                                 out->bv_val[i] = TOLOWER( in->bv_val[i] );
2324                 }
2325         }
2326
2327         rc = LDAP_SUCCESS;
2328         out->bv_val[ out->bv_len ] = '\0';
2329
2330         if( 0 ) {
2331 handle_error:
2332                 slap_sl_free( out->bv_val, ctx );
2333                 out->bv_val = NULL;
2334         }
2335
2336         return rc;
2337 }
2338
2339 int
2340 UUIDNormalize(
2341         slap_mask_t usage,
2342         Syntax *syntax,
2343         MatchingRule *mr,
2344         struct berval *val,
2345         struct berval *normalized,
2346         void *ctx )
2347 {
2348         unsigned char octet = '\0';
2349         int i;
2350         int j;
2351         normalized->bv_len = 16;
2352         normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
2353
2354         for( i=0, j=0; i<36; i++ ) {
2355                 unsigned char nibble;
2356                 if( val->bv_val[i] == '-' ) {
2357                         continue;
2358
2359                 } else if( ASCII_DIGIT( val->bv_val[i] ) ) {
2360                         nibble = val->bv_val[i] - '0';
2361
2362                 } else if( ASCII_HEXLOWER( val->bv_val[i] ) ) {
2363                         nibble = val->bv_val[i] - ('a'-10);
2364
2365                 } else if( ASCII_HEXUPPER( val->bv_val[i] ) ) {
2366                         nibble = val->bv_val[i] - ('A'-10);
2367
2368                 } else {
2369                         slap_sl_free( normalized->bv_val, ctx );
2370                         return LDAP_INVALID_SYNTAX;
2371                 }
2372
2373                 if( j & 1 ) {
2374                         octet |= nibble;
2375                         normalized->bv_val[j>>1] = octet;
2376                 } else {
2377                         octet = nibble << 4;
2378                 }
2379                 j++;
2380         }
2381
2382         normalized->bv_val[normalized->bv_len] = 0;
2383         return LDAP_SUCCESS;
2384 }
2385
2386
2387
2388 static int
2389 numericStringValidate(
2390         Syntax *syntax,
2391         struct berval *in )
2392 {
2393         ber_len_t i;
2394
2395         if( BER_BVISEMPTY( in ) ) return LDAP_INVALID_SYNTAX;
2396
2397         for(i=0; i < in->bv_len; i++) {
2398                 if( !SLAP_NUMERIC(in->bv_val[i]) ) {
2399                         return LDAP_INVALID_SYNTAX;
2400                 }
2401         }
2402
2403         return LDAP_SUCCESS;
2404 }
2405
2406 static int
2407 numericStringNormalize(
2408         slap_mask_t usage,
2409         Syntax *syntax,
2410         MatchingRule *mr,
2411         struct berval *val,
2412         struct berval *normalized,
2413         void *ctx )
2414 {
2415         /* removal all spaces */
2416         char *p, *q;
2417
2418         assert( !BER_BVISEMPTY( val ) );
2419
2420         normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
2421
2422         p = val->bv_val;
2423         q = normalized->bv_val;
2424
2425         while ( *p ) {
2426                 if ( ASCII_SPACE( *p ) ) {
2427                         /* Ignore whitespace */
2428                         p++;
2429                 } else {
2430                         *q++ = *p++;
2431                 }
2432         }
2433
2434         /* we should have copied no more then is in val */
2435         assert( (q - normalized->bv_val) <= (p - val->bv_val) );
2436
2437         /* null terminate */
2438         *q = '\0';
2439
2440         normalized->bv_len = q - normalized->bv_val;
2441
2442         if( BER_BVISEMPTY( normalized ) ) {
2443                 normalized->bv_val = slap_sl_realloc( normalized->bv_val, 2, ctx );
2444                 normalized->bv_val[0] = ' ';
2445                 normalized->bv_val[1] = '\0';
2446                 normalized->bv_len = 1;
2447         }
2448
2449         return LDAP_SUCCESS;
2450 }
2451
2452 /*
2453  * Integer conversion macros that will use the largest available
2454  * type.
2455  */
2456 #if defined(HAVE_STRTOLL) && defined(HAVE_LONG_LONG)
2457 # define SLAP_STRTOL(n,e,b)  strtoll(n,e,b) 
2458 # define SLAP_LONG           long long
2459 #else
2460 # define SLAP_STRTOL(n,e,b)  strtol(n,e,b)
2461 # define SLAP_LONG           long
2462 #endif /* HAVE_STRTOLL ... */
2463
2464 static int
2465 integerBitAndMatch(
2466         int *matchp,
2467         slap_mask_t flags,
2468         Syntax *syntax,
2469         MatchingRule *mr,
2470         struct berval *value,
2471         void *assertedValue )
2472 {
2473         SLAP_LONG lValue, lAssertedValue;
2474
2475         errno = 0;
2476         /* safe to assume integers are NUL terminated? */
2477         lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
2478         if( errno == ERANGE )
2479         {
2480                 return LDAP_CONSTRAINT_VIOLATION;
2481         }
2482
2483         lAssertedValue = SLAP_STRTOL(((struct berval *)assertedValue)->bv_val,
2484                 NULL, 10);
2485         if( errno == ERANGE )
2486         {
2487                 return LDAP_CONSTRAINT_VIOLATION;
2488         }
2489
2490         *matchp = ((lValue & lAssertedValue) == lAssertedValue) ? 0 : 1;
2491         return LDAP_SUCCESS;
2492 }
2493
2494 static int
2495 integerBitOrMatch(
2496         int *matchp,
2497         slap_mask_t flags,
2498         Syntax *syntax,
2499         MatchingRule *mr,
2500         struct berval *value,
2501         void *assertedValue )
2502 {
2503         SLAP_LONG lValue, lAssertedValue;
2504
2505         errno = 0;
2506         /* safe to assume integers are NUL terminated? */
2507         lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
2508         if( errno == ERANGE )
2509         {
2510                 return LDAP_CONSTRAINT_VIOLATION;
2511         }
2512
2513         lAssertedValue = SLAP_STRTOL( ((struct berval *)assertedValue)->bv_val,
2514                 NULL, 10);
2515         if( errno == ERANGE )
2516         {
2517                 return LDAP_CONSTRAINT_VIOLATION;
2518         }
2519
2520         *matchp = ((lValue & lAssertedValue) != 0) ? 0 : -1;
2521         return LDAP_SUCCESS;
2522 }
2523
2524 static int
2525 serialNumberAndIssuerValidate(
2526         Syntax *syntax,
2527         struct berval *in )
2528 {
2529         int rc;
2530         ber_len_t n;
2531         struct berval sn, i;
2532
2533         Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n",
2534                 in->bv_val, 0, 0 );
2535
2536         if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
2537
2538         if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
2539                 /* Parse old format */
2540                 i.bv_val = ber_bvchr( in, '$' );
2541                 if( BER_BVISNULL( &i ) ) return LDAP_INVALID_SYNTAX;
2542
2543                 sn.bv_val = in->bv_val;
2544                 sn.bv_len = i.bv_val - in->bv_val;
2545
2546                 i.bv_val++;
2547                 i.bv_len = in->bv_len - (sn.bv_len + 1);
2548
2549                 /* eat leading zeros */
2550                 for( n=0; n < (sn.bv_len-1); n++ ) {
2551                         if( sn.bv_val[n] != '0' ) break;
2552                 }
2553                 sn.bv_val += n;
2554                 sn.bv_len -= n;
2555
2556                 for( n=0; n < sn.bv_len; n++ ) {
2557                         if( !ASCII_DIGIT(sn.bv_val[n]) ) return LDAP_INVALID_SYNTAX;
2558                 }
2559
2560         } else {
2561                 /* Parse GSER format */ 
2562                 int havesn=0,haveissuer=0;
2563                 struct berval x = *in;
2564                 x.bv_val++;
2565                 x.bv_len-=2;
2566
2567                 /* eat leading spaces */
2568                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2569                         /* empty */;
2570                 }
2571
2572                 if ( x.bv_len < STRLENOF("serialNumber 0,issuer \"\"")) {
2573                         return LDAP_INVALID_SYNTAX;
2574                 }
2575
2576                 /* should be at issuer or serialNumber NamedValue */
2577                 if( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer")) == 0 ) {
2578                         /* parse issuer */
2579                         x.bv_val += STRLENOF("issuer");
2580                         x.bv_len -= STRLENOF("issuer");
2581
2582                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2583                         x.bv_val++; x.bv_len--;
2584
2585                         /* eat leading spaces */
2586                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2587                                 /* empty */;
2588                         }
2589                         
2590                         if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
2591                         x.bv_val++; x.bv_len--;
2592
2593                         i.bv_val = x.bv_val;
2594                         i.bv_len = 0;
2595
2596                         for( ; i.bv_len < x.bv_len; ) {
2597                                 if ( i.bv_val[i.bv_len] != '"' ) {
2598                                         i.bv_len++;
2599                                         continue;
2600                                 }
2601                                 if ( i.bv_val[i.bv_len+1] == '"' ) {
2602                                         /* double dquote */
2603                                         i.bv_len+=2;
2604                                         continue;
2605                                 }
2606                                 break;
2607                         }
2608                         x.bv_val += i.bv_len+1;
2609                         x.bv_len -= i.bv_len+1;
2610
2611                         if ( x.bv_len < STRLENOF(",serialNumber 0")) {
2612                                 return LDAP_INVALID_SYNTAX;
2613                         }
2614
2615                         haveissuer++;
2616
2617                 } else if( strncasecmp( x.bv_val, "serialNumber",
2618                         STRLENOF("serialNumber")) == 0 )
2619                 {
2620                         /* parse serialNumber */
2621                         int neg=0;
2622                         x.bv_val += STRLENOF("serialNumber");
2623                         x.bv_len -= STRLENOF("serialNumber");
2624
2625                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2626                         x.bv_val++; x.bv_len--;
2627
2628                         /* eat leading spaces */
2629                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2630                                 /* empty */;
2631                         }
2632                         
2633                         sn.bv_val = x.bv_val;
2634                         sn.bv_len = 0;
2635
2636                         if( sn.bv_val[0] == '-' ) {
2637                                 neg++;
2638                                 sn.bv_len++;
2639                         }
2640
2641                         for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) {
2642                                 if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break;
2643                         }
2644
2645                         if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX;
2646                         if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) {
2647                                 return LDAP_INVALID_SYNTAX;
2648                         }
2649
2650                         x.bv_val += sn.bv_len; x.bv_len -= sn.bv_len;
2651
2652                         if ( x.bv_len < STRLENOF( ",issuer \"\"" )) {
2653                                 return LDAP_INVALID_SYNTAX;
2654                         }
2655
2656                         havesn++;
2657
2658                 } else return LDAP_INVALID_SYNTAX;
2659
2660                 if( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX;
2661                 x.bv_val++; x.bv_len--;
2662
2663                 /* eat spaces */
2664                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2665                         /* empty */;
2666                 }
2667
2668                 /* should be at remaining NamedValue */
2669                 if( !haveissuer && (strncasecmp( x.bv_val, "issuer",
2670                         STRLENOF("issuer" )) == 0 ))
2671                 {
2672                         /* parse issuer */
2673                         x.bv_val += STRLENOF("issuer");
2674                         x.bv_len -= STRLENOF("issuer");
2675
2676                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2677                         x.bv_val++; x.bv_len--;
2678
2679                         /* eat leading spaces */
2680                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2681                                  /* empty */;
2682                         }
2683                         
2684                         if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
2685                         x.bv_val++; x.bv_len--;
2686
2687                         i.bv_val = x.bv_val;
2688                         i.bv_len = 0;
2689
2690                         for( ; i.bv_len < x.bv_len; ) {
2691                                 if ( i.bv_val[i.bv_len] != '"' ) {
2692                                         i.bv_len++;
2693                                         continue;
2694                                 }
2695                                 if ( i.bv_val[i.bv_len+1] == '"' ) {
2696                                         /* double dquote */
2697                                         i.bv_len+=2;
2698                                         continue;
2699                                 }
2700                                 break;
2701                         }
2702                         x.bv_val += i.bv_len+1;
2703                         x.bv_len -= i.bv_len+1;
2704
2705                 } else if( !havesn && (strncasecmp( x.bv_val, "serialNumber",
2706                         STRLENOF("serialNumber")) == 0 ))
2707                 {
2708                         /* parse serialNumber */
2709                         int neg=0;
2710                         x.bv_val += STRLENOF("serialNumber");
2711                         x.bv_len -= STRLENOF("serialNumber");
2712
2713                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2714                         x.bv_val++; x.bv_len--;
2715
2716                         /* eat leading spaces */
2717                         for( ; (x.bv_val[0] == ' ') && x.bv_len ; x.bv_val++, x.bv_len--) {
2718                                 /* empty */;
2719                         }
2720                         
2721                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2722                         x.bv_val++; x.bv_len--;
2723
2724                         sn.bv_val = x.bv_val;
2725                         sn.bv_len = 0;
2726
2727                         if( sn.bv_val[0] == '-' ) {
2728                                 neg++;
2729                                 sn.bv_len++;
2730                         }
2731
2732                         for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) {
2733                                 if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break;
2734                         }
2735
2736                         if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX;
2737                         if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) {
2738                                 return LDAP_INVALID_SYNTAX;
2739                         }
2740
2741                         x.bv_val += sn.bv_len;
2742                         x.bv_len -= sn.bv_len;
2743
2744                 } else return LDAP_INVALID_SYNTAX;
2745
2746                 /* eat trailing spaces */
2747                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2748                         /* empty */;
2749                 }
2750
2751                 /* should have no characters left... */
2752                 if( x.bv_len ) return LDAP_INVALID_SYNTAX;
2753         }
2754
2755         /* validate DN -- doesn't handle double dquote */ 
2756         rc = dnValidate( NULL, &i );
2757         if( rc ) return LDAP_INVALID_SYNTAX;
2758
2759         Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: OKAY\n",
2760                 0, 0, 0 );
2761         return LDAP_SUCCESS;
2762 }
2763
2764 int
2765 serialNumberAndIssuerPretty(
2766         Syntax *syntax,
2767         struct berval *in,
2768         struct berval *out,
2769         void *ctx )
2770 {
2771         int rc;
2772         ber_len_t n;
2773         struct berval sn, i, ni;
2774
2775         assert( in != NULL );
2776         assert( out != NULL );
2777
2778         Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerPretty: <%s>\n",
2779                 in->bv_val, 0, 0 );
2780
2781         if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
2782
2783         if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
2784                 /* Parse old format */
2785                 i.bv_val = ber_bvchr( in, '$' );
2786                 if( BER_BVISNULL( &i ) ) return LDAP_INVALID_SYNTAX;
2787
2788                 sn.bv_val = in->bv_val;
2789                 sn.bv_len = i.bv_val - in->bv_val;
2790
2791                 i.bv_val++;
2792                 i.bv_len = in->bv_len - (sn.bv_len + 1);
2793
2794                 /* eat leading zeros */
2795                 for( n=0; n < (sn.bv_len-1); n++ ) {
2796                         if( sn.bv_val[n] != '0' ) break;
2797                 }
2798                 sn.bv_val += n;
2799                 sn.bv_len -= n;
2800
2801                 for( n=0; n < sn.bv_len; n++ ) {
2802                         if( !ASCII_DIGIT(sn.bv_val[n]) ) return LDAP_INVALID_SYNTAX;
2803                 }
2804
2805         } else {
2806                 /* Parse GSER format */ 
2807                 int havesn=0,haveissuer=0;
2808                 struct berval x = *in;
2809                 x.bv_val++;
2810                 x.bv_len-=2;
2811
2812                 /* eat leading spaces */
2813                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2814                         /* empty */;
2815                 }
2816
2817                 if ( x.bv_len < STRLENOF("serialNumber 0,issuer \"\"")) {
2818                         return LDAP_INVALID_SYNTAX;
2819                 }
2820
2821                 /* should be at issuer or serialNumber NamedValue */
2822                 if( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer")) == 0 ) {
2823                         /* parse issuer */
2824                         x.bv_val += STRLENOF("issuer");
2825                         x.bv_len -= STRLENOF("issuer");
2826
2827                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2828                         x.bv_val++; x.bv_len--;
2829
2830                         /* eat leading spaces */
2831                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2832                                 /* empty */;
2833                         }
2834                         
2835                         if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
2836                         x.bv_val++; x.bv_len--;
2837
2838                         i.bv_val = x.bv_val;
2839                         i.bv_len = 0;
2840
2841                         for( ; i.bv_len < x.bv_len; ) {
2842                                 if ( i.bv_val[i.bv_len] != '"' ) {
2843                                         i.bv_len++;
2844                                         continue;
2845                                 }
2846                                 if ( i.bv_val[i.bv_len+1] == '"' ) {
2847                                         /* double dquote */
2848                                         i.bv_len+=2;
2849                                         continue;
2850                                 }
2851                                 break;
2852                         }
2853                         x.bv_val += i.bv_len+1;
2854                         x.bv_len -= i.bv_len+1;
2855
2856                         if ( x.bv_len < STRLENOF(",serialNumber 0")) {
2857                                 return LDAP_INVALID_SYNTAX;
2858                         }
2859
2860                         haveissuer++;
2861
2862                 } else if( strncasecmp( x.bv_val, "serialNumber",
2863                         STRLENOF("serialNumber")) == 0 )
2864                 {
2865                         /* parse serialNumber */
2866                         int neg=0;
2867                         x.bv_val += STRLENOF("serialNumber");
2868                         x.bv_len -= STRLENOF("serialNumber");
2869
2870                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2871                         x.bv_val++; x.bv_len--;
2872
2873                         /* eat leading spaces */
2874                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2875                                 /* empty */;
2876                         }
2877                         
2878                         sn.bv_val = x.bv_val;
2879                         sn.bv_len = 0;
2880
2881                         if( sn.bv_val[0] == '-' ) {
2882                                 neg++;
2883                                 sn.bv_len++;
2884                         }
2885
2886                         for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) {
2887                                 if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break;
2888                         }
2889
2890                         if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX;
2891                         if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) {
2892                                 return LDAP_INVALID_SYNTAX;
2893                         }
2894
2895                         x.bv_val += sn.bv_len; x.bv_len -= sn.bv_len;
2896
2897                         if ( x.bv_len < STRLENOF( ",issuer \"\"" )) {
2898                                 return LDAP_INVALID_SYNTAX;
2899                         }
2900
2901                         havesn++;
2902
2903                 } else return LDAP_INVALID_SYNTAX;
2904
2905                 if( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX;
2906                 x.bv_val++; x.bv_len--;
2907
2908                 /* eat spaces */
2909                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2910                         /* empty */;
2911                 }
2912
2913                 /* should be at remaining NamedValue */
2914                 if( !haveissuer && (strncasecmp( x.bv_val, "issuer",
2915                         STRLENOF("issuer" )) == 0 ))
2916                 {
2917                         /* parse issuer */
2918                         x.bv_val += STRLENOF("issuer");
2919                         x.bv_len -= STRLENOF("issuer");
2920
2921                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2922                         x.bv_val++; x.bv_len--;
2923
2924                         /* eat leading spaces */
2925                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2926                                  /* empty */;
2927                         }
2928                         
2929                         if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
2930                         x.bv_val++; x.bv_len--;
2931
2932                         i.bv_val = x.bv_val;
2933                         i.bv_len = 0;
2934
2935                         for( ; i.bv_len < x.bv_len; ) {
2936                                 if ( i.bv_val[i.bv_len] != '"' ) {
2937                                         i.bv_len++;
2938                                         continue;
2939                                 }
2940                                 if ( i.bv_val[i.bv_len+1] == '"' ) {
2941                                         /* double dquote */
2942                                         i.bv_len+=2;
2943                                         continue;
2944                                 }
2945                                 break;
2946                         }
2947                         x.bv_val += i.bv_len+1;
2948                         x.bv_len -= i.bv_len+1;
2949
2950                 } else if( !havesn && (strncasecmp( x.bv_val, "serialNumber",
2951                         STRLENOF("serialNumber")) == 0 ))
2952                 {
2953                         /* parse serialNumber */
2954                         int neg=0;
2955                         x.bv_val += STRLENOF("serialNumber");
2956                         x.bv_len -= STRLENOF("serialNumber");
2957
2958                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
2959                         x.bv_val++; x.bv_len--;
2960
2961                         /* eat leading spaces */
2962                         for( ; (x.bv_val[0] == ' ') && x.bv_len ; x.bv_val++, x.bv_len--) {
2963                                 /* empty */;
2964                         }
2965                         
2966                         sn.bv_val = x.bv_val;
2967                         sn.bv_len = 0;
2968
2969                         if( sn.bv_val[0] == '-' ) {
2970                                 neg++;
2971                                 sn.bv_len++;
2972                         }
2973
2974                         for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) {
2975                                 if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break;
2976                         }
2977
2978                         if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX;
2979                         if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) {
2980                                 return LDAP_INVALID_SYNTAX;
2981                         }
2982
2983                         x.bv_val += sn.bv_len;
2984                         x.bv_len -= sn.bv_len;
2985
2986                 } else return LDAP_INVALID_SYNTAX;
2987
2988                 /* eat trailing spaces */
2989                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
2990                         /* empty */;
2991                 }
2992
2993                 /* should have no characters left... */
2994                 if( x.bv_len ) return LDAP_INVALID_SYNTAX;
2995
2996                 ber_dupbv_x( &ni, &i, ctx );
2997                 i = ni;
2998
2999                 /* need to handle double dquotes here */
3000         }
3001
3002         rc = dnPretty( syntax, &i, &ni, ctx );
3003
3004         if( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3005                 slap_sl_free( i.bv_val, ctx );
3006         }
3007
3008         if( rc ) return LDAP_INVALID_SYNTAX;
3009
3010         /* make room from sn + "$" */
3011         out->bv_len = STRLENOF("{ serialNumber , issuer \"\" }")
3012                 + sn.bv_len + ni.bv_len;
3013         out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
3014
3015         if( out->bv_val == NULL ) {
3016                 out->bv_len = 0;
3017                 slap_sl_free( ni.bv_val, ctx );
3018                 return LDAP_OTHER;
3019         }
3020
3021         n = 0;
3022         AC_MEMCPY( &out->bv_val[n], "{ serialNumber ",
3023                 STRLENOF("{ serialNumber "));
3024         n = STRLENOF("{ serialNumber ");
3025
3026         AC_MEMCPY( &out->bv_val[n], sn.bv_val, sn.bv_len );
3027         n += sn.bv_len;
3028
3029         AC_MEMCPY( &out->bv_val[n], ", issuer \"", STRLENOF(", issuer \""));
3030         n += STRLENOF(", issuer \"");
3031
3032         AC_MEMCPY( &out->bv_val[n], ni.bv_val, ni.bv_len );
3033         n += ni.bv_len;
3034
3035         AC_MEMCPY( &out->bv_val[n], "\" }", STRLENOF("\" }"));
3036         n += STRLENOF("\" }");
3037
3038         out->bv_val[n] = '\0';
3039
3040         assert( n == out->bv_len );
3041
3042         Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s>\n",
3043                 out->bv_val, 0, 0 );
3044
3045         slap_sl_free( ni.bv_val, ctx );
3046
3047         return LDAP_SUCCESS; 
3048 }
3049
3050 /*
3051  * This routine is called by certificateExactNormalize when
3052  * certificateExactNormalize receives a search string instead of
3053  * a certificate. This routine checks if the search value is valid
3054  * and then returns the normalized value
3055  */
3056 static int
3057 serialNumberAndIssuerNormalize(
3058         slap_mask_t usage,
3059         Syntax *syntax,
3060         MatchingRule *mr,
3061         struct berval *in,
3062         struct berval *out,
3063         void *ctx )
3064 {
3065         int rc;
3066         ber_len_t n;
3067         struct berval sn, i, ni;
3068
3069         assert( in != NULL );
3070         assert( out != NULL );
3071
3072         Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n",
3073                 in->bv_val, 0, 0 );
3074
3075         if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
3076
3077         if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) {
3078                 /* Parse old format */
3079                 i.bv_val = ber_bvchr( in, '$' );
3080                 if( BER_BVISNULL( &i ) ) return LDAP_INVALID_SYNTAX;
3081
3082                 sn.bv_val = in->bv_val;
3083                 sn.bv_len = i.bv_val - in->bv_val;
3084
3085                 i.bv_val++;
3086                 i.bv_len = in->bv_len - (sn.bv_len + 1);
3087
3088                 /* eat leading zeros */
3089                 for( n=0; n < (sn.bv_len-1); n++ ) {
3090                         if( sn.bv_val[n] != '0' ) break;
3091                 }
3092                 sn.bv_val += n;
3093                 sn.bv_len -= n;
3094
3095                 for( n=0; n < sn.bv_len; n++ ) {
3096                         if( !ASCII_DIGIT(sn.bv_val[n]) ) return LDAP_INVALID_SYNTAX;
3097                 }
3098
3099         } else {
3100                 /* Parse GSER format */ 
3101                 int havesn=0,haveissuer=0;
3102                 struct berval x = *in;
3103                 x.bv_val++;
3104                 x.bv_len-=2;
3105
3106                 /* eat leading spaces */
3107                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
3108                         /* empty */;
3109                 }
3110
3111                 if ( x.bv_len < STRLENOF("serialNumber 0,issuer \"\"")) {
3112                         return LDAP_INVALID_SYNTAX;
3113                 }
3114
3115                 /* should be at issuer or serialNumber NamedValue */
3116                 if( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer")) == 0 ) {
3117                         /* parse issuer */
3118                         x.bv_val += STRLENOF("issuer");
3119                         x.bv_len -= STRLENOF("issuer");
3120
3121                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3122                         x.bv_val++; x.bv_len--;
3123
3124                         /* eat leading spaces */
3125                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
3126                                 /* empty */;
3127                         }
3128                         
3129                         if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3130                         x.bv_val++; x.bv_len--;
3131
3132                         i.bv_val = x.bv_val;
3133                         i.bv_len = 0;
3134
3135                         for( ; i.bv_len < x.bv_len; ) {
3136                                 if ( i.bv_val[i.bv_len] != '"' ) {
3137                                         i.bv_len++;
3138                                         continue;
3139                                 }
3140                                 if ( i.bv_val[i.bv_len+1] == '"' ) {
3141                                         /* double dquote */
3142                                         i.bv_len+=2;
3143                                         continue;
3144                                 }
3145                                 break;
3146                         }
3147                         x.bv_val += i.bv_len+1;
3148                         x.bv_len -= i.bv_len+1;
3149
3150                         if ( x.bv_len < STRLENOF(",serialNumber 0")) {
3151                                 return LDAP_INVALID_SYNTAX;
3152                         }
3153
3154                         haveissuer++;
3155
3156                 } else if( strncasecmp( x.bv_val, "serialNumber",
3157                         STRLENOF("serialNumber")) == 0 )
3158                 {
3159                         /* parse serialNumber */
3160                         int neg=0;
3161                         x.bv_val += STRLENOF("serialNumber");
3162                         x.bv_len -= STRLENOF("serialNumber");
3163
3164                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3165                         x.bv_val++; x.bv_len--;
3166
3167                         /* eat leading spaces */
3168                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
3169                                 /* empty */;
3170                         }
3171                         
3172                         sn.bv_val = x.bv_val;
3173                         sn.bv_len = 0;
3174
3175                         if( sn.bv_val[0] == '-' ) {
3176                                 neg++;
3177                                 sn.bv_len++;
3178                         }
3179
3180                         for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) {
3181                                 if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break;
3182                         }
3183
3184                         if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX;
3185                         if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) {
3186                                 return LDAP_INVALID_SYNTAX;
3187                         }
3188
3189                         x.bv_val += sn.bv_len; x.bv_len -= sn.bv_len;
3190
3191                         if ( x.bv_len < STRLENOF( ",issuer \"\"" )) {
3192                                 return LDAP_INVALID_SYNTAX;
3193                         }
3194
3195                         havesn++;
3196
3197                 } else return LDAP_INVALID_SYNTAX;
3198
3199                 if( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX;
3200                 x.bv_val++; x.bv_len--;
3201
3202                 /* eat spaces */
3203                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
3204                         /* empty */;
3205                 }
3206
3207                 /* should be at remaining NamedValue */
3208                 if( !haveissuer && (strncasecmp( x.bv_val, "issuer",
3209                         STRLENOF("issuer" )) == 0 ))
3210                 {
3211                         /* parse issuer */
3212                         x.bv_val += STRLENOF("issuer");
3213                         x.bv_len -= STRLENOF("issuer");
3214
3215                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3216                         x.bv_val++; x.bv_len--;
3217
3218                         /* eat leading spaces */
3219                         for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
3220                                  /* empty */;
3221                         }
3222                         
3223                         if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
3224                         x.bv_val++; x.bv_len--;
3225
3226                         i.bv_val = x.bv_val;
3227                         i.bv_len = 0;
3228
3229                         for( ; i.bv_len < x.bv_len; ) {
3230                                 if ( i.bv_val[i.bv_len] != '"' ) {
3231                                         i.bv_len++;
3232                                         continue;
3233                                 }
3234                                 if ( i.bv_val[i.bv_len+1] == '"' ) {
3235                                         /* double dquote */
3236                                         i.bv_len+=2;
3237                                         continue;
3238                                 }
3239                                 break;
3240                         }
3241                         x.bv_val += i.bv_len+1;
3242                         x.bv_len -= i.bv_len+1;
3243
3244                 } else if( !havesn && (strncasecmp( x.bv_val, "serialNumber",
3245                         STRLENOF("serialNumber")) == 0 ))
3246                 {
3247                         /* parse serialNumber */
3248                         int neg=0;
3249                         x.bv_val += STRLENOF("serialNumber");
3250                         x.bv_len -= STRLENOF("serialNumber");
3251
3252                         if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
3253                         x.bv_val++; x.bv_len--;
3254
3255                         /* eat leading spaces */
3256                         for( ; (x.bv_val[0] == ' ') && x.bv_len ; x.bv_val++, x.bv_len--) {
3257                                 /* empty */;
3258                         }
3259                         
3260                         sn.bv_val = x.bv_val;
3261                         sn.bv_len = 0;
3262
3263                         if( sn.bv_val[0] == '-' ) {
3264                                 neg++;
3265                                 sn.bv_len++;
3266                         }
3267
3268                         for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) {
3269                                 if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break;
3270                         }
3271
3272                         if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX;
3273                         if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) {
3274                                 return LDAP_INVALID_SYNTAX;
3275                         }
3276
3277                         x.bv_val += sn.bv_len;
3278                         x.bv_len -= sn.bv_len;
3279
3280                 } else return LDAP_INVALID_SYNTAX;
3281
3282                 /* eat trailing spaces */
3283                 for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
3284                         /* empty */;
3285                 }
3286
3287                 /* should have no characters left... */
3288                 if( x.bv_len ) return LDAP_INVALID_SYNTAX;
3289
3290                 ber_dupbv_x( &ni, &i, ctx );
3291                 i = ni;
3292
3293                 /* need to handle double dquotes here */
3294         }
3295
3296         rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
3297
3298         if( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
3299                 slap_sl_free( i.bv_val, ctx );
3300         }
3301
3302         if( rc ) return LDAP_INVALID_SYNTAX;
3303
3304         /* make room from sn + "$" */
3305         out->bv_len = STRLENOF( "{ serialNumber , issuer \"\" }" )
3306                 + sn.bv_len + ni.bv_len;
3307         out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
3308
3309         if( out->bv_val == NULL ) {
3310                 out->bv_len = 0;
3311                 slap_sl_free( ni.bv_val, ctx );
3312                 return LDAP_OTHER;
3313         }
3314
3315         n = 0;
3316         AC_MEMCPY( &out->bv_val[n], "{ serialNumber ",
3317                 STRLENOF( "{ serialNumber " ));
3318         n = STRLENOF( "{ serialNumber " );
3319
3320         AC_MEMCPY( &out->bv_val[n], sn.bv_val, sn.bv_len );
3321         n += sn.bv_len;
3322
3323         AC_MEMCPY( &out->bv_val[n], ", issuer \"", STRLENOF( ", issuer \"" ));
3324         n += STRLENOF( ", issuer \"" );
3325
3326         AC_MEMCPY( &out->bv_val[n], ni.bv_val, ni.bv_len );
3327         n += ni.bv_len;
3328
3329         AC_MEMCPY( &out->bv_val[n], "\" }", STRLENOF( "\" }" ));
3330         n += STRLENOF( "\" }" );
3331
3332         out->bv_val[n] = '\0';
3333
3334         assert( n == out->bv_len );
3335
3336         Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s>\n",
3337                 out->bv_val, 0, 0 );
3338
3339         slap_sl_free( ni.bv_val, ctx );
3340
3341         return LDAP_SUCCESS;
3342 }
3343
3344 static int
3345 certificateExactNormalize(
3346         slap_mask_t usage,
3347         Syntax *syntax,
3348         MatchingRule *mr,
3349         struct berval *val,
3350         struct berval *normalized,
3351         void *ctx )
3352 {
3353         BerElementBuffer berbuf;
3354         BerElement *ber = (BerElement *)&berbuf;
3355         ber_tag_t tag;
3356         ber_len_t len;
3357         ber_int_t i;
3358         char serialbuf[64], *serial = serialbuf;
3359         ber_len_t seriallen;
3360         struct berval issuer_dn = BER_BVNULL, bvdn;
3361         unsigned char *p;
3362         int rc = LDAP_INVALID_SYNTAX;
3363
3364         if( BER_BVISEMPTY( val ) ) goto done;
3365
3366         if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
3367                 return serialNumberAndIssuerNormalize(0,NULL,NULL,val,normalized,ctx);
3368         }
3369
3370         assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
3371
3372         ber_init2( ber, val, LBER_USE_DER );
3373         tag = ber_skip_tag( ber, &len );        /* Signed Sequence */
3374         tag = ber_skip_tag( ber, &len );        /* Sequence */
3375         tag = ber_peek_tag( ber, &len );        /* Optional version? */
3376         if ( tag == SLAP_X509_OPT_C_VERSION ) {
3377                 tag = ber_skip_tag( ber, &len );
3378                 tag = ber_get_int( ber, &i );   /* version */
3379         }
3380
3381         /* NOTE: move the test here from certificateNormalize,
3382          * so that we can validate certs with serial longer
3383          * than sizeof(ber_int_t) */
3384         tag = ber_peek_tag( ber, &len );        /* serial */
3385         if ( len > sizeof(ber_int_t) ) {
3386                 unsigned char *ptr;
3387                 
3388                 tag = ber_skip_tag( ber, &len );
3389                 ptr = (unsigned char *)ber->ber_ptr;
3390                 ber_skip_data( ber, len );
3391
3392                 while ( ptr[0] == '\0' && len > 0 ) {
3393                         ptr++;
3394                         len--;
3395                 }
3396
3397 #if defined(USE_MP_BIGNUM)
3398                 /* TODO */
3399
3400 #elif defined(USE_MP_GMP)
3401                 /* TODO */
3402                 /* hint: use mpz_import(), mpz_get_str() */
3403
3404 #elif defined(USE_MP_LONG_LONG)
3405                 if ( len <= sizeof( unsigned long long ) ) {
3406                         unsigned long long      sn = 0;
3407                         int                     i;
3408
3409                         sn = ptr[0];
3410
3411                         for ( i = 1; i < len; i++ ) {
3412                                 sn <<= 8;
3413                                 sn += ptr[i];
3414                         }
3415
3416                         seriallen = snprintf( serialbuf, sizeof(serialbuf), "%llu", sn );
3417
3418                 } else {
3419                         /* do not accept serialNumber that requires
3420                          * more than long long */
3421                         rc = LDAP_INVALID_SYNTAX;
3422                         goto done;
3423                 }
3424
3425 #else
3426                 /* do not accept serialNumber that requires
3427                  * more than long */
3428                 rc = LDAP_INVALID_SYNTAX;
3429                 goto done;
3430 #endif
3431
3432         } else {
3433                 tag = ber_get_int( ber, &i );   /* serial */
3434                 seriallen = snprintf( serialbuf, sizeof(serialbuf), "%d", i );
3435         }
3436         tag = ber_skip_tag( ber, &len );        /* SignatureAlg */
3437         ber_skip_data( ber, len );
3438         tag = ber_peek_tag( ber, &len );        /* IssuerDN */
3439         len = ber_ptrlen( ber );
3440         bvdn.bv_val = val->bv_val + len;
3441         bvdn.bv_len = val->bv_len - len;
3442
3443         rc = dnX509normalize( &bvdn, &issuer_dn );
3444         if( rc != LDAP_SUCCESS ) goto done;
3445
3446         normalized->bv_len = STRLENOF( "{ serialNumber , issuer \"\" }" )
3447                 + seriallen + issuer_dn.bv_len;
3448         normalized->bv_val = ch_malloc(normalized->bv_len+1);
3449
3450         p = (unsigned char *)normalized->bv_val;
3451
3452         AC_MEMCPY(p, "{ serialNumber ", STRLENOF( "{ serialNumber " ));
3453         p += STRLENOF( "{ serialNumber " );
3454
3455         AC_MEMCPY(p, serial, seriallen);
3456         p += seriallen;
3457
3458         AC_MEMCPY(p, ", issuer \"", STRLENOF( ", issuer \"" ));
3459         p += STRLENOF( ", issuer \"" );
3460
3461         AC_MEMCPY(p, issuer_dn.bv_val, issuer_dn.bv_len);
3462         p += issuer_dn.bv_len;
3463
3464         AC_MEMCPY(p, "\" }", STRLENOF( "\" }" ));
3465         p += STRLENOF( "\" }" );
3466
3467         *p = '\0';
3468
3469         Debug( LDAP_DEBUG_TRACE, "certificateExactNormalize: %s\n",
3470                 normalized->bv_val, NULL, NULL );
3471
3472         rc = LDAP_SUCCESS;
3473
3474 done:
3475         if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
3476         if ( serial != serialbuf ) ber_memfree_x( serial, ctx );
3477
3478         return rc;
3479 }
3480
3481
3482 #ifndef SUPPORT_OBSOLETE_UTC_SYNTAX
3483 /* slight optimization - does not need the start parameter */
3484 #define check_time_syntax(v, start, p, f) (check_time_syntax)(v, p, f)
3485 enum { start = 0 };
3486 #endif
3487
3488 static int
3489 check_time_syntax (struct berval *val,
3490         int start,
3491         int *parts,
3492         struct berval *fraction)
3493 {
3494         /*
3495          * start=0 GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM])
3496          * start=1 UTCTime         YYmmddHHMM[SS][Z|(+/-)HHMM]
3497          * GeneralizedTime supports leap seconds, UTCTime does not.
3498          */
3499         static const int ceiling[9] = { 100, 100, 12, 31, 24, 60, 60, 24, 60 };
3500         static const int mdays[2][12] = {
3501                 /* non-leap years */
3502                 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
3503                 /* leap years */
3504                 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
3505         };
3506         char *p, *e;
3507         int part, c, c1, c2, tzoffset, leapyear = 0;
3508
3509         p = val->bv_val;
3510         e = p + val->bv_len;
3511
3512 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
3513         parts[0] = 20; /* century - any multiple of 4 from 04 to 96 */
3514 #endif
3515         for (part = start; part < 7 && p < e; part++) {
3516                 c1 = *p;
3517                 if (!ASCII_DIGIT(c1)) {
3518                         break;
3519                 }
3520                 p++;
3521                 if (p == e) {
3522                         return LDAP_INVALID_SYNTAX;
3523                 }
3524                 c = *p++;
3525                 if (!ASCII_DIGIT(c)) {
3526                         return LDAP_INVALID_SYNTAX;
3527                 }
3528                 c += c1 * 10 - '0' * 11;
3529                 if ((part | 1) == 3) {
3530                         --c;
3531                         if (c < 0) {
3532                                 return LDAP_INVALID_SYNTAX;
3533                         }
3534                 }
3535                 if (c >= ceiling[part]) {
3536                         if (! (c == 60 && part == 6 && start == 0))
3537                                 return LDAP_INVALID_SYNTAX;
3538                 }
3539                 parts[part] = c;
3540         }
3541         if (part < 5 + start) {
3542                 return LDAP_INVALID_SYNTAX;
3543         }
3544         for (; part < 9; part++) {
3545                 parts[part] = 0;
3546         }
3547
3548         /* leapyear check for the Gregorian calendar (year>1581) */
3549         if (parts[parts[1] == 0 ? 0 : 1] % 4 == 0) {
3550                 leapyear = 1;
3551         }
3552
3553         if (parts[3] >= mdays[leapyear][parts[2]]) {
3554                 return LDAP_INVALID_SYNTAX;
3555         }
3556
3557         if (start == 0) {
3558                 fraction->bv_val = p;
3559                 fraction->bv_len = 0;
3560                 if (p < e && (*p == '.' || *p == ',')) {
3561                         char *end_num;
3562                         while (++p < e && ASCII_DIGIT(*p)) {
3563                                 /* EMTPY */;
3564                         }
3565                         if (p - fraction->bv_val == 1) {
3566                                 return LDAP_INVALID_SYNTAX;
3567                         }
3568                         for (end_num = p; end_num[-1] == '0'; --end_num) {
3569                                 /* EMPTY */;
3570                         }
3571                         c = end_num - fraction->bv_val;
3572                         if (c != 1) fraction->bv_len = c;
3573                 }
3574         }
3575
3576         if (p == e) {
3577                 /* no time zone */
3578                 return start == 0 ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
3579         }
3580
3581         tzoffset = *p++;
3582         switch (tzoffset) {
3583         default:
3584                 return LDAP_INVALID_SYNTAX;
3585         case 'Z':
3586                 /* UTC */
3587                 break;
3588         case '+':
3589         case '-':
3590                 for (part = 7; part < 9 && p < e; part++) {
3591                         c1 = *p;
3592                         if (!ASCII_DIGIT(c1)) {
3593                                 break;
3594                         }
3595                         p++;
3596                         if (p == e) {
3597                                 return LDAP_INVALID_SYNTAX;
3598                         }
3599                         c2 = *p++;
3600                         if (!ASCII_DIGIT(c2)) {
3601                                 return LDAP_INVALID_SYNTAX;
3602                         }
3603                         parts[part] = c1 * 10 + c2 - '0' * 11;
3604                         if (parts[part] >= ceiling[part]) {
3605                                 return LDAP_INVALID_SYNTAX;
3606                         }
3607                 }
3608                 if (part < 8 + start) {
3609                         return LDAP_INVALID_SYNTAX;
3610                 }
3611
3612                 if (tzoffset == '-') {
3613                         /* negative offset to UTC, ie west of Greenwich */
3614                         parts[4] += parts[7];
3615                         parts[5] += parts[8];
3616                         /* offset is just hhmm, no seconds */
3617                         for (part = 6; --part >= 0; ) {
3618                                 if (part != 3) {
3619                                         c = ceiling[part];
3620                                 } else {
3621                                         c = mdays[leapyear][parts[2]];
3622                                 }
3623                                 if (parts[part] >= c) {
3624                                         if (part == 0) {
3625                                                 return LDAP_INVALID_SYNTAX;
3626                                         }
3627                                         parts[part] -= c;
3628                                         parts[part - 1]++;
3629                                         continue;
3630                                 } else if (part != 5) {
3631                                         break;
3632                                 }
3633                         }
3634                 } else {
3635                         /* positive offset to UTC, ie east of Greenwich */
3636                         parts[4] -= parts[7];
3637                         parts[5] -= parts[8];
3638                         for (part = 6; --part >= 0; ) {
3639                                 if (parts[part] < 0) {
3640                                         if (part == 0) {
3641                                                 return LDAP_INVALID_SYNTAX;
3642                                         }
3643                                         if (part != 3) {
3644                                                 c = ceiling[part];
3645                                         } else {
3646                                                 /* make first arg to % non-negative */
3647                                                 c = mdays[leapyear][(parts[2] - 1 + 12) % 12];
3648                                         }
3649                                         parts[part] += c;
3650                                         parts[part - 1]--;
3651                                         continue;
3652                                 } else if (part != 5) {
3653                                         break;
3654                                 }
3655                         }
3656                 }
3657         }
3658
3659         return p != e ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
3660 }
3661
3662 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
3663
3664 #if 0
3665 static int
3666 xutcTimeNormalize(
3667         Syntax *syntax,
3668         struct berval *val,
3669         struct berval *normalized )
3670 {
3671         int parts[9], rc;
3672
3673         rc = check_time_syntax(val, 1, parts, NULL);
3674         if (rc != LDAP_SUCCESS) {
3675                 return rc;
3676         }
3677
3678         normalized->bv_val = ch_malloc( 14 );
3679         if ( normalized->bv_val == NULL ) {
3680                 return LBER_ERROR_MEMORY;
3681         }
3682
3683         sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02dZ",
3684                 parts[1], parts[2] + 1, parts[3] + 1,
3685                 parts[4], parts[5], parts[6] );
3686         normalized->bv_len = 13;
3687
3688         return LDAP_SUCCESS;
3689 }
3690 #endif /* 0 */
3691
3692 static int
3693 utcTimeValidate(
3694         Syntax *syntax,
3695         struct berval *in )
3696 {
3697         int parts[9];
3698         return check_time_syntax(in, 1, parts, NULL);
3699 }
3700
3701 #endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */
3702
3703 static int
3704 generalizedTimeValidate(
3705         Syntax *syntax,
3706         struct berval *in )
3707 {
3708         int parts[9];
3709         struct berval fraction;
3710         return check_time_syntax(in, 0, parts, &fraction);
3711 }
3712
3713 static int
3714 generalizedTimeNormalize(
3715         slap_mask_t usage,
3716         Syntax *syntax,
3717         MatchingRule *mr,
3718         struct berval *val,
3719         struct berval *normalized,
3720         void *ctx )
3721 {
3722         int parts[9], rc;
3723         unsigned int len;
3724         struct berval fraction;
3725
3726         rc = check_time_syntax(val, 0, parts, &fraction);
3727         if (rc != LDAP_SUCCESS) {
3728                 return rc;
3729         }
3730
3731         len = STRLENOF("YYYYmmddHHMMSSZ") + fraction.bv_len;
3732         normalized->bv_val = slap_sl_malloc( len + 1, ctx );
3733         if ( BER_BVISNULL( normalized ) ) {
3734                 return LBER_ERROR_MEMORY;
3735         }
3736
3737         sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02d%02d",
3738                 parts[0], parts[1], parts[2] + 1, parts[3] + 1,
3739                 parts[4], parts[5], parts[6] );
3740         if ( !BER_BVISEMPTY( &fraction ) ) {
3741                 memcpy( normalized->bv_val + STRLENOF("YYYYmmddHHMMSSZ")-1,
3742                         fraction.bv_val, fraction.bv_len );
3743                 normalized->bv_val[STRLENOF("YYYYmmddHHMMSSZ")-1] = '.';
3744         }
3745         strcpy( normalized->bv_val + len-1, "Z" );
3746         normalized->bv_len = len;
3747
3748         return LDAP_SUCCESS;
3749 }
3750
3751 static int
3752 generalizedTimeOrderingMatch(
3753         int *matchp,
3754         slap_mask_t flags,
3755         Syntax *syntax,
3756         MatchingRule *mr,
3757         struct berval *value,
3758         void *assertedValue )
3759 {
3760         struct berval *asserted = (struct berval *) assertedValue;
3761         ber_len_t v_len  = value->bv_len;
3762         ber_len_t av_len = asserted->bv_len;
3763
3764         /* ignore trailing 'Z' when comparing */
3765         int match = memcmp( value->bv_val, asserted->bv_val,
3766                 (v_len < av_len ? v_len : av_len) - 1 );
3767         if ( match == 0 ) match = v_len - av_len;
3768
3769         *matchp = match;
3770         return LDAP_SUCCESS;
3771 }
3772
3773 /* Index generation function */
3774 int generalizedTimeIndexer(
3775         slap_mask_t use,
3776         slap_mask_t flags,
3777         Syntax *syntax,
3778         MatchingRule *mr,
3779         struct berval *prefix,
3780         BerVarray values,
3781         BerVarray *keysp,
3782         void *ctx )
3783 {
3784         int i, j;
3785         BerVarray keys;
3786         char tmp[5];
3787         BerValue bvtmp; /* 40 bit index */
3788         struct lutil_tm tm;
3789         struct lutil_timet tt;
3790
3791         bvtmp.bv_len = sizeof(tmp);
3792         bvtmp.bv_val = tmp;
3793         for( i=0; values[i].bv_val != NULL; i++ ) {
3794                 /* just count them */
3795         }
3796
3797         /* we should have at least one value at this point */
3798         assert( i > 0 );
3799
3800         keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
3801
3802         /* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
3803         for( i=0, j=0; values[i].bv_val != NULL; i++ ) {
3804                 assert(values[i].bv_val != NULL && values[i].bv_len >= 10);
3805                 /* Use 40 bits of time for key */
3806                 if ( lutil_parsetime( values[i].bv_val, &tm ) == 0 ) {
3807                         lutil_tm2time( &tm, &tt );
3808                         tmp[0] = tt.tt_gsec & 0xff;
3809                         tmp[4] = tt.tt_sec & 0xff;
3810                         tt.tt_sec >>= 8;
3811                         tmp[3] = tt.tt_sec & 0xff;
3812                         tt.tt_sec >>= 8;
3813                         tmp[2] = tt.tt_sec & 0xff;
3814                         tt.tt_sec >>= 8;
3815                         tmp[1] = tt.tt_sec & 0xff;
3816                         
3817                         ber_dupbv_x(&keys[j++], &bvtmp, ctx );
3818                 }
3819         }
3820
3821         keys[j].bv_val = NULL;
3822         keys[j].bv_len = 0;
3823
3824         *keysp = keys;
3825
3826         return LDAP_SUCCESS;
3827 }
3828
3829 /* Index generation function */
3830 int generalizedTimeFilter(
3831         slap_mask_t use,
3832         slap_mask_t flags,
3833         Syntax *syntax,
3834         MatchingRule *mr,
3835         struct berval *prefix,
3836         void * assertedValue,
3837         BerVarray *keysp,
3838         void *ctx )
3839 {
3840         BerVarray keys;
3841         char tmp[5];
3842         BerValue bvtmp; /* 40 bit index */
3843         BerValue *value = (BerValue *) assertedValue;
3844         struct lutil_tm tm;
3845         struct lutil_timet tt;
3846         
3847         bvtmp.bv_len = sizeof(tmp);
3848         bvtmp.bv_val = tmp;
3849         /* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
3850         /* Use 40 bits of time for key */
3851         if ( value->bv_val && value->bv_len >= 10 &&
3852                 lutil_parsetime( value->bv_val, &tm ) == 0 ) {
3853
3854                 lutil_tm2time( &tm, &tt );
3855                 tmp[0] = tt.tt_gsec & 0xff;
3856                 tmp[4] = tt.tt_sec & 0xff;
3857                 tt.tt_sec >>= 8;
3858                 tmp[3] = tt.tt_sec & 0xff;
3859                 tt.tt_sec >>= 8;
3860                 tmp[2] = tt.tt_sec & 0xff;
3861                 tt.tt_sec >>= 8;
3862                 tmp[1] = tt.tt_sec & 0xff;
3863
3864                 keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
3865                 ber_dupbv_x(keys, &bvtmp, ctx );
3866                 keys[1].bv_val = NULL;
3867                 keys[1].bv_len = 0;
3868         } else {
3869                 keys = NULL;
3870         }
3871
3872         *keysp = keys;
3873
3874         return LDAP_SUCCESS;
3875 }
3876
3877 static int
3878 deliveryMethodValidate(
3879         Syntax *syntax,
3880         struct berval *val )
3881 {
3882 #undef LENOF
3883 #define LENOF(s) (sizeof(s)-1)
3884         struct berval tmp = *val;
3885         /*
3886      *  DeliveryMethod = pdm *( WSP DOLLAR WSP DeliveryMethod )
3887          *      pdm = "any" / "mhs" / "physical" / "telex" / "teletex" /
3888          *              "g3fax" / "g4fax" / "ia5" / "videotex" / "telephone"
3889          */
3890 again:
3891         if( tmp.bv_len < 3 ) return LDAP_INVALID_SYNTAX;
3892
3893         switch( tmp.bv_val[0] ) {
3894         case 'a':
3895         case 'A':
3896                 if(( tmp.bv_len >= LENOF("any") ) &&
3897                         ( strncasecmp(tmp.bv_val, "any", LENOF("any")) == 0 ))
3898                 {
3899                         tmp.bv_len -= LENOF("any");
3900                         tmp.bv_val += LENOF("any");
3901                         break;
3902                 }
3903                 return LDAP_INVALID_SYNTAX;
3904
3905         case 'm':
3906         case 'M':
3907                 if(( tmp.bv_len >= LENOF("mhs") ) &&
3908                         ( strncasecmp(tmp.bv_val, "mhs", LENOF("mhs")) == 0 ))
3909                 {
3910                         tmp.bv_len -= LENOF("mhs");
3911                         tmp.bv_val += LENOF("mhs");
3912                         break;
3913                 }
3914                 return LDAP_INVALID_SYNTAX;
3915
3916         case 'p':
3917         case 'P':
3918                 if(( tmp.bv_len >= LENOF("physical") ) &&
3919                         ( strncasecmp(tmp.bv_val, "physical", LENOF("physical")) == 0 ))
3920                 {
3921                         tmp.bv_len -= LENOF("physical");
3922                         tmp.bv_val += LENOF("physical");
3923                         break;
3924                 }
3925                 return LDAP_INVALID_SYNTAX;
3926
3927         case 't':
3928         case 'T': /* telex or teletex or telephone */
3929                 if(( tmp.bv_len >= LENOF("telex") ) &&
3930                         ( strncasecmp(tmp.bv_val, "telex", LENOF("telex")) == 0 ))
3931                 {
3932                         tmp.bv_len -= LENOF("telex");
3933                         tmp.bv_val += LENOF("telex");
3934                         break;
3935                 }
3936                 if(( tmp.bv_len >= LENOF("teletex") ) &&
3937                         ( strncasecmp(tmp.bv_val, "teletex", LENOF("teletex")) == 0 ))
3938                 {
3939                         tmp.bv_len -= LENOF("teletex");
3940                         tmp.bv_val += LENOF("teletex");
3941                         break;
3942                 }
3943                 if(( tmp.bv_len >= LENOF("telephone") ) &&
3944                         ( strncasecmp(tmp.bv_val, "telephone", LENOF("telephone")) == 0 ))
3945                 {
3946                         tmp.bv_len -= LENOF("telephone");
3947                         tmp.bv_val += LENOF("telephone");
3948                         break;
3949                 }
3950                 return LDAP_INVALID_SYNTAX;
3951
3952         case 'g':
3953         case 'G': /* g3fax or g4fax */
3954                 if(( tmp.bv_len >= LENOF("g3fax") ) && (
3955                         ( strncasecmp(tmp.bv_val, "g3fax", LENOF("g3fax")) == 0 ) ||
3956                         ( strncasecmp(tmp.bv_val, "g4fax", LENOF("g4fax")) == 0 )))
3957                 {
3958                         tmp.bv_len -= LENOF("g3fax");
3959                         tmp.bv_val += LENOF("g3fax");
3960                         break;
3961                 }
3962                 return LDAP_INVALID_SYNTAX;
3963
3964         case 'i':
3965         case 'I':
3966                 if(( tmp.bv_len >= LENOF("ia5") ) &&
3967                         ( strncasecmp(tmp.bv_val, "ia5", LENOF("ia5")) == 0 ))
3968                 {
3969                         tmp.bv_len -= LENOF("ia5");
3970                         tmp.bv_val += LENOF("ia5");
3971                         break;
3972                 }
3973                 return LDAP_INVALID_SYNTAX;
3974
3975         case 'v':
3976         case 'V':
3977                 if(( tmp.bv_len >= LENOF("videotex") ) &&
3978                         ( strncasecmp(tmp.bv_val, "videotex", LENOF("videotex")) == 0 ))
3979                 {
3980                         tmp.bv_len -= LENOF("videotex");
3981                         tmp.bv_val += LENOF("videotex");
3982                         break;
3983                 }
3984                 return LDAP_INVALID_SYNTAX;
3985
3986         default:
3987                 return LDAP_INVALID_SYNTAX;
3988         }
3989
3990         if( BER_BVISEMPTY( &tmp ) ) return LDAP_SUCCESS;
3991
3992         while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
3993                 tmp.bv_len++;
3994                 tmp.bv_val--;
3995         }
3996         if( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == '$' ) ) {
3997                 tmp.bv_len++;
3998                 tmp.bv_val--;
3999         } else {
4000                 return LDAP_INVALID_SYNTAX;
4001         }
4002         while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
4003                 tmp.bv_len++;
4004                 tmp.bv_val--;
4005         }
4006
4007         goto again;
4008 }
4009
4010 static int
4011 nisNetgroupTripleValidate(
4012         Syntax *syntax,
4013         struct berval *val )
4014 {
4015         char *p, *e;
4016         int commas = 0;
4017
4018         if ( BER_BVISEMPTY( val ) ) {
4019                 return LDAP_INVALID_SYNTAX;
4020         }
4021
4022         p = (char *)val->bv_val;
4023         e = p + val->bv_len;
4024
4025         if ( *p != '(' /*')'*/ ) {
4026                 return LDAP_INVALID_SYNTAX;
4027         }
4028
4029         for ( p++; ( p < e ) && ( *p != /*'('*/ ')' ); p++ ) {
4030                 if ( *p == ',' ) {
4031                         commas++;
4032                         if ( commas > 2 ) {
4033                                 return LDAP_INVALID_SYNTAX;
4034                         }
4035
4036                 } else if ( !AD_CHAR( *p ) ) {
4037                         return LDAP_INVALID_SYNTAX;
4038                 }
4039         }
4040
4041         if ( ( commas != 2 ) || ( *p != /*'('*/ ')' ) ) {
4042                 return LDAP_INVALID_SYNTAX;
4043         }
4044
4045         p++;
4046
4047         if (p != e) {
4048                 return LDAP_INVALID_SYNTAX;
4049         }
4050
4051         return LDAP_SUCCESS;
4052 }
4053
4054 static int
4055 bootParameterValidate(
4056         Syntax *syntax,
4057         struct berval *val )
4058 {
4059         char *p, *e;
4060
4061         if ( BER_BVISEMPTY( val ) ) {
4062                 return LDAP_INVALID_SYNTAX;
4063         }
4064
4065         p = (char *)val->bv_val;
4066         e = p + val->bv_len;
4067
4068         /* key */
4069         for (; ( p < e ) && ( *p != '=' ); p++ ) {
4070                 if ( !AD_CHAR( *p ) ) {
4071                         return LDAP_INVALID_SYNTAX;
4072                 }
4073         }
4074
4075         if ( *p != '=' ) {
4076                 return LDAP_INVALID_SYNTAX;
4077         }
4078
4079         /* server */
4080         for ( p++; ( p < e ) && ( *p != ':' ); p++ ) {
4081                 if ( !AD_CHAR( *p ) ) {
4082                         return LDAP_INVALID_SYNTAX;
4083                 }
4084         }
4085
4086         if ( *p != ':' ) {
4087                 return LDAP_INVALID_SYNTAX;
4088         }
4089
4090         /* path */
4091         for ( p++; p < e; p++ ) {
4092                 if ( !SLAP_PRINTABLE( *p ) ) {
4093                         return LDAP_INVALID_SYNTAX;
4094                 }
4095         }
4096
4097         return LDAP_SUCCESS;
4098 }
4099
4100 static int
4101 firstComponentNormalize(
4102         slap_mask_t usage,
4103         Syntax *syntax,
4104         MatchingRule *mr,
4105         struct berval *val,
4106         struct berval *normalized,
4107         void *ctx )
4108 {
4109         int rc;
4110         struct berval comp;
4111         ber_len_t len;
4112
4113         if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( usage )) {
4114                 ber_dupbv_x( normalized, val, ctx );
4115                 return LDAP_SUCCESS;
4116         }
4117
4118         if( val->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
4119
4120         if( val->bv_val[0] != '(' /*')'*/ &&
4121                 val->bv_val[0] != '{' /*'}'*/ )
4122         {
4123                 return LDAP_INVALID_SYNTAX;
4124         }
4125
4126         /* trim leading white space */
4127         for( len=1;
4128                 len < val->bv_len && ASCII_SPACE(val->bv_val[len]);
4129                 len++ )
4130         {
4131                 /* empty */
4132         }
4133
4134         /* grab next word */
4135         comp.bv_val = &val->bv_val[len];
4136         len = val->bv_len - len;
4137         for( comp.bv_len = 0;
4138                 !ASCII_SPACE(comp.bv_val[comp.bv_len]) && comp.bv_len < len;
4139                 comp.bv_len++ )
4140         {
4141                 /* empty */
4142         }
4143
4144         if( mr == slap_schema.si_mr_objectIdentifierFirstComponentMatch ) {
4145                 rc = numericoidValidate( NULL, &comp );
4146         } else if( mr == slap_schema.si_mr_integerFirstComponentMatch ) {
4147                 rc = integerValidate( NULL, &comp );
4148         } else {
4149                 rc = LDAP_INVALID_SYNTAX;
4150         }
4151         
4152
4153         if( rc == LDAP_SUCCESS ) {
4154                 ber_dupbv_x( normalized, &comp, ctx );
4155         }
4156
4157         return rc;
4158 }
4159
4160 static char *country_gen_syn[] = {
4161         "1.3.6.1.4.1.1466.115.121.1.15",
4162         "1.3.6.1.4.1.1466.115.121.1.26",
4163         "1.3.6.1.4.1.1466.115.121.1.44",
4164         NULL
4165 };
4166
4167 #define X_BINARY "X-BINARY-TRANSFER-REQUIRED 'TRUE' "
4168 #define X_NOT_H_R "X-NOT-HUMAN-READABLE 'TRUE' "
4169
4170 static slap_syntax_defs_rec syntax_defs[] = {
4171         {"( 1.3.6.1.4.1.1466.115.121.1.1 DESC 'ACI Item' "
4172                 X_BINARY X_NOT_H_R ")",
4173                 SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL, NULL},
4174         {"( 1.3.6.1.4.1.1466.115.121.1.2 DESC 'Access Point' " X_NOT_H_R ")",
4175                 0, NULL, NULL, NULL},
4176         {"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )",
4177                 0, NULL, NULL, NULL},
4178         {"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' "
4179                 X_NOT_H_R ")",
4180                 SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
4181         {"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' "
4182                 X_NOT_H_R ")",
4183                 SLAP_SYNTAX_BER, NULL, berValidate, NULL},
4184         {"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )",
4185                 0, NULL, bitStringValidate, NULL },
4186         {"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )",
4187                 0, NULL, booleanValidate, NULL},
4188         {"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' "
4189                 X_BINARY X_NOT_H_R ")",
4190                 SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
4191                 NULL, certificateValidate, NULL},
4192         {"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' "
4193                 X_BINARY X_NOT_H_R ")",
4194                 SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
4195                 NULL, certificateListValidate, NULL},
4196         {"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' "
4197                 X_BINARY X_NOT_H_R ")",
4198                 SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
4199                 NULL, sequenceValidate, NULL},
4200 #if 0   /* need to go __after__ printableString */
4201         {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
4202                 0, "1.3.6.1.4.1.1466.115.121.1.44",
4203                 countryStringValidate, NULL},
4204 #endif
4205         {"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )",
4206                 0, NULL, dnValidate, dnPretty},
4207         {"( 1.2.36.79672281.1.5.0 DESC 'RDN' )",
4208                 0, NULL, rdnValidate, rdnPretty},
4209 #ifdef LDAP_COMP_MATCH
4210         {"( 1.2.36.79672281.1.5.3 DESC 'allComponents' )",
4211                 0, NULL, allComponentsValidate, NULL},
4212         {"( 1.2.36.79672281.1.5.2 DESC 'componentFilterMatch assertion') ",
4213                 0, NULL, componentFilterValidate, NULL},
4214 #endif
4215         {"( 1.3.6.1.4.1.1466.115.121.1.13 DESC 'Data Quality' )",
4216                 0, NULL, NULL, NULL},
4217         {"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )",
4218                 0, NULL, deliveryMethodValidate, NULL},
4219         {"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )",
4220                 0, NULL, UTF8StringValidate, NULL},
4221         {"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )",
4222                 0, NULL, NULL, NULL},
4223         {"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )",
4224                 0, NULL, NULL, NULL},
4225         {"( 1.3.6.1.4.1.1466.115.121.1.19 DESC 'DSA Quality' )",
4226                 0, NULL, NULL, NULL},
4227         {"( 1.3.6.1.4.1.1466.115.121.1.20 DESC 'DSE Type' )",
4228                 0, NULL, NULL, NULL},
4229         {"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )",
4230                 0, NULL, NULL, NULL},
4231         {"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )",
4232                 0, NULL, printablesStringValidate, NULL},
4233         {"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' " X_NOT_H_R ")",
4234                 SLAP_SYNTAX_BLOB, NULL, NULL, NULL},
4235         {"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )",
4236                 0, NULL, generalizedTimeValidate, NULL},
4237         {"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )",
4238                 0, NULL, NULL, NULL},
4239         {"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )",
4240                 0, NULL, IA5StringValidate, NULL},
4241         {"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )",
4242                 0, NULL, integerValidate, NULL},
4243         {"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' " X_NOT_H_R ")",
4244                 SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
4245         {"( 1.3.6.1.4.1.1466.115.121.1.29 DESC 'Master And Shadow Access Points' )",
4246                 0, NULL, NULL, NULL},
4247         {"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )",
4248                 0, NULL, NULL, NULL},
4249         {"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )",
4250                 0, NULL, NULL, NULL},
4251         {"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )",
4252                 0, NULL, NULL, NULL},
4253         {"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )",
4254                 0, NULL, NULL, NULL},
4255         {"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )",
4256                 0, NULL, nameUIDValidate, nameUIDPretty },
4257         {"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )",
4258                 0, NULL, NULL, NULL},
4259         {"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )",
4260                 0, NULL, numericStringValidate, NULL},
4261         {"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )",
4262                 0, NULL, NULL, NULL},
4263         {"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )",
4264                 0, NULL, numericoidValidate, NULL},
4265         {"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )",
4266                 0, NULL, IA5StringValidate, NULL},
4267         {"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )",
4268                 0, NULL, blobValidate, NULL},
4269         {"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )",
4270                 0, NULL, UTF8StringValidate, NULL},
4271         {"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )",
4272                 0, NULL, NULL, NULL},
4273         {"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )",
4274                 0, NULL, NULL, NULL},
4275         {"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )",
4276                 0, NULL, printableStringValidate, NULL},
4277         /* moved here because now depends on Directory String, IA5 String 
4278          * and Printable String */
4279         {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
4280                 0, country_gen_syn, countryStringValidate, NULL},
4281         {"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )",
4282 #define subtreeSpecificationValidate UTF8StringValidate /* FIXME */
4283                 0, NULL, subtreeSpecificationValidate, NULL},
4284         {"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' "
4285                 X_BINARY X_NOT_H_R ")",
4286                 SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, berValidate, NULL},
4287         {"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )",
4288                 0, NULL, printableStringValidate, NULL},
4289         {"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )",
4290                 0, NULL, NULL, NULL},
4291         {"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )",
4292                 0, NULL, printablesStringValidate, NULL},
4293 #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
4294         {"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )",
4295                 0, NULL, utcTimeValidate, NULL},
4296 #endif
4297         {"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )",
4298                 0, NULL, NULL, NULL},
4299         {"( 1.3.6.1.4.1.1466.115.121.1.55 DESC 'Modify Rights' )",
4300                 0, NULL, NULL, NULL},
4301         {"( 1.3.6.1.4.1.1466.115.121.1.56 DESC 'LDAP Schema Definition' )",
4302                 0, NULL, NULL, NULL},
4303         {"( 1.3.6.1.4.1.1466.115.121.1.57 DESC 'LDAP Schema Description' )",
4304                 0, NULL, NULL, NULL},
4305         {"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )",
4306                 0, NULL, NULL, NULL},
4307
4308         /* RFC 2307 NIS Syntaxes */
4309         {"( 1.3.6.1.1.1.0.0  DESC 'RFC2307 NIS Netgroup Triple' )",
4310                 0, NULL, nisNetgroupTripleValidate, NULL},
4311         {"( 1.3.6.1.1.1.0.1  DESC 'RFC2307 Boot Parameter' )",
4312                 0, NULL, bootParameterValidate, NULL},
4313
4314         /* draft-zeilenga-ldap-x509 */
4315         {"( 1.3.6.1.1.15.1 DESC 'Certificate Exact Assertion' )",
4316                 SLAP_SYNTAX_HIDE, NULL,
4317                 serialNumberAndIssuerValidate,
4318                 serialNumberAndIssuerPretty},
4319         {"( 1.3.6.1.1.15.2 DESC 'Certificate Assertion' )",
4320                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4321         {"( 1.3.6.1.1.15.3 DESC 'Certificate Pair Exact Assertion' )",
4322                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4323         {"( 1.3.6.1.1.15.4 DESC 'Certificate Pair Assertion' )",
4324                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4325         {"( 1.3.6.1.1.15.5 DESC 'Certificate List Exact Assertion' )",
4326                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4327         {"( 1.3.6.1.1.15.6 DESC 'Certificate List Assertion' )",
4328                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4329         {"( 1.3.6.1.1.15.7 DESC 'Algorithm Identifier' )",
4330                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4331
4332 #ifdef SLAPD_AUTHPASSWD
4333         /* needs updating */
4334         {"( 1.3.6.1.4.1.4203.666.2.2 DESC 'OpenLDAP authPassword' )",
4335                 SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
4336 #endif
4337
4338         {"( 1.3.6.1.1.16.1 DESC 'UUID' )",
4339                 0, NULL, UUIDValidate, UUIDPretty},
4340
4341         {"( 1.3.6.1.4.1.4203.666.11.2.1 DESC 'CSN' )",
4342                 SLAP_SYNTAX_HIDE, NULL, csnValidate, NULL},
4343
4344         /* OpenLDAP Void Syntax */
4345         {"( 1.3.6.1.4.1.4203.1.1.1 DESC 'OpenLDAP void' )" ,
4346                 SLAP_SYNTAX_HIDE, NULL, inValidate, NULL},
4347
4348         /* FIXME: OID is unused, but not registered yet */
4349         {"( 1.3.6.1.4.1.4203.666.2.7 DESC 'OpenLDAP authz' )",
4350                 SLAP_SYNTAX_HIDE, NULL, authzValidate, authzPretty},
4351
4352         {NULL, 0, NULL, NULL, NULL}
4353 };
4354
4355 char *certificateExactMatchSyntaxes[] = {
4356         "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
4357         NULL
4358 };
4359 #ifdef LDAP_COMP_MATCH
4360 char *componentFilterMatchSyntaxes[] = {
4361         "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
4362         NULL
4363 };
4364 #endif
4365 char *directoryStringSyntaxes[] = {
4366         "1.3.6.1.4.1.1466.115.121.1.44" /* printableString */,
4367         NULL
4368 };
4369 char *integerFirstComponentMatchSyntaxes[] = {
4370         "1.3.6.1.4.1.1466.115.121.1.27" /* INTEGER */,
4371         "1.3.6.1.4.1.1466.115.121.1.17" /* dITStructureRuleDescription */,
4372         NULL
4373 };
4374 char *objectIdentifierFirstComponentMatchSyntaxes[] = {
4375         "1.3.6.1.4.1.1466.115.121.1.38" /* OID */,
4376         "1.3.6.1.4.1.1466.115.121.1.3"  /* attributeTypeDescription */,
4377         "1.3.6.1.4.1.1466.115.121.1.16" /* dITContentRuleDescription */,
4378         "1.3.6.1.4.1.1466.115.121.1.54" /* ldapSyntaxDescription */,
4379         "1.3.6.1.4.1.1466.115.121.1.30" /* matchingRuleDescription */,
4380         "1.3.6.1.4.1.1466.115.121.1.31" /* matchingRuleUseDescription */,
4381         "1.3.6.1.4.1.1466.115.121.1.35" /* nameFormDescription */,
4382         "1.3.6.1.4.1.1466.115.121.1.37" /* objectClassDescription */,
4383         NULL
4384 };
4385
4386 /*
4387  * Other matching rules in X.520 that we do not use (yet):
4388  *
4389  * 2.5.13.25    uTCTimeMatch
4390  * 2.5.13.26    uTCTimeOrderingMatch
4391  * 2.5.13.31*   directoryStringFirstComponentMatch
4392  * 2.5.13.32*   wordMatch
4393  * 2.5.13.33*   keywordMatch
4394  * 2.5.13.36+   certificatePairExactMatch
4395  * 2.5.13.37+   certificatePairMatch
4396  * 2.5.13.38+   certificateListExactMatch
4397  * 2.5.13.39+   certificateListMatch
4398  * 2.5.13.40+   algorithmIdentifierMatch
4399  * 2.5.13.41*   storedPrefixMatch
4400  * 2.5.13.42    attributeCertificateMatch
4401  * 2.5.13.43    readerAndKeyIDMatch
4402  * 2.5.13.44    attributeIntegrityMatch
4403  *
4404  * (*) described in RFC 3698 (LDAP: Additional Matching Rules)
4405  * (+) described in draft-zeilenga-ldap-x509
4406  */
4407 static slap_mrule_defs_rec mrule_defs[] = {
4408         /*
4409          * EQUALITY matching rules must be listed after associated APPROX
4410          * matching rules.  So, we list all APPROX matching rules first.
4411          */
4412         {"( " directoryStringApproxMatchOID " NAME 'directoryStringApproxMatch' "
4413                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
4414                 SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
4415                 NULL, NULL, directoryStringApproxMatch,
4416                 directoryStringApproxIndexer, directoryStringApproxFilter,
4417                 NULL},
4418
4419         {"( " IA5StringApproxMatchOID " NAME 'IA5StringApproxMatch' "
4420                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
4421                 SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
4422                 NULL, NULL, IA5StringApproxMatch,
4423                 IA5StringApproxIndexer, IA5StringApproxFilter,
4424                 NULL},
4425
4426         /*
4427          * Other matching rules
4428          */
4429         
4430         {"( 2.5.13.0 NAME 'objectIdentifierMatch' "
4431                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
4432                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4433                 NULL, NULL, octetStringMatch,
4434                 octetStringIndexer, octetStringFilter,
4435                 NULL },
4436
4437         {"( 2.5.13.1 NAME 'distinguishedNameMatch' "
4438                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
4439                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4440                 NULL, dnNormalize, dnMatch,
4441                 octetStringIndexer, octetStringFilter,
4442                 NULL },
4443
4444         {"( 1.3.6.1.4.1.4203.666.4.9 NAME 'dnSubtreeMatch' "
4445                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
4446                 SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
4447                 NULL, dnNormalize, dnRelativeMatch,
4448                 NULL, NULL,
4449                 NULL },
4450
4451         {"( 1.3.6.1.4.1.4203.666.4.8 NAME 'dnOneLevelMatch' "
4452                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
4453                 SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
4454                 NULL, dnNormalize, dnRelativeMatch,
4455                 NULL, NULL,
4456                 NULL },
4457
4458         {"( 1.3.6.1.4.1.4203.666.4.10 NAME 'dnSubordinateMatch' "
4459                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
4460                 SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
4461                 NULL, dnNormalize, dnRelativeMatch,
4462                 NULL, NULL,
4463                 NULL },
4464
4465         {"( 1.3.6.1.4.1.4203.666.4.11 NAME 'dnSuperiorMatch' "
4466                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
4467                 SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
4468                 NULL, dnNormalize, dnRelativeMatch,
4469                 NULL, NULL,
4470                 NULL },
4471
4472         {"( 1.2.36.79672281.1.13.3 NAME 'rdnMatch' "
4473                 "SYNTAX 1.2.36.79672281.1.5.0 )",
4474                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4475                 NULL, rdnNormalize, rdnMatch,
4476                 octetStringIndexer, octetStringFilter,
4477                 NULL },
4478
4479 #ifdef LDAP_COMP_MATCH
4480         {"( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch' "
4481                 "SYNTAX 1.2.36.79672281.1.5.2 )",
4482                 SLAP_MR_EXT|SLAP_MR_COMPONENT, componentFilterMatchSyntaxes,
4483                 NULL, NULL , componentFilterMatch,
4484                 octetStringIndexer, octetStringFilter,
4485                 NULL },
4486
4487         {"( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch' "
4488                 "SYNTAX 1.2.36.79672281.1.5.3 )",
4489                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
4490                 NULL, NULL , allComponentsMatch,
4491                 octetStringIndexer, octetStringFilter,
4492                 NULL },
4493
4494         {"( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch' "
4495                 "SYNTAX 1.2.36.79672281.1.5.3 )",
4496                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
4497                 NULL, NULL , directoryComponentsMatch,
4498                 octetStringIndexer, octetStringFilter,
4499                 NULL },
4500 #endif
4501
4502         {"( 2.5.13.2 NAME 'caseIgnoreMatch' "
4503                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
4504                 SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
4505                 NULL, UTF8StringNormalize, octetStringMatch,
4506                 octetStringIndexer, octetStringFilter,
4507                 directoryStringApproxMatchOID },
4508
4509         {"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' "
4510                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
4511                 SLAP_MR_ORDERING, directoryStringSyntaxes,
4512                 NULL, UTF8StringNormalize, octetStringOrderingMatch,
4513                 NULL, NULL,
4514                 "caseIgnoreMatch" },
4515
4516         {"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' "
4517                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
4518                 SLAP_MR_SUBSTR, directoryStringSyntaxes,
4519                 NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
4520                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4521                 "caseIgnoreMatch" },
4522
4523         {"( 2.5.13.5 NAME 'caseExactMatch' "
4524                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
4525                 SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
4526                 NULL, UTF8StringNormalize, octetStringMatch,
4527                 octetStringIndexer, octetStringFilter,
4528                 directoryStringApproxMatchOID },
4529
4530         {"( 2.5.13.6 NAME 'caseExactOrderingMatch' "
4531                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
4532                 SLAP_MR_ORDERING, directoryStringSyntaxes,
4533                 NULL, UTF8StringNormalize, octetStringOrderingMatch,
4534                 NULL, NULL,
4535                 "caseExactMatch" },
4536
4537         {"( 2.5.13.7 NAME 'caseExactSubstringsMatch' "
4538                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
4539                 SLAP_MR_SUBSTR, directoryStringSyntaxes,
4540                 NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
4541                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4542                 "caseExactMatch" },
4543
4544         {"( 2.5.13.8 NAME 'numericStringMatch' "
4545                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
4546                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4547                 NULL, numericStringNormalize, octetStringMatch,
4548                 octetStringIndexer, octetStringFilter,
4549                 NULL },
4550
4551         {"( 2.5.13.9 NAME 'numericStringOrderingMatch' "
4552                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
4553                 SLAP_MR_ORDERING, NULL,
4554                 NULL, numericStringNormalize, octetStringOrderingMatch,
4555                 NULL, NULL,
4556                 "numericStringMatch" },
4557
4558         {"( 2.5.13.10 NAME 'numericStringSubstringsMatch' "
4559                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
4560                 SLAP_MR_SUBSTR, NULL,
4561                 NULL, numericStringNormalize, octetStringSubstringsMatch,
4562                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4563                 "numericStringMatch" },
4564
4565         {"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
4566                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )",
4567                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4568                 NULL, NULL, NULL, NULL, NULL, NULL },
4569
4570         {"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' "
4571                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
4572                 SLAP_MR_SUBSTR, NULL,
4573                 NULL, NULL, NULL, NULL, NULL,
4574                 "caseIgnoreListMatch" },
4575
4576         {"( 2.5.13.13 NAME 'booleanMatch' "
4577                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )",
4578                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4579                 NULL, NULL, booleanMatch,
4580                 octetStringIndexer, octetStringFilter,
4581                 NULL },
4582
4583         {"( 2.5.13.14 NAME 'integerMatch' "
4584                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
4585                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4586                 NULL, NULL, integerMatch,
4587                 octetStringIndexer, octetStringFilter,
4588                 NULL },
4589
4590         {"( 2.5.13.15 NAME 'integerOrderingMatch' "
4591                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
4592                 SLAP_MR_ORDERING, NULL,
4593                 NULL, NULL, integerMatch,
4594                 NULL, NULL,
4595                 "integerMatch" },
4596
4597         {"( 2.5.13.16 NAME 'bitStringMatch' "
4598                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )",
4599                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4600                 NULL, NULL, octetStringMatch,
4601                 octetStringIndexer, octetStringFilter,
4602                 NULL },
4603
4604         {"( 2.5.13.17 NAME 'octetStringMatch' "
4605                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
4606                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4607                 NULL, NULL, octetStringMatch,
4608                 octetStringIndexer, octetStringFilter,
4609                 NULL },
4610
4611         {"( 2.5.13.18 NAME 'octetStringOrderingMatch' "
4612                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
4613                 SLAP_MR_ORDERING, NULL,
4614                 NULL, NULL, octetStringOrderingMatch,
4615                 NULL, NULL,
4616                 "octetStringMatch" },
4617
4618         {"( 2.5.13.19 NAME 'octetStringSubstringsMatch' "
4619                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
4620                 SLAP_MR_SUBSTR, NULL,
4621                 NULL, NULL, octetStringSubstringsMatch,
4622                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4623                 "octetStringMatch" },
4624
4625         {"( 2.5.13.20 NAME 'telephoneNumberMatch' "
4626                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )",
4627                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4628                 NULL,
4629                 telephoneNumberNormalize, octetStringMatch,
4630                 octetStringIndexer, octetStringFilter,
4631                 NULL },
4632
4633         {"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' "
4634                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
4635                 SLAP_MR_SUBSTR, NULL,
4636                 NULL, telephoneNumberNormalize, octetStringSubstringsMatch,
4637                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4638                 "telephoneNumberMatch" },
4639
4640         {"( 2.5.13.22 NAME 'presentationAddressMatch' "
4641                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )",
4642                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4643                 NULL, NULL, NULL, NULL, NULL, NULL },
4644
4645         {"( 2.5.13.23 NAME 'uniqueMemberMatch' "
4646                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )",
4647                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4648                 NULL, uniqueMemberNormalize, uniqueMemberMatch,
4649                 uniqueMemberIndexer, uniqueMemberFilter,
4650                 NULL },
4651
4652         {"( 2.5.13.24 NAME 'protocolInformationMatch' "
4653                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )",
4654                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4655                 NULL, NULL, NULL, NULL, NULL, NULL },
4656
4657         {"( 2.5.13.27 NAME 'generalizedTimeMatch' "
4658                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
4659                 SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
4660                 NULL, generalizedTimeNormalize, octetStringMatch,
4661                 generalizedTimeIndexer, generalizedTimeFilter,
4662                 NULL },
4663
4664         {"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' "
4665                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
4666                 SLAP_MR_ORDERING | SLAP_MR_ORDERED_INDEX, NULL,
4667                 NULL, generalizedTimeNormalize, generalizedTimeOrderingMatch,
4668                 NULL, NULL,
4669                 "generalizedTimeMatch" },
4670
4671         {"( 2.5.13.29 NAME 'integerFirstComponentMatch' "
4672                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
4673                 SLAP_MR_EQUALITY | SLAP_MR_EXT,
4674                         integerFirstComponentMatchSyntaxes,
4675                 NULL, firstComponentNormalize, integerMatch,
4676                 octetStringIndexer, octetStringFilter,
4677                 NULL },
4678
4679         {"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' "
4680                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
4681                 SLAP_MR_EQUALITY | SLAP_MR_EXT,
4682                         objectIdentifierFirstComponentMatchSyntaxes,
4683                 NULL, firstComponentNormalize, octetStringMatch,
4684                 octetStringIndexer, octetStringFilter,
4685                 NULL },
4686
4687         {"( 2.5.13.34 NAME 'certificateExactMatch' "
4688                 "SYNTAX 1.3.6.1.1.15.1 )",
4689                 SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes,
4690                 NULL, certificateExactNormalize, octetStringMatch,
4691                 octetStringIndexer, octetStringFilter,
4692                 NULL },
4693
4694         {"( 2.5.13.35 NAME 'certificateMatch' "
4695                 "SYNTAX 1.3.6.1.1.15.2 )",
4696                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4697                 NULL, NULL, NULL, NULL, NULL,
4698                 NULL },
4699
4700         {"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' "
4701                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
4702                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4703                 NULL, IA5StringNormalize, octetStringMatch,
4704                 octetStringIndexer, octetStringFilter,
4705                 IA5StringApproxMatchOID },
4706
4707         {"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' "
4708                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
4709                 SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
4710                 NULL, IA5StringNormalize, octetStringMatch,
4711                 octetStringIndexer, octetStringFilter,
4712                 IA5StringApproxMatchOID },
4713
4714         {"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' "
4715                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
4716                 SLAP_MR_SUBSTR, NULL,
4717                 NULL, IA5StringNormalize, directoryStringSubstringsMatch,
4718                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4719                 "caseIgnoreIA5Match" },
4720
4721         {"( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch' "
4722                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
4723                 SLAP_MR_SUBSTR, NULL,
4724                 NULL, IA5StringNormalize, directoryStringSubstringsMatch,
4725                 octetStringSubstringsIndexer, octetStringSubstringsFilter,
4726                 "caseExactIA5Match" },
4727
4728 #ifdef SLAPD_AUTHPASSWD
4729         /* needs updating */
4730         {"( 1.3.6.1.4.1.4203.666.4.1 NAME 'authPasswordMatch' "
4731                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
4732                 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
4733                 NULL, NULL, authPasswordMatch,
4734                 NULL, NULL,
4735                 NULL},
4736 #endif
4737
4738         {"( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch' "
4739                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
4740                 SLAP_MR_EXT, NULL,
4741                 NULL, NULL, integerBitAndMatch,
4742                 NULL, NULL,
4743                 "integerMatch" },
4744
4745         {"( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch' "
4746                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
4747                 SLAP_MR_EXT, NULL,
4748                 NULL, NULL, integerBitOrMatch,
4749                 NULL, NULL,
4750                 "integerMatch" },
4751
4752         {"( 1.3.6.1.1.16.2 NAME 'UUIDMatch' "
4753                 "SYNTAX 1.3.6.1.1.16.1 )",
4754                 SLAP_MR_EQUALITY | SLAP_MR_MUTATION_NORMALIZER, NULL,
4755                 NULL, UUIDNormalize, octetStringMatch,
4756                 octetStringIndexer, octetStringFilter,
4757                 NULL},
4758
4759         {"( 1.3.6.1.1.16.3 NAME 'UUIDOrderingMatch' "
4760                 "SYNTAX 1.3.6.1.1.16.1 )",
4761                 SLAP_MR_ORDERING | SLAP_MR_MUTATION_NORMALIZER, NULL,
4762                 NULL, UUIDNormalize, octetStringOrderingMatch,
4763                 octetStringIndexer, octetStringFilter,
4764                 "UUIDMatch"},
4765
4766         {"( 1.3.6.1.4.1.4203.666.11.2.2 NAME 'CSNMatch' "
4767                 "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
4768                 SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_ORDERED_INDEX, NULL,
4769                 NULL, NULL, csnMatch,
4770                 csnIndexer, csnFilter,
4771                 NULL},
4772
4773         {"( 1.3.6.1.4.1.4203.666.11.2.3 NAME 'CSNOrderingMatch' "
4774                 "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
4775                 SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_ORDERED_INDEX, NULL,
4776                 NULL, NULL, csnOrderingMatch,
4777                 NULL, NULL,
4778                 "CSNMatch" },
4779
4780         /* FIXME: OID is unused, but not registered yet */
4781         {"( 1.3.6.1.4.1.4203.666.4.12 NAME 'authzMatch' "
4782                 "SYNTAX 1.3.6.1.4.1.4203.666.2.7 )",
4783                 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
4784                 NULL, authzNormalize, authzMatch,
4785                 NULL, NULL,
4786                 NULL},
4787
4788         {NULL, SLAP_MR_NONE, NULL,
4789                 NULL, NULL, NULL, NULL, NULL,
4790                 NULL }
4791 };
4792
4793 int
4794 slap_schema_init( void )
4795 {
4796         int             res;
4797         int             i;
4798
4799         /* we should only be called once (from main) */
4800         assert( schema_init_done == 0 );
4801
4802         for ( i=0; syntax_defs[i].sd_desc != NULL; i++ ) {
4803                 res = register_syntax( &syntax_defs[i] );
4804
4805                 if ( res ) {
4806                         fprintf( stderr, "slap_schema_init: Error registering syntax %s\n",
4807                                  syntax_defs[i].sd_desc );
4808                         return LDAP_OTHER;
4809                 }
4810         }
4811
4812         for ( i=0; mrule_defs[i].mrd_desc != NULL; i++ ) {
4813                 if( mrule_defs[i].mrd_usage == SLAP_MR_NONE &&
4814                         mrule_defs[i].mrd_compat_syntaxes == NULL )
4815                 {
4816                         fprintf( stderr,
4817                                 "slap_schema_init: Ignoring unusable matching rule %s\n",
4818                                  mrule_defs[i].mrd_desc );
4819                         continue;
4820                 }
4821
4822                 res = register_matching_rule( &mrule_defs[i] );
4823
4824                 if ( res ) {
4825                         fprintf( stderr,
4826                                 "slap_schema_init: Error registering matching rule %s\n",
4827                                  mrule_defs[i].mrd_desc );
4828                         return LDAP_OTHER;
4829                 }
4830         }
4831
4832         res = slap_schema_load();
4833         schema_init_done = 1;
4834         return res;
4835 }
4836
4837 void
4838 schema_destroy( void )
4839 {
4840         oidm_destroy();
4841         oc_destroy();
4842         at_destroy();
4843         mr_destroy();
4844         mru_destroy();
4845         syn_destroy();
4846
4847         if( schema_init_done ) {
4848                 ldap_pvt_thread_mutex_destroy( &ad_undef_mutex );
4849                 ldap_pvt_thread_mutex_destroy( &oc_undef_mutex );
4850         }
4851 }