aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/auth.c47
-rw-r--r--src/backend/libpq/hba.c33
-rw-r--r--src/include/libpq/hba.h9
-rw-r--r--src/test/ssl/t/001_ssltests.pl23
-rw-r--r--src/test/ssl/t/SSLServer.pm10
5 files changed, 99 insertions, 23 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index d5115aad729..fb86e9e9d41 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -363,7 +363,7 @@ ClientAuthentication(Port *port)
* current connection, so perform any verifications based on the hba
* options field that should be done *before* the authentication here.
*/
- if (port->hba->clientcert)
+ if (port->hba->clientcert != clientCertOff)
{
/* If we haven't loaded a root certificate store, fail */
if (!secure_loaded_verify_locations())
@@ -583,22 +583,30 @@ ClientAuthentication(Port *port)
Assert(false);
#endif
break;
-
- case uaCert:
-#ifdef USE_SSL
- status = CheckCertAuth(port);
-#else
- Assert(false);
-#endif
- break;
case uaRADIUS:
status = CheckRADIUSAuth(port);
break;
+ case uaCert:
+ /* uaCert will be treated as if clientcert=verify-full (uaTrust) */
case uaTrust:
status = STATUS_OK;
break;
}
+ if ((status == STATUS_OK && port->hba->clientcert == clientCertFull)
+ || port->hba->auth_method == uaCert)
+ {
+ /*
+ * Make sure we only check the certificate if we use the cert method
+ * or verify-full option.
+ */
+#ifdef USE_SSL
+ status = CheckCertAuth(port);
+#else
+ Assert(false);
+#endif
+ }
+
if (ClientAuthentication_hook)
(*ClientAuthentication_hook) (port, status);
@@ -2788,6 +2796,8 @@ errdetail_for_ldap(LDAP *ldap)
static int
CheckCertAuth(Port *port)
{
+ int status_check_usermap = STATUS_ERROR;
+
Assert(port->ssl);
/* Make sure we have received a username in the certificate */
@@ -2800,8 +2810,23 @@ CheckCertAuth(Port *port)
return STATUS_ERROR;
}
- /* Just pass the certificate CN to the usermap check */
- return check_usermap(port->hba->usermap, port->user_name, port->peer_cn, false);
+ /* Just pass the certificate cn to the usermap check */
+ status_check_usermap = check_usermap(port->hba->usermap, port->user_name, port->peer_cn, false);
+ if (status_check_usermap != STATUS_OK)
+ {
+ /*
+ * If clientcert=verify-full was specified and the authentication
+ * method is other than uaCert, log the reason for rejecting the
+ * authentication.
+ */
+ if (port->hba->clientcert == clientCertFull && port->hba->auth_method != uaCert)
+ {
+ ereport(LOG,
+ (errmsg("certificate validation (clientcert=verify-full) failed for user \"%s\": cn mismatch",
+ port->user_name)));
+ }
+ }
+ return status_check_usermap;
}
#endif
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index b17c7147355..59de1b76395 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1609,7 +1609,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
*/
if (parsedline->auth_method == uaCert)
{
- parsedline->clientcert = true;
+ parsedline->clientcert = clientCertCA;
}
return parsedline;
@@ -1675,23 +1675,38 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
*err_msg = "clientcert can only be configured for \"hostssl\" rows";
return false;
}
- if (strcmp(val, "1") == 0)
+ if (strcmp(val, "1") == 0
+ || strcmp(val, "verify-ca") == 0)
{
- hbaline->clientcert = true;
+ hbaline->clientcert = clientCertCA;
}
- else
+ else if (strcmp(val, "verify-full") == 0)
+ {
+ hbaline->clientcert = clientCertFull;
+ }
+ else if (strcmp(val, "0") == 0
+ || strcmp(val, "no-verify") == 0)
{
if (hbaline->auth_method == uaCert)
{
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
+ errmsg("clientcert can not be set to \"no-verify\" when using \"cert\" authentication"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- *err_msg = "clientcert can not be set to 0 when using \"cert\" authentication";
+ *err_msg = "clientcert can not be set to \"no-verify\" when using \"cert\" authentication";
return false;
}
- hbaline->clientcert = false;
+ hbaline->clientcert = clientCertOff;
+ }
+ else
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("invalid value for clientcert: \"%s\"", val),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
}
}
else if (strcmp(name, "pamservice") == 0)
@@ -2252,9 +2267,9 @@ gethba_options(HbaLine *hba)
options[noptions++] =
CStringGetTextDatum(psprintf("map=%s", hba->usermap));
- if (hba->clientcert)
+ if (hba->clientcert != clientCertOff)
options[noptions++] =
- CStringGetTextDatum("clientcert=true");
+ CStringGetTextDatum(psprintf("clientcert=%s", (hba->clientcert == clientCertCA) ? "verify-ca" : "verify-full"));
if (hba->pamservice)
options[noptions++] =
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 5f68f4c6661..c65eb9dc8a5 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -58,6 +58,13 @@ typedef enum ConnType
ctHostNoSSL
} ConnType;
+typedef enum ClientCertMode
+{
+ clientCertOff,
+ clientCertCA,
+ clientCertFull
+} ClientCertMode;
+
typedef struct HbaLine
{
int linenumber;
@@ -86,7 +93,7 @@ typedef struct HbaLine
int ldapscope;
char *ldapprefix;
char *ldapsuffix;
- bool clientcert;
+ ClientCertMode clientcert;
char *krb_realm;
bool include_realm;
bool compat_realm;
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 2f6dfad23c4..d2346baf2a1 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -13,7 +13,7 @@ use SSLServer;
if ($ENV{with_openssl} eq 'yes')
{
- plan tests => 71;
+ plan tests => 75;
}
else
{
@@ -378,6 +378,27 @@ test_connect_fails(
qr/SSL error/,
"certificate authorization fails with revoked client cert");
+# Check that connecting with auth-option verify-full in pg_hba:
+# works, iff username matches Common Name
+# fails, iff username doesn't match Common Name.
+$common_connstr =
+"sslrootcert=ssl/root+server_ca.crt sslmode=require dbname=verifydb hostaddr=$SERVERHOSTADDR";
+
+test_connect_ok($common_connstr,
+ "user=ssltestuser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key",
+ "auth_option clientcert=verify-full succeeds with matching username and Common Name");
+
+test_connect_fails($common_connstr,
+ "user=anotheruser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key",
+ qr/FATAL/,
+ "auth_option clientcert=verify-full fails with mismatching username and Common Name");
+
+# Check that connecting with auth-optionverify-ca in pg_hba :
+# works, when username doesn't match Common Name
+test_connect_ok($common_connstr,
+ "user=yetanotheruser sslcert=ssl/client.crt sslkey=ssl/client_tmp.key",
+ "auth_option clientcert=verify-ca succeeds with mismatching username and Common Name");
+
# intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file
switch_server_cert($node, 'server-cn-only', 'root_ca');
$common_connstr =
diff --git a/src/test/ssl/t/SSLServer.pm b/src/test/ssl/t/SSLServer.pm
index b1b5b7f0b34..d25c38dbbc7 100644
--- a/src/test/ssl/t/SSLServer.pm
+++ b/src/test/ssl/t/SSLServer.pm
@@ -103,8 +103,10 @@ sub configure_test_server_for_ssl
# Create test users and databases
$node->psql('postgres', "CREATE USER ssltestuser");
$node->psql('postgres', "CREATE USER anotheruser");
+ $node->psql('postgres', "CREATE USER yetanotheruser");
$node->psql('postgres', "CREATE DATABASE trustdb");
$node->psql('postgres', "CREATE DATABASE certdb");
+ $node->psql('postgres', "CREATE DATABASE verifydb");
# Update password of each user as needed.
if (defined($password))
@@ -183,12 +185,18 @@ sub configure_hba_for_ssl
# When connecting to certdb, also check the client certificate.
open my $hba, '>', "$pgdata/pg_hba.conf";
print $hba
- "# TYPE DATABASE USER ADDRESS METHOD\n";
+ "# TYPE DATABASE USER ADDRESS METHOD OPTIONS\n";
print $hba
"hostssl trustdb all $serverhost/32 $authmethod\n";
print $hba
"hostssl trustdb all ::1/128 $authmethod\n";
print $hba
+ "hostssl verifydb ssltestuser $serverhost/32 $authmethod clientcert=verify-full\n";
+ print $hba
+ "hostssl verifydb anotheruser $serverhost/32 $authmethod clientcert=verify-full\n";
+ print $hba
+ "hostssl verifydb yetanotheruser $serverhost/32 $authmethod clientcert=verify-ca\n";
+ print $hba
"hostssl certdb all $serverhost/32 cert\n";
print $hba
"hostssl certdb all ::1/128 cert\n";