+ p = out->bv_val;
+
+ p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
+ p = lutil_strncopy( p, ni.bv_val, ni.bv_len );
+ p = lutil_strcopy( p, "\", thisUpdate \"" );
+ p = lutil_strncopy( p, tu2.bv_val, tu2.bv_len );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+func_leave:
+ Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateNormalize: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 );
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return rc;
+}
+
+static int
+certificateListExactNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t version;
+ struct berval issuer_dn = BER_BVNULL, bvdn,
+ thisUpdate, bvtu;
+ char *p, tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
+ int rc = LDAP_INVALID_SYNTAX;
+
+ assert( val != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> certificateListExactNormalize: <%p, %lu>\n",
+ val->bv_val, val->bv_len, 0 );
+
+ if ( BER_BVISEMPTY( val ) ) goto done;
+
+ if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
+ return issuerAndThisUpdateNormalize( 0, NULL, NULL, val, normalized, ctx );
+ }
+
+ assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
+
+ ber_init2( ber, val, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Signed wrapper */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_peek_tag( ber, &len );
+ /* Optional version */
+ if ( tag == LBER_INTEGER ) {
+ tag = ber_get_int( ber, &version );
+ assert( tag == LBER_INTEGER );
+ if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
+ }
+ tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_peek_tag( ber, &len ); /* IssuerDN */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ len = ber_ptrlen( ber );
+ bvdn.bv_val = val->bv_val + len;
+ bvdn.bv_len = val->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;
+
+ rc = dnX509normalize( &bvdn, &issuer_dn );
+ if ( rc != LDAP_SUCCESS ) goto done;
+
+ thisUpdate.bv_val = tubuf;
+ thisUpdate.bv_len = sizeof(tubuf);
+ if ( checkTime( &bvtu, &thisUpdate ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ normalized->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
+ + issuer_dn.bv_len + thisUpdate.bv_len;
+ normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
+
+ p = normalized->bv_val;
+
+ p = lutil_strcopy( p, "{ issuer rdnSequence:\"" );
+ p = lutil_strncopy( p, issuer_dn.bv_val, issuer_dn.bv_len );
+ p = lutil_strcopy( p, "\", thisUpdate \"" );
+ p = lutil_strncopy( p, thisUpdate.bv_val, thisUpdate.bv_len );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ rc = LDAP_SUCCESS;
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "<<< certificateListExactNormalize: <%p, %lu> => <%s>\n",
+ val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
+
+ if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
+
+ return rc;
+}
+
+/* X.509 PMI serialNumberAndIssuerSerialCheck
+
+AttributeCertificateExactAssertion ::= SEQUENCE {
+ serialNumber CertificateSerialNumber,
+ issuer AttCertIssuer }
+
+CertificateSerialNumber ::= INTEGER
+
+AttCertIssuer ::= [0] SEQUENCE {
+issuerName GeneralNames OPTIONAL,
+baseCertificateID [0] IssuerSerial OPTIONAL,
+objectDigestInfo [1] ObjectDigestInfo OPTIONAL }
+-- At least one component shall be present
+
+GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+GeneralName ::= CHOICE {
+ otherName [0] INSTANCE OF OTHER-NAME,
+ rfc822Name [1] IA5String,
+ dNSName [2] IA5String,
+ x400Address [3] ORAddress,
+ directoryName [4] Name,
+ ediPartyName [5] EDIPartyName,
+ uniformResourceIdentifier [6] IA5String,
+ iPAddress [7] OCTET STRING,
+ registeredID [8] OBJECT IDENTIFIER }
+
+IssuerSerial ::= SEQUENCE {
+ issuer GeneralNames,
+ serial CertificateSerialNumber,
+ issuerUID UniqueIdentifier OPTIONAL }
+
+ObjectDigestInfo ::= SEQUENCE {
+ digestedObjectType ENUMERATED {
+ publicKey (0),
+ publicKeyCert (1),
+ otherObjectTypes (2) },
+ otherObjectTypeID OBJECT IDENTIFIER OPTIONAL,
+ digestAlgorithm AlgorithmIdentifier,
+ objectDigest BIT STRING }
+
+ * The way I interpret it, an assertion should look like
+
+ { serialNumber 'dd'H,
+ issuer { issuerName { directoryName:rdnSequence:"cn=yyy" }, -- optional
+ baseCertificateID { serial '1d'H,
+ issuer { directoryName:rdnSequence:"cn=zzz" },
+ issuerUID <value> -- optional
+ }, -- optional
+ objectDigestInfo { ... } -- optional
+ }
+ }
+
+ * with issuerName, baseCertificateID and objectDigestInfo optional,
+ * at least one present; the way it's currently implemented, it is
+
+ { serialNumber 'dd'H,
+ issuer { baseCertificateID { serial '1d'H,
+ issuer { directoryName:rdnSequence:"cn=zzz" }
+ }
+ }
+ }
+
+ * with all the above parts mandatory.
+ */
+static int
+serialNumberAndIssuerSerialCheck(
+ struct berval *in,
+ struct berval *sn,
+ struct berval *is,
+ struct berval *i_sn, /* contain serial of baseCertificateID */
+ void *ctx )
+{
+ /* Parse GSER format */
+ enum {
+ HAVE_NONE = 0x0,
+ HAVE_SN = 0x1,
+ HAVE_ISSUER = 0x2,
+ HAVE_ALL = ( HAVE_SN | HAVE_ISSUER )
+ } have = HAVE_NONE, have2 = HAVE_NONE;
+ int numdquotes = 0;
+ struct berval x = *in;
+ struct berval ni;
+
+ if ( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
+
+ /* no old format */
+ if ( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) return LDAP_INVALID_SYNTAX;
+
+ x.bv_val++;
+ x.bv_len -= 2;
+
+ do {