diff --git a/test/recipes/80-test_cmp_http.t b/test/recipes/80-test_cmp_http.t new file mode 100644 index 0000000000000000000000000000000000000000..1dc76e5fd390331c10a3ee6fa8a06d661255fb1c --- /dev/null +++ b/test/recipes/80-test_cmp_http.t @@ -0,0 +1,284 @@ +#! /usr/bin/env perl +# Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved. +# Copyright Nokia 2007-2019 +# Copyright Siemens AG 2015-2019 +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use POSIX; +use OpenSSL::Test qw/:DEFAULT with data_file data_dir srctop_dir bldtop_dir result_dir/; +use OpenSSL::Test::Utils; + +BEGIN { + setup("test_cmp_http"); +} +use lib srctop_dir('Configurations'); +use lib bldtop_dir('.'); +use platform; +plan skip_all => "These tests are not supported in a fuzz build" + if config('options') =~ /-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION/; + +plan skip_all => "These tests are not supported in a no-cmp build" + if disabled("cmp"); +plan skip_all => "These tests are not supported in a no-ec build" + if disabled("ec"); + +plan skip_all => "Tests involving local HTTP server not available on Windows or VMS" + if $^O =~ /^(VMS|MSWin32)$/; +plan skip_all => "Tests involving local HTTP server not available in cross-compile builds" + if defined $ENV{EXE_SHELL}; +plan skip_all => "Tests involving local HTTP server require 'kill' command" + if system("which kill"); +plan skip_all => "Tests involving local HTTP server require 'lsof' command" + if system("which lsof"); # this typically excludes Solaris + +sub chop_dblquot { # chop any leading and trailing '"' (needed for Windows) + my $str = shift; + $str =~ s/^\"(.*?)\"$/$1/; + return $str; +} + +my $proxy = ""; +$proxy = chop_dblquot($ENV{http_proxy} // $ENV{HTTP_PROXY} // $proxy); +$proxy =~ s{^https?://}{}i; +my $no_proxy = $ENV{no_proxy} // $ENV{NO_PROXY}; + +my $app = "apps/openssl cmp"; + +# the CMP server configuration consists of: +my $ca_dn; # The CA's Distinguished Name +my $server_dn; # The server's Distinguished Name +my $server_host;# The server's host name or IP address +my $server_port;# The server's port +my $server_tls; # The server's TLS port, if any, or 0 +my $server_path;# The server's CMP alias +my $server_cert;# The server's cert +my $kur_port; # The server's port for kur (cert update) +my $pbm_port; # The server port to be used for PBM +my $pbm_ref; # The reference for PBM +my $pbm_secret; # The secret for PBM +my $column; # The column number of the expected result +my $sleep = 0; # The time to sleep between two requests + +# The local $server_name variables below are among others taken as the name of a +# sub-directory with server-specific certs etc. and CA-specific config section. + +sub load_config { + my $server_name = shift; + my $section = shift; + my $test_config = $ENV{OPENSSL_CMP_CONFIG} // "$server_name/test.cnf"; + open (CH, $test_config) or die "Cannot open $test_config: $!"; + my $active = 0; + while () { + if (m/\[\s*$section\s*\]/) { + $active = 1; + } elsif (m/\[\s*.*?\s*\]/) { + $active = 0; + } elsif ($active) { + $ca_dn = $1 eq "" ? '""""' : $1 if m/^\s*ca_dn\s*=\s*(.*)?\s*$/; + $server_dn = $1 eq "" ? '""""' : $1 if m/^\s*server_dn\s*=\s*(.*)?\s*$/; + $server_host = $1 eq "" ? '""""' : $1 if m/^\s*server_host\s*=\s*(\S*)?\s*(\#.*)?$/; + $server_port = $1 eq "" ? '""""' : $1 if m/^\s*server_port\s*=\s*(.*)?\s*$/; + $server_tls = $1 eq "" ? '""""' : $1 if m/^\s*server_tls\s*=\s*(.*)?\s*$/; + $server_path = $1 eq "" ? '""""' : $1 if m/^\s*server_path\s*=\s*(.*)?\s*$/; + $server_cert = $1 eq "" ? '""""' : $1 if m/^\s*server_cert\s*=\s*(.*)?\s*$/; + $kur_port = $1 eq "" ? '""""' : $1 if m/^\s*kur_port\s*=\s*(.*)?\s*$/; + $pbm_port = $1 eq "" ? '""""' : $1 if m/^\s*pbm_port\s*=\s*(.*)?\s*$/; + $pbm_ref = $1 eq "" ? '""""' : $1 if m/^\s*pbm_ref\s*=\s*(.*)?\s*$/; + $pbm_secret = $1 eq "" ? '""""' : $1 if m/^\s*pbm_secret\s*=\s*(.*)?\s*$/; + $column = $1 eq "" ? '""""' : $1 if m/^\s*column\s*=\s*(.*)?\s*$/; + $sleep = $1 eq "" ? '""""' : $1 if m/^\s*sleep\s*=\s*(.*)?\s*$/; + } + } + close CH; + die "Cannot find all CMP server config values in $test_config section [$section]\n" + if !defined $ca_dn + || !defined $server_dn || !defined $server_host + || !defined $server_port || !defined $server_tls + || !defined $server_path || !defined $server_cert + || !defined $kur_port || !defined $pbm_port + || !defined $pbm_ref || !defined $pbm_secret + || !defined $column || !defined $sleep; + $server_dn = $server_dn // $ca_dn; +} + +my @server_configurations = ("Mock"); +@server_configurations = split /\s+/, $ENV{OPENSSL_CMP_SERVER} if $ENV{OPENSSL_CMP_SERVER}; +# set env variable, e.g., OPENSSL_CMP_SERVER="Mock Insta" to include further CMP servers + +my @all_aspects = ("connection", "verification", "credentials", "commands", "enrollment"); +@all_aspects = split /\s+/, $ENV{OPENSSL_CMP_ASPECTS} if $ENV{OPENSSL_CMP_ASPECTS}; +# set env variable, e.g., OPENSSL_CMP_ASPECTS="commands enrollment" to select specific aspects + +my $faillog; +my $file = $ENV{HARNESS_FAILLOG}; # pathname relative to result_dir +if ($file) { + open($faillog, ">", $file) or die "Cannot open $file for writing: $!"; +} + +sub test_cmp_http { + my $server_name = shift; + my $aspect = shift; + my $n = shift; + my $i = shift; + my $title = shift; + my $params = shift; + my $expected_exit = shift; + my $path_app = bldtop_dir($app); + with({ exit_checker => sub { + my $actual_exit = shift; + my $OK = $actual_exit == $expected_exit; + if ($faillog && !$OK) { + my $quote_spc_empty = sub { $_ eq "" ? '""' : $_ =~ m/ / ? '"'.$_.'"' : $_ }; + my $invocation = "$path_app ".join(' ', map $quote_spc_empty->($_), @$params); + print $faillog "$server_name $aspect \"$title\" ($i/$n)". + " expected=$expected_exit actual=$actual_exit\n"; + print $faillog "$invocation\n\n"; + } + return $OK; } }, + sub { ok(run(cmd([$path_app, @$params,])), + $title); }); +} + +sub test_cmp_http_aspect { + my $server_name = shift; + my $aspect = shift; + my $tests = shift; + subtest "CMP app CLI $server_name $aspect\n" => sub { + my $n = scalar @$tests; + plan tests => $n; + my $i = 1; + foreach (@$tests) { + test_cmp_http($server_name, $aspect, $n, $i++, $$_[0], $$_[1], $$_[2]); + sleep($sleep); + } + }; + # not unlinking test.certout*.pem, test.cacerts.pem, and test.extracerts.pem +} + +# The input files for the tests done here dynamically depend on the test server +# selected (where the Mock server used by default is just one possibility). +# On the other hand the main test configuration file test.cnf, which references +# several server-dependent input files by relative file names, is static. +# Moreover the tests use much greater variety of input files than output files. +# Therefore we chose the current directory as a subdirectory of $SRCTOP and it +# was simpler to prepend the output file names by BLDTOP than doing the tests +# from $BLDTOP/test-runs/test_cmp_http and prepending the input files by SRCTOP. + +indir data_dir() => sub { + plan tests => @server_configurations * @all_aspects + + (grep(/^Mock$/, @server_configurations) + && grep(/^certstatus$/, @all_aspects)); + + foreach my $server_name (@server_configurations) { + $server_name = chop_dblquot($server_name); + load_config($server_name, $server_name); + { + my $pid; + if ($server_name eq "Mock") { + indir "Mock" => sub { + $pid = start_mock_server(""); + skip "Cannot start or find the started CMP mock server", + scalar @all_aspects unless $pid; + } + } + foreach my $aspect (@all_aspects) { + $aspect = chop_dblquot($aspect); + next if $server_name eq "Mock" && $aspect eq "certstatus"; + load_config($server_name, $aspect); # update with any aspect-specific settings + indir $server_name => sub { + my $tests = load_tests($server_name, $aspect); + test_cmp_http_aspect($server_name, $aspect, $tests); + }; + }; + stop_mock_server($pid) if $pid; + } + }; +}; + +close($faillog) if $faillog; + +sub load_tests { + my $server_name = shift; + my $aspect = shift; + my $test_config = $ENV{OPENSSL_CMP_CONFIG} // "$server_name/test.cnf"; + my $file = data_file("test_$aspect.csv"); + my $result_dir = result_dir(); + my @result; + + open(my $data, '<', $file) || die "Cannot open $file for reading: $!"; + LOOP: + while (my $line = <$data>) { + chomp $line; + $line =~ s{\r\n}{\n}g; # adjust line endings + $line =~ s{_CA_DN}{$ca_dn}g; + $line =~ s{_SERVER_DN}{$server_dn}g; + $line =~ s{_SERVER_HOST}{$server_host}g; + $line =~ s{_SERVER_PORT}{$server_port}g; + $line =~ s{_SERVER_TLS}{$server_tls}g; + $line =~ s{_SERVER_PATH}{$server_path}g; + $line =~ s{_SERVER_CERT}{$server_cert}g; + $line =~ s{_KUR_PORT}{$kur_port}g; + $line =~ s{_PBM_PORT}{$pbm_port}g; + $line =~ s{_PBM_REF}{$pbm_ref}g; + $line =~ s{_PBM_SECRET}{$pbm_secret}g; + $line =~ s{_RESULT_DIR}{$result_dir}g; + + next LOOP if $server_tls == 0 && $line =~ m/,\s*-tls_used\s*,/; + my $noproxy = $no_proxy; + if ($line =~ m/,\s*-no_proxy\s*,(.*?)(,|$)/) { + $noproxy = $1; + } elsif ($server_host eq "127.0.0.1") { + # do connections to localhost (e.g., Mock server) without proxy + $line =~ s{-section,,}{-section,,-no_proxy,127.0.0.1,} ; + } + if ($line =~ m/,\s*-proxy\s*,/) { + next LOOP if $no_proxy && ($noproxy =~ $server_host); + } else { + $line =~ s{-section,,}{-section,,-proxy,$proxy,}; + } + $line =~ s{-section,,}{-section,,-certout,$result_dir/test.cert.pem,}; + $line =~ s{-section,,}{-config,../$test_config,-section,$server_name $aspect,}; + + my @fields = grep /\S/, split ",", $line; + s/^$// for (@fields); # used for proxy="" + s/^\s+// for (@fields); # remove leading whitespace from elements + s/\s+$// for (@fields); # remove trailing whitespace from elements + s/^\"(\".*?\")\"$/$1/ for (@fields); # remove escaping from quotation marks from elements + my $expected_exit = $fields[$column]; + my $description = 1; + my $title = $fields[$description]; + next LOOP if (!defined($expected_exit) + || ($expected_exit ne 0 && $expected_exit ne 1)); + @fields = grep {$_ ne 'BLANK'} @fields[$description + 1 .. @fields - 1]; + push @result, [$title, \@fields, $expected_exit]; + } + close($data); + return \@result; +} + +sub mock_server_pid { + return `lsof -iTCP:$server_port` =~ m/\n\S+\s+(\d+)\s+[^\n]+LISTEN/s ? $1 : 0; +} + +sub start_mock_server { + my $args = $_[0]; # optional further CLI arguments + my $dir = bldtop_dir(""); + my $cmd = "LD_LIBRARY_PATH=$dir DYLD_LIBRARY_PATH=$dir " . + bldtop_dir($app) . " -config server.cnf $args"; + my $pid = mock_server_pid(); + return $pid if $pid; # already running + return system("$cmd &") == 0 # start in background, check for success + ? (sleep 1, mock_server_pid()) : 0; +} + +sub stop_mock_server { + my $pid = $_[0]; + system("kill $pid") if $pid; +} diff --git a/test/recipes/81-test_cmp_cli_data/Mock/12345.txt b/test/recipes/80-test_cmp_http_data/Mock/12345.txt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/12345.txt rename to test/recipes/80-test_cmp_http_data/Mock/12345.txt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/big_issuing.crt b/test/recipes/80-test_cmp_http_data/Mock/big_issuing.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/big_issuing.crt rename to test/recipes/80-test_cmp_http_data/Mock/big_issuing.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/big_root.crt b/test/recipes/80-test_cmp_http_data/Mock/big_root.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/big_root.crt rename to test/recipes/80-test_cmp_http_data/Mock/big_root.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/big_server.crt b/test/recipes/80-test_cmp_http_data/Mock/big_server.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/big_server.crt rename to test/recipes/80-test_cmp_http_data/Mock/big_server.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/big_trusted.crt b/test/recipes/80-test_cmp_http_data/Mock/big_trusted.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/big_trusted.crt rename to test/recipes/80-test_cmp_http_data/Mock/big_trusted.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/csr.pem b/test/recipes/80-test_cmp_http_data/Mock/csr.pem similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/csr.pem rename to test/recipes/80-test_cmp_http_data/Mock/csr.pem diff --git a/test/recipes/81-test_cmp_cli_data/Mock/empty.txt b/test/recipes/80-test_cmp_http_data/Mock/empty.txt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/empty.txt rename to test/recipes/80-test_cmp_http_data/Mock/empty.txt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/issuing.crt b/test/recipes/80-test_cmp_http_data/Mock/issuing.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/issuing.crt rename to test/recipes/80-test_cmp_http_data/Mock/issuing.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/new.key b/test/recipes/80-test_cmp_http_data/Mock/new.key old mode 100755 new mode 100644 similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/new.key rename to test/recipes/80-test_cmp_http_data/Mock/new.key diff --git a/test/recipes/81-test_cmp_cli_data/Mock/new_pass_12345.key b/test/recipes/80-test_cmp_http_data/Mock/new_pass_12345.key similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/new_pass_12345.key rename to test/recipes/80-test_cmp_http_data/Mock/new_pass_12345.key diff --git a/test/recipes/81-test_cmp_cli_data/Mock/random.bin b/test/recipes/80-test_cmp_http_data/Mock/random.bin similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/random.bin rename to test/recipes/80-test_cmp_http_data/Mock/random.bin diff --git a/test/recipes/81-test_cmp_cli_data/Mock/root.crt b/test/recipes/80-test_cmp_http_data/Mock/root.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/root.crt rename to test/recipes/80-test_cmp_http_data/Mock/root.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/server.cnf b/test/recipes/80-test_cmp_http_data/Mock/server.cnf similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/server.cnf rename to test/recipes/80-test_cmp_http_data/Mock/server.cnf diff --git a/test/recipes/81-test_cmp_cli_data/Mock/server.crt b/test/recipes/80-test_cmp_http_data/Mock/server.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/server.crt rename to test/recipes/80-test_cmp_http_data/Mock/server.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/server.key b/test/recipes/80-test_cmp_http_data/Mock/server.key similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/server.key rename to test/recipes/80-test_cmp_http_data/Mock/server.key diff --git a/test/recipes/81-test_cmp_cli_data/Mock/signer.crt b/test/recipes/80-test_cmp_http_data/Mock/signer.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/signer.crt rename to test/recipes/80-test_cmp_http_data/Mock/signer.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/signer.key b/test/recipes/80-test_cmp_http_data/Mock/signer.key old mode 100755 new mode 100644 similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/signer.key rename to test/recipes/80-test_cmp_http_data/Mock/signer.key diff --git a/test/recipes/81-test_cmp_cli_data/Mock/signer.p12 b/test/recipes/80-test_cmp_http_data/Mock/signer.p12 similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/signer.p12 rename to test/recipes/80-test_cmp_http_data/Mock/signer.p12 diff --git a/test/recipes/81-test_cmp_cli_data/Mock/signer_issuing.crt b/test/recipes/80-test_cmp_http_data/Mock/signer_issuing.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/signer_issuing.crt rename to test/recipes/80-test_cmp_http_data/Mock/signer_issuing.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/signer_only.crt b/test/recipes/80-test_cmp_http_data/Mock/signer_only.crt old mode 100755 new mode 100644 similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/signer_only.crt rename to test/recipes/80-test_cmp_http_data/Mock/signer_only.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/signer_root.crt b/test/recipes/80-test_cmp_http_data/Mock/signer_root.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/signer_root.crt rename to test/recipes/80-test_cmp_http_data/Mock/signer_root.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/test.cnf b/test/recipes/80-test_cmp_http_data/Mock/test.cnf similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/test.cnf rename to test/recipes/80-test_cmp_http_data/Mock/test.cnf diff --git a/test/recipes/81-test_cmp_cli_data/Mock/trusted.crt b/test/recipes/80-test_cmp_http_data/Mock/trusted.crt similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/trusted.crt rename to test/recipes/80-test_cmp_http_data/Mock/trusted.crt diff --git a/test/recipes/81-test_cmp_cli_data/Mock/wrong_csr.pem b/test/recipes/80-test_cmp_http_data/Mock/wrong_csr.pem similarity index 100% rename from test/recipes/81-test_cmp_cli_data/Mock/wrong_csr.pem rename to test/recipes/80-test_cmp_http_data/Mock/wrong_csr.pem diff --git a/test/recipes/81-test_cmp_cli_data/test_commands.csv b/test/recipes/80-test_cmp_http_data/test_commands.csv similarity index 100% rename from test/recipes/81-test_cmp_cli_data/test_commands.csv rename to test/recipes/80-test_cmp_http_data/test_commands.csv diff --git a/test/recipes/81-test_cmp_cli_data/test_connection.csv b/test/recipes/80-test_cmp_http_data/test_connection.csv similarity index 100% rename from test/recipes/81-test_cmp_cli_data/test_connection.csv rename to test/recipes/80-test_cmp_http_data/test_connection.csv diff --git a/test/recipes/81-test_cmp_cli_data/test_credentials.csv b/test/recipes/80-test_cmp_http_data/test_credentials.csv similarity index 100% rename from test/recipes/81-test_cmp_cli_data/test_credentials.csv rename to test/recipes/80-test_cmp_http_data/test_credentials.csv diff --git a/test/recipes/81-test_cmp_cli_data/test_enrollment.csv b/test/recipes/80-test_cmp_http_data/test_enrollment.csv similarity index 100% rename from test/recipes/81-test_cmp_cli_data/test_enrollment.csv rename to test/recipes/80-test_cmp_http_data/test_enrollment.csv diff --git a/test/recipes/81-test_cmp_cli_data/test_verification.csv b/test/recipes/80-test_cmp_http_data/test_verification.csv similarity index 100% rename from test/recipes/81-test_cmp_cli_data/test_verification.csv rename to test/recipes/80-test_cmp_http_data/test_verification.csv diff --git a/test/recipes/81-test_cmp_cli.t b/test/recipes/81-test_cmp_cli.t index 2a89093446a19d4a604da6daf5e7ed0144f69c1d..667cd55236b51f08e12531ee634fafe30596c536 100644 --- a/test/recipes/81-test_cmp_cli.t +++ b/test/recipes/81-test_cmp_cli.t @@ -12,11 +12,9 @@ use strict; use warnings; use POSIX; -use File::Spec::Functions qw/catfile/; use File::Compare qw/compare_text/; -use OpenSSL::Test qw/:DEFAULT with data_file data_dir srctop_dir bldtop_dir result_dir result_file/; +use OpenSSL::Test qw/:DEFAULT with srctop_file srctop_dir bldtop_dir result_file/; use OpenSSL::Test::Utils; -use Data::Dumper; # for debugging purposes only BEGIN { setup("test_cmp_cli"); @@ -29,303 +27,56 @@ plan skip_all => "These tests are not supported in a fuzz build" plan skip_all => "These tests are not supported in a no-cmp build" if disabled("cmp"); -plan skip_all => "These tests are not supported in a no-ec build" - if disabled("ec"); -plan skip_all => "Tests involving CMP server not available on Windows or VMS" - if $^O =~ /^(VMS|MSWin32)$/; -plan skip_all => "Tests involving CMP server not available in cross-compile builds" - if defined $ENV{EXE_SHELL}; -plan skip_all => "Tests involving CMP server require 'kill' command" - if system("which kill"); -plan skip_all => "Tests involving CMP server require 'lsof' command" - if system("which lsof"); # this typically excludes Solaris - -sub chop_dblquot { # chop any leading & trailing '"' (needed for Windows) - my $str = shift; - $str =~ s/^\"(.*?)\"$/$1/; - return $str; -} - -my $proxy = ""; -$proxy = chop_dblquot($ENV{http_proxy} // $ENV{HTTP_PROXY} // $proxy); -$proxy =~ s{^https?://}{}i; -my $no_proxy = $ENV{no_proxy} // $ENV{NO_PROXY}; - -my $app = "apps/openssl cmp"; +my $app = bldtop_dir("apps/openssl cmp"); my @cmp_basic_tests = ( - [ "show help", [ "-config", '""', "-help" ], 0 ], - [ "CLI option not starting with '-'", [ "-config", '""', "days", "1" ], 1 ], - [ "unknown CLI option", [ "-config", '""', "-dayss" ], 1 ], - [ "bad int syntax: non-digit", [ "-config", '""', "-days", "a/" ], 1 ], - [ "bad int syntax: float", [ "-config", '""', "-days", "3.14" ], 1 ], - [ "bad int syntax: trailing garbage", [ "-config", '""', "-days", "314_+" ], 1 ], - [ "bad int: out of range", [ "-config", '""', "-days", "2147483648" ], 1 ], -); + [ "show help", [ "-help" ], 1 ], + [ "CLI option not starting with '-'", [ "days", "1" ], 0 ], + [ "unknown CLI option", [ "-dayss" ], 0 ], + [ "bad int syntax: non-digit", [ "-days", "a/" ], 0 ], + [ "bad int syntax: float", [ "-days", "3.14" ], 0 ], + [ "bad int syntax: trailing garbage", [ "-days", "314_+" ], 0 ], + [ "bad int: out of range", [ "-days", "2147483648" ], 0 ], + ); + +my @cmp_server_tests = ( + [ "with polling", [ "-poll_count", "1" ], 1 ], + [ "with loader_attic engine", [ "-engine", "loader_attic"], + !disabled('dynamic-engine') && + !disabled("deprecated-3.0") ] + ); + +plan tests => @cmp_basic_tests + @cmp_server_tests; + +foreach (@cmp_basic_tests) { + my $title = $$_[0]; + my $params = $$_[1]; + my $expected = $$_[2]; + ok($expected == run(cmd([$app, "-config", '""', @$params])), + $title); +} -# this uses the mock server directly in the cmp app, without TCP -sub use_mock_srv_internally -{ +# these use the mock server directly in the cmp app, without TCP +foreach (@cmp_server_tests) { + my $title = $$_[0]; + my $extra_args = $$_[1]; + my $expected = $$_[2]; my $secret = "pass:test"; - my $rsp_cert = "signer_only.crt"; + my $rsp_cert = srctop_file('test', 'certs', 'ee-cert-1024.pem'); my $outfile = result_file("test.certout.pem"); - ok(run(cmd([bldtop_dir($app), - "-config", '""', + ok($expected == + run(cmd([$app, "-config", '""', @$extra_args, "-use_mock_srv", "-srv_ref", "mock server", - "-srv_cert", "server.crt", # used for setting sender "-srv_secret", $secret, - "-poll_count", "1", "-rsp_cert", $rsp_cert, "-cmd", "cr", "-subject", "/CN=any", - "-newkey", "signer.key", - "-recipient", "/O=openssl_cmp", # if given must be consistent with sender + "-newkey", srctop_file('test', 'certs', 'ee-key-1024.pem'), "-secret", $secret, "-ref", "client under test", "-certout", $outfile])) && compare_text($outfile, $rsp_cert) == 0, - "CMP app with -use_mock_srv and -poll_count 1"); + $title); # not unlinking $outfile } - -# the CMP server configuration consists of: -my $ca_dn; # The CA's Distinguished Name -my $server_dn; # The server's Distinguished Name -my $server_host;# The server's host name or IP address -my $server_port;# The server's port -my $server_tls; # The server's TLS port, if any, or 0 -my $server_path;# The server's CMP alias -my $server_cert;# The server's cert -my $kur_port; # The server's port for kur (cert update) -my $pbm_port; # The server port to be used for PBM -my $pbm_ref; # The reference for PBM -my $pbm_secret; # The secret for PBM -my $column; # The column number of the expected result -my $sleep = 0; # The time to sleep between two requests - -# The local $server_name variables below are among others taken as the name of a -# sub-directory with server-specific certs etc. and CA-specific config section. - -sub load_config { - my $server_name = shift; - my $section = shift; - my $test_config = $ENV{OPENSSL_CMP_CONFIG} // "$server_name/test.cnf"; - open (CH, $test_config) or die "Cannot open $test_config: $!"; - my $active = 0; - while () { - if (m/\[\s*$section\s*\]/) { - $active = 1; - } elsif (m/\[\s*.*?\s*\]/) { - $active = 0; - } elsif ($active) { - $ca_dn = $1 eq "" ? '""""' : $1 if m/^\s*ca_dn\s*=\s*(.*)?\s*$/; - $server_dn = $1 eq "" ? '""""' : $1 if m/^\s*server_dn\s*=\s*(.*)?\s*$/; - $server_host = $1 eq "" ? '""""' : $1 if m/^\s*server_host\s*=\s*(\S*)?\s*(\#.*)?$/; - $server_port = $1 eq "" ? '""""' : $1 if m/^\s*server_port\s*=\s*(.*)?\s*$/; - $server_tls = $1 eq "" ? '""""' : $1 if m/^\s*server_tls\s*=\s*(.*)?\s*$/; - $server_path = $1 eq "" ? '""""' : $1 if m/^\s*server_path\s*=\s*(.*)?\s*$/; - $server_cert = $1 eq "" ? '""""' : $1 if m/^\s*server_cert\s*=\s*(.*)?\s*$/; - $kur_port = $1 eq "" ? '""""' : $1 if m/^\s*kur_port\s*=\s*(.*)?\s*$/; - $pbm_port = $1 eq "" ? '""""' : $1 if m/^\s*pbm_port\s*=\s*(.*)?\s*$/; - $pbm_ref = $1 eq "" ? '""""' : $1 if m/^\s*pbm_ref\s*=\s*(.*)?\s*$/; - $pbm_secret = $1 eq "" ? '""""' : $1 if m/^\s*pbm_secret\s*=\s*(.*)?\s*$/; - $column = $1 eq "" ? '""""' : $1 if m/^\s*column\s*=\s*(.*)?\s*$/; - $sleep = $1 eq "" ? '""""' : $1 if m/^\s*sleep\s*=\s*(.*)?\s*$/; - } - } - close CH; - die "Cannot find all CMP server config values in $test_config section [$section]\n" - if !defined $ca_dn - || !defined $server_dn || !defined $server_host - || !defined $server_port || !defined $server_tls - || !defined $server_path || !defined $server_cert - || !defined $kur_port || !defined $pbm_port - || !defined $pbm_ref || !defined $pbm_secret - || !defined $column || !defined $sleep; - $server_dn = $server_dn // $ca_dn; -} - -my @server_configurations = ("Mock"); -@server_configurations = split /\s+/, $ENV{OPENSSL_CMP_SERVER} if $ENV{OPENSSL_CMP_SERVER}; -# set env variable, e.g., OPENSSL_CMP_SERVER="Mock Insta" to include further CMP servers - -my @all_aspects = ("connection", "verification", "credentials", "commands", "enrollment"); -@all_aspects = split /\s+/, $ENV{OPENSSL_CMP_ASPECTS} if $ENV{OPENSSL_CMP_ASPECTS}; -# set env variable, e.g., OPENSSL_CMP_ASPECTS="commands enrollment" to select specific aspects - -my $faillog; -if ($ENV{HARNESS_FAILLOG}) { - my $file = $ENV{HARNESS_FAILLOG}; - open($faillog, ">", $file) or die "Cannot open $file for writing: $!"; -} - -sub test_cmp_cli { - my $server_name = shift; - my $aspect = shift; - my $n = shift; - my $i = shift; - my $title = shift; - my $params = shift; - my $expected_exit = shift; - my $path_app = bldtop_dir($app); - with({ exit_checker => sub { - my $actual_exit = shift; - my $OK = $actual_exit == $expected_exit; - if ($faillog && !$OK) { - my $quote_spc_empty = sub { $_ eq "" ? '""' : $_ =~ m/ / ? '"'.$_.'"' : $_ }; - my $invocation = "$path_app ".join(' ', map $quote_spc_empty->($_), @$params); - print $faillog "$server_name $aspect \"$title\" ($i/$n)". - " expected=$expected_exit actual=$actual_exit\n"; - print $faillog "$invocation\n\n"; - } - return $OK; } }, - sub { ok(run(cmd([$path_app, @$params,])), - $title); }); -} - -sub test_cmp_cli_aspect { - my $server_name = shift; - my $aspect = shift; - my $tests = shift; - subtest "CMP app CLI $server_name $aspect\n" => sub { - my $n = scalar @$tests; - plan tests => $n; - my $i = 1; - foreach (@$tests) { - SKIP: { - test_cmp_cli($server_name, $aspect, $n, $i++, $$_[0], $$_[1], $$_[2]); - sleep($sleep); - } - } - }; - # not unlinking test.certout*.pem, test.cacerts.pem, and test.extracerts.pem -} - -# The input files for the tests done here dynamically depend on the test server -# selected (where the Mock server used by default is just one possibility). -# On the other hand the main test configuration file test.cnf, which references -# several server-dependent input files by relative file names, is static. -# Moreover the tests use much greater variety of input files than output files. -# Therefore we chose the current directory as a subdirectory of $SRCTOP and it -# was simpler to prepend the output file names by BLDTOP than doing the tests -# from $BLDTOP/test-runs/test_cmp_cli and prepending the input files by SRCTOP. - -indir data_dir() => sub { - plan tests => 1 + @server_configurations * @all_aspects - + (grep(/^Mock$/, @server_configurations) - && grep(/^certstatus$/, @all_aspects) ? 0 : 1); - - test_cmp_cli_aspect("basic", "options", \@cmp_basic_tests); - - indir "Mock" => sub { - use_mock_srv_internally(); - }; - - foreach my $server_name (@server_configurations) { - $server_name = chop_dblquot($server_name); - load_config($server_name, $server_name); - SKIP: - { - my $pid; - if ($server_name eq "Mock") { - indir "Mock" => sub { - $pid = start_mock_server(""); - skip "Cannot start or find the started CMP mock server", - scalar @all_aspects unless $pid; - } - } - foreach my $aspect (@all_aspects) { - $aspect = chop_dblquot($aspect); - next if $server_name eq "Mock" && $aspect eq "certstatus"; - load_config($server_name, $aspect); # update with any aspect-specific settings - indir $server_name => sub { - my $tests = load_tests($server_name, $aspect); - test_cmp_cli_aspect($server_name, $aspect, $tests); - }; - }; - stop_mock_server($pid) if $pid; - } - }; -}; - -close($faillog) if $faillog; - -sub load_tests { - my $server_name = shift; - my $aspect = shift; - my $test_config = $ENV{OPENSSL_CMP_CONFIG} // "$server_name/test.cnf"; - my $file = data_file("test_$aspect.csv"); - my $result_dir = result_dir(); - my @result; - - open(my $data, '<', $file) || die "Cannot open $file for reading: $!"; - LOOP: - while (my $line = <$data>) { - chomp $line; - $line =~ s{\r\n}{\n}g; # adjust line endings - $line =~ s{_CA_DN}{$ca_dn}g; - $line =~ s{_SERVER_DN}{$server_dn}g; - $line =~ s{_SERVER_HOST}{$server_host}g; - $line =~ s{_SERVER_PORT}{$server_port}g; - $line =~ s{_SERVER_TLS}{$server_tls}g; - $line =~ s{_SERVER_PATH}{$server_path}g; - $line =~ s{_SERVER_CERT}{$server_cert}g; - $line =~ s{_KUR_PORT}{$kur_port}g; - $line =~ s{_PBM_PORT}{$pbm_port}g; - $line =~ s{_PBM_REF}{$pbm_ref}g; - $line =~ s{_PBM_SECRET}{$pbm_secret}g; - $line =~ s{_RESULT_DIR}{$result_dir}g; - - next LOOP if $server_tls == 0 && $line =~ m/,\s*-tls_used\s*,/; - my $noproxy = $no_proxy; - if ($line =~ m/,\s*-no_proxy\s*,(.*?)(,|$)/) { - $noproxy = $1; - } elsif ($server_host eq "127.0.0.1") { - # do connections to localhost (e.g., Mock server) without proxy - $line =~ s{-section,,}{-section,,-no_proxy,127.0.0.1,} ; - } - if ($line =~ m/,\s*-proxy\s*,/) { - next LOOP if $no_proxy && ($noproxy =~ $server_host); - } else { - $line =~ s{-section,,}{-section,,-proxy,$proxy,}; - } - $line =~ s{-section,,}{-section,,-certout,$result_dir/test.cert.pem,}; - $line =~ s{-section,,}{-config,../$test_config,-section,$server_name $aspect,}; - - my @fields = grep /\S/, split ",", $line; - s/^$// for (@fields); # used for proxy="" - s/^\s+// for (@fields); # remove leading whitespace from elements - s/\s+$// for (@fields); # remove trailing whitespace from elements - s/^\"(\".*?\")\"$/$1/ for (@fields); # remove escaping from quotation marks from elements - my $expected_exit = $fields[$column]; - my $description = 1; - my $title = $fields[$description]; - next LOOP if (!defined($expected_exit) - || ($expected_exit ne 0 && $expected_exit ne 1)); - @fields = grep {$_ ne 'BLANK'} @fields[$description + 1 .. @fields - 1]; - push @result, [$title, \@fields, $expected_exit]; - } - close($data); - return \@result; -} - -sub mock_server_pid { - return `lsof -iTCP:$server_port` =~ m/\n\S+\s+(\d+)\s+[^\n]+LISTEN/s ? $1 : 0; -} - -sub start_mock_server { - my $args = $_[0]; # optional further CLI arguments - my $dir = bldtop_dir(""); - my $cmd = "LD_LIBRARY_PATH=$dir DYLD_LIBRARY_PATH=$dir " . - bldtop_dir($app) . " -config server.cnf $args"; - my $pid = mock_server_pid(); - return $pid if $pid; # already running - return system("$cmd &") == 0 # start in background, check for success - ? (sleep 1, mock_server_pid()) : 0; -} - -sub stop_mock_server { - my $pid = $_[0]; - system("kill $pid") if $pid; -}