mirror of
https://github.com/isc-projects/bind9.git
synced 2026-07-15 19:00:56 -04:00
[9.20] fix: usr: CDS/CDNSKEY records were not removed when re-configuring the server
When on an ``rndc reconfig`` the DNSSEC policy changes such that it changes the expected ``CDNSKEY`` and/or ``CDS`` records in the zone, the RRset should be updated accordingly. This did not happen when removing digests from the configuration, or setting `cdnskey no;`. This has been fixed. Closes #6166 Backport of MR !12265 Merge branch 'backport-6166-reconfig-delete-cds-9.20' into 'bind-9.20' See merge request isc-projects/bind9!12284
This commit is contained in:
commit
20db02121f
11 changed files with 231 additions and 46 deletions
|
|
@ -87,8 +87,9 @@ sed 's/ add \(.*\) IN DS / add \1 3600 IN DS /' <UP.swap >UP.swapttl
|
|||
|
||||
sign() {
|
||||
cat >db.$1
|
||||
cat $keyz.key $key1.key $key2.key >>db.$1
|
||||
$SIGNER >/dev/null \
|
||||
-S -O full -o $Z -f sig.$1 db.$1
|
||||
-O full -o $Z -f sig.$1 db.$1 $keyz $key1 $key2
|
||||
}
|
||||
|
||||
sign null <<EOF
|
||||
|
|
|
|||
6
bin/tests/system/kasp/ns3/named-reconfig.conf.j2
Normal file
6
bin/tests/system/kasp/ns3/named-reconfig.conf.j2
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* A zone which has CDS and CDNSKEY published. */
|
||||
zone "cds-cdnskey.kasp" {
|
||||
type primary;
|
||||
file "cds-cdnskey.kasp.db";
|
||||
dnssec-policy "cds-cdnskey";
|
||||
};
|
||||
6
bin/tests/system/kasp/ns3/named-reconfig2.conf.j2
Normal file
6
bin/tests/system/kasp/ns3/named-reconfig2.conf.j2
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* A zone which has CDS and CDNSKEY published. */
|
||||
zone "cds-cdnskey.kasp" {
|
||||
type primary;
|
||||
file "cds-cdnskey.kasp.db";
|
||||
dnssec-policy "cds-cdnskey-no";
|
||||
};
|
||||
6
bin/tests/system/kasp/ns3/named-reconfig3.conf.j2
Normal file
6
bin/tests/system/kasp/ns3/named-reconfig3.conf.j2
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* A zone which has CDS and CDNSKEY published. */
|
||||
zone "cds-cdnskey.kasp" {
|
||||
type primary;
|
||||
file "cds-cdnskey.kasp.db";
|
||||
dnssec-policy "cds-cdnskey-alt";
|
||||
};
|
||||
|
|
@ -15,6 +15,7 @@ include "policies/autosign.conf";
|
|||
include "policies/kasp-fips.conf";
|
||||
include "named-common.conf";
|
||||
include "named-fips.conf";
|
||||
include "named-reconfig.conf";
|
||||
|
||||
{% if RSASHA1_SUPPORTED == "1" %}
|
||||
include "policies/kasp-rsasha1.conf";
|
||||
|
|
|
|||
|
|
@ -119,3 +119,36 @@ dnssec-policy "keystore" {
|
|||
zsk key-store "zsk" lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
};
|
||||
};
|
||||
|
||||
dnssec-policy "cds-cdnskey" {
|
||||
dnskey-ttl 3600;
|
||||
|
||||
keys {
|
||||
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
};
|
||||
};
|
||||
|
||||
dnssec-policy "cds-cdnskey-no" {
|
||||
dnskey-ttl 3600;
|
||||
|
||||
keys {
|
||||
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
};
|
||||
|
||||
cdnskey no;
|
||||
cds-digest-types { };
|
||||
};
|
||||
|
||||
dnssec-policy "cds-cdnskey-alt" {
|
||||
dnskey-ttl 3600;
|
||||
|
||||
keys {
|
||||
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
};
|
||||
|
||||
cdnskey yes;
|
||||
cds-digest-types { "sha-384"; };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ for zn in default dnssec-keygen some-keys legacy-keys pregenerated \
|
|||
rumoured rsasha256 rsasha512 ecdsa256 ecdsa384 \
|
||||
dynamic dynamic-inline-signing inline-signing \
|
||||
checkds-ksk checkds-doubleksk checkds-csk inherit unlimited \
|
||||
keystore; do
|
||||
keystore cds-cdnskey; do
|
||||
setup "${zn}.kasp"
|
||||
cp template.db.in "$zonefile"
|
||||
done
|
||||
|
|
@ -139,6 +139,17 @@ $SETTIME -s -g $O -k $R $Tpub -r $R $Tpub -d $H $Tpub "$KSK" >settime.out.$zone.
|
|||
$SETTIME -s -g $O -k $R $Tpub -z $R $Tpub "$ZSK1" >settime.out.$zone.2 2>&1
|
||||
$SETTIME -s -g $O -k $R $Tpub -z $R $Tpub "$ZSK2" >settime.out.$zone.2 2>&1
|
||||
|
||||
# CDS and CDNSKEY should be published.
|
||||
zone="cds-cdnskey.kasp"
|
||||
echo_i "setting up zone: $zone"
|
||||
Tpub="now-7d"
|
||||
Tact="now"
|
||||
keytimes="-P ${Tpub} -A ${Tact}"
|
||||
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
|
||||
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $keytimes $zone 2>keygen.out.$zone.2)
|
||||
$SETTIME -s -g $O -k $R $Tpub -r $R $Tpub -d $H $Tpub "$KSK" >settime.out.$zone.1 2>&1
|
||||
$SETTIME -s -g $O -k $R $Tpub -z $R $Tpub "$ZSK" >settime.out.$zone.2 2>&1
|
||||
|
||||
#
|
||||
# Set up zones that are already signed.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -175,7 +175,15 @@ def fips_properties(alg, bits=None):
|
|||
|
||||
|
||||
def check_all(
|
||||
server, zone, policy, ksks, zsks, manual_mode=False, zsk_missing=False, tsig=None
|
||||
server,
|
||||
zone,
|
||||
policy,
|
||||
ksks,
|
||||
zsks,
|
||||
manual_mode=False,
|
||||
zsk_missing=False,
|
||||
cdss=None,
|
||||
tsig=None,
|
||||
):
|
||||
isctest.kasp.check_dnssecstatus(server, zone, ksks + zsks, policy=policy)
|
||||
isctest.kasp.check_apex(
|
||||
|
|
@ -183,6 +191,7 @@ def check_all(
|
|||
zone,
|
||||
ksks,
|
||||
zsks,
|
||||
cdss=cdss,
|
||||
manual_mode=manual_mode,
|
||||
zsk_missing=zsk_missing,
|
||||
tsig=tsig,
|
||||
|
|
@ -1067,6 +1076,44 @@ def test_kasp_dynamic(ns3):
|
|||
assert f"zone_resigninc: zone {zone}/IN (unsigned): enter" not in "ns3/named.run"
|
||||
|
||||
|
||||
def test_kasp_cds_cdnskey(ns3, default_algorithm):
|
||||
# Zone: cds-cdnskey.kasp.
|
||||
zone = "cds-cdnskey.kasp"
|
||||
policy = "cds-cdnskey"
|
||||
policy_keys = [
|
||||
f"ksk unlimited {default_algorithm.number} {default_algorithm.bits} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:rumoured",
|
||||
f"zsk unlimited {default_algorithm.number} {default_algorithm.bits} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent",
|
||||
]
|
||||
|
||||
isctest.kasp.wait_keymgr_done(ns3, zone)
|
||||
|
||||
expected = isctest.kasp.policy_to_properties(ttl=3600, keys=policy_keys)
|
||||
keys = isctest.kasp.keydir_to_keylist(zone, "ns3")
|
||||
ksks = [k for k in keys if k.is_ksk()]
|
||||
zsks = [k for k in keys if k.is_zsk()]
|
||||
isctest.kasp.check_dnssec_verify(ns3, zone)
|
||||
isctest.kasp.check_keys(zone, keys, expected)
|
||||
check_all(ns3, zone, policy, ksks, zsks)
|
||||
|
||||
# Reconfig
|
||||
shutil.copyfile("ns3/named-reconfig2.conf", "ns3/named-reconfig.conf")
|
||||
with ns3.watch_log_from_here() as watcher:
|
||||
ns3.rndc("reconfig")
|
||||
watcher.wait_for_line(f"keymgr: {zone} done")
|
||||
|
||||
policy = "cds-cdnskey-no"
|
||||
check_all(ns3, zone, policy, ksks, zsks, cdss=[])
|
||||
|
||||
# Reconfig, put CDS, CDNSKEY back
|
||||
shutil.copyfile("ns3/named-reconfig3.conf", "ns3/named-reconfig.conf")
|
||||
with ns3.watch_log_from_here() as watcher:
|
||||
ns3.rndc("reconfig")
|
||||
watcher.wait_for_line(f"keymgr: {zone} done")
|
||||
|
||||
policy = "cds-cdnskey-alt"
|
||||
check_all(ns3, zone, policy, ksks, zsks, cdss=["CDNSKEY", "CDS (SHA-384)"])
|
||||
|
||||
|
||||
def test_kasp_checkds(ns3, default_algorithm):
|
||||
def wait_for_metadata():
|
||||
return isctest.util.file_contents_contain(ksk.statefile, metadata)
|
||||
|
|
|
|||
155
lib/dns/dnssec.c
155
lib/dns/dnssec.c
|
|
@ -1849,9 +1849,22 @@ add_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr,
|
|||
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
|
||||
"CDS (%s) for key %s is now published", algbuf,
|
||||
keystr);
|
||||
r = addrdata(&cdsrdata, diff, origin, ttl, mctx);
|
||||
return addrdata(&cdsrdata, diff, origin, ttl, mctx);
|
||||
}
|
||||
return r;
|
||||
return DNS_R_UNCHANGED;
|
||||
}
|
||||
|
||||
static bool
|
||||
contains_digest(dns_kasp_digestlist_t *digests, unsigned int digesttype) {
|
||||
for (dns_kasp_digest_t *alg = ISC_LIST_HEAD(*digests); alg != NULL;
|
||||
alg = ISC_LIST_NEXT(alg, link))
|
||||
{
|
||||
if (digesttype == alg->digest) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
|
|
@ -1877,9 +1890,9 @@ delete_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr,
|
|||
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
|
||||
"CDS (%s) for key %s is now deleted", algbuf,
|
||||
keystr);
|
||||
r = delrdata(&cdsrdata, diff, origin, cds->ttl, mctx);
|
||||
return delrdata(&cdsrdata, diff, origin, cds->ttl, mctx);
|
||||
}
|
||||
return r;
|
||||
return DNS_R_UNCHANGED;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
|
|
@ -1893,6 +1906,7 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
|
|||
dns_dnsseckey_t *key;
|
||||
dns_ttl_t cdsttl = ttl;
|
||||
dns_ttl_t cdnskeyttl = ttl;
|
||||
bool changed = false;
|
||||
|
||||
REQUIRE(digests != NULL);
|
||||
REQUIRE(keys != NULL);
|
||||
|
|
@ -1923,9 +1937,15 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
|
|||
for (dns_kasp_digest_t *alg = ISC_LIST_HEAD(*digests);
|
||||
alg != NULL; alg = ISC_LIST_NEXT(alg, link))
|
||||
{
|
||||
CHECK(add_cds(key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
alg->digest, cdsttl, diff, mctx));
|
||||
result = add_cds(key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
alg->digest, cdsttl, diff,
|
||||
mctx);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
changed = true;
|
||||
} else if (result != DNS_R_UNCHANGED) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (gencdnskey &&
|
||||
|
|
@ -1939,41 +1959,64 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
|
|||
keystr);
|
||||
RETERR(addrdata(&cdnskeyrdata, diff, origin,
|
||||
cdnskeyttl, mctx));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (syncdelete(key->key, now)) {
|
||||
if (dns_rdataset_isassociated(cds)) {
|
||||
char keystr[DST_KEY_FORMATSIZE];
|
||||
dst_key_format(key->key, keystr, sizeof(keystr));
|
||||
|
||||
if (dns_rdataset_isassociated(cds)) {
|
||||
/* Delete all possible CDS records */
|
||||
delete_cds(key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
DNS_DSDIGEST_SHA1, diff, mctx);
|
||||
delete_cds(key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
DNS_DSDIGEST_SHA256, diff, mctx);
|
||||
delete_cds(key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
DNS_DSDIGEST_SHA384, diff, mctx);
|
||||
}
|
||||
|
||||
if (dns_rdataset_isassociated(cdnskey)) {
|
||||
if (exists(cdnskey, &cdnskeyrdata)) {
|
||||
isc_log_write(dns_lctx,
|
||||
DNS_LOGCATEGORY_GENERAL,
|
||||
DNS_LOGMODULE_DNSSEC,
|
||||
ISC_LOG_INFO,
|
||||
"CDNSKEY for key %s is "
|
||||
"now deleted",
|
||||
keystr);
|
||||
RETERR(delrdata(&cdnskeyrdata, diff,
|
||||
origin, cdnskey->ttl,
|
||||
mctx));
|
||||
/* Delete all possible CDS records */
|
||||
for (dns_dsdigest_t digest = DNS_DSDIGEST_SHA1;
|
||||
digest < DNS_DSDIGEST_TOTAL; digest++)
|
||||
{
|
||||
if (syncdelete(key->key, now) ||
|
||||
!contains_digest(digests, digest))
|
||||
{
|
||||
result = delete_cds(
|
||||
key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
digest, diff, mctx);
|
||||
switch (result) {
|
||||
case ISC_R_SUCCESS:
|
||||
changed = true;
|
||||
break;
|
||||
case DNS_R_UNCHANGED:
|
||||
case ISC_R_NOTIMPLEMENTED:
|
||||
/*
|
||||
* Either the digest is not
|
||||
* supported and we cannot
|
||||
* construct the CDS for it, or
|
||||
* the CDS with this digest is
|
||||
* not present in the CDS RRset.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dns_rdataset_isassociated(cdnskey) &&
|
||||
exists(cdnskey, &cdnskeyrdata))
|
||||
{
|
||||
if (syncdelete(key->key, now) || !gencdnskey) {
|
||||
char keystr[DST_KEY_FORMATSIZE];
|
||||
dst_key_format(key->key, keystr,
|
||||
sizeof(keystr));
|
||||
|
||||
isc_log_write(
|
||||
dns_lctx, DNS_LOGCATEGORY_GENERAL,
|
||||
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
|
||||
"CDNSKEY for key %s is now deleted",
|
||||
keystr);
|
||||
delrdata(&cdnskeyrdata, diff, origin,
|
||||
cdnskey->ttl, mctx);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dns_rdataset_isassociated(cds) &&
|
||||
|
|
@ -1998,12 +2041,30 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
|
|||
&cdnskeyrdata));
|
||||
|
||||
if (dns_rdataset_isassociated(cds)) {
|
||||
delete_cds(key, &cdnskeyrdata, (const char *)keystr,
|
||||
cds, DNS_DSDIGEST_SHA1, diff, mctx);
|
||||
delete_cds(key, &cdnskeyrdata, (const char *)keystr,
|
||||
cds, DNS_DSDIGEST_SHA256, diff, mctx);
|
||||
delete_cds(key, &cdnskeyrdata, (const char *)keystr,
|
||||
cds, DNS_DSDIGEST_SHA384, diff, mctx);
|
||||
for (dns_dsdigest_t digest = DNS_DSDIGEST_SHA1;
|
||||
digest < DNS_DSDIGEST_TOTAL; digest++)
|
||||
{
|
||||
result = delete_cds(key, &cdnskeyrdata,
|
||||
(const char *)keystr, cds,
|
||||
digest, diff, mctx);
|
||||
switch (result) {
|
||||
case ISC_R_SUCCESS:
|
||||
changed = true;
|
||||
break;
|
||||
case DNS_R_UNCHANGED:
|
||||
case ISC_R_NOTIMPLEMENTED:
|
||||
/*
|
||||
* Either the digest is not
|
||||
* supported and we cannot
|
||||
* construct the CDS for it, or
|
||||
* the CDS with this digest is
|
||||
* not present in the CDS RRset.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dns_rdataset_isassociated(cdnskey)) {
|
||||
|
|
@ -2015,11 +2076,15 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
|
|||
keystr);
|
||||
RETERR(delrdata(&cdnskeyrdata, diff, origin,
|
||||
cdnskey->ttl, mctx));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = ISC_R_SUCCESS;
|
||||
if (changed) {
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
return DNS_R_UNCHANGED;
|
||||
|
||||
cleanup:
|
||||
return result;
|
||||
|
|
@ -2036,6 +2101,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
|
|||
dns_rdata_t cds_delete = DNS_RDATA_INIT;
|
||||
dns_rdata_t cdnskey_delete = DNS_RDATA_INIT;
|
||||
isc_region_t r;
|
||||
bool changed = false;
|
||||
|
||||
r.base = keybuf;
|
||||
r.length = sizeof(keybuf);
|
||||
|
|
@ -2058,6 +2124,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
|
|||
"published",
|
||||
namebuf);
|
||||
RETERR(addrdata(&cds_delete, diff, origin, ttl, mctx));
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (dns_rdataset_isassociated(cds) && exists(cds, &cds_delete))
|
||||
|
|
@ -2069,6 +2136,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
|
|||
namebuf);
|
||||
RETERR(delrdata(&cds_delete, diff, origin, cds->ttl,
|
||||
mctx));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2083,6 +2151,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
|
|||
namebuf);
|
||||
RETERR(addrdata(&cdnskey_delete, diff, origin, ttl,
|
||||
mctx));
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (dns_rdataset_isassociated(cdnskey) &&
|
||||
|
|
@ -2095,10 +2164,14 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
|
|||
namebuf);
|
||||
RETERR(delrdata(&cdnskey_delete, diff, origin,
|
||||
cdnskey->ttl, mctx));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
if (changed) {
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
return DNS_R_UNCHANGED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#define DNS_DSDIGEST_SHA256 (2)
|
||||
#define DNS_DSDIGEST_GOST (3)
|
||||
#define DNS_DSDIGEST_SHA384 (4)
|
||||
#define DNS_DSDIGEST_TOTAL (5)
|
||||
|
||||
#define DNS_DSDIGEST_MAX (255)
|
||||
|
||||
|
|
|
|||
|
|
@ -22797,7 +22797,7 @@ zone_rekey(dns_zone_t *zone) {
|
|||
result = dns_dnssec_syncupdate(&dnskeys, &rmkeys, &cdsset,
|
||||
&cdnskeyset, now, &digests,
|
||||
cdnskeypub, ttl, &diff, mctx);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
|
||||
dnssec_log(zone, ISC_LOG_ERROR,
|
||||
"zone_rekey:couldn't update CDS/CDNSKEY: %s",
|
||||
isc_result_totext(result));
|
||||
|
|
@ -22835,7 +22835,7 @@ zone_rekey(dns_zone_t *zone) {
|
|||
result = dns_dnssec_syncdelete(
|
||||
&cdsset, &cdnskeyset, &zone->origin, zone->rdclass, ttl,
|
||||
&diff, mctx, cdsdel, cdnskeydel);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
|
||||
dnssec_log(zone, ISC_LOG_ERROR,
|
||||
"zone_rekey:couldn't update CDS/CDNSKEY "
|
||||
"DELETE records: %s",
|
||||
|
|
|
|||
Loading…
Reference in a new issue