Commit 82f3982b authored by Kurt Zeilenga's avatar Kurt Zeilenga
Browse files

Bring in LDBM error handling fixes

parent cf2cf35f
......@@ -6,12 +6,14 @@ OpenLDAP 2.0.12 Engineering
Fixed slapd empty rootdn bug (ITS#1172)
Fixed slapd numericString empty value bug (ITS#1182)
Fixed slapd acl default clause bug (ITS#1187)
Fixed slapd passwd modify replication
Added slapadd already exists check (ITS#1191)
Added slapd modrdn children check (ITS#1053,1192)
Added slapd sb_max_incoming_auth support (ITS#1181)
Added slapd crypt salt format support (ITS#1202)
Updated slapd schema check handling
Updated ldbm to use BerkeleyDB's CDB (ITS#1176)
Updated ldbm error handling
Updated slapd filter checks
Updated ldaptcl API (contrib)
Build environment
......
......@@ -17,8 +17,9 @@ SRCS = main.c daemon.c connection.c search.c filter.c add.c charray.c \
repl.c lock.c controls.c extended.c kerberos.c passwd.c \
schema.c schema_check.c schema_init.c schema_prep.c \
schemaparse.c ad.c at.c mr.c syntax.c oc.c \
monitor.c configinfo.c starttls.c index.c sets.c\
root_dse.c sasl.c module.c suffixalias.c $(@PLAT@_SRCS)
configinfo.c starttls.c index.c sets.c \
root_dse.c sasl.c module.c suffixalias.c mods.c \
$(@PLAT@_SRCS)
OBJS = main.o daemon.o connection.o search.o filter.o add.o charray.o \
attr.o entry.o config.o backend.o result.o operation.o \
......@@ -28,8 +29,9 @@ OBJS = main.o daemon.o connection.o search.o filter.o add.o charray.o \
repl.o lock.o controls.o extended.o kerberos.o passwd.o \
schema.o schema_check.o schema_init.o schema_prep.o \
schemaparse.o ad.o at.o mr.o syntax.o oc.o \
monitor.o configinfo.o starttls.o index.o sets.o\
root_dse.o sasl.o module.o suffixalias.o $(@PLAT@_OBJS)
configinfo.o starttls.o index.o sets.o \
root_dse.o sasl.o module.o suffixalias.o mods.o \
$(@PLAT@_OBJS)
LDAP_INCDIR= ../../include
LDAP_LIBDIR= ../../libraries
......
......@@ -28,7 +28,8 @@ ldbm_back_add(
char *pdn;
Entry *p = NULL;
int rootlock = 0;
int rc;
int rc;
ID id = NOID;
const char *text = NULL;
AttributeDescription *children = slap_schema.si_ad_children;
char textbuf[SLAP_TEXT_BUFLEN];
......@@ -36,12 +37,15 @@ ldbm_back_add(
Debug(LDAP_DEBUG_ARGS, "==> ldbm_back_add: %s\n", e->e_dn, 0, 0);
/* nobody else can add until we lock our parent */
ldap_pvt_thread_mutex_lock(&li->li_add_mutex);
if ( ( dn2id( be, e->e_ndn ) ) != NOID ) {
if ( ( rc = dn2id( be, e->e_ndn, &id ) ) || id != NOID ) {
/* if (rc) something bad happened to ldbm cache */
ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
send_ldap_result( conn, op, LDAP_ALREADY_EXISTS,
send_ldap_result( conn, op,
rc ? LDAP_OPERATIONS_ERROR : LDAP_ALREADY_EXISTS,
NULL, NULL, NULL, NULL );
return( -1 );
}
......@@ -54,6 +58,7 @@ ldbm_back_add(
Debug( LDAP_DEBUG_TRACE, "entry failed schema check: %s\n",
text, 0, 0 );
send_ldap_result( conn, op, rc,
NULL, text, NULL, NULL );
return( -1 );
......@@ -94,8 +99,10 @@ ldbm_back_add(
Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
0, 0, 0 );
send_ldap_result( conn, op, LDAP_REFERRAL,
matched_dn, NULL, refs, NULL );
send_ldap_result( conn, op, LDAP_REFERRAL, matched_dn,
refs == NULL ? "parent does not exist" : "parent is referral",
refs, NULL );
if( matched != NULL ) {
ber_bvecfree( refs );
......@@ -119,6 +126,7 @@ ldbm_back_add(
Debug( LDAP_DEBUG_TRACE, "no write access to parent\n", 0,
0, 0 );
send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
NULL, "no write access to parent", NULL, NULL );
......@@ -135,6 +143,7 @@ ldbm_back_add(
Debug( LDAP_DEBUG_TRACE, "parent is alias\n", 0,
0, 0 );
send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM,
NULL, "parent is an alias", NULL, NULL );
......@@ -153,6 +162,7 @@ ldbm_back_add(
Debug( LDAP_DEBUG_TRACE, "parent is referral\n", 0,
0, 0 );
send_ldap_result( conn, op, LDAP_REFERRAL,
matched_dn, NULL, refs, NULL );
......@@ -175,6 +185,7 @@ ldbm_back_add(
pdn == NULL ? "suffix" : "entry at root",
0, 0 );
send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
NULL, NULL, NULL, NULL );
......@@ -190,9 +201,7 @@ ldbm_back_add(
ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
}
e->e_id = next_id( be );
if( e->e_id == NOID ) {
if ( next_id( be, &e->e_id ) ) {
if( p != NULL) {
/* free parent and writer lock */
cache_return_entry_w( &li->li_cache, p );
......@@ -206,6 +215,7 @@ ldbm_back_add(
Debug( LDAP_DEBUG_ANY, "ldbm_add: next_id failed\n",
0, 0, 0 );
send_ldap_result( conn, op, LDAP_OTHER,
NULL, "next_id add failed", NULL, NULL );
......@@ -231,6 +241,7 @@ ldbm_back_add(
Debug( LDAP_DEBUG_ANY, "cache_add_entry_lock failed\n", 0, 0,
0 );
send_ldap_result( conn, op,
rc > 0 ? LDAP_ALREADY_EXISTS : LDAP_OTHER,
NULL, rc > 0 ? NULL : "cache add failed", NULL, NULL );
......@@ -244,6 +255,7 @@ ldbm_back_add(
if ( index_entry_add( be, e, e->e_attrs ) != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "index_entry_add failed\n", 0,
0, 0 );
send_ldap_result( conn, op, LDAP_OTHER,
NULL, "index generation failed", NULL, NULL );
......@@ -254,6 +266,8 @@ ldbm_back_add(
if ( dn2id_add( be, e->e_ndn, e->e_id ) != 0 ) {
Debug( LDAP_DEBUG_TRACE, "dn2id_add failed\n", 0,
0, 0 );
/* FIXME: delete attr indices? */
send_ldap_result( conn, op, LDAP_OTHER,
NULL, "DN index generation failed", NULL, NULL );
......@@ -264,7 +278,10 @@ ldbm_back_add(
if ( id2entry_add( be, e ) != 0 ) {
Debug( LDAP_DEBUG_TRACE, "id2entry_add failed\n", 0,
0, 0 );
/* FIXME: delete attr indices? */
(void) dn2id_delete( be, e->e_ndn, e->e_id );
send_ldap_result( conn, op, LDAP_OTHER,
NULL, "entry store failed", NULL, NULL );
......@@ -273,7 +290,12 @@ ldbm_back_add(
send_ldap_result( conn, op, LDAP_SUCCESS,
NULL, NULL, NULL, NULL );
/* marks the entry as committed, so it is added to the cache;
* otherwise it is removed from the cache, but not destroyed;
* it will be destroyed by the caller */
rc = 0;
cache_entry_commit( e );
return_results:;
if (p != NULL) {
......@@ -287,7 +309,8 @@ return_results:;
}
if ( rc ) {
/* free entry and writer lock */
/* in case of error, writer lock is freed
* and entry's private data is destroyed */
cache_return_entry_w( &li->li_cache, e );
}
......
......@@ -29,9 +29,10 @@ typedef struct ldbm_entry_info {
int lei_state; /* for the cache */
#define CACHE_ENTRY_UNDEFINED 0
#define CACHE_ENTRY_CREATING 1
#define CACHE_ENTRY_READY 2
#define CACHE_ENTRY_DELETED 3
#define CACHE_ENTRY_READY 2
#define CACHE_ENTRY_DELETED 3
#define CACHE_ENTRY_COMMITTED 4
int lei_refcnt; /* # threads ref'ing this entry */
Entry *lei_lrunext; /* for cache lru list */
Entry *lei_lruprev;
......@@ -113,6 +114,23 @@ cache_entry_private_init( Entry*e )
return 0;
}
/*
* marks an entry in CREATING state as committed, so it is really returned
* to the cache. Otherwise an entry in CREATING state is removed.
* Makes e_private be destroyed at the following cache_return_entry_w,
* but lets the entry untouched (owned by someone else)
*/
void
cache_entry_commit( Entry *e )
{
assert( e );
assert( e->e_private );
assert( LEI(e)->lei_state == CACHE_ENTRY_CREATING );
/* assert( LEI(e)->lei_refcnt == 1 ); */
LEI(e)->lei_state = CACHE_ENTRY_COMMITTED;
}
static int
cache_entry_private_destroy( Entry*e )
{
......@@ -129,7 +147,7 @@ void
cache_return_entry_rw( Cache *cache, Entry *e, int rw )
{
ID id;
int refcnt;
int refcnt, freeit = 1;
/* set cache mutex */
ldap_pvt_thread_mutex_lock( &cache->c_mutex );
......@@ -141,7 +159,18 @@ cache_return_entry_rw( Cache *cache, Entry *e, int rw )
id = e->e_id;
refcnt = --LEI(e)->lei_refcnt;
if ( LEI(e)->lei_state == CACHE_ENTRY_CREATING ) {
/*
* if the entry is returned when in CREATING state, it is deleted
* but not freed because it may belong to someone else (do_add,
* for instance)
*/
if ( LEI(e)->lei_state == CACHE_ENTRY_CREATING ) {
cache_delete_entry_internal( cache, e );
freeit = 0;
/* now the entry is in DELETED state */
}
if ( LEI(e)->lei_state == CACHE_ENTRY_COMMITTED ) {
LEI(e)->lei_state = CACHE_ENTRY_READY;
/* free cache mutex */
......@@ -162,7 +191,9 @@ cache_return_entry_rw( Cache *cache, Entry *e, int rw )
} else {
cache_entry_private_destroy( e );
entry_free( e );
if ( freeit ) {
entry_free( e );
}
/* free cache mutex */
ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
......@@ -439,14 +470,33 @@ cache_find_entry_dn2id(
Cache *cache,
const char *dn
)
{
char *ndn;
ID id;
ndn = ch_strdup( dn );
(void) dn_normalize( ndn );
id = cache_find_entry_ndn2id( be, cache, ndn );
free( ndn );
return ( id );
}
ID
cache_find_entry_ndn2id(
Backend *be,
Cache *cache,
const char *ndn
)
{
Entry e, *ep;
ID id;
int count = 0;
e.e_dn = (char *) dn;
e.e_ndn = ch_strdup( dn );
(void) dn_normalize( e.e_ndn );
/* this function is always called with normalized DN */
e.e_ndn = (char *)ndn;
try_again:
/* set cache mutex */
......@@ -481,7 +531,7 @@ try_again:
Debug(LDAP_DEBUG_TRACE,
"====> cache_find_entry_dn2id(\"%s\"): %ld (not ready) %d\n",
dn, id, state);
ndn, id, state);
ldap_pvt_thread_yield();
goto try_again;
......@@ -496,7 +546,7 @@ try_again:
Debug(LDAP_DEBUG_TRACE,
"====> cache_find_entry_dn2id(\"%s\"): %ld (%d tries)\n",
dn, id, count);
ndn, id, count);
} else {
/* free cache mutex */
......@@ -505,8 +555,6 @@ try_again:
id = NOID;
}
free(e.e_ndn);
return( id );
}
......
......@@ -95,31 +95,38 @@ dn2id_add(
return( rc );
}
ID
int
dn2id(
Backend *be,
const char *dn
const char *dn,
ID *idp
)
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
DBCache *db;
ID id;
Datum key, data;
Debug( LDAP_DEBUG_TRACE, "=> dn2id( \"%s\" )\n", dn, 0, 0 );
assert( idp );
/* first check the cache */
if ( (id = cache_find_entry_dn2id( be, &li->li_cache, dn )) != NOID ) {
Debug( LDAP_DEBUG_TRACE, "<= dn2id %ld (in cache)\n", id,
if ( (*idp = cache_find_entry_ndn2id( be, &li->li_cache, dn )) != NOID ) {
Debug( LDAP_DEBUG_TRACE, "<= dn2id %ld (in cache)\n", *idp,
0, 0 );
return( id );
return( 0 );
}
if ( (db = ldbm_cache_open( be, "dn2id", LDBM_SUFFIX, LDBM_WRCREAT ))
== NULL ) {
Debug( LDAP_DEBUG_ANY, "<= dn2id could not open dn2id%s\n",
LDBM_SUFFIX, 0, 0 );
return( NOID );
/*
* return code !0 if ldbm cache open failed;
* callers should handle this
*/
*idp = NOID;
return( -1 );
}
ldbm_datum_init( key );
......@@ -136,37 +143,42 @@ dn2id(
if ( data.dptr == NULL ) {
Debug( LDAP_DEBUG_TRACE, "<= dn2id NOID\n", 0, 0, 0 );
return( NOID );
*idp = NOID;
return( 0 );
}
AC_MEMCPY( (char *) &id, data.dptr, sizeof(ID) );
AC_MEMCPY( (char *) idp, data.dptr, sizeof(ID) );
assert( id != NOID );
assert( *idp != NOID );
ldbm_datum_free( db->dbc_db, data );
Debug( LDAP_DEBUG_TRACE, "<= dn2id %ld\n", id, 0, 0 );
return( id );
Debug( LDAP_DEBUG_TRACE, "<= dn2id %ld\n", *idp, 0, 0 );
return( 0 );
}
ID_BLOCK *
int
dn2idl(
Backend *be,
const char *dn,
int prefix
int prefix,
ID_BLOCK **idlp
)
{
DBCache *db;
Datum key;
ID_BLOCK *idl;
Debug( LDAP_DEBUG_TRACE, "=> dn2idl( \"%c%s\" )\n", prefix, dn, 0 );
assert( idlp != NULL );
*idlp = NULL;
if ( (db = ldbm_cache_open( be, "dn2id", LDBM_SUFFIX, LDBM_WRCREAT ))
== NULL ) {
Debug( LDAP_DEBUG_ANY, "<= dn2idl could not open dn2id%s\n",
LDBM_SUFFIX, 0, 0 );
return NULL;
return -1;
}
ldbm_datum_init( key );
......@@ -175,13 +187,13 @@ dn2idl(
key.dptr = ch_malloc( key.dsize );
sprintf( key.dptr, "%c%s", prefix, dn );
idl = idl_fetch( be, db, key );
*idlp = idl_fetch( be, db, key );
ldbm_cache_close( be, db );
free( key.dptr );
return( idl );
return( 0 );
}
......@@ -287,13 +299,16 @@ dn2entry_rw(
*matched = NULL;
}
if ( (id = dn2id( be, dn )) != NOID &&
(e = id2entry_rw( be, id, rw )) != NULL )
{
return( e );
}
if ( dn2id( be, dn, &id ) ) {
/* something bad happened to ldbm cache */
return( NULL );
} else if ( id != NOID ) {
/* try to return the entry */
if ((e = id2entry_rw( be, id, rw )) != NULL ) {
return( e );
}
if ( id != NOID ) {
Debug(LDAP_DEBUG_ANY,
"dn2entry_%s: no entry for valid id (%ld), dn \"%s\"\n",
rw ? "w" : "r", id, dn);
......
......@@ -44,12 +44,24 @@ filter_candidates(
switch ( f->f_choice ) {
case SLAPD_FILTER_DN_ONE:
Debug( LDAP_DEBUG_FILTER, "\tDN ONE\n", 0, 0, 0 );
result = dn2idl( be, f->f_dn, DN_ONE_PREFIX );
/* an error is treated as an empty list */
if ( dn2idl( be, f->f_dn, DN_ONE_PREFIX, &result ) != 0
&& result != NULL ) {
idl_free( result );
result = NULL;
}
break;
case SLAPD_FILTER_DN_SUBTREE:
Debug( LDAP_DEBUG_FILTER, "\tDN SUBTREE\n", 0, 0, 0 );
result = dn2idl( be, f->f_dn, DN_SUBTREE_PREFIX );
/* an error is treated as an empty list */
if ( dn2idl( be, f->f_dn, DN_SUBTREE_PREFIX, &result ) != 0
&& result != NULL ) {
idl_free( result );
result = NULL;
}
break;
case LDAP_FILTER_PRESENT:
......
......@@ -176,5 +176,9 @@ id2entry_rw( Backend *be, ID id, int rw )
Debug( LDAP_DEBUG_TRACE, "<= id2entry_%s( %ld ) 0x%lx (disk)\n",
rw ? "w" : "r", id, (unsigned long) e );
/* marks the entry as committed, so it will get added to the cache
* when the lock is released */
cache_entry_commit( e );
return( e );
}
......@@ -65,10 +65,14 @@ ID_BLOCK *
idl_allids( Backend *be )
{
ID_BLOCK *idl;
ID id;
idl = idl_alloc( 0 );
ID_BLOCK_NMAX(idl) = ID_BLOCK_ALLIDS_VALUE;
ID_BLOCK_NIDS(idl) = next_id_get( be );
if ( next_id_get( be, &id ) ) {
return NULL;
}
ID_BLOCK_NIDS(idl) = id;
return( idl );
}
......
......@@ -51,27 +51,25 @@ ldbm_back_modrdn(
Entry *e, *p = NULL;
Entry *matched;
int rootlock = 0;
int rc = -1;
int rc = -1, rc_id = 0;
ID id = NOID;
const char *text = NULL;
char textbuf[SLAP_TEXT_BUFLEN];
size_t textlen = sizeof textbuf;
/* Added to support LDAP v2 correctly (deleteoldrdn thing) */
char *new_rdn_val = NULL; /* Val of new rdn */
char *new_rdn_type = NULL; /* Type of new rdn */
char *old_rdn = NULL; /* Old rdn's attr type & val */
char *old_rdn_type = NULL; /* Type of old rdn attr. */
char *old_rdn_val = NULL; /* Old rdn attribute value */
char **new_rdn_vals = NULL; /* Vals of new rdn */
char **new_rdn_types = NULL; /* Types of new rdn */
int a_cnt, d_cnt;
char *old_rdn = NULL; /* Old rdn's attr type & val */
char **old_rdn_types = NULL; /* Types of old rdn attrs. */
char **old_rdn_vals = NULL; /* Old rdn attribute values */
/* Added to support newSuperior */
Entry *np = NULL; /* newSuperior Entry */
char *np_dn = NULL; /* newSuperior dn */
char *np_dn = NULL; /* newSuperior dn */
char *np_ndn = NULL; /* newSuperior ndn */
char *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
/* Used to interface with ldbm_modify_internal() */
struct berval add_bv; /* Stores new rdn att */
struct berval *add_bvals[2]; /* Stores new rdn att */
struct berval del_bv; /* Stores old rdn att */
struct berval *del_bvals[2]; /* Stores old rdn att */
Modifications mod[2]; /* Used to delete old rdn */
Modifications *mod = NULL; /* Used to delete old/add new rdn */
int manageDSAit = get_manageDSAit( op );
Debug( LDAP_DEBUG_TRACE, "==>ldbm_back_modrdn(newSuperior=%s)\n",
......@@ -121,7 +119,7 @@ ldbm_back_modrdn(
}
if ( has_children( be, e ) ) {
Debug( LDAP_DEBUG_TRACE, "entry %s referral\n", 0,
Debug( LDAP_DEBUG_TRACE, "entry %s referral\n", e->e_dn,
0, 0 );
send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF,
......@@ -129,8 +127,7 @@ ldbm_back_modrdn(
goto return_results;
}
if ( (p_ndn = dn_parent( be, e->e_ndn )) != NULL ) {
if ( (p_ndn = dn_parent( be, e->e_ndn )) != NULL && p_ndn[0] != '\0' ) {
/* Make sure parent entry exist and we can write its
* children.
*/
......@@ -138,6 +135,7 @@ ldbm_back_modrdn(
if( (p = dn2entry_w( be, p_ndn, NULL )) == NULL) {
Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
0, 0, 0);
send_ldap_result( conn, op, LDAP_OTHER,
NULL, "parent entry does not exist", NULL, NULL );
......@@ -150,6 +148,7 @@ ldbm_back_modrdn(
{
Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
0, 0 );
send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
NULL, NULL, NULL, NULL );
goto return_results;
......@@ -158,18 +157,18 @@ ldbm_back_modrdn(
Debug( LDAP_DEBUG_TRACE,
"ldbm_back_modrdn: wr to children of entry %s OK\n",
p_ndn, 0, 0 );
p_dn = dn_parent( be, e->e_dn );
Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: parent dn=%s\n",
p_dn, 0, 0 );
} else {
/* no parent, modrdn entry directly under root */
if( ! be_isroot( be, op->o_ndn ) ) {
if( ! be_isroot( be, op->o_ndn ) && ! be_issuffix( be, "" ) ) {
Debug( LDAP_DEBUG_TRACE, "no parent & not root\n",
0, 0, 0);
send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
NULL, NULL, NULL, NULL );
goto return_results;
......@@ -181,7 +180,6 @@ ldbm_back_modrdn(
Debug( LDAP_DEBUG_TRACE,
"ldbm_back_modrdn: no parent, locked root\n",
0, 0, 0 );
}