openldap/servers/slapd/dn.c
Pierangelo Masarati fbc11bd16a - added the capability to filter based on hasSubordinate attribute
to back-bdb, back-ldbm and back-sql (the latter with limitations);
- added handling of ":dn" attributes to extended rfc2254 filters
  and to matched value filter
- altered the behavior of get_mra() when a matching rule is given:
  now it checks whether it is compatible with the attribute syntax
  and, in case it is, the given mr is used.  In case of no type,
  the check is delayed when filtering
2002-08-29 10:55:48 +00:00

922 lines
18 KiB
C

/* dn.c - routines for dealing with distinguished names */
/* $OpenLDAP$ */
/*
* Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
* COPYING RESTRICTIONS APPLY, see COPYRIGHT file
*/
#include "portable.h"
#include <stdio.h>
#include <ac/ctype.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include "ldap_pvt.h"
#include "slap.h"
#include "lutil.h"
const struct berval slap_empty_bv = { 0, "" };
/*
* 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
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;
}
}
}
}
return LDAP_SUCCESS;
}
/*
* dn validate routine
*/
int
dnValidate(
Syntax *syntax,
struct berval *in )
{
int rc;
LDAPDN *dn = NULL;
assert( in );
if ( in->bv_len == 0 ) {
return LDAP_SUCCESS;
} else if ( in->bv_len > SLAP_LDAPDN_MAXLEN ) {
return LDAP_INVALID_SYNTAX;
}
rc = ldap_bv2dn( in, &dn, LDAP_DN_FORMAT_LDAP );
if ( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
assert( strlen( in->bv_val ) == in->bv_len );
/*
* Schema-aware validate
*/
rc = LDAPDN_validate( dn );
ldap_dnfree( dn );
if ( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
return LDAP_SUCCESS;
}
/*
* 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
AVA_Sort( LDAPRDN *rdn, int iAVA )
{
int i;
LDAPAVA *ava_in = rdn[ 0 ][ iAVA ];
assert( rdn );
assert( ava_in );
for ( i = 0; i < iAVA; i++ ) {
LDAPAVA *ava = rdn[ 0 ][ i ];
int a, j;
assert( ava );
a = strcmp( ava_in->la_attr.bv_val, ava->la_attr.bv_val );
if ( a > 0 ) {
break;
}
while ( a == 0 ) {
int v, d;
d = ava_in->la_value.bv_len - ava->la_value.bv_len;
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 );
if ( v == 0 && d != 0 ) {
v = d;
}
if ( v <= 0 ) {
/*
* got it!
*/
break;
}
if ( ++i == iAVA ) {
/*
* already sorted
*/
return;
}
ava = rdn[ 0 ][ i ];
a = strcmp( ava_in->la_attr.bv_val,
ava->la_attr.bv_val );
}
/*
* move ahead
*/
for ( j = iAVA; j > i; j-- ) {
rdn[ 0 ][ j ] = rdn[ 0 ][ j - 1 ];
}
rdn[ 0 ][ i ] = ava_in;
return;
}
}
/*
* In-place, schema-aware normalization / "pretty"ing of the
* structural representation of a distinguished name.
*/
static int
LDAPDN_rewrite( LDAPDN *dn, unsigned flags )
{
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 *validf = NULL;
slap_syntax_transform_func *transf = NULL;
MatchingRule *mr;
struct berval bv = { 0, NULL };
int do_sort = 0;
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;
do_sort = 1;
}
/*
* Replace attr oid/name with the canonical name
*/
ava->la_attr = ad->ad_cname;
if( ava->la_flags & LDAP_AVA_BINARY ) {
/* AVA is binary encoded, don't muck with it */
validf = NULL;
transf = NULL;
mr = NULL;
} else if( flags & SLAP_LDAPDN_PRETTY ) {
validf = NULL;
transf = ad->ad_type->sat_syntax->ssyn_pretty;
mr = NULL;
} else {
validf = ad->ad_type->sat_syntax->ssyn_validate;
transf = ad->ad_type->sat_syntax->ssyn_normalize;
mr = ad->ad_type->sat_equality;
}
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 );
if ( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
}
if ( transf ) {
/*
* transform value by normalize/pretty function
* 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( mr && ( mr->smr_usage & SLAP_MR_DN_FOLD ) ) {
char *s = bv.bv_val;
if ( UTF8bvnormalize( &bv, &bv,
LDAP_UTF8_CASEFOLD ) == NULL ) {
return LDAP_INVALID_SYNTAX;
}
free( s );
}
if( bv.bv_val ) {
free( ava->la_value.bv_val );
ava->la_value = bv;
}
if( do_sort ) AVA_Sort( rdn, iAVA );
}
}
return LDAP_SUCCESS;
}
/*
* dn normalize routine
*/
int
dnNormalize(
Syntax *syntax,
struct berval *val,
struct berval **normalized )
{
struct berval *out;
int rc;
assert( normalized && *normalized == NULL );
out = ch_malloc( sizeof( struct berval ) );
rc = dnNormalize2( syntax, val, out );
if ( rc != LDAP_SUCCESS )
free( out );
else
*normalized = out;
return rc;
}
int
dnNormalize2(
Syntax *syntax,
struct berval *val,
struct berval *out )
{
assert( val );
assert( out );
Debug( LDAP_DEBUG_TRACE, ">>> dnNormalize: <%s>\n", val->bv_val, 0, 0 );
if ( val->bv_len != 0 ) {
LDAPDN *dn = NULL;
int rc;
/*
* Go to structural representation
*/
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, 0 ) != LDAP_SUCCESS ) {
ldap_dnfree( dn );
return LDAP_INVALID_SYNTAX;
}
/*
* Back to string representation
*/
rc = ldap_dn2bv( dn, out,
LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
ldap_dnfree( dn );
if ( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
} else {
ber_dupbv( out, val );
}
Debug( LDAP_DEBUG_TRACE, "<<< dnNormalize: <%s>\n", out->bv_val, 0, 0 );
return LDAP_SUCCESS;
}
/*
* dn "pretty"ing routine
*/
int
dnPretty(
Syntax *syntax,
struct berval *val,
struct berval **pretty)
{
struct berval *out;
int rc;
assert( pretty && *pretty == NULL );
out = ch_malloc( sizeof( struct berval ) );
rc = dnPretty2( syntax, val, out );
if ( rc != LDAP_SUCCESS )
free( out );
else
*pretty = out;
return rc;
}
int
dnPretty2(
Syntax *syntax,
struct berval *val,
struct berval *out)
{
assert( val );
assert( out );
#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
if ( val->bv_len == 0 ) {
ber_dupbv( out, val );
} else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
return LDAP_INVALID_SYNTAX;
} else {
LDAPDN *dn = NULL;
int rc;
/* 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;
}
/* 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 );
ldap_dnfree( dn );
if ( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
}
Debug( LDAP_DEBUG_TRACE, "<<< dnPretty: <%s>\n", out->bv_val, 0, 0 );
return LDAP_SUCCESS;
}
int
dnPrettyNormalDN(
Syntax *syntax,
struct berval *val,
LDAPDN **dn,
int flags )
{
assert( val );
assert( dn );
#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
if ( val->bv_len == 0 ) {
return LDAP_SUCCESS;
} else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
return LDAP_INVALID_SYNTAX;
} else {
int rc;
/* 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, flags ) != LDAP_SUCCESS ) {
ldap_dnfree( *dn );
*dn = NULL;
return LDAP_INVALID_SYNTAX;
}
}
Debug( LDAP_DEBUG_TRACE, "<<< dn%sDN\n",
flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal",
0, 0 );
return LDAP_SUCCESS;
}
/*
* Combination of both dnPretty and dnNormalize
*/
int
dnPrettyNormal(
Syntax *syntax,
struct berval *val,
struct berval *pretty,
struct berval *normal)
{
#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
assert( val );
assert( pretty );
assert( normal );
if ( val->bv_len == 0 ) {
ber_dupbv( pretty, val );
ber_dupbv( normal, val );
} else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
/* too big */
return LDAP_INVALID_SYNTAX;
} else {
LDAPDN *dn = NULL;
int rc;
pretty->bv_val = NULL;
normal->bv_val = NULL;
pretty->bv_len = 0;
normal->bv_len = 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;
}
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;
}
rc = ldap_dn2bv( dn, pretty,
LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
if ( rc != LDAP_SUCCESS ) {
ldap_dnfree( dn );
return LDAP_INVALID_SYNTAX;
}
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;
}
rc = ldap_dn2bv( dn, normal,
LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
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;
}
/*
* dnMatch routine
*/
int
dnMatch(
int *matchp,
slap_mask_t flags,
Syntax *syntax,
MatchingRule *mr,
struct berval *value,
void *assertedValue )
{
int match;
struct berval *asserted = (struct berval *) assertedValue;
assert( matchp );
assert( value );
assert( assertedValue );
match = value->bv_len - asserted->bv_len;
if ( match == 0 ) {
match = memcmp( value->bv_val, asserted->bv_val,
value->bv_len );
}
#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
*matchp = match;
return( LDAP_SUCCESS );
}
/*
* 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;
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;
}
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);
return;
}
int
dnExtractRdn(
struct berval *dn,
struct berval *rdn )
{
LDAPRDN *tmpRDN;
const char *p;
int rc;
assert( dn );
assert( rdn );
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;
}
/*
* We can assume the input is a prettied or normalized DN
*/
int
dn_rdnlen(
Backend *be,
struct berval *dn_in )
{
const char *p;
assert( dn_in );
if ( dn_in == NULL ) {
return 0;
}
if ( !dn_in->bv_len ) {
return 0;
}
if ( be != NULL && be_issuffix( be, dn_in ) ) {
return 0;
}
p = strchr( dn_in->bv_val, ',' );
return p ? p - dn_in->bv_val : dn_in->bv_len;
}
/* 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;
}
return strchr( rdn->bv_val, ',' ) == NULL
? LDAP_SUCCESS : LDAP_INVALID_SYNTAX;
#else
LDAPRDN *RDN, **DN[ 2 ] = { &RDN, NULL };
const char *p;
int rc;
/*
* must be non-empty
*/
if ( rdn == NULL || rdn == '\0' ) {
return 0;
}
/*
* must be parsable
*/
rc = ldap_bv2rdn( rdn, &RDN, (char **)&p, LDAP_DN_FORMAT_LDAP );
if ( rc != LDAP_SUCCESS ) {
return 0;
}
/*
* Must be one-level
*/
if ( p[ 0 ] != '\0' ) {
return 0;
}
/*
* Schema-aware validate
*/
if ( rc == LDAP_SUCCESS ) {
rc = LDAPDN_validate( DN );
}
ldap_rdnfree( RDN );
/*
* Must validate (there's a repeated parsing ...)
*/
return ( rc == LDAP_SUCCESS );
#endif
}
/* build_new_dn:
*
* Used by ldbm/bdb2 back_modrdn to create the new dn of entries being
* renamed.
*
* new_dn = parent (p_dn) + separator + rdn (newrdn) + null.
*/
void
build_new_dn( struct berval * new_dn,
struct berval * parent_dn,
struct berval * newrdn )
{
char *ptr;
if ( parent_dn == NULL ) {
ber_dupbv( new_dn, newrdn );
return;
}
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 );
}
/*
* dnIsSuffix - tells whether suffix is a suffix of dn.
* Both dn and suffix must be normalized.
*/
int
dnIsSuffix(
const struct berval *dn,
const struct berval *suffix )
{
int d = dn->bv_len - suffix->bv_len;
assert( dn );
assert( suffix );
/* empty suffix matches any dn */
if ( suffix->bv_len == 0 ) {
return 1;
}
/* suffix longer than dn */
if ( d < 0 ) {
return 0;
}
/* no rdn separator or escaped rdn separator */
if ( d > 1 && !DN_SEPARATOR( dn->bv_val[ d - 1 ] ) ) {
return 0;
}
/* no possible match or malformed dn */
if ( d == 1 ) {
return 0;
}
/* compare */
return( strcmp( dn->bv_val + d, suffix->bv_val ) == 0 );
}
#ifdef HAVE_TLS
/*
* Convert an X.509 DN into a normalized LDAP DN
*/
int
dnX509normalize( void *x509_name, struct berval *out )
{
/* Invoke the LDAP library's converter with our schema-rewriter */
return ldap_X509dn2bv( x509_name, out, LDAPDN_rewrite, 0 );
}
/*
* Get the TLS session's peer's DN into a normalized LDAP DN
*/
int
dnX509peerNormalize( void *ssl, struct berval *dn )
{
return ldap_pvt_tls_get_peer_dn( ssl, dn, (LDAPDN_rewrite_dummy *)LDAPDN_rewrite, 0 );
}
#endif