X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fdn.c;h=b1168901c001dac8fadb72f57778ef1e983be181;hb=9b4bf8a973c8cfea809be9a4b658f785a6b16f2f;hp=cbd8dba91fcb05767f3cd7f470eb4f4758bded23;hpb=a453d7eacffdbc582b72b8a1b27fef1ca09370bc;p=openldap diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c index cbd8dba91f..b1168901c0 100644 --- a/servers/slapd/dn.c +++ b/servers/slapd/dn.c @@ -1,22 +1,7 @@ /* dn.c - routines for dealing with distinguished names */ /* $OpenLDAP$ */ /* - * The functions normalize_unicode(), get_hexpair(), write_hex_pair(), - * get_next_byte(), get_next_char(), get_ber_length(), - * ber_parse_primitive_string(), ber_parse_string(), String_normalize(), - * DirectoryString_normalize(), PrintableString_normalize(), - * IA5String_normalize(), ber_parse_primitive_bitstring(), - * ber_parse_bitstring(), getNext8bits(), bitString_normalize(), match_oid(), - * match_key(), get_validated_av_in_dn(), get_validated_rdn_in_dn(), - * and get_validated_dn() in this file were developed at the National Institute - * of Standards and Technology by employees of the Federal Government in the - * course of their official duties. Pursuant to title 17 Section 105 of the - * United States Code the code in these functions is not subject to copyright - * protection and is in the public domain. The copyright for all other code in - * this file is as specified below. - */ -/* - * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved. + * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved. * COPYING RESTRICTIONS APPLY, see COPYRIGHT file */ @@ -33,2851 +18,926 @@ #include "slap.h" -#define B4LEADTYPE 0 -#define B4TYPE 1 -#define INOIDTYPE 2 -#define INKEYTYPE 3 -#define B4EQUAL 4 -#define B4VALUE 5 -#define INVALUE 6 -#define INQUOTEDVALUE 7 -#define B4SEPARATOR 8 -#define INBERENCODEDVALUE 9 - -#define UTF8DN 1 - -typedef int (*av_normalize_type)(char **, char **, int *, int, int, int, unsigned long *); - -#define PRINTABLE_STRING 1 -#define IA5_STRING 2 -#define TELETEX_STRING 3 -#define BMP_STRING 4 -#define UNIVERSAL_STRING 5 -#define UTF8_STRING 6 -#define DIRECTORY_STRING 7 - -/* unnormalized_unicode contains a string of ucs4 encoded unicode characters of length - * len. Place in *d a normalized UTF8 encoded version of unnormalized_unicode. If firstchar is - * true, then the first character output by uccanoncomp is the first character of the - * attribute value. If successful, return 1 and advance *d to the end of the UTF8 encoded string. - * Otherwise, return 0. +#include "lutil.h" + +/* + * The DN syntax-related functions take advantage of the dn representation + * handling functions ldap_str2dn/ldap_dn2str. The latter are not schema- + * aware, so the attributes and their values need be validated (and possibly + * normalized). In the current implementation the required validation/nor- + * malization/"pretty"ing are done on newly created DN structural represen- + * tations; however the idea is to move towards DN handling in structural + * representation instead of the current string representation. To this + * purpose, we need to do only the required operations and keep track of + * what has been done to minimize their impact on performances. + * + * Developers are strongly encouraged to use this feature, to speed-up + * its stabilization. + */ + +#define AVA_PRIVATE( ava ) ( ( AttributeDescription * )(ava)->la_private ) + +/* + * In-place, schema-aware validation of the + * structural representation of a distinguished name. */ static int -normalize_unicode(unsigned long *unnormalized_unicode, int len, char **d, int *av_length) { - unsigned long *normalized_unicode; - int i, normalized_len, char_len; - char tmp; - -#ifdef UTF8DN - i = uccanondecomp(unnormalized_unicode, len, &normalized_unicode, &normalized_len); - if ( (i == -1) || (normalized_unicode == NULL) ) - return 0; - normalized_len = uccanoncomp(normalized_unicode, normalized_len); - - char_len = ldap_ucs4_to_utf8(normalized_unicode[0], *d); - *d += char_len; - - for(i=1; i < normalized_len; i++) { - char_len = ldap_ucs4_to_utf8(normalized_unicode[i], *d); - tmp = **d; - if ( RDN_NEEDSESCAPE( tmp ) || RDN_SPECIAL( tmp ) ) { - **d = '\\'; - *d += 1; - **d = tmp; - *d+= 1; - } else if ( ASCII_WHITESPACE( tmp ) && ASCII_SPACE( *(*d - 1) ) ) { - /* There should not be two consequtive space characters in the - * normalized string. */ - normalized_len--; - } else { - *d += char_len; +LDAPDN_validate( LDAPDN *dn ) +{ + int iRDN; + int rc; + + assert( dn ); + + for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + int iAVA; + + assert( rdn ); + + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + AttributeDescription *ad; + slap_syntax_validate_func *validate = NULL; + + assert( ava ); + + if ( ( ad = AVA_PRIVATE( ava ) ) == NULL ) { + const char *text = NULL; + + rc = slap_bv2ad( &ava->la_attr, &ad, &text ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + ava->la_private = ( void * )ad; + } + + /* + * Replace attr oid/name with the canonical name + */ + ava->la_attr = ad->ad_cname; + + validate = ad->ad_type->sat_syntax->ssyn_validate; + + if ( validate ) { + /* + * validate value by validate function + */ + rc = ( *validate )( ad->ad_type->sat_syntax, + &ava->la_value ); + + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + } } } - *av_length += normalized_len; - ch_free(normalized_unicode); -#endif - - return 1; + return LDAP_SUCCESS; } -/* The next two bytes in the string beginning at *sec should be - * a pair of hexadecimal characters. If they are, the value of that - * hexpair is placed in *out and 1 is returned. Otherwise, 0 is returned. +/* + * dn validate routine */ -static int -get_hexpair(char **src, unsigned char *out) +int +dnValidate( + Syntax *syntax, + struct berval *in ) { - unsigned char ch; + int rc; + LDAPDN *dn = NULL; - ch = **src; + assert( in ); - if ( !ASCII_XDIGIT(ch) ) { - return 0; - } + if ( in->bv_len == 0 ) { + return LDAP_SUCCESS; - if ( ASCII_DIGIT(ch) ) { - *out = ch - '0'; - } else if ( ch >= 'A' && ch <= 'F' ) { - *out = ch - 'A' + 10; - } else { - *out = ch - 'a' + 10; + } else if ( in->bv_len > SLAP_LDAPDN_MAXLEN ) { + return LDAP_INVALID_SYNTAX; } - *src += 1; - - ch = **src; - - if ( !ASCII_XDIGIT(ch) ) { - return 0; + rc = ldap_bv2dn( in, &dn, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; } - *out = *out << 4; + assert( strlen( in->bv_val ) == in->bv_len ); - if ( ASCII_DIGIT(ch) ) { - *out += ch - '0'; - } else if ( ch >= 'A' && ch <= 'F' ) { - *out += ch - 'A' + 10; - } else { - *out += ch - 'a' + 10; - } + /* + * Schema-aware validate + */ + rc = LDAPDN_validate( dn ); + ldap_dnfree( dn ); - *src += 1; + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } - return 1; + return LDAP_SUCCESS; } - -/* output in as a hexadecimal pair to the string pointed to be *d and advance *d to the end - * of the hexpair. +/* + * AVA sorting inside a RDN + * + * rule: sort attributeTypes in alphabetical order; in case of multiple + * occurrences of the same attributeType, sort values in byte order + * (use memcmp, which implies alphabetical order in case of IA5 value; + * this should guarantee the repeatability of the operation). + * + * Note: the sorting can be slightly improved by sorting first + * by attribute type length, then by alphabetical order. + * + * uses a linear search; should be fine since the number of AVAs in + * a RDN should be limited. */ static void -write_hex_pair(char **d, unsigned char in) { - unsigned char upper_nibble, lower_nibble; - - upper_nibble = (in & 0xF0) >> 4; - lower_nibble = in & 0x0F; +AVA_Sort( LDAPRDN *rdn, int iAVA ) +{ + int i; + LDAPAVA *ava_in = rdn[ 0 ][ iAVA ]; - if (upper_nibble < 10) - **d = upper_nibble + '0'; - else - **d = upper_nibble - 10 + 'A'; + assert( rdn ); + assert( ava_in ); + + for ( i = 0; i < iAVA; i++ ) { + LDAPAVA *ava = rdn[ 0 ][ i ]; + int a, j; - *d += 1; + assert( ava ); - if (lower_nibble < 10) - **d = lower_nibble + '0'; - else - **d = lower_nibble - 10 + 'A'; + a = strcmp( ava_in->la_attr.bv_val, ava->la_attr.bv_val ); - *d += 1; -} + if ( a > 0 ) { + break; + } + while ( a == 0 ) { + int v, d; -/* The string beginning at *src represents a octet. - * The octet is either represented by a single byte or - * a '\' followed by a 2-byte hexpair or a single byte. - * Place the octet in *out, increment *src to the beginning - * of the next character. If the representation of the octet - * began with a '\' then *is_escaped is set to 1. Otherwise, - * *is_escaped is set to 0. If the string beginning at *src - * does not represent a well formed octet, then 0 is returned. - * Otherwise 1 is returned. - */ -static int -get_next_byte(char **src, unsigned char *out, int *is_escaped) -{ - unsigned char tmp; - unsigned char s1, s2; + d = ava_in->la_value.bv_len - ava->la_value.bv_len; - s1 = **src; - if (s1 == '\0') - return 0; + v = memcmp( ava_in->la_value.bv_val, + ava->la_value.bv_val, + d <= 0 ? ava_in->la_value.bv_len + : ava->la_value.bv_len ); - *src += 1; + if ( v == 0 && d != 0 ) { + v = d; + } - if ( s1 != '\\' ) { - *out = s1; - *is_escaped = 0; - return 1; - } + if ( v <= 0 ) { + /* + * got it! + */ + break; + } - *is_escaped = 1; + if ( ++i == iAVA ) { + /* + * already sorted + */ + return; + } - s1 = **src; - if ( s1 == '\0' ) - return 0; + ava = rdn[ 0 ][ i ]; + a = strcmp( ava_in->la_attr.bv_val, + ava->la_attr.bv_val ); + } - if ( !ASCII_XDIGIT( s1 ) ) { - *src += 1; - *out = s1; - return 1; - } else { - if ( get_hexpair(src, &s2) ) { - *out = s2; - return 1; - } else { - return 0; + /* + * move ahead + */ + for ( j = iAVA; j > i; j-- ) { + rdn[ 0 ][ j ] = rdn[ 0 ][ j - 1 ]; } + rdn[ 0 ][ i ] = ava_in; + + return; } } - -/* If the string beginning at *src is a well formed UTF8 character, - * then the value of that character is placed in *out and 1 is returned. - * If the string is not a well formed UTF8 character, 0 is returned. - * If the character is an ASCII character, and its representation began - * with a '\', then *is_escaped is set to 1. Otherwise *is_escaped is set to 0. - * When the function returns, *src points to the first byte after the character. +/* + * In-place, schema-aware normalization / "pretty"ing of the + * structural representation of a distinguished name. */ static int -get_next_char(char **src, unsigned long int *out, int *is_escaped) +LDAPDN_rewrite( LDAPDN *dn, unsigned flags ) { - unsigned char tmp; - int i, res, len; - unsigned long int ch; + int iRDN; + int rc; - static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + assert( dn ); - res = get_next_byte( src, &tmp, is_escaped ); + for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) { + LDAPRDN *rdn = dn[ 0 ][ iRDN ]; + int iAVA; - *out = tmp; + assert( rdn ); -#ifndef UTF8DN - return res; -#else - if ( ( res == 0 ) || ( tmp < 128 ) ) { - return res; - } + for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ 0 ][ iAVA ]; + AttributeDescription *ad; + slap_syntax_validate_func *validf = NULL; +#ifdef SLAP_NVALUES + slap_mr_normalize_func *normf = NULL; +#endif + slap_syntax_transform_func *transf = NULL; + MatchingRule *mr = NULL; + struct berval bv = { 0, NULL }; + int do_sort = 0; - /* This is a UTF8 encoded, non-ASCII character */ - len = ldap_utf8_charlen( &tmp ); + assert( ava ); - if ( len == 0 ) - return 0; + if ( ( ad = AVA_PRIVATE( ava ) ) == NULL ) { + const char *text = NULL; - ch = tmp & mask[len]; + rc = slap_bv2ad( &ava->la_attr, &ad, &text ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + ava->la_private = ( void * )ad; + do_sort = 1; + } - for(i=1; i < len; i++) { - res = get_next_byte( src, &tmp, is_escaped ); - if ( ( res == 0) || ( ( tmp & 0xc0 ) != 0x80 ) ) return 0; + /* + * Replace attr oid/name with the canonical name + */ + ava->la_attr = ad->ad_cname; - ch <<= 6; - ch |= tmp & 0x3f; - } + if( ava->la_flags & LDAP_AVA_BINARY ) { + if( ava->la_value.bv_len == 0 ) { + /* BER encoding is empty */ + return LDAP_INVALID_SYNTAX; + } - *is_escaped = 0; - *out = ch; + /* AVA is binary encoded, don't muck with it */ + } else if( flags & SLAP_LDAPDN_PRETTY ) { + transf = ad->ad_type->sat_syntax->ssyn_pretty; + if( !transf ) { + validf = ad->ad_type->sat_syntax->ssyn_validate; + } + } else { /* normalization */ + validf = ad->ad_type->sat_syntax->ssyn_validate; + mr = ad->ad_type->sat_equality; +#ifdef SLAP_NVALUES + if( mr ) normf = mr->smr_normalize; +#else + transf = ad->ad_type->sat_syntax->ssyn_normalize; #endif -} - - -/* The string beginning at *s should be an ASCII-hex encoding of BER encoded - * length data. If so, place the length in *length, add the length of the - * length encoding to *encoded_length, advance *s to next byte after the end - * of the length encoding, and return 1. Otherwise, return 0. - */ -static int -get_ber_length( - char **s, - unsigned int *encoded_length, - unsigned long int *length -) -{ - int res; - unsigned char ch, ch2; + } - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + if ( validf ) { + /* validate value before normalization */ + rc = ( *validf )( ad->ad_type->sat_syntax, + ava->la_value.bv_len + ? &ava->la_value + : (struct berval *) &slap_empty_bv ); - *encoded_length += 1; + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + } - if ( (ch & 0x80) == 0) { - /* Bit 8 is 0, so this byte gives the length */ - *length = ch; - } else { - /* This byte specifies the number of remaining length octets */ - ch = ch & 0x7F; + if ( transf ) { + /* +#ifdef SLAP_NVALUES + * transform value by pretty function +#else + * transform value by normalize/pretty function +#endif + * if value is empty, use empty_bv + */ + rc = ( *transf )( ad->ad_type->sat_syntax, + ava->la_value.bv_len + ? &ava->la_value + : (struct berval *) &slap_empty_bv, + &bv ); + + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + } - if (ch > 4) { - /* This assumes that length can hold up to a 32-bit - * integer and that bit strings will always be shorter - * than 2**32 bytes. - */ - return 0; - } +#ifdef SLAP_NVALUES + if ( normf ) { + /* + * normalize value + * if value is empty, use empty_bv + */ + rc = ( *normf )( + 0, + ad->ad_type->sat_syntax, + mr, + ava->la_value.bv_len + ? &ava->la_value + : (struct berval *) &slap_empty_bv, + &bv ); + + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + } - *length = 0; - while (ch > 0) { - *length = *length << 8; +#else + if( mr && ( mr->smr_usage & SLAP_MR_DN_FOLD ) ) { + char *s = bv.bv_val; - res = get_hexpair(s, &ch2); - if (res == 0) - return 0; + if ( UTF8bvnormalize( &bv, &bv, + LDAP_UTF8_CASEFOLD ) == NULL ) { + return LDAP_INVALID_SYNTAX; + } + free( s ); + } +#endif - *encoded_length += 1; - *length = *length | ch2; + if( bv.bv_val ) { + free( ava->la_value.bv_val ); + ava->la_value = bv; + } - ch--; + if( do_sort ) AVA_Sort( rdn, iAVA ); } } - return 1; + return LDAP_SUCCESS; } - -/* The string beginning at *s should be an ASCII-hex encoding of a BER - * encoded string of type string_type (minus the "tag" octet) in which the - * encoding is primitive, definite length. If it is, write a UTF8 encoding - * of the string, according to RFC 2253, to *d, advance *s to one byte after - * the end of the BER encoded string, advance *d to one byte after the UTF8 - * encoded string, add to *encoded_length the length of the BER encoding, add - * to *av_length the number of UTF8 characters written to *d, set *firstchar - * to 0 if any characters are written to *d, and return 1. Otherwise, return - * 0. If make_uppercase is 1, write all of the characters in uppercase. If - * not, write the characters as they occur in the BER encoding. If - * normalize is 1, remove all leading and trailing whitespace, and - * compress all whitespace between words to a single space. If not, transfer - * whitespace from the BER encoding to the UTF8 encoding unchanged. - */ -static int -ber_parse_primitive_string( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int string_type, - unsigned int *encoded_length, - int *firstchar, - unsigned long *unnormalized_unicode, - int *unnormalized_unicode_len -) +int +#ifdef SLAP_NVALUES +dnNormalize( + slap_mask_t use, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *out ) +#else +dnNormalize( + Syntax *syntax, + struct berval *val, + struct berval *out ) +#endif { - int i, len, res; - unsigned char ch; - unsigned long int uch; - unsigned long int length; - char tmp; + assert( val ); + assert( out ); - static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; - - res = get_ber_length(s, encoded_length, &length); - if (res == 0) - return 0; + Debug( LDAP_DEBUG_TRACE, ">>> dnNormalize: <%s>\n", val->bv_val, 0, 0 ); - while (length > 0) { - /* read in next character */ - if (string_type == PRINTABLE_STRING) { - /* each character is one byte */ - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + if ( val->bv_len != 0 ) { + LDAPDN *dn = NULL; + int rc; - *encoded_length += 1; - length -= 1; + /* + * Go to structural representation + */ + rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } - if ( !SLAP_PRINTABLE(ch) ) - return 0; + assert( strlen( val->bv_val ) == val->bv_len ); - uch = ch; + /* + * Schema-aware rewrite + */ + if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) { + ldap_dnfree( dn ); + return LDAP_INVALID_SYNTAX; + } - } else if (string_type == IA5_STRING) { - /* each character is one byte */ - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + /* + * Back to string representation + */ + rc = ldap_dn2bv( dn, out, + LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY ); - *encoded_length += 1; - length -= 1; + ldap_dnfree( dn ); - if ( !SLAP_IA5(ch) ) - return 0; + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + } else { + ber_dupbv( out, val ); + } - uch = ch; + Debug( LDAP_DEBUG_TRACE, "<<< dnNormalize: <%s>\n", out->bv_val, 0, 0 ); - } else if (string_type == TELETEX_STRING) { - /* This code is not correct. Each character is one byte. - * However, the enocodings need to be transliterated to - * unicode. - */ - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + return LDAP_SUCCESS; +} - *encoded_length += 1; - length -= 1; +#if 0 +/* + * dn "pretty"ing routine + */ +int +dnPretty( + Syntax *syntax, + struct berval *val, + struct berval **pretty) +{ + struct berval *out; + int rc; - uch = ch; + assert( pretty && *pretty == NULL ); - } else if (string_type == BMP_STRING) { - /* This is a 2-byte unicode character */ - if (length < 2) - return 0; + out = ch_malloc( sizeof( struct berval ) ); + rc = dnPretty2( syntax, val, out ); + if ( rc != LDAP_SUCCESS ) + free( out ); + else + *pretty = out; + return rc; +} +#endif - uch = 0; +int +dnPretty2( + Syntax *syntax, + struct berval *val, + struct berval *out) +{ + assert( val ); + assert( out ); - for(i=0; i < 2; i++) { - res = get_hexpair(s, &ch); - if (res == 0) - return 0; +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ARGS, ">>> dnPretty: <%s>\n", val->bv_val, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, ">>> dnPretty: <%s>\n", val->bv_val, 0, 0 ); +#endif - uch = uch << 8; - uch = uch | ch; - } + if ( val->bv_len == 0 ) { + ber_dupbv( out, val ); - *encoded_length += 2; - length -= 2; - } else if (string_type == UNIVERSAL_STRING) { - /* This is a 4-byte unicode character */ - if (length < 4) - return 0; + } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) { + return LDAP_INVALID_SYNTAX; - uch = 0; + } else { + LDAPDN *dn = NULL; + int rc; - for(i=0; i < 4; i++) { - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + /* FIXME: should be liberal in what we accept */ + rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } - uch = uch << 8; - uch = uch | ch; - } + assert( strlen( val->bv_val ) == val->bv_len ); - *encoded_length += 4; - length -= 4; - } else if (string_type == UTF8_STRING) { - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + /* + * Schema-aware rewrite + */ + if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY ) != LDAP_SUCCESS ) { + ldap_dnfree( dn ); + return LDAP_INVALID_SYNTAX; + } - *encoded_length += 1; + /* FIXME: not sure why the default isn't pretty */ + /* RE: the default is the form that is used as + * an internal representation; the pretty form + * is a variant */ + rc = ldap_dn2bv( dn, out, + LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY ); - #ifndef UTF8DN - /* Not sure what to do here */ - uch = ch; - length -= 1; - #else - len = ldap_utf8_charlen( &ch ); - if ( ( len == 0) || ( length < len ) ) - return 0; + ldap_dnfree( dn ); - uch = ch & mask[len]; + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + } - for(i=1; i < len; i++) { - res = get_hexpair(s, &ch); - if ( ( res == 0) || ( ( ch & 0xc0 ) != 0x80 ) ) return 0; + Debug( LDAP_DEBUG_TRACE, "<<< dnPretty: <%s>\n", out->bv_val, 0, 0 ); - *encoded_length += 1; + return LDAP_SUCCESS; +} - uch <<= 6; - uch |= ch & 0x3f; - } +int +dnPrettyNormalDN( + Syntax *syntax, + struct berval *val, + LDAPDN **dn, + int flags ) +{ + assert( val ); + assert( dn ); - length -= len; - #endif - } else { - /* Unknown string type */ - return 0; - } +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ARGS, ">>> dn%sDN: <%s>\n", + flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal", + val->bv_val, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, ">>> dn%sDN: <%s>\n", + flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal", + val->bv_val, 0 ); +#endif - /* Now add character to *d */ + if ( val->bv_len == 0 ) { + return LDAP_SUCCESS; - #ifdef UTF8DN - if (make_uppercase) { - uch = uctoupper( uch ); - } + } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) { + return LDAP_INVALID_SYNTAX; - if ( (uch < 128) && (*unnormalized_unicode_len > 0) ) { - res = normalize_unicode(unnormalized_unicode, *unnormalized_unicode_len, d, av_length); - if (res == 0) - return 0; - *unnormalized_unicode_len = 0; - } + } else { + int rc; - if ( !normalize || !ASCII_WHITESPACE(uch) ) { - if ( (*firstchar) && ASCII_SPACE(uch) ) { - **d = '\\'; - *d += 1; - **d = '2'; - *d += 1; - **d = '0'; - *d += 1; - *av_length += 1; - } else { - if ( normalize && (uch > 127) ) { - if (*unnormalized_unicode_len == 0) { - /* The previous output character must be ASCII - * and it should be normalized. - */ - *d -= 1; - unnormalized_unicode[0] = **d; - *unnormalized_unicode_len = 1; - *av_length -= 1; - } - unnormalized_unicode[*unnormalized_unicode_len] = uch; - *unnormalized_unicode_len += 1; - } else { - len = ldap_ucs4_to_utf8( uch, *d ); - tmp = **d; - if ( RDN_NEEDSESCAPE( tmp ) || RDN_SPECIAL( tmp ) ) { - **d = '\\'; - *d += 1; - **d = tmp; - *d += 1; - } else if ( (*firstchar) && ( uch == '#' ) ) { - **d = '\\'; - *d += 1; - **d = tmp; - *d += 1; - } else { - *d += len; - } - *av_length += 1; - } - } - *firstchar = 0; - } else if ( !(*firstchar) && !ASCII_SPACE( *(*d - 1) ) ) { - **d = ' '; - *d += 1; - *av_length += 1; - } - #else - /* Not sure what to do here either */ - if (uch > 127) - return 0; + /* FIXME: should be liberal in what we accept */ + rc = ldap_bv2dn( val, dn, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } - if (make_uppercase) { - uch = TOUPPER( uch ); - } + assert( strlen( val->bv_val ) == val->bv_len ); - if ( !normalize || !ASCII_WHITESPACE(uch) ) { - if ( (*firstchar) && ASCII_SPACE(uch) ) { - **d = '\\'; - *d += 1; - **d = '2'; - *d += 1; - **d = '0'; - *d += 1; - } else { - if ( RDN_NEEDSESCAPE( uch ) || RDN_SPECIAL( uch ) ) { - **d = '\\'; - *d += 1; - } else if ( (*firstchar) && ( uch == '#' ) ) { - **d = '\\'; - *d += 1; - } - **d = uch; - *d += 1; - } - *firstchar = 0; - *av_length += 1; - } else if ( !(*firstchar) && !ASCII_SPACE( *(*d - 1) ) ) { - **d = ' '; - *d += 1; - *av_length += 1; - } - #endif + /* + * Schema-aware rewrite + */ + if ( LDAPDN_rewrite( *dn, flags ) != LDAP_SUCCESS ) { + ldap_dnfree( *dn ); + *dn = NULL; + return LDAP_INVALID_SYNTAX; + } } - return 1; -} + Debug( LDAP_DEBUG_TRACE, "<<< dn%sDN\n", + flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal", + 0, 0 ); + return LDAP_SUCCESS; +} -/* The string beginning at *s should be an ASCII-hex encoding of a BER - * encoded string of type string_type. If it is, write a UTF8 encoding - * of the string, according to RFC 2253, to *d, advance *s to one byte after - * the end of the BER encoded string, advance *d to one byte after the UTF8 - * encoded string, add to *encoded_length the length of the BER encoding, add - * to *av_length the number of UTF8 characters written to *d, set *firstchar - * to 0 if any characters are written to *d, and return 1. Otherwise, return - * 0. If make_uppercase is 1, write all of the characters in uppercase. If - * not, write the characters as they occur in the BER encoding. If - * normalize is 1, remove all leading and trailing whitespace, and - * compress all whitespace between words to a single space. If not, transfer - * whitespace from the BER encoding to the UTF8 encoding unchanged. +/* + * Combination of both dnPretty and dnNormalize */ -static int -ber_parse_string( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int string_type, - unsigned int *encoded_length, - int *firstchar, - unsigned long *unnormalized_unicode, - int *unnormalized_unicode_len -) +int +dnPrettyNormal( + Syntax *syntax, + struct berval *val, + struct berval *pretty, + struct berval *normal) { - int res; - unsigned char ch, tag, encoding_method; - int ber_string_type; - unsigned long int length; - unsigned int component_encoded_length; - - res = get_hexpair(s, &ch); - if (res == 0) - return 0; +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, ">>> dnPrettyNormal: <%s>\n", val->bv_val, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, ">>> dnPrettyNormal: <%s>\n", val->bv_val, 0, 0 ); +#endif - *encoded_length = 1; - - /* zero out bit 5 */ - tag = ch & 0xDF; - - if (tag == 12) - ber_string_type = UTF8_STRING; - else if (tag == 19) - ber_string_type = PRINTABLE_STRING; - else if (tag == 20) - ber_string_type = TELETEX_STRING; - else if (tag == 22) - ber_string_type = IA5_STRING; - else if (tag == 28) - ber_string_type = UNIVERSAL_STRING; - else if (tag == 30) - ber_string_type = BMP_STRING; - else { - /* Unknown string type or not a string type */ - return 0; - } + assert( val ); + assert( pretty ); + assert( normal ); - /* Check that this is an acceptable string type */ - if ( ber_string_type == string_type ) { - /* OK */ - } else if ( ( string_type == DIRECTORY_STRING ) && - ( ( ber_string_type == PRINTABLE_STRING ) || - ( ber_string_type == TELETEX_STRING ) || - ( ber_string_type == BMP_STRING ) || - ( ber_string_type == UNIVERSAL_STRING ) || - ( ber_string_type == UTF8_STRING ) ) ) { - /* OK */ - } else { - /* Bad string type */ - return 0; - } + if ( val->bv_len == 0 ) { + ber_dupbv( pretty, val ); + ber_dupbv( normal, val ); - /* Bit 5 specifies the encoding method */ - encoding_method = ch & 0x20; + } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) { + /* too big */ + return LDAP_INVALID_SYNTAX; - if (encoding_method == 0) { - /* Primitive, definite-length encoding */ - res = ber_parse_primitive_string(s, d, av_length, make_uppercase, normalize, ber_string_type, encoded_length, firstchar, unnormalized_unicode, unnormalized_unicode_len); - if (res == 0) - return 0; } else { - /* Constructed encoding */ + LDAPDN *dn = NULL; + int rc; - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + pretty->bv_val = NULL; + normal->bv_val = NULL; + pretty->bv_len = 0; + normal->bv_len = 0; - if (ch == 128) { - /* Constructed, indefinite-length */ - *encoded_length += 1; + /* FIXME: should be liberal in what we accept */ + rc = ldap_bv2dn( val, &dn, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + assert( strlen( val->bv_val ) == val->bv_len ); + + /* + * Schema-aware rewrite + */ + if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY ) != LDAP_SUCCESS ) { + ldap_dnfree( dn ); + return LDAP_INVALID_SYNTAX; + } - while (ch != 0) { - res = ber_parse_string(s, d, av_length, make_uppercase, normalize, ber_string_type, &component_encoded_length, firstchar, unnormalized_unicode, unnormalized_unicode_len); - if (res == 0) - return 0; + rc = ldap_dn2bv( dn, pretty, + LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY ); - *encoded_length += component_encoded_length; + if ( rc != LDAP_SUCCESS ) { + ldap_dnfree( dn ); + return LDAP_INVALID_SYNTAX; + } - /* Must end in "0000" */ - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) { + ldap_dnfree( dn ); + free( pretty->bv_val ); + pretty->bv_val = NULL; + pretty->bv_len = 0; + return LDAP_INVALID_SYNTAX; + } - if (ch == 0) { - res = get_hexpair(s, &ch); - if ( (res == 0) || (ch != 0) ) - return 0; + rc = ldap_dn2bv( dn, normal, + LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY ); - *encoded_length += 2; - } else { - *s -= 2; - } - } - } else { - /* Constructed, definite-length */ - *s -= 2; - res = get_ber_length(s, encoded_length, &length); - if (res == 0) - return 0; - - while (length > 0) { - res = ber_parse_string(s, d, av_length, make_uppercase, normalize, ber_string_type, &component_encoded_length, firstchar, unnormalized_unicode, unnormalized_unicode_len); - if ( (res == 0) || (component_encoded_length > length) ) - return 0; - - length -= component_encoded_length; - *encoded_length += component_encoded_length; - } + ldap_dnfree( dn ); + if ( rc != LDAP_SUCCESS ) { + free( pretty->bv_val ); + pretty->bv_val = NULL; + pretty->bv_len = 0; + return LDAP_INVALID_SYNTAX; } } -} +#ifdef NEW_LOGGING + LDAP_LOG (OPERATION, RESULTS, "<<< dnPrettyNormal: <%s>, <%s>\n", + pretty->bv_val, normal->bv_val, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "<<< dnPrettyNormal: <%s>, <%s>\n", + pretty->bv_val, normal->bv_val, 0 ); +#endif + + return LDAP_SUCCESS; +} -/* The string beginning at *s should be a string of type string_type encoded - * as described in RFC 2253. If it is, write a UTF8 encoding - * of the string, according to RFC 2253, to *d, advance *s to one byte after - * the end of the BER encoded string, advance *d to one byte after the UTF8 - * encoded string, set *av_length the number of UTF8 characters written to *d, - * and return 1. Otherwise, return 0. If make_uppercase is 1, write all of the - * characters in uppercase. If not, write the characters as they occur. If - * normalize is 1, remove all leading and trailing whitespace, and - * compress all whitespace between words to a single space. If not, transfer - * whitespace from the BER encoding to the UTF8 encoding unchanged. - * representation specifies whether the string is encoding as ASCII-hex BER, - * within quotation marks, or as a plain string. +/* + * dnMatch routine */ -static int -String_normalize( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int representation, - int string_type, - unsigned long *unnormalized_unicode -) +int +dnMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) { - int done = 0; - int firstchar = 1; - int first_dstchar = 0; - char *lastchar; - unsigned long int tmp; - int res, len; - int is_escaped; - unsigned int encoded_length; - int unnormalized_unicode_len = 0; - - *av_length = 0; - lastchar = *d; - - switch ( representation ) { - - case INVALUE: - case INQUOTEDVALUE: - if ( representation == INQUOTEDVALUE ) { - *s += 1; - if ( !normalize ) - firstchar = 0; - } + int match; + struct berval *asserted = (struct berval *) assertedValue; - while( !done ) { - if ( **s == '\0' ) { - if (unnormalized_unicode_len > 0) { - res = normalize_unicode(unnormalized_unicode, unnormalized_unicode_len, d, av_length); - if (res == 0) - return 0; - } else { - *av_length -= (*d - lastchar); - if ( !normalize && ( ASCII_SPACE(*(lastchar - 1)) ) ) { - /* a space at the end of the string must be escaped */ - *(lastchar - 1) = '\\'; - *lastchar++ = '2'; - *lastchar++ = '0'; - } - *d = lastchar; - } - - if (representation == INQUOTEDVALUE) { - /* Missing end quote */ - return 0; - } - done = 1; - } else if ( representation == INVALUE && RDN_SEPARATOR( **s ) ) { - if (unnormalized_unicode_len > 0) { - res = normalize_unicode(unnormalized_unicode, unnormalized_unicode_len, d, av_length); - if (res == 0) - return 0; - } else { - *av_length -= (*d - lastchar); - if ( !normalize && ( ASCII_SPACE(*(lastchar - 1)) ) ) { - /* a space at the end of the string must be escaped */ - *(lastchar - 1) = '\\'; - *lastchar++ = '2'; - *lastchar++ = '0'; - } - *d = lastchar; - } - done = 1; - } else if ( representation == INQUOTEDVALUE && **s == '"' ) { - if (unnormalized_unicode_len > 0) { - res = normalize_unicode(unnormalized_unicode, unnormalized_unicode_len, d, av_length); - if (res == 0) - return 0; - } else { - *av_length -= (*d - lastchar); - if ( !normalize && ( ASCII_SPACE(*(lastchar - 1)) ) ) { - /* a space at the end of the string must be escaped */ - *(lastchar - 1) = '\\'; - *lastchar++ = '2'; - *lastchar++ = '0'; - } - *d = lastchar; - } - *s += 1; - done = 1; - } else { - if ( !normalize && !ASCII_SPACE( **s ) ) - firstchar = 0; - - res = get_next_char( s, &tmp, &is_escaped ); - if (res == 0) - return 0; - - if ( string_type == PRINTABLE_STRING ) { - if ( !SLAP_PRINTABLE(tmp) ) - return 0; - } else if (string_type == IA5_STRING ) { - if ( !SLAP_IA5(tmp) ) - return 0; - } + assert( matchp ); + assert( value ); + assert( assertedValue ); + + match = value->bv_len - asserted->bv_len; - if ( !ASCII_WHITESPACE( tmp ) ) - firstchar = 0; + if ( match == 0 ) { + match = memcmp( value->bv_val, asserted->bv_val, + value->bv_len ); + } - if ( (tmp < 128) && (unnormalized_unicode_len > 0) ) { - res = normalize_unicode(unnormalized_unicode, unnormalized_unicode_len, d, av_length); - if (res == 0) - return 0; - unnormalized_unicode_len = 0; - lastchar = *d; - } +#ifdef NEW_LOGGING + LDAP_LOG( CONFIG, ENTRY, "dnMatch: %d\n %s\n %s\n", + match, value->bv_val, asserted->bv_val ); +#else + Debug( LDAP_DEBUG_ARGS, "dnMatch %d\n\t\"%s\"\n\t\"%s\"\n", + match, value->bv_val, asserted->bv_val ); +#endif - if ( RDN_NEEDSESCAPE( tmp ) || - RDN_SPECIAL( tmp ) ) { - if ( ( representation == INVALUE ) && !is_escaped ) { - /* This character should have been escaped according to - * RFC 2253, but was not */ - return 0; - } - /* This must be an ASCII character */ - **d = '\\'; - *d += 1; - **d = tmp; - *d += 1; - *av_length += 1; - lastchar = *d; - first_dstchar = 1; - } else if ( tmp == 0 ) { - strncpy(*d, "\\00", 3); - *d += 3; - *av_length += 1; - lastchar = *d; - first_dstchar = 1; - } else if ( !first_dstchar && (tmp == '#') ) { - **d = '\\'; - *d += 1; - **d = tmp; - *d += 1; - *av_length += 1; - lastchar = *d; - first_dstchar = 1; - } else if ( !normalize && !ASCII_SPACE( tmp ) ) { - #ifdef UTF8DN - if (make_uppercase) { - tmp = uctoupper( tmp ); - } - len = ldap_ucs4_to_utf8( tmp, *d ); - *d += len; - #else - if (make_uppercase) { - **d = TOUPPER( tmp ); - } else { - **d = tmp; - } - *d += 1; - #endif - *av_length += 1; - lastchar = *d; - first_dstchar = 1; - } else if ( !ASCII_WHITESPACE( tmp ) ) { - #ifdef UTF8DN - if (make_uppercase) { - tmp = uctoupper( tmp ); - } - if ( normalize && (tmp > 127) ) { - if ( (unnormalized_unicode_len == 0) && first_dstchar ) { - /* The previous output character must be ASCII - * and it should be normalized. - */ - *d -= 1; - unnormalized_unicode[unnormalized_unicode_len++] = **d; - *av_length -= 1; - } - unnormalized_unicode[unnormalized_unicode_len++] = tmp; - } else { - len = ldap_ucs4_to_utf8( tmp, *d ); - *d += len; - *av_length += 1; - } - #else - if (make_uppercase) { - **d = TOUPPER( tmp ); - } else { - **d = tmp; - } - *d += 1; - *av_length += 1; - #endif - lastchar = *d; - first_dstchar = 1; - } else if ( !firstchar && ( !normalize || !ASCII_SPACE( *(*d - 1) ) ) ) { - if ( !first_dstchar ) { - **d = '\\'; - *d += 1; - **d = '2'; - *d += 1; - **d = '0'; - *d += 1; - first_dstchar = 1; - } else { - **d = ' '; - *d +=1; - } - *av_length += 1; - if ( !normalize && ( is_escaped || representation == INQUOTEDVALUE ) ) - lastchar = *d; - } - } - } - break; - - case INBERENCODEDVALUE: - /* Skip over the '#' */ - *s += 1; - - encoded_length = 0; - - res = ber_parse_string(s, d, av_length, make_uppercase, normalize, string_type, &encoded_length, &firstchar, unnormalized_unicode, &unnormalized_unicode_len); - if (res == 0) - return 0; - - if (unnormalized_unicode_len > 0) { - res = normalize_unicode(unnormalized_unicode, unnormalized_unicode_len, d, av_length); - if (res == 0) - return 0; - } else if ( ASCII_SPACE( *(*d - 1) ) ) { - if ( normalize ) { - *d -= 1; - *av_length -= 1; - } else { - *(*d - 1) = '\\'; - **d = '2'; - *d += 1; - **d = '0'; - *d += 1; - } - } + *matchp = match; + return( LDAP_SUCCESS ); +} - break; +/* + * dnParent - dn's parent, in-place + * + * note: the incoming dn is assumed to be normalized/prettyfied, + * so that escaped rdn/ava separators are in '\'+hexpair form + */ +void +dnParent( + struct berval *dn, + struct berval *pdn ) +{ + char *p; - default: - /* Something must be wrong, representation shouldn't - * have any other value. - */ - return 0; - break; + p = strchr( dn->bv_val, ',' ); + + /* one-level dn */ + if ( p == NULL ) { + pdn->bv_len = 0; + pdn->bv_val = dn->bv_val + dn->bv_len; + return; } - return 1; -} + assert( DN_SEPARATOR( p[ 0 ] ) ); + p++; + assert( ATTR_LEADCHAR( p[ 0 ] ) ); + pdn->bv_val = p; + pdn->bv_len = dn->bv_len - (p - dn->bv_val); -/* Normalize a directory string */ -static int -DirectoryString_normalize( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int representation, - unsigned long *unnormalized_unicode -) -{ - return String_normalize(s, d, av_length, make_uppercase, normalize, representation, DIRECTORY_STRING, unnormalized_unicode); + return; } - -/* Normalize a printable string */ -static int -PrintableString_normalize( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int representation, - unsigned long *unnormalized_unicode -) +int +dnExtractRdn( + struct berval *dn, + struct berval *rdn ) { - return String_normalize(s, d, av_length, make_uppercase, normalize, representation, PRINTABLE_STRING, unnormalized_unicode); -} + LDAPRDN *tmpRDN; + const char *p; + int rc; + assert( dn ); + assert( rdn ); -/* Normalize an IA5 string */ -static int -IA5String_normalize( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int representation, - unsigned long *unnormalized_unicode -) -{ - return String_normalize(s, d, av_length, make_uppercase, normalize, representation, IA5_STRING, unnormalized_unicode); -} + if( dn->bv_len == 0 ) { + return LDAP_OTHER; + } + rc = ldap_bv2rdn( dn, &tmpRDN, (char **)&p, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + rc = ldap_rdn2bv( tmpRDN, rdn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY ); + + ldap_rdnfree( tmpRDN ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + return LDAP_SUCCESS; +} -/* The string beginning at *s represents an ASCII-hex encoding of a BER - * encoded bitstring, where the encoding is primitive, definite-length. - * If the string is properly encoded, place the string in *d, advance *s - * and *d, add the number of bits in the string to *av_length, add - * the length of the BER encoding to *encoded_length, and return 1. Otherwise, - * return 0. +/* + * We can assume the input is a prettied or normalized DN */ -static int -ber_parse_primitive_bitstring( - char **s, - char **d, - int *av_length, - unsigned int *encoded_length -) +int +dn_rdnlen( + Backend *be, + struct berval *dn_in ) { - int res; - unsigned char ch; - unsigned long int length; - unsigned char unused; - int bit_pos; - - static unsigned char mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + const char *p; - res = get_ber_length(s, encoded_length, &length); - if (res == 0) - return 0; + assert( dn_in ); - if (length < 1) { - /* There must be a least one byte containing the number of - * unused bits. - */ + if ( dn_in == NULL ) { return 0; } - /* get number of unused bits */ - res = get_hexpair(s, &unused); - if ( ( res == 0 ) || ( unused > 7 ) ) + if ( !dn_in->bv_len ) { return 0; + } - if ( (length == 0) && (unused != 0) ) { - /* If there are no content bits, there can be no unused bits */ + if ( be != NULL && be_issuffix( be, dn_in ) ) { return 0; } - *encoded_length += 1; - length--; + p = strchr( dn_in->bv_val, ',' ); - while( length > 1 ) { - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + return p ? p - dn_in->bv_val : dn_in->bv_len; +} - *encoded_length += 1; - length--; - for(bit_pos = 7; bit_pos >= 0; bit_pos--) { - if ( (ch & mask[bit_pos]) == 0 ) { - **d = '0'; - } else { - **d = '1'; - } - *d += 1; - *av_length += 1; - } +/* rdnValidate: + * + * LDAP_SUCCESS if rdn is a legal rdn; + * LDAP_INVALID_SYNTAX otherwise (including a sequence of rdns) + */ +int +rdnValidate( struct berval *rdn ) +{ +#if 1 + /* Major cheat! + * input is a pretty or normalized DN + * hence, we can just search for ',' + */ + if( rdn == NULL || rdn->bv_len == 0 || + rdn->bv_len > SLAP_LDAPDN_MAXLEN ) + { + return LDAP_INVALID_SYNTAX; } - if ( length == 1) { - res = get_hexpair(s, &ch); - if (res == 0) - return 0; + return strchr( rdn->bv_val, ',' ) == NULL + ? LDAP_SUCCESS : LDAP_INVALID_SYNTAX; - *encoded_length += 1; +#else + LDAPRDN *RDN, **DN[ 2 ] = { &RDN, NULL }; + const char *p; + int rc; - for(bit_pos = 7; bit_pos >= unused; bit_pos--) { - if ( (ch & mask[bit_pos]) == 0 ) { - **d = '0'; - } else { - **d = '1'; - } - *d += 1; - *av_length += 1; - } + /* + * must be non-empty + */ + if ( rdn == NULL || rdn == '\0' ) { + return 0; } - return 1; -} - - -/* The string beginning at *s represents an ASCII-hex encoding of a BER - * encoded bitstring. If the string is properly encoded, place the string - * in *d, advance *s and *d, add the number of bits in the string to - * *av_length, add the length of the BER encoding to *encoded_length, and - * return 1. Otherwise, return 0. - */ -static int -ber_parse_bitstring( - char **s, - char **d, - int *av_length, - unsigned int *encoded_length -) -{ - int res; - unsigned char ch; - unsigned long int length; - unsigned int component_encoded_length; - - res = get_hexpair(s, &ch); - if (res == 0) + /* + * must be parsable + */ + rc = ldap_bv2rdn( rdn, &RDN, (char **)&p, LDAP_DN_FORMAT_LDAP ); + if ( rc != LDAP_SUCCESS ) { return 0; + } - *encoded_length = 1; - - if (ch == '\x03') { - /* Primitive, definite-length encoding */ - res = ber_parse_primitive_bitstring(s, d, av_length, encoded_length); - if (res == 0) - return 0; - } else if ( ch == '\x23' ) { - /* Constructed encoding */ - - res = get_hexpair(s, &ch); - if (res == 0) - return 0; - - if ( ch == 128 ) { - /* Constructed, indefinite-length */ - *encoded_length += 1; - - while ( ch != 0 ) { - res = ber_parse_bitstring(s, d, av_length, &component_encoded_length); - if (res == 0) - return 0; - - *encoded_length += component_encoded_length; - - /* Must end in "0000" */ - res = get_hexpair(s, &ch); - if (res == 0) - return 0; - - if (ch == 0) { - res = get_hexpair(s, &ch); - if ( (res == 0) || (ch != 0) ) - return 0; - - *encoded_length += 2; - } else { - *s -= 2; - } - } - } else { - /* Constructed, definite-length */ - *s -= 2; - res = get_ber_length(s, encoded_length, &length); - if (res == 0) - return 0; - - while (length > 0) { - res = ber_parse_bitstring(s, d, av_length, &component_encoded_length); - if ( (res == 0) || (component_encoded_length > length) ) - return 0; - - length -= component_encoded_length; - *encoded_length += component_encoded_length; - } - } - } else { - /* Not a valid bitstring */ + /* + * Must be one-level + */ + if ( p[ 0 ] != '\0' ) { return 0; } -} - - -/* *s is a pointer to a string of zero or more 0's and 1's. Return a binary encoding of the next 8 bits of *s and advance - * *s to the end of the parsed sub-string. If the string is less than 8-bytes long, pad the binary encoding with 0's. - */ -static unsigned char -getNext8bits( - char **s -) -{ - static unsigned char mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; - int pos; - unsigned char output; - - output = 0; - pos = 8; - - while ( ( pos > 0 ) && ( ( **s == '0' ) || ( **s == '1' ) ) ) { - pos--; - if ( **s == '1' ) { - output = output | mask[pos]; - } - - *s += 1; + /* + * Schema-aware validate + */ + if ( rc == LDAP_SUCCESS ) { + rc = LDAPDN_validate( DN ); } + ldap_rdnfree( RDN ); - return output; + /* + * Must validate (there's a repeated parsing ...) + */ + return ( rc == LDAP_SUCCESS ); +#endif } -/* The string beginning at *s represents a bitstring encoded according to - * RFC 2253. If the string is properly encoded, place the string - * in *d, advance *s and *d, place the length of the string representation of - * the bitstring in *av_length, and return 1. Otherwise, return 0. - * representation specifies whether the string is encoding as ASCII-hex BER, - * within quotation marks, or as a plain string. - * - * According to RFC 2252, the string representation for - * bit strings is described by the following BNF: +/* build_new_dn: * - * bitstring = "'" *binary-digit "'B" + * Used by ldbm/bdb2 back_modrdn to create the new dn of entries being + * renamed. * - * binary-digit = "0" / "1" + * new_dn = parent (p_dn) + separator + rdn (newrdn) + null. */ -static int -bitString_normalize( - char **s, - char **d, - int *av_length, - int make_uppercase, - int normalize, - int representation, - unsigned long *unnormalized_unicode /* not used in this function */ -) -{ - int res; - int is_escaped; - unsigned char ch; - unsigned int encoded_length; - - int DER_length; - unsigned char unused_bits; - unsigned char byte1, byte2, temp; - char *src, *start_of_output; - - *av_length = 0; - start_of_output = *d; - - switch ( representation ) { - - case INVALUE: - case INQUOTEDVALUE: - if ( representation == INQUOTEDVALUE ) { - /* Skip over quotation mark */ - *s += 1; - } - /* First non-space character must be a "'" */ - res = get_next_byte(s, &ch, &is_escaped); - if ( (res == 0) || (ch != '\'') ) - return 0; - - **d = '\''; - *d += 1; - *av_length += 1; - - /* Next should be a sequence of 0's and 1's followed by a "'" */ - res = get_next_byte(s, &ch, &is_escaped); - if (res == 0) - return 0; - - while ( ( ch == '0' ) || ( ch == '1' ) ) { - **d = ch; - *d += 1; - *av_length += 1; - - res = get_next_byte(s, &ch, &is_escaped); - if (res == 0) - return 0; - } - - if ( ch != '\'' ) - return 0; - - **d = '\''; - *d += 1; - *av_length += 1; - - /* The last character should be a 'B' */ - res = get_next_byte(s, &ch, &is_escaped); - if ( (res == 0) || ( TOUPPER(ch) != 'B' ) ) - return 0; - - **d = 'B'; - *d += 1; - *av_length += 1; - - if ( representation == INQUOTEDVALUE ) { - if ( **s != '\"' ) - return 0; - else - *s += 1; - } - break; - - case INBERENCODEDVALUE: - /* Skip over the '#' */ - *s += 1; - - **d = '\''; - *d += 1; - *av_length +=1; - - encoded_length = 0; - - ber_parse_bitstring(s, d, av_length, &encoded_length); - if (res == 0) - return 0; - - **d = '\''; - *d += 1; - **d = 'B'; - *d += 1; - *av_length += 2; - - break; - - default: - /* Something must be wrong, representation shouldn't - * have any other value. - */ - return 0; - break; - } - - if ( !normalize && (representation != INBERENCODEDVALUE) ) - return 1; - - *av_length -= 3; - - unused_bits = *av_length % 8; - if ( unused_bits == 0 ) { - DER_length = (*av_length / 8) + 1; - } else { - DER_length = (*av_length / 8) + 2; - unused_bits = 8 - unused_bits; - } - - *d = start_of_output; - src = start_of_output + 1; - - if (DER_length > 1) - byte1 = getNext8bits( &src ); - if (DER_length > 2) - byte2 = getNext8bits( &src ); - - **d = '#'; - *d += 1; - **d = '0'; - *d += 1; - **d = '3'; - *d += 1; - - /* Insert length into string */ - if (DER_length < 128) { - temp = DER_length; - write_hex_pair(d, temp); - *av_length = 7 + 2 * DER_length; - } else if (DER_length < 256) { - **d = '8'; - *d += 1; - **d = '1'; - *d += 1; - temp = DER_length; - write_hex_pair(d, temp); - *av_length = 9 + 2 * DER_length; - } else if (DER_length < 65536) { - **d = '8'; - *d += 1; - **d = '2'; - *d += 1; - temp = (DER_length >> 8) & 0xFF; - write_hex_pair(d, temp); - temp = DER_length & 0xFF; - write_hex_pair(d, temp); - *av_length = 11 + 2 * DER_length; - } else if (DER_length < 16777216) { - **d = '8'; - *d += 1; - **d = '3'; - *d += 1; - temp = (DER_length >> 16) & 0xFF; - write_hex_pair(d, temp); - temp = (DER_length >> 8) & 0xFF; - write_hex_pair(d, temp); - temp = DER_length & 0xFF; - write_hex_pair(d, temp); - *av_length = 13 + 2 * DER_length; - } else { - /* NOTE: I am assuming that the length will always fit in 4 octets */ - **d = '8'; - *d += 1; - **d = '4'; - *d += 1; - temp = (DER_length >> 24) & 0xFF; - write_hex_pair(d, temp); - temp = (DER_length >> 16) & 0xFF; - write_hex_pair(d, temp); - temp = (DER_length >> 8) & 0xFF; - write_hex_pair(d, temp); - temp = DER_length & 0xFF; - write_hex_pair(d, temp); - *av_length = 15 + 2 * DER_length; - } - - /* Insert number of unused bits into string */ - write_hex_pair(d, unused_bits); - - if (DER_length > 1) - write_hex_pair(d, byte1); - if (DER_length > 2) - write_hex_pair(d, byte2); - - if (DER_length > 3) { - DER_length -= 3; - - while (DER_length > 0) { - byte1 = getNext8bits( &src ); - write_hex_pair(d, byte1); - DER_length--; - } - } - - return 1; -} - - -/* - * match_oid - determine if the OID represented by the string beginning - * at *src and of length len is a known attribute type. If so, copy the - * string representation to *dst and return a pointer to the normalization - * function for the attribute value. If the attribute type places an - * upper bound on the length of the attribute value, make *ub that - * upper bound, otherwise set *ub to -1. - * If the OID is unknown, copy the OID to *dst and return NULL. - */ -static av_normalize_type -match_oid(char **src, char **dst, int *ub, int len, int make_uppercase) +void +build_new_dn( struct berval * new_dn, + struct berval * parent_dn, + struct berval * newrdn ) { - int i; - int dst_len = 0; - av_normalize_type normalize_function = NULL; - - *ub = -1; - - switch( len ) { - case 7: - if (strncmp(*src, "2.5.4.6", len) == 0) { - /* Country */ - **dst = 'c'; - dst_len = 1; - *ub = 2; - normalize_function = PrintableString_normalize; - } else if (strncmp(*src, "2.5.4.3", len) == 0) { - /* Common Name */ - strncpy(*dst, "cn", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.8", len) == 0) { - /* State or Province Name */ - strncpy(*dst, "st", 2); - dst_len = 2; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.7", len) == 0) { - /* locality */ - **dst = 'l'; - dst_len = 1; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.5", len) == 0) { - /* serial number */ - strncpy(*dst, "snu", 3); - dst_len = 3; - *ub = 64; - normalize_function = PrintableString_normalize; - } else if (strncmp(*src, "2.5.4.4", len) == 0) { - /* surname */ - strncpy(*dst, "sn", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.9", len) == 0) { - /* street address */ - strncpy(*dst, "street", 6); - dst_len = 6; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; + char *ptr; - case 8: - if (strncmp(*src, "2.5.4.10", len) == 0) { - /* Organization */ - **dst = 'o'; - dst_len = 1; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.11", len) == 0) { - /* Organizational Unit */ - strncpy(*dst, "ou", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.12", len) == 0) { - /* title */ - strncpy(*dst, "title", 5); - dst_len = 5; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.42", len) == 0) { - /* givenName */ - strncpy(*dst, "givenName", 9); - dst_len = 9; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.43", len) == 0) { - /* initials */ - strncpy(*dst, "initials", 8); - dst_len = 8; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.44", len) == 0) { - /* generationQualifier */ - strncpy(*dst, "generationQualifier", 19); - dst_len = 19; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncmp(*src, "2.5.4.45", len) == 0) { - /* uniqueIdentifier */ - strncpy(*dst, "uniqueIdentifier", 16); - dst_len = 16; - *ub = -1; - normalize_function = bitString_normalize; - } else if (strncmp(*src, "2.5.4.46", len) == 0) { - /* dnQualifier */ - strncpy(*dst, "dnQualifier", 11); - dst_len = 11; - *ub = -1; - normalize_function = PrintableString_normalize; - } else if (strncmp(*src, "2.5.4.65", len) == 0) { - /* Pseudonym */ - strncpy(*dst, "Pseudonym", 9); - dst_len = 9; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 20: - if (strncmp(*src, "1.2.840.113549.1.9.1", len) == 0) { - /* email */ - **dst = 'e'; - dst_len = 1; - *ub = 128; - normalize_function = IA5String_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 17: - if (strncmp(*src, "0.2.262.1.10.7.20", len) == 0) { - /* name distinguisher */ - strncpy(*dst, "nameDistinguisher", 17); - dst_len = 17; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 25: - if (strncmp(*src, "0.9.2342.19200300.100.1.1", len) == 0) { - /* userID */ - strncpy(*dst, "uid", 3); - dst_len = 3; - *ub = 256; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 26: - if (strncmp(*src, "0.9.2342.19200300.100.1.25", len) == 0) { - /* domainComponent */ - strncpy(*dst, "dc", 2); - dst_len = 2; - *ub = -1; - normalize_function = IA5String_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - default: - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - break; + if ( parent_dn == NULL ) { + ber_dupbv( new_dn, newrdn ); + return; } - if (make_uppercase) { - for(i=0; i < dst_len; i++) { - **dst = TOUPPER( **dst ); - *dst += 1; - } - } else { - *dst += dst_len; - } - *src += len; - return normalize_function; + new_dn->bv_len = parent_dn->bv_len + newrdn->bv_len + 1; + new_dn->bv_val = (char *) ch_malloc( new_dn->bv_len + 1 ); + + ptr = lutil_strcopy( new_dn->bv_val, newrdn->bv_val ); + *ptr++ = ','; + strcpy( ptr, parent_dn->bv_val ); } /* - * match_key - determine if the attribute type represented by the string - * beginning at *src and of length len is a known attribute type. If so, - * copy the string representation to *dst and return a pointer to the - * normalization function for the attribute value. If the attribute type - * places an upper bound on the length of the attribute value, make *ub that - * upper bound, otherwise set *ub to -1. - * If the attribute type is unknown, copy the string representation of the - * attribute type to *dst and return NULL. + * dnIsSuffix - tells whether suffix is a suffix of dn. + * Both dn and suffix must be normalized. */ -static av_normalize_type -match_key(char **src, char **dst, int *ub, int len, int make_uppercase) +int +dnIsSuffix( + const struct berval *dn, + const struct berval *suffix ) { - int i; - int dst_len = 0; - av_normalize_type normalize_function = NULL; - - *ub = -1; - - switch( len ) { - case 1: - if (strncasecmp(*src, "C", len) == 0) { - /* country */ - **dst = 'c'; - dst_len = 1; - *ub = 2; - normalize_function = PrintableString_normalize; - } else if (strncasecmp(*src, "O", len) == 0) { - /* organization */ - **dst = 'o'; - dst_len = 1; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "T", len) == 0) { - /* title */ - strncpy(*dst, "title", 5); - dst_len = 5; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "S", len) == 0) { - /* state or province */ - strncpy(*dst, "st", 2); - dst_len = 2; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "L", len) == 0) { - /* locality */ - **dst = 'l'; - dst_len = 1; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "E", len) == 0) { - /* e-mail */ - **dst = 'e'; - dst_len = 1; - *ub = 255; - normalize_function = IA5String_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 2: - if (strncasecmp(*src, "CN", len) == 0) { - /* common name */ - strncpy(*dst, "cn", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "OU", len) == 0) { - /* organizational unit */ - strncpy(*dst, "ou", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "DC", len) == 0) { - /* domainComponent */ - strncpy(*dst, "dc", 2); - dst_len = 2; - *ub = -1; - normalize_function = IA5String_normalize; - } else if (strncasecmp(*src, "SN", len) == 0) { - /* surname */ - strncpy(*dst, "sn", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "ST", len) == 0) { - /* state or province */ - strncpy(*dst, "st", 2); - dst_len = 2; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 3: - if (strncasecmp(*src, "SNU", len) == 0) { - /* serial number */ - strncpy(*dst, "snu", 3); - dst_len = 3; - *ub = 64; - normalize_function = PrintableString_normalize; - } else if (strncasecmp(*src, "UID", len) == 0) { - /* userID */ - strncpy(*dst, "uid", 3); - dst_len = 3; - *ub = 256; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 5: - if (strncasecmp(*src, "TITLE", len) == 0) { - /* title */ - strncpy(*dst, "title", 5); - dst_len = 5; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "STATE", len) == 0) { - /* state or province */ - strncpy(*dst, "st", 2); - dst_len = 2; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 6: - if (strncasecmp(*src, "USERID", len) == 0) { - /* userID */ - strncpy(*dst, "uid", 3); - dst_len = 3; - *ub = 256; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "STREET", len) == 0) { - /* street address */ - strncpy(*dst, "street", 6); - dst_len = 6; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 7: - if (strncasecmp(*src, "SURNAME", len) == 0) { - /* surname */ - strncpy(*dst, "sn", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 8: - if (strncasecmp(*src, "INITIALS", len) == 0) { - /* initials */ - strncpy(*dst, "initials", 8); - dst_len = 8; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "PROVINCE", len) == 0) { - /* state or province */ - strncpy(*dst, "st", 2); - dst_len = 2; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 9: - if (strncasecmp(*src, "GIVENNAME", len) == 0) { - /* givenName */ - strncpy(*dst, "givenName", 9); - dst_len = 9; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "PSEUDONYM", len) == 0) { - /* Pseudonym */ - strncpy(*dst, "Pseudonym", 9); - dst_len = 9; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 10: - if (strncasecmp(*src, "COMMONNAME", len) == 0) { - /* common name */ - strncpy(*dst, "cn", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 11: - if (strncasecmp(*src, "DNQUALIFIER", len) == 0) { - /* Distinguished Name Quailifier */ - strncpy(*dst, "dnQualifier", 11); - dst_len = 11; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "COUNTRYNAME", len) == 0) { - /* country */ - **dst = 'c'; - dst_len = 1; - *ub = 2; - normalize_function = PrintableString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 12: - if (strncasecmp(*src, "SERIALNUMBER", len) == 0) { - /* serial number */ - strncpy(*dst, "snu", 3); - dst_len = 3; - *ub = 64; - normalize_function = PrintableString_normalize; - } else if (strncasecmp(*src, "LOCALITYNAME", len) == 0) { - /* locality */ - **dst = 'l'; - dst_len = 1; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "EMAILADDRESS", len) == 0) { - /* e-mail */ - **dst = 'e'; - dst_len = 1; - *ub = 255; - normalize_function = IA5String_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 13: - if (strncasecmp(*src, "STREETADDRESS", len) == 0) { - /* street address */ - strncpy(*dst, "street", 6); - dst_len = 6; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 15: - if (strncasecmp(*src, "DOMAINCOMPONENT", len) == 0) { - /* domainComponent */ - strncpy(*dst, "dc", 2); - dst_len = 2; - *ub = -1; - normalize_function = IA5String_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 16: - if (strncasecmp(*src, "UNIQUEIDENTIFIER", len) == 0) { - /* uniqueIdentifier */ - strncpy(*dst, "uniqueIdentifier", 16); - dst_len = 16; - *ub = -1; - normalize_function = bitString_normalize; - } else if (strncasecmp(*src, "ORGANIZATIONNAME", len) == 0) { - /* organization */ - **dst = 'o'; - dst_len = 1; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 17: - if (strncasecmp(*src, "NAMEDISTINGUISHER", len) == 0) { - /* name distinguisher */ - strncpy(*dst, "nameDistinguisher", 17); - dst_len = 17; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - case 19: - if (strncasecmp(*src, "GENERATIONQUALIFIER", len) == 0) { - /* Distinguished Name Quailifier */ - strncpy(*dst, "generationQualifier", 19); - dst_len = 19; - *ub = -1; - normalize_function = DirectoryString_normalize; - } else if (strncasecmp(*src, "STATEORPROVINCENAME", len) == 0) { - /* state or province */ - strncpy(*dst, "st", 2); - dst_len = 2; - *ub = 128; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - + int d = dn->bv_len - suffix->bv_len; - case 22: - if (strncasecmp(*src, "ORGANIZATIONALUNITNAME", len) == 0) { - /* organizational unit */ - strncpy(*dst, "ou", 2); - dst_len = 2; - *ub = 64; - normalize_function = DirectoryString_normalize; - } else { - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - } - break; - - default: - /* Unknown attributeType */ - strncpy(*dst, *src, len); - dst_len = len; - *ub = -1; - normalize_function = NULL; - break; - } + assert( dn ); + assert( suffix ); - if (make_uppercase) { - for(i=0; i < dst_len; i++) { - **dst = TOUPPER( **dst ); - *dst += 1; - } - } else { - *dst += dst_len; + /* empty suffix matches any dn */ + if ( suffix->bv_len == 0 ) { + return 1; } - *src += len; - return normalize_function; -} - -static int -get_validated_av_in_dn(char **s, char **d, int make_uppercase, int normalize, unsigned long *unnormalized_unicode) { - char *i; - int status, av_ub, len, av_length; - av_normalize_type av_normalize; - - /* First skip over any leading spaces */ - while ( ASCII_SPACE( **s ) ) - *s += 1; - - /* Next get the attribute type */ - if ( OID_LEADCHAR(**s) ) { - i = *s; - while ( *i != '\0' && OID_CHAR(*i) ) - i++; - if ( *i == '\0' ) - return 0; - - len = i - *s; - av_normalize = match_oid(s, d, &av_ub, len, make_uppercase); - } else if ( DESC_LEADCHAR(**s) ) { - if ( TOUPPER ( **s ) == 'O' && - TOUPPER ( *(*s+1) ) == 'I' && - TOUPPER ( *(*s+2) ) == 'D' && - *(*s+3) == '.' ) { - *s += 4; - if ( !OID_LEADCHAR(**s) ) - return 0; - - i = *s; - while ( *i != '\0' && OID_CHAR(*i) ) - i++; - if ( *i == '\0' ) - return 0; - - len = i - *s; - av_normalize = match_oid(s, d, &av_ub, len, make_uppercase); - } else { - i = *s; - while ( *i != '\0' && DESC_CHAR(*i) ) - i++; - if ( *i == '\0' ) - return 0; - - len = i - *s; - av_normalize = match_key(s, d, &av_ub, len, make_uppercase); - } - } else { + /* suffix longer than dn */ + if ( d < 0 ) { return 0; } - - /* Next should be the equal sign */ - - while ( (**s != '=') && (**s != '\0') ) { - if ( !ASCII_SPACE(**s) ) - return 0; - - *s += 1; - } - - if (**s != '=') + /* no rdn separator or escaped rdn separator */ + if ( d > 1 && !DN_SEPARATOR( dn->bv_val[ d - 1 ] ) ) { return 0; - - *s += 1; - **d = '='; - *d += 1; - - while ( ASCII_SPACE(**s) ) - *s += 1; - - /* The final part is the attribute value */ - if ( **s == '"' ) { - if (av_normalize == NULL) { - av_ub = -1; - av_normalize = DirectoryString_normalize; - } - status = (*av_normalize)(s, d, &av_length, make_uppercase, normalize, INQUOTEDVALUE, unnormalized_unicode); - if (status == 0) - return 0; - if ( ( av_ub != -1 ) && ( av_length > av_ub ) ) { - /* attribute value too long */ - return 0; - } - } else if ( **s == '#' ) { - if (av_normalize == NULL) { - /* Unknown attribute type. Since we don't know its string representation, - * just leave it as a BER encoded value. - */ - **d = **s; - *s += 1; *d += 1; - av_length = 1; - while ( ASCII_XDIGIT(**s) ) { - **d = TOUPPER(**s); - *s += 1; *d += 1; - av_length++; - } - - /* The length must be odd, since there must be an even number of - * hexadecimal charaters after the '#'. - */ - if ( (av_length & 1) == 0) - return 0; - } else { - status = (*av_normalize)(s, d, &av_length, make_uppercase, normalize, INBERENCODEDVALUE, unnormalized_unicode); - if (status == 0) - return 0; - if ( ( av_ub != -1 ) && ( av_length > av_ub ) ) { - /* attribute value too long */ - return 0; - } - } - } else { - if (av_normalize == NULL) { - av_ub = -1; - av_normalize = DirectoryString_normalize; - } - status = (*av_normalize)(s, d, &av_length, make_uppercase, normalize, INVALUE, unnormalized_unicode); - if (status == 0) - return 0; - if ( ( av_ub != -1 ) && ( av_length > av_ub ) ) { - /* attribute value too long */ - return 0; - } } - return 1; -} - -/* The string *s is a distinguished name encoded according to RFC 2253. - * If the first RDN in *s is properly encoded, place in *d a normalized - * version of the first RDN in *s, advance *d to the end of the normalized - * RDN, advance *s to the end of the input string, and return 1. - * If *s is not properly encoded, return 0. - */ -static int -get_validated_rdn_in_dn(char **s, char **d, int make_uppercase, int normalize, unsigned long *unnormalized_unicode) { - char *av_pair[1001]; /* Assume there are less than 1000 attribute value pairs per RDN */ - int av_pair_len[1001]; - char *temp, *work_space; - int i, j, num_av_pairs, status, state, len; - - /* An RDN is a set of 1 or more attribute/value pairs. Get the first AV pair */ - av_pair[0] = *d; - status = get_validated_av_in_dn(s, d, make_uppercase, normalize, unnormalized_unicode); - if (status == 0) + /* no possible match or malformed dn */ + if ( d == 1 ) { return 0; - - num_av_pairs = 1; - - state = B4SEPARATOR; - - while ( ASCII_SPACE( **s ) ) { - *s += 1; } - if ( **s != '+') { - /* This RDN contains only 1 attribute value pair */ - return 1; - } - - /* Since RDNs with more than one attribute value pair are - * rare, the above code was optimized for the case of an - * RDN with only one AV pair. This RDN, however, contains - * two or more AV pairs and they must be sorted to ensure - * consistency when performing matches. The ordering does - * not matter as long as it is consistent. - */ - - /* Create temporary space to hold the AV pairs before sorting */ - **d = '\0'; - - /* Compute the length of the first AV pair */ - av_pair_len[0] = *d - av_pair[0]; - - work_space = (char *)ch_malloc(4 * strlen( *s ) + av_pair_len[0] + 1000); - - /* Move *d back so that the whole RDN can be written in the proper order */ - *d = av_pair[0]; - - av_pair[0] = work_space; - bcopy(*d, av_pair[0], av_pair_len[0]+1); - - av_pair[1] = av_pair[0] + av_pair_len[0] + 1; - while ( (num_av_pairs < 1000) && (**s != ',') && (**s != ';') && (**s != '\0') ) { - if ( **s != '+' ) { - ch_free(work_space); - return 0; - } - *s += 1; - - temp = av_pair[num_av_pairs]; - status = get_validated_av_in_dn(s, &temp, make_uppercase, normalize, unnormalized_unicode); - if (status == 0) { - ch_free(work_space); - return 0; - } - av_pair_len[num_av_pairs] = temp - av_pair[num_av_pairs]; - - *temp++ = '\0'; - num_av_pairs++; - av_pair[num_av_pairs] = temp; - - while ( ASCII_SPACE(**s) ) - *s += 1; - } - - if (num_av_pairs == 1000) { - ch_free(work_space); - return 0; - } - - if ( normalize ) { - /* Sort the AV pairs. Since the number of AV pairs in an RDN should always - * be very small, bubblesort is used. - */ - for(i = 0; i < num_av_pairs; i++) { - for(j = 1; j < num_av_pairs; j++) { - if (strcasecmp(av_pair[j-1], av_pair[j]) > 0) { - temp = av_pair[j-1]; - av_pair[j-1] = av_pair[j]; - av_pair[j] = temp; - - len = av_pair_len[j-1]; - av_pair_len[j-1] = av_pair_len[j]; - av_pair_len[j] = len; - } - } - } - } - - /* place the AV pairs in *d, separated by commas */ - for(i=0; i < num_av_pairs; i++) { - bcopy(av_pair[i], *d, av_pair_len[i]); - *d += av_pair_len[i]; - **d = '+'; - *d += 1; - } - *d -= 1; - - ch_free(work_space); - - return 1; -} - -/* The string dn is a distinguished name encoded according to RFC 2253. - * If dn is properly encoded, return a normalized version of the string. - * If not, return NULL. If make_uppercase is 0, do not change the case of - * characters in attribute values, otherwise make all characters in attribute - * values uppercase. If normalize is 0, do not compress whitespace - * within attribute values, otherwise remove any leading and trailing - * whitespace characters from attribute values and replace any strings of - * whitespace characters between "words" with a single space character. - */ -char * -get_validated_dn( char *dn, int make_uppercase, int normalize) -{ - char *ret_val, *s, *d; - unsigned long *unnormalized_unicode; - int dn_len, status, state; - - state = B4LEADTYPE; - - dn_len = strlen(dn); - d = ret_val = (char *)ch_malloc(4 * dn_len + 1); - s = dn; - - /* Create temporary workspace to hold unicode characters before - * they have been normalized. - */ - if ( normalize ) - unnormalized_unicode = (unsigned long *)ch_malloc(dn_len * sizeof(unsigned long)); - else - unnormalized_unicode = NULL; - - /* A DN consists of a sequence of 0 or more RDNs */ - - while ( ret_val != NULL && *s != '\0' ) { - if ( ASCII_SPACE( *s ) ) { - s++; - } else if ( (state == B4SEPARATOR) && ( (*s == ',') || (*s == ';') ) ) { - *d++ = ','; - s++; - state = B4VALUE; - } else { - status = get_validated_rdn_in_dn(&s, &d, make_uppercase, normalize, unnormalized_unicode); - if (status == 0) { - /* not a valid RDN */ - ch_free(ret_val); - ret_val = NULL; - } - state = B4SEPARATOR; - } - } - - if (state == B4VALUE) { - /* not a valid DN */ - ch_free(ret_val); - ret_val = NULL; - } - - *d = '\0'; - return ret_val; -} - -/* - * dn_validate - validate and compress dn. the dn is - * compressed in place are returned if valid. - */ - -char * -dn_validate( char *dn_in ) -{ - char *dn_out; - int len; - - len = strlen(dn_in); - - if (len != 0) { - dn_out = get_validated_dn(dn_in, 0, 0); - if (dn_out == NULL) { - return NULL; - } else if (strlen(dn_out) <= len) { - strcpy(dn_in, dn_out); - ch_free(dn_out); - } else { - ch_free(dn_out); - return NULL; - } - } - return( dn_in ); -} - -/* - * dn_normalize - put dn into a canonical form suitable for storing - * in a hash database. this involves normalizing the case as well as - * the format. the dn is normalized in place as well as returned if valid. - */ - -char * -dn_normalize( char *dn ) -{ - char *dn_out; - int len; - - len = strlen(dn); - - if (len != 0) { - dn_out = get_validated_dn(dn, 1, 1); - if (dn_out == NULL) { - return NULL; - } else if (strlen(dn_out) <= len) { - strcpy(dn, dn_out); - ch_free(dn_out); - } else { - ch_free(dn_out); - return NULL; - } - } - return( dn ); -} - -/* - * dn_parent - return a copy of the dn of dn's parent - */ - -char * -dn_parent( - Backend *be, - const char *dn -) -{ - const char *s; - int inquote; - - if( dn == NULL ) { - return NULL; - } - - while(*dn != '\0' && ASCII_SPACE(*dn)) { - dn++; - } - - if( *dn == '\0' ) { - return NULL; - } - - if ( be != NULL && be_issuffix( be, dn ) ) { - return NULL; - } - - /* - * assume it is an X.500-style name, which looks like - * foo=bar,sha=baz,... - */ - - inquote = 0; - for ( s = dn; *s; s++ ) { - if ( *s == '\\' ) { - if ( *(s + 1) ) { - s++; - } - continue; - } - if ( inquote ) { - if ( *s == '"' ) { - inquote = 0; - } - } else { - if ( *s == '"' ) { - inquote = 1; - } else if ( DN_SEPARATOR( *s ) ) { - return ch_strdup( &s[1] ); - } - } - } - - return ch_strdup( "" ); -} - -char * dn_rdn( - Backend *be, - const char *dn_in ) -{ - char *dn, *s; - int inquote; - - if( dn_in == NULL ) { - return NULL; - } - - while(*dn_in && ASCII_SPACE(*dn_in)) { - dn_in++; - } - - if( *dn_in == '\0' ) { - return( NULL ); - } - - if ( be != NULL && be_issuffix( be, dn_in ) ) { - return( NULL ); - } - - dn = ch_strdup( dn_in ); - - inquote = 0; - - for ( s = dn; *s; s++ ) { - if ( *s == '\\' ) { - if ( *(s + 1) ) { - s++; - } - continue; - } - if ( inquote ) { - if ( *s == '"' ) { - inquote = 0; - } - } else { - if ( *s == '"' ) { - inquote = 1; - } else if ( DN_SEPARATOR( *s ) ) { - *s = '\0'; - return( dn ); - } - } - } - - return( dn ); -} - - -/* - * return a charray of all subtrees to which the DN resides in - */ -char **dn_subtree( - Backend *be, - const char *dn ) -{ - char *child, *parent; - char **subtree = NULL; - - child = ch_strdup( dn ); - - do { - charray_add( &subtree, child ); - - parent = dn_parent( be, child ); - - free( child ); - - child = parent; - } while ( child != NULL ); - - return subtree; + /* compare */ + return( strcmp( dn->bv_val + d, suffix->bv_val ) == 0 ); } - +#ifdef HAVE_TLS /* - * dn_issuffix - tells whether suffix is a suffix of dn. both dn - * and suffix must be normalized. + * Convert an X.509 DN into a normalized LDAP DN */ - int -dn_issuffix( - const char *dn, - const char *suffix -) +dnX509normalize( void *x509_name, struct berval *out ) { - int dnlen, suffixlen; - - if ( dn == NULL ) { - return( 0 ); - } - - suffixlen = strlen( suffix ); - dnlen = strlen( dn ); - - if ( suffixlen > dnlen ) { - return( 0 ); - } - - return( strcmp( dn + dnlen - suffixlen, suffix ) == 0 ); + /* Invoke the LDAP library's converter with our schema-rewriter */ + return ldap_X509dn2bv( x509_name, out, LDAPDN_rewrite, 0 ); } /* - * get_next_substring(), rdn_attr_type(), rdn_attr_value(), and - * build_new_dn(). - * - * Copyright 1999, Juan C. Gomez, All rights reserved. - * This software is not subject to any license of Silicon Graphics - * Inc. or Purdue University. - * - * Redistribution and use in source and binary forms are permitted - * without restriction or fee of any kind as long as this notice - * is preserved. - * - */ - -/* get_next_substring: - * - * Gets next substring in s, using d (or the end of the string '\0') as a - * string delimiter, and places it in a duplicated memory space. Leading - * spaces are ignored. String s **must** be null-terminated. - */ - -static char * -get_next_substring( const char * s, char d ) -{ - - char *str, *r; - - r = str = ch_malloc( strlen(s) + 1 ); - - /* Skip leading spaces */ - - while ( *s && ASCII_SPACE(*s) ) { - s++; - } - - /* Copy word */ - - while ( *s && (*s != d) ) { - - /* Don't stop when you see trailing spaces may be a multi-word - * string, i.e. name=John Doe! - */ - - *str++ = *s++; - - } - - *str = '\0'; - - return r; - -} - - -/* rdn_attr_type: - * - * Given a string (i.e. an rdn) of the form: - * "attribute_type = attribute_value" - * this function returns the type of an attribute, that is the - * string "attribute_type" which is placed in newly allocated - * memory. The returned string will be null-terminated. - */ - -char * rdn_attr_type( const char * s ) -{ - return get_next_substring( s, '=' ); -} - - -/* rdn_attr_value: - * - * Given a string (i.e. an rdn) of the form: - * "attribute_type = attribute_value" - * this function returns "attribute_type" which is placed in newly allocated - * memory. The returned string will be null-terminated and may contain - * spaces (i.e. "John Doe\0"). - */ - -char * -rdn_attr_value( const char * rdn ) -{ - - const char *str; - - if ( (str = strchr( rdn, '=' )) != NULL ) { - return get_next_substring(++str, '\0'); - } - - return NULL; - -} - - -/* rdn_attrs: - * - * Given a string (i.e. an rdn) of the form: - * "attribute_type=attribute_value[+attribute_type=attribute_value[...]]" - * this function stores the types of the attributes in ptypes, that is the - * array of strings "attribute_type" which is placed in newly allocated - * memory, and the values of the attributes in pvalues, that is the - * array of strings "attribute_value" which is placed in newly allocated - * memory. Returns 0 on success, -1 on failure. - * - * note: got part of the code from dn_validate + * Get the TLS session's peer's DN into a normalized LDAP DN */ - int -rdn_attrs( const char * rdn_in, char ***ptypes, char ***pvalues) +dnX509peerNormalize( void *ssl, struct berval *dn ) { - char **parts, **p; - - *ptypes = NULL; - *pvalues = NULL; - - /* - * explode the rdn in parts - */ - parts = ldap_explode_rdn( rdn_in, 0 ); - - if ( parts == NULL ) { - return( -1 ); - } - - for ( p = parts; p[0]; p++ ) { - char *s, *e, *d; - - /* split each rdn part in type value */ - s = strchr( p[0], '=' ); - if ( s == NULL ) { - charray_free( *ptypes ); - charray_free( *pvalues ); - charray_free( parts ); - return( -1 ); - } - - /* type should be fine */ - charray_add_n( ptypes, p[0], ( s-p[0] ) ); - - /* value needs to be unescaped - * (maybe this should be moved to ldap_explode_rdn?) */ - for ( e = d = s + 1; e[0]; e++ ) { - if ( *e != '\\' ) { - *d++ = *e; - } - } - d[0] = '\0'; - charray_add( pvalues, s + 1 ); - } - - /* free array */ - charray_free( parts ); - return( 0 ); -} - - -/* rdn_validate: - * - * 1 if rdn is a legal rdn; - * 0 otherwise (including a sequence of rdns) - * - * note: got it from dn_rdn; it should be rewritten - * according to dn_validate - */ -int -rdn_validate( const char * rdn ) -{ - int inquote; - - if ( rdn == NULL ) { - return( 0 ); - } - - if ( strchr( rdn, '=' ) == NULL ) { - return( 0 ); - } - - while ( *rdn && ASCII_SPACE( *rdn ) ) { - rdn++; - } - - if( *rdn == '\0' ) { - return( 0 ); - } - - inquote = 0; - - for ( ; *rdn; rdn++ ) { - if ( *rdn == '\\' ) { - if ( *(rdn + 1) ) { - rdn++; - } - continue; - } - if ( inquote ) { - if ( *rdn == '"' ) { - inquote = 0; - } - } else { - if ( *rdn == '"' ) { - inquote = 1; - } else if ( DN_SEPARATOR( *rdn ) ) { - return( 0 ); - } - } - } - - return( 1 ); -} - - -/* build_new_dn: - * - * Used by ldbm/bdb2_back_modrdn to create the new dn of entries being - * renamed. - * - * new_dn = parent (p_dn) + separator(s) + rdn (newrdn) + null. - */ - -void -build_new_dn( char ** new_dn, - const char *e_dn, - const char * p_dn, - const char * newrdn ) -{ - - if ( p_dn == NULL ) { - *new_dn = ch_strdup( newrdn ); - return; - } - - *new_dn = (char *) ch_malloc( strlen( p_dn ) + strlen( newrdn ) + 3 ); - - strcpy( *new_dn, newrdn ); - strcat( *new_dn, "," ); - strcat( *new_dn, p_dn ); + return ldap_pvt_tls_get_peer_dn( ssl, dn, (LDAPDN_rewrite_dummy *)LDAPDN_rewrite, 0 ); } +#endif