diff options
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 170 |
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); |