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 1tktFd-007eDq-7f for pgsql-hackers@arkaria.postgresql.org; Wed, 19 Feb 2025 23:13:09 +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 1tktFb-00FCmK-Io for pgsql-hackers@arkaria.postgresql.org; Wed, 19 Feb 2025 23:13:07 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tktFa-00FCm7-SZ for pgsql-hackers@lists.postgresql.org; Wed, 19 Feb 2025 23:13:07 +0000 Received: from smtp.outgoing.loopia.se ([93.188.3.37]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tktFY-001nUC-0c for pgsql-hackers@lists.postgresql.org; Wed, 19 Feb 2025 23:13:06 +0000 Received: from s807.loopia.se (localhost [127.0.0.1]) by s807.loopia.se (Postfix) with ESMTP id C665129829F for ; Thu, 20 Feb 2025 00:13:01 +0100 (CET) Received: from s979.loopia.se (unknown [172.22.191.6]) by s807.loopia.se (Postfix) with ESMTP id AC7C7297ADB; Thu, 20 Feb 2025 00:13:01 +0100 (CET) Received: from s473.loopia.se (unknown [172.22.191.5]) by s979.loopia.se (Postfix) with ESMTP id A922210BC44D; Thu, 20 Feb 2025 00:13:01 +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: s473.loopia.se (amavisd-new); dkim=pass (2048-bit key) header.d=yesql.se Received: from s981.loopia.se ([172.22.191.6]) by s473.loopia.se (s473.loopia.se [172.22.190.13]) (amavisd-new, port 10024) with LMTP id NJ5OFLvxv37j; Thu, 20 Feb 2025 00:13:01 +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 s981.loopia.se (Postfix) with ESMTPSA id 1547E22B1658; Thu, 20 Feb 2025 00:13:01 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yesql.se; s=loopiadkim1707475645; t=1740006781; bh=Sj1PhcZKFNPcxXkhfZzTRt5wMDQhTeJu77eOjedNO+k=; h=From:Subject:Date:References:To:In-Reply-To; b=rwJuDh0rmytj3Vzl6qPz3NyHD2ojvwHoAnrf/ZeGV5uVx+FWrR26xCGOf7s6fMDT9 zJ+phV+CRIvGZ/nsmhm68N6J82f78EbMJva29zQkkEfv5Z9deBsw9vIfLa7verQrkM bN2DHwYNzb869hewvIwS3QTI26b4Xy30yhXrQarhEAXKhipDh5J2fkW4Fe/lyUIy0T WAKkq7zdvvFR1+skjsF4DVxuZ8SQV5l5luAbu/pTDEzyFRYaN3djjRVyaGjV6IHRuR AnFD7XuXa4G82mqdQe4UHpstUBAK70wN3+J0ud49GoLlN7od6IAviEimr7QfiCuG9b 7I6LGG+3G1KDA== From: Daniel Gustafsson Content-Type: multipart/mixed; boundary="Apple-Mail=_1ADC1EBF-4F29-474E-B875-C0357C1E6354" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51.11.1\)) Subject: Re: Serverside SNI support in libpq Date: Thu, 20 Feb 2025 00:12:50 +0100 References: <1C81CD0D-407E-44F9-833A-DD0331C202E5@yesql.se> <88986722-5A72-4DEC-8750-BDBF67FF8C01@yesql.se> <7E77028B-5A3A-436B-9046-8E9992E9F94A@yesql.se> To: Michael Paquier , Jacob Champion , Pgsql Hackers In-Reply-To: Message-Id: 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=_1ADC1EBF-4F29-474E-B875-C0357C1E6354 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Attached is a rebase which fixes a few smaller things (and a pgperltidy = run); and adds a paragraph to the docs about how HBA clientname settings can't = be made per certificate set in an SNI config. As discussed with Jacob = offlist, there might be a case for supporting that but it will be a niche usecase = within a niche feature, so rather than complicating the code for something = which might never be used, it's likely better to document it and await feedback. Are there any blockers for getting this in? -- Daniel Gustafsson --Apple-Mail=_1ADC1EBF-4F29-474E-B875-C0357C1E6354 Content-Disposition: attachment; filename=v5-0001-Serverside-SNI-support-for-libpq.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v5-0001-Serverside-SNI-support-for-libpq.patch" Content-Transfer-Encoding: quoted-printable =46rom=20ed0a6b24a686b85077643dc8d3617957782eac11=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Mon,=204=20Nov=202024=2013:52:23=20= +0100=0ASubject:=20[PATCH=20v5]=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=2062=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=2057=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|=20307=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= 5=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|=20128=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,=20839=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=209eedcf6f0f4..a10a1a24890=20100644=0A= ---=20a/doc/src/sgml/config.sgml=0A+++=20b/doc/src/sgml/config.sgml=0A@@=20= -1652,6=20+1652,68=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=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=20a/doc/src/sgml/runtime.sgml=20b/doc/src/sgml/runtime.sgml=0A= index=2059f39e89924..57dfe972100=20100644=0A---=20= a/doc/src/sgml/runtime.sgml=0A+++=20b/doc/src/sgml/runtime.sgml=0A@@=20= -2444,6=20+2444,12=20@@=20pg_dumpall=20-p=205432=20|=20psql=20-d=20= postgres=20-p=205433=0A=20=20=20=20=20=20=20client=20certificate=20= must=20not=20be=20on=20this=20list=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,57=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= =20=20It=20is=20currently=20not=20possible=20to=20set=20different=20= clientname=0A+=20=20=20=20=20values=20for=20the=20= different=20certificates.=20=20Any=20clientname=0A+=20= =20=20=20=20setting=20in=20pg_hba.conf=20will=20be=20= applied=20during=0A+=20=20=20=20=20authentication=20regardless=20of=20= which=20set=20of=20certificates=20have=20been=20loaded=0A+=20=20=20=20=20= via=20an=20SNI=20enabled=20connection.=0A+=20=20=20=0A+=20=20= =0A=20=20=0A=20=0A=20=20=0A= diff=20--git=20a/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..bb3e724a190=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,16=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*Host_context=20=3D=20= NULL;=0A=20static=20bool=20dummy_ssl_passwd_cb_called=20=3D=20false;=0A=20= static=20bool=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+110,147=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+=0A+=09= /*=0A+=09=20*=20If=20there=20are=20contexts=20loaded=20when=20we=20init=20= they=20should=20be=20released.=0A+=09=20*/=0A+=09if=20(contexts=20!=3D=20= NIL)=0A+=09=09free_contexts();=0A+=0A+=09/*=0A+=09=20*=20When=20= ssl_snimode=20is=20off=20or=20default=20we=20load=20the=20SSL=20= configuration=0A+=09=20*=20specified=20in=20postgresql.conf=20and=20set=20= that=20as=20the=20default=20host.=0A+=09=20*/=0A+=09if=20(ssl_snimode=20= =3D=3D=20SSL_SNIMODE_OFF=20||=20ssl_snimode=20=3D=3D=20= SSL_SNIMODE_DEFAULT)=0A+=09{=0A+=09=09HostContext=20*host_context;=0A+=09= =09HostsLine=09line;=0A+=0A+=09=09line.ssl_cert=20=3D=20ssl_cert_file;=0A= +=09=09line.ssl_key=20=3D=20ssl_key_file;=0A+=09=09line.ssl_ca=20=3D=20= ssl_ca_file;=0A+=09=09line.ssl_passphrase_cmd=20=3D=20= ssl_passphrase_command;=0A+=09=09line.ssl_passphrase_reload=20=3D=20= ssl_passphrase_command_supports_reload;=0A+=0A+=09=09ctx=20=3D=20= ssl_init_context(isServerStart,=20&line);=0A+=09=09if=20(ctx=20=3D=3D=20= NULL)=0A+=09=09{=0A+=09=09=09ereport(isServerStart=20?=20FATAL=20:=20= LOG,=0A+=09=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09= =09=20errmsg("could=20not=20load=20default=20certificate")));=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+=0A+=09=09host_context=20=3D=20= palloc0(sizeof(HostContext));=0A+=0A+=09=09host_context->hostname=20=3D=20= pstrdup("*");=0A+=09=09host_context->context=20=3D=20ctx;=0A+=09=09= host_context->default_host=20=3D=20true;=0A+=0A+=09=09/*=0A+=09=09=20*=20= Set=20flag=20to=20remember=20whether=20CA=20store=20has=20been=20loaded=20= into=0A+=09=09=20*=20SSL_context.=0A+=09=09=20*/=0A+=09=09if=20= (ssl_ca_file[0])=0A+=09=09=09host_context->ssl_loaded_verify_locations=20= =3D=20true;=0A+=0A+=09=09/*=0A+=09=09=20*=20The=20contexts=20list=20is=20= not=20used=20in=20ssl_snimode=20off=20but=20we=20add=20the=0A+=09=09=20*=20= entry=20there=20anyways=20for=20consistency=20with=20the=20other=20= modes.=0A+=09=09=20*/=0A+=09=09contexts=20=3D=20lappend(contexts,=20= host_context);=0A+=0A+=09=09/*=0A+=09=09=20*=20Install=20the=20default=20= certificate=20which=20for=20ssl_snimode=20default=20can=0A+=09=09=20*=20= be=20overridden=20in=20the=20callback=20if=20a=20hostname=20match=20is=20= found.=0A+=09=09=20*/=0A+=09=09SSL_context=20=3D=20ctx;=0A+=09=09= Host_context=20=3D=20host_context;=0A+=09}=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.=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/*=20In=20strict=20ssl_snimode=20there=20needs=20to=20be=20a=20= working=20pg_hosts=20file=20*/=0A+=09=09if=20(sni_hosts=20=3D=3D=20NIL=20= &&=20ssl_snimode=20=3D=3D=20SSL_SNIMODE_STRICT)=0A+=09=09{=0A+=09=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09errmsg("could=20= not=20load=20%s",=20"pg_hosts.conf"));=0A+=09=09=09return=20-1;=0A+=09=09= }=0A+=0A+=09=09foreach(line,=20sni_hosts)=0A+=09=09{=0A+=09=09=09= HostContext=20*host_context;=0A+=09=09=09HostsLine=20=20*host=20=3D=20= lfirst(line);=0A+=0A+=09=09=09SSL_context=20=3D=20= ssl_init_context(isServerStart,=20host);=0A+=09=09=09if=20(SSL_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=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= SSL_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+276,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+294,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= +312,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+476,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+558,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-1132,7=20+1280,7=20@@=20ssl_external_passwd_cb(char=20*buf,=20= int=20size,=20int=20rwflag,=20void=20*userdata)=0A=20=0A=20=09= Assert(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+1517,60=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+=09ListCell=20=20=20= *cell;=0A+=09HostContext=20*host_context;=0A+=0A+=09Assert(ssl_snimode=20= !=3D=20SSL_SNIMODE_OFF);=0A+=0A+=09tlsext_hostname=20=3D=20= SSL_get_servername(ssl,=20TLSEXT_NAMETYPE_host_name);=0A+=0A+=09if=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=09return=20= SSL_TLSEXT_ERR_OK;=0A+=09}=0A+=0A+=09foreach(cell,=20contexts)=0A+=09{=0A= +=09=09host_context=20=3D=20lfirst(cell);=0A+=0A+=09=09if=20= (strcmp(host_context->hostname,=20tlsext_hostname)=20=3D=3D=200)=0A+=09=09= {=0A+=09=09=09Host_context=20=3D=20host_context;=0A+=09=09=09SSL_context=20= =3D=20host_context->context;=0A+=09=09=09SSL_set_SSL_CTX(ssl,=20= SSL_context);=0A+=09=09=09return=20SSL_TLSEXT_ERR_OK;=0A+=09=09}=0A+=09}=0A= +=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=20can=20return=20without=20doing=20= anything=20since=20we=0A+=09=20*=20already=20installed=20the=20context=20= for=20the=20default=20host=20when=20parsing=20the=0A+=09=20*=20hosts=20= file.=0A+=09=20*/=0A+=09Assert(SSL_context);=0A+=09return=20= SSL_TLSEXT_ERR_OK;=0A+}=0A=20=0A=20/*=0A=20=20*=20Set=20DH=20parameters=20= for=20generating=20ephemeral=20DH=20keys.=20=20The=0A@@=20-1578,6=20= +1780,12=20@@=20be_tls_get_peer_serial(Port=20*port,=20char=20*ptr,=20= size_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+1979,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+2007,22=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+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=200f0421037e4..aed5ec16af2=20= 100644=0A---=20a/src/backend/libpq/meson.build=0A+++=20= b/src/backend/libpq/meson.build=0A@@=20-30,5=20+30,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..608210686e1=0A---=20/dev/null=0A+++=20= b/src/backend/libpq/pg_hosts.conf.sample=0A@@=20-0,0=20+1,5=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=0A+=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=203cde94a1759..002e12fe1e4=20= 100644=0A---=20a/src/backend/utils/misc/guc_tables.c=0A+++=20= b/src/backend/utils/misc/guc_tables.c=0A@@=20-474,6=20+474,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= -538,6=20+545,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-4622,6=20+4630,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-5264,6=20+5283,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= 415f253096c..dec30a53a35=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=0A=20= #-------------------------------------------------------------------------= -----=0Adiff=20--git=20a/src/bin/initdb/initdb.c=20= b/src/bin/initdb/initdb.c=0Aindex=2021a0fe3ecd9..a6e680f7b44=20100644=0A= ---=20a/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= b20d0051f7d..a1ea3cf3e8c=20100644=0A---=20a/src/include/libpq/hba.h=0A= +++=20b/src/include/libpq/hba.h=0A@@=20-146,6=20+146,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= 8defcb6de19..6d9332cbe22=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..0542c59ebcb=0A---=20/dev/null=0A+++=20= b/src/test/ssl/t/004_sni.pl=0A@@=20-0,0=20+1,128=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"dbname=3Dtrustdb=20= hostaddr=3D$SERVERHOSTADDR=20host=3Dlocalhost=20sslsni=3D1";=0A+=0A= +$node->append_conf('postgresql.conf',=20"ssl_snimode=3Ddefault");=0A= +$node->reload;=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+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf('pg_hosts.conf',=0A+=09= "localhost=20server.crt=20server.key=20root.crt");=0A= +$node->append_conf('postgresql.conf',=20"ssl_snimode=3Dstrict");=0A= +$node->reload;=0A+=0A+$node->connect_fails(=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+=09expected_stderr=20=3D>=20qr/unexpected=20= eof/);=0A+=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-cn-only.key=20root_ca.crt");=0A= +$node->reload;=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+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+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+$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->reload;=0A+$node->connect_ok(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A= +=09"1=20connect=20with=20correct=20server=20CA=20cert=20file=20= sslmode=3Drequire");=0A+=0A+#=20Test=20reloading=20a=20passphrase=20= protected=20key=20without=20reloading=20support=20in=20the=0A+#=20= passphrase=20hook.=20Connecting=20after=20restart=20should=20succeed=20= but=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/unexpected=20= eof/);=0A+=0A+done_testing();=0Adiff=20--git=20= a/src/tools/pgindent/typedefs.list=20b/src/tools/pgindent/typedefs.list=0A= index=20fb39c915d76..4410bb24d53=20100644=0A---=20= a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-1165,6=20+1165,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=_1ADC1EBF-4F29-474E-B875-C0357C1E6354--