summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/share/h2o/fetch-ocsp-response
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xweb/server/h2o/libh2o/share/h2o/fetch-ocsp-response164
1 files changed, 164 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/share/h2o/fetch-ocsp-response b/web/server/h2o/libh2o/share/h2o/fetch-ocsp-response
new file mode 100755
index 00000000..f74b738b
--- /dev/null
+++ b/web/server/h2o/libh2o/share/h2o/fetch-ocsp-response
@@ -0,0 +1,164 @@
+#! /bin/sh
+exec ${H2O_PERL:-perl} -x $0 "$@"
+#! perl
+
+# Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku, Tatsuhiro Tsujikawa
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+use strict;
+use warnings;
+use File::Temp qw(tempdir);
+use Getopt::Long;
+
+# from sysexits.h
+use constant EX_TEMPFAIL => 75;
+
+my ($issuer_fn, $opt_help);
+my $openssl_cmd = 'openssl';
+
+GetOptions(
+ "issuer=s" => \$issuer_fn,
+ "openssl=s", => \$openssl_cmd,
+ help => \$opt_help,
+) or exit(1);
+if ($opt_help) {
+ print << "EOT";
+Usage: $0 [<options>] <certificate-file>
+
+Options:
+ --issuer <file> issuer certificate (if omitted, is extracted from the
+ certificate chain)
+ --openssl <cmd> openssl command to use (default: "openssl")
+ --help prints this help
+
+The command issues an OCSP request for given server certificate, verifies the
+response and prints the resulting DER.
+
+The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error.
+Other exit codes may be returned in case of hard errors.
+
+EOT
+ exit(0);
+}
+
+die "no certificate file\n"
+ if @ARGV == 0;
+my $cert_fn = shift @ARGV;
+
+my $tempdir = tempdir(CLEANUP => 1);
+
+my $openssl_version = run_openssl("version");
+chomp $openssl_version;
+print STDERR "fetch-ocsp-response (using $openssl_version)\n";
+
+# obtain ocsp uri
+my $ocsp_uri = run_openssl("x509 -in $cert_fn -noout -ocsp_uri");
+chomp $ocsp_uri;
+die "failed to extract ocsp URI from $cert_fn\n"
+ if $ocsp_uri !~ m{^https?://};
+my($ocsp_host) = $ocsp_uri =~ m{^https?://([^/]+)};
+
+# save issuer certificate
+if (! defined $issuer_fn) {
+ my $chain = read_file($cert_fn);
+ $chain =~ m{-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)}s
+ or die "--issuer option was not used, and failed to extract issuer certificate from the certificate\n";
+ $issuer_fn = "$tempdir/issuer.crt";
+ write_file($issuer_fn, "$1\n");
+}
+
+# obtain response (without verification)
+print STDERR "sending OCSP request to $ocsp_uri\n";
+my $resp = run_openssl(
+ "ocsp -issuer $issuer_fn -cert $cert_fn -url $ocsp_uri"
+ . ($openssl_version =~ /^(OpenSSL 1\.|LibreSSL )/is ? " -header Host@{[$openssl_version =~ /^(OpenSSL 1\.0\.|LibreSSL )/is ? ' ' : '=']}$ocsp_host" : "")
+ . " -noverify -respout $tempdir/resp.der " . join(' ', @ARGV),
+ 1,
+);
+print STDERR $resp;
+
+# OpenSSL 1.0.2 still returns exit code 0 even if ocsp responder
+# returned error status (e.g., trylater(3))
+ die "responder returned error\n"
+ if $resp =~ /Responder Error:/is;
+
+# verify the response
+print STDERR "verifying the response signature\n";
+my $success;
+for my $args (
+ # try from exotic options
+ "-VAfile $issuer_fn", # for comodo
+ "-partial_chain -trusted_first -CAfile $issuer_fn", # these options are only available in OpenSSL >= 1.0.2
+ "-CAfile $issuer_fn", # for OpenSSL <= 1.0.1
+) {
+ if (system("$openssl_cmd ocsp -respin $tempdir/resp.der $args > $tempdir/verify.out 2>&1") == 0) {
+ # OpenSSL <= 1.0.1, openssl ocsp still returns exit code 0
+ # even if verification was failed. So check the error message
+ # in stderr output.
+ my $verifyout = read_file("$tempdir/verify.out");
+ if ($verifyout =~ /Response Verify Failure/is) {
+ print STDERR $verifyout;
+ print STDERR "try next verify argument options\n";
+ next;
+ }
+ print STDERR "verify OK (used: $args)\n";
+ $success = 1;
+ last;
+ }
+}
+if (! $success) {
+ print STDERR read_file("$tempdir/verify.out");
+ tempfail("failed to verify the response\n");
+}
+
+# success
+print read_file("$tempdir/resp.der");
+exit 0;
+
+sub run_openssl {
+ my ($args, $tempfail) = @_;
+ open my $fh, "-|", "$openssl_cmd $args"
+ or die "failed to invoke $openssl_cmd:$!";
+ my $resp = do { local $/; <$fh> };
+ close $fh
+ or ($tempfail ? \&tempfail : \&CORE::die)->("OpenSSL exitted abnormally: $openssl_cmd $args:$!");
+ $resp;
+}
+
+sub read_file {
+ my $fn = shift;
+ open my $fh, "<", $fn
+ or die "failed to open file:$fn:$!";
+ local $/;
+ <$fh>;
+}
+
+sub write_file {
+ my ($fn, $data) = @_;
+ open my $fh, ">", $fn
+ or die "failed to open file:$fn:$!";
+ print $fh $data;
+ close $fh;
+}
+
+sub tempfail {
+ print STDERR @_;
+ exit EX_TEMPFAIL;
+}