aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5638b223cb4..dd4b98e0998 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -307,6 +307,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"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)},
@@ -595,6 +599,7 @@ pqDropServerData(PGconn *conn)
/* 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);
@@ -1238,6 +1243,170 @@ connectOptions2(PGconn *conn)
}
/*
+ * 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
*/
if (conn->channel_binding)
@@ -4055,6 +4224,7 @@ freePGconn(PGconn *conn)
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);