mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
libpq: Add support for require_auth to control authorized auth methods
The new connection parameter require_auth allows a libpq client to define a list of comma-separated acceptable authentication types for use with the server. There is no negotiation: if the server does not present one of the allowed authentication requests, the connection attempt done by the client fails. The following keywords can be defined in the list: - password, for AUTH_REQ_PASSWORD. - md5, for AUTH_REQ_MD5. - gss, for AUTH_REQ_GSS[_CONT]. - sspi, for AUTH_REQ_SSPI and AUTH_REQ_GSS_CONT. - scram-sha-256, for AUTH_REQ_SASL[_CONT|_FIN]. - creds, for AUTH_REQ_SCM_CREDS (perhaps this should be removed entirely now). - none, to control unauthenticated connections. All the methods that can be defined in the list can be negated, like "!password", in which case the server must NOT use the listed authentication type. The special method "none" allows/disallows the use of unauthenticated connections (but it does not govern transport-level authentication via TLS or GSSAPI). Internally, the patch logic is tied to check_expected_areq(), that was used for channel_binding, ensuring that an incoming request is compatible with conn->require_auth. It also introduces a new flag, conn->client_finished_auth, which is set by various authentication routines when the client side of the handshake is finished. This signals to check_expected_areq() that an AUTH_REQ_OK from the server is expected, and allows the client to complain if the server bypasses authentication entirely, with for example the reception of a too-early AUTH_REQ_OK message. Regression tests are added in authentication TAP tests for all the keywords supported (except "creds", because it is around only for compatibility reasons). A new TAP script has been added for SSPI, as there was no script dedicated to it yet. It relies on SSPI being the default authentication method on Windows, as set by pg_regress. Author: Jacob Champion Reviewed-by: Peter Eisentraut, David G. Johnston, Michael Paquier Discussion: https://postgr.es/m/9e5a8ccddb8355ea9fa4b75a1e3a9edc88a70cd3.camel@vmware.com
This commit is contained in:
@@ -10,6 +10,7 @@ tests += {
|
||||
't/002_saslprep.pl',
|
||||
't/003_peer.pl',
|
||||
't/004_file_inclusion.pl',
|
||||
't/005_sspi.pl',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@@ -115,6 +115,114 @@ is($res, 't',
|
||||
"users with trust authentication use SYSTEM_USER = NULL in parallel workers"
|
||||
);
|
||||
|
||||
# Explicitly specifying an empty require_auth (the default) should always
|
||||
# succeed.
|
||||
$node->connect_ok("user=scram_role require_auth=",
|
||||
"empty require_auth succeeds");
|
||||
|
||||
# All these values of require_auth should fail, as trust is expected.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=gss",
|
||||
"GSS authentication required, fails with trust auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "gss" requirement failed: server did not complete authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=sspi",
|
||||
"SSPI authentication required, fails with trust auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "sspi" requirement failed: server did not complete authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=password",
|
||||
"password authentication required, fails with trust auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "password" requirement failed: server did not complete authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=md5",
|
||||
"MD5 authentication required, fails with trust auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "md5" requirement failed: server did not complete authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=scram-sha-256",
|
||||
"SCRAM authentication required, fails with trust auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "scram-sha-256" requirement failed: server did not complete authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=password,scram-sha-256",
|
||||
"password and SCRAM authentication required, fails with trust auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "password,scram-sha-256" requirement failed: server did not complete authentication/
|
||||
);
|
||||
|
||||
# These negative patterns of require_auth should succeed.
|
||||
$node->connect_ok("user=scram_role require_auth=!gss",
|
||||
"GSS authentication can be forbidden, succeeds with trust auth");
|
||||
$node->connect_ok("user=scram_role require_auth=!sspi",
|
||||
"SSPI authentication can be forbidden, succeeds with trust auth");
|
||||
$node->connect_ok("user=scram_role require_auth=!password",
|
||||
"password authentication can be forbidden, succeeds with trust auth");
|
||||
$node->connect_ok("user=scram_role require_auth=!md5",
|
||||
"md5 authentication can be forbidden, succeeds with trust auth");
|
||||
$node->connect_ok("user=scram_role require_auth=!scram-sha-256",
|
||||
"SCRAM authentication can be forbidden, succeeds with trust auth");
|
||||
$node->connect_ok(
|
||||
"user=scram_role require_auth=!password,!scram-sha-256",
|
||||
"multiple authentication types forbidden, succeeds with trust auth");
|
||||
|
||||
# require_auth=[!]none should interact correctly with trust auth.
|
||||
$node->connect_ok("user=scram_role require_auth=none",
|
||||
"all authentication types forbidden, succeeds with trust auth");
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!none",
|
||||
"any authentication types required, fails with trust auth",
|
||||
expected_stderr => qr/server did not complete authentication/);
|
||||
|
||||
# Negative and positive require_auth methods can't be mixed.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=scram-sha-256,!md5",
|
||||
"negative require_auth methods cannot be mixed with positive ones",
|
||||
expected_stderr =>
|
||||
qr/negative require_auth method "!md5" cannot be mixed with non-negative methods/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!password,!none,scram-sha-256",
|
||||
"positive require_auth methods cannot be mixed with negative one",
|
||||
expected_stderr =>
|
||||
qr/require_auth method "scram-sha-256" cannot be mixed with negative methods/
|
||||
);
|
||||
|
||||
# require_auth methods cannot have duplicated values.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=password,md5,password",
|
||||
"require_auth methods cannot include duplicates, positive case",
|
||||
expected_stderr =>
|
||||
qr/require_auth method "password" is specified more than once/);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!password,!md5,!password",
|
||||
"require_auth methods cannot be duplicated, negative case",
|
||||
expected_stderr =>
|
||||
qr/require_auth method "!password" is specified more than once/);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=none,md5,none",
|
||||
"require_auth methods cannot be duplicated, none case",
|
||||
expected_stderr =>
|
||||
qr/require_auth method "none" is specified more than once/);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!none,!md5,!none",
|
||||
"require_auth methods cannot be duplicated, !none case",
|
||||
expected_stderr =>
|
||||
qr/require_auth method "!none" is specified more than once/);
|
||||
|
||||
# Unknown value defined in require_auth.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=none,abcdefg",
|
||||
"unknown require_auth methods are rejected",
|
||||
expected_stderr => qr/invalid require_auth method: "abcdefg"/);
|
||||
|
||||
# For plain "password" method, all users should also be able to connect.
|
||||
reset_pg_hba($node, 'all', 'all', 'password');
|
||||
test_conn($node, 'user=scram_role', 'password', 0,
|
||||
@@ -124,6 +232,47 @@ test_conn($node, 'user=md5_role', 'password', 0,
|
||||
log_like =>
|
||||
[qr/connection authenticated: identity="md5_role" method=password/]);
|
||||
|
||||
# require_auth succeeds here with a plaintext password.
|
||||
$node->connect_ok("user=scram_role require_auth=password",
|
||||
"password authentication required, works with password auth");
|
||||
$node->connect_ok("user=scram_role require_auth=!none",
|
||||
"any authentication required, works with password auth");
|
||||
$node->connect_ok(
|
||||
"user=scram_role require_auth=scram-sha-256,password,md5",
|
||||
"multiple authentication types required, works with password auth");
|
||||
|
||||
# require_auth fails for other authentication types.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=md5",
|
||||
"md5 authentication required, fails with password auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "md5" requirement failed: server requested a cleartext password/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=scram-sha-256",
|
||||
"SCRAM authentication required, fails with password auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "scram-sha-256" requirement failed: server requested a cleartext password/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=none",
|
||||
"all authentication forbidden, fails with password auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "none" requirement failed: server requested a cleartext password/
|
||||
);
|
||||
|
||||
# Disallowing password authentication fails, even if requested by server.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!password",
|
||||
"password authentication forbidden, fails with password auth",
|
||||
expected_stderr => qr/server requested a cleartext password/);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!password,!md5,!scram-sha-256",
|
||||
"multiple authentication types forbidden, fails with password auth",
|
||||
expected_stderr =>
|
||||
qr/ method "!password,!md5,!scram-sha-256" requirement failed: server requested a cleartext password/
|
||||
);
|
||||
|
||||
# For "scram-sha-256" method, user "scram_role" should be able to connect.
|
||||
reset_pg_hba($node, 'all', 'all', 'scram-sha-256');
|
||||
test_conn(
|
||||
@@ -137,6 +286,46 @@ test_conn(
|
||||
test_conn($node, 'user=md5_role', 'scram-sha-256', 2,
|
||||
log_unlike => [qr/connection authenticated:/]);
|
||||
|
||||
# require_auth should succeeds with SCRAM when it is required.
|
||||
$node->connect_ok(
|
||||
"user=scram_role require_auth=scram-sha-256",
|
||||
"SCRAM authentication required, works with SCRAM auth");
|
||||
$node->connect_ok("user=scram_role require_auth=!none",
|
||||
"any authentication required, works with SCRAM auth");
|
||||
$node->connect_ok(
|
||||
"user=scram_role require_auth=password,scram-sha-256,md5",
|
||||
"multiple authentication types required, works with SCRAM auth");
|
||||
|
||||
# Authentication fails for other authentication types.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=password",
|
||||
"password authentication required, fails with SCRAM auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "password" requirement failed: server requested SASL authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=md5",
|
||||
"md5 authentication required, fails with SCRAM auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "md5" requirement failed: server requested SASL authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=none",
|
||||
"all authentication forbidden, fails with SCRAM auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "none" requirement failed: server requested SASL authentication/
|
||||
);
|
||||
|
||||
# Authentication fails if SCRAM authentication is forbidden.
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!scram-sha-256",
|
||||
"SCRAM authentication forbidden, fails with SCRAM auth",
|
||||
expected_stderr => qr/server requested SASL authentication/);
|
||||
$node->connect_fails(
|
||||
"user=scram_role require_auth=!password,!md5,!scram-sha-256",
|
||||
"multiple authentication types forbidden, fails with SCRAM auth",
|
||||
expected_stderr => qr/server requested SASL authentication/);
|
||||
|
||||
# Test that bad passwords are rejected.
|
||||
$ENV{"PGPASSWORD"} = 'badpass';
|
||||
test_conn($node, 'user=scram_role', 'scram-sha-256', 2,
|
||||
@@ -153,6 +342,49 @@ test_conn($node, 'user=md5_role', 'md5', 0,
|
||||
log_like =>
|
||||
[qr/connection authenticated: identity="md5_role" method=md5/]);
|
||||
|
||||
# require_auth succeeds with MD5 required.
|
||||
$node->connect_ok("user=md5_role require_auth=md5",
|
||||
"MD5 authentication required, works with MD5 auth");
|
||||
$node->connect_ok("user=md5_role require_auth=!none",
|
||||
"any authentication required, works with MD5 auth");
|
||||
$node->connect_ok(
|
||||
"user=md5_role require_auth=md5,scram-sha-256,password",
|
||||
"multiple authentication types required, works with MD5 auth");
|
||||
|
||||
# Authentication fails if other types are required.
|
||||
$node->connect_fails(
|
||||
"user=md5_role require_auth=password",
|
||||
"password authentication required, fails with MD5 auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "password" requirement failed: server requested a hashed password/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=md5_role require_auth=scram-sha-256",
|
||||
"SCRAM authentication required, fails with MD5 auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "scram-sha-256" requirement failed: server requested a hashed password/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=md5_role require_auth=none",
|
||||
"all authentication types forbidden, fails with MD5 auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "none" requirement failed: server requested a hashed password/
|
||||
);
|
||||
|
||||
# Authentication fails if MD5 is forbidden.
|
||||
$node->connect_fails(
|
||||
"user=md5_role require_auth=!md5",
|
||||
"password authentication forbidden, fails with MD5 auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "!md5" requirement failed: server requested a hashed password/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"user=md5_role require_auth=!password,!md5,!scram-sha-256",
|
||||
"multiple authentication types forbidden, fails with MD5 auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "!password,!md5,!scram-sha-256" requirement failed: server requested a hashed password/
|
||||
);
|
||||
|
||||
# Test SYSTEM_USER <> NULL with parallel workers.
|
||||
$node->safe_psql(
|
||||
'postgres',
|
||||
|
41
src/test/authentication/t/005_sspi.pl
Normal file
41
src/test/authentication/t/005_sspi.pl
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
# Copyright (c) 2021-2023, PostgreSQL Global Development Group
|
||||
|
||||
# Tests targeting SSPI on Windows.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use PostgreSQL::Test::Cluster;
|
||||
use PostgreSQL::Test::Utils;
|
||||
use Test::More;
|
||||
|
||||
if (!$windows_os || $use_unix_sockets)
|
||||
{
|
||||
plan skip_all =>
|
||||
"SSPI tests require Windows (without PG_TEST_USE_UNIX_SOCKETS)";
|
||||
}
|
||||
|
||||
# Initialize primary node
|
||||
my $node = PostgreSQL::Test::Cluster->new('primary');
|
||||
$node->init;
|
||||
$node->append_conf('postgresql.conf', "log_connections = on\n");
|
||||
$node->start;
|
||||
|
||||
# SSPI is set up by default. Make sure it interacts correctly with
|
||||
# require_auth.
|
||||
$node->connect_ok("require_auth=sspi",
|
||||
"SSPI authentication required, works with SSPI auth");
|
||||
$node->connect_fails(
|
||||
"require_auth=!sspi",
|
||||
"SSPI authentication forbidden, fails with SSPI auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "!sspi" requirement failed: server requested SSPI authentication/
|
||||
);
|
||||
$node->connect_fails(
|
||||
"require_auth=scram-sha-256",
|
||||
"SCRAM authentication required, fails with SSPI auth",
|
||||
expected_stderr =>
|
||||
qr/auth method "scram-sha-256" requirement failed: server requested SSPI authentication/
|
||||
);
|
||||
|
||||
done_testing();
|
Reference in New Issue
Block a user