| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997 |
- #!/usr/bin/perl
- #%# Copyright (C) 2014-2024 Christoph Biedl <debian.axhn@manchmal.in-ulm.de>
- #%# License: GPL-2.0+
- =head1 NAME
- ngircd-test-tls-link - test certificate validation in ngircd's TLS based s2s links
- =head1 VERSION
- Version YYYY.MM.DD
- =cut
- our $VERSION = 'YYYY.MM.DD';
- =head1 SYNOPSIS
- ngircd-test-tls-link --x509-dir /path/to/dir --x509-populate
- ngircd-test-tls-link --x509-dir /path/to/dir [<test-name> ...]
- =cut
- use 5.010;
- use strict;
- use warnings;
- use Test::More;
- use File::Basename;
- use File::Copy;
- use File::Slurp;
- use File::Temp qw<tempdir>;
- use Getopt::Long;
- use Pod::Usage;
- use Proc::Simple;
- use Socket;
- use Time::HiRes qw<sleep time>;
- my $ngircd_exe_default = '/usr/sbin/ngircd';
- my @ngircd_exe = ( $ngircd_exe_default );
- my @tls_mode;
- my $x509_dir;
- my $x509_populate;
- my $verbose = 0;
- =head1 OPTIONS
- =over
- =cut
- {
- my $help;
- my $man;
- my %GetOptions = (
- 'help|?' => \$help,
- 'man' => \$man,
- );
- =item B<--x509-dir> F<directory>
- A directory to hold the x509 credentials needed. Default: Use a
- temporary directory which is deleted upon exit. This delays every
- execution of this program by a few seconds.
- =cut
- $GetOptions{'x509-dir=s'} = \$x509_dir;
- =item B<--x509-populate>
- Populate a given C<--x509-dir> with the files needed, then exit.
- =cut
- $GetOptions{'x509-populate'} = \$x509_populate;
- =item B<--tls-mode> C<mode>
- One of C<openssl> or C<gnutls>.
- The type of TLS configuration to use. Must match ngircd's compilation
- option. Use only if auto-detection failed, and report a bug then.
- For TLS interoperability tests, you can provide two strings, separated
- by C<:> (colon).
- Default: Detect from the binary.
- =cut
- $GetOptions{'tls-mode=s'} = sub {
- @tls_mode = split (/:/, $_[1], 2);
- };
- =item B<--ngircd> F<program>
- The ngircd binary. For TLS interoperability tests, you can provide two
- programs, separated by C<:> (colon).
- Default: F</usr/sbin/ngircd>
- =cut
- $GetOptions{'ngircd=s'} = sub {
- @ngircd_exe = split (/:/, $_[1], 2);
- };
- =item B<--verbose>
- Add verbosity.
- =cut
- $GetOptions{'verbose+'} = \$verbose++;
- =item B<--Version>
- Show the version number and exit.
- =cut
- $GetOptions{'Version'} = sub {
- printf "%s version %s\n",
- (split (/\//, $0))[-1],
- $VERSION;
- exit 1;
- };
- =item [ <test> ... ]
- Select specific tests to run only. Default: Run all tests.
- =back
- =cut
- GetOptions (%GetOptions) or pod2usage (2);
- $help and pod2usage (1);
- $man and pod2usage (-exitstatus => 0, -verbose => 2);
- if (@tls_mode) {
- foreach my $m (@tls_mode) {
- ($m =~ /^(openssl|gnutls)$/) or
- die ("Invalid '--tls-mode' value '$m'");
- }
- } else {
- for (my $i = 0; $i < scalar (@ngircd_exe); $i++) {
- my $x = $ngircd_exe[$i];
- my $tls_mode;
- (-x $x) or
- die ("Not an executable: '$x'");
- my $pipe;
- open ($pipe, '-|', 'ldd', $x) or
- die ("Cannot run ldd on '$x': $!");
- while (defined (my $line = <$pipe>)) {
- if ($line =~ /^\tlibssl\.so\./) {
- $tls_mode = 'openssl';
- last;
- } elsif ($line =~ /^\tlibgnutls/) {
- $tls_mode = 'gnutls';
- last;
- }
- }
- close ($pipe);
- if ($tls_mode) {
- note ("Auto-detected TLS mode for '$x' is '$tls_mode'");
- $tls_mode[$i] = $tls_mode;
- } else {
- die ("BUG: Failed to determine TLS mode for '$x'");
- }
- }
- }
- $ngircd_exe[1] //= $ngircd_exe[0];
- $tls_mode[1] //= $tls_mode[0];
- }
- my $temp_dir = tempdir (
- "ngircd-test-tls-link.$$.XXXXX",
- 'TMPDIR' => 1,
- 'CLEANUP' => 1,
- );
- my $exit_after_x509_populate;
- if ($x509_dir) {
- (-d $x509_dir || $x509_populate) or
- die ("Not a directory: '$x509_dir' - perhaps you forgot --x509-populate?");
- $exit_after_x509_populate = $x509_populate;
- } else {
- # not provided, use a temporary directoryy
- $x509_dir = "$temp_dir/CA";
- # always populate it, and continue
- $x509_populate and
- note ('Ignoring --x509-populate option as no --x509-dir was provided');
- $x509_populate = 1;
- $exit_after_x509_populate = undef;
- }
- my $conf_dir = "$temp_dir/conf";
- my $hosts_file ="$temp_dir/hosts";
- write_file (
- $hosts_file,
- <<__EOS__,
- 127.0.0.1 server1
- 127.0.0.1 server2
- 127.0.0.1 server1.example.com
- 127.0.0.1 server2.example.com
- __EOS__
- );
- foreach my $dir (($x509_dir, $conf_dir)) {
- (
- -d $dir ||
- mkdir ($dir)
- ) or die ("Cannot create directory '$dir'");
- }
- my $src_dir = dirname ($0);
- my $one_month_future = $^T + 30*86400;
- my %tests = (
- 'regular' => {
- 'expect' => 'pass',
- 'fail' => qr/Connection [0-9]+ with "server2:6692" closed/,
- ':order' => __LINE__,
- },
- 'different-CAs' => {
- 'expect' => 'pass',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA2/root-ca.crt",
- },
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CertFile' => "$x509_dir/CA2/server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA2/server2.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'CN-mismatch' => {
- # using certificate with wrong CN, must fail
- 'expect' => 'fail',
- 'server2-extra' => {
- 'SSL' => {
- 'CertFile' => "$x509_dir/CA1/server3.example.com.crt",
- 'KeyFile' => "$x509_dir/CA1/server3.example.com.key",
- },
- },
- 'pass' => qr/Peer certificate check failed for/,
- ':order' => __LINE__,
- },
- 'using-peer-cert-as-CA' => {
- # using peer cert as CA a.k.a. self-signed
- 'unsupported' => 1, # fails on openssl
- 'expect' => 'pass',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA1/server2.example.com.crt",
- },
- },
- ':order' => __LINE__,
- },
- 'peer-cert-signed-by-unknown-CA' => {
- # peer cert signed by unknown CA, must fail
- 'expect' => 'fail',
- 'server2-extra' => {
- 'SSL' => {
- 'CertFile' => "$x509_dir/CA2/server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA2/server2.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'peer-cert-signed-by-unknown-CA-but-verify-disabled' => {
- 'expect' => 'pass',
- 'server1-extra' => {
- 'Server' => {
- 'SSLVerify' => 'no',
- }
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CertFile' => "$x509_dir/CA2/server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA2/server2.example.com.key",
- },
- },
- 'pass' => qr/Synchronization with "ngircd\.test\.server2" done/,
- 'noop' => qr/Certificate validation failed/,
- ':order' => __LINE__,
- },
- 'peer-cert-revoked' => {
- # certificate is revoked, must fail
- 'expect' => 'fail',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA3/root-ca.crt",
- 'CertFile' => "$x509_dir/CA3/server1.example.com.crt",
- 'KeyFile' => "$x509_dir/CA3/server1.example.com.key",
- 'CRLFile' => "$x509_dir/CA3/crl.pem",
- },
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA3/root-ca.crt",
- 'CertFile' => "$x509_dir/CA3/server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA3/server2.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'peer-cert-revoked-but-verify-disabled' => {
- 'expect' => 'pass',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA3/root-ca.crt",
- 'CertFile' => "$x509_dir/CA3/server1.example.com.crt",
- 'KeyFile' => "$x509_dir/CA3/server1.example.com.key",
- 'CRLFile' => "$x509_dir/CA3/crl.pem",
- },
- 'Server' => {
- 'SSLVerify' => 'no',
- }
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA3/root-ca.crt",
- 'CertFile' => "$x509_dir/CA3/server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA3/server2.example.com.key",
- },
- },
- 'pass' => qr/Synchronization with "ngircd\.test\.server2" done/,
- 'noop' => qr/Certificate validation failed/,
- ':order' => __LINE__,
- },
- 'revocation-by-wrong-CA' => {
- # CRL signed by a different CA
- 'expect' => 'fail',
- 'server1-extra' => {
- 'SSL' => {
- 'CRLFile' => "$x509_dir/CA2/crl.pem",
- },
- },
- ':order' => __LINE__,
- },
- 'wildcard-cert' => {
- # server uses a wildcard certificate
- 'expect' => 'pass',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA4/root-ca.crt",
- 'CertFile' => "$x509_dir/CA4/server1.example.com.crt",
- 'KeyFile' => "$x509_dir/CA4/server1.example.com.key",
- },
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA4/root-ca.crt",
- 'CertFile' => "$x509_dir/CA4/*.example.com.crt",
- 'KeyFile' => "$x509_dir/CA4/*.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'mixed-case' => {
- # uppercase letters in certificate name
- 'expect' => 'pass',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA5/root-ca.crt",
- 'CertFile' => "$x509_dir/CA5/server1.example.com.crt",
- 'KeyFile' => "$x509_dir/CA5/server1.example.com.key",
- },
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA5/root-ca.crt",
- 'CertFile' => "$x509_dir/CA5/Server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA5/Server2.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'subject-alternate-name' => {
- 'expect' => 'pass',
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA6/root-ca.crt",
- 'CertFile' => "$x509_dir/CA6/server1.example.com.crt",
- 'KeyFile' => "$x509_dir/CA6/server1.example.com.key",
- },
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA6/root-ca.crt",
- 'CertFile' => "$x509_dir/CA6/server2.example.com.crt",
- 'KeyFile' => "$x509_dir/CA6/server2.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'subject-alternate-name-mismatch' => {
- 'expect' => 'fail',
- 'pass' => qr/Failed to verify the hostname, expected/,
- 'server1-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA6/root-ca.crt",
- 'CertFile' => "$x509_dir/CA6/server1.example.com.crt",
- 'KeyFile' => "$x509_dir/CA6/server1.example.com.key",
- },
- },
- 'server2-extra' => {
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA6/root-ca.crt",
- 'CertFile' => "$x509_dir/CA6/server3.example.com.crt",
- 'KeyFile' => "$x509_dir/CA6/server3.example.com.key",
- },
- },
- ':order' => __LINE__,
- },
- 'cert-expired' => {
- 'expect' => 'fail',
- 'pass' => qr/SSL error: A TLS fatal alert has been received/,
- 'prefix' => [ 'faketime', "\@$one_month_future" ],
- ':order' => __LINE__,
- },
- );
- sub write_config {
- my ($file, $number, $tls_mode, @extras) = @_;
- my $cipher_list = ($tls_mode =~ /^o/ ?
- 'HIGH:!aNULL:@STRENGTH:!SSLv3' :
- 'SECURE128:-VERS-SSL3.0'
- );
- my $peer_number = 3 - $number;
- # write server configurations
- my %config = (
- 'Global' => {
- 'Name' => "ngircd.test.server$number",
- 'Info' => "ngIRCd Test-Server $number",
- 'Listen' => '127.0.0.1',
- 'Ports' => "678$number",
- 'AdminEMail' => "admin\@server$number.example",
- 'ServerUID' => $<,
- 'ServerGID' => $(,
- 'MotdFile' => '/dev/null',
- },
- 'Options' => {
- 'OperCanUseMode' => 'yes',
- 'Ident' => 'no',
- 'IncludeDir' => '',
- 'PAM' => 'no',
- },
- 'Operator' => {
- 'Name' => 'TestOp',
- 'Password' => '123',
- },
- 'Server' => {
- 'Name' => "ngircd.test.server$peer_number",
- 'Host' => "server$peer_number.example.com",
- 'Port' => "669$peer_number",
- 'MyPassword' => "pwd$number",
- 'PeerPassword' => "pwd$peer_number",
- 'SSLConnect' => 'yes',
- 'SSLVerify' => 'yes',
- },
- 'SSL' => {
- 'CAFile' => "$x509_dir/CA1/root-ca.crt",
- 'CertFile' => "$x509_dir/CA1/server$number.example.com.crt",
- 'CipherList' => $cipher_list,
- 'DHFile' => "$x509_dir/dhparams.pem",
- 'KeyFile' => "$x509_dir/CA1/server$number.example.com.key",
- 'Ports' => "669$number",
- },
- );
- foreach my $extra (@extras) {
- foreach my $section (keys %$extra) {
- foreach my $key (keys %{$extra->{$section}}) {
- if (
- exists ($config{$section}{$key}) &&
- defined ($config{$section}{$key}) &&
- exists ($extra->{$section}{$key}) &&
- defined ($extra->{$section}{$key}) &&
- $config{$section}{$key} eq $extra->{$section}{$key}
- ) {
- note ("W: Identical re-definition of server/section/key: $number/$section/$key");
- }
- $config{$section}{$key} = $extra->{$section}{$key};
- }
- }
- }
- my $return = '';
- my $fh;
- open ($fh, '>', \$return);
- foreach my $section (sort keys %config) {
- print $fh "[$section]\n";
- foreach my $key (sort keys %{$config{$section}}) {
- my $value = $config{$section}{$key};
- defined ($value) and
- printf $fh " %s = %s\n", $key, $value;
- }
- }
- close ($fh);
- write_file ($file, $return);
- }
- sub write_configs {
- my (
- $server1_config_file,
- $server2_config_file,
- $server1_config_extra,
- $server2_config_extra,
- ) = @_;
- write_config (
- $server1_config_file,
- '1',
- $tls_mode[0],
- {
- 'SSL' => {
- 'Ports' => undef,
- },
- },
- $server1_config_extra,
- );
- write_config (
- $server2_config_file,
- '2',
- $tls_mode[1],
- {
- 'Server' => {
- 'Passive' => 'yes',
- },
- },
- $server2_config_extra,
- );
- }
- sub test1 {
- my ($name, $test) = @_;
- {
- my $l = length ($name);
- note ('+-' . ('-' x $l) . '-+');
- note ("| $name | ");
- note ('+-' . ('-' x $l) . '-+');
- }
- my $server1_config = "$conf_dir/ngircd-test1.conf";
- my $server2_config = "$conf_dir/ngircd-test2.conf";
- write_configs (
- $server1_config,
- $server2_config,
- $test->{'server1-extra'},
- $test->{'server2-extra'},
- ),
- my $prefix = $test->{'prefix'} // [];
- # start the receiving server2 first, it might need a
- # little extra time
- my $server2_log = "$temp_dir/server2.log";
- write_file ($server2_log, ''); # so open below won't fail
- my $server2 = Proc::Simple->new;
- $server2->redirect_output ($server2_log, $server2_log);
- $server2->start ((
- @$prefix,
- $ngircd_exe[1],
- '--config', $server2_config,
- '--nodaemon',
- ));
- $server2->kill_on_destroy (1);
- sleep (0.5);
- # start connecting server1
- my $server1_log = "$temp_dir/server1.log";
- write_file ($server1_log, ''); # so open below won't fail
- my $server1 = Proc::Simple->new;
- $server1->redirect_output ($server1_log, $server1_log);
- $server1->start ((
- @$prefix,
- $ngircd_exe[0],
- '--config', $server1_config,
- '--nodaemon',
- ));
- $server1->kill_on_destroy (1);
- sleep (0.5);
- my $fh;
- open ($fh, '<', $server1_log) or
- die ("Cannot read '$server1_log': $!");
- # read output from server1, find 'pass' or 'fail' line
- my $timeout = time + 10;
- my $t0 = time;
- my $firstline;
- my $got_verdict;
- TAIL:
- while (1) {
- if (time > $timeout) {
- fail (sprintf ('Timeout (%u sec)', time - $t0));
- last TAIL;
- }
- my $curpos;
- my $line;
- for ($curpos = tell ($fh); $line = <$fh>; $curpos = tell ($fh)) {
- chomp ($line);
- $firstline //= $line;
- $verbose and note (sprintf ('%.2f %s', time-$t0, $line));
- # generic pass/fail pattern
- my $passed;
- if ($line =~ /(Can't bind socket to address 127\.0\.0\.1.*$)/) {
- # previous instance running
- fail ($1);
- $got_verdict = 1;
- last TAIL;
- } elsif (
- # configured pass/fail pattern
- $test->{'pass'} && $line =~ /$test->{'pass'}/
- ) {
- $passed = 1;
- } elsif ($test->{'fail'} && $line =~ /$test->{'fail'}/) {
- $passed = 0;
- } elsif ($test->{'noop'} && $line =~ /$test->{'noop'}/) {
- next;
- } elsif ($line =~ /Synchronization with "ngircd\.test\.server2" done/) {
- $passed = $test->{'expect'};
- } elsif ($line =~ /Certificate validation failed/) {
- $passed = 1 - $test->{'expect'};
- } elsif ($line =~ /Fatail: /) {
- # some fatal error
- fail ($1);
- }
- if (defined ($passed)) {
- if ($passed) {
- pass ("Got expected line: '$line'");
- } else {
- fail ("Got line that should not be there: '$line'");
- }
- $got_verdict = 1;
- last TAIL;
- }
- }
- if (!$server1->poll) {
- note ('server1 has left the building');
- $got_verdict = 1;
- last TAIL;
- }
- sleep (0.1);
- seek ($fh, $curpos, 0);
- }
- close ($fh);
- $server1->kill;
- $server2->kill;
- wait;
- ok ($got_verdict, 'have a verdict');
- if ($firstline && $firstline =~ /^\[[0-9]+:[0-9] +[0-9]+\./) {
- # have absolute timestamps
- my @log = read_file ($server1_log);
- my $first_server2_line = scalar (@log);
- push @log, read_file ($server2_log);
- my $RED = "\e[1;31m";
- my $GREEN = "\e[1;32m";
- my $NORMAL = "\e[0m";
- my @log_sorter;
- for (my $i = 0; $i < scalar (@log); $i++) {
- my $line = $log[$i];
- chomp ($line);
- my $srt;
- my $server = $i < $first_server2_line ? 1 : 2;
- if ($line =~ /^
- \[
- (?<pid>[0-9]+):
- (?<prio>[0-9])\s+
- (?<time>[0-9]+\.[0-9]+)
- \]\s(?<msg>.+)
- $/x) {
- $srt = [
- $+{'time'},
- $server,
- $+{'prio'},
- $+{'msg'},
- ];
- } else {
- fail ("Cannot parse log line '$line'");
- $srt = [ 0, $server, 0, $line ];
- }
- $log_sorter[$i] = $srt;
- }
- # sort the log file by time, process, line
- my @idx = sort {
- $log_sorter[$a][0] <=> $log_sorter[$b][0] ||
- $log_sorter[$a][1] <=> $log_sorter[$b][1] ||
- $log_sorter[$a][3] cmp $log_sorter[$b][3] ||
- $a cmp $b
- } 0..$#log_sorter;
- note ('combined log ([server:prio ms])');
- my $t1 = $log_sorter[$idx[0]][0];
- foreach my $idx (@idx) {
- my $data = $log_sorter[$idx];
- note (sprintf (
- '| %s[%s:%d %.6f] %s%s',
- ($data->[1] == 1 ? $GREEN : $RED),
- $data->[1],
- $data->[2],
- $data->[0] - $t1,
- $data->[3],
- $NORMAL,
- ));
- }
- } else {
- # Just combine
- my @log;
- @log = read_file ($server1_log);
- note ('server 1 log:');
- foreach my $line (@log) {
- note ("1: $line");
- }
- @log = read_file ($server2_log);
- note ('server 2 log:');
- foreach my $line (@log) {
- note ("2: $line");
- }
- }
- }
- # start the show
- if ($x509_populate) {
- note ('setting up x509 stuff');
- my $fail_x509;
- $ENV{'RNDFILE'} = "$temp_dir/.rnd";
- for my $f (qw<dhparams.pem openssl.cnf openssl.san.cnf>) {
- copy ("$src_dir/data/$f", "$x509_dir/$f") or
- die ("Failed to copy $f: $!");
- }
- foreach my $command ((
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA1 server1.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA1 server2.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA1 server3.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA2 server2.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA3 server1.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<--revoke CA3 server2.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA4 server1.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA4 *.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA5 server1.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA5 Server2.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<CA6 server1.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<--san CA6 server2.example.com> ],
- [ "$src_dir/gen-x509-stuff", $x509_dir, qw<--san CA6 server3.example.com> ],
- )) {
- my $run = Proc::Simple->new;
- my $log = "$temp_dir/gen-stuff.log";
- write_file ($log, '');
- $run->redirect_output ($log, $log);
- $run->start (@$command);
- my $exit = $run->wait;
- my $fail = !is (
- $exit,
- 0,
- 'gen-x509-stuff ' . join (' ', @$command[2..$#$command]),
- );
- $fail and $fail_x509++;
- if ($fail || $verbose) {
- my $out = read_file ($log);
- $log and note ("Output:\n$out");
- }
- }
- $fail_x509 and exit 1;
- if ($exit_after_x509_populate) {
- pass ('here we go');
- done_testing;
- exit 0;
- }
- }
- $ENV{'LD_PRELOAD'} = 'libnss_wrapper.so';
- $ENV{'NSS_WRAPPER_HOSTS'} = $hosts_file;
- note ('checking mocked resolver');
- {
- my $fail;
- foreach my $hostname (qw<server1 server2>) {
- my $output = `getent hosts $hostname`;
- is ($?, 0, "resolve $hostname") or $fail++;
- is ($?, 0, "resolve $hostname.example.com") or $fail++;
- }
- }
- my @tests;
- # check test description integrity
- {
- my $fail;
- my @required = qw<expect :order>; # NB: pass and fail may be missing
- foreach my $test (sort keys %tests) {
- foreach my $r (@required) {
- exists ($tests{$test}{$r}) and next;
- fail ("No '$r' field in test '$test'");
- $fail++;
- }
- }
- $fail and die ('Cannot continue');
- @tests =
- sort { $tests{$a}{':order'} <=> $tests{$b}{':order'} }
- keys %tests;
- foreach my $test (@tests) {
- my $got = $tests{$test}{'expect'};
- if ($got =~ /^(pass|fail)$/) {
- $tests{$test}{'expect'} = $got eq 'pass' ? 1 : 0;
- next;
- }
- fail ("The 'expect' in test '$test' is '$got', not 'pass' or 'fail'");
- $fail++;
- }
- $fail and die ('Cannot continue');
- # drop those who are expected to fail
- @tests = grep { !$tests{$_}{'unsupported'} } @tests;
- }
- if (@ARGV) {
- my $warned;
- foreach my $t (@ARGV) {
- if (exists ($tests{$t})) {
- $tests{$t}{'unsupported'} and
- note ("Warn: Test '$t' is marked unsupported. Expect breakage");
- test1 ($t, $tests{$t});
- next;
- }
- fail ("Don't know how to test '$t'");
- if (!$warned) {
- note ("Available tests:\n" . join ("\n", map { " $_" } @tests));
- $warned = 1;
- }
- }
- } else {
- # run all
- foreach my $test (@tests) {
- test1 ($test, $tests{$test});
- }
- }
- done_testing;
- exit 0;
- =head1 DESCRIPTION
- Build ngircd for both TLS linkages, possibly using the following script:
- #!/bin/sh
- set -e
- case "$1" in
- openssl | gnutls)
- [ -f ./configure ] || ./autogen.sh
- ./configure \
- --prefix=/usr \
- --mandir="\${prefix}/share/man" \
- --infodir="\${prefix}/share/info" \
- --sysconfdir=/etc/ngircd \
- "--with-$1"
- make 2>&1 | tee "../build.$1.log"
- cp src/ngircd/ngircd "../ngircd-$1"
- ;;
- *)
- echo "Usage: $0 <gnutls|openssl>"
- exit 1
- ;;
- esac
- One time only: Create the certificates
- perl ngircd-test-tls-link \
- --x509-populate \
- --x509-dir ../x509-data/ \
- --ngircd <path to any ngircd binary>
- Then run this program
- For GnuTLS:
- ngircd-test-tls-link \
- --x509-dir ../x509-data/ \
- --ngircd ../ngircd-gnutls
- Likewise for OpenSSL:
- (...)
- --ngircd ../ngircd-openssl
- For interoperability tests:
- ngircd-test-tls-link \
- --x509-dir ../x509-data/ \
- --ngircd ../ngircd-gnutls:../ngircd-openssl
- ... also with the two binaries swapped.
- =head1 DEPENDENCIES
- The following programs, libraries and Perl modules must be installed:
- faketime
- openssl
- libnss-wrapper
- File::Slurp
- Proc::Simple
- =head1 BUGS
- Hack.
- =head1 SEE ALSO
- ngircd(8)
- =head1 AUTHOR
- Christoph Biedl C<< <debian.axhn@manchmal.in-ulm.de> >>
- =head1 ACKNOWLEDGEMENTS
- Alex Barton for ngircd.
- =head1 COPYRIGHT & LICENSE
- Copyright (C) 2014-2024 Christoph Biedl <debian.axhn@manchmal.in-ulm.de>
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
- This package is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>
- On Debian systems, the complete text of the GNU General Public
- License version 2 can be found in
- "/usr/share/common-licenses/GPL-2".
- =cut
|