From cd7e6fa5d2e0145bf961be55b3aed25faa3d7637 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 21 Nov 2025 13:59:28 +0100 Subject: [PATCH 3/4] Fix pg_isblank() There was a pg_isblank() function that claimed to be a replacement for the standard isblank() function, which was thought to be "not very portable yet". We can now assume that it's portable (it's in C99). But pg_isblank() actually diverged from the standard isblank() by also accepting '\r', while the standard one only accepts space and tab. This was added to support parsing pg_hba.conf under Windows. But the hba parsing code now works completely differently and already handles line endings before we get to pg_isblank(). The other user of pg_isblank() is for ident protocol message parsing, which also handles '\r' separately. So this behavior is now obsolete and confusing. To improve clarity, I separated those concerns. The ident parsing now gets its own function that hardcodes the whitespace characters mentioned by the relevant RFC. The hba parsing uses the standard isblank(). pg_isblank() is removed. --- src/backend/libpq/auth.c | 17 +++++++++++++---- src/backend/libpq/hba.c | 15 ++------------- src/include/libpq/hba.h | 1 - 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index ec4dbacf015..5854a2433bb 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -1580,6 +1580,15 @@ pg_SSPI_make_upn(char *accountname, *---------------------------------------------------------------- */ +/* + * Per RFC 1413, space and tab are whitespace in ident messages. + */ +static bool +is_ident_whitespace(const char c) +{ + return c == ' ' || c == '\t'; +} + /* * Parse the string "*ident_response" as a response from a query to an Ident * server. If it's a normal response indicating a user name, return true @@ -1613,14 +1622,14 @@ interpret_ident_response(const char *ident_response, int i; /* Index into *response_type */ cursor++; /* Go over colon */ - while (pg_isblank(*cursor)) + while (is_ident_whitespace(*cursor)) cursor++; /* skip blanks */ i = 0; - while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) && + while (*cursor != ':' && *cursor != '\r' && !is_ident_whitespace(*cursor) && i < (int) (sizeof(response_type) - 1)) response_type[i++] = *cursor++; response_type[i] = '\0'; - while (pg_isblank(*cursor)) + while (is_ident_whitespace(*cursor)) cursor++; /* skip blanks */ if (strcmp(response_type, "USERID") != 0) return false; @@ -1643,7 +1652,7 @@ interpret_ident_response(const char *ident_response, else { cursor++; /* Go over colon */ - while (pg_isblank(*cursor)) + while (is_ident_whitespace(*cursor)) cursor++; /* skip blanks */ /* Rest of line is user name. Copy it over. */ i = 0; diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 97a3586000b..787aee49d24 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -138,17 +138,6 @@ static int regexec_auth_token(const char *match, AuthToken *token, static void tokenize_error_callback(void *arg); -/* - * isblank() exists in the ISO C99 spec, but it's not very portable yet, - * so provide our own version. - */ -bool -pg_isblank(const char c) -{ - return c == ' ' || c == '\t' || c == '\r'; -} - - /* * Grab one token out of the string pointed to by *lineptr. * @@ -198,7 +187,7 @@ next_token(char **lineptr, StringInfo buf, *terminating_comma = false; /* Move over any whitespace and commas preceding the next token */ - while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ',')) + while ((c = (*(*lineptr)++)) != '\0' && (isblank((unsigned char) c) || c == ',')) ; /* @@ -206,7 +195,7 @@ next_token(char **lineptr, StringInfo buf, * unquoted whitespace. */ while (c != '\0' && - (!pg_isblank(c) || in_quote)) + (!isblank((unsigned char) c) || in_quote)) { /* skip comments to EOL */ if (c == '#' && !in_quote) diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index e3748d3c8c9..7b93ba4a709 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -181,7 +181,6 @@ extern int check_usermap(const char *usermap_name, bool case_insensitive); extern HbaLine *parse_hba_line(TokenizedAuthLine *tok_line, int elevel); extern IdentLine *parse_ident_line(TokenizedAuthLine *tok_line, int elevel); -extern bool pg_isblank(const char c); extern FILE *open_auth_file(const char *filename, int elevel, int depth, char **err_msg); extern void free_auth_file(FILE *file, int depth); -- 2.51.0