Fix libpq_encryption tests when compiled without SSL support
authorHeikki Linnakangas
Fri, 12 Apr 2024 16:52:28 +0000 (19:52 +0300)
committerHeikki Linnakangas
Fri, 12 Apr 2024 16:52:28 +0000 (19:52 +0300)
It correctly skipped tests involving SSL in the server when SSL
support was not compiled in, but even when SSL is not enabled in the
server and the connection is established without SSL, libpq behaves
differently in many of the test scenarios when libpq is compiled
without SSL support. For example, with sslmode=prefer, if libpq is
compiled with SSL support it will attempt to use SSL, but without SSL
support it will try authenticating in plaintext mode directly. The
expected test output didn't take that into account.

Discussion: https://www.postgresql.org/message-id/CA%2BhUKG%2BHRTtB%2Bx%[email protected]

src/test/libpq_encryption/t/001_negotiate_encryption.pl

index e18e9ad4ad4e49ee7193148c94e9cd3e072c8067..d07d9498bbc3518c75ce007b193262235dd5fe00 100644 (file)
@@ -9,7 +9,7 @@
 # We test all combinations of:
 #
 # - all the libpq client options that affect the protocol negotiations
-#   (gssencmode, sslmode)
+#   (gssencmode, sslmode, sslnegotiation)
 # - server accepting or rejecting the authentication due to
 #   pg_hba.conf entries
 # - SSL and GSS enabled/disabled in the server
@@ -216,7 +216,9 @@ my $server_config = {
 ###
 ### Run tests with GSS and SSL disabled in the server
 ###
-my $test_table = q{
+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
@@ -227,24 +229,45 @@ testuser    disable      disable      *              connect, authok
 .           .            .            direct         connect, directsslreject, reconnect, sslreject -> fail
 .           .            .            requiredirect  connect, directsslreject    -> fail
 .           prefer       disable      *              connect, authok             -> plain
-.           .            allow        postgres       connect, authok             -> plain
-.           .            .            direct         connect, authok             -> plain
-.           .            .            requiredirect  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
-.           require      *            *              - -> fail
+# 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_sslmodes, \@all_gssencmodes, parse_table($test_table));
+           ['testuser'], \@all_gssencmodes, \@all_sslmodes, \@all_sslnegotiations,
+           parse_table($test_table));
+
 
 ###
 ### Run tests with GSS disabled and SSL enabled in the server
@@ -293,7 +316,8 @@ nossluser   .            disable      *              connect, authok
    note("Running tests with SSL enabled in server");
    test_matrix($node, $server_config,
                ['testuser', 'ssluser', 'nossluser'],
-               \@all_sslmodes, ['disable'], parse_table($test_table));
+               ['disable'], \@all_sslmodes, \@all_sslnegotiations,
+               parse_table($test_table));
 
    # Disable SSL again
    $node->adjust_conf('postgresql.conf', 'ssl', 'off');
@@ -307,6 +331,11 @@ nossluser   .            disable      *              connect, authok
 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
@@ -357,20 +386,26 @@ nogssuser   disable      disable      *              connect, authok
 .           .            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
-};
-
-   # Sanity check that the connection fails when no kerberos ticket
-   # is present in the client
-   connect_test($node, 'user=testuser gssencmode=require sslmode=disable', '- -> fail');
-
-   $krb->create_principal('gssuser', $gssuser_password);
-   $krb->create_ticket('gssuser', $gssuser_password);
-   $server_config->{server_gss} = 1;
+   };
+
+   # 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_sslmodes, \@all_gssencmodes, parse_table($test_table));
+               \@all_gssencmodes, $sslmodes, $sslnegotiations,
+               parse_table($test_table));
 }
 
 ###
@@ -380,6 +415,14 @@ 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
@@ -476,20 +519,13 @@ nossluser   disable      disable      *              connect, authok
 .           .            .            requiredirect  connect, directsslaccept, authfail -> fail
 .           prefer       *            *              connect, gssaccept, authok   -> gss
 .           require      *            *              connect, gssaccept, authok   -> gss
-};
-
-   # 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;
+   };
 
    note("Running tests with both GSS and SSL enabled in server");
    test_matrix($node, $server_config,
                ['testuser', 'gssuser', 'ssluser', 'nogssuser', 'nossluser'],
-               \@all_sslmodes, \@all_gssencmodes, parse_table($test_table));
+               \@all_gssencmodes, \@all_sslmodes, \@all_sslnegotiations,
+               parse_table($test_table));
 }
 
 ###
@@ -499,7 +535,9 @@ SKIP:
 {
    skip "Unix domain sockets not supported" unless ($unixdir ne "");
 
-   connect_test($node, "user=localuser gssencmode=prefer sslmode=require host=$unixdir", 'connect, authok -> plain');
+   # 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');
 }
 
@@ -514,7 +552,7 @@ sub test_matrix
    local $Test::Builder::Level = $Test::Builder::Level + 1;
 
    my ($pg_node, $node_conf,
-       $test_users, $sslmodes, $gssencmodes, %expected) = @_;
+       $test_users, $gssencmodes, $sslmodes, $sslnegotiations, %expected) = @_;
 
    foreach my $test_user (@{$test_users})
    {
@@ -524,7 +562,7 @@ sub test_matrix
            {
                # sslnegotiation only makes a difference if SSL is enabled. This saves a few combinations.
                my ($key, $expected_events);
-               foreach my $negotiation (@all_sslnegotiations)
+               foreach my $negotiation (@{$sslnegotiations})
                {
                    $key = "$test_user $gssencmode $client_mode $negotiation";
                    $expected_events = $expected{$key};