Am Donnerstag, dem 27.03.2025 um 17:41 +0100 schrieb Daniel Leidert:
> Am Samstag, dem 01.03.2025 um 10:57 +0000 schrieb Adam D. Barratt:

[..]
> > +fort-validator (1.5.4-1+deb12u1) bookworm-security; urgency=medium
> > 
> > That should be "bookworm".

I just realized that I missde that hint. Attached the corrected
debdiff.

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

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to