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.94.2) (envelope-from ) id 1tne66-0077T6-NS for pgsql-hackers@arkaria.postgresql.org; Thu, 27 Feb 2025 13:38:44 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tne64-00ErEH-6C for pgsql-hackers@arkaria.postgresql.org; Thu, 27 Feb 2025 13:38:40 +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.94.2) (envelope-from ) id 1tne63-00ErE8-KD for pgsql-hackers@lists.postgresql.org; Thu, 27 Feb 2025 13:38:39 +0000 Received: from smtp.outgoing.loopia.se ([93.188.3.37]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tne60-000ONs-1r for pgsql-hackers@lists.postgresql.org; Thu, 27 Feb 2025 13:38:39 +0000 Received: from s807.loopia.se (localhost [127.0.0.1]) by s807.loopia.se (Postfix) with ESMTP id 1C7B5293027 for ; Thu, 27 Feb 2025 14:38:36 +0100 (CET) Received: from s981.loopia.se (unknown [172.22.191.5]) by s807.loopia.se (Postfix) with ESMTP id 0677F292069; Thu, 27 Feb 2025 14:38:36 +0100 (CET) Received: from s898.loopia.se (unknown [172.22.191.6]) by s981.loopia.se (Postfix) with ESMTP id 0452922B1770; Thu, 27 Feb 2025 14:38:36 +0100 (CET) X-Virus-Scanned: amavisd-new at amavis.loopia.se X-Spam-Flag: NO X-Spam-Score: -1.2 X-Spam-Level: X-Spam-Status: No, score=-1.2 tagged_above=-999 required=6.2 tests=[ALL_TRUSTED=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1] autolearn=disabled Authentication-Results: s898.loopia.se (amavisd-new); dkim=pass (2048-bit key) header.d=yesql.se Received: from s899.loopia.se ([172.22.191.6]) by s898.loopia.se (s898.loopia.se [172.22.190.17]) (amavisd-new, port 10024) with LMTP id FO96BJ5WPm08; Thu, 27 Feb 2025 14:38:35 +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 s899.loopia.se (Postfix) with ESMTPSA id 3FA5D2C8BA63; Thu, 27 Feb 2025 14:38:35 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yesql.se; s=loopiadkim1707475645; t=1740663515; bh=HfqZYFOiM20Q/4ON3mTtOSE7Z6ZlrAlVKTnXMitKV4I=; h=From:Subject:Date:In-Reply-To:Cc:To:References; b=gH840PF21nEt95tjCM63Tk9dfKjY7/QJAQrQzP3LAD7zu223l5/bghpKLwHqBo82H mnYS4xtiz8kzmE8E2wqy7BL8KvRzORYfmcy8w1bcZpKCVHEY30JP+bwWppnd7zhAIJ Apx2F0eegXj6K/o8M8AwVgoREUgwJwvNmztDVtWWhSrPjR2y58qPVviUU5fL5lOqWe Enxq/O/SUbvVB1/n0RcfumMxR1LO/SRTqlKmXfyEVr8QRLEFBDA2TF/8PgAGvnVwDx pUQWUgGAPBWtrIxoEXyk4tugsjyGWvnABxlsh1r+sKbHFZlUuaTK96qY19/GBw5Uwz 9AeS7BjD32XLA== From: Daniel Gustafsson Message-Id: <0BC5B9B1-6503-4563-AAC6-33DEF264AE3F@yesql.se> Content-Type: multipart/mixed; boundary="Apple-Mail=_00218A96-8338-41F4-A13A-9EB3F2339350" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51.11.1\)) Subject: Re: Serverside SNI support in libpq Date: Thu, 27 Feb 2025 14:38:24 +0100 In-Reply-To: Cc: Michael Paquier , Pgsql Hackers To: Jacob Champion References: <1C81CD0D-407E-44F9-833A-DD0331C202E5@yesql.se> <88986722-5A72-4DEC-8750-BDBF67FF8C01@yesql.se> <7E77028B-5A3A-436B-9046-8E9992E9F94A@yesql.se> X-Mailer: Apple Mail (2.3776.700.51.11.1) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_00218A96-8338-41F4-A13A-9EB3F2339350 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On 24 Feb 2025, at 22:51, Jacob Champion = wrote: >=20 > On Wed, Feb 19, 2025 at 3:13=E2=80=AFPM Daniel Gustafsson = wrote: >> Are there any blockers for getting this in? >=20 >> + SSL_context =3D ssl_init_context(isServerStart, host); >=20 > I'm still not quite following the rationale behind the SSL_context > assignment. To maybe illustrate, attached are some tests that I > expected to pass, but don't. >=20 > After adding an additional host and reloading the config, the behavior > of the original fallback host seems to change. Am I misunderstanding > the designed fallback behavior, have I misdesigned my test, or is this > a bug? Thanks for the tests, they did in fact uncover a bug in how fallback was handled which is now fixed. In doing so I revamped how the default = context handling is done, it now always use the GUCs in postgresql.conf for consistency. The attached v6 rebase contains this as well as your tests = as well as general cleanup and comment writing etc. -- Daniel Gustafsson --Apple-Mail=_00218A96-8338-41F4-A13A-9EB3F2339350 Content-Disposition: attachment; filename=v6-0001-Serverside-SNI-support-for-libpq.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v6-0001-Serverside-SNI-support-for-libpq.patch" Content-Transfer-Encoding: quoted-printable =46rom=20ee5508fa4a2b114a6493d60d923b6586250713c5=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Thu,=2027=20Feb=202025=2014:03:31=20= +0100=0ASubject:=20[PATCH=20v6]=20Serverside=20SNI=20support=20for=20= libpq=0A=0AExperimental=20support=20for=20serverside=20SNI=20support=20= in=20libpq,=20a=20new=20config=0Afile=20$datadir/pg_hosts.conf=20is=20= used=20for=20configuring=20which=20certicate=20and=0Akey=20should=20be=20= used=20for=20which=20hostname.=20A=20new=20GUC,=20ssl_snimode,=20is=20= added=0Awhich=20controls=20how=20the=20hostname=20TLS=20extension=20is=20= handled.=20The=20possible=0Avalues=20are=20off,=20default=20and=20= strict:=0A=0A=20=20-=20off:=20pg_hosts.conf=20is=20not=20parsed=20and=20= the=20hostname=20TLS=20extension=20is=0A=20=20=20=20not=20inspected=20at=20= all.=20The=20normal=20SSL=20GUCs=20for=20certificates=20and=20keys=0A=09= are=20used.=0A=20=20-=20default:=20pg_hosts.conf=20is=20loaded=20as=20= well=20as=20the=20normal=20GUCs.=20If=20no=0A=20=20=20=20match=20for=20= the=20TLS=20extension=20hostname=20is=20found=20in=20pg_hosts=20the=20= cert=0A=09and=20key=20from=20the=20postgresql.conf=20GUCs=20is=20used=20= as=20the=20default=20(used=0A=09as=20a=20wildcard=20host).=0A=20=20-=20= strict:=20only=20pg_hosts.conf=20is=20loaded=20and=20the=20TLS=20= extension=20hostname=0A=20=20=20=20MUST=20be=20passed=20and=20MUST=20= have=20a=20match=20in=20the=20configuration,=20else=20the=0A=09= connection=20is=20refused.=0A=0ACRL=20file(s)=20are=20applied=20from=20= postgresql.conf=20to=20all=20configured=20hostnames.=0A=0AReviewed-by:=20= Cary=20Huang=20=0AReviewed-by:=20Jacob=20Champion=20= =0ADiscussion:=20= https://postgr.es/m/1C81CD0D-407E-44F9-833A-DD0331C202E5@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|=20=2066=20++++=0A=20doc/src/sgml/runtime.sgml=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=2067=20++++=0A= =20src/backend/Makefile=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20|=20=20=201=20+=0A=20= src/backend/libpq/be-secure-common.c=20=20=20=20=20=20=20=20=20=20|=20= 203=20+++++++++-=0A=20src/backend/libpq/be-secure-openssl.c=20=20=20=20=20= =20=20=20=20|=20356=20++++++++++++++++--=0A=20= src/backend/libpq/be-secure.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20|=20=20=208=20+-=0A=20src/backend/libpq/meson.build=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20|=20=20=201=20+=0A=20= src/backend/libpq/pg_hosts.conf.sample=20=20=20=20=20=20=20=20|=20=20=20= 4=20+=0A=20src/backend/utils/misc/guc.c=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=2026=20++=0A=20= src/backend/utils/misc/guc_tables.c=20=20=20=20=20=20=20=20=20=20=20|=20=20= 31=20++=0A=20src/backend/utils/misc/postgresql.conf.sample=20|=20=20=203=20= +=0A=20src/bin/initdb/initdb.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20|=20=2016=20+-=0A=20src/include/libpq/hba.h=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=2019=20= +=0A=20src/include/libpq/libpq-be.h=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/libpq/libpq.h=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=2011=20+-=0A=20= src/include/utils/guc.h=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=20=201=20+=0A=20.../ssl_passphrase_func.c=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=204=20+-=0A= =20src/test/ssl/meson.build=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=20=201=20+=0A=20src/test/ssl/t/004_sni.pl=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20164=20++++++++=0A= =20src/tools/pgindent/typedefs.list=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|=20=20=202=20+=0A=2020=20files=20changed,=20937=20insertions(+),=20= 50=20deletions(-)=0A=20create=20mode=20100644=20= src/backend/libpq/pg_hosts.conf.sample=0A=20create=20mode=20100644=20= src/test/ssl/t/004_sni.pl=0A=0Adiff=20--git=20a/doc/src/sgml/config.sgml=20= b/doc/src/sgml/config.sgml=0Aindex=20e55700f35b8..61f3178df82=20100644=0A= ---=20a/doc/src/sgml/config.sgml=0A+++=20b/doc/src/sgml/config.sgml=0A@@=20= -1678,6=20+1678,72=20@@=20include_dir=20'conf.d'=0A=20=20=20=20=20=20=20=20= =0A=20=20=20=20=20=20=20=0A=20=20=20=20=20=20= =0A+=0A+=20=20=20=20=20=0A+=20=20=20=20=20=20= ssl_snimode=20(enum)=0A+=20=20=20=20= =20=20=0A+=20=20=20=20=20=20=20= ssl_snimode=20configuration=20= parameter=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=20This=20parameter=20determines=20if=20the=20= server=20will=20inspect=20the=20SNI=20TLS=20extension=0A= +=20=20=20=20=20=20=20=20when=20establishing=20the=20connection,=20and=20= how=20it=20should=20be=20interpreted.=0A+=20=20=20=20=20=20=20=20Valid=20= values=20are=20currently:=20off,=20= default=20and=20strict.=0A+=20=20=20= =20=20=20=20=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =20=0A+=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20=20= off=0A+=20=20=20=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20=20=20=20SNI=20is=20not=20enabled=20and=20no=20configuration=20= from=0A+=20=20=20=20=20=20=20=20=20=20=20=20= pg_hosts.conf=20is=20loaded.=20=20Configuration=20= of=20SSL=0A+=20=20=20=20=20=20=20=20=20=20=20=20for=20all=20connections=20= is=20done=20with=20,=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20and=20= .=0A+=20=20=20=20=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20=0A+=0A+=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20=20= default=0A+=20=20=20=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20=20=20=20SNI=20is=20enabled=20and=20hostname=20configuration=20= is=20loaded=20from=0A+=20=20=20=20=20=20=20=20=20=20=20=20= pg_hosts.conf.=20,=0A+=20=20=20=20=20=20=20=20=20=20=20=20= =20and=20=0A+=20=20=20=20=20=20=20=20=20=20=20=20are=20= loaded=20as=20the=20default=20configuration.=20=20Connections=20= specifying=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20to=201=0A+=20=20=20= =20=20=20=20=20=20=20=20=20will=20be=20attempted=20using=20the=20default=20= configuration=20if=20the=20hostname=0A+=20=20=20=20=20=20=20=20=20=20=20=20= is=20missing=20in=20pg_hosts.conf.=20=20If=20the=20= hostname=0A+=20=20=20=20=20=20=20=20=20=20=20=20matches=20an=20entry=20= from=20pg_hosts.conf,=20then=20the=0A+=20=20=20=20=20= =20=20=20=20=20=20=20configuration=20from=20that=20entry=20will=20be=20= used=20for=20setting=20up=20the=0A+=20=20=20=20=20=20=20=20=20=20=20=20= connection.=0A+=20=20=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20= =20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20=0A= +=0A+=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20=20= strict=0A+=20=20=20=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20=20=20=20SNI=20is=20enabled=20and=20all=20connections=20are=20= required=20to=20set=20=20to=201=20and=0A+=20= =20=20=20=20=20=20=20=20=20=20=20specify=20a=20hostname=20matching=20an=20= entry=20in=0A+=20=20=20=20=20=20=20=20=20=20=20=20= pg_hosts.conf.=20Any=20connection=20without=20=20= or=20with=20a=20hostname=20missing=20from=0A+=20=20=20=20=20=20=20=20=20=20= =20=20pg_hosts.conf=20will=20be=20rejected.=0A+=20=20= =20=20=20=20=20=20=20=20=20=20,=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20and=20=0A+=20=20=20=20=20=20=20=20=20=20=20=20are=20= loaded=20in=20order=20to=20drive=20the=20handshake=20until=20the=20= appropriate=0A+=20=20=20=20=20=20=20=20=20=20=20=20configuration=20has=20= been=20selected.=0A+=20=20=20=20=20=20=20=20=20=20=20=0A+=20=20=20= =20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A=20=20=20=20=20=0A=20=20=20=20=20= =0A=20=20=20=20=0Adiff=20--git=20= a/doc/src/sgml/runtime.sgml=20b/doc/src/sgml/runtime.sgml=0Aindex=20= 59f39e89924..1e8f06ba2ce=20100644=0A---=20a/doc/src/sgml/runtime.sgml=0A= +++=20b/doc/src/sgml/runtime.sgml=0A@@=20-2444,6=20+2444,12=20@@=20= pg_dumpall=20-p=205432=20|=20psql=20-d=20postgres=20-p=205433=0A=20=20=20= =20=20=20=20client=20certificate=20must=20not=20be=20on=20this=20= list=0A=20=20=20=20=20=20=0A=20=0A+=20=20=20=20=20=0A= +=20=20=20=20=20=20= $PGDATA/pg_hosts.conf=0A+=20=20=20=20= =20=20SNI=20configuration=0A+=20=20=20=20=20=20= defines=20which=20certificates=20to=20use=20for=20which=20server=20= hostname=0A+=20=20=20=20=20=0A+=0A=20=20=20=20=20=0A= =20=20=20=20=0A=20=20=20=0A@@=20-2571,6=20+2577,67=20@@=20= openssl=20x509=20-req=20-in=20server.csr=20-text=20-days=20365=20\=0A=20=20= =20=20=0A=20=20=20=0A=20=0A+=20=20=0A= +=20=20=20SNI=20Configuration=0A+=0A+=20=20=20=0A+=20= =20=20=20PostgreSQL=20can=20be=20configured=20= for=0A+=20=20=20=20SNI=20using=20the=20= pg_hosts.conf=0A+=20=20=20=20configuration=20file.=20= PostgreSQL=20inspects=20the=20TLS=0A+=20=20=20= =20hostname=20extension=20in=20the=20SSL=20connection=20handshake,=20and=20= selects=20the=20right=0A+=20=20=20=20TLS=20certificate,=20key=20and=20CA=20= certificate=20to=20use=20for=20the=20connection.=0A+=20=20=20=0A+=0A= +=20=20=20=0A+=20=20=20=20SNI=20configuration=20is=20defined=20in=20= the=20hosts=20configuration=20file,=0A+=20=20=20=20= pg_hosts.conf,=20which=20is=20stored=20in=20the=20= clusters=0A+=20=20=20=20data=20directory.=20=20The=20hosts=20= configuration=20file=20contains=20lines=20of=20the=20general=0A+=20=20=20= =20forms:=0A+=0A+hostname=20= SSL_certificate=20= SSL_key=20= SSL_CA_certificate=20= SSL_passphrase_cmd=20= SSL_passphrase_cmd_reload=0A= +include=20file=0A= +include_if_exists=20= file=0A= +include_dir=20= directory=0A+=0A+=20=20=20=20= Comments,=20whitespace=20and=20line=20continuations=20are=20handled=20in=20= the=20same=20way=20as=0A+=20=20=20=20in=20= pg_hba.conf.=20=20= hostname=0A+=20=20=20=20is=20matched=20= against=20the=20hostname=20TLS=20extension=20in=20the=20SSL=20handshake.=0A= +=20=20=20=20SSL_certificate,=0A+=20=20=20=20= SSL_key,=0A+=20=20=20=20= SSL_CA_certificate,=0A+=20=20=20=20= SSL_passphrase_cmd,=20and=0A+=20=20=20=20= SSL_passphrase_cmd_reload=0A+=20=20=20=20are=20= treated=20like=0A+=20=20=20=20,=0A= +=20=20=20=20,=0A+=20=20=20=20= ,=0A+=20=20=20=20,=20and=0A+=20=20=20=20=20respectively.=0A= +=20=20=20=20All=20fields=20except=20= SSL_passphrase_cmd=20and=0A+=20=20=20=20= SSL_passphrase_cmd_reload=20are=20required.=20= If=0A+=20=20=20=20SSL_passphrase_cmd=20is=20= defined=20but=20not=0A+=20=20=20=20= SSL_passphrase_cmd_reload=20then=20the=20= default=0A+=20=20=20=20value=20for=20= SSL_passphrase_cmd_reload=20is=0A+=20=20=20=20= off.=0A+=20=20=20=0A+=20=20=20=0A+=20=20=20= =20The=20SSL=20configuration=20from=20= postgresql.conf=20is=20used=0A+=20=20=20=20in=20= order=20to=20set=20up=20the=20TLS=20handshake=20such=20that=20the=20= hostname=20extension=20can=0A+=20=20=20=20be=20inspected.=20=20When=20= =20is=20set=20to=0A+=20=20=20=20= default=20this=20configuration=20will=20be=20the=20= defualt=20fallback=0A+=20=20=20=20if=20no=20matching=20hostname=20is=20= found=20in=20pg_hosts.conf.=20=20If=0A+=20=20=20=20= =20is=20set=20to=20= strict=20it=0A+=20=20=20=20will=20only=20be=20used=20= to=20for=20the=20handshake=20until=20the=20hostname=20is=20inspected,=20= it=0A+=20=20=20=20will=20not=20be=20used=20for=20the=20connection.=0A+=20= =20=20=0A+=20=20=20=0A+=20=20=20=20It=20is=20currently=20= not=20possible=20to=20set=20different=20clientname=0A= +=20=20=20=20values=20for=20the=20different=20certificates.=20=20Any=20= clientname=0A+=20=20=20=20setting=20in=20= pg_hba.conf=20will=20be=20applied=20during=0A+=20=20= =20=20authentication=20regardless=20of=20which=20set=20of=20certificates=20= have=20been=20loaded=0A+=20=20=20=20via=20an=20SNI=20enabled=20= connection.=0A+=20=20=20=0A+=20=20=0A=20=20=0A=20=0A= =20=20=0Adiff=20--git=20= a/src/backend/Makefile=20b/src/backend/Makefile=0Aindex=20= 42d4a28e5aa..96adabf9581=20100644=0A---=20a/src/backend/Makefile=0A+++=20= b/src/backend/Makefile=0A@@=20-186,6=20+186,7=20@@=20endif=0A=20=09= $(MAKE)=20-C=20utils=20install-data=0A=20=09$(INSTALL_DATA)=20= $(srcdir)/libpq/pg_hba.conf.sample=20= '$(DESTDIR)$(datadir)/pg_hba.conf.sample'=0A=20=09$(INSTALL_DATA)=20= $(srcdir)/libpq/pg_ident.conf.sample=20= '$(DESTDIR)$(datadir)/pg_ident.conf.sample'=0A+=09$(INSTALL_DATA)=20= $(srcdir)/libpq/pg_hosts.conf.sample=20= '$(DESTDIR)$(datadir)/pg_hosts.conf.sample'=0A=20=09$(INSTALL_DATA)=20= $(srcdir)/utils/misc/postgresql.conf.sample=20= '$(DESTDIR)$(datadir)/postgresql.conf.sample'=0A=20=0A=20ifeq=20= ($(with_llvm),=20yes)=0Adiff=20--git=20= a/src/backend/libpq/be-secure-common.c=20= b/src/backend/libpq/be-secure-common.c=0Aindex=20= e8b837d1fa7..67a50c7b24c=20100644=0A---=20= a/src/backend/libpq/be-secure-common.c=0A+++=20= b/src/backend/libpq/be-secure-common.c=0A@@=20-24,8=20+24,13=20@@=0A=20=0A= =20#include=20"common/percentrepl.h"=0A=20#include=20"common/string.h"=0A= +#include=20"libpq/hba.h"=0A=20#include=20"libpq/libpq.h"=0A=20#include=20= "storage/fd.h"=0A+#include=20"utils/guc.h"=0A+#include=20= "utils/memutils.h"=0A+=0A+static=20HostsLine=20= *parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel);=0A=20=0A= =20/*=0A=20=20*=20Run=20ssl_passphrase_command=0A@@=20-37,19=20+42,20=20= @@=0A=20=20*=20value=20is=20the=20length=20of=20the=20actual=20result.=0A= =20=20*/=0A=20int=0A-run_ssl_passphrase_command(const=20char=20*prompt,=20= bool=20is_server_start,=20char=20*buf,=20int=20size)=0A= +run_ssl_passphrase_command(const=20char=20*prompt,=20bool=20= is_server_start,=20char=20*buf,=20int=20size,=20void=20*userdata)=0A=20{=0A= =20=09int=09=09=09loglevel=20=3D=20is_server_start=20?=20ERROR=20:=20= LOG;=0A=20=09char=09=20=20=20*command;=0A=20=09FILE=09=20=20=20*fh;=0A=20= =09int=09=09=09pclose_rc;=0A=20=09size_t=09=09len=20=3D=200;=0A+=09char=09= =20=20=20*cmd=20=3D=20(char=20*)=20userdata;=0A=20=0A=20=09= Assert(prompt);=0A=20=09Assert(size=20>=200);=0A=20=09buf[0]=20=3D=20= '\0';=0A=20=0A-=09command=20=3D=20= replace_percent_placeholders(ssl_passphrase_command,=20= "ssl_passphrase_command",=20"p",=20prompt);=0A+=09command=20=3D=20= replace_percent_placeholders(cmd,=20"ssl_passphrase_command",=20"p",=20= prompt);=0A=20=0A=20=09fh=20=3D=20OpenPipeStream(command,=20"r");=0A=20=09= if=20(fh=20=3D=3D=20NULL)=0A@@=20-175,3=20+181,196=20@@=20= check_ssl_key_file_permissions(const=20char=20*ssl_key_file,=20bool=20= isServerStart)=0A=20=0A=20=09return=20true;=0A=20}=0A+=0A+/*=0A+=20*=20= parse_hosts_line=0A+=20*=0A+=20*=20Parses=20a=20loaded=20line=20from=20= the=20pg_hosts.conf=20configuration=20and=20pulls=20out=20the=0A+=20*=20= hostname,=20certificate,=20key=20and=20CA=20parts=20in=20order=20to=20= build=20an=20SNI=20config=20in=0A+=20*=20the=20TLS=20backend.=20= Validation=20of=20the=20parsed=20values=20is=20left=20for=20the=20TLS=20= backend=0A+=20*=20to=20implement.=0A+=20*/=0A+static=20HostsLine=20*=0A= +parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel)=0A+{=0A= +=09HostsLine=20=20*parsedline;=0A+=09List=09=20=20=20*tokens;=0A+=09= ListCell=20=20=20*field;=0A+=09AuthToken=20=20*token;=0A+=0A+=09= parsedline=20=3D=20palloc0(sizeof(HostsLine));=0A+=09= parsedline->sourcefile=20=3D=20pstrdup(tok_line->file_name);=0A+=09= parsedline->linenumber=20=3D=20tok_line->line_num;=0A+=09= parsedline->rawline=20=3D=20pstrdup(tok_line->raw_line);=0A+=0A+=09/*=20= Initialize=20optional=20fields=20*/=0A+=09parsedline->ssl_passphrase_cmd=20= =3D=20NULL;=0A+=09parsedline->ssl_passphrase_reload=20=3D=20false;=0A+=0A= +=09/*=20Hostname=20*/=0A+=09field=20=3D=20list_head(tok_line->fields);=0A= +=09tokens=20=3D=20lfirst(field);=0A+=09token=20=3D=20linitial(tokens);=0A= +=09parsedline->hostname=20=3D=20pstrdup(token->string);=0A+=0A+=09/*=20= SSL=20Certificate=20(Required)=20*/=0A+=09field=20=3D=20= lnext(tok_line->fields,=20field);=0A+=09if=20(!field)=0A+=09{=0A+=09=09= ereport(elevel,=0A+=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09errmsg("missing=20entry=20at=20end=20of=20line"),=0A+=09=09=09=09= errcontext("line=20%d=20of=20configuration=20file=20\"%s\"",=0A+=09=09=09= =09=09=09=20=20=20tok_line->line_num,=20tok_line->file_name));=0A+=09=09= return=20NULL;=0A+=09}=0A+=09tokens=20=3D=20lfirst(field);=0A+=09token=20= =3D=20linitial(tokens);=0A+=09parsedline->ssl_cert=20=3D=20= pstrdup(token->string);=0A+=0A+=09/*=20SSL=20key=20(Required)=20*/=0A+=09= field=20=3D=20lnext(tok_line->fields,=20field);=0A+=09if=20(!field)=0A+=09= {=0A+=09=09ereport(elevel,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("missing=20= entry=20at=20end=20of=20line"),=0A+=09=09=09=09errcontext("line=20%d=20= of=20configuration=20file=20\"%s\"",=0A+=09=09=09=09=09=09=20=20=20= tok_line->line_num,=20tok_line->file_name));=0A+=09=09return=20NULL;=0A+=09= }=0A+=09tokens=20=3D=20lfirst(field);=0A+=09token=20=3D=20= linitial(tokens);=0A+=09parsedline->ssl_key=20=3D=20= pstrdup(token->string);=0A+=0A+=09/*=20SSL=20CA=20(Required)=20*/=0A+=09= field=20=3D=20lnext(tok_line->fields,=20field);=0A+=09if=20(!field)=0A+=09= {=0A+=09=09ereport(elevel,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("missing=20= entry=20at=20end=20of=20line"),=0A+=09=09=09=09errcontext("line=20%d=20= of=20configuration=20file=20\"%s\"",=0A+=09=09=09=09=09=09=20=20=20= tok_line->line_num,=20tok_line->file_name));=0A+=09=09return=20NULL;=0A+=09= }=0A+=09tokens=20=3D=20lfirst(field);=0A+=09token=20=3D=20= linitial(tokens);=0A+=09parsedline->ssl_ca=20=3D=20= pstrdup(token->string);=0A+=0A+=09/*=20SSL=20Passphrase=20Command=20= (optional)=20*/=0A+=09field=20=3D=20lnext(tok_line->fields,=20field);=0A= +=09if=20(field)=0A+=09{=0A+=09=09tokens=20=3D=20lfirst(field);=0A+=09=09= token=20=3D=20linitial(tokens);=0A+=09=09parsedline->ssl_passphrase_cmd=20= =3D=20pstrdup(token->string);=0A+=0A+=09=09/*=0A+=09=09=20*=20SSL=20= Passphrase=20Command=20support=20reload=20(optional).=20This=20field=20= is=0A+=09=09=20*=20only=20supported=20if=20there=20was=20a=20passphrase=20= command=20parsed=20first,=20so=0A+=09=09=20*=20nest=20it=20under=20the=20= previous=20token.=0A+=09=09=20*/=0A+=09=09field=20=3D=20= lnext(tok_line->fields,=20field);=0A+=09=09if=20(field)=0A+=09=09{=0A+=09= =09=09tokens=20=3D=20lfirst(field);=0A+=09=09=09token=20=3D=20= linitial(tokens);=0A+=0A+=09=09=09if=20(token->string[0]=20=3D=3D=20'1'=0A= +=09=09=09=09||=20pg_strcasecmp(token->string,=20"true")=20=3D=3D=200=0A= +=09=09=09=09||=20pg_strcasecmp(token->string,=20"on")=20=3D=3D=200=0A+=09= =09=09=09||=20pg_strcasecmp(token->string,=20"yes")=20=3D=3D=200)=0A+=09=09= =09=09parsedline->ssl_passphrase_reload=20=3D=20true;=0A+=09=09=09else=20= if=20(token->string[0]=20=3D=3D=20'0'=0A+=09=09=09=09=09=20||=20= pg_strcasecmp(token->string,=20"false")=20=3D=3D=200=0A+=09=09=09=09=09=20= ||=20pg_strcasecmp(token->string,=20"off")=20=3D=3D=200=0A+=09=09=09=09=09= =20||=20pg_strcasecmp(token->string,=20"no")=20=3D=3D=200)=0A+=09=09=09=09= parsedline->ssl_passphrase_reload=20=3D=20false;=0A+=09=09=09else=0A+=09=09= =09=09ereport(elevel,=0A+=09=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09= errmsg("incorrect=20syntax=20for=20boolean=20value=20= SSL_passphrase_cmd_reload"),=0A+=09=09=09=09=09=09errcontext("line=20%d=20= of=20configuration=20file=20\"%s\"",=0A+=09=09=09=09=09=09=09=09=20=20=20= tok_line->line_num,=20tok_line->file_name));=0A+=09=09}=0A+=09}=0A+=0A+=09= return=20parsedline;=0A+}=0A+=0A+/*=0A+=20*=20load_hosts=0A+=20*=0A+=20*=20= Reads=20pg_hosts.conf=20and=20passes=20back=20a=20List=20of=20parsed=20= lines,=20or=20NIL=20in=20case=0A+=20*=20of=20errors.=0A+=20*/=0A+List=20= *=0A+load_hosts(void)=0A+{=0A+=09FILE=09=20=20=20*file;=0A+=09ListCell=20= =20=20*line;=0A+=09List=09=20=20=20*hosts_lines=20=3D=20NIL;=0A+=09List=09= =20=20=20*parsed_lines=20=3D=20NIL;=0A+=09HostsLine=20=20*newline;=0A+=09= bool=09=09ok=20=3D=20true;=0A+=09MemoryContext=20oldcxt;=0A+=09= MemoryContext=20hostcxt;=0A+=0A+=09file=20=3D=20= open_auth_file(HostsFileName,=20LOG,=200,=20NULL);=0A+=09if=20(file=20=3D=3D= =20NULL)=0A+=09{=0A+=09=09/*=20An=20error=20has=20already=20been=20= logged=20so=20no=20need=20to=20add=20one=20here=20*/=0A+=09=09return=20= NIL;=0A+=09}=0A+=0A+=09tokenize_auth_file(HostsFileName,=20file,=20= &hosts_lines,=20LOG,=200);=0A+=0A+=09hostcxt=20=3D=20= AllocSetContextCreate(PostmasterContext,=0A+=09=09=09=09=09=09=09=09=09= "hosts=20file=20parser=20context",=0A+=09=09=09=09=09=09=09=09=09= ALLOCSET_SMALL_SIZES);=0A+=09oldcxt=20=3D=20= MemoryContextSwitchTo(hostcxt);=0A+=0A+=09foreach(line,=20hosts_lines)=0A= +=09{=0A+=09=09TokenizedAuthLine=20*tok_line=20=3D=20(TokenizedAuthLine=20= *)=20lfirst(line);=0A+=0A+=09=09if=20(tok_line->err_msg=20!=3D=20NULL)=0A= +=09=09{=0A+=09=09=09ok=20=3D=20false;=0A+=09=09=09continue;=0A+=09=09}=0A= +=0A+=09=09if=20((newline=20=3D=20parse_hosts_line(tok_line,=20LOG))=20= =3D=3D=20NULL)=0A+=09=09{=0A+=09=09=09ok=20=3D=20false;=0A+=09=09=09= continue;=0A+=09=09}=0A+=0A+=09=09parsed_lines=20=3D=20= lappend(parsed_lines,=20newline);=0A+=09}=0A+=0A+=09free_auth_file(file,=20= 0);=0A+=09MemoryContextSwitchTo(oldcxt);=0A+=0A+=09/*=0A+=09=20*=20If=20= we=20didn't=20find=20any=20SNI=20configuration=20then=20that's=20not=20= an=20error=20since=0A+=09=20*=20the=20pg_hosts=20file=20is=20additive=20= to=20the=20default=20SSL=20configuration.=0A+=09=20*/=0A+=09if=20(ok=20= &&=20parsed_lines=20=3D=3D=20NIL)=0A+=09{=0A+=09=09ereport(DEBUG1,=0A+=09= =09=09=09errmsg("no=20SNI=20configuration=20added=20from=20configuration=20= file=20=20\"%s\"",=0A+=09=09=09=09=09=20=20=20HostsFileName));=0A+=09=09= MemoryContextDelete(hostcxt);=0A+=09=09return=20NIL;=0A+=09}=0A+=0A+=09= if=20(!ok)=0A+=09{=0A+=09=09MemoryContextDelete(hostcxt);=0A+=09=09= return=20NIL;=0A+=09}=0A+=0A+=09return=20parsed_lines;=0A+}=0Adiff=20= --git=20a/src/backend/libpq/be-secure-openssl.c=20= b/src/backend/libpq/be-secure-openssl.c=0Aindex=20= 64ff3ce3d6a..29544efa667=20100644=0A---=20= a/src/backend/libpq/be-secure-openssl.c=0A+++=20= b/src/backend/libpq/be-secure-openssl.c=0A@@=20-51,9=20+51,18=20@@=0A=20= #endif=0A=20#include=20=0A=20=0A+typedef=20struct=20= HostContext=0A+{=0A+=09const=20char=20*hostname;=0A+=09const=20char=20= *ssl_passphrase;=0A+=09SSL_CTX=20=20=20=20*context;=0A+=09bool=09=09= default_host;=0A+=09bool=09=09ssl_loaded_verify_locations;=0A+=09bool=09=09= ssl_passphrase_support_reload;=0A+}=20HostContext;=0A=20=0A=20/*=20= default=20init=20hook=20can=20be=20overridden=20by=20a=20shared=20= library=20*/=0A-static=20void=20default_openssl_tls_init(SSL_CTX=20= *context,=20bool=20isServerStart);=0A+static=20void=20= default_openssl_tls_init(SSL_CTX=20*context,=20bool=20isServerStart,=20= HostsLine=20*hosts);=0A=20openssl_tls_init_hook_typ=20= openssl_tls_init_hook=20=3D=20default_openssl_tls_init;=0A=20=0A=20= static=20int=09port_bio_read(BIO=20*h,=20char=20*buf,=20int=20size);=0A= @@=20-73,6=20+82,7=20@@=20static=20int=09alpn_cb(SSL=20*ssl,=0A=20=09=09=09= =09=09const=20unsigned=20char=20*in,=0A=20=09=09=09=09=09unsigned=20int=20= inlen,=0A=20=09=09=09=09=09void=20*userdata);=0A+static=20int=09= sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg);=0A=20static=20= bool=20initialize_dh(SSL_CTX=20*context,=20bool=20isServerStart);=0A=20= static=20bool=20initialize_ecdh(SSL_CTX=20*context,=20bool=20= isServerStart);=0A=20static=20const=20char=20*SSLerrmessageExt(unsigned=20= long=20ecode,=20const=20char=20*replacement);=0A@@=20-80,12=20+90,17=20= @@=20static=20const=20char=20*SSLerrmessage(unsigned=20long=20ecode);=0A=20= =0A=20static=20char=20*X509_NAME_to_cstring(X509_NAME=20*name);=0A=20=0A= +static=20List=20*contexts=20=3D=20NIL;=0A=20static=20SSL_CTX=20= *SSL_context=20=3D=20NULL;=0A+static=20HostContext=20*Default_context=20= =3D=20NULL;=0A+static=20HostContext=20*Host_context=20=3D=20NULL;=0A=20= static=20bool=20dummy_ssl_passwd_cb_called=20=3D=20false;=0A=20static=20= bool=20ssl_is_server_start;=0A=20=0A=20static=20int=09= ssl_protocol_version_to_openssl(int=20v);=0A=20static=20const=20char=20= *ssl_protocol_version_to_string(int=20v);=0A+static=20SSL_CTX=20= *ssl_init_context(bool=20isServerStart,=20HostsLine=20*host);=0A+static=20= void=20free_contexts(void);=0A=20=0A=20/*=20for=20passing=20data=20back=20= from=20verify_cb()=20*/=0A=20static=20const=20char=20*cert_errdetail;=0A= @@=20-96,11=20+111,160=20@@=20static=20const=20char=20*cert_errdetail;=0A= =20=0A=20int=0A=20be_tls_init(bool=20isServerStart)=0A+{=0A+=09SSL_CTX=20= =20=20=20*ctx;=0A+=09List=09=20=20=20*sni_hosts=20=3D=20NIL;=0A+=09= HostsLine=09line;=0A+=0A+=09/*=0A+=09=20*=20If=20there=20are=20contexts=20= loaded=20when=20we=20init=20they=20must=20be=20released.=0A+=09=20*/=0A+=09= if=20(contexts=20!=3D=20NIL)=0A+=09{=0A+=09=09free_contexts();=0A+=09=09= Host_context=20=3D=20NULL;=0A+=09=09SSL_context=20=3D=20NULL;=0A+=09=09= Default_context=20=3D=20NULL;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Load=20= the=20default=20configuration=20from=20postgresql.conf=20such=20that=20= we=20have=20a=0A+=09=20*=20context=20to=20either=20be=20used=20for=20the=20= entire=20connection,=20or=20drive=20the=0A+=09=20*=20handshake=20until=20= the=20SNI=20callback=20replace=20it=20with=20a=20configuration=20from=0A= +=09=20*=20the=20pg_hosts.conf=20file.=0A+=09=20*/=0A+=09line.ssl_cert=20= =3D=20ssl_cert_file;=0A+=09line.ssl_key=20=3D=20ssl_key_file;=0A+=09= line.ssl_ca=20=3D=20ssl_ca_file;=0A+=09line.ssl_passphrase_cmd=20=3D=20= ssl_passphrase_command;=0A+=09line.ssl_passphrase_reload=20=3D=20= ssl_passphrase_command_supports_reload;=0A+=0A+=09ctx=20=3D=20= ssl_init_context(isServerStart,=20&line);=0A+=09if=20(ctx=20=3D=3D=20= NULL)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09= =09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20= errmsg("could=20not=20load=20default=20certificate")));=0A+=09=09return=20= -1;=0A+=09}=0A+=0A+=09Default_context=20=3D=20= palloc0(sizeof(HostContext));=0A+=09Default_context->hostname=20=3D=20= pstrdup("*");=0A+=09Default_context->context=20=3D=20ctx;=0A+=09= Default_context->default_host=20=3D=20true;=0A+=0A+=09/*=0A+=09=20*=20= Set=20flag=20to=20remember=20whether=20CA=20store=20has=20been=20loaded=20= into=20SSL_context.=0A+=09=20*/=0A+=09if=20(ssl_ca_file[0])=0A+=09=09= Default_context->ssl_loaded_verify_locations=20=3D=20true;=0A+=0A+=09/*=0A= +=09=20*=20While=20the=20default=20context=20isn't=20matched=20against=20= when=20searching=20for=20host=0A+=09=20*=20contexts=20we=20still=20add=20= it=20to=20the=20list=20to=20ensure=20that=20cleanup=20code=20can=0A+=09=20= *=20iterate=20over=20a=20single=20structure=20to=20clean=20up=20= everything.=0A+=09=20*/=0A+=09contexts=20=3D=20lappend(contexts,=20= Default_context);=0A+=0A+=09/*=0A+=09=20*=20Install=20the=20default=20= context=20to=20use=20as=20the=20initial=20context=20for=20the=0A+=09=20*=20= connection.=20=20This=20might=20be=20replaced=20in=20the=20SNI=20= callback=20if=20there=20is=20a=0A+=09=20*=20host/snimode=20match,=20but=20= we=20need=20something=20to=20drive=20the=20hand-=20shake=20till=0A+=09=20= *=20then.=0A+=09=20*/=0A+=09Host_context=20=3D=20Default_context;=0A+=09= SSL_context=20=3D=20Host_context->context;=0A+=0A+=09/*=0A+=09=20*=20In=20= default=20or=20strict=20ssl_snimode=20we=20load=20all=20= certificates/keys=20which=0A+=09=20*=20are=20configured=20in=20= pg_hosts.conf.=20=20In=20strict=20mode=20it=20is=20considered=20a=0A+=09=20= *=20fatal=20error=20in=20case=20there=20are=20no=20configured=20entries.=0A= +=09=20*/=0A+=09if=20(ssl_snimode=20=3D=3D=20SSL_SNIMODE_STRICT=20||=20= ssl_snimode=20=3D=3D=20SSL_SNIMODE_DEFAULT)=0A+=09{=0A+=09=09ListCell=20=20= =20*line;=0A+=0A+=09=09/*=0A+=09=09=20*=20Load=20pg_hosts.conf=20and=20= parse=20each=20row,=20returning=20the=20set=20of=20hosts=0A+=09=09=20*=20= as=20a=20list.=0A+=09=09=20*/=0A+=09=09sni_hosts=20=3D=20load_hosts();=0A= +=0A+=09=09/*=0A+=09=09=20*=20In=20strict=20ssl_snimode=20there=20needs=20= to=20be=20at=20least=20one=20configured=0A+=09=09=20*=20host=20in=20the=20= pg_hosts=20file=20since=20the=20default=20fallback=20context=20isn't=0A+=09= =09=20*=20allowed=20to=20connect=20with.=0A+=09=09=20*/=0A+=09=09if=20= (sni_hosts=20=3D=3D=20NIL=20&&=20ssl_snimode=20=3D=3D=20= SSL_SNIMODE_STRICT)=0A+=09=09{=0A+=09=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=09errmsg("could=20not=20load=20%s",=20"pg_hosts.conf"));=0A= +=09=09=09return=20-1;=0A+=09=09}=0A+=0A+=09=09foreach(line,=20= sni_hosts)=0A+=09=09{=0A+=09=09=09HostContext=20*host_context;=0A+=09=09=09= HostsLine=20=20*host=20=3D=20lfirst(line);=0A+=09=09=09static=20SSL_CTX=20= *tmp_context=20=3D=20NULL;=0A+=0A+=09=09=09tmp_context=20=3D=20= ssl_init_context(isServerStart,=20host);=0A+=09=09=09if=20(tmp_context=20= =3D=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09errmsg("unable=20= to=20load=20certificate=20from=20pg_hosts.conf=20file"));=0A+=09=09=09=09= return=20-1;=0A+=09=09=09}=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20The=20= parsing=20logic=20has=20already=20verified=20that=20the=20hostname=20= exist=0A+=09=09=09=20*=20so=20we=20need=20not=20check=20that.=20=20The=20= passphrase=20command=20fields=20are=0A+=09=09=09=20*=20however=20= optional=20so=20we=20need=20to=20check=20whether=20those=20were=20set.=0A= +=09=09=09=20*/=0A+=09=09=09host_context=20=3D=20= palloc0(sizeof(HostContext));=0A+=09=09=09host_context->hostname=20=3D=20= pstrdup(host->hostname);=0A+=09=09=09host_context->context=20=3D=20= tmp_context;=0A+=09=09=09host_context->default_host=20=3D=20false;=0A+=09= =09=09if=20(host->ssl_passphrase_cmd=20!=3D=20NULL)=0A+=09=09=09=09= host_context->ssl_passphrase=20=3D=20pstrdup(host->ssl_passphrase_cmd);=0A= +=09=09=09host_context->ssl_passphrase_support_reload=20=3D=20= host->ssl_passphrase_reload;=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20Set=20= flag=20to=20remember=20whether=20CA=20store=20has=20been=20loaded=20into=20= this=0A+=09=09=09=20*=20SSL_context.=0A+=09=09=09=20*/=0A+=09=09=09if=20= (host->ssl_ca)=0A+=09=09=09=09host_context->ssl_loaded_verify_locations=20= =3D=20true;=0A+=0A+=09=09=09contexts=20=3D=20lappend(contexts,=20= host_context);=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20Make=20sure=20we=20have=20= at=20least=20one=20certificate=20loaded=20*/=0A+=09if=20= (list_length(contexts)=20<=201)=0A+=09{=0A+=09=09ereport(isServerStart=20= ?=20FATAL=20:=20LOG,=0A+=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=20errmsg("no=20SSL=20contexts=20loaded")));=0A+=09=09= return=20-1;=0A+=09}=0A+=0A+=09return=200;=0A+}=0A+=0A+static=20SSL_CTX=20= *=0A+ssl_init_context(bool=20isServerStart,=20HostsLine=20*host_line)=0A=20= {=0A=20=09SSL_CTX=20=20=20=20*context;=0A=20=09int=09=09=09ssl_ver_min=20= =3D=20-1;=0A=20=09int=09=09=09ssl_ver_max=20=3D=20-1;=0A=20=0A+=09const=20= char=20*ctx_ssl_cert_file=20=3D=20host_line->ssl_cert;=0A+=09const=20= char=20*ctx_ssl_key_file=20=3D=20host_line->ssl_key;=0A+=09const=20char=20= *ctx_ssl_ca_file=20=3D=20host_line->ssl_ca;=0A+=0A=20=09/*=0A=20=09=20*=20= Create=20a=20new=20SSL=20context=20into=20which=20we'll=20load=20all=20= the=20configuration=0A=20=09=20*=20settings.=20=20If=20we=20fail=20= partway=20through,=20we=20can=20avoid=20memory=20leakage=20by=0A@@=20= -126,10=20+290,17=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09=20*/=0A= =20=09SSL_CTX_set_mode(context,=20SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A= =20=0A+=09/*=0A+=09=20*=20Install=20SNI=20TLS=20extension=20callback=20= in=20case=20the=20server=20is=20configured=20to=0A+=09=20*=20validate=20= hostnames.=0A+=09=20*/=0A+=09if=20(ssl_snimode=20!=3D=20SSL_SNIMODE_OFF)=0A= +=09=09SSL_CTX_set_tlsext_servername_callback(context,=20= sni_servername_cb);=0A+=0A=20=09/*=0A=20=09=20*=20Call=20init=20hook=20= (usually=20to=20set=20password=20callback)=0A=20=09=20*/=0A-=09= (*openssl_tls_init_hook)=20(context,=20isServerStart);=0A+=09= (*openssl_tls_init_hook)=20(context,=20isServerStart,=20host_line);=0A=20= =0A=20=09/*=20used=20by=20the=20callback=20*/=0A=20=09= ssl_is_server_start=20=3D=20isServerStart;=0A@@=20-137,16=20+308,16=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09/*=0A=20=09=20*=20Load=20and=20= verify=20server's=20certificate=20and=20private=20key=0A=20=09=20*/=0A-=09= if=20(SSL_CTX_use_certificate_chain_file(context,=20ssl_cert_file)=20!=3D=20= 1)=0A+=09if=20(SSL_CTX_use_certificate_chain_file(context,=20= ctx_ssl_cert_file)=20!=3D=201)=0A=20=09{=0A=20=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=20errmsg("could=20= not=20load=20server=20certificate=20file=20\"%s\":=20%s",=0A-=09=09=09=09= =09=09ssl_cert_file,=20SSLerrmessage(ERR_get_error()))));=0A+=09=09=09=09= =09=09ctx_ssl_cert_file,=20SSLerrmessage(ERR_get_error()))));=0A=20=09=09= goto=20error;=0A=20=09}=0A=20=0A-=09if=20= (!check_ssl_key_file_permissions(ssl_key_file,=20isServerStart))=0A+=09= if=20(!check_ssl_key_file_permissions(ctx_ssl_key_file,=20= isServerStart))=0A=20=09=09goto=20error;=0A=20=0A=20=09/*=0A@@=20-155,19=20= +326,19=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09= dummy_ssl_passwd_cb_called=20=3D=20false;=0A=20=0A=20=09if=20= (SSL_CTX_use_PrivateKey_file(context,=0A-=09=09=09=09=09=09=09=09=09= ssl_key_file,=0A+=09=09=09=09=09=09=09=09=09ctx_ssl_key_file,=0A=20=09=09= =09=09=09=09=09=09=09SSL_FILETYPE_PEM)=20!=3D=201)=0A=20=09{=0A=20=09=09= if=20(dummy_ssl_passwd_cb_called)=0A=20=09=09=09ereport(isServerStart=20= ?=20FATAL=20:=20LOG,=0A=20=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=20= errmsg("private=20key=20file=20\"%s\"=20cannot=20be=20reloaded=20because=20= it=20requires=20a=20passphrase",=0A-=09=09=09=09=09=09=09= ssl_key_file)));=0A+=09=09=09=09=09=09=09ctx_ssl_key_file)));=0A=20=09=09= else=0A=20=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09= =09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=20= errmsg("could=20not=20load=20private=20key=20file=20\"%s\":=20%s",=0A-=09= =09=09=09=09=09=09ssl_key_file,=20SSLerrmessage(ERR_get_error()))));=0A+=09= =09=09=09=09=09=09ctx_ssl_key_file,=20SSLerrmessage(ERR_get_error()))));=0A= =20=09=09goto=20error;=0A=20=09}=0A=20=0A@@=20-319,17=20+490,17=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09/*=0A=20=09=20*=20Load=20CA=20= store,=20so=20we=20can=20verify=20client=20certificates=20if=20needed.=0A= =20=09=20*/=0A-=09if=20(ssl_ca_file[0])=0A+=09if=20(ctx_ssl_ca_file[0])=0A= =20=09{=0A=20=09=09STACK_OF(X509_NAME)=20*=20root_cert_list;=0A=20=0A-=09= =09if=20(SSL_CTX_load_verify_locations(context,=20ssl_ca_file,=20NULL)=20= !=3D=201=20||=0A-=09=09=09(root_cert_list=20=3D=20= SSL_load_client_CA_file(ssl_ca_file))=20=3D=3D=20NULL)=0A+=09=09if=20= (SSL_CTX_load_verify_locations(context,=20ctx_ssl_ca_file,=20NULL)=20!=3D=20= 1=20||=0A+=09=09=09(root_cert_list=20=3D=20= SSL_load_client_CA_file(ctx_ssl_ca_file))=20=3D=3D=20NULL)=0A=20=09=09{=0A= =20=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09=09=09= =09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=20= errmsg("could=20not=20load=20root=20certificate=20file=20\"%s\":=20%s",=0A= -=09=09=09=09=09=09=09ssl_ca_file,=20SSLerrmessage(ERR_get_error()))));=0A= +=09=09=09=09=09=09=09ctx_ssl_ca_file,=20= SSLerrmessage(ERR_get_error()))));=0A=20=09=09=09goto=20error;=0A=20=09=09= }=0A=20=0A@@=20-401,38=20+572,29=20@@=20be_tls_init(bool=20= isServerStart)=0A=20=09=09}=0A=20=09}=0A=20=0A-=09/*=0A-=09=20*=20= Success!=20=20Replace=20any=20existing=20SSL_context.=0A-=09=20*/=0A-=09= if=20(SSL_context)=0A-=09=09SSL_CTX_free(SSL_context);=0A-=0A-=09= SSL_context=20=3D=20context;=0A-=0A-=09/*=0A-=09=20*=20Set=20flag=20to=20= remember=20whether=20CA=20store=20has=20been=20loaded=20into=20= SSL_context.=0A-=09=20*/=0A-=09if=20(ssl_ca_file[0])=0A-=09=09= ssl_loaded_verify_locations=20=3D=20true;=0A-=09else=0A-=09=09= ssl_loaded_verify_locations=20=3D=20false;=0A-=0A-=09return=200;=0A+=09= return=20context;=0A=20=0A=20=09/*=20Clean=20up=20by=20releasing=20= working=20context.=20*/=0A=20error:=0A=20=09if=20(context)=0A=20=09=09= SSL_CTX_free(context);=0A-=09return=20-1;=0A+=09return=20NULL;=0A=20}=0A=20= =0A=20void=0A=20be_tls_destroy(void)=0A=20{=0A-=09if=20(SSL_context)=0A-=09= =09SSL_CTX_free(SSL_context);=0A+=09ListCell=20=20=20*cell;=0A+=0A+=09= foreach(cell,=20contexts)=0A+=09{=0A+=09=09HostContext=20*host_context=20= =3D=20lfirst(cell);=0A+=0A+=09=09SSL_CTX_free(host_context->context);=0A= +=09=09pfree(host_context);=0A+=09}=0A+=0A=20=09SSL_context=20=3D=20= NULL;=0A-=09ssl_loaded_verify_locations=20=3D=20false;=0A=20}=0A=20=0A=20= int=0A@@=20-759,6=20+921,9=20@@=20be_tls_close(Port=20*port)=0A=20=09=09= pfree(port->peer_dn);=0A=20=09=09port->peer_dn=20=3D=20NULL;=0A=20=09}=0A= +=0A+=09Host_context=20=3D=20NULL;=0A+=09SSL_context=20=3D=20NULL;=0A=20= }=0A=20=0A=20ssize_t=0A@@=20-1132,7=20+1297,7=20@@=20= ssl_external_passwd_cb(char=20*buf,=20int=20size,=20int=20rwflag,=20void=20= *userdata)=0A=20=0A=20=09Assert(rwflag=20=3D=3D=200);=0A=20=0A-=09return=20= run_ssl_passphrase_command(prompt,=20ssl_is_server_start,=20buf,=20= size);=0A+=09return=20run_ssl_passphrase_command(prompt,=20= ssl_is_server_start,=20buf,=20size,=20userdata);=0A=20}=0A=20=0A=20/*=0A= @@=20-1369,6=20+1534,88=20@@=20alpn_cb(SSL=20*ssl,=0A=20=09}=0A=20}=0A=20= =0A+static=20int=0A+sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20= *arg)=0A+{=0A+=09const=20char=20*tlsext_hostname;=0A+=0A+=09/*=0A+=09=20= *=20Executing=20this=20callback=20when=20SNI=20is=20turned=20off=20= indicates=20a=20programmer=0A+=09=20*=20error=20or=20something=20worse.=0A= +=09=20*/=0A+=09Assert(ssl_snimode=20!=3D=20SSL_SNIMODE_OFF);=0A+=0A+=09= tlsext_hostname=20=3D=20SSL_get_servername(ssl,=20= TLSEXT_NAMETYPE_host_name);=0A+=0A+=09/*=0A+=09=20*=20If=20there=20is=20= no=20hostname=20set=20in=20the=20TLS=20extension,=20we=20have=20two=20= options.=0A+=09=20*=20For=20ssl_snimode=20strict=20we=20error=20out=20= since=20we=20cannot=20match=20a=20host=20config=0A+=09=20*=20for=20the=20= connection.=20=20For=20the=20default=20mode=20we=20fall=20back=20on=20= the=20default=0A+=09=20*=20hostname=20configuration.=0A+=09=20*/=0A+=09= if=20(!tlsext_hostname)=0A+=09{=0A+=09=09if=20(ssl_snimode=20=3D=3D=20= SSL_SNIMODE_STRICT)=0A+=09=09{=0A+=09=09=09ereport(COMMERROR,=0A+=09=09=09= =09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20= errmsg("no=20hostname=20provided=20in=20callback")));=0A+=09=09=09return=20= SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09=09}=0A+=09=09else=0A+=09=09{=0A+=09=09= =09Host_context=20=3D=20Default_context;=0A+=09=09=09SSL_context=20=3D=20= Host_context->context;=0A+=09=09=09SSL_set_SSL_CTX(ssl,=20SSL_context);=0A= +=09=09=09return=20SSL_TLSEXT_ERR_OK;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=0A= +=09=20*=20We=20have=20a=20requested=20hostname=20from=20the=20client,=20= match=20against=20all=20entries=0A+=09=20*=20in=20the=20pg_hosts=20= configuration=20to=20find=20a=20match.=0A+=09=20*/=0A+=09= foreach_ptr(HostContext,=20host,=20contexts)=0A+=09{=0A+=09=09/*=0A+=09=09= =20*=20For=20strict=20mode=20we=20will=20never=20want=20the=20default=20= host=20so=20we=20can=20skip=0A+=09=09=20*=20past=20it=20immediately.=0A+=09= =09=20*/=0A+=09=09if=20(ssl_snimode=20=3D=3D=20SSL_SNIMODE_STRICT=20&&=20= host->default_host)=0A+=09=09=09continue;=0A+=0A+=09=09if=20= (strcmp(host->hostname,=20tlsext_hostname)=20=3D=3D=200)=0A+=09=09{=0A+=09= =09=09Host_context=20=3D=20host;=0A+=09=09=09SSL_context=20=3D=20= host->context;=0A+=09=09=09SSL_set_SSL_CTX(ssl,=20SSL_context);=0A+=09=09= =09return=20SSL_TLSEXT_ERR_OK;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=0A+=09=20= *=20In=20ssl_snimode=20"strict"=20it's=20an=20error=20if=20there=20was=20= no=20match=20for=20the=0A+=09=20*=20hostname=20in=20the=20TLS=20= extension.=20=20Terminate=20the=20connection.=0A+=09=20*/=0A+=09if=20= (ssl_snimode=20=3D=3D=20SSL_SNIMODE_STRICT)=0A+=09{=0A+=09=09= ereport(COMMERROR,=0A+=09=09=09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A= +=09=09=09=09=20errmsg("no=20matching=20pg_hosts=20entry=20found=20for=20= hostname:=20\"%s\"",=0A+=09=09=09=09=09=09tlsext_hostname)));=0A+=09=09= return=20SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20= In=20ssl_snimode=20"default"=20we=20fall=20back=20on=20the=20default=20= host=20configured=20in=0A+=09=20*=20postgresql.conf=20when=20no=20match=20= is=20found=20in=20pg_hosts.conf.=0A+=09=20*/=0A+=09Host_context=20=3D=20= Default_context;=0A+=09SSL_context=20=3D=20Host_context->context;=0A+=09= SSL_set_SSL_CTX(ssl,=20SSL_context);=0A+=09Assert(SSL_context);=0A+=09= return=20SSL_TLSEXT_ERR_OK;=0A+}=0A=20=0A=20/*=0A=20=20*=20Set=20DH=20= parameters=20for=20generating=20ephemeral=20DH=20keys.=20=20The=0A@@=20= -1578,6=20+1825,12=20@@=20be_tls_get_peer_serial(Port=20*port,=20char=20= *ptr,=20size_t=20len)=0A=20=09=09ptr[0]=20=3D=20'\0';=0A=20}=0A=20=0A= +bool=0A+be_tls_loaded_verify_locations(void)=0A+{=0A+=09return=20= Host_context->ssl_loaded_verify_locations;=0A+}=0A+=0A=20char=20*=0A=20= be_tls_get_certificate_hash(Port=20*port,=20size_t=20*len)=0A=20{=0A@@=20= -1771,17=20+2024,23=20@@=20ssl_protocol_version_to_string(int=20v)=0A=20=0A= =20=0A=20static=20void=0A-default_openssl_tls_init(SSL_CTX=20*context,=20= bool=20isServerStart)=0A+default_openssl_tls_init(SSL_CTX=20*context,=20= bool=20isServerStart,=20HostsLine=20*host)=0A=20{=0A=20=09if=20= (isServerStart)=0A=20=09{=0A-=09=09if=20(ssl_passphrase_command[0])=0A+=09= =09if=20(host->ssl_passphrase_cmd=20!=3D=20NULL)=0A+=09=09{=0A=20=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20ssl_external_passwd_cb);=0A+=09=09= =09SSL_CTX_set_default_passwd_cb_userdata(context,=20= host->ssl_passphrase_cmd);=0A+=09=09}=0A=20=09}=0A=20=09else=0A=20=09{=0A= -=09=09if=20(ssl_passphrase_command[0]=20&&=20= ssl_passphrase_command_supports_reload)=0A+=09=09if=20= (host->ssl_passphrase_cmd=20!=3D=20NULL=20&&=20= host->ssl_passphrase_reload)=0A+=09=09{=0A=20=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20ssl_external_passwd_cb);=0A+=09=09= =09SSL_CTX_set_default_passwd_cb_userdata(context,=20= host->ssl_passphrase_cmd);=0A+=09=09}=0A=20=09=09else=0A=20=0A=20=09=09=09= /*=0A@@=20-1793,3=20+2052,26=20@@=20default_openssl_tls_init(SSL_CTX=20= *context,=20bool=20isServerStart)=0A=20=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20dummy_ssl_passwd_cb);=0A=20=09}=0A= =20}=0A+=0A+/*=0A+=20*=20Cleanup=20function=20for=20when=20hostname=20= configuration=20is=20reloaded=20from=20the=0A+=20*=20pg_hosts.conf=20= file,=20at=20that=20point=20we=20Must=20discard=20all=20existing=20= contexts.=0A+=20*/=0A+static=20void=0A+free_contexts(void)=0A+{=0A+=09if=20= (contexts=20=3D=3D=20NIL)=0A+=09=09return;=0A+=0A+=09= foreach_ptr(HostContext,=20host,=20contexts)=0A+=09{=0A+=09=09if=20= (host->hostname)=0A+=09=09=09pfree(unconstify(char=20*,=20= host->hostname));=0A+=09=09if=20(host->ssl_passphrase)=0A+=09=09=09= pfree(unconstify(char=20*,=20host->ssl_passphrase));=0A+=09=09= SSL_CTX_free(host->context);=0A+=09}=0A+=0A+=09list_free_deep(contexts);=0A= +=09contexts=20=3D=20NIL;=0A+}=0Adiff=20--git=20= a/src/backend/libpq/be-secure.c=20b/src/backend/libpq/be-secure.c=0A= index=2091576f94285..b10e8f995ac=20100644=0A---=20= a/src/backend/libpq/be-secure.c=0A+++=20b/src/backend/libpq/be-secure.c=0A= @@=20-43,10=20+43,6=20@@=20char=09=20=20=20*ssl_dh_params_file;=0A=20= char=09=20=20=20*ssl_passphrase_command;=0A=20bool=09=09= ssl_passphrase_command_supports_reload;=0A=20=0A-#ifdef=20USE_SSL=0A= -bool=09=09ssl_loaded_verify_locations=20=3D=20false;=0A-#endif=0A-=0A=20= /*=20GUC=20variable=20controlling=20SSL=20cipher=20list=20*/=0A=20char=09= =20=20=20*SSLCipherSuites=20=3D=20NULL;=0A=20char=09=20=20=20= *SSLCipherList=20=3D=20NULL;=0A@@=20-60,6=20+56,8=20@@=20bool=09=09= SSLPreferServerCiphers;=0A=20int=09=09=09ssl_min_protocol_version=20=3D=20= PG_TLS1_2_VERSION;=0A=20int=09=09=09ssl_max_protocol_version=20=3D=20= PG_TLS_ANY;=0A=20=0A+int=09=09=09ssl_snimode=20=3D=20= SSL_SNIMODE_DEFAULT;=0A+=0A=20/*=20= ------------------------------------------------------------=20*/=0A=20= /*=09=09=09=20Procedures=20common=20to=20all=20secure=20sessions=09=09=09= */=0A=20/*=20= ------------------------------------------------------------=20*/=0A@@=20= -99,7=20+97,7=20@@=20bool=0A=20secure_loaded_verify_locations(void)=0A=20= {=0A=20#ifdef=20USE_SSL=0A-=09return=20ssl_loaded_verify_locations;=0A+=09= return=20be_tls_loaded_verify_locations();=0A=20#else=0A=20=09return=20= false;=0A=20#endif=0Adiff=20--git=20a/src/backend/libpq/meson.build=20= b/src/backend/libpq/meson.build=0Aindex=2031aa2faae1e..4f6ec13bc74=20= 100644=0A---=20a/src/backend/libpq/meson.build=0A+++=20= b/src/backend/libpq/meson.build=0A@@=20-31,5=20+31,6=20@@=20endif=0A=20= install_data(=0A=20=20=20'pg_hba.conf.sample',=0A=20=20=20= 'pg_ident.conf.sample',=0A+=20=20'pg_hosts.conf.sample',=0A=20=20=20= install_dir:=20dir_data,=0A=20)=0Adiff=20--git=20= a/src/backend/libpq/pg_hosts.conf.sample=20= b/src/backend/libpq/pg_hosts.conf.sample=0Anew=20file=20mode=20100644=0A= index=2000000000000..5a47f9cae7d=0A---=20/dev/null=0A+++=20= b/src/backend/libpq/pg_hosts.conf.sample=0A@@=20-0,0=20+1,4=20@@=0A+#=20= PostgreSQL=20SNI=20Hostname=20mappings=0A+#=20= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=0A+=0A+#=20HOSTNAME=20=20=20=20=20=20=20SSL=20= CERTIFICATE=20=20=20=20=20=20=20=20=20=20=20=20=20SSL=20KEY=0Adiff=20= --git=20a/src/backend/utils/misc/guc.c=20b/src/backend/utils/misc/guc.c=0A= index=2012192445218..1a3e5011b35=20100644=0A---=20= a/src/backend/utils/misc/guc.c=0A+++=20b/src/backend/utils/misc/guc.c=0A= @@=20-55,6=20+55,7=20@@=0A=20#define=20CONFIG_FILENAME=20= "postgresql.conf"=0A=20#define=20HBA_FILENAME=09"pg_hba.conf"=0A=20= #define=20IDENT_FILENAME=09"pg_ident.conf"=0A+#define=20HOSTS_FILENAME=09= "pg_hosts.conf"=0A=20=0A=20#ifdef=20EXEC_BACKEND=0A=20#define=20= CONFIG_EXEC_PARAMS=20"global/config_exec_params"=0A@@=20-1968,6=20= +1969,31=20@@=20SelectConfigFiles(const=20char=20*userDoption,=20const=20= char=20*progname)=0A=20=09}=0A=20=09SetConfigOption("ident_file",=20= fname,=20PGC_POSTMASTER,=20PGC_S_OVERRIDE);=0A=20=0A+=09/*=0A+=09=20*=20= Likewise=20for=20pg_hosts.conf=0A+=09=20*/=0A+=09if=20(HostsFileName)=0A= +=09{=0A+=09=09fname=20=3D=20make_absolute_path(HostsFileName);=0A+=09=09= fname_is_malloced=20=3D=20true;=0A+=09}=0A+=09else=20if=20(configdir)=0A= +=09{=0A+=09=09fname=20=3D=20guc_malloc(FATAL,=0A+=09=09=09=09=09=09=20=20= =20strlen(configdir)=20+=20strlen(HOSTS_FILENAME)=20+=202);=0A+=09=09= sprintf(fname,=20"%s/%s",=20configdir,=20HOSTS_FILENAME);=0A+=09=09= fname_is_malloced=20=3D=20false;=0A+=09}=0A+=09else=0A+=09{=0A+=09=09= write_stderr("%s=20does=20not=20know=20where=20to=20find=20the=20= \"hosts\"=20configuration=20file.\n"=0A+=09=09=09=09=09=20"This=20can=20= be=20specified=20as=20\"hosts_file\"=20in=20\"%s\",=20"=0A+=09=09=09=09=09= =20"or=20by=20the=20-D=20invocation=20option,=20or=20by=20the=20"=0A+=09=09= =09=09=09=20"PGDATA=20environment=20variable.\n",=0A+=09=09=09=09=09=20= progname,=20ConfigFileName);=0A+=09}=0A+=09SetConfigOption("hosts_file",=20= fname,=20PGC_POSTMASTER,=20PGC_S_OVERRIDE);=0A+=0A=20=09if=20= (fname_is_malloced)=0A=20=09=09free(fname);=0A=20=09else=0Adiff=20--git=20= a/src/backend/utils/misc/guc_tables.c=20= b/src/backend/utils/misc/guc_tables.c=0Aindex=20ad25cbb39c5..b7e868b128e=20= 100644=0A---=20a/src/backend/utils/misc/guc_tables.c=0A+++=20= b/src/backend/utils/misc/guc_tables.c=0A@@=20-476,6=20+476,13=20@@=20= static=20const=20struct=20config_enum_entry=20wal_compression_options[]=20= =3D=20{=0A=20=09{NULL,=200,=20false}=0A=20};=0A=20=0A+static=20const=20= struct=20config_enum_entry=20ssl_snimode_options[]=20=3D=20{=0A+=09= {"off",=20SSL_SNIMODE_OFF,=20false},=0A+=09{"default",=20= SSL_SNIMODE_DEFAULT,=20false},=0A+=09{"strict",=20SSL_SNIMODE_STRICT,=20= false},=0A+=09{NULL,=200,=20false}=0A+};=0A+=0A=20/*=0A=20=20*=20Options=20= for=20enum=20values=20stored=20in=20other=20modules=0A=20=20*/=0A@@=20= -540,6=20+547,7=20@@=20char=09=20=20=20*cluster_name=20=3D=20"";=0A=20= char=09=20=20=20*ConfigFileName;=0A=20char=09=20=20=20*HbaFileName;=0A=20= char=09=20=20=20*IdentFileName;=0A+char=09=20=20=20*HostsFileName;=0A=20= char=09=20=20=20*external_pid_file;=0A=20=0A=20char=09=20=20=20= *application_name;=0A@@=20-4624,6=20+4632,17=20@@=20struct=20= config_string=20ConfigureNamesString[]=20=3D=0A=20=09=09NULL,=20NULL,=20= NULL=0A=20=09},=0A=20=0A+=09{=0A+=09=09{"hosts_file",=20PGC_POSTMASTER,=20= FILE_LOCATIONS,=0A+=09=09=09gettext_noop("Sets=20the=20server's=20= \"hosts\"=20configuration=20file."),=0A+=09=09=09NULL,=0A+=09=09=09= GUC_SUPERUSER_ONLY=0A+=09=09},=0A+=09=09&HostsFileName,=0A+=09=09NULL,=0A= +=09=09NULL,=20NULL,=20NULL=0A+=09},=0A+=0A=20=09{=0A=20=09=09= {"external_pid_file",=20PGC_POSTMASTER,=20FILE_LOCATIONS,=0A=20=09=09=09= gettext_noop("Writes=20the=20postmaster=20PID=20to=20the=20specified=20= file."),=0A@@=20-5277,6=20+5296,18=20@@=20struct=20config_enum=20= ConfigureNamesEnum[]=20=3D=0A=20=09=09NULL,=20NULL,=20NULL=0A=20=09},=0A=20= =0A+=09{=0A+=09=09{"ssl_snimode",=20PGC_SIGHUP,=20CONN_AUTH_SSL,=0A+=09=09= =09gettext_noop("Sets=20the=20SNI=20mode=20to=20use."),=0A+=09=09=09= NULL,=0A+=09=09=09GUC_SUPERUSER_ONLY,=0A+=09=09},=0A+=09=09&ssl_snimode,=0A= +=09=09SSL_SNIMODE_DEFAULT,=0A+=09=09ssl_snimode_options,=0A+=09=09NULL,=20= NULL,=20NULL=0A+=09},=0A+=0A=20=09{=0A=20=09=09= {"recovery_init_sync_method",=20PGC_SIGHUP,=20ERROR_HANDLING_OPTIONS,=0A=20= =09=09=09gettext_noop("Sets=20the=20method=20for=20synchronizing=20the=20= data=20directory=20before=20crash=20recovery."),=0Adiff=20--git=20= a/src/backend/utils/misc/postgresql.conf.sample=20= b/src/backend/utils/misc/postgresql.conf.sample=0Aindex=20= 5362ff80519..a20a5ba5d75=20100644=0A---=20= a/src/backend/utils/misc/postgresql.conf.sample=0A+++=20= b/src/backend/utils/misc/postgresql.conf.sample=0A@@=20-45,6=20+45,8=20= @@=0A=20=09=09=09=09=09#=20(change=20requires=20restart)=0A=20= #ident_file=20=3D=20'ConfigDir/pg_ident.conf'=09#=20ident=20= configuration=20file=0A=20=09=09=09=09=09#=20(change=20requires=20= restart)=0A+#hosts_file=20=3D=20'ConfigDir/pg_hosts.conf'=20#=20hosts=20= configuration=20file=0A+=09=09=09=09=09#=20(change=20requires=20restart)=0A= =20=0A=20#=20If=20external_pid_file=20is=20not=20explicitly=20set,=20no=20= extra=20PID=20file=20is=20written.=0A=20#external_pid_file=20=3D=20''=09=09= =09#=20write=20an=20extra=20PID=20file=0A@@=20-120,6=20+122,7=20@@=0A=20= #ssl_dh_params_file=20=3D=20''=0A=20#ssl_passphrase_command=20=3D=20''=0A= =20#ssl_passphrase_command_supports_reload=20=3D=20off=0A+#ssl_snimode=20= =3D=20default=0A=20=0A=20#=20OAuth=0A=20#oauth_validator_libraries=20=3D=20= ''=09#=20comma-separated=20list=20of=20trusted=20validator=20modules=0A= diff=20--git=20a/src/bin/initdb/initdb.c=20b/src/bin/initdb/initdb.c=0A= index=2021a0fe3ecd9..a6e680f7b44=20100644=0A---=20= a/src/bin/initdb/initdb.c=0A+++=20b/src/bin/initdb/initdb.c=0A@@=20= -176,6=20+176,7=20@@=20static=20int=09encodingid;=0A=20static=20char=20= *bki_file;=0A=20static=20char=20*hba_file;=0A=20static=20char=20= *ident_file;=0A+static=20char=20*hosts_file;=0A=20static=20char=20= *conf_file;=0A=20static=20char=20*dictionary_file;=0A=20static=20char=20= *info_schema_file;=0A@@=20-1542,6=20+1543,14=20@@=20setup_config(void)=0A= =20=0A=20=09snprintf(path,=20sizeof(path),=20"%s/pg_ident.conf",=20= pg_data);=0A=20=0A+=09writefile(path,=20conflines);=0A+=09if=20= (chmod(path,=20pg_file_create_mode)=20!=3D=200)=0A+=09=09pg_fatal("could=20= not=20change=20permissions=20of=20\"%s\":=20%m",=20path);=0A+=0A+=09/*=20= pg_hosts.conf=20*/=0A+=09conflines=20=3D=20readfile(hosts_file);=0A+=09= snprintf(path,=20sizeof(path),=20"%s/pg_hosts.conf",=20pg_data);=0A+=0A=20= =09writefile(path,=20conflines);=0A=20=09if=20(chmod(path,=20= pg_file_create_mode)=20!=3D=200)=0A=20=09=09pg_fatal("could=20not=20= change=20permissions=20of=20\"%s\":=20%m",=20path);=0A@@=20-2805,6=20= +2814,7=20@@=20setup_data_file_paths(void)=0A=20=09set_input(&bki_file,=20= "postgres.bki");=0A=20=09set_input(&hba_file,=20"pg_hba.conf.sample");=0A= =20=09set_input(&ident_file,=20"pg_ident.conf.sample");=0A+=09= set_input(&hosts_file,=20"pg_hosts.conf.sample");=0A=20=09= set_input(&conf_file,=20"postgresql.conf.sample");=0A=20=09= set_input(&dictionary_file,=20"snowball_create.sql");=0A=20=09= set_input(&info_schema_file,=20"information_schema.sql");=0A@@=20= -2820,12=20+2830,13=20@@=20setup_data_file_paths(void)=0A=20=09=09=09=09= "PGDATA=3D%s\nshare_path=3D%s\nPGPATH=3D%s\n"=0A=20=09=09=09=09= "POSTGRES_SUPERUSERNAME=3D%s\nPOSTGRES_BKI=3D%s\n"=0A=20=09=09=09=09= "POSTGRESQL_CONF_SAMPLE=3D%s\n"=0A-=09=09=09=09= "PG_HBA_SAMPLE=3D%s\nPG_IDENT_SAMPLE=3D%s\n",=0A+=09=09=09=09= "PG_HBA_SAMPLE=3D%s\nPG_IDENT_SAMPLE=3D%s\n"=0A+=09=09=09=09= "PG_HOSTS_SAMPLE=3D%s\n",=0A=20=09=09=09=09PG_VERSION,=0A=20=09=09=09=09= pg_data,=20share_path,=20bin_path,=0A=20=09=09=09=09username,=20= bki_file,=0A=20=09=09=09=09conf_file,=0A-=09=09=09=09hba_file,=20= ident_file);=0A+=09=09=09=09hba_file,=20ident_file,=20hosts_file);=0A=20=09= =09if=20(show_setting)=0A=20=09=09=09exit(0);=0A=20=09}=0A@@=20-2833,6=20= +2844,7=20@@=20setup_data_file_paths(void)=0A=20=09= check_input(bki_file);=0A=20=09check_input(hba_file);=0A=20=09= check_input(ident_file);=0A+=09check_input(hosts_file);=0A=20=09= check_input(conf_file);=0A=20=09check_input(dictionary_file);=0A=20=09= check_input(info_schema_file);=0Adiff=20--git=20= a/src/include/libpq/hba.h=20b/src/include/libpq/hba.h=0Aindex=20= 3657f182db3..3d8e33533b8=20100644=0A---=20a/src/include/libpq/hba.h=0A= +++=20b/src/include/libpq/hba.h=0A@@=20-151,6=20+151,25=20@@=20typedef=20= struct=20IdentLine=0A=20=09AuthToken=20=20*pg_user;=0A=20}=20IdentLine;=0A= =20=0A+typedef=20struct=20HostsLine=0A+{=0A+=09int=09=09=09linenumber;=0A= +=0A+=09char=09=20=20=20*sourcefile;=0A+=09char=09=20=20=20*rawline;=0A+=0A= +=09/*=20Required=20fields=20*/=0A+=09bool=09=09default_host;=0A+=09char=09= =20=20=20*hostname;=0A+=09char=09=20=20=20*ssl_key;=0A+=09char=09=20=20=20= *ssl_cert;=0A+=09char=09=20=20=20*ssl_ca;=0A+=0A+=09/*=20Optional=20= fields=20*/=0A+=09char=09=20=20=20*ssl_passphrase_cmd;=0A+=09bool=09=09= ssl_passphrase_reload;=0A+}=20HostsLine;=0A+=0A=20/*=0A=20=20*=20= TokenizedAuthLine=20represents=20one=20line=20lexed=20from=20an=20= authentication=0A=20=20*=20configuration=20file.=20=20Each=20item=20in=20= the=20"fields"=20list=20is=20a=20sub-list=20of=0Adiff=20--git=20= a/src/include/libpq/libpq-be.h=20b/src/include/libpq/libpq-be.h=0Aindex=20= 7fe92b15477..a5f07aff046=20100644=0A---=20a/src/include/libpq/libpq-be.h=0A= +++=20b/src/include/libpq/libpq-be.h=0A@@=20-323,6=20+323,7=20@@=20= extern=20const=20char=20*be_tls_get_cipher(Port=20*port);=0A=20extern=20= void=20be_tls_get_peer_subject_name(Port=20*port,=20char=20*ptr,=20= size_t=20len);=0A=20extern=20void=20be_tls_get_peer_issuer_name(Port=20= *port,=20char=20*ptr,=20size_t=20len);=0A=20extern=20void=20= be_tls_get_peer_serial(Port=20*port,=20char=20*ptr,=20size_t=20len);=0A= +extern=20bool=20be_tls_loaded_verify_locations(void);=0A=20=0A=20/*=0A=20= =20*=20Get=20the=20server=20certificate=20hash=20for=20SCRAM=20channel=20= binding=20type=0A@@=20-335,7=20+336,7=20@@=20extern=20char=20= *be_tls_get_certificate_hash(Port=20*port,=20size_t=20*len);=0A=20=0A=20= /*=20init=20hook=20for=20SSL,=20the=20default=20sets=20the=20password=20= callback=20if=20appropriate=20*/=0A=20#ifdef=20USE_OPENSSL=0A-typedef=20= void=20(*openssl_tls_init_hook_typ)=20(SSL_CTX=20*context,=20bool=20= isServerStart);=0A+typedef=20void=20(*openssl_tls_init_hook_typ)=20= (SSL_CTX=20*context,=20bool=20isServerStart,=20HostsLine=20*host);=0A=20= extern=20PGDLLIMPORT=20openssl_tls_init_hook_typ=20= openssl_tls_init_hook;=0A=20#endif=0A=20=0Adiff=20--git=20= a/src/include/libpq/libpq.h=20b/src/include/libpq/libpq.h=0Aindex=20= aeb66ca40cf..5feed0eb0a4=20100644=0A---=20a/src/include/libpq/libpq.h=0A= +++=20b/src/include/libpq/libpq.h=0A@@=20-107,6=20+107,7=20@@=20extern=20= PGDLLIMPORT=20char=20*ssl_crl_dir;=0A=20extern=20PGDLLIMPORT=20char=20= *ssl_key_file;=0A=20extern=20PGDLLIMPORT=20int=20= ssl_min_protocol_version;=0A=20extern=20PGDLLIMPORT=20int=20= ssl_max_protocol_version;=0A+extern=20PGDLLIMPORT=20int=20ssl_snimode;=0A= =20extern=20PGDLLIMPORT=20char=20*ssl_passphrase_command;=0A=20extern=20= PGDLLIMPORT=20bool=20ssl_passphrase_command_supports_reload;=0A=20extern=20= PGDLLIMPORT=20char=20*ssl_dh_params_file;=0A@@=20-134,12=20+135,20=20@@=20= enum=20ssl_protocol_versions=0A=20=09PG_TLS1_3_VERSION,=0A=20};=0A=20=0A= +enum=20ssl_snimode=0A+{=0A+=09SSL_SNIMODE_OFF=20=3D=200,=0A+=09= SSL_SNIMODE_DEFAULT,=0A+=09SSL_SNIMODE_STRICT=0A+};=0A+=0A=20/*=0A=20=20= *=20prototypes=20for=20functions=20in=20be-secure-common.c=0A=20=20*/=0A=20= extern=20int=09run_ssl_passphrase_command(const=20char=20*prompt,=20bool=20= is_server_start,=0A-=09=09=09=09=09=09=09=09=09=20=20=20char=20*buf,=20= int=20size);=0A+=09=09=09=09=09=09=09=09=09=20=20=20char=20*buf,=20int=20= size,=20void=20*userdata);=0A=20extern=20bool=20= check_ssl_key_file_permissions(const=20char=20*ssl_key_file,=0A=20=09=09=09= =09=09=09=09=09=09=09=20=20=20bool=20isServerStart);=0A+extern=20List=20= *load_hosts(void);=0A=20=0A=20#endif=09=09=09=09=09=09=09/*=20LIBPQ_H=20= */=0Adiff=20--git=20a/src/include/utils/guc.h=20= b/src/include/utils/guc.h=0Aindex=201233e07d7da..37cb3ecb5ae=20100644=0A= ---=20a/src/include/utils/guc.h=0A+++=20b/src/include/utils/guc.h=0A@@=20= -288,6=20+288,7=20@@=20extern=20PGDLLIMPORT=20char=20*cluster_name;=0A=20= extern=20PGDLLIMPORT=20char=20*ConfigFileName;=0A=20extern=20PGDLLIMPORT=20= char=20*HbaFileName;=0A=20extern=20PGDLLIMPORT=20char=20*IdentFileName;=0A= +extern=20PGDLLIMPORT=20char=20*HostsFileName;=0A=20extern=20PGDLLIMPORT=20= char=20*external_pid_file;=0A=20=0A=20extern=20PGDLLIMPORT=20char=20= *application_name;=0Adiff=20--git=20= a/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=20= b/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=0Aindex=20= d5992149821..a85d85735cf=20100644=0A---=20= a/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=0A+++=20= b/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=0A@@=20= -26,7=20+26,7=20@@=20static=20char=20*ssl_passphrase=20=3D=20NULL;=0A=20= static=20int=09rot13_passphrase(char=20*buf,=20int=20size,=20int=20= rwflag,=20void=20*userdata);=0A=20=0A=20/*=20hook=20function=20to=20set=20= the=20callback=20*/=0A-static=20void=20set_rot13(SSL_CTX=20*context,=20= bool=20isServerStart);=0A+static=20void=20set_rot13(SSL_CTX=20*context,=20= bool=20isServerStart,=20HostsLine=20*host);=0A=20=0A=20/*=0A=20=20*=20= Module=20load=20callback=0A@@=20-53,7=20+53,7=20@@=20_PG_init(void)=0A=20= }=0A=20=0A=20static=20void=0A-set_rot13(SSL_CTX=20*context,=20bool=20= isServerStart)=0A+set_rot13(SSL_CTX=20*context,=20bool=20isServerStart,=20= HostsLine=20*host)=0A=20{=0A=20=09/*=20warn=20if=20the=20user=20has=20= set=20ssl_passphrase_command=20*/=0A=20=09if=20= (ssl_passphrase_command[0])=0Adiff=20--git=20a/src/test/ssl/meson.build=20= b/src/test/ssl/meson.build=0Aindex=20cf8b2b9303a..7a2a5b8ca8c=20100644=0A= ---=20a/src/test/ssl/meson.build=0A+++=20b/src/test/ssl/meson.build=0A@@=20= -13,6=20+13,7=20@@=20tests=20+=3D=20{=0A=20=20=20=20=20=20=20= 't/001_ssltests.pl',=0A=20=20=20=20=20=20=20't/002_scram.pl',=0A=20=20=20= =20=20=20=20't/003_sslinfo.pl',=0A+=20=20=20=20=20=20't/004_sni.pl',=0A=20= =20=20=20=20],=0A=20=20=20},=0A=20}=0Adiff=20--git=20= a/src/test/ssl/t/004_sni.pl=20b/src/test/ssl/t/004_sni.pl=0Anew=20file=20= mode=20100644=0Aindex=2000000000000..f0ce048273a=0A---=20/dev/null=0A+++=20= b/src/test/ssl/t/004_sni.pl=0A@@=20-0,0=20+1,164=20@@=0A+=0A+#=20= Copyright=20(c)=202024,=20PostgreSQL=20Global=20Development=20Group=0A+=0A= +use=20strict;=0A+use=20warnings=20FATAL=20=3D>=20'all';=0A+=0A+use=20= PostgreSQL::Test::Cluster;=0A+use=20PostgreSQL::Test::Utils;=0A+use=20= Test::More;=0A+=0A+use=20FindBin;=0A+use=20lib=20$FindBin::RealBin;=0A+=0A= +use=20SSL::Server;=0A+=0A+#=20This=20is=20the=20hostname=20used=20to=20= connect=20to=20the=20server.=20This=20cannot=20be=20a=0A+#=20hostname,=20= because=20the=20server=20certificate=20is=20always=20for=20the=20domain=0A= +#=20postgresql-ssl-regression.test.=0A+my=20$SERVERHOSTADDR=20=3D=20= '127.0.0.1';=0A+#=20This=20is=20the=20pattern=20to=20use=20in=20= pg_hba.conf=20to=20match=20incoming=20connections.=0A+my=20= $SERVERHOSTCIDR=20=3D=20'127.0.0.1/32';=0A+=0A+if=20($ENV{with_ssl}=20ne=20= 'openssl')=0A+{=0A+=09plan=20skip_all=20=3D>=20'OpenSSL=20not=20= supported=20by=20this=20build';=0A+}=0A+=0A+if=20(!$ENV{PG_TEST_EXTRA}=20= ||=20$ENV{PG_TEST_EXTRA}=20!~=20/\bssl\b/)=0A+{=0A+=09plan=20skip_all=20= =3D>=0A+=09=20=20'Potentially=20unsafe=20test=20SSL=20not=20enabled=20in=20= PG_TEST_EXTRA';=0A+}=0A+=0A+my=20$ssl_server=20=3D=20SSL::Server->new();=0A= +=0A+my=20$node=20=3D=20PostgreSQL::Test::Cluster->new('primary');=0A= +$node->init;=0A+=0A+#=20PGHOST=20is=20enforced=20here=20to=20set=20up=20= the=20node,=20subsequent=20connections=0A+#=20will=20use=20a=20dedicated=20= connection=20string.=0A+$ENV{PGHOST}=20=3D=20$node->host;=0A= +$ENV{PGPORT}=20=3D=20$node->port;=0A+$node->start;=0A+=0A= +$ssl_server->configure_test_server_for_ssl($node,=20$SERVERHOSTADDR,=0A= +=09$SERVERHOSTCIDR,=20'trust');=0A+=0A= +$ssl_server->switch_server_cert($node,=20certfile=20=3D>=20= 'server-cn-only');=0A+=0A+my=20$connstr=20=3D=0A+=20=20"user=3Dssltestuser= =20dbname=3Dtrustdb=20hostaddr=3D$SERVERHOSTADDR=20host=3Dlocalhost=20= sslsni=3D1";=0A+=0A+$node->append_conf('postgresql.conf',=20= "ssl_snimode=3Ddefault");=0A+$node->reload;=0A+=0A+$node->connect_ok(=0A= +=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",= =0A+=09"connect=20with=20correct=20server=20CA=20cert=20file=20= sslmode=3Drequire");=0A+=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dssl/root_ca.crt=20sslmode=3Dverify-ca",=0A+=09"connect=20= fails=20with=20fallback=20hostname,=20without=20intermediate",=0A+=09= expected_stderr=20=3D>=20qr/certificate=20verify=20failed/);=0A+=0A+#=20= example.org=20serves=20the=20server=20cert=20and=20its=20intermediate=20= CA.=0A+$node->append_conf('pg_hosts.conf',=0A+=09"example.org=20= server-cn-only+server_ca.crt=20server-cn-only.key=20root_ca.crt"=0A+);=0A= +$node->reload;=0A+=0A+$node->connect_ok(=0A+=09"$connstr=20= host=3Dexample.org=20sslrootcert=3Dssl/root_ca.crt=20sslmode=3Dverify-ca",= =0A+=09"connect=20with=20configured=20hostname,=20serving=20intermediate=20= server=20CA");=0A+=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dinvalid=20sslmode=3Dverify-ca",=0A+=09"connect=20without=20= server=20root=20cert=20sslmode=3Dverify-ca",=0A+=09expected_stderr=20=3D>=20= qr/root=20certificate=20file=20"invalid"=20does=20not=20exist/);=0A+=0A= +$node->connect_fails(=0A+=09"$connstr=20sslrootcert=3Dssl/root_ca.crt=20= sslmode=3Dverify-ca",=0A+=09"connect=20still=20fails=20with=20fallback=20= hostname,=20without=20intermediate",=0A+=09expected_stderr=20=3D>=20= qr/certificate=20verify=20failed/);=0A+=0A+$node->connect_ok(=0A+=09= "$connstr=20host=3Dlocalhost=20sslrootcert=3Dssl/root+server_ca.crt=20= sslmode=3Dverify-ca",=0A+=09"connect=20with=20fallback=20hostname,=20= intermediate=20included");=0A+=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf('pg_hosts.conf',=0A+=09= "localhost=20server-cn-only.crt=20server-cn-only.key=20root_ca.crt");=0A= +$node->append_conf('postgresql.conf',=20"ssl_snimode=3Dstrict");=0A= +$node->reload;=0A+=0A+$node->connect_fails(=0A+=09"$connstr=20= host=3Dexample.org=20sslrootcert=3Dssl/root+server_ca.crt=20= sslmode=3Drequire",=0A+=09"connect=20with=20missing=20hostconfig=20and=20= snimode=3Dstruct",=0A+=09expected_stderr=20=3D>=20qr/tlsv1=20= unrecognized=20name/);=0A+=0A+$node->connect_ok(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20sslsni=3D1",=0A= +=09"connect=20with=20correct=20server=20CA=20cert=20file=20= sslmode=3Drequire");=0A+=0A+#=20Attempts=20at=20connecting=20without=20= SNI=20when=20the=20server=20is=20using=20strict=20mode=20should=0A+#=20= result=20in=20connection=20failure.=0A+$node->connect_fails(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= sslsni=3D0",=0A+=09"connect=20with=20correct=20server=20CA=20cert=20file=20= without=20SNI=20for=20strict=20mode",=0A+=09expected_stderr=20=3D>=20= qr/tlsv1=20unrecognized=20name/);=0A+=0A+#=20Reconfigure=20with=20broken=20= configuration=20for=20the=20key=20passphrase,=20the=20server=0A+#=20= should=20not=20start=20up=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf('pg_hosts.conf',=0A+=09= 'localhost=20server-cn-only.crt=20server-password.key=20= root+client_ca.crt=20"echo=20wrongpassword"=20on'=0A+);=0A+my=20$result=20= =3D=20$node->restart(fail_ok=20=3D>=201);=0A+is($result,=200,=0A+=09= 'restart=20fails=20with=20password-protected=20key=20when=20using=20the=20= wrong=20passphrase=20command'=0A+);=0A+=0A+#=20Reconfigure=20again=20but=20= with=20the=20correct=20passphrase=20set=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf('pg_hosts.conf',=0A+=09= 'localhost=20server-cn-only.crt=20server-password.key=20= root+client_ca.crt=20"echo=20secret1"=20on'=0A+);=0A+$result=20=3D=20= $node->restart(fail_ok=20=3D>=201);=0A+is($result,=201,=0A+=09'restart=20= succeeds=20with=20password-protected=20key=20when=20using=20the=20= correct=20passphrase=20command'=0A+);=0A+=0A+#=20Make=20sure=20= connecting=20works,=20and=20try=20to=20stress=20the=20reload=20logic=20= by=20issuing=0A+#=20subsequent=20reloads=0A+$node->connect_ok(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A= +=09"connect=20with=20correct=20server=20CA=20cert=20file=20= sslmode=3Drequire");=0A+$node->reload;=0A+$node->reload;=0A= +$node->connect_ok(=0A+=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt= =20sslmode=3Drequire",=0A+=09"1=20connect=20with=20correct=20server=20CA=20= cert=20file=20sslmode=3Drequire");=0A+=0A+#=20Test=20reloading=20a=20= passphrase=20protected=20key=20without=20reloading=20support=20in=20the=0A= +#=20passphrase=20hook.=20Connecting=20after=20restart=20should=20= succeed=20but=20not=20after=20the=0A+#=20following=20reload.=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf('pg_hosts.conf',=0A+=09'localhost=20= server-cn-only.crt=20server-password.key=20root+client_ca.crt=20"echo=20= secret1"=20off'=0A+);=0A+$result=20=3D=20$node->restart(fail_ok=20=3D>=20= 1);=0A+is($result,=201,=0A+=09'restart=20succeeds=20with=20= password-protected=20key=20when=20using=20the=20correct=20passphrase=20= command'=0A+);=0A+$node->connect_ok(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A+=09= "connect=20with=20correct=20server=20CA=20cert=20file=20= sslmode=3Drequire");=0A+=0A+$node->reload;=0A+$node->connect_fails(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A= +=09"connect=20fails=20since=20the=20passphrase=20protected=20key=20= cannot=20be=20reloaded",=0A+=09expected_stderr=20=3D>=20qr/tlsv1=20= unrecognized=20name/);=0A+=0A+done_testing();=0Adiff=20--git=20= a/src/tools/pgindent/typedefs.list=20b/src/tools/pgindent/typedefs.list=0A= index=20cfbab589d61..96c91831feb=20100644=0A---=20= a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-1168,6=20+1168,8=20@@=20= HeapTupleHeader=0A=20HeapTupleHeaderData=0A=20HeapTupleTableSlot=0A=20= HistControl=0A+HostContext=0A+HostsLine=0A=20HotStandbyState=0A=20I32=0A=20= ICU_Convert_Func=0A--=20=0A2.39.3=20(Apple=20Git-146)=0A=0A= --Apple-Mail=_00218A96-8338-41F4-A13A-9EB3F2339350--