From 64cd7d3346b26ea840bfd328b11e1faae6922116 Mon Sep 17 00:00:00 2001 From: Kurt Zeilenga Date: Wed, 30 Dec 1998 03:35:23 +0000 Subject: [PATCH] Preliminary Fixes for ITS#24, ITS#26, and ldbm_back_add race condition. Resolved deadlock by passing target entry to be_group and using this if dn same as bdn. It might actually be safer to check entry ids instead of dns. Resolved bogus add to cache after failed acl check by deferring cache add until after parent/acl checks have successful been completed. Eliminated race condition caused by concurrent adds of same dn by adding 'li_add_mutex' around the critical section of code (most of ldbm_back_add). This code is preliminary and still needs significant testing. --- servers/slapd/acl.c | 4 +- servers/slapd/back-ldbm/add.c | 80 +++++++++++++++++------------ servers/slapd/back-ldbm/back-ldbm.h | 1 + servers/slapd/back-ldbm/group.c | 37 +++++++++---- servers/slapd/back-ldbm/init.c | 1 + servers/slapd/backend.c | 4 +- servers/slapd/proto-slap.h | 6 ++- servers/slapd/slap.h | 4 +- 8 files changed, 89 insertions(+), 48 deletions(-) diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c index 3afcda9f02..e94bfe3cd1 100644 --- a/servers/slapd/acl.c +++ b/servers/slapd/acl.c @@ -360,7 +360,9 @@ acl_access_allowed( /* see if asker is listed in dnattr */ string_expand(buf, sizeof(buf), b->a_group, edn, matches); - if (be_group(be, buf, odn, b->a_objectclassvalue, b->a_groupattrname) == 0) { + if (be_group(be, e, buf, odn, + b->a_objectclassvalue, b->a_groupattrname) == 0) + { Debug( LDAP_DEBUG_ACL, "<= acl_access_allowed: matched by clause #%d (group) access granted\n", i, 0, 0 ); diff --git a/servers/slapd/back-ldbm/add.c b/servers/slapd/back-ldbm/add.c index 3b3dcee662..801359e27b 100644 --- a/servers/slapd/back-ldbm/add.c +++ b/servers/slapd/back-ldbm/add.c @@ -28,22 +28,22 @@ ldbm_back_add( Debug(LDAP_DEBUG_ARGS, "==> ldbm_back_add: %s\n", dn, 0, 0); + pthread_mutex_lock(&li->li_add_mutex); + if ( ( dn2id( be, dn ) ) != NOID ) { + pthread_mutex_unlock(&li->li_add_mutex); entry_free( e ); free( dn ); send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, "", "" ); return( -1 ); } - /* XXX race condition here til we cache_add_entry_lock below XXX */ - if ( global_schemacheck && oc_schema_check( e ) != 0 ) { + pthread_mutex_unlock(&li->li_add_mutex); + Debug( LDAP_DEBUG_TRACE, "entry failed schema check\n", 0, 0, 0 ); - /* XXX this should be ok, no other thread should have access - * because e hasn't been added to the cache yet - */ entry_free( e ); free( dn ); send_ldap_result( conn, op, LDAP_OBJECT_CLASS_VIOLATION, "", @@ -51,28 +51,6 @@ ldbm_back_add( return( -1 ); } - /* - * Try to add the entry to the cache, assign it a new dnid - * and mark it locked. This should only fail if the entry - * already exists. - */ - - e->e_id = next_id( be ); - if ( cache_add_entry_lock( &li->li_cache, e, ENTRY_STATE_CREATING ) - != 0 ) { - Debug( LDAP_DEBUG_ANY, "cache_add_entry_lock failed\n", 0, 0, - 0 ); - next_id_return( be, e->e_id ); - - /* XXX this should be ok, no other thread should have access - * because e hasn't been added to the cache yet - */ - entry_free( e ); - free( dn ); - send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, "", "" ); - return( -1 ); - } - /* * Get the parent dn and see if the corresponding entry exists. * If the parent does not exist, only allow the "root" user to @@ -85,6 +63,7 @@ ldbm_back_add( /* get entry with reader lock */ if ( (p = dn2entry_r( be, pdn, &matched )) == NULL ) { + pthread_mutex_unlock(&li->li_add_mutex); Debug( LDAP_DEBUG_TRACE, "parent does not exist\n", 0, 0, 0 ); send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, @@ -94,32 +73,62 @@ ldbm_back_add( free( matched ); } - rc = -1; - goto return_results; + entry_free( e ); + free( dn ); + return -1; } if ( ! access_allowed( be, conn, op, p, "children", NULL, - op->o_dn, ACL_WRITE ) ) { + op->o_dn, ACL_WRITE ) ) + { + pthread_mutex_unlock(&li->li_add_mutex); Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0, 0, 0 ); send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, "", "" ); - rc = -1; - goto return_results; + entry_free( e ); + free( dn ); + return -1; } + } else { if ( ! be_isroot( be, op->o_dn ) ) { + pthread_mutex_unlock(&li->li_add_mutex); Debug( LDAP_DEBUG_TRACE, "no parent & not root\n", 0, 0, 0 ); send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, "", "" ); - rc = -1; - goto return_results; + entry_free( e ); + free( dn ); + return -1; } } + /* + * Try to add the entry to the cache, assign it a new dnid + * and mark it locked. This should only fail if the entry + * already exists. + */ + + e->e_id = next_id( be ); + if ( cache_add_entry_lock( &li->li_cache, e, ENTRY_STATE_CREATING ) != 0 ) { + pthread_mutex_unlock(&li->li_add_mutex); + + Debug( LDAP_DEBUG_ANY, "cache_add_entry_lock failed\n", 0, 0, + 0 ); + next_id_return( be, e->e_id ); + + /* XXX this should be ok, no other thread should have access + * because e hasn't been added to the cache yet + */ + entry_free( e ); + free( dn ); + send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, "", "" ); + return( -1 ); + } + /* * add it to the id2children index for the parent */ @@ -192,5 +201,8 @@ return_results:; cache_return_entry_r( &li->li_cache, p ); } + /* it might actually be okay to release this lock sooner */ + pthread_mutex_unlock(&li->li_add_mutex); + return( rc ); } diff --git a/servers/slapd/back-ldbm/back-ldbm.h b/servers/slapd/back-ldbm/back-ldbm.h index c85df11367..7bf2861002 100644 --- a/servers/slapd/back-ldbm/back-ldbm.h +++ b/servers/slapd/back-ldbm/back-ldbm.h @@ -106,6 +106,7 @@ struct attrinfo { struct ldbminfo { ID li_nextid; + pthread_mutex_t li_add_mutex; pthread_mutex_t li_nextid_mutex; int li_mode; char *li_directory; diff --git a/servers/slapd/back-ldbm/group.c b/servers/slapd/back-ldbm/group.c index a889041c25..1b2b2e8581 100644 --- a/servers/slapd/back-ldbm/group.c +++ b/servers/slapd/back-ldbm/group.c @@ -20,6 +20,7 @@ int ldbm_back_group( Backend *be, + Entry *target, char *bdn, char *edn, char *objectclassValue, @@ -28,6 +29,7 @@ ldbm_back_group( { struct ldbminfo *li = (struct ldbminfo *) be->be_private; Entry *e; + char *tdn; char *matched; Attribute *objectClass; Attribute *member; @@ -38,15 +40,30 @@ ldbm_back_group( Debug( LDAP_DEBUG_TRACE, "=> ldbm_back_group: objectClass: %s attrName: %s\n", objectclassValue, groupattrName, 0 ); - /* can we find bdn entry with reader lock */ - if ((e = dn2entry_r(be, bdn, &matched )) == NULL) { - Debug( LDAP_DEBUG_TRACE, "=> ldbm_back_group: cannot find bdn: %s matched: %s\n", bdn, (matched ? matched : ""), 0 ); - if (matched != NULL) - free(matched); - return( 1 ); + tdn = dn_normalize_case( ch_strdup( target->e_dn ) ); + if (strcmp(tdn, bdn) == 0) { + /* we already have a LOCKED copy of the entry */ + e = target; + Debug( LDAP_DEBUG_ARGS, + "=> ldbm_back_group: target is bdn: %s\n", + bdn, 0, 0 ); + } else { + /* can we find bdn entry with reader lock */ + if ((e = dn2entry_r(be, bdn, &matched )) == NULL) { + Debug( LDAP_DEBUG_TRACE, + "=> ldbm_back_group: cannot find bdn: %s matched: %s\n", + bdn, (matched ? matched : ""), 0 ); + if (matched != NULL) + free(matched); + free(tdn); + return( 1 ); + } + Debug( LDAP_DEBUG_ARGS, + "=> ldbm_back_group: found bdn: %s\n", + bdn, 0, 0 ); } + free(tdn); - Debug( LDAP_DEBUG_ARGS, "=> ldbm_back_group: found bdn: %s\n", bdn, 0, 0 ); /* check for deleted */ @@ -90,8 +107,10 @@ ldbm_back_group( } } - /* free entry and reader lock */ - cache_return_entry_r( &li->li_cache, e ); + if( target != e ) { + /* free entry and reader lock */ + cache_return_entry_r( &li->li_cache, e ); + } Debug( LDAP_DEBUG_ARGS, "ldbm_back_group: rc: %d\n", rc, 0, 0 ); return(rc); } diff --git a/servers/slapd/back-ldbm/init.c b/servers/slapd/back-ldbm/init.c index 36ebbc963c..1f322e67bc 100644 --- a/servers/slapd/back-ldbm/init.c +++ b/servers/slapd/back-ldbm/init.c @@ -63,6 +63,7 @@ ldbm_back_init( free( argv[ 1 ] ); /* initialize various mutex locks & condition variables */ + pthread_mutex_init( &li->li_add_mutex, pthread_mutexattr_default ); pthread_mutex_init( &li->li_cache.c_mutex, pthread_mutexattr_default ); pthread_mutex_init( &li->li_nextid_mutex, pthread_mutexattr_default ); pthread_mutex_init( &li->li_dbcache_mutex, pthread_mutexattr_default ); diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index c3b5d33283..445db7bbb1 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -261,6 +261,7 @@ be_unbind( int be_group( Backend *be, + Entry *e, char *bdn, char *edn, char *objectclassValue, @@ -268,7 +269,8 @@ be_group( ) { if (be->be_group) - return(be->be_group(be, bdn, edn, objectclassValue, groupattrName)); + return(be->be_group(be, e, bdn, edn, + objectclassValue, groupattrName)); else return(1); } diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index f213ac202e..1bf95deb2d 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -256,7 +256,8 @@ extern struct acl *global_acl; extern struct objclass *global_oc; extern time_t currenttime; -extern int be_group LDAP_P((Backend *be, char *bdn, char *edn, char *objectclassValue, char *groupattrName)); +extern int be_group LDAP_P((Backend *be, Entry *e, + char *bdn, char *edn, char *objectclassValue, char *groupattrName)); extern void init LDAP_P((void)); extern void be_unbind LDAP_P((Connection *conn, Operation *op)); extern void config_info LDAP_P((Connection *conn, Operation *op)); @@ -295,7 +296,8 @@ extern void ldbm_back_abandon LDAP_P((Backend *be, Connection *c, Operation *o, extern void ldbm_back_config LDAP_P((Backend *be, char *fname, int lineno, int argc, char **argv )); extern void ldbm_back_init LDAP_P((Backend *be)); extern void ldbm_back_close LDAP_P((Backend *be)); -extern int ldbm_back_group LDAP_P((Backend *be, char *bdn, char *edn, char *objectclassValue, char *groupattrName )); +extern int ldbm_back_group LDAP_P((Backend *be, Entry *target, + char *bdn, char *edn, char *objectclassValue, char *groupattrName )); #endif #ifdef SLAPD_PASSWD diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index fbce7eba8e..df7ac5dab6 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -249,7 +249,9 @@ struct backend { void (*be_close) LDAP_P((Backend *be)); #ifdef SLAPD_ACLGROUPS - int (*be_group) LDAP_P((Backend *be, char *bdn, char *edn, char *objectclassValue, char *groupattrName )); + int (*be_group) LDAP_P((Backend *be, Entry *e, + char *bdn, char *edn, + char *objectclassValue, char *groupattrName )); #endif }; -- 2.39.5