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 1pUpLF-0005p1-PQ for pgsql-hackers@arkaria.postgresql.org; Wed, 22 Feb 2023 13:39:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1pUpLE-0006C4-DO for pgsql-hackers@arkaria.postgresql.org; Wed, 22 Feb 2023 13:39:28 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pUpLE-0006Bm-1R for pgsql-hackers@lists.postgresql.org; Wed, 22 Feb 2023 13:39:28 +0000 Received: from smtp.outgoing.loopia.se ([93.188.3.37]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pUpLB-0003L4-RR for pgsql-hackers@lists.postgresql.org; Wed, 22 Feb 2023 13:39:27 +0000 Received: from s807.loopia.se (localhost [127.0.0.1]) by s807.loopia.se (Postfix) with ESMTP id 4CD242F908B7 for ; Wed, 22 Feb 2023 14:39:24 +0100 (CET) Received: from s979.loopia.se (unknown [172.22.191.6]) by s807.loopia.se (Postfix) with ESMTP id 3C7412E28711; Wed, 22 Feb 2023 14:39:24 +0100 (CET) Received: from s898.loopia.se (unknown [172.22.191.5]) by s979.loopia.se (Postfix) with ESMTP id 3909810BC485; Wed, 22 Feb 2023 14:39:24 +0100 (CET) X-Virus-Scanned: amavisd-new at amavis.loopia.se X-Spam-Flag: NO X-Spam-Score: -1 X-Spam-Level: X-Spam-Status: No, score=-1 tagged_above=-999 required=6.2 tests=[ALL_TRUSTED=-1] autolearn=disabled Received: from s934.loopia.se ([172.22.191.6]) by s898.loopia.se (s898.loopia.se [172.22.190.17]) (amavisd-new, port 10024) with UTF8LMTP id 4wo7bSKvyB8v; Wed, 22 Feb 2023 14:39:23 +0100 (CET) X-Loopia-Auth: user X-Loopia-User: daniel@yesql.se X-Loopia-Originating-IP: 89.255.232.193 Received: from smtpclient.apple (customer-89-255-232-193.stosn.net [89.255.232.193]) (Authenticated sender: daniel@yesql.se) by s934.loopia.se (Postfix) with ESMTPSA id 7E3B07CEA3A; Wed, 22 Feb 2023 14:39:23 +0100 (CET) From: Daniel Gustafsson Message-Id: <85ACA0F4-82BF-4546-9731-95B09E7D20CE@yesql.se> Content-Type: multipart/mixed; boundary="Apple-Mail=_D70C5AAA-F174-4B43-9315-457BA361B9EC" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3696.120.41.1.2\)) Subject: Re: Raising the SCRAM iteration count Date: Wed, 22 Feb 2023 14:39:23 +0100 In-Reply-To: Cc: "Jonathan S. Katz" , Andres Freund , PostgreSQL Hackers To: Michael Paquier References: <20221210001538.czikyvk3xtkphxfu@awork3.anarazel.de> <4880738a-cbf7-c9a1-4faf-ba861c731c63@postgresql.org> <75619A47-4CD0-4E0F-8A30-32F83FF593DD@yesql.se> <869f3aee-5c46-998d-be42-b3b232415bef@postgresql.org> <1D275A6B-EAE5-4C5E-B223-32C28AB79E36@yesql.se> X-Mailer: Apple Mail (2.3696.120.41.1.2) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_D70C5AAA-F174-4B43-9315-457BA361B9EC Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii > On 17 Dec 2022, at 04:27, Michael Paquier wrote: >=20 > On Thu, Dec 15, 2022 at 12:09:15PM +0100, Daniel Gustafsson wrote: >>> On 15 Dec 2022, at 00:52, Michael Paquier = wrote: >>> conn->in_hot_standby =3D PG_BOOL_UNKNOWN; >>> + conn->scram_iterations =3D SCRAM_DEFAULT_ITERATIONS; >>>=20 >>> s/SCRAM_DEFAULT_ITERATIONS/SCRAM_SHA_256_DEFAULT_ITERATIONS/ and >>> s/scram_iterations/scram_sha_256_interations/ perhaps? =20 >>=20 >> Distinct members in the conn object is only of interest if there is a = way for >> the user to select a different password method in \password right? I = can >> rename it now but I think doing too much here is premature, awaiting = work on >> \password (should that materialize) seems reasonable no? >=20 > You could do that already, somewhat indirectly, with > password_encryption, assuming that it supports more than one mode > whose password build is influenced by it. If you wish to keep it > named this way, this is no big deal for me either way, so feel free to > use what you think is best based on the state of HEAD. I think that > I'd value more the consistency with the backend in terms of naming, > though. ok, renamed. >>> @@ -692,7 +697,7 @@ mock_scram_secret(const char *username, int = *iterations, char **salt, >>> encoded_salt[encoded_len] =3D '\0'; >>>=20 >>> *salt =3D encoded_salt; >>> - *iterations =3D SCRAM_DEFAULT_ITERATIONS; >>> + *iterations =3D scram_sha256_iterations; >>>=20 >>> This looks incorrect to me? The mock authentication is here to >>> produce a realistic verifier, still it will fail. It seems to me = that >>> we'd better stick to the default in all the cases. >>=20 >> For avoiding revealing anything, I think a case can be argued for = both. I've >> reverted back to the default though. >>=20 >> I also renamed the GUC sha_256 to match terminology we use. >=20 > + "SET password_encryption=3D'scram-sha-256'; > + SET scram_sha_256_iterations=3D100000; > Maybe use a lower value to keep the test cheap? Fixed. > + time of encryption. In order to make use of a changed value, = new > + password must be set. > "A new password must be set". Fixed. > Superuser-only GUCs should be documented as such, or do you intend to > make it user-settable like I suggested upthread :) ? I don't really have strong feelings, so I reverted to being = user-settable since I can't really present a strong argument for superuser-only. The attached is a rebase on top of master with no other additional = hacking done on top of the above review comments. -- Daniel Gustafsson --Apple-Mail=_D70C5AAA-F174-4B43-9315-457BA361B9EC Content-Disposition: attachment; filename=v4-0001-Make-SCRAM-iteration-count-configurable.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v4-0001-Make-SCRAM-iteration-count-configurable.patch" Content-Transfer-Encoding: quoted-printable =46rom=20d6afaca4da3cd60f837e27c2f371aceeb196c6e2=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Wed,=2022=20Feb=202023=2014:36:36=20= +0100=0ASubject:=20[PATCH=20v4]=20Make=20SCRAM=20iteration=20count=20= configurable=0A=0AReplace=20the=20hardcoded=20value=20with=20a=20GUC=20= such=20that=20the=20iteration=20count=0Acan=20be=20raised=20in=20order=20= to=20increase=20protection=20against=20brute-force=0Aattacks.=20=20The=20= hardcoded=20value=20for=20SCRAM=20iteration=20count=20was=20defined=0Ato=20= be=204096,=20which=20is=20taken=20from=20RFC=207677,=20so=20set=20the=20= default=20for=20the=0AGUC=20to=204096=20to=20match.=20=20Raising=20the=20= iteration=20count=20will=20make=20stored=0Apasswords=20more=20resilient=20= to=20brute-force=20attacks=20at=20the=20cost=20of=20more=0Acomputation=20= performed=20during=20connection=20establishment.=20Lowering=20the=0A= count=20will=20reduce=20computational=20overhead=20during=20connections=20= at=20the=0Atradeoff=20of=20reducing=20strength=20against=20brute-force=20= attacks.=20When=20the=0Aalternative=20is=20to=20fall=20back=20to=20md5=20= instead,=20SCRAM=20with=20a=20very=20low=0Aiteration=20count=20is=20= still=20preferrable=20so=20allow=20iteration=20counts=20all=0Athe=20way=20= down=20to=20one.=0A=0AReviewed-by:=20Michael=20Paquier=20= =0AReviewed-by:=20Jonathan=20S.=20Katz=20= =0ADiscussion:=20= https://postgr.es/m/F72E7BC7-189F-4B17-BF47-9735EB72C364@yesql.se=0A---=0A= =20doc/src/sgml/config.sgml=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=2020=20+++++++++++++++++++=0A=20= src/backend/libpq/auth-scram.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|=20=209=20+++++++--=0A=20src/backend/utils/misc/guc_tables.c=20=20=20= =20=20=20=20=20=20=20=20|=2013=20++++++++++++=0A=20= src/backend/utils/misc/postgresql.conf.sample=20|=20=201=20+=0A=20= src/common/scram-common.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20|=20=203=20+--=0A=20src/include/common/scram-common.h=20=20= =20=20=20=20=20=20=20=20=20=20=20|=20=202=20+-=0A=20= src/include/libpq/scram.h=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20|=20=203=20+++=0A=20src/interfaces/libpq/fe-auth-scram.c=20= =20=20=20=20=20=20=20=20=20|=20=204=20++--=0A=20= src/interfaces/libpq/fe-auth.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|=20=204=20+++-=0A=20src/interfaces/libpq/fe-auth.h=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20|=20=201=20+=0A=20= src/interfaces/libpq/fe-connect.c=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=202=20++=0A=20src/interfaces/libpq/fe-exec.c=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20|=20=204=20++++=0A=20= src/interfaces/libpq/libpq-int.h=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=201=20+=0A=20src/test/authentication/t/001_password.pl=20=20=20=20=20= |=2016=20+++++++++++++++=0A=20src/test/regress/expected/password.out=20=20= =20=20=20=20=20=20|=20=207=20++++++-=0A=20= src/test/regress/sql/password.sql=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=205=20+++++=0A=2016=20files=20changed,=2086=20insertions(+),=209=20= deletions(-)=0A=0Adiff=20--git=20a/doc/src/sgml/config.sgml=20= b/doc/src/sgml/config.sgml=0Aindex=20e5c41cc6c6..a0eecaaaf0=20100644=0A= ---=20a/doc/src/sgml/config.sgml=0A+++=20b/doc/src/sgml/config.sgml=0A@@=20= -1132,6=20+1132,26=20@@=20include_dir=20'conf.d'=0A=20=20=20=20=20=20=20= =0A=20=20=20=20=20=20=0A=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=20=20= scram_sha_256_iterations=20= (integer)=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =20=20scram_sha_256_iterations=20= configuration=20parameter=0A+=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=20=20The=20number=20of=20= computational=20iterations=20to=20be=20performed=20when=20encrypting=0A+=20= =20=20=20=20=20=20=20a=20password=20using=20SCRAM-SHA-256.=20The=20= default=20is=204096.=0A+=20=20=20=20=20=20=20=20A=20= higher=20number=20of=20iterations=20provides=20additional=20protection=20= against=0A+=20=20=20=20=20=20=20=20brute-force=20attacks=20on=20stored=20= passwords,=20but=20makes=20authentication=0A+=20=20=20=20=20=20=20=20= slower.=20Changing=20the=20value=20has=20no=20effect=20on=20existing=20= passwords=0A+=20=20=20=20=20=20=20=20encrypted=20with=20SCRAM-SHA-256=20= as=20the=20iteration=20count=20is=20fixed=20at=20the=0A+=20=20=20=20=20=20= =20=20time=20of=20encryption.=20In=20order=20to=20make=20use=20of=20a=20= changed=20value,=20a=20new=0A+=20=20=20=20=20=20=20=20password=20must=20= be=20set.=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=0A+=0A=20=20=20=20=20=20= =0A=20=20=20=20=20=20=20= krb_server_keyfile=20(string)=0A=20= =20=20=20=20=20=20=0Adiff=20--git=20= a/src/backend/libpq/auth-scram.c=20b/src/backend/libpq/auth-scram.c=0A= index=204441e0d774..75583dcbf0=20100644=0A---=20= a/src/backend/libpq/auth-scram.c=0A+++=20= b/src/backend/libpq/auth-scram.c=0A@@=20-191,6=20+191,11=20@@=20static=20= char=20*scram_mock_salt(const=20char=20*username,=0A=20=09=09=09=09=09=09= =09=20pg_cryptohash_type=20hash_type,=0A=20=09=09=09=09=09=09=09=20int=20= key_length);=0A=20=0A+/*=0A+=20*=20The=20number=20of=20iterations=20to=20= use=20when=20generating=20new=20secrets.=0A+=20*/=0A+int=09=09=09= scram_sha_256_iterations;=0A+=0A=20/*=0A=20=20*=20Get=20a=20list=20of=20= SASL=20mechanisms=20that=20this=20module=20supports.=0A=20=20*=0A@@=20= -496,7=20+501,7=20@@=20pg_be_scram_build_secret(const=20char=20= *password)=0A=20=0A=20=09result=20=3D=20scram_build_secret(PG_SHA256,=20= SCRAM_SHA_256_KEY_LEN,=0A=20=09=09=09=09=09=09=09=09saltbuf,=20= SCRAM_DEFAULT_SALT_LEN,=0A-=09=09=09=09=09=09=09=09= SCRAM_DEFAULT_ITERATIONS,=20password,=0A+=09=09=09=09=09=09=09=09= scram_sha_256_iterations,=20password,=0A=20=09=09=09=09=09=09=09=09= &errstr);=0A=20=0A=20=09if=20(prep_password)=0A@@=20-717,7=20+722,7=20@@=20= mock_scram_secret(const=20char=20*username,=20pg_cryptohash_type=20= *hash_type,=0A=20=09encoded_salt[encoded_len]=20=3D=20'\0';=0A=20=0A=20=09= *salt=20=3D=20encoded_salt;=0A-=09*iterations=20=3D=20= SCRAM_DEFAULT_ITERATIONS;=0A+=09*iterations=20=3D=20= SCRAM_SHA_256_DEFAULT_ITERATIONS;=0A=20=0A=20=09/*=20StoredKey=20and=20= ServerKey=20are=20not=20used=20in=20a=20doomed=20authentication=20*/=0A=20= =09memset(stored_key,=200,=20SCRAM_MAX_KEY_LEN);=0Adiff=20--git=20= a/src/backend/utils/misc/guc_tables.c=20= b/src/backend/utils/misc/guc_tables.c=0Aindex=201c0583fe26..554f706499=20= 100644=0A---=20a/src/backend/utils/misc/guc_tables.c=0A+++=20= b/src/backend/utils/misc/guc_tables.c=0A@@=20-41,9=20+41,11=20@@=0A=20= #include=20"commands/trigger.h"=0A=20#include=20"commands/user.h"=0A=20= #include=20"commands/vacuum.h"=0A+#include=20"common/scram-common.h"=0A=20= #include=20"jit/jit.h"=0A=20#include=20"libpq/auth.h"=0A=20#include=20= "libpq/libpq.h"=0A+#include=20"libpq/scram.h"=0A=20#include=20= "nodes/queryjumble.h"=0A=20#include=20"optimizer/cost.h"=0A=20#include=20= "optimizer/geqo.h"=0A@@=20-3468,6=20+3470,17=20@@=20struct=20config_int=20= ConfigureNamesInt[]=20=3D=0A=20=09=09NULL,=20NULL,=20NULL=0A=20=09},=0A=20= =0A+=09{=0A+=09=09{"scram_sha_256_iterations",=20PGC_USERSET,=20= CONN_AUTH_AUTH,=0A+=09=09=09gettext_noop("Sets=20the=20iteration=20count=20= for=20SCRAM=20secret=20generation."),=0A+=09=09=09NULL,=0A+=09=09=09= GUC_REPORT=0A+=09=09},=0A+=09=09&scram_sha_256_iterations,=0A+=09=09= SCRAM_SHA_256_DEFAULT_ITERATIONS,=201,=20INT_MAX,=0A+=09=09NULL,=20NULL,=20= NULL=0A+=09},=0A+=0A=20=09/*=20End-of-list=20marker=20*/=0A=20=09{=0A=20=09= =09{NULL,=200,=200,=20NULL,=20NULL},=20NULL,=200,=200,=200,=20NULL,=20= NULL,=20NULL=0Adiff=20--git=20= a/src/backend/utils/misc/postgresql.conf.sample=20= b/src/backend/utils/misc/postgresql.conf.sample=0Aindex=20= d06074b86f..9e901fc112=20100644=0A---=20= a/src/backend/utils/misc/postgresql.conf.sample=0A+++=20= b/src/backend/utils/misc/postgresql.conf.sample=0A@@=20-95,6=20+95,7=20= @@=0A=20=0A=20#authentication_timeout=20=3D=201min=09=09#=201s-600s=0A=20= #password_encryption=20=3D=20scram-sha-256=09#=20scram-sha-256=20or=20= md5=0A+#scram_sha_256_iterations=20=3D=204096=0A=20#db_user_namespace=20= =3D=20off=0A=20=0A=20#=20GSSAPI=20using=20Kerberos=0Adiff=20--git=20= a/src/common/scram-common.c=20b/src/common/scram-common.c=0Aindex=20= bd40d497a9..ef997ef684=20100644=0A---=20a/src/common/scram-common.c=0A= +++=20b/src/common/scram-common.c=0A@@=20-214,8=20+214,7=20@@=20= scram_build_secret(pg_cryptohash_type=20hash_type,=20int=20key_length,=0A= =20=09/*=20Only=20this=20hash=20method=20is=20supported=20currently=20*/=0A= =20=09Assert(hash_type=20=3D=3D=20PG_SHA256);=0A=20=0A-=09if=20= (iterations=20<=3D=200)=0A-=09=09iterations=20=3D=20= SCRAM_DEFAULT_ITERATIONS;=0A+=09Assert(iterations=20>=200);=0A=20=0A=20=09= /*=20Calculate=20StoredKey=20and=20ServerKey=20*/=0A=20=09if=20= (scram_SaltedPassword(password,=20hash_type,=20key_length,=0Adiff=20= --git=20a/src/include/common/scram-common.h=20= b/src/include/common/scram-common.h=0Aindex=200c75df5559..5ccff96ece=20= 100644=0A---=20a/src/include/common/scram-common.h=0A+++=20= b/src/include/common/scram-common.h=0A@@=20-47,7=20+47,7=20@@=0A=20=20*=20= Default=20number=20of=20iterations=20when=20generating=20secret.=20=20= Should=20be=20at=20least=0A=20=20*=204096=20per=20RFC=207677.=0A=20=20*/=0A= -#define=20SCRAM_DEFAULT_ITERATIONS=094096=0A+#define=20= SCRAM_SHA_256_DEFAULT_ITERATIONS=094096=0A=20=0A=20extern=20int=09= scram_SaltedPassword(const=20char=20*password,=0A=20=09=09=09=09=09=09=09= =09=20pg_cryptohash_type=20hash_type,=20int=20key_length,=0Adiff=20--git=20= a/src/include/libpq/scram.h=20b/src/include/libpq/scram.h=0Aindex=20= b275e1e87e..310bc36517=20100644=0A---=20a/src/include/libpq/scram.h=0A= +++=20b/src/include/libpq/scram.h=0A@@=20-18,6=20+18,9=20@@=0A=20= #include=20"libpq/libpq-be.h"=0A=20#include=20"libpq/sasl.h"=0A=20=0A+/*=20= Number=20of=20iterations=20when=20generating=20new=20secrets=20*/=0A= +extern=20PGDLLIMPORT=20int=20scram_sha_256_iterations;=0A+=0A=20/*=20= SASL=20implementation=20callbacks=20*/=0A=20extern=20PGDLLIMPORT=20const=20= pg_be_sasl_mech=20pg_be_scram_mech;=0A=20=0Adiff=20--git=20= a/src/interfaces/libpq/fe-auth-scram.c=20= b/src/interfaces/libpq/fe-auth-scram.c=0Aindex=209c42ea4f81..7162fdbb85=20= 100644=0A---=20a/src/interfaces/libpq/fe-auth-scram.c=0A+++=20= b/src/interfaces/libpq/fe-auth-scram.c=0A@@=20-894,7=20+894,7=20@@=20= verify_server_signature(fe_scram_state=20*state,=20bool=20*match,=0A=20=20= *=20error=20details.=0A=20=20*/=0A=20char=20*=0A= -pg_fe_scram_build_secret(const=20char=20*password,=20const=20char=20= **errstr)=0A+pg_fe_scram_build_secret(const=20char=20*password,=20int=20= iterations,=20const=20char=20**errstr)=0A=20{=0A=20=09char=09=20=20=20= *prep_password;=0A=20=09pg_saslprep_rc=20rc;=0A@@=20-926,7=20+926,7=20@@=20= pg_fe_scram_build_secret(const=20char=20*password,=20const=20char=20= **errstr)=0A=20=0A=20=09result=20=3D=20scram_build_secret(PG_SHA256,=20= SCRAM_SHA_256_KEY_LEN,=20saltbuf,=0A=20=09=09=09=09=09=09=09=09= SCRAM_DEFAULT_SALT_LEN,=0A-=09=09=09=09=09=09=09=09= SCRAM_DEFAULT_ITERATIONS,=20password,=0A+=09=09=09=09=09=09=09=09= iterations,=20password,=0A=20=09=09=09=09=09=09=09=09errstr);=0A=20=0A=20= =09free(prep_password);=0Adiff=20--git=20= a/src/interfaces/libpq/fe-auth.c=20b/src/interfaces/libpq/fe-auth.c=0A= index=209afc6f19b9..0935356075=20100644=0A---=20= a/src/interfaces/libpq/fe-auth.c=0A+++=20= b/src/interfaces/libpq/fe-auth.c=0A@@=20-1253,7=20+1253,9=20@@=20= PQencryptPasswordConn(PGconn=20*conn,=20const=20char=20*passwd,=20const=20= char=20*user,=0A=20=09{=0A=20=09=09const=20char=20*errstr=20=3D=20NULL;=0A= =20=0A-=09=09crypt_pwd=20=3D=20pg_fe_scram_build_secret(passwd,=20= &errstr);=0A+=09=09crypt_pwd=20=3D=20pg_fe_scram_build_secret(passwd,=0A= +=09=09=09=09=09=09=09=09=09=09=09=20conn->scram_sha_256_iterations,=0A+=09= =09=09=09=09=09=09=09=09=09=09=20&errstr);=0A=20=09=09if=20(!crypt_pwd)=0A= =20=09=09=09libpq_append_conn_error(conn,=20"could=20not=20encrypt=20= password:=20%s",=20errstr);=0A=20=09}=0Adiff=20--git=20= a/src/interfaces/libpq/fe-auth.h=20b/src/interfaces/libpq/fe-auth.h=0A= index=201aa556ea2f..124dd5d031=20100644=0A---=20= a/src/interfaces/libpq/fe-auth.h=0A+++=20= b/src/interfaces/libpq/fe-auth.h=0A@@=20-26,6=20+26,7=20@@=20extern=20= char=20*pg_fe_getauthname(PQExpBuffer=20errorMessage);=0A=20/*=20= Mechanisms=20in=20fe-auth-scram.c=20*/=0A=20extern=20const=20= pg_fe_sasl_mech=20pg_scram_mech;=0A=20extern=20char=20= *pg_fe_scram_build_secret(const=20char=20*password,=0A+=09=09=09=09=09=09= =09=09=09=20=20int=20iterations,=0A=20=09=09=09=09=09=09=09=09=09=20=20= const=20char=20**errstr);=0A=20=0A=20#endif=09=09=09=09=09=09=09/*=20= FE_AUTH_H=20*/=0Adiff=20--git=20a/src/interfaces/libpq/fe-connect.c=20= b/src/interfaces/libpq/fe-connect.c=0Aindex=2050b5df3490..3bf9b08e36=20= 100644=0A---=20a/src/interfaces/libpq/fe-connect.c=0A+++=20= b/src/interfaces/libpq/fe-connect.c=0A@@=20-586,6=20+586,7=20@@=20= pqDropServerData(PGconn=20*conn)=0A=20=09conn->std_strings=20=3D=20= false;=0A=20=09conn->default_transaction_read_only=20=3D=20= PG_BOOL_UNKNOWN;=0A=20=09conn->in_hot_standby=20=3D=20PG_BOOL_UNKNOWN;=0A= +=09conn->scram_sha_256_iterations=20=3D=20= SCRAM_SHA_256_DEFAULT_ITERATIONS;=0A=20=09conn->sversion=20=3D=200;=0A=20= =0A=20=09/*=20Drop=20large-object=20lookup=20data=20*/=0A@@=20-3909,6=20= +3910,7=20@@=20makeEmptyPGconn(void)=0A=20=09conn->std_strings=20=3D=20= false;=09/*=20unless=20server=20says=20differently=20*/=0A=20=09= conn->default_transaction_read_only=20=3D=20PG_BOOL_UNKNOWN;=0A=20=09= conn->in_hot_standby=20=3D=20PG_BOOL_UNKNOWN;=0A+=09= conn->scram_sha_256_iterations=20=3D=20SCRAM_SHA_256_DEFAULT_ITERATIONS;=0A= =20=09conn->verbosity=20=3D=20PQERRORS_DEFAULT;=0A=20=09= conn->show_context=20=3D=20PQSHOW_CONTEXT_ERRORS;=0A=20=09conn->sock=20=3D= =20PGINVALID_SOCKET;=0Adiff=20--git=20a/src/interfaces/libpq/fe-exec.c=20= b/src/interfaces/libpq/fe-exec.c=0Aindex=20ec62550e38..8607bc062e=20= 100644=0A---=20a/src/interfaces/libpq/fe-exec.c=0A+++=20= b/src/interfaces/libpq/fe-exec.c=0A@@=20-1181,6=20+1181,10=20@@=20= pqSaveParameterStatus(PGconn=20*conn,=20const=20char=20*name,=20const=20= char=20*value)=0A=20=09=09conn->in_hot_standby=20=3D=0A=20=09=09=09= (strcmp(value,=20"on")=20=3D=3D=200)=20?=20PG_BOOL_YES=20:=20PG_BOOL_NO;=0A= =20=09}=0A+=09else=20if=20(strcmp(name,=20"scram_sha_256_iterations")=20= =3D=3D=200)=0A+=09{=0A+=09=09conn->scram_sha_256_iterations=20=3D=20= atoi(value);=0A+=09}=0A=20}=0A=20=0A=20=0Adiff=20--git=20= a/src/interfaces/libpq/libpq-int.h=20b/src/interfaces/libpq/libpq-int.h=0A= index=20d7ec5ed429..940d1a35a1=20100644=0A---=20= a/src/interfaces/libpq/libpq-int.h=0A+++=20= b/src/interfaces/libpq/libpq-int.h=0A@@=20-515,6=20+515,7=20@@=20struct=20= pg_conn=0A=20=09/*=20Assorted=20state=20for=20SASL,=20SSL,=20GSS,=20etc=20= */=0A=20=09const=20pg_fe_sasl_mech=20*sasl;=0A=20=09void=09=20=20=20= *sasl_state;=0A+=09int=09=09=09scram_sha_256_iterations;=0A=20=0A=20=09= /*=20SSL=20structures=20*/=0A=20=09bool=09=09ssl_in_use;=0Adiff=20--git=20= a/src/test/authentication/t/001_password.pl=20= b/src/test/authentication/t/001_password.pl=0Aindex=20= a2fde1408b..e0ab39ef52=20100644=0A---=20= a/src/test/authentication/t/001_password.pl=0A+++=20= b/src/test/authentication/t/001_password.pl=0A@@=20-86,6=20+86,14=20@@=20= $node->safe_psql('postgres',=0A=20=09q{SET=20password_encryption=3D'md5';=20= CREATE=20ROLE=20"md5,role"=20LOGIN=20PASSWORD=20'pass';}=0A=20);=0A=20=0A= +#=20Create=20a=20role=20with=20a=20non-default=20iteration=20count=0A= +$node->safe_psql(=0A+=09'postgres',=0A+=09"SET=20= password_encryption=3D'scram-sha-256';=0A+=09=20SET=20= scram_sha_256_iterations=3D10000;=0A+=09=20CREATE=20ROLE=20= scram_role_iter=20LOGIN=20PASSWORD=20'pass';"=0A+);=0A+=0A=20#=20Create=20= a=20database=20to=20test=20regular=20expression.=0A=20= $node->safe_psql('postgres',=20"CREATE=20database=20regex_testdb;");=0A=20= =0A@@=20-134,6=20+142,14=20@@=20test_conn(=0A=20=09log_like=20=3D>=20[=0A= =20=09=09qr/connection=20authenticated:=20identity=3D"scram_role"=20= method=3Dscram-sha-256/=0A=20=09]);=0A+test_conn(=0A+=09$node,=0A+=09= 'user=3Dscram_role_iter',=0A+=09'scram-sha-256',=0A+=090,=0A+=09log_like=20= =3D>=20[=0A+=09=09qr/connection=20authenticated:=20= identity=3D"scram_role_iter"=20method=3Dscram-sha-256/=0A+=09]);=0A=20= test_conn($node,=20'user=3Dmd5_role',=20'scram-sha-256',=202,=0A=20=09= log_unlike=20=3D>=20[qr/connection=20authenticated:/]);=0A=20=0Adiff=20= --git=20a/src/test/regress/expected/password.out=20= b/src/test/regress/expected/password.out=0Aindex=20= 7c84c9da33..53db07d1d5=20100644=0A---=20= a/src/test/regress/expected/password.out=0A+++=20= b/src/test/regress/expected/password.out=0A@@=20-72,6=20+72,9=20@@=20= CREATE=20ROLE=20regress_passwd6=20PASSWORD=20'SCRAM-SHA-256$1234';=0A=20= CREATE=20ROLE=20regress_passwd7=20PASSWORD=20= 'md5012345678901234567890123456789zz';=0A=20--=20invalid=20length=0A=20= CREATE=20ROLE=20regress_passwd8=20PASSWORD=20= 'md501234567890123456789012345678901zz';=0A+--=20Increasing=20the=20= SCRAM=20iteration=20count=0A+SET=20scram_sha_256_iterations=20=3D=20= 9999;=0A+CREATE=20ROLE=20regress_passwd9=20PASSWORD=20= 'raisediterationcount';=0A=20SELECT=20rolname,=20= regexp_replace(rolpassword,=20= '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=3D]+)\$([a-zA-Z0-9+=3D/]+):([a-zA-Z0= -9+/=3D]+)',=20'\1$\2:$:')=20as=20= rolpassword_masked=0A=20=20=20=20=20FROM=20pg_authid=0A=20=20=20=20=20= WHERE=20rolname=20LIKE=20'regress_passwd%'=0A@@=20-86,7=20+89,8=20@@=20= SELECT=20rolname,=20regexp_replace(rolpassword,=20= '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+=0A=20=20regress_passwd6=20|=20= SCRAM-SHA-256$4096:$:=0A=20=20= regress_passwd7=20|=20SCRAM-SHA-256$4096:$:=0A= =20=20regress_passwd8=20|=20= SCRAM-SHA-256$4096:$:=0A-(8=20rows)=0A+=20= regress_passwd9=20|=20SCRAM-SHA-256$9999:$:=0A= +(9=20rows)=0A=20=0A=20--=20An=20empty=20password=20is=20not=20allowed,=20= in=20any=20form=0A=20CREATE=20ROLE=20regress_passwd_empty=20PASSWORD=20= '';=0A@@=20-129,6=20+133,7=20@@=20DROP=20ROLE=20regress_passwd5;=0A=20= DROP=20ROLE=20regress_passwd6;=0A=20DROP=20ROLE=20regress_passwd7;=0A=20= DROP=20ROLE=20regress_passwd8;=0A+DROP=20ROLE=20regress_passwd9;=0A=20= DROP=20ROLE=20regress_passwd_empty;=0A=20DROP=20ROLE=20= regress_passwd_sha_len0;=0A=20DROP=20ROLE=20regress_passwd_sha_len1;=0A= diff=20--git=20a/src/test/regress/sql/password.sql=20= b/src/test/regress/sql/password.sql=0Aindex=2098f49916e5..59ef7de21e=20= 100644=0A---=20a/src/test/regress/sql/password.sql=0A+++=20= b/src/test/regress/sql/password.sql=0A@@=20-63,6=20+63,10=20@@=20CREATE=20= ROLE=20regress_passwd7=20PASSWORD=20= 'md5012345678901234567890123456789zz';=0A=20--=20invalid=20length=0A=20= CREATE=20ROLE=20regress_passwd8=20PASSWORD=20= 'md501234567890123456789012345678901zz';=0A=20=0A+--=20Increasing=20the=20= SCRAM=20iteration=20count=0A+SET=20scram_sha_256_iterations=20=3D=20= 9999;=0A+CREATE=20ROLE=20regress_passwd9=20PASSWORD=20= 'raisediterationcount';=0A+=0A=20SELECT=20rolname,=20= regexp_replace(rolpassword,=20= '(SCRAM-SHA-256)\$(\d+):([a-zA-Z0-9+/=3D]+)\$([a-zA-Z0-9+=3D/]+):([a-zA-Z0= -9+/=3D]+)',=20'\1$\2:$:')=20as=20= rolpassword_masked=0A=20=20=20=20=20FROM=20pg_authid=0A=20=20=20=20=20= WHERE=20rolname=20LIKE=20'regress_passwd%'=0A@@=20-97,6=20+101,7=20@@=20= DROP=20ROLE=20regress_passwd5;=0A=20DROP=20ROLE=20regress_passwd6;=0A=20= DROP=20ROLE=20regress_passwd7;=0A=20DROP=20ROLE=20regress_passwd8;=0A= +DROP=20ROLE=20regress_passwd9;=0A=20DROP=20ROLE=20regress_passwd_empty;=0A= =20DROP=20ROLE=20regress_passwd_sha_len0;=0A=20DROP=20ROLE=20= regress_passwd_sha_len1;=0A--=20=0A2.32.1=20(Apple=20Git-133)=0A=0A= --Apple-Mail=_D70C5AAA-F174-4B43-9315-457BA361B9EC Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii --Apple-Mail=_D70C5AAA-F174-4B43-9315-457BA361B9EC--