</listitem>
</varlistentry>
+ <varlistentry id="libpq-connect-require-auth" xreflabel="require_auth">
+ <term><literal>require_auth</literal></term>
+ <listitem>
+ <para>
+ Specifies the authentication method that the client requires from the
+ server. If the server does not use the required method to authenticate
+ the client, or if the authentication handshake is not fully completed by
+ the server, the connection will fail. A comma-separated list of methods
+ may also be provided, of which the server must use exactly one in order
+ for the connection to succeed. By default, any authentication method is
+ accepted, and the server is free to skip authentication altogether.
+ </para>
+ <para>
+ Methods may be negated with the addition of a <literal>!</literal>
+ prefix, in which case the server must <emphasis>not</emphasis> attempt
+ the listed method; any other method is accepted, and the server is free
+ not to authenticate the client at all. If a comma-separated list is
+ provided, the server may not attempt <emphasis>any</emphasis> of the
+ listed negated methods. Negated and non-negated forms may not be
+ combined in the same setting.
+ </para>
+ <para>
+ As a final special case, the <literal>none</literal> method requires the
+ server not to use an authentication challenge. (It may also be negated,
+ to require some form of authentication.)
+ </para>
+ <para>
+ The following methods may be specified:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>password</literal></term>
+ <listitem>
+ <para>
+ The server must request plaintext password authentication.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>md5</literal></term>
+ <listitem>
+ <para>
+ The server must request MD5 hashed password authentication.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>gss</literal></term>
+ <listitem>
+ <para>
+ The server must either request a Kerberos handshake via
+ <acronym>GSSAPI</acronym> or establish a
+ <acronym>GSS</acronym>-encrypted channel (see also
+ <xref linkend="libpq-connect-gssencmode" />).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sspi</literal></term>
+ <listitem>
+ <para>
+ The server must request Windows <acronym>SSPI</acronym>
+ authentication.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>scram-sha-256</literal></term>
+ <listitem>
+ <para>
+ The server must successfully complete a SCRAM-SHA-256 authentication
+ exchange with the client.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>creds</literal></term>
+ <listitem>
+ <para>
+ The server must request SCM credential authentication (deprecated
+ as of <productname>PostgreSQL</productname> 9.1).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>none</literal></term>
+ <listitem>
+ <para>
+ The server must not prompt the client for an authentication
+ exchange. (This does not prohibit client certificate authentication
+ via TLS, nor GSS authentication via its encrypted transport.)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-connect-channel-binding" xreflabel="channel_binding">
<term><literal>channel_binding</literal></term>
<listitem>
</para>
</listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary><envar>PGREQUIREAUTH</envar></primary>
+ </indexterm>
+ <envar>PGREQUIREAUTH</envar> behaves the same as the <xref
+ linkend="libpq-connect-require-auth"/> connection parameter.
+ </para>
+ </listitem>
+
<listitem>
<para>
<indexterm>
#define AUTH_REQ_SASL 10 /* Begin SASL authentication */
#define AUTH_REQ_SASL_CONT 11 /* Continue SASL authentication */
#define AUTH_REQ_SASL_FIN 12 /* Final SASL message */
+#define AUTH_REQ_MAX AUTH_REQ_SASL_FIN /* maximum AUTH_REQ_* value */
typedef uint32 AuthRequest;
}
*done = true;
state->state = FE_SCRAM_FINISHED;
+ state->conn->client_finished_auth = true;
break;
default:
}
if (maj_stat == GSS_S_COMPLETE)
+ {
+ conn->client_finished_auth = true;
gss_release_name(&lmin_s, &conn->gtarg_nam);
+ }
return STATUS_OK;
}
FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
}
+ if (r == SEC_E_OK)
+ conn->client_finished_auth = true;
+
/* Cleanup is handled by the code in freePGconn() */
return STATUS_OK;
}
strerror_r(errno, sebuf, sizeof(sebuf)));
return STATUS_ERROR;
}
+
+ conn->client_finished_auth = true;
return STATUS_OK;
#else
libpq_append_conn_error(conn, "SCM_CRED authentication method not supported");
return ret;
}
+/*
+ * Translate a disallowed AuthRequest code into an error message.
+ */
+static const char *
+auth_method_description(AuthRequest areq)
+{
+ switch (areq)
+ {
+ case AUTH_REQ_PASSWORD:
+ return libpq_gettext("server requested a cleartext password");
+ case AUTH_REQ_MD5:
+ return libpq_gettext("server requested a hashed password");
+ case AUTH_REQ_GSS:
+ case AUTH_REQ_GSS_CONT:
+ return libpq_gettext("server requested GSSAPI authentication");
+ case AUTH_REQ_SSPI:
+ return libpq_gettext("server requested SSPI authentication");
+ case AUTH_REQ_SCM_CREDS:
+ return libpq_gettext("server requested UNIX socket credentials");
+ case AUTH_REQ_SASL:
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+ return libpq_gettext("server requested SASL authentication");
+ }
+
+ return libpq_gettext("server requested an unknown authentication type");
+}
+
+/*
+ * Convenience macro for checking the allowed_auth_methods bitmask. Caller
+ * must ensure that type is not greater than 31 (high bit of the bitmask).
+ */
+#define auth_method_allowed(conn, type) \
+ (((conn)->allowed_auth_methods & (1 << (type))) != 0)
+
/*
* Verify that the authentication request is expected, given the connection
* parameters. This is especially important when the client wishes to
check_expected_areq(AuthRequest areq, PGconn *conn)
{
bool result = true;
+ const char *reason = NULL;
+
+ StaticAssertDecl((sizeof(conn->allowed_auth_methods) * CHAR_BIT) > AUTH_REQ_MAX,
+ "AUTH_REQ_MAX overflows the allowed_auth_methods bitmask");
+
+ /*
+ * If the user required a specific auth method, or specified an allowed
+ * set, then reject all others here, and make sure the server actually
+ * completes an authentication exchange.
+ */
+ if (conn->require_auth)
+ {
+ switch (areq)
+ {
+ case AUTH_REQ_OK:
+
+ /*
+ * Check to make sure we've actually finished our exchange (or
+ * else that the user has allowed an authentication-less
+ * connection).
+ *
+ * If the user has allowed both SCRAM and unauthenticated
+ * (trust) connections, then this check will silently accept
+ * partial SCRAM exchanges, where a misbehaving server does
+ * not provide its verifier before sending an OK. This is
+ * consistent with historical behavior, but it may be a point
+ * to revisit in the future, since it could allow a server
+ * that doesn't know the user's password to silently harvest
+ * material for a brute force attack.
+ */
+ if (!conn->auth_required || conn->client_finished_auth)
+ break;
+
+ /*
+ * No explicit authentication request was made by the server
+ * -- or perhaps it was made and not completed, in the case of
+ * SCRAM -- but there is one special case to check. If the
+ * user allowed "gss", then a GSS-encrypted channel also
+ * satisfies the check.
+ */
+#ifdef ENABLE_GSS
+ if (auth_method_allowed(conn, AUTH_REQ_GSS) && conn->gssenc)
+ {
+ /*
+ * If implicit GSS auth has already been performed via GSS
+ * encryption, we don't need to have performed an
+ * AUTH_REQ_GSS exchange. This allows require_auth=gss to
+ * be combined with gssencmode, since there won't be an
+ * explicit authentication request in that case.
+ */
+ }
+ else
+#endif
+ {
+ reason = libpq_gettext("server did not complete authentication");
+ result = false;
+ }
+
+ break;
+
+ case AUTH_REQ_PASSWORD:
+ case AUTH_REQ_MD5:
+ case AUTH_REQ_GSS:
+ case AUTH_REQ_GSS_CONT:
+ case AUTH_REQ_SSPI:
+ case AUTH_REQ_SCM_CREDS:
+ case AUTH_REQ_SASL:
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+
+ /*
+ * We don't handle these with the default case, to avoid
+ * bit-shifting past the end of the allowed_auth_methods mask
+ * if the server sends an unexpected AuthRequest.
+ */
+ result = auth_method_allowed(conn, areq);
+ break;
+
+ default:
+ result = false;
+ break;
+ }
+ }
+
+ if (!result)
+ {
+ if (!reason)
+ reason = auth_method_description(areq);
+
+ libpq_append_conn_error(conn, "auth method \"%s\" requirement failed: %s",
+ conn->require_auth, reason);
+ return result;
+ }
/*
* When channel_binding=require, we must protect against two cases: (1) we
"fe_sendauth: error sending password authentication\n");
return STATUS_ERROR;
}
+
+ /* We expect no further authentication requests. */
+ conn->client_finished_auth = true;
break;
}
"Require-Peer", "", 10,
offsetof(struct pg_conn, requirepeer)},
+ {"require_auth", "PGREQUIREAUTH", NULL, NULL,
+ "Require-Auth", "", 14, /* sizeof("scram-sha-256") == 14 */
+ offsetof(struct pg_conn, require_auth)},
+
{"ssl_min_protocol_version", "PGSSLMINPROTOCOLVERSION", "TLSv1.2", NULL,
"SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
offsetof(struct pg_conn, ssl_min_protocol_version)},
/* Reset assorted other per-connection state */
conn->last_sqlstate[0] = '\0';
conn->auth_req_received = false;
+ conn->client_finished_auth = false;
conn->password_needed = false;
conn->write_failed = false;
free(conn->write_err_msg);
}
}
+ /*
+ * parse and validate require_auth option
+ */
+ if (conn->require_auth && conn->require_auth[0])
+ {
+ char *s = conn->require_auth;
+ bool first,
+ more;
+ bool negated = false;
+
+ /*
+ * By default, start from an empty set of allowed options and add to
+ * it.
+ */
+ conn->auth_required = true;
+ conn->allowed_auth_methods = 0;
+
+ for (first = true, more = true; more; first = false)
+ {
+ char *method,
+ *part;
+ uint32 bits;
+
+ part = parse_comma_separated_list(&s, &more);
+ if (part == NULL)
+ goto oom_error;
+
+ /*
+ * Check for negation, e.g. '!password'. If one element is
+ * negated, they all have to be.
+ */
+ method = part;
+ if (*method == '!')
+ {
+ if (first)
+ {
+ /*
+ * Switch to a permissive set of allowed options, and
+ * subtract from it.
+ */
+ conn->auth_required = false;
+ conn->allowed_auth_methods = -1;
+ }
+ else if (!negated)
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "negative require_auth method \"%s\" cannot be mixed with non-negative methods",
+ method);
+
+ free(part);
+ return false;
+ }
+
+ negated = true;
+ method++;
+ }
+ else if (negated)
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "require_auth method \"%s\" cannot be mixed with negative methods",
+ method);
+
+ free(part);
+ return false;
+ }
+
+ if (strcmp(method, "password") == 0)
+ {
+ bits = (1 << AUTH_REQ_PASSWORD);
+ }
+ else if (strcmp(method, "md5") == 0)
+ {
+ bits = (1 << AUTH_REQ_MD5);
+ }
+ else if (strcmp(method, "gss") == 0)
+ {
+ bits = (1 << AUTH_REQ_GSS);
+ bits |= (1 << AUTH_REQ_GSS_CONT);
+ }
+ else if (strcmp(method, "sspi") == 0)
+ {
+ bits = (1 << AUTH_REQ_SSPI);
+ bits |= (1 << AUTH_REQ_GSS_CONT);
+ }
+ else if (strcmp(method, "scram-sha-256") == 0)
+ {
+ /* This currently assumes that SCRAM is the only SASL method. */
+ bits = (1 << AUTH_REQ_SASL);
+ bits |= (1 << AUTH_REQ_SASL_CONT);
+ bits |= (1 << AUTH_REQ_SASL_FIN);
+ }
+ else if (strcmp(method, "creds") == 0)
+ {
+ bits = (1 << AUTH_REQ_SCM_CREDS);
+ }
+ else if (strcmp(method, "none") == 0)
+ {
+ /*
+ * Special case: let the user explicitly allow (or disallow)
+ * connections where the server does not send an explicit
+ * authentication challenge, such as "trust" and "cert" auth.
+ */
+ if (negated) /* "!none" */
+ {
+ if (conn->auth_required)
+ goto duplicate;
+
+ conn->auth_required = true;
+ }
+ else /* "none" */
+ {
+ if (!conn->auth_required)
+ goto duplicate;
+
+ conn->auth_required = false;
+ }
+
+ free(part);
+ continue; /* avoid the bitmask manipulation below */
+ }
+ else
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "invalid require_auth method: \"%s\"",
+ method);
+
+ free(part);
+ return false;
+ }
+
+ /* Update the bitmask. */
+ if (negated)
+ {
+ if ((conn->allowed_auth_methods & bits) == 0)
+ goto duplicate;
+
+ conn->allowed_auth_methods &= ~bits;
+ }
+ else
+ {
+ if ((conn->allowed_auth_methods & bits) == bits)
+ goto duplicate;
+
+ conn->allowed_auth_methods |= bits;
+ }
+
+ free(part);
+ continue;
+
+ duplicate:
+
+ /*
+ * A duplicated method probably indicates a typo in a setting
+ * where typos are extremely risky.
+ */
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "require_auth method \"%s\" is specified more than once",
+ part);
+
+ free(part);
+ return false;
+ }
+ }
+
/*
* validate channel_binding option
*/
free(conn->sslcompression);
free(conn->sslsni);
free(conn->requirepeer);
+ free(conn->require_auth);
free(conn->ssl_min_protocol_version);
free(conn->ssl_max_protocol_version);
free(conn->gssencmode);
char *ssl_min_protocol_version; /* minimum TLS protocol version */
char *ssl_max_protocol_version; /* maximum TLS protocol version */
char *target_session_attrs; /* desired session properties */
+ char *require_auth; /* name of the expected auth method */
/* Optional file to write trace info to */
FILE *Pfdebug;
bool write_failed; /* have we had a write failure on sock? */
char *write_err_msg; /* write error message, or NULL if OOM */
+ bool auth_required; /* require an authentication challenge from
+ * the server? */
+ uint32 allowed_auth_methods; /* bitmask of acceptable AuthRequest
+ * codes */
+ bool client_finished_auth; /* have we finished our half of the
+ * authentication exchange? */
+
+
/* Transient state needed while establishing connection */
PGTargetServerType target_server_type; /* desired session properties */
bool try_next_addr; /* time to advance to next address/host? */
't/002_saslprep.pl',
't/003_peer.pl',
't/004_file_inclusion.pl',
+ 't/005_sspi.pl',
],
},
}
"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,
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(
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,
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',
--- /dev/null
+
+# 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();
'gssencmode=require',
'sending 100K lines works');
+# require_auth=gss succeeds if required.
+$node->connect_ok(
+ $node->connstr('postgres')
+ . " user=test1 host=$host hostaddr=$hostaddr gssencmode=disable require_auth=gss",
+ "GSS authentication requested, works with non-encyrpted GSS");
+$node->connect_ok(
+ $node->connstr('postgres')
+ . " user=test1 host=$host hostaddr=$hostaddr gssencmode=require require_auth=gss",
+ "GSS authentication requested, works with encrypted GSS auth");
+
+# require_auth=sspi fails if required.
+$node->connect_fails(
+ $node->connstr('postgres')
+ . " user=test1 host=$host hostaddr=$hostaddr gssencmode=disable require_auth=sspi",
+ "SSPI authentication requested, fails with non-encrypted GSS",
+ expected_stderr =>
+ qr/auth method "sspi" requirement failed: server requested GSSAPI authentication/
+);
+$node->connect_fails(
+ $node->connstr('postgres')
+ . " user=test1 host=$host hostaddr=$hostaddr gssencmode=require require_auth=sspi",
+ "SSPI authentication requested, fails with encrypted GSS",
+ expected_stderr =>
+ qr/auth method "sspi" requirement failed: server did not complete authentication/
+);
+
# Test that SYSTEM_USER works.
test_query($node, 'test1', 'SELECT SYSTEM_USER;',
qr/^gss:test1\@$realm$/s, 'gssencmode=require', 'testing system_user');
test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
'fails with GSS encryption disabled and hostgssenc hba');
+# require_auth=gss succeeds if required.
+$node->connect_ok(
+ $node->connstr('postgres')
+ . " user=test1 host=$host hostaddr=$hostaddr gssencmode=require require_auth=gss",
+ "GSS authentication requested, works with GSS encryption");
+$node->connect_ok(
+ $node->connstr('postgres')
+ . " user=test1 host=$host hostaddr=$hostaddr gssencmode=require require_auth=gss,scram-sha-256",
+ "multiple authentication types requested, works with GSS encryption");
+
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
qr/connection authenticated: identity="uid=test1,dc=example,dc=net" method=ldap/
],);
+# require_auth=password should complete successfully; other methods should fail.
+$node->connect_ok("user=test1 require_auth=password",
+ "password authentication required, works with ldap auth");
+$node->connect_fails("user=test1 require_auth=scram-sha-256",
+ "SCRAM authentication required, fails with ldap auth");
+
note "search+bind";
unlink($node->data_dir . '/pg_hba.conf');
qr/connection authenticated: identity="ssltestuser" method=scram-sha-256/
]);
+# channel_binding should continue to work independently of require_auth.
+$node->connect_ok(
+ "$common_connstr user=ssltestuser channel_binding=disable require_auth=scram-sha-256",
+ "SCRAM with SSL, channel_binding=disable, and require_auth=scram-sha-256"
+);
+$node->connect_fails(
+ "$common_connstr user=md5testuser require_auth=md5 channel_binding=require",
+ "channel_binding can fail even when require_auth succeeds",
+ expected_stderr =>
+ qr/channel binding required but not supported by server's authentication request/
+);
+if ($supports_tls_server_end_point)
+{
+ $node->connect_ok(
+ "$common_connstr user=ssltestuser channel_binding=require require_auth=scram-sha-256",
+ "SCRAM with SSL, channel_binding=require, and require_auth=scram-sha-256"
+ );
+}
+else
+{
+ $node->connect_fails(
+ "$common_connstr user=ssltestuser channel_binding=require require_auth=scram-sha-256",
+ "SCRAM with SSL, channel_binding=require, and require_auth=scram-sha-256",
+ expected_stderr =>
+ qr/channel binding is required, but server did not offer an authentication method that supports channel binding/
+ );
+}
+
# Now test with a server certificate that uses the RSA-PSS algorithm.
# This checks that the certificate can be loaded and that channel binding
# works. (see bug #17760)