]> git.sur5r.net Git - openldap/blob - servers/slapd/dn.c
24b7c73ff7abb72d41d87f2eaa3c97dfa17b171a
[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                                 ber_str2bv( UTF8normalize( bv.bv_val ? &bv
302                                         : &ava->la_value, LDAP_UTF8_CASEFOLD ),
303                                         0, 0, &bv );
304                                 free( s );
305                         }
306
307                         if( bv.bv_val ) {
308                                 free( ava->la_value.bv_val );
309                                 ava->la_value = bv;
310                         }
311
312                         if( do_sort ) AVA_Sort( rdn, iAVA );
313                 }
314         }
315
316         return LDAP_SUCCESS;
317 }
318
319 /*
320  * dn normalize routine
321  */
322 int
323 dnNormalize(
324         Syntax *syntax,
325         struct berval *val,
326         struct berval **normalized )
327 {
328         struct berval *out;
329         int rc;
330
331         assert( normalized && *normalized == NULL );
332
333         out = ch_malloc( sizeof( struct berval ) );
334         rc = dnNormalize2( syntax, val, out );
335         if ( rc != LDAP_SUCCESS )
336                 free( out );
337         else
338                 *normalized = out;
339         return rc;
340 }
341
342 int
343 dnNormalize2(
344         Syntax *syntax,
345         struct berval *val,
346         struct berval *out )
347 {
348         assert( val );
349         assert( out );
350
351         Debug( LDAP_DEBUG_TRACE, ">>> dnNormalize: <%s>\n", val->bv_val, 0, 0 );
352
353         if ( val->bv_len != 0 ) {
354                 LDAPDN          *dn = NULL;
355                 int             rc;
356
357                 /*
358                  * Go to structural representation
359                  */
360                 rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP );
361                 if ( rc != LDAP_SUCCESS ) {
362                         return LDAP_INVALID_SYNTAX;
363                 }
364
365                 assert( strlen( val->bv_val ) == val->bv_len );
366
367                 /*
368                  * Schema-aware rewrite
369                  */
370                 if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) {
371                         ldap_dnfree( dn );
372                         return LDAP_INVALID_SYNTAX;
373                 }
374
375                 /*
376                  * Back to string representation
377                  */
378                 rc = ldap_dn2bv( dn, out, LDAP_DN_FORMAT_LDAPV3 );
379
380                 ldap_dnfree( dn );
381
382                 if ( rc != LDAP_SUCCESS ) {
383                         return LDAP_INVALID_SYNTAX;
384                 }
385         } else {
386                 ber_dupbv( out, val );
387         }
388
389         Debug( LDAP_DEBUG_TRACE, "<<< dnNormalize: <%s>\n", out->bv_val, 0, 0 );
390
391         return LDAP_SUCCESS;
392 }
393
394 /*
395  * dn "pretty"ing routine
396  */
397 int
398 dnPretty(
399         Syntax *syntax,
400         struct berval *val,
401         struct berval **pretty)
402 {
403         struct berval *out;
404         int rc;
405
406         assert( pretty && *pretty == NULL );
407
408         out = ch_malloc( sizeof( struct berval ) );
409         rc = dnPretty2( syntax, val, out );
410         if ( rc != LDAP_SUCCESS )
411                 free( out );
412         else
413                 *pretty = out;
414         return rc;
415 }
416
417 int
418 dnPretty2(
419         Syntax *syntax,
420         struct berval *val,
421         struct berval *out)
422 {
423         assert( val );
424         assert( out );
425
426         Debug( LDAP_DEBUG_TRACE, ">>> dnPretty: <%s>\n", val->bv_val, 0, 0 );
427
428         if ( val->bv_len == 0 ) {
429                 ber_dupbv( out, val );
430
431         } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
432                 return LDAP_INVALID_SYNTAX;
433
434         } else {
435                 LDAPDN          *dn = NULL;
436                 int             rc;
437
438                 /* FIXME: should be liberal in what we accept */
439                 rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP );
440                 if ( rc != LDAP_SUCCESS ) {
441                         return LDAP_INVALID_SYNTAX;
442                 }
443
444                 assert( strlen( val->bv_val ) == val->bv_len );
445
446                 /*
447                  * Schema-aware rewrite
448                  */
449                 if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY ) != LDAP_SUCCESS ) {
450                         ldap_dnfree( dn );
451                         return LDAP_INVALID_SYNTAX;
452                 }
453
454                 /* FIXME: not sure why the default isn't pretty */
455                 /* RE: the default is the form that is used as
456                  * an internal representation; the pretty form
457                  * is a variant */
458                 rc = ldap_dn2bv( dn, out,
459                         LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
460
461                 ldap_dnfree( dn );
462
463                 if ( rc != LDAP_SUCCESS ) {
464                         return LDAP_INVALID_SYNTAX;
465                 }
466         }
467
468         Debug( LDAP_DEBUG_TRACE, "<<< dnPretty: <%s>\n", out->bv_val, 0, 0 );
469
470         return LDAP_SUCCESS;
471 }
472
473 /*
474  * Combination of both dnPretty and dnNormalize
475  */
476 int
477 dnPrettyNormal(
478         Syntax *syntax,
479         struct berval *val,
480         struct berval *pretty,
481         struct berval *normal)
482 {
483         Debug( LDAP_DEBUG_TRACE, ">>> dnPrettyNormal: <%s>\n", val->bv_val, 0, 0 );
484
485         assert( val );
486         assert( pretty );
487         assert( normal );
488
489         if ( val->bv_len == 0 ) {
490                 ber_dupbv( pretty, val );
491                 ber_dupbv( normal, val );
492
493         } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
494                 /* too big */
495                 return LDAP_INVALID_SYNTAX;
496
497         } else {
498                 LDAPDN          *dn = NULL;
499                 int             rc;
500
501                 pretty->bv_val = NULL;
502                 normal->bv_val = NULL;
503                 pretty->bv_len = 0;
504                 normal->bv_len = 0;
505
506                 /* FIXME: should be liberal in what we accept */
507                 rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP );
508                 if ( rc != LDAP_SUCCESS ) {
509                         return LDAP_INVALID_SYNTAX;
510                 }
511
512                 assert( strlen( val->bv_val ) == val->bv_len );
513
514                 /*
515                  * Schema-aware rewrite
516                  */
517                 if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY ) != LDAP_SUCCESS ) {
518                         ldap_dnfree( dn );
519                         return LDAP_INVALID_SYNTAX;
520                 }
521
522                 rc = ldap_dn2bv( dn, pretty,
523                         LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
524
525                 if ( rc != LDAP_SUCCESS ) {
526                         ldap_dnfree( dn );
527                         return LDAP_INVALID_SYNTAX;
528                 }
529
530                 if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) {
531                         ldap_dnfree( dn );
532                         free( pretty->bv_val );
533                         pretty->bv_val = NULL;
534                         pretty->bv_len = 0;
535                         return LDAP_INVALID_SYNTAX;
536                 }
537
538                 rc = ldap_dn2bv( dn, normal, LDAP_DN_FORMAT_LDAPV3 );
539
540                 ldap_dnfree( dn );
541                 if ( rc != LDAP_SUCCESS ) {
542                         free( pretty->bv_val );
543                         pretty->bv_val = NULL;
544                         pretty->bv_len = 0;
545                         return LDAP_INVALID_SYNTAX;
546                 }
547         }
548
549         Debug( LDAP_DEBUG_TRACE, "<<< dnPrettyNormal: <%s>, <%s>\n",
550                 pretty->bv_val, normal->bv_val, 0 );
551
552         return LDAP_SUCCESS;
553 }
554
555 /*
556  * dnMatch routine
557  */
558 int
559 dnMatch(
560         int *matchp,
561         slap_mask_t flags,
562         Syntax *syntax,
563         MatchingRule *mr,
564         struct berval *value,
565         void *assertedValue )
566 {
567         int match;
568         struct berval *asserted = (struct berval *) assertedValue;
569
570         assert( matchp );
571         assert( value );
572         assert( assertedValue );
573         
574         match = value->bv_len - asserted->bv_len;
575
576         if ( match == 0 ) {
577                 match = memcmp( value->bv_val, asserted->bv_val, 
578                                 value->bv_len );
579         }
580
581 #ifdef NEW_LOGGING
582         LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
583                 "dnMatch: %d\n    %s\n    %s\n", match,
584                 value->bv_val, asserted->bv_val ));
585 #else
586         Debug( LDAP_DEBUG_ARGS, "dnMatch %d\n\t\"%s\"\n\t\"%s\"\n",
587                 match, value->bv_val, asserted->bv_val );
588 #endif
589
590         *matchp = match;
591         return( LDAP_SUCCESS );
592 }
593
594 /*
595  * dnParent - dn's parent, in-place
596  *
597  * note: the incoming dn is assumed to be normalized/prettyfied,
598  * so that escaped rdn/ava separators are in '\'+hexpair form
599  */
600 void
601 dnParent( 
602         struct berval   *dn, 
603         struct berval   *pdn )
604 {
605         char    *p;
606
607         p = strchr( dn->bv_val, ',' );
608
609         /* one-level dn */
610         if ( p == NULL ) {
611                 *pdn = slap_empty_bv;
612                 return;
613         }
614
615         assert( DN_SEPARATOR( p[ 0 ] ) );
616         p++;
617
618         assert( ATTR_LEADCHAR( p[ 0 ] ) );
619         pdn->bv_val = p;
620         pdn->bv_len = dn->bv_len - (p - dn->bv_val);
621
622         return;
623 }
624
625 int
626 dnExtractRdn( 
627         struct berval   *dn, 
628         struct berval   *rdn )
629 {
630         LDAPRDN         *tmpRDN;
631         const char      *p;
632         int             rc;
633
634         assert( dn );
635         assert( rdn );
636
637         if( dn->bv_len == 0 ) {
638                 return LDAP_OTHER;
639         }
640
641         rc = ldap_bv2rdn( dn, &tmpRDN, (char **)&p, LDAP_DN_FORMAT_LDAP );
642         if ( rc != LDAP_SUCCESS ) {
643                 return rc;
644         }
645
646         rc = ldap_rdn2bv( tmpRDN, rdn, LDAP_DN_FORMAT_LDAPV3 );
647         ldap_rdnfree( tmpRDN );
648         if ( rc != LDAP_SUCCESS ) {
649                 return rc;
650         }
651
652         return LDAP_SUCCESS;
653 }
654
655 /*
656  * We can assume the input is a prettied or normalized DN
657  */
658 int 
659 dn_rdnlen(
660         Backend         *be,
661         struct berval   *dn_in )
662 {
663         const char      *p;
664
665         assert( dn_in );
666
667         if ( dn_in == NULL ) {
668                 return 0;
669         }
670
671         if ( !dn_in->bv_len ) {
672                 return 0;
673         }
674
675         if ( be != NULL && be_issuffix( be, dn_in ) ) {
676                 return 0;
677         }
678
679         p = strchr( dn_in->bv_val, ',' );
680
681         return p ? p - dn_in->bv_val : dn_in->bv_len;
682 }
683
684
685 /* rdnValidate:
686  *
687  * LDAP_SUCCESS if rdn is a legal rdn;
688  * LDAP_INVALID_SYNTAX otherwise (including a sequence of rdns)
689  */
690 int
691 rdnValidate( struct berval *rdn )
692 {
693 #if 1
694         /* Major cheat!
695          * input is a pretty or normalized DN
696          * hence, we can just search for ','
697          */
698         if( rdn == NULL || rdn->bv_len == 0 ||
699                 rdn->bv_len > SLAP_LDAPDN_MAXLEN )
700         {
701                 return LDAP_INVALID_SYNTAX;
702         }
703
704         return strchr( rdn->bv_val, ',' ) == NULL
705                 ? LDAP_SUCCESS : LDAP_INVALID_SYNTAX;
706
707 #else
708         LDAPRDN         *RDN, **DN[ 2 ] = { &RDN, NULL };
709         const char      *p;
710         int             rc;
711
712         /*
713          * must be non-empty
714          */
715         if ( rdn == NULL || rdn == '\0' ) {
716                 return 0;
717         }
718
719         /*
720          * must be parsable
721          */
722         rc = ldap_bv2rdn( rdn, &RDN, (char **)&p, LDAP_DN_FORMAT_LDAP );
723         if ( rc != LDAP_SUCCESS ) {
724                 return 0;
725         }
726
727         /*
728          * Must be one-level
729          */
730         if ( p[ 0 ] != '\0' ) {
731                 return 0;
732         }
733
734         /*
735          * Schema-aware validate
736          */
737         if ( rc == LDAP_SUCCESS ) {
738                 rc = LDAPDN_validate( DN );
739         }
740         ldap_rdnfree( RDN );
741
742         /*
743          * Must validate (there's a repeated parsing ...)
744          */
745         return ( rc == LDAP_SUCCESS );
746 #endif
747 }
748
749
750 /* build_new_dn:
751  *
752  * Used by ldbm/bdb2 back_modrdn to create the new dn of entries being
753  * renamed.
754  *
755  * new_dn = parent (p_dn) + separator + rdn (newrdn) + null.
756  */
757
758 void
759 build_new_dn( struct berval * new_dn,
760         struct berval * parent_dn,
761         struct berval * newrdn )
762 {
763         char *ptr;
764
765         if ( parent_dn == NULL ) {
766                 ber_dupbv( new_dn, newrdn );
767                 return;
768         }
769
770         new_dn->bv_len = parent_dn->bv_len + newrdn->bv_len + 1;
771         new_dn->bv_val = (char *) ch_malloc( new_dn->bv_len + 1 );
772
773         ptr = slap_strcopy( new_dn->bv_val, newrdn->bv_val );
774         *ptr++ = ',';
775         strcpy( ptr, parent_dn->bv_val );
776 }
777
778
779 /*
780  * dnIsSuffix - tells whether suffix is a suffix of dn.
781  * Both dn and suffix must be normalized.
782  */
783 int
784 dnIsSuffix(
785         const struct berval *dn,
786         const struct berval *suffix )
787 {
788         int     d = dn->bv_len - suffix->bv_len;
789
790         assert( dn );
791         assert( suffix );
792
793         /* empty suffix matches any dn */
794         if ( suffix->bv_len == 0 ) {
795                 return 1;
796         }
797
798         /* suffix longer than dn */
799         if ( d < 0 ) {
800                 return 0;
801         }
802
803         /* no rdn separator or escaped rdn separator */
804         if ( d > 1 && !DN_SEPARATOR( dn->bv_val[ d - 1 ] ) ) {
805                 return 0;
806         }
807
808         /* no possible match or malformed dn */
809         if ( d == 1 ) {
810                 return 0;
811         }
812
813         /* compare */
814         return( strcmp( dn->bv_val + d, suffix->bv_val ) == 0 );
815 }