Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w0h9E-00289L-0j for pgsql-hackers@arkaria.postgresql.org; Thu, 12 Mar 2026 14:36:25 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w0h9A-00FNar-2d for pgsql-hackers@arkaria.postgresql.org; Thu, 12 Mar 2026 14:36:21 +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.96) (envelope-from ) id 1w0h99-00FNaj-2p for pgsql-hackers@lists.postgresql.org; Thu, 12 Mar 2026 14:36:20 +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.98.2) (envelope-from ) id 1w0h96-00000001mYs-2yIv for pgsql-hackers@lists.postgresql.org; Thu, 12 Mar 2026 14:36:19 +0000 Received: from s807.loopia.se (localhost [127.0.0.1]) by s807.loopia.se (Postfix) with ESMTP id 7DB6557B716 for ; Thu, 12 Mar 2026 15:36:11 +0100 (CET) Received: from s934.loopia.se (unknown [172.22.191.5]) by s807.loopia.se (Postfix) with ESMTP id 6777D57A7B1; Thu, 12 Mar 2026 15:36:11 +0100 (CET) Received: from localhost (unknown [172.22.191.6]) by s934.loopia.se (Postfix) with ESMTP id 5DD447CEAAC; Thu, 12 Mar 2026 15:36:11 +0100 (CET) X-Virus-Scanned: amavis 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: s474.loopia.se (amavis); dkim=pass (2048-bit key) header.d=yesql.se Received: from s979.loopia.se ([172.22.191.6]) by localhost (s474.loopia.se [172.22.190.14]) (amavis, port 10024) with UTF8LMTP id u4s0sxdjq1Yu; Thu, 12 Mar 2026 15:36:10 +0100 (CET) X-Loopia-Auth: user X-Loopia-User: daniel@yesql.se X-Loopia-Originating-IP: 89.255.232.236 Received: from smtpclient.apple (customer-89-255-232-236.stosn.net [89.255.232.236]) (Authenticated sender: daniel@yesql.se) by s979.loopia.se (Postfix) with ESMTPSA id 9BA2910BC44E; Thu, 12 Mar 2026 15:36:10 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yesql.se; s=loopiadkim1707475645; t=1773326170; bh=fLu0tZhKCQQE7OE1PkCmexVSCpDVCi3xB+1pD1/z0e4=; h=From:Subject:Date:In-Reply-To:Cc:To:References; b=utrVLKK5FD+FiIoNZKh3ydE0FvM2G2xPtQyDRHrc8FJChc4q+TYDzmBJq6TQISKxY /pxUAjTLLJ01xOdBs6tiVfMzgejfSKTKLh8nXCtxtb6TrqTH5rB8qIY4UBzWooDH/1 1yIlWipW/Kj+kySNFkagqakr3skf0OW7HBdlp6yAqz3Uj+wwPKJbZhERVc+bHLowFU TvSZwkNyfch81qCi0Im5stzp2BFc3ruiOidTrlSF1iEbccFgWf+hTIksKWLITL9xsr xxOY/cPs/3p3J9uMy91314Fppy9uBM4f+6YlOkgOR900yHHeQgWiWGYdJQz/GSMCIK fQr5fq2NurxSQ== From: Daniel Gustafsson Message-Id: Content-Type: multipart/mixed; boundary="Apple-Mail=_EBCF6E35-AEFD-4AE0-81FB-BCF50C94834B" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51.11.2\)) Subject: Re: Serverside SNI support in libpq Date: Thu, 12 Mar 2026 15:36:00 +0100 In-Reply-To: Cc: Jelte Fennema-Nio , Heikki Linnakangas , Dewei Dai , "li.evan.chao" , Michael Paquier , Andres Freund , Pgsql Hackers To: Jacob Champion References: <88986722-5A72-4DEC-8750-BDBF67FF8C01@yesql.se> <7E77028B-5A3A-436B-9046-8E9992E9F94A@yesql.se> <0BC5B9B1-6503-4563-AAC6-33DEF264AE3F@yesql.se> <80F4F8F4-8E4F-4B6F-866B-D837057C1192@yesql.se> <0C53C316-C24E-4307-807B-D825CA3F7254@yesql.se> <378D83FA-338C-4EA1-BC60-397BE08D0F01@yesql.se> <2025112617144938459246@163.com> <0217DEFA-9684-4A77-A005-D30EBEF155C4@yesql.se> <5D0E78E0-EA79-480E-ABD3-B1EF0156BF8B@yesql.se> <785C0B88-7068-4576-AF55-251D06CEC112@yesql.se> <01412917-C42E-4238-97E2-707C32940DDD@yesql.se> X-Mailer: Apple Mail (2.3776.700.51.11.2) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_EBCF6E35-AEFD-4AE0-81FB-BCF50C94834B Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii > On 10 Mar 2026, at 14:11, Daniel Gustafsson wrote: >=20 > The attached rebase adds a check, and testcase, for duplicated = hostname entries > in pg_hosts.conf and errors out in case a host is configured multiple = times. And another small update to SKIP the newly added tests on LibreSSL since = they use sslmode require which is only available in OpenSSL. No other = changes to the patchset in this version (apart from a freshly brewed rebase of = course). -- Daniel Gustafsson --Apple-Mail=_EBCF6E35-AEFD-4AE0-81FB-BCF50C94834B Content-Disposition: attachment; filename=v17-0002-ssl-Serverside-SNI-support-for-libpq.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v17-0002-ssl-Serverside-SNI-support-for-libpq.patch" Content-Transfer-Encoding: quoted-printable =46rom=20477cba1c0dfcf697d4e598f20fbe0e748a4f4942=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Fri,=206=20Mar=202026=2023:00:51=20= +0100=0ASubject:=20[PATCH=20v17=202/2]=20ssl:=20Serverside=20SNI=20= support=20for=20libpq=0A=0ASupport=20for=20SNI=20was=20added=20to=20= clientside=20libpq=20in=205c55dc8b4733=20with=20the=0Asslsni=20= parameter,=20but=20there=20was=20no=20support=20for=20utilizing=20it=20= serverside.=0AThis=20adds=20support=20for=20serverside=20SNI=20such=20= that=20certificate/key=20handling=0Ais=20available=20per=20host.=20=20A=20= new=20config=20file,=20$datadir/pg_hosts.conf,=20is=0Aused=20for=20= configuring=20which=20certificate=20and=20key=20should=20be=20used=20for=20= which=0Ahostname.=20=20In=20order=20to=20use=20SNI=20the=20ssl_sni=20GUC=20= must=20be=20set=20to=20on,=20when=0Ait=20is=20off=20the=20ssl=20= configuration=20works=20just=20like=20before.=20=20If=20ssl_sni=20is=0A= enabled=20and=20pg_hosts.conf=20is=20non-empty=20it=20will=20take=20= precedence=20over=0Athe=20regular=20SSL=20GUCs,=20if=20it=20is=20empty=20= or=20missing=20the=20regular=20GUCs=20will=0Abe=20used=20just=20as=20= before=20this=20commit=20with=20no=20hostname=20specific=20handling.=0A=0A= Host=20configuration=20can=20either=20be=20for=20a=20literal=20hostname=20= to=20match,=20non-=0ASNI=20connections=20using=20the=20no_sni=20keyword=20= or=20a=20default=20fallback=20matching=0Aall=20connections.=20=20By=20= omitting=20no_sni=20and=20the=20fallback=20a=20strict=20mode=0Acan=20be=20= achieved=20where=20only=20connections=20using=20sslsni=3D1=20and=20a=20= specified=0Ahostname=20are=20allowed.=0A=0ACRL=20file(s)=20are=20applied=20= from=20postgresql.conf=20to=20all=20configured=20hostnames.=0A=0A= Serverside=20SNI=20requires=20OpenSSL,=20currently=20LibreSSL=20does=20= not=20support=0Athe=20required=20infrastructure=20to=20update=20the=20= SSL=20context=20during=20the=20TLS=0Ahandshake.=0A=0AAuthor:=20Daniel=20= Gustafsson=20=0ACo-authored-by:=20Jacob=20Champion=20= =0AReviewed-by:=20Jacob=20Champion=20= =0AReviewed-by:=20Chao=20Li=20= =0AReviewed-by:=20Dewei=20Dai=20= =0AReviewed-by:=20Cary=20Huang=20= =0AReviewed-by:=20Heikki=20Linnakangas=20= =0ADiscussion:=20= https://postgr.es/m/1C81CD0D-407E-44F9-833A-DD0331C202E5@yesql.se=0A---=0A= =20configure=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=20=20=20=20=20=20=20=20=20|=20=20=202=20+-=0A=20= configure.ac=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=20=20=20=20=20=20|=20=20=202=20+-=0A=20= doc/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|=20123=20+++=0A=20meson.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=20=20=20=20=20=20=20=20=20=20=20= |=20=20=201=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=202=20+=0A=20= src/backend/commands/variable.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|=20=2021=20+=0A=20src/backend/libpq/be-secure-common.c=20=20=20=20=20= =20=20=20=20=20|=20258=20+++++-=0A=20= src/backend/libpq/be-secure-openssl.c=20=20=20=20=20=20=20=20=20|=20846=20= ++++++++++++++++--=0A=20src/backend/libpq/be-secure.c=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20|=20=20=203=20+=0A=20= src/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=20src/backend/libpq/pg_hosts.conf.sample=20=20=20= =20=20=20=20=20|=20=20=204=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=2032=20+=0A=20= src/backend/utils/misc/guc_parameters.dat=20=20=20=20=20|=20=2015=20+=0A=20= src/backend/utils/misc/guc_tables.c=20=20=20=20=20=20=20=20=20=20=20|=20=20= =201=20+=0A=20src/backend/utils/misc/postgresql.conf.sample=20|=20=20=20= 3=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=2015=20+-=0A=20= src/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=2030=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=20=205=20+-=0A=20= src/include/pg_config.h.in=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20|=20=20=203=20+=0A=20src/include/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= src/include/utils/guc_hooks.h=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20|=20=20=201=20+=0A=20src/test/perl/PostgreSQL/Test/Cluster.pm=20=20= =20=20=20=20|=20=2035=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=20= src/test/ssl/t/001_ssltests.pl=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|=20=20=206=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|=20412=20+++++++++=0A=20= src/tools/pgindent/typedefs.list=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=202=20+=0A=2026=20files=20changed,=201727=20insertions(+),=2098=20= deletions(-)=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/configure=20b/configure=0A= index=2042621ecd051..f1d2a802bcb=20100755=0A---=20a/configure=0A+++=20= b/configure=0A@@=20-13200,7=20+13200,7=20@@=20fi=0A=20done=0A=20=0A=20=20= =20#=20Function=20introduced=20in=20OpenSSL=201.1.1,=20not=20in=20= LibreSSL.=0A-=20=20for=20ac_func=20in=20X509_get_signature_info=20= SSL_CTX_set_num_tickets=20SSL_CTX_set_keylog_callback=0A+=20=20for=20= ac_func=20in=20X509_get_signature_info=20SSL_CTX_set_num_tickets=20= SSL_CTX_set_keylog_callback=20SSL_CTX_set_client_hello_cb=0A=20do=20:=0A=20= =20=20as_ac_var=3D`$as_echo=20"ac_cv_func_$ac_func"=20|=20$as_tr_sh`=0A=20= ac_fn_c_check_func=20"$LINENO"=20"$ac_func"=20"$as_ac_var"=0Adiff=20= --git=20a/configure.ac=20b/configure.ac=0Aindex=20= 61ec895d23c..dd57e087da8=20100644=0A---=20a/configure.ac=0A+++=20= b/configure.ac=0A@@=20-1450,7=20+1450,7=20@@=20if=20test=20"$with_ssl"=20= =3D=20openssl=20;=20then=0A=20=20=20#=20Function=20introduced=20in=20= OpenSSL=201.0.2,=20not=20in=20LibreSSL.=0A=20=20=20= AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])=0A=20=20=20#=20Function=20= introduced=20in=20OpenSSL=201.1.1,=20not=20in=20LibreSSL.=0A-=20=20= AC_CHECK_FUNCS([X509_get_signature_info=20SSL_CTX_set_num_tickets=20= SSL_CTX_set_keylog_callback])=0A+=20=20= AC_CHECK_FUNCS([X509_get_signature_info=20SSL_CTX_set_num_tickets=20= SSL_CTX_set_keylog_callback=20SSL_CTX_set_client_hello_cb])=0A=20=20=20= AC_DEFINE([USE_OPENSSL],=201,=20[Define=20to=201=20to=20build=20with=20= OpenSSL=20support.=20(--with-ssl=3Dopenssl)])=0A=20elif=20test=20= "$with_ssl"=20!=3D=20no=20;=20then=0A=20=20=20AC_MSG_ERROR([--with-ssl=20= must=20specify=20openssl])=0Adiff=20--git=20a/doc/src/sgml/runtime.sgml=20= b/doc/src/sgml/runtime.sgml=0Aindex=20b1937cd13ab..cf2e7302ddb=20100644=0A= ---=20a/doc/src/sgml/runtime.sgml=0A+++=20b/doc/src/sgml/runtime.sgml=0A= @@=20-2469,6=20+2469,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-2596,6=20+2602,123=20= @@=20openssl=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=20= be=20configured=20for=20Server=20Name=0A+=20=20=20=20Indication,=20= SNI,=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=20based=20on=0A+=20=20=20= =20the=20hosts=20which=20are=20defined=20in=20= pg_hosts.conf.=0A+=20=20=20=0A+=0A+=20=20=20= =0A+=20=20=20=20SNI=20configuration=20is=20defined=20in=20the=20= hosts=20configuration=20file,=0A+=20=20=20=20= pg_hosts.conf,=20which=20is=20stored=20in=20the=20= cluster's=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=20file=0A= +include_dir=20directory=0A= +=0A+=20=20=20=20Comments,=20whitespace=20and=20line=20= continuations=20are=20handled=20in=20the=20same=20way=20as=0A+=20=20=20=20= in=20pg_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_CA_certificate,=0A+=20=20=20=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+=0A+=20=20=20=0A+=20= =20=20=20hostname=20should=20either=20be=20= set=20to=20the=20literal=0A+=20=20=20=20hostname=20for=20the=20= connection,=20/no_sni/=20or=20*.=0A= +=20=20=20=20=20contains=20details=20= on=20how=20these=20values=20are=0A+=20=20=20=20used.=0A+=20=20=20=20= =0A+=20=20=20=20=20Hostname=20= setting=20values=0A+=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= =20Host=20Entry=0A+=20=20=20=20=20=20=20=20= sslsni=0A+=20=20=20=20=20=20=20=20= Description=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20= =20=20=0A+=0A+=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=20Not=20required=0A+=20=20=20=20=20=20=20= =20=0A+=20=20=20=20=20=20=20=20=20Default=20host,=20matches=20all=20= connections.=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =0A+=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20= /no_sni/=0A+=20=20=20=20=20=20=20=20= Not=20allowed=0A+=20=20=20=20=20=20=20=20=0A+=20=20= =20=20=20=20=20=20=20Certificate=20and=20key=20to=20use=20for=20= connections=20with=20no=0A+=20=20=20=20=20=20=20=20=20= sslsni=20defined.=0A+=20=20=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=20=0A+=0A+=20=20=20=20=20=20=20=0A+=20=20=20= =20=20=20=20=20hostname=0A+=20=20= =20=20=20=20=20=20Required=0A+=20=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=20Certificate=20and=20key=20to=20use=20= for=20connections=20to=20the=20host=20specified=20in=0A+=20=20=20=20=20=20= =20=20=20the=20connection.=20=20Multiple=20hostnames=20can=20be=20= defined=20by=20using=20a=20comma=0A+=20=20=20=20=20=20=20=20=20separated=20= list.=20The=20certificate=20and=20key=20will=20be=20used=20for=20= connections=0A+=20=20=20=20=20=20=20=20=20to=20all=20hosts=20in=20the=20= list.=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= =0A+=20=20=20=0A+=0A+=20=20=20=0A+=20=20=20=20If=20= pg_hosts.conf=20is=20empty,=20or=20missing,=20then=20= the=20SSL=0A+=20=20=20=20configuration=20in=20= postgresql.conf=20will=20be=20used=20for=20all=0A+=20= =20=20=20connections.=20If=20pg_hosts.conf=20is=20= non-empty=20then=20it=0A+=20=20=20=20will=20take=20precedence=20over=20= certificate=20and=20key=20settings=20in=0A+=20=20=20=20= postgresql.conf.=0A+=20=20=20=0A+=0A+=20=20=20= =0A+=20=20=20=20It=20is=20currently=20not=20possible=20to=20set=20= different=20clientname=0A+=20=20=20=20values=20for=20= the=20different=20certificates.=20=20Any=20clientname=0A= +=20=20=20=20setting=20in=20pg_hba.conf=20will=20be=20= applied=20during=0A+=20=20=20=20authentication=20regardless=20of=20which=20= set=20of=20certificates=20have=20been=20loaded=0A+=20=20=20=20via=20an=20= SNI=20enabled=20connection.=0A+=20=20=20=0A+=0A+=20=20=20=0A= +=20=20=20=20The=20CRL=20configuration=20in=20= postgresql.conf=20is=20applied=0A+=20=20=20=20on=20= all=20connections=20regardless=20of=20if=20they=20use=20SNI=20or=20not.=0A= +=20=20=20=0A+=20=20=0A=20=20=0A=20=0A=20=20= =0Adiff=20--git=20a/meson.build=20= b/meson.build=0Aindex=202df54409ca6..e4804badb91=20100644=0A---=20= a/meson.build=0A+++=20b/meson.build=0A@@=20-1674,6=20+1674,7=20@@=20if=20= sslopt=20in=20['auto',=20'openssl']=0A=20=20=20=20=20=20=20= ['X509_get_signature_info'],=0A=20=20=20=20=20=20=20= ['SSL_CTX_set_num_tickets'],=0A=20=20=20=20=20=20=20= ['SSL_CTX_set_keylog_callback'],=0A+=20=20=20=20=20=20= ['SSL_CTX_set_client_hello_cb'],=0A=20=20=20=20=20]=0A=20=0A=20=20=20=20=20= are_openssl_funcs_complete=20=3D=20true=0Adiff=20--git=20= a/src/backend/Makefile=20b/src/backend/Makefile=0Aindex=20= ba53cd9d998..162d3f1f2a9=20100644=0A---=20a/src/backend/Makefile=0A+++=20= b/src/backend/Makefile=0A@@=20-221,6=20+221,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)=0A@@=20-280,6=20+281,7=20@@=20endif=0A=20=09= $(MAKE)=20-C=20utils=20uninstall-data=0A=20=09rm=20-f=20= '$(DESTDIR)$(datadir)/pg_hba.conf.sample'=20\=0A=20=09=20=20=20=20=20=20= '$(DESTDIR)$(datadir)/pg_ident.conf.sample'=20\=0A+=09=20=20=20=20=20=20= '$(DESTDIR)$(datadir)/pg_hosts.conf.sample'=20\=0A=20=09=20=20=20=20=20=20= '$(DESTDIR)$(datadir)/postgresql.conf.sample'=0A=20ifeq=20($(with_llvm),=20= yes)=0A=20=09$(call=20uninstall_llvm_module,postgres)=0Adiff=20--git=20= a/src/backend/commands/variable.c=20b/src/backend/commands/variable.c=0A= index=204440aff4925..8afd252fc8c=20100644=0A---=20= a/src/backend/commands/variable.c=0A+++=20= b/src/backend/commands/variable.c=0A@@=20-1258,6=20+1258,27=20@@=20= check_ssl(bool=20*newval,=20void=20**extra,=20GucSource=20source)=0A=20=09= return=20true;=0A=20}=0A=20=0A+bool=0A+check_ssl_sni(bool=20*newval,=20= void=20**extra,=20GucSource=20source)=0A+{=0A+#ifndef=20USE_SSL=0A+=09if=20= (*newval)=0A+=09{=0A+=09=09GUC_check_errmsg("SSL=20is=20not=20supported=20= by=20this=20build");=0A+=09=09return=20false;=0A+=09}=0A+#else=0A= +#ifndef=20HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A+=09if=20(*newval)=0A+=09{=0A= +=09=09GUC_check_errmsg("SNI=20requires=20OpenSSL=201.1.1=20or=20= later");=0A+=09=09return=20false;=0A+=09}=0A+#endif=0A+#endif=0A+=09= return=20true;=0A+}=0A+=0A=20bool=0A=20= check_standard_conforming_strings(bool=20*newval,=20void=20**extra,=20= GucSource=20source)=0A=20{=0Adiff=20--git=20= a/src/backend/libpq/be-secure-common.c=20= b/src/backend/libpq/be-secure-common.c=0Aindex=20= c074556dbfc..6b61fb59ba1=20100644=0A---=20= a/src/backend/libpq/be-secure-common.c=0A+++=20= b/src/backend/libpq/be-secure-common.c=0A@@=20-26,18=20+26,25=20@@=0A=20= #include=20"common/string.h"=0A=20#include=20"libpq/libpq.h"=0A=20= #include=20"storage/fd.h"=0A+#include=20"utils/builtins.h"=0A+#include=20= "utils/guc.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=20*=0A=20=20*=20= prompt=20will=20be=20substituted=20for=20%p.=20=20is_server_start=20= determines=20the=20loglevel=0A-=20*=20of=20error=20messages.=0A+=20*=20= of=20error=20messages=20from=20executing=20the=20command,=20the=20= loglevel=20for=20failures=20in=0A+=20*=20param=20substitution=20will=20= be=20ERROR=20regardless=20of=20is_server_start.=20=20The=20actual=0A+=20= *=20command=20used=20depends=20on=20the=20configuration=20for=20the=20= current=20host.=0A=20=20*=0A=20=20*=20The=20result=20will=20be=20put=20= in=20buffer=20buf,=20which=20is=20of=20size=20size.=20=20The=20return=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,=20bool=20= is_server_start,=20char=20*buf,=20int=20size)=0A= +run_ssl_passphrase_command(const=20char=20*cmd,=20const=20char=20= *prompt,=0A+=09=09=09=09=09=09=20=20=20bool=20is_server_start,=20char=20= *buf,=20int=20size)=0A=20{=0A=20=09int=09=09=09loglevel=20=3D=20= is_server_start=20?=20ERROR=20:=20LOG;=0A=20=09char=09=20=20=20*command;=0A= @@=20-49,7=20+56,7=20@@=20run_ssl_passphrase_command(const=20char=20= *prompt,=20bool=20is_server_start,=20char=20*buf,=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+182,248=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+=09= parsedline->hostnames=20=3D=20NIL;=0A+=0A+=09/*=20Initialize=20optional=20= fields=20*/=0A+=09parsedline->ssl_passphrase_cmd=20=3D=20NULL;=0A+=09= parsedline->ssl_passphrase_reload=20=3D=20false;=0A+=0A+=09/*=20Hostname=20= */=0A+=09field=20=3D=20list_head(tok_line->fields);=0A+=09tokens=20=3D=20= lfirst(field);=0A+=09foreach_ptr(AuthToken,=20hostname,=20tokens)=0A+=09= {=0A+=09=09if=20((tokens->length=20>=201)=20&&=0A+=09=09=09= (strcmp(hostname->string,=20"*")=20=3D=3D=200=20||=20= strcmp(hostname->string,=20"/no_sni/")=20=3D=3D=200))=0A+=09=09{=0A+=09=09= =09ereport(elevel,=0A+=09=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=09errmsg("default=20and=20non-SNI=20entries=20cannot=20be=20= mixed=20with=20other=20entries"),=0A+=09=09=09=09=09errcontext("line=20= %d=20of=20configuration=20file=20\"%s\"",=0A+=09=09=09=09=09=09=09=20=20=20= tok_line->line_num,=20tok_line->file_name));=0A+=09=09=09return=20NULL;=0A= +=09=09}=0A+=0A+=09=09parsedline->hostnames=20=3D=20= lappend(parsedline->hostnames,=20pstrdup(hostname->string));=0A+=09}=0A+=0A= +=09/*=20SSL=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+=09if=20= (tokens->length=20>=201)=0A+=09{=0A+=09=09ereport(elevel,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("multiple=20= values=20specified=20for=20SSL=20certificate"),=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+=09token=20=3D=20linitial(tokens);=0A+=09= parsedline->ssl_cert=20=3D=20pstrdup(token->string);=0A+=0A+=09/*=20SSL=20= key=20(Required)=20*/=0A+=09field=20=3D=20lnext(tok_line->fields,=20= field);=0A+=09if=20(!field)=0A+=09{=0A+=09=09ereport(elevel,=0A+=09=09=09= =09errcode(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+=09if=20(tokens->length=20>=20= 1)=0A+=09{=0A+=09=09ereport(elevel,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("multiple=20= values=20specified=20for=20SSL=20key"),=0A+=09=09=09=09errcontext("line=20= %d=20of=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+=09token=20=3D=20linitial(tokens);=0A+=09parsedline->ssl_key=20=3D=20= pstrdup(token->string);=0A+=0A+=09/*=20SSL=20CA=20(optional)=20*/=0A+=09= field=20=3D=20lnext(tok_line->fields,=20field);=0A+=09if=20(!field)=0A+=09= =09return=20parsedline;=0A+=09tokens=20=3D=20lfirst(field);=0A+=09if=20= (tokens->length=20>=201)=0A+=09{=0A+=09=09ereport(elevel,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("multiple=20= values=20specified=20for=20SSL=20CA"),=0A+=09=09=09=09errcontext("line=20= %d=20of=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+=09token=20=3D=20linitial(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=09/*=0A+=09=09=09=20*=20There=20should=20= be=20no=20more=20tokens=20after=20this,=20if=20there=20are=20break=0A+=09= =09=09=20*=20parsing=20and=20report=20error=20to=20avoid=20silently=20= accepting=20incorrect=0A+=09=09=09=20*=20config.=0A+=09=09=09=20*/=0A+=09= =09=09if=20(tokens->length=20>=201)=0A+=09=09=09{=0A+=09=09=09=09= ereport(elevel,=0A+=09=09=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=09=09errmsg("extra=20fields=20at=20end=20of=20line"),=0A+=09= =09=09=09=09=09errcontext("line=20%d=20of=20configuration=20file=20= \"%s\"",=0A+=09=09=09=09=09=09=09=09=20=20=20tok_line->line_num,=20= tok_line->file_name));=0A+=09=09=09=09return=20NULL;=0A+=09=09=09}=0A+=0A= +=09=09=09if=20(!parse_bool(token->string,=20= &parsedline->ssl_passphrase_reload))=0A+=09=09=09{=0A+=09=09=09=09= ereport(elevel,=0A+=09=09=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=09=09errmsg("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=09=09return=20= NULL;=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A+=09return=20parsedline;=0A= +}=0A+=0A+/*=0A+=20*=20load_hosts=0A+=20*=0A+=20*=20Reads=20and=20parses=20= the=20pg_hosts.conf=20configuration=20file=20and=20passes=20back=20a=20= List=0A+=20*=20of=20HostsLine=20elements=20containing=20the=20parsed=20= lines,=20or=20NIL=20in=20case=20of=20an=20empty=0A+=20*=20file.=20=20The=20= list=20is=20returned=20in=20the=20hosts=20parameter.=20The=20function=20= will=20return=0A+=20*=20a=20HostsFileLoadResult=20value=20detailing=20= the=20result=20of=20the=20operation.=20=20When=0A+=20*=20the=20hosts=20= configuration=20failed=20to=20load,=20the=20err_msg=20variable=20may=20= have=20more=0A+=20*=20information=20in=20case=20it=20was=20passed=20as=20= non-NULL.=0A+=20*/=0A+int=0A+load_hosts(List=20**hosts,=20char=20= **err_msg)=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+=09bool=09= =09ok=20=3D=20true;=0A+=0A+=09/*=0A+=09=20*=20If=20we=20cannot=20return=20= results=20then=20error=20out=20immediately.=20This=20implies=0A+=09=20*=20= API=20misuse=20or=20a=20similar=20kind=20of=20programmer=20error.=0A+=09=20= */=0A+=09if=20(!hosts)=0A+=09{=0A+=09=09if=20(err_msg)=0A+=09=09=09= *err_msg=20=3D=20psprintf("cannot=20load=20config=20from=20\"%s\",=20= return=20variable=20missing",=0A+=09=09=09=09=09=09=09=09HostsFileName);=0A= +=09=09return=20HOSTSFILE_LOAD_FAILED;=0A+=09}=0A+=09*hosts=20=3D=20NIL;=0A= +=0A+=09/*=0A+=09=20*=20This=20is=20not=20an=20auth=20file=20per=20se,=20= but=20it=20is=20using=20the=20same=20file=20format=0A+=09=20*=20as=20the=20= pg_hba=20and=20pg_ident=20files=20and=20thus=20the=20same=20code=20= infrastructure.=0A+=09=20*=20A=20future=20TODO=20might=20be=20to=20= rename=20the=20supporting=20code=20with=20a=20more=0A+=09=20*=20generic=20= name?=0A+=09=20*/=0A+=09file=20=3D=20open_auth_file(HostsFileName,=20= LOG,=200,=20err_msg);=0A+=09if=20(file=20=3D=3D=20NULL)=0A+=09{=0A+=09=09= if=20(errno=20=3D=3D=20ENOENT)=0A+=09=09=09return=20HOSTSFILE_MISSING;=0A= +=0A+=09=09return=20HOSTSFILE_LOAD_FAILED;=0A+=09}=0A+=0A+=09= tokenize_auth_file(HostsFileName,=20file,=20&hosts_lines,=20LOG,=200);=0A= +=0A+=09foreach(line,=20hosts_lines)=0A+=09{=0A+=09=09TokenizedAuthLine=20= *tok_line=20=3D=20(TokenizedAuthLine=20*)=20lfirst(line);=0A+=0A+=09=09= /*=0A+=09=09=20*=20Mark=20processing=20as=20not-ok=20in=20case=20lines=20= are=20found=20with=20errors=20in=0A+=09=09=20*=20tokenization=20= (.err_msg=20is=20set)=20or=20during=20parsing.=0A+=09=09=20*/=0A+=09=09= if=20((tok_line->err_msg=20!=3D=20NULL)=20||=0A+=09=09=09((newline=20=3D=20= parse_hosts_line(tok_line,=20LOG))=20=3D=3D=20NULL))=0A+=09=09{=0A+=09=09= =09ok=20=3D=20false;=0A+=09=09=09continue;=0A+=09=09}=0A+=0A+=09=09= parsed_lines=20=3D=20lappend(parsed_lines,=20newline);=0A+=09}=0A+=0A+=09= /*=20Free=20memory=20from=20tokenizer=20*/=0A+=09free_auth_file(file,=20= 0);=0A+=09*hosts=20=3D=20parsed_lines;=0A+=0A+=09if=20(!ok)=0A+=09{=0A+=09= =09if=20(err_msg)=0A+=09=09=09*err_msg=20=3D=20psprintf("loading=20= config=20from=20\"%s\"=20failed=20due=20to=20parsing=20error",=0A+=09=09=09= =09=09=09=09=09HostsFileName);=0A+=09=09return=20HOSTSFILE_LOAD_FAILED;=0A= +=09}=0A+=0A+=09if=20(parsed_lines=20=3D=3D=20NIL)=0A+=09=09return=20= HOSTSFILE_EMPTY;=0A+=0A+=09return=20HOSTSFILE_LOAD_OK;=0A+}=0Adiff=20= --git=20a/src/backend/libpq/be-secure-openssl.c=20= b/src/backend/libpq/be-secure-openssl.c=0Aindex=20= 14c6532bb16..c9391a1e714=20100644=0A---=20= a/src/backend/libpq/be-secure-openssl.c=0A+++=20= b/src/backend/libpq/be-secure-openssl.c=0A@@=20-27,6=20+27,7=20@@=0A=20= #include=20=0A=20#include=20=0A=20=0A= +#include=20"common/hashfn.h"=0A=20#include=20"common/string.h"=0A=20= #include=20"libpq/libpq.h"=0A=20#include=20"miscadmin.h"=0A@@=20-52,6=20= +53,27=20@@=0A=20#endif=0A=20#include=20=0A=20=0A+/*=0A= +=20*=20Simplehash=20for=20tracking=20configured=20hostnames=20to=20= guard=20against=20duplicate=0A+=20*=20entries.=20=20Each=20list=20of=20= hosts=20is=20traversed=20and=20added=20to=20the=20hash=20during=0A+=20*=20= parsing=20and=20if=20a=20duplicate=20error=20is=20detected=20an=20error=20= will=20be=20thrown.=0A+=20*/=0A+typedef=20struct=0A+{=0A+=09uint32=09=09= status;=0A+=09const=20char=20*hostname;=0A+}=09=09=09HostCacheEntry;=0A= +static=20uint32=20host_cache_pointer(const=20char=20*key);=0A+#define=20= SH_PREFIX=09=09host_cache=0A+#define=20SH_ELEMENT_TYPE=09HostCacheEntry=0A= +#define=20SH_KEY_TYPE=09=09const=20char=20*=0A+#define=20SH_KEY=09=09=09= hostname=0A+#define=20SH_HASH_KEY(tb,=20key)=09host_cache_pointer(key)=0A= +#define=20SH_EQUAL(tb,=20a,=20b)=09=09(pg_strcasecmp(a,=20b)=20=3D=3D=20= 0)=0A+#define=20SH_SCOPE=09=09=09=09static=20inline=0A+#define=20= SH_DECLARE=0A+#define=20SH_DEFINE=0A+#include=20"lib/simplehash.h"=0A=20=0A= =20/*=20default=20init=20hook=20can=20be=20overridden=20by=20a=20shared=20= library=20*/=0A=20static=20void=20default_openssl_tls_init(SSL_CTX=20= *context,=20bool=20isServerStart);=0A@@=20-78,10=20+100,34=20@@=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=20static=20const=20char=20= *SSLerrmessage(unsigned=20long=20ecode);=0A+static=20bool=20= init_host_context(HostsLine=20*host,=20bool=20isServerStart);=0A+static=20= void=20host_context_cleanup_cb(void=20*arg);=0A+#ifdef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A+static=20int=09= sni_clienthello_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg);=0A+#endif=0A=20= =0A=20static=20char=20*X509_NAME_to_cstring(X509_NAME=20*name);=0A=20=0A=20= static=20SSL_CTX=20*SSL_context=20=3D=20NULL;=0A+static=20MemoryContext=20= SSL_hosts_memcxt=20=3D=20NULL;=0A+static=20struct=20hosts=0A+{=0A+=09/*=0A= +=09=20*=20List=20of=20HostsLine=20structures=20containing=20SSL=20= configurations=20for=0A+=09=20*=20connections=20with=20hostnames=20= defined=20in=20the=20SNI=20extension.=0A+=09=20*/=0A+=09List=09=20=20=20= *sni;=0A+=0A+=09/*=20The=20SSL=20configuration=20to=20use=20for=20= connections=20without=20SNI=20*/=0A+=09HostsLine=20=20*no_sni;=0A+=0A+=09= /*=0A+=09=20*=20The=20default=20SSL=20configuration=20to=20use=20as=20a=20= fallback=20in=20case=20no=20hostname=0A+=09=20*=20matches=20the=20= supplied=20hostname=20in=20the=20SNI=20extension.=0A+=09=20*/=0A+=09= HostsLine=20=20*default_host;=0A+}=09=09=20=20=20*SSL_hosts;=0A+=0A=20= static=20bool=20dummy_ssl_passwd_cb_called=20=3D=20false;=0A=20static=20= bool=20ssl_is_server_start;=0A=20=0A@@=20-104,88=20+150,269=20@@=20= struct=20CallbackErr=0A=20int=0A=20be_tls_init(bool=20isServerStart)=0A=20= {=0A-=09SSL_CTX=20=20=20=20*context;=0A+=09List=09=20=20=20*pg_hosts=20=3D= =20NIL;=0A+=09ListCell=20=20=20*line;=0A+=09MemoryContext=20oldcxt;=0A+=09= MemoryContext=20host_memcxt=20=3D=20NULL;=0A+=09MemoryContextCallback=20= *host_memcxt_cb;=0A+=09char=09=20=20=20*err_msg=20=3D=20NULL;=0A+=09int=09= =09=09res;=0A+=09struct=20hosts=20*new_hosts;=0A+=09SSL_CTX=20=20=20=20= *context=20=3D=20NULL;=0A=20=09int=09=09=09ssl_ver_min=20=3D=20-1;=0A=20=09= int=09=09=09ssl_ver_max=20=3D=20-1;=0A+=09host_cache_hash=20*host_cache=20= =3D=20NULL;=0A=20=0A=20=09/*=0A-=09=20*=20Create=20a=20new=20SSL=20= context=20into=20which=20we'll=20load=20all=20the=20configuration=0A-=09=20= *=20settings.=20=20If=20we=20fail=20partway=20through,=20we=20can=20= avoid=20memory=20leakage=20by=0A-=09=20*=20freeing=20this=20context;=20= we=20don't=20install=20it=20as=20active=20until=20the=20end.=0A+=09=20*=20= Since=20we=20don't=20know=20which=20host=20we're=20using=20until=20the=20= ClientHello=20is=0A+=09=20*=20sent,=20ssl_loaded_verify_locations=20= *always*=20starts=20out=20as=20false.=20The=0A+=09=20*=20only=20place=20= it's=20set=20to=20true=20is=20in=20sni_clienthello_cb().=0A+=09=20*/=0A+=09= ssl_loaded_verify_locations=20=3D=20false;=0A+=0A+=09host_memcxt=20=3D=20= AllocSetContextCreate(CurrentMemoryContext,=0A+=09=09=09=09=09=09=09=09=09= =09"hosts=20file=20parser=20context",=0A+=09=09=09=09=09=09=09=09=09=09= ALLOCSET_SMALL_SIZES);=0A+=09oldcxt=20=3D=20= MemoryContextSwitchTo(host_memcxt);=0A+=0A+=09/*=20Allocate=20a=20= tentative=20replacement=20for=20SSL_hosts.=20*/=0A+=09new_hosts=20=3D=20= palloc0_object(struct=20hosts);=0A+=0A+=09/*=0A+=09=20*=20Register=20a=20= reset=20callback=20for=20the=20memory=20context=20which=20is=20= responsible=0A+=09=20*=20for=20freeing=20OpenSSL=20managed=20allocations=20= upon=20context=20deletion.=20=20The=0A+=09=20*=20callback=20is=20= allocated=20here=20to=20make=20sure=20it=20gets=20cleaned=20up=20along=20= with=0A+=09=20*=20the=20memory=20context=20it's=20registered=20for.=0A+=09= =20*/=0A+=09host_memcxt_cb=20=3D=20= palloc0_object(MemoryContextCallback);=0A+=09host_memcxt_cb->func=20=3D=20= host_context_cleanup_cb;=0A+=09host_memcxt_cb->arg=20=3D=20new_hosts;=0A= +=09MemoryContextRegisterResetCallback(host_memcxt,=20host_memcxt_cb);=0A= +=0A+=09/*=0A+=09=20*=20If=20ssl_sni=20is=20enabled,=20attempt=20to=20= load=20and=20parse=20TLS=20configuration=20from=0A+=09=20*=20the=20= pg_hosts.conf=20file=20with=20the=20set=20of=20hosts=20returned=20as=20a=20= list.=20=20If=0A+=09=20*=20there=20are=20hosts=20configured=20they=20= take=20precedence=20over=20the=20configuration=0A+=09=20*=20in=20= postgresql.conf.=20=20Make=20sure=20to=20allocate=20the=20parsed=20rows=20= in=20their=20own=0A+=09=20*=20memory=20context=20so=20that=20we=20can=20= delete=20them=20easily=20in=20case=20parsing=20fails.=0A+=09=20*=20If=20= ssl_sni=20is=20disabled=20then=20set=20the=20state=20accordingly=20to=20= make=20sure=20we=0A+=09=20*=20instead=20parse=20the=20config=20from=20= postgresql.conf.=0A=20=09=20*=0A-=09=20*=20We=20use=20SSLv23_method()=20= because=20it=20can=20negotiate=20use=20of=20the=20highest=0A-=09=20*=20= mutually=20supported=20protocol=20version,=20while=20alternatives=20like=0A= -=09=20*=20TLSv1_2_method()=20permit=20only=20one=20specific=20version.=20= =20Note=20that=20we=20don't=0A-=09=20*=20actually=20allow=20SSL=20v2=20= or=20v3,=20only=20TLS=20protocols=20(see=20below).=0A+=09=20*=20The=20= reason=20for=20not=20doing=20everything=20in=20this=20if-else=20= conditional=20is=20that=0A+=09=20*=20we=20want=20to=20use=20the=20same=20= processing=20of=20postgresql.conf=20for=20when=20ssl_sni=0A+=09=20*=20is=20= off=20as=20well=20as=20when=20it's=20on=20but=20the=20hostsfile=20is=20= missing=20etc.=20=20Thus=0A+=09=20*=20we=20set=20res=20to=20the=20state=20= and=20continue=20with=20a=20new=20conditional=20instead=20of=0A+=09=20*=20= duplicating=20logic=20and=20risk=20it=20diverging=20over=20time.=0A=20=09= =20*/=0A-=09context=20=3D=20SSL_CTX_new(SSLv23_method());=0A-=09if=20= (!context)=0A+=09if=20(ssl_sni)=0A=20=09{=0A+=09=09/*=0A+=09=09=20*=20= The=20GUC=20check=20hook=20should=20have=20already=20blocked=20this=20= but=20to=20be=20on=0A+=09=09=20*=20the=20safe=20side=20we=20doublecheck=20= here.=0A+=09=09=20*/=0A+#ifndef=20HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A=20=09= =09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A-=09=09=09=09= (errmsg("could=20not=20create=20SSL=20context:=20%s",=0A-=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("ssl_sni=20is=20= not=20supported=20with=20LibreSSL"));=0A=20=09=09goto=20error;=0A+#endif=0A= +=0A+=09=09/*=20Attempt=20to=20load=20configuration=20from=20= pg_hosts.conf=20*/=0A+=09=09res=20=3D=20load_hosts(&pg_hosts,=20= &err_msg);=0A+=0A+=09=09/*=0A+=09=09=20*=20pg_hosts.conf=20is=20not=20= required=20to=20contain=20configuration,=20but=20if=20it=0A+=09=09=20*=20= does=20we=20error=20out=20in=20case=20it=20fails=20to=20load=20rather=20= than=20continue=20to=0A+=09=09=20*=20try=20the=20postgresql.conf=20= configuration=20to=20avoid=20silently=20falling=0A+=09=09=20*=20back=20= on=20an=20undesired=20configuration.=0A+=09=09=20*/=0A+=09=09if=20(res=20= =3D=3D=20HOSTSFILE_LOAD_FAILED)=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%s",=20"pg_hosts.conf",=0A+=09=09=09=09=09=09=20=20= =20err_msg=20?=20err_msg=20:=20"unknown=20error"));=0A+=09=09=09goto=20= error;=0A+=09=09}=0A=20=09}=0A+=09else=0A+=09=09res=20=3D=20= HOSTSFILE_DISABLED;=0A=20=0A=20=09/*=0A-=09=20*=20Disable=20OpenSSL's=20= moving-write-buffer=20sanity=20check,=20because=20it=20causes=0A-=09=20*=20= unnecessary=20failures=20in=20nonblocking=20send=20cases.=0A+=09=20*=20= Loading=20and=20parsing=20the=20hosts=20file=20was=20successful,=20= create=20configs=20for=0A+=09=20*=20each=20host=20entry=20and=20add=20to=20= the=20list=20of=20hosts=20to=20be=20checked=20during=0A+=09=20*=20login.=0A= =20=09=20*/=0A-=09SSL_CTX_set_mode(context,=20= SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A+=09if=20(res=20=3D=3D=20= HOSTSFILE_LOAD_OK)=0A+=09{=0A+=09=09Assert(ssl_sni);=0A+=0A+=09=09= foreach(line,=20pg_hosts)=0A+=09=09{=0A+=09=09=09HostsLine=20=20*host=20= =3D=20lfirst(line);=0A+=0A+=09=09=09if=20(!init_host_context(host,=20= isServerStart))=0A+=09=09=09=09goto=20error;=0A+=0A+=09=09=09/*=0A+=09=09= =09=20*=20The=20hostname=20in=20the=20config=20will=20be=20set=20to=20= NULL=20for=20the=20default=0A+=09=09=09=20*=20host=20as=20well=20as=20in=20= configs=20used=20for=20non-SNI=20connections.=20=20Lists=0A+=09=09=09=20= *=20of=20hostnames=20in=20pg_hosts.conf=20are=20not=20allowed=20to=20= contain=20the=0A+=09=09=09=20*=20default=20'*'=20entry=20or=20a=20= '/no_sni/'=20entry=20and=20this=20is=20checked=0A+=09=09=09=20*=20during=20= parsing.=20=20Thus=20we=20can=20inspect=20the=20head=20of=20the=20= hostnames=0A+=09=09=09=20*=20list=20for=20these=20since=20they=20will=20= never=20be=20anywhere=20else.=0A+=09=09=09=20*/=0A+=09=09=09if=20= (strcmp(linitial(host->hostnames),=20"*")=20=3D=3D=200)=0A+=09=09=09{=0A= +=09=09=09=09if=20(new_hosts->default_host)=0A+=09=09=09=09{=0A+=09=09=09= =09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09=09= errmsg("multiple=20default=20hosts=20specified"),=0A+=09=09=09=09=09=09=09= errcontext("line=20%d=20of=20configuration=20file=20\"%s\"",=0A+=09=09=09= =09=09=09=09=09=09=20=20=20host->linenumber,=20host->sourcefile));=0A+=09= =09=09=09=09goto=20error;=0A+=09=09=09=09}=0A+=0A+=09=09=09=09= new_hosts->default_host=20=3D=20host;=0A+=09=09=09}=0A+=09=09=09else=20= if=20(strcmp(linitial(host->hostnames),=20"/no_sni/")=20=3D=3D=200)=0A+=09= =09=09{=0A+=09=09=09=09if=20(new_hosts->no_sni)=0A+=09=09=09=09{=0A+=09=09= =09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09=09= =09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09=09= errmsg("multiple=20no_sni=20hosts=20specified"),=0A+=09=09=09=09=09=09=09= errcontext("line=20%d=20of=20configuration=20file=20\"%s\"",=0A+=09=09=09= =09=09=09=09=09=09=20=20=20host->linenumber,=20host->sourcefile));=0A+=09= =09=09=09=09goto=20error;=0A+=09=09=09=09}=0A+=0A+=09=09=09=09= new_hosts->no_sni=20=3D=20host;=0A+=09=09=09}=0A+=09=09=09else=0A+=09=09=09= {=0A+=09=09=09=09/*=20Check=20the=20hostnames=20for=20duplicates=20*/=0A= +=09=09=09=09if=20(!host_cache)=0A+=09=09=09=09=09host_cache=20=3D=20= host_cache_create(host_memcxt,=2032,=20NULL);=0A+=0A+=09=09=09=09= foreach_ptr(char,=20hostname,=20host->hostnames)=0A+=09=09=09=09{=0A+=09=09= =09=09=09HostCacheEntry=20*entry;=0A+=09=09=09=09=09bool=09=09found;=0A+=0A= +=09=09=09=09=09entry=20=3D=20host_cache_insert(host_cache,=20hostname,=20= &found);=0A+=09=09=09=09=09if=20(found)=0A+=09=09=09=09=09{=0A+=09=09=09=09= =09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09=09=09= =09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09=09=09= errmsg("multiple=20entries=20for=20host=20\"%s\"=20specified",=0A+=09=09=09= =09=09=09=09=09=09=20=20=20hostname),=0A+=09=09=09=09=09=09=09=09= errcontext("line=20%d=20of=20configuration=20file=20\"%s\"",=0A+=09=09=09= =09=09=09=09=09=09=09=20=20=20host->linenumber,=20host->sourcefile));=0A= +=09=09=09=09=09=09goto=20error;=0A+=09=09=09=09=09}=0A+=09=09=09=09=09= else=0A+=09=09=09=09=09=09entry->hostname=20=3D=20pstrdup(hostname);=0A+=09= =09=09=09}=0A+=0A+=09=09=09=09/*=0A+=09=09=09=09=20*=20At=20this=20point=20= we=20know=20we=20have=20a=20configuration=20with=20a=20list=0A+=09=09=09=09= =20*=20of=20distnct=201..n=20hostnames=20for=20literal=20string=20= matching=20with=0A+=09=09=09=09=20*=20the=20SNI=20extension=20from=20the=20= user.=0A+=09=09=09=09=20*/=0A+=09=09=09=09new_hosts->sni=20=3D=20= lappend(new_hosts->sni,=20host);=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A=20=0A= =20=09/*=0A-=09=20*=20Call=20init=20hook=20(usually=20to=20set=20= password=20callback)=0A+=09=20*=20If=20SNI=20is=20disabled,=20then=20we=20= load=20configuration=20from=20postgresql.conf.=20If=0A+=09=20*=20SNI=20= is=20enabled=20but=20the=20pg_hosts.conf=20file=20doesn't=20exist,=20or=20= is=20empty,=0A+=09=20*=20then=20we=20also=20load=20the=20config=20from=20= postgresql.conf.=0A=20=09=20*/=0A-=09(*openssl_tls_init_hook)=20= (context,=20isServerStart);=0A+=09else=20if=20(res=20=3D=3D=20= HOSTSFILE_DISABLED=20||=20res=20=3D=3D=20HOSTSFILE_EMPTY=20||=20res=20=3D=3D= =20HOSTSFILE_MISSING)=0A+=09{=0A+=09=09HostsLine=20=20*pgconf=20=3D=20= palloc0(sizeof(HostsLine));=0A=20=0A-=09/*=20used=20by=20the=20callback=20= */=0A-=09ssl_is_server_start=20=3D=20isServerStart;=0A+#ifdef=20= USE_ASSERT_CHECKING=0A+=09=09if=20(res=20=3D=3D=20HOSTSFILE_DISABLED)=0A= +=09=09=09Assert(ssl_sni=20=3D=3D=20false);=0A+#endif=0A+=0A+=09=09= pgconf->ssl_cert=20=3D=20ssl_cert_file;=0A+=09=09pgconf->ssl_key=20=3D=20= ssl_key_file;=0A+=09=09pgconf->ssl_ca=20=3D=20ssl_ca_file;=0A+=09=09= pgconf->ssl_passphrase_cmd=20=3D=20ssl_passphrase_command;=0A+=09=09= pgconf->ssl_passphrase_reload=20=3D=20= ssl_passphrase_command_supports_reload;=0A+=0A+=09=09if=20= (!init_host_context(pgconf,=20isServerStart))=0A+=09=09=09goto=20error;=0A= +=0A+=09=09/*=0A+=09=09=20*=20If=20postgresql.conf=20is=20used=20to=20= configure=20SSL=20then=20by=20definition=20it=0A+=09=09=20*=20will=20be=20= the=20default=20context=20as=20we=20don't=20have=20per-host=20config.=0A= +=09=09=20*/=0A+=09=09new_hosts->default_host=20=3D=20pgconf;=0A+=09}=0A=20= =0A=20=09/*=0A-=09=20*=20Load=20and=20verify=20server's=20certificate=20= and=20private=20key=0A+=09=20*=20Make=20sure=20we=20have=20at=20least=20= one=20configuration=20loaded=20to=20use,=20without=0A+=09=20*=20that=20= we=20cannot=20drive=20a=20connection=20so=20exit.=0A=20=09=20*/=0A-=09if=20= (SSL_CTX_use_certificate_chain_file(context,=20ssl_cert_file)=20!=3D=20= 1)=0A+=09if=20(new_hosts->sni=20=3D=3D=20NIL=20&&=20= !new_hosts->default_host=20&&=20!new_hosts->no_sni)=0A=20=09{=0A=20=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A-=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A-=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= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("no=20SSL=20= configurations=20loaded"),=0A+=09=09/*-=20translator:=20The=20two=20%s=20= contain=20filenames=20*/=0A+=09=09=09=09errhint("If=20ssl_sni=20is=20= enabled=20then=20add=20configuration=20to=20\"%s\",=20else=20\"%s\"",=0A= +=09=09=09=09=09=09"pg_hosts.conf",=20"postgresql.conf"));=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=09= goto=20error;=0A+#ifdef=20HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A=20=0A=20=09= /*=0A-=09=20*=20OK,=20try=20to=20load=20the=20private=20key=20file.=0A+=09= =20*=20Create=20a=20new=20SSL=20context=20into=20which=20we'll=20load=20= all=20the=20configuration=0A+=09=20*=20settings.=20=20If=20we=20fail=20= partway=20through,=20we=20can=20avoid=20memory=20leakage=20by=0A+=09=20*=20= freeing=20this=20context;=20we=20don't=20install=20it=20as=20active=20= until=20the=20end.=0A+=09=20*=0A+=09=20*=20We=20use=20SSLv23_method()=20= because=20it=20can=20negotiate=20use=20of=20the=20highest=0A+=09=20*=20= mutually=20supported=20protocol=20version,=20while=20alternatives=20like=0A= +=09=20*=20TLSv1_2_method()=20permit=20only=20one=20specific=20version.=20= =20Note=20that=20we=20don't=0A+=09=20*=20actually=20allow=20SSL=20v2=20= or=20v3,=20only=20TLS=20protocols=20(see=20below).=0A=20=09=20*/=0A-=09= dummy_ssl_passwd_cb_called=20=3D=20false;=0A-=0A-=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=09SSL_FILETYPE_PEM)=20!=3D=20= 1)=0A-=09{=0A-=09=09if=20(dummy_ssl_passwd_cb_called)=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=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=09else=0A-=09=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A-=09=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= -=09=09=09=09=09=20errmsg("could=20not=20load=20private=20key=20file=20= \"%s\":=20%s",=0A-=09=09=09=09=09=09=09ssl_key_file,=20= SSLerrmessage(ERR_get_error()))));=0A-=09=09goto=20error;=0A-=09}=0A-=0A= -=09if=20(SSL_CTX_check_private_key(context)=20!=3D=201)=0A+=09context=20= =3D=20SSL_CTX_new(SSLv23_method());=0A+=09if=20(!context)=0A=20=09{=0A=20= =09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A-=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A-=09=09=09=09=20errmsg("check=20= of=20private=20key=20failed:=20%s",=0A+=09=09=09=09(errmsg("could=20not=20= create=20SSL=20context:=20%s",=0A=20=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A=20=09=09goto=20error;=0A=20=09}=0A= +#else=0A+=0A+=09/*=0A+=09=20*=20If=20the=20client=20hello=20callback=20= isn't=20supported=20we=20want=20to=20use=20the=20default=0A+=09=20*=20= context=20as=20the=20one=20to=20drive=20the=20handshake=20so=20avoid=20= creating=20a=20new=20one=0A+=09=20*=20and=20use=20the=20already=20= existing=20default=20one=20instead.=0A+=09=20*/=0A+=09context=20=3D=20= new_hosts->default_host->ssl_ctx;=0A+=0A+=09/*=0A+=09=20*=20Since=20we=20= don't=20allocate=20a=20new=20SSL_CTX=20here=20like=20we=20do=20when=20= SNI=20has=20been=0A+=09=20*=20enabled=20we=20need=20to=20bump=20the=20= reference=20count=20on=20context=20to=20avoid=20double=0A+=09=20*=20free=20= of=20the=20context=20when=20using=20the=20same=20cleanup=20logic=20= across=20the=20cases.=0A+=09=20*/=0A+=09SSL_CTX_up_ref(context);=0A= +#endif=0A+=0A+=09/*=0A+=09=20*=20Disable=20OpenSSL's=20= moving-write-buffer=20sanity=20check,=20because=20it=20causes=0A+=09=20*=20= unnecessary=20failures=20in=20nonblocking=20send=20cases.=0A+=09=20*/=0A= +=09SSL_CTX_set_mode(context,=20SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A=20= =0A=20=09if=20(ssl_min_protocol_version)=0A=20=09{=0A@@=20-323,20=20= +550,186=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09if=20= (SSLPreferServerCiphers)=0A=20=09=09SSL_CTX_set_options(context,=20= SSL_OP_CIPHER_SERVER_PREFERENCE);=0A=20=0A+=09/*=0A+=09=20*=20Success!=20= =20Replace=20any=20existing=20SSL_context=20and=20host=20configurations.=0A= +=09=20*/=0A+=09if=20(SSL_context)=0A+=09{=0A+=09=09= SSL_CTX_free(SSL_context);=0A+=09=09SSL_context=20=3D=20NULL;=0A+=09}=0A= +=0A+=09MemoryContextSwitchTo(oldcxt);=0A+=0A+=09if=20(SSL_hosts_memcxt)=0A= +=09=09MemoryContextDelete(SSL_hosts_memcxt);=0A+=0A+=09SSL_hosts_memcxt=20= =3D=20host_memcxt;=0A+=09SSL_hosts=20=3D=20new_hosts;=0A+=09SSL_context=20= =3D=20context;=0A+=0A+=09return=200;=0A+=0A+=09/*=0A+=09=20*=20Clean=20= up=20by=20releasing=20working=20SSL=20contexts=20as=20well=20as=20= allocations=0A+=09=20*=20performed=20during=20parsing.=20=20Since=20all=20= our=20allocations=20are=20done=20in=20a=0A+=09=20*=20local=20memory=20= context=20all=20we=20need=20to=20do=20is=20delete=20it.=0A+=09=20*/=0A= +error:=0A+=09if=20(context)=0A+=09=09SSL_CTX_free(context);=0A+=0A+=09= MemoryContextSwitchTo(oldcxt);=0A+=09MemoryContextDelete(host_memcxt);=0A= +=09return=20-1;=0A+}=0A+=0A+/*=0A+=20*=20host_context_cleanup_cb=0A+=20= *=0A+=20*=20Memory=20context=20reset=20callback=20for=20clearing=20= OpenSSL=20managed=20resources=20when=0A+=20*=20hosts=20are=20reloaded=20= and=20the=20previous=20set=20of=20configured=20hosts=20are=20freed.=20As=0A= +=20*=20all=20hosts=20are=20allocated=20in=20a=20single=20context=20we=20= don't=20need=20to=20free=20each=20host=0A+=20*=20individually,=20just=20= resources=20managed=20by=20OpenSSL.=0A+=20*/=0A+static=20void=0A= +host_context_cleanup_cb(void=20*arg)=0A+{=0A+=09struct=20hosts=20*hosts=20= =3D=20arg;=0A+=0A+=09foreach_ptr(HostsLine,=20host,=20hosts->sni)=0A+=09= {=0A+=09=09if=20(host->ssl_ctx=20!=3D=20NULL)=0A+=09=09=09= SSL_CTX_free(host->ssl_ctx);=0A+=09}=0A+=0A+=09if=20(hosts->no_sni=20&&=20= hosts->no_sni->ssl_ctx)=0A+=09=09SSL_CTX_free(hosts->no_sni->ssl_ctx);=0A= +=0A+=09if=20(hosts->default_host=20&&=20hosts->default_host->ssl_ctx)=0A= +=09=09SSL_CTX_free(hosts->default_host->ssl_ctx);=0A+}=0A+=0A+static=20= bool=0A+init_host_context(HostsLine=20*host,=20bool=20isServerStart)=0A= +{=0A+=09SSL_CTX=20=20=20=20*ctx=20=3D=20SSL_CTX_new(SSLv23_method());=0A= +=0A+=09if=20(!ctx)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20FATAL=20= :=20LOG,=0A+=09=09=09=09(errmsg("could=20not=20create=20SSL=20context:=20= %s",=0A+=09=09=09=09=09=09SSLerrmessage(ERR_get_error()))));=0A+=09=09= goto=20error;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Call=20init=20hook=20= (usually=20to=20set=20password=20callback)=20in=20case=20SNI=20hasn't=0A= +=09=20*=20been=20enabled.=20If=20SNI=20is=20enabled=20the=20hook=20= won't=20operate=20on=20the=20actual=0A+=09=20*=20TLS=20context=20used=20= so=20it=20cannot=20function=20properly.=20TODO:=20issue=20a=20warning=0A= +=09=20*=20in=20case=20there=20is=20a=20non-default=20hook=20installed=20= and=20SNI=20is=20enabled.=0A+=09=20*=0A+=09=20*=20If=20SNI=20is=20= enabled,=20we=20set=20password=20callback=20based=20what=20was=20= configured.=0A+=09=20*/=0A+=09if=20(!ssl_sni)=0A+=09=09= (*openssl_tls_init_hook)=20(ctx,=20isServerStart);=0A+=09else=0A+=09{=0A= +=09=09/*=0A+=09=09=20*=20Set=20up=20the=20password=20callback,=20if=20= configured.=0A+=09=09=20*/=0A+=09=09if=20(isServerStart)=0A+=09=09{=0A+=09= =09=09if=20(host->ssl_passphrase_cmd=20&&=20host->ssl_passphrase_cmd[0])=0A= +=09=09=09{=0A+=09=09=09=09SSL_CTX_set_default_passwd_cb(ctx,=20= ssl_external_passwd_cb);=0A+=09=09=09=09= SSL_CTX_set_default_passwd_cb_userdata(ctx,=20host->ssl_passphrase_cmd);=0A= +=09=09=09}=0A+=09=09}=0A+=09=09else=0A+=09=09{=0A+=09=09=09if=20= (host->ssl_passphrase_reload=20&&=20host->ssl_passphrase_cmd[0])=0A+=09=09= =09{=0A+=09=09=09=09SSL_CTX_set_default_passwd_cb(ctx,=20= ssl_external_passwd_cb);=0A+=09=09=09=09= SSL_CTX_set_default_passwd_cb_userdata(ctx,=20host->ssl_passphrase_cmd);=0A= +=09=09=09}=0A+=09=09=09else=0A+=09=09=09{=0A+=09=09=09=09/*=0A+=09=09=09= =09=20*=20If=20reloading=20and=20no=20external=20command=20is=20= configured,=0A+=09=09=09=09=20*=20override=20OpenSSL's=20default=20= handling=20of=20passphrase-protected=0A+=09=09=09=09=20*=20files,=20= because=20we=20don't=20want=20to=20prompt=20for=20a=20passphrase=20in=0A= +=09=09=09=09=20*=20an=20already-running=20server.=0A+=09=09=09=09=20*/=0A= +=09=09=09=09SSL_CTX_set_default_passwd_cb(ctx,=20dummy_ssl_passwd_cb);=0A= +=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Load=20and=20= verify=20server's=20certificate=20and=20private=20key=0A+=09=20*/=0A+=09= if=20(SSL_CTX_use_certificate_chain_file(ctx,=20host->ssl_cert)=20!=3D=20= 1)=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=20server=20certificate=20file=20\"%s\":=20= %s",=0A+=09=09=09=09=09=09host->ssl_cert,=20= SSLerrmessage(ERR_get_error()))));=0A+=09=09goto=20error;=0A+=09}=0A+=0A= +=09if=20(!check_ssl_key_file_permissions(host->ssl_key,=20= isServerStart))=0A+=09=09goto=20error;=0A+=0A+=0A+=09/*=20used=20by=20= the=20callback=20*/=0A+=09ssl_is_server_start=20=3D=20isServerStart;=0A+=0A= +=09/*=0A+=09=20*=20OK,=20try=20to=20load=20the=20private=20key=20file.=0A= +=09=20*/=0A+=09dummy_ssl_passwd_cb_called=20=3D=20false;=0A+=0A+=09if=20= (SSL_CTX_use_PrivateKey_file(ctx,=0A+=09=09=09=09=09=09=09=09=09= host->ssl_key,=0A+=09=09=09=09=09=09=09=09=09SSL_FILETYPE_PEM)=20!=3D=20= 1)=0A+=09{=0A+=09=09if=20(dummy_ssl_passwd_cb_called)=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=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= host->ssl_key)));=0A+=09=09else=0A+=09=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=09=20errmsg("could=20not=20load=20private=20key=20file=20= \"%s\":=20%s",=0A+=09=09=09=09=09=09=09host->ssl_key,=20= SSLerrmessage(ERR_get_error()))));=0A+=09=09goto=20error;=0A+=09}=0A+=0A= +=09if=20(SSL_CTX_check_private_key(ctx)=20!=3D=201)=0A+=09{=0A+=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20errmsg("check=20= of=20private=20key=20failed:=20%s",=0A+=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09goto=20error;=0A+=09}=0A+=0A=20= =09/*=0A=20=09=20*=20Load=20CA=20store,=20so=20we=20can=20verify=20= client=20certificates=20if=20needed.=0A=20=09=20*/=0A-=09if=20= (ssl_ca_file[0])=0A+=09if=20(host->ssl_ca=20&&=20host->ssl_ca[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=20= 1=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(ctx,=20host->ssl_ca,=20NULL)=20!=3D=201=20= ||=0A+=09=09=09(root_cert_list=20=3D=20= SSL_load_client_CA_file(host->ssl_ca))=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=09host->ssl_ca,=20SSLerrmessage(ERR_get_error()))));=0A= =20=09=09=09goto=20error;=0A=20=09=09}=0A=20=0A@@=20-347,17=20+740,7=20= @@=20be_tls_init(bool=20isServerStart)=0A=20=09=09=20*=20that=20the=20= SSL=20context=20will=20"own"=20the=20root_cert_list=20and=20remember=20= to=0A=20=09=09=20*=20free=20it=20when=20no=20longer=20needed.=0A=20=09=09= =20*/=0A-=09=09SSL_CTX_set_client_CA_list(context,=20root_cert_list);=0A= -=0A-=09=09/*=0A-=09=09=20*=20Always=20ask=20for=20SSL=20client=20cert,=20= but=20don't=20fail=20if=20it's=20not=0A-=09=09=20*=20presented.=20=20We=20= might=20fail=20such=20connections=20later,=20depending=20on=20what=0A-=09= =09=20*=20we=20find=20in=20pg_hba.conf.=0A-=09=09=20*/=0A-=09=09= SSL_CTX_set_verify(context,=0A-=09=09=09=09=09=09=20=20=20= (SSL_VERIFY_PEER=20|=0A-=09=09=09=09=09=09=09SSL_VERIFY_CLIENT_ONCE),=0A= -=09=09=09=09=09=09=20=20=20verify_cb);=0A+=09=09= SSL_CTX_set_client_CA_list(ctx,=20root_cert_list);=0A=20=09}=0A=20=0A=20=09= /*----------=0A@@=20-367,7=20+750,7=20@@=20be_tls_init(bool=20= isServerStart)=0A=20=09=20*/=0A=20=09if=20(ssl_crl_file[0]=20||=20= ssl_crl_dir[0])=0A=20=09{=0A-=09=09X509_STORE=20*cvstore=20=3D=20= SSL_CTX_get_cert_store(context);=0A+=09=09X509_STORE=20*cvstore=20=3D=20= SSL_CTX_get_cert_store(ctx);=0A=20=0A=20=09=09if=20(cvstore)=0A=20=09=09= {=0A@@=20-408,29=20+791,13=20@@=20be_tls_init(bool=20isServerStart)=0A=20= =09=09}=0A=20=09}=0A=20=0A-=09/*=0A-=09=20*=20Success!=20=20Replace=20= any=20existing=20SSL_context.=0A-=09=20*/=0A-=09if=20(SSL_context)=0A-=09= =09SSL_CTX_free(SSL_context);=0A-=0A-=09SSL_context=20=3D=20context;=0A-=0A= -=09/*=0A-=09=20*=20Set=20flag=20to=20remember=20whether=20CA=20store=20= has=20been=20loaded=20into=20SSL_context.=0A-=09=20*/=0A-=09if=20= (ssl_ca_file[0])=0A-=09=09ssl_loaded_verify_locations=20=3D=20true;=0A-=09= else=0A-=09=09ssl_loaded_verify_locations=20=3D=20false;=0A-=0A-=09= return=200;=0A+=09host->ssl_ctx=20=3D=20ctx;=0A+=09return=20true;=0A=20=0A= -=09/*=20Clean=20up=20by=20releasing=20working=20context.=20*/=0A=20= error:=0A-=09if=20(context)=0A-=09=09SSL_CTX_free(context);=0A-=09return=20= -1;=0A+=09if=20(ctx)=0A+=09=09SSL_CTX_free(ctx);=0A+=09return=20false;=0A= =20}=0A=20=0A=20void=0A@@=20-486,6=20+853,38=20@@=20= be_tls_open_server(Port=20*port)=0A=20=09=09return=20-1;=0A=20=09}=0A=20=0A= +=09/*=0A+=09=20*=20If=20the=20underlying=20TLS=20library=20supports=20= the=20client=20hello=20callback=20we=20use=0A+=09=20*=20that=20in=20= order=20to=20support=20host=20based=20configuration=20using=20the=20SNI=20= TLS=0A+=09=20*=20extension.=20=20If=20the=20user=20has=20disabled=20SNI=20= via=20the=20ssl_sni=20GUC=20we=20still=0A+=09=20*=20make=20use=20of=20= the=20callback=20in=20order=20to=20have=20consistent=20handling=20of=0A+=09= =20*=20OpenSSL=20contexts,=20except=20in=20that=20case=20the=20callback=20= will=20install=20the=0A+=09=20*=20default=20configuration=20regardless=20= of=20the=20hostname=20sent=20by=20the=20user=20in=0A+=09=20*=20the=20= handshake.=0A+=09=20*=0A+=09=20*=20In=20case=20the=20TLS=20library=20= does=20not=20support=20the=20client=20hello=20callback,=20as=0A+=09=20*=20= of=20this=20writing=20LibreSSL=20does=20not,=20we=20need=20to=20install=20= the=20client=20cert=0A+=09=20*=20verification=20callback=20here=20(if=20= the=20user=20configured=20a=20CA)=20since=20we=0A+=09=20*=20cannot=20use=20= the=20OpenSSL=20context=20update=20functionality.=0A+=09=20*/=0A+#ifdef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A+=09= SSL_CTX_set_client_hello_cb(SSL_context,=20sni_clienthello_cb,=20NULL);=0A= +#else=0A+=09if=20(SSL_hosts->default_host->ssl_ca=20&&=20= SSL_hosts->default_host->ssl_ca[0])=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20= Always=20ask=20for=20SSL=20client=20cert,=20but=20don't=20fail=20if=20= it's=20not=0A+=09=09=20*=20presented.=20=20We=20might=20fail=20such=20= connections=20later,=20depending=20on=20what=0A+=09=09=20*=20we=20find=20= in=20pg_hba.conf.=0A+=09=09=20*/=0A+=09=09SSL_set_verify(port->ssl,=0A+=09= =09=09=09=09=20=20=20(SSL_VERIFY_PEER=20|=20SSL_VERIFY_CLIENT_ONCE),=0A+=09= =09=09=09=09=20=20=20verify_cb);=0A+=0A+=09=09= ssl_loaded_verify_locations=20=3D=20true;=0A+=09}=0A+#endif=0A+=0A=20=09= err_context.cert_errdetail=20=3D=20NULL;=0A=20=09= SSL_set_ex_data(port->ssl,=200,=20&err_context);=0A=20=0A@@=20-1142,10=20= +1541,11=20@@=20ssl_external_passwd_cb(char=20*buf,=20int=20size,=20int=20= rwflag,=20void=20*userdata)=0A=20{=0A=20=09/*=20same=20prompt=20as=20= OpenSSL=20uses=20internally=20*/=0A=20=09const=20char=20*prompt=20=3D=20= "Enter=20PEM=20pass=20phrase:";=0A+=09const=20char=20*cmd=20=3D=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(cmd,=20prompt,=20= ssl_is_server_start,=20buf,=20size);=0A=20}=0A=20=0A=20/*=0A@@=20-1391,6=20= +1791,258=20@@=20alpn_cb(SSL=20*ssl,=0A=20=09}=0A=20}=0A=20=0A+#ifdef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A+/*=0A+=20*=20ssl_update_ssl=0A+=20*=0A= +=20*=20Replace=20certificate/key=20and=20CA=20in=20an=20SSL=20object=20= to=20match=20the,=20via=20the=20SNI=0A+=20*=20extension,=20selected=20= host=20configuration=20for=20the=20connection.=20=20The=20SSL_CTX=0A+=20= *=20object=20to=20use=20should=20be=20passed=20in=20as=20ctx.=20=20This=20= function=20will=20update=20the=0A+=20*=20SSL=20object=20in-place.=0A+=20= */=0A+static=20bool=0A+ssl_update_ssl(SSL=20*ssl,=20HostsLine=20= *host_config)=0A+{=0A+=09SSL_CTX=20=20=20=20*ctx=20=3D=20= host_config->ssl_ctx;=0A+=0A+=09X509=09=20=20=20*cert;=0A+=09EVP_PKEY=20=20= =20*key;=0A+=0A+=09STACK_OF(X509)=20*=20chain;=0A+=0A+=09Assert(ctx=20!=3D= =20NULL);=0A+=09/*-=0A+=09=20*=20Make=20use=20of=20the=20already-loaded=20= certificate=20chain=20and=20key.=20At=20first=0A+=09=20*=20glance,=20= SSL_set_SSL_CTX()=20looks=20like=20the=20easiest=20way=20to=20do=20this,=20= but=0A+=09=20*=20beware=20--=20it=20has=20very=20odd=20behavior:=0A+=09=20= *=0A+=09=20*=20=20=20=20=20= https://github.com/openssl/openssl/issues/6109=0A+=09=20*/=0A+=09cert=20= =3D=20SSL_CTX_get0_certificate(ctx);=0A+=09key=20=3D=20= SSL_CTX_get0_privatekey(ctx);=0A+=0A+=09Assert(cert=20&&=20key);=0A+=0A+=09= if=20(!SSL_CTX_get0_chain_certs(ctx,=20&chain)=0A+=09=09||=20= !SSL_use_cert_and_key(ssl,=20cert,=20key,=20chain,=201=20/*=20override=20= */=20)=0A+=09=09||=20!SSL_check_private_key(ssl))=0A+=09{=0A+=09=09/*=0A= +=09=09=20*=20This=20shouldn't=20really=20be=20possible,=20since=20the=20= inputs=20came=20from=20a=0A+=09=09=20*=20SSL_CTX=20that=20was=20already=20= populated=20by=20OpenSSL.=0A+=09=09=20*/=0A+=09=09ereport(COMMERROR,=0A+=09= =09=09=09errcode(ERRCODE_INTERNAL_ERROR),=0A+=09=09=09=09= errmsg_internal("could=20not=20update=20certificate=20chain:=20%s",=0A+=09= =09=09=09=09=09=09=09SSLerrmessage(ERR_get_error())));=0A+=09=09return=20= false;=0A+=09}=0A+=0A+=09if=20(host_config->ssl_ca=20&&=20= host_config->ssl_ca[0])=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20Copy=20the=20= trust=20store=20and=20list=20of=20roots=20over=20from=20the=20SSL_CTX.=0A= +=09=09=20*/=0A+=09=09X509_STORE=20*ca_store=20=3D=20= SSL_CTX_get_cert_store(ctx);=0A+=0A+=09=09STACK_OF(X509_NAME)=20*=20= roots;=0A+=0A+=09=09/*=0A+=09=09=20*=20The=20trust=20store=20appears=20= to=20be=20the=20only=20setting=20that=20this=20function=0A+=09=09=20*=20= can't=20override=20via=20the=20(SSL=20*)=20pointer=20directly.=20= Instead,=20share=20it=0A+=09=09=20*=20with=20the=20active=20SSL_CTX=20= (this=20should=20always=20be=20SSL_context).=0A+=09=09=20*/=0A+=09=09= Assert(SSL_context=20=3D=3D=20SSL_get_SSL_CTX(ssl));=0A+=09=09= SSL_CTX_set1_cert_store(SSL_context,=20ca_store);=0A+=0A+=09=09/*=0A+=09=09= =20*=20TODO:=20test=20that=20the=20new=20locations=20don't=20stack=20= with=20prior=20CA=20config;=0A+=09=09=20*=20that's=20CVE-worthy=0A+=09=09= =20*=0A+=09=09=20*=20TODO:=20test=20interactions=20with=20CRLs.=0A+=09=09= =20*/=0A+=0A+=09=09/*=0A+=09=09=20*=20SSL_set_client_CA_list()=20will=20= take=20ownership=20of=20its=20argument,=20so=20we=0A+=09=09=20*=20need=20= to=20duplicate=20it.=0A+=09=09=20*/=0A+=09=09if=20((roots=20=3D=20= SSL_CTX_get_client_CA_list(ctx))=20=3D=3D=20NULL=0A+=09=09=09||=20(roots=20= =3D=20SSL_dup_CA_list(roots))=20=3D=3D=20NULL)=0A+=09=09{=0A+=09=09=09= ereport(COMMERROR,=0A+=09=09=09=09=09errcode(ERRCODE_INTERNAL_ERROR),=0A= +=09=09=09=09=09errmsg_internal("could=20not=20duplicate=20SSL_CTX=20CA=20= list:=20%s",=0A+=09=09=09=09=09=09=09=09=09= SSLerrmessage(ERR_get_error())));=0A+=09=09=09return=20false;=0A+=09=09}=0A= +=0A+=09=09SSL_set_client_CA_list(ssl,=20roots);=0A+=0A+=09=09/*=0A+=09=09= =20*=20Always=20ask=20for=20SSL=20client=20cert,=20but=20don't=20fail=20= if=20it's=20not=0A+=09=09=20*=20presented.=20=20We=20might=20fail=20such=20= connections=20later,=20depending=20on=20what=0A+=09=09=20*=20we=20find=20= in=20pg_hba.conf.=0A+=09=09=20*/=0A+=09=09SSL_set_verify(ssl,=0A+=09=09=09= =09=09=20=20=20(SSL_VERIFY_PEER=20|=20SSL_VERIFY_CLIENT_ONCE),=0A+=09=09=09= =09=09=20=20=20verify_cb);=0A+=0A+=09=09ssl_loaded_verify_locations=20=3D=20= true;=0A+=09}=0A+=0A+=09return=20true;=0A+}=0A+=0A+/*=0A+=20*=20= sni_clienthello_cb=0A+=20*=0A+=20*=20Callback=20for=20extracting=20the=20= servername=20extension=20from=20the=20TLS=20handshake=0A+=20*=20during=20= ClientHello.=20=20There=20is=20a=20callback=20in=20OpenSSL=20for=20the=20= servername=0A+=20*=20specifically=20but=20OpenSSL=20themselves=20advice=20= against=20using=20it=20as=20it=20is=20more=0A+=20*=20dependent=20on=20= ordering=20for=20execution.=0A+=20*/=0A+static=20int=0A= +sni_clienthello_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg)=0A+{=0A+=09= const=20char=20*tlsext_hostname;=0A+=09const=20unsigned=20char=20= *tlsext;=0A+=09size_t=09=09left,=0A+=09=09=09=09len;=0A+=09HostsLine=20=20= *install_config=20=3D=20NULL;=0A+=0A+=09if=20(!ssl_sni)=0A+=09{=0A+=09=09= install_config=20=3D=20SSL_hosts->default_host;=0A+=09=09goto=20found;=0A= +=09}=0A+=0A+=09if=20(SSL_client_hello_get0_ext(ssl,=20= TLSEXT_TYPE_server_name,=20&tlsext,=20&left))=0A+=09{=0A+=09=09if=20= (left=20<=3D=202)=0A+=09=09{=0A+=09=09=09*al=20=3D=20= SSL_AD_MISSING_EXTENSION;=0A+=09=09=09return=200;=0A+=09=09}=0A+=09=09= len=20=3D=20(*(tlsext++)=20<<=208);=0A+=09=09len=20+=3D=20*(tlsext)++;=0A= +=09=09if=20(len=20+=202=20!=3D=20left)=0A+=09=09{=0A+=09=09=09*al=20=3D=20= SSL_AD_MISSING_EXTENSION;=0A+=09=09=09return=200;=0A+=09=09}=0A+=0A+=09=09= left=20=3D=20len;=0A+=0A+=09=09if=20(left=20=3D=3D=200=20||=20*tlsext++=20= !=3D=20TLSEXT_NAMETYPE_host_name)=0A+=09=09{=0A+=09=09=09*al=20=3D=20= SSL_AD_MISSING_EXTENSION;=0A+=09=09=09return=200;=0A+=09=09}=0A+=0A+=09=09= left--;=0A+=0A+=09=09/*=0A+=09=09=20*=20Now=20we=20can=20finally=20pull=20= out=20the=20byte=20array=20with=20the=20actual=0A+=09=09=20*=20hostname.=0A= +=09=09=20*/=0A+=09=09if=20(left=20<=3D=202)=0A+=09=09{=0A+=09=09=09*al=20= =3D=20SSL_AD_MISSING_EXTENSION;=0A+=09=09=09return=200;=0A+=09=09}=0A+=09= =09len=20=3D=20(*(tlsext++)=20<<=208);=0A+=09=09len=20+=3D=20= *(tlsext++);=0A+=09=09if=20(len=20+=202=20>=20left)=0A+=09=09{=0A+=09=09=09= *al=20=3D=20SSL_AD_MISSING_EXTENSION;=0A+=09=09=09return=200;=0A+=09=09}=0A= +=09=09left=20=3D=20len;=0A+=09=09tlsext_hostname=20=3D=20(const=20char=20= *)=20tlsext;=0A+=0A+=09=09/*=0A+=09=09=20*=20We=20have=20a=20requested=20= hostname=20from=20the=20client,=20match=20against=20all=0A+=09=09=20*=20= entries=20in=20the=20pg_hosts=20configuration=20and=20attempt=20to=20= find=20a=20match.=0A+=09=09=20*=20Matching=20is=20done=20case=20= insensitive=20as=20per=20RFC=20952=20and=20RFC=20921.=0A+=09=09=20*/=0A+=09= =09foreach_ptr(HostsLine,=20host,=20SSL_hosts->sni)=0A+=09=09{=0A+=09=09=09= foreach_ptr(char,=20hostname,=20host->hostnames)=0A+=09=09=09{=0A+=09=09=09= =09if=20(strlen(hostname)=20=3D=3D=20len=20&&=0A+=09=09=09=09=09= pg_strncasecmp(hostname,=20tlsext_hostname,=20len)=20=3D=3D=200)=0A+=09=09= =09=09{=0A+=09=09=09=09=09install_config=20=3D=20host;=0A+=09=09=09=09=09= goto=20found;=0A+=09=09=09=09}=0A+=09=09=09}=0A+=09=09}=0A+=0A+=09=09/*=0A= +=09=09=20*=20If=20no=20host=20specific=20match=20was=20found,=20and=20= there=20is=20a=20default=20config,=0A+=09=09=20*=20then=20fall=20back=20= to=20using=20that.=0A+=09=09=20*/=0A+=09=09if=20(!install_config=20&&=20= SSL_hosts->default_host)=0A+=09=09=09install_config=20=3D=20= SSL_hosts->default_host;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20No=20hostname=20= TLS=20extension=20in=20the=20handshake,=20use=20the=20default=20or=20= no_sni=0A+=09=20*=20configurations=20if=20available.=0A+=09=20*/=0A+=09= else=0A+=09{=0A+=09=09if=20(SSL_hosts->no_sni)=0A+=09=09=09= install_config=20=3D=20SSL_hosts->no_sni;=0A+=09=09else=20if=20= (SSL_hosts->default_host)=0A+=09=09=09install_config=20=3D=20= SSL_hosts->default_host;=0A+=09=09else=0A+=09=09{=0A+=09=09=09/*=0A+=09=09= =09=20*=20Reaching=20here=20means=20that=20we=20didn't=20get=20a=20= hostname=20in=20the=20TLS=0A+=09=09=09=20*=20extension=20and=20the=20= server=20has=20been=20configured=20to=20not=20allow=20any=0A+=09=09=09=20= *=20connections=20without=20a=20specified=20hostname.=0A+=09=09=09=20*=0A= +=09=09=09=20*=20The=20error=20message=20for=20a=20missing=20server_name=20= should,=20according=0A+=09=09=09=20*=20to=20RFC=208446,=20be=20= missing_extension.=20This=20isn't=20entirely=20ideal=0A+=09=09=09=20*=20= since=20the=20user=20won't=20be=20able=20to=20tell=20which=20extension=20= the=20server=0A+=09=09=09=20*=20considered=20missing.=20=20Sending=20= unrecognized_name=20would=20be=20a=20more=0A+=09=09=09=20*=20helpful=20= error,=20but=20for=20now=20we=20stick=20to=20the=20RFC.=0A+=09=09=09=20= */=0A+=09=09=09*al=20=3D=20SSL_AD_MISSING_EXTENSION;=0A+=0A+=09=09=09= ereport(COMMERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20errmsg("no=20= hostname=20provided=20in=20callback,=20and=20no=20fallback=20= configured")));=0A+=09=09=09return=20SSL_CLIENT_HELLO_ERROR;=0A+=09=09}=0A= +=09}=0A+=0A+=09/*=0A+=09=20*=20If=20we=20reach=20here=20without=20a=20= context=20chosen=20as=20the=20session=20context=20then=0A+=09=20*=20fail=20= the=20handshake=20and=20terminate=20the=20connection.=0A+=09=20*/=0A+=09= if=20(install_config=20=3D=3D=20NULL)=0A+=09{=0A+=09=09if=20= (tlsext_hostname)=0A+=09=09=09*al=20=3D=20SSL_AD_UNRECOGNIZED_NAME;=0A+=09= =09else=0A+=09=09=09*al=20=3D=20SSL_AD_MISSING_EXTENSION;=0A+=09=09= return=20SSL_CLIENT_HELLO_ERROR;=0A+=09}=0A+=0A+found:=0A+=09if=20= (!ssl_update_ssl(ssl,=20install_config))=0A+=09{=0A+=09=09= ereport(COMMERROR,=0A+=09=09=09=09errcode(ERRCODE_PROTOCOL_VIOLATION),=0A= +=09=09=09=09errmsg("failed=20to=20switch=20to=20SSL=20configuration=20= for=20host,=20terminating=20connection"));=0A+=09=09return=20= SSL_CLIENT_HELLO_ERROR;=0A+=09}=0A+=0A+=09return=20= SSL_CLIENT_HELLO_SUCCESS;=0A+}=0A+#endif=09=09=09=09=09=09=09/*=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=20*/=0A=20=0A=20/*=0A=20=20*=20Set=20DH=20= parameters=20for=20generating=20ephemeral=20DH=20keys.=20=20The=0A@@=20= -1791,6=20+2443,20=20@@=20ssl_protocol_version_to_string(int=20v)=0A=20=09= return=20"(unrecognized)";=0A=20}=0A=20=0A+static=20uint32=0A= +host_cache_pointer(const=20char=20*key)=0A+{=0A+=09uint32=09=09hash;=0A= +=09char=09=20=20=20*lkey=20=3D=20pstrdup(key);=0A+=09int=09=09=09len=20= =3D=20strlen(key);=0A+=0A+=09for=20(int=20i=20=3D=200;=20i=20<=20len;=20= i++)=0A+=09=09lkey[i]=20=3D=20pg_tolower(lkey[i]);=0A+=0A+=09hash=20=3D=20= string_hash((const=20void=20*)=20lkey,=20len);=0A+=09pfree(lkey);=0A+=09= return=20hash;=0A+}=0A=20=0A=20static=20void=0A=20= default_openssl_tls_init(SSL_CTX=20*context,=20bool=20isServerStart)=0A= @@=20-1798,12=20+2464,18=20@@=20default_openssl_tls_init(SSL_CTX=20= *context,=20bool=20isServerStart)=0A=20=09if=20(isServerStart)=0A=20=09{=0A= =20=09=09if=20(ssl_passphrase_command[0])=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= ssl_passphrase_command);=0A+=09=09}=0A=20=09}=0A=20=09else=0A=20=09{=0A=20= =09=09if=20(ssl_passphrase_command[0]=20&&=20= ssl_passphrase_command_supports_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= ssl_passphrase_command);=0A+=09=09}=0A=20=09=09else=0A=20=0A=20=09=09=09= /*=0Adiff=20--git=20a/src/backend/libpq/be-secure.c=20= b/src/backend/libpq/be-secure.c=0Aindex=20edd69823b92..617704bb993=20= 100644=0A---=20a/src/backend/libpq/be-secure.c=0A+++=20= b/src/backend/libpq/be-secure.c=0A@@=20-61,6=20+61,9=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+/*=20GUC=20variable:=20if=20false,=20discards=20= hostname=20extensions=20in=20handshake=20*/=0A+bool=09=09ssl_sni=20=3D=20= false;=0A+=0A=20/*=20= ------------------------------------------------------------=20*/=0A=20= /*=09=09=09=20Procedures=20common=20to=20all=20secure=20sessions=09=09=09= */=0A=20/*=20= ------------------------------------------------------------=20*/=0Adiff=20= --git=20a/src/backend/libpq/meson.build=20= b/src/backend/libpq/meson.build=0Aindex=20ee337cf42cc..8571f652844=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..a31c49b01f7=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=20=20=20=20=20= SSL=20CA=20=20=20=20=20=20=20PASSPHRASE=20COMMAND=20=20=20=20=20=20=20=20= =20PASSPHRASE=20COMMAND=20RELOAD=0Adiff=20--git=20= a/src/backend/utils/misc/guc.c=20b/src/backend/utils/misc/guc.c=0Aindex=20= d77502838c4..e1546d9c97a=20100644=0A---=20a/src/backend/utils/misc/guc.c=0A= +++=20b/src/backend/utils/misc/guc.c=0A@@=20-56,6=20+56,7=20@@=0A=20= #define=20CONFIG_FILENAME=20"postgresql.conf"=0A=20#define=20= HBA_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=20CONFIG_EXEC_PARAMS=20= "global/config_exec_params"=0A@@=20-1838,6=20+1839,37=20@@=20= SelectConfigFiles(const=20char=20*userDoption,=20const=20char=20= *progname)=0A=20=09}=0A=20=09SetConfigOption("ident_file",=20fname,=20= PGC_POSTMASTER,=20PGC_S_OVERRIDE);=0A=20=0A+=09if=20(fname_is_malloced)=0A= +=09=09free(fname);=0A+=09else=0A+=09=09guc_free(fname);=0A+=0A+=09/*=0A= +=09=20*=20Likewise=20for=20pg_hosts.conf.=0A+=09=20*/=0A+=09if=20= (HostsFileName)=0A+=09{=0A+=09=09fname=20=3D=20= make_absolute_path(HostsFileName);=0A+=09=09fname_is_malloced=20=3D=20= true;=0A+=09}=0A+=09else=20if=20(configdir)=0A+=09{=0A+=09=09fname=20=3D=20= guc_malloc(FATAL,=0A+=09=09=09=09=09=09=20=20=20strlen(configdir)=20+=20= strlen(HOSTS_FILENAME)=20+=202);=0A+=09=09sprintf(fname,=20"%s/%s",=20= configdir,=20HOSTS_FILENAME);=0A+=09=09fname_is_malloced=20=3D=20false;=0A= +=09}=0A+=09else=0A+=09{=0A+=09=09write_stderr("%s=20does=20not=20know=20= where=20to=20find=20the=20\"hosts\"=20configuration=20file.\n"=0A+=09=09=09= =09=09=20"This=20can=20be=20specified=20as=20\"hosts_file\"=20in=20= \"%s\",=20"=0A+=09=09=09=09=09=20"or=20by=20the=20-D=20invocation=20= option,=20or=20by=20the=20"=0A+=09=09=09=09=09=20"PGDATA=20environment=20= variable.\n",=0A+=09=09=09=09=09=20progname,=20ConfigFileName);=0A+=09=09= goto=20fail;=0A+=09}=0A+=09SetConfigOption("hosts_file",=20fname,=20= PGC_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_parameters.dat=20= b/src/backend/utils/misc/guc_parameters.dat=0Aindex=20= a5a0edf2534..bf092b4cdeb=20100644=0A---=20= a/src/backend/utils/misc/guc_parameters.dat=0A+++=20= b/src/backend/utils/misc/guc_parameters.dat=0A@@=20-1177,6=20+1177,13=20= @@=0A=20=20=20boot_val=20=3D>=20'NULL',=0A=20},=0A=20=0A+{=20name=20=3D>=20= 'hosts_file',=20type=20=3D>=20'string',=20context=20=3D>=20= 'PGC_POSTMASTER',=20group=20=3D>=20'FILE_LOCATIONS',=0A+=20=20short_desc=20= =3D>=20'Sets=20the=20server\'s=20"hosts"=20configuration=20file.',=0A+=20= =20flags=20=3D>=20'GUC_SUPERUSER_ONLY',=0A+=20=20variable=20=3D>=20= 'HostsFileName',=0A+=20=20boot_val=20=3D>=20'NULL',=0A+},=0A+=0A=20{=20= name=20=3D>=20'hot_standby',=20type=20=3D>=20'bool',=20context=20=3D>=20= 'PGC_POSTMASTER',=20group=20=3D>=20'REPLICATION_STANDBY',=0A=20=20=20= short_desc=20=3D>=20'Allows=20connections=20and=20queries=20during=20= recovery.',=0A=20=20=20variable=20=3D>=20'EnableHotStandby',=0A@@=20= -2764,6=20+2771,14=20@@=0A=20=20=20max=20=3D>=20'0',=0A=20},=0A=20=0A+{=20= name=20=3D>=20'ssl_sni',=20type=20=3D>=20'bool',=20context=20=3D>=20= 'PGC_SIGHUP',=20group=20=3D>=20'CONN_AUTH_SSL',=0A+=20=20short_desc=20=3D>= =20'Sets=20whether=20to=20interpret=20SNI=20extensions=20in=20SSL=20= connections.',=0A+=20=20flags=20=3D>=20'GUC_SUPERUSER_ONLY',=0A+=20=20= variable=20=3D>=20'ssl_sni',=0A+=20=20boot_val=20=3D>=20'false',=0A+=20=20= check_nook=20=3D>=20'check_ssl_sni',=0A+},=0A+=0A=20{=20name=20=3D>=20= 'ssl_tls13_ciphers',=20type=20=3D>=20'string',=20context=20=3D>=20= 'PGC_SIGHUP',=20group=20=3D>=20'CONN_AUTH_SSL',=0A=20=20=20short_desc=20= =3D>=20'Sets=20the=20list=20of=20allowed=20TLSv1.3=20cipher=20suites.',=0A= =20=20=20long_desc=20=3D>=20'An=20empty=20string=20means=20use=20the=20= default=20cipher=20suites.',=0Adiff=20--git=20= a/src/backend/utils/misc/guc_tables.c=20= b/src/backend/utils/misc/guc_tables.c=0Aindex=2038aaf82f120..1e14b7b4af0=20= 100644=0A---=20a/src/backend/utils/misc/guc_tables.c=0A+++=20= b/src/backend/utils/misc/guc_tables.c=0A@@=20-565,6=20+565,7=20@@=20char=09= =20=20=20*cluster_name=20=3D=20"";=0A=20char=09=20=20=20*ConfigFileName;=0A= =20char=09=20=20=20*HbaFileName;=0A=20char=09=20=20=20*IdentFileName;=0A= +char=09=20=20=20*HostsFileName;=0A=20char=09=20=20=20= *external_pid_file;=0A=20=0A=20char=09=20=20=20*application_name;=0Adiff=20= --git=20a/src/backend/utils/misc/postgresql.conf.sample=20= b/src/backend/utils/misc/postgresql.conf.sample=0Aindex=20= e686d88afc4..e4abe6c0077=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=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=20=20=20=20=20=20=20=20=20=20=20=20#=20(change=20= requires=20restart)=0A=20#ident_file=20=3D=20'ConfigDir/pg_ident.conf'=20= #=20ident=20configuration=20file=0A=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=20=20=20=20=20=20=20=20=20=20= =20=20=20#=20(change=20requires=20restart)=0A+#hosts_file=20=3D=20= 'ConfigDir/pg_hosts.conf'=20#=20hosts=20configuration=20file=0A+=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= =20=20=20=20=20=20=20=20=20=20=20=20#=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''=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20#=20write=20an=20extra=20= PID=20file=0A@@=20-122,6=20+124,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_sni=20=3D=20= off=0A=20=0A=20=0A=20= #-------------------------------------------------------------------------= -----=0Adiff=20--git=20a/src/bin/initdb/initdb.c=20= b/src/bin/initdb/initdb.c=0Aindex=20f3174d79f32..509f1114ef6=20100644=0A= ---=20a/src/bin/initdb/initdb.c=0A+++=20b/src/bin/initdb/initdb.c=0A@@=20= -177,6=20+177,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-1547,6=20+1548,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-2808,6=20= +2817,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= -2823,12=20+2833,12=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\nPG_HOSTS_SAMPLE=3D%s\n",=0A=20= =09=09=09=09PG_VERSION,=0A=20=09=09=09=09pg_data,=20share_path,=20= bin_path,=0A=20=09=09=09=09username,=20bki_file,=0A=20=09=09=09=09= conf_file,=0A-=09=09=09=09hba_file,=20ident_file);=0A+=09=09=09=09= hba_file,=20ident_file,=20hosts_file);=0A=20=09=09if=20(show_setting)=0A=20= =09=09=09exit(0);=0A=20=09}=0A@@=20-2836,6=20+2846,7=20@@=20= setup_data_file_paths(void)=0A=20=09check_input(bki_file);=0A=20=09= check_input(hba_file);=0A=20=09check_input(ident_file);=0A+=09= check_input(hosts_file);=0A=20=09check_input(conf_file);=0A=20=09= check_input(dictionary_file);=0A=20=09check_input(info_schema_file);=0A= diff=20--git=20a/src/include/libpq/hba.h=20b/src/include/libpq/hba.h=0A= index=207b93ba4a709..bbc6a97ccdc=20100644=0A---=20= a/src/include/libpq/hba.h=0A+++=20b/src/include/libpq/hba.h=0A@@=20= -151,6=20+151,36=20@@=20typedef=20struct=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+=09= List=09=20=20=20*hostnames;=0A+=09char=09=20=20=20*ssl_key;=0A+=09char=09= =20=20=20*ssl_cert;=0A+=0A+=09/*=20Optional=20fields=20*/=0A+=09char=09=20= =20=20*ssl_ca;=0A+=09char=09=20=20=20*ssl_passphrase_cmd;=0A+=09bool=09=09= ssl_passphrase_reload;=0A+=0A+=09/*=20Internal=20bookkeeping=20*/=0A+=09= void=09=20=20=20*ssl_ctx;=09=09/*=20associated=20SSL_CTX*=20for=20the=20= above=20settings=20*/=0A+}=20HostsLine;=0A+=0A+typedef=20enum=20= HostsFileLoad=0A+{=0A+=09HOSTSFILE_LOAD_OK=20=3D=200,=0A+=09= HOSTSFILE_LOAD_FAILED,=0A+=09HOSTSFILE_EMPTY,=0A+=09HOSTSFILE_MISSING,=0A= +=09HOSTSFILE_DISABLED,=0A+}=20HostsFileLoadResult;=0A+=0A=20/*=0A=20=20= *=20TokenizedAuthLine=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.h=20b/src/include/libpq/libpq.h=0Aindex=20= 790724b6a0b..c9b934d2321=20100644=0A---=20a/src/include/libpq/libpq.h=0A= +++=20b/src/include/libpq/libpq.h=0A@@=20-113,6=20+113,7=20@@=20extern=20= PGDLLIMPORT=20int=20ssl_max_protocol_version;=0A=20extern=20PGDLLIMPORT=20= char=20*ssl_passphrase_command;=0A=20extern=20PGDLLIMPORT=20bool=20= ssl_passphrase_command_supports_reload;=0A=20extern=20PGDLLIMPORT=20char=20= *ssl_dh_params_file;=0A+extern=20PGDLLIMPORT=20bool=20ssl_sni;=0A=20= extern=20PGDLLIMPORT=20char=20*SSLCipherSuites;=0A=20extern=20= PGDLLIMPORT=20char=20*SSLCipherList;=0A=20extern=20PGDLLIMPORT=20char=20= *SSLECDHCurve;=0A@@=20-158,9=20+159,11=20@@=20enum=20= ssl_protocol_versions=0A=20/*=0A=20=20*=20prototypes=20for=20functions=20= in=20be-secure-common.c=0A=20=20*/=0A-extern=20int=09= run_ssl_passphrase_command(const=20char=20*prompt,=20bool=20= is_server_start,=0A+extern=20int=09run_ssl_passphrase_command(const=20= char=20*cmd,=20const=20char=20*prompt,=0A+=09=09=09=09=09=09=09=09=09=20=20= =20bool=20is_server_start,=0A=20=09=09=09=09=09=09=09=09=09=20=20=20char=20= *buf,=20int=20size);=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=20int=09= load_hosts(List=20**hosts,=20char=20**err_msg);=0A=20=0A=20#endif=09=09=09= =09=09=09=09/*=20LIBPQ_H=20*/=0Adiff=20--git=20= a/src/include/pg_config.h.in=20b/src/include/pg_config.h.in=0Aindex=20= cb0f53fade4..bb9ea39bd60=20100644=0A---=20a/src/include/pg_config.h.in=0A= +++=20b/src/include/pg_config.h.in=0A@@=20-368,6=20+368,9=20@@=0A=20/*=20= Define=20to=201=20if=20you=20have=20the=20`SSL_CTX_set_ciphersuites'=20= function.=20*/=0A=20#undef=20HAVE_SSL_CTX_SET_CIPHERSUITES=0A=20=0A+/*=20= Define=20to=201=20if=20you=20have=20the=20`SSL_CTX_set_client_hello_cb'=20= function.=20*/=0A+#undef=20HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A+=0A=20/*=20= Define=20to=201=20if=20you=20have=20the=20`SSL_CTX_set_keylog_callback'=20= function.=20*/=0A=20#undef=20HAVE_SSL_CTX_SET_KEYLOG_CALLBACK=0A=20=0A= diff=20--git=20a/src/include/utils/guc.h=20b/src/include/utils/guc.h=0A= index=20c46203fabfe..dc406d6651a=20100644=0A---=20= a/src/include/utils/guc.h=0A+++=20b/src/include/utils/guc.h=0A@@=20= -312,6=20+312,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=20a/src/include/utils/guc_hooks.h=20= b/src/include/utils/guc_hooks.h=0Aindex=209c90670d9b8..b01697c1f60=20= 100644=0A---=20a/src/include/utils/guc_hooks.h=0A+++=20= b/src/include/utils/guc_hooks.h=0A@@=20-133,6=20+133,7=20@@=20extern=20= void=20assign_session_authorization(const=20char=20*newval,=20void=20= *extra);=0A=20extern=20void=20assign_session_replication_role(int=20= newval,=20void=20*extra);=0A=20extern=20void=20= assign_stats_fetch_consistency(int=20newval,=20void=20*extra);=0A=20= extern=20bool=20check_ssl(bool=20*newval,=20void=20**extra,=20GucSource=20= source);=0A+extern=20bool=20check_ssl_sni(bool=20*newval,=20void=20= **extra,=20GucSource=20source);=0A=20extern=20bool=20= check_stage_log_stats(bool=20*newval,=20void=20**extra,=20GucSource=20= source);=0A=20extern=20bool=20check_standard_conforming_strings(bool=20= *newval,=20void=20**extra,=0A=20=09=09=09=09=09=09=09=09=09=09=09=20=20= GucSource=20source);=0Adiff=20--git=20= a/src/test/perl/PostgreSQL/Test/Cluster.pm=20= b/src/test/perl/PostgreSQL/Test/Cluster.pm=0Aindex=20= e267ba868fe..b44aefb545a=20100644=0A---=20= a/src/test/perl/PostgreSQL/Test/Cluster.pm=0A+++=20= b/src/test/perl/PostgreSQL/Test/Cluster.pm=0A@@=20-1302,6=20+1302,27=20= @@=20Wrapper=20for=20pg_ctl=20restart.=0A=20With=20optional=20extra=20= param=20fail_ok=20=3D>=201,=20returns=200=20for=20failure=0A=20instead=20= of=20bailing=20out.=0A=20=0A+=3Dover=0A+=0A+=3Ditem=20fail_ok=20=3D>=201=0A= +=0A+By=20default,=20failure=20terminates=20the=20entire=20F=20= invocation.=20=20If=20given,=0A+instead=20return=200=20for=20failure=20= instead=20of=20bailing=20out.=0A+=0A+=3Ditem=20log_unlike=20=3D>=20= B=0A+=0A+When=20defined,=20the=20logfile=20is=20inspected=20for=20= the=20presence=20of=20the=20fragment=20by=0A+matching=20the=20specified=20= pattern.=20If=20the=20pattern=20matches=20against=20the=20logfile=20a=0A= +test=20failure=20will=20be=20logged.=0A+=0A+=3Ditem=20log_like=20=3D>=20= B=0A+=0A+When=20defined,=20the=20logfile=20is=20inspected=20for=20= the=20presence=20of=20the=20fragment=20by=0A+matching=20the=20pattern.=20= If=20the=20pattern=20doesn't=20match=20a=20test=20failure=20will=20be=0A= +logged.=0A+=0A+=3Dback=0A+=0A=20=3Dcut=0A=20=0A=20sub=20restart=0A@@=20= -1314,6=20+1335,8=20@@=20sub=20restart=0A=20=0A=20=09print=20"###=20= Restarting=20node=20\"$name\"\n";=0A=20=0A+=09my=20$log_location=20=3D=20= -s=20$self->logfile;=0A+=0A=20=09#=20-w=20is=20now=20the=20default=20but=20= having=20it=20here=20does=20no=20harm=20and=20helps=0A=20=09#=20= compatibility=20with=20older=20versions.=0A=20=09$ret=20=3D=20= PostgreSQL::Test::Utils::system_log(=0A@@=20-1322,6=20+1345,18=20@@=20= sub=20restart=0A=20=09=09'--log'=20=3D>=20$self->logfile,=0A=20=09=09= 'restart');=0A=20=0A+=09#=20Check=20for=20expected=20and/or=20unexpected=20= log=20fragments=20if=20the=20caller=0A+=09#=20specified=20such=20checks=20= in=20the=20params=0A+=09if=20(defined=20$params{log_unlike}=20||=20= defined=20$params{log_like})=0A+=09{=0A+=09=09my=20$log=20=3D=0A+=09=09=20= =20PostgreSQL::Test::Utils::slurp_file($self->logfile,=20$log_location);=0A= +=09=09unlike($log,=20$params{log_unlike},=20"unexpected=20fragment=20= found=20in=20log")=0A+=09=09=09if=20defined=20$params{log_unlike};=0A+=09= =09like($log,=20$params{log_like},=20"expected=20fragment=20not=20found=20= in=20log")=0A+=09=09=09if=20defined=20$params{log_like};=0A+=09}=0A+=0A=20= =09if=20($ret=20!=3D=200)=0A=20=09{=0A=20=09=09print=20"#=20pg_ctl=20= restart=20failed;=20see=20logfile=20for=20details:=20"=0Adiff=20--git=20= a/src/test/ssl/meson.build=20b/src/test/ssl/meson.build=0Aindex=20= 9e5bdbb6136..d7e7ce23433=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=20a/src/test/ssl/t/001_ssltests.pl=20= b/src/test/ssl/t/001_ssltests.pl=0Aindex=20963bfea8ed5..0af887caa63=20= 100644=0A---=20a/src/test/ssl/t/001_ssltests.pl=0A+++=20= b/src/test/ssl/t/001_ssltests.pl=0A@@=20-380,11=20+380,11=20@@=20= switch_server_cert($node,=20certfile=20=3D>=20'server-ip-cn-only');=0A=20= $common_connstr=20=3D=0A=20=20=20"$default_ssl_connstr=20= user=3Dssltestuser=20dbname=3Dtrustdb=20= sslrootcert=3Dssl/root+server_ca.crt=20hostaddr=3D$SERVERHOSTADDR=20= sslmode=3Dverify-full";=0A=20=0A-$node->connect_ok("$common_connstr=20= host=3D192.0.2.1",=0A+$node->connect_ok("$common_connstr=20= host=3D192.0.2.1=20sslsni=3D0",=0A=20=09"IP=20address=20in=20the=20= Common=20Name");=0A=20=0A=20$node->connect_fails(=0A-=09"$common_connstr=20= host=3D192.000.002.001",=0A+=09"$common_connstr=20host=3D192.000.002.001=20= sslsni=3D0",=0A=20=09"mismatch=20between=20host=20name=20and=20server=20= certificate=20IP=20address",=0A=20=09expected_stderr=20=3D>=0A=20=09=20=20= qr/\Qserver=20certificate=20for=20"192.0.2.1"=20does=20not=20match=20= host=20name=20"192.000.002.001"\E/=0A@@=20-394,7=20+394,7=20@@=20= $node->connect_fails(=0A=20#=20long-standing=20behavior.)=0A=20= switch_server_cert($node,=20certfile=20=3D>=20'server-ip-in-dnsname');=0A= =20=0A-$node->connect_ok("$common_connstr=20host=3D192.0.2.1",=0A= +$node->connect_ok("$common_connstr=20host=3D192.0.2.1=20sslsni=3D0",=0A=20= =09"IP=20address=20in=20a=20dNSName");=0A=20=0A=20#=20Test=20Subject=20= Alternative=20Names.=0Adiff=20--git=20a/src/test/ssl/t/004_sni.pl=20= b/src/test/ssl/t/004_sni.pl=0Anew=20file=20mode=20100644=0Aindex=20= 00000000000..6fe93fc1607=0A---=20/dev/null=0A+++=20= b/src/test/ssl/t/004_sni.pl=0A@@=20-0,0=20+1,412=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=20hostaddr=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+if=20($ssl_server->is_libressl)=0A+{=0A+=09plan=20skip_all=20=3D>=20= 'SNI=20not=20supported=20when=20building=20with=20LibreSSL';=0A+}=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=20sslsni=3D1";=0A+=0A= +#########################################################################= #####=0A+#=20postgresql.conf=0A= +#########################################################################= #####=0A+=0A+#=20Connect=20without=20any=20hosts=20configured=20in=20= pg_hosts.conf,=20thus=20using=20the=20cert=0A+#=20and=20key=20in=20= postgresql.conf.=20pg_hosts.conf=20exists=20at=20this=20point=20but=20is=20= empty=0A+#=20apart=20from=20the=20comments=20stemming=20from=20the=20= sample.=0A+$node->connect_ok(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A+=09= "pg.conf:=20connect=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"pg.conf:=20= connect=20fails=20without=20intermediate=20for=20sslmode=3Dverify-ca",=0A= +=09expected_stderr=20=3D>=20qr/certificate=20verify=20failed/);=0A+=0A= +#=20Add=20an=20entry=20in=20pg_hosts.conf=20with=20no=20default,=20and=20= reload.=20Since=20ssl_sni=20is=0A+#=20still=20'off'=20we=20should=20= still=20be=20able=20to=20connect=20using=20the=20certificates=20in=0A+#=20= postgresql.conf=0A+$node->append_conf('pg_hosts.conf',=0A+=09= "example.org=20server-cn-only.crt=20server-cn-only.key");=0A= +$node->reload;=0A+$node->connect_ok(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A+=09= "pg.conf:=20connect=20with=20correct=20server=20CA=20cert=20file=20= sslmode=3Drequire");=0A+=0A+#=20Turn=20on=20SNI=20support=20and=20remove=20= pg_hosts.conf=20and=20reload=20to=20make=20sure=20a=0A+#=20missing=20= file=20is=20treated=20like=20an=20empty=20file.=0A= +$node->append_conf('postgresql.conf',=20'ssl_sni=20=3D=20on');=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A+$node->reload;=0A= +=0A+$node->connect_ok(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A+=09= "pg.conf:=20connect=20after=20deleting=20pg_hosts.conf");=0A+=0A= +#########################################################################= #####=0A+#=20pg_hosts.conf=0A= +#########################################################################= #####=0A+=0A+#=20Replicate=20the=20postgresql.conf=20configuration=20= into=20pg_hosts.conf=20and=20retry=20the=0A+#=20same=20tests=20as=20= above.=0A+$node->append_conf('pg_hosts.conf',=0A+=09"*=20= server-cn-only.crt=20server-cn-only.key");=0A+$node->reload;=0A+=0A= +$node->connect_ok(=0A+=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt= =20sslmode=3Drequire",=0A+=09"pg_hosts.conf:=20connect=20to=20default,=20= with=20correct=20server=20CA=20cert=20file=20sslmode=3Drequire"=0A+);=0A= +=0A+$node->connect_fails(=0A+=09"$connstr=20sslrootcert=3Dssl/root_ca.crt= =20sslmode=3Dverify-ca",=0A+=09"pg_hosts.conf:=20connect=20to=20default,=20= fail=20without=20intermediate=20for=20sslmode=3Dverify-ca",=0A+=09= expected_stderr=20=3D>=20qr/certificate=20verify=20failed/);=0A+=0A+#=20= Add=20host=20entry=20for=20example.org=20which=20serves=20the=20server=20= cert=20and=20its=0A+#=20intermediate=20CA.=20=20The=20previously=20= existing=20default=20host=20still=20exists=20without=0A+#=20a=20CA.=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"pg_hosts.conf:=20connect=20to=20example.org=20and=20verify=20= server=20CA");=0A+=0A+$node->connect_ok(=0A+=09"$connstr=20= host=3DExample.ORG=20sslrootcert=3Dssl/root_ca.crt=20sslmode=3Dverify-ca",= =0A+=09"pg_hosts.conf:=20connect=20to=20Example.ORG=20and=20verify=20= server=20CA");=0A+=0A+$node->connect_fails(=0A+=09"$connstr=20= host=3Dexample.org=20sslrootcert=3Dinvalid=20sslmode=3Dverify-ca",=0A+=09= "pg_hosts.conf:=20connect=20to=20example.org=20but=20without=20server=20= root=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"pg_hosts.conf:=20connect=20to=20default=20= and=20fail=20to=20verify=20CA",=0A+=09expected_stderr=20=3D>=20= qr/certificate=20verify=20failed/);=0A+=0A+$node->connect_ok(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire",=0A= +=09"pg_hosts.conf:=20connect=20to=20default=20with=20sslmode=3Drequire");= =0A+=0A+#=20Use=20multiple=20hostnames=20for=20a=20single=20= configuration=0A+ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf('pg_hosts.conf',=0A+=09= "example.org,example.com,example.net=20server-cn-only+server_ca.crt=20= server-cn-only.key=20root_ca.crt"=0A+);=0A+$node->reload;=0A+=0A= +$node->connect_ok(=0A+=09"$connstr=20host=3Dexample.org=20= sslrootcert=3Dssl/root_ca.crt=20sslmode=3Dverify-ca",=0A+=09= "pg_hosts.conf:=20connect=20to=20example.org=20and=20verify=20server=20= CA");=0A+$node->connect_ok(=0A+=09"$connstr=20host=3Dexample.com=20= sslrootcert=3Dssl/root_ca.crt=20sslmode=3Dverify-ca",=0A+=09= "pg_hosts.conf:=20connect=20to=20example.com=20and=20verify=20server=20= CA");=0A+$node->connect_ok(=0A+=09"$connstr=20host=3Dexample.net=20= sslrootcert=3Dssl/root_ca.crt=20sslmode=3Dverify-ca",=0A+=09= "pg_hosts.conf:=20connect=20to=20example.net=20and=20verify=20server=20= CA");=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dexample.se",=0A+=09"pg_hosts.conf:=20connect=20to=20default=20= with=20sslmode=3Drequire",=0A+=09expected_stderr=20=3D>=20= qr/unrecognized=20name/);=0A+=0A+#=20Test=20@-inclusion=20of=20= hostnames.=0A+ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf('pg_hosts.conf',=0A+=09'example.org,@hostnames.txt=20= server-cn-only+server_ca.crt=20server-cn-only.key=20root_ca.crt'=0A+);=0A= +$node->append_conf(=0A+=09'hostnames.txt',=20qq{=0A+example.com=0A= +example.net=0A+});=0A+$node->reload;=0A+=0A+$node->connect_ok(=0A+=09= "$connstr=20host=3Dexample.org=20sslrootcert=3Dssl/root_ca.crt=20= sslmode=3Dverify-ca",=0A+=09'@hostnames.txt:=20connect=20to=20= example.org=20and=20verify=20server=20CA');=0A+$node->connect_ok(=0A+=09= "$connstr=20host=3Dexample.com=20sslrootcert=3Dssl/root_ca.crt=20= sslmode=3Dverify-ca",=0A+=09'@hostnames.txt:=20connect=20to=20= example.com=20and=20verify=20server=20CA');=0A+$node->connect_ok(=0A+=09= "$connstr=20host=3Dexample.net=20sslrootcert=3Dssl/root_ca.crt=20= sslmode=3Dverify-ca",=0A+=09'@hostnames.txt:=20connect=20to=20= example.net=20and=20verify=20server=20CA');=0A+$node->connect_fails(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dexample.se",=0A+=09'@hostnames.txt:=20connect=20to=20default=20= with=20sslmode=3Drequire',=0A+=09expected_stderr=20=3D>=20= qr/unrecognized=20name/);=0A+=0A+#=20Add=20an=20incorrect=20entry=20= specifying=20a=20default=20entry=20combined=20with=20hostnames=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf('pg_hosts.conf',=0A+=09"example.org,*,example.net=20= server-cn-only+server_ca.crt=20server-cn-only.key=20root_ca.crt"=0A+);=0A= +my=20$result=20=3D=20$node->restart(fail_ok=20=3D>=201);=0A+is($result,=20= 0,=0A+=09'pg_hosts.conf:=20restart=20fails=20with=20default=20entry=20= combined=20with=20hostnames'=0A+);=0A+=0A+#=20Add=20incorrect=20= duplicate=20entries.=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf(=0A+=09'pg_hosts.conf',=20qq{=0A= +*=20server-cn-only.crt=20server-cn-only.key=0A+*=20server-cn-only.crt=20= server-cn-only.key=0A+});=0A+$result=20=3D=20$node->restart(fail_ok=20=3D>= =201);=0A+is($result,=200,=20'pg_hosts.conf:=20restart=20fails=20with=20= two=20default=20entries');=0A+=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf(=0A+=09'pg_hosts.conf',=20qq{=0A= +/no_sni/=20server-cn-only.crt=20server-cn-only.key=0A+/no_sni/=20= server-cn-only.crt=20server-cn-only.key=0A+});=0A+$result=20=3D=20= $node->restart(fail_ok=20=3D>=201);=0A+is($result,=200,=20= 'pg_hosts.conf:=20restart=20fails=20with=20two=20no_sni=20entries');=0A+=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf(=0A+=09'pg_hosts.conf',=20qq{=0A+example.org=20= server-cn-only.crt=20server-cn-only.key=0A+example.net=20= server-cn-only.crt=20server-cn-only.key=0A+example.org=20= server-cn-only.crt=20server-cn-only.key=0A+});=0A+$result=20=3D=20= $node->restart(fail_ok=20=3D>=201);=0A+is($result,=200,=20= 'pg_hosts.conf:=20restart=20fails=20with=20two=20identical=20hostname=20= entries');=0A+ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf(=0A+=09'pg_hosts.conf',=20qq{=0A+example.org=20= server-cn-only.crt=20server-cn-only.key=0A= +example.net,example.com,Example.org=20server-cn-only.crt=20= server-cn-only.key=0A+});=0A+$result=20=3D=20$node->restart(fail_ok=20=3D>= =201);=0A+is($result,=200,=20'pg_hosts.conf:=20restart=20fails=20with=20= two=20identical=20hostname=20entries=20in=20lists');=0A+=0A+#=20Modify=20= pg_hosts.conf=20to=20no=20longer=20have=20the=20default=20host=20entry.=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=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->restart;=0A+=0A+#=20Connecting=20without=20a=20hostname=20as=20= well=20as=20with=20a=20hostname=20which=20isn't=20in=20the=0A+#=20= pg_hosts=20configuration=20should=20fail.=0A+$node->connect_fails(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= sslsni=3D0",=0A+=09"pg_hosts.conf:=20connect=20to=20default=20with=20= sslmode=3Drequire",=0A+=09expected_stderr=20=3D>=20qr/handshake=20= failure/);=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dexample.com",=0A+=09"pg_hosts.conf:=20connect=20to=20default=20= with=20sslmode=3Drequire",=0A+=09expected_stderr=20=3D>=20= qr/unrecognized=20name/);=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dexample",=0A+=09"pg_hosts.conf:=20connect=20to=20'example'=20with=20= sslmode=3Drequire",=0A+=09expected_stderr=20=3D>=20qr/unrecognized=20= name/);=0A+=0A+#=20Reconfigure=20with=20broken=20configuration=20for=20= the=20key=20passphrase,=20the=20server=0A+#=20should=20not=20start=20up=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= wrongpassword"=20on'=0A+);=0A+$result=20=3D=20$node->restart(fail_ok=20= =3D>=201);=0A+is($result,=200,=0A+=09'pg_hosts.conf:=20restart=20fails=20= with=20password-protected=20key=20when=20using=20the=20wrong=20= passphrase=20command'=0A+);=0A+=0A+#=20Reconfigure=20again=20but=20with=20= the=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= 'pg_hosts.conf:=20restart=20succeeds=20with=20password-protected=20key=20= when=20using=20the=20correct=20passphrase=20command'=0A+);=0A+=0A+#=20= Make=20sure=20connecting=20works,=20and=20try=20to=20stress=20the=20= reload=20logic=20by=20issuing=0A+#=20subsequent=20reloads=0A= +$node->connect_ok(=0A+=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt= =20sslmode=3Drequire=20host=3Dlocalhost",=0A+=09"pg_hosts.conf:=20= connect=20with=20correct=20server=20CA=20cert=20file=20sslmode=3Drequire"=0A= +);=0A+$node->reload;=0A+$node->reload;=0A+$node->connect_ok(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dlocalhost",=0A+=09"pg_hosts.conf:=20connect=20with=20correct=20= server=20CA=20cert=20file=20after=20reloads");=0A+$node->reload;=0A= +$node->reload;=0A+$node->connect_ok(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dlocalhost",=0A+=09"pg_hosts.conf:=20connect=20with=20correct=20= server=20CA=20cert=20file=20after=20more=20reloads"=0A+);=0A+=0A+#=20= Test=20reloading=20a=20passphrase=20protected=20key=20without=20= reloading=20support=20in=20the=0A+#=20passphrase=20hook.=20Restarting=20= should=20not=20give=20any=20errors=20in=20the=20log,=20but=20the=0A+#=20= subsequent=20reload=20should=20fail=20with=20an=20error=20regarding=20= reloading.=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+my=20$node_loglocation=20=3D=20-s=20= $node->logfile;=0A+$result=20=3D=20$node->restart(fail_ok=20=3D>=201);=0A= +is($result,=201,=0A+=09'pg_hosts.conf:=20restart=20succeeds=20with=20= password-protected=20key=20when=20using=20the=20correct=20passphrase=20= command'=0A+);=0A+my=20$log=20=3D=0A+=20=20= PostgreSQL::Test::Utils::slurp_file($node->logfile,=20= $node_loglocation);=0A+unlike(=0A+=09$log,=0A+=09qr/cannot=20be=20= reloaded=20because=20it=20requires=20a=20passphrase/,=0A+=09'log=20= reload=20failure=20due=20to=20passphrase=20command=20reloading');=0A+=0A= +SKIP:=0A+{=0A+=09#=20Passphrase=20reloads=20must=20be=20enabled=20on=20= Windows=20to=20succeed=20even=20without=20a=0A+=09#=20restart=0A+=09skip=20= "Passphrase=20command=20reload=20required=20on=20Windows",=201=20if=20= ($windows_os);=0A+=0A+=09$node->connect_ok(=0A+=09=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dlocalhost",=0A+=09=09"pg_hosts.conf:=20connect=20with=20correct=20= server=20CA=20cert=20file=20sslmode=3Drequire"=0A+=09);=0A+=09#=20= Reloading=20should=20fail=20since=20the=20passphrase=20cannot=20be=20= reloaded,=20with=20an=0A+=09#=20error=20recorded=20in=20the=20log.=20=20= Since=20we=20keep=20existing=20contexts=20around=20it=0A+=09#=20should=20= still=20work.=0A+=09$node_loglocation=20=3D=20-s=20$node->logfile;=0A+=09= $node->reload;=0A+=09$node->connect_ok(=0A+=09=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dlocalhost",=0A+=09=09"pg_hosts.conf:=20connect=20with=20correct=20= server=20CA=20cert=20file=20sslmode=3Drequire"=0A+=09);=0A+=09$log=20=3D=0A= +=09=20=20PostgreSQL::Test::Utils::slurp_file($node->logfile,=20= $node_loglocation);=0A+=09like(=0A+=09=09$log,=0A+=09=09qr/cannot=20be=20= reloaded=20because=20it=20requires=20a=20passphrase/,=0A+=09=09'log=20= reload=20failure=20due=20to=20passphrase=20command=20reloading');=0A+}=0A= +=0A+#=20Configure=20with=20only=20non-SNI=20connections=20allowed=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf('pg_hosts.conf',=0A+=09"/no_sni/=20= server-cn-only.crt=20server-cn-only.key");=0A+$node->restart;=0A+=0A= +$node->connect_ok(=0A+=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt= =20sslmode=3Drequire=20sslsni=3D0",=0A+=09"pg_hosts.conf:=20only=20= non-SNI=20connections=20allowed");=0A+=0A+$node->connect_fails(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dexample.org",=0A+=09"pg_hosts.conf:=20only=20non-SNI=20= connections=20allowed,=20connecting=20with=20SNI",=0A+=09expected_stderr=20= =3D>=20qr/unrecognized=20name/);=0A+=0A+#=20Test=20client=20CAs=0A+=0A+#=20= pg_hosts=20configuration=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+#=20example.org=20has=20an=20unconfigured=20CA.=0A= +$node->append_conf('pg_hosts.conf',=0A+=09'example.org=20= server-cn-only.crt=20server-cn-only.key');=0A+#=20example.com=20uses=20= the=20client=20CA.=0A+$node->append_conf('pg_hosts.conf',=0A+=09= 'example.com=20server-cn-only.crt=20server-cn-only.key=20= root+client_ca.crt');=0A+#=20example.net=20uses=20the=20server=20CA=20= (which=20is=20wrong).=0A+$node->append_conf('pg_hosts.conf',=0A+=09= 'example.net=20server-cn-only.crt=20server-cn-only.key=20= root+server_ca.crt');=0A+$node->restart;=0A+=0A+$connstr=20=3D=0A+=20=20= "user=3Dssltestuser=20dbname=3Dcertdb=20hostaddr=3D$SERVERHOSTADDR=20= sslmode=3Drequire=20sslsni=3D1";=0A+=0A+#=20example.org=20is=20= unconfigured=20and=20should=20fail.=0A+$node->connect_fails(=0A+=09= "$connstr=20host=3Dexample.org=20sslcertmode=3Drequire=20= sslcert=3Dssl/client.crt"=0A+=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A+=09"host:=20'example.org',=20ca:=20= '':=20connect=20with=20sslcert,=20no=20client=20CA=20configured",=0A+=09= expected_stderr=20=3D>=0A+=09=20=20qr/client=20certificates=20can=20only=20= be=20checked=20if=20a=20root=20certificate=20store=20is=20available/=0A= +);=0A+=0A+#=20example.com=20is=20configured=20and=20should=20require=20= a=20valid=20client=20cert.=0A+$node->connect_fails(=0A+=09"$connstr=20= host=3Dexample.com=20sslcertmode=3Ddisable",=0A+=09"host:=20= 'example.com',=20ca:=20'root+client_ca.crt':=20connect=20fails=20if=20no=20= client=20certificate=20sent",=0A+=09expected_stderr=20=3D>=20= qr/connection=20requires=20a=20valid=20client=20certificate/);=0A+=0A= +$node->connect_ok(=0A+=09"$connstr=20host=3Dexample.com=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20"=0A+=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A+=09"host:=20'example.com',=20ca:=20= 'root+client_ca.crt':=20connect=20with=20sslcert,=20client=20certificate=20= sent"=0A+);=0A+=0A+#=20example.net=20is=20configured=20and=20should=20= require=20a=20client=20cert,=20but=20will=0A+#=20always=20fail=20= verification.=0A+$node->connect_fails(=0A+=09"$connstr=20= host=3Dexample.net=20sslcertmode=3Ddisable",=0A+=09"host:=20= 'example.net',=20ca:=20'root+server_ca.crt':=20connect=20fails=20if=20no=20= client=20certificate=20sent",=0A+=09expected_stderr=20=3D>=20= qr/connection=20requires=20a=20valid=20client=20certificate/);=0A+=0A= +$node->connect_fails(=0A+=09"$connstr=20host=3Dexample.net=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20"=0A+=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A+=09"host:=20'example.net',=20ca:=20= 'root+server_ca.crt':=20connect=20with=20sslcert,=20client=20certificate=20= sent",=0A+=09expected_stderr=20=3D>=20qr/unknown=20ca/);=0A+=0A= +done_testing();=0Adiff=20--git=20a/src/tools/pgindent/typedefs.list=20= b/src/tools/pgindent/typedefs.list=0Aindex=20141b9d6e077..ba2bad0c35a=20= 100644=0A---=20a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-1227,6=20+1227,8=20@@=20= HeapTupleHeader=0A=20HeapTupleHeaderData=0A=20HeapTupleTableSlot=0A=20= HistControl=0A+HostsFileLoadResult=0A+HostsLine=0A=20HotStandbyState=0A=20= I32=0A=20ICU_Convert_Func=0A--=20=0A2.39.3=20(Apple=20Git-146)=0A=0A= --Apple-Mail=_EBCF6E35-AEFD-4AE0-81FB-BCF50C94834B Content-Disposition: attachment; filename=v17-0001-ssl-Add-tests-for-client-CA.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v17-0001-ssl-Add-tests-for-client-CA.patch" Content-Transfer-Encoding: quoted-printable =46rom=203a1edcb7ed8ec151b5fc3a22d2a443f6e33c8560=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Fri,=206=20Mar=202026=2022:43:06=20= +0100=0ASubject:=20[PATCH=20v17=201/2]=20ssl:=20Add=20tests=20for=20= client=20CA=0A=0AThese=20tests=20were=20originally=20written=20to=20test=20= the=20SSL=20SNI=20patchset=0Abut=20they=20have=20merit=20on=20their=20= own=20since=20we=20lack=20coverage=20for=20these=0Ascenarios=20in=20the=20= non=20SNI=20case=20as=20well.=0A=0AAuthor:=20Jacob=20Champion=20= =0ACo-authored-by:=20Daniel=20= Gustafsson=20=0ADiscussion:=20= https://postgr.es/m/1C81CD0D-407E-44F9-833A-DD0331C202E5@yesql.se=0A---=0A= =20src/test/ssl/t/001_ssltests.pl=20=20=20=20=20=20=20=20|=2056=20= +++++++++++++++++++++++++++=0A=20src/test/ssl/t/SSL/Backend/OpenSSL.pm=20= |=2016=20++++++--=0A=202=20files=20changed,=2069=20insertions(+),=203=20= deletions(-)=0A=0Adiff=20--git=20a/src/test/ssl/t/001_ssltests.pl=20= b/src/test/ssl/t/001_ssltests.pl=0Aindex=202b9b3dfd663..963bfea8ed5=20= 100644=0A---=20a/src/test/ssl/t/001_ssltests.pl=0A+++=20= b/src/test/ssl/t/001_ssltests.pl=0A@@=20-1004,4=20+1004,60=20@@=20= $node->connect_fails(=0A=20=09=09qr{Failed=20certificate=20data=20= \(unverified\):=20subject=20= "/CN=3D\\xce\\x9f\\xce\\xb4\\xcf\\x85\\xcf\\x83\\xcf\\x83\\xce\\xad\\xce\\= xb1\\xcf\\x82",=20serial=20number=20\d+,=20issuer=20"/CN=3DTest=20CA=20= for=20PostgreSQL=20SSL=20regression=20test=20client=20certs"},=0A=20=09= ]);=0A=20=0A+SKIP:=0A+{=0A+=09skip=20"sslmode=20require=20not=20= supported=20in=20this=20build",=204=0A+=09=20=20unless=20= ($supports_sslcertmode_require);=0A+=0A+=09#=20Test=20client=20CAs=0A+=09= my=20$connstr=20=3D=0A+=09=20=20"user=3Dssltestuser=20dbname=3Dcertdb=20= hostaddr=3D$SERVERHOSTADDR=20sslmode=3Drequire=20sslsni=3D1";=0A+=0A+=09= switch_server_cert($node,=20certfile=20=3D>=20'server-cn-only',=20cafile=20= =3D>=20'');=0A+=09#=20example.org=20is=20unconfigured=20and=20should=20= fail.=0A+=09$node->connect_fails(=0A+=09=09"$connstr=20host=3Dexample.org=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt"=0A+=09=09=20=20.=20= sslkey('client.key'),=0A+=09=09"host:=20'example.org',=20ca:=20'':=20= connect=20with=20sslcert,=20no=20client=20CA=20configured",=0A+=09=09= expected_stderr=20=3D>=0A+=09=09=20=20qr/client=20certificates=20can=20= only=20be=20checked=20if=20a=20root=20certificate=20store=20is=20= available/=0A+=09);=0A+=0A+=09#=20example.com=20uses=20the=20client=20= CA.=0A+=09switch_server_cert(=0A+=09=09$node,=0A+=09=09certfile=20=3D>=20= 'server-cn-only',=0A+=09=09cafile=20=3D>=20'root+client_ca');=0A+=09#=20= example.com=20is=20configured=20and=20should=20require=20a=20valid=20= client=20cert.=0A+=09$node->connect_fails(=0A+=09=09"$connstr=20= host=3Dexample.com=20sslcertmode=3Ddisable",=0A+=09=09"host:=20= 'example.com',=20ca:=20'root+client_ca.crt':=20connect=20fails=20if=20no=20= client=20certificate=20sent",=0A+=09=09expected_stderr=20=3D>=20= qr/connection=20requires=20a=20valid=20client=20certificate/=0A+=09);=0A= +=09$node->connect_ok(=0A+=09=09"$connstr=20host=3Dexample.com=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20"=0A+=09=09=20=20.=20= sslkey('client.key'),=0A+=09=09"host:=20'example.com',=20ca:=20= 'root+client_ca.crt':=20connect=20with=20sslcert,=20client=20certificate=20= sent"=0A+=09);=0A+=0A+=09#=20example.net=20uses=20the=20server=20CA=20= (which=20is=20wrong).=0A+=09switch_server_cert(=0A+=09=09$node,=0A+=09=09= certfile=20=3D>=20'server-cn-only',=0A+=09=09cafile=20=3D>=20= 'root+server_ca');=0A+=09#=20example.net=20is=20configured=20and=20= should=20require=20a=20client=20cert,=20but=20will=0A+=09#=20always=20= fail=20verification.=0A+=09$node->connect_fails(=0A+=09=09"$connstr=20= host=3Dexample.net=20sslcertmode=3Ddisable",=0A+=09=09"host:=20= 'example.net',=20ca:=20'root+server_ca.crt':=20connect=20fails=20if=20no=20= client=20certificate=20sent",=0A+=09=09expected_stderr=20=3D>=20= qr/connection=20requires=20a=20valid=20client=20certificate/=0A+=09);=0A= +=0A+=09$node->connect_fails(=0A+=09=09"$connstr=20host=3Dexample.net=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20"=0A+=09=09=20=20.=20= sslkey('client.key'),=0A+=09=09"host:=20'example.net',=20ca:=20= 'root+server_ca.crt':=20connect=20with=20sslcert,=20client=20certificate=20= sent",=0A+=09=09expected_stderr=20=3D>=20qr/unknown=20ca/);=0A+}=0A+=0A=20= done_testing();=0Adiff=20--git=20a/src/test/ssl/t/SSL/Backend/OpenSSL.pm=20= b/src/test/ssl/t/SSL/Backend/OpenSSL.pm=0Aindex=20= 7ea05572a8d..6060771c1a8=20100644=0A---=20= a/src/test/ssl/t/SSL/Backend/OpenSSL.pm=0A+++=20= b/src/test/ssl/t/SSL/Backend/OpenSSL.pm=0A@@=20-72,6=20+72,7=20@@=20sub=20= init=0A=20=09chmod(0600,=20glob=20"$pgdata/server-*.key")=0A=20=09=20=20= or=20die=20"failed=20to=20change=20permissions=20on=20server=20keys:=20= $!";=0A=20=09_copy_files("ssl/root+client_ca.crt",=20$pgdata);=0A+=09= _copy_files("ssl/root+server_ca.crt",=20$pgdata);=0A=20=09= _copy_files("ssl/root_ca.crt",=20$pgdata);=0A=20=09= _copy_files("ssl/root+client.crl",=20$pgdata);=0A=20=09= mkdir("$pgdata/root+client-crldir")=0A@@=20-146,7=20+147,8=20@@=20= following=20parameters=20are=20supported:=0A=20=3Ditem=20cafile=20=3D>=20= B=0A=20=0A=20The=20CA=20certificate=20file=20to=20use=20for=20the=20= C=20GUC.=20If=20omitted=20it=20will=0A-default=20to=20= 'root+client_ca.crt'.=0A+default=20to=20'root+client_ca.crt'.=20If=20= empty,=20no=20C=20configuration=0A+parameter=20will=20be=20= set.=0A=20=0A=20=3Ditem=20certfile=20=3D>=20B=0A=20=0A@@=20= -181,10=20+183,18=20@@=20sub=20set_server_cert=0A=20=09=20=20unless=20= defined=20$params->{keyfile};=0A=20=0A=20=09my=20$sslconf=20=3D=0A-=09=09= "ssl_ca_file=3D'$params->{cafile}.crt'\n"=0A-=09=20=20.=20= "ssl_cert_file=3D'$params->{certfile}.crt'\n"=0A+=09=09= "ssl_cert_file=3D'$params->{certfile}.crt'\n"=0A=20=09=20=20.=20= "ssl_key_file=3D'$params->{keyfile}.key'\n"=0A=20=09=20=20.=20= "ssl_crl_file=3D'$params->{crlfile}'\n";=0A+=09if=20($params->{cafile}=20= ne=20"")=0A+=09{=0A+=09=09$sslconf=20.=3D=20= "ssl_ca_file=3D'$params->{cafile}.crt'\n";=0A+=09}=0A+=09else=0A+=09{=0A= +=09=09$sslconf=20.=3D=20"ssl_ca_file=3D''\n";=0A+=09}=0A+=0A=20=09= $sslconf=20.=3D=20"ssl_crl_dir=3D'$params->{crldir}'\n"=0A=20=09=20=20if=20= defined=20$params->{crldir};=0A=20=0A--=20=0A2.39.3=20(Apple=20Git-146)=0A= =0A= --Apple-Mail=_EBCF6E35-AEFD-4AE0-81FB-BCF50C94834B--