Package: release.debian.org Severity: normal Tags: bookworm X-Debbugs-Cc: fort-valida...@packages.debian.org Control: affects -1 + src:fort-validator User: release.debian....@packages.debian.org Usertags: pu
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 [ Reason ] There are multiple known CVEs (CVE-2024-45234, CVE-2024-45235, CVE-2024-45236, CVE-2024-45237, CVE-2024-45238, CVE-2024-45239, CVE-2024-48943). These have been fixed in Bullseye, and they are also fixed in Trixie/Sid. This update intends to provide a fix for users of Debian Bookworm as well. [ Impact ] If the update is not approved, users will continue to be vulnerable to the listed vulnerabilities. [ Tests ] The tests provided by upstream have already enabled in this version. For CVE-2024-48943, a separate test case has been added. [ Risks ] There is always a risk of regression. But the changed test cases ran successfully. Furthermore, the update has also been provided to Bullseye via DLA 4066-1. [ Checklist ] [x] *all* changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in (old)stable [x] the issue is verified as fixed in unstable [ Changes ] For CVE-2024-45234, the fix consists of adding a safety to prevent a potential crash. A few functions have been rewritten to return an error value, which can be checked. For CVE-2024-45235, a NULL check has been added. For CVE-2024-45236, the fix adds another NULL check for signedAttrs and some meaningful error messages. For CVE-2024-45237, the fix adds a length check to prevent a buffer overflow. For CVE-2024-45238, the fix adds another NULL check. For CVE-2024-45239, the fix contains a special case handling to prevent a potential crash. The fix for CVE-2024-48943 is the most complex one, but it also contains a new testcase. It introduces a timeout feature that can be turned off to regain the original behavior. This shall prevent a DoS like situation with rsync by killing it after reaching the timeout. Although one can argue that not all changes that upstream did here are necessary for the task, IMHO it is best to use the upstream code instead of risking regressions by attempting to rewrite it. This change also requires rsync to be present in the build environment. [ Other info ] The patches contain further information about bug reports, patch origins, etc. -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEvu1N7VVEpMA+KD3HS80FZ8KW0F0FAme7zfIACgkQS80FZ8KW 0F1p4RAA2jnvHZ3/61nWxe8X6jLwUR/tNpUmSEtvSHA7awkfIyD01UP50OfVDyRu i3oHHQA9NYhfOXrj141/1AqNqtIUHdf4R5tv7PEBEvQtPezqTyY4ZIq6iZWSh0Ev bm5/Ak6cM3k0eiiYoWxyzaBs7fnBMD9sQ7ygypJ7J/xWNPd0e14oxSEJfI5W4IKp 74oqiiSGlJSIY16tT0F/y6sFncBcy1tgrFR6/8NzXx0iR8Vx4+jC1fsjczRpgXrj qmnrCmaAlI31DnFKk/C7rCcLAYEfFpP8DGAtVdHMYi5QF/SzX2VmpygGT2VbDXOS R7cQRkQ6k/VM9NQTkHMzjFnf+wanLLafiH//BS9LeN1uStuRQaj2Yg6qy8GF8jAc KJtOMl8YYWbzOfb0Mpm+VhhJg6Qmw9Dso4dJP/iP171JBpBaaDpcAN2IDiKS1c+O Xe5aqrPD71MLTwJ9s9HcgyF4vrrQu2I0+ucVj8XNz+o5nyfjm+W0PeXHx0hn72j7 qng0+k/hUZo0bXkz8tFMGxJp51b8cdNaAWZOZsrklvMD9DUulqOpuHmSuZkZanze vIiVcChb8yBW4k5vJ3Fvrk13ovbSNNh82s+yTV4j4JTwfQlkj05QExQ6SRjBxghT nKCIE/f/Odp/EUT4i/rLfkPWUwljmItjr1Xyj3xAULCrdY0L9R4= =v3qz -----END PGP SIGNATURE-----
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-02-24 01:34:04.000000000 +0100 @@ -1,3 +1,57 @@ +fort-validator (1.5.4-1+deb12u1) bookworm-security; 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> Mon, 24 Feb 2025 01:34:04 +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-02-24 01:34:04.000000000 +0100 @@ -10,7 +10,8 @@ libjansson-dev, libssl-dev, libxml2-dev, -Standards-Version: 4.6.2.0 + rsync, +Standards-Version: 4.6.0.1 Rules-Requires-Root: no Homepage: https://nicmx.github.io/FORT-validator/ Vcs-Git: https://salsa.debian.org/md/fort-validator.git 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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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-02-24 01:34:04.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