Am Donnerstag, dem 27.03.2025 um 17:41 +0100 schrieb Daniel Leidert: > Am Samstag, dem 01.03.2025 um 10:57 +0000 schrieb Adam D. Barratt:
[..] > > +fort-validator (1.5.4-1+deb12u1) bookworm-security; urgency=medium > > > > That should be "bookworm". I just realized that I missde that hint. Attached the corrected debdiff. Regards, Daniel >
diff -Nru fort-validator-1.5.4/debian/changelog fort-validator-1.5.4/debian/changelog --- fort-validator-1.5.4/debian/changelog 2023-02-07 14:58:46.000000000 +0100 +++ fort-validator-1.5.4/debian/changelog 2025-03-29 03:13:08.000000000 +0100 @@ -1,3 +1,57 @@ +fort-validator (1.5.4-1+deb12u1) bookworm; urgency=medium + + * Non-maintainer upload by the Debian LTS Team. + * d/control (Build-Depends): Add rsync for running tests. + * d/patches/CVE-2024-45234.patch: Add patch to fix CVE-2024-45234. + - A malicious RPKI repository that descends from a (trusted) Trust Anchor + can serve (via rsync or RRDP) an ROA or a Manifest containing a + signedAttrs encoded in non-canonical form. This bypasses Fort's BER + decoder, reaching a point in the code that panics when faced with data + not encoded in DER. Because Fort is an RPKI Relying Party, a panic can + lead to Route Origin Validation unavailability, which can lead to + compromised routing. + * d/patches/CVE-2024-45235.patch: Add patch to fix CVE-2024-45235. + - A malicious RPKI repository that descends from a (trusted) Trust Anchor + can serve (via rsync or RRDP) a resource certificate containing an + Authority Key Identifier extension that lacks the keyIdentifier field. + Fort references this pointer without sanitizing it first. Because Fort + is an RPKI Relying Party, a crash can lead to Route Origin Validation + unavailability, which can lead to compromised routing. + * d/patches/CVE-2024-45236.patch: Add patch to fix CVE-2024-45236. + - A malicious RPKI repository that descends from a (trusted) Trust Anchor + can serve (via rsync or RRDP) a signed object containing an empty + signedAttributes field. Fort accesses the set's elements without + sanitizing it first. Because Fort is an RPKI Relying Party, a crash can + lead to Route Origin Validation unavailability, which can lead to + compromised routing. + * d/patches/CVE-2024-45237.patch: Add patch to fix CVE-2024-45237. + - A malicious RPKI repository that descends from a (trusted) Trust Anchor + can serve (via rsync or RRDP) a resource certificate containing a Key + Usage extension composed of more than two bytes of data. Fort writes this + string into a 2-byte buffer without properly sanitizing its length, + leading to a buffer overflow. + * d/patches/CVE-2024-45238.patch: Add patch to fix CVE-2024-45238. + - A malicious RPKI repository that descends from a (trusted) Trust Anchor + can serve (via rsync or RRDP) a resource certificate containing a bit + string that doesn't properly decode into a Subject Public Key. OpenSSL + does not report this problem during parsing, and when compiled with + OpenSSL libcrypto versions below 3, Fort recklessly dereferences the + pointer. Because Fort is an RPKI Relying Party, a crash can lead to Route + Origin Validation unavailability, which can lead to compromised routing. + * d/patches/CVE-2024-45239.patch: Add patch to fix CVE-2024-45239. + - A malicious RPKI repository that descends from a (trusted) Trust Anchor + can serve (via rsync or RRDP) an ROA or a Manifest containing a null + eContent field. Fort dereferences the pointer without sanitizing it + first. Because Fort is an RPKI Relying Party, a crash can lead to Route + Origin Validation unavailability, which can lead to compromised routing. + * d/patches/CVE-2024-48943.patch: Add patch to fix CVE-2024-48943. + - A malicious RPKI rsync repository can prevent Fort from finishing its + validation run by drip-feeding its content. This can lead to delayed + validation and a stale or unavailable Route Origin Validation. + (thanks to Jochen Sprickerhof for helping backporting the test case) + + -- Daniel Leidert <dleid...@debian.org> Sat, 29 Mar 2025 03:13:08 +0100 + fort-validator (1.5.4-1) unstable; urgency=medium * New upstream release. diff -Nru fort-validator-1.5.4/debian/control fort-validator-1.5.4/debian/control --- fort-validator-1.5.4/debian/control 2023-02-07 14:52:51.000000000 +0100 +++ fort-validator-1.5.4/debian/control 2025-03-29 03:13:08.000000000 +0100 @@ -10,6 +10,7 @@ libjansson-dev, libssl-dev, libxml2-dev, + rsync, Standards-Version: 4.6.2.0 Rules-Requires-Root: no Homepage: https://nicmx.github.io/FORT-validator/ diff -Nru fort-validator-1.5.4/debian/gbp.conf fort-validator-1.5.4/debian/gbp.conf --- fort-validator-1.5.4/debian/gbp.conf 2023-02-07 05:56:47.000000000 +0100 +++ fort-validator-1.5.4/debian/gbp.conf 2025-03-29 03:13:08.000000000 +0100 @@ -1,7 +1,7 @@ [DEFAULT] -upstream-tag = %(version)s -pristine-tar = False -compression = xz +upstream-branch = upstream/bookworm +debian-branch = debian/bookworm +pristine-tar = true [pq] patch-numbers = False diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-45234.patch fort-validator-1.5.4/debian/patches/CVE-2024-45234.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-45234.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-45234.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,208 @@ +From: Alberto Leiva Popper <ydah...@gmail.com> +Date: Tue, 6 Aug 2024 10:35:59 -0600 +Subject: Prevent crash on BER-encoded signedAttrs + +The code was assuming the object was DER-encoded, and the relevant +integer was therefore in short form. + +Because I postponed the DER enforcement in +deef7b7823f21914b17838f152a8bd510a348f54, the code should not make +reckless assumptions about the signedAttrs encoding. + +Thanks to Niklas Vogel for reporting this. + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/521b1a0db5041258096fbabdf8fc1e10ecc793cf +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-45234 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-45234 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-45234 +--- + src/object/certificate.c | 86 ++++++++++++++++++++++++++++-------------------- + 1 file changed, 51 insertions(+), 35 deletions(-) + +diff --git a/src/object/certificate.c b/src/object/certificate.c +index c6f61ef..05ea4ee 100644 +--- a/src/object/certificate.c ++++ b/src/object/certificate.c +@@ -540,47 +540,47 @@ struct progress { + /** + * Skip the "T" part of a TLV. + */ +-static void ++static int + skip_t(ANY_t *content, struct progress *p, unsigned int tag) + { +- /* +- * BTW: I made these errors critical because the signedData is supposed +- * to be validated by this point. +- */ ++ /* These errors happen when the object is not DER-encoded */ + + if (content->buf[p->offset] != tag) +- pr_crit("Expected tag 0x%x, got 0x%x", tag, +- content->buf[p->offset]); +- ++ return pr_val_err("Expected tag 0x%x, got 0x%x.", ++ tag, content->buf[p->offset]); + if (p->remaining == 0) +- pr_crit("Buffer seems to be truncated"); ++ return pr_val_err("Buffer seems truncated."); ++ + p->offset++; + p->remaining--; ++ return 0; + } + + /** + * Skip the "TL" part of a TLV. + */ +-static void ++static int + skip_tl(ANY_t *content, struct progress *p, unsigned int tag) + { + ssize_t len_len; /* Length of the length field */ + ber_tlv_len_t value_len; /* Length of the value */ + +- skip_t(content, p, tag); ++ if (skip_t(content, p, tag) != 0) ++ return -EINVAL; + + len_len = ber_fetch_length(true, &content->buf[p->offset], p->remaining, + &value_len); + if (len_len == -1) +- pr_crit("Could not decipher length (Cause is unknown)"); ++ return pr_val_err("Could not decipher length (Unknown cause)."); + if (len_len == 0) +- pr_crit("Buffer seems to be truncated"); ++ return pr_val_err("Buffer seems truncated."); + + p->offset += len_len; + p->remaining -= len_len; ++ return 0; + } + +-static void ++static int + skip_tlv(ANY_t *content, struct progress *p, unsigned int tag) + { + int is_constructed; +@@ -588,17 +588,19 @@ skip_tlv(ANY_t *content, struct progress *p, unsigned int tag) + + is_constructed = BER_TLV_CONSTRUCTED(&content->buf[p->offset]); + +- skip_t(content, p, tag); ++ if (skip_t(content, p, tag) != 0) ++ return -EINVAL; + + skip = ber_skip_length(NULL, is_constructed, &content->buf[p->offset], + p->remaining); + if (skip == -1) +- pr_crit("Could not skip length (Cause is unknown)"); ++ return pr_val_err("Could not skip length (Unknown cause)."); + if (skip == 0) +- pr_crit("Buffer seems to be truncated"); ++ return pr_val_err("Buffer seems truncated."); + + p->offset += skip; + p->remaining -= skip; ++ return 0; + } + + /** +@@ -609,12 +611,12 @@ struct encoded_signedAttrs { + ber_tlv_len_t size; + }; + +-static void ++static int + find_signedAttrs(ANY_t *signedData, struct encoded_signedAttrs *result) + { +-#define INTEGER_TAG 0x02 +-#define SEQUENCE_TAG 0x30 +-#define SET_TAG 0x31 ++ static const unsigned int INTEGER_TAG = 0x02; ++ static const unsigned int SEQUENCE_TAG = 0x30; ++ static const unsigned int SET_TAG = 0x31; + + struct progress p; + ssize_t len_len; +@@ -625,43 +627,55 @@ find_signedAttrs(ANY_t *signedData, struct encoded_signedAttrs *result) + p.remaining = signedData->size; + + /* SignedData: SEQUENCE */ +- skip_tl(signedData, &p, SEQUENCE_TAG); ++ if (skip_tl(signedData, &p, SEQUENCE_TAG) != 0) ++ return -EINVAL; + + /* SignedData.version: CMSVersion -> INTEGER */ +- skip_tlv(signedData, &p, INTEGER_TAG); ++ if (skip_tlv(signedData, &p, INTEGER_TAG) != 0) ++ return -EINVAL; + /* SignedData.digestAlgorithms: DigestAlgorithmIdentifiers -> SET */ +- skip_tlv(signedData, &p, SET_TAG); ++ if (skip_tlv(signedData, &p, SET_TAG) != 0) ++ return -EINVAL; + /* SignedData.encapContentInfo: EncapsulatedContentInfo -> SEQUENCE */ +- skip_tlv(signedData, &p, SEQUENCE_TAG); ++ if (skip_tlv(signedData, &p, SEQUENCE_TAG) != 0) ++ return -EINVAL; + /* SignedData.certificates: CertificateSet -> SET */ +- skip_tlv(signedData, &p, 0xA0); ++ if (skip_tlv(signedData, &p, 0xA0) != 0) ++ return -EINVAL; + /* SignedData.signerInfos: SignerInfos -> SET OF SEQUENCE */ +- skip_tl(signedData, &p, SET_TAG); +- skip_tl(signedData, &p, SEQUENCE_TAG); ++ if (skip_tl(signedData, &p, SET_TAG) != 0) ++ return -EINVAL; ++ if (skip_tl(signedData, &p, SEQUENCE_TAG) != 0) ++ return -EINVAL; + + /* SignedData.signerInfos.version: CMSVersion -> INTEGER */ +- skip_tlv(signedData, &p, INTEGER_TAG); ++ if (skip_tlv(signedData, &p, INTEGER_TAG) != 0) ++ return -EINVAL; + /* + * SignedData.signerInfos.sid: SignerIdentifier -> CHOICE -> always + * subjectKeyIdentifier, which is a [0]. + */ +- skip_tlv(signedData, &p, 0x80); ++ if (skip_tlv(signedData, &p, 0x80) != 0) ++ return -EINVAL; + /* SignedData.signerInfos.digestAlgorithm: DigestAlgorithmIdentifier + * -> AlgorithmIdentifier -> SEQUENCE */ +- skip_tlv(signedData, &p, SEQUENCE_TAG); ++ if (skip_tlv(signedData, &p, SEQUENCE_TAG) != 0) ++ return -EINVAL; + + /* SignedData.signerInfos.signedAttrs: SignedAttributes -> SET */ + /* We will need to replace the tag 0xA0 with 0x31, so skip it as well */ +- skip_t(signedData, &p, 0xA0); ++ if (skip_t(signedData, &p, 0xA0) != 0) ++ return -EINVAL; + + result->buffer = &signedData->buf[p.offset]; + len_len = ber_fetch_length(true, result->buffer, + p.remaining, &result->size); + if (len_len == -1) +- pr_crit("Could not decipher length (Cause is unknown)"); ++ return pr_val_err("Could not decipher length (Unknown cause.)"); + if (len_len == 0) +- pr_crit("Buffer seems to be truncated"); ++ return pr_val_err("Buffer seems truncated."); + result->size += len_len; ++ return 0; + } + + /* +@@ -743,7 +757,9 @@ certificate_validate_signature(X509 *cert, ANY_t *signedData, + * Second option it is. + */ + +- find_signedAttrs(signedData, &signedAttrs); ++ error = find_signedAttrs(signedData, &signedAttrs); ++ if (error) ++ goto end; + + error = EVP_DigestVerifyUpdate(ctx, &EXPLICIT_SET_OF_TAG, + sizeof(EXPLICIT_SET_OF_TAG)); diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-45235.patch fort-validator-1.5.4/debian/patches/CVE-2024-45235.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-45235.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-45235.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,33 @@ +From: Alberto Leiva Popper <ydah...@gmail.com> +Date: Tue, 6 Aug 2024 10:29:44 -0600 +Subject: [PATCH] Prevent crash on missing Authority Key Identifier + +Another missing NULL check. + +Thanks to Niklas Vogel for reporting this. + + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/b1eb3c507ae920859bbe294776ebc2bb30bb7e56 +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-45235 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-45235 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-45235 +--- + src/extension.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/extension.c b/src/extension.c +index d8bd58e..a004532 100644 +--- a/src/extension.c ++++ b/src/extension.c +@@ -318,6 +318,10 @@ handle_aki(X509_EXTENSION *ext, void *arg) + if (aki == NULL) + return cannot_decode(ext_aki()); + ++ if (aki->keyid == NULL) { ++ error = pr_val_err("%s extension lacks a keyIdentifier.", ++ ext_aki()->name); ++ } + if (aki->issuer != NULL) { + error = pr_val_err("%s extension contains an authorityCertIssuer.", + ext_aki()->name); diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-45236.patch fort-validator-1.5.4/debian/patches/CVE-2024-45236.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-45236.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-45236.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,65 @@ +From: Alberto Leiva Popper <ydah...@gmail.com> +Date: Tue, 6 Aug 2024 10:35:14 -0600 +Subject: [PATCH] Prevent crash on missing signedAttrs + +Though RPKI enforces the presence of this field, it is very much +optional in CMS. +Also adds missing validation messages in relevant error paths. + +Thanks to Niklas Vogel for reporting this. + + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/4dafbd9de64a5a0616af97365bc1751465b29d2e +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-45236 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-45236 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-45236 +--- + src/asn1/signed_data.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/src/asn1/signed_data.c b/src/asn1/signed_data.c +index 41e4f81..f956ed7 100644 +--- a/src/asn1/signed_data.c ++++ b/src/asn1/signed_data.c +@@ -496,30 +496,32 @@ get_content_type_attr(struct SignedData *sdata, OBJECT_IDENTIFIER_t **result) + bool equal; + + if (sdata == NULL) +- return -EINVAL; ++ return pr_val_err("SignedData is NULL."); + if (sdata->signerInfos.list.array == NULL) +- return -EINVAL; ++ return pr_val_err("SignerInfos array is NULL."); + if (sdata->signerInfos.list.array[0] == NULL) +- return -EINVAL; ++ return pr_val_err("SignerInfos array first element is NULL."); + + signedAttrs = sdata->signerInfos.list.array[0]->signedAttrs; ++ if (signedAttrs == NULL) ++ return pr_val_err("signedAttrs is NULL."); + if (signedAttrs->list.array == NULL) +- return -EINVAL; ++ return pr_val_err("signedAttrs array is NULL."); + + for (i = 0; i < signedAttrs->list.count; i++) { + attr = signedAttrs->list.array[i]; + if (!attr) +- return -EINVAL; ++ return pr_val_err("signedAttrs array element %d is NULL.", i); + error = oid2arcs(&attr->attrType, &arcs); + if (error) +- return -EINVAL; ++ return error; + equal = ARCS_EQUAL_OIDS(&arcs, oid_cta); + free_arcs(&arcs); + if (equal) { + if (attr->attrValues.list.array == NULL) +- return -EINVAL; ++ return pr_val_err("signedAttrs attrValue array is NULL."); + if (attr->attrValues.list.array[0] == NULL) +- return -EINVAL; ++ return pr_val_err("signedAttrs attrValue array first element is NULL."); + return asn1_decode_any(attr->attrValues.list.array[0], + &asn_DEF_OBJECT_IDENTIFIER, + (void **) result, true, false); diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-45237.patch fort-validator-1.5.4/debian/patches/CVE-2024-45237.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-45237.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-45237.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,35 @@ +From: Alberto Leiva Popper <ydah...@gmail.com> +Date: Tue, 6 Aug 2024 10:29:16 -0600 +Subject: [PATCH] Prevent crash on malformed Key Usage + +Key Usage bit strings longer than 2 bytes were inducing buffer overflow. + +Thanks to Niklas Vogel for reporting this. + + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/939d988551d17996be73f52c376a70a3d6ba69f9 +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-45237 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-45237 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-45237 +--- + src/object/certificate.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/object/certificate.c b/src/object/certificate.c +index 05ea4ee..5b69b31 100644 +--- a/src/object/certificate.c ++++ b/src/object/certificate.c +@@ -1354,9 +1354,9 @@ handle_ku(X509_EXTENSION *ext, unsigned char byte1) + if (ku == NULL) + return cannot_decode(ext_ku()); + +- if (ku->length == 0) { +- error = pr_val_err("%s bit string has no enabled bits.", +- ext_ku()->name); ++ if (ku->length != 2 && ku->length != 1) { ++ error = pr_val_err("Bogus %s length: %d", ++ ext_ku()->name, ku->length); + goto end; + } + diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-45238.patch fort-validator-1.5.4/debian/patches/CVE-2024-45238.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-45238.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-45238.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,42 @@ +From: Alberto Leiva Popper <ydah...@gmail.com> +Date: Tue, 6 Aug 2024 10:28:57 -0600 +Subject: [PATCH] Prevent crash on malformed subjectPublicKey + +A malformed subjectPublicKey causes X509_PUBKEY_get0() to return NULL. +Fort wasn't catching this when linked specifically to OpenSSL < 3. + +Thanks to Niklas Vogel for reporting this. + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/5689dea5e878fed28c5f338a27d7cda4151a14f1 +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-45238 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-45238 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-45238 +--- + src/object/certificate.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/object/certificate.c b/src/object/certificate.c +index 5b69b31..95a7df2 100644 +--- a/src/object/certificate.c ++++ b/src/object/certificate.c +@@ -380,13 +380,18 @@ validate_subject_public_key(X509_PUBKEY *pubkey) + + #define MODULUS 2048 + #define EXPONENT "65537" ++ EVP_PKEY *pkey; + const RSA *rsa; + const BIGNUM *exp; + char *exp_str; + int modulus; + int error; + +- rsa = EVP_PKEY_get0_RSA(X509_PUBKEY_get0(pubkey)); ++ pkey = X509_PUBKEY_get0(pubkey); ++ if (pkey == NULL) ++ return val_crypto_err("The certificate's Subject Public Key is missing or malformed."); ++ ++ rsa = EVP_PKEY_get0_RSA(pkey); + if (rsa == NULL) + return val_crypto_err("EVP_PKEY_get0_RSA() returned NULL"); + diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-45239.patch fort-validator-1.5.4/debian/patches/CVE-2024-45239.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-45239.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-45239.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,46 @@ +From: Alberto Leiva Popper <ydah...@gmail.com> +Date: Tue, 6 Aug 2024 10:35:24 -0600 +Subject: [PATCH] Prevent crash on missing eContent + +Applies to the RouteOriginAttestation and Manifest octet strings. + +Thanks to Niklas Vogel for reporting this. + + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/942f921ba7244cdcf4574cedc4c16392a7cc594b +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-45239 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-45239 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-45239 +--- + src/asn1/decode.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/asn1/decode.c b/src/asn1/decode.c +index a765294..c3a9b66 100644 +--- a/src/asn1/decode.c ++++ b/src/asn1/decode.c +@@ -128,8 +128,9 @@ int + asn1_decode_any(ANY_t *any, asn_TYPE_descriptor_t const *descriptor, + void **result, bool log, bool dec_as_der) + { +- return asn1_decode(any->buf, any->size, descriptor, result, log, +- dec_as_der); ++ return (any != NULL) ++ ? asn1_decode(any->buf, any->size, descriptor, result, log, dec_as_der) ++ : pr_val_err("ANY '%s' is NULL.", descriptor->name); + } + + int +@@ -137,8 +138,9 @@ asn1_decode_octet_string(OCTET_STRING_t *string, + asn_TYPE_descriptor_t const *descriptor, void **result, bool log, + bool dec_as_der) + { +- return asn1_decode(string->buf, string->size, descriptor, result, log, +- dec_as_der); ++ return (string != NULL) ++ ? asn1_decode(string->buf, string->size, descriptor, result, log, dec_as_der) ++ : pr_val_err("Octet String '%s' is NULL.", descriptor->name); + } + + /* diff -Nru fort-validator-1.5.4/debian/patches/CVE-2024-48943.patch fort-validator-1.5.4/debian/patches/CVE-2024-48943.patch --- fort-validator-1.5.4/debian/patches/CVE-2024-48943.patch 1970-01-01 01:00:00.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/CVE-2024-48943.patch 2025-03-29 03:13:08.000000000 +0100 @@ -0,0 +1,819 @@ +From: Job Snijders <j...@sobornost.net> +Date: Thu, 22 Aug 2024 16:33:59 +0000 +Subject: [PATCH 1/7] Introduce a rsync transfer timeout + +Default set to 900 (same as rpki-client) + +Fixes https://github.com/NICMx/FORT-validator/issues/74 + +Reviewed-By: Daniel Leidert <dleid...@debian.org> +Reviewed-By: Jochen Sprickerhof <jspri...@debian.org> +Origin: https://github.com/NICMx/FORT-validator/commit/4ee88d1c3fa7df763dd52312134cd93c1ce50870 +Bug: https://nicmx.github.io/FORT-validator/CVE.html#cve-2024-48943 +Bug: https://github.com/NICMx/FORT-validator/issues/74 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-48943 +Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-48943 +--- + docs/usage.md | 14 ++ + examples/config.json | 1 + + man/fort.8 | 12 ++ + src/config.c | 16 +++ + src/config.h | 1 + + src/rsync/rsync.c | 181 ++++++++++++++++++-------- + test/Makefile.am | 4 + + test/impersonator.c | 6 + + test/rsync/rsync_test.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ + 9 files changed, 522 insertions(+), 52 deletions(-) + create mode 100644 test/rsync/rsync_test.c + +diff --git a/docs/usage.md b/docs/usage.md +index 6f3e193..64a5172 100644 +--- a/docs/usage.md ++++ b/docs/usage.md +@@ -71,6 +71,7 @@ description: Guide to use arguments of FORT Validator. + 3. [`root-except-ta`](#root-except-ta) + 53. [`--rsync.retry.count`](#--rsyncretrycount) + 54. [`--rsync.retry.interval`](#--rsyncretryinterval) ++ 40. [`--rsync.transfer-timeout`](#--rsynctransfer-timeout) + 55. [`--configuration-file`](#--configuration-file) + 56. [`rsync.program`](#rsyncprogram) + 57. [`rsync.arguments-recursive`](#rsyncarguments-recursive) +@@ -115,6 +116,7 @@ description: Guide to use arguments of FORT Validator. + [--rsync.strategy=root|root-except-ta] + [--rsync.retry.count=<unsigned integer>] + [--rsync.retry.interval=<unsigned integer>] ++ [--rsync.transfer-timeout=<unsigned integer>] + [--rrdp.enabled=true|false] + [--rrdp.priority=<32-bit unsigned integer>] + [--rrdp.retry.count=<unsigned integer>] +@@ -1023,6 +1025,17 @@ Whenever is necessary to execute an RSYNC, the validator will try at least one t + + Period of time (in seconds) to wait between each retry to execute an RSYNC. + ++### `--rsync.transfer-timeout` ++ ++- **Type:** Integer ++- **Availability:** `argv` and JSON ++- **Default:** 900 ++- **Range:** [0, [`UINT_MAX`](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)] ++ ++Maximum time in seconds that the rsync transfer can last. ++ ++Once the connection is established with the server, the request will last a maximum of `rsync.transfer-timeout` seconds. A value of 0 means unlimited time. ++ + ### `--configuration-file` + + - **Type:** String (Path to file) +@@ -1104,6 +1117,7 @@ The configuration options are mostly the same as the ones from the `argv` interf + "<a href="#--rsyncretrycount">count</a>": 2, + "<a href="#--rsyncretryinterval">interval</a>": 5 + }, ++ "<a href="#--rsynctransfer-timeout">transfer-timeout</a>": 0, + "<a href="#rsyncprogram">program</a>": "rsync", + "<a href="#rsyncarguments-recursive">arguments-recursive</a>": [ + "--recursive", +diff --git a/examples/config.json b/examples/config.json +index 6c10c32..d724766 100644 +--- a/examples/config.json ++++ b/examples/config.json +@@ -58,6 +58,7 @@ + "count": 2, + "interval": 5 + }, ++ "transfer-timeout": 900, + "program": "rsync", + "arguments-recursive": [ + "--recursive", +diff --git a/man/fort.8 b/man/fort.8 +index 05443d8..a6b88ee 100644 +--- a/man/fort.8 ++++ b/man/fort.8 +@@ -1061,6 +1061,18 @@ By default, the value is \fI5\fR. + .RE + .P + ++.B \-\-rsync.transfer\-timeout=\fIUNSIGNED_INTEGER\fR ++.RS 4 ++Maximum time in seconds that the rsync process can last. ++.P ++Once the connection is established with the server, the request will last a ++maximum of \fBrsync.transfer-timeout\fR seconds. A value of \fI0\fR means ++unlimited time (default value). ++.P ++By default, it has a value of \fI900\fR. ++.RE ++.P ++ + .B \-\-output.roa=\fIFILE\fR + .RS 4 + File where the ROAs will be printed in the configured format (see +diff --git a/src/config.c b/src/config.c +index a068be6..de4f861 100644 +--- a/src/config.c ++++ b/src/config.c +@@ -102,6 +102,7 @@ struct rpki_config { + /* Interval (in seconds) between each retry */ + unsigned int interval; + } retry; ++ unsigned int transfer_timeout; + char *program; + struct { + struct string_array flat; +@@ -519,6 +520,14 @@ static const struct option_field options[] = { + .availability = AVAILABILITY_JSON, + /* Unlimited */ + .max = 0, ++ }, { ++ .id = 3008, ++ .name = "rsync.transfer-timeout", ++ .type = >_uint, ++ .offset = offsetof(struct rpki_config, rsync.transfer_timeout), ++ .doc = "Maximum transfer time before killing the rsync process", ++ .min = 0, ++ .max = UINT_MAX, + }, + + /* RRDP fields */ +@@ -1034,6 +1043,7 @@ set_default_values(void) + rpki_config.rsync.strategy = RSYNC_ROOT_EXCEPT_TA; + rpki_config.rsync.retry.count = 1; + rpki_config.rsync.retry.interval = 4; ++ rpki_config.rsync.transfer_timeout = 900; + rpki_config.rsync.program = strdup("rsync"); + if (rpki_config.rsync.program == NULL) { + error = pr_enomem(); +@@ -1518,6 +1528,12 @@ config_get_rsync_retry_interval(void) + return rpki_config.rsync.retry.interval; + } + ++long ++config_get_rsync_transfer_timeout(void) ++{ ++ return rpki_config.rsync.transfer_timeout; ++} ++ + char * + config_get_rsync_program(void) + { +diff --git a/src/config.h b/src/config.h +index f5ffe1b..516edfa 100644 +--- a/src/config.h ++++ b/src/config.h +@@ -44,6 +44,7 @@ unsigned int config_get_rsync_priority(void); + enum rsync_strategy config_get_rsync_strategy(void); + unsigned int config_get_rsync_retry_count(void); + unsigned int config_get_rsync_retry_interval(void); ++long config_get_rsync_transfer_timeout(void); + char *config_get_rsync_program(void); + struct string_array const *config_get_rsync_args(bool); + bool config_get_http_enabled(void); +diff --git a/src/rsync/rsync.c b/src/rsync/rsync.c +index be46097..81eeffc 100644 +--- a/src/rsync/rsync.c ++++ b/src/rsync/rsync.c +@@ -3,6 +3,7 @@ + #include <errno.h> + #include <stdlib.h> + #include <unistd.h> ++#include <poll.h> + #include <signal.h> /* SIGINT, SIGQUIT, etc */ + #include <syslog.h> + #include <sys/queue.h> +@@ -16,6 +17,11 @@ + #include "str_token.h" + #include "thread_var.h" + ++#define STDERR_WRITE(fds) fds[0][1] ++#define STDOUT_WRITE(fds) fds[1][1] ++#define STDERR_READ(fds) fds[0][0] ++#define STDOUT_READ(fds) fds[1][0] ++ + struct uri { + struct rpki_uri *uri; + SLIST_ENTRY(uri) next; +@@ -181,15 +187,15 @@ static void + duplicate_fds(int fds[2][2]) + { + /* Use the loop to catch interruptions */ +- while ((dup2(fds[0][1], STDERR_FILENO) == -1) ++ while ((dup2(STDERR_WRITE(fds), STDERR_FILENO) == -1) + && (errno == EINTR)) {} +- close(fds[0][1]); +- close(fds[0][0]); ++ close(STDERR_WRITE(fds)); ++ close(STDERR_READ(fds)); + +- while ((dup2(fds[1][1], STDOUT_FILENO) == -1) ++ while ((dup2(STDOUT_WRITE(fds), STDOUT_FILENO) == -1) + && (errno == EINTR)) {} +- close(fds[1][1]); +- close(fds[1][0]); ++ close(STDOUT_WRITE(fds)); ++ close(STDOUT_READ(fds)); + } + + static void +@@ -278,8 +284,8 @@ create_pipes(int fds[2][2]) + error = errno; + + /* Close pipe previously created */ +- close(fds[0][0]); +- close(fds[0][1]); ++ close(STDERR_READ(fds)); ++ close(STDERR_WRITE(fds)); + + pr_op_err_st("Piping rsync stdout: %s", strerror(error)); + return -error; +@@ -288,8 +294,17 @@ create_pipes(int fds[2][2]) + return 0; + } + ++static long ++get_current_millis(void) ++{ ++ struct timespec now; ++ if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) ++ pr_crit("clock_gettime() returned %d", errno); ++ return 1000L * now.tv_sec + now.tv_nsec / 1000000L; ++} ++ + static int +-log_buffer(char const *buffer, ssize_t read, int type, bool log_operation) ++log_buffer(char const *buffer, ssize_t read, bool is_error, bool log_operation) + { + #define PRE_RSYNC "[RSYNC exec]: " + char *cpy, *cur, *tmp; +@@ -309,7 +324,7 @@ log_buffer(char const *buffer, ssize_t read, int type, bool log_operation) + cur = tmp + 1; + continue; + } +- if (type == 0) { ++ if (is_error) { + if (log_operation) + pr_op_err_st(PRE_RSYNC "%s", cur); + pr_val_err(PRE_RSYNC "%s", cur); +@@ -323,58 +338,120 @@ log_buffer(char const *buffer, ssize_t read, int type, bool log_operation) + #undef PRE_RSYNC + } + ++#define DROP_FD(f, fail) \ ++ do { \ ++ pfd[f].fd = -1; \ ++ error |= fail; \ ++ } while (0) ++#define CLOSE_FD(f, fail) \ ++ do { \ ++ close(pfd[f].fd); \ ++ DROP_FD(f, fail); \ ++ } while (0) ++ ++/* ++ * Consumes (and throws away) all the bytes in read streams @fderr and @fdout, ++ * then closes them once they reach end of stream. ++ * ++ * Returns: ok -> 0, error -> 1, timeout -> 2. ++ */ + static int +-read_pipe(int fd_pipe[2][2], int type, bool log_operation) ++exhaust_read_fds(int fderr, int fdout, bool log_operation) + { +- char buffer[4096]; +- ssize_t count; +- int error; ++ struct pollfd pfd[2]; ++ int error, nready, f; ++ long epoch, delta, timeout; ++ ++ memset(&pfd, 0, sizeof(pfd)); ++ pfd[0].fd = fderr; ++ pfd[0].events = POLLIN; ++ pfd[1].fd = fdout; ++ pfd[1].events = POLLIN; ++ ++ error = 0; ++ ++ epoch = get_current_millis(); ++ delta = 0; ++ timeout = 1000 * config_get_rsync_transfer_timeout(); + + while (1) { +- count = read(fd_pipe[type][0], buffer, sizeof(buffer)); +- if (count == -1) { ++ nready = poll(pfd, 2, timeout - delta); ++ if (nready == 0) ++ goto timed_out; ++ if (nready == -1) { + error = errno; + if (error == EINTR) + continue; +- close(fd_pipe[type][0]); /* Close read end */ +- pr_val_err("rsync buffer read error: %s", +- strerror(error)); +- return -error; ++ pr_val_err("rsync bad poll: %s", strerror(error)); ++ error = 1; ++ goto fail; + } +- if (count == 0) +- break; + +- error = log_buffer(buffer, count, type, log_operation); +- if (error) +- return error; ++ for (f = 0; f < 2; f++) { ++ if (pfd[f].revents & POLLNVAL) { ++ pr_val_err("rsync bad fd: %i", pfd[f].fd); ++ DROP_FD(f, 1); ++ ++ } else if (pfd[f].revents & POLLERR) { ++ pr_val_err("Generic error during rsync poll."); ++ CLOSE_FD(f, 1); ++ ++ } else if (pfd[f].revents & (POLLIN|POLLHUP)) { ++ char buffer[4096]; ++ ssize_t count; ++ ++ count = read(pfd[f].fd, buffer, sizeof(buffer)); ++ if (count == -1) { ++ error = errno; ++ if (error == EINTR) ++ continue; ++ pr_val_err("rsync buffer read error: %s", ++ strerror(error)); ++ CLOSE_FD(f, 1); ++ continue; ++ } ++ ++ if (count == 0) ++ CLOSE_FD(f, 0); ++ log_buffer(buffer, count, pfd[f].fd == fderr, log_operation); ++ } ++ } ++ ++ if (pfd[0].fd == -1 && pfd[1].fd == -1) ++ return error; /* Happy path! */ ++ ++ delta = get_current_millis() - epoch; ++ if (delta < 0) { ++ pr_val_err("This clock does not seem monotonic. " ++ "I'm going to have to give up this rsync."); ++ error = 1; ++ goto fail; ++ } ++ if (delta >= timeout) ++ goto timed_out; /* Read took too long */ + } +- close(fd_pipe[type][0]); /* Close read end */ +- return 0; ++ ++timed_out: ++ pr_val_err("rsync transfer timeout reached"); ++ error = 2; ++fail: for (f = 0; f < 2; f++) ++ if (pfd[f].fd != -1) ++ close(pfd[f].fd); ++ return error; + } + + /* +- * Read the piped output from the child, assures that all pipes are closed on +- * success and on error. ++ * Completely consumes @fds' streams, and closes them. ++ * ++ * Allegedly, this is a portable way to wait for the child process to finish. ++ * (IIRC, waitpid() doesn't do this reliably.) + */ + static int +-read_pipes(int fds[2][2], bool log_operation) ++exhaust_pipes(int fds[2][2], bool log_operation) + { +- int error; +- +- /* Won't be needed (sterr/stdout write ends) */ +- close(fds[0][1]); +- close(fds[1][1]); +- +- /* stderr pipe */ +- error = read_pipe(fds, 0, log_operation); +- if (error) { +- /* Close the other pipe pending to read */ +- close(fds[1][0]); +- return error; +- } +- +- /* stdout pipe, always logs to info */ +- return read_pipe(fds, 1, true); ++ close(STDERR_WRITE(fds)); ++ close(STDOUT_WRITE(fds)); ++ return exhaust_read_fds(STDERR_READ(fds), STDOUT_READ(fds), log_operation); + } + + /* +@@ -441,17 +518,17 @@ do_rsync(struct rpki_uri *uri, bool is_ta, bool log_operation) + pr_op_err_st("Couldn't fork to execute rsync: %s", + strerror(error)); + /* Close all ends from the created pipes */ +- close(fork_fds[0][0]); +- close(fork_fds[1][0]); +- close(fork_fds[0][1]); +- close(fork_fds[1][1]); ++ close(STDERR_READ(fork_fds)); ++ close(STDOUT_READ(fork_fds)); ++ close(STDERR_WRITE(fork_fds)); ++ close(STDOUT_WRITE(fork_fds)); + goto release_args; + } + + /* This code is run by us. */ +- error = read_pipes(fork_fds, log_operation); ++ error = exhaust_pipes(fork_fds, log_operation); + if (error) +- kill(child_pid, SIGCHLD); /* Stop the child */ ++ kill(child_pid, SIGTERM); /* Stop the child */ + + error = waitpid(child_pid, &child_status, 0); + do { +diff --git a/test/Makefile.am b/test/Makefile.am +index 8cc0bbd..c7b679e 100644 +--- a/test/Makefile.am ++++ b/test/Makefile.am +@@ -26,6 +26,7 @@ check_PROGRAMS += line_file.test + check_PROGRAMS += pdu_handler.test + check_PROGRAMS += rrdp_objects.test + check_PROGRAMS += rsync.test ++check_PROGRAMS += rsync2.test + check_PROGRAMS += serial.test + check_PROGRAMS += tal.test + check_PROGRAMS += thread_pool.test +@@ -58,6 +59,9 @@ rrdp_objects_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS} ${XML2_LIBS} + rsync_test_SOURCES = rsync_test.c + rsync_test_LDADD = ${MY_LDADD} + ++rsync2_test_SOURCES = rsync/rsync_test.c ++rsync2_test_LDADD = ${MY_LDADD} ++ + serial_test_SOURCES = types/serial_test.c + serial_test_LDADD = ${MY_LDADD} + +diff --git a/test/impersonator.c b/test/impersonator.c +index 7ccf649..dde093b 100644 +--- a/test/impersonator.c ++++ b/test/impersonator.c +@@ -91,6 +91,12 @@ config_get_rsync_priority(void) + return rsync_priority; + } + ++long ++config_get_rsync_transfer_timeout(void) ++{ ++ return 4; ++} ++ + unsigned int + config_get_http_priority(void) + { +diff --git a/test/rsync/rsync_test.c b/test/rsync/rsync_test.c +new file mode 100644 +index 0000000..b355ac0 +--- /dev/null ++++ b/test/rsync/rsync_test.c +@@ -0,0 +1,339 @@ ++#include <check.h> ++ ++#include "common.c" ++#include "log.c" ++#include "config/string_array.c" ++#include "impersonator.c" ++#include "rsync/rsync.c" ++#include "types/uri.c" ++ ++static char const STR64[] = "abcdefghijklmnopqrstuvwxyz" ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++ "0123456789 \n"; ++static const size_t STR64LEN = sizeof(STR64) - 1; ++static char content[1024]; ++ ++struct validation * ++state_retrieve(void) ++{ ++ return NULL; ++} ++ ++char * ++config_get_rsync_program(void) ++{ ++ return "rsync"; ++} ++ ++unsigned int ++config_get_rsync_retry_count(void) ++{ ++ return 0; ++} ++ ++unsigned int ++config_get_rsync_retry_interval(void) ++{ ++ return 10; ++} ++ ++size_t ++token_count(struct string_tokenizer *t) ++{ ++ ck_abort(); ++ return 0; ++} ++ ++int ++token_read(struct string_tokenizer *t, char **s) ++{ ++ ck_abort(); ++ return 0; ++} ++ ++void ++string_tokenizer_init(struct string_tokenizer *t, char const *s, size_t sl, unsigned char sp) ++{ ++ ck_abort(); ++} ++ ++bool ++string_tokenizer_next(struct string_tokenizer *t) ++{ ++ ck_abort(); ++ return false; ++} ++ ++json_t *json_array_get(const json_t *array, size_t index) ++{ ++ ck_abort(); ++ return 0; ++} ++ ++size_t json_array_size(const json_t *array) ++{ ++ ck_abort(); ++ return 0; ++} ++ ++int ++parse_json_string(json_t *json, char const *name, char const **result) ++{ ++ ck_abort(); ++ return 0; ++} ++ ++ ++struct string_array const * ++config_get_rsync_args(bool b) ++{ ++ static char const *strs[] = { ++ /* Note, --bwlimit does not seem to exist in openrsync */ ++ "--bwlimit=1K", "-vvv", "$REMOTE", "$LOCAL" ++ }; ++ static struct string_array args; ++ if (args.length == 0) ++ string_array_init(&args, strs, ARRAY_LEN(strs)); ++ return &args; ++} ++ ++/* Tests */ ++ ++static void ++disable_sigpipe(void) ++{ ++ struct sigaction action = { .sa_handler = SIG_IGN }; ++ if (sigaction(SIGPIPE, &action, NULL) == -1) ++ pr_crit("Cannot disable SIGPIPE: %s", strerror(errno)); ++} ++ ++static void ++init_content(void) ++{ ++ size_t i; ++ ++ if (sizeof(content) % STR64LEN != 0) ++ pr_crit("content's length isn't divisible by str64's length"); ++ for (i = 0; i < (sizeof(content) / STR64LEN); i++) ++ memcpy(content + 64 * i, STR64, STR64LEN); ++} ++ ++static void ++init_tmp(void) ++{ ++ int res = mkdir("tmp/", 0700); ++ if (res && errno != EEXIST) ++ pr_crit("Could not create tmp/: %s", strerror(errno)); ++} ++ ++static void * ++rsync_fast(void *arg) ++{ ++ int fds[2][2]; ++ memcpy(fds, arg, sizeof(fds)); ++ ++ ck_assert_int_eq(STR64LEN, write(STDERR_WRITE(fds), STR64, STR64LEN)); ++ ck_assert_int_eq(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ ck_assert_int_eq(STR64LEN, write(STDERR_WRITE(fds), STR64, STR64LEN)); ++ ck_assert_int_eq(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ ++ close(STDERR_WRITE(fds)); ++ close(STDOUT_WRITE(fds)); ++ return NULL; ++} ++ ++static void * ++rsync_stalled(void *arg) ++{ ++ int fds[2][2]; ++ memcpy(fds, arg, sizeof(fds)); ++ ++ ck_assert_int_eq(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ ++ sleep(5); /* The timeout is 4 seconds */ ++ ++ ck_assert_int_ne(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ ++ close(STDERR_WRITE(fds)); ++ close(STDOUT_WRITE(fds)); ++ return NULL; ++} ++ ++static void * ++rsync_drip_feed(void *arg) ++{ ++ int fds[2][2]; ++ memcpy(fds, arg, sizeof(fds)); ++ ++ ck_assert_int_eq(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ sleep(1); ++ ck_assert_int_eq(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ ck_assert_int_eq(STR64LEN, write(STDERR_WRITE(fds), STR64, STR64LEN)); ++ sleep(1); ++ ck_assert_int_eq(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ sleep(1); ++ ck_assert_int_eq(STR64LEN, write(STDERR_WRITE(fds), STR64, STR64LEN)); ++ sleep(2); ++ ck_assert_int_ne(STR64LEN, write(STDOUT_WRITE(fds), STR64, STR64LEN)); ++ ++ close(STDERR_WRITE(fds)); ++ close(STDOUT_WRITE(fds)); ++ return NULL; ++} ++ ++static void ++prepare_exhaust(int fds[2][2], pthread_t *thread, void *(*rsync_simulator)(void *)) ++{ ++ ck_assert_int_eq(0, pipe(fds[0])); ++ ck_assert_int_eq(0, pipe(fds[1])); ++ ck_assert_int_eq(0, pthread_create(thread, NULL, rsync_simulator, fds)); ++} ++ ++static void ++finish_exhaust(pthread_t thread) ++{ ++ pthread_join(thread, NULL); ++} ++ ++START_TEST(exhaust_read_fds_test_normal) ++{ ++ int fds[2][2]; ++ pthread_t rsync_writer; ++ ++ printf("Normal transfer\n"); ++ prepare_exhaust(fds, &rsync_writer, rsync_fast); ++ ck_assert_int_eq(0, exhaust_read_fds(STDERR_READ(fds), STDOUT_READ(fds), true)); ++ finish_exhaust(rsync_writer); ++} ++END_TEST ++ ++START_TEST(exhaust_read_fds_test_stalled) ++{ ++ int fds[2][2]; ++ pthread_t rsync_writer; ++ ++ printf("Stalled transfer\n"); ++ prepare_exhaust(fds, &rsync_writer, rsync_stalled); ++ ck_assert_int_eq(2, exhaust_read_fds(STDERR_READ(fds), STDOUT_READ(fds), true)); ++ finish_exhaust(rsync_writer); ++} ++END_TEST ++ ++START_TEST(exhaust_read_fds_test_drip) ++{ ++ int fds[2][2]; ++ pthread_t rsync_writer; ++ ++ printf("Drip-feed\n"); ++ prepare_exhaust(fds, &rsync_writer, rsync_drip_feed); ++ ck_assert_int_eq(2, exhaust_read_fds(STDERR_READ(fds), STDOUT_READ(fds), true)); ++ finish_exhaust(rsync_writer); ++} ++END_TEST ++ ++static void ++create_file(char const *name, unsigned int kbs) ++{ ++ FILE *file; ++ unsigned int k; ++ ++ file = fopen(name, "wb"); ++ ck_assert_ptr_ne(NULL, file); ++ for (k = 0; k < kbs; k++) ++ ck_assert_int_eq(sizeof(content), fwrite(content, 1, sizeof(content), file)); ++ ck_assert_int_eq(0, fclose(file)); ++} ++ ++static void ++ensure_file_deleted(char const *name) ++{ ++ int ret; ++ int error; ++ ++ errno = 0; ++ ret = unlink(name); ++ error = errno; ++ ++ ck_assert(ret == 0 || error == ENOENT); ++} ++ ++START_TEST(full_rsync_timeout_test_1kb) ++{ ++ printf("1kb\n"); ++ create_file("tmp/1kb", 1); ++ ensure_file_deleted("tmp/1kb-copy"); ++ struct rpki_uri *uri = malloc(sizeof (struct rpki_uri)); ++ uri->global = strndup("tmp/1kb", 7); ++ uri->global_len = 7; ++ uri->local = strndup("tmp/1kb-copy", 12); ++ ck_assert_int_eq(0, do_rsync(uri, false, false)); ++} ++END_TEST ++ ++START_TEST(full_rsync_timeout_test_3kb) ++{ ++ printf("3kb\n"); ++ create_file("tmp/3kb", 3); ++ ensure_file_deleted("tmp/3kb-copy"); ++ struct rpki_uri *uri = malloc(sizeof (struct rpki_uri)); ++ uri->global = strndup("tmp/3kb", 7); ++ uri->global_len = 7; ++ uri->local = strndup("tmp/3kb-copy", 12); ++ ck_assert_int_eq(0, do_rsync(uri, false, false)); ++} ++END_TEST ++ ++START_TEST(full_rsync_timeout_test_5kb) ++{ ++ printf("5kb\n"); ++ create_file("tmp/5kb", 5); ++ ensure_file_deleted("tmp/5kb-copy"); ++ struct rpki_uri *uri = malloc(sizeof (struct rpki_uri)); ++ uri->global = strndup("tmp/5kb", 7); ++ uri->global_len = 7; ++ uri->local = strndup("tmp/5kb-copy", 12); ++ /* Max speed is 1kbps, timeout is 4 seconds */ ++ ck_assert_int_eq(EREQFAILED, do_rsync(uri, false, false)); ++} ++END_TEST ++ ++static Suite *xml_load_suite(void) ++{ ++ Suite *suite; ++ TCase *pipes; ++ ++ pipes = tcase_create("pipes"); ++ tcase_add_test(pipes, exhaust_read_fds_test_normal); ++ tcase_add_test(pipes, exhaust_read_fds_test_stalled); ++ tcase_add_test(pipes, exhaust_read_fds_test_drip); ++ tcase_add_test(pipes, full_rsync_timeout_test_1kb); ++ tcase_add_test(pipes, full_rsync_timeout_test_3kb); ++ tcase_add_test(pipes, full_rsync_timeout_test_5kb); ++ tcase_set_timeout(pipes, 6); ++ ++ suite = suite_create("rsync"); ++ suite_add_tcase(suite, pipes); ++ ++ return suite; ++} ++ ++int main(void) ++{ ++ Suite *suite; ++ SRunner *runner; ++ int tests_failed; ++ ++ printf("This test needs to exhaust some timeouts. Please be patient.\n"); ++ disable_sigpipe(); ++ init_content(); ++ init_tmp(); ++ ++ suite = xml_load_suite(); ++ ++ runner = srunner_create(suite); ++ srunner_run_all(runner, CK_NORMAL); ++ tests_failed = srunner_ntests_failed(runner); ++ srunner_free(runner); ++ ++ return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; ++} diff -Nru fort-validator-1.5.4/debian/patches/series fort-validator-1.5.4/debian/patches/series --- fort-validator-1.5.4/debian/patches/series 2023-02-07 05:56:47.000000000 +0100 +++ fort-validator-1.5.4/debian/patches/series 2025-03-29 03:13:08.000000000 +0100 @@ -1,3 +1,10 @@ paths documentation program_defaults +CVE-2024-45234.patch +CVE-2024-45235.patch +CVE-2024-45236.patch +CVE-2024-45237.patch +CVE-2024-45238.patch +CVE-2024-45239.patch +CVE-2024-48943.patch
signature.asc
Description: This is a digitally signed message part