]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/schema_init.c
ITS#7059 replace previous patch
[openldap] / servers / slapd / schema_init.c
index 85c62249df2d1b343c86de464ee3ec6174470023..65a7e2e87807f63e8957b152a9fed6699a6d4b9e 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2009 The OpenLDAP Foundation.
+ * Copyright 1998-2011 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * <http://www.OpenLDAP.org/license.html>.
  */
 
+/*
+ * Syntaxes - implementation notes:
+ *
+ * Validate function(syntax, value):
+ *   Called before the other functions here to check if the value
+ *   is valid according to the syntax.
+ *
+ * Pretty function(syntax, input value, output prettified...):
+ *   If it exists, maps different notations of the same value to a
+ *   unique representation which can be stored in the directory and
+ *   possibly be passed to the Match/Indexer/Filter() functions.
+ *
+ *   E.g. DN "2.5.4.3 = foo\,bar, o = BAZ" -> "cn=foo\2Cbar,o=BAZ",
+ *   but unlike DN normalization, "BAZ" is not mapped to "baz".
+ */
+
+/*
+ * Matching rules - implementation notes:
+ *
+ * Matching rules match an attribute value (often from the directory)
+ * against an asserted value (e.g. from a filter).
+ *
+ * Invoked with validated and commonly pretty/normalized arguments, thus
+ * a number of matching rules can simply use the octetString functions.
+ *
+ * Normalize function(...input value, output normalized...):
+ *   If it exists, maps matching values to a unique representation
+ *   which is passed to the Match/Indexer/Filter() functions.
+ *
+ *   Different matching rules can normalize values of the same syntax
+ *   differently.  E.g. caseIgnore rules normalize to lowercase,
+ *   caseExact rules do not.
+ *
+ * Match function(*output matchp, ...value, asserted value):
+ *   On success, set *matchp.  0 means match.  For ORDERING/most EQUALITY,
+ *   less/greater than 0 means value less/greater than asserted.  However:
+ *
+ *   In extensible match filters, ORDERING rules match if value<asserted.
+ *
+ *   EQUALITY rules may order values differently than ORDERING rules for
+ *   speed, since EQUALITY ordering is only used for SLAP_AT_SORTED_VAL.
+ *   Some EQUALITY rules do not order values (ITS#6722).
+ *
+ * Indexer function(...attribute values, *output keysp,...):
+ *   Generates index keys for the attribute values.  Backends can store
+ *   them in an index, a {key->entry ID set} mapping, for the attribute.
+ *
+ *   A search can look up the DN/scope and asserted values in the
+ *   indexes, if any, to narrow down the number of entires to check
+ *   against the search criteria.
+ *
+ * Filter function(...asserted value, *output keysp,...):
+ *   Generates index key(s) for the asserted value, to be looked up in
+ *   the index from the Indexer function.  *keysp is an array because
+ *   substring matching rules can generate multiple lookup keys.
+ *
+ * Index keys:
+ *   A key is usually a hash of match type, attribute value and schema
+ *   info, because one index can contain keys for many filtering types.
+ *
+ *   Some indexes instead have EQUALITY keys ordered so that if
+ *   key(val1) < key(val2), then val1 < val2 by the ORDERING rule.
+ *   That way the ORDERING rule can use the EQUALITY index.
+ *
+ * Substring indexing:
+ *   This chops the attribute values up in small chunks and indexes all
+ *   possible chunks of certain sizes.  Substring filtering looks up
+ *   SOME of the asserted value's chunks, and the caller uses the
+ *   intersection of the resulting entry ID sets.
+ *   See the index_substr_* keywords in slapd.conf(5).
+ */
+
 #include "portable.h"
 
 #include <stdio.h>
@@ -156,8 +228,8 @@ enum {
 
 enum {
        SLAP_X509_OPT_C_VERSION         = SLAP_X509_OPTION + 0,
-       SLAP_X509_OPT_C_ISSUERUNIQUEID  = SLAP_X509_OPTION + 1,
-       SLAP_X509_OPT_C_SUBJECTUNIQUEID = SLAP_X509_OPTION + 2,
+       SLAP_X509_OPT_C_ISSUERUNIQUEID  = LBER_CLASS_CONTEXT + 1,
+       SLAP_X509_OPT_C_SUBJECTUNIQUEID = LBER_CLASS_CONTEXT + 2,
        SLAP_X509_OPT_C_EXTENSIONS      = SLAP_X509_OPTION + 3
 };
 
@@ -273,18 +345,25 @@ certificateValidate( Syntax *syntax, struct berval *in )
 }
 
 /* X.509 certificate list validation */
