]> git.sur5r.net Git - openldap/blob - servers/slapd/dn.c
first round at replacing UTF8normalize with UTF8bvnormalize
[openldap] / servers / slapd / dn.c
1 /* dn.c - routines for dealing with distinguished names */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/socket.h>
14 #include <ac/string.h>
15 #include <ac/time.h>
16
17 #include "ldap_pvt.h"
18
19 #include "slap.h"
20
21 const struct berval slap_empty_bv = { 0, "" };
22
23 #define SLAP_LDAPDN_PRETTY 0x1
24
25 #define SLAP_LDAPDN_MAXLEN 8192
26
27 /*
28  * The DN syntax-related functions take advantage of the dn representation
29  * handling functions ldap_str2dn/ldap_dn2str.  The latter are not schema-
30  * aware, so the attributes and their values need be validated (and possibly
31  * normalized).  In the current implementation the required validation/nor-
32  * malization/"pretty"ing are done on newly created DN structural represen-
33  * tations; however the idea is to move towards DN handling in structural
34  * representation instead of the current string representation.  To this
35  * purpose, we need to do only the required operations and keep track of
36  * what has been done to minimize their impact on performances.
37  *
38  * Developers are strongly encouraged to use this feature, to speed-up
39  * its stabilization.
40  */
41
42 #define AVA_PRIVATE( ava ) ( ( AttributeDescription * )(ava)->la_private )
43
44 /*
45  * In-place, schema-aware validation of the
46  * structural representation of a distinguished name.
47  */
48 static int
49 LDAPDN_validate( LDAPDN *dn )
50 {
51         int             iRDN;
52         int             rc;
53
54         assert( dn );
55
56         for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
57                 LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
58                 int             iAVA;
59
60                 assert( rdn );
61
62                 for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
63                         LDAPAVA                 *ava = rdn[ 0 ][ iAVA ];
64                         AttributeDescription    *ad;
65                         slap_syntax_validate_func *validate = NULL;
66
67                         assert( ava );
68                         
69                         if ( ( ad = AVA_PRIVATE( ava ) ) == NULL ) {
70                                 const char      *text = NULL;
71
72                                 rc = slap_bv2ad( &ava->la_attr, &ad, &text );
73                                 if ( rc != LDAP_SUCCESS ) {
74                                         return LDAP_INVALID_SYNTAX;
75                                 }
76
77                                 ava->la_private = ( void * )ad;
78                         }
79
80                         /* 
81                          * Replace attr oid/name with the canonical name
82                          */
83                         ava->la_attr = ad->ad_cname;
84
85                         validate = ad->ad_type->sat_syntax->ssyn_validate;
86
87                         if ( validate ) {
88                                 /*
89                                  * validate value by validate function
90                                  */
91                                 rc = ( *validate )( ad->ad_type->sat_syntax,
92                                         &ava->la_value );
93                         
94                                 if ( rc != LDAP_SUCCESS ) {
95                                         return LDAP_INVALID_SYNTAX;
96                                 }
97                         }
98                 }
99         }
100
101         return LDAP_SUCCESS;
102 }
103
104 /*
105  * dn validate routine
106  */
107 int
108 dnValidate(
109         Syntax *syntax,
110         struct berval *in )
111 {
112         int             rc;
113         LDAPDN          *dn = NULL;
114
115         assert( in );
116
117         if ( in->bv_len == 0 ) {
118                 return LDAP_SUCCESS;
119
120         } else if ( in->bv_len > SLAP_LDAPDN_MAXLEN ) {
121                 return LDAP_INVALID_SYNTAX;
122         }
123
124         rc = ldap_bv2dn( in, &dn, LDAP_DN_FORMAT_LDAP );
125         if ( rc != LDAP_SUCCESS ) {
126                 return LDAP_INVALID_SYNTAX;
127         }
128
129         assert( strlen( in->bv_val ) == in->bv_len );
130
131         /*
132          * Schema-aware validate
133          */
134         rc = LDAPDN_validate( dn );
135         ldap_dnfree( dn );
136
137         if ( rc != LDAP_SUCCESS ) {
138                 return LDAP_INVALID_SYNTAX;
139         }
140
141         return LDAP_SUCCESS;
142 }
143
144 /*
145  * AVA sorting inside a RDN
146  *
147  * rule: sort attributeTypes in alphabetical order; in case of multiple
148  * occurrences of the same attributeType, sort values in byte order
149  * (use memcmp, which implies alphabetical order in case of IA5 value;
150  * this should guarantee the repeatability of the operation).
151  *
152  * Note: the sorting can be slightly improved by sorting first
153  * by attribute type length, then by alphabetical order.
154  *
155  * uses a linear search; should be fine since the number of AVAs in
156  * a RDN should be limited.
157  */
158 static void
159 AVA_Sort( LDAPRDN *rdn, int iAVA )
160 {
161         int             i;
162         LDAPAVA         *ava_in = rdn[ 0 ][ iAVA ];
163
164         assert( rdn );
165         assert( ava_in );
166         
167         for ( i = 0; i < iAVA; i++ ) {
168                 LDAPAVA         *ava = rdn[ 0 ][ i ];
169                 int             a, j;
170
171                 assert( ava );
172
173                 a = strcmp( ava_in->la_attr.bv_val, ava->la_attr.bv_val );
174
175                 if ( a > 0 ) {
176                         break;
177                 }
178
179                 while ( a == 0 ) {
180                         int             v, d;
181
182                         d = ava_in->la_value.bv_len - ava->la_value.bv_len;
183
184                         v = memcmp( ava_in->la_value.bv_val, 
185                                         ava->la_value.bv_val,
186                                         d <= 0 ? ava_in->la_value.bv_len 
187                                                 : ava->la_value.bv_len );
188
189                         if ( v == 0 && d != 0 ) {
190                                 v = d;
191                         }
192
193                         if ( v <= 0 ) {
194                                 /* 
195                                  * got it!
196                                  */
197                                 break;
198                         }
199
200                         if ( ++i == iAVA ) {
201                                 /*
202                                  * already sorted
203                                  */
204                                 return;
205                         }
206
207                         ava = rdn[ 0 ][ i ];
208                         a = strcmp( ava_in->la_attr.bv_val, 
209                                         ava->la_attr.bv_val );
210                 }
211
212                 /*
213                  * move ahead
214                  */
215                 for ( j = iAVA; j > i; j-- ) {
216                         rdn[ 0 ][ j ] = rdn[ 0 ][ j - 1 ];
217                 }
218                 rdn[ 0 ][ i ] = ava_in;
219
220                 return;
221         }
222 }
223
224 /*
225  * In-place, schema-aware normalization / "pretty"ing of the
226  * structural representation of a distinguished name.
227  */
228 static int
229 LDAPDN_rewrite( LDAPDN *dn, unsigned flags )
230 {
231         int             iRDN;
232         int             rc;
233
234         assert( dn );
235
236         for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
237                 LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
238                 int             iAVA;
239
240                 assert( rdn );
241
242                 for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
243                         LDAPAVA                 *ava = rdn[ 0 ][ iAVA ];
244                         AttributeDescription    *ad;
245                         slap_syntax_transform_func *transf = NULL;
246                         MatchingRule *mr;
247                         struct berval           bv = { 0, NULL };
248                         int                     do_sort = 0;
249
250                         assert( ava );
251
252                         if ( ( ad = AVA_PRIVATE( ava ) ) == NULL ) {
253                                 const char      *text = NULL;
254
255                                 rc = slap_bv2ad( &ava->la_attr, &ad, &text );
256                                 if ( rc != LDAP_SUCCESS ) {
257                                         return LDAP_INVALID_SYNTAX;
258                                 }
259                                 
260                                 ava->la_private = ( void * )ad;
261                                 do_sort = 1;
262                         }
263
264                         /* 
265                          * Replace attr oid/name with the canonical name
266                          */
267                         ava->la_attr = ad->ad_cname;
268
269                         if( ava->la_flags & LDAP_AVA_BINARY ) {
270                                 /* AVA is binary encoded, don't muck with it */
271                                 transf = NULL;
272                                 mr = NULL;
273
274                         } else if( flags & SLAP_LDAPDN_PRETTY ) {
275                                 transf = ad->ad_type->sat_syntax->ssyn_pretty;
276                                 mr = NULL;
277                         } else {
278                                 transf = ad->ad_type->sat_syntax->ssyn_normalize;
279                                 mr = ad->ad_type->sat_equality;
280                         }
281
282                         if ( transf ) {
283                                 /*
284                                  * transform value by normalize/pretty function
285                                  *      if value is empty, use empty_bv
286                                  */
287                                 rc = ( *transf )( ad->ad_type->sat_syntax,
288                                         ava->la_value.bv_len
289                                                 ? &ava->la_value
290                                                 : (struct berval *) &slap_empty_bv,
291                                         &bv );
292                         
293                                 if ( rc != LDAP_SUCCESS ) {
294                                         return LDAP_INVALID_SYNTAX;
295                                 }
296                         }
297
298                         if( mr && ( mr->smr_usage & SLAP_MR_DN_FOLD ) ) {
299                                 char *s = bv.bv_val;
300
301                                 if ( UTF8bvnormalize( &bv, &bv, 
302                                                 LDAP_UTF8_CASEFOLD ) == NULL ) {
303                                         return LDAP_INVALID_SYNTAX;
304                                 }
305                                 free( s );
306                         }
307
308                         if( bv.bv_val ) {
309                                 free( ava->la_value.bv_val );
310                                 ava->la_value = bv;
311                         }
312
313                         if( do_sort ) AVA_Sort( rdn, iAVA );
314                 }
315         }
316
317         return LDAP_SUCCESS;
318 }
319
320 /*
321  * dn normalize routine
322  */
323 int
324 dnNormalize(
325         Syntax *syntax,
326         struct berval *val,
327         struct berval **normalized )
328 {
329         struct berval *out;
330         int rc;
331
332         assert( normalized && *normalized == NULL );
333
334         out = ch_malloc( sizeof( struct berval ) );
335         rc = dnNormalize2( syntax, val, out );
336         if ( rc != LDAP_SUCCESS )
337                 free( out );
338         else
339                 *normalized = out;
340         return rc;
341 }
342
343 int
344 dnNormalize2(
345         Syntax *syntax,
346         struct berval *val,
347         struct berval *out )
348 {
349         assert( val );
350         assert( out );
351
352         Debug( LDAP_DEBUG_TRACE, ">>> dnNormalize: <%s>\n", val->bv_val, 0, 0 );
353
354         if ( val->bv_len != 0 ) {
355                 LDAPDN          *dn = NULL;
356                 int             rc;
357
358                 /*
359                  * Go to structural representation
360                  */
361                 rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP );
362                 if ( rc != LDAP_SUCCESS ) {
363                         return LDAP_INVALID_SYNTAX;
364                 }
365
366                 assert( strlen( val->bv_val ) == val->bv_len );
367
368                 /*
369                  * Schema-aware rewrite
370                  */
371                 if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) {
372                         ldap_dnfree( dn );
373                         return LDAP_INVALID_SYNTAX;
374                 }
375
376                 /*
377                  * Back to string representation
378                  */
379                 rc = ldap_dn2bv( dn, out, LDAP_DN_FORMAT_LDAPV3 );
380
381                 ldap_dnfree( dn );
382
383                 if ( rc != LDAP_SUCCESS ) {
384                         return LDAP_INVALID_SYNTAX;
385                 }
386         } else {
387                 ber_dupbv( out, val );
388         }
389
390         Debug( LDAP_DEBUG_TRACE, "<<< dnNormalize: <%s>\n", out->bv_val, 0, 0 );
391
392         return LDAP_SUCCESS;
393 }
394
395 /*
396  * dn "pretty"ing routine
397  */
398 int
399 dnPretty(
400         Syntax *syntax,
401         struct berval *val,
402         struct berval **pretty)
403 {
404         struct berval *out;
405         int rc;
406
407         assert( pretty && *pretty == NULL );
408
409         out = ch_malloc( sizeof( struct berval ) );
410         rc = dnPretty2( syntax, val, out );
411         if ( rc != LDAP_SUCCESS )
412                 free( out );
413         else
414                 *pretty = out;
415         return rc;
416 }
417
418 int
419 dnPretty2(
420         Syntax *syntax,
421         struct berval *val,
422         struct berval *out)
423 {
424         assert( val );
425         assert( out );
426
427         Debug( LDAP_DEBUG_TRACE, ">>> dnPretty: <%s>\n", val->bv_val, 0, 0 );
428
429         if ( val->bv_len == 0 ) {
430                 ber_dupbv( out, val );
431
432         } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
433                 return LDAP_INVALID_SYNTAX;
434
435         } else {
436                 LDAPDN          *dn = NULL;
437                 int             rc;
438
439                 /* FIXME: should be liberal in what we accept */
440                 rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP );
441                 if ( rc != LDAP_SUCCESS ) {
442                         return LDAP_INVALID_SYNTAX;
443                 }
444
445                 assert( strlen( val->bv_val ) == val->bv_len );
446
447                 /*
448                  * Schema-aware rewrite
449                  */
450                 if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY ) != LDAP_SUCCESS ) {
451                         ldap_dnfree( dn );
452                         return LDAP_INVALID_SYNTAX;
453                 }
454
455                 /* FIXME: not sure why the default isn't pretty */
456                 /* RE: the default is the form that is used as
457                  * an internal representation; the pretty form
458                  * is a variant */
459                 rc = ldap_dn2bv( dn, out,
460                         LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
461
462                 ldap_dnfree( dn );
463
464                 if ( rc != LDAP_SUCCESS ) {
465                         return LDAP_INVALID_SYNTAX;
466                 }
467         }
468
469         Debug( LDAP_DEBUG_TRACE, "<<< dnPretty: <%s>\n", out->bv_val, 0, 0 );
470
471         return LDAP_SUCCESS;
472 }
473
474 /*
475  * Combination of both dnPretty and dnNormalize
476  */
477 int
478 dnPrettyNormal(
479         Syntax *syntax,
480         struct berval *val,
481         struct berval *pretty,
482         struct berval *normal)
483 {
484         Debug( LDAP_DEBUG_TRACE, ">>> dnPrettyNormal: <%s>\n", val->bv_val, 0, 0 );
485
486         assert( val );
487         assert( pretty );
488         assert( normal );
489
490         if ( val->bv_len == 0 ) {
491                 ber_dupbv( pretty, val );
492                 ber_dupbv( normal, val );
493
494         } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
495                 /* too big */
496                 return LDAP_INVALID_SYNTAX;
497
498         } else {
499                 LDAPDN          *dn = NULL;
500                 int             rc;
501
502                 pretty->bv_val = NULL;
503                 normal->bv_val = NULL;
504                 pretty->bv_len = 0;
505                 normal->bv_len = 0;
506
507                 /* FIXME: should be liberal in what we accept */
508                 rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP );
509                 if ( rc != LDAP_SUCCESS ) {
510                         return LDAP_INVALID_SYNTAX;
511                 }
512
513                 assert( strlen( val->bv_val ) == val->bv_len );
514
515                 /*
516                  * Schema-aware rewrite
517                  */
518                 if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY ) != LDAP_SUCCESS ) {
519                         ldap_dnfree( dn );
520                         return LDAP_INVALID_SYNTAX;
521                 }
522
523                 rc = ldap_dn2bv( dn, pretty,
524                         LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
525
526                 if ( rc != LDAP_SUCCESS ) {
527                         ldap_dnfree( dn );
528                         return LDAP_INVALID_SYNTAX;
529                 }
530
531                 if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) {
532                         ldap_dnfree( dn );
533                         free( pretty->bv_val );
534                         pretty->bv_val = NULL;
535                         pretty->bv_len = 0;
536                         return LDAP_INVALID_SYNTAX;
537                 }
538
539                 rc = ldap_dn2bv( dn, normal, LDAP_DN_FORMAT_LDAPV3 );
540
541                 ldap_dnfree( dn );
542                 if ( rc != LDAP_SUCCESS ) {
543                         free( pretty->bv_val );
544                         pretty->bv_val = NULL;
545                         pretty->bv_len = 0;
546                         return LDAP_INVALID_SYNTAX;
547                 }
548         }
549
550         Debug( LDAP_DEBUG_TRACE, "<<< dnPrettyNormal: <%s>, <%s>\n",
551                 pretty->bv_val, normal->bv_val, 0 );
552
553         return LDAP_SUCCESS;
554 }
555
556 /*
557  * dnMatch routine
558  */
559 int
560 dnMatch(
561         int *matchp,
562         slap_mask_t flags,
563         Syntax *syntax,
564         MatchingRule *mr,
565         struct berval *value,
566         void *assertedValue )
567 {
568         int match;
569         struct berval *asserted = (struct berval *) assertedValue;
570
571         assert( matchp );
572         assert( value );
573         assert( assertedValue );
574         
575         match = value->bv_len - asserted->bv_len;
576
577         if ( match == 0 ) {
578                 match = memcmp( value->bv_val, asserted->bv_val, 
579                                 value->bv_len );
580         }
581
582 #ifdef NEW_LOGGING
583         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
584                 "dnMatch: %d\n    %s\n    %s\n", match,
585                 value->bv_val, asserted->bv_val ));
586 #else
587         Debug( LDAP_DEBUG_ARGS, "dnMatch %d\n\t\"%s\"\n\t\"%s\"\n",
588                 match, value->bv_val, asserted->bv_val );
589 #endif
590
591         *matchp = match;
592         return( LDAP_SUCCESS );
593 }
594
595 /*
596  * dnParent - dn's parent, in-place
597  *
598  * note: the incoming dn is assumed to be normalized/prettyfied,
599  * so that escaped rdn/ava separators are in '\'+hexpair form
600  */
601 void
602 dnParent( 
603         struct berval   *dn, 
604         struct berval   *pdn )
605 {
606         char    *p;
607
608         p = strchr( dn->bv_val, ',' );
609
610         /* one-level dn */
611         if ( p == NULL ) {
612                 *pdn = slap_empty_bv;
613                 return;
614         }
615
616         assert( DN_SEPARATOR( p[ 0 ] ) );
617         p++;
618
619         assert( ATTR_LEADCHAR( p[ 0 ] ) );
620         pdn->bv_val = p;
621         pdn->bv_len = dn->bv_len - (p - dn->bv_val);
622
623         return;
624 }
625
626 int
627 dnExtractRdn( 
628         struct berval   *dn, 
629         struct berval   *rdn )
630 {
631         LDAPRDN         *tmpRDN;
632         const char      *p;
633         int             rc;
634
635         assert( dn );
636         assert( rdn );
637
638         if( dn->bv_len == 0 ) {
639                 return LDAP_OTHER;
640         }
641
642         rc = ldap_bv2rdn( dn, &tmpRDN, (char **)&p, LDAP_DN_FORMAT_LDAP );
643         if ( rc != LDAP_SUCCESS ) {
644                 return rc;
645         }
646
647         rc = ldap_rdn2bv( tmpRDN, rdn, LDAP_DN_FORMAT_LDAPV3 );
648         ldap_rdnfree( tmpRDN );
649         if ( rc != LDAP_SUCCESS ) {
650                 return rc;
651         }
652
653         return LDAP_SUCCESS;
654 }
655
656 /*
657  * We can assume the input is a prettied or normalized DN
658  */
659 int 
660 dn_rdnlen(
661         Backend         *be,
662         struct berval   *dn_in )
663 {
664         const char      *p;
665
666         assert( dn_in );
667
668         if ( dn_in == NULL ) {
669                 return 0;
670         }
671
672         if ( !dn_in->bv_len ) {
673                 return 0;
674         }
675
676         if ( be != NULL && be_issuffix( be, dn_in ) ) {
677                 return 0;
678         }
679
680         p = strchr( dn_in->bv_val, ',' );
681
682         return p ? p - dn_in->bv_val : dn_in->bv_len;
683 }
684
685
686 /* rdnValidate:
687  *
688  * LDAP_SUCCESS if rdn is a legal rdn;
689  * LDAP_INVALID_SYNTAX otherwise (including a sequence of rdns)
690  */
691 int
692 rdnValidate( struct berval *rdn )
693 {
694 #if 1
695         /* Major cheat!
696          * input is a pretty or normalized DN
697          * hence, we can just search for ','
698          */
699         if( rdn == NULL || rdn->bv_len == 0 ||
700                 rdn->bv_len > SLAP_LDAPDN_MAXLEN )
701         {
702                 return LDAP_INVALID_SYNTAX;
703         }
704
705         return strchr( rdn->bv_val, ',' ) == NULL
706                 ? LDAP_SUCCESS : LDAP_INVALID_SYNTAX;
707
708 #else
709         LDAPRDN         *RDN, **DN[ 2 ] = { &RDN, NULL };
710         const char      *p;
711         int             rc;
712
713         /*
714          * must be non-empty
715          */
716         if ( rdn == NULL || rdn == '\0' ) {
717                 return 0;
718         }
719
720         /*
721          * must be parsable
722          */
723         rc = ldap_bv2rdn( rdn, &RDN, (char **)&p, LDAP_DN_FORMAT_LDAP );
724         if ( rc != LDAP_SUCCESS ) {
725                 return 0;
726         }
727
728         /*
729          * Must be one-level
730          */
731         if ( p[ 0 ] != '\0' ) {
732                 return 0;
733         }
734
735         /*
736          * Schema-aware validate
737          */
738         if ( rc == LDAP_SUCCESS ) {
739                 rc = LDAPDN_validate( DN );
740         }
741         ldap_rdnfree( RDN );
742
743         /*
744          * Must validate (there's a repeated parsing ...)
745          */
746         return ( rc == LDAP_SUCCESS );
747 #endif
748 }
749
750
751 /* build_new_dn:
752  *
753  * Used by ldbm/bdb2 back_modrdn to create the new dn of entries being
754  * renamed.
755  *
756  * new_dn = parent (p_dn) + separator + rdn (newrdn) + null.
757  */
758
759 void
760 build_new_dn( struct berval * new_dn,
761         struct berval * parent_dn,
762         struct berval * newrdn )
763 {
764         char *ptr;
765
766         if ( parent_dn == NULL ) {
767                 ber_dupbv( new_dn, newrdn );
768                 return;
769         }
770
771         new_dn->bv_len = parent_dn->bv_len + newrdn->bv_len + 1;
772         new_dn->bv_val = (char *) ch_malloc( new_dn->bv_len + 1 );
773
774         ptr = slap_strcopy( new_dn->bv_val, newrdn->bv_val );
775         *ptr++ = ',';
776         strcpy( ptr, parent_dn->bv_val );
777 }
778
779
780 /*
781  * dnIsSuffix - tells whether suffix is a suffix of dn.
782  * Both dn and suffix must be normalized.
783  */
784 int
785 dnIsSuffix(
786         const struct berval *dn,
787         const struct berval *suffix )
788 {
789         int     d = dn->bv_len - suffix->bv_len;
790
791         assert( dn );
792         assert( suffix );
793
794         /* empty suffix matches any dn */
795         if ( suffix->bv_len == 0 ) {
796                 return 1;
797         }
798
799         /* suffix longer than dn */
800         if ( d < 0 ) {
801                 return 0;
802         }
803
804         /* no rdn separator or escaped rdn separator */
805         if ( d > 1 && !DN_SEPARATOR( dn->bv_val[ d - 1 ] ) ) {
806                 return 0;
807         }
808
809         /* no possible match or malformed dn */
810         if ( d == 1 ) {
811                 return 0;
812         }
813
814         /* compare */
815         return( strcmp( dn->bv_val + d, suffix->bv_val ) == 0 );
816 }