X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fschema_init.c;h=bb027823ecf971351b06f02531f0ddc1e9b4e014;hb=40f785f62a209c0653340cc5d11e7a965df873ae;hp=6f218dcf95e81ce2b2f22a6b67af561274bbf48b;hpb=da6d9eb0463255782f3fa70c61fd958d94c048cf;p=openldap diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c index 6f218dcf95..bb027823ec 100644 --- a/servers/slapd/schema_init.c +++ b/servers/slapd/schema_init.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 1998-2007 The OpenLDAP Foundation. + * Copyright 1998-2009 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,21 +27,10 @@ #include #include "slap.h" +#include "../../libraries/liblber/lber-int.h" /* get ber_ptrlen() */ #include "ldap_utf8.h" -#ifdef HAVE_TLS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - #include "lutil.h" #include "lutil_hash.h" #define HASH_BYTES LUTIL_HASH_BYTES @@ -61,23 +50,63 @@ #define IA5StringApproxFilter approxFilter /* Change Sequence Number (CSN) - much of this will change */ -#define csnValidate blobValidate #define csnMatch octetStringMatch #define csnOrderingMatch octetStringOrderingMatch #define csnIndexer generalizedTimeIndexer #define csnFilter generalizedTimeFilter -/* FIXME: temporary */ #define authzMatch octetStringMatch +/* X.509 PMI ldapSyntaxes */ +/* FIXME: need to create temporary OIDs under OpenLDAP's arc; + * these are currently hijacked + * + * 1.3.6.1.4.1.4203.666 OpenLDAP + * 1.3.6.1.4.1.4203.666.11 self-contained works + * 1.3.6.1.4.1.4203.666.11.10 X.509 PMI + * 1.3.6.1.4.1.4203.666.11.10.2 X.509 PMI ldapSyntaxes + * 1.3.6.1.4.1.4203.666.11.10.2.1 AttributeCertificate (supported) + * 1.3.6.1.4.1.4203.666.11.10.2.2 AttributeCertificateExactAssertion (supported) + * 1.3.6.1.4.1.4203.666.11.10.2.3 AttributeCertificateAssertion (not supported) + * 1.3.6.1.4.1.4203.666.11.10.2.4 AttCertPath (X-SUBST'ed right now in pmi.schema) + * 1.3.6.1.4.1.4203.666.11.10.2.5 PolicySyntax (X-SUBST'ed right now in pmi.schema) + * 1.3.6.1.4.1.4203.666.11.10.2.6 RoleSyntax (X-SUBST'ed right now in pmi.schema) + */ +#if 0 /* from (expired) */ +#define attributeCertificateSyntaxOID "1.2.826.0.1.3344810.7.5" +#define attributeCertificateExactAssertionSyntaxOID "1.2.826.0.1.3344810.7.6" +#define attributeCertificateAssertionSyntaxOID "1.2.826.0.1.3344810.7.7" +#else /* from OpenLDAP's experimental oid arc */ +#define X509_PMI_SyntaxOID "1.3.6.1.4.1.4203.666.11.10.2" +#define attributeCertificateSyntaxOID X509_PMI_SyntaxOID ".1" +#define attributeCertificateExactAssertionSyntaxOID X509_PMI_SyntaxOID ".2" +#define attributeCertificateAssertionSyntaxOID X509_PMI_SyntaxOID ".3" +#endif + unsigned int index_substr_if_minlen = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT; unsigned int index_substr_if_maxlen = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT; unsigned int index_substr_any_len = SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT; unsigned int index_substr_any_step = SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT; +unsigned int index_intlen = SLAP_INDEX_INTLEN_DEFAULT; +unsigned int index_intlen_strlen = SLAP_INDEX_INTLEN_STRLEN( + SLAP_INDEX_INTLEN_DEFAULT ); + ldap_pvt_thread_mutex_t ad_undef_mutex; ldap_pvt_thread_mutex_t oc_undef_mutex; +static int +generalizedTimeValidate( + Syntax *syntax, + struct berval *in ); + +#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX +static int +utcTimeValidate( + Syntax *syntax, + struct berval *in ); +#endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */ + static int inValidate( Syntax *syntax, @@ -109,21 +138,290 @@ sequenceValidate( return LDAP_SUCCESS; } +/* X.509 related stuff */ + +enum { + SLAP_X509_V1 = 0, + SLAP_X509_V2 = 1, + SLAP_X509_V3 = 2 +}; + +enum { + SLAP_TAG_UTCTIME = 0x17U, + SLAP_TAG_GENERALIZEDTIME = 0x18U +}; + + +#define SLAP_X509_OPTION (LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + +enum { + SLAP_X509_OPT_C_VERSION = SLAP_X509_OPTION + 0, + 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 +}; + +enum { + SLAP_X509_OPT_CL_CRLEXTENSIONS = SLAP_X509_OPTION + 0 +}; + +/* +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 } +*/ +enum { + SLAP_X509_GN_OTHERNAME = SLAP_X509_OPTION + 0, + SLAP_X509_GN_RFC822NAME = SLAP_X509_OPTION + 1, + SLAP_X509_GN_DNSNAME = SLAP_X509_OPTION + 2, + SLAP_X509_GN_X400ADDRESS = SLAP_X509_OPTION + 3, + SLAP_X509_GN_DIRECTORYNAME = SLAP_X509_OPTION + 4, + SLAP_X509_GN_EDIPARTYNAME = SLAP_X509_OPTION + 5, + SLAP_X509_GN_URI = SLAP_X509_OPTION + 6, + SLAP_X509_GN_IPADDRESS = SLAP_X509_OPTION + 7, + SLAP_X509_GN_REGISTEREDID = SLAP_X509_OPTION + 8 +}; + +/* X.509 PMI related stuff */ +enum { + SLAP_X509AC_V1 = 0, + SLAP_X509AC_V2 = 1 +}; + +enum { + SLAP_X509AC_ISSUER = SLAP_X509_OPTION + 0 +}; -#ifdef HAVE_TLS -static int certificateValidate( Syntax *syntax, struct berval *in ) +/* X.509 certificate validation */ +static int +certificateValidate( Syntax *syntax, struct berval *in ) { - X509 *xcert=NULL; - unsigned char *p = (unsigned char *)in->bv_val; - - xcert = d2i_X509(NULL, &p, in->bv_len); - if ( !xcert ) return LDAP_INVALID_SYNTAX; - X509_free(xcert); + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t version = SLAP_X509_V1; + + ber_init2( ber, in, 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 == SLAP_X509_OPT_C_VERSION ) { + tag = ber_skip_tag( ber, &len ); + tag = ber_get_int( ber, &version ); + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + } + /* NOTE: don't try to parse Serial, because it might be longer + * than sizeof(ber_int_t); deferred to certificateExactNormalize() */ + tag = ber_skip_tag( ber, &len ); /* Serial */ + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + 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 */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Validity */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Subject DN */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Subject PublicKeyInfo */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + if ( tag == SLAP_X509_OPT_C_ISSUERUNIQUEID ) { /* issuerUniqueID */ + if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + if ( tag == SLAP_X509_OPT_C_SUBJECTUNIQUEID ) { /* subjectUniqueID */ + if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + if ( tag == SLAP_X509_OPT_C_EXTENSIONS ) { /* Extensions */ + if ( version < SLAP_X509_V3 ) return LDAP_INVALID_SYNTAX; + tag = ber_skip_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + /* signatureAlgorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Signature */ + if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Must be at end now */ + if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX; + return LDAP_SUCCESS; +} + +/* X.509 certificate list validation */ +static int +certificateListValidate( Syntax *syntax, struct berval *in ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t version = SLAP_X509_V1; + + ber_init2( ber, in, 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_skip_tag( ber, &len ); /* Issuer DN */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + 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; + ber_skip_data( ber, len ); + /* Optional nextUpdate */ + tag = ber_skip_tag( ber, &len ); + if ( tag == SLAP_TAG_UTCTIME || tag == SLAP_TAG_GENERALIZEDTIME ) { + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + /* 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 ); + tag = ber_skip_tag( ber, &len ); + } + } + /* Optional Extensions */ + if ( tag == SLAP_X509_OPT_CL_CRLEXTENSIONS ) { /* ? */ + if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + tag = ber_skip_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + /* signatureAlgorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Signature */ + if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Must be at end now */ + if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX; + return LDAP_SUCCESS; +} + +/* X.509 PMI Attribute Certificate Validate */ +static int +attributeCertificateValidate( Syntax *syntax, struct berval *in ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t version; + int cont = 0; + + ber_init2( ber, in, 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 ); /* Version */ + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + tag = ber_get_int( ber, &version ); /* X.509 only allows v2 */ + if ( version != SLAP_X509AC_V2 ) return LDAP_INVALID_SYNTAX; + + tag = ber_skip_tag( ber, &len ); /* Holder */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Issuer */ + if ( tag != SLAP_X509AC_ISSUER ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Signature */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Serial number */ + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* AttCertValidityPeriod */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Attributes */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + ber_peek_tag( ber, &len ); + + if ( tag == LBER_BITSTRING ) { /* issuerUniqueID */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_SEQUENCE ) { /* extensions or signatureAlgorithm */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + cont++; + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_SEQUENCE ) { /* signatureAlgorithm */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + cont++; + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_BITSTRING ) { /* Signature */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + cont++; + tag = ber_peek_tag( ber, &len ); + } + + /* Must be at end now */ + if ( len != 0 || tag != LBER_DEFAULT || cont < 2 ) return LDAP_INVALID_SYNTAX; + return LDAP_SUCCESS; } -#else -#define certificateValidate sequenceValidate -#endif int octetStringMatch( @@ -145,7 +443,7 @@ octetStringMatch( return LDAP_SUCCESS; } -static int +int octetStringOrderingMatch( int *matchp, slap_mask_t flags, @@ -711,8 +1009,8 @@ bitStringValidate( } /* RFC 4517 Section 3.3.2 Bit String: - * BitString = SQUOTE *binary-digit SQUOTE "B" - * binary-digit = "0" / "1" + * BitString = SQUOTE *binary-digit SQUOTE "B" + * binary-digit = "0" / "1" * * where SQUOTE [RFC4512] is * SQUOTE = %x27 ; single quote ("'") @@ -807,12 +1105,7 @@ bitStringValidate( ... * - * Note: normalization strips any leading "0"s, unless the - * bit string is exactly "'0'B", so the normalized example, - * in slapd, would result in - * - * 1.3.6.1.4.1.1466.0=#04024869,o=test,c=gb#'101'B - * + * Note: * RFC 4514 clarifies that SHARP, i.e. "#", doesn't have to * be escaped except when at the beginning of a value, the * definition of Name and Optional UID appears to be flawed, @@ -836,11 +1129,11 @@ bitStringValidate( * * in fact "com#'1'B" is a valid IA5 string. * - * As a consequence, current slapd code assumes that the - * presence of portions of a BitString at the end of the string - * representation of a NameAndOptionalUID means a BitString - * is expected, and cause an error otherwise. This is quite - * arbitrary, and might change in the future. + * As a consequence, current slapd code takes the presence of + * # at the end of the string representation + * of a NameAndOptionalUID to mean this is indeed a BitString. + * This is quite arbitrary - it has changed the past and might + * change in the future. */ @@ -911,7 +1204,8 @@ nameUIDPretty( if ( rc == LDAP_SUCCESS ) { ber_dupbv_x( &dnval, val, ctx ); - dnval.bv_len -= uidval.bv_len + 1; + uidval.bv_val--; + dnval.bv_len -= ++uidval.bv_len; dnval.bv_val[dnval.bv_len] = '\0'; } else { @@ -928,36 +1222,18 @@ nameUIDPretty( } if( !BER_BVISNULL( &uidval ) ) { - int i, c, got1; char *tmp; tmp = slap_sl_realloc( out->bv_val, out->bv_len - + STRLENOF( "#" ) + uidval.bv_len + 1, + + uidval.bv_len + 1, ctx ); if( tmp == NULL ) { ber_memfree_x( out->bv_val, ctx ); return LDAP_OTHER; } out->bv_val = tmp; - out->bv_val[out->bv_len++] = '#'; - out->bv_val[out->bv_len++] = '\''; - - got1 = uidval.bv_len < sizeof("'0'B"); - for( i = 1; i < uidval.bv_len - 2; i++ ) { - c = uidval.bv_val[i]; - switch(c) { - case '0': - if( got1 ) out->bv_val[out->bv_len++] = c; - break; - case '1': - got1 = 1; - out->bv_val[out->bv_len++] = c; - break; - } - } - - out->bv_val[out->bv_len++] = '\''; - out->bv_val[out->bv_len++] = 'B'; + memcpy( out->bv_val + out->bv_len, uidval.bv_val, uidval.bv_len ); + out->bv_len += uidval.bv_len; out->bv_val[out->bv_len] = '\0'; } } @@ -1311,12 +1587,11 @@ PrintableString In ASN.1, Printable string is just a string of printable characters and can be empty. In X.500, semantics much like NumericString (see serialNumber for a like example) excepting uses insignificant space - handling instead of ignore all spaces. + handling instead of ignore all spaces. They must be non-empty. IA5String Basically same as PrintableString. There are no examples in X.500, - but same logic applies. So we require them to be non-empty as - well. + but same logic applies. Empty strings are allowed. -------------------------------------------------------------------*/ @@ -1389,8 +1664,8 @@ UTF8StringNormalize( void *ctx ) { struct berval tmp, nvalue; - int flags; - int i, wasspace; + int flags, wasspace; + ber_len_t i; assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 ); @@ -1465,7 +1740,7 @@ directoryStringSubstringsMatch( int match = 0; SubstringsAssertion *sub = assertedValue; struct berval left = *value; - int i; + ber_len_t i; int priorspace=0; if ( !BER_BVISNULL( &sub->sa_initial ) ) { @@ -1638,7 +1913,7 @@ approxMatch( } /* Work through the asserted value's words, to see if at least some - of the words are there, in the same order. */ + * of the words are there, in the same order. */ len = 0; while ( (ber_len_t) nextchunk < assertv->bv_len ) { len = strcspn( assertv->bv_val + nextchunk, SLAPD_APPROX_DELIMITER); @@ -1848,6 +2123,119 @@ telephoneNumberNormalize( return LDAP_SUCCESS; } +static int +postalAddressValidate( + Syntax *syntax, + struct berval *in ) +{ + struct berval bv = *in; + ber_len_t c; + + for ( c = 0; c < in->bv_len; c++ ) { + if ( in->bv_val[c] == '\\' ) { + c++; + if ( strncasecmp( &in->bv_val[c], "24", STRLENOF( "24" ) ) != 0 + && strncasecmp( &in->bv_val[c], "5C", STRLENOF( "5C" ) ) != 0 ) + { + return LDAP_INVALID_SYNTAX; + } + continue; + } + + if ( in->bv_val[c] == '$' ) { + bv.bv_len = &in->bv_val[c] - bv.bv_val; + if ( UTF8StringValidate( NULL, &bv ) != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + bv.bv_val = &in->bv_val[c] + 1; + } + } + + bv.bv_len = &in->bv_val[c] - bv.bv_val; + return UTF8StringValidate( NULL, &bv ); +} + +static int +postalAddressNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + BerVarray lines = NULL, nlines = NULL; + ber_len_t l, c; + int rc = LDAP_SUCCESS; + MatchingRule *xmr = NULL; + char *p; + + if ( SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseIgnoreListMatch ) ) { + xmr = slap_schema.si_mr_caseIgnoreMatch; + + } else { + xmr = slap_schema.si_mr_caseExactMatch; + } + + for ( l = 0, c = 0; c < val->bv_len; c++ ) { + if ( val->bv_val[c] == '$' ) { + l++; + } + } + + lines = slap_sl_calloc( sizeof( struct berval ), 2 * ( l + 2 ), ctx ); + nlines = &lines[l + 2]; + + lines[0].bv_val = val->bv_val; + for ( l = 0, c = 0; c < val->bv_len; c++ ) { + if ( val->bv_val[c] == '$' ) { + lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val; + l++; + lines[l].bv_val = &val->bv_val[c + 1]; + } + } + lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val; + + normalized->bv_len = l; + + for ( l = 0; !BER_BVISNULL( &lines[l] ); l++ ) { + /* NOTE: we directly normalize each line, + * without unescaping the values, since the special + * values '\24' ('$') and '\5C' ('\') are not affected + * by normalization */ + rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx ); + if ( rc != LDAP_SUCCESS ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + normalized->bv_len += nlines[l].bv_len; + } + + 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 ); + + *p++ = '$'; + } + *--p = '\0'; + + assert( p == &normalized->bv_val[normalized->bv_len] ); + +done:; + if ( nlines != NULL ) { + for ( l = 0; !BER_BVISNULL( &nlines[ l ] ); l++ ) { + slap_sl_free( nlines[l].bv_val, ctx ); + } + + slap_sl_free( lines, ctx ); + } + + return rc; +} + int numericoidValidate( Syntax *syntax, @@ -1974,7 +2362,188 @@ integerMatch( *matchp = match; return LDAP_SUCCESS; } - + +/* 10**Chop < 256**Chopbytes and Chop > Chopbytes<<1 (for sign bit and itmp) */ +#define INDEX_INTLEN_CHOP 7 +#define INDEX_INTLEN_CHOPBYTES 3 + +static int +integerVal2Key( + struct berval *in, + struct berval *key, + struct berval *tmp, + void *ctx ) +{ + /* index format: + * only if too large: one's complement , + * two's complement value (sign-extended or chopped as needed), + * however the top bits of first byte + * above is the inverse sign. The next bit is the sign as delimiter. + */ + ber_slen_t k = index_intlen_strlen; + ber_len_t chop = 0; + unsigned signmask = ~0x7fU; + unsigned char lenbuf[sizeof(k) + 2], *lenp, neg = 0xff; + struct berval val = *in, itmp = *tmp; + + if ( val.bv_val[0] != '-' ) { + neg = 0; + --k; + } + + /* Chop least significant digits, increase length instead */ + if ( val.bv_len > (ber_len_t) k ) { + chop = (val.bv_len-k+2)/INDEX_INTLEN_CHOP; /* 2 fewer digits */ + val.bv_len -= chop * INDEX_INTLEN_CHOP; /* #digits chopped */ + chop *= INDEX_INTLEN_CHOPBYTES; /* #bytes added */ + } + + if ( lutil_str2bin( &val, &itmp, ctx )) { + return LDAP_INVALID_SYNTAX; + } + + /* Omit leading sign byte */ + if ( itmp.bv_val[0] == neg ) { + itmp.bv_val++; + itmp.bv_len--; + } + + k = (ber_slen_t) index_intlen - (ber_slen_t) (itmp.bv_len + chop); + if ( k > 0 ) { + assert( chop == 0 ); + memset( key->bv_val, neg, k ); /* sign-extend */ + } else if ( k != 0 || ((itmp.bv_val[0] ^ neg) & 0xc0) ) { + lenp = lenbuf + sizeof(lenbuf); + chop = - (ber_len_t) k; + do { + *--lenp = ((unsigned char) chop & 0xff) ^ neg; + 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. */ + k = (lenbuf + sizeof(lenbuf)) - lenp; + if ( k > (ber_slen_t) index_intlen ) + k = index_intlen; + memcpy( key->bv_val, lenp, k ); + itmp.bv_len = index_intlen - k; + } + memcpy( key->bv_val + k, itmp.bv_val, itmp.bv_len ); + key->bv_val[0] ^= (unsigned char) signmask & 0xff; /* invert sign */ + return 0; +} + +/* Index generation function */ +static int +integerIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + char ibuf[64]; + struct berval itmp; + BerVarray keys; + ber_len_t vlen; + int i, rc; + unsigned maxstrlen = index_intlen_strlen + INDEX_INTLEN_CHOP-1; + + /* count the values and find max needed length */ + vlen = 0; + for( i = 0; !BER_BVISNULL( &values[i] ); i++ ) { + if ( vlen < values[i].bv_len ) + vlen = values[i].bv_len; + } + if ( vlen > maxstrlen ) + vlen = maxstrlen; + + /* we should have at least one value at this point */ + assert( i > 0 ); + + keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx ); + for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) { + keys[i].bv_len = index_intlen; + keys[i].bv_val = slap_sl_malloc( index_intlen, ctx ); + } + keys[i].bv_len = 0; + keys[i].bv_val = NULL; + + if ( vlen > sizeof(ibuf) ) { + itmp.bv_val = slap_sl_malloc( vlen, ctx ); + } else { + itmp.bv_val = ibuf; + } + itmp.bv_len = sizeof(ibuf); + + for ( i=0; !BER_BVISNULL( &values[i] ); i++ ) { + if ( itmp.bv_val != ibuf ) { + itmp.bv_len = values[i].bv_len; + if ( itmp.bv_len <= sizeof(ibuf) ) + itmp.bv_len = sizeof(ibuf); + else if ( itmp.bv_len > maxstrlen ) + itmp.bv_len = maxstrlen; + } + rc = integerVal2Key( &values[i], &keys[i], &itmp, ctx ); + if ( rc ) + goto func_leave; + } + *keysp = keys; +func_leave: + if ( itmp.bv_val != ibuf ) { + slap_sl_free( itmp.bv_val, ctx ); + } + return rc; +} + +/* Index generation function */ +static int +integerFilter( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx ) +{ + char ibuf[64]; + struct berval iv; + BerVarray keys; + struct berval *value; + int rc; + + value = (struct berval *) assertedValue; + + keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx ); + + keys[0].bv_len = index_intlen; + keys[0].bv_val = slap_sl_malloc( index_intlen, ctx ); + keys[1].bv_len = 0; + keys[1].bv_val = NULL; + + iv.bv_len = value->bv_len < index_intlen_strlen + INDEX_INTLEN_CHOP-1 + ? value->bv_len : index_intlen_strlen + INDEX_INTLEN_CHOP-1; + if ( iv.bv_len > (int) sizeof(ibuf) ) { + iv.bv_val = slap_sl_malloc( iv.bv_len, ctx ); + } else { + iv.bv_val = ibuf; + iv.bv_len = sizeof(ibuf); + } + + rc = integerVal2Key( value, keys, &iv, ctx ); + if ( rc == 0 ) + *keysp = keys; + + if ( iv.bv_val != ibuf ) { + slap_sl_free( iv.bv_val, ctx ); + } + return rc; +} + static int countryStringValidate( Syntax *syntax, @@ -2049,8 +2618,6 @@ IA5StringValidate( { ber_len_t i; - if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX; - for(i=0; i < val->bv_len; i++) { if( !LDAP_ASCII(val->bv_val[i]) ) { return LDAP_INVALID_SYNTAX; @@ -2073,8 +2640,6 @@ IA5StringNormalize( int casefold = !SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseExactIA5Match ); - assert( !BER_BVISEMPTY( val ) ); - assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 ); p = val->bv_val; @@ -2082,7 +2647,11 @@ IA5StringNormalize( /* Ignore initial whitespace */ while ( ASCII_SPACE( *p ) ) p++; - normalized->bv_val = ber_strdup_x( p, ctx ); + normalized->bv_len = val->bv_len - ( p - val->bv_val ); + normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx ); + AC_MEMCPY( normalized->bv_val, p, normalized->bv_len ); + normalized->bv_val[normalized->bv_len] = '\0'; + p = q = normalized->bv_val; while ( *p ) { @@ -2111,18 +2680,12 @@ IA5StringNormalize( * position. One is enough because the above loop collapsed * all whitespace to a single space. */ - if ( ASCII_SPACE( q[-1] ) ) --q; + if ( q > normalized->bv_val && ASCII_SPACE( q[-1] ) ) --q; /* null terminate */ *q = '\0'; normalized->bv_len = q - normalized->bv_val; - if( BER_BVISEMPTY( normalized ) ) { - normalized->bv_val = slap_sl_realloc( normalized->bv_val, 2, ctx ); - normalized->bv_val[0] = ' '; - normalized->bv_val[1] = '\0'; - normalized->bv_len = 1; - } return LDAP_SUCCESS; } @@ -2219,6 +2782,19 @@ UUIDNormalize( unsigned char octet = '\0'; int i; int j; + + if ( SLAP_MR_IS_DENORMALIZE( usage ) ) { + /* NOTE: must be a normalized UUID */ + assert( val->bv_len == 16 ); + + normalized->bv_val = slap_sl_malloc( LDAP_LUTIL_UUIDSTR_BUFSIZE, ctx ); + normalized->bv_len = lutil_uuidstr_from_normalized( val->bv_val, + val->bv_len, normalized->bv_val, LDAP_LUTIL_UUIDSTR_BUFSIZE ); + assert( normalized->bv_len == STRLENOF( "BADBADBA-DBAD-0123-4567-BADBADBADBAD" ) ); + + return LDAP_SUCCESS; + } + normalized->bv_len = 16; normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx ); @@ -2238,6 +2814,7 @@ UUIDNormalize( } else { slap_sl_free( normalized->bv_val, ctx ); + BER_BVZERO( normalized ); return LDAP_INVALID_SYNTAX; } @@ -2256,7 +2833,7 @@ UUIDNormalize( -static int +int numericStringValidate( Syntax *syntax, struct berval *in ) @@ -2302,7 +2879,7 @@ numericStringNormalize( } } - /* we should have copied no more then is in val */ + /* we should have copied no more than is in val */ assert( (q - normalized->bv_val) <= (p - val->bv_val) ); /* null terminate */ @@ -2393,246 +2970,269 @@ integerBitOrMatch( } static int -serialNumberAndIssuerValidate( - Syntax *syntax, - struct berval *in ) +checkNum( struct berval *in, struct berval *out ) { - int rc; - ber_len_t n; - struct berval sn, i; + /* parse serialNumber */ + ber_len_t neg = 0, extra = 0; + char first = '\0'; - Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n", - in->bv_val, 0, 0 ); + out->bv_val = in->bv_val; + out->bv_len = 0; - if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX; + if ( out->bv_val[0] == '-' ) { + neg++; + out->bv_len++; + } - if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) { - /* Parse old format */ - i.bv_val = ber_bvchr( in, '$' ); - if( BER_BVISNULL( &i ) ) return LDAP_INVALID_SYNTAX; + if ( strncasecmp( out->bv_val, "0x", STRLENOF("0x") ) == 0 ) { + first = out->bv_val[2]; + extra = 2; - sn.bv_val = in->bv_val; - sn.bv_len = i.bv_val - in->bv_val; + out->bv_len += STRLENOF("0x"); + for ( ; out->bv_len < in->bv_len; out->bv_len++ ) { + if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break; + } - i.bv_val++; - i.bv_len = in->bv_len - (sn.bv_len + 1); + } else if ( out->bv_val[0] == '\'' ) { + first = out->bv_val[1]; + extra = 3; - /* eat leading zeros */ - for( n=0; n < (sn.bv_len-1); n++ ) { - if( sn.bv_val[n] != '0' ) break; - } - sn.bv_val += n; - sn.bv_len -= n; + out->bv_len += STRLENOF("'"); - for( n=0; n < sn.bv_len; n++ ) { - if( !ASCII_DIGIT(sn.bv_val[n]) ) return LDAP_INVALID_SYNTAX; + for ( ; out->bv_len < in->bv_len; out->bv_len++ ) { + if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break; + } + if ( strncmp( &out->bv_val[out->bv_len], "'H", STRLENOF("'H") ) != 0 ) { + return -1; } + out->bv_len += STRLENOF("'H"); } else { - /* Parse GSER format */ - int havesn=0,haveissuer=0; - struct berval x = *in; - x.bv_val++; - x.bv_len-=2; - - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; + first = out->bv_val[0]; + for ( ; out->bv_len < in->bv_len; out->bv_len++ ) { + if ( !ASCII_DIGIT( out->bv_val[out->bv_len] ) ) break; } + } - if ( x.bv_len < STRLENOF("serialNumber 0,issuer \"\"")) { - return LDAP_INVALID_SYNTAX; - } + if ( !( out->bv_len > neg ) ) { + return -1; + } - /* should be at issuer or serialNumber NamedValue */ - if( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer")) == 0 ) { - /* parse issuer */ - x.bv_val += STRLENOF("issuer"); - x.bv_len -= STRLENOF("issuer"); + if ( ( out->bv_len > extra + 1 + neg ) && ( first == '0' ) ) { + return -1; + } - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + return 0; +} - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; +static int +serialNumberAndIssuerCheck( + struct berval *in, + struct berval *sn, + struct berval *is, + void *ctx ) +{ + ber_len_t n; - i.bv_val = x.bv_val; - i.bv_len = 0; + if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX; - for( ; i.bv_len < x.bv_len; ) { - if ( i.bv_val[i.bv_len] != '"' ) { - i.bv_len++; - continue; - } - if ( i.bv_val[i.bv_len+1] == '"' ) { - /* double dquote */ - i.bv_len+=2; - continue; - } - break; - } - x.bv_val += i.bv_len+1; - x.bv_len -= i.bv_len+1; + if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) { + /* Parse old format */ + is->bv_val = ber_bvchr( in, '$' ); + if( BER_BVISNULL( is ) ) return LDAP_INVALID_SYNTAX; - if ( x.bv_len < STRLENOF(",serialNumber 0")) { - return LDAP_INVALID_SYNTAX; - } + sn->bv_val = in->bv_val; + sn->bv_len = is->bv_val - in->bv_val; - haveissuer++; + is->bv_val++; + is->bv_len = in->bv_len - (sn->bv_len + 1); - } else if( strncasecmp( x.bv_val, "serialNumber", - STRLENOF("serialNumber")) == 0 ) - { - /* parse serialNumber */ - int neg=0; - x.bv_val += STRLENOF("serialNumber"); - x.bv_len -= STRLENOF("serialNumber"); + /* eat leading zeros */ + for( n=0; n < (sn->bv_len-1); n++ ) { + if( sn->bv_val[n] != '0' ) break; + } + sn->bv_val += n; + sn->bv_len -= n; + + for( n=0; n < sn->bv_len; n++ ) { + if( !ASCII_DIGIT(sn->bv_val[n]) ) return LDAP_INVALID_SYNTAX; + } - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + } else { + /* Parse GSER format */ + enum { + HAVE_NONE = 0x0, + HAVE_ISSUER = 0x1, + HAVE_SN = 0x2, + HAVE_ALL = ( HAVE_ISSUER | HAVE_SN ) + } have = HAVE_NONE; + + int numdquotes = 0; + struct berval x = *in; + struct berval ni; + x.bv_val++; + x.bv_len -= 2; + do { /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { /* empty */; } - - sn.bv_val = x.bv_val; - sn.bv_len = 0; - - if( sn.bv_val[0] == '-' ) { - neg++; - sn.bv_len++; - } - for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) { - if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break; - } + /* should be at issuer or serialNumber NamedValue */ + if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) { + if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX; - if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX; - if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) { - return LDAP_INVALID_SYNTAX; - } + /* parse issuer */ + x.bv_val += STRLENOF("issuer"); + x.bv_len -= STRLENOF("issuer"); - x.bv_val += sn.bv_len; x.bv_len -= sn.bv_len; + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; - if ( x.bv_len < STRLENOF( ",issuer \"\"" )) { - return LDAP_INVALID_SYNTAX; - } + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } - havesn++; + /* For backward compatibility, this part is optional */ + if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) == 0 ) { + x.bv_val += STRLENOF("rdnSequence:"); + x.bv_len -= STRLENOF("rdnSequence:"); + } - } else return LDAP_INVALID_SYNTAX; + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; - if( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + is->bv_val = x.bv_val; + is->bv_len = 0; - /* eat spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } + for ( ; is->bv_len < x.bv_len; ) { + if ( is->bv_val[is->bv_len] != '"' ) { + is->bv_len++; + continue; + } + if ( is->bv_val[is->bv_len+1] == '"' ) { + /* double dquote */ + is->bv_len += 2; + continue; + } + break; + } + x.bv_val += is->bv_len + 1; + x.bv_len -= is->bv_len + 1; - /* should be at remaining NamedValue */ - if( !haveissuer && (strncasecmp( x.bv_val, "issuer", - STRLENOF("issuer" )) == 0 )) - { - /* parse issuer */ - x.bv_val += STRLENOF("issuer"); - x.bv_len -= STRLENOF("issuer"); + have |= HAVE_ISSUER; - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + } else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) + { + if ( have & HAVE_SN ) return LDAP_INVALID_SYNTAX; - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + /* parse serialNumber */ + x.bv_val += STRLENOF("serialNumber"); + x.bv_len -= STRLENOF("serialNumber"); - i.bv_val = x.bv_val; - i.bv_len = 0; + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; - for( ; i.bv_len < x.bv_len; ) { - if ( i.bv_val[i.bv_len] != '"' ) { - i.bv_len++; - continue; + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; } - if ( i.bv_val[i.bv_len+1] == '"' ) { - /* double dquote */ - i.bv_len+=2; - continue; + + if ( checkNum( &x, sn ) ) { + return LDAP_INVALID_SYNTAX; } - break; - } - x.bv_val += i.bv_len+1; - x.bv_len -= i.bv_len+1; - } else if( !havesn && (strncasecmp( x.bv_val, "serialNumber", - STRLENOF("serialNumber")) == 0 )) - { - /* parse serialNumber */ - int neg=0; - x.bv_val += STRLENOF("serialNumber"); - x.bv_len -= STRLENOF("serialNumber"); + x.bv_val += sn->bv_len; + x.bv_len -= sn->bv_len; - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + have |= HAVE_SN; - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len ; x.bv_val++, x.bv_len--) { - /* empty */; + } else { + return LDAP_INVALID_SYNTAX; } - - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; - sn.bv_val = x.bv_val; - sn.bv_len = 0; - - if( sn.bv_val[0] == '-' ) { - neg++; - sn.bv_len++; + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; } - for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) { - if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break; + if ( have == HAVE_ALL ) { + break; } - if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX; - if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) { + if ( x.bv_val[0] != ',' ) { return LDAP_INVALID_SYNTAX; } - x.bv_val += sn.bv_len; - x.bv_len -= sn.bv_len; + x.bv_val++; + x.bv_len--; + } while ( 1 ); - } else return LDAP_INVALID_SYNTAX; + /* should have no characters left... */ + if ( x.bv_len ) return LDAP_INVALID_SYNTAX; - /* eat trailing spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; + if ( numdquotes == 0 ) { + ber_dupbv_x( &ni, is, ctx ); + + } else { + ber_len_t src, dst; + + ni.bv_len = is->bv_len - numdquotes; + ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx ); + for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) { + if ( is->bv_val[src] == '"' ) { + src++; + } + ni.bv_val[dst] = is->bv_val[src]; + } + ni.bv_val[dst] = '\0'; } + + *is = ni; + } - /* should have no characters left... */ - if( x.bv_len ) return LDAP_INVALID_SYNTAX; + return 0; +} + +static int +serialNumberAndIssuerValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval sn, i; + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerCheck( in, &sn, &i, NULL ); + if ( rc ) { + goto done; } /* validate DN -- doesn't handle double dquote */ rc = dnValidate( NULL, &i ); - if( rc ) return LDAP_INVALID_SYNTAX; + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + } - Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: OKAY\n", - in->bv_val, 0, 0 ); - return LDAP_SUCCESS; + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, NULL ); + } + + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: <%s> err=%d\n", + in->bv_val, rc, 0 ); + +done:; + return rc; } -int +static int serialNumberAndIssuerPretty( Syntax *syntax, struct berval *in, @@ -2640,292 +3240,652 @@ serialNumberAndIssuerPretty( void *ctx ) { int rc; - ber_len_t n; - struct berval sn, i, ni; + struct berval sn, i, ni = BER_BVNULL; + char *p; assert( in != NULL ); assert( out != NULL ); + BER_BVZERO( out ); + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerPretty: <%s>\n", in->bv_val, 0, 0 ); - if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX; + rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx ); + if ( rc ) { + goto done; + } - if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) { - /* Parse old format */ - i.bv_val = ber_bvchr( in, '$' ); - if( BER_BVISNULL( &i ) ) return LDAP_INVALID_SYNTAX; + rc = dnPretty( syntax, &i, &ni, ctx ); - sn.bv_val = in->bv_val; - sn.bv_len = i.bv_val - in->bv_val; + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } - i.bv_val++; - i.bv_len = in->bv_len - (sn.bv_len + 1); + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } - /* eat leading zeros */ - for( n=0; n < (sn.bv_len-1); n++ ) { - if( sn.bv_val[n] != '0' ) break; - } - sn.bv_val += n; - sn.bv_len -= n; + /* make room from sn + "$" */ + out->bv_len = STRLENOF("{ serialNumber , issuer rdnSequence:\"\" }") + + sn.bv_len + ni.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); - for( n=0; n < sn.bv_len; n++ ) { - if( !ASCII_DIGIT(sn.bv_val[n]) ) return LDAP_INVALID_SYNTAX; - } + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto done; + } - } else { - /* Parse GSER format */ - int havesn=0,haveissuer=0; - struct berval x = *in; - x.bv_val++; - x.bv_len-=2; + p = out->bv_val; + p = lutil_strcopy( p, "{ serialNumber " /*}*/ ); + p = lutil_strncopy( p, sn.bv_val, sn.bv_len ); + p = lutil_strcopy( p, ", issuer rdnSequence:\"" ); + p = lutil_strncopy( p, ni.bv_val, ni.bv_len ); + p = lutil_strcopy( p, /*{*/ "\" }" ); - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } + assert( p == &out->bv_val[out->bv_len] ); - if ( x.bv_len < STRLENOF("serialNumber 0,issuer \"\"")) { - return LDAP_INVALID_SYNTAX; - } +done:; + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); - /* should be at issuer or serialNumber NamedValue */ - if( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer")) == 0 ) { - /* parse issuer */ - x.bv_val += STRLENOF("issuer"); - x.bv_len -= STRLENOF("issuer"); + slap_sl_free( ni.bv_val, ctx ); - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + return LDAP_SUCCESS; +} - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; +static int +slap_bin2hex( + struct berval *in, + struct berval *out, + void *ctx ) - i.bv_val = x.bv_val; - i.bv_len = 0; +{ + /* Use hex format. '123456789abcdef'H */ + unsigned char *ptr, zero = '\0'; + char *sptr; + int first; + ber_len_t i, len, nlen; - for( ; i.bv_len < x.bv_len; ) { - if ( i.bv_val[i.bv_len] != '"' ) { - i.bv_len++; - continue; - } - if ( i.bv_val[i.bv_len+1] == '"' ) { - /* double dquote */ - i.bv_len+=2; - continue; - } - break; + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + assert( out != NULL ); + assert( !BER_BVISNULL( out ) ); + + ptr = (unsigned char *)in->bv_val; + len = in->bv_len; + + /* Check for minimal encodings */ + if ( len > 1 ) { + if ( ptr[0] & 0x80 ) { + if ( ( ptr[0] == 0xff ) && ( ptr[1] & 0x80 ) ) { + return -1; } - x.bv_val += i.bv_len+1; - x.bv_len -= i.bv_len+1; - if ( x.bv_len < STRLENOF(",serialNumber 0")) { - return LDAP_INVALID_SYNTAX; + } else if ( ptr[0] == 0 ) { + if ( !( ptr[1] & 0x80 ) ) { + return -1; } + len--; + ptr++; + } - haveissuer++; + } else if ( len == 0 ) { + /* FIXME: this should not be possible, + * since a value of zero would have length 1 */ + len = 1; + ptr = &zero; + } - } else if( strncasecmp( x.bv_val, "serialNumber", - STRLENOF("serialNumber")) == 0 ) - { - /* parse serialNumber */ - int neg=0; - x.bv_val += STRLENOF("serialNumber"); - x.bv_len -= STRLENOF("serialNumber"); + first = !( ptr[0] & 0xf0U ); + nlen = len * 2 - first + STRLENOF("''H"); /* quotes, H */ + if ( nlen >= out->bv_len ) { + out->bv_val = slap_sl_malloc( nlen + 1, ctx ); + } + sptr = out->bv_val; + *sptr++ = '\''; + i = 0; + if ( first ) { + sprintf( sptr, "%01X", ( ptr[0] & 0x0fU ) ); + sptr++; + i = 1; + } + for ( ; i < len; i++ ) { + sprintf( sptr, "%02X", ptr[i] ); + sptr += 2; + } + *sptr++ = '\''; + *sptr++ = 'H'; + *sptr = '\0'; - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + assert( sptr == &out->bv_val[nlen] ); - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - sn.bv_val = x.bv_val; - sn.bv_len = 0; + out->bv_len = nlen; - if( sn.bv_val[0] == '-' ) { - neg++; - sn.bv_len++; - } + return 0; +} - for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) { - if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break; - } +#define SLAP_SN_BUFLEN (64) - if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX; - if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) { - return LDAP_INVALID_SYNTAX; - } +/* + * This routine is called by certificateExactNormalize when + * certificateExactNormalize receives a search string instead of + * a certificate. This routine checks if the search value is valid + * and then returns the normalized value + */ +static int +serialNumberAndIssuerNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval sn, sn2, sn3, i, ni; + char sbuf2[SLAP_SN_BUFLEN]; + char sbuf3[SLAP_SN_BUFLEN]; + char *p; + int rc; - x.bv_val += sn.bv_len; x.bv_len -= sn.bv_len; + assert( in != NULL ); + assert( out != NULL ); - if ( x.bv_len < STRLENOF( ",issuer \"\"" )) { - return LDAP_INVALID_SYNTAX; - } + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n", + in->bv_val, 0, 0 ); - havesn++; + rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx ); + if ( rc ) { + return rc; + } - } else return LDAP_INVALID_SYNTAX; + rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); - if( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } - /* eat spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } + if ( rc ) { + return LDAP_INVALID_SYNTAX; + } - /* should be at remaining NamedValue */ - if( !haveissuer && (strncasecmp( x.bv_val, "issuer", - STRLENOF("issuer" )) == 0 )) - { - /* parse issuer */ - x.bv_val += STRLENOF("issuer"); - x.bv_len -= STRLENOF("issuer"); + /* Convert sn to canonical hex */ + sn2.bv_val = sbuf2; + if ( sn.bv_len > sizeof( sbuf2 ) ) { + 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; + } - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + sn3.bv_val = sbuf3; + sn3.bv_len = sizeof(sbuf3); + if ( slap_bin2hex( &sn2, &sn3, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + out->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" ) + + sn3.bv_len + ni.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); - i.bv_val = x.bv_val; - i.bv_len = 0; + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto func_leave; + } - for( ; i.bv_len < x.bv_len; ) { - if ( i.bv_val[i.bv_len] != '"' ) { - i.bv_len++; - continue; - } - if ( i.bv_val[i.bv_len+1] == '"' ) { - /* double dquote */ - i.bv_len+=2; - continue; - } - break; - } - x.bv_val += i.bv_len+1; - x.bv_len -= i.bv_len+1; + p = out->bv_val; - } else if( !havesn && (strncasecmp( x.bv_val, "serialNumber", - STRLENOF("serialNumber")) == 0 )) - { - /* parse serialNumber */ - int neg=0; - x.bv_val += STRLENOF("serialNumber"); - x.bv_len -= STRLENOF("serialNumber"); + p = lutil_strcopy( p, "{ serialNumber " /*}*/ ); + p = lutil_strncopy( p, sn3.bv_val, sn3.bv_len ); + p = lutil_strcopy( p, ", issuer rdnSequence:\"" ); + p = lutil_strncopy( p, ni.bv_val, ni.bv_len ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +func_leave: + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + if ( sn2.bv_val != sbuf2 ) { + slap_sl_free( sn2.bv_val, ctx ); + } + + if ( sn3.bv_val != sbuf3 ) { + slap_sl_free( sn3.bv_val, ctx ); + } + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +static int +certificateExactNormalize( + 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 i; + char serialbuf2[SLAP_SN_BUFLEN]; + struct berval sn, sn2 = BER_BVNULL; + struct berval issuer_dn = BER_BVNULL, bvdn; + char *p; + int rc = LDAP_INVALID_SYNTAX; + + assert( val != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> certificateExactNormalize: <%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 serialNumberAndIssuerNormalize( 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 Sequence */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_peek_tag( ber, &len ); /* Optional version? */ + if ( tag == SLAP_X509_OPT_C_VERSION ) { + tag = ber_skip_tag( ber, &len ); + tag = ber_get_int( ber, &i ); /* version */ + } + + /* NOTE: move the test here from certificateValidate, + * so that we can validate certs with serial longer + * than sizeof(ber_int_t) */ + tag = ber_skip_tag( ber, &len ); /* serial */ + sn.bv_len = len; + sn.bv_val = (char *)ber->ber_ptr; + sn2.bv_val = serialbuf2; + sn2.bv_len = sizeof(serialbuf2); + if ( slap_bin2hex( &sn, &sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* SignatureAlg */ + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); /* IssuerDN */ + len = ber_ptrlen( ber ); + bvdn.bv_val = val->bv_val + len; + bvdn.bv_len = val->bv_len - len; + + rc = dnX509normalize( &bvdn, &issuer_dn ); + if ( rc != LDAP_SUCCESS ) goto done; + + normalized->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" ) + + sn2.bv_len + issuer_dn.bv_len; + normalized->bv_val = ch_malloc( normalized->bv_len + 1 ); + + p = normalized->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " /*}*/ ); + p = lutil_strncopy( p, sn2.bv_val, sn2.bv_len ); + p = lutil_strcopy( p, ", issuer rdnSequence:\"" ); + p = lutil_strncopy( p, issuer_dn.bv_val, issuer_dn.bv_len ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + rc = LDAP_SUCCESS; + +done: + Debug( LDAP_DEBUG_TRACE, "<<< certificateExactNormalize: <%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 ); + if ( sn2.bv_val != serialbuf2 ) ber_memfree_x( sn2.bv_val, ctx ); + + return rc; +} + +/* X.509 PKI certificateList stuff */ +static int +checkTime( struct berval *in, struct berval *out ) +{ + int rc; + ber_len_t i; + char buf[STRLENOF("YYYYmmddHHMMSSZ") + 1]; + struct berval bv; + + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + assert( !BER_BVISEMPTY( in ) ); + + if ( in->bv_len < STRLENOF( "YYmmddHHMMSSZ" ) ) { + return -1; + } + + if ( out != NULL ) { + assert( !BER_BVISNULL( out ) ); + assert( out->bv_len >= sizeof( buf ) ); + bv.bv_val = out->bv_val; + + } else { + bv.bv_val = buf; + } + + for ( i = 0; i < STRLENOF( "YYYYmmddHHMMSS" ); i++ ) { + if ( !ASCII_DIGIT( in->bv_val[i] ) ) break; + } + + if ( in->bv_val[i] != 'Z' ) { + return -1; + } + i++; + + if ( i != in->bv_len ) { + return -1; + } + + if ( i == STRLENOF( "YYYYmmddHHMMSSZ" ) ) { + lutil_strncopy( bv.bv_val, in->bv_val, i ); + bv.bv_len = i; + + } else if ( i == STRLENOF( "YYmmddHHMMSSZ" ) ) { + char *p = bv.bv_val; + if ( in->bv_val[0] < '7' ) { + p = lutil_strcopy( p, "20" ); + + } else { + p = lutil_strcopy( p, "19" ); + } + lutil_strncopy( p, in->bv_val, i ); + bv.bv_len = 2 + i; + + } else { + return -1; + } + + rc = generalizedTimeValidate( NULL, &bv ); + if ( rc == LDAP_SUCCESS && out != NULL ) { + out->bv_len = bv.bv_len; + } + + return rc != LDAP_SUCCESS; +} + +static int +issuerAndThisUpdateCheck( + struct berval *in, + struct berval *is, + struct berval *tu, + void *ctx ) +{ + int numdquotes = 0; + struct berval x = *in; + struct berval ni = BER_BVNULL; + /* Parse GSER format */ + enum { + HAVE_NONE = 0x0, + HAVE_ISSUER = 0x1, + HAVE_THISUPDATE = 0x2, + HAVE_ALL = ( HAVE_ISSUER | HAVE_THISUPDATE ) + } have = HAVE_NONE; + + + if ( in->bv_len < STRLENOF( "{issuer \"\",thisUpdate \"YYMMDDhhmmssZ\"}" ) ) return LDAP_INVALID_SYNTAX; + + if ( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val++; + x.bv_len -= STRLENOF("{}"); + + do { + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* should be at issuer or thisUpdate */ + if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) { + if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX; + + /* parse issuer */ + x.bv_val += STRLENOF("issuer"); + x.bv_len -= STRLENOF("issuer"); - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len ; x.bv_val++, x.bv_len--) { + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { /* empty */; } - - sn.bv_val = x.bv_val; - sn.bv_len = 0; - if( sn.bv_val[0] == '-' ) { - neg++; - sn.bv_len++; + /* For backward compatibility, this part is optional */ + if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) != 0 ) { + return LDAP_INVALID_SYNTAX; } + x.bv_val += STRLENOF("rdnSequence:"); + x.bv_len -= STRLENOF("rdnSequence:"); - for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) { - if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break; + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + is->bv_val = x.bv_val; + is->bv_len = 0; + + for ( ; is->bv_len < x.bv_len; ) { + if ( is->bv_val[is->bv_len] != '"' ) { + is->bv_len++; + continue; + } + if ( is->bv_val[is->bv_len+1] == '"' ) { + /* double dquote */ + is->bv_len += 2; + continue; + } + break; } + x.bv_val += is->bv_len + 1; + x.bv_len -= is->bv_len + 1; - if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX; - if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) { - return LDAP_INVALID_SYNTAX; + have |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "thisUpdate", STRLENOF("thisUpdate") ) == 0 ) + { + if ( have & HAVE_THISUPDATE ) return LDAP_INVALID_SYNTAX; + + /* parse thisUpdate */ + x.bv_val += STRLENOF("thisUpdate"); + x.bv_len -= STRLENOF("thisUpdate"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + tu->bv_val = x.bv_val; + tu->bv_len = 0; + + for ( ; tu->bv_len < x.bv_len; tu->bv_len++ ) { + if ( tu->bv_val[tu->bv_len] == '"' ) { + break; + } } + x.bv_val += tu->bv_len + 1; + x.bv_len -= tu->bv_len + 1; - x.bv_val += sn.bv_len; - x.bv_len -= sn.bv_len; + have |= HAVE_THISUPDATE; - } else return LDAP_INVALID_SYNTAX; + } else { + return LDAP_INVALID_SYNTAX; + } - /* eat trailing spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { /* empty */; } - /* should have no characters left... */ - if( x.bv_len ) return LDAP_INVALID_SYNTAX; + if ( have == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val++; + x.bv_len--; + } while ( 1 ); + + /* should have no characters left... */ + if ( x.bv_len ) return LDAP_INVALID_SYNTAX; + + if ( numdquotes == 0 ) { + ber_dupbv_x( &ni, is, ctx ); - ber_dupbv_x( &ni, &i, ctx ); - i = ni; + } else { + ber_len_t src, dst; - /* need to handle double dquotes here */ + ni.bv_len = is->bv_len - numdquotes; + ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx ); + for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) { + if ( is->bv_val[src] == '"' ) { + src++; + } + ni.bv_val[dst] = is->bv_val[src]; + } + ni.bv_val[dst] = '\0'; } + + *is = ni; - rc = dnPretty( syntax, &i, &ni, ctx ); + return 0; +} - if( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { - slap_sl_free( i.bv_val, ctx ); +static int +issuerAndThisUpdateValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval i, tu; + + Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateValidate: <%s>\n", + in->bv_val, 0, 0 ); + + rc = issuerAndThisUpdateCheck( in, &i, &tu, NULL ); + if ( rc ) { + goto done; } - if( rc ) return LDAP_INVALID_SYNTAX; + /* validate DN -- doesn't handle double dquote */ + rc = dnValidate( NULL, &i ); + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; - /* make room from sn + "$" */ - out->bv_len = STRLENOF("{ serialNumber , issuer \"\" }") - + sn.bv_len + ni.bv_len; - out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + } else if ( checkTime( &tu, NULL ) ) { + rc = LDAP_INVALID_SYNTAX; + } - if( out->bv_val == NULL ) { - out->bv_len = 0; - slap_sl_free( ni.bv_val, ctx ); - return LDAP_OTHER; + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, NULL ); } - n = 0; - AC_MEMCPY( &out->bv_val[n], "{ serialNumber ", - STRLENOF("{ serialNumber ")); - n = STRLENOF("{ serialNumber "); + Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateValidate: <%s> err=%d\n", + in->bv_val, rc, 0 ); - AC_MEMCPY( &out->bv_val[n], sn.bv_val, sn.bv_len ); - n += sn.bv_len; +done:; + return rc; +} + +static int +issuerAndThisUpdatePretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + int rc; + struct berval i, tu, ni = BER_BVNULL; + char *p; + + assert( in != NULL ); + assert( out != NULL ); + + BER_BVZERO( out ); + + Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdatePretty: <%s>\n", + in->bv_val, 0, 0 ); - AC_MEMCPY( &out->bv_val[n], ", issuer \"", STRLENOF(", issuer \"")); - n += STRLENOF(", issuer \""); + rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx ); + if ( rc ) { + goto done; + } + + rc = dnPretty( syntax, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc || checkTime( &tu, NULL ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } - AC_MEMCPY( &out->bv_val[n], ni.bv_val, ni.bv_len ); - n += ni.bv_len; + /* make room */ + out->bv_len = STRLENOF("{ issuer rdnSequence:\"\", thisUpdate \"\" }") + + ni.bv_len + tu.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); - AC_MEMCPY( &out->bv_val[n], "\" }", STRLENOF("\" }")); - n += STRLENOF("\" }"); + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto done; + } - out->bv_val[n] = '\0'; + 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, tu.bv_val, tu.bv_len ); + p = lutil_strcopy( p, /*{*/ "\" }" ); - assert( n == out->bv_len ); + assert( p == &out->bv_val[out->bv_len] ); - Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s>\n", - out->bv_val, 0, 0 ); +done:; + Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdatePretty: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); slap_sl_free( ni.bv_val, ctx ); - return LDAP_SUCCESS; + return rc; } -/* - * This routine is called by certificateExactNormalize when - * certificateExactNormalize receives a search string instead of - * a certificate. This routine checks if the search value is valid - * and then returns the normalized value - */ static int -serialNumberAndIssuerNormalize( +issuerAndThisUpdateNormalize( slap_mask_t usage, Syntax *syntax, MatchingRule *mr, @@ -2933,288 +3893,1287 @@ serialNumberAndIssuerNormalize( struct berval *out, void *ctx ) { + struct berval i, ni, tu, tu2; + char sbuf[STRLENOF("YYYYmmddHHMMSSZ") + 1]; + char *p; int rc; - ber_len_t n; - struct berval sn, i, ni; assert( in != NULL ); assert( out != NULL ); - Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n", + Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateNormalize: <%s>\n", in->bv_val, 0, 0 ); - if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX; + rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx ); + if ( rc ) { + return rc; + } - if( in->bv_val[0] != '{' && in->bv_val[in->bv_len-1] != '}' ) { - /* Parse old format */ - i.bv_val = ber_bvchr( in, '$' ); - if( BER_BVISNULL( &i ) ) return LDAP_INVALID_SYNTAX; + rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + tu2.bv_val = sbuf; + tu2.bv_len = sizeof( sbuf ); + if ( rc || checkTime( &tu, &tu2 ) ) { + return LDAP_INVALID_SYNTAX; + } + + out->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" ) + + ni.bv_len + tu2.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; + goto func_leave; + } + + 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 -- 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 { + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* should be at issuer or serialNumber NamedValue */ + if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) { + if ( have & HAVE_ISSUER ) { + return LDAP_INVALID_SYNTAX; + } + + /* parse IssuerSerial */ + x.bv_val += STRLENOF("issuer"); + x.bv_len -= STRLENOF("issuer"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( strncasecmp( x.bv_val, "baseCertificateID ", STRLENOF("baseCertificateID ") ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val += STRLENOF("baseCertificateID "); + x.bv_len -= STRLENOF("baseCertificateID "); + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + do { + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* parse issuer of baseCertificateID */ + if ( strncasecmp( x.bv_val, "issuer ", STRLENOF("issuer ") ) == 0 ) { + if ( have2 & HAVE_ISSUER ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += STRLENOF("issuer "); + x.bv_len -= STRLENOF("issuer "); + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( strncasecmp( x.bv_val, "directoryName:rdnSequence:", STRLENOF("directoryName:rdnSequence:") ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val += STRLENOF("directoryName:rdnSequence:"); + x.bv_len -= STRLENOF("directoryName:rdnSequence:"); + + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + is->bv_val = x.bv_val; + is->bv_len = 0; + + for ( ; is->bv_len < x.bv_len; ) { + if ( is->bv_val[is->bv_len] != '"' ) { + is->bv_len++; + continue; + } + if ( is->bv_val[is->bv_len + 1] == '"' ) { + /* double dquote */ + is->bv_len += 2; + continue; + } + break; + } + x.bv_val += is->bv_len + 1; + x.bv_len -= is->bv_len + 1; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + have2 |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "serial ", STRLENOF("serial ") ) == 0 ) { + if ( have2 & HAVE_SN ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += STRLENOF("serial "); + x.bv_len -= STRLENOF("serial "); + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { + /* empty */; + } + + if ( checkNum( &x, i_sn ) ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += i_sn->bv_len; + x.bv_len -= i_sn->bv_len; + + have2 |= HAVE_SN; + + } else { + return LDAP_INVALID_SYNTAX; + } + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( have2 == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + } while ( 1 ); + + if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + have |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) { + if ( have & HAVE_SN ) { + return LDAP_INVALID_SYNTAX; + } + + /* parse serialNumber */ + x.bv_val += STRLENOF("serialNumber"); + x.bv_len -= STRLENOF("serialNumber"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( checkNum( &x, sn ) ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += sn->bv_len; + x.bv_len -= sn->bv_len; + + have |= HAVE_SN; + + } else { + return LDAP_INVALID_SYNTAX; + } + + /* eat spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( have == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val++ ; + x.bv_len--; + } while ( 1 ); + + /* should have no characters left... */ + if( x.bv_len ) return LDAP_INVALID_SYNTAX; + + if ( numdquotes == 0 ) { + ber_dupbv_x( &ni, is, ctx ); + + } else { + ber_len_t src, dst; + + ni.bv_len = is->bv_len - numdquotes; + ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx ); + for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) { + if ( is->bv_val[src] == '"' ) { + src++; + } + ni.bv_val[dst] = is->bv_val[src]; + } + ni.bv_val[dst] = '\0'; + } + + *is = ni; + + /* need to handle double dquotes here */ + return 0; +} + +/* X.509 PMI serialNumberAndIssuerSerialValidate */ +static int +serialNumberAndIssuerSerialValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval sn, i, i_sn; + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialValidate: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, NULL ); + if ( rc ) { + goto done; + } + + /* validate DN -- doesn't handle double dquote */ + rc = dnValidate( NULL, &i ); + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + } + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, NULL ); + } + +done:; + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialValidate: <%s> err=%d\n", + in->bv_val, rc, 0 ); + + return rc; +} + +/* X.509 PMI serialNumberAndIssuerSerialPretty */ +static int +serialNumberAndIssuerSerialPretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval sn, i, i_sn, ni = BER_BVNULL; + char *p; + int rc; + + assert( in != NULL ); + assert( out != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialPretty: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx ); + if ( rc ) { + goto done; + } + + rc = dnPretty( syntax, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + /* make room from sn + "$" */ + out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }") + + sn.bv_len + ni.bv_len + i_sn.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; + goto done; + } + + p = out->bv_val; + p = lutil_strcopy( p, "{ serialNumber " ); + p = lutil_strncopy( p, sn.bv_val, sn.bv_len ); + p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" ); + p = lutil_strncopy( p, ni.bv_val, ni.bv_len ); + p = lutil_strcopy( p, "\" }, serial " ); + p = lutil_strncopy( p, i_sn.bv_val, i_sn.bv_len ); + p = lutil_strcopy( p, " } } }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +done:; + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialPretty: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +/* X.509 PMI serialNumberAndIssuerSerialNormalize */ +/* + * This routine is called by attributeCertificateExactNormalize + * when attributeCertificateExactNormalize receives a search + * string instead of a attribute certificate. This routine + * checks if the search value is valid and then returns the + * normalized value + */ +static int +serialNumberAndIssuerSerialNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval i, ni = BER_BVNULL, + sn, sn2 = BER_BVNULL, sn3 = BER_BVNULL, + i_sn, i_sn2 = BER_BVNULL, i_sn3 = BER_BVNULL; + char sbuf2[SLAP_SN_BUFLEN], i_sbuf2[SLAP_SN_BUFLEN], + sbuf3[SLAP_SN_BUFLEN], i_sbuf3[SLAP_SN_BUFLEN]; + char *p; + int rc; + + assert( in != NULL ); + assert( out != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialNormalize: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx ); + if ( rc ) { + goto func_leave; + } + + rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + /* Convert sn to canonical hex */ + sn2.bv_val = sbuf2; + sn2.bv_len = sn.bv_len; + if ( sn.bv_len > sizeof( sbuf2 ) ) { + sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx ); + } + if ( lutil_str2bin( &sn, &sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + /* Convert i_sn to canonical hex */ + i_sn2.bv_val = i_sbuf2; + i_sn2.bv_len = i_sn.bv_len; + if ( i_sn.bv_len > sizeof( i_sbuf2 ) ) { + i_sn2.bv_val = slap_sl_malloc( i_sn.bv_len, ctx ); + } + if ( lutil_str2bin( &i_sn, &i_sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + sn3.bv_val = sbuf3; + sn3.bv_len = sizeof(sbuf3); + if ( slap_bin2hex( &sn2, &sn3, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + i_sn3.bv_val = i_sbuf3; + i_sn3.bv_len = sizeof(i_sbuf3); + if ( slap_bin2hex( &i_sn2, &i_sn3, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }") + + sn3.bv_len + ni.bv_len + i_sn3.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; + goto func_leave; + } + + p = out->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " ); + p = lutil_strncopy( p, sn3.bv_val, sn3.bv_len ); + p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" ); + p = lutil_strncopy( p, ni.bv_val, ni.bv_len ); + p = lutil_strcopy( p, "\" }, serial " ); + p = lutil_strncopy( p, i_sn3.bv_val, i_sn3.bv_len ); + p = lutil_strcopy( p, " } } }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +func_leave: + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialNormalize: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + if ( sn2.bv_val != sbuf2 ) { + slap_sl_free( sn2.bv_val, ctx ); + } + + if ( i_sn2.bv_val != i_sbuf2 ) { + slap_sl_free( i_sn2.bv_val, ctx ); + } + + if ( sn3.bv_val != sbuf3 ) { + slap_sl_free( sn3.bv_val, ctx ); + } + + if ( i_sn3.bv_val != i_sbuf3 ) { + slap_sl_free( i_sn3.bv_val, ctx ); + } + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +/* X.509 PMI attributeCertificateExactNormalize */ +static int +attributeCertificateExactNormalize( + 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; + char issuer_serialbuf[SLAP_SN_BUFLEN], serialbuf[SLAP_SN_BUFLEN]; + struct berval sn, i_sn, sn2, i_sn2; + struct berval issuer_dn = BER_BVNULL, bvdn; + char *p; + int rc = LDAP_INVALID_SYNTAX; + + if ( BER_BVISEMPTY( val ) ) { + goto done; + } + + if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { + return serialNumberAndIssuerSerialNormalize( 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 Sequence */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_skip_tag( ber, &len ); /* (Mandatory) version; must be v2(1) */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Holder Sequence */ + ber_skip_data( ber, len ); + + /* Issuer */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + /* issuerName (GeneralNames sequence; optional)? */ + tag = ber_skip_tag( ber, &len ); /* baseCertificateID (sequence; optional)? */ + 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; + } + tag = ber_peek_tag( ber, &len ); /* sequence of RDN */ + len = ber_ptrlen( ber ); + bvdn.bv_val = val->bv_val + len; + bvdn.bv_len = val->bv_len - len; + rc = dnX509normalize( &bvdn, &issuer_dn ); + if ( rc != LDAP_SUCCESS ) goto done; + + tag = ber_skip_tag( ber, &len ); /* sequence of RDN */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* serial number */ + if ( tag != LBER_INTEGER ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + i_sn.bv_val = (char *)ber->ber_ptr; + i_sn.bv_len = len; + i_sn2.bv_val = issuer_serialbuf; + i_sn2.bv_len = sizeof(issuer_serialbuf); + if ( slap_bin2hex( &i_sn, &i_sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + ber_skip_data( ber, len ); + + /* issuerUID (bitstring; optional)? */ + /* objectDigestInfo (sequence; optional)? */ + + tag = ber_skip_tag( ber, &len ); /* Signature (sequence) */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* serial number */ + if ( tag != LBER_INTEGER ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + sn.bv_val = (char *)ber->ber_ptr; + sn.bv_len = len; + sn2.bv_val = serialbuf; + sn2.bv_len = sizeof(serialbuf); + if ( slap_bin2hex( &sn, &sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + ber_skip_data( ber, len ); + + normalized->bv_len = STRLENOF( "{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }" ) + + sn2.bv_len + issuer_dn.bv_len + i_sn2.bv_len; + normalized->bv_val = ch_malloc( normalized->bv_len + 1 ); + + p = normalized->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " ); + p = lutil_strncopy( p, sn2.bv_val, sn2.bv_len ); + p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" ); + p = lutil_strncopy( p, issuer_dn.bv_val, issuer_dn.bv_len ); + p = lutil_strcopy( p, "\" }, serial " ); + p = lutil_strncopy( p, i_sn2.bv_val, i_sn2.bv_len ); + p = lutil_strcopy( p, " } } }" ); + + Debug( LDAP_DEBUG_TRACE, "attributeCertificateExactNormalize: %s\n", + normalized->bv_val, NULL, NULL ); + + rc = LDAP_SUCCESS; + +done: + if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val ); + if ( i_sn2.bv_val != issuer_serialbuf ) ber_memfree_x( i_sn2.bv_val, ctx ); + if ( sn2.bv_val != serialbuf ) ber_memfree_x( sn2.bv_val, ctx ); + + return rc; +} + + +static int +hexValidate( + Syntax *syntax, + struct berval *in ) +{ + ber_len_t i; + + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + + for ( i = 0; i < in->bv_len; i++ ) { + if ( !ASCII_HEX( in->bv_val[ i ] ) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +/* Normalize a SID as used inside a CSN: + * three-digit numeric string */ +static int +hexNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + ber_len_t i; + + assert( val != NULL ); + assert( normalized != NULL ); + + ber_dupbv_x( normalized, val, ctx ); + + for ( i = 0; i < normalized->bv_len; i++ ) { + if ( !ASCII_HEX( normalized->bv_val[ i ] ) ) { + ber_memfree_x( normalized->bv_val, ctx ); + BER_BVZERO( normalized ); + return LDAP_INVALID_SYNTAX; + } + + normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] ); + } + + return LDAP_SUCCESS; +} + +static int +sidValidate ( + Syntax *syntax, + struct berval *in ) +{ + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + + if ( in->bv_len != 3 ) { + return LDAP_INVALID_SYNTAX; + } + + return hexValidate( NULL, in ); +} + +/* Normalize a SID as used inside a CSN: + * three-digit numeric string */ +static int +sidNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + if ( val->bv_len != 3 ) { + return LDAP_INVALID_SYNTAX; + } + + return hexNormalize( 0, NULL, NULL, val, normalized, ctx ); +} + +static int +sidPretty( + Syntax *syntax, + struct berval *val, + struct berval *out, + void *ctx ) +{ + return sidNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx ); +} + +/* Normalize a SID as used inside a CSN, either as-is + * (assertion value) or extracted from the CSN + * (attribute value) */ +static int +csnSidNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval bv; + char *ptr, + buf[ 4 ]; + + + if ( BER_BVISEMPTY( val ) ) { + return LDAP_INVALID_SYNTAX; + } + + if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { + return sidNormalize( 0, NULL, NULL, val, normalized, ctx ); + } + + assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 ); + + ptr = ber_bvchr( val, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_val = ptr + 1; + bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val ); + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_val = ptr + 1; + bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val ); + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - sn.bv_val = in->bv_val; - sn.bv_len = i.bv_val - in->bv_val; + bv.bv_len = ptr - bv.bv_val; - i.bv_val++; - i.bv_len = in->bv_len - (sn.bv_len + 1); + if ( bv.bv_len == 2 ) { + /* OpenLDAP 2.3 SID */ + buf[ 0 ] = '0'; + buf[ 1 ] = bv.bv_val[ 0 ]; + buf[ 2 ] = bv.bv_val[ 1 ]; + buf[ 3 ] = '\0'; - /* eat leading zeros */ - for( n=0; n < (sn.bv_len-1); n++ ) { - if( sn.bv_val[n] != '0' ) break; - } - sn.bv_val += n; - sn.bv_len -= n; + bv.bv_val = buf; + bv.bv_len = 3; + } - for( n=0; n < sn.bv_len; n++ ) { - if( !ASCII_DIGIT(sn.bv_val[n]) ) return LDAP_INVALID_SYNTAX; - } + return sidNormalize( 0, NULL, NULL, &bv, normalized, ctx ); +} - } else { - /* Parse GSER format */ - int havesn=0,haveissuer=0; - struct berval x = *in; - x.bv_val++; - x.bv_len-=2; +static int +csnValidate( + Syntax *syntax, + struct berval *in ) +{ + struct berval bv; + char *ptr; + int rc; - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); - if ( x.bv_len < STRLENOF("serialNumber 0,issuer \"\"")) { - return LDAP_INVALID_SYNTAX; - } + if ( BER_BVISEMPTY( in ) ) { + return LDAP_INVALID_SYNTAX; + } - /* should be at issuer or serialNumber NamedValue */ - if( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer")) == 0 ) { - /* parse issuer */ - x.bv_val += STRLENOF("issuer"); - x.bv_len -= STRLENOF("issuer"); + bv = *in; - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &bv.bv_val[bv.bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + bv.bv_len = ptr - bv.bv_val; + if ( bv.bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) && + bv.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) + { + return LDAP_INVALID_SYNTAX; + } - i.bv_val = x.bv_val; - i.bv_len = 0; + rc = generalizedTimeValidate( NULL, &bv ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } - for( ; i.bv_len < x.bv_len; ) { - if ( i.bv_val[i.bv_len] != '"' ) { - i.bv_len++; - continue; - } - if ( i.bv_val[i.bv_len+1] == '"' ) { - /* double dquote */ - i.bv_len+=2; - continue; - } - break; - } - x.bv_val += i.bv_len+1; - x.bv_len -= i.bv_len+1; + bv.bv_val = ptr + 1; + bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); - if ( x.bv_len < STRLENOF(",serialNumber 0")) { - return LDAP_INVALID_SYNTAX; - } + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - haveissuer++; + bv.bv_len = ptr - bv.bv_val; + if ( bv.bv_len != 6 ) { + return LDAP_INVALID_SYNTAX; + } - } else if( strncasecmp( x.bv_val, "serialNumber", - STRLENOF("serialNumber")) == 0 ) - { - /* parse serialNumber */ - int neg=0; - x.bv_val += STRLENOF("serialNumber"); - x.bv_len -= STRLENOF("serialNumber"); + rc = hexValidate( NULL, &bv ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + bv.bv_val = ptr + 1; + bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - sn.bv_val = x.bv_val; - sn.bv_len = 0; + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - if( sn.bv_val[0] == '-' ) { - neg++; - sn.bv_len++; - } + bv.bv_len = ptr - bv.bv_val; + if ( bv.bv_len == 2 ) { + /* tolerate old 2-digit replica-id */ + rc = hexValidate( NULL, &bv ); - for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) { - if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break; - } + } else { + rc = sidValidate( NULL, &bv ); + } + if ( rc != LDAP_SUCCESS ) { + return rc; + } - if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX; - if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) { - return LDAP_INVALID_SYNTAX; - } + bv.bv_val = ptr + 1; + bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); - x.bv_val += sn.bv_len; x.bv_len -= sn.bv_len; + if ( bv.bv_len != 6 ) { + return LDAP_INVALID_SYNTAX; + } - if ( x.bv_len < STRLENOF( ",issuer \"\"" )) { - return LDAP_INVALID_SYNTAX; - } + return hexValidate( NULL, &bv ); +} - havesn++; +/* Normalize a CSN in OpenLDAP 2.1 format */ +static int +csnNormalize21( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval gt, cnt, sid, mod; + struct berval bv; + char buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ]; + char *ptr; + ber_len_t i; - } else return LDAP_INVALID_SYNTAX; + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + assert( !BER_BVISEMPTY( val ) ); - if( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + gt = *val; - /* eat spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } + ptr = ber_bvchr( >, '#' ); + if ( ptr == NULL || ptr == >.bv_val[gt.bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - /* should be at remaining NamedValue */ - if( !haveissuer && (strncasecmp( x.bv_val, "issuer", - STRLENOF("issuer" )) == 0 )) - { - /* parse issuer */ - x.bv_val += STRLENOF("issuer"); - x.bv_len -= STRLENOF("issuer"); + gt.bv_len = ptr - gt.bv_val; + if ( gt.bv_len != STRLENOF( "YYYYmmddHH:MM:SSZ" ) ) { + return LDAP_INVALID_SYNTAX; + } - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + if ( gt.bv_val[ 10 ] != ':' || gt.bv_val[ 13 ] != ':' ) { + return LDAP_INVALID_SYNTAX; + } - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } - - if( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + cnt.bv_val = ptr + 1; + cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val ); - i.bv_val = x.bv_val; - i.bv_len = 0; + ptr = ber_bvchr( &cnt, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - for( ; i.bv_len < x.bv_len; ) { - if ( i.bv_val[i.bv_len] != '"' ) { - i.bv_len++; - continue; - } - if ( i.bv_val[i.bv_len+1] == '"' ) { - /* double dquote */ - i.bv_len+=2; - continue; - } - break; - } - x.bv_val += i.bv_len+1; - x.bv_len -= i.bv_len+1; + cnt.bv_len = ptr - cnt.bv_val; + if ( cnt.bv_len != STRLENOF( "0x0000" ) ) { + return LDAP_INVALID_SYNTAX; + } - } else if( !havesn && (strncasecmp( x.bv_val, "serialNumber", - STRLENOF("serialNumber")) == 0 )) - { - /* parse serialNumber */ - int neg=0; - x.bv_val += STRLENOF("serialNumber"); - x.bv_len -= STRLENOF("serialNumber"); + if ( strncmp( cnt.bv_val, "0x", STRLENOF( "0x" ) ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } - if( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; - x.bv_val++; x.bv_len--; + cnt.bv_val += STRLENOF( "0x" ); + cnt.bv_len -= STRLENOF( "0x" ); - /* eat leading spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len ; x.bv_val++, x.bv_len--) { - /* empty */; - } - - sn.bv_val = x.bv_val; - sn.bv_len = 0; + sid.bv_val = ptr + 1; + sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val ); + + ptr = ber_bvchr( &sid, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - if( sn.bv_val[0] == '-' ) { - neg++; - sn.bv_len++; - } + sid.bv_len = ptr - sid.bv_val; + if ( sid.bv_len != STRLENOF( "0" ) ) { + return LDAP_INVALID_SYNTAX; + } - for( ; sn.bv_len < x.bv_len; sn.bv_len++ ) { - if ( !ASCII_DIGIT( sn.bv_val[sn.bv_len] )) break; - } + mod.bv_val = ptr + 1; + mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val ); + if ( mod.bv_len != STRLENOF( "0000" ) ) { + return LDAP_INVALID_SYNTAX; + } - if (!( sn.bv_len > neg )) return LDAP_INVALID_SYNTAX; - if (( sn.bv_len > 1+neg ) && ( sn.bv_val[neg] == '0' )) { - return LDAP_INVALID_SYNTAX; - } + bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ); + bv.bv_val = buf; + + ptr = bv.bv_val; + ptr = lutil_strncopy( ptr, gt.bv_val, STRLENOF( "YYYYmmddHH" ) ); + ptr = lutil_strncopy( ptr, >.bv_val[ STRLENOF( "YYYYmmddHH:" ) ], + STRLENOF( "MM" ) ); + ptr = lutil_strncopy( ptr, >.bv_val[ STRLENOF( "YYYYmmddHH:MM:" ) ], + STRLENOF( "SS" ) ); + ptr = lutil_strcopy( ptr, ".000000Z#00" ); + ptr = lutil_strncopy( ptr, cnt.bv_val, cnt.bv_len ); + *ptr++ = '#'; + *ptr++ = '0'; + *ptr++ = '0'; + *ptr++ = sid.bv_val[ 0 ]; + *ptr++ = '#'; + *ptr++ = '0'; + *ptr++ = '0'; + for ( i = 0; i < mod.bv_len; i++ ) { + *ptr++ = TOLOWER( mod.bv_val[ i ] ); + } + *ptr = '\0'; + + assert( ptr == &bv.bv_val[bv.bv_len] ); + + if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } - x.bv_val += sn.bv_len; - x.bv_len -= sn.bv_len; + ber_dupbv_x( normalized, &bv, ctx ); - } else return LDAP_INVALID_SYNTAX; + return LDAP_SUCCESS; +} - /* eat trailing spaces */ - for( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { - /* empty */; - } +/* Normalize a CSN in OpenLDAP 2.3 format */ +static int +csnNormalize23( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval gt, cnt, sid, mod; + struct berval bv; + char buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ]; + char *ptr; + ber_len_t i; - /* should have no characters left... */ - if( x.bv_len ) return LDAP_INVALID_SYNTAX; + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + assert( !BER_BVISEMPTY( val ) ); - ber_dupbv_x( &ni, &i, ctx ); - i = ni; + gt = *val; - /* need to handle double dquotes here */ + ptr = ber_bvchr( >, '#' ); + if ( ptr == NULL || ptr == >.bv_val[gt.bv_len] ) { + return LDAP_INVALID_SYNTAX; } - rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); - - if( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { - slap_sl_free( i.bv_val, ctx ); + gt.bv_len = ptr - gt.bv_val; + if ( gt.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) { + return LDAP_INVALID_SYNTAX; } - if( rc ) return LDAP_INVALID_SYNTAX; - - /* make room from sn + "$" */ - out->bv_len = STRLENOF( "{ serialNumber , issuer \"\" }" ) - + sn.bv_len + ni.bv_len; - out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + cnt.bv_val = ptr + 1; + cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val ); - if( out->bv_val == NULL ) { - out->bv_len = 0; - slap_sl_free( ni.bv_val, ctx ); - return LDAP_OTHER; + ptr = ber_bvchr( &cnt, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; } - n = 0; - AC_MEMCPY( &out->bv_val[n], "{ serialNumber ", - STRLENOF( "{ serialNumber " )); - n = STRLENOF( "{ serialNumber " ); - - AC_MEMCPY( &out->bv_val[n], sn.bv_val, sn.bv_len ); - n += sn.bv_len; + cnt.bv_len = ptr - cnt.bv_val; + if ( cnt.bv_len != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY( &out->bv_val[n], ", issuer \"", STRLENOF( ", issuer \"" )); - n += STRLENOF( ", issuer \"" ); + sid.bv_val = ptr + 1; + sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val ); + + ptr = ber_bvchr( &sid, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY( &out->bv_val[n], ni.bv_val, ni.bv_len ); - n += ni.bv_len; + sid.bv_len = ptr - sid.bv_val; + if ( sid.bv_len != STRLENOF( "00" ) ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY( &out->bv_val[n], "\" }", STRLENOF( "\" }" )); - n += STRLENOF( "\" }" ); + mod.bv_val = ptr + 1; + mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val ); + if ( mod.bv_len != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } - out->bv_val[n] = '\0'; + bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ); + bv.bv_val = buf; - assert( n == out->bv_len ); + 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++ = '#'; + *ptr++ = '0'; + for ( i = 0; i < sid.bv_len; i++ ) { + *ptr++ = TOLOWER( sid.bv_val[ i ] ); + } + *ptr++ = '#'; + for ( i = 0; i < mod.bv_len; i++ ) { + *ptr++ = TOLOWER( mod.bv_val[ i ] ); + } + *ptr = '\0'; - Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s>\n", - out->bv_val, 0, 0 ); + assert( ptr == &bv.bv_val[bv.bv_len] ); + if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } - slap_sl_free( ni.bv_val, ctx ); + ber_dupbv_x( normalized, &bv, ctx ); return LDAP_SUCCESS; } -#ifdef HAVE_TLS +/* Normalize a CSN */ static int -certificateExactNormalize( +csnNormalize( slap_mask_t usage, Syntax *syntax, MatchingRule *mr, @@ -3222,75 +5181,97 @@ certificateExactNormalize( struct berval *normalized, void *ctx ) { - int rc = LDAP_INVALID_SYNTAX; - unsigned char *p; - char *serial = NULL; - ber_len_t seriallen; - struct berval issuer_dn = BER_BVNULL; - X509_NAME *name = NULL; - ASN1_INTEGER *sn = NULL; - X509 *xcert = NULL; + struct berval cnt, sid, mod; + char *ptr; + ber_len_t i; - if( BER_BVISEMPTY( val ) ) goto done; + assert( val != NULL ); + assert( normalized != NULL ); + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); - if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { - return serialNumberAndIssuerNormalize(0,NULL,NULL,val,normalized,ctx); + if ( BER_BVISEMPTY( val ) ) { + return LDAP_INVALID_SYNTAX; } - assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 ); + if ( val->bv_len == STRLENOF( "YYYYmmddHHMMSSZ#SSSSSS#ID#ssssss" ) ) { + /* Openldap <= 2.3 */ + + return csnNormalize23( usage, syntax, mr, val, normalized, ctx ); + } - p = (unsigned char *)val->bv_val; - xcert = d2i_X509( NULL, &p, val->bv_len); - if( xcert == NULL ) goto done; + if ( val->bv_len == STRLENOF( "YYYYmmddHH:MM:SSZ#0xSSSS#I#ssss" ) ) { + /* Openldap 2.1 */ - sn=X509_get_serialNumber(xcert); - if ( sn == NULL ) goto done; - serial=i2s_ASN1_INTEGER(0, sn ); - if( serial == NULL ) goto done; - seriallen=strlen(serial); + return csnNormalize21( usage, syntax, mr, val, normalized, ctx ); + } - name=X509_get_issuer_name(xcert); - if( name == NULL ) goto done; - rc = dnX509normalize( name, &issuer_dn ); - if( rc != LDAP_SUCCESS ) goto done; + if ( val->bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) ) { + return LDAP_INVALID_SYNTAX; + } - normalized->bv_len = STRLENOF( "{ serialNumber , issuer \"\" }" ) - + seriallen + issuer_dn.bv_len; - normalized->bv_val = ch_malloc(normalized->bv_len+1); + ptr = ber_bvchr( val, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - p = (unsigned char *)normalized->bv_val; + if ( ptr - val->bv_val != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY(p, "{ serialNumber ", STRLENOF( "{ serialNumber " )); - p += STRLENOF( "{ serialNumber " ); + cnt.bv_val = ptr + 1; + cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val ); - AC_MEMCPY(p, serial, seriallen); - p += seriallen; + ptr = ber_bvchr( &cnt, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY(p, ", issuer \"", STRLENOF( ", issuer \"" )); - p += STRLENOF( ", issuer \"" ); + if ( ptr - cnt.bv_val != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY(p, issuer_dn.bv_val, issuer_dn.bv_len); - p += issuer_dn.bv_len; + sid.bv_val = ptr + 1; + sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val ); + + ptr = ber_bvchr( &sid, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } - AC_MEMCPY(p, "\" }", STRLENOF( "\" }" )); - p += STRLENOF( "\" }" ); + sid.bv_len = ptr - sid.bv_val; + if ( sid.bv_len != STRLENOF( "000" ) ) { + return LDAP_INVALID_SYNTAX; + } - *p = '\0'; + mod.bv_val = ptr + 1; + mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val ); - Debug( LDAP_DEBUG_TRACE, "certificateExactNormalize: %s\n", - normalized->bv_val, NULL, NULL ); + if ( mod.bv_len != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } - rc = LDAP_SUCCESS; + ber_dupbv_x( normalized, val, ctx ); -done: - if (xcert) X509_free(xcert); - if (serial) ch_free(serial); - if (issuer_dn.bv_val) ber_memfree(issuer_dn.bv_val); + for ( i = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#" ); + i < normalized->bv_len; i++ ) + { + /* assume it's already validated that's all hex digits */ + normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] ); + } - return rc; + return LDAP_SUCCESS; } -#endif /* HAVE_TLS */ +static int +csnPretty( + Syntax *syntax, + struct berval *val, + struct berval *out, + void *ctx ) +{ + return csnNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx ); +} #ifndef SUPPORT_OBSOLETE_UTC_SYNTAX /* slight optimization - does not need the start parameter */ @@ -3930,8 +5911,10 @@ firstComponentNormalize( if( val->bv_len < 3 ) return LDAP_INVALID_SYNTAX; - if( val->bv_val[0] != '(' /*')'*/ && - val->bv_val[0] != '{' /*'}'*/ ) + if( ! ( val->bv_val[0] == '(' /*')'*/ + && val->bv_val[val->bv_len - 1] == /*'('*/ ')' ) + && ! ( val->bv_val[0] == '{' /*'}'*/ + && val->bv_val[val->bv_len - 1] == /*'('*/ '}' ) ) { return LDAP_INVALID_SYNTAX; } @@ -3946,7 +5929,7 @@ firstComponentNormalize( /* grab next word */ comp.bv_val = &val->bv_val[len]; - len = val->bv_len - len; + len = val->bv_len - len - STRLENOF(/*"{"*/ "}"); for( comp.bv_len = 0; !ASCII_SPACE(comp.bv_val[comp.bv_len]) && comp.bv_len < len; comp.bv_len++ ) @@ -3970,6 +5953,12 @@ firstComponentNormalize( return rc; } +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", + NULL +}; #define X_BINARY "X-BINARY-TRANSFER-REQUIRED 'TRUE' " #define X_NOT_H_R "X-NOT-HUMAN-READABLE 'TRUE' " @@ -3977,188 +5966,229 @@ firstComponentNormalize( static slap_syntax_defs_rec syntax_defs[] = { {"( 1.3.6.1.4.1.1466.115.121.1.1 DESC 'ACI Item' " X_BINARY X_NOT_H_R ")", - SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL}, + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.2 DESC 'Access Point' " X_NOT_H_R ")", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' " X_NOT_H_R ")", - SLAP_SYNTAX_BLOB, blobValidate, NULL}, + SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' " X_NOT_H_R ")", - SLAP_SYNTAX_BER, berValidate, NULL}, + SLAP_SYNTAX_BER, NULL, berValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )", - 0, bitStringValidate, NULL }, + 0, NULL, bitStringValidate, NULL }, {"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )", - 0, booleanValidate, NULL}, + 0, NULL, booleanValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' " X_BINARY X_NOT_H_R ")", - SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, certificateValidate, NULL}, + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, certificateValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' " X_BINARY X_NOT_H_R ")", - SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, sequenceValidate, NULL}, + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, certificateListValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' " X_BINARY X_NOT_H_R ")", - SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, sequenceValidate, NULL}, + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, sequenceValidate, NULL}, + {"( " attributeCertificateSyntaxOID " DESC 'X.509 AttributeCertificate' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, attributeCertificateValidate, NULL}, +#if 0 /* need to go __after__ printableString */ {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )", - 0, countryStringValidate, NULL}, + 0, "1.3.6.1.4.1.1466.115.121.1.44", + countryStringValidate, NULL}, +#endif {"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )", - 0, dnValidate, dnPretty}, + 0, NULL, dnValidate, dnPretty}, {"( 1.2.36.79672281.1.5.0 DESC 'RDN' )", - 0, rdnValidate, rdnPretty}, + 0, NULL, rdnValidate, rdnPretty}, #ifdef LDAP_COMP_MATCH {"( 1.2.36.79672281.1.5.3 DESC 'allComponents' )", - 0, allComponentsValidate, NULL}, + 0, NULL, allComponentsValidate, NULL}, {"( 1.2.36.79672281.1.5.2 DESC 'componentFilterMatch assertion') ", - 0, componentFilterValidate, NULL}, + 0, NULL, componentFilterValidate, NULL}, #endif {"( 1.3.6.1.4.1.1466.115.121.1.13 DESC 'Data Quality' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )", - 0, deliveryMethodValidate, NULL}, + 0, NULL, deliveryMethodValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )", - 0, UTF8StringValidate, NULL}, + 0, NULL, UTF8StringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.19 DESC 'DSA Quality' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.20 DESC 'DSE Type' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )", - 0, printablesStringValidate, NULL}, + 0, NULL, printablesStringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' " X_NOT_H_R ")", - SLAP_SYNTAX_BLOB, NULL, NULL}, + SLAP_SYNTAX_BLOB, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )", - 0, generalizedTimeValidate, NULL}, + 0, NULL, generalizedTimeValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )", - 0, IA5StringValidate, NULL}, + 0, NULL, IA5StringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )", - 0, integerValidate, NULL}, + 0, NULL, integerValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' " X_NOT_H_R ")", - SLAP_SYNTAX_BLOB, blobValidate, NULL}, + SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.29 DESC 'Master And Shadow Access Points' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )", - 0, nameUIDValidate, nameUIDPretty }, + 0, NULL, nameUIDValidate, nameUIDPretty }, {"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )", - 0, numericStringValidate, NULL}, + 0, NULL, numericStringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )", - 0, numericoidValidate, NULL}, + 0, NULL, numericoidValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )", - 0, IA5StringValidate, NULL}, + 0, NULL, IA5StringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )", - 0, blobValidate, NULL}, + 0, NULL, blobValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )", - 0, UTF8StringValidate, NULL}, + 0, NULL, postalAddressValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )", - 0, printableStringValidate, NULL}, + 0, NULL, printableStringValidate, NULL}, + /* moved here because now depends on Directory String, IA5 String + * and Printable String */ + {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )", + 0, country_gen_syn, countryStringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )", #define subtreeSpecificationValidate UTF8StringValidate /* FIXME */ - 0, subtreeSpecificationValidate, NULL}, + 0, NULL, subtreeSpecificationValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' " X_BINARY X_NOT_H_R ")", - SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, berValidate, NULL}, + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, berValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )", - 0, printableStringValidate, NULL}, + 0, NULL, printableStringValidate, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )", - 0, printablesStringValidate, NULL}, + 0, NULL, printablesStringValidate, NULL}, #ifdef SUPPORT_OBSOLETE_UTC_SYNTAX {"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )", - 0, utcTimeValidate, NULL}, + 0, NULL, utcTimeValidate, NULL}, #endif {"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.55 DESC 'Modify Rights' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.56 DESC 'LDAP Schema Definition' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.57 DESC 'LDAP Schema Description' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, {"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )", - 0, NULL, NULL}, + 0, NULL, NULL, NULL}, /* RFC 2307 NIS Syntaxes */ {"( 1.3.6.1.1.1.0.0 DESC 'RFC2307 NIS Netgroup Triple' )", - 0, nisNetgroupTripleValidate, NULL}, + 0, NULL, nisNetgroupTripleValidate, NULL}, {"( 1.3.6.1.1.1.0.1 DESC 'RFC2307 Boot Parameter' )", - 0, bootParameterValidate, NULL}, + 0, NULL, bootParameterValidate, NULL}, /* draft-zeilenga-ldap-x509 */ {"( 1.3.6.1.1.15.1 DESC 'Certificate Exact Assertion' )", - SLAP_SYNTAX_HIDE, + SLAP_SYNTAX_HIDE, NULL, serialNumberAndIssuerValidate, serialNumberAndIssuerPretty}, {"( 1.3.6.1.1.15.2 DESC 'Certificate Assertion' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, {"( 1.3.6.1.1.15.3 DESC 'Certificate Pair Exact Assertion' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, {"( 1.3.6.1.1.15.4 DESC 'Certificate Pair Assertion' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, {"( 1.3.6.1.1.15.5 DESC 'Certificate List Exact Assertion' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, + issuerAndThisUpdateValidate, + issuerAndThisUpdatePretty}, {"( 1.3.6.1.1.15.6 DESC 'Certificate List Assertion' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, {"( 1.3.6.1.1.15.7 DESC 'Algorithm Identifier' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + {"( " attributeCertificateExactAssertionSyntaxOID " DESC 'AttributeCertificate Exact Assertion' )", + SLAP_SYNTAX_HIDE, NULL, + serialNumberAndIssuerSerialValidate, + serialNumberAndIssuerSerialPretty}, + {"( " attributeCertificateAssertionSyntaxOID " DESC 'AttributeCertificate Assertion' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, #ifdef SLAPD_AUTHPASSWD /* needs updating */ {"( 1.3.6.1.4.1.4203.666.2.2 DESC 'OpenLDAP authPassword' )", - SLAP_SYNTAX_HIDE, NULL, NULL}, + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, #endif {"( 1.3.6.1.1.16.1 DESC 'UUID' )", - 0, UUIDValidate, UUIDPretty}, + 0, NULL, UUIDValidate, UUIDPretty}, {"( 1.3.6.1.4.1.4203.666.11.2.1 DESC 'CSN' )", - SLAP_SYNTAX_HIDE, csnValidate, NULL}, + SLAP_SYNTAX_HIDE, NULL, csnValidate, csnPretty }, + + {"( 1.3.6.1.4.1.4203.666.11.2.4 DESC 'CSN SID' )", + SLAP_SYNTAX_HIDE, NULL, sidValidate, sidPretty }, /* OpenLDAP Void Syntax */ {"( 1.3.6.1.4.1.4203.1.1.1 DESC 'OpenLDAP void' )" , - SLAP_SYNTAX_HIDE, inValidate, NULL}, + SLAP_SYNTAX_HIDE, NULL, inValidate, NULL}, /* FIXME: OID is unused, but not registered yet */ {"( 1.3.6.1.4.1.4203.666.2.7 DESC 'OpenLDAP authz' )", - SLAP_SYNTAX_HIDE, authzValidate, authzPretty}, + SLAP_SYNTAX_HIDE, NULL, authzValidate, authzPretty}, - {NULL, 0, NULL, NULL} + {NULL, 0, NULL, NULL, NULL} }; +char *csnSIDMatchSyntaxes[] = { + "1.3.6.1.4.1.4203.666.11.2.1" /* csn */, + NULL +}; char *certificateExactMatchSyntaxes[] = { "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */, NULL }; +char *certificateListExactMatchSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */, + NULL +}; +char *attributeCertificateExactMatchSyntaxes[] = { + attributeCertificateSyntaxOID /* attributeCertificate */, + NULL +}; + #ifdef LDAP_COMP_MATCH char *componentFilterMatchSyntaxes[] = { "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */, + "1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */, + attributeCertificateSyntaxOID /* attributeCertificate */, NULL }; #endif + char *directoryStringSyntaxes[] = { "1.3.6.1.4.1.1466.115.121.1.44" /* printableString */, NULL @@ -4190,8 +6220,6 @@ char *objectIdentifierFirstComponentMatchSyntaxes[] = { * 2.5.13.33* keywordMatch * 2.5.13.36+ certificatePairExactMatch * 2.5.13.37+ certificatePairMatch - * 2.5.13.38+ certificateListExactMatch - * 2.5.13.39+ certificateListMatch * 2.5.13.40+ algorithmIdentifierMatch * 2.5.13.41* storedPrefixMatch * 2.5.13.42 attributeCertificateMatch @@ -4362,7 +6390,9 @@ static slap_mrule_defs_rec mrule_defs[] = { {"( 2.5.13.11 NAME 'caseIgnoreListMatch' " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, - NULL, NULL, NULL, NULL, NULL, 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 )", @@ -4379,14 +6409,14 @@ static slap_mrule_defs_rec mrule_defs[] = { {"( 2.5.13.14 NAME 'integerMatch' " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", - SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL, NULL, NULL, integerMatch, - octetStringIndexer, octetStringFilter, + integerIndexer, integerFilter, NULL }, {"( 2.5.13.15 NAME 'integerOrderingMatch' " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", - SLAP_MR_ORDERING, NULL, + SLAP_MR_ORDERING | SLAP_MR_ORDERED_INDEX, NULL, NULL, NULL, integerMatch, NULL, NULL, "integerMatch" }, @@ -4484,12 +6514,8 @@ static slap_mrule_defs_rec mrule_defs[] = { {"( 2.5.13.34 NAME 'certificateExactMatch' " "SYNTAX 1.3.6.1.1.15.1 )", SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes, -#ifdef HAVE_TLS NULL, certificateExactNormalize, octetStringMatch, octetStringIndexer, octetStringFilter, -#else - NULL, NULL, NULL, NULL, NULL, -#endif NULL }, {"( 2.5.13.35 NAME 'certificateMatch' " @@ -4498,6 +6524,32 @@ static slap_mrule_defs_rec mrule_defs[] = { NULL, NULL, NULL, NULL, NULL, NULL }, + {"( 2.5.13.38 NAME 'certificateListExactMatch' " + "SYNTAX 1.3.6.1.1.15.5 )", + 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 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL }, + + {"( 2.5.13.45 NAME 'attributeCertificateExactMatch' " + "SYNTAX " attributeCertificateExactAssertionSyntaxOID " )", + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, attributeCertificateExactMatchSyntaxes, + NULL, attributeCertificateExactNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.46 NAME 'attributeCertificateMatch' " + "SYNTAX " attributeCertificateAssertionSyntaxOID " )", + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL }, + {"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, @@ -4567,17 +6619,24 @@ static slap_mrule_defs_rec mrule_defs[] = { {"( 1.3.6.1.4.1.4203.666.11.2.2 NAME 'CSNMatch' " "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )", SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_ORDERED_INDEX, NULL, - NULL, NULL, csnMatch, + NULL, csnNormalize, csnMatch, csnIndexer, csnFilter, NULL}, {"( 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, + NULL, csnNormalize, csnOrderingMatch, NULL, NULL, "CSNMatch" }, + {"( 1.3.6.1.4.1.4203.666.11.2.5 NAME 'CSNSIDMatch' " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.4 )", + SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_EXT, csnSIDMatchSyntaxes, + NULL, csnSidNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + /* 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 )",