--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
X-Debbugs-Cc: la...@packages.debian.org
Control: affects -1 + src:lacme
User: release.debian....@packages.debian.org
Usertags: pu
[ Reason ]
Let's Encrypt has recently rotated its intermediate certificates [0].
The previously used intermediate certificates were used as trust anchors
for validation of the issued X.509 certificate before its deployment.
The intermediate rotation means the validation step currently fails with
the default configuration.
[ Impact ]
Post-issuance validation failure (and therefore unusable package) in the
default configuration, see #1072847.
[ Tests ]
lacme has a comprehensive test suite. It does not run at build time nor
via debci as it requires an open 80/tcp and test hostnames pointing to
it (to reply to ACME challenges), but I succesfully ran in manually.
Moreover the package is already used in production where it indeed
solves the post-issuance validation failure.
[ Risks ]
A quick fix would be to simply replace the old intermediates in the
certificate bundle, but that would cease to work again next time Let's
Encrypt rotate their intermediates. In fact the new intermediates
expire sooner than the old ones (2 years from now, but of course the
rotation is likely not to happen at the last minute). This appears to
be deliberate in order to discourage pining them [0].
The validation logic has therefore changed to use arbitrary
intermediate(s) provided during the issuance step as -untrusted (for
chain building). Only the root certificates are used as trust anchor.
The patch is rather trival:
diff -Nru lacme-0.8.0/lacme lacme-0.8.0/lacme
--- lacme-0.8.0/lacme 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/lacme 2024-06-13 19:23:31.000000000 +0200
@@ -826,9 +826,14 @@
The (patch-applied) debdiff is longer as it includes changes to the test
suite made to accomodate Let's Encrypt current staging environment.
These changes could be omitted from debian/patches since the test suite
doesn't run, but I think there is some value in having working manual
tests out of the box.
[ 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 oldstable
[x] the issue is verified as fixed in unstable
[ Changes ]
* Backport upstream patch to fix post-issuance validation logic.
Avoid pining the intermediate certificates in the bundle and instead
validate the leaf certificate with intermediate(s) supplied during
issuance as untrusted (used for chain building only). Only the root
certificates are used as trust anchor.
Not pining intermediate certificates is in line with Let's Encrypt's
latest recommendations.
Closes: #1072847
* Adjust test suite against current Let's Encrypt staging environment.
--
Guilhem.
[0] https://letsencrypt.org/2024/03/19/new-intermediate-certificates
diffstat for lacme-0.8.0 lacme-0.8.0
Makefile | 8 +---
debian/changelog | 13 +++++++
debian/patches/series | 2 +
lacme | 7 +++-
tests/account-encrypted-gpg | 2 -
tests/account-encrypted-openssl | 1
tests/accountd | 1
tests/accountd-kid | 4 +-
tests/cert-install | 69 +++++++++++++++++++++++++++++++---------
tests/cert-revoke | 4 +-
tests/cert-verify | 20 ++---------
tests/old-accountd | 1
tests/old-lacme | 1
13 files changed, 91 insertions(+), 42 deletions(-)
diff -Nru lacme-0.8.0/debian/changelog lacme-0.8.0/debian/changelog
--- lacme-0.8.0/debian/changelog 2023-04-28 10:25:54.000000000 +0200
+++ lacme-0.8.0/debian/changelog 2024-06-13 19:19:07.000000000 +0200
@@ -1,3 +1,16 @@
+lacme (0.8.0-2+deb11u2) bullseye; urgency=medium
+
+ * Backport upstream patches to fix fix post-issuance validation logic.
+ We avoid pining the intermediate certificates in the bundle and instead
+ validate the leaf certificate with intermediates supplied during issuance
+ as untrusted (used for chain building only). Only the root certificates
+ are used as trust anchor. Not pining intermediate certificates is in line
+ with Let's Encrypt's latest recommendations.
+ Closes: #1072847
+ * Adjust test suite against current Let's Encrypt staging environment.
+
+ -- Guilhem Moulin <guil...@debian.org> Thu, 13 Jun 2024 19:19:07 +0200
+
lacme (0.8.0-2+deb11u1) bullseye; urgency=medium
* client: Handle "ready" → "processing" → "valid" status change during
diff -Nru lacme-0.8.0/debian/patches/series lacme-0.8.0/debian/patches/series
--- lacme-0.8.0/debian/patches/series 2023-04-28 10:25:54.000000000 +0200
+++ lacme-0.8.0/debian/patches/series 2024-06-13 19:19:07.000000000 +0200
@@ -1,2 +1,4 @@
Mention-the-Debian-BTS-in-the-manpages.patch
client-Handle-ready-processing-valid-status-change-during.patch
+Fix-post-issuance-validation-logic.patch
+Adjust-test-suite-against-current-Let-s-Encrypt-staging-e.patch
diff -Nru lacme-0.8.0/lacme lacme-0.8.0/lacme
--- lacme-0.8.0/lacme 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/lacme 2024-06-13 19:23:31.000000000 +0200
@@ -826,9 +826,14 @@
# verify certificate validity against the CA bundle
if ((my $CAfile = $conf->{CAfile} //
'@@datadir@@/lacme/ca-certificates.crt') ne '') {
+ my $chain_tmp = File::Temp::->new(SUFFIX => '.crt', TMPDIR => 1)
// die;
+ $chain_tmp->printflush($x509);
+
my %args = (in => $x509);
$args{out} = \*STDERR if $OPTS{debug};
- my @options = ('-trusted', $CAfile, '-purpose', 'sslserver',
'-x509_strict');
+ my @options = ('-trusted', $CAfile);
+ push @options, '-untrusted', $chain_tmp->filename();
+ push @options, ('-purpose', 'sslserver', '-x509_strict');
push @options, '-show_chain' if $OPTS{debug};
if (spawn(\%args, 'openssl', 'verify', @options)) {
print STDERR "[$s] Error: Received invalid X.509 certificate
from ACME server!\n";
diff -Nru lacme-0.8.0/Makefile lacme-0.8.0/Makefile
--- lacme-0.8.0/Makefile 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/Makefile 2024-06-13 19:23:31.000000000 +0200
@@ -16,17 +16,13 @@
# used for validation, see https://letsencrypt.org/certificates/
$(BUILDDIR)/certs/ca-certificates.crt: \
certs/isrgrootx1.pem \
- certs/isrg-root-x2.pem \
- certs/lets-encrypt-r[34].pem \
- certs/lets-encrypt-e[12].pem
+ certs/isrg-root-x2.pem
mkdir -pv -- $(dir $@)
cat -- $^ >$@
# Staging Environment for tests, see
https://letsencrypt.org/docs/staging-environment/
$(BUILDDIR)/certs-staging/ca-certificates.crt: \
- certs-staging/letsencrypt-stg-root-x[12].pem \
- certs-staging/letsencrypt-stg-int-r[34].pem \
- certs-staging/letsencrypt-stg-int-e[12].pem
+ certs-staging/letsencrypt-stg-root-x[12].pem
mkdir -pv -- $(dir $@)
cat -- $^ >$@
diff -Nru lacme-0.8.0/tests/accountd lacme-0.8.0/tests/accountd
--- lacme-0.8.0/tests/accountd 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/accountd 2024-06-13 19:23:31.000000000 +0200
@@ -65,6 +65,7 @@
# rotate the log and start accountd
rm -f ~lacme-account/.local/share/lacme/accountd.log
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" --quiet & PID=$!
+sleep 1
# run lacme(8) multiple times using that single lacme-accountd(1) instance
lacme --socket="$SOCKET" --debug account 2>"$STDERR" || fail
diff -Nru lacme-0.8.0/tests/accountd-kid lacme-0.8.0/tests/accountd-kid
--- lacme-0.8.0/tests/accountd-kid 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/accountd-kid 2024-06-13 19:23:31.000000000 +0200
@@ -23,6 +23,7 @@
SOCKET=~lacme-account/S.lacme
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" --quiet & PID=$!
+sleep 1
# newAccount resource fails as per RFC 8555 sec. 6.2 it requires a JWK
! lacme --socket="$SOCKET" account 2>"$STDERR" || fail
@@ -37,6 +38,7 @@
rm ~lacme-account/.local/share/lacme/accountd.log
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" --quiet & PID=$!
+sleep 1
# newOrder works fine without JWK
lacme --socket="$SOCKET" newOrder
@@ -46,7 +48,7 @@
lacme --socket="$SOCKET" revokeCert /etc/lacme/simpletest.rsa.crt
! lacme --socket="$SOCKET" revokeCert /etc/lacme/simpletest.rsa.crt
2>"$STDERR" || fail
grepstderr -Fxq "Revoking /etc/lacme/simpletest.rsa.crt"
-grepstderr -Fxq "400 Bad Request (Certificate already revoked)"
+grepstderr -Fq "400 Bad Request (unable to revoke"
grepstderr -Fxq "Warning: Couldn't revoke /etc/lacme/simpletest.rsa.crt"
kill $PID
diff -Nru lacme-0.8.0/tests/account-encrypted-gpg
lacme-0.8.0/tests/account-encrypted-gpg
--- lacme-0.8.0/tests/account-encrypted-gpg 2021-02-22 03:19:57.000000000
+0100
+++ lacme-0.8.0/tests/account-encrypted-gpg 2024-06-13 19:23:31.000000000
+0200
@@ -9,7 +9,7 @@
gpg --encrypt -r "$keyid" /etc/lacme/account.key
sed -ri '0,\|^#?privkey\s*=.*| {s||privkey = gpg:/etc/lacme/account.key.gpg|}'
/etc/lacme/lacme-accountd.conf
-export GPG_TTY="$(tty)"
+export GPG_TTY="$(tty)" TERM="linux"
lacme account
# vim: set filetype=sh :
diff -Nru lacme-0.8.0/tests/account-encrypted-openssl
lacme-0.8.0/tests/account-encrypted-openssl
--- lacme-0.8.0/tests/account-encrypted-openssl 2021-02-22 03:19:57.000000000
+0100
+++ lacme-0.8.0/tests/account-encrypted-openssl 2024-06-13 19:23:31.000000000
+0200
@@ -5,6 +5,7 @@
openssl rsa -aes128 -passout pass:"$PASSPHRASE" </etc/lacme/account.key
>/etc/lacme/account.enc.key
sed -ri '0,\|^#?privkey\s*=.*| {s||privkey =
file:/etc/lacme/account.enc.key|}' /etc/lacme/lacme-accountd.conf
+export TERM="linux"
lacme account
# vim: set filetype=sh :
diff -Nru lacme-0.8.0/tests/cert-install lacme-0.8.0/tests/cert-install
--- lacme-0.8.0/tests/cert-install 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/cert-install 2024-06-13 19:23:31.000000000 +0200
@@ -28,9 +28,58 @@
grepstderr -Fxq "[bad3] Warning: Couldn't generate CSR, skipping"
+check_spki() {
+ local p1="$1" p2="$2" s1 s2
+ s1="$(openssl x509 -in "$p1" -noout -pubkey \
+ | openssl pkey -pubin -outform DER \
+ | openssl dgst -sha256 \
+ | sed 's/.*=\s*//')"
+ s2="$(openssl pkey -in "$p2" -pubout -outform DER \
+ | openssl dgst -sha256 \
+ | sed 's/.*=\s*//')"
+ if [ -n "$s1" ] && [ "$s1" = "$s2" ]; then
+ return 0
+ else
+ printf "%s != %s\\n" "$s1" "$s2" >&2
+ return 1
+ fi
+}
+check_chain() {
+ local priv="$1" chain="$2" leaf="${3-}" pem0
+
+ csplit -f "${chain%.crt}.chain.pem" "$chain" \
+ "/-----BEGIN CERTIFICATE-----/" "{*}"
+
+ pem0="${chain%.crt}.chain.pem00"
+ if [ ! -s "$pem0" ]; then
+ # 00 is empty, leaf cert is at 01
+ rm -f -- "$pem0"
+ pem0="${chain%.crt}.chain.pem01"
+ fi
+ test -s "$pem0" || return 1
+ check_spki "$pem0" "$priv"
+
+ if [ -n "$leaf" ]; then
+ diff --ignore-blank-lines --unified "$pem0" "$leaf" || return 1
+ fi
+
+ leaf="${chain%.crt}.leaf.pem"
+ mv -T -- "$pem0" "$leaf"
+
+ intermediates="${chain%.crt}.intermediates.pem"
+ sed "/^$/d" "${chain%.crt}.chain.pem"[0-9]* >"$intermediates"
+ test -s "$intermediates" || return 1 # ensure there is at least one
intermediate
+
+ openssl verify -trusted /usr/share/lacme/ca-certificates.crt \
+ -untrusted "$intermediates" \
+ -purpose sslserver -x509_strict \
+ -show_chain \
+ -- "$leaf" || return 1
+}
+
# 'certificate' installs only the leaf certificate
openssl genpkey -algorithm RSA -out /etc/lacme/test1.key
-subject="/CN=$(head -c10 /dev/urandom | base32 -w0).$DOMAINNAME"
+subject="/CN=$(head -c10 /dev/urandom | base32 -w0 | tr "A-Z"
"a-z").$DOMAINNAME"
cat >"/etc/lacme/lacme-certs.conf.d/test1.conf" <<- EOF
[test1]
certificate-key = /etc/lacme/test1.key
@@ -42,6 +91,7 @@
test /etc/lacme/test1.crt -nt /etc/lacme/test1.key
sed -n "0,/^-----END CERTIFICATE-----$/ p" /etc/lacme/test1.crt
>/etc/lacme/test1.pem
diff --unified /etc/lacme/test1.crt /etc/lacme/test1.pem
+check_spki /etc/lacme/test1.crt /etc/lacme/test1.key
check_hash() {
@@ -70,16 +120,7 @@
lacme newOrder test2 2>"$STDERR" || fail newOrder test2
test /etc/lacme/test2.crt -nt /etc/lacme/test2.key
-csplit -f /etc/lacme/test2.chain.pem /etc/lacme/test2.crt \
- "/-----BEGIN CERTIFICATE-----/" "{*}"
-test -s /etc/lacme/test2.chain.pem01 # leaf cert (00 is empty)
-rm -f /etc/lacme/test2.chain.pem0[01]
-test -s /etc/lacme/test2.chain.pem02 # depth 1
-
-# all certificates at depth >=1 must be in our CA bundle
-for p in /etc/lacme/test2.chain.pem*; do
- check_hash "$p"
-done
+check_chain /etc/lacme/test2.key /etc/lacme/test2.crt
# 'certificate' + 'certificate-chain'
openssl genpkey -algorithm RSA -out /etc/lacme/test3.key
@@ -94,10 +135,8 @@
lacme newOrder test3 2>"$STDERR" || fail newOrder test3
test /etc/lacme/test3.pem -nt /etc/lacme/test3.key
test /etc/lacme/test3.crt -nt /etc/lacme/test3.key
-csplit -f /etc/lacme/test3.chain.pem /etc/lacme/test3.crt \
- "/-----BEGIN CERTIFICATE-----/" "{*}"
-sed -i "/^$/d" /etc/lacme/test3.chain.pem*
-diff -q /etc/lacme/test3.chain.pem01 /etc/lacme/test3.pem
+check_chain /etc/lacme/test3.key /etc/lacme/test3.crt /etc/lacme/test3.pem
+
st="$(stat -c "%U:%G %#a" /etc/lacme/test3.pem)"
[ "$st" = "root:root 0644" ]
st="$(stat -c "%U:%G %#a" /etc/lacme/test3.crt)"
diff -Nru lacme-0.8.0/tests/cert-revoke lacme-0.8.0/tests/cert-revoke
--- lacme-0.8.0/tests/cert-revoke 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/cert-revoke 2024-06-13 19:23:31.000000000 +0200
@@ -18,7 +18,7 @@
lacme revokeCert /etc/lacme/simpletest.ecdsa.crt
! lacme revokeCert /etc/lacme/simpletest.ecdsa.crt 2>"$STDERR" || fail
grepstderr -Fxq "Revoking /etc/lacme/simpletest.ecdsa.crt"
-grepstderr -Fxq "400 Bad Request (Certificate already revoked)"
+grepstderr -Fq "400 Bad Request (unable to revoke"
grepstderr -Fxq "Warning: Couldn't revoke /etc/lacme/simpletest.ecdsa.crt"
# and the RSA certificate using the service key
@@ -26,7 +26,7 @@
lacme revokeCert /etc/lacme/simpletest.rsa.crt
! lacme revokeCert /etc/lacme/simpletest.rsa.crt 2>"$STDERR" || fail
grepstderr -Fxq "Revoking /etc/lacme/simpletest.rsa.crt"
-grepstderr -Fxq "400 Bad Request (Certificate already revoked)"
+grepstderr -Fq "400 Bad Request (unable to revoke"
grepstderr -Fxq "Warning: Couldn't revoke /etc/lacme/simpletest.rsa.crt"
# vim: set filetype=sh :
diff -Nru lacme-0.8.0/tests/cert-verify lacme-0.8.0/tests/cert-verify
--- lacme-0.8.0/tests/cert-verify 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/cert-verify 2024-06-13 19:23:31.000000000 +0200
@@ -8,9 +8,9 @@
done
update-ca-certificates
-# test (modified) trust store for intermediate certificates
-openssl verify -no-CAfile -CApath /etc/ssl/certs
-show_chain /usr/share/lacme/letsencrypt-stg-int-*.pem
-openssl verify -no-CApath -CAfile /etc/ssl/certs/ca-certificates.crt
-show_chain /usr/share/lacme/letsencrypt-stg-int-*.pem
+# test (modified) trust store
+openssl verify -no-CAfile -CApath /etc/ssl/certs
-show_chain /usr/share/lacme/letsencrypt-stg-root-x1.pem
+openssl verify -no-CApath -CAfile /etc/ssl/certs/ca-certificates.crt
-show_chain /usr/share/lacme/letsencrypt-stg-root-x1.pem
mv /usr/share/lacme/ca-certificates.crt
/usr/share/lacme/ca-certificates.crt.back
! lacme newOrder 2>"$STDERR" || fail
@@ -20,19 +20,7 @@
# verification error for unrelated CA bundle
cat /etc/ssl/certs/ssl-cert-snakeoil.pem >/usr/share/lacme/ca-certificates.crt
! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "error 20 at 0 depth lookup: unable to get local issuer
certificate"
-grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate
from ACME server!"
-
-# verification error when the CA bundle contains only the root certificates
-cat /usr/share/lacme/letsencrypt-stg-root-*.pem
>/usr/share/lacme/ca-certificates.crt
-! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "error 20 at 0 depth lookup: unable to get local issuer
certificate"
-grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate
from ACME server!"
-
-# verification error when the CA bundle contains only the intermediate
certificates
-cat /usr/share/lacme/letsencrypt-stg-int-*.pem
>/usr/share/lacme/ca-certificates.crt
-! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "error 2 at 1 depth lookup: unable to get issuer certificate"
+grepstderr -Fxq "error 20 at 1 depth lookup: unable to get local issuer
certificate"
grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate
from ACME server!"
# use saved bundle as custom CAfile
diff -Nru lacme-0.8.0/tests/old-accountd lacme-0.8.0/tests/old-accountd
--- lacme-0.8.0/tests/old-accountd 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/old-accountd 2024-06-13 19:23:31.000000000 +0200
@@ -21,6 +21,7 @@
SOCKET=~lacme-account/S.lacme
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" & PID=$!
+sleep 1
lacme --socket="$SOCKET" account
lacme --socket="$SOCKET" newOrder
diff -Nru lacme-0.8.0/tests/old-lacme lacme-0.8.0/tests/old-lacme
--- lacme-0.8.0/tests/old-lacme 2021-02-22 03:19:57.000000000 +0100
+++ lacme-0.8.0/tests/old-lacme 2024-06-13 19:23:31.000000000 +0200
@@ -26,6 +26,7 @@
SOCKET=~lacme-account/S.lacme
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" & PID=$!
+sleep 1
sed -ri "s/^\[accountd]$/#&/" /etc/lacme/lacme.conf #
https://bugs.debian.org/955767
lacme --socket="$SOCKET" account
lacme --socket="$SOCKET" newOrder
signature.asc
Description: PGP signature
--- End Message ---