aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rwxr-xr-xlacme26
-rw-r--r--tests/cert-install82
3 files changed, 76 insertions, 40 deletions
diff --git a/Makefile b/Makefile
index 10e55c5..cb2f4ed 100644
--- a/Makefile
+++ b/Makefile
@@ -16,17 +16,13 @@ $(MANUAL_FILES): $(BUILDDIR)/%: $(BUILDDIR)/%.md
# 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 -- $(@D)
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 -- $(@D)
cat -- $^ >$@
diff --git a/lacme b/lacme
index 19d78a9..b167f9b 100755
--- a/lacme
+++ b/lacme
@@ -822,21 +822,31 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
next;
}
- my $cert;
+ my @chain;
eval {
my $mem = Net::SSLeay::BIO_s_mem() or die;
my $bio = Net::SSLeay::BIO_new($mem) or die;
die "incomplete write" unless
Net::SSLeay::BIO_write($bio, $chain) == length($chain);
- my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
- $cert = Net::SSLeay::PEM_get_string_X509($x509);
+
+ my $sk_x509_info = Net::SSLeay::PEM_X509_INFO_read_bio($bio);
+
+ my $n = Net::SSLeay::sk_X509_INFO_num($sk_x509_info);
+ for (my $i = 0; $i < $n; $i++) {
+ my $x509_info = Net::SSLeay::sk_X509_INFO_value($sk_x509_info, $i);
+ my $x509 = Net::SSLeay::P_X509_INFO_get_x509($x509_info);
+ my $cert = Net::SSLeay::PEM_get_string_X509($x509);
+ push @chain, $cert;
+ }
+
Net::SSLeay::BIO_free($bio) or die;
};
- if ($@) {
+ if ($@ or !@chain) {
print STDERR "[$s] Error: Received bogus X.509 certificate from ACME server!\n";
$rv = 1;
next;
}
+ my $cert = shift @chain; # leave only the intermediate in @chain
# extract pubkeys from CSR and cert, and ensure they match
# XXX would be nice to use X509_get_X509_PUBKEY and X509_REQ_get_X509_PUBKEY here,
@@ -852,9 +862,15 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
# 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->say($_) foreach @chain;
+ $chain_tmp->flush();
+
my %args = (in => $cert);
$args{out} = \*STDERR if $OPTS{debug};
- my @options = ('-trusted', $CAfile, '-purpose', 'sslserver', '-x509_strict');
+ my @options = ('-trusted', $CAfile);
+ push @options, '-untrusted', $chain_tmp->filename() if @chain;
+ 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 --git a/tests/cert-install b/tests/cert-install
index e24fe34..279309f 100644
--- a/tests/cert-install
+++ b/tests/cert-install
@@ -28,6 +28,55 @@ EOF
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 | tr "A-Z" "a-z").$DOMAINNAME"
@@ -42,23 +91,9 @@ lacme newOrder test1 2>"$STDERR" || fail newOrder test1
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() {
- local p1="$1" p2 s1 s2
- s1="$(openssl x509 -in "$p1" -noout -hash)"
- for p2 in /usr/share/lacme/ca-certificates.pem.*; do
- s2="$(openssl x509 -in "$p2" -noout -hash)"
- if [ "$s1" = "$s2" ]; then
- return 0
- fi
- done
- return 1
-}
-csplit -f /usr/share/lacme/ca-certificates.pem. /usr/share/lacme/ca-certificates.crt \
- "/-----BEGIN CERTIFICATE-----/" "{*}"
-rm -f /usr/share/lacme/ca-certificates.pem.00
-
# 'certificate-chain' appends the chain of trust
openssl genpkey -algorithm RSA -out /etc/lacme/test2.key
cat >"/etc/lacme/lacme-certs.conf.d/test2.conf" <<- EOF
@@ -70,16 +105,7 @@ EOF
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 +120,8 @@ EOF
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)"