From e982dade00a310e1828b771a275b4b5282ee942f Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Wed, 28 Dec 2022 09:56:04 +0100 Subject: [PATCH v2 2/2] Support using all for the db user in pg_ident.conf While pg_hba.conf has supported the "all" keyword since a very long time, pg_ident.conf doesn't have this same functionality. This changes permission checking in pg_ident.conf to handle "all" differently from any other value in the database-username column. If "all" is specified and the system-user matches the identifier, then the user is allowed to authenticate no matter what user it tries to authenticate as. This change makes it much easier to have a certain database administrator peer or cert authentication, that allows connecting as any user. Without this change you would need to add a line to pg_ident.conf for every user that is in the database. --- doc/src/sgml/client-auth.sgml | 5 +++- src/backend/libpq/hba.c | 34 +++++++++++++++++++-------- src/backend/utils/adt/hbafuncs.c | 2 +- src/include/libpq/hba.h | 2 +- src/test/authentication/t/003_peer.pl | 21 +++++++++++++++++ 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index cc8c59206c..64c7713057 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -941,7 +941,10 @@ local db1,db2,@demodbs all md5 implying that they are equivalent. The connection will be allowed if there is any map entry that pairs the user name obtained from the external authentication system with the database user name that the - user has requested to connect as. + user has requested to connect as. The value all + can be used as the database-username to specify + that all users are matched. Quoting all makes the keyword + lose its special meaning. If the system-username field starts with a slash (/), diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 3e966acfc0..045faa7520 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -2800,7 +2800,7 @@ parse_ident_line(TokenizedAuthLine *tok_line, int elevel) tokens = lfirst(field); IDENT_MULTI_VALUE(tokens); token = linitial(tokens); - parsedline->pg_role = pstrdup(token->string); + parsedline->pg_role = copy_auth_token(token); /* * Now that the field validation is done, compile a regex from the user @@ -2865,7 +2865,16 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, return; } - if ((ofs = strstr(identLine->pg_role, "\\1")) != NULL) + /* + * We can return early if the pg_role is the all keyword + */ + if (token_is_keyword(identLine->pg_role, "all")) + { + *found_p = true; + return; + } + + if ((ofs = strstr(identLine->pg_role->string, "\\1")) != NULL) { int offset; @@ -2875,7 +2884,7 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, ereport(LOG, (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"", - identLine->systemuser->string + 1, identLine->pg_role))); + identLine->systemuser->string + 1, identLine->pg_role->string))); *error_p = true; return; } @@ -2884,9 +2893,9 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, * length: original length minus length of \1 plus length of match * plus null terminator */ - regexp_pgrole = palloc0(strlen(identLine->pg_role) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1); - offset = ofs - identLine->pg_role; - memcpy(regexp_pgrole, identLine->pg_role, offset); + regexp_pgrole = palloc0(strlen(identLine->pg_role->string) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1); + offset = ofs - identLine->pg_role->string; + memcpy(regexp_pgrole, identLine->pg_role->string, offset); memcpy(regexp_pgrole + offset, ident_user + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); @@ -2895,7 +2904,7 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, else { /* no substitution, so copy the match */ - regexp_pgrole = pstrdup(identLine->pg_role); + regexp_pgrole = pstrdup(identLine->pg_role->string); } /* @@ -2918,16 +2927,19 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name, } else { - /* Not regular expression, so make complete match */ if (case_insensitive) { - if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 && + if ( + (token_is_keyword(identLine->pg_role, "all") || + pg_strcasecmp(identLine->pg_role->string, pg_role) == 0) && pg_strcasecmp(identLine->systemuser->string, ident_user) == 0) *found_p = true; } else { - if (strcmp(identLine->pg_role, pg_role) == 0 && + if ( + (token_is_keyword(identLine->pg_role, "all") || + strcmp(identLine->pg_role->string, pg_role) == 0) && strcmp(identLine->systemuser->string, ident_user) == 0) *found_p = true; } @@ -3074,6 +3086,7 @@ load_ident(void) { newline = (IdentLine *) lfirst(parsed_line_cell); free_auth_token(newline->systemuser); + free_auth_token(newline->pg_role); } MemoryContextDelete(ident_context); return false; @@ -3086,6 +3099,7 @@ load_ident(void) { newline = (IdentLine *) lfirst(parsed_line_cell); free_auth_token(newline->systemuser); + free_auth_token(newline->pg_role); } } if (parsed_ident_context != NULL) diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c index be6b513d64..31b5f2241f 100644 --- a/src/backend/utils/adt/hbafuncs.c +++ b/src/backend/utils/adt/hbafuncs.c @@ -493,7 +493,7 @@ fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, { values[index++] = CStringGetTextDatum(ident->usermap); values[index++] = CStringGetTextDatum(ident->systemuser->string); - values[index++] = CStringGetTextDatum(ident->pg_role); + values[index++] = CStringGetTextDatum(ident->pg_role->string); } else { diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index f1f27d6138..c52405387d 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -142,8 +142,8 @@ typedef struct IdentLine int linenumber; char *usermap; - char *pg_role; AuthToken *systemuser; + AuthToken *pg_role; } IdentLine; /* diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl index 26c34d05d3..f6d57f77f0 100644 --- a/src/test/authentication/t/003_peer.pl +++ b/src/test/authentication/t/003_peer.pl @@ -123,11 +123,32 @@ test_role($node, qq{testmapuser}, 'peer', 0, 'with user name map', log_like => [qr/connection authenticated: identity="$system_user" method=peer/]); +# Tests with the "all" keyword +reset_pg_ident($node, 'mypeermap', $system_user, 'all'); + +# Success as the database role is the "all" keyword +test_role($node, qq{testmapuser}, 'peer', 0, 'with user name map', + log_like => + [qr/connection authenticated: identity="$system_user" method=peer/]); + # Test with regular expression in user name map. # Extract the last 3 characters from the system_user # or the entire system_user (if its length is <= -3). my $regex_test_string = substr($system_user, -3); +# Success as the regular expression matches and database role is the "all" +# keyword. +reset_pg_ident($node, 'mypeermap', qq{/^.*$regex_test_string\$}, + 'all'); +test_role( + $node, + qq{testmapuser}, + 'peer', + 0, + 'with regular expression in user name map', + log_like => + [qr/connection authenticated: identity="$system_user" method=peer/]); + # Success as the regular expression matches. reset_pg_ident($node, 'mypeermap', qq{/^.*$regex_test_string\$}, 'testmapuser'); -- 2.34.1