Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wRXu2-002S5o-2Y for pgsql-hackers@arkaria.postgresql.org; Mon, 25 May 2026 16:11:43 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wRXu0-001cbN-2b for pgsql-hackers@arkaria.postgresql.org; Mon, 25 May 2026 16:11:41 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wRXu0-001cbD-1M for pgsql-hackers@lists.postgresql.org; Mon, 25 May 2026 16:11:41 +0000 Received: from mail-vk1-xa2b.google.com ([2607:f8b0:4864:20::a2b]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wRXty-00000001NQu-2aG6 for pgsql-hackers@postgresql.org; Mon, 25 May 2026 16:11:40 +0000 Received: by mail-vk1-xa2b.google.com with SMTP id 71dfb90a1353d-5752b27958fso3281559e0c.1 for ; Mon, 25 May 2026 09:11:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1779725496; cv=none; d=google.com; s=arc-20240605; b=Gu49iodhZ4OIm8MI9JG5KoNFosiOssaQJsh+utb5rjtTe/UBMyIxFPeDZyse52q6su fvxoTybwMFQSWSNUBdjty2lqj3AVXPKpyrrMH5MfExgJL9mtS5wQk+wOaavdIaVx3IME 9mpYSxtD75rc68/CNTA0ZBfP/XgBLutdtJaB3Jfwl8Ry02vNx6sifBj9lzHjkTNvmirf Lvs50J8DaMmwS3QMaTENMftoZAwugpkKDni/B5E+olzooSADvvNEBrcYmtK2WJsc5xp3 bWRun+UMnuwJSLnGKQFIHKv1SFMI4El6Yt5JzPxblmOrGdZ51p4lPMa9BA8HiP6BJXvC ImEw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=0b4V6sixu6gDzvNSCb51eo5JZVvT1LVWCT2Uu9omCDw=; fh=T2SDbkEiKfFo+aJ4egn6YgilCo2nQjAWdnA/Iy6zeOU=; b=iOsLY7mgeNffmcs4PbkL6ZZbG2K2Pon1tHYXFXdQ2AI+rTjZkEt4tFW4J8GSm0R0OA 1ILcH5GOR0E4ayreak8vfHXnftEChkrTK6MAZSMk93QPw4sSmVU9JaXR9ppI2ifreLvi kvNrip+ynmEtpDb7FPSOweb78Il6yRe6pNC6pqd3iUwQU+OJ8a/tfvh0Oz0Shh7ajsy3 ISMlbs3beJXwaIL9NasVZfnX/mm0PhfYD5LN+DA1KVFuvIerIqJXxEwgyDWFOknQMFpW ztFFVIiCWmMVnIE3AfYhqnGaVHbLyr2HHDjIQEYxTib6n+MMxKX+nDd7K99EEc5PfhMR 0ecQ==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jackdb.com; s=google; t=1779725496; x=1780330296; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=0b4V6sixu6gDzvNSCb51eo5JZVvT1LVWCT2Uu9omCDw=; b=FtWBwobZm8lPs68brW+HZ2WTjjOUYiCr0m8tCvffUb4l5Hp3M2miaRiciE/wJzX0Cx TVK09TLAHhrBINNoxYqkgt6dVNlKMaI7vxZaXFtFpC8ionfRzrVqJVJyFlL846HYxh64 tAIXATp1ImYhrpMcQjJz2UE+dSYoyfp4EyJeM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779725496; x=1780330296; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=0b4V6sixu6gDzvNSCb51eo5JZVvT1LVWCT2Uu9omCDw=; b=TWTY/k1hLmjDbQCESFkjHwJxIZKKT3omvMHgopy7/0uOhxqYYFjK+VsAZMmE8P9Vqp SbJbs4OLr7SUKYcuf6J+DhfzqXkjYtDZwvhBwF8P/EmH99zswuuA6V5bQswi3SJt2+KL ycq6ZoiKEMRbxxJ67UVaAG++DuWfAzm63IzXC2rE0YgVVjKfdVmQswrxlqTX9jjlwrNu 6dE2g6kgKcB9imSqnvrbB0Z+OL73ciX+stuwfLIh/gUy8em+aCjckOpdk4cxnh93m4TR 2+1w+8JKpuJOEfJV5d0rPO8gs7qvBZpq0HGQ0OMw4m6U/JGGzQzB7vFZNTy1mJVnDUHv lP5w== X-Gm-Message-State: AOJu0Yw26pgwz9HskLFXB+wNE1KRR0+Y4MMjnyqPTCPtGX4KBNDw9l3Q JtvbLaSezbDDbLi7wdTJVXlxfRkLhL0NYufFjLaaFKCmTmD4jSMx/RXr8jIEl8YM3/CCODaFHYI DkxsArW8Ia5M0xxIntRDQ4ABnvhHV6xrb7e+mRC+NspT0nUNm5bjSFl0= X-Gm-Gg: Acq92OEArNXskwnOO6+Gc/VJ0vk19c2rbVQFj7pLurTtcAy+jwbtkFo5qLXWWrRSpNh 6PCW66JVI2B/8nAUCcXz7+vWEWCgbe4pdR3tYGOppfTb7IJignPKDbzrhLm2huLuMiuV//pYYQ5 UqWFmwfFODgd9l75uae0PK0HwIOhNtZGYx+7iXmczf1mlCE+oQckfArnVTyyArI5NGVm4FtADEW 3jAg3/BRd2vCJax1i/QHmlni7SRR5fZl59VyF7clL/9aCQIkVdNoQDFBZSMolX367F3A+zl3dm9 XKZhHw== X-Received: by 2002:a05:6122:65a1:b0:56f:61d8:86d7 with SMTP id 71dfb90a1353d-586607b73aemr7018517e0c.7.1779725496006; Mon, 25 May 2026 09:11:36 -0700 (PDT) MIME-Version: 1.0 References: <836EAAF0-1765-4B0F-A145-123464A19255@yesql.se> In-Reply-To: <836EAAF0-1765-4B0F-A145-123464A19255@yesql.se> From: Sehrope Sarkuni Date: Mon, 25 May 2026 12:11:25 -0400 X-Gm-Features: AVHnY4INNryys3WVRERnQSA8t08cid_gRi2db3PQDd_9yw9vsYeO6MLOdXIC72U Message-ID: Subject: Re: Tighten SCRAM iteration parsing and bound libpq PBKDF2 work To: Daniel Gustafsson Cc: PostgreSQL Hackers Content-Type: multipart/alternative; boundary="000000000000fcc90a0652a6a12a" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000fcc90a0652a6a12a Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, May 25, 2026 at 10:13=E2=80=AFAM Daniel Gustafsson wrote: > > On 24 May 2026, at 22:04, Sehrope Sarkuni wrote: > > The first patch fixes parsing of SCRAM iteration counts in both the > backend SCRAM verifier parser and libpq's server-first-message parser. > Previously both paths parsed into a long and then stored the result in an > int, so values in (INT_MAX, LONG_MAX] could be accepted by strtol() and > then narrowed incorrectly. I don't think this allows for any invalid logi= ns > as the password verifier would have tried to verify a different iteration > count, but it's still wrong. > > The iteration count parsing you refer to is extracting the iterations fro= m > the > stored password, it's not parsing user input in any way. The iteration > count > in set in the password using scram_sha_256_iterations, which in turn can > be set > by the GUC scram_iterations which is limited to 1..INT_MAX (before being > accessible as a GUC it was hardcoded to 4096). > > strtol() to an int without checking if the parsed value exceeds INT_MAX > isn't > good code hygiene, and we should fix that, but it cannot in practice caus= e > truncation AFAICS. > > Can you show a sequence of generating a SCRAM secret which has an iterati= on > count outside of 0..INT_MAX, or one which doesn't correspond to the > setting in > scram_iterations? > You can directly update pg_authid to skip the empty password check entirely. We do that in pgjdbc to test out connecting against some nonsensical values. A regular user can also specify the pre-hashed password in CREATE / ALTER USER. The server will still try to verify the password to ensure that it is not an empty string: // src/backend/commands/user.c#L442 if (password[0] =3D=3D '\0' || plain_crypt_verify(stmt->role, password, "", &logdetail) =3D=3D STATUS_OK) { ereport(NOTICE, (errmsg("empty string is not a valid password, clearing password"))); new_record_nulls[Anum_pg_authid_rolpassword - 1] =3D true; } Normally that would prevent you from trying to create something with a huge value of iterations as it'd never finish. But the long-to-int overflow means that it finishes immediately. postgres=3D# CREATE USER scram_test PASSWORD 'abcd'; CREATE ROLE # Default of 4096 iterations postgres=3D# SELECT rolpassword FROM pg_authid WHERE rolname =3D 'scram_tes= t'; rolpassword ---------------------------------------------------------------------------= ------------------------------------------------------------ SCRAM-SHA-256$4096:PTh5Qe4SmvJnUSHsa6CJpg=3D=3D$rbWsmPJVYzgjIwnVfmfZ43A0FR= 0eThILm+TuYpYF4M0=3D:gFahnsW7KHa73RW1Vk/eP6avCOWf1O2LgCW1ZgiRHtw=3D (1 row) # This hangs effectively for ever as it's trying 2^31-1 iterations to verify that it's not empty string postgres=3D# ALTER USER scram_test PASSWORD 'SCRAM-SHA-256$2147483647:PTh5Qe4SmvJnUSHsa6CJpg=3D=3D$rbWsmPJVYzgjIwnVfmfZ= 43A0FR0eThILm+TuYpYF4M0=3D:gFahnsW7KHa73RW1Vk/eP6avCOWf1O2LgCW1ZgiRHtw=3D'; ^C Cancel request sent ERROR: canceling statement due to user request Time: 4812.007 ms (00:04.812) # This completes instantly because it overflows and the empty string password check passes. Trying to connect gives a scram iterations from the server of "-1". postgres=3D# ALTER USER scram_test PASSWORD 'SCRAM-SHA-256$2147483648:PTh5Qe4SmvJnUSHsa6CJpg=3D=3D$rbWsmPJVYzgjIwnVfmfZ= 43A0FR0eThILm+TuYpYF4M0=3D:gFahnsW7KHa73RW1Vk/eP6avCOWf1O2LgCW1ZgiRHtw=3D'; ALTER ROLE Time: 0.773 ms > > Patches 0003 through 0005 address a separate client-side resource issue= . > In a SCRAM exchange, the PBKDF2 iteration count is supplied by the server= . > A hostile or misconfigured server can advertise a very large iteration > count and keep a blocking libpq connection inside scram_SaltedPassword() > beyond the caller's connect_timeout. The backend has CHECK_FOR_INTERRUPTS= () > inside the loop, but the frontend previously had no equivalent. > > A server which request the iteration count of the stored secret, whatever > it > is, cannot be considered misconfigured or hostile. A server which sends = an > incorrect iteration count in order to reject a connection after causing t= he > client to perform CPU work is hostile (but I'd be quite glad if a hostile > server rejected my connection rather than tricking me into logging in and > stealing/corrupting data). > The attack vector here is a client side denial of service. I previously submitted this to the security list and it was deemed out of scope (as a security issue for core) as it's a client side cpu / memory issue. A real world example of it would be a SaaS or reporting system that connects to user specified databases. A malicious user who could specify the remote database host / port (common in that kind of platform) could get the app to perform the PBKDF2 iterations forever. There's plenty of other things a malicious server can do as well (e.g., forcing allocation of infinite memory). So again out of scope for core as a security issue, but still a problem in specific use cases. > Making the SCRAM calculation honor the connection timeout sounds like a > good > way to address the latter. > > > These patches mirror the blocking connection attempt's deadline into > PGconn, add an optional interrupt callback to the common SCRAM PBKDF2 > helper, and have libpq use that callback to abort once the in-flight > bl:qaocking connection deadline has expired. The test modifies a SCRAM > verifier to advertise a very large iteration count and verifies that > connect_timeout interrupts the PBKDF2 loop. > > > > This protection applies to the blocking connection path, such as > PQconnectdb(). It does not make connect_timeout apply automatically to > applications driving PQconnectPoll() themselves. > > > > I considered passing the connection deadline directly into the SCRAM > PBKDF2 helper, but used an interrupt callback instead. That keeps the > common SCRAM code independent of libpq's timeout representation and allow= s > the same mechanism to support other frontend abort conditions, such as > cancellation of an in-progress connection attempt. > > Making sure SCRAM secret calculation doesn't exceed connection_timeout > sounds > like a good idea. However, ode shared between frontend and backend shoul= d > IMHO > not have parameters which only work in the frontend. I suppose it could be enabled for the backend as well. It'd end up being an additional if-check to skip the NULL arg. Alternatively the backend could supply CFI/wrapper as the callback. Then the scram code would not know anything about who's invoking it. > I'm also not convinced > that it's a good idea to add a callback which in your case gets the entir= e > PGconn object. I'd prefer a check more like the CFI we have in the backe= nd > code. > Is there a front end example of that to look at? Or are you describing something more general that does not exist yet? Another issue is the interval for checking. One point of allowing > configurable > iteration counts was to allow constrained low-power IOT platforms to use > SCRAM > by using a much lower iteration count. Only checking for timeout every > 4096 > iterations might be well past the connection timeout. Perhaps the > interval of > checking should be a function of the iterations sent by the server? > The time check itself is relatively cheap compared to the hashing so lowering it something more fine grained should not matter much. I picked 4096 to match the default scram iterations so it does not even run in the normal case. Maybe 256? The SCRAM RFC estimates it taking .5 seconds to run 4096 iterations on 2015 "current mobile handsets". Would figure it to be even less for devices 10+ years later. And connect timeout granularity is in seconds. > > The attached patch currently uses a default cap of 100K. That is well > above PostgreSQL's normal SCRAM iteration count (4K), but it is still a > client-side behavior change for installations using unusually high SCRAM > iteration counts. For reference, we added a similar patch to pgjdbc with > the same 100K default. > > I am off the cuff -1 on adding more connection parameters for niche > usecases > like this (we already have many enough to make it confusing), it will be > very > hard to document what an appropriate setting is, and getting it wrong mig= ht > mean rejecting connections in err. > For additional reference, we added similar parameters to the java driver pgjdbc and the node driver node-postgres. Both with a 100K limit. Both also had a bit more of a direct issue with the large values (v.s. libpq) as the java driver would create a background thread for the login (which would get pinned to 100% forever with a huge iteration count) and the node driver would also hang indefinitely (as node is single threaded and the crypto functions are not interruptible). Regards, -- Sehrope Sarkuni Founder & CEO | JackDB, Inc. | https://www.jackdb.com/ --000000000000fcc90a0652a6a12a Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Mon, May 25, 2026 at 10:13=E2=80=AFAM Daniel Gustafsson &= lt;daniel@yesql.se= > wrote:
> On 24 May 2026, at 22:04, Sehrope Sarkuni <= ;sehrope@jackdb.com= > wrote:
> The first patch fixes parsing of SCRAM iteration co= unts in both the backend SCRAM verifier parser and libpq's server-first= -message parser. Previously both paths parsed into a long and then stored t= he result in an int, so values in (INT_MAX, LONG_MAX] could be accepted by = strtol() and then narrowed incorrectly. I don't think this allows for a= ny invalid logins as the password verifier would have tried to verify a dif= ferent iteration count, but it's still wrong.

The iteration count parsing you refer to is extracting the iterations from = the
stored password, it's not parsing user input in any way.=C2=A0 The iter= ation count
in set in the password using scram_sha_256_iterations, which in turn can be= set
by the GUC scram_iterations which is limited to 1..INT_MAX (before being accessible as a GUC it was hardcoded to 4096).

strtol() to an int without checking if the parsed value exceeds INT_MAX isn= 't
good code hygiene, and we should fix that, but it cannot in practice cause<= br> truncation AFAICS.

Can you show a sequence of generating a SCRAM secret which has an iteration=
count outside of 0..INT_MAX, or one which doesn't correspond to the set= ting in
scram_iterations?

You can directly upda= te pg_authid to skip the empty password check entirely. We do that in pgjdb= c to test out connecting against some nonsensical values.

A regular = user can also specify the pre-hashed password in CREATE / ALTER USER. The s= erver will still try to verify the password to ensure that it is not an emp= ty string:

// src/backend/commands/user.c#L442
if (password[0] = =3D=3D '\0' || plain_crypt_verify(stmt->role, password, "&q= uot;, &logdetail) =3D=3D STATUS_OK)
{
=C2=A0 ereport(NOTICE,
= =C2=A0 (errmsg("empty string is not a valid password, clearing passwor= d")));
=C2=A0 new_record_nulls[Anum_pg_authid_rolpassword - 1] =3D = true;
}

Normally that would prevent you from trying to create som= ething with a huge value of iterations as it'd never finish. But the lo= ng-to-int overflow means that it finishes immediately.

postgres=3D# = CREATE USER scram_test PASSWORD 'abcd';
CREATE ROLE

# Def= ault of 4096 iterations
postgres=3D# SELECT rolpassword FROM pg_authid W= HERE rolname =3D 'scram_test';
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rolpassword =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0
--------------------------= ---------------------------------------------------------------------------= ----------------------------------
=C2=A0SCRAM-SHA-256$4096:PTh5Qe4SmvJn= USHsa6CJpg=3D=3D$rbWsmPJVYzgjIwnVfmfZ43A0FR0eThILm+TuYpYF4M0=3D:gFahnsW7KHa= 73RW1Vk/eP6avCOWf1O2LgCW1ZgiRHtw=3D
(1 row)

# This hangs effectiv= ely for ever as it's trying 2^31-1 iterations to verify that it's n= ot empty string
postgres=3D# ALTER USER scram_test PASSWORD 'SCRAM-S= HA-256$2147483647:PTh5Qe4SmvJnUSHsa6CJpg=3D=3D$rbWsmPJVYzgjIwnVfmfZ43A0FR0e= ThILm+TuYpYF4M0=3D:gFahnsW7KHa73RW1Vk/eP6avCOWf1O2LgCW1ZgiRHtw=3D';
= ^C
Cancel request sent
ERROR: =C2=A0canceling statement due to user r= equest
Time: 4812.007 ms (00:04.812)

# This completes instantly b= ecause it overflows and the empty string password check passes. Trying to c= onnect gives a scram iterations from the server of "-1".
postg= res=3D# ALTER USER scram_test PASSWORD 'SCRAM-SHA-256$2147483648:PTh5Qe= 4SmvJnUSHsa6CJpg=3D=3D$rbWsmPJVYzgjIwnVfmfZ43A0FR0eThILm+TuYpYF4M0=3D:gFahn= sW7KHa73RW1Vk/eP6avCOWf1O2LgCW1ZgiRHtw=3D';
ALTER ROLE
Time: 0.77= 3 ms
=C2=A0
> Patches 0003 through 0005 address a separate client-side resource issu= e. In a SCRAM exchange, the PBKDF2 iteration count is supplied by the serve= r. A hostile or misconfigured server can advertise a very large iteration c= ount and keep a blocking libpq connection inside scram_SaltedPassword() bey= ond the caller's connect_timeout. The backend has CHECK_FOR_INTERRUPTS(= ) inside the loop, but the frontend previously had no equivalent.

A server which request the iteration count of the stored secret, whatever i= t
is, cannot be considered misconfigured or hostile.=C2=A0 A server which sen= ds an
incorrect iteration count in order to reject a connection after causing the=
client to perform CPU work is hostile (but I'd be quite glad if a hosti= le
server rejected my connection rather than tricking me into logging in and stealing/corrupting data).

The attack v= ector here is a client side denial of service. I previously submitted this = to the security list and it was deemed out of scope (as a security issue fo= r core) as it's a client side cpu / memory issue. A real world example = of it would be a SaaS or reporting system that connects to user specified d= atabases. A malicious user who could specify the remote database host / por= t (common in that kind of platform) could get the app to perform the PBKDF2= iterations forever. There's plenty of other things a malicious server = can do as well (e.g., forcing allocation of infinite memory). So again out = of scope for core as a security issue, but still a problem in specific use = cases.
=C2=A0
Making the SCRAM calculation honor the connection timeout sounds like a goo= d
way to address the latter.

> These patches mirror the blocking connection attempt's deadline in= to PGconn, add an optional interrupt callback to the common SCRAM PBKDF2 he= lper, and have libpq use that callback to abort once the in-flight bl:qaock= ing connection deadline has expired. The test modifies a SCRAM verifier to = advertise a very large iteration count and verifies that connect_timeout in= terrupts the PBKDF2 loop.
>
> This protection applies to the blocking connection path, such as PQcon= nectdb(). It does not make connect_timeout apply automatically to applicati= ons driving PQconnectPoll() themselves.
>
> I considered passing the connection deadline directly into the SCRAM P= BKDF2 helper, but used an interrupt callback instead. That keeps the common= SCRAM code independent of libpq's timeout representation and allows th= e same mechanism to support other frontend abort conditions, such as cancel= lation of an in-progress connection attempt.

Making sure SCRAM secret calculation doesn't exceed connection_timeout = sounds
like a good idea.=C2=A0 However, ode shared between frontend and backend sh= ould IMHO
not have parameters which only work in the frontend.

<= /div>
I suppose it could be enabled for the backend as well. It'd e= nd up being an additional if-check to skip the NULL arg.

Alternatively the backend could supply CFI/wrapper as the callback. = Then the scram code would not know anything about who's invoking it.
=C2=A0
=C2= =A0 I'm also not convinced
that it's a good idea to add a callback which in your case gets the ent= ire
PGconn object.=C2=A0 I'd prefer a check more like the CFI we have in th= e backend
code.

Is there a front end example of t= hat to look at? Or are you describing something more general that does not = exist yet?

Another issue is the interval for checking.=C2=A0 One point of allowing con= figurable
iteration counts was to allow constrained low-power IOT platforms to use SC= RAM
by using a much lower iteration count.=C2=A0 Only checking for timeout ever= y 4096
iterations might be well past the connection timeout.=C2=A0 Perhaps the int= erval of
checking should be a function of the iterations sent by the server?

The time check itself is relatively cheap comp= ared to the hashing so lowering it something more fine grained should not m= atter much. I picked 4096 to match the default scram iterations so it does = not even run in the normal case. Maybe 256?

The SC= RAM RFC estimates it taking .5 seconds to run 4096 iterations on 2015 "= ;current mobile handsets". Would figure it to be even less for devices= 10+ years later. And connect timeout granularity is in seconds.
= =C2=A0
> The attached patch currently uses a default cap of 100K. That is well = above PostgreSQL's normal SCRAM iteration count (4K), but it is still a= client-side behavior change for installations using unusually high SCRAM i= teration counts. For reference, we added a similar patch to pgjdbc with the= same 100K default.

I am off the cuff -1 on adding more connection parameters for niche usecase= s
like this (we already have many enough to make it confusing), it will be ve= ry
hard to document what an appropriate setting is, and getting it wrong might=
mean rejecting connections in err.
=C2=A0
Fo= r additional reference, we added similar parameters to the java driver pgjd= bc and the node driver node-postgres. Both with a 100K limit.
Both also had a bit more of a direct issue with the large value= s (v.s. libpq) as the java driver would create a background thread for the = login (which would get pinned to 100% forever with a huge iteration count) = and the node driver would also hang indefinitely (as node is single threade= d and the crypto functions are not interruptible).

Regards,
-- Sehrope Sarkuni
Founder & CEO= | JackDB, Inc. |=C2=A0https://www.jackdb.com/

=C2=A0
--000000000000fcc90a0652a6a12a--