Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qIMKL-0000HG-L4 for pgsql-hackers@arkaria.postgresql.org; Sun, 09 Jul 2023 04:47:17 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1qIMKK-0002w2-6W for pgsql-hackers@arkaria.postgresql.org; Sun, 09 Jul 2023 04:47:16 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qIMKJ-0002vs-Jr for pgsql-hackers@lists.postgresql.org; Sun, 09 Jul 2023 04:47:15 +0000 Received: from mail-pg1-x52b.google.com ([2607:f8b0:4864:20::52b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1qIMKG-002pBW-Oi for pgsql-hackers@lists.postgresql.org; Sun, 09 Jul 2023 04:47:14 +0000 Received: by mail-pg1-x52b.google.com with SMTP id 41be03b00d2f7-55b66ce047cso1660745a12.0 for ; Sat, 08 Jul 2023 21:47:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1688878031; x=1691470031; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:message-id:subject:cc:to:from:date:from:to :cc:subject:date:message-id:reply-to; bh=qJSCmwWhFDi++JdTP6f5XoclPuLxRkWYwSVIrSjA4RE=; b=hFTEfc51u2mx6l1vC5Ss/IYRDsGInGBRwaTW9ZKGEG8H5K+VWQ1v5Bz7HySu7kfp5a Ui7X5XgXOAdV2pUSAmAJP+Jd28nDPA3zRijqhyKhFqm+R3Ro8fEivNn989z4WGOvD9fa YS/5AeNtxobkvrCYJwX1EC8nihJezWbM3OA/30EtcotokNgD/tviHIJ+btG/ezpqVBEc A7AATByJQh9K+rKWOkYmX1xYuGeVQkp5yyC7A6XKxtnR2NrIsW6BQAOvUEgndutbWsWc eiGUYwK3UcGRMDjUsLv5JIdw+J9UD7EXew9OkFvVDRI5Q3jvlrmIdR9YWwKdficUmUAl dhHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1688878031; x=1691470031; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:message-id:subject:cc:to:from:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=qJSCmwWhFDi++JdTP6f5XoclPuLxRkWYwSVIrSjA4RE=; b=Q6LOlg5VQ0zhYuc3vdNN00OOWk/VR8OgFf+mXMFxumd2IKqWCma8MEk+qx2rcS6hiq yllHsu1rbe93iPPxekNXI40kNZEi0guX+Of+NQ21+t9zMVGOuZLaqEoRazeOeXWlweiQ gJLqJVtRHzTBAofYLRWTf2UTi1gkM1fVMvN5sXyOMK3M17CjYPsk1Vj290BKfMfFSYsW k0yTpT+F8v1/Bb0JKZnqGHFNdWAvaAPZ5tDv01BUOAg4SBnA7kBt/wBs6ujhvyv5xjri 0bfT/kdeqtRShG04HdkfXsjiYIvPZoS+y/eW78oM63KwHUVD2dyVdzIgAy1YaAwOsMAf tbAA== X-Gm-Message-State: ABy/qLY+7LCe+zV8P0JbxlumG2RTehsjDHTzY5MlNLJ1mPvbxHjn38W5 6zw1IITEypQ8vMmmuqeFXpM= X-Google-Smtp-Source: APBJJlHOIRH/dbJsqOIrmTyLLZ8OUVAw2dtiTYTvZt86hQ92d0wz60sohQnsV868RqKuJIkoA1z1ew== X-Received: by 2002:a17:90a:ea01:b0:263:e39d:a7e2 with SMTP id w1-20020a17090aea0100b00263e39da7e2mr7232392pjy.4.1688878031491; Sat, 08 Jul 2023 21:47:11 -0700 (PDT) Received: from nathanxps13 ([50.47.162.83]) by smtp.gmail.com with ESMTPSA id 6-20020a17090a01c600b00265a7145fe5sm990809pjd.41.2023.07.08.21.47.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Jul 2023 21:47:10 -0700 (PDT) Date: Sat, 8 Jul 2023 21:47:08 -0700 From: Nathan Bossart To: Joseph Koshakow Cc: PostgreSQL Hackers Subject: Re: Preventing non-superusers from altering session authorization Message-ID: <20230709044708.GA4102497@nathanxps13> References: <20230622034818.GA1077640@nathanxps13> <20230623175416.GA1268820@nathanxps13> <20230708220904.GB4044380@nathanxps13> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="45Z9DzgjV8m4Oswq" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --45Z9DzgjV8m4Oswq Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit On Sat, Jul 08, 2023 at 07:08:35PM -0400, Joseph Koshakow wrote: > On Sat, Jul 8, 2023 at 6:09 PM Nathan Bossart > wrote: > >>> I think the issue here is that if a session loses the ability to set >>> their session authorization in the middle of a transaction, then >>> rolling back the transaction may fail and cause the server to panic. >>> That's probably what the deleted comment mean when it said: >>> >>>> * It's OK because the check does not require catalog access and can't >>>> * fail during an end-of-transaction GUC reversion >> >> Yeah. IIUC the ERROR longjmps to a block that calls AbortTransaction(), >> which ERRORs again when resetting the session authorization, which causes >> us to call AbortTransaction() again, etc., etc. src/backend/utils/misc/README has the following relevant text: Note that there is no provision for a failure result code. assign_hooks should never fail except under the most dire circumstances, since a failure may for example result in GUC settings not being rolled back properly during transaction abort. In general, try to do anything that could conceivably fail in a check_hook instead, and pass along the results in an "extra" struct, so that the assign hook has little to do beyond copying the data to someplace. This applies particularly to catalog lookups: any required lookups must be done in the check_hook, since the assign_hook may be executed during transaction rollback when lookups will be unsafe. > Everything seems to work fine if the privilege check is moved to > check_session_authorization. Which is maybe what the comment meant > instead of assign_session_authorization. Ah, that does make more sense. I think we should split this into two patches: one to move the permission check to check_session_authorization() and another for the behavior change. I've attached an attempt at the first one (that borrows heavily from your latest patch). AFAICT the only reason that the permission check lives in SetSessionAuthorization() is because AuthenticatedUserIsSuperuser is static to miscinit.c and doesn't have an accessor function. I added one, but it would probably just be removed by the following patch. WDYT? -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --45Z9DzgjV8m4Oswq Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v4-0001-move-session-auth-permission-check.patch" From 4972f29cef6dfbb948e843517ce5ff413628c2f1 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Sat, 8 Jul 2023 21:35:03 -0700 Subject: [PATCH v4 1/1] move session auth permission check --- src/backend/commands/variable.c | 23 +++++++++++++++++++++++ src/backend/utils/init/miscinit.c | 29 ++++++++++------------------- src/include/miscadmin.h | 1 + 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index f0f2e07655..f8e308f1d0 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -846,6 +846,29 @@ check_session_authorization(char **newval, void **extra, GucSource source) ReleaseSysCache(roleTup); + /* + * Only a superuser may set auth ID to something other than himself. Note + * that in case of multiple SETs in a single session, the original + * userid's superuserness is what matters. But we set the GUC variable + * is_superuser to indicate whether the *current* session userid is a + * superuser. + */ + if (roleid != GetAuthenticatedUserId() && + !GetAuthenticatedUserIsSuperuser()) + { + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission will be denied to set session authorization \"%s\"", + *newval))); + return true; + } + GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE); + GUC_check_errmsg("permission denied to set session authorization"); + return false; + } + /* Set up "extra" struct for assign_session_authorization to use */ myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra)); if (!myextra) diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index a604432126..f5548a0f47 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -582,6 +582,16 @@ GetAuthenticatedUserId(void) return AuthenticatedUserId; } +/* + * Return whether the authenticated user was superuser at connection start. + */ +bool +GetAuthenticatedUserIsSuperuser(void) +{ + Assert(OidIsValid(AuthenticatedUserId)); + return AuthenticatedUserIsSuperuser; +} + /* * GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID @@ -888,29 +898,10 @@ system_user(PG_FUNCTION_ARGS) /* * Change session auth ID while running - * - * Only a superuser may set auth ID to something other than himself. Note - * that in case of multiple SETs in a single session, the original userid's - * superuserness is what matters. But we set the GUC variable is_superuser - * to indicate whether the *current* session userid is a superuser. - * - * Note: this is not an especially clean place to do the permission check. - * It's OK because the check does not require catalog access and can't - * fail during an end-of-transaction GUC reversion, but we may someday - * have to push it up into assign_session_authorization. */ void SetSessionAuthorization(Oid userid, bool is_superuser) { - /* Must have authenticated already, else can't make permission check */ - Assert(OidIsValid(AuthenticatedUserId)); - - if (userid != AuthenticatedUserId && - !AuthenticatedUserIsSuperuser) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set session authorization"))); - SetSessionUserId(userid, is_superuser); SetConfigOption("is_superuser", diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 14bd574fc2..11d6e6869d 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -357,6 +357,7 @@ extern Oid GetUserId(void); extern Oid GetOuterUserId(void); extern Oid GetSessionUserId(void); extern Oid GetAuthenticatedUserId(void); +extern bool GetAuthenticatedUserIsSuperuser(void); extern void GetUserIdAndSecContext(Oid *userid, int *sec_context); extern void SetUserIdAndSecContext(Oid userid, int sec_context); extern bool InLocalUserIdChange(void); -- 2.25.1 --45Z9DzgjV8m4Oswq--