diff options
Diffstat (limited to 'src/test/libpq_encryption/t/001_negotiate_encryption.pl')
-rw-r--r-- | src/test/libpq_encryption/t/001_negotiate_encryption.pl | 724 |
1 files changed, 0 insertions, 724 deletions
diff --git a/src/test/libpq_encryption/t/001_negotiate_encryption.pl b/src/test/libpq_encryption/t/001_negotiate_encryption.pl deleted file mode 100644 index d07d9498bbc..00000000000 --- a/src/test/libpq_encryption/t/001_negotiate_encryption.pl +++ /dev/null @@ -1,724 +0,0 @@ - -# Copyright (c) 2021-2024, PostgreSQL Global Development Group - -# OVERVIEW -# -------- -# -# Test negotiation of SSL and GSSAPI encryption -# -# We test all combinations of: -# -# - all the libpq client options that affect the protocol negotiations -# (gssencmode, sslmode, sslnegotiation) -# - server accepting or rejecting the authentication due to -# pg_hba.conf entries -# - SSL and GSS enabled/disabled in the server -# -# That's a lot of combinations, so we use a table-driven approach. -# Each combination is represented by a line in a table. The line lists -# the options specifying the test case, and an expected outcome. The -# expected outcome includes whether the connection succeeds or fails, -# and whether it uses SSL, GSS or no encryption. It also includes a -# condensed trace of what steps were taken during the negotiation. -# That can catch cases like useless retries, or if the encryption -# methods are attempted in wrong order, even when it doesn't affect -# the final outcome. -# -# TEST TABLE FORMAT -# ----------------- -# -# Example of the test table format: -# -# # USER GSSENCMODE SSLMODE EVENTS -> OUTCOME -# testuser disable allow connect, authok -> plain -# . . prefer connect, sslaccept, authok -> ssl -# testuser require * connect, gssreject -> fail -# -# USER, GSSENCMODE and SSLMODE fields are the libpq 'user', -# 'gssencmode' and 'sslmode' options used in the test. As a shorthand, -# a single dot ('.') can be used in the USER, GSSENCMODE, and SSLMODE -# fields, to indicate "same as on previous line". A '*' can be used -# as a wildcard; it is expanded to mean all possible values of that -# field. -# -# The EVENTS field is a condensed trace of expected steps during the -# negotiation: -# -# connect: a TCP connection was established -# reconnect: TCP connection was disconnected, and a new one was established -# sslaccept: client requested SSL encryption and server accepted it -# sslreject: client requested SSL encryption but server rejected it -# gssaccept: client requested GSSAPI encryption and server accepted it -# gssreject: client requested GSSAPI encryption but server rejected it -# authok: client sent startup packet and authentication was performed successfully -# authfail: client sent startup packet but server rejected the authentication -# -# The event trace can be used to verify that the client negotiated the -# connection properly in more detail than just by looking at the -# outcome. For example, if the client opens spurious extra TCP -# connections, that would show up in the EVENTS. -# -# The OUTCOME field indicates the expected result of the test: -# -# plain: an unencrypted connection was established -# ssl: SSL connection was established -# gss: GSSAPI encrypted connection was established -# fail: the connection attempt failed -# -# Empty lines are ignored. '#' can be used to mark the rest of the -# line as a comment. - -use strict; -use warnings FATAL => 'all'; -use PostgreSQL::Test::Utils; -use PostgreSQL::Test::Cluster; -use PostgreSQL::Test::Kerberos; -use File::Basename; -use File::Copy; -use Test::More; - -if (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\blibpq_encryption\b/) -{ - plan skip_all => - 'Potentially unsafe test libpq_encryption not enabled in PG_TEST_EXTRA'; -} - -my $ssl_supported = $ENV{with_ssl} eq 'openssl'; -my $gss_supported = $ENV{with_gssapi} eq 'yes'; - -### -### Prepare test server for GSSAPI and SSL authentication, with a few -### different test users and helper functions. We don't actually -### enable SSL and kerberos in the server yet, we will do that later. -### - -my $host = 'enc-test-localhost.postgresql.example.com'; -my $hostaddr = '127.0.0.1'; -my $servercidr = '127.0.0.1/32'; - -my $node = PostgreSQL::Test::Cluster->new('node'); -$node->init; -$node->append_conf( - 'postgresql.conf', qq{ -listen_addresses = '$hostaddr' - -# Capturing the EVENTS that occur during tests requires these settings -log_connections = on -log_disconnections = on -trace_connection_negotiation = on -lc_messages = 'C' -}); -my $pgdata = $node->data_dir; - -my $dbname = 'postgres'; -my $username = 'enctest'; -my $application = '001_negotiate_encryption.pl'; - -my $gssuser_password = 'secret1'; - -my $krb; - -if ($gss_supported != 0) -{ - note "setting up Kerberos"; - - my $realm = 'EXAMPLE.COM'; - $krb = PostgreSQL::Test::Kerberos->new($host, $hostaddr, $realm); - $node->append_conf('postgresql.conf', "krb_server_keyfile = '$krb->{keytab}'\n"); -} - -if ($ssl_supported != 0) -{ - my $certdir = dirname(__FILE__) . "/../../ssl/ssl"; - - copy "$certdir/server-cn-only.crt", "$pgdata/server.crt" - || die "copying server.crt: $!"; - copy "$certdir/server-cn-only.key", "$pgdata/server.key" - || die "copying server.key: $!"; - chmod(0600, "$pgdata/server.key") - or die "failed to change permissions on server keys: $!"; - - # Start with SSL disabled. - $node->append_conf('postgresql.conf', "ssl = off\n"); -} - -$node->start; - -$node->safe_psql('postgres', 'CREATE USER localuser;'); -$node->safe_psql('postgres', 'CREATE USER testuser;'); -$node->safe_psql('postgres', 'CREATE USER ssluser;'); -$node->safe_psql('postgres', 'CREATE USER nossluser;'); -$node->safe_psql('postgres', 'CREATE USER gssuser;'); -$node->safe_psql('postgres', 'CREATE USER nogssuser;'); - -my $unixdir = $node->safe_psql('postgres', 'SHOW unix_socket_directories;'); -chomp($unixdir); - -# Helper function that returns the encryption method in use in the -# connection. -$node->safe_psql('postgres', q{ -CREATE FUNCTION current_enc() RETURNS text LANGUAGE plpgsql AS $$ -DECLARE - ssl_in_use bool; - gss_in_use bool; -BEGIN - ssl_in_use = (SELECT ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid()); - gss_in_use = (SELECT encrypted FROM pg_stat_gssapi WHERE pid = pg_backend_pid()); - - raise log 'ssl % gss %', ssl_in_use, gss_in_use; - - IF ssl_in_use AND gss_in_use THEN - RETURN 'ssl+gss'; -- shouldn't happen - ELSIF ssl_in_use THEN - RETURN 'ssl'; - ELSIF gss_in_use THEN - RETURN 'gss'; - ELSE - RETURN 'plain'; - END IF; -END; -$$; -}); - -# Only accept SSL connections from $servercidr. Our tests don't depend on this -# but seems best to keep it as narrow as possible for security reasons. -open my $hba, '>', "$pgdata/pg_hba.conf" or die $!; -print $hba qq{ -# TYPE DATABASE USER ADDRESS METHOD OPTIONS -local postgres localuser trust -host postgres testuser $servercidr trust -hostnossl postgres nossluser $servercidr trust -hostnogssenc postgres nogssuser $servercidr trust -}; - -print $hba qq{ -hostssl postgres ssluser $servercidr trust -} if ($ssl_supported != 0); - -print $hba qq{ -hostgssenc postgres gssuser $servercidr trust -} if ($gss_supported != 0); -close $hba; -$node->reload; - -# Ok, all prepared. Run the tests. - -my @all_test_users = ('testuser', 'ssluser', 'nossluser', 'gssuser', 'nogssuser'); -my @all_gssencmodes = ('disable', 'prefer', 'require'); -my @all_sslmodes = ('disable', 'allow', 'prefer', 'require'); -my @all_sslnegotiations = ('postgres', 'direct', 'requiredirect'); - -my $server_config = { - server_ssl => 0, - server_gss => 0, -}; - -### -### Run tests with GSS and SSL disabled in the server -### -my $test_table; -if ($ssl_supported) { - $test_table = q{ -# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME -testuser disable disable * connect, authok -> plain -. . allow * connect, authok -> plain -. . prefer postgres connect, sslreject, authok -> plain -. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain -. . . requiredirect connect, directsslreject, reconnect, authok -> plain -. . require postgres connect, sslreject -> fail -. . . direct connect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, directsslreject -> fail -. prefer disable * connect, authok -> plain -. . allow * connect, authok -> plain -. . prefer postgres connect, sslreject, authok -> plain -. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain -. . . requiredirect connect, directsslreject, reconnect, authok -> plain -. . require postgres connect, sslreject -> fail -. . . direct connect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, directsslreject -> fail - }; -} else { - # Compiled without SSL support - $test_table = q{ -# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME -testuser disable disable postgres connect, authok -> plain -. . allow postgres connect, authok -> plain -. . prefer postgres connect, authok -> plain -. prefer disable postgres connect, authok -> plain -. . allow postgres connect, authok -> plain -. . prefer postgres connect, authok -> plain - -# Without SSL support, sslmode=require and sslnegotiation=direct/requiredirect -# are not accepted at all. -. * require * - -> fail -. * * direct - -> fail -. * * requiredirect - -> fail - }; -} - -# All attempts with gssencmode=require fail without connecting because -# no credential cache has been configured in the client. (Or if GSS -# support is not compiled in, they will fail because of that.) -$test_table .= q{ -testuser require * * - -> fail -}; - -note("Running tests with SSL and GSS disabled in the server"); -test_matrix($node, $server_config, - ['testuser'], \@all_gssencmodes, \@all_sslmodes, \@all_sslnegotiations, - parse_table($test_table)); - - -### -### Run tests with GSS disabled and SSL enabled in the server -### -SKIP: -{ - skip "SSL not supported by this build" if $ssl_supported == 0; - - $test_table = q{ -# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME -testuser disable disable * connect, authok -> plain -. . allow * connect, authok -> plain -. . prefer postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. . require postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -ssluser . disable * connect, authfail -> fail -. . allow postgres connect, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, authfail, reconnect, directsslaccept, authok -> ssl -. . prefer postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. . require postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -nossluser . disable * connect, authok -> plain -. . allow postgres connect, authok -> plain -. . . direct connect, authok -> plain -. . . requiredirect connect, authok -> plain -. . prefer postgres connect, sslaccept, authfail, reconnect, authok -> plain -. . . direct connect, directsslaccept, authfail, reconnect, authok -> plain -. . . requiredirect connect, directsslaccept, authfail, reconnect, authok -> plain -. . require postgres connect, sslaccept, authfail -> fail -. . require direct connect, directsslaccept, authfail -> fail -. . require requiredirect connect, directsslaccept, authfail -> fail -}; - - # Enable SSL in the server - $node->adjust_conf('postgresql.conf', 'ssl', 'on'); - $node->reload; - $server_config->{server_ssl} = 1; - - note("Running tests with SSL enabled in server"); - test_matrix($node, $server_config, - ['testuser', 'ssluser', 'nossluser'], - ['disable'], \@all_sslmodes, \@all_sslnegotiations, - parse_table($test_table)); - - # Disable SSL again - $node->adjust_conf('postgresql.conf', 'ssl', 'off'); - $node->reload; - $server_config->{server_ssl} = 0; -} - -### -### Run tests with GSS enabled, SSL disabled in the server -### -SKIP: -{ - skip "GSSAPI/Kerberos not supported by this build" if $gss_supported == 0; - - $krb->create_principal('gssuser', $gssuser_password); - $krb->create_ticket('gssuser', $gssuser_password); - $server_config->{server_gss} = 1; - - $test_table = q{ -# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME -testuser disable disable * connect, authok -> plain -. . allow * connect, authok -> plain -. . prefer postgres connect, sslreject, authok -> plain -. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain -. . . requiredirect connect, directsslreject, reconnect, authok -> plain -. . require postgres connect, sslreject -> fail -. . . direct connect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, directsslreject -> fail -. prefer * * connect, gssaccept, authok -> gss -. require * * connect, gssaccept, authok -> gss - -gssuser disable disable * connect, authfail -> fail -. . allow postgres connect, authfail, reconnect, sslreject -> fail -. . . direct connect, authfail, reconnect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, authfail, reconnect, directsslreject -> fail -. . prefer postgres connect, sslreject, authfail -> fail -. . . direct connect, directsslreject, reconnect, sslreject, authfail -> fail -. . . requiredirect connect, directsslreject, reconnect, authfail -> fail -. . require postgres connect, sslreject -> fail -. . . direct connect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, directsslreject -> fail -. prefer * * connect, gssaccept, authok -> gss -. require * * connect, gssaccept, authok -> gss - -nogssuser disable disable * connect, authok -> plain -. . allow postgres connect, authok -> plain -. . . direct connect, authok -> plain -. . . requiredirect connect, authok -> plain -. . prefer postgres connect, sslreject, authok -> plain -. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain -. . . requiredirect connect, directsslreject, reconnect, authok -> plain -. . require postgres connect, sslreject -> fail -. . . direct connect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, directsslreject -> fail -. prefer disable * connect, gssaccept, authfail, reconnect, authok -> plain -. . allow postgres connect, gssaccept, authfail, reconnect, authok -> plain -. . . direct connect, gssaccept, authfail, reconnect, authok -> plain -. . . requiredirect connect, gssaccept, authfail, reconnect, authok -> plain -. . prefer postgres connect, gssaccept, authfail, reconnect, sslreject, authok -> plain -. . . direct connect, gssaccept, authfail, reconnect, directsslreject, reconnect, sslreject, authok -> plain -. . . requiredirect connect, gssaccept, authfail, reconnect, directsslreject, reconnect, authok -> plain -. . require postgres connect, gssaccept, authfail, reconnect, sslreject -> fail -. . . direct connect, gssaccept, authfail, reconnect, directsslreject, reconnect, sslreject -> fail -. . . requiredirect connect, gssaccept, authfail, reconnect, directsslreject -> fail -. require disable * connect, gssaccept, authfail -> fail -. . allow * connect, gssaccept, authfail -> fail -. . prefer * connect, gssaccept, authfail -> fail -. . require * connect, gssaccept, authfail -> fail # If both GSSAPI and sslmode are required, and GSS is not available -> fail - }; - - # The expected events and outcomes above assume that SSL support - # is enabled. When libpq is compiled without SSL support, all - # attempts to connect with sslmode=require or - # sslnegotition=direct/requiredirectwould fail immediately without - # even connecting to the server. Skip those, because we tested - # them earlier already. - my ($sslmodes, $sslnegotiations); - if ($ssl_supported != 0) { - ($sslmodes, $sslnegotiations) = (\@all_sslmodes, \@all_sslnegotiations); - } else { - ($sslmodes, $sslnegotiations) = (['disable'], ['postgres']); - } - - note("Running tests with GSS enabled in server"); - test_matrix($node, $server_config, - ['testuser', 'gssuser', 'nogssuser'], - \@all_gssencmodes, $sslmodes, $sslnegotiations, - parse_table($test_table)); -} - -### -### Tests with both GSS and SSL enabled in the server -### -SKIP: -{ - skip "GSSAPI/Kerberos or SSL not supported by this build" unless ($ssl_supported && $gss_supported); - - # Sanity check that GSSAPI is still enabled from previous test. - connect_test($node, 'user=testuser gssencmode=prefer sslmode=prefer', 'connect, gssaccept, authok -> gss'); - - # Enable SSL - $node->adjust_conf('postgresql.conf', 'ssl', 'on'); - $node->reload; - $server_config->{server_ssl} = 1; - - $test_table = q{ -# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME -testuser disable disable * connect, authok -> plain -. . allow * connect, authok -> plain -. . prefer postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. . require postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. prefer disable * connect, gssaccept, authok -> gss -. . allow * connect, gssaccept, authok -> gss -. . prefer * connect, gssaccept, authok -> gss -. . require * connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require -. require disable * connect, gssaccept, authok -> gss -. . allow * connect, gssaccept, authok -> gss -. . prefer * connect, gssaccept, authok -> gss -. . require * connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require - -gssuser disable disable * connect, authfail -> fail -. . allow postgres connect, authfail, reconnect, sslaccept, authfail -> fail -. . . direct connect, authfail, reconnect, directsslaccept, authfail -> fail -. . . requiredirect connect, authfail, reconnect, directsslaccept, authfail -> fail -. . prefer postgres connect, sslaccept, authfail, reconnect, authfail -> fail -. . . direct connect, directsslaccept, authfail, reconnect, authfail -> fail -. . . requiredirect connect, directsslaccept, authfail, reconnect, authfail -> fail -. . require postgres connect, sslaccept, authfail -> fail -. . . direct connect, directsslaccept, authfail -> fail -. . . requiredirect connect, directsslaccept, authfail -> fail -. prefer disable * connect, gssaccept, authok -> gss -. . allow * connect, gssaccept, authok -> gss -. . prefer * connect, gssaccept, authok -> gss -. . require * connect, gssaccept, authok -> gss # GSS is chosen over SSL, even though sslmode=require -. require disable * connect, gssaccept, authok -> gss -. . allow * connect, gssaccept, authok -> gss -. . prefer * connect, gssaccept, authok -> gss -. . require * connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require - -ssluser disable disable * connect, authfail -> fail -. . allow postgres connect, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, authfail, reconnect, directsslaccept, authok -> ssl -. . prefer postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. . require postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. prefer disable * connect, gssaccept, authfail, reconnect, authfail -> fail -. . allow postgres connect, gssaccept, authfail, reconnect, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, gssaccept, authfail, reconnect, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, gssaccept, authfail, reconnect, authfail, reconnect, directsslaccept, authok -> ssl -. . prefer postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. . require postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. require disable * connect, gssaccept, authfail -> fail -. . allow * connect, gssaccept, authfail -> fail -. . prefer * connect, gssaccept, authfail -> fail -. . require * connect, gssaccept, authfail -> fail # If both GSS and SSL are required, the sslmode=require is effectively ignored and GSS is required - -nogssuser disable disable * connect, authok -> plain -. . allow postgres connect, authok -> plain -. . . direct connect, authok -> plain -. . . requiredirect connect, authok -> plain -. . prefer postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. . require postgres connect, sslaccept, authok -> ssl -. . . direct connect, directsslaccept, authok -> ssl -. . . requiredirect connect, directsslaccept, authok -> ssl -. prefer disable * connect, gssaccept, authfail, reconnect, authok -> plain -. . allow * connect, gssaccept, authfail, reconnect, authok -> plain -. . prefer postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. . require postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl -. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl -. require disable * connect, gssaccept, authfail -> fail -. . allow * connect, gssaccept, authfail -> fail -. . prefer * connect, gssaccept, authfail -> fail -. . require * connect, gssaccept, authfail -> fail # If both GSS and SSL are required, the sslmode=require is effectively ignored and GSS is required - -nossluser disable disable * connect, authok -> plain -. . allow * connect, authok -> plain -. . prefer postgres connect, sslaccept, authfail, reconnect, authok -> plain -. . . direct connect, directsslaccept, authfail, reconnect, authok -> plain -. . . requiredirect connect, directsslaccept, authfail, reconnect, authok -> plain -. . require postgres connect, sslaccept, authfail -> fail -. . . direct connect, directsslaccept, authfail -> fail -. . . requiredirect connect, directsslaccept, authfail -> fail -. prefer * * connect, gssaccept, authok -> gss -. require * * connect, gssaccept, authok -> gss - }; - - note("Running tests with both GSS and SSL enabled in server"); - test_matrix($node, $server_config, - ['testuser', 'gssuser', 'ssluser', 'nogssuser', 'nossluser'], - \@all_gssencmodes, \@all_sslmodes, \@all_sslnegotiations, - parse_table($test_table)); -} - -### -### Test negotiation over unix domain sockets. -### -SKIP: -{ - skip "Unix domain sockets not supported" unless ($unixdir ne ""); - - # libpq doesn't attempt SSL or GSSAPI over Unix domain - # sockets. The server would reject them too. - connect_test($node, "user=localuser gssencmode=prefer sslmode=prefer host=$unixdir", 'connect, authok -> plain'); - connect_test($node, "user=localuser gssencmode=require sslmode=prefer host=$unixdir", '- -> fail'); -} - -done_testing(); - - -### Helper functions - -# Test the cube of parameters: user, gssencmode, sslmode, and sslnegotitation -sub test_matrix -{ - local $Test::Builder::Level = $Test::Builder::Level + 1; - - my ($pg_node, $node_conf, - $test_users, $gssencmodes, $sslmodes, $sslnegotiations, %expected) = @_; - - foreach my $test_user (@{$test_users}) - { - foreach my $gssencmode (@{$gssencmodes}) - { - foreach my $client_mode (@{$sslmodes}) - { - # sslnegotiation only makes a difference if SSL is enabled. This saves a few combinations. - my ($key, $expected_events); - foreach my $negotiation (@{$sslnegotiations}) - { - $key = "$test_user $gssencmode $client_mode $negotiation"; - $expected_events = $expected{$key}; - if (!defined($expected_events)) { - $expected_events = "<line missing from expected output table>"; - } - connect_test($pg_node, "user=$test_user gssencmode=$gssencmode sslmode=$client_mode sslnegotiation=$negotiation", $expected_events); - } - } - } - } -} - -# Try to establish a connection to the server using libpq. Verify the -# negotiation events and outcome. -sub connect_test -{ - local $Test::Builder::Level = $Test::Builder::Level + 1; - - my ($node, $connstr, $expected_events_and_outcome) = @_; - - my $test_name = " '$connstr' -> $expected_events_and_outcome"; - - my $connstr_full = ""; - $connstr_full .= "dbname=postgres " unless $connstr =~ m/dbname=/; - $connstr_full .= "host=$host hostaddr=$hostaddr " unless $connstr =~ m/host=/; - $connstr_full .= $connstr; - - # Get the current size of the logfile before running the test. - # After the test, we can then check just the new lines that have - # appeared. (This is the same approach that the $node->log_contains - # function uses). - my $log_location = -s $node->logfile; - - # XXX: Pass command with -c, because I saw intermittent test - # failures like this: - # - # ack Broken pipe: write( 13, 'SELECT current_enc()' ) at /usr/local/lib/perl5/site_perl/IPC/Run/IO.pm line 550. - # - # I think that happens if the connection fails before we write the - # query to its stdin. This test gets a lot of connection failures - # on purpose. - my ($ret, $stdout, $stderr) = $node->psql( - 'postgres', - '', - extra_params => ['-w', '-c', 'SELECT current_enc()'], - connstr => "$connstr_full", - on_error_stop => 0); - - my $outcome = $ret == 0 ? $stdout : 'fail'; - - # Parse the EVENTS from the log file. - my $log_contents = - PostgreSQL::Test::Utils::slurp_file($node->logfile, $log_location); - my @events = parse_log_events($log_contents); - - # Check that the events and outcome match the expected events and - # outcome - my $events_and_outcome = join(', ', @events) . " -> $outcome"; - is($events_and_outcome, $expected_events_and_outcome, $test_name) or diag("$stderr"); -} - -# Parse a test table. See comment at top of the file for the format. -sub parse_table -{ - my ($table) = @_; - my @lines = split /\n/, $table; - - my %expected; - - my ($user, $gssencmode, $sslmode, $sslnegotiation); - foreach my $line (@lines) { - - # Trim comments - $line =~ s/#.*$//; - - # Trim whitespace at beginning and end - $line =~ s/^\s+//; - $line =~ s/\s+$//; - - # Ignore empty lines (includes comment-only lines) - next if $line eq ''; - - $line =~ m/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S.*)\s*->\s*(\S+)\s*$/ or die "could not parse line \"$line\""; - $user = $1 unless $1 eq "."; - $gssencmode = $2 unless $2 eq "."; - $sslmode = $3 unless $3 eq "."; - $sslnegotiation = $4 unless $4 eq "."; - - # Normalize the whitespace in the "EVENTS -> OUTCOME" part - my @events = split /,\s*/, $5; - my $outcome = $6; - my $events_str = join(', ', @events); - $events_str =~ s/\s+$//; # trim whitespace - my $events_and_outcome = "$events_str -> $outcome"; - - my %expanded = expand_expected_line($user, $gssencmode, $sslmode, $sslnegotiation, $events_and_outcome); - %expected = (%expected, %expanded); - } - return %expected; -} - -# Expand wildcards on a test table line -sub expand_expected_line -{ - my ($user, $gssencmode, $sslmode, $sslnegotiation, $expected) = @_; - - my %result; - if ($user eq '*') { - foreach my $x (@all_test_users) { - %result = (%result, expand_expected_line($x, $gssencmode, $sslmode, $sslnegotiation, $expected)); - } - } elsif ($gssencmode eq '*') { - foreach my $x (@all_gssencmodes) { - %result = (%result, expand_expected_line($user, $x, $sslmode, $sslnegotiation, $expected)); - } - } elsif ($sslmode eq '*') { - foreach my $x (@all_sslmodes) { - %result = (%result, expand_expected_line($user, $gssencmode, $x, $sslnegotiation, $expected)); - } - } elsif ($sslnegotiation eq '*') { - foreach my $x (@all_sslnegotiations) { - %result = (%result, expand_expected_line($user, $gssencmode, $sslmode, $x, $expected)); - } - } else { - $result{"$user $gssencmode $sslmode $sslnegotiation"} = $expected; - } - return %result; -} - -# Scrape the server log for the negotiation events that match the -# EVENTS field of the test tables. -sub parse_log_events -{ - my ($log_contents) = (@_); - - my @events = (); - - my @lines = split /\n/, $log_contents; - foreach my $line (@lines) { - push @events, "reconnect" if $line =~ /connection received/ && scalar(@events) > 0; - push @events, "connect" if $line =~ /connection received/ && scalar(@events) == 0; - push @events, "sslaccept" if $line =~ /SSLRequest accepted/; - push @events, "sslreject" if $line =~ /SSLRequest rejected/; - push @events, "directsslaccept" if $line =~ /direct SSL connection accepted/; - push @events, "directsslreject" if $line =~ /direct SSL connection rejected/; - push @events, "gssaccept" if $line =~ /GSSENCRequest accepted/; - push @events, "gssreject" if $line =~ /GSSENCRequest rejected/; - push @events, "authfail" if $line =~ /no pg_hba.conf entry/; - push @events, "authok" if $line =~ /connection authenticated/; - } - - # No events at all is represented by "-" - if (scalar @events == 0) { - push @events, "-" - } - - return @events; -} |