On Tue, 2022-04-05 at 15:13 +0900, Michael Paquier wrote: > On Wed, Mar 30, 2022 at 04:02:09PM +0000, Jacob Champion wrote: > > Whether that's a problem in the future entirely depends on whether > > there's some authentication method that considers the empty string a > > sane and meaningful identity. We might reasonably decide that the > > answer is "no", but I like being able to make that decision as opposed > > to delegating it to an existing generic framework. > > My guess on the matter is that an empty authn holds the same meaning > as NULL because it has no data,
Whether it holds meaning or not depends entirely on the auth method, I think. Hypothetical example -- a system could accept client certificates with an empty Subject. What identity that Subject represents would depend on the organization, but it's distinct from NULL/unauthenticated because the certificate is still signed by a CA. (Postgres rejects empty Subjects when using clientname=DN and I'm not proposing that we change that; I'm haven't actually checked that they're RFC-legal. But it's possible that a future auth method could have a reasonable standard definition for an empty identifier.) > but I can see your point as well to > make this distinction. In order to do that, couldn't you just use > shm_toc_lookup(noError=true)? PARALLEL_KEY_SHAREDPORT could be an > optional entry in the TOC data. The current patch already handles NULL with a byte of overhead; is there any advantage to using noError? (It might make things messier once a second member gets added to the struct.) My concern was directed at the GUC proposal. > The name choice is still an issue, as per Andres' point that > MyProcShared is confusing as it can refer to shared memory. What we > want is a structure name for something that's related to MyProc and > shared across all parallel workers including the leader. I would > give up on the "Shared" part, using "Parallel" and "Info" instead. > Here are some ideas: > - ProcParallelInfo > - ProcInfoParallel > - ParallelProcInfo I like ParallelProcInfo; it reads nicely. I've used that in v9. Thanks! --Jacob
commit b3fb176a4e4f09f7436f2df8c3411b4b51c71906 Author: Jacob Champion <pchamp...@vmware.com> Date: Tue Apr 5 10:18:16 2022 -0700 squash! Allow parallel workers to use pg_session_authn_id() Update name to MyParallelProcInfo. diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index c88eab0933..27eda766b1 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -76,7 +76,7 @@ #define PARALLEL_KEY_REINDEX_STATE UINT64CONST(0xFFFFFFFFFFFF000C) #define PARALLEL_KEY_RELMAPPER_STATE UINT64CONST(0xFFFFFFFFFFFF000D) #define PARALLEL_KEY_UNCOMMITTEDENUMS UINT64CONST(0xFFFFFFFFFFFF000E) -#define PARALLEL_KEY_SHAREDPORT UINT64CONST(0xFFFFFFFFFFFF000F) +#define PARALLEL_KEY_PROCINFO UINT64CONST(0xFFFFFFFFFFFF000F) /* Fixed-size parallel state. */ typedef struct FixedParallelState @@ -213,7 +213,7 @@ InitializeParallelDSM(ParallelContext *pcxt) Size reindexlen = 0; Size relmapperlen = 0; Size uncommittedenumslen = 0; - Size sharedportlen = 0; + Size procinfolen = 0; Size segsize = 0; int i; FixedParallelState *fps; @@ -274,8 +274,8 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_estimate_chunk(&pcxt->estimator, relmapperlen); uncommittedenumslen = EstimateUncommittedEnumsSpace(); shm_toc_estimate_chunk(&pcxt->estimator, uncommittedenumslen); - sharedportlen = EstimateSharedPortSpace(); - shm_toc_estimate_chunk(&pcxt->estimator, sharedportlen); + procinfolen = EstimateParallelProcInfoSpace(); + shm_toc_estimate_chunk(&pcxt->estimator, procinfolen); /* If you add more chunks here, you probably need to add keys. */ shm_toc_estimate_keys(&pcxt->estimator, 12); @@ -356,7 +356,7 @@ InitializeParallelDSM(ParallelContext *pcxt) char *session_dsm_handle_space; char *entrypointstate; char *uncommittedenumsspace; - char *sharedportspace; + char *procinfospace; Size lnamelen; /* Serialize shared libraries we have loaded. */ @@ -427,11 +427,11 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_insert(pcxt->toc, PARALLEL_KEY_UNCOMMITTEDENUMS, uncommittedenumsspace); - /* Serialize our SharedPort. */ - sharedportspace = shm_toc_allocate(pcxt->toc, sharedportlen); - SerializeSharedPort(sharedportlen, sharedportspace); - shm_toc_insert(pcxt->toc, PARALLEL_KEY_SHAREDPORT, - sharedportspace); + /* Serialize our ParallelProcInfo. */ + procinfospace = shm_toc_allocate(pcxt->toc, procinfolen); + SerializeParallelProcInfo(procinfolen, procinfospace); + shm_toc_insert(pcxt->toc, PARALLEL_KEY_PROCINFO, + procinfospace); /* Allocate space for worker information. */ pcxt->worker = palloc0(sizeof(ParallelWorkerInfo) * pcxt->nworkers); @@ -1281,7 +1281,7 @@ ParallelWorkerMain(Datum main_arg) char *reindexspace; char *relmapperspace; char *uncommittedenumsspace; - char *sharedportspace; + char *procinfospace; StringInfoData msgbuf; char *session_dsm_handle_space; Snapshot tsnapshot; @@ -1491,9 +1491,9 @@ ParallelWorkerMain(Datum main_arg) false); RestoreUncommittedEnums(uncommittedenumsspace); - /* Restore the SharedPort. */ - sharedportspace = shm_toc_lookup(toc, PARALLEL_KEY_SHAREDPORT, false); - RestoreSharedPort(sharedportspace); + /* Restore the ParallelProcInfo. */ + procinfospace = shm_toc_lookup(toc, PARALLEL_KEY_PROCINFO, false); + RestoreParallelProcInfo(procinfospace); /* Attach to the leader's serializable transaction, if SERIALIZABLE. */ AttachSerializableXact(fps->serializable_xact_handle); diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index bceda9755a..2e5fe2cc19 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -342,15 +342,15 @@ auth_failed(Port *port, int status, const char *logdetail) * authorization will fail later. * * The provided string will be copied into TopMemoryContext, to match the - * lifetime of MyProcShared, so it is safe to pass a string that is managed by - * an external library. + * lifetime of MyParallelProcInfo, so it is safe to pass a string that is + * managed by an external library. */ static void set_authn_id(Port *port, const char *id) { Assert(id); - if (MyProcShared.authn_id) + if (MyParallelProcInfo.authn_id) { /* * An existing authn_id should never be overwritten; that means two @@ -361,17 +361,17 @@ set_authn_id(Port *port, const char *id) ereport(FATAL, (errmsg("authentication identifier set more than once"), errdetail_log("previous identifier: \"%s\"; new identifier: \"%s\"", - MyProcShared.authn_id, id))); + MyParallelProcInfo.authn_id, id))); } - MyProcShared.authn_id = MemoryContextStrdup(TopMemoryContext, id); + MyParallelProcInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id); if (Log_connections) { ereport(LOG, errmsg("connection authenticated: identity=\"%s\" method=%s " "(%s:%d)", - MyProcShared.authn_id, + MyParallelProcInfo.authn_id, hba_authname(port->hba->auth_method), HbaFileName, port->hba->linenumber)); } @@ -1910,7 +1910,7 @@ auth_peer(hbaPort *port) set_authn_id(port, pw->pw_name); ret = check_usermap(port->hba->usermap, port->user_name, - MyProcShared.authn_id, false); + MyParallelProcInfo.authn_id, false); return ret; #else diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c index 6d497e63d9..24a06bf933 100644 --- a/src/backend/utils/adt/name.c +++ b/src/backend/utils/adt/name.c @@ -275,10 +275,10 @@ session_user(PG_FUNCTION_ARGS) Datum pg_session_authn_id(PG_FUNCTION_ARGS) { - if (!MyProcShared.authn_id) + if (!MyParallelProcInfo.authn_id) PG_RETURN_NULL(); - PG_RETURN_TEXT_P(cstring_to_text(MyProcShared.authn_id)); + PG_RETURN_TEXT_P(cstring_to_text(MyParallelProcInfo.authn_id)); } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 0afab3e142..91b3347398 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -930,50 +930,50 @@ GetUserNameFromId(Oid roleid, bool noerr) } /* ------------------------------------------------------------------------ - * "Shared" connection state + * Parallel connection state * - * MyProcShared contains pieces of information about the client that need to be - * synced to parallel workers when they initialize. Over time, this list will - * probably grow, and may subsume some of the "user state" variables above. + * MyParallelProcInfo contains pieces of information about the client that need + * to be synced to parallel workers when they initialize. Over time, this list + * will probably grow, and may subsume some of the "user state" variables above. *------------------------------------------------------------------------- */ -SharedPort MyProcShared; +ParallelProcInfo MyParallelProcInfo; /* - * Calculate the space needed to serialize MyProcShared. + * Calculate the space needed to serialize MyParallelProcInfo. */ Size -EstimateSharedPortSpace(void) +EstimateParallelProcInfoSpace(void) { Size size = 1; - if (MyProcShared.authn_id) - size = add_size(size, strlen(MyProcShared.authn_id) + 1); + if (MyParallelProcInfo.authn_id) + size = add_size(size, strlen(MyParallelProcInfo.authn_id) + 1); return size; } /* - * Serialize MyProcShared for use by parallel workers. + * Serialize MyParallelProcInfo for use by parallel workers. */ void -SerializeSharedPort(Size maxsize, char *start_address) +SerializeParallelProcInfo(Size maxsize, char *start_address) { /* * First byte is an indication of whether or not authn_id has been set to * non-NULL, to differentiate that case from the empty string. */ Assert(maxsize > 0); - start_address[0] = MyProcShared.authn_id ? 1 : 0; + start_address[0] = MyParallelProcInfo.authn_id ? 1 : 0; start_address++; maxsize--; - if (MyProcShared.authn_id) + if (MyParallelProcInfo.authn_id) { Size len; - len = strlcpy(start_address, MyProcShared.authn_id, maxsize) + 1; + len = strlcpy(start_address, MyParallelProcInfo.authn_id, maxsize) + 1; Assert(len <= maxsize); maxsize -= len; start_address += len; @@ -981,22 +981,22 @@ SerializeSharedPort(Size maxsize, char *start_address) } /* - * Restore MyProcShared from its serialized representation. + * Restore MyParallelProcInfo from its serialized representation. */ void -RestoreSharedPort(char *sharedport) +RestoreParallelProcInfo(char *procinfo) { - if (sharedport[0] == 0) + if (procinfo[0] == 0) { - MyProcShared.authn_id = NULL; - sharedport++; + MyParallelProcInfo.authn_id = NULL; + procinfo++; } else { - sharedport++; - MyProcShared.authn_id = MemoryContextStrdup(TopMemoryContext, - sharedport); - sharedport += strlen(sharedport) + 1; + procinfo++; + MyParallelProcInfo.authn_id = MemoryContextStrdup(TopMemoryContext, + procinfo); + procinfo += strlen(procinfo) + 1; } } diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 911b8246ce..5cc4091216 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -101,10 +101,10 @@ typedef struct /* * Fields from Port that need to be copied over to parallel workers go into the - * SharedPort. The same rules apply for allocations here as for Port (must be - * malloc'd or palloc'd in TopMemoryContext). + * ParallelProcInfo. The same rules apply for allocations here as for Port (must + * be malloc'd or palloc'd in TopMemoryContext). */ -typedef struct SharedPort +typedef struct { /* * Authenticated identity. The meaning of this identifier is dependent on @@ -118,7 +118,7 @@ typedef struct SharedPort * example if the "trust" auth method is in use. */ const char *authn_id; -} SharedPort; +} ParallelProcInfo; /* * This is used by the postmaster in its communication with frontends. It @@ -336,7 +336,7 @@ extern ssize_t be_gssapi_write(Port *port, void *ptr, size_t len); #endif /* ENABLE_GSS */ extern ProtocolVersion FrontendProtocol; -extern SharedPort MyProcShared; +extern ParallelProcInfo MyParallelProcInfo; /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 68cc1517a0..7a08a73b85 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -481,9 +481,9 @@ extern void process_session_preload_libraries(void); extern void pg_bindtextdomain(const char *domain); extern bool has_rolreplication(Oid roleid); -extern Size EstimateSharedPortSpace(void); -extern void SerializeSharedPort(Size maxsize, char *start_address); -extern void RestoreSharedPort(char *sharedport); +extern Size EstimateParallelProcInfoSpace(void); +extern void SerializeParallelProcInfo(Size maxsize, char *start_address); +extern void RestoreParallelProcInfo(char *procinfo); /* in access/transam/xlog.c */ extern bool BackupInProgress(void);
From 7916cb42344e13baf973bcc9b9af822742b64c59 Mon Sep 17 00:00:00 2001 From: Jacob Champion <pchamp...@vmware.com> Date: Mon, 14 Feb 2022 08:10:53 -0800 Subject: [PATCH v9 1/3] Add API to retrieve authn_id from SQL The authn_id field in MyProcPort is currently only accessible to the backend itself. Add a SQL function, pg_session_authn_id(), to expose the field to triggers that may want to make use of it. --- doc/src/sgml/func.sgml | 26 +++++++++++++++++++++++ src/backend/utils/adt/name.c | 12 ++++++++++- src/include/catalog/pg_proc.dat | 3 +++ src/test/authentication/t/001_password.pl | 11 ++++++++++ src/test/ssl/t/001_ssltests.pl | 7 ++++++ 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4001cb2bda..f1a42bbbf5 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -22290,6 +22290,32 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>pg_session_authn_id</primary> + </indexterm> + <function>pg_session_authn_id</function> () + <returnvalue>text</returnvalue> + </para> + <para> + Returns the authenticated identity for the current connection, or + <literal>NULL</literal> if the user has not been authenticated. + </para> + <para> + The authenticated identity is an immutable identifier for the user + presented during the connection handshake; the exact format depends on + the authentication method in use. (For example, when using the + <literal>scram-sha-256</literal> auth method, the authenticated identity + is simply the username. When using the <literal>cert</literal> auth + method, the authenticated identity is the Distinguished Name of the + client certificate.) Even for auth methods which use the username as + the authenticated identity, this function differs from + <literal>session_user</literal> in that its return value cannot be + changed after login. + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c index e8bba3670c..662a7943ed 100644 --- a/src/backend/utils/adt/name.c +++ b/src/backend/utils/adt/name.c @@ -23,6 +23,7 @@ #include "catalog/namespace.h" #include "catalog/pg_collation.h" #include "catalog/pg_type.h" +#include "libpq/libpq-be.h" #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" @@ -257,7 +258,7 @@ namestrcmp(Name name, const char *str) /* - * SQL-functions CURRENT_USER, SESSION_USER + * SQL-functions CURRENT_USER, SESSION_USER, PG_SESSION_AUTHN_ID */ Datum current_user(PG_FUNCTION_ARGS) @@ -271,6 +272,15 @@ session_user(PG_FUNCTION_ARGS) PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false)))); } +Datum +pg_session_authn_id(PG_FUNCTION_ARGS) +{ + if (!MyProcPort || !MyProcPort->authn_id) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(cstring_to_text(MyProcPort->authn_id)); +} + /* * SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 25304430f4..fb1126b090 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1508,6 +1508,9 @@ { oid => '746', descr => 'session user name', proname => 'session_user', provolatile => 's', prorettype => 'name', proargtypes => '', prosrc => 'session_user' }, +{ oid => '9774', descr => 'session authenticated identity', + proname => 'pg_session_authn_id', provolatile => 's', proparallel => 'r', + prorettype => 'text', proargtypes => '', prosrc => 'pg_session_authn_id' }, { oid => '744', proname => 'array_eq', prorettype => 'bool', diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl index 3e3079c824..f0bdeda52d 100644 --- a/src/test/authentication/t/001_password.pl +++ b/src/test/authentication/t/001_password.pl @@ -82,6 +82,10 @@ test_role($node, 'scram_role', 'trust', 0, test_role($node, 'md5_role', 'trust', 0, log_unlike => [qr/connection authenticated:/]); +my $res = + $node->safe_psql('postgres', "SELECT pg_session_authn_id() IS NULL;"); +is($res, 't', "users with trust authentication have NULL authn_id"); + # For plain "password" method, all users should also be able to connect. reset_pg_hba($node, 'password'); test_role($node, 'scram_role', 'password', 0, @@ -91,6 +95,13 @@ test_role($node, 'md5_role', 'password', 0, log_like => [qr/connection authenticated: identity="md5_role" method=password/]); +$res = $node->safe_psql( + 'postgres', + "SELECT pg_session_authn_id();", + connstr => "user=md5_role"); +is($res, 'md5_role', + "users with md5 authentication have authn_id matching role name"); + # For "scram-sha-256" method, user "scram_role" should be able to connect. reset_pg_hba($node, 'scram-sha-256'); test_role( diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index 58d2bc336f..a4ae6b680f 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -547,6 +547,13 @@ $node->connect_ok( qr/connection authenticated: identity="CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG" method=cert/ ],); +# Sanity-check pg_session_authn_id() for long ID strings +my $res = $node->safe_psql('postgres', + "SELECT pg_session_authn_id();", + connstr => "$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt " . sslkey('client-dn.key'), +); +is($res, "CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG", "users with cert authentication have entire DN as authn_id"); + # same thing but with a regex $dn_connstr = "$common_connstr dbname=certdb_dn_re"; -- 2.25.1
From ac555ab562a9491c3479a0cee495414b63073ed9 Mon Sep 17 00:00:00 2001 From: Jacob Champion <pchamp...@vmware.com> Date: Wed, 23 Mar 2022 15:07:05 -0700 Subject: [PATCH v9 2/3] Allow parallel workers to use pg_session_authn_id() Move authn_id into a new global, MyParallelProcInfo, which is intended to hold all the information that can be shared between the backend and any parallel workers. MyParallelProcInfo is serialized and restored using a new parallel key. With this change, the parallel restriction can be removed from pg_session_authn_id(). --- src/backend/access/transam/parallel.c | 18 +++++- src/backend/libpq/auth.c | 16 ++--- src/backend/utils/adt/name.c | 4 +- src/backend/utils/init/miscinit.c | 71 +++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 2 +- src/include/libpq/libpq-be.h | 35 ++++++----- src/include/miscadmin.h | 4 ++ src/test/authentication/t/001_password.pl | 33 +++++++++++ 8 files changed, 159 insertions(+), 24 deletions(-) diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index df0cd77558..27eda766b1 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -76,6 +76,7 @@ #define PARALLEL_KEY_REINDEX_STATE UINT64CONST(0xFFFFFFFFFFFF000C) #define PARALLEL_KEY_RELMAPPER_STATE UINT64CONST(0xFFFFFFFFFFFF000D) #define PARALLEL_KEY_UNCOMMITTEDENUMS UINT64CONST(0xFFFFFFFFFFFF000E) +#define PARALLEL_KEY_PROCINFO UINT64CONST(0xFFFFFFFFFFFF000F) /* Fixed-size parallel state. */ typedef struct FixedParallelState @@ -212,6 +213,7 @@ InitializeParallelDSM(ParallelContext *pcxt) Size reindexlen = 0; Size relmapperlen = 0; Size uncommittedenumslen = 0; + Size procinfolen = 0; Size segsize = 0; int i; FixedParallelState *fps; @@ -272,8 +274,10 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_estimate_chunk(&pcxt->estimator, relmapperlen); uncommittedenumslen = EstimateUncommittedEnumsSpace(); shm_toc_estimate_chunk(&pcxt->estimator, uncommittedenumslen); + procinfolen = EstimateParallelProcInfoSpace(); + shm_toc_estimate_chunk(&pcxt->estimator, procinfolen); /* If you add more chunks here, you probably need to add keys. */ - shm_toc_estimate_keys(&pcxt->estimator, 11); + shm_toc_estimate_keys(&pcxt->estimator, 12); /* Estimate space need for error queues. */ StaticAssertStmt(BUFFERALIGN(PARALLEL_ERROR_QUEUE_SIZE) == @@ -352,6 +356,7 @@ InitializeParallelDSM(ParallelContext *pcxt) char *session_dsm_handle_space; char *entrypointstate; char *uncommittedenumsspace; + char *procinfospace; Size lnamelen; /* Serialize shared libraries we have loaded. */ @@ -422,6 +427,12 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_insert(pcxt->toc, PARALLEL_KEY_UNCOMMITTEDENUMS, uncommittedenumsspace); + /* Serialize our ParallelProcInfo. */ + procinfospace = shm_toc_allocate(pcxt->toc, procinfolen); + SerializeParallelProcInfo(procinfolen, procinfospace); + shm_toc_insert(pcxt->toc, PARALLEL_KEY_PROCINFO, + procinfospace); + /* Allocate space for worker information. */ pcxt->worker = palloc0(sizeof(ParallelWorkerInfo) * pcxt->nworkers); @@ -1270,6 +1281,7 @@ ParallelWorkerMain(Datum main_arg) char *reindexspace; char *relmapperspace; char *uncommittedenumsspace; + char *procinfospace; StringInfoData msgbuf; char *session_dsm_handle_space; Snapshot tsnapshot; @@ -1479,6 +1491,10 @@ ParallelWorkerMain(Datum main_arg) false); RestoreUncommittedEnums(uncommittedenumsspace); + /* Restore the ParallelProcInfo. */ + procinfospace = shm_toc_lookup(toc, PARALLEL_KEY_PROCINFO, false); + RestoreParallelProcInfo(procinfospace); + /* Attach to the leader's serializable transaction, if SERIALIZABLE. */ AttachSerializableXact(fps->serializable_xact_handle); diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index efc53f3135..2e5fe2cc19 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -342,15 +342,15 @@ auth_failed(Port *port, int status, const char *logdetail) * authorization will fail later. * * The provided string will be copied into TopMemoryContext, to match the - * lifetime of the Port, so it is safe to pass a string that is managed by an - * external library. + * lifetime of MyParallelProcInfo, so it is safe to pass a string that is + * managed by an external library. */ static void set_authn_id(Port *port, const char *id) { Assert(id); - if (port->authn_id) + if (MyParallelProcInfo.authn_id) { /* * An existing authn_id should never be overwritten; that means two @@ -361,17 +361,18 @@ set_authn_id(Port *port, const char *id) ereport(FATAL, (errmsg("authentication identifier set more than once"), errdetail_log("previous identifier: \"%s\"; new identifier: \"%s\"", - port->authn_id, id))); + MyParallelProcInfo.authn_id, id))); } - port->authn_id = MemoryContextStrdup(TopMemoryContext, id); + MyParallelProcInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id); if (Log_connections) { ereport(LOG, errmsg("connection authenticated: identity=\"%s\" method=%s " "(%s:%d)", - port->authn_id, hba_authname(port->hba->auth_method), HbaFileName, + MyParallelProcInfo.authn_id, + hba_authname(port->hba->auth_method), HbaFileName, port->hba->linenumber)); } } @@ -1908,7 +1909,8 @@ auth_peer(hbaPort *port) */ set_authn_id(port, pw->pw_name); - ret = check_usermap(port->hba->usermap, port->user_name, port->authn_id, false); + ret = check_usermap(port->hba->usermap, port->user_name, + MyParallelProcInfo.authn_id, false); return ret; #else diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c index 662a7943ed..24a06bf933 100644 --- a/src/backend/utils/adt/name.c +++ b/src/backend/utils/adt/name.c @@ -275,10 +275,10 @@ session_user(PG_FUNCTION_ARGS) Datum pg_session_authn_id(PG_FUNCTION_ARGS) { - if (!MyProcPort || !MyProcPort->authn_id) + if (!MyParallelProcInfo.authn_id) PG_RETURN_NULL(); - PG_RETURN_TEXT_P(cstring_to_text(MyProcPort->authn_id)); + PG_RETURN_TEXT_P(cstring_to_text(MyParallelProcInfo.authn_id)); } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index bdc77af719..91b3347398 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -929,6 +929,77 @@ GetUserNameFromId(Oid roleid, bool noerr) return result; } +/* ------------------------------------------------------------------------ + * Parallel connection state + * + * MyParallelProcInfo contains pieces of information about the client that need + * to be synced to parallel workers when they initialize. Over time, this list + * will probably grow, and may subsume some of the "user state" variables above. + *------------------------------------------------------------------------- + */ + +ParallelProcInfo MyParallelProcInfo; + +/* + * Calculate the space needed to serialize MyParallelProcInfo. + */ +Size +EstimateParallelProcInfoSpace(void) +{ + Size size = 1; + + if (MyParallelProcInfo.authn_id) + size = add_size(size, strlen(MyParallelProcInfo.authn_id) + 1); + + return size; +} + +/* + * Serialize MyParallelProcInfo for use by parallel workers. + */ +void +SerializeParallelProcInfo(Size maxsize, char *start_address) +{ + /* + * First byte is an indication of whether or not authn_id has been set to + * non-NULL, to differentiate that case from the empty string. + */ + Assert(maxsize > 0); + start_address[0] = MyParallelProcInfo.authn_id ? 1 : 0; + start_address++; + maxsize--; + + if (MyParallelProcInfo.authn_id) + { + Size len; + + len = strlcpy(start_address, MyParallelProcInfo.authn_id, maxsize) + 1; + Assert(len <= maxsize); + maxsize -= len; + start_address += len; + } +} + +/* + * Restore MyParallelProcInfo from its serialized representation. + */ +void +RestoreParallelProcInfo(char *procinfo) +{ + if (procinfo[0] == 0) + { + MyParallelProcInfo.authn_id = NULL; + procinfo++; + } + else + { + procinfo++; + MyParallelProcInfo.authn_id = MemoryContextStrdup(TopMemoryContext, + procinfo); + procinfo += strlen(procinfo) + 1; + } +} + /*------------------------------------------------------------------------- * Interlock-file support diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index fb1126b090..9bd1af42c0 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1509,7 +1509,7 @@ proname => 'session_user', provolatile => 's', prorettype => 'name', proargtypes => '', prosrc => 'session_user' }, { oid => '9774', descr => 'session authenticated identity', - proname => 'pg_session_authn_id', provolatile => 's', proparallel => 'r', + proname => 'pg_session_authn_id', provolatile => 's', prorettype => 'text', proargtypes => '', prosrc => 'pg_session_authn_id' }, { oid => '744', diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index dd3e5efba3..5cc4091216 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -99,6 +99,27 @@ typedef struct } pg_gssinfo; #endif +/* + * Fields from Port that need to be copied over to parallel workers go into the + * ParallelProcInfo. The same rules apply for allocations here as for Port (must + * be malloc'd or palloc'd in TopMemoryContext). + */ +typedef struct +{ + /* + * Authenticated identity. The meaning of this identifier is dependent on + * hba->auth_method; it is the identity (if any) that the user presented + * during the authentication cycle, before they were assigned a database + * role. (It is effectively the "SYSTEM-USERNAME" of a pg_ident usermap + * -- though the exact string in use may be different, depending on pg_hba + * options.) + * + * authn_id is NULL if the user has not actually been authenticated, for + * example if the "trust" auth method is in use. + */ + const char *authn_id; +} ParallelProcInfo; + /* * This is used by the postmaster in its communication with frontends. It * contains all state information needed during this communication before the @@ -159,19 +180,6 @@ typedef struct Port */ HbaLine *hba; - /* - * Authenticated identity. The meaning of this identifier is dependent on - * hba->auth_method; it is the identity (if any) that the user presented - * during the authentication cycle, before they were assigned a database - * role. (It is effectively the "SYSTEM-USERNAME" of a pg_ident usermap - * -- though the exact string in use may be different, depending on pg_hba - * options.) - * - * authn_id is NULL if the user has not actually been authenticated, for - * example if the "trust" auth method is in use. - */ - const char *authn_id; - /* * TCP keepalive and user timeout settings. * @@ -328,6 +336,7 @@ extern ssize_t be_gssapi_write(Port *port, void *ptr, size_t len); #endif /* ENABLE_GSS */ extern ProtocolVersion FrontendProtocol; +extern ParallelProcInfo MyParallelProcInfo; /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 0abc3ad540..7a08a73b85 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -481,6 +481,10 @@ extern void process_session_preload_libraries(void); extern void pg_bindtextdomain(const char *domain); extern bool has_rolreplication(Oid roleid); +extern Size EstimateParallelProcInfoSpace(void); +extern void SerializeParallelProcInfo(Size maxsize, char *start_address); +extern void RestoreParallelProcInfo(char *procinfo); + /* in access/transam/xlog.c */ extern bool BackupInProgress(void); extern void CancelBackup(void); diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl index f0bdeda52d..3f8629b3a6 100644 --- a/src/test/authentication/t/001_password.pl +++ b/src/test/authentication/t/001_password.pl @@ -74,6 +74,14 @@ $node->safe_psql('postgres', ); $ENV{"PGPASSWORD"} = 'pass'; +# Set up a table for parallel worker testing. +$node->safe_psql('postgres', + 'CREATE TABLE nulls (n) AS SELECT NULL FROM generate_series(1, 200000);' +); +$node->safe_psql('postgres', + 'GRANT SELECT ON nulls TO md5_role;' +); + # For "trust" method, all users should be able to connect. These users are not # considered to be authenticated. reset_pg_hba($node, 'trust'); @@ -86,6 +94,19 @@ my $res = $node->safe_psql('postgres', "SELECT pg_session_authn_id() IS NULL;"); is($res, 't', "users with trust authentication have NULL authn_id"); +# Test pg_session_authn_id() with parallel workers. +$res = $node->safe_psql( + 'postgres', ' + SET min_parallel_table_scan_size TO 0; + SET parallel_setup_cost TO 0; + SET parallel_tuple_cost TO 0; + SET max_parallel_workers_per_gather TO 2; + + SELECT bool_and(pg_session_authn_id() IS NOT DISTINCT FROM n) FROM nulls; + ', + connstr => "user=md5_role"); +is($res, 't', "parallel workers return a null authn_id when not authenticated"); + # For plain "password" method, all users should also be able to connect. reset_pg_hba($node, 'password'); test_role($node, 'scram_role', 'password', 0, @@ -102,6 +123,18 @@ $res = $node->safe_psql( is($res, 'md5_role', "users with md5 authentication have authn_id matching role name"); +$res = $node->safe_psql( + 'postgres', ' + SET min_parallel_table_scan_size TO 0; + SET parallel_setup_cost TO 0; + SET parallel_tuple_cost TO 0; + SET max_parallel_workers_per_gather TO 2; + + SELECT bool_and(pg_session_authn_id() IS DISTINCT FROM n) FROM nulls; + ', + connstr => "user=md5_role"); +is($res, 't', "parallel workers return a non-null authn_id when authenticated"); + # For "scram-sha-256" method, user "scram_role" should be able to connect. reset_pg_hba($node, 'scram-sha-256'); test_role( -- 2.25.1