+/* $OpenLDAP$ */
/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/* Portions
* Copyright (c) 1994 Regents of the University of Michigan.
* All rights reserved.
*
* getdn.c
*/
-#ifndef lint
-static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
-#endif
+#include "portable.h"
#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#ifdef MACOS
-#include <stdlib.h>
-#include "macos.h"
-#else /* MACOS */
-#if defined( DOS ) || defined( _WIN32 )
-#include <malloc.h>
-#include "msdos.h"
-#else /* DOS */
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif /* DOS */
-#endif /* MACOS */
-
-#include "lber.h"
-#include "ldap.h"
+#include <ac/stdlib.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_schema.h"
+
+/* extension to UFN that turns trailing "dc=value" rdns in DNS style,
+ * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
+#define DC_IN_UFN
+#define PRETTY_ESCAPE
+
+/* parsing/printing routines */
+static int str2strval( const char *str, ber_len_t stoplen, struct berval *val,
+ const char **next, unsigned flags, unsigned *retFlags );
+static int DCE2strval( const char *str, struct berval *val,
+ const char **next, unsigned flags );
+static int IA52strval( const char *str, struct berval *val,
+ const char **next, unsigned flags );
+static int quotedIA52strval( const char *str, struct berval *val,
+ const char **next, unsigned flags );
+static int hexstr2binval( const char *str, struct berval *val,
+ const char **next, unsigned flags );
+static int hexstr2bin( const char *str, char *c );
+static int byte2hexpair( const char *val, char *pair );
+static int binval2hexstr( struct berval *val, char *str );
+static int strval2strlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2str( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2IA5strlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2IA5str( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2DCEstrlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2DCEstr( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2ADstrlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2ADstr( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN );
+
+/* AVA helpers */
+static LDAPAVA * ldapava_new(
+ const struct berval *attr, const struct berval *val, unsigned flags );
+
+/* Higher level helpers */
+static int rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len,
+ int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
+static int rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len,
+ int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
+static int rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len );
+static int rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flag, ber_len_t *len, int first );
+static int rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first );
+
+/*
+ * RFC 1823 ldap_get_dn
+ */
char *
ldap_get_dn( LDAP *ld, LDAPMessage *entry )
{
}
tmp = *entry->lm_ber; /* struct copy */
- if ( ber_scanf( &tmp, "{a", &dn ) == LBER_ERROR ) {
+ if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
ld->ld_errno = LDAP_DECODING_ERROR;
return( NULL );
}
return( dn );
}
+/*
+ * RFC 1823 ldap_dn2ufn
+ */
char *
-ldap_dn2ufn( char *dn )
+ldap_dn2ufn( LDAP_CONST char *dn )
{
- char *p, *ufn, *r;
- int state;
+ char *out = NULL;
Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
- if ( ldap_is_dns_dn( dn ) || ( p = strchr( dn, '=' )) == NULL )
- return( strdup( dn ));
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_UFN );
+
+ return( out );
+}
+
+/*
+ * RFC 1823 ldap_explode_dn
+ */
+char **
+ldap_explode_dn( LDAP_CONST char *dn, int notypes )
+{
+ LDAPDN *tmpDN;
+ char **values = NULL;
+ int iRDN;
+ unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
+
+ if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP )
+ != LDAP_SUCCESS ) {
+ return NULL;
+ }
- ufn = strdup( ++p );
+ if( tmpDN == NULL ) {
+ values = LDAP_MALLOC( sizeof( char * ) );
+ if( values == NULL ) return NULL;
-#define INQUOTE 1
-#define OUTQUOTE 2
- state = OUTQUOTE;
- for ( p = ufn, r = ufn; *p; p++ ) {
- switch ( *p ) {
- case '\\':
- if ( *++p == '\0' )
- p--;
- else {
- *r++ = '\\';
- *r++ = *p;
- }
- break;
- case '"':
- if ( state == INQUOTE )
- state = OUTQUOTE;
- else
- state = INQUOTE;
- *r++ = *p;
- break;
- case ';':
- case ',':
- if ( state == OUTQUOTE )
- *r++ = ',';
- else
- *r++ = *p;
- break;
- case '=':
- if ( state == INQUOTE )
- *r++ = *p;
- else {
- char *rsave = r;
-
- *r-- = '\0';
- while ( !isspace( *r ) && *r != ';'
- && *r != ',' && r > ufn )
- r--;
- r++;
-
- if ( strcasecmp( r, "c" )
- && strcasecmp( r, "o" )
- && strcasecmp( r, "ou" )
- && strcasecmp( r, "st" )
- && strcasecmp( r, "l" )
- && strcasecmp( r, "cn" ) ) {
- r = rsave;
- *r++ = '=';
- }
- }
- break;
- default:
- *r++ = *p;
- break;
- }
+ values[0] = NULL;
+ return values;
+ }
+
+ for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ );
+
+ values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
+ if ( values == NULL ) {
+ ldap_dnfree( tmpDN );
+ return NULL;
+ }
+
+ for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ ) {
+ ldap_rdn2str( tmpDN[ 0 ][ iRDN ], &values[ iRDN ], flag );
}
- *r = '\0';
+ ldap_dnfree( tmpDN );
+ values[ iRDN ] = NULL;
- return( ufn );
+ return values;
}
char **
-ldap_explode_dns( char *dn )
+ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
{
- int ncomps, maxcomps;
- char *s;
- char **rdns;
+ LDAPRDN *tmpRDN;
+ char **values = NULL;
+ const char *p;
+ int iAVA;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
- if ( (rdns = (char **) malloc( 8 * sizeof(char *) )) == NULL ) {
+ /*
+ * we only parse the first rdn
+ * FIXME: we prefer efficiency over checking if the _ENTIRE_
+ * dn can be parsed
+ */
+ if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP )
+ != LDAP_SUCCESS ) {
return( NULL );
}
- maxcomps = 8;
- ncomps = 0;
- for ( s = strtok( dn, "@." ); s != NULL; s = strtok( NULL, "@." ) ) {
- if ( ncomps == maxcomps ) {
- maxcomps *= 2;
- if ( (rdns = (char **) realloc( rdns, maxcomps *
- sizeof(char *) )) == NULL ) {
- return( NULL );
+ for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) ;
+ values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
+ if ( values == NULL ) {
+ ldap_rdnfree( tmpRDN );
+ return( NULL );
+ }
+
+ for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) {
+ ber_len_t l = 0, vl, al = 0;
+ char *str;
+ LDAPAVA *ava = tmpRDN[ 0 ][ iAVA ];
+
+ if ( ava->la_flags == LDAP_AVA_BINARY ) {
+ vl = 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ if ( strval2strlen( &ava->la_value,
+ ava->la_flags, &vl ) ) {
+ goto error_return;
}
}
- rdns[ncomps++] = strdup( s );
+
+ if ( !notypes ) {
+ al = ava->la_attr.bv_len;
+ l = vl + ava->la_attr.bv_len + 1;
+
+ str = LDAP_MALLOC( l + 1 );
+ AC_MEMCPY( str, ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ str[ al++ ] = '=';
+
+ } else {
+ l = vl;
+ str = LDAP_MALLOC( l + 1 );
+ }
+
+ if ( ava->la_flags == LDAP_AVA_BINARY ) {
+ str[ al++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
+ goto error_return;
+ }
+
+ } else {
+ if ( strval2str( &ava->la_value, &str[ al ],
+ ava->la_flags, &vl ) ) {
+ goto error_return;
+ }
+ }
+
+ str[ l ] = '\0';
+ values[ iAVA ] = str;
}
- rdns[ncomps] = NULL;
+ values[ iAVA ] = NULL;
- return( rdns );
+ ldap_rdnfree( tmpRDN );
+
+ return( values );
+
+error_return:;
+ LBER_VFREE( values );
+ ldap_rdnfree( tmpRDN );
+ return( NULL );
}
-char **
-ldap_explode_dn( char *dn, int notypes )
+char *
+ldap_dn2dcedn( LDAP_CONST char *dn )
{
- char *p, *q, *rdnstart, **rdns = NULL;
- int state, count = 0, endquote, len;
+ char *out = NULL;
- Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_DCE );
+
+ return( out );
+}
+
+char *
+ldap_dcedn2dn( LDAP_CONST char *dce )
+{
+ char *out = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
+
+ return( out );
+}
+
+char *
+ldap_dn2ad_canonical( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_AD_CANONICAL );
+
+ return( out );
+}
+
+/*
+ * function that changes the string representation of dnin
+ * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
+ *
+ * fin can be one of:
+ * LDAP_DN_FORMAT_LDAP (rfc 2253 and ldapbis liberal,
+ * plus some rfc 1779)
+ * LDAP_DN_FORMAT_LDAPV3 (rfc 2253 and ldapbis)
+ * LDAP_DN_FORMAT_LDAPV2 (rfc 1779)
+ * LDAP_DN_FORMAT_DCE (?)
+ *
+ * fout can be any of the above except
+ * LDAP_DN_FORMAT_LDAP
+ * plus:
+ * LDAP_DN_FORMAT_UFN (rfc 1781, partial and with extensions)
+ * LDAP_DN_FORMAT_AD_CANONICAL (?)
+ */
+int
+ldap_dn_normalize( LDAP_CONST char *dnin,
+ unsigned fin, char **dnout, unsigned fout )
+{
+ int rc;
+ LDAPDN *tmpDN = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 );
+
+ assert( dnout );
+
+ *dnout = NULL;
+
+ if ( dnin == NULL ) {
+ return( LDAP_SUCCESS );
+ }
- if ( ldap_is_dns_dn( dn ) ) {
- return( ldap_explode_dns( dn ) );
+ rc = ldap_str2dn( dnin , &tmpDN, fin );
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
}
- rdnstart = dn;
- p = dn-1;
- state = OUTQUOTE;
+ rc = ldap_dn2str( tmpDN, dnout, fout );
- do {
+ ldap_dnfree( tmpDN );
- ++p;
- switch ( *p ) {
- case '\\':
- if ( *++p == '\0' )
- p--;
- break;
- case '"':
- if ( state == INQUOTE )
- state = OUTQUOTE;
- else
- state = INQUOTE;
- break;
- case ';':
- case ',':
- case '\0':
- if ( state == OUTQUOTE ) {
- ++count;
- if ( rdns == NULL ) {
- if (( rdns = (char **)malloc( 8
- * sizeof( char *))) == NULL )
- return( NULL );
- } else if ( count >= 8 ) {
- if (( rdns = (char **)realloc( rdns,
- (count+1) * sizeof( char *)))
- == NULL )
- return( NULL );
- }
- rdns[ count ] = NULL;
- endquote = 0;
- if ( notypes ) {
- for ( q = rdnstart;
- q < p && *q != '='; ++q ) {
- ;
- }
- if ( q < p ) {
- rdnstart = ++q;
- }
- if ( *rdnstart == '"' ) {
- ++rdnstart;
- }
-
- if ( *(p-1) == '"' ) {
- endquote = 1;
- --p;
- }
+ return( rc );
+}
+
+/* States */
+#define B4AVA 0x0000
+
+/* #define B4ATTRTYPE 0x0001 */
+#define B4OIDATTRTYPE 0x0002
+#define B4STRINGATTRTYPE 0x0003
+
+#define B4AVAEQUALS 0x0100
+#define B4AVASEP 0x0200
+#define B4RDNSEP 0x0300
+#define GOTAVA 0x0400
+
+#define B4ATTRVALUE 0x0010
+#define B4STRINGVALUE 0x0020
+#define B4IA5VALUEQUOTED 0x0030
+#define B4IA5VALUE 0x0040
+#define B4BINARYVALUE 0x0050
+
+/*
+ * Helpers (mostly from slap.h)
+ * c is assumed to Unicode in an ASCII compatible format (UTF-8)
+ * Macros assume "C" Locale (ASCII)
+ */
+#define LDAP_DN_ASCII_SPACE(c) \
+ ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
+#define LDAP_DN_ASCII_LOWER(c) LDAP_LOWER(c)
+#define LDAP_DN_ASCII_UPPER(c) LDAP_UPPER(c)
+#define LDAP_DN_ASCII_ALPHA(c) LDAP_ALPHA(c)
+
+#define LDAP_DN_ASCII_DIGIT(c) LDAP_DIGIT(c)
+#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) LDAP_HEXLOWER(c)
+#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) LDAP_HEXUPPER(c)
+#define LDAP_DN_ASCII_HEXDIGIT(c) LDAP_HEX(c)
+#define LDAP_DN_ASCII_ALNUM(c) LDAP_ALNUM(c)
+#define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' )
+
+/* attribute type */
+#define LDAP_DN_OID_LEADCHAR(c) LDAP_DIGIT(c)
+#define LDAP_DN_DESC_LEADCHAR(c) LDAP_ALPHA(c)
+#define LDAP_DN_DESC_CHAR(c) LDAP_LDH(c)
+#define LDAP_DN_LANG_SEP(c) ( (c) == ';' )
+#define LDAP_DN_ATTRDESC_CHAR(c) \
+ ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
+
+/* special symbols */
+#define LDAP_DN_AVA_EQUALS(c) ( (c) == '=' )
+#define LDAP_DN_AVA_SEP(c) ( (c) == '+' )
+#define LDAP_DN_RDN_SEP(c) ( (c) == ',' )
+#define LDAP_DN_RDN_SEP_V2(c) ( LDAP_DN_RDN_SEP(c) || (c) == ';' )
+#define LDAP_DN_OCTOTHORPE(c) ( (c) == '#' )
+#define LDAP_DN_QUOTES(c) ( (c) == '\"' )
+#define LDAP_DN_ESCAPE(c) ( (c) == '\\' )
+#define LDAP_DN_VALUE_END(c) \
+ ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) )
+#define LDAP_DN_NE(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
+ || LDAP_DN_QUOTES(c) || (c) == '<' || (c) == '>' )
+#define LDAP_DN_MAYESCAPE(c) \
+ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
+ || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_NEEDESCAPE(c) \
+ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
+#define LDAP_DN_NEEDESCAPE_LEAD(c) LDAP_DN_MAYESCAPE(c)
+#define LDAP_DN_NEEDESCAPE_TRAIL(c) \
+ ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
+#define LDAP_DN_WILLESCAPE_CHAR(c) \
+ ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_IS_PRETTY(f) ( (f) & LDAP_DN_PRETTY )
+#define LDAP_DN_WILLESCAPE_HEX(f, c) \
+ ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
+
+/* LDAPv2 */
+#define LDAP_DN_VALUE_END_V2(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) )
+/* RFC 1779 */
+#define LDAP_DN_V2_SPECIAL(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \
+ || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \
+ || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_V2_PAIR(c) \
+ ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) )
+
+/*
+ * DCE (mostly from Luke Howard and IBM implementation for AIX)
+ *
+ * From: "Application Development Guide - Directory Services" (FIXME: add link?)
+ * Here escapes and valid chars for GDS are considered; as soon as more
+ * specific info is found, the macros will be updated.
+ *
+ * Chars: 'a'-'z', 'A'-'Z', '0'-'9',
+ * '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '.
+ *
+ * Metachars: '/', ',', '=', '\'.
+ *
+ * the '\' is used to escape other metachars.
+ *
+ * Assertion: '='
+ * RDN separator: '/'
+ * AVA separator: ','
+ *
+ * Attribute types must start with alphabetic chars and can contain
+ * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
+ */
+#define LDAP_DN_RDN_SEP_DCE(c) ( (c) == '/' )
+#define LDAP_DN_AVA_SEP_DCE(c) ( (c) == ',' )
+#define LDAP_DN_ESCAPE_DCE(c) ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_VALUE_END_DCE(c) \
+ ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) )
+#define LDAP_DN_NEEDESCAPE_DCE(c) \
+ ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* AD Canonical */
+#define LDAP_DN_RDN_SEP_AD(c) ( (c) == '/' )
+#define LDAP_DN_ESCAPE_AD(c) ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_AVA_SEP_AD(c) ( (c) == ',' ) /* assume same as DCE */
+#define LDAP_DN_VALUE_END_AD(c) \
+ ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) )
+#define LDAP_DN_NEEDESCAPE_AD(c) \
+ ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* generics */
+#define LDAP_DN_HEXPAIR(s) \
+ ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
+/* better look at the AttributeDescription? */
+
+/* FIXME: no composite rdn or non-"dc" types, right?
+ * (what about "dc" in OID form?) */
+/* FIXME: we do not allow binary values in domain, right? */
+/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
+/* NOTE: don't use strcasecmp() as it is locale specific! */
+#define LDAP_DC_ATTR "dc"
+#define LDAP_DC_ATTRU "DC"
+#define LDAP_DN_IS_RDN_DC( r ) \
+ ( (r) && (r)[0][0] && !(r)[0][1] \
+ && ((r)[0][0]->la_flags == LDAP_AVA_STRING) \
+ && ((r)[0][0]->la_attr.bv_len == 2) \
+ && (((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
+ || ((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
+ && (((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
+ || ((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
+
+/* Composite rules */
+#define LDAP_DN_ALLOW_ONE_SPACE(f) \
+ ( LDAP_DN_LDAPV2(f) \
+ || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
+#define LDAP_DN_ALLOW_SPACES(f) \
+ ( LDAP_DN_LDAPV2(f) \
+ || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
+#define LDAP_DN_LDAP(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
+#define LDAP_DN_LDAPV3(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
+#define LDAP_DN_LDAPV2(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 )
+#define LDAP_DN_DCE(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE )
+#define LDAP_DN_UFN(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN )
+#define LDAP_DN_ADC(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL )
+#define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK )
+
+/*
+ * LDAPAVA helpers (will become part of the API for operations
+ * on structural representations of DNs).
+ */
+LDAPAVA *
+ldapava_new( const struct berval *attr, const struct berval *val,
+ unsigned flags )
+{
+ LDAPAVA *ava;
+
+ assert( attr );
+ assert( val );
+
+ ava = LDAP_MALLOC( sizeof( LDAPAVA ) + attr->bv_len + 1 );
+
+ /* should we test it? */
+ if ( ava == NULL ) {
+ return( NULL );
+ }
+
+ ava->la_attr.bv_len = attr->bv_len;
+ ava->la_attr.bv_val = (char *)(ava+1);
+ AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
+ ava->la_attr.bv_val[attr->bv_len] = '\0';
+
+ ava->la_value = *val;
+ ava->la_flags = flags;
+
+ ava->la_private = NULL;
+
+ return( ava );
+}
+
+void
+ldap_avafree( LDAPAVA *ava )
+{
+ assert( ava );
+
+#if 0
+ /* ava's private must be freed by caller
+ * (at present let's skip this check because la_private
+ * basically holds static data) */
+ assert( ava->la_private == NULL );
+#endif
+
+#if 0
+ /* la_attr is now contiguous with ava, not freed separately */
+ LDAP_FREE( ava->la_attr.bv_val );
+#endif
+ LDAP_FREE( ava->la_value.bv_val );
+
+ LDAP_FREE( ava );
+}
+
+void
+ldap_rdnfree( LDAPRDN *rdn )
+{
+ int iAVA;
+
+ if ( rdn == NULL ) {
+ return;
+ }
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ ldap_avafree( rdn[ 0 ][ iAVA ] );
+ }
+
+ LDAP_FREE( rdn );
+}
+
+void
+ldap_dnfree( LDAPDN *dn )
+{
+ int iRDN;
+
+ if ( dn == NULL ) {
+ return;
+ }
+
+ for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ldap_rdnfree( dn[ 0 ][ iRDN ] );
+ }
+
+ LDAP_FREE( dn );
+}
+
+/*
+ * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
+ * into a structural representation of the DN, by separating attribute
+ * types and values encoded in the more appropriate form, which is
+ * string or OID for attribute types and binary form of the BER encoded
+ * value or Unicode string. Formats different from LDAPv3 are parsed
+ * according to their own rules and turned into the more appropriate
+ * form according to LDAPv3.
+ *
+ * NOTE: I realize the code is getting spaghettish; it is rather
+ * experimental and will hopefully turn into something more simple
+ * and readable as soon as it works as expected.
+ */
+
+/*
+ * Default sizes of AVA and RDN static working arrays; if required
+ * the are dynamically resized. The values can be tuned in case
+ * of special requirements (e.g. very deep DN trees or high number
+ * of AVAs per RDN).
+ */
+#define TMP_AVA_SLOTS 8
+#define TMP_RDN_SLOTS 32
+
+int
+ldap_str2dn( LDAP_CONST char *str, LDAPDN **dn, unsigned flags )
+{
+ struct berval bv;
+
+ assert( str );
+
+ bv.bv_len = strlen( str );
+ bv.bv_val = (char *) str;
+
+ return ldap_bv2dn( &bv, dn, flags );
+}
+
+int
+ldap_bv2dn( struct berval *bv, LDAPDN **dn, unsigned flags )
+{
+ const char *p;
+ int rc = LDAP_DECODING_ERROR;
+ int nrdns = 0;
+
+ LDAPDN *newDN = NULL;
+ LDAPRDN *newRDN = NULL, *tmpDN_[TMP_RDN_SLOTS], **tmpDN = tmpDN_;
+ int num_slots = TMP_RDN_SLOTS;
+ char *str = bv->bv_val;
+ char *end = str + bv->bv_len;
+
+ assert( bv );
+ assert( bv->bv_val );
+ assert( dn );
+
+ Debug( LDAP_DEBUG_TRACE, "=> ldap_bv2dn(%s,%u)\n%s", str, flags, "" );
+
+ *dn = NULL;
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ case LDAP_DN_FORMAT_DCE:
+ break;
+
+ /* unsupported in str2dn */
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return LDAP_PARAM_ERROR;
+
+ case LDAP_DN_FORMAT_LBER:
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( bv->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+ }
+
+ if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+ /* value must have embedded NULs */
+ return LDAP_DECODING_ERROR;
+ }
+
+ p = str;
+ if ( LDAP_DN_DCE( flags ) ) {
+
+ /*
+ * (from Luke Howard: thnx) A RDN separator is required
+ * at the beginning of an (absolute) DN.
+ */
+ if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+ p++;
+
+ /*
+ * actually we do not want to accept by default the DCE form,
+ * we do not want to auto-detect it
+ */
+#if 0
+ } else if ( LDAP_DN_LDAP( flags ) ) {
+ /*
+ * if dn starts with '/' let's make it a DCE dn
+ */
+ if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ flags |= LDAP_DN_FORMAT_DCE;
+ p++;
+ }
+#endif
+ }
+
+ for ( ; p < end; p++ ) {
+ int err;
+ struct berval tmpbv;
+ tmpbv.bv_len = bv->bv_len - ( p - str );
+ tmpbv.bv_val = (char *)p;
+
+ err = ldap_bv2rdn( &tmpbv, &newRDN, (char **) &p, flags );
+ if ( err != LDAP_SUCCESS ) {
+ goto parsing_error;
+ }
+
+ /*
+ * We expect a rdn separator
+ */
+ if ( p < end && p[ 0 ] ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
}
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+ }
+ }
+
- len = p - rdnstart;
- if (( rdns[ count-1 ] = (char *)calloc( 1,
- len + 1 )) != NULL ) {
- SAFEMEMCPY( rdns[ count-1 ], rdnstart,
- len );
- rdns[ count-1 ][ len ] = '\0';
+ tmpDN[nrdns++] = newRDN;
+ newRDN = NULL;
+
+ /*
+ * make the static RDN array dynamically rescalable
+ */
+ if ( nrdns == num_slots ) {
+ LDAPRDN **tmp;
+
+ if ( tmpDN == tmpDN_ ) {
+ tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPRDN * ) );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
}
+ AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
- /*
- * Don't forget to increment 'p' back to where
- * it should be. If we don't, then we will
- * never get past an "end quote."
- */
- if ( endquote == 1 )
- p++;
+ } else {
+ tmp = LDAP_REALLOC( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ) );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ }
+
+ tmpDN = tmp;
+ num_slots *= 2;
+ }
+
+ if ( p >= end || p[ 0 ] == '\0' ) {
+ /*
+ * the DN is over, phew
+ */
+ newDN = (LDAPDN *)LDAP_MALLOC( sizeof(LDAPDN) +
+ sizeof(LDAPRDN *) * (nrdns+1));
+ if ( newDN == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ } else {
+ int i;
+
+ newDN[0] = (LDAPRDN **)(newDN+1);
- rdnstart = *p ? p + 1 : p;
- while ( isspace( *rdnstart ))
- ++rdnstart;
+ if ( LDAP_DN_DCE( flags ) ) {
+ /* add in reversed order */
+ for ( i=0; i<nrdns; i++ )
+ newDN[0][i] = tmpDN[nrdns-1-i];
+ } else {
+ for ( i=0; i<nrdns; i++ )
+ newDN[0][i] = tmpDN[i];
+ }
+ newDN[0][nrdns] = NULL;
+ rc = LDAP_SUCCESS;
}
- break;
+ goto return_result;
}
- } while ( *p );
+ }
+
+parsing_error:;
+ if ( newRDN ) {
+ ldap_rdnfree( newRDN );
+ }
- return( rdns );
-}
+ for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
+ ldap_rdnfree( tmpDN[nrdns] );
+ }
+return_result:;
+
+ if ( tmpDN != tmpDN_ ) {
+ LDAP_FREE( tmpDN );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= ldap_bv2dn(%s,%u)=%d\n", str, flags, rc );
+ *dn = newDN;
+
+ return( rc );
+}
+/*
+ * ldap_str2rdn
+ *
+ * Parses a relative DN according to flags up to a rdn separator
+ * or to the end of str.
+ * Returns the rdn and a pointer to the string continuation, which
+ * corresponds to the rdn separator or to '\0' in case the string is over.
+ */
int
-ldap_is_dns_dn( char *dn )
+ldap_str2rdn( LDAP_CONST char *str, LDAPRDN **rdn,
+ char **n_in, unsigned flags )
{
- return( dn[ 0 ] != '\0' && strchr( dn, '=' ) == NULL &&
- strchr( dn, ',' ) == NULL );
-}
+ struct berval bv;
+ assert( str );
+ assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */
-#if defined( ultrix ) || defined( NeXT )
+ bv.bv_len = strlen( str );
+ bv.bv_val = (char *) str;
-char *strdup( char *s )
+ return ldap_bv2rdn( &bv, rdn, n_in, flags );
+}
+
+int
+ldap_bv2rdn( struct berval *bv, LDAPRDN **rdn,
+ char **n_in, unsigned flags )
{
- char *p;
+ const char **n = (const char **) n_in;
+ const char *p;
+ int navas = 0;
+ int state = B4AVA;
+ int rc = LDAP_DECODING_ERROR;
+ int attrTypeEncoding = LDAP_AVA_STRING,
+ attrValueEncoding = LDAP_AVA_STRING;
- if ( (p = (char *) malloc( strlen( s ) + 1 )) == NULL )
- return( NULL );
+ struct berval attrType = { 0, NULL };
+ struct berval attrValue = { 0, NULL };
+
+ LDAPRDN *newRDN = NULL;
+ LDAPAVA *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
+ int num_slots = TMP_AVA_SLOTS;
+
+ char *str;
+ ber_len_t stoplen;
+
+ assert( bv );
+ assert( bv->bv_len );
+ assert( bv->bv_val );
+ assert( rdn || flags & LDAP_DN_SKIP );
+ assert( n );
+
+ str = bv->bv_val;
+ stoplen = bv->bv_len;
+
+ if ( rdn ) {
+ *rdn = NULL;
+ }
+ *n = NULL;
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ case LDAP_DN_FORMAT_DCE:
+ break;
+
+ /* unsupported in str2dn */
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return LDAP_PARAM_ERROR;
+
+ case LDAP_DN_FORMAT_LBER:
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( bv->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+
+ }
+
+ if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+ /* value must have embedded NULs */
+ return LDAP_DECODING_ERROR;
+ }
+
+ p = str;
+ for ( ; p[ 0 ] || state == GOTAVA; ) {
+
+ /*
+ * The parser in principle advances one token a time,
+ * or toggles state if preferable.
+ */
+ switch (state) {
+
+ /*
+ * an AttributeType can be encoded as:
+ * - its string representation; in detail, implementations
+ * MUST recognize AttributeType string type names listed
+ * in section 2.3 of draft-ietf-ldapbis-dn-XX.txt, and
+ * MAY recognize other names.
+ * - its numeric OID (a dotted decimal string); in detail
+ * RFC 2253 asserts that ``Implementations MUST allow
+ * an oid in the attribute type to be prefixed by one
+ * of the character strings "oid." or "OID."''. As soon
+ * as draft-ietf-ldapbis-dn-XX.txt obsoletes RFC 2253
+ * I'm not sure whether this is required or not any
+ * longer; to be liberal, we still implement it.
+ */
+ case B4AVA:
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
+ /* error */
+ goto parsing_error;
+ }
+ p++;
+ }
+
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ /* error */
+ goto parsing_error;
+ }
+
+ /* whitespace is allowed (and trimmed) */
+ p++;
+ while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ p++;
+ }
+
+ if ( !p[ 0 ] ) {
+ /* error: we expected an AVA */
+ goto parsing_error;
+ }
+ }
+
+ /* oid */
+ if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
+ state = B4OIDATTRTYPE;
+ break;
+ }
+
+ /* else must be alpha */
+ if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+
+ /* LDAPv2 "oid." prefix */
+ if ( LDAP_DN_LDAPV2( flags ) ) {
+ /*
+ * to be overly pedantic, we only accept
+ * "OID." or "oid."
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ if ( !strncmp( p, "OID.", 4 )
+ || !strncmp( p, "oid.", 4 ) ) {
+ p += 4;
+ state = B4OIDATTRTYPE;
+ break;
+ }
+ } else {
+ if ( !strncasecmp( p, "oid.", 4 ) ) {
+ p += 4;
+ state = B4OIDATTRTYPE;
+ break;
+ }
+ }
+ }
+
+ state = B4STRINGATTRTYPE;
+ break;
+
+ case B4OIDATTRTYPE: {
+ int err = LDAP_SUCCESS;
+
+ attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
+ LDAP_SCHEMA_SKIP);
+
+ if ( err != LDAP_SUCCESS ) {
+ goto parsing_error;
+ }
+ attrType.bv_len = p - attrType.bv_val;
+
+ attrTypeEncoding = LDAP_AVA_BINARY;
+
+ state = B4AVAEQUALS;
+ break;
+ }
+
+ case B4STRINGATTRTYPE: {
+ const char *startPos, *endPos = NULL;
+ ber_len_t len;
+
+ /*
+ * the starting char has been found to be
+ * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
+ * FIXME: DCE attr types seem to have a more
+ * restrictive syntax (no '-' ...)
+ */
+ for ( startPos = p++; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
+ continue;
+ }
+
+ if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) {
+
+ /*
+ * RFC 2253 does not explicitly
+ * allow lang extensions to attribute
+ * types in DNs ...
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ goto parsing_error;
+ }
+
+ /*
+ * we trim ';' and following lang
+ * and so from attribute types
+ */
+ endPos = p;
+ for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] )
+ || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) {
+ /* no op */ ;
+ }
+ break;
+ }
+ break;
+ }
+
+ len = ( endPos ? endPos : p ) - startPos;
+ if ( len == 0 ) {
+ goto parsing_error;
+ }
+
+ attrTypeEncoding = LDAP_AVA_STRING;
+
+ /*
+ * here we need to decide whether to use it as is
+ * or turn it in OID form; as a consequence, we
+ * need to decide whether to binary encode the value
+ */
+
+ state = B4AVAEQUALS;
+
+ if ( flags & LDAP_DN_SKIP ) {
+ break;
+ }
+
+ attrType.bv_val = (char *)startPos;
+ attrType.bv_len = len;
+
+ break;
+ }
+
+ case B4AVAEQUALS:
+ /* spaces may not be allowed */
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ goto parsing_error;
+ }
+
+ /* trim spaces */
+ for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+ }
+
+ /* need equal sign */
+ if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+ p++;
+
+ /* spaces may not be allowed */
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ goto parsing_error;
+ }
+
+ /* trim spaces */
+ for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+ }
+
+ /*
+ * octothorpe means a BER encoded value will follow
+ * FIXME: I don't think DCE will allow it
+ */
+ if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) {
+ p++;
+ attrValueEncoding = LDAP_AVA_BINARY;
+ state = B4BINARYVALUE;
+ break;
+ }
+
+ /* STRING value expected */
+
+ /*
+ * if we're pedantic, an attribute type in OID form
+ * SHOULD imply a BER encoded attribute value; we
+ * should at least issue a warning
+ */
+ if ( ( flags & LDAP_DN_PEDANTIC )
+ && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) {
+ /* OID attrType SHOULD use binary encoding */
+ goto parsing_error;
+ }
+
+ attrValueEncoding = LDAP_AVA_STRING;
+
+ /*
+ * LDAPv2 allows the attribute value to be quoted;
+ * also, IA5 values are expected, in principle
+ */
+ if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
+ if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+ p++;
+ state = B4IA5VALUEQUOTED;
+ break;
+ }
+
+ if ( LDAP_DN_LDAPV2( flags ) ) {
+ state = B4IA5VALUE;
+ break;
+ }
+ }
+
+ /*
+ * here STRING means RFC 2253 string
+ * FIXME: what about DCE strings?
+ */
+ if ( !p[ 0 ] ) {
+ /* empty value */
+ state = GOTAVA;
+ } else {
+ state = B4STRINGVALUE;
+ }
+ break;
+
+ case B4BINARYVALUE:
+ if ( hexstr2binval( p, &attrValue, &p, flags ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4STRINGVALUE:
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( str2strval( p, stoplen - ( p - str ),
+ &attrValue, &p, flags,
+ &attrValueEncoding ) ) {
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( DCE2strval( p, &attrValue, &p, flags ) ) {
+ goto parsing_error;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4IA5VALUE:
+ if ( IA52strval( p, &attrValue, &p, flags ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4IA5VALUEQUOTED:
+
+ /* lead quote already stripped */
+ if ( quotedIA52strval( p, &attrValue,
+ &p, flags ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case GOTAVA: {
+ int rdnsep = 0;
+
+ if ( !( flags & LDAP_DN_SKIP ) ) {
+ LDAPAVA *ava;
+
+ /*
+ * we accept empty values
+ */
+ ava = ldapava_new( &attrType, &attrValue,
+ attrValueEncoding );
+
+ if ( ava == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ tmpRDN[navas++] = ava;
+
+ attrValue.bv_val = NULL;
+ attrValue.bv_len = 0;
+
+ /*
+ * prepare room for new AVAs if needed
+ */
+ if (navas == num_slots) {
+ LDAPAVA **tmp;
+
+ if ( tmpRDN == tmpRDN_ ) {
+ tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPAVA * ) );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
+
+ } else {
+ tmp = LDAP_REALLOC( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ) );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ }
+
+ tmpRDN = tmp;
+ num_slots *= 2;
+ }
+ }
+
+ /*
+ * if we got an AVA separator ('+', or ',' for DCE )
+ * we expect a new AVA for this RDN; otherwise
+ * we add the RDN to the DN
+ */
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
+ rdnsep = 1;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) {
+ rdnsep = 1;
+ }
+ break;
+ }
+
+ if ( rdnsep ) {
+ /*
+ * the RDN is over, phew
+ */
+ *n = p;
+ if ( !( flags & LDAP_DN_SKIP ) ) {
+ newRDN = (LDAPRDN *)LDAP_MALLOC( sizeof(LDAPRDN)
+ + sizeof(LDAPAVA *) * (navas+1) );
+ if ( newRDN == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ } else {
+ int i;
+
+ newRDN[0] = (LDAPAVA**)(newRDN+1);
+
+ for (i=0; i<navas; i++)
+ newRDN[0][i] = tmpRDN[i];
+ newRDN[0][i] = NULL;
+ }
+
+ }
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+
+ /* they should have been used in an AVA */
+ attrType.bv_val = NULL;
+ attrValue.bv_val = NULL;
+
+ p++;
+ state = B4AVA;
+ break;
+ }
+
+ default:
+ assert( 0 );
+ goto parsing_error;
+ }
+ }
+ *n = p;
+
+parsing_error:;
+ /* They are set to NULL after they're used in an AVA */
+
+ if ( attrValue.bv_val ) {
+ free( attrValue.bv_val );
+ }
+
+ for ( navas-- ; navas >= 0; navas-- ) {
+ ldap_avafree( tmpRDN[navas] );
+ }
+
+return_result:;
+
+ if ( tmpRDN != tmpRDN_ ) {
+ LDAP_FREE( tmpRDN );
+ }
+
+ if ( rdn ) {
+ *rdn = newRDN;
+ }
+
+ return( rc );
+}
+
+/*
+ * reads in a UTF-8 string value, unescaping stuff:
+ * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c'
+ * '\' + HEXPAIR(p) -> unhex(p)
+ */
+static int
+str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, unsigned *retFlags )
+{
+ const char *p, *end, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str );
+ assert( val );
+ assert( next );
+
+ *next = NULL;
+ end = str + stoplen;
+ for ( startPos = p = str, escapes = 0; p < end; p++ ) {
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ p++;
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+ if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
+ escapes++;
+ continue;
+ }
+
+ if ( LDAP_DN_HEXPAIR( p ) ) {
+ char c;
+
+ hexstr2bin( p, &c );
+ escapes += 2;
+
+ if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
+
+ /*
+ * we assume the string is UTF-8
+ */
+ *retFlags = LDAP_AVA_NONPRINTABLE;
+ }
+ p++;
+
+ continue;
+ }
+
+ if ( LDAP_DN_PEDANTIC & flags ) {
+ return( 1 );
+ }
+ /*
+ * we do not allow escaping
+ * of chars that don't need
+ * to and do not belong to
+ * HEXDIGITS
+ */
+ return( 1 );
+
+ } else if (!LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+ *retFlags = LDAP_AVA_NONPRINTABLE;
+
+ } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) )
+ || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
+ break;
+
+ } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
+ /*
+ * FIXME: maybe we can add
+ * escapes if not pedantic?
+ */
+ return( 1 );
+ }
+ }
+
+ /*
+ * we do allow unescaped spaces at the end
+ * of the value only in non-pedantic mode
+ */
+ if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p - 1;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ /*
+ * FIXME: test memory?
+ */
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+
+ if ( escapes == 0 ) {
+ if ( *retFlags == LDAP_AVA_NONPRINTABLE ) {
+ val->bv_val = LDAP_MALLOC( len + 1 );
+ AC_MEMCPY( val->bv_val, startPos, len );
+ val->bv_val[ len ] = '\0';
+ } else {
+ val->bv_val = LDAP_STRNDUP( startPos, len );
+ }
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOC( len + 1 );
+ for ( s = 0, d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+ s++;
+ if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
+ val->bv_val[ d++ ] =
+ startPos[ s++ ];
+
+ } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
+ char c;
+
+ hexstr2bin( &startPos[ s ], &c );
+ val->bv_val[ d++ ] = c;
+ s += 2;
+
+ } else {
+ /* we should never get here */
+ assert( 0 );
+ }
+
+ } else {
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ }
+
+ val->bv_val[ d ] = '\0';
+ assert( d == len );
+ }
+
+ return( 0 );
+}
+
+static int
+DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str );
+ assert( val );
+ assert( next );
+
+ *next = NULL;
+
+ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) {
+ p++;
+ if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+ escapes++;
+
+ } else {
+ return( 1 );
+ }
+
+ } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ /*
+ * (unescaped) trailing spaces are trimmed must be silently ignored;
+ * so we eat them
+ */
+ if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p - 1;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+ if ( escapes == 0 ){
+ val->bv_val = LDAP_STRNDUP( startPos, len );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOC( len + 1 );
+ for ( s = 0, d = 0; d < len; ) {
+ /*
+ * This point is reached only if escapes
+ * are properly used, so all we need to
+ * do is eat them
+ */
+ if ( LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) {
+ s++;
+
+ }
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+IA52strval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str );
+ assert( val );
+ assert( next );
+
+ *next = NULL;
+
+ /*
+ * LDAPv2 (RFC 1779)
+ */
+
+ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ p++;
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+
+ if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ && ( LDAP_DN_PEDANTIC & flags ) ) {
+ return( 1 );
+ }
+ escapes++;
+
+ } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+ if ( escapes == 0 ) {
+ val->bv_val = LDAP_STRNDUP( startPos, len );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOC( len + 1 );
+ for ( s = 0, d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+ s++;
+ }
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len;
+ unsigned escapes = 0;
+
+ assert( str );
+ assert( val );
+ assert( next );
+
+ *next = NULL;
+
+ /* initial quote already eaten */
+ for ( startPos = p = str; p[ 0 ]; p++ ) {
+ /*
+ * According to RFC 1779, the quoted value can
+ * contain escaped as well as unescaped special values;
+ * as a consequence we tolerate escaped values
+ * (e.g. '"\,"' -> '\,') and escape unescaped specials
+ * (e.g. '","' -> '\,').
+ */
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ if ( p[ 1 ] == '\0' ) {
+ return( 1 );
+ }
+ p++;
+
+ if ( !LDAP_DN_V2_PAIR( p[ 0 ] )
+ && ( LDAP_DN_PEDANTIC & flags ) ) {
+ /*
+ * do we allow to escape normal chars?
+ * LDAPv2 does not allow any mechanism
+ * for escaping chars with '\' and hex
+ * pair
+ */
+ return( 1 );
+ }
+ escapes++;
+
+ } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+ endPos = p;
+ /* eat closing quotes */
+ p++;
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ if ( endPos == NULL ) {
+ return( 1 );
+ }
+
+ /* Strip trailing (unescaped) spaces */
+ for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = endPos - startPos - escapes;
+ assert( len >= 0 );
+ val->bv_len = len;
+ if ( escapes == 0 ) {
+ val->bv_val = LDAP_STRNDUP( startPos, len );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOC( len + 1 );
+ val->bv_len = len;
+
+ for ( s = d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
+ s++;
+ }
+ val->bv_val[ d++ ] = str[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+hexstr2bin( const char *str, char *c )
+{
+ char c1, c2;
+
+ assert( str );
+ assert( c );
+
+ c1 = str[ 0 ];
+ c2 = str[ 1 ];
+
+ if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
+ *c = c1 - '0';
+
+ } else {
+ if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
+ *c = c1 - 'A' + 10;
+ } else {
+ assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
+ *c = c1 - 'a' + 10;
+ }
+ }
+
+ *c <<= 4;
+
+ if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
+ *c += c2 - '0';
+
+ } else {
+ if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
+ *c += c2 - 'A' + 10;
+ } else {
+ assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
+ *c += c2 - 'a' + 10;
+ }
+ }
+
+ return( 0 );
+}
+
+static int
+hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len;
+ ber_len_t s, d;
+
+ assert( str );
+ assert( val );
+ assert( next );
+
+ *next = NULL;
+
+ for ( startPos = p = str; p[ 0 ]; p += 2 ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+ }
+
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+ endPos = p;
+
+ for ( ; p[ 0 ]; p++ ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ if ( !LDAP_DN_HEXPAIR( p ) ) {
+ return( 1 );
+ }
+ }
+
+end_of_value:;
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( ( endPos ? endPos : p ) - startPos ) / 2;
+ /* must be even! */
+ assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
+
+ val->bv_len = len;
+ val->bv_val = LDAP_MALLOC( len + 1 );
+ if ( val->bv_val == NULL ) {
+ return( LDAP_NO_MEMORY );
+ }
+
+ for ( s = 0, d = 0; d < len; s += 2, d++ ) {
+ char c;
+
+ hexstr2bin( &startPos[ s ], &c );
+
+ val->bv_val[ d ] = c;
+ }
+
+ val->bv_val[ d ] = '\0';
+
+ return( 0 );
+}
+
+/*
+ * convert a byte in a hexadecimal pair
+ */
+static int
+byte2hexpair( const char *val, char *pair )
+{
+ static const char hexdig[] = "0123456789ABCDEF";
+
+ assert( val );
+ assert( pair );
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ];
+ pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ];
+
+ return( 0 );
+}
+
+/*
+ * convert a binary value in hexadecimal pairs
+ */
+static int
+binval2hexstr( struct berval *val, char *str )
+{
+ ber_len_t s, d;
+
+ assert( val );
+ assert( str );
+
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) {
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ }
+
+ return( 0 );
+}
+
+/*
+ * Length of the string representation, accounting for escaped hex
+ * of UTF-8 chars
+ */
+static int
+strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l, cl = 1;
+ char *p;
+ int escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
+#ifdef PRETTY_ESCAPE
+ int escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
+#endif /* PRETTY_ESCAPE */
+
+ assert( val );
+ assert( len );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ for ( l = 0, p = val->bv_val; p < val->bv_val + val->bv_len; p += cl ) {
+
+ /*
+ * escape '%x00'
+ */
+ if ( p[ 0 ] == '\0' ) {
+ cl = 1;
+ l += 3;
+ continue;
+ }
+
+ cl = LDAP_UTF8_CHARLEN2( p, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char! */
+ return( -1 );
+
+ } else if ( cl > 1 ) {
+ ber_len_t cnt;
+
+ for ( cnt = 1; cnt < cl; cnt++ ) {
+ if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
+ return( -1 );
+ }
+ }
+ l += escaped_byte_len * cl;
+
+ } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+ || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+#ifdef PRETTY_ESCAPE
+#if 0
+ if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
+#else
+ if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
+#endif
+
+ /*
+ * there might be some chars we want
+ * to escape in form of a couple
+ * of hexdigits for optimization purposes
+ */
+ l += 3;
+
+ } else {
+ l += escaped_ascii_len;
+ }
+#else /* ! PRETTY_ESCAPE */
+ l += 3;
+#endif /* ! PRETTY_ESCAPE */
+
+ } else {
+ l++;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to string representation, escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, end;
+
+ assert( val );
+ assert( str );
+ assert( len );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+ ber_len_t cl;
+
+ /*
+ * escape '%x00'
+ */
+ if ( val->bv_val[ s ] == '\0' ) {
+ cl = 1;
+ str[ d++ ] = '\\';
+ str[ d++ ] = '0';
+ str[ d++ ] = '0';
+ s++;
+ continue;
+ }
+
+ /*
+ * The length was checked in strval2strlen();
+ * LDAP_UTF8_CHARLEN() should suffice
+ */
+ cl = LDAP_UTF8_CHARLEN2( &val->bv_val[ s ], cl );
+ assert( cl > 0 );
+
+ /*
+ * there might be some chars we want to escape in form
+ * of a couple of hexdigits for optimization purposes
+ */
+ if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) )
+#ifdef PRETTY_ESCAPE
+#if 0
+ || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] )
+#else
+ || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] )
+#endif
+#else /* ! PRETTY_ESCAPE */
+ || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
+
+#endif /* ! PRETTY_ESCAPE */
+ ) {
+ for ( ; cl--; ) {
+ str[ d++ ] = '\\';
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ s++;
+ d += 2;
+ }
+
+ } else if ( cl > 1 ) {
+ for ( ; cl--; ) {
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+
+ } else {
+#ifdef PRETTY_ESCAPE
+ if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+ str[ d++ ] = '\\';
+ if ( !LDAP_DN_IS_PRETTY( flags ) ) {
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ s++;
+ d += 2;
+ continue;
+ }
+ }
+#endif /* PRETTY_ESCAPE */
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the IA5 string representation (no UTF-8 allowed)
+ */
+static int
+strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val );
+ assert( len );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * Turn value into a binary encoded BER
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+ || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to string representation (np UTF-8)
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, end;
+
+ assert( val );
+ assert( str );
+ assert( len );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * Turn value into a binary encoded BER
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the (supposedly) DCE string representation,
+ * accounting for escaped hex of UTF-8 chars
+ */
+static int
+strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val );
+ assert( len );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to (supposedly) DCE string representation,
+ * escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d;
+
+ assert( val );
+ assert( str );
+ assert( len );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the (supposedly) AD canonical string representation,
+ * accounting for escaped hex of UTF-8 chars
+ */
+static int
+strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val );
+ assert( len );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to (supposedly) AD string representation,
+ * escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d;
+
+ assert( val );
+ assert( str );
+ assert( len );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE_AD( val->bv_val[ s ] ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
+ * the first part of the AD representation of the DN is written in DNS
+ * form, i.e. dot separated domain name components (as suggested
+ * by Luke Howard, http://www.padl.com/~lukeh)
+ */
+static int
+dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN )
+{
+ int i;
+ int domain = 0, first = 1;
+ ber_len_t l = 1; /* we move the null also */
+ char *str;
+
+ /* we are guaranteed there's enough memory in str */
+
+ /* sanity */
+ assert( dn );
+ assert( bv );
+ assert( iRDN );
+ assert( *iRDN >= 0 );
+
+ str = bv->bv_val + pos;
+
+ for ( i = *iRDN; i >= 0; i-- ) {
+ LDAPRDN *rdn;
+ LDAPAVA *ava;
+
+ assert( dn[ 0 ][ i ] );
+ rdn = dn[ 0 ][ i ];
+
+ assert( rdn[ 0 ][ 0 ] );
+ ava = rdn[ 0 ][ 0 ];
+
+ if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
+ break;
+ }
+
+ domain = 1;
+
+ if ( first ) {
+ first = 0;
+ AC_MEMCPY( str, ava->la_value.bv_val,
+ ava->la_value.bv_len + 1);
+ l += ava->la_value.bv_len;
+
+ } else {
+ AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
+ AC_MEMCPY( str, ava->la_value.bv_val,
+ ava->la_value.bv_len );
+ str[ ava->la_value.bv_len ] = '.';
+ l += ava->la_value.bv_len + 1;
+ }
+ }
+
+ *iRDN = i;
+ bv->bv_len = pos + l - 1;
+
+ return( domain );
+}
+
+static int
+rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len,
+ int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ /* len(type) + '=' + '+' | ',' */
+ l += ava->la_attr.bv_len + 2;
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the length */
+ l += 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len,
+ int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ l += ava->la_attr.bv_len;
+
+ str[ l++ ] = '=';
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ str[ l++ ] = ( rdn[ 0 ][ iAVA + 1 ] ? '+' : ',' );
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ /* len(type) + '=' + ',' | '/' */
+ l += ava->la_attr.bv_len + 2;
+
+ switch ( ava->la_flags ) {
+ case LDAP_AVA_BINARY:
+ /* octothorpe + twice the length */
+ l += 1 + 2 * ava->la_value.bv_len;
+ break;
+
+ case LDAP_AVA_STRING: {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ break;
+ }
+
+ default:
+ return( -1 );
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ if ( first ) {
+ first = 0;
+ } else {
+ str[ l++ ] = ( iAVA ? ',' : '/' );
+ }
+
+ AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ l += ava->la_attr.bv_len;
+
+ str[ l++ ] = '=';
+
+ switch ( ava->la_flags ) {
+ case LDAP_AVA_BINARY:
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+ break;
+
+ case LDAP_AVA_STRING: {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ break;
+ }
+
+ default:
+ return( -1 );
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ assert( rdn );
+ assert( len );
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ /* ' + ' | ', ' */
+ l += ( rdn[ 0 ][ iAVA + 1 ] ? 3 : 2 );
+
+ /* FIXME: are binary values allowed in UFN? */
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the value */
+ l += 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2strlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+
+ if ( rdn[ 0 ][ iAVA + 1 ]) {
+ AC_MEMCPY( &str[ l ], " + ", 3 );
+ l += 3;
+
+ } else {
+ AC_MEMCPY( &str[ l ], ", ", 2 );
+ l += 2;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ assert( rdn );
+ assert( len );
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ /* ',' | '/' */
+ l++;
+
+ /* FIXME: are binary values allowed in UFN? */
+ switch ( ava->la_flags ) {
+ case LDAP_AVA_BINARY:
+ /* octothorpe + twice the value */
+ l += 1 + 2 * ava->la_value.bv_len;
+ break;
+
+ case LDAP_AVA_STRING: {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ break;
+ }
+
+ default:
+ return( -1 );
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ 0 ][ iAVA ];
+
+ if ( first ) {
+ first = 0;
+ } else {
+ str[ l++ ] = ( iAVA ? ',' : '/' );
+ }
+
+ switch ( ava->la_flags ) {
+ case LDAP_AVA_BINARY:
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+ break;
+
+ case LDAP_AVA_STRING: {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ break;
+ }
+
+ default:
+ return( -1 );
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * ldap_rdn2str
+ *
+ * Returns in str a string representation of rdn based on flags.
+ * There is some duplication of code between this and ldap_dn2str;
+ * this is wanted to reduce the allocation of temporary buffers.
+ */
+int
+ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags )
+{
+ struct berval bv;
+ int rc;
+
+ assert( str );
+
+ if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = ldap_rdn2bv( rdn, &bv, flags );
+ *str = bv.bv_val;
+ return rc;
+}
+
+int
+ldap_rdn2bv( LDAPRDN *rdn, struct berval *bv, unsigned flags )
+{
+ int rc, back;
+ ber_len_t l;
+
+ assert( bv );
+
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ if ( rdn == NULL ) {
+ bv->bv_val = LDAP_STRDUP( "" );
+ return( LDAP_SUCCESS );
+ }
+
+ /*
+ * This routine wastes "back" bytes at the end of the string
+ */
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_UFN:
+ if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ if ( rdn2ADstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ default:
+ return( LDAP_PARAM_ERROR );
+ }
+
+ bv->bv_val = LDAP_MALLOC( l + 1 );
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
+ back = 1;
+ break;
+
+ case LDAP_DN_FORMAT_LDAPV2:
+ rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
+ back = 1;
+ break;
+
+ case LDAP_DN_FORMAT_UFN:
+ rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
+ back = 2;
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
+ back = 0;
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
+ back = 0;
+ break;
+
+ default:
+ /* need at least one of the previous */
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( rc ) {
+ ldap_memfree( bv->bv_val );
+ return rc;
+ }
+
+ bv->bv_len = l - back;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Very bulk implementation; many optimizations can be performed
+ * - a NULL dn results in an empty string ""
+ *
+ * FIXME: doubts
+ * a) what do we do if a UTF-8 string must be converted in LDAPv2?
+ * we must encode it in binary form ('#' + HEXPAIRs)
+ * b) does DCE/AD support UTF-8?
+ * no clue; don't think so.
+ * c) what do we do when binary values must be converted in UTF/DCE/AD?
+ * use binary encoded BER
+ */
+int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags )
+{
+ struct berval bv;
+ int rc;
+
+ assert( str );
+
+ if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = ldap_dn2bv( dn, &bv, flags );
+ *str = bv.bv_val;
+ return rc;
+}
+
+int ldap_dn2bv( LDAPDN *dn, struct berval *bv, unsigned flags )
+{
+ int iRDN;
+ int rc = LDAP_ENCODING_ERROR;
+ ber_len_t len, l;
+
+ /* stringifying helpers for LDAPv3/LDAPv2 */
+ int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
+ int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
+
+ assert( bv );
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2bv(%u)\n%s%s", flags, "", "" );
+
+ /*
+ * a null dn means an empty dn string
+ * FIXME: better raise an error?
+ */
+ if ( dn == NULL ) {
+ bv->bv_val = LDAP_STRDUP( "" );
+ return( LDAP_SUCCESS );
+ }
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ sv2l = strval2strlen;
+ sv2s = strval2str;
+
+ if( 0 ) {
+ case LDAP_DN_FORMAT_LDAPV2:
+ sv2l = strval2IA5strlen;
+ sv2s = strval2IA5str;
+ }
+
+ for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2strlen( rdn, flags, &rdnl, sv2l ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2str( rdn, &bv->bv_val[ l ], flags,
+ &rdnl, sv2s ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ assert( l == len );
+
+ /*
+ * trim the last ',' (the allocated memory
+ * is one byte longer than required)
+ */
+ bv->bv_len = len - 1;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_DN_FORMAT_UFN: {
+ /*
+ * FIXME: quoting from RFC 1781:
+ *
+ To take a distinguished name, and generate a name of this format with
+ attribute types omitted, the following steps are followed.
+
+ 1. If the first attribute is of type CommonName, the type may be
+ omitted.
+
+ 2. If the last attribute is of type Country, the type may be
+ omitted.
+
+ 3. If the last attribute is of type Country, the last
+ Organisation attribute may have the type omitted.
+
+ 4. All attributes of type OrganisationalUnit may have the type
+ omitted, unless they are after an Organisation attribute or
+ the first attribute is of type OrganisationalUnit.
+
+ * this should be the pedantic implementation.
+ *
+ * Here the standard implementation reflects
+ * the one historically provided by OpenLDAP
+ * (and UMIch, I presume), with the variant
+ * of spaces and plusses (' + ') separating
+ * rdn components.
+ *
+ * A non-standard but nice implementation could
+ * be to turn the final "dc" attributes into a
+ * dot-separated domain.
+ *
+ * Other improvements could involve the use of
+ * friendly country names and so.
+ */
+#ifdef DC_IN_UFN
+ int leftmost_dc = -1;
+ int last_iRDN = -1;
+#endif /* DC_IN_UFN */
+
+ for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2UFNstrlen( rdn, flags, &rdnl ) ) {
+ goto return_results;
+ }
+ len += rdnl;
+
+#ifdef DC_IN_UFN
+ if ( LDAP_DN_IS_RDN_DC( rdn ) ) {
+ if ( leftmost_dc == -1 ) {
+ leftmost_dc = iRDN;
+ }
+ } else {
+ leftmost_dc = -1;
+ }
+#endif /* DC_IN_UFN */
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+#ifdef DC_IN_UFN
+ if ( leftmost_dc == -1 ) {
+#endif /* DC_IN_UFN */
+ for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ber_len_t vl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2UFNstr( rdn, &bv->bv_val[ l ],
+ flags, &vl ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += vl;
+ }
+
+ /*
+ * trim the last ', ' (the allocated memory
+ * is two bytes longer than required)
+ */
+ bv->bv_len = len - 2;
+ bv->bv_val[ bv->bv_len ] = '\0';
+#ifdef DC_IN_UFN
+ } else {
+ last_iRDN = iRDN - 1;
+
+ for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
+ ber_len_t vl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2UFNstr( rdn, &bv->bv_val[ l ],
+ flags, &vl ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += vl;
+ }
+
+ if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+
+ /* the string is correctly terminated by dn2domain */
+ }
+#endif /* DC_IN_UFN */
+
+ rc = LDAP_SUCCESS;
+
+ } break;
+
+ case LDAP_DN_FORMAT_DCE:
+ for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2DCEstrlen( rdn, flags, &rdnl ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for ( l = 0; iRDN--; ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2DCEstr( rdn, &bv->bv_val[ l ], flags,
+ &rdnl, 0 ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ assert( l == len );
+
+ bv->bv_len = len;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL: {
+ /*
+ * Sort of UFN for DCE DNs: a slash ('/') separated
+ * global->local DN with no types; strictly speaking,
+ * the naming context should be a domain, which is
+ * written in DNS-style, e.g. dot-deparated.
+ *
+ * Example:
+ *
+ * "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
+ *
+ * will read
+ *
+ * "microsoft.com/People/Bill,Gates"
+ */
+ for ( iRDN = 0, len = -1; dn[ 0 ][ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2ADstrlen( rdn, flags, &rdnl ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ iRDN--;
+ if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) ) {
+ for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2ADstr( rdn, &bv->bv_val[ l ],
+ flags, &rdnl, 0 ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ } else {
+ int first = 1;
+
+ /*
+ * Strictly speaking, AD canonical requires
+ * a DN to be in the form "..., dc=smtg",
+ * i.e. terminated by a domain component
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ rc = LDAP_ENCODING_ERROR;
+ break;
+ }
+
+ for ( l = 0; iRDN >= 0 ; iRDN-- ) {
+ ber_len_t rdnl;
+ LDAPRDN *rdn = dn[ 0 ][ iRDN ];
+
+ if ( rdn2ADstr( rdn, &bv->bv_val[ l ],
+ flags, &rdnl, first ) ) {
+ LDAP_FREE( bv->bv_val );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ if ( first ) {
+ first = 0;
+ }
+ l += rdnl;
+ }
+ }
+
+ bv->bv_len = len;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ } break;
+
+ default:
+ return LDAP_PARAM_ERROR;
+ }
- strcpy( p, s );
+ Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2bv(%s,%u)=%d\n",
+ bv->bv_val, flags, rc );
- return( p );
+return_results:;
+ return( rc );
}
-#endif /* ultrix */
#include <lber.h>
-#define malloc(x) ber_memalloc(x)
-#define realloc(x,y) ber_memrealloc(x,y)
-#define free(x) ber_memfree(x)
-
#include <ldap_utf8.h>
#include <ldap_pvt_uc.h>
+#define malloc(x) ber_memalloc(x)
+#define realloc(x,y) ber_memrealloc(x,y)
+#define free(x) ber_memfree(x)
int ucstrncmp(
const ldap_unicode_t *u1,
/* acl.c - routines to parse and check acl's */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#ifdef sunos5
-#include "regexpr.h"
-#else
-#include "regex.h"
-#endif
+
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
#include "slap.h"
+#include "sets.h"
+#include "lber_pvt.h"
+
-extern Attribute *attr_find();
-extern char *re_comp();
-extern struct acl *global_acl;
-extern int global_default_access;
-extern char *access2str();
-extern char *dn_normalize_case();
+/*
+ * speed up compares
+ */
+static struct berval
+ aci_bv_entry = { sizeof("entry") - 1, "entry" },
+ aci_bv_br_entry = { sizeof("[entry]") - 1, "[entry]" },
+ aci_bv_br_all = { sizeof("[all]") - 1, "[all]" },
+ aci_bv_access_id = { sizeof("access-id") - 1, "access-id" },
+ aci_bv_anonymous = { sizeof("anonymous") - 1, "anonymous" },
+ aci_bv_users = { sizeof("users") - 1, "users" },
+ aci_bv_self = { sizeof("self") - 1, "self" },
+ aci_bv_dnattr = { sizeof("dnattr") - 1, "dnattr" },
+ aci_bv_group = { sizeof("group") - 1, "group" },
+ aci_bv_role = { sizeof("role") - 1, "role" },
+ aci_bv_set = { sizeof("set") - 1, "set" },
+ aci_bv_set_ref = { sizeof("set-ref") - 1, "set-ref"},
+ aci_bv_grant = { sizeof("grant") - 1, "grant" },
+ aci_bv_deny = { sizeof("deny") - 1, "deny" };
+
+static AccessControl * acl_get(
+ AccessControl *ac, int *count,
+ Backend *be, Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ int nmatches, regmatch_t *matches );
+
+static slap_control_t acl_mask(
+ AccessControl *ac, slap_mask_t *mask,
+ Backend *be, Connection *conn, Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ regmatch_t *matches,
+ int count,
+ AccessControlState *state );
-int acl_access_allowed();
-int access_allowed();
-struct acl *acl_get_applicable();
+#ifdef SLAPD_ACI_ENABLED
+static int aci_mask(
+ Backend *be,
+ Connection *conn,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *aci,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny );
+#endif
+
+static int regex_matches(
+ struct berval *pat, char *str, char *buf, regmatch_t *matches);
+static void string_expand(
+ struct berval *newbuf, struct berval *pattern,
+ char *match, regmatch_t *matches);
-static int regex_matches();
+typedef struct AciSetCookie {
+ Backend *be;
+ Entry *e;
+ Connection *conn;
+ Operation *op;
+} AciSetCookie;
-extern pthread_mutex_t regex_mutex;
+SLAP_SET_GATHER aci_set_gather;
+static int aci_match_set ( struct berval *subj, Backend *be,
+ Entry *e, Connection *conn, Operation *op, int setref );
/*
- * access_allowed - check whether dn is allowed the requested access
+ * access_allowed - check whether op->o_ndn is allowed the requested access
* to entry e, attribute attr, value val. if val is null, access to
- * the whole attribute is assumed (all values). this routine finds
- * the applicable acl and calls acl_access_allowed() to make the
- * decision.
+ * the whole attribute is assumed (all values).
*
- * returns 0 access NOT allowed
- * 1 access allowed
+ * This routine loops through all access controls and calls
+ * acl_mask() on each applicable access control.
+ * The loop exits when a definitive answer is reached or
+ * or no more controls remain.
+ *
+ * returns:
+ * 0 access denied
+ * 1 access granted
*/
int
Connection *conn,
Operation *op,
Entry *e,
- char *attr,
+ AttributeDescription *desc,
struct berval *val,
- char *dn,
- int access
-)
+ slap_access_t access,
+ AccessControlState *state )
{
- int rc;
- struct acl *a;
+ int ret = 1;
+ int count;
+ AccessControl *a;
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif
+ slap_mask_t mask;
+ slap_control_t control;
+ const char *attr;
+ regmatch_t matches[MAXREMATCHES];
- if ( be == NULL ) {
- return( 0 );
+ assert( e != NULL );
+ assert( desc != NULL );
+ assert( access > ACL_NONE );
+
+ attr = desc->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ if( state && state->as_recorded ) {
+ if( state->as_recorded & ACL_STATE_RECORDED_NV &&
+ val == NULL )
+ {
+ return state->as_result;
+
+ } else if ( state->as_recorded & ACL_STATE_RECORDED_VD &&
+ val != NULL && state->as_vd_acl == NULL )
+ {
+ return state->as_result;
+ }
+ }
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+ "access_allowed: conn %d %s access to \"%s\" \"%s\" requested\n",
+ conn ? conn->c_connid : -1, access2str( access ), e->e_dn, attr ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
+ access2str( access ), e->e_dn, attr );
+#endif
+
+ if ( op == NULL ) {
+ /* no-op call */
+ goto done;
+ }
+
+ if ( be == NULL ) be = &backends[0];
+ assert( be != NULL );
+
+ /* grant database root access */
+ if ( be != NULL && be_isroot( be, &op->o_ndn ) ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_INFO,
+ "access_allowed: conn %d root access granted\n",
+ conn->c_connid));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= root access granted\n",
+ 0, 0, 0 );
+#endif
+ goto done;
+ }
+
+ /*
+ * no-user-modification operational attributes are ignored
+ * by ACL_WRITE checking as any found here are not provided
+ * by the user
+ */
+ if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type )
+ && desc != slap_schema.si_ad_entry
+ && desc != slap_schema.si_ad_children )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "access_allowed: conn %d NoUserMod Operational attribute: %s access granted\n",
+ conn->c_connid, attr ));
+#else
+ Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
+ " %s access granted\n",
+ attr, 0, 0 );
+#endif
+ goto done;
+ }
+
+ /* use backend default access if no backend acls */
+ if( be != NULL && be->be_acl == NULL ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "access_allowed: conn %d backend default %s access %s to \"%s\"\n",
+ conn->c_connid, access2str( access ),
+ be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: backend default %s access %s to \"%s\"\n",
+ access2str( access ),
+ be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn.bv_val );
+#endif
+ ret = be->be_dfltaccess >= access;
+ goto done;
+
+#ifdef notdef
+ /* be is always non-NULL */
+ /* use global default access if no global acls */
+ } else if ( be == NULL && global_acl == NULL ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "access_allowed: conn %d global default %s access %s to \"%s\"\n",
+ conn->c_connid, access2str( access ),
+ global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: global default %s access %s to \"%s\"\n",
+ access2str( access ),
+ global_default_access >= access ? "granted" : "denied", op->o_dn.bv_val );
+#endif
+ ret = global_default_access >= access;
+ goto done;
+#endif
+ }
+
+ ret = 0;
+ control = ACL_BREAK;
+
+ if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )) {
+ assert( state->as_vd_acl != NULL );
+
+ a = state->as_vd_acl;
+ mask = state->as_vd_acl_mask;
+ count = state->as_vd_acl_count;
+ AC_MEMCPY( matches, state->as_vd_acl_matches,
+ sizeof(matches) );
+ goto vd_access;
+
+ } else {
+ a = NULL;
+ ACL_INIT(mask);
+ count = 0;
+ memset(matches, '\0', sizeof(matches));
+ }
+
+ while((a = acl_get( a, &count, be, op, e, desc,
+ MAXREMATCHES, matches )) != NULL)
+ {
+ int i;
+
+ for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "access_allowed: conn %d match[%d]: %d %d ",
+ conn->c_connid, i,
+ (int)matches[i].rm_so, (int)matches[i].rm_eo ));
+#else
+ Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
+ (int)matches[i].rm_so, (int)matches[i].rm_eo );
+#endif
+ if( matches[i].rm_so <= matches[0].rm_eo ) {
+ int n;
+ for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
+ Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
+ }
+ }
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_ARGS, "\n" ));
+#else
+ Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
+#endif
+ }
+
+vd_access:
+ control = acl_mask( a, &mask, be, conn, op,
+ e, desc, val, matches, count, state );
+
+ if ( control != ACL_BREAK ) {
+ break;
+ }
+
+ memset(matches, '\0', sizeof(matches));
+ }
+
+ if ( ACL_IS_INVALID( mask ) ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "access_allowed: conn %d \"%s\" (%s) invalid!\n",
+ conn->c_connid, e->e_dn, attr ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: \"%s\" (%s) invalid!\n",
+ e->e_dn, attr, 0 );
+#endif
+ ACL_INIT(mask);
+
+ } else if ( control == ACL_BREAK ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "access_allowed: conn %d no more rules\n", conn->c_connid ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: no more rules\n", 0, 0, 0);
+#endif
+
+ goto done;
}
- a = acl_get_applicable( be, op, e, attr );
- rc = acl_access_allowed( a, be, conn, e, val, op, access );
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+ "access_allowed: conn %d %s access %s by %s\n",
+ conn->c_connid,
+ access2str( access ),
+ ACL_GRANT( mask, access ) ? "granted" : "denied",
+ accessmask2str( mask, accessmaskbuf ) ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: %s access %s by %s\n",
+ access2str( access ),
+ ACL_GRANT(mask, access) ? "granted" : "denied",
+ accessmask2str( mask, accessmaskbuf ) );
+#endif
- return( rc );
+ ret = ACL_GRANT(mask, access);
+
+done:
+ if( state != NULL ) {
+ state->as_recorded |= ACL_STATE_RECORDED;
+ state->as_result = ret;
+ }
+ return ret;
}
/*
- * acl_get_applicable - return the acl applicable to entry e, attribute
+ * acl_get - return the acl applicable to entry e, attribute
* attr. the acl returned is suitable for use in subsequent calls to
* acl_access_allowed().
*/
-struct acl *
-acl_get_applicable(
+static AccessControl *
+acl_get(
+ AccessControl *a,
+ int *count,
Backend *be,
- Operation *op,
+ Operation *op,
Entry *e,
- char *attr
-)
+ AttributeDescription *desc,
+ int nmatch,
+ regmatch_t *matches )
{
- int i;
- struct acl *a;
- char *edn;
+ const char *attr;
+ int dnlen, patlen;
- Debug( LDAP_DEBUG_ACL, "=> acl_get: entry (%s) attr (%s)\n", e->e_dn,
- attr, 0 );
+ assert( e != NULL );
+ assert( count != NULL );
+ assert( desc != NULL );
- if ( be_isroot( be, op->o_dn ) ) {
- Debug( LDAP_DEBUG_ACL,
- "<= acl_get: no acl applicable to database root\n", 0, 0,
- 0 );
- return( NULL );
- }
+ attr = desc->ad_cname.bv_val;
- /* check for a backend-specific acl that matches the entry */
- for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) {
- if ( a->acl_dnpat != NULL ) {
- edn = dn_normalize_case( strdup( e->e_dn ) );
- if ( ! regex_matches( a->acl_dnpat, edn ) ) {
- free( edn );
- continue;
- }
- free( edn );
- }
- if ( a->acl_filter != NULL ) {
- if ( test_filter( NULL, NULL, NULL, e, a->acl_filter )
- != 0 ) {
- continue;
- }
- }
- if ( attr == NULL || a->acl_attrs == NULL ||
- charray_inlist( a->acl_attrs, attr ) ) {
- Debug( LDAP_DEBUG_ACL, "<= acl_get: backend acl #%d\n",
- i, e->e_dn, attr );
- return( a );
+ assert( attr != NULL );
+
+ if( a == NULL ) {
+ if( be == NULL ) {
+ a = global_acl;
+ } else {
+ a = be->be_acl;
}
+
+ assert( a != NULL );
+
+ } else {
+ a = a->acl_next;
}
- /* check for a global acl that matches the entry */
- for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
- if ( a->acl_dnpat != NULL ) {
- edn = dn_normalize_case( strdup( e->e_dn ) );
- if ( ! regex_matches( a->acl_dnpat, edn ) ) {
- free( edn );
- continue;
+ dnlen = e->e_nname.bv_len;
+
+ for ( ; a != NULL; a = a->acl_next ) {
+ (*count) ++;
+
+ if (a->acl_dn_pat.bv_len != 0) {
+ if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_get: dnpat [%d] %s nsub: %d\n",
+ *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub ));
+#else
+ Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n",
+ *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
+#endif
+ if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0))
+ continue;
+
+ } else {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_get: dn [%d] %s\n",
+ *count, a->acl_dn_pat.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n",
+ *count, a->acl_dn_pat.bv_val, 0 );
+#endif
+ patlen = a->acl_dn_pat.bv_len;
+ if ( dnlen < patlen )
+ continue;
+
+ if ( a->acl_dn_style == ACL_STYLE_BASE ) {
+ /* base dn -- entire object DN must match */
+ if ( dnlen != patlen )
+ continue;
+
+ } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
+ int rdnlen = -1;
+
+ if ( dnlen <= patlen )
+ continue;
+
+ if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+ continue;
+
+ rdnlen = dn_rdnlen( NULL, &e->e_nname );
+ if ( rdnlen != dnlen - patlen - 1 )
+ continue;
+
+ } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
+ if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+ continue;
+
+ } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
+ if ( dnlen <= patlen )
+ continue;
+ if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+ continue;
+ }
+
+ if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
+ continue;
}
- free( edn );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_get: [%d] matched\n",
+ *count ));
+#else
+ Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
+ *count, 0, 0 );
+#endif
}
+
if ( a->acl_filter != NULL ) {
- if ( test_filter( NULL, NULL, NULL, e, a->acl_filter )
- != 0 ) {
+ ber_int_t rc = test_filter( NULL, NULL, NULL, e, a->acl_filter );
+ if ( rc != LDAP_COMPARE_TRUE ) {
continue;
}
}
- if ( attr == NULL || a->acl_attrs == NULL || charray_inlist(
- a->acl_attrs, attr ) ) {
- Debug( LDAP_DEBUG_ACL, "<= acl_get: global acl #%d\n",
- i, e->e_dn, attr );
- return( a );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_get: [%d] check attr %s\n",
+ *count, attr ));
+#else
+ Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n",
+ *count, attr, 0);
+#endif
+ if ( attr == NULL || a->acl_attrs == NULL ||
+ ad_inlist( desc, a->acl_attrs ) )
+ {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_get: [%d] acl %s attr: %s\n",
+ *count, e->e_dn, attr ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_get: [%d] acl %s attr: %s\n",
+ *count, e->e_dn, attr );
+#endif
+ return a;
}
+ matches[0].rm_so = matches[0].rm_eo = -1;
}
- Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+ "acl_get: done.\n" ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
+#endif
return( NULL );
}
/*
- * acl_access_allowed - check whether the given acl allows dn the
+ * Record value-dependent access control state
+ */
+#define ACL_RECORD_VALUE_STATE do { \
+ if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { \
+ state->as_recorded |= ACL_STATE_RECORDED_VD; \
+ state->as_vd_acl = a; \
+ AC_MEMCPY( state->as_vd_acl_matches, matches, \
+ sizeof( state->as_vd_acl_matches )) ; \
+ state->as_vd_acl_count = count; \
+ state->as_vd_access = b; \
+ state->as_vd_access_count = i; \
+ } \
+ } while( 0 )
+
+/*
+ * acl_mask - modifies mask based upon the given acl and the
* requested access to entry e, attribute attr, value val. if val
* is null, access to the whole attribute is assumed (all values).
*
* 1 access allowed
*/
-int
-acl_access_allowed(
- struct acl *a,
+static slap_control_t
+acl_mask(
+ AccessControl *a,
+ slap_mask_t *mask,
Backend *be,
- Connection *conn,
+ Connection *conn,
+ Operation *op,
Entry *e,
+ AttributeDescription *desc,
struct berval *val,
- Operation *op,
- int access
-)
+ regmatch_t *matches,
+ int count,
+ AccessControlState *state )
{
- int i;
- char *edn, *odn;
- struct access *b;
- Attribute *at;
- struct berval bv;
- int default_access;
-
- Debug( LDAP_DEBUG_ACL, "=> acl: %s access to value \"%s\" by \"%s\"\n",
- access2str( access ), val ? val->bv_val : "any", op->o_dn ?
- op->o_dn : "" );
-
- if ( be_isroot( be, op->o_dn ) ) {
- Debug( LDAP_DEBUG_ACL, "<= acl: granted to database root\n",
- 0, 0, 0 );
- return( 1 );
- }
+ int i, odnlen, patlen;
+ int vd_recorded = 0;
+ Access *b;
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif
+ const char *attr;
- default_access = be->be_dfltaccess ? be->be_dfltaccess :
- global_default_access;
- if ( a == NULL ) {
- Debug( LDAP_DEBUG_ACL,
- "<= acl: %s by default (no matching to)\n",
- default_access >= access ? "granted" : "denied", 0, 0 );
- return( default_access >= access );
- }
+ assert( a != NULL );
+ assert( mask != NULL );
+ assert( desc != NULL );
+
+ attr = desc->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_ENTRY,
+ "acl_mask: conn %d access to entry \"%s\", attr \"%s\" requested\n",
+ conn->c_connid, e->e_dn, attr ));
+
+ LDAP_LOG(( "acl", LDAP_LEVEL_ARGS,
+ " to %s by \"%s\", (%s) \n",
+ val ? "value" : "all values",
+ op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
+ accessmask2str( *mask, accessmaskbuf ) ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
+ e->e_dn, attr, 0 );
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> acl_mask: to %s by \"%s\", (%s) \n",
+ val ? "value" : "all values",
+ op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
+ accessmask2str( *mask, accessmaskbuf ) );
+#endif
+
+ if( state && ( state->as_recorded & ACL_STATE_RECORDED_VD )
+ && state->as_vd_acl == a )
+ {
+ b = state->as_vd_access;
+ i = state->as_vd_access_count;
- odn = NULL;
- if ( op->o_dn != NULL ) {
- odn = dn_normalize_case( strdup( op->o_dn ) );
- bv.bv_val = odn;
- bv.bv_len = strlen( odn );
+ } else {
+ b = a->acl_access;
+ i = 1;
}
- for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
- if ( b->a_dnpat != NULL ) {
+
+ for ( ; b != NULL; b = b->a_next, i++ ) {
+ slap_mask_t oldmask, modmask;
+
+ ACL_INVALIDATE( modmask );
+
+ /* AND <who> clauses */
+ if ( b->a_dn_pat.bv_len != 0 ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_dn_pat: %s\n",
+ conn->c_connid, b->a_dn_pat.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
+ b->a_dn_pat.bv_val, 0, 0);
+#endif
/*
* if access applies to the entry itself, and the
* user is bound as somebody in the same namespace as
* the entry, OR the given dn matches the dn pattern
*/
- if ( strcasecmp( b->a_dnpat, "self" ) == 0 && op->o_dn
- != NULL && *(op->o_dn) && e->e_dn != NULL ) {
- edn = dn_normalize_case( strdup( e->e_dn ) );
- if ( strcasecmp( edn, op->o_dn ) == 0 ) {
- free( edn );
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL,
- "<= acl: matched by clause #%d access %s\n",
- i, (b->a_access & ~ACL_SELF) >=
- access ? "granted" : "denied", 0 );
-
- return( (b->a_access & ~ACL_SELF)
- >= access );
- }
- free( edn );
- } else {
- if ( regex_matches( b->a_dnpat, odn ) ) {
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL,
- "<= acl: matched by clause #%d access %s\n",
- i, (b->a_access & ~ACL_SELF) >= access ?
- "granted" : "denied", 0 );
-
- return( (b->a_access & ~ACL_SELF)
- >= access );
+ if ( ber_bvcmp( &b->a_dn_pat, &aci_bv_anonymous ) == 0 ) {
+ if ( op->o_ndn.bv_len != 0 ) {
+ continue;
}
- }
- }
- if ( b->a_addrpat != NULL ) {
- if ( regex_matches( b->a_addrpat, conn->c_addr ) ) {
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL,
- "<= acl: matched by clause #%d access %s\n",
- i, (b->a_access & ~ACL_SELF) >= access ?
- "granted" : "denied", 0 );
- return( (b->a_access & ~ACL_SELF) >= access );
- }
- }
- if ( b->a_domainpat != NULL ) {
- if ( regex_matches( b->a_domainpat, conn->c_domain ) ) {
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL,
- "<= acl: matched by clause #%d access %s\n",
- i, (b->a_access & ~ACL_SELF) >= access ?
- "granted" : "denied", 0 );
+ } else if ( ber_bvcmp( &b->a_dn_pat, &aci_bv_users ) == 0 ) {
+ if ( op->o_ndn.bv_len == 0 ) {
+ continue;
+ }
- return( (b->a_access & ~ACL_SELF) >= access );
- }
- }
- if ( b->a_dnattr != NULL && op->o_dn != NULL ) {
- /* see if asker is listed in dnattr */
- if ( (at = attr_find( e->e_attrs, b->a_dnattr ))
- != NULL && value_find( at->a_vals, &bv,
- at->a_syntax, 3 ) == 0 )
- {
- if ( (b->a_access & ACL_SELF) && (val == NULL
- || value_cmp( &bv, val, at->a_syntax,
- 2 )) ) {
+ } else if ( ber_bvcmp( &b->a_dn_pat, &aci_bv_self ) == 0 ) {
+ if ( op->o_ndn.bv_len == 0 ) {
+ continue;
+ }
+
+ if ( e->e_dn == NULL || !dn_match( &e->e_nname, &op->o_ndn ) ) {
continue;
}
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL,
- "<= acl: matched by clause #%d access %s\n",
- i, (b->a_access & ~ACL_SELF) >= access ?
- "granted" : "denied", 0 );
+ } else if ( b->a_dn_style == ACL_STYLE_REGEX ) {
+ if ( ber_bvccmp( &b->a_dn_pat, '*' ) == 0 ) {
+ int ret = regex_matches( &b->a_dn_pat,
+ op->o_ndn.bv_val, e->e_ndn, matches );
- return( (b->a_access & ~ACL_SELF) >= access );
- }
+ if( ret == 0 ) {
+ continue;
+ }
+ }
- /* asker not listed in dnattr - check for self access */
- if ( ! (b->a_access & ACL_SELF) || val == NULL ||
- value_cmp( &bv, val, at->a_syntax, 2 ) != 0 ) {
- continue;
- }
+ } else {
+ if ( e->e_dn == NULL )
+ continue;
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL,
- "<= acl: matched by clause #%d (self) access %s\n",
- i, (b->a_access & ~ACL_SELF) >= access ? "granted"
- : "denied", 0 );
+ patlen = b->a_dn_pat.bv_len;
+ odnlen = op->o_ndn.bv_len;
+ if ( odnlen < patlen )
+ continue;
- return( (b->a_access & ~ACL_SELF) >= access );
- }
- }
+ if ( b->a_dn_style == ACL_STYLE_BASE ) {
+ /* base dn -- entire object DN must match */
+ if ( odnlen != patlen )
+ continue;
- if ( odn ) free( odn );
- Debug( LDAP_DEBUG_ACL, "<= acl: %s by default (no matching by)\n",
- default_access >= access ? "granted" : "denied", 0, 0 );
+ } else if ( b->a_dn_style == ACL_STYLE_ONE ) {
+ int rdnlen = -1;
- return( default_access >= access );
-}
+ if ( odnlen <= patlen )
+ continue;
-/*
- * acl_check_mods - check access control on the given entry to see if
- * it allows the given modifications by the user associated with op.
- * returns LDAP_SUCCESS mods allowed ok
- * anything else mods not allowed - return is an error
- * code indicating the problem
- */
+ if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+ continue;
-int
-acl_check_mods(
- Backend *be,
- Connection *conn,
- Operation *op,
- Entry *e,
- LDAPMod *mods
-)
-{
- int i;
- struct acl *a;
+ rdnlen = dn_rdnlen( NULL, &op->o_ndn );
+ if ( rdnlen != odnlen - patlen - 1 )
+ continue;
- for ( ; mods != NULL; mods = mods->mod_next ) {
- if ( strcasecmp( mods->mod_type, "modifiersname" ) == 0 ||
- strcasecmp( mods->mod_type, "modifytimestamp" ) == 0 ) {
- continue;
+ } else if ( b->a_dn_style == ACL_STYLE_SUBTREE ) {
+ if ( odnlen > patlen && !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+ continue;
+
+ } else if ( b->a_dn_style == ACL_STYLE_CHILDREN ) {
+ if ( odnlen <= patlen )
+ continue;
+ if ( !DN_SEPARATOR( op->o_ndn.bv_val[odnlen - patlen - 1] ) )
+ continue;
+ }
+
+ if ( strcmp( b->a_dn_pat.bv_val, op->o_ndn.bv_val + odnlen - patlen ) != 0 )
+ continue;
+ }
}
- a = acl_get_applicable( be, op, e, mods->mod_type );
+ if ( b->a_sockurl_pat.bv_len ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_sockurl_pat: %s\n",
+ conn->c_connid, b->a_sockurl_pat.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
+ b->a_sockurl_pat.bv_val, 0, 0 );
+#endif
- switch ( mods->mod_op & ~LDAP_MOD_BVALUES ) {
- case LDAP_MOD_REPLACE:
- case LDAP_MOD_ADD:
- if ( mods->mod_bvalues == NULL ) {
- break;
+ if ( ber_bvccmp( &b->a_sockurl_pat, '*' ) != 0) {
+ if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
+ if (!regex_matches( &b->a_sockurl_pat, conn->c_listener_url.bv_val,
+ e->e_ndn, matches ) )
+ {
+ continue;
+ }
+ } else {
+ if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &conn->c_listener_url ) != 0 )
+ continue;
+ }
}
- for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
- if ( ! acl_access_allowed( a, be, conn, e,
- mods->mod_bvalues[i], op, ACL_WRITE ) ) {
- return( LDAP_INSUFFICIENT_ACCESS );
+ }
+
+ if ( b->a_domain_pat.bv_len ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_domain_pat: %s\n",
+ conn->c_connid, b->a_domain_pat.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
+ b->a_domain_pat.bv_val, 0, 0 );
+#endif
+ if ( ber_bvccmp( &b->a_domain_pat, '*' ) != 0) {
+ if ( b->a_domain_style == ACL_STYLE_REGEX) {
+ if (!regex_matches( &b->a_domain_pat, conn->c_peer_domain.bv_val,
+ e->e_ndn, matches ) )
+ {
+ continue;
+ }
+ } else {
+ if ( ber_bvstrcasecmp( &b->a_domain_pat, &conn->c_peer_domain ) != 0 )
+ continue;
}
}
- break;
+ }
- case LDAP_MOD_DELETE:
- if ( mods->mod_bvalues == NULL ) {
- if ( ! acl_access_allowed( a, be, conn, e,
- NULL, op, ACL_WRITE ) ) {
- return( LDAP_INSUFFICIENT_ACCESS );
+ if ( b->a_peername_pat.bv_len ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_perrname_path: %s\n",
+ conn->c_connid, b->a_peername_pat.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
+ b->a_peername_pat.bv_val, 0, 0 );
+#endif
+ if ( ber_bvccmp( &b->a_peername_pat, '*' ) != 0) {
+ if ( b->a_peername_style == ACL_STYLE_REGEX) {
+ if (!regex_matches( &b->a_peername_pat, conn->c_peer_name.bv_val,
+ e->e_ndn, matches ) )
+ {
+ continue;
+ }
+ } else {
+ if ( ber_bvstrcasecmp( &b->a_peername_pat, &conn->c_peer_name ) != 0 )
+ continue;
}
- break;
}
- for ( i = 0; mods->mod_bvalues[i] != NULL; i++ ) {
- if ( ! acl_access_allowed( a, be, conn, e,
- mods->mod_bvalues[i], op, ACL_WRITE ) ) {
- return( LDAP_INSUFFICIENT_ACCESS );
+ }
+
+ if ( b->a_sockname_pat.bv_len ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_sockname_path: %s\n",
+ conn->c_connid, b->a_sockname_pat.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
+ b->a_sockname_pat.bv_val, 0, 0 );
+#endif
+ if ( ber_bvccmp( &b->a_sockname_pat, '*' ) != 0) {
+ if ( b->a_sockname_style == ACL_STYLE_REGEX) {
+ if (!regex_matches( &b->a_sockname_pat, conn->c_sock_name.bv_val,
+ e->e_ndn, matches ) )
+ {
+ continue;
+ }
+ } else {
+ if ( ber_bvstrcasecmp( &b->a_sockname_pat, &conn->c_sock_name ) != 0 )
+ continue;
}
}
- break;
}
- }
- return( LDAP_SUCCESS );
-}
+ if ( b->a_dn_at != NULL && op->o_ndn.bv_len != 0 ) {
+ Attribute *at;
+ struct berval bv;
+ int rc, match = 0;
+ const char *text;
+ const char *attr = b->a_dn_at->ad_cname.bv_val;
-#ifdef sunos5
+ assert( attr != NULL );
-static int
-regex_matches( char *pat, char *str )
-{
- char *e;
- int rc;
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_dn_pat: %s\n",
+ conn->c_connid, attr ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
+ attr, 0, 0);
+#endif
+ bv = op->o_ndn;
- if ( (e = compile( pat, NULL, NULL )) == NULL ) {
- Debug( LDAP_DEBUG_ANY,
- "compile( \"%s\", \"%s\") failed\n", pat, str, 0 );
- return( 0 );
- }
- rc = step( str ? str : "", e );
- free( e );
+ /* see if asker is listed in dnattr */
+ for( at = attrs_find( e->e_attrs, b->a_dn_at );
+ at != NULL;
+ at = attrs_find( at->a_next, b->a_dn_at ) )
+ {
+ if( value_find( b->a_dn_at, at->a_vals, &bv ) == 0 ) {
+ /* found it */
+ match = 1;
+ break;
+ }
+ }
- return( rc );
-}
+ if( match ) {
+ /* have a dnattr match. if this is a self clause then
+ * the target must also match the op dn.
+ */
+ if ( b->a_dn_self ) {
+ /* check if the target is an attribute. */
+ if ( val == NULL )
+ continue;
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, b->a_dn_at,
+ b->a_dn_at->ad_type->sat_equality, 0,
+ val, &bv, &text );
+ /* on match error or no match, fail the ACL clause */
+ if (rc != LDAP_SUCCESS || match != 0 )
+ continue;
+ }
+ } else {
+ /* no dnattr match, check if this is a self clause */
+ if ( ! b->a_dn_self )
+ continue;
-#else /* sunos5 */
+ ACL_RECORD_VALUE_STATE;
+
+ /* this is a self clause, check if the target is an
+ * attribute.
+ */
+ if ( val == NULL )
+ continue;
-static int
-regex_matches( char *pat, char *str )
-{
- char *e;
- int rc;
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, b->a_dn_at,
+ b->a_dn_at->ad_type->sat_equality, 0,
+ val, &bv, &text );
- pthread_mutex_lock( ®ex_mutex );
- if ( (e = re_comp( pat )) != NULL ) {
- Debug( LDAP_DEBUG_ANY,
- "re_comp( \"%s\", \"%s\") failed because (%s)\n", pat, str,
- e );
- pthread_mutex_unlock( ®ex_mutex );
- return( 0 );
- }
- rc = re_exec( str ? str : "" );
- pthread_mutex_unlock( ®ex_mutex );
+ /* on match error or no match, fail the ACL clause */
+ if (rc != LDAP_SUCCESS || match != 0 )
+ continue;
+ }
+ }
+
+ if ( b->a_group_pat.bv_len && op->o_ndn.bv_len ) {
+ char buf[1024];
+ struct berval bv;
+ struct berval ndn = { 0, NULL };
+ int rc;
+
+ bv.bv_len = sizeof(buf) - 1;
+ bv.bv_val = buf;
- return( rc == 1 );
+ /* b->a_group is an unexpanded entry name, expanded it should be an
+ * entry with objectclass group* and we test to see if odn is one of
+ * the values in the attribute group
+ */
+ /* see if asker is listed in dnattr */
+ if ( b->a_group_style == ACL_STYLE_REGEX ) {
+ string_expand(&bv, &b->a_group_pat, e->e_ndn, matches);
+ if ( dnNormalize2(NULL, &bv, &ndn) != LDAP_SUCCESS ) {
+ /* did not expand to a valid dn */
+ continue;
+ }
+ bv = ndn;
+ } else {
+ bv = b->a_group_pat;
+ }
+
+ rc = backend_group(be, conn, op, e, &bv, &op->o_ndn,
+ b->a_group_oc, b->a_group_at);
+ if ( ndn.bv_val )
+ free( ndn.bv_val );
+ if ( rc != 0 ) {
+ continue;
+ }
+ }
+
+ if ( b->a_set_pat.bv_len != 0 ) {
+ if (aci_match_set( &b->a_set_pat, be, e, conn, op, 0 ) == 0) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_ssf ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_authz.sai_ssf: ACL %u > OP %u\n",
+ conn->c_connid, b->a_authz.sai_ssf, op->o_ssf ));
+#else
+ Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_ssf, op->o_ssf, 0 );
+#endif
+ if ( b->a_authz.sai_ssf > op->o_ssf ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_transport_ssf ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
+ conn->c_connid, b->a_authz.sai_transport_ssf, op->o_transport_ssf ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
+#endif
+ if ( b->a_authz.sai_transport_ssf > op->o_transport_ssf ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_tls_ssf ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
+ conn->c_connid, b->a_authz.sai_tls_ssf, op->o_tls_ssf ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
+#endif
+ if ( b->a_authz.sai_tls_ssf > op->o_tls_ssf ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_sasl_ssf ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
+ conn->c_connid, b->a_authz.sai_sasl_ssf, op->o_sasl_ssf ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
+#endif
+ if ( b->a_authz.sai_sasl_ssf > op->o_sasl_ssf ) {
+ continue;
+ }
+ }
+
+#ifdef SLAPD_ACI_ENABLED
+ if ( b->a_aci_at != NULL ) {
+ Attribute *at;
+ slap_access_t grant, deny, tgrant, tdeny;
+
+ /* this case works different from the others above.
+ * since aci's themselves give permissions, we need
+ * to first check b->a_access_mask, the ACL's access level.
+ */
+
+ if ( e->e_nname.bv_len == 0 ) {
+ /* no ACIs in the root DSE */
+ continue;
+ }
+
+ /* first check if the right being requested
+ * is allowed by the ACL clause.
+ */
+ if ( ! ACL_GRANT( b->a_access_mask, *mask ) ) {
+ continue;
+ }
+
+ /* get the aci attribute */
+ at = attr_find( e->e_attrs, b->a_aci_at );
+ if ( at == NULL ) {
+ continue;
+ }
+
+ ACL_RECORD_VALUE_STATE;
+
+ /* start out with nothing granted, nothing denied */
+ ACL_INIT(tgrant);
+ ACL_INIT(tdeny);
+
+ /* the aci is an multi-valued attribute. The
+ * rights are determined by OR'ing the individual
+ * rights given by the acis.
+ */
+ for ( i = 0; at->a_vals[i].bv_val != NULL; i++ ) {
+ if (aci_mask( be, conn, op,
+ e, desc, val, &at->a_vals[i],
+ matches, &grant, &deny ) != 0)
+ {
+ tgrant |= grant;
+ tdeny |= deny;
+ }
+ }
+
+ /* remove anything that the ACL clause does not allow */
+ tgrant &= b->a_access_mask & ACL_PRIV_MASK;
+ tdeny &= ACL_PRIV_MASK;
+
+ /* see if we have anything to contribute */
+ if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {
+ continue;
+ }
+
+ /* this could be improved by changing acl_mask so that it can deal with
+ * by clauses that return grant/deny pairs. Right now, it does either
+ * additive or subtractive rights, but not both at the same time. So,
+ * we need to combine the grant/deny pair into a single rights mask in
+ * a smart way: if either grant or deny is "empty", then we use the
+ * opposite as is, otherwise we remove any denied rights from the grant
+ * rights mask and construct an additive mask.
+ */
+ if (ACL_IS_INVALID(tdeny)) {
+ modmask = tgrant | ACL_PRIV_ADDITIVE;
+
+ } else if (ACL_IS_INVALID(tgrant)) {
+ modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
+
+ } else {
+ modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
+ }
+
+ } else
+#endif
+ {
+ modmask = b->a_access_mask;
+ }
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_RESULTS,
+ "acl_mask: conn %d [%d] applying %s (%s)\n",
+ conn->c_connid, i, accessmask2str( modmask, accessmaskbuf),
+ b->a_type == ACL_CONTINUE ? "continue" : b->a_type == ACL_BREAK
+ ? "break" : "stop" ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_mask: [%d] applying %s (%s)\n",
+ i, accessmask2str( modmask, accessmaskbuf ),
+ b->a_type == ACL_CONTINUE
+ ? "continue"
+ : b->a_type == ACL_BREAK
+ ? "break"
+ : "stop" );
+#endif
+ /* save old mask */
+ oldmask = *mask;
+
+ if( ACL_IS_ADDITIVE(modmask) ) {
+ /* add privs */
+ ACL_PRIV_SET( *mask, modmask );
+
+ /* cleanup */
+ ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
+
+ } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
+ /* substract privs */
+ ACL_PRIV_CLR( *mask, modmask );
+
+ /* cleanup */
+ ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
+
+ } else {
+ /* assign privs */
+ *mask = modmask;
+ }
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+ "acl_mask: conn %d [%d] mask: %s\n",
+ conn->c_connid, i, accessmask2str( *mask, accessmaskbuf) ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_mask: [%d] mask: %s\n",
+ i, accessmask2str(*mask, accessmaskbuf), 0 );
+#endif
+
+ if( b->a_type == ACL_CONTINUE ) {
+ continue;
+
+ } else if ( b->a_type == ACL_BREAK ) {
+ return ACL_BREAK;
+
+ } else {
+ return ACL_STOP;
+ }
+ }
+
+ /* implicit "by * none" clause */
+ ACL_INIT(*mask);
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_RESULTS,
+ "acl_mask: conn %d no more <who> clauses, returning %d (stop)\n",
+ conn->c_connid, accessmask2str( *mask, accessmaskbuf) ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
+ accessmask2str(*mask, accessmaskbuf), 0, 0 );
+#endif
+ return ACL_STOP;
+}
+
+/*
+ * acl_check_modlist - check access control on the given entry to see if
+ * it allows the given modifications by the user associated with op.
+ * returns 1 if mods allowed ok
+ * 0 mods not allowed
+ */
+
+int
+acl_check_modlist(
+ Backend *be,
+ Connection *conn,
+ Operation *op,
+ Entry *e,
+ Modifications *mlist
+)
+{
+ struct berval *bv;
+
+ assert( be != NULL );
+
+ /* short circuit root database access */
+ if ( be_isroot( be, &op->o_ndn ) ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "acl", LDAP_LEVEL_DETAIL1,
+ "acl_check_modlist: conn %d access granted to root user\n",
+ conn->c_connid ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_access_allowed: granted to database root\n",
+ 0, 0, 0 );
+#endif
+ return 1;
+ }
+
+ /* use backend default access if no backend acls */
+ if( be != NULL && be->be_acl == NULL ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+ "acl_check_modlist: conn %d backend default %s access %s to \"%s\"\n",
+ conn->c_connid, access2str( ACL_WRITE ),
+ be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: backend default %s access %s to \"%s\"\n",
+ access2str( ACL_WRITE ),
+ be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn.bv_val );
+#endif
+ return be->be_dfltaccess >= ACL_WRITE;
+
+#ifdef notdef
+ /* be is always non-NULL */
+ /* use global default access if no global acls */
+ } else if ( be == NULL && global_acl == NULL ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+ "acl_check_modlist: conn %d global default %s access %s to \"%s\"\n",
+ conn->c_connid, access2str( ACL_WRITE ),
+ global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn ));
+#else
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: global default %s access %s to \"%s\"\n",
+ access2str( ACL_WRITE ),
+ global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn );
+#endif
+ return global_default_access >= ACL_WRITE;
+#endif
+ }
+
+ for ( ; mlist != NULL; mlist = mlist->sml_next ) {
+ static AccessControlState state_init = ACL_STATE_INIT;
+ AccessControlState state;
+
+ /*
+ * no-user-modification operational attributes are ignored
+ * by ACL_WRITE checking as any found here are not provided
+ * by the user
+ */
+ if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+ "acl_check_modlist: conn %d no-user-mod %s: modify access granted\n",
+ conn->c_connid, mlist->sml_desc->ad_cname.bv_val ));
+#else
+ Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
+ " modify access granted\n",
+ mlist->sml_desc->ad_cname.bv_val, 0, 0 );
+#endif
+ continue;
+ }
+
+ state = state_init;
+
+ switch ( mlist->sml_op ) {
+ case LDAP_MOD_REPLACE:
+ /*
+ * We must check both permission to delete the whole
+ * attribute and permission to add the specific attributes.
+ * This prevents abuse from selfwriters.
+ */
+ if ( ! access_allowed( be, conn, op, e,
+ mlist->sml_desc, NULL, ACL_WRITE, &state ) )
+ {
+ return( 0 );
+ }
+
+ if ( mlist->sml_bvalues == NULL ) break;
+
+ /* fall thru to check value to add */
+
+ case LDAP_MOD_ADD:
+ assert( mlist->sml_bvalues != NULL );
+
+ for ( bv = mlist->sml_bvalues; bv->bv_val != NULL; bv++ ) {
+ if ( ! access_allowed( be, conn, op, e,
+ mlist->sml_desc, bv, ACL_WRITE, &state ) )
+ {
+ return( 0 );
+ }
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ if ( mlist->sml_bvalues == NULL ) {
+ if ( ! access_allowed( be, conn, op, e,
+ mlist->sml_desc, NULL, ACL_WRITE, NULL ) )
+ {
+ return( 0 );
+ }
+ break;
+ }
+ for ( bv = mlist->sml_bvalues; bv->bv_val != NULL; bv++ ) {
+ if ( ! access_allowed( be, conn, op, e,
+ mlist->sml_desc, bv, ACL_WRITE, &state ) )
+ {
+ return( 0 );
+ }
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ /* allow adding attribute via modrdn thru */
+ break;
+
+ default:
+ assert( 0 );
+ return( 0 );
+ }
+ }
+
+ return( 1 );
+}
+
+static char *
+aci_bvstrdup( struct berval *bv )
+{
+ char *s;
+
+ s = (char *)ch_malloc(bv->bv_len + 1);
+ if (s != NULL) {
+ AC_MEMCPY(s, bv->bv_val, bv->bv_len);
+ s[bv->bv_len] = 0;
+ }
+ return(s);
+}
+
+static int
+aci_get_part(
+ struct berval *list,
+ int ix,
+ char sep,
+ struct berval *bv )
+{
+ int len;
+ char *p;
+
+ if (bv) {
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+ }
+ len = list->bv_len;
+ p = list->bv_val;
+ while (len >= 0 && --ix >= 0) {
+ while (--len >= 0 && *p++ != sep) ;
+ }
+ while (len >= 0 && *p == ' ') {
+ len--;
+ p++;
+ }
+ if (len < 0)
+ return(-1);
+
+ if (!bv)
+ return(0);
+
+ bv->bv_val = p;
+ while (--len >= 0 && *p != sep) {
+ bv->bv_len++;
+ p++;
+ }
+ while (bv->bv_len > 0 && *--p == ' ')
+ bv->bv_len--;
+ return(bv->bv_len);
+}
+
+BerVarray
+aci_set_gather (void *cookie, struct berval *name, struct berval *attr)
+{
+ AciSetCookie *cp = cookie;
+ BerVarray bvals = NULL;
+ struct berval ndn;
+
+ /* this routine needs to return the bervals instead of
+ * plain strings, since syntax is not known. It should
+ * also return the syntax or some "comparison cookie".
+ */
+
+ if (dnNormalize2(NULL, name, &ndn) == LDAP_SUCCESS) {
+ const char *text;
+ AttributeDescription *desc = NULL;
+ if (slap_bv2ad(attr, &desc, &text) == LDAP_SUCCESS) {
+ backend_attribute(cp->be, NULL, NULL,
+ cp->e, &ndn, desc, &bvals);
+ }
+ free(ndn.bv_val);
+ }
+ return(bvals);
+}
+
+static int
+aci_match_set (
+ struct berval *subj,
+ Backend *be,
+ Entry *e,
+ Connection *conn,
+ Operation *op,
+ int setref
+)
+{
+ struct berval set = { 0, NULL };
+ int rc = 0;
+ AciSetCookie cookie;
+
+ if (setref == 0) {
+ ber_dupbv( &set, subj );
+ } else {
+ struct berval subjdn, ndn = { 0, NULL };
+ struct berval setat;
+ BerVarray bvals;
+ const char *text;
+ AttributeDescription *desc = NULL;
+
+ /* format of string is "entry/setAttrName" */
+ if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
+ return(0);
+ }
+
+ if ( aci_get_part(subj, 1, '/', &setat) < 0 ) {
+ setat.bv_val = SLAPD_ACI_SET_ATTR;
+ setat.bv_len = sizeof(SLAPD_ACI_SET_ATTR)-1;
+ }
+
+ if ( setat.bv_val != NULL ) {
+ /*
+ * NOTE: dnNormalize2 honors the ber_len field
+ * as the length of the dn to be normalized
+ */
+ if ( dnNormalize2(NULL, &subjdn, &ndn) == LDAP_SUCCESS
+ && slap_bv2ad(&setat, &desc, &text) == LDAP_SUCCESS )
+ {
+ backend_attribute(be, NULL, NULL, e,
+ &ndn, desc, &bvals);
+ if ( bvals != NULL ) {
+ if ( bvals[0].bv_val != NULL ) {
+ int i;
+ set = bvals[0];
+ bvals[0].bv_val = NULL;
+ for (i=1;bvals[i].bv_val;i++);
+ bvals[0].bv_val = bvals[i-1].bv_val;
+ bvals[i-1].bv_val = NULL;
+ }
+ ber_bvarray_free(bvals);
+ }
+ }
+ if (ndn.bv_val)
+ free(ndn.bv_val);
+ }
+ }
+
+ if (set.bv_val != NULL) {
+ cookie.be = be;
+ cookie.e = e;
+ cookie.conn = conn;
+ cookie.op = op;
+ rc = (slap_set_filter(aci_set_gather, &cookie, &set,
+ &op->o_ndn, &e->e_nname, NULL) > 0);
+ ch_free(set.bv_val);
+ }
+ return(rc);
+}
+
+#ifdef SLAPD_ACI_ENABLED
+static int
+aci_list_map_rights(
+ struct berval *list )
+{
+ struct berval bv;
+ slap_access_t mask;
+ int i;
+
+ ACL_INIT(mask);
+ for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
+ if (bv.bv_len <= 0)
+ continue;
+ switch (*bv.bv_val) {
+ case 'c':
+ ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
+ break;
+ case 's':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+ * the right 's' to mean "set", but in the examples states
+ * that the right 's' means "search". The latter definition
+ * is used here.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
+ break;
+ case 'r':
+ ACL_PRIV_SET(mask, ACL_PRIV_READ);
+ break;
+ case 'w':
+ ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
+ break;
+ case 'x':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
+ * define any equivalent to the AUTH right, so I've just used
+ * 'x' for now.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+ break;
+ default:
+ break;
+ }
+
+ }
+ return(mask);
+}
+
+static int
+aci_list_has_attr(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val )
+{
+ struct berval bv, left, right;
+ int i;
+
+ for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
+ if (aci_get_part(&bv, 0, '=', &left) < 0
+ || aci_get_part(&bv, 1, '=', &right) < 0)
+ {
+ if (ber_bvstrcasecmp(attr, &bv) == 0)
+ return(1);
+ } else if (val == NULL) {
+ if (ber_bvstrcasecmp(attr, &left) == 0)
+ return(1);
+ } else {
+ if (ber_bvstrcasecmp(attr, &left) == 0) {
+ /* this is experimental code that implements a
+ * simple (prefix) match of the attribute value.
+ * the ACI draft does not provide for aci's that
+ * apply to specific values, but it would be
+ * nice to have. If the <attr> part of an aci's
+ * rights list is of the form <attr>=<value>,
+ * that means the aci applies only to attrs with
+ * the given value. Furthermore, if the attr is
+ * of the form <attr>=<value>*, then <value> is
+ * treated as a prefix, and the aci applies to
+ * any value with that prefix.
+ *
+ * Ideally, this would allow r.e. matches.
+ */
+ if (aci_get_part(&right, 0, '*', &left) < 0
+ || right.bv_len <= left.bv_len)
+ {
+ if (ber_bvstrcasecmp(val, &right) == 0)
+ return(1);
+ } else if (val->bv_len >= left.bv_len) {
+ if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
+ return(1);
+ }
+ }
+ }
+ }
+ return(0);
+}
+
+static slap_access_t
+aci_list_get_attr_rights(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val )
+{
+ struct berval bv;
+ slap_access_t mask;
+ int i;
+
+ /* loop through each rights/attr pair, skip first part (action) */
+ ACL_INIT(mask);
+ for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
+ if (aci_list_has_attr(&bv, attr, val) == 0)
+ continue;
+ if (aci_get_part(list, i, ';', &bv) < 0)
+ continue;
+ mask |= aci_list_map_rights(&bv);
+ }
+ return(mask);
+}
+
+static int
+aci_list_get_rights(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ struct berval perm, actn;
+ slap_access_t *mask;
+ int i, found;
+
+ if (attr == NULL || attr->bv_len == 0
+ || ber_bvstrcasecmp( attr, &aci_bv_entry ) == 0) {
+ attr = &aci_bv_br_entry;
+ }
+
+ found = 0;
+ ACL_INIT(*grant);
+ ACL_INIT(*deny);
+ /* loop through each permissions clause */
+ for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
+ if (aci_get_part(&perm, 0, ';', &actn) < 0)
+ continue;
+ if (ber_bvstrcasecmp( &aci_bv_grant, &actn ) == 0) {
+ mask = grant;
+ } else if (ber_bvstrcasecmp( &aci_bv_deny, &actn ) == 0) {
+ mask = deny;
+ } else {
+ continue;
+ }
+
+ found = 1;
+ *mask |= aci_list_get_attr_rights(&perm, attr, val);
+ *mask |= aci_list_get_attr_rights(&perm, &aci_bv_br_all, NULL);
+ }
+ return(found);
+}
+
+static int
+aci_group_member (
+ struct berval *subj,
+ struct berval *defgrpoc,
+ struct berval *defgrpat,
+ Backend *be,
+ Entry *e,
+ Connection *conn,
+ Operation *op,
+ regmatch_t *matches
+)
+{
+ struct berval bv;
+ struct berval subjdn;
+ struct berval grpoc;
+ struct berval grpat;
+ ObjectClass *grp_oc = NULL;
+ AttributeDescription *grp_ad = NULL;
+ const char *text;
+ int rc;
+
+ /* format of string is "group/objectClassValue/groupAttrName" */
+ if (aci_get_part(subj, 0, '/', &subjdn) < 0) {
+ return(0);
+ }
+
+ if (aci_get_part(subj, 1, '/', &grpoc) < 0) {
+ grpoc = *defgrpoc;
+ }
+
+ if (aci_get_part(subj, 2, '/', &grpat) < 0) {
+ grpat = *defgrpat;
+ }
+
+ rc = slap_bv2ad( &grpat, &grp_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ rc = 0;
+ goto done;
+ }
+ rc = 0;
+
+ grp_oc = oc_bvfind( &grpoc );
+
+ if (grp_oc != NULL && grp_ad != NULL ) {
+ struct berval ndn;
+ bv.bv_val = (char *)ch_malloc(1024);
+ bv.bv_len = 1024;
+ string_expand(&bv, &subjdn, e->e_ndn, matches);
+ if ( dnNormalize2(NULL, &bv, &ndn) == LDAP_SUCCESS ) {
+ rc = (backend_group(be, conn, op, e, &ndn, &op->o_ndn, grp_oc, grp_ad) == 0);
+ free( ndn.bv_val );
+ }
+ ch_free(bv.bv_val);
+ }
+
+done:
+ return(rc);
+}
+
+static struct berval GroupClass = {
+ sizeof(SLAPD_GROUP_CLASS)-1, SLAPD_GROUP_CLASS };
+static struct berval GroupAttr = {
+ sizeof(SLAPD_GROUP_ATTR)-1, SLAPD_GROUP_ATTR };
+static struct berval RoleClass = {
+ sizeof(SLAPD_ROLE_CLASS)-1, SLAPD_ROLE_CLASS };
+static struct berval RoleAttr = {
+ sizeof(SLAPD_ROLE_ATTR)-1, SLAPD_ROLE_ATTR };
+
+static int
+aci_mask(
+ Backend *be,
+ Connection *conn,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *aci,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny
+)
+{
+ struct berval bv, perms, sdn;
+ int rc;
+
+
+ assert( desc->ad_cname.bv_val != NULL );
+
+ /* parse an aci of the form:
+ oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
+
+ See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
+ a full description of the format for this attribute.
+
+ For now, this routine only supports scope=entry.
+ */
+
+ /* check that the aci has all 5 components */
+ if (aci_get_part(aci, 4, '#', NULL) < 0)
+ return(0);
+
+ /* check that the aci family is supported */
+ if (aci_get_part(aci, 0, '#', &bv) < 0)
+ return(0);
+
+ /* check that the scope is "entry" */
+ if (aci_get_part(aci, 1, '#', &bv) < 0
+ || ber_bvstrcasecmp( &aci_bv_entry, &bv ) != 0)
+ {
+ return(0);
+ }
+
+ /* get the list of permissions clauses, bail if empty */
+ if (aci_get_part(aci, 2, '#', &perms) <= 0)
+ return(0);
+
+ /* check if any permissions allow desired access */
+ if (aci_list_get_rights(&perms, &desc->ad_cname, val, grant, deny) == 0)
+ return(0);
+
+ /* see if we have a DN match */
+ if (aci_get_part(aci, 3, '#', &bv) < 0)
+ return(0);
+
+ if (aci_get_part(aci, 4, '#', &sdn) < 0)
+ return(0);
+
+ if (ber_bvstrcasecmp( &aci_bv_access_id, &bv ) == 0) {
+ struct berval ndn;
+ rc = 1;
+ if ( dnNormalize2(NULL, &sdn, &ndn) == LDAP_SUCCESS ) {
+ if (!dn_match( &op->o_ndn, &ndn))
+ rc = 0;
+ free(ndn.bv_val);
+ }
+ return(rc);
+ }
+
+ if (ber_bvstrcasecmp( &aci_bv_self, &bv ) == 0) {
+ if (dn_match(&op->o_ndn, &e->e_nname))
+ return(1);
+
+ } else if (ber_bvstrcasecmp( &aci_bv_dnattr, &bv ) == 0) {
+ Attribute *at;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ rc = slap_bv2ad( &sdn, &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ rc = 0;
+
+ bv = op->o_ndn;
+
+ for(at = attrs_find( e->e_attrs, ad );
+ at != NULL;
+ at = attrs_find( at->a_next, ad ) )
+ {
+ if (value_find( ad, at->a_vals, &bv) == 0 ) {
+ rc = 1;
+ break;
+ }
+ }
+
+ return rc;
+
+
+ } else if (ber_bvstrcasecmp( &aci_bv_group, &bv ) == 0) {
+ if (aci_group_member(&sdn, &GroupClass, &GroupAttr, be, e, conn, op, matches))
+ return(1);
+
+ } else if (ber_bvstrcasecmp( &aci_bv_role, &bv ) == 0) {
+ if (aci_group_member(&sdn, &RoleClass, &RoleAttr, be, e, conn, op, matches))
+ return(1);
+
+ } else if (ber_bvstrcasecmp( &aci_bv_set, &bv ) == 0) {
+ if (aci_match_set(&sdn, be, e, conn, op, 0))
+ return(1);
+
+ } else if (ber_bvstrcasecmp( &aci_bv_set_ref, &bv ) == 0) {
+ if (aci_match_set(&sdn, be, e, conn, op, 1))
+ return(1);
+
+ }
+
+ return(0);
+}
+
+#endif /* SLAPD_ACI_ENABLED */
+
+static void
+string_expand(
+ struct berval *bv,
+ struct berval *pat,
+ char *match,
+ regmatch_t *matches)
+{
+ ber_len_t size;
+ char *sp;
+ char *dp;
+ int flag;
+
+ size = 0;
+ bv->bv_val[0] = '\0';
+ bv->bv_len--; /* leave space for lone $ */
+
+ flag = 0;
+ for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
+ sp < pat->bv_val + pat->bv_len ; sp++) {
+ /* did we previously see a $ */
+ if (flag) {
+ if (*sp == '$') {
+ *dp++ = '$';
+ size++;
+ } else if (*sp >= '0' && *sp <= '9' ) {
+ int n;
+ int i;
+ int l;
+
+ n = *sp - '0';
+ *dp = '\0';
+ i = matches[n].rm_so;
+ l = matches[n].rm_eo;
+ for ( ; size < bv->bv_len && i < l; size++, i++ ) {
+ *dp++ = match[i];
+ size++;
+ }
+ *dp = '\0';
+ }
+ flag = 0;
+ } else {
+ if (*sp == '$') {
+ flag = 1;
+ } else {
+ *dp++ = *sp;
+ size++;
+ }
+ }
+ }
+
+ if (flag) {
+ /* must have ended with a single $ */
+ *dp++ = '$';
+ size++;
+ }
+
+ *dp = '\0';
+ bv->bv_len = size;
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+ "string_expand: pattern = %.*s\n", pat->bv_len, pat->bv_val ));
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL1,
+ "string_expand: expanded = %s\n", bv->bv_val ));
+#else
+ Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern: %.*s\n", pat->bv_len, pat->bv_val, 0 );
+ Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
+#endif
+}
+
+static int
+regex_matches(
+ struct berval *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ char *buf, /* buffer with $N expansion variables */
+ regmatch_t *matches /* offsets in buffer for $N expansion variables */
+)
+{
+ regex_t re;
+ char newbuf[512];
+ struct berval bv;
+ int rc;
+
+ bv.bv_len = sizeof(newbuf);
+ bv.bv_val = newbuf;
+
+ if(str == NULL) str = "";
+
+ string_expand(&bv, pat, buf, matches);
+ if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
+ char error[512];
+ regerror(rc, &re, error, sizeof(error));
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_ERR,
+ "regex_matches: compile( \"%s\", \"%s\") failed %s\n",
+ pat->bv_val, str, error ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "compile( \"%s\", \"%s\") failed %s\n",
+ pat->bv_val, str, error );
+#endif
+ return( 0 );
+ }
+
+ rc = regexec(&re, str, 0, NULL, 0);
+ regfree( &re );
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL2,
+ "regex_matches: string: %s\n", str ));
+ LDAP_LOG(( "aci", LDAP_LEVEL_DETAIL2,
+ "regex_matches: rc: %d %s\n",
+ rc, rc ? "matches" : "no matches" ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: string: %s\n", str, 0, 0 );
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: rc: %d %s\n",
+ rc, !rc ? "matches" : "no matches", 0 );
+#endif
+ return( !rc );
}
-#endif /* sunos5 */
--- /dev/null
+/* backglue.c - backend glue routines */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2001-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+/*
+ * Functions to glue a bunch of other backends into a single tree.
+ * All of the glued backends must share a common suffix. E.g., you
+ * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar.
+ *
+ * This uses the backend structures and routines extensively, but is
+ * not an actual backend of its own. To use it you must add a "subordinate"
+ * keyword to the configuration of other backends. Subordinates will
+ * automatically be connected to their parent backend.
+ *
+ * The purpose of these functions is to allow you to split a single database
+ * into pieces (for load balancing purposes, whatever) but still be able
+ * to treat it as a single database after it's been split. As such, each
+ * of the glued backends should have identical rootdn and rootpw.
+ *
+ * If you need more elaborate configuration, you probably should be using
+ * back-meta instead.
+ * -- Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#define SLAPD_TOOLS
+#include "slap.h"
+
+typedef struct gluenode {
+ BackendDB *be;
+ struct berval pdn;
+} gluenode;
+
+typedef struct glueinfo {
+ BackendDB *be;
+ int nodes;
+ gluenode n[1];
+} glueinfo;
+
+static int glueMode;
+static BackendDB *glueBack;
+
+/* Just like select_backend, but only for our backends */
+static BackendDB *
+glue_back_select (
+ BackendDB *be,
+ const char *dn
+)
+{
+ glueinfo *gi = (glueinfo *) be->be_private;
+ struct berval bv;
+ int i;
+
+ bv.bv_len = strlen(dn);
+ bv.bv_val = (char *) dn;
+
+ for (i = 0; i<gi->nodes; i++) {
+ if (dnIsSuffix(&bv, gi->n[i].be->be_nsuffix[0])) {
+ return gi->n[i].be;
+ }
+ }
+ return NULL;
+}
+
+/* This function will only be called in tool mode */
+static int
+glue_back_open (
+ BackendInfo *bi
+)
+{
+ int rc = 0;
+ static int glueOpened = 0;
+
+ if (glueOpened) return 0;
+
+ glueOpened = 1;
+
+ /* If we were invoked in tool mode, open all the underlying backends */
+ if (slapMode & SLAP_TOOL_MODE) {
+ rc = backend_startup (NULL);
+ } /* other case is impossible */
+ return rc;
+}
+
+/* This function will only be called in tool mode */
+static int
+glue_back_close (
+ BackendInfo *bi
+)
+{
+ static int glueClosed = 0;
+ int rc;
+
+ if (glueClosed) return 0;
+
+ glueClosed = 1;
+
+ if (slapMode & SLAP_TOOL_MODE) {
+ rc = backend_shutdown (NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_db_open (
+ BackendDB *be
+)
+{
+ glueinfo *gi = (glueinfo *)be->be_private;
+ static int glueOpened = 0;
+ int rc = 0;
+
+ if (glueOpened) return 0;
+
+ glueOpened = 1;
+
+ gi->be->be_acl = be->be_acl;
+
+ if (gi->be->bd_info->bi_db_open)
+ rc = gi->be->bd_info->bi_db_open(gi->be);
+
+ return rc;
+}
+
+static int
+glue_back_db_close (
+ BackendDB *be
+)
+{
+ glueinfo *gi = (glueinfo *)be->be_private;
+ static int glueClosed = 0;
+
+ if (glueClosed) return 0;
+
+ glueClosed = 1;
+
+ /* Close the master */
+ if (gi->be->bd_info->bi_db_close)
+ gi->be->bd_info->bi_db_close( gi->be );
+
+ return 0;
+}
+
+static int
+glue_back_db_destroy (
+ BackendDB *be
+)
+{
+ glueinfo *gi = (glueinfo *)be->be_private;
+
+ if (gi->be->bd_info->bi_db_destroy)
+ gi->be->bd_info->bi_db_destroy( gi->be );
+ free (gi->be);
+ free (gi);
+ return 0;
+}
+
+typedef struct glue_state {
+ int err;
+ int nentries;
+ int matchlen;
+ char *matched;
+ int nrefs;
+ BerVarray refs;
+ slap_callback *prevcb;
+} glue_state;
+
+static void
+glue_back_response (
+ Connection *conn,
+ Operation *op,
+ ber_tag_t tag,
+ ber_int_t msgid,
+ ber_int_t err,
+ const char *matched,
+ const char *text,
+ BerVarray ref,
+ const char *resoid,
+ struct berval *resdata,
+ struct berval *sasldata,
+ LDAPControl **ctrls
+)
+{
+ glue_state *gs = op->o_callback->sc_private;
+
+ if (err == LDAP_SUCCESS || gs->err != LDAP_SUCCESS)
+ gs->err = err;
+ if (gs->err == LDAP_SUCCESS && gs->matched) {
+ free (gs->matched);
+ gs->matchlen = 0;
+ }
+ if (gs->err != LDAP_SUCCESS && matched) {
+ int len;
+ len = strlen (matched);
+ if (len > gs->matchlen) {
+ if (gs->matched)
+ free (gs->matched);
+ gs->matched = ch_strdup (matched);
+ gs->matchlen = len;
+ }
+ }
+ if (ref) {
+ int i, j, k;
+ BerVarray new;
+
+ for (i=0; ref[i].bv_val; i++);
+
+ j = gs->nrefs;
+ if (!j) {
+ new = ch_malloc ((i+1)*sizeof(struct berval));
+ } else {
+ new = ch_realloc(gs->refs,
+ (j+i+1)*sizeof(struct berval));
+ }
+ for (k=0; k<i; j++,k++) {
+ ber_dupbv( &new[j], &ref[k] );
+ }
+ new[j].bv_val = NULL;
+ gs->nrefs = j;
+ gs->refs = new;
+ }
+}
+
+static void
+glue_back_sresult (
+ Connection *c,
+ Operation *op,
+ ber_int_t err,
+ const char *matched,
+ const char *text,
+ BerVarray refs,
+ LDAPControl **ctrls,
+ int nentries
+)
+{
+ glue_state *gs = op->o_callback->sc_private;
+
+ gs->nentries += nentries;
+ glue_back_response (c, op, 0, 0, err, matched, text, refs,
+ NULL, NULL, NULL, ctrls);
+}
+
+static int
+glue_back_sendentry (
+ BackendDB *be,
+ Connection *c,
+ Operation *op,
+ Entry *e,
+ AttributeName *an,
+ int ao,
+ LDAPControl **ctrls
+)
+{
+ slap_callback *tmp = op->o_callback;
+ glue_state *gs = tmp->sc_private;
+ int rc;
+
+ op->o_callback = gs->prevcb;
+ if (op->o_callback && op->o_callback->sc_sendentry) {
+ rc = op->o_callback->sc_sendentry(be, c, op, e, an, ao, ctrls);
+ } else {
+ rc = send_search_entry(be, c, op, e, an, ao, ctrls);
+ }
+ op->o_callback = tmp;
+ return rc;
+}
+
+static int
+glue_back_search (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn,
+ int scope,
+ int deref,
+ int slimit,
+ int tlimit,
+ Filter *filter,
+ struct berval *filterstr,
+ AttributeName *attrs,
+ int attrsonly
+)
+{
+ glueinfo *gi = (glueinfo *)b0->be_private;
+ BackendDB *be;
+ int i, rc, t2limit = 0, s2limit = 0;
+ long stoptime = 0;
+ struct berval bv;
+ glue_state gs = {0};
+ slap_callback cb;
+
+ cb.sc_response = glue_back_response;
+ cb.sc_sresult = glue_back_sresult;
+ cb.sc_sendentry = glue_back_sendentry;
+ cb.sc_private = &gs;
+
+ gs.prevcb = op->o_callback;
+
+ if (tlimit) {
+ stoptime = slap_get_time () + tlimit;
+ }
+
+ switch (scope) {
+ case LDAP_SCOPE_BASE:
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_search) {
+ rc = be->be_search (be, conn, op, dn, ndn, scope,
+ deref, slimit, tlimit, filter, filterstr,
+ attrs, attrsonly);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL,
+ "No search target found", NULL, NULL);
+ }
+ return rc;
+
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBTREE:
+ op->o_callback = &cb;
+
+ /*
+ * Execute in reverse order, most general first
+ */
+ for (i = gi->nodes-1; i >= 0; i--) {
+ if (!gi->n[i].be || !gi->n[i].be->be_search)
+ continue;
+ if (tlimit) {
+ t2limit = stoptime - slap_get_time ();
+ if (t2limit <= 0)
+ break;
+ }
+ if (slimit) {
+ s2limit = slimit - gs.nentries;
+ if (s2limit <= 0)
+ break;
+ }
+ /*
+ * check for abandon
+ */
+ ldap_pvt_thread_mutex_lock (&op->o_abandonmutex);
+ rc = op->o_abandon;
+ ldap_pvt_thread_mutex_unlock (&op->o_abandonmutex);
+ if (rc) {
+ rc = 0;
+ goto done;
+ }
+ be = gi->n[i].be;
+ if (scope == LDAP_SCOPE_ONELEVEL &&
+ dn_match(&gi->n[i].pdn, ndn)) {
+ rc = be->be_search (be, conn, op,
+ be->be_suffix[0], be->be_nsuffix[0],
+ LDAP_SCOPE_BASE, deref,
+ s2limit, t2limit, filter, filterstr,
+ attrs, attrsonly);
+
+ } else if (scope == LDAP_SCOPE_SUBTREE &&
+ dnIsSuffix(be->be_nsuffix[0], ndn)) {
+ rc = be->be_search (be, conn, op,
+ be->be_suffix[0], be->be_nsuffix[0],
+ scope, deref,
+ s2limit, t2limit, filter, filterstr,
+ attrs, attrsonly);
+
+ } else if (dnIsSuffix(&bv, be->be_nsuffix[0])) {
+ rc = be->be_search (be, conn, op, dn, ndn,
+ scope, deref,
+ s2limit, t2limit, filter, filterstr,
+ attrs, attrsonly);
+ }
+ }
+ break;
+ }
+ op->o_callback = gs.prevcb;
+
+ send_search_result (conn, op, gs.err, gs.matched, NULL,
+ gs.refs, NULL, gs.nentries);
+
+done:
+ if (gs.matched)
+ free (gs.matched);
+ if (gs.refs)
+ ber_bvarray_free(gs.refs);
+ return rc;
+}
+
+static int
+glue_back_bind (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn,
+ int method,
+ struct berval *cred,
+ struct berval *edn
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_bind) {
+ conn->c_authz_backend = be;
+ rc = be->be_bind (be, conn, op, dn, ndn, method, cred, edn);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL, "No bind target found",
+ NULL, NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_compare (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn,
+ AttributeAssertion *ava
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_compare) {
+ rc = be->be_compare (be, conn, op, dn, ndn, ava);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL, "No compare target found",
+ NULL, NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_modify (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn,
+ Modifications *mod
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_modify) {
+ rc = be->be_modify (be, conn, op, dn, ndn, mod);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL,
+ "No modify target found", NULL, NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_modrdn (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn,
+ struct berval *newrdn,
+ struct berval *nnewrdn,
+ int del,
+ struct berval *newsup,
+ struct berval *nnewsup
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_modrdn) {
+ rc = be->be_modrdn (be, conn, op, dn, ndn,
+ newrdn, nnewrdn, del, newsup, nnewsup );
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL,
+ "No modrdn target found", NULL, NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_add (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ Entry *e
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, e->e_ndn);
+
+ if (be && be->be_add) {
+ rc = be->be_add (be, conn, op, e);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL, "No add target found",
+ NULL, NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_delete (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_delete) {
+ rc = be->be_delete (be, conn, op, dn, ndn);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result (conn, op, rc, NULL, "No delete target found",
+ NULL, NULL);
+ }
+ return rc;
+}
+
+static int
+glue_back_release_rw (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ Entry *e,
+ int rw
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, e->e_ndn);
+
+ if (be && be->be_release) {
+ rc = be->be_release (be, conn, op, e, rw);
+ } else {
+ entry_free (e);
+ rc = 0;
+ }
+ return rc;
+}
+
+static int
+glue_back_group (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ Entry *target,
+ struct berval *ndn,
+ struct berval *ondn,
+ ObjectClass *oc,
+ AttributeDescription * ad
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_group) {
+ rc = be->be_group (be, conn, op, target, ndn, ondn, oc, ad);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ }
+ return rc;
+}
+
+static int
+glue_back_attribute (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ Entry *target,
+ struct berval *ndn,
+ AttributeDescription *ad,
+ BerVarray *vals
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_attribute) {
+ rc = be->be_attribute (be, conn, op, target, ndn, ad, vals);
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ }
+ return rc;
+}
+
+static int
+glue_back_referrals (
+ BackendDB *b0,
+ Connection *conn,
+ Operation *op,
+ struct berval *dn,
+ struct berval *ndn,
+ const char **text
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, ndn->bv_val);
+
+ if (be && be->be_chk_referrals) {
+ rc = be->be_chk_referrals (be, conn, op, dn, ndn, text);
+ } else {
+ rc = LDAP_SUCCESS;;
+ }
+ return rc;
+}
+
+static int
+glue_tool_entry_open (
+ BackendDB *b0,
+ int mode
+)
+{
+ /* We don't know which backend to talk to yet, so just
+ * remember the mode and move on...
+ */
+
+ glueMode = mode;
+ glueBack = NULL;
+
+ return 0;
+}
+
+static int
+glue_tool_entry_close (
+ BackendDB *b0
+)
+{
+ int rc = 0;
+
+ if (glueBack) {
+ if (!glueBack->be_entry_close)
+ return 0;
+ rc = glueBack->be_entry_close (glueBack);
+ }
+ return rc;
+}
+
+static ID
+glue_tool_entry_first (
+ BackendDB *b0
+)
+{
+ glueinfo *gi = (glueinfo *) b0->be_private;
+ int i;
+
+ /* If we're starting from scratch, start at the most general */
+ if (!glueBack) {
+ for (i = gi->nodes-1; i >= 0; i--) {
+ if (gi->n[i].be->be_entry_open &&
+ gi->n[i].be->be_entry_first) {
+ glueBack = gi->n[i].be;
+ break;
+ }
+ }
+
+ }
+ if (!glueBack || glueBack->be_entry_open (glueBack, glueMode) != 0)
+ return NOID;
+
+ return glueBack->be_entry_first (glueBack);
+}
+
+static ID
+glue_tool_entry_next (
+ BackendDB *b0
+)
+{
+ glueinfo *gi = (glueinfo *) b0->be_private;
+ int i;
+ ID rc;
+
+ if (!glueBack || !glueBack->be_entry_next)
+ return NOID;
+
+ rc = glueBack->be_entry_next (glueBack);
+
+ /* If we ran out of entries in one database, move on to the next */
+ if (rc == NOID) {
+ glueBack->be_entry_close (glueBack);
+ for (i=0; i<gi->nodes; i++) {
+ if (gi->n[i].be == glueBack)
+ break;
+ }
+ if (i == 0) {
+ glueBack = NULL;
+ rc = NOID;
+ } else {
+ glueBack = gi->n[i-1].be;
+ rc = glue_tool_entry_first (b0);
+ }
+ }
+ return rc;
+}
+
+static Entry *
+glue_tool_entry_get (
+ BackendDB *b0,
+ ID id
+)
+{
+ if (!glueBack || !glueBack->be_entry_get)
+ return NULL;
+
+ return glueBack->be_entry_get (glueBack, id);
+}
+
+static ID
+glue_tool_entry_put (
+ BackendDB *b0,
+ Entry *e,
+ struct berval *text
+)
+{
+ BackendDB *be;
+ int rc;
+
+ be = glue_back_select (b0, e->e_ndn);
+ if (!be->be_entry_put)
+ return NOID;
+
+ if (!glueBack) {
+ rc = be->be_entry_open (be, glueMode);
+ if (rc != 0)
+ return NOID;
+ } else if (be != glueBack) {
+ /* If this entry belongs in a different branch than the
+ * previous one, close the current database and open the
+ * new one.
+ */
+ glueBack->be_entry_close (glueBack);
+ rc = be->be_entry_open (be, glueMode);
+ if (rc != 0)
+ return NOID;
+ }
+ glueBack = be;
+ return be->be_entry_put (be, e, text);
+}
+
+static int
+glue_tool_entry_reindex (
+ BackendDB *b0,
+ ID id
+)
+{
+ if (!glueBack || !glueBack->be_entry_reindex)
+ return -1;
+
+ return glueBack->be_entry_reindex (glueBack, id);
+}
+
+static int
+glue_tool_sync (
+ BackendDB *b0
+)
+{
+ glueinfo *gi = (glueinfo *) b0->be_private;
+ int i;
+
+ /* just sync everyone */
+ for (i = 0; i<gi->nodes; i++)
+ if (gi->n[i].be->be_sync)
+ gi->n[i].be->be_sync (gi->n[i].be);
+ return 0;
+}
+
+int
+glue_sub_init( )
+{
+ int i, j;
+ int cont = num_subordinates;
+ BackendDB *b1, *be;
+ BackendInfo *bi;
+ glueinfo *gi;
+
+ /* While there are subordinate backends, search backwards through the
+ * backends and connect them to their superior.
+ */
+ for (i = nBackendDB - 1, b1=&backendDB[i]; cont && i>=0; b1--,i--) {
+ if (b1->be_flags & SLAP_BFLAG_GLUE_SUBORDINATE) {
+ /* The last database cannot be a subordinate of noone */
+ if (i == nBackendDB - 1) {
+ b1->be_flags ^= SLAP_BFLAG_GLUE_SUBORDINATE;
+ }
+ continue;
+ }
+ gi = NULL;
+ for (j = i-1, be=&backendDB[j]; j>=0; be--,j--) {
+ if (!(be->be_flags & SLAP_BFLAG_GLUE_SUBORDINATE)) {
+ continue;
+ }
+ /* We will only link it once */
+ if (be->be_flags & SLAP_BFLAG_GLUE_LINKED) {
+ continue;
+ }
+ if (!dnIsSuffix(be->be_nsuffix[0], b1->be_nsuffix[0])) {
+ continue;
+ }
+ cont--;
+ be->be_flags |= SLAP_BFLAG_GLUE_LINKED;
+ if (gi == NULL) {
+ /* We create a copy of the superior's be
+ * structure, pointing to all of its original
+ * information. Then we replace elements of
+ * the superior's info with our own. The copy
+ * is used whenever we have operations to pass
+ * down to the real database.
+ */
+ b1->be_flags |= SLAP_BFLAG_GLUE_INSTANCE;
+ gi = (glueinfo *)ch_malloc(sizeof(glueinfo));
+ gi->be = (BackendDB *)ch_malloc(
+ sizeof(BackendDB) + sizeof(BackendInfo));
+ bi = (BackendInfo *)(gi->be+1);
+ *gi->be = *b1;
+ gi->nodes = 0;
+ *bi = *b1->bd_info;
+ bi->bi_open = glue_back_open;
+ bi->bi_close = glue_back_close;
+ bi->bi_db_open = glue_back_db_open;
+ bi->bi_db_close = glue_back_db_close;
+ bi->bi_db_destroy = glue_back_db_destroy;
+
+ bi->bi_op_bind = glue_back_bind;
+ bi->bi_op_search = glue_back_search;
+ bi->bi_op_compare = glue_back_compare;
+ bi->bi_op_modify = glue_back_modify;
+ bi->bi_op_modrdn = glue_back_modrdn;
+ bi->bi_op_add = glue_back_add;
+ bi->bi_op_delete = glue_back_delete;
+
+ bi->bi_entry_release_rw = glue_back_release_rw;
+ bi->bi_acl_group = glue_back_group;
+ bi->bi_acl_attribute = glue_back_attribute;
+ bi->bi_chk_referrals = glue_back_referrals;
+
+ /*
+ * hooks for slap tools
+ */
+ bi->bi_tool_entry_open = glue_tool_entry_open;
+ bi->bi_tool_entry_close = glue_tool_entry_close;
+ bi->bi_tool_entry_first = glue_tool_entry_first;
+ bi->bi_tool_entry_next = glue_tool_entry_next;
+ bi->bi_tool_entry_get = glue_tool_entry_get;
+ bi->bi_tool_entry_put = glue_tool_entry_put;
+ bi->bi_tool_entry_reindex = glue_tool_entry_reindex;
+ bi->bi_tool_sync = glue_tool_sync;
+ } else {
+ gi = (glueinfo *)ch_realloc(gi,
+ sizeof(glueinfo) +
+ gi->nodes * sizeof(gluenode));
+ }
+ gi->n[gi->nodes].be = be;
+ dnParent( be->be_nsuffix[0], &gi->n[gi->nodes].pdn );
+ gi->nodes++;
+ }
+ if (gi) {
+ /* One more node for the master */
+ gi = (glueinfo *)ch_realloc(gi,
+ sizeof(glueinfo) + gi->nodes * sizeof(gluenode));
+ gi->n[gi->nodes].be = gi->be;
+ dnParent( b1->be_nsuffix[0], &gi->n[gi->nodes].pdn );
+ gi->nodes++;
+ b1->be_private = gi;
+ b1->bd_info = bi;
+ }
+ }
+ /* If there are any unresolved subordinates left, something is wrong */
+ return cont;
+}
/* entry.c - routines for dealing with entries */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include "slap.h"
-void entry_free();
-char *entry2str();
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "ldif.h"
-static unsigned char *ebuf; /* buf returned by entry2str */
+static unsigned char *ebuf; /* buf returned by entry2str */
static unsigned char *ecur; /* pointer to end of currently used ebuf */
-static int emaxsize;/* max size of ebuf */
+static int emaxsize;/* max size of ebuf */
+
+/*
+ * Empty root entry
+ */
+const Entry slap_entry_root = { NOID, { 0, "" }, { 0, "" }, NULL, 0, { 0, "" }, NULL };
+
+int entry_destroy(void)
+{
+ if ( ebuf ) free( ebuf );
+ ebuf = NULL;
+ ecur = NULL;
+ emaxsize = 0;
+ return 0;
+}
+
Entry *
-str2entry( char *s )
+str2entry( char *s )
{
- int i;
+ int rc;
Entry *e;
- Attribute **a;
char *type;
- char *value;
- char *next;
- int vlen, nvals, maxvals;
- struct berval bval;
- struct berval *vals[2];
- char ptype[64];
+ struct berval vals[2];
+ AttributeDescription *ad;
+ const char *text;
+ char *next;
/*
- * In string format, an entry looks like this:
+ * LDIF is used as the string format.
+ * An entry looks like this:
*
- * <id>\n
* dn: <dn>\n
* [<attr>:[:] <value>\n]
* [<tab><continuedvalue>\n]*
* or newline.
*/
- Debug( LDAP_DEBUG_TRACE, "=> str2entry\n", s, 0, 0 );
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "str2entry: \"%s\"\n", s ? s : "NULL" ));
+#else
+ Debug( LDAP_DEBUG_TRACE, "=> str2entry\n",
+ s ? s : "NULL", 0, 0 );
+#endif
+ /* initialize reader/writer lock */
e = (Entry *) ch_calloc( 1, sizeof(Entry) );
- /* check to see if there's an id included */
- next = s;
- if ( isdigit( *s ) ) {
- e->e_id = atoi( s );
- if ( (s = str_getline( &next )) == NULL ) {
- Debug( LDAP_DEBUG_TRACE,
- "<= str2entry NULL (missing newline after id)\n",
- 0, 0, 0 );
- return( NULL );
- }
+ if( e == NULL ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+ "str2entry: entry allocation failed.\n" ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "<= str2entry NULL (entry allocation failed)\n",
+ 0, 0, 0 );
+#endif
+ return( NULL );
}
+ /* initialize entry */
+ e->e_id = NOID;
+
/* dn + attributes */
- e->e_attrs = NULL;
- vals[0] = &bval;
- vals[1] = NULL;
- ptype[0] = '\0';
- while ( (s = str_getline( &next )) != NULL ) {
+ vals[1].bv_val = NULL;
+
+ next = s;
+ while ( (s = ldif_getline( &next )) != NULL ) {
if ( *s == '\n' || *s == '\0' ) {
break;
}
- if ( str_parse_line( s, &type, &value, &vlen ) != 0 ) {
+ if ( ldif_parse_line( s, &type, &vals[0].bv_val, &vals[0].bv_len ) != 0 ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "str2entry: NULL (parse_line)\n" ));
+#else
Debug( LDAP_DEBUG_TRACE,
"<= str2entry NULL (parse_line)\n", 0, 0, 0 );
+#endif
continue;
}
- if ( strcasecmp( type, ptype ) != 0 ) {
- strncpy( ptype, type, sizeof(ptype) - 1 );
- nvals = 0;
- maxvals = 0;
- a = NULL;
- }
if ( strcasecmp( type, "dn" ) == 0 ) {
+ struct berval *pdn = NULL;
+
+ free( type );
+
if ( e->e_dn != NULL ) {
- Debug( LDAP_DEBUG_ANY,
- "str2entry: entry %d has multiple dns \"%s\" and \"%s\" (second ignored)\n",
- e->e_id, e->e_dn, value );
- continue;
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1, "str2entry: "
+ "entry %ld has multiple DNs \"%s\" and \"%s\"\n",
+ (long) e->e_id, e->e_dn,
+ vals[0].bv_val != NULL ? vals[0].bv_val : "" ));
+#else
+ Debug( LDAP_DEBUG_ANY, "str2entry: "
+ "entry %ld has multiple DNs \"%s\" and \"%s\"\n",
+ (long) e->e_id, e->e_dn,
+ vals[0].bv_val != NULL ? vals[0].bv_val : "" );
+#endif
+ if( vals[0].bv_val != NULL ) free( vals[0].bv_val );
+ entry_free( e );
+ return NULL;
+ }
+
+ rc = dnPrettyNormal( NULL, &vals[0], &e->e_name, &e->e_nname );
+ free( vals[0].bv_val );
+ if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1, "str2entry: "
+ "entry %ld has invalid DN \"%s\"\n",
+ (long) e->e_id,
+ pdn->bv_val ? pdn->bv_val : "" ));
+#else
+ Debug( LDAP_DEBUG_ANY, "str2entry: "
+ "entry %ld has invalid DN \"%s\"\n",
+ (long) e->e_id,
+ pdn->bv_val ? pdn->bv_val : "", 0 );
+#endif
+ entry_free( e );
+ return NULL;
}
- e->e_dn = strdup( value );
continue;
}
- bval.bv_val = value;
- bval.bv_len = vlen;
- if ( attr_merge_fast( e, type, vals, nvals, 1, &maxvals, &a )
- != 0 ) {
- Debug( LDAP_DEBUG_TRACE,
+ ad = NULL;
+ rc = slap_str2ad( type, &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "str2entry: str2ad(%s): %s\n", type, text ));
+#else
+ Debug( slapMode & SLAP_TOOL_MODE
+ ? LDAP_DEBUG_ANY : LDAP_DEBUG_TRACE,
+ "<= str2entry: str2ad(%s): %s\n", type, text, 0 );
+#endif
+ if( slapMode & SLAP_TOOL_MODE ) {
+ entry_free( e );
+ free( vals[0].bv_val );
+ free( type );
+ return NULL;
+ }
+
+ rc = slap_str2undef_ad( type, &ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "str2entry: str2undef_ad(%s): %s\n", type, text ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "<= str2entry: str2undef_ad(%s): %s\n",
+ type, text, 0 );
+#endif
+ entry_free( e );
+ free( vals[0].bv_val );
+ free( type );
+ return NULL;
+ }
+ }
+
+ if( slapMode & SLAP_TOOL_MODE ) {
+ struct berval pval;
+ slap_syntax_validate_func *validate =
+ ad->ad_type->sat_syntax->ssyn_validate;
+ slap_syntax_transform_func *pretty =
+ ad->ad_type->sat_syntax->ssyn_pretty;
+
+ if( pretty ) {
+ rc = pretty( ad->ad_type->sat_syntax,
+ &vals[0], &pval );
+
+ } else if( validate ) {
+ /*
+ * validate value per syntax
+ */
+ rc = validate( ad->ad_type->sat_syntax, &vals[0] );
+
+ } else {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+ "str2entry: no validator for syntax %s\n",
+ ad->ad_type->sat_syntax->ssyn_oid ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "str2entry: no validator for syntax %s\n",
+ ad->ad_type->sat_syntax->ssyn_oid, 0, 0 );
+#endif
+ entry_free( e );
+ free( vals[0].bv_val );
+ free( type );
+ return NULL;
+ }
+
+ if( rc != 0 ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
+ "str2entry: invalid value for syntax %s\n",
+ ad->ad_type->sat_syntax->ssyn_oid ));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "str2entry: invalid value for syntax %s\n",
+ ad->ad_type->sat_syntax->ssyn_oid, 0, 0 );
+#endif
+ entry_free( e );
+ free( vals[0].bv_val );
+ free( type );
+ return NULL;
+ }
+
+ if( pretty ) {
+ free( vals[0].bv_val );
+ vals[0] = pval;
+ }
+ }
+
+ rc = attr_merge( e, ad, vals );
+ if( rc != 0 ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "str2entry: NULL (attr_merge)\n" ));
+#else
+ Debug( LDAP_DEBUG_ANY,
"<= str2entry NULL (attr_merge)\n", 0, 0, 0 );
+#endif
+ entry_free( e );
+ free( vals[0].bv_val );
+ free( type );
return( NULL );
}
- nvals++;
+
+ free( type );
+ free( vals[0].bv_val );
}
/* check to make sure there was a dn: line */
if ( e->e_dn == NULL ) {
- Debug( LDAP_DEBUG_ANY, "str2entry: entry %d has no dn\n",
- e->e_id, 0, 0 );
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+ "str2entry: entry %ld has no dn.\n",
+ (long) e->e_id ));
+#else
+ Debug( LDAP_DEBUG_ANY, "str2entry: entry %ld has no dn\n",
+ (long) e->e_id, 0, 0 );
+#endif
entry_free( e );
return( NULL );
}
- Debug( LDAP_DEBUG_TRACE, "<= str2entry 0x%x\n", e, 0, 0 );
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL2,
+ "str2entry(%s) -> 0x%lx\n", e->e_dn, (unsigned long)e ));
+#else
+ Debug(LDAP_DEBUG_TRACE, "<= str2entry(%s) -> 0x%lx\n",
+ e->e_dn, (unsigned long) e, 0 );
+#endif
return( e );
}
+
#define GRABSIZE BUFSIZ
#define MAKE_SPACE( n ) { \
while ( ecur + (n) > ebuf + emaxsize ) { \
- int offset; \
+ ptrdiff_t offset; \
offset = (int) (ecur - ebuf); \
ebuf = (unsigned char *) ch_realloc( (char *) ebuf, \
emaxsize + GRABSIZE ); \
emaxsize += GRABSIZE; \
ecur = ebuf + offset; \
} \
-}
+ }
char *
entry2str(
Entry *e,
- int *len,
- int printid
-)
+ int *len )
{
Attribute *a;
struct berval *bv;
- int i, tmplen;
+ int i;
+ ber_len_t tmplen;
/*
* In string format, an entry looks like this:
- * <id>\n
* dn: <dn>\n
* [<attr>: <value>\n]*
*/
ecur = ebuf;
- if ( printid ) {
- /* id + newline */
- MAKE_SPACE( 10 );
- sprintf( (char *) ecur, "%ld\n", e->e_id );
- ecur = (unsigned char *) strchr( (char *) ecur, '\0' );
- }
-
/* put the dn */
if ( e->e_dn != NULL ) {
/* put "dn: <dn>" */
- tmplen = strlen( e->e_dn );
+ tmplen = e->e_name.bv_len;
MAKE_SPACE( LDIF_SIZE_NEEDED( 2, tmplen ));
- put_type_and_value( (char **) &ecur, "dn", e->e_dn, tmplen );
+ ldif_sput( (char **) &ecur, LDIF_PUT_VALUE, "dn", e->e_dn, tmplen );
}
/* put the attributes */
for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
/* put "<type>:[:] <value>" line for each value */
- for ( i = 0; a->a_vals[i] != NULL; i++ ) {
- bv = a->a_vals[i];
- tmplen = strlen( a->a_type );
+ for ( i = 0; a->a_vals[i].bv_val != NULL; i++ ) {
+ bv = &a->a_vals[i];
+ tmplen = a->a_desc->ad_cname.bv_len;
MAKE_SPACE( LDIF_SIZE_NEEDED( tmplen, bv->bv_len ));
- put_type_and_value( (char **) &ecur, a->a_type,
+ ldif_sput( (char **) &ecur, LDIF_PUT_VALUE,
+ a->a_desc->ad_cname.bv_val,
bv->bv_val, bv->bv_len );
}
}
void
entry_free( Entry *e )
{
- int i;
- Attribute *a, *next;
+ /* free an entry structure */
+ assert( e != NULL );
+
+ /* e_private must be freed by the caller */
+ assert( e->e_private == NULL );
+ e->e_private = NULL;
+ /* free DNs */
if ( e->e_dn != NULL ) {
free( e->e_dn );
+ e->e_dn = NULL;
}
- for ( a = e->e_attrs; a != NULL; a = next ) {
- next = a->a_next;
- attr_free( a );
+ if ( e->e_ndn != NULL ) {
+ free( e->e_ndn );
+ e->e_ndn = NULL;
}
+
+ if ( e->e_bv.bv_val != NULL ) {
+ free( e->e_bv.bv_val );
+ e->e_bv.bv_val = NULL;
+ }
+
+ /* free attributes */
+ attrs_free( e->e_attrs );
+ e->e_attrs = NULL;
+
free( e );
}
+
+/*
+ * These routines are used only by Backend.
+ *
+ * the Entry has three entry points (ways to find things):
+ *
+ * by entry e.g., if you already have an entry from the cache
+ * and want to delete it. (really by entry ptr)
+ * by dn e.g., when looking for the base object of a search
+ * by id e.g., for search candidates
+ *
+ * these correspond to three different avl trees that are maintained.
+ */
+
+int
+entry_cmp( Entry *e1, Entry *e2 )
+{
+ return( e1 < e2 ? -1 : (e1 > e2 ? 1 : 0) );
+}
+
+int
+entry_dn_cmp( Entry *e1, Entry *e2 )
+{
+ /* compare their normalized UPPERCASED dn's */
+ int rc = e1->e_nname.bv_len - e2->e_nname.bv_len;
+ if (rc) return rc;
+ return( strcmp( e1->e_ndn, e2->e_ndn ) );
+}
+
+int
+entry_id_cmp( Entry *e1, Entry *e2 )
+{
+ return( e1->e_id < e2->e_id ? -1 : (e1->e_id > e2->e_id ? 1 : 0) );
+}
+
+#ifdef SLAPD_BDB
+
+/* This is like a ber_len */
+static ber_len_t
+entry_lenlen(ber_len_t len)
+{
+ if (len <= 0x7f)
+ return 1;
+ if (len <= 0xff)
+ return 2;
+ if (len <= 0xffff)
+ return 3;
+ if (len <= 0xffffff)
+ return 4;
+ return 5;
+}
+
+static void
+entry_putlen(unsigned char **buf, ber_len_t len)
+{
+ ber_len_t lenlen = entry_lenlen(len);
+
+ if (lenlen == 1) {
+ **buf = (unsigned char) len;
+ } else {
+ int i;
+ **buf = 0x80 | ((unsigned char) lenlen - 1);
+ for (i=lenlen-1; i>0; i--) {
+ (*buf)[i] = (unsigned char) len;
+ len >>= 8;
+ }
+ }
+ *buf += lenlen;
+}
+
+static ber_len_t
+entry_getlen(unsigned char **buf)
+{
+ ber_len_t len;
+ int i;
+
+ len = *(*buf)++;
+ if (len <= 0x7f)
+ return len;
+ i = len & 0x7f;
+ len = 0;
+ for (;i > 0; i--) {
+ len <<= 8;
+ len |= *(*buf)++;
+ }
+ return len;
+}
+
+/* Flatten an Entry into a buffer. The buffer is filled with just the
+ * strings/bervals of all the entry components. Each field is preceded
+ * by its length, encoded the way ber_put_len works. Every field is NUL
+ * terminated. The entire buffer size is precomputed so that a single
+ * malloc can be performed. The entry size is also recorded,
+ * to aid in entry_decode.
+ */
+int entry_encode(Entry *e, struct berval *bv)
+{
+ ber_len_t siz = sizeof(Entry);
+ ber_len_t len, dnlen, ndnlen;
+ int i;
+ Attribute *a;
+ unsigned char *ptr;
+
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "entry_encode: id: 0x%08lx \"%s\"\n",
+ (long) e->e_id, e->e_dn ));
+#else
+ Debug( LDAP_DEBUG_TRACE, "=> entry_encode(0x%08lx): %s\n",
+ (long) e->e_id, e->e_dn, 0 );
+#endif
+ dnlen = e->e_name.bv_len;
+ ndnlen = e->e_nname.bv_len;
+ len = dnlen + ndnlen + 2; /* two trailing NUL bytes */
+ len += entry_lenlen(dnlen);
+ len += entry_lenlen(ndnlen);
+ for (a=e->e_attrs; a; a=a->a_next) {
+ /* For AttributeDesc, we only store the attr name */
+ siz += sizeof(Attribute);
+ len += a->a_desc->ad_cname.bv_len+1;
+ len += entry_lenlen(a->a_desc->ad_cname.bv_len);
+ for (i=0; a->a_vals[i].bv_val; i++) {
+ siz += sizeof(struct berval);
+ len += a->a_vals[i].bv_len + 1;
+ len += entry_lenlen(a->a_vals[i].bv_len);
+ }
+ len += entry_lenlen(i);
+ siz += sizeof(struct berval); /* empty berval at end */
+ }
+ len += 1; /* NUL byte at end */
+ len += entry_lenlen(siz);
+ bv->bv_len = len;
+ bv->bv_val = ch_malloc(len);
+ ptr = (unsigned char *)bv->bv_val;
+ entry_putlen(&ptr, siz);
+ entry_putlen(&ptr, dnlen);
+ AC_MEMCPY(ptr, e->e_dn, dnlen);
+ ptr += dnlen;
+ *ptr++ = '\0';
+ entry_putlen(&ptr, ndnlen);
+ AC_MEMCPY(ptr, e->e_ndn, ndnlen);
+ ptr += ndnlen;
+ *ptr++ = '\0';
+
+ for (a=e->e_attrs; a; a=a->a_next) {
+ entry_putlen(&ptr, a->a_desc->ad_cname.bv_len);
+ AC_MEMCPY(ptr, a->a_desc->ad_cname.bv_val,
+ a->a_desc->ad_cname.bv_len);
+ ptr += a->a_desc->ad_cname.bv_len;
+ *ptr++ = '\0';
+ if (a->a_vals) {
+ for (i=0; a->a_vals[i].bv_val; i++);
+ entry_putlen(&ptr, i);
+ for (i=0; a->a_vals[i].bv_val; i++) {
+ entry_putlen(&ptr, a->a_vals[i].bv_len);
+ memcpy(ptr, a->a_vals[i].bv_val,
+ a->a_vals[i].bv_len);
+ ptr += a->a_vals[i].bv_len;
+ *ptr++ = '\0';
+ }
+ }
+ }
+ *ptr = '\0';
+ return 0;
+}
+
+/* Retrieve an Entry that was stored using entry_encode above.
+ * We malloc a single block with the size stored above for the Entry
+ * and all if its Attributes. We also must lookup the stored
+ * attribute names to get AttributeDescriptions. To detect if the
+ * attributes of an Entry are later modified, we note that e->e_attr
+ * is always a constant offset from (e).
+ *
+ * Note: everything is stored in a single contiguous block, so
+ * you can not free individual attributes or names from this
+ * structure. Attempting to do so will likely corrupt memory.
+ */
+int entry_decode(struct berval *bv, Entry **e)
+{
+ int i, j;
+ int rc;
+ Attribute *a;
+ Entry *x;
+ const char *text;
+ AttributeDescription *ad;
+ unsigned char *ptr = (unsigned char *)bv->bv_val;
+ BerVarray bptr;
+
+ i = entry_getlen(&ptr);
+ x = ch_calloc(1, i);
+ i = entry_getlen(&ptr);
+ x->e_name.bv_val = ptr;
+ x->e_name.bv_len = i;
+ ptr += i+1;
+ i = entry_getlen(&ptr);
+ x->e_nname.bv_val = ptr;
+ x->e_nname.bv_len = i;
+ ptr += i+1;
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL2,
+ "entry_decode: \"%s\"\n", x->e_dn ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "entry_decode: \"%s\"\n",
+ x->e_dn, 0, 0 );
+#endif
+ x->e_bv = *bv;
+
+ /* A valid entry must have at least one attr, so this
+ * pointer can never be NULL
+ */
+ x->e_attrs = (Attribute *)(x+1);
+ bptr = (BerVarray)x->e_attrs;
+ a = NULL;
+
+ while (i = entry_getlen(&ptr)) {
+ struct berval bv;
+ bv.bv_len = i;
+ bv.bv_val = ptr;
+ if (a) {
+ a->a_next = (Attribute *)bptr;
+ }
+ a = (Attribute *)bptr;
+ ad = NULL;
+ rc = slap_bv2ad( &bv, &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+ "entry_decode: str2ad(%s): %s\n", ptr, text ));
+#else
+ Debug( LDAP_DEBUG_TRACE,
+ "<= entry_decode: str2ad(%s): %s\n", ptr, text, 0 );
+#endif
+ rc = slap_bv2undef_ad( &bv, &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
+ "entry_decode: str2undef_ad(%s): %s\n", ptr, text));
+#else
+ Debug( LDAP_DEBUG_ANY,
+ "<= entry_decode: str2undef_ad(%s): %s\n",
+ ptr, text, 0 );
+#endif
+ return rc;
+ }
+ }
+ ptr += i + 1;
+ a->a_desc = ad;
+ bptr = (BerVarray)(a+1);
+ a->a_vals = bptr;
+ a->a_flags = 0;
+ j = entry_getlen(&ptr);
+
+ while (j) {
+ i = entry_getlen(&ptr);
+ bptr->bv_len = i;
+ bptr->bv_val = (char *)ptr;
+ ptr += i+1;
+ bptr++;
+ j--;
+ }
+ bptr->bv_val = NULL;
+ bptr->bv_len = 0;
+ bptr++;
+ }
+ if (a)
+ a->a_next = NULL;
+#ifdef NEW_LOGGING
+ LDAP_LOG(( "operation", LDAP_LEVEL_DETAIL1,
+ "entry_decode: %s\n", x->e_dn ));
+#else
+ Debug(LDAP_DEBUG_TRACE, "<= entry_decode(%s)\n",
+ x->e_dn, 0, 0 );
+#endif
+ *e = x;
+ return 0;
+}
+#endif
--- /dev/null
+/* $OpenLDAP$ */
+/*
+ * Copyright 2000-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "sets.h"
+
+static BerVarray set_join (BerVarray lset, int op, BerVarray rset);
+static BerVarray set_chase (SLAP_SET_GATHER gatherer,
+ void *cookie, BerVarray set, struct berval *attr, int closure);
+static int set_samedn (char *dn1, char *dn2);
+
+long
+slap_set_size (BerVarray set)
+{
+ int i;
+
+ i = 0;
+ if (set != NULL) {
+ while (set[i].bv_val)
+ i++;
+ }
+ return(i);
+}
+
+void
+slap_set_dispose (BerVarray set)
+{
+ ber_bvarray_free(set);
+}
+
+static BerVarray
+set_join (BerVarray lset, int op, BerVarray rset)
+{
+ BerVarray set;
+ long i, j, last;
+
+ set = NULL;
+ if (op == '|') {
+ if (lset == NULL || lset->bv_val == NULL) {
+ if (rset == NULL) {
+ if (lset == NULL)
+ return(ch_calloc(1, sizeof(struct berval)));
+ return(lset);
+ }
+ slap_set_dispose(lset);
+ return(rset);
+ }
+ if (rset == NULL || rset->bv_val == NULL) {
+ slap_set_dispose(rset);
+ return(lset);
+ }
+
+ i = slap_set_size(lset) + slap_set_size(rset) + 1;
+ set = ch_calloc(i, sizeof(struct berval));
+ if (set != NULL) {
+ /* set_chase() depends on this routine to
+ * keep the first elements of the result
+ * set the same (and in the same order)
+ * as the left-set.
+ */
+ for (i = 0; lset[i].bv_val; i++)
+ set[i] = lset[i];
+ ch_free(lset);
+ for (i = 0; rset[i].bv_val; i++) {
+ for (j = 0; set[j].bv_val; j++) {
+ if (set_samedn(rset[i].bv_val, set[j].bv_val)) {
+ ch_free(rset[i].bv_val);
+ rset[i].bv_val = NULL;
+ break;
+ }
+ }
+ if (rset[i].bv_val)
+ set[j] = rset[i];
+ }
+ ch_free(rset);
+ }
+ return(set);
+ }
+
+ if (op == '&') {
+ if (lset == NULL || lset->bv_val == NULL || rset == NULL || rset->bv_val == NULL) {
+ set = ch_calloc(1, sizeof(struct berval));
+ } else {
+ set = lset;
+ lset = NULL;
+ last = slap_set_size(set) - 1;
+ for (i = 0; set[i].bv_val; i++) {
+ for (j = 0; rset[j].bv_val; j++) {
+ if (set_samedn(set[i].bv_val, rset[j].bv_val))
+ break;
+ }
+ if (rset[j].bv_val == NULL) {
+ ch_free(set[i].bv_val);
+ set[i] = set[last];
+ set[last].bv_val = NULL;
+ last--;
+ i--;
+ }
+ }
+ }
+ }
+
+ slap_set_dispose(lset);
+ slap_set_dispose(rset);
+ return(set);
+}
+
+static BerVarray
+set_chase (SLAP_SET_GATHER gatherer,
+ void *cookie, BerVarray set, struct berval *attr, int closure)
+{
+ BerVarray vals, nset;
+ char attrstr[32];
+ struct berval bv;
+ int i;
+
+ bv.bv_len = attr->bv_len;
+ bv.bv_val = attrstr;
+
+ if (set == NULL)
+ return(ch_calloc(1, sizeof(struct berval)));
+
+ if (set->bv_val == NULL)
+ return(set);
+
+ if (attr->bv_len > (sizeof(attrstr) - 1)) {
+ slap_set_dispose(set);
+ return(NULL);
+ }
+ AC_MEMCPY(attrstr, attr->bv_val, attr->bv_len);
+ attrstr[attr->bv_len] = 0;
+
+ nset = ch_calloc(1, sizeof(struct berval));
+ if (nset == NULL) {
+ slap_set_dispose(set);
+ return(NULL);
+ }
+ for (i = 0; set[i].bv_val; i++) {
+ vals = (gatherer)(cookie, &set[i], &bv);
+ if (vals != NULL)
+ nset = set_join(nset, '|', vals);
+ }
+ slap_set_dispose(set);
+
+ if (closure) {
+ for (i = 0; nset[i].bv_val; i++) {
+ vals = (gatherer)(cookie, &nset[i], &bv);
+ if (vals != NULL) {
+ nset = set_join(nset, '|', vals);
+ if (nset == NULL)
+ break;
+ }
+ }
+ }
+ return(nset);
+}
+
+static int
+set_samedn (char *dn1, char *dn2)
+{
+ char c1, c2;
+
+ while (*dn1 == ' ') dn1++;
+ while (*dn2 == ' ') dn2++;
+ while (*dn1 || *dn2) {
+ if (*dn1 != '=' && *dn1 != ','
+ && *dn2 != '=' && *dn2 != ',')
+ {
+ c1 = *dn1++;
+ c2 = *dn2++;
+ if (c1 >= 'a' && c1 <= 'z')
+ c1 -= 'a' - 'A';
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= 'a' - 'A';
+ if (c1 != c2)
+ return(0);
+ } else {
+ while (*dn1 == ' ') dn1++;
+ while (*dn2 == ' ') dn2++;
+ if (*dn1++ != *dn2++)
+ return(0);
+ while (*dn1 == ' ') dn1++;
+ while (*dn2 == ' ') dn2++;
+ }
+ }
+ return(1);
+}
+
+int
+slap_set_filter (SLAP_SET_GATHER gatherer,
+ void *cookie, struct berval *fbv,
+ struct berval *user, struct berval *this, BerVarray *results)
+{
+#define IS_SET(x) ( (long)(x) >= 256 )
+#define IS_OP(x) ( (long)(x) < 256 )
+#define SF_ERROR(x) do { rc = -1; goto _error; } while (0)
+#define SF_TOP() ( (BerVarray)( (stp < 0) ? 0 : stack[stp] ) )
+#define SF_POP() ( (BerVarray)( (stp < 0) ? 0 : stack[stp--] ) )
+#define SF_PUSH(x) do { \
+ if (stp >= 63) SF_ERROR(overflow); \
+ stack[++stp] = (BerVarray)(long)(x); \
+ } while (0)
+
+ BerVarray set, lset;
+ BerVarray stack[64];
+ int len, op, rc, stp;
+ char c, *filter = fbv->bv_val;
+
+ if (results)
+ *results = NULL;
+
+ stp = -1;
+ while ((c = *filter++)) {
+ set = NULL;
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\x0A':
+ case '\x0D':
+ break;
+
+ case '(':
+ if (IS_SET(SF_TOP()))
+ SF_ERROR(syntax);
+ SF_PUSH(c);
+ break;
+
+ case ')':
+ set = SF_POP();
+ if (IS_OP(set))
+ SF_ERROR(syntax);
+ if (SF_TOP() == (void *)'(') {
+ SF_POP();
+ SF_PUSH(set);
+ set = NULL;
+ } else if (IS_OP(SF_TOP())) {
+ op = (long)SF_POP();
+ lset = SF_POP();
+ SF_POP();
+ set = set_join(lset, op, set);
+ if (set == NULL)
+ SF_ERROR(memory);
+ SF_PUSH(set);
+ set = NULL;
+ } else {
+ SF_ERROR(syntax);
+ }
+ break;
+
+ case '&':
+ case '|':
+ set = SF_POP();
+ if (IS_OP(set))
+ SF_ERROR(syntax);
+ if (SF_TOP() == 0 || SF_TOP() == (void *)'(') {
+ SF_PUSH(set);
+ set = NULL;
+ } else if (IS_OP(SF_TOP())) {
+ op = (long)SF_POP();
+ lset = SF_POP();
+ set = set_join(lset, op, set);
+ if (set == NULL)
+ SF_ERROR(memory);
+ SF_PUSH(set);
+ set = NULL;
+ } else {
+ SF_ERROR(syntax);
+ }
+ SF_PUSH(c);
+ break;
+
+ case '[':
+ if ((SF_TOP() == (void *)'/') || IS_SET(SF_TOP()))
+ SF_ERROR(syntax);
+ for ( len = 0;
+ (c = *filter++) && (c != ']');
+ len++)
+ { }
+ if (c == 0)
+ SF_ERROR(syntax);
+
+ set = ch_calloc(2, sizeof(struct berval));
+ if (set == NULL)
+ SF_ERROR(memory);
+ set->bv_val = ch_calloc(len + 1, sizeof(char));
+ if (set->bv_val == NULL)
+ SF_ERROR(memory);
+ AC_MEMCPY(set->bv_val, &filter[-len - 1], len);
+ set->bv_len = len;
+ SF_PUSH(set);
+ set = NULL;
+ break;
+
+ case '-':
+ c = *filter++;
+ if (c != '>')
+ SF_ERROR(syntax);
+ /* fall through to next case */
+
+ case '/':
+ if (IS_OP(SF_TOP()))
+ SF_ERROR(syntax);
+ SF_PUSH('/');
+ break;
+
+ default:
+ if ((c != '_')
+ && (c < 'A' || c > 'Z')
+ && (c < 'a' || c > 'z'))
+ {
+ SF_ERROR(syntax);
+ }
+ filter--;
+ for ( len = 1;
+ (c = filter[len])
+ && ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z'));
+ len++)
+ { }
+ if (len == 4
+ && memcmp("this", filter, len) == 0)
+ {
+ if ((SF_TOP() == (void *)'/') || IS_SET(SF_TOP()))
+ SF_ERROR(syntax);
+ set = ch_calloc(2, sizeof(struct berval));
+ if (set == NULL)
+ SF_ERROR(memory);
+ ber_dupbv( set, this );
+ if (set->bv_val == NULL)
+ SF_ERROR(memory);
+ } else if (len == 4
+ && memcmp("user", filter, len) == 0)
+ {
+ if ((SF_TOP() == (void *)'/') || IS_SET(SF_TOP()))
+ SF_ERROR(syntax);
+ set = ch_calloc(2, sizeof(struct berval));
+ if (set == NULL)
+ SF_ERROR(memory);
+ ber_dupbv( set, user );
+ if (set->bv_val == NULL)
+ SF_ERROR(memory);
+ } else if (SF_TOP() != (void *)'/') {
+ SF_ERROR(syntax);
+ } else {
+ struct berval fb2;
+ SF_POP();
+ fb2.bv_val = filter;
+ fb2.bv_len = len;
+ set = set_chase(gatherer,
+ cookie, SF_POP(), &fb2, c == '*');
+ if (set == NULL)
+ SF_ERROR(memory);
+ if (c == '*')
+ len++;
+ }
+ filter += len;
+ SF_PUSH(set);
+ set = NULL;
+ break;
+ }
+ }
+
+ set = SF_POP();
+ if (IS_OP(set))
+ SF_ERROR(syntax);
+ if (SF_TOP() == 0) {
+
+ } else if (IS_OP(SF_TOP())) {
+ op = (long)SF_POP();
+ lset = SF_POP();
+ set = set_join(lset, op, set);
+ if (set == NULL)
+ SF_ERROR(memory);
+ } else {
+ SF_ERROR(syntax);
+ }
+
+ rc = slap_set_size(set);
+ if (results) {
+ *results = set;
+ set = NULL;
+ }
+
+_error:
+ if (IS_SET(set))
+ slap_set_dispose(set);
+ while ((set = SF_POP())) {
+ if (IS_SET(set))
+ slap_set_dispose(set);
+ }
+ return(rc);
+}
--- /dev/null
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lber.h>
+#include <ldif.h>
+
+#include "slapcommon.h"
+
+int
+main( int argc, char **argv )
+{
+ char *buf = NULL;
+ int lineno;
+ int lmax;
+ int rc = EXIT_SUCCESS;
+
+ const char *text;
+ char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
+ size_t textlen = sizeof textbuf;
+
+ slap_tool_init( "slapadd", SLAPADD, argc, argv );
+
+ if( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !be->be_entry_put )
+ {
+ fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ lmax = 0;
+ lineno = 0;
+
+ if( be->be_entry_open( be, 1 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ while( ldif_read_record( ldiffp, &lineno, &buf, &lmax ) ) {
+ Entry *e = str2entry( buf );
+ struct berval bvtext;
+
+ bvtext.bv_len = textlen;
+ bvtext.bv_val = textbuf;
+
+ if( e == NULL ) {
+ fprintf( stderr, "%s: could not parse entry (line=%d)\n",
+ progname, lineno );
+ rc = EXIT_FAILURE;
+ if( continuemode ) continue;
+ break;
+ }
+
+ /* make sure the DN is not empty */
+ if( !e->e_nname.bv_len ) {
+ fprintf( stderr, "%s: empty dn=\"%s\" (line=%d)\n",
+ progname, e->e_dn, lineno );
+ rc = EXIT_FAILURE;
+ entry_free( e );
+ if( continuemode ) continue;
+ break;
+ }
+
+ /* check backend */
+ if( select_backend( &e->e_nname, is_entry_referral(e), nosubordinates )
+ != be )
+ {
+ fprintf( stderr, "%s: line %d: "
+ "database (%s) not configured to hold \"%s\"\n",
+ progname, lineno,
+ be ? be->be_suffix[0]->bv_val : "<none>",
+ e->e_dn );
+ fprintf( stderr, "%s: line %d: "
+ "database (%s) not configured to hold \"%s\"\n",
+ progname, lineno,
+ be ? be->be_nsuffix[0]->bv_val : "<none>",
+ e->e_ndn );
+ rc = EXIT_FAILURE;
+ entry_free( e );
+ if( continuemode ) continue;
+ break;
+ }
+
+ {
+ Attribute *sc = attr_find( e->e_attrs,
+ slap_schema.si_ad_structuralObjectClass );
+ Attribute *oc = attr_find( e->e_attrs,
+ slap_schema.si_ad_objectClass );
+
+ if( oc == NULL ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+ progname, e->e_dn, lineno,
+ "no objectClass attribute");
+ rc = EXIT_FAILURE;
+ entry_free( e );
+ if( continuemode ) continue;
+ break;
+ }
+
+ if( sc == NULL ) {
+ struct berval vals[2];
+
+ int ret = structural_class( oc->a_vals, vals,
+ NULL, &text, textbuf, textlen );
+
+ if( vals[0].bv_len == 0 ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+ progname, e->e_dn, lineno, text );
+ rc = EXIT_FAILURE;
+ entry_free( e );
+ if( continuemode ) continue;
+ break;
+ }
+
+ vals[1].bv_val = NULL;
+ attr_merge( e, slap_schema.si_ad_structuralObjectClass,
+ vals );
+ }
+ }
+
+ if( global_schemacheck ) {
+ /* check schema */
+
+ rc = entry_schema_check( be, e, NULL, &text, textbuf, textlen );
+
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+ progname, e->e_dn, lineno, text );
+ rc = EXIT_FAILURE;
+ entry_free( e );
+ if( continuemode ) continue;
+ break;
+ }
+ }
+
+ if (!dryrun) {
+ ID id = be->be_entry_put( be, e, &bvtext );
+ if( id == NOID ) {
+ fprintf( stderr, "%s: could not add entry dn=\"%s\" (line=%d): %s\n",
+ progname, e->e_dn, lineno, bvtext.bv_val );
+ rc = EXIT_FAILURE;
+ entry_free( e );
+ if( continuemode ) continue;
+ break;
+ }
+
+ if ( verbose ) {
+ fprintf( stderr, "added: \"%s\" (%08lx)\n",
+ e->e_dn, (long) id );
+ }
+ } else {
+ if ( verbose ) {
+ fprintf( stderr, "(dry) added: \"%s\"\n", e->e_dn );
+ }
+ }
+
+ entry_free( e );
+ }
+
+ ch_free( buf );
+
+ be->be_entry_close( be );
+
+ if( be->be_sync ) {
+ be->be_sync( be );
+ }
+
+ slap_tool_destroy();
+ return rc;
+}