From: Ondřej Kuzník Date: Mon, 10 Feb 2025 18:03:23 +0000 (+0000) Subject: ITS#10301 Send assert control with forwarded mods if configured X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7717e2ba4dde7d8372628e320fc2d04966645562;p=thirdparty%2Fopenldap.git ITS#10301 Send assert control with forwarded mods if configured --- diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c index 7200e8c338..90a02c7ca6 100644 --- a/servers/slapd/bconfig.c +++ b/servers/slapd/bconfig.c @@ -176,6 +176,7 @@ enum { CFG_LASTMOD, CFG_LASTBIND, CFG_LASTBIND_PRECISION, + CFG_LASTBIND_ASSERT, CFG_AZPOLICY, CFG_AZREGEXP, CFG_AZDUC, @@ -457,6 +458,12 @@ static ConfigTable config_back_cf_table[] = { "SYNTAX OMsInteger SINGLE-VALUE )", NULL, { .v_uint = 0 } }, + { "lastbind-send-assert", "on|off", 2, 2, 0, + ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_LASTBIND_ASSERT, + &config_generic, "( OLcfgDbAt:0.24 NAME 'olcLastBindSendAssert' " + "EQUALITY booleanMatch " + "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL, + }, { "ldapsyntax", "syntax", 2, 0, 0, ARG_PAREN|ARG_MAGIC|CFG_SYNTAX, &config_generic, "( OLcfgGlAt:85 NAME 'olcLdapSyntaxes' " @@ -1034,7 +1041,7 @@ static ConfigOCs cf_ocs[] = { "olcReplogFile $ olcRequires $ olcRestrict $ olcRootDN $ olcRootPW $ " "olcSchemaDN $ olcSecurity $ olcSizeLimit $ olcSyncUseSubentry $ olcSyncrepl $ " "olcTimeLimit $ olcUpdateDN $ olcUpdateRef $ olcMultiProvider $ " - "olcMonitoring $ olcExtraAttrs ) )", + "olcMonitoring $ olcExtraAttrs $ olcLastBindSendAssert ) )", Cft_Database, NULL, cfAddDatabase }, { "( OLcfgGlOc:5 " "NAME 'olcOverlayConfig' " @@ -1413,6 +1420,9 @@ config_generic(ConfigArgs *c) { case CFG_LASTBIND_PRECISION: c->value_uint = c->be->be_lastbind_precision; break; + case CFG_LASTBIND_ASSERT: + c->value_int = (SLAP_LASTBIND_ASSERT(c->be) != 0); + break; case CFG_SYNC_SUBENTRY: c->value_int = (SLAP_SYNC_SUBENTRY(c->be) != 0); break; @@ -1570,6 +1580,10 @@ config_generic(ConfigArgs *c) { c->be->be_lastbind_precision = 0; break; + case CFG_LASTBIND_ASSERT: + SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_LASTBIND_ASSERT; + break; + case CFG_RO: c->be->be_restrictops &= ~SLAP_RESTRICT_READONLY; break; @@ -2445,6 +2459,13 @@ sortval_reject: c->be->be_lastbind_precision = c->value_uint; break; + case CFG_LASTBIND_ASSERT: + if (c->value_int) + SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_LASTBIND_ASSERT; + else + SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_LASTBIND_ASSERT; + break; + case CFG_MULTIPROVIDER: /* Matching on sid_list rather than serverID to keep tools in check */ if ( c->value_int && !sid_list ) { diff --git a/servers/slapd/bind.c b/servers/slapd/bind.c index 331f5d0e74..ec0709d70e 100644 --- a/servers/slapd/bind.c +++ b/servers/slapd/bind.c @@ -406,13 +406,14 @@ fe_op_lastbind( Operation *op ) Operation op2 = *op; SlapReply r2 = { REP_RESULT }; slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; - LDAPControl c, *ca[2]; + LDAPControl c_relax, c_assert = {}, *ca[3]; Modifications *m; Entry *e; Attribute *a; char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ]; struct berval timestamp; time_t bindtime = (time_t)-1; + unsigned int precision = op->o_bd->be_lastbind_precision; int rc; rc = be_entry_get_rw( op, &op->o_conn->c_ndn, NULL, NULL, 0, &e ); @@ -424,7 +425,6 @@ fe_op_lastbind( Operation *op ) if ( (a = attr_find( e->e_attrs, slap_schema.si_ad_pwdLastSuccess )) != NULL ) { struct lutil_tm tm; struct lutil_timet tt; - unsigned int precision = op->o_bd->be_lastbind_precision; if ( precision == 0 ) { precision = frontendDB->be_lastbind_precision; @@ -491,11 +491,53 @@ fe_op_lastbind( Operation *op ) /* Must use Relax control since these are no-user-mod */ op2.o_relax = SLAP_CONTROL_CRITICAL; op2.o_ctrls = ca; - ca[0] = &c; + ca[0] = &c_relax; ca[1] = NULL; - BER_BVZERO( &c.ldctl_value ); - c.ldctl_iscritical = 1; - c.ldctl_oid = LDAP_CONTROL_RELAX; + BER_BVZERO( &c_relax.ldctl_value ); + c_relax.ldctl_iscritical = 1; + c_relax.ldctl_oid = LDAP_CONTROL_RELAX; + + if ( SLAP_LASTBIND_ASSERT( op->o_bd ) ) { + /* + * We assert that the following filter still holds: + * "(!(pwdLastSuccess>=timestamp))" + * + * Where "timestamp" is the lowest timestamp within precision. This + * lets the server skip this mod if it would be superfluous. + * + * Keep it non-critical so if the server doesn't implement RFC 4528, + * it will still work, just won't be able to filter any noise out. + */ + + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + time_t threshold = op->o_time - precision; + + ber_init2( ber, NULL, LBER_USE_DER ); + if ( op2.o_tmpmemctx ) { + ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op2.o_tmpmemctx ); + } + + timestamp.bv_len = sizeof(nowstr); + slap_timestamp( &threshold, ×tamp ); + + ca[1] = &c_assert; + ca[2] = NULL; + + c_assert.ldctl_oid = LDAP_CONTROL_ASSERT; + c_assert.ldctl_iscritical = 0; + + if ( ber_printf( ber, "t{t{OO}}", + LDAP_FILTER_NOT, LDAP_FILTER_GE, + &slap_schema.si_ad_pwdLastSuccess->ad_cname, ×tamp + ) < 0 || + ber_flatten2( ber, &c_assert.ldctl_value, 0 ) == -1 ) { + Debug( LDAP_DEBUG_ANY, "%s fe_op_lastbind: " + "failed to construct assertion control for forwarding\n", + op->o_log_prefix ); + ca[1] = NULL; + } + } } else { /* If not forwarding, don't update opattrs and don't replicate */ if ( SLAP_SINGLE_SHADOW( op->o_bd )) { @@ -505,6 +547,13 @@ fe_op_lastbind( Operation *op ) } rc = op2.o_bd->be_modify( &op2, &r2 ); + if ( !BER_BVISNULL( &c_assert.ldctl_value ) ) { + if ( rc == LDAP_ASSERTION_FAILED ) { + /* We intended this to be a noop */ + rc = LDAP_SUCCESS; + } + op->o_tmpfree( c_assert.ldctl_value.bv_val, op2.o_tmpmemctx ); + } slap_mods_free( m, 1 ); done: diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index cf071b813f..5754d91277 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -1894,6 +1894,7 @@ struct BackendDB { #define SLAP_DBFLAG_DISABLED 0x100000U #define SLAP_DBFLAG_LASTBIND 0x200000U #define SLAP_DBFLAG_OPEN 0x400000U /* db is currently open */ +#define SLAP_DBFLAG_LASTBIND_ASSERT 0x800000U /* send assert control when forwarding pwdLastSuccess */ slap_mask_t be_flags; #define SLAP_DBFLAGS(be) ((be)->be_flags) #define SLAP_NOLASTMOD(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_NOLASTMOD) @@ -1926,6 +1927,7 @@ struct BackendDB { #define SLAP_DBOPEN(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_OPEN) #define SLAP_DBACL_ADD(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_ACL_ADD) #define SLAP_SYNC_SUBENTRY(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SYNC_SUBENTRY) +#define SLAP_LASTBIND_ASSERT(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_LASTBIND_ASSERT) slap_mask_t be_restrictops; /* restriction operations */ #define SLAP_RESTRICT_OP_ADD 0x0001U