On Wed, 2022-03-23 at 16:54 -0700, Andres Freund wrote: > On 2022-03-23 23:06:14 +0000, Jacob Champion wrote: > > On Wed, 2022-03-23 at 19:00 -0400, Tom Lane wrote: > > > Hm. I was more envisioning getting the "sharable" info out of Port > > > entirely, although I'm not quite sure where it should go instead. > > > > If it helps, I can move the substruct out and up to a new global struct > > (MyProcShared?). At this point I think it's mostly search-and-replace. > > Perhaps alongside CurrentUserId etc in miscinit.c? It would be nicer if all > those were together in a struct, but oh well.
Next draft in v7. My naming choices probably make even less sense now. Any ideas for names for "a bag of stuff that we want parallel workers to have too"? > Another option would be to make it a GUC. With a bit of care it could be > automatically synced by the existing parallelism infrastructure... Like a write-once, PGC_INTERNAL setting? I guess I don't have any intuition on how that would compare to the separate-global-and-accessor approach. Is the primary advantage that you don't have to maintain the serialization logic, or is there more to it? Thanks, --Jacob
commit e6eca817f3cc359fff762600ad286d92046ba07d Author: Jacob Champion <pchamp...@vmware.com> Date: Thu Mar 24 10:00:30 2022 -0700 squash! Allow parallel workers to use pg_session_authn_id() Move SharedPort out of Port and over to miscinit.c. diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index dda2aab7b1..c88eab0933 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -478,79 +478,6 @@ InitializeParallelDSM(ParallelContext *pcxt) MemoryContextSwitchTo(oldcontext); } -/* - * Calculate the space needed to serialize MyProcPort->shared. - */ -Size -EstimateSharedPortSpace(void) -{ - SharedPort *shared = &MyProcPort->shared; - Size size = 1; - - if (shared->authn_id) - size = add_size(size, strlen(shared->authn_id) + 1); - - return size; -} - -/* - * Serialize MyProcPort->shared for use by parallel workers. - */ -void -SerializeSharedPort(Size maxsize, char *start_address) -{ - SharedPort *shared = &MyProcPort->shared; - - /* - * 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] = shared->authn_id ? 1 : 0; - start_address++; - maxsize--; - - if (shared->authn_id) - { - Size len; - - len = strlcpy(start_address, shared->authn_id, maxsize) + 1; - Assert(len <= maxsize); - maxsize -= len; - start_address += len; - } -} - -/* - * Restore MyProcPort->shared from its serialized representation, allocating - * MyProcPort if necessary. - */ -void -RestoreSharedPort(char *sharedport) -{ - /* First make sure we have a place to put the information. */ - if (!MyProcPort) - { - if (!(MyProcPort = calloc(1, sizeof(Port)))) - ereport(FATAL, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - } - - if (sharedport[0] == 0) - { - MyProcPort->shared.authn_id = NULL; - sharedport++; - } - else - { - sharedport++; - MyProcPort->shared.authn_id = MemoryContextStrdup(TopMemoryContext, - sharedport); - sharedport += strlen(sharedport) + 1; - } -} - /* * Reinitialize the dynamic shared memory segment for a parallel context such * that we could launch workers for it again. diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 40384a31b0..bceda9755a 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 MyProcShared, 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->shared.authn_id) + if (MyProcShared.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\"", - port->shared.authn_id, id))); + MyProcShared.authn_id, id))); } - port->shared.authn_id = MemoryContextStrdup(TopMemoryContext, id); + MyProcShared.authn_id = MemoryContextStrdup(TopMemoryContext, id); if (Log_connections) { ereport(LOG, errmsg("connection authenticated: identity=\"%s\" method=%s " "(%s:%d)", - port->shared.authn_id, + MyProcShared.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, - port->shared.authn_id, false); + MyProcShared.authn_id, false); return ret; #else diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c index 9000ad05f8..6d497e63d9 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->shared.authn_id) + if (!MyProcShared.authn_id) PG_RETURN_NULL(); - PG_RETURN_TEXT_P(cstring_to_text(MyProcPort->shared.authn_id)); + PG_RETURN_TEXT_P(cstring_to_text(MyProcShared.authn_id)); } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index bdc77af719..0afab3e142 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; } +/* ------------------------------------------------------------------------ + * "Shared" 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. + *------------------------------------------------------------------------- + */ + +SharedPort MyProcShared; + +/* + * Calculate the space needed to serialize MyProcShared. + */ +Size +EstimateSharedPortSpace(void) +{ + Size size = 1; + + if (MyProcShared.authn_id) + size = add_size(size, strlen(MyProcShared.authn_id) + 1); + + return size; +} + +/* + * Serialize MyProcShared for use by parallel workers. + */ +void +SerializeSharedPort(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++; + maxsize--; + + if (MyProcShared.authn_id) + { + Size len; + + len = strlcpy(start_address, MyProcShared.authn_id, maxsize) + 1; + Assert(len <= maxsize); + maxsize -= len; + start_address += len; + } +} + +/* + * Restore MyProcShared from its serialized representation. + */ +void +RestoreSharedPort(char *sharedport) +{ + if (sharedport[0] == 0) + { + MyProcShared.authn_id = NULL; + sharedport++; + } + else + { + sharedport++; + MyProcShared.authn_id = MemoryContextStrdup(TopMemoryContext, + sharedport); + sharedport += strlen(sharedport) + 1; + } +} + /*------------------------------------------------------------------------- * Interlock-file support diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 0a9dc61d04..911b8246ce 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -180,12 +180,6 @@ typedef struct Port */ HbaLine *hba; - /* - * Information that's copied between the backend and any parallel workers. - * This is the only part of the Port that a parallel worker may access! - */ - SharedPort shared; - /* * TCP keepalive and user timeout settings. * @@ -342,6 +336,7 @@ extern ssize_t be_gssapi_write(Port *port, void *ptr, size_t len); #endif /* ENABLE_GSS */ extern ProtocolVersion FrontendProtocol; +extern SharedPort MyProcShared; /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ @@ -355,8 +350,4 @@ extern int pq_setkeepalivesinterval(int interval, Port *port); extern int pq_setkeepalivescount(int count, Port *port); extern int pq_settcpusertimeout(int timeout, Port *port); -extern Size EstimateSharedPortSpace(void); -extern void SerializeSharedPort(Size maxsize, char *start_address); -extern void RestoreSharedPort(char *sharedport); - #endif /* LIBPQ_BE_H */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 0abc3ad540..68cc1517a0 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 EstimateSharedPortSpace(void); +extern void SerializeSharedPort(Size maxsize, char *start_address); +extern void RestoreSharedPort(char *sharedport); + /* in access/transam/xlog.c */ extern bool BackupInProgress(void); extern void CancelBackup(void);
From e30055b1a56f82214dc730077114b4167bff53be Mon Sep 17 00:00:00 2001 From: Jacob Champion <pchamp...@vmware.com> Date: Mon, 14 Feb 2022 08:10:53 -0800 Subject: [PATCH v7 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 8a802fb225..441a0fd63d 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -22280,6 +22280,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 d8e8715ed1..a1bf898476 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 605e405de3..22b3edc51e 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -451,6 +451,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=$key{'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 2127fc4f60caa33c368c23d4e8cd713d9f150249 Mon Sep 17 00:00:00 2001 From: Jacob Champion <pchamp...@vmware.com> Date: Wed, 23 Mar 2022 15:07:05 -0700 Subject: [PATCH v7 2/3] Allow parallel workers to use pg_session_authn_id() Move authn_id into a new global, MyProcShared, which is intended to hold all the information that can be shared between the backend and any parallel workers. MyProcShared 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..c88eab0933 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_SHAREDPORT 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 sharedportlen = 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); + sharedportlen = EstimateSharedPortSpace(); + shm_toc_estimate_chunk(&pcxt->estimator, sharedportlen); /* 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 *sharedportspace; 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 SharedPort. */ + sharedportspace = shm_toc_allocate(pcxt->toc, sharedportlen); + SerializeSharedPort(sharedportlen, sharedportspace); + shm_toc_insert(pcxt->toc, PARALLEL_KEY_SHAREDPORT, + sharedportspace); + /* 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 *sharedportspace; StringInfoData msgbuf; char *session_dsm_handle_space; Snapshot tsnapshot; @@ -1479,6 +1491,10 @@ ParallelWorkerMain(Datum main_arg) false); RestoreUncommittedEnums(uncommittedenumsspace); + /* Restore the SharedPort. */ + sharedportspace = shm_toc_lookup(toc, PARALLEL_KEY_SHAREDPORT, false); + RestoreSharedPort(sharedportspace); + /* 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..bceda9755a 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 MyProcShared, 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 (MyProcShared.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))); + MyProcShared.authn_id, id))); } - port->authn_id = MemoryContextStrdup(TopMemoryContext, id); + MyProcShared.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, + MyProcShared.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, + MyProcShared.authn_id, false); return ret; #else diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c index 662a7943ed..6d497e63d9 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 (!MyProcShared.authn_id) PG_RETURN_NULL(); - PG_RETURN_TEXT_P(cstring_to_text(MyProcPort->authn_id)); + PG_RETURN_TEXT_P(cstring_to_text(MyProcShared.authn_id)); } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index bdc77af719..0afab3e142 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; } +/* ------------------------------------------------------------------------ + * "Shared" 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. + *------------------------------------------------------------------------- + */ + +SharedPort MyProcShared; + +/* + * Calculate the space needed to serialize MyProcShared. + */ +Size +EstimateSharedPortSpace(void) +{ + Size size = 1; + + if (MyProcShared.authn_id) + size = add_size(size, strlen(MyProcShared.authn_id) + 1); + + return size; +} + +/* + * Serialize MyProcShared for use by parallel workers. + */ +void +SerializeSharedPort(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++; + maxsize--; + + if (MyProcShared.authn_id) + { + Size len; + + len = strlcpy(start_address, MyProcShared.authn_id, maxsize) + 1; + Assert(len <= maxsize); + maxsize -= len; + start_address += len; + } +} + +/* + * Restore MyProcShared from its serialized representation. + */ +void +RestoreSharedPort(char *sharedport) +{ + if (sharedport[0] == 0) + { + MyProcShared.authn_id = NULL; + sharedport++; + } + else + { + sharedport++; + MyProcShared.authn_id = MemoryContextStrdup(TopMemoryContext, + sharedport); + sharedport += strlen(sharedport) + 1; + } +} + /*------------------------------------------------------------------------- * Interlock-file support diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index a1bf898476..b044a71c93 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..911b8246ce 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 + * SharedPort. The same rules apply for allocations here as for Port (must be + * malloc'd or palloc'd in TopMemoryContext). + */ +typedef struct SharedPort +{ + /* + * 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; +} SharedPort; + /* * 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 SharedPort MyProcShared; /* 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..68cc1517a0 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 EstimateSharedPortSpace(void); +extern void SerializeSharedPort(Size maxsize, char *start_address); +extern void RestoreSharedPort(char *sharedport); + /* 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