Hi Adam, thanks for going through the PU. Am Samstag, dem 01.03.2025 um 10:57 +0000 schrieb Adam D. Barratt: > Control: tags -1 + moreinfo > > On Mon, 2025-02-24 at 02:40 +0100, Daniel Leidert wrote: > > 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. > > +fort-validator (1.5.4-1+deb12u1) bookworm-security; urgency=medium > > That should be "bookworm". > > + * Non-maintainer upload by the Debian LTS Team. > > Although it's harmless, p-u uploads don't really need to mention that > they're NMUs. It's quite common. Standard boilerplate for the LTS team. If you feel strongly about it, I'll change it. > -Standards-Version: 4.6.2.0 > + rsync, > +Standards-Version: 4.6.0.1 > > What happened with Standards-Version here? Good catch. I was definitely the one who changed that when I added rsync to B-D, but I have no idea how that happened (I assume an unfortunate and inadvertent key-combo in vim). I have reverted that part of the changes. Current debdiff is attached. If you have any further questions, please let me know. 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-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 <dleidert@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,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-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 <ydahhrk@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 <dleidert@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 <ydahhrk@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 <dleidert@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 <ydahhrk@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 <dleidert@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 <ydahhrk@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 <dleidert@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 <ydahhrk@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 <dleidert@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 <ydahhrk@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 <dleidert@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 <job@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 <dleidert@debian.org>
+Reviewed-By: Jochen Sprickerhof <jspricke@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
Attachment:
signature.asc
Description: This is a digitally signed message part