+static int
+checkTime( struct berval *in, struct berval *out );
+
 static int
 certificateListValidate( Syntax *syntax, struct berval *in )
 {
        BerElementBuffer berbuf;
        BerElement *ber = (BerElement *)&berbuf;
        ber_tag_t tag;
-       ber_len_t len;
+       ber_len_t len, wrapper_len;
+       char *wrapper_start;
+       int wrapper_ok = 0;
        ber_int_t version = SLAP_X509_V1;
+       struct berval bvdn, bvtu;
 
        ber_init2( ber, in, LBER_USE_DER );
-       tag = ber_skip_tag( ber, &len );        /* Signed wrapper */
+       tag = ber_skip_tag( ber, &wrapper_len );        /* Signed wrapper */
        if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+       wrapper_start = ber->ber_ptr;
        tag = ber_skip_tag( ber, &len );        /* Sequence */
        if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
        tag = ber_peek_tag( ber, &len );
@@ -297,12 +376,18 @@ certificateListValidate( Syntax *syntax, struct berval *in )
        tag = ber_skip_tag( ber, &len );        /* Signature Algorithm */
        if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
        ber_skip_data( ber, len );
-       tag = ber_skip_tag( ber, &len );        /* Issuer DN */
+       tag = ber_peek_tag( ber, &len );        /* Issuer DN */
        if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+       len = ber_ptrlen( ber );
+       bvdn.bv_val = in->bv_val + len;
+       bvdn.bv_len = in->bv_len - len;
+       tag = ber_skip_tag( ber, &len );
        ber_skip_data( ber, len );
        tag = ber_skip_tag( ber, &len );        /* thisUpdate */
        /* Time is a CHOICE { UTCTime, GeneralizedTime } */
        if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX;
+       bvtu.bv_val = (char *)ber->ber_ptr;
+       bvtu.bv_len = len;
        ber_skip_data( ber, len );
        /* Optional nextUpdate */
        tag = ber_skip_tag( ber, &len );
@@ -313,16 +398,20 @@ certificateListValidate( Syntax *syntax, struct berval *in )
        /* revokedCertificates - Sequence of Sequence, Optional */
        if ( tag == LBER_SEQUENCE ) {
                ber_len_t seqlen;
-               if ( ber_peek_tag( ber, &seqlen ) == LBER_SEQUENCE ) {
-                       /* Should NOT be empty */
-                       ber_skip_data( ber, len );
+               ber_tag_t stag;
+               stag = ber_peek_tag( ber, &seqlen );
+               if ( stag == LBER_SEQUENCE || !len ) {
+                       /* RFC5280 requires non-empty, but X.509(2005) allows empty. */
+                       if ( len )
+                               ber_skip_data( ber, len );
                        tag = ber_skip_tag( ber, &len );
                }
        }
-       /* Optional Extensions */
+       /* Optional Extensions - Sequence of Sequence */
        if ( tag == SLAP_X509_OPT_CL_CRLEXTENSIONS ) { /* ? */
+               ber_len_t seqlen;
                if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
-               tag = ber_skip_tag( ber, &len );
+               tag = ber_peek_tag( ber, &seqlen );
                if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
                ber_skip_data( ber, len );
                tag = ber_skip_tag( ber, &len );
@@ -334,9 +423,44 @@ certificateListValidate( Syntax *syntax, struct berval *in )
        /* Signature */
        if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; 
        ber_skip_data( ber, len );
+       if ( ber->ber_ptr == wrapper_start + wrapper_len ) wrapper_ok = 1;
        tag = ber_skip_tag( ber, &len );
        /* Must be at end now */
-       if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX;
+       /* NOTE: OpenSSL tolerates CL with garbage past the end */
+       if ( len || tag != LBER_DEFAULT ) {
+               struct berval issuer_dn = BER_BVNULL, thisUpdate;
+               char tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
+               int rc;
+
+               if ( ! wrapper_ok ) {
+                       return LDAP_INVALID_SYNTAX;
+               }
+
+               rc = dnX509normalize( &bvdn, &issuer_dn );
+               if ( rc != LDAP_SUCCESS ) {
+                       rc = LDAP_INVALID_SYNTAX;
+                       goto done;
+               }
+
+               thisUpdate.bv_val = tubuf;
+               thisUpdate.bv_len = sizeof(tubuf); 
+               if ( checkTime( &bvtu, &thisUpdate ) ) {
+                       rc = LDAP_INVALID_SYNTAX;
+                       goto done;
+               }
+
+               Debug( LDAP_DEBUG_ANY,
+                       "certificateListValidate issuer=\"%s\", thisUpdate=%s: extra cruft past end of certificateList\n",
+                       issuer_dn.bv_val, thisUpdate.bv_val, 0 );
+
+done:;
+               if ( ! BER_BVISNULL( &issuer_dn ) ) {
+                       ber_memfree( issuer_dn.bv_val );
+               }
+
+               return rc;
+       }
+
        return LDAP_SUCCESS;
 }
 
@@ -388,7 +512,7 @@ attributeCertificateValidate( Syntax *syntax, struct berval *in )
        if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
        ber_skip_data( ber, len );
 
-       ber_peek_tag( ber, &len );
+       tag = ber_peek_tag( ber, &len );
 
        if ( tag == LBER_BITSTRING ) {  /* issuerUniqueID */
                tag = ber_skip_tag( ber, &len );
@@ -433,13 +557,12 @@ octetStringMatch(
        void *assertedValue )
 {
        struct berval *asserted = (struct berval *) assertedValue;
-       int match = value->bv_len - asserted->bv_len;
+       ber_slen_t d = (ber_slen_t) value->bv_len - (ber_slen_t) asserted->bv_len;
 
-       if( match == 0 ) {
-               match = memcmp( value->bv_val, asserted->bv_val, value->bv_len );
-       }
+       /* For speed, order first by length, then by contents */
+       *matchp = d ? (sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1)
+               : memcmp( value->bv_val, asserted->bv_val, value->bv_len );
 
-       *matchp = match;
        return LDAP_SUCCESS;
 }
 
@@ -459,12 +582,20 @@ octetStringOrderingMatch(
        int match = memcmp( value->bv_val, asserted->bv_val,
                (v_len < av_len ? v_len : av_len) );
 
-       if( match == 0 ) match = v_len - av_len;
+       if( match == 0 )
+               match = sizeof(v_len) == sizeof(int)
+                       ? (int) v_len - (int) av_len
+                       : v_len < av_len ? -1 : v_len > av_len;
+
+       /* If used in extensible match filter, match if value < asserted */
+       if ( flags & SLAP_MR_EXT )
+               match = (match >= 0);
 
        *matchp = match;
        return LDAP_SUCCESS;
 }
 
+/* Initialize HASHcontext from match type and schema info */
 static void
 hashPreset(
        HASH_CONTEXT *HASHcontext,
@@ -484,6 +615,7 @@ hashPreset(
        return;
 }
 
+/* Set HASHdigest from HASHcontext and value:len */
 static void
 hashIter(
        HASH_CONTEXT *HASHcontext,
@@ -496,7 +628,7 @@ hashIter(
        HASH_Final( HASHdigest, &ctx );
 }
 
-/* Index generation function */
+/* Index generation function: Attribute values -> index hash keys */
 int octetStringIndexer(
        slap_mask_t use,
        slap_mask_t flags,
@@ -542,7 +674,7 @@ int octetStringIndexer(
        return LDAP_SUCCESS;
 }
 
-/* Index generation function */
+/* Index generation function: Asserted value -> index hash key */
 int octetStringFilter(
        slap_mask_t use,
        slap_mask_t flags,
@@ -703,7 +835,7 @@ done:
        return LDAP_SUCCESS;
 }
 
-/* Substrings Index generation function */
+/* Substring index generation function: Attribute values -> index hash keys */
 static int
 octetStringSubstringsIndexer(
        slap_mask_t use,
@@ -823,6 +955,7 @@ octetStringSubstringsIndexer(
        return LDAP_SUCCESS;
 }
 
+/* Substring index generation function: Assertion value -> index hash keys */
 static int
 octetStringSubstringsFilter (
        slap_mask_t use,
@@ -1367,9 +1500,10 @@ uniqueMemberMatch(
        }
 
        if( valueUID.bv_len && assertedUID.bv_len ) {
-               match = valueUID.bv_len - assertedUID.bv_len;
-               if ( match ) {
-                       *matchp = match;
+               ber_slen_t d;
+               d = (ber_slen_t) valueUID.bv_len - (ber_slen_t) assertedUID.bv_len;
+               if ( d ) {
+                       *matchp = sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1;
                        return LDAP_SUCCESS;
                }
 
@@ -1520,7 +1654,7 @@ booleanMatch(
 {
        /* simplistic matching allowed by rigid validation */
        struct berval *asserted = (struct berval *) assertedValue;
-       *matchp = value->bv_len != asserted->bv_len;
+       *matchp = (int) asserted->bv_len - (int) value->bv_len;
        return LDAP_SUCCESS;
 }
 
@@ -1683,8 +1817,9 @@ UTF8StringNormalize(
                ? LDAP_UTF8_APPROX : 0;
 
        val = UTF8bvnormalize( val, &tmp, flags, ctx );
+       /* out of memory or syntax error, the former is unlikely */
        if( val == NULL ) {
-               return LDAP_OTHER;
+               return LDAP_INVALID_SYNTAX;
        }
        
        /* collapse spaces (in place) */
@@ -1717,12 +1852,12 @@ UTF8StringNormalize(
                }
                nvalue.bv_val[nvalue.bv_len] = '\0';
 
-       } else {
+       } else if ( tmp.bv_len )  {
                /* string of all spaces is treated as one space */
                nvalue.bv_val[0] = ' ';
                nvalue.bv_val[1] = '\0';
                nvalue.bv_len = 1;
-       }
+       }       /* should never be entered with 0-length val */
 
        *normalized = nvalue;
        return LDAP_SUCCESS;
@@ -2196,13 +2331,18 @@ postalAddressNormalize(
        }
        lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
 
-       normalized->bv_len = l;
+       normalized->bv_len = c = l;
 
-       for ( l = 0; !BER_BVISNULL( &lines[l] ); l++ ) {
+       for ( l = 0; l <= c; l++ ) {
                /* NOTE: we directly normalize each line,
                 * without unescaping the values, since the special
                 * values '\24' ('$') and '\5C' ('\') are not affected
                 * by normalization */
+               if ( !lines[l].bv_len ) {
+                       nlines[l].bv_len = 0;
+                       nlines[l].bv_val = NULL;
+                       continue;
+               }
                rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx );
                if ( rc != LDAP_SUCCESS ) {
                        rc = LDAP_INVALID_SYNTAX;
@@ -2215,9 +2355,8 @@ postalAddressNormalize(
        normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
 
        p = normalized->bv_val;
-       for ( l = 0; !BER_BVISNULL( &nlines[l] ); l++ ) {
-               p = lutil_strncopy( p, nlines[l].bv_val, nlines[l].bv_len );
-
+       for ( l = 0; l <= c ; l++ ) {
+               p = lutil_strbvcopy( p, &nlines[l] );
                *p++ = '$';
        }
        *--p = '\0';
@@ -2359,6 +2498,10 @@ integerMatch(
                if( vsign < 0 ) match = -match;
        }
 
+       /* Ordering rule used in extensible match filter? */
+       if ( (flags & SLAP_MR_EXT) && (mr->smr_usage & SLAP_MR_ORDERING) )
+               match = (match >= 0);
+
        *matchp = match;
        return LDAP_SUCCESS;
 }
@@ -2374,11 +2517,11 @@ integerVal2Key(
        struct berval *tmp,
        void *ctx )
 {
-       /* index format:
-        * only if too large: one's complement <sign*exponent (chopped bytes)>,
+       /* Integer index key format, designed for memcmp to collate correctly:
+        * if too large: one's complement sign*<approx exponent=chopped bytes>,
         * two's complement value (sign-extended or chopped as needed),
-        * however the top <number of exponent-bytes + 1> bits of first byte
-        * above is the inverse sign.   The next bit is the sign as delimiter.
+        * however in first byte above, the top <number of exponent-bytes + 1>
+        * bits are the inverse sign and next bit is the sign as delimiter.
         */
        ber_slen_t k = index_intlen_strlen;
        ber_len_t chop = 0;
@@ -2413,6 +2556,7 @@ integerVal2Key(
                assert( chop == 0 );
                memset( key->bv_val, neg, k );  /* sign-extend */
        } else if ( k != 0 || ((itmp.bv_val[0] ^ neg) & 0xc0) ) {
+               /* Got exponent -k, or no room for 2 sign bits */
                lenp = lenbuf + sizeof(lenbuf);
                chop = - (ber_len_t) k;
                do {
@@ -2420,7 +2564,7 @@ integerVal2Key(
                        signmask >>= 1;
                } while ( (chop >>= 8) != 0 || (signmask >> 1) & (*lenp ^ neg) );
                /* With n bytes in lenbuf, the top n+1 bits of (signmask&0xff)
-                * are 1, and the top n+2 bits of lenp[] are the sign bit. */
+                * are 1, and the top n+2 bits of lenp[0] are the sign bit. */
                k = (lenbuf + sizeof(lenbuf)) - lenp;
                if ( k > (ber_slen_t) index_intlen )
                        k = index_intlen;
@@ -2432,7 +2576,7 @@ integerVal2Key(
        return 0;
 }
 
-/* Index generation function */
+/* Index generation function: Ordered index */
 static int
 integerIndexer(
        slap_mask_t use,
@@ -2498,7 +2642,7 @@ func_leave:
        return rc;
 }
 
-/* Index generation function */
+/* Index generation function: Ordered index */
 static int
 integerFilter(
        slap_mask_t use,
@@ -3280,9 +3424,9 @@ serialNumberAndIssuerPretty(
 
        p = out->bv_val;
        p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
-       p = lutil_strncopy( p, sn.bv_val, sn.bv_len );
+       p = lutil_strbvcopy( p, &sn );
        p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
-       p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+       p = lutil_strbvcopy( p, &ni );
        p = lutil_strcopy( p, /*{*/ "\" }" );
 
        assert( p == &out->bv_val[out->bv_len] );
@@ -3417,14 +3561,9 @@ serialNumberAndIssuerNormalize(
                sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
        }
        sn2.bv_len = sn.bv_len;
-       if ( lutil_str2bin( &sn, &sn2, ctx )) {
-               rc = LDAP_INVALID_SYNTAX;
-               goto func_leave;
-       }
-
        sn3.bv_val = sbuf3;
        sn3.bv_len = sizeof(sbuf3);
-       if ( slap_bin2hex( &sn2, &sn3, ctx ) ) {
+       if ( lutil_str2bin( &sn, &sn2, ctx ) || slap_bin2hex( &sn2, &sn3, ctx ) ) {
                rc = LDAP_INVALID_SYNTAX;
                goto func_leave;
        }
@@ -3432,7 +3571,6 @@ serialNumberAndIssuerNormalize(
        out->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
                + sn3.bv_len + ni.bv_len;
        out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
-
        if ( out->bv_val == NULL ) {
                out->bv_len = 0;
                rc = LDAP_OTHER;
@@ -3442,9 +3580,9 @@ serialNumberAndIssuerNormalize(
        p = out->bv_val;
 
        p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
-       p = lutil_strncopy( p, sn3.bv_val, sn3.bv_len );
+       p = lutil_strbvcopy( p, &sn3 );
        p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
-       p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+       p = lutil_strbvcopy( p, &ni );
        p = lutil_strcopy( p, /*{*/ "\" }" );
 
        assert( p == &out->bv_val[out->bv_len] );
@@ -3539,9 +3677,9 @@ certificateExactNormalize(
        p = normalized->bv_val;
 
        p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
-       p = lutil_strncopy( p, sn2.bv_val, sn2.bv_len );
+       p = lutil_strbvcopy( p, &sn2 );
        p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
-       p = lutil_strncopy( p, issuer_dn.bv_val, issuer_dn.bv_len );
+       p = lutil_strbvcopy( p, &issuer_dn );
        p = lutil_strcopy( p, /*{*/ "\" }" );
 
        rc = LDAP_SUCCESS;
@@ -3616,6 +3754,9 @@ checkTime( struct berval *in, struct berval *out )
 
        rc = generalizedTimeValidate( NULL, &bv );
        if ( rc == LDAP_SUCCESS && out != NULL ) {
+               if ( out->bv_len > bv.bv_len ) {
+                       out->bv_val[ bv.bv_len ] = '\0';
+               }
                out->bv_len = bv.bv_len;
        }
 
@@ -3868,9 +4009,9 @@ issuerAndThisUpdatePretty(
 
        p = out->bv_val;
        p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
-       p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+       p = lutil_strbvcopy( p, &ni );
        p = lutil_strcopy( p, "\", thisUpdate \"" );
-       p = lutil_strncopy( p, tu.bv_val, tu.bv_len );
+       p = lutil_strbvcopy( p, &tu );
        p = lutil_strcopy( p, /*{*/ "\" }" );
 
        assert( p == &out->bv_val[out->bv_len] );
@@ -3934,9 +4075,9 @@ issuerAndThisUpdateNormalize(
        p = out->bv_val;
 
        p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
-       p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+       p = lutil_strbvcopy( p, &ni );
        p = lutil_strcopy( p, "\", thisUpdate \"" );
-       p = lutil_strncopy( p, tu2.bv_val, tu2.bv_len );
+       p = lutil_strbvcopy( p, &tu2 );
        p = lutil_strcopy( p, /*{*/ "\" }" );
 
        assert( p == &out->bv_val[out->bv_len] );
@@ -4029,9 +4170,9 @@ certificateListExactNormalize(
        p = normalized->bv_val;
 
        p = lutil_strcopy( p, "{ issuer rdnSequence:\"" );
-       p = lutil_strncopy( p, issuer_dn.bv_val, issuer_dn.bv_len );
+       p = lutil_strbvcopy( p, &issuer_dn );
        p = lutil_strcopy( p, "\", thisUpdate \"" );
-       p = lutil_strncopy( p, thisUpdate.bv_val, thisUpdate.bv_len );
+       p = lutil_strbvcopy( p, &thisUpdate );
        p = lutil_strcopy( p, /*{*/ "\" }" );
 
        rc = LDAP_SUCCESS;
@@ -4464,11 +4605,11 @@ serialNumberAndIssuerSerialPretty(
 
        p = out->bv_val;
        p = lutil_strcopy( p, "{ serialNumber " );
-       p = lutil_strncopy( p, sn.bv_val, sn.bv_len );
+       p = lutil_strbvcopy( p, &sn );
        p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
-       p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+       p = lutil_strbvcopy( p, &ni );
        p = lutil_strcopy( p, "\" }, serial " );
-       p = lutil_strncopy( p, i_sn.bv_val, i_sn.bv_len );
+       p = lutil_strbvcopy( p, &i_sn );
        p = lutil_strcopy( p, " } } }" );
 
        assert( p == &out->bv_val[out->bv_len] );
@@ -4578,11 +4719,11 @@ serialNumberAndIssuerSerialNormalize(
        p = out->bv_val;
 
        p = lutil_strcopy( p, "{ serialNumber " );
-       p = lutil_strncopy( p, sn3.bv_val, sn3.bv_len );
+       p = lutil_strbvcopy( p, &sn3 );
        p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
-       p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+       p = lutil_strbvcopy( p, &ni );
        p = lutil_strcopy( p, "\" }, serial " );
-       p = lutil_strncopy( p, i_sn3.bv_val, i_sn3.bv_len );
+       p = lutil_strbvcopy( p, &i_sn3 );
        p = lutil_strcopy( p, " } } }" );
 
        assert( p == &out->bv_val[out->bv_len] );
@@ -4627,13 +4768,13 @@ attributeCertificateExactNormalize(
        ber_tag_t tag;
        ber_len_t len;
        char issuer_serialbuf[SLAP_SN_BUFLEN], serialbuf[SLAP_SN_BUFLEN];
-       struct berval sn, i_sn, sn2, i_sn2;
+       struct berval sn, i_sn, sn2 = BER_BVNULL, i_sn2 = BER_BVNULL;
        struct berval issuer_dn = BER_BVNULL, bvdn;
        char *p;
        int rc = LDAP_INVALID_SYNTAX;
 
        if ( BER_BVISEMPTY( val ) ) {
-               goto done;
+               return rc;
        }
 
        if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
@@ -4657,8 +4798,7 @@ attributeCertificateExactNormalize(
        tag = ber_skip_tag( ber, &len );        /* GeneralNames (sequence) */
        tag = ber_skip_tag( ber, &len );        /* directoryName (we only accept this form of GeneralName) */
        if ( tag != SLAP_X509_GN_DIRECTORYNAME ) { 
-               rc = LDAP_INVALID_SYNTAX; 
-               goto done;
+               return LDAP_INVALID_SYNTAX; 
        }
        tag = ber_peek_tag( ber, &len );        /* sequence of RDN */
        len = ber_ptrlen( ber );
@@ -4711,11 +4851,11 @@ attributeCertificateExactNormalize(
        p = normalized->bv_val;
 
        p = lutil_strcopy( p, "{ serialNumber " );
-       p = lutil_strncopy( p, sn2.bv_val, sn2.bv_len );
+       p = lutil_strbvcopy( p, &sn2 );
        p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
-       p = lutil_strncopy( p, issuer_dn.bv_val, issuer_dn.bv_len );
+       p = lutil_strbvcopy( p, &issuer_dn );
        p = lutil_strcopy( p, "\" }, serial " );
-       p = lutil_strncopy( p, i_sn2.bv_val, i_sn2.bv_len );
+       p = lutil_strbvcopy( p, &i_sn2 );
        p = lutil_strcopy( p, " } } }" );
 
        Debug( LDAP_DEBUG_TRACE, "attributeCertificateExactNormalize: %s\n",
@@ -5056,7 +5196,7 @@ csnNormalize21(
        ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:MM:" ) ],
                STRLENOF( "SS" ) );
        ptr = lutil_strcopy( ptr, ".000000Z#00" );
-       ptr = lutil_strncopy( ptr, cnt.bv_val, cnt.bv_len );
+       ptr = lutil_strbvcopy( ptr, &cnt );
        *ptr++ = '#';
        *ptr++ = '0';
        *ptr++ = '0';
@@ -5149,7 +5289,7 @@ csnNormalize23(
        ptr = bv.bv_val;
        ptr = lutil_strncopy( ptr, gt.bv_val, gt.bv_len - 1 );
        ptr = lutil_strcopy( ptr, ".000000Z#" );
-       ptr = lutil_strncopy( ptr, cnt.bv_val, cnt.bv_len );
+       ptr = lutil_strbvcopy( ptr, &cnt );
        *ptr++ = '#';
        *ptr++ = '0';
        for ( i = 0; i < sid.bv_len; i++ ) {
@@ -5560,11 +5700,15 @@ generalizedTimeOrderingMatch(
                (v_len < av_len ? v_len : av_len) - 1 );
        if ( match == 0 ) match = v_len - av_len;
 
+       /* If used in extensible match filter, match if value < asserted */
+       if ( flags & SLAP_MR_EXT )
+               match = (match >= 0);
+
        *matchp = match;
        return LDAP_SUCCESS;
 }
 
-/* Index generation function */
+/* Index generation function: Ordered index */
 int generalizedTimeIndexer(
        slap_mask_t use,
        slap_mask_t flags,
@@ -5620,7 +5764,7 @@ int generalizedTimeIndexer(
        return LDAP_SUCCESS;
 }
 
-/* Index generation function */
+/* Index generation function: Ordered index */
 int generalizedTimeFilter(
        slap_mask_t use,
        slap_mask_t flags,
@@ -5954,9 +6098,9 @@ firstComponentNormalize(
 }
 
 static char *country_gen_syn[] = {
-       "1.3.6.1.4.1.1466.115.121.1.15",
-       "1.3.6.1.4.1.1466.115.121.1.26",
-       "1.3.6.1.4.1.1466.115.121.1.44",
+       "1.3.6.1.4.1.1466.115.121.1.15",        /* Directory String */
+       "1.3.6.1.4.1.1466.115.121.1.26",        /* IA5 String */
+       "1.3.6.1.4.1.1466.115.121.1.44",        /* Printable String */
        NULL
 };
 
@@ -6003,7 +6147,7 @@ static slap_syntax_defs_rec syntax_defs[] = {
                countryStringValidate, NULL},
 #endif
        {"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )",
-               0, NULL, dnValidate, dnPretty},
+               SLAP_SYNTAX_DN, NULL, dnValidate, dnPretty},
        {"( 1.2.36.79672281.1.5.0 DESC 'RDN' )",
                0, NULL, rdnValidate, rdnPretty},
 #ifdef LDAP_COMP_MATCH
@@ -6053,7 +6197,7 @@ static slap_syntax_defs_rec syntax_defs[] = {
        {"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )",
                0, NULL, NULL, NULL},
        {"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )",
-               0, NULL, nameUIDValidate, nameUIDPretty },
+               SLAP_SYNTAX_DN, NULL, nameUIDValidate, nameUIDPretty },
        {"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )",
                0, NULL, NULL, NULL},
        {"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )",
@@ -6303,21 +6447,21 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
 #ifdef LDAP_COMP_MATCH
        {"( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch' "
-               "SYNTAX 1.2.36.79672281.1.5.2 )",
+               "SYNTAX 1.2.36.79672281.1.5.2 )", /* componentFilterMatch assertion */
                SLAP_MR_EXT|SLAP_MR_COMPONENT, componentFilterMatchSyntaxes,
                NULL, NULL , componentFilterMatch,
                octetStringIndexer, octetStringFilter,
                NULL },
 
         {"( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch' "
-                "SYNTAX 1.2.36.79672281.1.5.3 )",
+                "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
                 NULL, NULL , allComponentsMatch,
                 octetStringIndexer, octetStringFilter,
                 NULL },
 
         {"( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch' "
-                "SYNTAX 1.2.36.79672281.1.5.3 )",
+                "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
                 SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
                 NULL, NULL , directoryComponentsMatch,
                 octetStringIndexer, octetStringFilter,
@@ -6333,13 +6477,13 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
-               SLAP_MR_ORDERING, directoryStringSyntaxes,
+               SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
                NULL, UTF8StringNormalize, octetStringOrderingMatch,
                NULL, NULL,
                "caseIgnoreMatch" },
 
        {"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
                SLAP_MR_SUBSTR, directoryStringSyntaxes,
                NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
                octetStringSubstringsIndexer, octetStringSubstringsFilter,
@@ -6354,13 +6498,13 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 2.5.13.6 NAME 'caseExactOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
-               SLAP_MR_ORDERING, directoryStringSyntaxes,
+               SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
                NULL, UTF8StringNormalize, octetStringOrderingMatch,
                NULL, NULL,
                "caseExactMatch" },
 
        {"( 2.5.13.7 NAME 'caseExactSubstringsMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
                SLAP_MR_SUBSTR, directoryStringSyntaxes,
                NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
                octetStringSubstringsIndexer, octetStringSubstringsFilter,
@@ -6375,27 +6519,27 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 2.5.13.9 NAME 'numericStringOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
-               SLAP_MR_ORDERING, NULL,
+               SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
                NULL, numericStringNormalize, octetStringOrderingMatch,
                NULL, NULL,
                "numericStringMatch" },
 
        {"( 2.5.13.10 NAME 'numericStringSubstringsMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
                SLAP_MR_SUBSTR, NULL,
                NULL, numericStringNormalize, octetStringSubstringsMatch,
                octetStringSubstringsIndexer, octetStringSubstringsFilter,
                "numericStringMatch" },
 
        {"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", /* Postal Address */
                SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
                NULL, postalAddressNormalize, octetStringMatch,
                octetStringIndexer, octetStringFilter,
                NULL },
 
        {"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
                SLAP_MR_SUBSTR, NULL,
                NULL, NULL, NULL, NULL, NULL,
                "caseIgnoreListMatch" },
@@ -6416,7 +6560,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 2.5.13.15 NAME 'integerOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
-               SLAP_MR_ORDERING | SLAP_MR_ORDERED_INDEX, NULL,
+               SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
                NULL, NULL, integerMatch,
                NULL, NULL,
                "integerMatch" },
@@ -6437,7 +6581,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 2.5.13.18 NAME 'octetStringOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
-               SLAP_MR_ORDERING, NULL,
+               SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
                NULL, NULL, octetStringOrderingMatch,
                NULL, NULL,
                "octetStringMatch" },
@@ -6458,7 +6602,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
                NULL },
 
        {"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
                SLAP_MR_SUBSTR, NULL,
                NULL, telephoneNumberNormalize, octetStringSubstringsMatch,
                octetStringSubstringsIndexer, octetStringSubstringsFilter,
@@ -6470,7 +6614,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
                NULL, NULL, NULL, NULL, NULL, NULL },
 
        {"( 2.5.13.23 NAME 'uniqueMemberMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )", /* Name And Optional UID */
                SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
                NULL, uniqueMemberNormalize, uniqueMemberMatch,
                uniqueMemberIndexer, uniqueMemberFilter,
@@ -6490,13 +6634,13 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
-               SLAP_MR_ORDERING | SLAP_MR_ORDERED_INDEX, NULL,
+               SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
                NULL, generalizedTimeNormalize, generalizedTimeOrderingMatch,
                NULL, NULL,
                "generalizedTimeMatch" },
 
        {"( 2.5.13.29 NAME 'integerFirstComponentMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
                SLAP_MR_EQUALITY | SLAP_MR_EXT,
                        integerFirstComponentMatchSyntaxes,
                NULL, firstComponentNormalize, integerMatch,
@@ -6504,7 +6648,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
                NULL },
 
        {"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", /* OID */
                SLAP_MR_EQUALITY | SLAP_MR_EXT,
                        objectIdentifierFirstComponentMatchSyntaxes,
                NULL, firstComponentNormalize, octetStringMatch,
@@ -6512,27 +6656,27 @@ static slap_mrule_defs_rec mrule_defs[] = {
                NULL },
 
        {"( 2.5.13.34 NAME 'certificateExactMatch' "
-               "SYNTAX 1.3.6.1.1.15.1 )",
+               "SYNTAX 1.3.6.1.1.15.1 )", /* Certificate Exact Assertion */
                SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes,
                NULL, certificateExactNormalize, octetStringMatch,
                octetStringIndexer, octetStringFilter,
                NULL },
 
        {"( 2.5.13.35 NAME 'certificateMatch' "
-               "SYNTAX 1.3.6.1.1.15.2 )",
+               "SYNTAX 1.3.6.1.1.15.2 )", /* Certificate Assertion */
                SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
                NULL, NULL, NULL, NULL, NULL,
                NULL },
 
        {"( 2.5.13.38 NAME 'certificateListExactMatch' "
-               "SYNTAX 1.3.6.1.1.15.5 )",
+               "SYNTAX 1.3.6.1.1.15.5 )", /* Certificate List Exact Assertion */
                SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateListExactMatchSyntaxes,
                NULL, certificateListExactNormalize, octetStringMatch,
                octetStringIndexer, octetStringFilter,
                NULL },
 
        {"( 2.5.13.39 NAME 'certificateListMatch' "
-               "SYNTAX 1.3.6.1.1.15.6 )",
+               "SYNTAX 1.3.6.1.1.15.6 )", /* Certificate List Assertion */
                SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
                NULL, NULL, NULL, NULL, NULL,
                NULL },
@@ -6581,7 +6725,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
 #ifdef SLAPD_AUTHPASSWD
        /* needs updating */
        {"( 1.3.6.1.4.1.4203.666.4.1 NAME 'authPasswordMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", /* Octet String */
                SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
                NULL, NULL, authPasswordMatch,
                NULL, NULL,
@@ -6589,14 +6733,14 @@ static slap_mrule_defs_rec mrule_defs[] = {
 #endif
 
        {"( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
                SLAP_MR_EXT, NULL,
                NULL, NULL, integerBitAndMatch,
                NULL, NULL,
                "integerMatch" },
 
        {"( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch' "
-               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
                SLAP_MR_EXT, NULL,
                NULL, NULL, integerBitOrMatch,
                NULL, NULL,
@@ -6625,8 +6769,8 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        {"( 1.3.6.1.4.1.4203.666.11.2.3 NAME 'CSNOrderingMatch' "
                "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
-               SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_ORDERED_INDEX, NULL,
-               NULL, NULL, csnOrderingMatch,
+               SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
+               NULL, csnNormalize, csnOrderingMatch,
                NULL, NULL,
                "CSNMatch" },
 
@@ -6639,7 +6783,7 @@ static slap_mrule_defs_rec mrule_defs[] = {
 
        /* FIXME: OID is unused, but not registered yet */
        {"( 1.3.6.1.4.1.4203.666.4.12 NAME 'authzMatch' "
-               "SYNTAX 1.3.6.1.4.1.4203.666.2.7 )",
+               "SYNTAX 1.3.6.1.4.1.4203.666.2.7 )", /* OpenLDAP authz */
                SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
                NULL, authzNormalize, authzMatch,
                NULL, NULL,