From c8c043bd39eb7921a1498eeb9f9ed0def2c94c1d Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Sat, 15 Jan 2005 18:43:34 +0000 Subject: [PATCH] address ITS#3472/3480/3488/3489 --- servers/slapd/back-sql/add.c | 231 +++++++++++++++------------ servers/slapd/back-sql/back-sql.h | 10 ++ servers/slapd/back-sql/bind.c | 44 ++--- servers/slapd/back-sql/compare.c | 87 +++++----- servers/slapd/back-sql/delete.c | 155 +++++++++++++++--- servers/slapd/back-sql/entry-id.c | 91 ++++++++--- servers/slapd/back-sql/modify.c | 20 +-- servers/slapd/back-sql/modrdn.c | 6 +- servers/slapd/back-sql/operational.c | 2 +- servers/slapd/back-sql/proto-sql.h | 10 +- servers/slapd/back-sql/search.c | 162 +++++++++++++------ 11 files changed, 530 insertions(+), 288 deletions(-) diff --git a/servers/slapd/back-sql/add.c b/servers/slapd/back-sql/add.c index 051503848f..c57d64e1f9 100644 --- a/servers/slapd/back-sql/add.c +++ b/servers/slapd/back-sql/add.c @@ -265,7 +265,7 @@ backsql_modify_delete_all_values( /* SQL procedure executed fine * but returned an error */ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc ); - rs->sr_text = op->oq_add.rs_e->e_name.bv_val; + rs->sr_text = op->ora_e->e_name.bv_val; SQLFreeStmt( sth, SQL_DROP ); return rs->sr_err; @@ -275,7 +275,7 @@ backsql_modify_delete_all_values( if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { rs->sr_err = LDAP_OTHER; - rs->sr_text = op->oq_add.rs_e->e_name.bv_val; + rs->sr_text = op->ora_e->e_name.bv_val; SQLFreeStmt( sth, SQL_DROP ); return rs->sr_err; } @@ -767,7 +767,7 @@ backsql_add_attr( Debug( LDAP_DEBUG_TRACE, " backsql_add_attr(\"%s\"): " "attribute \"%s\" is not registered " "in objectclass \"%s\"\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, at->a_desc->ad_cname.bv_val, BACKSQL_OC_NAME( oc ) ); @@ -785,7 +785,7 @@ backsql_add_attr( "add procedure is not defined " "for attribute \"%s\" " "of structuralObjectClass \"%s\"\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, at->a_desc->ad_cname.bv_val, BACKSQL_OC_NAME( oc ) ); @@ -905,7 +905,7 @@ backsql_add_attr( i, new_keyval ); Debug( LDAP_DEBUG_TRACE, " backsql_add_attr(\"%s\"): " "executing \"%s\" %s\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, at_rec->bam_add_proc, logbuf ); #endif rc = SQLExecute( sth ); @@ -916,12 +916,12 @@ backsql_add_attr( Debug( LDAP_DEBUG_TRACE, " backsql_add_attr(\"%s\"): " "add_proc execution failed (rc=%d, prc=%d)\n", - op->oq_add.rs_e->e_name.bv_val, rc, prc ); + op->ora_e->e_name.bv_val, rc, prc ); if ( prc != LDAP_SUCCESS ) { /* SQL procedure executed fine * but returned an error */ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc ); - rs->sr_text = op->oq_add.rs_e->e_name.bv_val; + rs->sr_text = op->ora_e->e_name.bv_val; SQLFreeStmt( sth, SQL_DROP ); return rs->sr_err; @@ -930,7 +930,7 @@ backsql_add_attr( sth, rc ); if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { rs->sr_err = LDAP_OTHER; - rs->sr_text = op->oq_add.rs_e->e_name.bv_val; + rs->sr_text = op->ora_e->e_name.bv_val; SQLFreeStmt( sth, SQL_DROP ); return rs->sr_err; } @@ -951,13 +951,15 @@ backsql_add( Operation *op, SlapReply *rs ) unsigned long new_keyval = 0; RETCODE rc; backsql_oc_map_rec *oc = NULL; + backsql_srch_info bsi; backsql_entryID parent_id = BACKSQL_ENTRYID_INIT; - Entry p; + Entry p = { 0 }, *e = NULL; Attribute *at, *at_objectClass = NULL; struct berval pdn; struct berval realdn = BER_BVNULL; int colnum; + slap_mask_t mask; #ifdef BACKSQL_SYNCPROV /* @@ -979,25 +981,26 @@ backsql_add( Operation *op, SlapReply *rs ) #endif /* BACKSQL_SYNCPROV */ Debug( LDAP_DEBUG_TRACE, "==>backsql_add(\"%s\")\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); /* check schema */ if ( global_schemacheck ) { char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' }; - rs->sr_err = entry_schema_check( op->o_bd, op->oq_add.rs_e, + rs->sr_err = entry_schema_check( op->o_bd, op->ora_e, NULL, &rs->sr_text, textbuf, sizeof( textbuf ) ); if ( rs->sr_err != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "entry failed schema check -- aborting\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); + e = NULL; goto done; } } /* search structural objectClass */ - for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) { + for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) { if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) { break; } @@ -1012,10 +1015,11 @@ backsql_add( Operation *op, SlapReply *rs ) if ( oc == NULL ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "cannot map structuralObjectClass \"%s\" -- aborting\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, at->a_vals[0].bv_val, 0 ); rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "operation not permitted within namingContext"; + e = NULL; goto done; } @@ -1023,10 +1027,11 @@ backsql_add( Operation *op, SlapReply *rs ) Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "create procedure is not defined " "for structuralObjectClass \"%s\" - aborting\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, at->a_vals[0].bv_val, 0 ); rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "operation not permitted within namingContext"; + e = NULL; goto done; } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) @@ -1035,10 +1040,11 @@ backsql_add( Operation *op, SlapReply *rs ) "create procedure needs select procedure, " "but none is defined for structuralObjectClass \"%s\" " "- aborting\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, at->a_vals[0].bv_val, 0 ); rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "operation not permitted within namingContext"; + e = NULL; goto done; } @@ -1046,9 +1052,10 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rs->sr_err != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "could not get connection handle - exiting\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); rs->sr_text = ( rs->sr_err == LDAP_OTHER ) ? "SQL-backend error" : NULL; + e = NULL; goto done; } @@ -1058,13 +1065,14 @@ backsql_add( Operation *op, SlapReply *rs ) * NOTE: backsql_api_dn2odbc() is called explicitly because * we need the mucked DN to pass it to the create procedure. */ - realdn = op->oq_add.rs_e->e_name; + realdn = op->ora_e->e_name; if ( backsql_api_dn2odbc( op, rs, &realdn ) ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "backsql_api_dn2odbc(\"%s\") failed\n", - op->oq_add.rs_e->e_name.bv_val, realdn.bv_val, 0 ); + op->ora_e->e_name.bv_val, realdn.bv_val, 0 ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1072,93 +1080,57 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rs->sr_err == LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "entry exists\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); rs->sr_err = LDAP_ALREADY_EXISTS; + e = op->ora_e; goto done; } /* * Get the parent dn and see if the corresponding entry exists. */ - if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) { + if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) { pdn = slap_empty_bv; } else { - dnParent( &op->oq_add.rs_e->e_nname, &pdn ); + dnParent( &op->ora_e->e_nname, &pdn ); } - rs->sr_err = backsql_dn2id( op, rs, dbh, &pdn, &parent_id, 0, 1 ); + /* + * Get the parent + */ + bsi.bsi_e = &p; + rs->sr_err = backsql_init_search( &bsi, &pdn, + LDAP_SCOPE_BASE, + SLAP_NO_LIMIT, SLAP_NO_LIMIT, + (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs, + ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); if ( rs->sr_err != LDAP_SUCCESS ) { - Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " - "could not lookup parent entry for new record \"%s\"\n", - op->oq_add.rs_e->e_name.bv_val, pdn.bv_val, 0 ); - - if ( rs->sr_err != LDAP_NO_SUCH_OBJECT ) { - goto done; - } - - /* - * no parent! - * if not attempting to add entry at suffix or with parent "" - */ - if ( ( ( !be_isroot( op ) && !be_shadow_update( op ) ) - || !BER_BVISEMPTY( &pdn ) ) && !is_entry_glue( op->oq_add.rs_e ) - && !BACKSQL_ALLOW_ORPHANS( bi ) ) - { - Debug( LDAP_DEBUG_TRACE, " backsql_add: %s denied\n", - BER_BVISEMPTY( &pdn ) ? "suffix" : "entry at root", - 0, 0 ); - /* - * Look for matched - */ - while ( 1 ) { - struct berval dn; - char *matched = NULL; - - dn = pdn; - dnParent( &dn, &pdn ); - - /* - * Empty DN ("") defaults to LDAP_SUCCESS - */ - rs->sr_err = backsql_dn2id( op, rs, dbh, &pdn, NULL, 0, 1 ); - switch ( rs->sr_err ) { - case LDAP_NO_SUCH_OBJECT: - if ( !BER_BVISEMPTY( &pdn ) ) { - break; - } - /* fail over to next case */ - - case LDAP_SUCCESS: - matched = pdn.bv_val; - /* fail over to next case */ - - default: - rs->sr_err = LDAP_NO_SUCH_OBJECT; - rs->sr_matched = matched; - goto done; - } - } - } else { - -#ifdef BACKSQL_ARBITRARY_KEY - ber_str2bv( "SUFFIX", 0, 1, &parent_id.eid_id ); -#else /* ! BACKSQL_ARBITRARY_KEY */ - parent_id.eid_id = 0; -#endif /* ! BACKSQL_ARBITRARY_KEY */ - rs->sr_err = LDAP_SUCCESS; - } + Debug( LDAP_DEBUG_TRACE, "backsql_add(): " + "could not retrieve addDN parent " + "\"%s\" ID - %s matched=\"%s\"\n", + pdn.bv_val, + rs->sr_err == LDAP_REFERRAL ? "referral" : "no such entry", + rs->sr_matched ? rs->sr_matched : "(null)" ); + e = &p; + goto done; } /* check "children" pseudo-attribute access to parent */ - p.e_attrs = NULL; - p.e_name = pdn; - dnParent( &op->oq_add.rs_e->e_nname, &p.e_nname ); - - /* FIXME: need the whole entry (ITS#3480) */ if ( !access_allowed( op, &p, slap_schema.si_ad_children, - NULL, ACL_WRITE, NULL ) ) { + NULL, ACL_WRITE, NULL ) ) + { rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + e = &p; + goto done; + } + + if ( !access_allowed_mask( op, op->ora_e, + slap_schema.si_ad_entry, + NULL, ACL_WRITE, NULL, &mask ) ) + { + rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + e = op->ora_e; goto done; } @@ -1168,11 +1140,11 @@ backsql_add( Operation *op, SlapReply *rs ) * the id of the added row; otherwise the procedure * is expected to return the id as the first column of a select */ - rc = SQLAllocStmt( dbh, &sth ); if ( rc != SQL_SUCCESS ) { rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1190,13 +1162,14 @@ backsql_add( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } colnum++; } if ( oc->bom_create_hint ) { - at = attr_find( op->oq_add.rs_e->e_attrs, oc->bom_create_hint ); + at = attr_find( op->ora_e->e_attrs, oc->bom_create_hint ); if ( at && at->a_vals ) { backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT, at->a_vals[0].bv_val, @@ -1216,16 +1189,17 @@ backsql_add( Operation *op, SlapReply *rs ) } Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): executing \"%s\"\n", - op->oq_add.rs_e->e_name.bv_val, oc->bom_create_proc, 0 ); + op->ora_e->e_name.bv_val, oc->bom_create_proc, 0 ); rc = SQLExecDirect( sth, oc->bom_create_proc, SQL_NTS ); if ( rc != SQL_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "create_proc execution failed\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc); SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1243,6 +1217,7 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rc != SQL_SUCCESS ) { rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1250,6 +1225,7 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rc != SQL_SUCCESS ) { rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } } @@ -1262,21 +1238,23 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rc != SQL_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "create_proc result evaluation failed\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc); SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } else if ( ncols != 1 ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "create_proc result is bogus (ncols=%d)\n", - op->oq_add.rs_e->e_name.bv_val, ncols, 0 ); + op->ora_e->e_name.bv_val, ncols, 0 ); backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc); SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1308,11 +1286,12 @@ backsql_add( Operation *op, SlapReply *rs ) if ( value_len <= 0 ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "create_proc result is empty?\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc); SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } } @@ -1321,9 +1300,9 @@ backsql_add( Operation *op, SlapReply *rs ) Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "create_proc returned keyval=%ld\n", - op->oq_add.rs_e->e_name.bv_val, new_keyval, 0 ); + op->ora_e->e_name.bv_val, new_keyval, 0 ); - for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) { + for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(): " "adding attribute \"%s\"\n", at->a_desc->ad_cname.bv_val, 0, 0 ); @@ -1346,6 +1325,7 @@ backsql_add( Operation *op, SlapReply *rs ) rs->sr_err = backsql_add_attr( op, rs, dbh, oc, at, new_keyval ); if ( rs->sr_err != LDAP_SUCCESS ) { + e = op->ora_e; goto done; } } @@ -1354,6 +1334,7 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rc != SQL_SUCCESS ) { rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1369,6 +1350,7 @@ backsql_add( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -1384,6 +1366,7 @@ backsql_add( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -1399,6 +1382,7 @@ backsql_add( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -1414,11 +1398,12 @@ backsql_add( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } Debug( LDAP_DEBUG_TRACE, " backsql_add(): executing \"%s\" for dn \"%s\"\n", - bi->sql_insentry_stmt, op->oq_add.rs_e->e_name.bv_val, 0 ); + bi->sql_insentry_stmt, op->ora_e->e_name.bv_val, 0 ); #ifdef BACKSQL_ARBITRARY_KEY Debug( LDAP_DEBUG_TRACE, " for oc_map_id=%ld, " "parent_id=%s, keyval=%ld\n", @@ -1432,7 +1417,7 @@ backsql_add( Operation *op, SlapReply *rs ) if ( rc != SQL_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): " "could not insert ldap_entries record\n", - op->oq_add.rs_e->e_name.bv_val, 0, 0 ); + op->ora_e->e_name.bv_val, 0, 0 ); backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); /* @@ -1441,6 +1426,7 @@ backsql_add( Operation *op, SlapReply *rs ) SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -1448,6 +1434,7 @@ backsql_add( Operation *op, SlapReply *rs ) if ( at_objectClass ) { rs->sr_err = backsql_add_attr( op, rs, dbh, oc, at_objectClass, new_keyval ); if ( rs->sr_err != LDAP_SUCCESS ) { + e = op->ora_e; goto done; } } @@ -1476,22 +1463,58 @@ done:; * in deleting that row. */ +#ifdef SLAP_ACL_HONOR_DISCLOSE + if ( e != NULL ) { + int disclose = 1; + + if ( e == op->ora_e && !ACL_GRANT( mask, ACL_DISCLOSE ) ) { + /* mask already collected */ + disclose = 0; + + } else if ( e == &p && !access_allowed( op, &p, + slap_schema.si_ad_entry, NULL, + ACL_DISCLOSE, NULL ) ) + { + disclose = 0; + } + + if ( disclose == 0 ) { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + rs->sr_text = NULL; + rs->sr_matched = NULL; + if ( rs->sr_ref ) { + ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; + } + } + } +#endif /* SLAP_ACL_HONOR_DISCLOSE */ + send_ldap_result( op, rs ); if ( !BER_BVISNULL( &realdn ) - && realdn.bv_val != op->oq_add.rs_e->e_name.bv_val ) + && realdn.bv_val != op->ora_e->e_name.bv_val ) { ch_free( realdn.bv_val ); } - if ( !BER_BVISNULL( &parent_id.eid_ndn ) ) { - (void)backsql_free_entryID( &parent_id, 0 ); + (void)backsql_free_entryID( op, &parent_id, 0 ); + + if ( !BER_BVISNULL( &p.e_nname ) ) { + entry_clean( &p ); } Debug( LDAP_DEBUG_TRACE, "<==backsql_add(\"%s\"): %d \"%s\"\n", - op->oq_add.rs_e->e_name.bv_val, + op->ora_e->e_name.bv_val, rs->sr_err, rs->sr_text ? rs->sr_text : "" ); - return ( ( rs->sr_err == LDAP_SUCCESS ) ? op->o_noop : 1 ); + rs->sr_text = NULL; + rs->sr_matched = NULL; + if ( rs->sr_ref ) { + ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; + } + + return rs->sr_err; } diff --git a/servers/slapd/back-sql/back-sql.h b/servers/slapd/back-sql/back-sql.h index e3dad04a34..fb285c0c5e 100644 --- a/servers/slapd/back-sql/back-sql.h +++ b/servers/slapd/back-sql/back-sql.h @@ -375,6 +375,16 @@ typedef struct berbuf { #define BB_NULL { BER_BVNULL, 0 } +/* the function must collect the entry associated to nbase */ +#define BACKSQL_ISF_GET_ID 0x1U +#define BACKSQL_ISF_GET_ENTRY ( 0x2U | BACKSQL_ISF_GET_ID ) +#define BACKSQL_ISF_MATCHED 0x4U +#define BACKSQL_IS_GET_ID(f) \ + ( ( (f) & BACKSQL_ISF_GET_ID ) == BACKSQL_ISF_GET_ID ) +#define BACKSQL_IS_GET_ENTRY(f) \ + ( ( (f) & BACKSQL_ISF_GET_ENTRY ) == BACKSQL_ISF_GET_ENTRY ) +#define BACKSQL_IS_MATCHED(f) \ + ( ( (f) & BACKSQL_ISF_MATCHED ) == BACKSQL_ISF_MATCHED ) typedef struct backsql_srch_info { Operation *bsi_op; SlapReply *bsi_rs; diff --git a/servers/slapd/back-sql/bind.c b/servers/slapd/back-sql/bind.c index 96c06fdd1e..3ce75c1f08 100644 --- a/servers/slapd/back-sql/bind.c +++ b/servers/slapd/back-sql/bind.c @@ -32,8 +32,7 @@ int backsql_bind( Operation *op, SlapReply *rs ) { SQLHDBC dbh = SQL_NULL_HDBC; - Entry *e = NULL, - user_entry = { 0 }; + Entry e = { 0 }; Attribute *a; backsql_srch_info bsi; AttributeName anlist[2]; @@ -45,7 +44,7 @@ backsql_bind( Operation *op, SlapReply *rs ) ber_dupbv( &op->oq_bind.rb_edn, be_root_dn( op->o_bd ) ); Debug( LDAP_DEBUG_TRACE, "<==backsql_bind() root bind\n", 0, 0, 0 ); - return 0; + return LDAP_SUCCESS; } ber_dupbv( &op->oq_bind.rb_edn, &op->o_req_ndn ); @@ -54,7 +53,7 @@ backsql_bind( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED; rs->sr_text = "authentication method not supported"; send_ldap_result( op, rs ); - return 1; + return rs->sr_err; } /* @@ -68,45 +67,33 @@ backsql_bind( Operation *op, SlapReply *rs ) rs->sr_text = ( rs->sr_err == LDAP_OTHER ) ? "SQL-backend error" : NULL; - send_ldap_result( op, rs ); - return 1; + goto error_return; } anlist[0].an_name = slap_schema.si_ad_userPassword->ad_cname; anlist[0].an_desc = slap_schema.si_ad_userPassword; anlist[1].an_name.bv_val = NULL; + bsi.bsi_e = &e; rc = backsql_init_search( &bsi, &op->o_req_ndn, LDAP_SCOPE_BASE, SLAP_NO_LIMIT, SLAP_NO_LIMIT, (time_t)(-1), NULL, dbh, op, rs, anlist, - BACKSQL_ISF_GET_ID ); + BACKSQL_ISF_GET_ENTRY ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "backsql_bind(): " "could not retrieve bindDN ID - no such entry\n", 0, 0, 0 ); rs->sr_err = LDAP_INVALID_CREDENTIALS; - send_ldap_result( op, rs ); - return 1; - } - - bsi.bsi_e = &user_entry; - rc = backsql_id2entry( &bsi, &bsi.bsi_base_id ); - if ( rc != LDAP_SUCCESS ) { - Debug( LDAP_DEBUG_TRACE, "backsql_bind(): " - "error %d in backsql_id2entry() " - "- auth failed\n", rc, 0, 0 ); - rs->sr_err = LDAP_INVALID_CREDENTIALS; goto error_return; } - e = &user_entry; - a = attr_find( e->e_attrs, slap_schema.si_ad_userPassword ); + a = attr_find( e.e_attrs, slap_schema.si_ad_userPassword ); if ( a == NULL ) { rs->sr_err = LDAP_INVALID_CREDENTIALS; goto error_return; } - if ( slap_passwd_check( op, e, a, &op->oq_bind.rb_cred, + if ( slap_passwd_check( op, &e, a, &op->oq_bind.rb_cred, &rs->sr_text ) != 0 ) { rs->sr_err = LDAP_INVALID_CREDENTIALS; @@ -114,25 +101,22 @@ backsql_bind( Operation *op, SlapReply *rs ) } error_return:; - if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { - (void)backsql_free_entryID( &bsi.bsi_base_id, 0 ); - } + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); - if ( e != NULL ) { - entry_clean( e ); + if ( bsi.bsi_e ) { + entry_clean( bsi.bsi_e ); } if ( bsi.bsi_attrs != NULL ) { op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); } - if ( rs->sr_err ) { + if ( rs->sr_err != LDAP_SUCCESS ) { send_ldap_result( op, rs ); - return 1; } - Debug(LDAP_DEBUG_TRACE,"<==backsql_bind()\n",0,0,0); + Debug( LDAP_DEBUG_TRACE,"<==backsql_bind()\n", 0, 0, 0 ); - return 0; + return rs->sr_err; } diff --git a/servers/slapd/back-sql/compare.c b/servers/slapd/back-sql/compare.c index ac23f9e6c1..d339009fb6 100644 --- a/servers/slapd/back-sql/compare.c +++ b/servers/slapd/back-sql/compare.c @@ -32,21 +32,17 @@ int backsql_compare( Operation *op, SlapReply *rs ) { SQLHDBC dbh = SQL_NULL_HDBC; - Entry *e = NULL, user_entry; + Entry e = { 0 }; Attribute *a = NULL; backsql_srch_info bsi; int rc; AttributeName anlist[2], *anlistp = NULL; - BER_BVZERO( &user_entry.e_name ); - BER_BVZERO( &user_entry.e_nname ); - user_entry.e_attrs = NULL; - Debug( LDAP_DEBUG_TRACE, "==>backsql_compare()\n", 0, 0, 0 ); rs->sr_err = backsql_get_db_conn( op, &dbh ); - if (!dbh) { + if ( !dbh ) { Debug( LDAP_DEBUG_TRACE, "backsql_compare(): " "could not get connection handle - exiting\n", 0, 0, 0 ); @@ -56,9 +52,9 @@ backsql_compare( Operation *op, SlapReply *rs ) goto return_results; } - memset( &anlist[0], 0, 2 * sizeof( AttributeName ) ); - anlist[0].an_name = op->oq_compare.rs_ava->aa_desc->ad_cname; - anlist[0].an_desc = op->oq_compare.rs_ava->aa_desc; + anlist[ 0 ].an_name = op->oq_compare.rs_ava->aa_desc->ad_cname; + anlist[ 0 ].an_desc = op->oq_compare.rs_ava->aa_desc; + BER_BVZERO( &anlist[ 1 ].an_name ); /* * Try to get attr as dynamic operational @@ -68,30 +64,30 @@ backsql_compare( Operation *op, SlapReply *rs ) } /* - * FIXME: deal with matchedDN/referral? + * Get the entry */ + bsi.bsi_e = &e; rc = backsql_init_search( &bsi, &op->o_req_ndn, LDAP_SCOPE_BASE, SLAP_NO_LIMIT, SLAP_NO_LIMIT, (time_t)(-1), NULL, dbh, op, rs, anlistp, - BACKSQL_ISF_GET_ID ); + ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "backsql_compare(): " "could not retrieve compareDN ID - no such entry\n", 0, 0, 0 ); - rs->sr_err = LDAP_NO_SUCH_OBJECT; goto return_results; } if ( is_at_operational( op->oq_compare.rs_ava->aa_desc->ad_type ) ) { SlapReply nrs = { 0 }; - user_entry.e_attrs = NULL; - user_entry.e_name = bsi.bsi_base_id.eid_dn; - user_entry.e_nname = bsi.bsi_base_id.eid_ndn; + e.e_attrs = NULL; + ber_dupbv( &e.e_name, &bsi.bsi_base_id.eid_dn ); + ber_dupbv( &e.e_nname, &bsi.bsi_base_id.eid_ndn ); nrs.sr_attrs = anlist; - nrs.sr_entry = &user_entry; + nrs.sr_entry = &e; nrs.sr_attr_flags = SLAP_OPATTRS_NO; nrs.sr_operational_attrs = NULL; @@ -100,10 +96,9 @@ backsql_compare( Operation *op, SlapReply *rs ) goto return_results; } - user_entry.e_attrs = nrs.sr_operational_attrs; + e.e_attrs = nrs.sr_operational_attrs; } else { - bsi.bsi_e = &user_entry; rc = backsql_id2entry( &bsi, &bsi.bsi_base_id ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "backsql_compare(): " @@ -113,25 +108,17 @@ backsql_compare( Operation *op, SlapReply *rs ) goto return_results; } } - e = &user_entry; - if ( ! access_allowed( op, e, op->oq_compare.rs_ava->aa_desc, + if ( ! access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc, &op->oq_compare.rs_ava->aa_value, - ACL_COMPARE, NULL ) ) { -#ifdef SLAP_ACL_HONOR_DISCLOSE - if ( ! access_allowed( op, &e, slap_schema.si_ad_entry, NULL, - ACL_DISCLOSE, NULL ) ) { - rs->sr_err = LDAP_NO_SUCH_OBJECT; - } else -#endif /* SLAP_ACL_HONOR_DISCLOSE */ - { - rs->sr_err = LDAP_INSUFFICIENT_ACCESS; - } + ACL_COMPARE, NULL ) ) + { + rs->sr_err = LDAP_INSUFFICIENT_ACCESS; goto return_results; } rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE; - for ( a = attrs_find( e->e_attrs, op->oq_compare.rs_ava->aa_desc ); + for ( a = attrs_find( e.e_attrs, op->oq_compare.rs_ava->aa_desc ); a != NULL; a = attrs_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) ) { @@ -149,14 +136,40 @@ backsql_compare( Operation *op, SlapReply *rs ) } return_results:; + switch ( rs->sr_err ) { + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + break; + + default: +#ifdef SLAP_ACL_HONOR_DISCLOSE + if ( !BER_BVISNULL( &e.e_nname ) && + ! access_allowed( op, &e, + slap_schema.si_ad_entry, NULL, + ACL_DISCLOSE, NULL ) ) + { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + rs->sr_text = NULL; + } +#endif /* SLAP_ACL_HONOR_DISCLOSE */ + break; + } + send_ldap_result( op, rs ); - if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { - (void)backsql_free_entryID( &bsi.bsi_base_id, 0 ); + if ( rs->sr_matched ) { + rs->sr_matched = NULL; } - if ( e != NULL ) { - entry_clean( e ); + if ( rs->sr_ref ) { + ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; + } + + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); + + if ( bsi.bsi_e ) { + entry_clean( bsi.bsi_e ); } if ( bsi.bsi_attrs != NULL ) { @@ -167,10 +180,10 @@ return_results:; switch ( rs->sr_err ) { case LDAP_COMPARE_TRUE: case LDAP_COMPARE_FALSE: - return 0; + return LDAP_SUCCESS; default: - return 1; + return rs->sr_err; } } diff --git a/servers/slapd/back-sql/delete.c b/servers/slapd/back-sql/delete.c index 20bab6dcc5..c07ee3400b 100644 --- a/servers/slapd/back-sql/delete.c +++ b/servers/slapd/back-sql/delete.c @@ -87,8 +87,11 @@ backsql_delete( Operation *op, SlapReply *rs ) RETCODE rc; int prc = LDAP_SUCCESS; backsql_oc_map_rec *oc = NULL; - backsql_entryID e_id = BACKSQL_ENTRYID_INIT; - Entry e; + backsql_srch_info bsi; + backsql_entryID e_id = { 0 }; + Entry d = { 0 }, p = { 0 }, *e = NULL; + struct berval pdn = BER_BVNULL; + int manageDSAit = get_manageDSAit( op ); /* first parameter no */ SQLUSMALLINT pno; SQLUSMALLINT CompletionType = SQL_ROLLBACK; @@ -96,22 +99,6 @@ backsql_delete( Operation *op, SlapReply *rs ) Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry \"%s\"\n", op->o_req_ndn.bv_val, 0, 0 ); - dnParent( &op->o_req_dn, &e.e_name ); - dnParent( &op->o_req_ndn, &e.e_nname ); - e.e_attrs = NULL; - - /* check parent for "children" acl */ - /* FIXME: need the whole entry (ITS#3480) */ - if ( !access_allowed( op, &e, slap_schema.si_ad_children, - NULL, ACL_WRITE, NULL ) ) { - Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " - "no write access to parent\n", - 0, 0, 0 ); - rs->sr_err = LDAP_INSUFFICIENT_ACCESS; - goto done; - - } - rs->sr_err = backsql_get_db_conn( op, &dbh ); if ( rs->sr_err != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " @@ -119,13 +106,55 @@ backsql_delete( Operation *op, SlapReply *rs ) 0, 0, 0 ); rs->sr_text = ( rs->sr_err == LDAP_OTHER ) ? "SQL-backend error" : NULL; + e = NULL; goto done; } - rs->sr_err = backsql_dn2id( op, rs, dbh, &op->o_req_ndn, &e_id, 0, 1 ); - if ( rs->sr_err != LDAP_SUCCESS ) { + /* + * Get the entry + */ + bsi.bsi_e = &d; + rc = backsql_init_search( &bsi, &op->o_req_ndn, + LDAP_SCOPE_BASE, + SLAP_NO_LIMIT, SLAP_NO_LIMIT, + (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs, + ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); + switch ( rc ) { + case LDAP_SUCCESS: + break; + + case LDAP_REFERRAL: + if ( !BER_BVISNULL( &bsi.bsi_e->e_nname ) && + dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) + && manageDSAit ) + { + rs->sr_err = LDAP_SUCCESS; + rs->sr_text = NULL; + rs->sr_matched = NULL; + if ( rs->sr_ref ) { + ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; + } + break; + } + e = &d; + /* fallthru */ + + default: + Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " + "could not retrieve deleteDN ID - no such entry\n", + 0, 0, 0 ); + goto done; + } + + if ( !access_allowed( op, &d, slap_schema.si_ad_entry, + NULL, ACL_WRITE, NULL ) ) + { Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " - "could not lookup entry id\n", 0, 0, 0 ); + "no write access to entry\n", + 0, 0, 0 ); + rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + e = &d; goto done; } @@ -144,16 +173,18 @@ backsql_delete( Operation *op, SlapReply *rs ) /* fallthru */ default: + e = &d; goto done; } - oc = backsql_id2oc( bi, e_id.eid_oc_id ); + oc = backsql_id2oc( bi, bsi.bsi_base_id.eid_oc_id ); if ( oc == NULL ) { Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " "cannot determine objectclass of entry -- aborting\n", 0, 0, 0 ); rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "operation not permitted within namingContext"; + e = NULL; goto done; } @@ -163,12 +194,48 @@ backsql_delete( Operation *op, SlapReply *rs ) "for this objectclass - aborting\n", 0, 0, 0 ); rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "operation not permitted within namingContext"; + e = NULL; goto done; } + /* + * Get the parent + */ + dnParent( &op->o_req_ndn, &pdn ); + bsi.bsi_e = &p; + e_id = bsi.bsi_base_id; + rc = backsql_init_search( &bsi, &pdn, + LDAP_SCOPE_BASE, + SLAP_NO_LIMIT, SLAP_NO_LIMIT, + (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs, + BACKSQL_ISF_GET_ENTRY ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " + "could not retrieve deleteDN ID - no such entry\n", + 0, 0, 0 ); + e = &p; + goto done; + } + + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); + + /* check parent for "children" acl */ + if ( !access_allowed( op, &p, slap_schema.si_ad_children, + NULL, ACL_WRITE, NULL ) ) + { + Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " + "no write access to parent\n", + 0, 0, 0 ); + rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + e = &p; + goto done; + + } + /* avl_apply ... */ rs->sr_err = backsql_delete_all_attrs( op, rs, dbh, &e_id, oc ); if ( rs->sr_err != LDAP_SUCCESS ) { + e = &d; goto done; } @@ -182,6 +249,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -199,6 +267,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -218,6 +287,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -242,6 +312,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_OTHER; } SQLFreeStmt( sth, SQL_DROP ); + e = &d; goto done; } SQLFreeStmt( sth, SQL_DROP ); @@ -257,6 +328,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -273,6 +345,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -292,6 +365,7 @@ backsql_delete( Operation *op, SlapReply *rs ) SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } SQLFreeStmt( sth, SQL_DROP ); @@ -307,6 +381,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -323,6 +398,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -342,6 +418,7 @@ backsql_delete( Operation *op, SlapReply *rs ) SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } SQLFreeStmt( sth, SQL_DROP ); @@ -357,6 +434,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } @@ -373,6 +451,7 @@ backsql_delete( Operation *op, SlapReply *rs ) rs->sr_text = "SQL-backend error"; rs->sr_err = LDAP_OTHER; + e = NULL; goto done; } @@ -385,6 +464,7 @@ backsql_delete( Operation *op, SlapReply *rs ) SQLFreeStmt( sth, SQL_DROP ); rs->sr_err = LDAP_OTHER; rs->sr_text = "SQL-backend error"; + e = NULL; goto done; } SQLFreeStmt( sth, SQL_DROP ); @@ -407,10 +487,39 @@ done:; } SQLTransact( SQL_NULL_HENV, dbh, CompletionType ); +#ifdef SLAP_ACL_HONOR_DISCLOSE + if ( e != NULL ) { + if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL, + ACL_DISCLOSE, NULL ) ) + { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + rs->sr_text = NULL; + rs->sr_matched = NULL; + if ( rs->sr_ref ) { + ber_bvarray_free( rs->sr_ref ); + rs->sr_ref = NULL; + } + } + } + +#endif /* SLAP_ACL_HONOR_DISCLOSE */ + send_ldap_result( op, rs ); Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n", 0, 0, 0 ); - return ( ( rs->sr_err == LDAP_SUCCESS ) ? op->o_noop : 1 ); + if ( !BER_BVISNULL( &e_id.eid_ndn ) ) { + (void)backsql_free_entryID( op, &e_id, 0 ); + } + + if ( !BER_BVISNULL( &d.e_nname ) ) { + entry_clean( &d ); + } + + if ( !BER_BVISNULL( &p.e_nname ) ) { + entry_clean( &p ); + } + + return rs->sr_err; } diff --git a/servers/slapd/back-sql/entry-id.c b/servers/slapd/back-sql/entry-id.c index 03e19aa295..18dbba5fd8 100644 --- a/servers/slapd/back-sql/entry-id.c +++ b/servers/slapd/back-sql/entry-id.c @@ -35,7 +35,7 @@ struct berval backsql_baseObject_bv = BER_BVC( BACKSQL_BASEOBJECT_IDSTR ); #endif /* BACKSQL_ARBITRARY_KEY */ backsql_entryID * -backsql_free_entryID( backsql_entryID *id, int freeit ) +backsql_free_entryID( Operation *op, backsql_entryID *id, int freeit ) { backsql_entryID *next; @@ -47,28 +47,28 @@ backsql_free_entryID( backsql_entryID *id, int freeit ) if ( !BER_BVISNULL( &id->eid_dn ) && id->eid_dn.bv_val != id->eid_ndn.bv_val ) { - free( id->eid_dn.bv_val ); + op->o_tmpfree( id->eid_dn.bv_val, op->o_tmpmemctx ); BER_BVZERO( &id->eid_dn ); } - free( id->eid_ndn.bv_val ); + op->o_tmpfree( id->eid_ndn.bv_val, op->o_tmpmemctx ); BER_BVZERO( &id->eid_ndn ); } #ifdef BACKSQL_ARBITRARY_KEY - if ( id->eid_id.bv_val ) { - free( id->eid_id.bv_val ); + if ( !BER_BVISNULL( &id->eid_id ) ) { + op->o_tmpfree( id->eid_id.bv_val, op->o_tmpmemctx ); BER_BVZERO( &id->eid_id ); } - if ( id->eid_keyval.bv_val ) { - free( id->eid_keyval.bv_val ); + if ( !BER_BVISNULL( &id->eid_keyval ) ) { + op->o_tmpfree( id->eid_keyval.bv_val, op->o_tmpmemctx ); BER_BVZERO( &id->eid_keyval ); } #endif /* BACKSQL_ARBITRARY_KEY */ if ( freeit ) { - free( id ); + op->o_tmpfree( id, op->o_tmpmemctx ); } return next; @@ -110,6 +110,11 @@ backsql_dn2id( ndn->bv_val, id == NULL ? " (no ID expected)" : "", matched ? " matched expected" : "" ); + if ( id ) { + /* NOTE: trap inconsistencies */ + assert( BER_BVISNULL( &id->eid_ndn ) ); + } + if ( ndn->bv_len > BACKSQL_MAX_DN_LEN ) { Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): DN length=%ld " @@ -125,16 +130,20 @@ backsql_dn2id( { if ( id != NULL ) { #ifdef BACKSQL_ARBITRARY_KEY - ber_dupbv( &id->eid_id, &backsql_baseObject_bv ); - ber_dupbv( &id->eid_keyval, &backsql_baseObject_bv ); + ber_dupbv_x( &id->eid_id, &backsql_baseObject_bv, + op->o_tmpmemctx ); + ber_dupbv_x( &id->eid_keyval, &backsql_baseObject_bv, + op->o_tmpmemctx ); #else /* ! BACKSQL_ARBITRARY_KEY */ id->eid_id = BACKSQL_BASEOBJECT_ID; id->eid_keyval = BACKSQL_BASEOBJECT_KEYVAL; #endif /* ! BACKSQL_ARBITRARY_KEY */ id->eid_oc_id = BACKSQL_BASEOBJECT_OC; - ber_dupbv( &id->eid_ndn, &bi->sql_baseObject->e_nname ); - ber_dupbv( &id->eid_dn, &bi->sql_baseObject->e_name ); + ber_dupbv_x( &id->eid_ndn, &bi->sql_baseObject->e_nname, + op->o_tmpmemctx ); + ber_dupbv_x( &id->eid_dn, &bi->sql_baseObject->e_name, + op->o_tmpmemctx ); id->eid_next = NULL; } @@ -242,8 +251,10 @@ backsql_dn2id( struct berval dn; #ifdef BACKSQL_ARBITRARY_KEY - ber_str2bv( row.cols[ 0 ], 0, 1, &id->eid_id ); - ber_str2bv( row.cols[ 1 ], 0, 1, &id->eid_keyval ); + ber_str2bv_x( row.cols[ 0 ], 0, 1, &id->eid_id, + op->o_tmpmemctx ); + ber_str2bv_x( row.cols[ 1 ], 0, 1, &id->eid_keyval, + op->o_tmpmemctx ); #else /* ! BACKSQL_ARBITRARY_KEY */ id->eid_id = strtol( row.cols[ 0 ], NULL, 0 ); id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 ); @@ -256,7 +267,9 @@ backsql_dn2id( res = LDAP_OTHER; } else { - res = dnPrettyNormal( NULL, &dn, &id->eid_dn, &id->eid_ndn, NULL ); + res = dnPrettyNormal( NULL, &dn, + &id->eid_dn, &id->eid_ndn, + op->o_tmpmemctx ); if ( res != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): " @@ -265,7 +278,7 @@ backsql_dn2id( ldap_err2string( res ) ); /* cleanup... */ - (void)backsql_free_entryID( id, 0 ); + (void)backsql_free_entryID( op, id, 0 ); } if ( dn.bv_val != row.cols[ 3 ] ) { @@ -278,6 +291,40 @@ backsql_dn2id( } else { res = LDAP_NO_SUCH_OBJECT; + if ( matched ) { + struct berval pdn = *ndn; + + /* + * Look for matched + */ + rs->sr_matched = NULL; + while ( !be_issuffix( op->o_bd, &pdn ) ) { + struct berval dn; + char *matchedDN = NULL; + + dn = pdn; + dnParent( &dn, &pdn ); + + /* + * Empty DN ("") defaults to LDAP_SUCCESS + */ + rs->sr_err = backsql_dn2id( op, rs, dbh, &pdn, id, 0, 1 ); + switch ( rs->sr_err ) { + case LDAP_NO_SUCH_OBJECT: + /* try another one */ + break; + + case LDAP_SUCCESS: + matchedDN = pdn.bv_val; + /* fail over to next case */ + + default: + rs->sr_err = LDAP_NO_SUCH_OBJECT; + rs->sr_matched = matchedDN; + goto done; + } + } + } } backsql_FreeRow( &row ); @@ -511,7 +558,8 @@ backsql_get_attr_vals( void *v_at, void *v_bsi ) int backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid ) { - backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; + Operation *op = bsi->bsi_op; + backsql_info *bi = (backsql_info *)op->o_bd->be_private; int i; int rc; @@ -534,8 +582,8 @@ backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid ) goto done; } - ber_dupbv( &bsi->bsi_e->e_name, &eid->eid_dn ); - ber_dupbv( &bsi->bsi_e->e_nname, &eid->eid_ndn ); + ber_dupbv_x( &bsi->bsi_e->e_name, &eid->eid_dn, op->o_tmpmemctx ); + ber_dupbv_x( &bsi->bsi_e->e_nname, &eid->eid_ndn, op->o_tmpmemctx ); bsi->bsi_e->e_attrs = NULL; bsi->bsi_e->e_private = NULL; @@ -545,6 +593,7 @@ backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid ) bsi->bsi_c_eid = eid; #ifndef BACKSQL_ARBITRARY_KEY + /* FIXME: unused */ bsi->bsi_e->e_id = eid->eid_id; #endif /* ! BACKSQL_ARBITRARY_KEY */ @@ -567,7 +616,7 @@ backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid ) } else { Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): " "custom attribute list\n", 0, 0, 0 ); - for ( i = 0; bsi->bsi_attrs[ i ].an_name.bv_val; i++ ) { + for ( i = 0; !BER_BVISNULL( &bsi->bsi_attrs[ i ].an_name ); i++ ) { backsql_at_map_rec **vat; AttributeName *an = &bsi->bsi_attrs[ i ]; int j; @@ -577,7 +626,7 @@ backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid ) * because subtypes are already dealt with * by backsql_supad2at() */ - for ( j = 0; bsi->bsi_attrs[ j ].an_name.bv_val; j++ ) { + for ( j = 0; !BER_BVISNULL( &bsi->bsi_attrs[ j ].an_name ); j++ ) { /* skip self */ if ( j == i ) { continue; diff --git a/servers/slapd/back-sql/modify.c b/servers/slapd/back-sql/modify.c index 2f4199d853..4ec90b2c3a 100644 --- a/servers/slapd/back-sql/modify.c +++ b/servers/slapd/back-sql/modify.c @@ -60,27 +60,19 @@ backsql_modify( Operation *op, SlapReply *rs ) goto done; } - /* FIXME: using all attributes because of access control later ... */ + bsi.bsi_e = &e; rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn, LDAP_SCOPE_BASE, SLAP_NO_LIMIT, SLAP_NO_LIMIT, (time_t)(-1), NULL, dbh, op, rs, slap_anlist_all_attributes, - BACKSQL_ISF_GET_ID ); + BACKSQL_ISF_GET_ENTRY ); if ( rs->sr_err != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " "could not retrieve modifyDN ID - no such entry\n", 0, 0, 0 ); - rs->sr_err = LDAP_NO_SUCH_OBJECT; - goto done; - } - - bsi.bsi_e = &e; - rs->sr_err = backsql_id2entry( &bsi, &bsi.bsi_base_id ); - if ( rs->sr_err != LDAP_SUCCESS ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " - "error %d in backsql_id2entry()\n", - rs->sr_err, 0, 0 ); + /* FIXME: we keep the error code + * set by backsql_init_search() */ goto done; } @@ -134,9 +126,7 @@ backsql_modify( Operation *op, SlapReply *rs ) done:; send_ldap_result( op, rs ); - if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { - (void)backsql_free_entryID( &bsi.bsi_base_id, 0 ); - } + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); if ( bsi.bsi_e != NULL ) { entry_clean( bsi.bsi_e ); diff --git a/servers/slapd/back-sql/modrdn.c b/servers/slapd/back-sql/modrdn.c index d48ccd14f8..046a726b12 100644 --- a/servers/slapd/back-sql/modrdn.c +++ b/servers/slapd/back-sql/modrdn.c @@ -223,7 +223,7 @@ backsql_modrdn( Operation *op, SlapReply *rs ) "old parent entry id is %ld\n", pe_id.eid_id, 0, 0 ); #endif /* ! BACKSQL_ARBITRARY_KEY */ - (void)backsql_free_entryID( &pe_id, 0 ); + (void)backsql_free_entryID( op, &pe_id, 0 ); rs->sr_err = backsql_dn2id( op, rs, dbh, new_npdn, &new_pe_id, 0, 1 ); if ( rs->sr_err != LDAP_SUCCESS ) { @@ -481,9 +481,7 @@ modrdn_return:; } } - if ( !BER_BVISNULL( &new_pe_id.eid_ndn ) ) { - (void)backsql_free_entryID( &new_pe_id, 0 ); - } + (void)backsql_free_entryID( op, &new_pe_id, 0 ); send_ldap_result( op, rs ); diff --git a/servers/slapd/back-sql/operational.c b/servers/slapd/back-sql/operational.c index dbc1d88b68..d997703d98 100644 --- a/servers/slapd/back-sql/operational.c +++ b/servers/slapd/back-sql/operational.c @@ -196,7 +196,7 @@ backsql_operational( *ap = backsql_operational_entryUUID( bi, &bsi.bsi_base_id ); - (void)backsql_free_entryID( &bsi.bsi_base_id, 0 ); + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); if ( bsi.bsi_attrs != NULL ) { op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); } diff --git a/servers/slapd/back-sql/proto-sql.h b/servers/slapd/back-sql/proto-sql.h index 75b2634941..86657d5b4f 100644 --- a/servers/slapd/back-sql/proto-sql.h +++ b/servers/slapd/back-sql/proto-sql.h @@ -124,7 +124,8 @@ int backsql_count_children( backsql_info *bi, SQLHDBC dbh, int backsql_has_children( backsql_info *bi, SQLHDBC dbh, struct berval *dn ); /* frees *id and returns next in list */ -backsql_entryID *backsql_free_entryID( backsql_entryID *id, int freeit ); +backsql_entryID *backsql_free_entryID( Operation *op, backsql_entryID *id, + int freeit ); /* turns an ID into an entry */ int backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *id ); @@ -162,13 +163,6 @@ int backsql_destroy_schema_map( backsql_info *si ); * search.c */ -/* the function must collect the entry associated to nbase */ -#define BACKSQL_ISF_GET_ID 0x1U -#define BACKSQL_ISF_MATCHED 0x2U -#define BACKSQL_IS_GET_ID(f) \ - ( ( (f) & BACKSQL_ISF_GET_ID ) == BACKSQL_ISF_GET_ID ) -#define BACKSQL_IS_MATCHED(f) \ - ( ( (f) & BACKSQL_ISF_MATCHED ) == BACKSQL_ISF_MATCHED ) int backsql_init_search( backsql_srch_info *bsi, struct berval *nbase, int scope, int slimit, int tlimit, time_t stoptime, Filter *filter, SQLHDBC dbh, diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c index 0f8ac3fa19..e501d1b3bc 100644 --- a/servers/slapd/back-sql/search.c +++ b/servers/slapd/back-sql/search.c @@ -269,10 +269,50 @@ backsql_init_search( bsi->bsi_filter_oc = NULL; if ( BACKSQL_IS_GET_ID( flags ) ) { + int matched = BACKSQL_IS_MATCHED( flags ); + int getentry = BACKSQL_IS_GET_ENTRY( flags ); + assert( op->o_bd->be_private ); rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id, - BACKSQL_IS_MATCHED( flags ), 1 ); + matched, 1 ); + + if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) { + if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) { + assert( bsi->bsi_e != NULL ); + + /* + * let's see if it is a referral and, in case, get it + */ + backsql_attrlist_add( bsi, slap_schema.si_ad_ref ); + rc = backsql_id2entry( bsi, &bsi->bsi_base_id ); + if ( rc == LDAP_SUCCESS && is_entry_referral( bsi->bsi_e ) ) + { + BerVarray erefs = get_entry_referrals( op, bsi->bsi_e ); + if ( erefs ) { + rc = rs->sr_err = LDAP_REFERRAL; + rs->sr_ref = referral_rewrite( erefs, + &bsi->bsi_e->e_nname, + &op->o_req_dn, + scope ); + ber_bvarray_free( erefs ); + + } else { + rc = rs->sr_err = LDAP_OTHER; + rs->sr_text = "bad referral object"; + } + + } else { + rc = rs->sr_err = getentry ? + LDAP_SUCCESS : LDAP_NO_SUCH_OBJECT; + } + + } else { + rs->sr_ref = referral_rewrite( default_referral, + NULL, &op->o_req_dn, scope ); + rc = rs->sr_err = LDAP_REFERRAL; + } + } } bsi->bsi_status = rc; @@ -1397,6 +1437,7 @@ backsql_oc_get_candidates( void *v_oc, void *v_bsi ) { backsql_oc_map_rec *oc = v_oc; backsql_srch_info *bsi = v_bsi; + Operation *op = bsi->bsi_op; backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; struct berval query; SQLHSTMT sth = SQL_NULL_HSTMT; @@ -1666,7 +1707,7 @@ backsql_oc_get_candidates( void *v_oc, void *v_bsi ) continue; } - ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, NULL ); + ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); if ( dn.bv_val != row.cols[ 3 ] ) { free( dn.bv_val ); } @@ -1676,16 +1717,18 @@ backsql_oc_get_candidates( void *v_oc, void *v_bsi ) } if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) { - free( pdn.bv_val ); - free( ndn.bv_val ); + op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); continue; } c_id = (backsql_entryID *)ch_calloc( 1, sizeof( backsql_entryID ) ); #ifdef BACKSQL_ARBITRARY_KEY - ber_str2bv( row.cols[ 0 ], 0, 1, &c_id->eid_id ); - ber_str2bv( row.cols[ 1 ], 0, 1, &c_id->eid_keyval ); + ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id, + op->o_tmpmemctx ); + ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval, + op->o_tmpmemctx ); #else /* ! BACKSQL_ARBITRARY_KEY */ c_id->eid_id = strtol( row.cols[ 0 ], NULL, 0 ); c_id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 ); @@ -1732,7 +1775,8 @@ backsql_search( Operation *op, SlapReply *rs ) backsql_info *bi = (backsql_info *)op->o_bd->be_private; SQLHDBC dbh = SQL_NULL_HDBC; int sres; - Entry user_entry = { 0 }; + Entry user_entry = { 0 }, + base_entry = { 0 }; int manageDSAit; time_t stoptime = 0; backsql_srch_info bsi; @@ -1793,30 +1837,57 @@ backsql_search( Operation *op, SlapReply *rs ) } /* init search */ + bsi.bsi_e = &base_entry; rs->sr_err = backsql_init_search( &bsi, &realndn, op->ors_scope, op->ors_slimit, op->ors_tlimit, stoptime, op->ors_filter, dbh, op, rs, op->ors_attrs, - BACKSQL_ISF_GET_ID ); + ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); if ( rs->sr_err != LDAP_SUCCESS ) { +#ifdef SLAP_ACL_HONOR_DISCLOSE + if ( !BER_BVISNULL( &base_entry.e_nname ) + && ! access_allowed( op, &base_entry, + slap_schema.si_ad_entry, NULL, + ACL_DISCLOSE, NULL ) ) + { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + if ( rs->sr_ref ) { + ber_bvarray_free( rs->sr_ref ); + } + rs->sr_matched = NULL; + rs->sr_text = NULL; + } +#endif /* SLAP_ACL_HONOR_DISCLOSE */ send_ldap_result( op, rs ); goto done; - } else { - Entry e = { 0 }; + } +#ifdef SLAP_ACL_HONOR_DISCLOSE + /* NOTE: __NEW__ "search" access is required + * on searchBase object */ + else { + slap_mask_t mask; - e.e_name = bsi.bsi_base_id.eid_dn; - e.e_nname = bsi.bsi_base_id.eid_ndn; /* FIXME: need the whole entry (ITS#3480) */ - if ( ! access_allowed( op, &e, slap_schema.si_ad_entry, - NULL, ACL_DISCLOSE, NULL ) ) + if ( ! access_allowed_mask( op, &base_entry, + slap_schema.si_ad_entry, + NULL, ACL_SEARCH, NULL, &mask ) ) { - rs->sr_err = LDAP_NO_SUCH_OBJECT; + if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) { + rs->sr_err = LDAP_NO_SUCH_OBJECT; + rs->sr_text = NULL; + + } else { + rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + } send_ldap_result( op, rs ); goto done; } } +#endif /* SLAP_ACL_HONOR_DISCLOSE */ + + bsi.bsi_e = NULL; bsi.bsi_n_candidates = ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : @@ -1872,7 +1943,8 @@ backsql_search( Operation *op, SlapReply *rs ) */ for ( eid = bsi.bsi_id_list; eid != NULL; - eid = backsql_free_entryID( eid, eid == &bsi.bsi_base_id ? 0 : 1 ) ) + eid = backsql_free_entryID( op, + eid, eid == &bsi.bsi_base_id ? 0 : 1 ) ) { int rc; Attribute *a_hasSubordinate = NULL, @@ -1942,15 +2014,20 @@ backsql_search( Operation *op, SlapReply *rs ) case LDAP_SCOPE_SUBTREE: /* FIXME: this should never fail... */ if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) { + assert( 0 ); goto next_entry2; } break; } - /* don't recollect baseObject ... */ if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) { + /* don't recollect baseObject... */ e = bi->sql_baseObject; + } else if ( eid == &bsi.bsi_base_id ) { + /* don't recollect searchBase object... */ + e = &base_entry; + } else { bsi.bsi_e = &user_entry; rc = backsql_id2entry( &bsi, eid ); @@ -1960,7 +2037,6 @@ backsql_search( Operation *op, SlapReply *rs ) "- skipping\n", rc, 0, 0 ); continue; } - e = &user_entry; } @@ -1977,20 +2053,22 @@ backsql_search( Operation *op, SlapReply *rs ) Entry user_entry2 = { 0 }; /* retry with the full entry... */ - (void)backsql_init_search( &bsi2, + bsi2.bsi_e = &user_entry2; + rc = backsql_init_search( &bsi2, &e->e_nname, LDAP_SCOPE_BASE, SLAP_NO_LIMIT, SLAP_NO_LIMIT, (time_t)(-1), NULL, - dbh, op, rs, NULL, 0 ); - bsi2.bsi_e = &user_entry2; - rc = backsql_id2entry( &bsi2, eid ); + dbh, op, rs, NULL, + BACKSQL_ISF_GET_ENTRY ); if ( rc == LDAP_SUCCESS ) { if ( is_entry_referral( &user_entry2 ) ) { refs = get_entry_referrals( op, &user_entry2 ); - } /* else: FIXME: inconsistency! */ + } else { + rs->sr_err = LDAP_OTHER; + } entry_clean( &user_entry2 ); } if ( bsi2.bsi_attrs != NULL ) { @@ -2007,12 +2085,14 @@ backsql_search( Operation *op, SlapReply *rs ) ber_bvarray_free( refs ); } - if ( !rs->sr_ref ) { + if ( rs->sr_ref ) { + rs->sr_err = LDAP_REFERRAL; + + } else { rs->sr_text = "bad referral object"; } rs->sr_entry = e; - rs->sr_err = LDAP_REFERRAL; rs->sr_matched = user_entry.e_name.bv_val; send_search_reference( op, rs ); @@ -2098,16 +2178,11 @@ backsql_search( Operation *op, SlapReply *rs ) rs->sr_attrs = NULL; rs->sr_operational_attrs = NULL; - switch ( sres ) { - case 0: - break; - - default: + if ( sres == -1 ) { /* * FIXME: send_search_entry failed; * better stop */ - case -1: Debug( LDAP_DEBUG_TRACE, "backsql_search(): " "connection lost\n", 0, 0, 0 ); goto end_of_search; @@ -2115,7 +2190,9 @@ backsql_search( Operation *op, SlapReply *rs ) } next_entry:; - entry_clean( &user_entry ); + if ( e == &user_entry ) { + entry_clean( &user_entry ); + } next_entry2:; if ( op->ors_slimit != SLAP_NO_LIMIT @@ -2128,6 +2205,8 @@ next_entry2:; } end_of_search:; + entry_clean( &base_entry ); + /* in case we got here accidentally */ entry_clean( &user_entry ); @@ -2172,9 +2251,7 @@ done:; ch_free( realndn.bv_val ); } - if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { - (void)backsql_free_entryID( &bsi.bsi_base_id, 0 ); - } + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); if ( bsi.bsi_attrs != NULL ) { op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); @@ -2213,6 +2290,8 @@ backsql_entry_get( SlapReply rs = { 0 }; AttributeName anlist[ 2 ]; + *ent = NULL; + rc = backsql_get_db_conn( op, &dbh ); if ( !dbh ) { return LDAP_OTHER; @@ -2224,23 +2303,16 @@ backsql_entry_get( BER_BVZERO( &anlist[ 1 ].an_name ); } + bsi.bsi_e = ch_malloc( sizeof( Entry ) ); rc = backsql_init_search( &bsi, ndn, LDAP_SCOPE_BASE, SLAP_NO_LIMIT, SLAP_NO_LIMIT, (time_t)(-1), NULL, dbh, op, &rs, at ? anlist : NULL, - BACKSQL_ISF_GET_ID ); - if ( rc != LDAP_SUCCESS ) { - return rc; - } + BACKSQL_ISF_GET_ENTRY ); - bsi.bsi_e = ch_malloc( sizeof( Entry ) ); - rc = backsql_id2entry( &bsi, &bsi.bsi_base_id ); - - if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { - (void)backsql_free_entryID( &bsi.bsi_base_id, 0 ); - } + (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 ); if ( rc == LDAP_SUCCESS ) { -- 2.39.5