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 1vfb9p-003EPx-1C for pgsql-hackers@arkaria.postgresql.org; Tue, 13 Jan 2026 09:57:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vfb9n-00428s-1j for pgsql-hackers@arkaria.postgresql.org; Tue, 13 Jan 2026 09:57:47 +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 1vfb9m-00428k-1r for pgsql-hackers@lists.postgresql.org; Tue, 13 Jan 2026 09:57:47 +0000 Received: from smtp.outgoing.loopia.se ([93.188.3.37]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vfb9h-000B18-25 for pgsql-hackers@lists.postgresql.org; Tue, 13 Jan 2026 09:57:45 +0000 Received: from s807.loopia.se (localhost [127.0.0.1]) by s807.loopia.se (Postfix) with ESMTP id EB4304D63E1 for ; Tue, 13 Jan 2026 10:57:38 +0100 (CET) Received: from s981.loopia.se (unknown [172.22.191.5]) by s807.loopia.se (Postfix) with ESMTP id D5B1B4D52CC; Tue, 13 Jan 2026 10:57:38 +0100 (CET) Received: from s473.loopia.se (unknown [172.22.191.6]) by s981.loopia.se (Postfix) with ESMTP id CFA2D22B17C3; Tue, 13 Jan 2026 10:57:38 +0100 (CET) X-Virus-Scanned: amavisd-new at amavis.loopia.se X-Spam-Flag: NO X-Spam-Score: -1.2 X-Spam-Level: X-Spam-Status: No, score=-1.2 tagged_above=-999 required=6.2 tests=[ALL_TRUSTED=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1] autolearn=disabled Authentication-Results: s473.loopia.se (amavisd-new); dkim=pass (2048-bit key) header.d=yesql.se Received: from s979.loopia.se ([172.22.191.5]) by s473.loopia.se (s473.loopia.se [172.22.190.13]) (amavisd-new, port 10024) with UTF8LMTP id slt5GtGqTEvZ; Tue, 13 Jan 2026 10:57:38 +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 2374110BC497; Tue, 13 Jan 2026 10:57:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yesql.se; s=loopiadkim1707475645; t=1768298258; bh=5W6X6X5Zt7FmH1GI1jtshDunDiaDub9BCWi+0uoxHSA=; h=From:Subject:Date:In-Reply-To:Cc:To:References; b=MfvPHvWhc5dA/+xZ+n7lzfCvoJbgygW+Vu7s5sJ6an58+SOfokikbd229m94H9LAZ GujP1CsPq4mFcduzyC78v3HbicXZy+gtPtoVuC8cRJailArENjKMe9F3Yx9cPBZFR+ uLxp4C3lyABTMLrIoWzQUvIpNfwBwgvRn/bfT6crVB9KL1kW64g84b/gTW8Bf9VDvQ DNb3PTbRreciELbz3L0LUx8Gf+fGK3+Hw+SPB861OuAwqp+2thUkUvAObZFvxNcAxB rKRXz2xbR3fZkXAYH3ml8HdVTAZZihK6iINOchQvF48lwEZj97geXoMfe42armuMqd LkeZBCv0e0PzA== From: Daniel Gustafsson Message-Id: Content-Type: multipart/mixed; boundary="Apple-Mail=_BA87BDEE-21BC-4337-BB45-129C4A4012C4" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51.11.2\)) Subject: Re: Serverside SNI support in libpq Date: Tue, 13 Jan 2026 10:57:27 +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=_BA87BDEE-21BC-4337-BB45-129C4A4012C4 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii >> The attached version allows ssl_ca to be omitted from the pg_host = config to >> match the ssl_ca GUC. >=20 > Aha! I think ssl_ca should be moved into the "Optional fields" section > of `struct HostsLine` now. Done, and removed the now unused default_host member from the struct as = well which I had missed in the previous version. > We should probably check tokens->length to make sure that the user > hasn't passed more than one token for each field, similar to how > parse_hba_line() does it. Done. >>> Will anyone be mad at us for camping on the "no_sni" identifier? I >>> know technically underscore isn't allowed in DNS hostnames, buuuut = [1, >>> 2] >>=20 >> Maybe, but I think that regardless of what we do someone will be mad. = The >> other option would be to use another single character like '?' or = something. >> Not sure that will improve readability though. >=20 > Hm, I agree that's not readable. Especially since other famous server > implementations use ? to match a single character in server alias > names. >=20 > Maybe we could enclose no_sni with something that's emphatically not > DNS. Braces, brackets, etc.? If we had control over the lower level > tokenizer, we could tell people to double-quote it to disambiguate, > but I don't think we have access to that information at our level. I've changed to /no_sni/ in the attached patch which should make it = safer, but it can easily be changed to braces or brackets or something else = entirely. >>> Should we support multiple hostname tokens in a single line, though, >>> and just copy the settings that follow across all of them? >>=20 >> I've been hesitant to add too much complexity, but perhaps just = allowing a >> comma separated list is a good middle ground to avoid going full = regex? >=20 > I think it could be a pretty good bump in usability. Wildcards seem > ideal but the cost is much higher. Hopefully the cost of > comma-separated hosts is just an extra inner loop in the parser, plus > the extra tests? I've added support for lists of hostnames along with tests and docs for = the same. The limitation is that one cannot specify '*' or '/no_sni/' in a = list, it must be just hostnames. I haven't added support for @hostnames.txt = yet to keep scope under control, but it can be added as well (in the future if = this patch is committed). > I'm trying to put on my "what could we possibly regret" hat for these > next ones. They may be uselessly speculative: I really appreciate thinking about this! > - If the goal is to eventually support wildcards, will the use of a > bare catch-all asterisk conflict with your plans (if any)? Possibly, I guess it depends on how we define a wildcard scheme. One = solution could perhaps be to use an enclosed name like the non-SNI case, like = /default/ or something similar. > - What kind of normalization should we do? Currently, `example.com` > will not match `example.COM` and it seems like that might be a problem > for somebody. The attached use case insensitive comparison. RFC 952 makes it clear = that hostnames are case insensitive, and RFC 921/1035 does the same for DNS. > - Do we need to consider IDNs and A-labels and U-labels? (Do we > support the latter today, at all?) There is nothing in the current patch which prevents supporting it in a = future update is there? > A nice-to-have v2ish feature might be to warn if the host configured > for a certificate cannot in fact match that certificate according to > OpenSSL. That would be quite nifty indeed. I think the attached is pretty clear improvement over the previous = version so thanks for the review suggestions. That being said, the test which was reported to still fail upstream is failing here as well (it does the = right thing with the connection, but terminates the handshake in a different = place). In an attempt to fix that I moved to using the ClientHello callback = which OpenSSL document to be the right one (yet they use the servername = callback themselves), but it renders the same result. I hope that your eagle = eyes (or someone elses) can figure out either what is wrong, or if this is a = different form of right. The same failing test is added to 0001 to run it in a = strictly non-SNI config as well. The attached also simplifies the tests you provided since there is no = longer any need to run the tests for different default values, as we no longer = have that mixed configfile handling it was intended to test. The actual = connection tests remain though. -- Daniel Gustafsson --Apple-Mail=_BA87BDEE-21BC-4337-BB45-129C4A4012C4 Content-Disposition: attachment; filename=v14-0001-Serverside-SNI-support-for-libpq.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v14-0001-Serverside-SNI-support-for-libpq.patch" Content-Transfer-Encoding: quoted-printable =46rom=20fb0353a10ea352619d585b09776860c19936978c=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Thu,=2011=20Dec=202025=2018:38:00=20= +0100=0ASubject:=20[PATCH=20v14=201/2]=20Serverside=20SNI=20support=20= for=20libpq=0A=0ASupport=20for=20SNI=20was=20added=20to=20clientside=20= libpq=20in=205c55dc8b4733=20with=20the=0Asslsni=20parameter,=20but=20= there=20was=20no=20support=20for=20utilizing=20it=20serverside.=0AThis=20= adds=20support=20for=20serverside=20SNI=20such=20that=20certificate/key=20= handling=0Ais=20available=20per=20host.=20=20A=20new=20config=20file,=20= $datadir/pg_hosts.conf,=20is=0Aused=20for=20configuring=20which=20= certificate=20and=20key=20should=20be=20used=20for=20which=0Ahostname.=20= =20If=20pg_hosts.conf=20is=20non-empty=20it=20will=20take=20precedence=20= over=0Athe=20regular=20SSL=20GUCs,=20if=20it=20is=20empty=20or=20missing=20= the=20regular=20GUCs=20will=0Abe=20used=20just=20as=20before=20this=20= commit=20with=20no=20hostname=20specific=20handling.=0A=0AHost=20= configuration=20can=20either=20be=20for=20a=20literal=20hostname=20to=20= match,=20non-=0ASNI=20connections=20using=20the=20no_sni=20keyword=20or=20= a=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=0AAuthor:=20= Daniel=20Gustafsson=20=0AReviewed-by:=20Jacob=20= Champion=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= =20doc/src/sgml/runtime.sgml=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20118=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=20= 2=20+=0A=20src/backend/libpq/be-secure-common.c=20=20=20=20=20=20=20=20=20= =20|=20198=20++++++++-=0A=20src/backend/libpq/be-secure-openssl.c=20=20=20= =20=20=20=20=20=20|=20415=20++++++++++++++++--=0A=20= src/backend/libpq/be-secure.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20|=20=20=206=20+-=0A=20src/backend/libpq/meson.build=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20|=20=20=201=20+=0A=20= src/backend/libpq/pg_hosts.conf.sample=20=20=20=20=20=20=20=20|=20=20=20= 4=20+=0A=20src/backend/utils/misc/guc.c=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=2031=20++=0A=20= src/backend/utils/misc/guc_parameters.dat=20=20=20=20=20|=20=20=207=20+=0A= =20src/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= 2=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=2027=20++=0A=20src/include/libpq/libpq-be.h=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=203=20+-=0A=20= src/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=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= .../ssl_passphrase_func.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20|=20=20=204=20+-=0A=20= src/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=20src/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=20= src/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|=20289=20++++++++++++=0A=20= src/test/ssl/t/SSL/Backend/OpenSSL.pm=20=20=20=20=20=20=20=20=20|=20=20= 16=20+-=0A=20src/tools/pgindent/typedefs.list=20=20=20=20=20=20=20=20=20=20= =20=20=20=20|=20=20=203=20+=0A=2023=20files=20changed,=201120=20= insertions(+),=2068=20deletions(-)=0A=20create=20mode=20100644=20= src/backend/libpq/pg_hosts.conf.sample=0A=20create=20mode=20100644=20= src/test/ssl/t/004_sni.pl=0A=0Adiff=20--git=20= a/doc/src/sgml/runtime.sgml=20b/doc/src/sgml/runtime.sgml=0Aindex=20= 0c60bafac63..ca0a114da76=20100644=0A---=20a/doc/src/sgml/runtime.sgml=0A= +++=20b/doc/src/sgml/runtime.sgml=0A@@=20-2445,6=20+2445,12=20@@=20= pg_dumpall=20-p=205432=20|=20psql=20-d=20postgres=20-p=205433=0A=20=20=20= =20=20=20=20client=20certificate=20must=20not=20be=20on=20this=20= list=0A=20=20=20=20=20=20=0A=20=0A+=20=20=20=20=20=0A= +=20=20=20=20=20=20= $PGDATA/pg_hosts.conf=0A+=20=20=20=20= =20=20SNI=20configuration=0A+=20=20=20=20=20=20= defines=20which=20certificates=20to=20use=20for=20which=20server=20= hostname=0A+=20=20=20=20=20=0A+=0A=20=20=20=20=20=0A= =20=20=20=20=0A=20=20=20=0A@@=20-2572,6=20+2578,118=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=20= file=0A= +include_dir=20= directory=0A+=0A+=20=20=20=20= Comments,=20whitespace=20and=20line=20continuations=20are=20handled=20in=20= the=20same=20way=20as=0A+=20=20=20=20in=20= pg_hba.conf.=20=20= hostname=0A+=20=20=20=20is=20matched=20= against=20the=20hostname=20TLS=20extension=20in=20the=20SSL=20handshake.=0A= +=20=20=20=20SSL_certificate,=0A+=20=20=20=20= SSL_key,=0A+=20=20=20=20= SSL_CA_certificate,=0A+=20=20=20=20= SSL_passphrase_cmd,=20and=0A+=20=20=20=20= SSL_passphrase_cmd_reload=0A+=20=20=20=20are=20= treated=20like=0A+=20=20=20=20,=0A= +=20=20=20=20,=0A+=20=20=20=20= ,=0A+=20=20=20=20,=20and=0A+=20=20=20=20=20respectively.=0A= +=20=20=20=20All=20fields=20except=20= SSL_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,=20no_sni=20or=20*.=0A+=20= =20=20=20=20contain=20details=20on=20= how=20these=20values=20are=0A+=20=20=20=20used.=0A+=20=20=20=20=0A+=20=20=20=20=20Hostname=20setting=20= values=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=20= Host=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= =20Default=20host,=20matches=20all=20connections=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=20no_sni=0A+=20=20=20=20=20=20=20= =20Not=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= connection=20with=20no=20sslsni=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=20= hostname=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=20for=20connections=20= to=20the=20host=20specified=20in=20the=0A+=20=20=20=20=20=20=20=20=20= connection.=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=20pg_hosts.conf=20is=20empty,=20or=20missing,=20= then=20the=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/src/backend/Makefile=20= b/src/backend/Makefile=0Aindex=20e03c92e70e4..e7ac9c8dcb0=20100644=0A---=20= a/src/backend/Makefile=0A+++=20b/src/backend/Makefile=0A@@=20-209,6=20= +209,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-268,6=20+269,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/libpq/be-secure-common.c=20= b/src/backend/libpq/be-secure-common.c=0Aindex=20= c074556dbfc..78430aad825=20100644=0A---=20= a/src/backend/libpq/be-secure-common.c=0A+++=20= b/src/backend/libpq/be-secure-common.c=0A@@=20-24,32=20+24,40=20@@=0A=20=0A= =20#include=20"common/percentrepl.h"=0A=20#include=20"common/string.h"=0A= +#include=20"libpq/hba.h"=0A=20#include=20"libpq/libpq.h"=0A=20#include=20= "storage/fd.h"=0A+#include=20"utils/guc.h"=0A+#include=20= "utils/memutils.h"=0A+=0A+static=20HostsLine=20= *parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel);=0A=20=0A= =20/*=0A=20=20*=20Run=20ssl_passphrase_command=0A=20=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*prompt,=20bool=20= is_server_start,=20char=20*buf,=20int=20size,=20void=20*userdata)=0A=20{=0A= =20=09int=09=09=09loglevel=20=3D=20is_server_start=20?=20ERROR=20:=20= LOG;=0A=20=09char=09=20=20=20*command;=0A=20=09FILE=09=20=20=20*fh;=0A=20= =09int=09=09=09pclose_rc;=0A=20=09size_t=09=09len=20=3D=200;=0A+=09const=20= char=20*cmd=20=3D=20(const=20char=20*)=20userdata;=0A=20=0A=20=09= Assert(prompt);=0A=20=09Assert(size=20>=200);=0A=20=09buf[0]=20=3D=20= '\0';=0A=20=0A-=09command=20=3D=20= replace_percent_placeholders(ssl_passphrase_command,=20= "ssl_passphrase_command",=20"p",=20prompt);=0A+=09command=20=3D=20= replace_percent_placeholders(cmd,=20"ssl_passphrase_command",=20"p",=20= prompt);=0A=20=0A=20=09fh=20=3D=20OpenPipeStream(command,=20"r");=0A=20=09= if=20(fh=20=3D=3D=20NULL)=0A@@=20-175,3=20+183,187=20@@=20= check_ssl_key_file_permissions(const=20char=20*ssl_key_file,=20bool=20= isServerStart)=0A=20=0A=20=09return=20true;=0A=20}=0A+=0A+/*=0A+=20*=20= parse_hosts_line=0A+=20*=0A+=20*=20Parses=20a=20loaded=20line=20from=20= the=20pg_hosts.conf=20configuration=20and=20pulls=20out=20the=0A+=20*=20= hostname,=20certificate,=20key=20and=20CA=20parts=20in=20order=20to=20= build=20an=20SNI=20config=20in=0A+=20*=20the=20TLS=20backend.=20= Validation=20of=20the=20parsed=20values=20is=20left=20for=20the=20TLS=20= backend=0A+=20*=20to=20implement.=0A+=20*/=0A+static=20HostsLine=20*=0A= +parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel)=0A+{=0A= +=09HostsLine=20=20*parsedline;=0A+=09List=09=20=20=20*tokens;=0A+=09= ListCell=20=20=20*field;=0A+=09AuthToken=20=20*token;=0A+=0A+=09= parsedline=20=3D=20palloc0(sizeof(HostsLine));=0A+=09= parsedline->sourcefile=20=3D=20pstrdup(tok_line->file_name);=0A+=09= parsedline->linenumber=20=3D=20tok_line->line_num;=0A+=09= parsedline->rawline=20=3D=20pstrdup(tok_line->raw_line);=0A+=0A+=09/*=20= Initialize=20optional=20fields=20*/=0A+=09parsedline->ssl_passphrase_cmd=20= =3D=20NULL;=0A+=09parsedline->ssl_passphrase_reload=20=3D=20false;=0A+=0A= +=09/*=20Hostname=20*/=0A+=09field=20=3D=20list_head(tok_line->fields);=0A= +=09tokens=20=3D=20lfirst(field);=0A+=09token=20=3D=20linitial(tokens);=0A= +=09parsedline->hostname=20=3D=20pstrdup(token->string);=0A+=0A+=09/*=20= SSL=20Certificate=20(Required)=20*/=0A+=09field=20=3D=20= lnext(tok_line->fields,=20field);=0A+=09if=20(!field)=0A+=09{=0A+=09=09= ereport(elevel,=0A+=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09errmsg("missing=20entry=20at=20end=20of=20line"),=0A+=09=09=09=09= errcontext("line=20%d=20of=20configuration=20file=20\"%s\"",=0A+=09=09=09= =09=09=09=20=20=20tok_line->line_num,=20tok_line->file_name));=0A+=09=09= return=20NULL;=0A+=09}=0A+=09tokens=20=3D=20lfirst(field);=0A+=09token=20= =3D=20linitial(tokens);=0A+=09parsedline->ssl_cert=20=3D=20= pstrdup(token->string);=0A+=0A+=09/*=20SSL=20key=20(Required)=20*/=0A+=09= field=20=3D=20lnext(tok_line->fields,=20field);=0A+=09if=20(!field)=0A+=09= {=0A+=09=09ereport(elevel,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("missing=20= entry=20at=20end=20of=20line"),=0A+=09=09=09=09errcontext("line=20%d=20= of=20configuration=20file=20\"%s\"",=0A+=09=09=09=09=09=09=20=20=20= tok_line->line_num,=20tok_line->file_name));=0A+=09=09return=20NULL;=0A+=09= }=0A+=09tokens=20=3D=20lfirst(field);=0A+=09token=20=3D=20= linitial(tokens);=0A+=09parsedline->ssl_key=20=3D=20= pstrdup(token->string);=0A+=0A+=09/*=20SSL=20CA=20(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+=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=09if=20(token->string[0]=20=3D=3D=20'1'=0A= +=09=09=09=09||=20pg_strcasecmp(token->string,=20"true")=20=3D=3D=200=0A= +=09=09=09=09||=20pg_strcasecmp(token->string,=20"on")=20=3D=3D=200=0A+=09= =09=09=09||=20pg_strcasecmp(token->string,=20"yes")=20=3D=3D=200)=0A+=09=09= =09=09parsedline->ssl_passphrase_reload=20=3D=20true;=0A+=09=09=09else=20= if=20(token->string[0]=20=3D=3D=20'0'=0A+=09=09=09=09=09=20||=20= pg_strcasecmp(token->string,=20"false")=20=3D=3D=200=0A+=09=09=09=09=09=20= ||=20pg_strcasecmp(token->string,=20"off")=20=3D=3D=200=0A+=09=09=09=09=09= =20||=20pg_strcasecmp(token->string,=20"no")=20=3D=3D=200)=0A+=09=09=09=09= parsedline->ssl_passphrase_reload=20=3D=20false;=0A+=09=09=09else=0A+=09=09= =09{=0A+=09=09=09=09ereport(elevel,=0A+=09=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09= errmsg("incorrect=20syntax=20for=20boolean=20value=20= SSL_passphrase_cmd_reload"),=0A+=09=09=09=09=09=09errcontext("line=20%d=20= of=20configuration=20file=20\"%s\"",=0A+=09=09=09=09=09=09=09=09=20=20=20= tok_line->line_num,=20tok_line->file_name));=0A+=09=09=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=20HostLine=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_lines=20parameter.=20If=20= loading=20the=0A+=20*=20file=20was=20successful,=20true=20is=20returned,=20= else=20false.=20=20This=20function=20is=0A+=20*=20intended=20to=20be=20= executed=20within=20a=20temporary=20memory=20context=20which=20can=20be=0A= +=20*=20discarded=20to=20free=20memory=20allocated=20during=20the=20= processing=20of=20the=20file.=0A+=20*/=0A+int=0A+load_hosts(List=20= **hosts,=20char=20**err_msg)=0A+{=0A+=09FILE=09=20=20=20*file;=0A+=09= ListCell=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=20= we=20cannot=20return=20results=20then=20error=20out=20immediately.=20= This=20implies=0A+=09=20*=20API=20misuse=20or=20a=20similar=20kind=20of=20= programmer=20error.=0A+=09=20*/=0A+=09if=20(!hosts)=0A+=09=09return=20= HOSTSFILE_LOAD_FAILED;=0A+=09*hosts=20=3D=20NIL;=0A+=0A+=09/*=0A+=09=20*=20= This=20is=20not=20an=20auth=20file=20per=20se,=20but=20it=20is=20using=20= the=20same=20file=20format=0A+=09=20*=20as=20the=20pg_hba=20and=20= pg_ident=20files=20and=20thus=20the=20same=20code=20infrastructure.=0A+=09= =20*=20A=20future=20TODO=20might=20be=20to=20rename=20the=20supporting=20= code=20with=20a=20more=0A+=09=20*=20generic=20name?=0A+=09=20*/=0A+=09= file=20=3D=20open_auth_file(HostsFileName,=20LOG,=200,=20err_msg);=0A+=09= if=20(file=20=3D=3D=20NULL)=0A+=09{=0A+=09=09if=20(errno=20=3D=3D=20= ENOENT)=0A+=09=09=09return=20HOSTSFILE_MISSING;=0A+=0A+=09=09return=20= HOSTSFILE_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= 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= free_auth_file(file,=200);=0A+=09*hosts=20=3D=20parsed_lines;=0A+=0A+=09= if=20(!ok)=0A+=09=09return=20HOSTSFILE_LOAD_FAILED;=0A+=0A+=09if=20= (parsed_lines=20=3D=3D=20NIL)=0A+=09=09return=20HOSTSFILE_EMPTY;=0A+=0A+=09= return=20HOSTSFILE_LOAD_OK;=0A+}=0Adiff=20--git=20= a/src/backend/libpq/be-secure-openssl.c=20= b/src/backend/libpq/be-secure-openssl.c=0Aindex=20= 4da6ac22ff9..a540cc93163=20100644=0A---=20= a/src/backend/libpq/be-secure-openssl.c=0A+++=20= b/src/backend/libpq/be-secure-openssl.c=0A@@=20-51,9=20+51,15=20@@=0A=20= #endif=0A=20#include=20=0A=20=0A+typedef=20struct=20= HostContext=0A+{=0A+=09const=20char=20*hostname;=0A+=09SSL_CTX=20=20=20=20= *context;=0A+=09bool=09=09ssl_loaded_verify_locations;=0A+}=20= HostContext;=0A=20=0A=20/*=20default=20init=20hook=20can=20be=20= overridden=20by=20a=20shared=20library=20*/=0A-static=20void=20= default_openssl_tls_init(SSL_CTX=20*context,=20bool=20isServerStart);=0A= +static=20void=20default_openssl_tls_init(SSL_CTX=20*context,=20bool=20= isServerStart,=20HostsLine=20*hosts);=0A=20openssl_tls_init_hook_typ=20= openssl_tls_init_hook=20=3D=20default_openssl_tls_init;=0A=20=0A=20= static=20int=09port_bio_read(BIO=20*h,=20char=20*buf,=20int=20size);=0A= @@=20-73,6=20+79,7=20@@=20static=20int=09alpn_cb(SSL=20*ssl,=0A=20=09=09=09= =09=09const=20unsigned=20char=20*in,=0A=20=09=09=09=09=09unsigned=20int=20= inlen,=0A=20=09=09=09=09=09void=20*userdata);=0A+static=20int=09= sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg);=0A=20static=20= bool=20initialize_dh(SSL_CTX=20*context,=20bool=20isServerStart);=0A=20= static=20bool=20initialize_ecdh(SSL_CTX=20*context,=20bool=20= isServerStart);=0A=20static=20const=20char=20*SSLerrmessageExt(unsigned=20= long=20ecode,=20const=20char=20*replacement);=0A@@=20-80,12=20+87,26=20= @@=20static=20const=20char=20*SSLerrmessage(unsigned=20long=20ecode);=0A=20= =0A=20static=20char=20*X509_NAME_to_cstring(X509_NAME=20*name);=0A=20=0A= +/*=20List=20of=20SSL=20contexts=20for=20hostname=20defined=20= connections=20*/=0A+static=20List=20*sni_contexts=20=3D=20NIL;=0A+=0A+/*=20= The=20default=20SSL=20context=20to=20use=20as=20fallback=20in=20case=20= no=20hostname=20matches=20*/=0A+static=20HostContext=20*default_context=20= =3D=20NULL;=0A+=0A+/*=20The=20SSL=20context=20to=20use=20for=20= connections=20without=20SNI=20*/=0A+static=20HostContext=20= *no_sni_context=20=3D=20NULL;=0A+=0A+/*=20The=20currently=20active=20= context=20*/=0A=20static=20SSL_CTX=20*SSL_context=20=3D=20NULL;=0A= +static=20HostContext=20*Host_context=20=3D=20NULL;=0A+=0A=20static=20= bool=20dummy_ssl_passwd_cb_called=20=3D=20false;=0A=20static=20bool=20= ssl_is_server_start;=0A=20=0A=20static=20int=09= ssl_protocol_version_to_openssl(int=20v);=0A=20static=20const=20char=20= *ssl_protocol_version_to_string(int=20v);=0A+static=20SSL_CTX=20= *ssl_init_context(bool=20isServerStart,=20HostsLine=20*host);=0A+static=20= void=20free_contexts(void);=0A=20=0A=20struct=20CallbackErr=0A=20{=0A@@=20= -102,11=20+123,181=20@@=20struct=20CallbackErr=0A=20=0A=20int=0A=20= be_tls_init(bool=20isServerStart)=0A+{=0A+=09List=09=20=20=20*pg_hosts=20= =3D=20NIL;=0A+=09ListCell=20=20=20*line;=0A+=09MemoryContext=20oldcxt;=0A= +=09MemoryContext=20host_memcxt;=0A+=09char=09=20=20=20*err_msg;=0A+=09= int=09=09=09res;=0A+=0A+=09/*=0A+=09=20*=20If=20there=20are=20contexts=20= loaded=20when=20we=20init=20they=20must=20be=20released.=20This=0A+=09=20= *=20should=20only=20be=20possible=20during=20configuration=20reloads=20= and=20not=20when=20the=0A+=09=20*=20server=20is=20starting=20up.=0A+=09=20= */=0A+=09if=20(sni_contexts=20!=3D=20NIL=20||=20default_context=20||=20= no_sni_context)=0A+=09{=0A+=09=09Assert(!isServerStart);=0A+=09=09= free_contexts();=0A+=09=09Host_context=20=3D=20NULL;=0A+=09=09= SSL_context=20=3D=20NULL;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Attempt=20to=20= load,=20and=20parse,=20TLS=20configuration=20from=20the=20pg_hosts.conf=0A= +=09=20*=20file=20with=20the=20set=20of=20hosts=20returned=20as=20a=20= list.=20=20If=20there=20are=20hosts=0A+=09=20*=20configured=20there=20= they=20take=20precedence=20over=20the=20postgresql.conf=20config.=0A+=09=20= *=20Make=20sure=20to=20allocate=20the=20parsed=20rows=20in=20a=20= temporary=20memory=20context=20so=0A+=09=20*=20that=20we=20can=20avoid=20= memory=20leaks=20from=20the=20parsing=20process.=0A+=09=20*/=0A+=09= host_memcxt=20=3D=20AllocSetContextCreate(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=09ALLOCSET_SMALL_SIZES);=0A+=09oldcxt=20=3D=20= MemoryContextSwitchTo(host_memcxt);=0A+=09res=20=3D=20= load_hosts(&pg_hosts,=20&err_msg);=0A+=09MemoryContextSwitchTo(oldcxt);=0A= +=0A+=09/*=0A+=09=20*=20pg_hosts.conf=20is=20not=20required=20to=20= contain=20configuration,=20but=20if=20it=20does=0A+=09=20*=20we=20error=20= out=20in=20case=20it=20fails=20to=20load=20rather=20than=20continue=20to=20= try=20the=0A+=09=20*=20postgresql.conf=20configuration=20to=20avoid=20= silently=20falling=20back=20on=20an=0A+=09=20*=20undesired=20= configuration.=0A+=09=20*/=0A+=09if=20(res=20=3D=3D=20= HOSTSFILE_LOAD_FAILED)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09errmsg("could=20not=20load=20\"%s\":=20%s",=20"pg_hosts.conf",=0A= +=09=09=09=09=09=20=20=20err_msg=20?=20err_msg=20:=20"unknown=20= error"));=0A+=09=09MemoryContextDelete(host_memcxt);=0A+=09=09return=20= -1;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Loading=20and=20parsing=20the=20= hosts=20file=20was=20successful,=20create=20contexts=20for=0A+=09=20*=20= each=20host=20entry=20and=20add=20to=20the=20list=20of=20hosts=20to=20be=20= checked=20during=0A+=09=20*=20login.=0A+=09=20*/=0A+=09else=20if=20(res=20= =3D=3D=20HOSTSFILE_LOAD_OK)=0A+=09{=0A+=09=09foreach(line,=20pg_hosts)=0A= +=09=09{=0A+=09=09=09HostContext=20*host_context;=0A+=09=09=09HostsLine=20= =20*host=20=3D=20lfirst(line);=0A+=09=09=09SSL_CTX=20=20=20=20= *tmp_context=20=3D=20NULL;=0A+=0A+=09=09=09tmp_context=20=3D=20= ssl_init_context(isServerStart,=20host);=0A+=09=09=09if=20(tmp_context=20= =3D=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09errmsg("unable=20= to=20load=20SSL=20config=20from=20\"%s\"=20line=20%i",=0A+=09=09=09=09=09= =09=09=20=20=20host->sourcefile,=20host->linenumber));=0A+=09=09=09=09= free_contexts();=0A+=09=09=09=09MemoryContextDelete(host_memcxt);=0A+=09=09= =09=09return=20-1;=0A+=09=09=09}=0A+=0A+=09=09=09host_context=20=3D=20= palloc0(sizeof(HostContext));=0A+=09=09=09host_context->context=20=3D=20= tmp_context;=0A+=0A+=09=09=09/*=20Set=20flag=20to=20remember=20whether=20= CA=20store=20has=20been=20loaded=20*/=0A+=09=09=09if=20(host->ssl_ca=20= &&=20host->ssl_ca[0]=20!=3D=20'\0')=0A+=09=09=09=09= host_context->ssl_loaded_verify_locations=20=3D=20true;=0A+=0A+=09=09=09= /*=0A+=09=09=09=20*=20The=20hostname=20in=20the=20context=20is=20NULL=20= in=20case=20it=20is=20the=20default=0A+=09=09=09=20*=20host,=20or=20a=20= context=20to=20use=20for=20non-SNI=20connections.=0A+=09=09=09=20*/=0A+=09= =09=09if=20(strcmp(host->hostname,=20"*")=20=3D=3D=200)=0A+=09=09=09=09= default_context=20=3D=20host_context;=0A+=09=09=09else=20if=20= (strcmp(host->hostname,=20"no_sni")=20=3D=3D=200)=0A+=09=09=09=09= no_sni_context=20=3D=20host_context;=0A+=09=09=09else=0A+=09=09=09{=0A+=09= =09=09=09host_context->hostname=20=3D=20pstrdup(host->hostname);=0A+=09=09= =09=09sni_contexts=20=3D=20lappend(sni_contexts,=20host_context);=0A+=09=09= =09}=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20There=20needs=20to=20be=20an=20= installed=20context=20to=20drive=20the=20handshake=0A+=09=09=09=20*=20= until=20the=20SNI=20callback=20switches=20over=20to=20the=20expected=20= one,=20for=0A+=09=09=09=20*=20now=20just=20set=20it=20to=20the=20first=20= one=20we=20see.=0A+=09=09=09=20*/=0A+=09=09=09if=20(!Host_context)=0A+=09= =09=09=09Host_context=20=3D=20host_context;=0A+=09=09}=0A+=0A+=09=09= MemoryContextDelete(host_memcxt);=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20If=20= the=20pg_hosts.conf=20file=20doesn't=20exist,=20or=20is=20empty,=20then=20= load=20the=0A+=09=20*=20config=20from=20postgresql.conf.=0A+=09=20*/=0A+=09= else=20if=20(res=20=3D=3D=20HOSTSFILE_EMPTY=20||=20res=20=3D=3D=20= HOSTSFILE_MISSING)=0A+=09{=0A+=09=09HostsLine=09pgconf;=0A+=09=09SSL_CTX=20= =20=20=20*tmp_context=20=3D=20NULL;=0A+=0A+=09=09memset(&pgconf,=200,=20= sizeof(pgconf));=0A+=09=09pgconf.ssl_cert=20=3D=20ssl_cert_file;=0A+=09=09= pgconf.ssl_key=20=3D=20ssl_key_file;=0A+=09=09pgconf.ssl_ca=20=3D=20= ssl_ca_file;=0A+=09=09pgconf.ssl_passphrase_cmd=20=3D=20= ssl_passphrase_command;=0A+=09=09pgconf.ssl_passphrase_reload=20=3D=20= ssl_passphrase_command_supports_reload;=0A+=0A+=09=09tmp_context=20=3D=20= ssl_init_context(isServerStart,=20&pgconf);=0A+=09=09if=20(tmp_context=20= =3D=3D=20NULL)=0A+=09=09{=0A+=09=09=09ereport(isServerStart=20?=20FATAL=20= :=20LOG,=0A+=09=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09= =09=09errmsg("could=20not=20load=20SSL=20configuration=20from=20\"%s\"",=0A= +=09=09=09=09=09=09=20=20=20"postgresql.conf"));=0A+=09=09=09return=20= -1;=0A+=09=09}=0A+=0A+=09=09/*=0A+=09=09=20*=20If=20postgresql.conf=20is=20= used=20to=20configure=20SSL=20then=20by=20definition=20it=0A+=09=09=20*=20= will=20be=20the=20default=20context=20as=20we=20don't=20have=20per-host=20= config.=20=20We=0A+=09=09=20*=20can=20also=20set=20it=20as=20the=20= Host_context=20since=20it=20will=20be=20used=20for=20all=0A+=09=09=20*=20= connections.=0A+=09=09=20*/=0A+=09=09default_context=20=3D=20= palloc0(sizeof(HostContext));=0A+=09=09default_context->context=20=3D=20= tmp_context;=0A+=09=09Host_context=20=3D=20default_context;=0A+=0A+=09=09= /*=20Set=20flag=20to=20remember=20whether=20CA=20store=20has=20been=20= loaded=20*/=0A+=09=09if=20(ssl_ca_file[0])=0A+=09=09=09= default_context->ssl_loaded_verify_locations=20=3D=20true;=0A+=09}=0A+=0A= +=09/*=20Make=20sure=20we=20have=20at=20least=20one=20certificate=20= loaded=20*/=0A+=09if=20(sni_contexts=20=3D=3D=20NIL=20&&=20= !default_context=20&&=20!no_sni_context)=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=09errmsg("no=20SSL=20= contexts=20loaded"));=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09= SSL_context=20=3D=20Host_context->context;=0A+=0A+=09return=200;=0A+}=0A= +=0A+static=20SSL_CTX=20*=0A+ssl_init_context(bool=20isServerStart,=20= HostsLine=20*host_line)=0A=20{=0A=20=09SSL_CTX=20=20=20=20*context;=0A=20= =09int=09=09=09ssl_ver_min=20=3D=20-1;=0A=20=09int=09=09=09ssl_ver_max=20= =3D=20-1;=0A=20=0A+=09const=20char=20*ctx_ssl_cert_file=20=3D=20= host_line->ssl_cert;=0A+=09const=20char=20*ctx_ssl_key_file=20=3D=20= host_line->ssl_key;=0A+=09const=20char=20*ctx_ssl_ca_file=20=3D=20= host_line->ssl_ca;=0A+=0A=20=09/*=0A=20=09=20*=20Create=20a=20new=20SSL=20= context=20into=20which=20we'll=20load=20all=20the=20configuration=0A=20=09= =20*=20settings.=20=20If=20we=20fail=20partway=20through,=20we=20can=20= avoid=20memory=20leakage=20by=0A@@=20-132,10=20+323,16=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09=20*/=0A=20=09= SSL_CTX_set_mode(context,=20SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A=20=0A= +=09/*=0A+=09=20*=20Install=20SNI=20TLS=20extension=20callback=20in=20= order=20to=20validate=20hostnames=20in=0A+=09=20*=20case=20we=20have=20= at=20least=20one=20context=20configured=20with=20a=20host=20name.=0A+=09=20= */=0A+=09SSL_CTX_set_tlsext_servername_callback(context,=20= sni_servername_cb);=0A+=0A=20=09/*=0A=20=09=20*=20Call=20init=20hook=20= (usually=20to=20set=20password=20callback)=0A=20=09=20*/=0A-=09= (*openssl_tls_init_hook)=20(context,=20isServerStart);=0A+=09= (*openssl_tls_init_hook)=20(context,=20isServerStart,=20host_line);=0A=20= =0A=20=09/*=20used=20by=20the=20callback=20*/=0A=20=09= ssl_is_server_start=20=3D=20isServerStart;=0A@@=20-143,16=20+340,16=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09/*=0A=20=09=20*=20Load=20and=20= verify=20server's=20certificate=20and=20private=20key=0A=20=09=20*/=0A-=09= if=20(SSL_CTX_use_certificate_chain_file(context,=20ssl_cert_file)=20!=3D=20= 1)=0A+=09if=20(SSL_CTX_use_certificate_chain_file(context,=20= ctx_ssl_cert_file)=20!=3D=201)=0A=20=09{=0A=20=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=20errmsg("could=20= not=20load=20server=20certificate=20file=20\"%s\":=20%s",=0A-=09=09=09=09= =09=09ssl_cert_file,=20SSLerrmessage(ERR_get_error()))));=0A+=09=09=09=09= =09=09ctx_ssl_cert_file,=20SSLerrmessage(ERR_get_error()))));=0A=20=09=09= goto=20error;=0A=20=09}=0A=20=0A-=09if=20= (!check_ssl_key_file_permissions(ssl_key_file,=20isServerStart))=0A+=09= if=20(!check_ssl_key_file_permissions(ctx_ssl_key_file,=20= isServerStart))=0A=20=09=09goto=20error;=0A=20=0A=20=09/*=0A@@=20-161,19=20= +358,19=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09= dummy_ssl_passwd_cb_called=20=3D=20false;=0A=20=0A=20=09if=20= (SSL_CTX_use_PrivateKey_file(context,=0A-=09=09=09=09=09=09=09=09=09= ssl_key_file,=0A+=09=09=09=09=09=09=09=09=09ctx_ssl_key_file,=0A=20=09=09= =09=09=09=09=09=09=09SSL_FILETYPE_PEM)=20!=3D=201)=0A=20=09{=0A=20=09=09= if=20(dummy_ssl_passwd_cb_called)=0A=20=09=09=09ereport(isServerStart=20= ?=20FATAL=20:=20LOG,=0A=20=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=20= errmsg("private=20key=20file=20\"%s\"=20cannot=20be=20reloaded=20because=20= it=20requires=20a=20passphrase",=0A-=09=09=09=09=09=09=09= ssl_key_file)));=0A+=09=09=09=09=09=09=09ctx_ssl_key_file)));=0A=20=09=09= else=0A=20=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09= =09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=20= errmsg("could=20not=20load=20private=20key=20file=20\"%s\":=20%s",=0A-=09= =09=09=09=09=09=09ssl_key_file,=20SSLerrmessage(ERR_get_error()))));=0A+=09= =09=09=09=09=09=09ctx_ssl_key_file,=20SSLerrmessage(ERR_get_error()))));=0A= =20=09=09goto=20error;=0A=20=09}=0A=20=0A@@=20-325,17=20+522,17=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09/*=0A=20=09=20*=20Load=20CA=20= store,=20so=20we=20can=20verify=20client=20certificates=20if=20needed.=0A= =20=09=20*/=0A-=09if=20(ssl_ca_file[0])=0A+=09if=20(ctx_ssl_ca_file=20&&=20= ctx_ssl_ca_file[0])=0A=20=09{=0A=20=09=09STACK_OF(X509_NAME)=20*=20= root_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(context,=20ctx_ssl_ca_file,=20NULL)=20!=3D=20= 1=20||=0A+=09=09=09(root_cert_list=20=3D=20= SSL_load_client_CA_file(ctx_ssl_ca_file))=20=3D=3D=20NULL)=0A=20=09=09{=0A= =20=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09=09=09= =09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=20= errmsg("could=20not=20load=20root=20certificate=20file=20\"%s\":=20%s",=0A= -=09=09=09=09=09=09=09ssl_ca_file,=20SSLerrmessage(ERR_get_error()))));=0A= +=09=09=09=09=09=09=09ctx_ssl_ca_file,=20= SSLerrmessage(ERR_get_error()))));=0A=20=09=09=09goto=20error;=0A=20=09=09= }=0A=20=0A@@=20-347,18=20+544,17=20@@=20be_tls_init(bool=20= isServerStart)=0A=20=09=09=20*=20free=20it=20when=20no=20longer=20= needed.=0A=20=09=09=20*/=0A=20=09=09SSL_CTX_set_client_CA_list(context,=20= root_cert_list);=0A-=0A-=09=09/*=0A-=09=09=20*=20Always=20ask=20for=20= SSL=20client=20cert,=20but=20don't=20fail=20if=20it's=20not=0A-=09=09=20= *=20presented.=20=20We=20might=20fail=20such=20connections=20later,=20= depending=20on=20what=0A-=09=09=20*=20we=20find=20in=20pg_hba.conf.=0A-=09= =09=20*/=0A-=09=09SSL_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=20=09}=0A=20=0A+=09/*=0A+=09=20= *=20Always=20ask=20for=20SSL=20client=20cert,=20but=20don't=20fail=20if=20= it's=20not=20presented.=0A+=09=20*=20We=20might=20fail=20such=20= connections=20later,=20depending=20on=20what=20we=20find=20in=0A+=09=20*=20= pg_hba.conf.=0A+=09=20*/=0A+=09SSL_CTX_set_verify(context,=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=20=09/*----------=0A=20=09=20*=20Load=20= the=20Certificate=20Revocation=20List=20(CRL).=0A=20=09=20*=20= http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.htm= l=0A@@=20-407,38=20+603,19=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+=09return=20context;=0A=20=0A=20=09/*=20Clean=20up=20by=20= releasing=20working=20context.=20*/=0A=20error:=0A=20=09if=20(context)=0A= =20=09=09SSL_CTX_free(context);=0A-=09return=20-1;=0A+=09return=20NULL;=0A= =20}=0A=20=0A=20void=0A=20be_tls_destroy(void)=0A=20{=0A-=09if=20= (SSL_context)=0A-=09=09SSL_CTX_free(SSL_context);=0A-=09SSL_context=20=3D=20= NULL;=0A-=09ssl_loaded_verify_locations=20=3D=20false;=0A+=09= free_contexts();=0A=20}=0A=20=0A=20int=0A@@=20-771,6=20+948,9=20@@=20= be_tls_close(Port=20*port)=0A=20=09=09pfree(port->peer_dn);=0A=20=09=09= port->peer_dn=20=3D=20NULL;=0A=20=09}=0A+=0A+=09Host_context=20=3D=20= NULL;=0A+=09SSL_context=20=3D=20NULL;=0A=20}=0A=20=0A=20ssize_t=0A@@=20= -1144,7=20+1324,7=20@@=20ssl_external_passwd_cb(char=20*buf,=20int=20= size,=20int=20rwflag,=20void=20*userdata)=0A=20=0A=20=09Assert(rwflag=20= =3D=3D=200);=0A=20=0A-=09return=20run_ssl_passphrase_command(prompt,=20= ssl_is_server_start,=20buf,=20size);=0A+=09return=20= run_ssl_passphrase_command(prompt,=20ssl_is_server_start,=20buf,=20size,=20= userdata);=0A=20}=0A=20=0A=20/*=0A@@=20-1390,6=20+1570,92=20@@=20= alpn_cb(SSL=20*ssl,=0A=20=09}=0A=20}=0A=20=0A+/*=0A+=20*=20= sni_servername_cb=0A+=20*=0A+=20*=20Callback=20executed=20by=20OpenSSL=20= during=20handshake=20in=20case=20the=20server=20has=20been=0A+=20*=20= configured=20to=20validate=20hostnames.=20=20Returning=20= SSL_TLSEXT_ERR_ALERT_FATAL=20to=0A+=20*=20OpenSSL=20will=20immediately=20= terminate=20the=20handshake.=0A+=20*/=0A+static=20int=0A= +sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg)=0A+{=0A+=09= const=20char=20*tlsext_hostname;=0A+=09HostContext=20*install_context=20= =3D=20NULL;=0A+=0A+=09tlsext_hostname=20=3D=20SSL_get_servername(ssl,=20= TLSEXT_NAMETYPE_host_name);=0A+=0A+=09/*=0A+=09=20*=20If=20there=20is=20= no=20hostname=20set=20in=20the=20TLS=20extension,=20we=20have=20two=20= options:=0A+=09=20*=20i)=20there=20is=20a=20HostContext=20defined=20for=20= non-SNI=20connections,=20in=20that=20case=0A+=09=20*=20we=20switch=20to=20= that;=20ii)=20there=20is=20no=20non-SNI=20config=20and=20we=20error=20= out=20as=0A+=09=20*=20there=20is=20no=20context=20to=20switch=20to.=0A+=09= =20*/=0A+=09if=20(!tlsext_hostname)=0A+=09{=0A+=09=09if=20= (no_sni_context)=0A+=09=09=09install_context=20=3D=20no_sni_context;=0A+=09= =09else=20if=20(default_context)=0A+=09=09=09install_context=20=3D=20= default_context;=0A+=09=09else=0A+=09=09{=0A+=09=09=09/*=0A+=09=09=09=20= *=20The=20error=20message=20for=20a=20missing=20server_name=20should,=20= according=0A+=09=09=09=20*=20to=20RFC=208446,=20be=20missing_extension.=20= This=20isn't=20entirely=20ideal=0A+=09=09=09=20*=20since=20the=20user=20= won't=20be=20able=20to=20tell=20which=20extension=20the=20server=0A+=09=09= =09=20*=20considered=20missing.=20=20Sending=20unrecognized_name=20would=20= be=20a=20more=0A+=09=09=09=20*=20helpful=20error,=20but=20for=20now=20we=20= stick=20to=20the=20RFC.=0A+=09=09=09=20*/=0A+=09=09=09*al=20=3D=20= SSL_AD_MISSING_EXTENSION;=0A+=0A+=09=09=09ereport(COMMERROR,=0A+=09=09=09= =09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20= errmsg("no=20hostname=20provided=20in=20callback")));=0A+=09=09=09return=20= SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09=09}=0A+=09}=0A+=09else=0A+=09{=0A+=09=09= /*=0A+=09=09=20*=20We=20have=20a=20requested=20hostname=20from=20the=20= client,=20match=20against=20all=0A+=09=09=20*=20entries=20in=20the=20= pg_hosts=20configuration=20and=20attempt=20to=20find=20a=20match.=0A+=09=09= =20*/=0A+=09=09foreach_ptr(HostContext,=20host,=20sni_contexts)=0A+=09=09= {=0A+=09=09=09if=20(strcmp(host->hostname,=20tlsext_hostname)=20=3D=3D=20= 0)=0A+=09=09=09{=0A+=09=09=09=09install_context=20=3D=20host;=0A+=09=09=09= =09break;=0A+=09=09=09}=0A+=09=09}=0A+=0A+=09=09/*=0A+=09=09=20*=20If=20= no=20host=20specific=20match=20was=20found,=20and=20there=20is=20a=20= default=20config,=0A+=09=09=20*=20then=20fall=20back=20to=20using=20= that.=0A+=09=09=20*/=0A+=09=09if=20(!install_context=20&&=20= default_context)=0A+=09=09=09install_context=20=3D=20default_context;=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_context=20=3D=3D=20NULL)=0A+=09=09return=20= SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=0A+=09Host_context=20=3D=20= install_context;=0A+=09SSL_context=20=3D=20install_context->context;=0A+=09= if=20(SSL_set_SSL_CTX(ssl,=20SSL_context)=20=3D=3D=20NULL)=0A+=09{=0A+=09= =09ereport(COMMERROR,=0A+=09=09=09=09= errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09errmsg("failed=20to=20= switch=20to=20SSL=20context=20for=20host"));=0A+=09=09return=20= SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09}=0A+=0A+=09return=20= SSL_TLSEXT_ERR_OK;=0A+}=0A=20=0A=20/*=0A=20=20*=20Set=20DH=20parameters=20= for=20generating=20ephemeral=20DH=20keys.=20=20The=0A@@=20-1599,6=20= +1865,14=20@@=20be_tls_get_peer_serial(Port=20*port,=20char=20*ptr,=20= size_t=20len)=0A=20=09=09ptr[0]=20=3D=20'\0';=0A=20}=0A=20=0A+bool=0A= +be_tls_loaded_verify_locations(void)=0A+{=0A+=09if=20(!Host_context)=0A= +=09=09return=20false;=0A+=09return=20= Host_context->ssl_loaded_verify_locations;=0A+}=0A+=0A=20char=20*=0A=20= be_tls_get_certificate_hash(Port=20*port,=20size_t=20*len)=0A=20{=0A@@=20= -1792,17=20+2066,23=20@@=20ssl_protocol_version_to_string(int=20v)=0A=20=0A= =20=0A=20static=20void=0A-default_openssl_tls_init(SSL_CTX=20*context,=20= bool=20isServerStart)=0A+default_openssl_tls_init(SSL_CTX=20*context,=20= bool=20isServerStart,=20HostsLine=20*host)=0A=20{=0A=20=09if=20= (isServerStart)=0A=20=09{=0A-=09=09if=20(ssl_passphrase_command[0])=0A+=09= =09if=20(host->ssl_passphrase_cmd=20!=3D=20NULL)=0A+=09=09{=0A=20=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20ssl_external_passwd_cb);=0A+=09=09= =09SSL_CTX_set_default_passwd_cb_userdata(context,=20= host->ssl_passphrase_cmd);=0A+=09=09}=0A=20=09}=0A=20=09else=0A=20=09{=0A= -=09=09if=20(ssl_passphrase_command[0]=20&&=20= ssl_passphrase_command_supports_reload)=0A+=09=09if=20= (host->ssl_passphrase_cmd=20!=3D=20NULL=20&&=20= host->ssl_passphrase_reload)=0A+=09=09{=0A=20=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20ssl_external_passwd_cb);=0A+=09=09= =09SSL_CTX_set_default_passwd_cb_userdata(context,=20= host->ssl_passphrase_cmd);=0A+=09=09}=0A=20=09=09else=0A=20=0A=20=09=09=09= /*=0A@@=20-1814,3=20+2094,42=20@@=20default_openssl_tls_init(SSL_CTX=20= *context,=20bool=20isServerStart)=0A=20=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20dummy_ssl_passwd_cb);=0A=20=09}=0A= =20}=0A+=0A+/*=0A+=20*=20Cleanup=20function=20for=20when=20hostname=20= configuration=20is=20reloaded=20from=20the=0A+=20*=20pg_hosts.conf=20= file,=20at=20that=20point=20we=20must=20discard=20all=20existing=20= contexts.=0A+=20*/=0A+static=20void=0A+free_contexts(void)=0A+{=0A+=09if=20= (sni_contexts=20!=3D=20NIL)=0A+=09{=0A+=09=09foreach_ptr(HostContext,=20= host,=20sni_contexts)=0A+=09=09{=0A+=09=09=09if=20(host->hostname)=0A+=09= =09=09=09pfree(unconstify(char=20*,=20host->hostname));=0A+=09=09=09= SSL_CTX_free(host->context);=0A+=09=09}=0A+=0A+=09=09= list_free_deep(sni_contexts);=0A+=09=09sni_contexts=20=3D=20NIL;=0A+=09}=0A= +=0A+=09/*=0A+=09=20*=20The=20hostname=20need=20not=20be=20freed=20for=20= the=20no_sni=20and=20default=20contexts=0A+=09=20*=20since=20they=20by=20= definition=20are=20not=20connected=20to=20a=20hostname=20and=20thus=20= have=0A+=09=20*=20none=20allocated.=0A+=09=20*/=0A+=09if=20= (no_sni_context)=0A+=09{=0A+=09=09SSL_CTX_free(no_sni_context->context);=0A= +=09=09pfree(no_sni_context);=0A+=09=09no_sni_context=20=3D=20NULL;=0A+=09= }=0A+=09if=20(default_context)=0A+=09{=0A+=09=09= SSL_CTX_free(default_context->context);=0A+=09=09pfree(default_context);=0A= +=09=09default_context=20=3D=20NULL;=0A+=09}=0A+}=0Adiff=20--git=20= a/src/backend/libpq/be-secure.c=20b/src/backend/libpq/be-secure.c=0A= index=203f9257ab010..6dcb673843a=20100644=0A---=20= a/src/backend/libpq/be-secure.c=0A+++=20b/src/backend/libpq/be-secure.c=0A= @@=20-43,10=20+43,6=20@@=20char=09=20=20=20*ssl_dh_params_file;=0A=20= char=09=20=20=20*ssl_passphrase_command;=0A=20bool=09=09= ssl_passphrase_command_supports_reload;=0A=20=0A-#ifdef=20USE_SSL=0A= -bool=09=09ssl_loaded_verify_locations=20=3D=20false;=0A-#endif=0A-=0A=20= /*=20GUC=20variable=20controlling=20SSL=20cipher=20list=20*/=0A=20char=09= =20=20=20*SSLCipherSuites=20=3D=20NULL;=0A=20char=09=20=20=20= *SSLCipherList=20=3D=20NULL;=0A@@=20-99,7=20+95,7=20@@=20bool=0A=20= secure_loaded_verify_locations(void)=0A=20{=0A=20#ifdef=20USE_SSL=0A-=09= return=20ssl_loaded_verify_locations;=0A+=09return=20= be_tls_loaded_verify_locations();=0A=20#else=0A=20=09return=20false;=0A=20= #endif=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= ae9d5f3fb70..360b08b5853=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,36=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}=0A= +=09SetConfigOption("hosts_file",=20fname,=20PGC_POSTMASTER,=20= PGC_S_OVERRIDE);=0A+=0A=20=09if=20(fname_is_malloced)=0A=20=09=09= free(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= 7c60b125564..b95c373fb41=20100644=0A---=20= a/src/backend/utils/misc/guc_parameters.dat=0A+++=20= b/src/backend/utils/misc/guc_parameters.dat=0A@@=20-1176,6=20+1176,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',=0Adiff=20= --git=20a/src/backend/utils/misc/guc_tables.c=20= b/src/backend/utils/misc/guc_tables.c=0Aindex=2073ff6ad0a32..a88690933c7=20= 100644=0A---=20a/src/backend/utils/misc/guc_tables.c=0A+++=20= b/src/backend/utils/misc/guc_tables.c=0A@@=20-556,6=20+556,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= dc9e2255f8a..1f360110564=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=0Adiff=20--git=20a/src/bin/initdb/initdb.c=20= b/src/bin/initdb/initdb.c=0Aindex=20a3980e5535f..023ef134856=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-1532,6=20+1533,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-2793,6=20= +2802,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= -2808,12=20+2818,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-2821,6=20+2831,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..38713381255=20100644=0A---=20= a/src/include/libpq/hba.h=0A+++=20b/src/include/libpq/hba.h=0A@@=20= -151,6=20+151,33=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= bool=09=09default_host;=0A+=09char=09=20=20=20*hostname;=0A+=09char=09=20= =20=20*ssl_key;=0A+=09char=09=20=20=20*ssl_cert;=0A+=09char=09=20=20=20= *ssl_ca;=0A+=0A+=09/*=20Optional=20fields=20*/=0A+=09char=09=20=20=20= *ssl_passphrase_cmd;=0A+=09bool=09=09ssl_passphrase_reload;=0A+}=20= HostsLine;=0A+=0A+typedef=20enum=20HostsFileLoad=0A+{=0A+=09= HOSTSFILE_LOAD_OK=20=3D=200,=0A+=09HOSTSFILE_LOAD_FAILED,=0A+=09= HOSTSFILE_EMPTY,=0A+=09HOSTSFILE_MISSING,=0A+}=20HostsFileLoadResult;=0A= +=0A=20/*=0A=20=20*=20TokenizedAuthLine=20represents=20one=20line=20= lexed=20from=20an=20authentication=0A=20=20*=20configuration=20file.=20=20= Each=20item=20in=20the=20"fields"=20list=20is=20a=20sub-list=20of=0Adiff=20= --git=20a/src/include/libpq/libpq-be.h=20b/src/include/libpq/libpq-be.h=0A= index=20921b2daa4ff..c85e631cda2=20100644=0A---=20= a/src/include/libpq/libpq-be.h=0A+++=20b/src/include/libpq/libpq-be.h=0A= @@=20-320,6=20+320,7=20@@=20extern=20const=20char=20= *be_tls_get_cipher(Port=20*port);=0A=20extern=20void=20= be_tls_get_peer_subject_name(Port=20*port,=20char=20*ptr,=20size_t=20= len);=0A=20extern=20void=20be_tls_get_peer_issuer_name(Port=20*port,=20= char=20*ptr,=20size_t=20len);=0A=20extern=20void=20= be_tls_get_peer_serial(Port=20*port,=20char=20*ptr,=20size_t=20len);=0A= +extern=20bool=20be_tls_loaded_verify_locations(void);=0A=20=0A=20/*=0A=20= =20*=20Get=20the=20server=20certificate=20hash=20for=20SCRAM=20channel=20= binding=20type=0A@@=20-332,7=20+333,7=20@@=20extern=20char=20= *be_tls_get_certificate_hash(Port=20*port,=20size_t=20*len);=0A=20=0A=20= /*=20init=20hook=20for=20SSL,=20the=20default=20sets=20the=20password=20= callback=20if=20appropriate=20*/=0A=20#ifdef=20USE_OPENSSL=0A-typedef=20= void=20(*openssl_tls_init_hook_typ)=20(SSL_CTX=20*context,=20bool=20= isServerStart);=0A+typedef=20void=20(*openssl_tls_init_hook_typ)=20= (SSL_CTX=20*context,=20bool=20isServerStart,=20HostsLine=20*host);=0A=20= extern=20PGDLLIMPORT=20openssl_tls_init_hook_typ=20= openssl_tls_init_hook;=0A=20#endif=0A=20=0Adiff=20--git=20= a/src/include/libpq/libpq.h=20b/src/include/libpq/libpq.h=0Aindex=20= 412bc9758fb..3d734266172=20100644=0A---=20a/src/include/libpq/libpq.h=0A= +++=20b/src/include/libpq/libpq.h=0A@@=20-156,8=20+156,9=20@@=20enum=20= ssl_protocol_versions=0A=20=20*=20prototypes=20for=20functions=20in=20= be-secure-common.c=0A=20=20*/=0A=20extern=20int=09= run_ssl_passphrase_command(const=20char=20*prompt,=20bool=20= is_server_start,=0A-=09=09=09=09=09=09=09=09=09=20=20=20char=20*buf,=20= int=20size);=0A+=09=09=09=09=09=09=09=09=09=20=20=20char=20*buf,=20int=20= size,=20void=20*userdata);=0A=20extern=20bool=20= check_ssl_key_file_permissions(const=20char=20*ssl_key_file,=0A=20=09=09=09= =09=09=09=09=09=09=09=20=20=20bool=20isServerStart);=0A+extern=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=20a/src/include/utils/guc.h=20= b/src/include/utils/guc.h=0Aindex=20bf39878c43e..f97d93136ed=20100644=0A= ---=20a/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=20= a/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=20= b/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=0Aindex=20= d5992149821..a85d85735cf=20100644=0A---=20= a/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=0A+++=20= b/src/test/modules/ssl_passphrase_callback/ssl_passphrase_func.c=0A@@=20= -26,7=20+26,7=20@@=20static=20char=20*ssl_passphrase=20=3D=20NULL;=0A=20= static=20int=09rot13_passphrase(char=20*buf,=20int=20size,=20int=20= rwflag,=20void=20*userdata);=0A=20=0A=20/*=20hook=20function=20to=20set=20= the=20callback=20*/=0A-static=20void=20set_rot13(SSL_CTX=20*context,=20= bool=20isServerStart);=0A+static=20void=20set_rot13(SSL_CTX=20*context,=20= bool=20isServerStart,=20HostsLine=20*host);=0A=20=0A=20/*=0A=20=20*=20= Module=20load=20callback=0A@@=20-53,7=20+53,7=20@@=20_PG_init(void)=0A=20= }=0A=20=0A=20static=20void=0A-set_rot13(SSL_CTX=20*context,=20bool=20= isServerStart)=0A+set_rot13(SSL_CTX=20*context,=20bool=20isServerStart,=20= HostsLine=20*host)=0A=20{=0A=20=09/*=20warn=20if=20the=20user=20has=20= set=20ssl_passphrase_command=20*/=0A=20=09if=20= (ssl_passphrase_command[0])=0Adiff=20--git=20= a/src/test/perl/PostgreSQL/Test/Cluster.pm=20= b/src/test/perl/PostgreSQL/Test/Cluster.pm=0Aindex=20= 955dfc0e7f8..3a3088f756b=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=202b9b3dfd663..c0104f6aa81=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..2dd70e7afee=0A---=20/dev/null=0A+++=20= b/src/test/ssl/t/004_sni.pl=0A@@=20-0,0=20+1,289=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+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= +#=20Remove=20pg_hosts.conf=20and=20reload=20to=20make=20sure=20a=20= missing=20file=20is=20treated=20like=0A+#=20an=20empty=20file.=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_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+#=20Modify=20pg_hosts.conf=20to=20no=20longer=20have=20the=20= default=20host=20entry.=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf('pg_hosts.conf',=0A+=09= "example.org=20server-cn-only+server_ca.crt=20server-cn-only.key=20= root_ca.crt"=0A+);=0A+$node->reload;=0A+=0A+#=20Connecting=20without=20a=20= hostname=20as=20well=20as=20with=20a=20hostname=20which=20isn't=20in=20= the=0A+#=20pg_hosts=20configuration=20should=20fail.=0A= +$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20sslsni=3D0",=0A= +=09"pg_hosts.conf:=20connect=20to=20default=20with=20sslmode=3Drequire",=0A= +=09expected_stderr=20=3D>=20qr/missing=20extension/);=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+=0A+#=20Reconfigure=20with=20broken=20= configuration=20for=20the=20key=20passphrase,=20the=20server=0A+#=20= should=20not=20start=20up=0A+ok(unlink($node->data_dir=20.=20= '/pg_hosts.conf'));=0A+$node->append_conf('pg_hosts.conf',=0A+=09= 'localhost=20server-cn-only.crt=20server-password.key=20= root+client_ca.crt=20"echo=20wrongpassword"=20on'=0A+);=0A+my=20$result=20= =3D=20$node->restart(fail_ok=20=3D>=201);=0A+is($result,=200,=0A+=09= 'pg_hosts.conf:=20restart=20fails=20with=20password-protected=20key=20= when=20using=20the=20wrong=20passphrase=20command'=0A+);=0A+=0A+#=20= Reconfigure=20again=20but=20with=20the=20correct=20passphrase=20set=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A= +$node->append_conf('pg_hosts.conf',=0A+=09'localhost=20= server-cn-only.crt=20server-password.key=20root+client_ca.crt=20"echo=20= secret1"=20on'=0A+);=0A+$result=20=3D=20$node->restart(fail_ok=20=3D>=20= 1);=0A+is($result,=201,=0A+=09'pg_hosts.conf:=20restart=20succeeds=20= with=20password-protected=20key=20when=20using=20the=20correct=20= passphrase=20command'=0A+);=0A+=0A+#=20Make=20sure=20connecting=20works,=20= and=20try=20to=20stress=20the=20reload=20logic=20by=20issuing=0A+#=20= subsequent=20reloads=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=20sslmode=3Drequire"=0A+);=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=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.=20Connecting=20= after=20restart=20should=20succeed=20but=20not=20after=20the=0A+#=20= following=20reload.=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"=20off'=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+SKIP:=0A+{=0A= +=09#=20Passphrase=20reloads=20must=20be=20enabled=20on=20Windows=20to=20= succeed=20even=20without=20a=0A+=09#=20restart=0A+=09skip=20"Passphrase=20= command=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+}=0A+=0A= +$node->reload;=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dlocalhost",=0A+=09"pg_hosts.conf:=20connect=20fails=20since=20the=20= passphrase=20protected=20key=20cannot=20be=20reloaded"=0A+);=0A+=0A+#=20= Configure=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=20server-cn-only.crt=20= server-cn-only.key");=0A+$node->reload;=0A+=0A+$node->connect_ok(=0A+=09= "$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= sslsni=3D0",=0A+=09"pg_hosts.conf:=20only=20non-SNI=20connections=20= allowed");=0A+=0A+$node->connect_fails(=0A+=09"$connstr=20= sslrootcert=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=20by=20= connecting=20to=20hosts=20in=20pg_hosts.conf=20while=20at=20the=20same=0A= +#=20time=20swapping=20out=20default=20contexts=20containing=20different=20= CA=20configurations.=0A+=0A+#=20pg_hosts=20configuration=0A= +ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A+#=20example.org=20= has=20an=20unconfigured=20CA.=0A+$node->append_conf('pg_hosts.conf',=0A+=09= 'example.org=20server-cn-only.crt=20server-cn-only.key=20""');=0A+#=20= example.com=20uses=20the=20client=20CA.=0A= +$node->append_conf('pg_hosts.conf',=0A+=09'example.com=20= server-cn-only.crt=20server-cn-only.key=20root+client_ca.crt');=0A+#=20= example.net=20uses=20the=20server=20CA=20(which=20is=20wrong).=0A= +$node->append_conf('pg_hosts.conf',=0A+=09'example.net=20= server-cn-only.crt=20server-cn-only.key=20root+server_ca.crt');=0A= +$node->reload;=0A+=0A+$connstr=20=3D=0A+=20=20"user=3Dssltestuser=20= dbname=3Dcertdb=20hostaddr=3D$SERVERHOSTADDR=20sslmode=3Drequire=20= sslsni=3D1";=0A+=0A+foreach=20my=20$default_ca=20("",=20= "root+client_ca",=20"root+server_ca")=0A+{=0A+=09#=20The=20default=20CA=20= should,=20not=20matter=20for=20the=20purposes=20of=20these=20tests,=20= since=0A+=09#=20we=20connect=20to=20the=20other=20hosts=20explicitly.=20= Test=20with=20various=20default=20CA=0A+=09#=20settings=20to=20ensure=20= it's=20isolated=20from=20the=20actual=20connections.=0A+=09= $ssl_server->switch_server_cert(=0A+=09=09$node,=0A+=09=09certfile=20=3D>=20= 'server-cn-only',=0A+=09=09cafile=20=3D>=20$default_ca);=0A+=0A+=09#=20= example.org=20is=20unconfigured=20and=20should=20fail.=0A+=09= $node->connect_fails(=0A+=09=09"$connstr=20host=3Dexample.org=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20"=0A+=09=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A+=09=09"host:=20'example.org',=20= ca:=20'$default_ca':=20connect=20with=20sslcert,=20no=20client=20CA=20= configured",=0A+=09=09expected_stderr=20=3D>=20qr/unknown=20ca/);=0A+=0A= +=09#=20example.com=20is=20configured=20and=20should=20require=20a=20= valid=20client=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'$default_ca':=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_ok(=0A+=09=09"$connstr=20host=3Dexample.com=20= sslrootcert=3Dssl/root+server_ca.crt=20sslcertmode=3Drequire=20= sslcert=3Dssl/client.crt=20"=0A+=09=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A+=09=09"host:=20'example.com',=20= ca:=20'$default_ca':=20connect=20with=20sslcert,=20client=20certificate=20= sent"=0A+=09);=0A+=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'$default_ca':=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= $ssl_server->sslkey('client.key'),=0A+=09=09"host:=20'example.net',=20= ca:=20'$default_ca':=20connect=20with=20sslcert,=20client=20certificate=20= sent",=0A+=09=09expected_stderr=20=3D>=20qr/unknown=20ca/);=0A+}=0A+=0A= +done_testing();=0Adiff=20--git=20= a/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=0Adiff=20--git=20= a/src/tools/pgindent/typedefs.list=20b/src/tools/pgindent/typedefs.list=0A= index=2014dec2d49c1..e6c9155a186=20100644=0A---=20= a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-1220,6=20+1220,9=20@@=20= HeapTupleHeader=0A=20HeapTupleHeaderData=0A=20HeapTupleTableSlot=0A=20= HistControl=0A+HostContext=0A+HostsFileLoadResult=0A+HostsLine=0A=20= HotStandbyState=0A=20I32=0A=20ICU_Convert_Func=0A--=20=0A2.39.3=20(Apple=20= Git-146)=0A=0A= --Apple-Mail=_BA87BDEE-21BC-4337-BB45-129C4A4012C4 Content-Disposition: attachment; filename=v14-0002-Review-comments.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v14-0002-Review-comments.patch" Content-Transfer-Encoding: quoted-printable =46rom=20d8033be991487f2bc80b5ae26b175014f66e9d3d=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20= =0ADate:=20Fri,=2019=20Dec=202025=2012:02:24=20= +0100=0ASubject:=20[PATCH=20v14=202/2]=20Review=20comments=0A=0A---=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|=20=2019=20+-=0A=20src/backend/libpq/be-secure-common.c=20= =20=20=20=20=20=20=20=20=20|=20=2047=20++-=0A=20= src/backend/libpq/be-secure-openssl.c=20=20=20=20=20=20=20=20=20|=20276=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/utils/misc/guc_parameters.dat=20=20=20=20=20|=20=20=207=20+=0A= =20src/backend/utils/misc/postgresql.conf.sample=20|=20=20=201=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=20=206=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=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=2041=20++-=0A=20src/test/ssl/t/004_sni.pl=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20|=20164=20+++++++----=0A=2010=20= files=20changed,=20397=20insertions(+),=20168=20deletions(-)=0A=0Adiff=20= --git=20a/doc/src/sgml/runtime.sgml=20b/doc/src/sgml/runtime.sgml=0A= index=20ca0a114da76..0705b72ca4e=20100644=0A---=20= a/doc/src/sgml/runtime.sgml=0A+++=20b/doc/src/sgml/runtime.sgml=0A@@=20= -2626,8=20+2626,8=20@@=20openssl=20x509=20-req=20-in=20server.csr=20= -text=20-days=20365=20\=0A=20=0A=20=20=20=20=0A=20=20=20=20=20= hostname=20should=20either=20be=20set=20to=20= the=20literal=0A-=20=20=20=20hostname=20for=20the=20connection,=20= no_sni=20or=20*.=0A-=20=20=20=20= =20contain=20details=20on=20how=20= these=20values=20are=0A+=20=20=20=20hostname=20for=20the=20connection,=20= /no_sni/=20or=20*.=0A+=20=20=20=20= =20contains=20details=20on=20how=20= these=20values=20are=0A=20=20=20=20=20used.=0A=20=20=20=20=20=0A=20=20=20=20=20=20Hostname=20setting=20= values=0A@@=20-2644,14=20+2644,17=20@@=20openssl=20x509=20-req=20= -in=20server.csr=20-text=20-days=20365=20\=0A=20=20=20=20=20=20=20=20= =0A=20=20=20=20=20=20=20=20=20*=0A= =20=20=20=20=20=20=20=20=20Not=20required=0A-=20=20=20=20=20= =20=20=20Default=20host,=20matches=20all=20connections=0A= +=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20Default=20= host,=20matches=20all=20connections.=0A+=20=20=20=20=20=20=20=20=0A= =20=20=20=20=20=20=20=20=0A=20=0A=20=20=20=20=20=20=20=20=0A-=20= =20=20=20=20=20=20=20no_sni=0A+=20=20=20= =20=20=20=20=20/no_sni/=0A=20=20=20=20=20= =20=20=20=20Not=20allowed=0A=20=20=20=20=20=20=20=20=20= =0A-=20=20=20=20=20=20=20=20=20Certificate=20and=20key=20to=20use=20= for=20connection=20with=20no=20sslsni=20defined.=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=20= =0A=20=20=20=20=20=20=20=20=0A=20=0A@@=20-2659,8=20= +2662,10=20@@=20openssl=20x509=20-req=20-in=20server.csr=20-text=20-days=20= 365=20\=0A=20=20=20=20=20=20=20=20=20= hostname=0A=20=20=20=20=20=20=20= =20=20Required=0A=20=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=20to=20the=20host=20specified=20in=20the=0A-=20=20=20=20=20=20= =20=20=20connection.=0A+=20=20=20=20=20=20=20=20=20Certificate=20and=20= key=20to=20use=20for=20connections=20to=20the=20host=20specified=20in=0A= +=20=20=20=20=20=20=20=20=20the=20connection.=20=20Multiple=20hostnames=20= can=20be=20defined=20by=20using=20a=20comma=0A+=20=20=20=20=20=20=20=20=20= separated=20list.=20The=20certificate=20and=20key=20will=20be=20used=20= for=20connections=0A+=20=20=20=20=20=20=20=20=20to=20all=20hosts=20in=20= the=20list.=0A=20=20=20=20=20=20=20=20=20=0A=20=20=20=20=20=20=20= =20=0A=20=20=20=20=20=20=20=0Adiff=20--git=20= a/src/backend/libpq/be-secure-common.c=20= b/src/backend/libpq/be-secure-common.c=0Aindex=20= 78430aad825..251100c27b8=20100644=0A---=20= a/src/backend/libpq/be-secure-common.c=0A+++=20= b/src/backend/libpq/be-secure-common.c=0A@@=20-204,6=20+204,7=20@@=20= parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel)=0A=20=09= parsedline->sourcefile=20=3D=20pstrdup(tok_line->file_name);=0A=20=09= parsedline->linenumber=20=3D=20tok_line->line_num;=0A=20=09= parsedline->rawline=20=3D=20pstrdup(tok_line->raw_line);=0A+=09= parsedline->hostnames=20=3D=20NIL;=0A=20=0A=20=09/*=20Initialize=20= optional=20fields=20*/=0A=20=09parsedline->ssl_passphrase_cmd=20=3D=20= NULL;=0A@@=20-212,8=20+213,21=20@@=20parse_hosts_line(TokenizedAuthLine=20= *tok_line,=20int=20elevel)=0A=20=09/*=20Hostname=20*/=0A=20=09field=20=3D=20= list_head(tok_line->fields);=0A=20=09tokens=20=3D=20lfirst(field);=0A-=09= token=20=3D=20linitial(tokens);=0A-=09parsedline->hostname=20=3D=20= pstrdup(token->string);=0A+=09foreach_ptr(AuthToken,=20hostname,=20= tokens)=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=20= =0A=20=09/*=20SSL=20Certificate=20(Required)=20*/=0A=20=09field=20=3D=20= lnext(tok_line->fields,=20field);=0A@@=20-227,6=20+241,15=20@@=20= parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel)=0A=20=09=09= return=20NULL;=0A=20=09}=0A=20=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=20=09token=20=3D=20linitial(tokens);=0A=20=09= parsedline->ssl_cert=20=3D=20pstrdup(token->string);=0A=20=0A@@=20-242,6=20= +265,15=20@@=20parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20= elevel)=0A=20=09=09return=20NULL;=0A=20=09}=0A=20=09tokens=20=3D=20= lfirst(field);=0A+=09if=20(tokens->length=20>=201)=0A+=09{=0A+=09=09= ereport(elevel,=0A+=09=09=09=09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09errmsg("multiple=20values=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=20tok_line->line_num,=20tok_line->file_name));=0A= +=09=09return=20NULL;=0A+=09}=0A=20=09token=20=3D=20linitial(tokens);=0A=20= =09parsedline->ssl_key=20=3D=20pstrdup(token->string);=0A=20=0A@@=20= -250,6=20+282,15=20@@=20parse_hosts_line(TokenizedAuthLine=20*tok_line,=20= int=20elevel)=0A=20=09if=20(!field)=0A=20=09=09return=20parsedline;=0A=20= =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=20=09token=20=3D=20linitial(tokens);=0A=20=09parsedline->ssl_ca=20=3D= =20pstrdup(token->string);=0A=20=0A@@=20-301,7=20+342,7=20@@=20= parse_hosts_line(TokenizedAuthLine=20*tok_line,=20int=20elevel)=0A=20=20= *=20load_hosts=0A=20=20*=0A=20=20*=20Reads=20and=20parses=20the=20= pg_hosts.conf=20configuration=20file=20and=20passes=20back=20a=20List=0A= -=20*=20of=20HostLine=20elements=20containing=20the=20parsed=20lines,=20= or=20NIL=20in=20case=20of=20an=20empty=0A+=20*=20of=20HostsLine=20= elements=20containing=20the=20parsed=20lines,=20or=20NIL=20in=20case=20= of=20an=20empty=0A=20=20*=20file.=20=20The=20list=20is=20returned=20in=20= the=20hosts_lines=20parameter.=20If=20loading=20the=0A=20=20*=20file=20= was=20successful,=20true=20is=20returned,=20else=20false.=20=20This=20= function=20is=0A=20=20*=20intended=20to=20be=20executed=20within=20a=20= temporary=20memory=20context=20which=20can=20be=0Adiff=20--git=20= a/src/backend/libpq/be-secure-openssl.c=20= b/src/backend/libpq/be-secure-openssl.c=0Aindex=20= a540cc93163..c60bc2209b5=20100644=0A---=20= a/src/backend/libpq/be-secure-openssl.c=0A+++=20= b/src/backend/libpq/be-secure-openssl.c=0A@@=20-53,7=20+53,7=20@@=0A=20=0A= =20typedef=20struct=20HostContext=0A=20{=0A-=09const=20char=20*hostname;=0A= +=09List=09=20=20=20*hostnames;=0A=20=09SSL_CTX=20=20=20=20*context;=0A=20= =09bool=09=09ssl_loaded_verify_locations;=0A=20}=20HostContext;=0A@@=20= -79,7=20+79,7=20@@=20static=20int=09alpn_cb(SSL=20*ssl,=0A=20=09=09=09=09= =09const=20unsigned=20char=20*in,=0A=20=09=09=09=09=09unsigned=20int=20= inlen,=0A=20=09=09=09=09=09void=20*userdata);=0A-static=20int=09= sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg);=0A+static=20= int=09sni_clienthello_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg);=0A=20= static=20bool=20initialize_dh(SSL_CTX=20*context,=20bool=20= isServerStart);=0A=20static=20bool=20initialize_ecdh(SSL_CTX=20*context,=20= bool=20isServerStart);=0A=20static=20const=20char=20= *SSLerrmessageExt(unsigned=20long=20ecode,=20const=20char=20= *replacement);=0A@@=20-130,33=20+130,31=20@@=20be_tls_init(bool=20= isServerStart)=0A=20=09MemoryContext=20host_memcxt;=0A=20=09char=09=20=20= =20*err_msg;=0A=20=09int=09=09=09res;=0A+=09List=09=20=20=20= *new_sni_contexts=20=3D=20NIL;=0A+=09HostContext=20*new_default_context=20= =3D=20NULL;=0A+=09HostContext=20*new_no_sni_context=20=3D=20NULL;=0A+=09= HostContext=20*new_Host_context=20=3D=20NULL;=0A=20=0A=20=09/*=0A-=09=20= *=20If=20there=20are=20contexts=20loaded=20when=20we=20init=20they=20= must=20be=20released.=20This=0A-=09=20*=20should=20only=20be=20possible=20= during=20configuration=20reloads=20and=20not=20when=20the=0A-=09=20*=20= server=20is=20starting=20up.=0A+=09=20*=20If=20ssl_sni=20is=20enabled,=20= attempt=20to=20load=20and=20parse=20TLS=20configuration=20from=0A+=09=20= *=20the=20pg_hosts.conf=20file=20with=20the=20set=20of=20hosts=20= returned=20as=20a=20list.=20=20If=0A+=09=20*=20there=20are=20hosts=20= configured=20they=20take=20precedence=20over=20the=0A+=09=20*=20= postgresql.conf=20config.=20=20Make=20sure=20to=20allocate=20the=20= parsed=20rows=20in=20a=0A+=09=20*=20temporary=20memory=20context=20so=20= that=20we=20can=20avoid=20memory=20leaks=20from=20the=0A+=09=20*=20= parsing=20process.=20=20If=20ssl_sni=20is=20disabled=20then=20set=20the=20= state=20accordingly=0A+=09=20*=20to=20make=20sure=20we=20instead=20parse=20= the=20config=20from=20postgresql.conf.=0A=20=09=20*/=0A-=09if=20= (sni_contexts=20!=3D=20NIL=20||=20default_context=20||=20no_sni_context)=0A= +=09if=20(ssl_sni)=0A=20=09{=0A-=09=09Assert(!isServerStart);=0A-=09=09= free_contexts();=0A-=09=09Host_context=20=3D=20NULL;=0A-=09=09= SSL_context=20=3D=20NULL;=0A+=09=09host_memcxt=20=3D=20= AllocSetContextCreate(CurrentMemoryContext,=0A+=09=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= =09ALLOCSET_SMALL_SIZES);=0A+=09=09oldcxt=20=3D=20= MemoryContextSwitchTo(host_memcxt);=0A+=09=09res=20=3D=20= load_hosts(&pg_hosts,=20&err_msg);=0A+=09=09= MemoryContextSwitchTo(oldcxt);=0A=20=09}=0A-=0A-=09/*=0A-=09=20*=20= Attempt=20to=20load,=20and=20parse,=20TLS=20configuration=20from=20the=20= pg_hosts.conf=0A-=09=20*=20file=20with=20the=20set=20of=20hosts=20= returned=20as=20a=20list.=20=20If=20there=20are=20hosts=0A-=09=20*=20= configured=20there=20they=20take=20precedence=20over=20the=20= postgresql.conf=20config.=0A-=09=20*=20Make=20sure=20to=20allocate=20the=20= parsed=20rows=20in=20a=20temporary=20memory=20context=20so=0A-=09=20*=20= that=20we=20can=20avoid=20memory=20leaks=20from=20the=20parsing=20= process.=0A-=09=20*/=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-=09res=20=3D=20= load_hosts(&pg_hosts,=20&err_msg);=0A-=09MemoryContextSwitchTo(oldcxt);=0A= +=09else=0A+=09=09res=20=3D=20HOSTSFILE_DISABLED;=0A=20=0A=20=09/*=0A=20=09= =20*=20pg_hosts.conf=20is=20not=20required=20to=20contain=20= configuration,=20but=20if=20it=20does=0A@@=20-194,12=20+192,13=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09=09=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A=20=09=09=09=09=09=09= errmsg("unable=20to=20load=20SSL=20config=20from=20\"%s\"=20line=20%i",=0A= =20=09=09=09=09=09=09=09=20=20=20host->sourcefile,=20host->linenumber));=0A= -=09=09=09=09free_contexts();=0A=20=09=09=09=09= MemoryContextDelete(host_memcxt);=0A=20=09=09=09=09return=20-1;=0A=20=09=09= =09}=0A=20=0A-=09=09=09host_context=20=3D=20= palloc0(sizeof(HostContext));=0A+=09=09=09host_context=20=3D=20= palloc(sizeof(HostContext));=0A+=09=09=09host_context->hostnames=20=3D=20= NIL;=0A+=09=09=09host_context->ssl_loaded_verify_locations=20=3D=20= false;=0A=20=09=09=09host_context->context=20=3D=20tmp_context;=0A=20=0A=20= =09=09=09/*=20Set=20flag=20to=20remember=20whether=20CA=20store=20has=20= been=20loaded=20*/=0A@@=20-208,16=20+207,21=20@@=20be_tls_init(bool=20= isServerStart)=0A=20=0A=20=09=09=09/*=0A=20=09=09=09=20*=20The=20= hostname=20in=20the=20context=20is=20NULL=20in=20case=20it=20is=20the=20= default=0A-=09=09=09=20*=20host,=20or=20a=20context=20to=20use=20for=20= non-SNI=20connections.=0A+=09=09=09=20*=20host,=20or=20a=20context=20to=20= use=20for=20non-SNI=20connections.=20We=20already=0A+=09=09=09=20*=20= know=20that=20default=20and=20non-SNI=20configurations=20are=20not=20= mixed=20with=0A+=09=09=09=20*=20hostnames=20so=20in=20those=20cases=20we=20= can=20just=20take=20the=20head=20of=20the=0A+=09=09=09=20*=20list.=0A=20=09= =09=09=20*/=0A-=09=09=09if=20(strcmp(host->hostname,=20"*")=20=3D=3D=20= 0)=0A-=09=09=09=09default_context=20=3D=20host_context;=0A-=09=09=09else=20= if=20(strcmp(host->hostname,=20"no_sni")=20=3D=3D=200)=0A-=09=09=09=09= no_sni_context=20=3D=20host_context;=0A+=09=09=09if=20= (strcmp(linitial(host->hostnames),=20"*")=20=3D=3D=200)=0A+=09=09=09=09= new_default_context=20=3D=20host_context;=0A+=09=09=09else=20if=20= (strcmp(linitial(host->hostnames),=20"/no_sni/")=20=3D=3D=200)=0A+=09=09=09= =09new_no_sni_context=20=3D=20host_context;=0A=20=09=09=09else=0A=20=09=09= =09{=0A-=09=09=09=09host_context->hostname=20=3D=20= pstrdup(host->hostname);=0A-=09=09=09=09sni_contexts=20=3D=20= lappend(sni_contexts,=20host_context);=0A+=09=09=09=09foreach_ptr(char,=20= hostname,=20host->hostnames)=0A+=09=09=09=09=09host_context->hostnames=20= =3D=20lappend(host_context->hostnames,=0A+=09=09=09=09=09=09=09=09=09=09=09= =09=09=20=20pstrdup(hostname));=0A+=09=09=09=09new_sni_contexts=20=3D=20= lappend(new_sni_contexts,=20host_context);=0A=20=09=09=09}=0A=20=0A=20=09= =09=09/*=0A@@=20-225,8=20+229,8=20@@=20be_tls_init(bool=20isServerStart)=0A= =20=09=09=09=20*=20until=20the=20SNI=20callback=20switches=20over=20to=20= the=20expected=20one,=20for=0A=20=09=09=09=20*=20now=20just=20set=20it=20= to=20the=20first=20one=20we=20see.=0A=20=09=09=09=20*/=0A-=09=09=09if=20= (!Host_context)=0A-=09=09=09=09Host_context=20=3D=20host_context;=0A+=09=09= =09if=20(!new_Host_context)=0A+=09=09=09=09new_Host_context=20=3D=20= host_context;=0A=20=09=09}=0A=20=0A=20=09=09= MemoryContextDelete(host_memcxt);=0A@@=20-236,7=20+240,7=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09=20*=20If=20the=20= pg_hosts.conf=20file=20doesn't=20exist,=20or=20is=20empty,=20then=20load=20= the=0A=20=09=20*=20config=20from=20postgresql.conf.=0A=20=09=20*/=0A-=09= else=20if=20(res=20=3D=3D=20HOSTSFILE_EMPTY=20||=20res=20=3D=3D=20= HOSTSFILE_MISSING)=0A+=09else=20if=20(res=20=3D=3D=20HOSTSFILE_DISABLED=20= ||=20res=20=3D=3D=20HOSTSFILE_EMPTY=20||=20res=20=3D=3D=20= HOSTSFILE_MISSING)=0A=20=09{=0A=20=09=09HostsLine=09pgconf;=0A=20=09=09= SSL_CTX=20=20=20=20*tmp_context=20=3D=20NULL;=0A@@=20-264,17=20+268,18=20= @@=20be_tls_init(bool=20isServerStart)=0A=20=09=09=20*=20can=20also=20= set=20it=20as=20the=20Host_context=20since=20it=20will=20be=20used=20for=20= all=0A=20=09=09=20*=20connections.=0A=20=09=09=20*/=0A-=09=09= default_context=20=3D=20palloc0(sizeof(HostContext));=0A-=09=09= default_context->context=20=3D=20tmp_context;=0A-=09=09Host_context=20=3D=20= default_context;=0A+=09=09new_default_context=20=3D=20= palloc(sizeof(HostContext));=0A+=09=09new_default_context->context=20=3D=20= tmp_context;=0A+=09=09new_default_context->ssl_loaded_verify_locations=20= =3D=20false;=0A+=09=09new_Host_context=20=3D=20new_default_context;=0A=20= =0A=20=09=09/*=20Set=20flag=20to=20remember=20whether=20CA=20store=20has=20= been=20loaded=20*/=0A=20=09=09if=20(ssl_ca_file[0])=0A-=09=09=09= default_context->ssl_loaded_verify_locations=20=3D=20true;=0A+=09=09=09= new_default_context->ssl_loaded_verify_locations=20=3D=20true;=0A=20=09}=0A= =20=0A=20=09/*=20Make=20sure=20we=20have=20at=20least=20one=20= certificate=20loaded=20*/=0A-=09if=20(sni_contexts=20=3D=3D=20NIL=20&&=20= !default_context=20&&=20!no_sni_context)=0A+=09if=20(new_sni_contexts=20= =3D=3D=20NIL=20&&=20!new_default_context=20&&=20!new_no_sni_context)=0A=20= =09{=0A=20=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A=20=09=09=09= =09errcode(ERRCODE_CONFIG_FILE_ERROR),=0A@@=20-282,6=20+287,24=20@@=20= be_tls_init(bool=20isServerStart)=0A=20=09=09return=20-1;=0A=20=09}=0A=20= =0A+=09/*=0A+=09=20*=20If=20there=20are=20contexts=20loaded=20when=20we=20= init=20they=20must=20be=20released.=20This=0A+=09=20*=20should=20only=20= be=20possible=20during=20configuration=20reloads=20and=20not=20when=20= the=0A+=09=20*=20server=20is=20starting=20up.=0A+=09=20*/=0A+=09if=20= (sni_contexts=20!=3D=20NIL=20||=20default_context=20||=20no_sni_context)=0A= +=09{=0A+=09=09Assert(!isServerStart);=0A+=09=09free_contexts();=0A+=09=09= Host_context=20=3D=20NULL;=0A+=09=09SSL_context=20=3D=20NULL;=0A+=09}=0A= +=0A+=09sni_contexts=20=3D=20new_sni_contexts;=0A+=09no_sni_context=20=3D=20= new_no_sni_context;=0A+=09default_context=20=3D=20new_default_context;=0A= +=09Host_context=20=3D=20new_Host_context;=0A+=0A=20=09SSL_context=20=3D=20= Host_context->context;=0A=20=0A=20=09return=200;=0A@@=20-323,12=20+346,6=20= @@=20ssl_init_context(bool=20isServerStart,=20HostsLine=20*host_line)=0A=20= =09=20*/=0A=20=09SSL_CTX_set_mode(context,=20= SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A=20=0A-=09/*=0A-=09=20*=20= Install=20SNI=20TLS=20extension=20callback=20in=20order=20to=20validate=20= hostnames=20in=0A-=09=20*=20case=20we=20have=20at=20least=20one=20= context=20configured=20with=20a=20host=20name.=0A-=09=20*/=0A-=09= SSL_CTX_set_tlsext_servername_callback(context,=20sni_servername_cb);=0A= -=0A=20=09/*=0A=20=09=20*=20Call=20init=20hook=20(usually=20to=20set=20= password=20callback)=0A=20=09=20*/=0A@@=20-544,16=20+561,23=20@@=20= ssl_init_context(bool=20isServerStart,=20HostsLine=20*host_line)=0A=20=09= =09=20*=20free=20it=20when=20no=20longer=20needed.=0A=20=09=09=20*/=0A=20= =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,=20but=20= don't=20fail=20if=20it's=20not=0A+=09=09=20*=20presented.=20We=20might=20= fail=20such=20connections=20later,=20depending=20on=20what=0A+=09=09=20*=20= we=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|=20SSL_VERIFY_CLIENT_ONCE),=0A+=09=09=09=09=09=09=20=20= =20verify_cb);=0A=20=09}=0A=20=0A=20=09/*=0A-=09=20*=20Always=20ask=20= for=20SSL=20client=20cert,=20but=20don't=20fail=20if=20it's=20not=20= presented.=0A-=09=20*=20We=20might=20fail=20such=20connections=20later,=20= depending=20on=20what=20we=20find=20in=0A-=09=20*=20pg_hba.conf.=0A+=09=20= *=20Install=20SNI=20TLS=20extension=20callback=20in=20order=20to=20= validate=20hostnames=20in=0A+=09=20*=20case=20ssl_sni=20has=20been=20= enabled.=0A=20=09=20*/=0A-=09SSL_CTX_set_verify(context,=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+=09if=20(ssl_sni)=0A+=09=09= SSL_CTX_set_client_hello_cb(context,=20sni_clienthello_cb,=20NULL);=0A=20= =0A=20=09/*----------=0A=20=09=20*=20Load=20the=20Certificate=20= Revocation=20List=20(CRL).=0A@@=20-645,6=20+669,10=20@@=20= be_tls_open_server(Port=20*port)=0A=20=09/*=20enable=20ALPN=20*/=0A=20=09= SSL_CTX_set_alpn_select_cb(SSL_context,=20alpn_cb,=20port);=0A=20=0A+=09= SSL_CTX_set_verify(SSL_context,=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=20= verify_cb);=0A+=0A=20=09if=20(!(port->ssl=20=3D=20SSL_new(SSL_context)))=0A= =20=09{=0A=20=09=09ereport(COMMERROR,=0A@@=20-1571,61=20+1599,83=20@@=20= alpn_cb(SSL=20*ssl,=0A=20}=0A=20=0A=20/*=0A-=20*=20sni_servername_cb=0A+=20= *=20sni_clienthello_cb=0A=20=20*=0A-=20*=20Callback=20executed=20by=20= OpenSSL=20during=20handshake=20in=20case=20the=20server=20has=20been=0A-=20= *=20configured=20to=20validate=20hostnames.=20=20Returning=20= SSL_TLSEXT_ERR_ALERT_FATAL=20to=0A-=20*=20OpenSSL=20will=20immediately=20= terminate=20the=20handshake.=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=20*/=0A=20static=20int=0A= -sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg)=0A= +sni_clienthello_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg)=0A=20{=0A=20=09= const=20char=20*tlsext_hostname;=0A+=09const=20unsigned=20char=20= *tlsext;=0A+=09size_t=09=09left,=0A+=09=09=09=09len;=0A=20=09HostContext=20= *install_context=20=3D=20NULL;=0A=20=0A-=09tlsext_hostname=20=3D=20= SSL_get_servername(ssl,=20TLSEXT_NAMETYPE_host_name);=0A+=09if=20= (!ssl_sni)=0A+=09=09return=20SSL_CLIENT_HELLO_SUCCESS;=0A=20=0A-=09/*=0A= -=09=20*=20If=20there=20is=20no=20hostname=20set=20in=20the=20TLS=20= extension,=20we=20have=20two=20options:=0A-=09=20*=20i)=20there=20is=20a=20= HostContext=20defined=20for=20non-SNI=20connections,=20in=20that=20case=0A= -=09=20*=20we=20switch=20to=20that;=20ii)=20there=20is=20no=20non-SNI=20= config=20and=20we=20error=20out=20as=0A-=09=20*=20there=20is=20no=20= context=20to=20switch=20to.=0A-=09=20*/=0A-=09if=20(!tlsext_hostname)=0A= +=09if=20(SSL_client_hello_get0_ext(ssl,=20TLSEXT_TYPE_server_name,=20= &tlsext,=20&left))=0A=20=09{=0A-=09=09if=20(no_sni_context)=0A-=09=09=09= install_context=20=3D=20no_sni_context;=0A-=09=09else=20if=20= (default_context)=0A-=09=09=09install_context=20=3D=20default_context;=0A= -=09=09else=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!=3D=20left)=0A=20=09=09{=0A-=09= =09=09/*=0A-=09=09=09=20*=20The=20error=20message=20for=20a=20missing=20= server_name=20should,=20according=0A-=09=09=09=20*=20to=20RFC=208446,=20= be=20missing_extension.=20This=20isn't=20entirely=20ideal=0A-=09=09=09=20= *=20since=20the=20user=20won't=20be=20able=20to=20tell=20which=20= extension=20the=20server=0A-=09=09=09=20*=20considered=20missing.=20=20= Sending=20unrecognized_name=20would=20be=20a=20more=0A-=09=09=09=20*=20= helpful=20error,=20but=20for=20now=20we=20stick=20to=20the=20RFC.=0A-=09=09= =09=20*/=0A=20=09=09=09*al=20=3D=20SSL_AD_MISSING_EXTENSION;=0A+=09=09=09= return=200;=0A+=09=09}=0A=20=0A-=09=09=09ereport(COMMERROR,=0A-=09=09=09=09= =09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A-=09=09=09=09=09=20errmsg("no=20= hostname=20provided=20in=20callback")));=0A-=09=09=09return=20= SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09=09left=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=20SSL_AD_MISSING_EXTENSION;=0A+=09=09=09= return=200;=0A=20=09=09}=0A-=09}=0A-=09else=0A-=09{=0A+=0A+=09=09left--;=0A= +=0A+=09=09/*=0A+=09=09=20*=20Now=20we=20can=20finally=20pull=20out=20= the=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=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>=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+=09=09= left=20=3D=20len;=0A+=09=09tlsext_hostname=20=3D=20(const=20char=20*)=20= tlsext;=0A+=0A=20=09=09/*=0A=20=09=09=20*=20We=20have=20a=20requested=20= hostname=20from=20the=20client,=20match=20against=20all=0A=20=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=20=09=09=20*/=0A= =20=09=09foreach_ptr(HostContext,=20host,=20sni_contexts)=0A=20=09=09{=0A= -=09=09=09if=20(strcmp(host->hostname,=20tlsext_hostname)=20=3D=3D=200)=0A= +=09=09=09foreach_ptr(char,=20hostname,=20host->hostnames)=0A=20=09=09=09= {=0A-=09=09=09=09install_context=20=3D=20host;=0A-=09=09=09=09break;=0A+=09= =09=09=09if=20(pg_strncasecmp(hostname,=20tlsext_hostname,=20len)=20=3D=3D= =200)=0A+=09=09=09=09{=0A+=09=09=09=09=09install_context=20=3D=20host;=0A= +=09=09=09=09=09goto=20found;=0A+=09=09=09=09}=0A=20=09=09=09}=0A=20=09=09= }=0A=20=0A@@=20-1636,14=20+1686,44=20@@=20sni_servername_cb(SSL=20*ssl,=20= int=20*al,=20void=20*arg)=0A=20=09=09if=20(!install_context=20&&=20= default_context)=0A=20=09=09=09install_context=20=3D=20default_context;=0A= =20=09}=0A+=09else=0A+=09{=0A+=09=09if=20(no_sni_context)=0A+=09=09=09= install_context=20=3D=20no_sni_context;=0A+=09=09else=20if=20= (default_context)=0A+=09=09=09install_context=20=3D=20default_context;=0A= +=09=09else=0A+=09=09{=0A+=09=09=09/*=0A+=09=09=09=20*=20The=20error=20= message=20for=20a=20missing=20server_name=20should,=20according=0A+=09=09= =09=20*=20to=20RFC=208446,=20be=20missing_extension.=20This=20isn't=20= entirely=20ideal=0A+=09=09=09=20*=20since=20the=20user=20won't=20be=20= able=20to=20tell=20which=20extension=20the=20server=0A+=09=09=09=20*=20= considered=20missing.=20=20Sending=20unrecognized_name=20would=20be=20a=20= more=0A+=09=09=09=20*=20helpful=20error,=20but=20for=20now=20we=20stick=20= to=20the=20RFC.=0A+=09=09=09=20*/=0A+=09=09=09*al=20=3D=20= SSL_AD_MISSING_EXTENSION;=0A+=0A+=09=09=09ereport(COMMERROR,=0A+=09=09=09= =09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20= errmsg("no=20hostname=20provided=20in=20callback,=20and=20no=20fallback=20= configured")));=0A+=09=09=09return=20SSL_CLIENT_HELLO_ERROR;=0A+=09=09}=0A= +=09}=0A=20=0A=20=09/*=0A=20=09=20*=20If=20we=20reach=20here=20without=20= a=20context=20chosen=20as=20the=20session=20context=20then=0A=20=09=20*=20= fail=20the=20handshake=20and=20terminate=20the=20connection.=0A=20=09=20= */=0A=20=09if=20(install_context=20=3D=3D=20NULL)=0A-=09=09return=20= SSL_TLSEXT_ERR_ALERT_FATAL;=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=09return=20= SSL_CLIENT_HELLO_ERROR;=0A+=09}=0A=20=0A+found:=0A=20=09Host_context=20=3D= =20install_context;=0A=20=09SSL_context=20=3D=20= install_context->context;=0A=20=09if=20(SSL_set_SSL_CTX(ssl,=20= SSL_context)=20=3D=3D=20NULL)=0A@@=20-1651,10=20+1731,14=20@@=20= sni_servername_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg)=0A=20=09=09= ereport(COMMERROR,=0A=20=09=09=09=09errcode(ERRCODE_PROTOCOL_VIOLATION),=0A= =20=09=09=09=09errmsg("failed=20to=20switch=20to=20SSL=20context=20for=20= host"));=0A-=09=09return=20SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09=09return=20= SSL_CLIENT_HELLO_ERROR;=0A=20=09}=0A=20=0A-=09return=20= SSL_TLSEXT_ERR_OK;=0A+=09/*=20Copy=20over=20context=20settings=20*/=0A+=09= SSL_clear_options(ssl,=200xFFFFFFFFL);=0A+=09SSL_set_options(ssl,=20= SSL_CTX_get_options(SSL_context));=0A+=0A+=09return=20= SSL_CLIENT_HELLO_SUCCESS;=0A=20}=0A=20=0A=20/*=0A@@=20-2106,8=20+2190,12=20= @@=20free_contexts(void)=0A=20=09{=0A=20=09=09foreach_ptr(HostContext,=20= host,=20sni_contexts)=0A=20=09=09{=0A-=09=09=09if=20(host->hostname)=0A-=09= =09=09=09pfree(unconstify(char=20*,=20host->hostname));=0A+=09=09=09if=20= (host->hostnames=20!=3D=20NIL)=0A+=09=09=09{=0A+=09=09=09=09= foreach_ptr(char,=20hostname,=20host->hostnames)=0A+=09=09=09=09=09= pfree(hostname);=0A+=09=09=09=09list_free(host->hostnames);=0A+=09=09=09= }=0A=20=09=09=09SSL_CTX_free(host->context);=0A=20=09=09}=0A=20=0A@@=20= -2116,7=20+2204,7=20@@=20free_contexts(void)=0A=20=09}=0A=20=0A=20=09/*=0A= -=09=20*=20The=20hostname=20need=20not=20be=20freed=20for=20the=20no_sni=20= and=20default=20contexts=0A+=09=20*=20The=20hostname=20list=20need=20not=20= be=20freed=20for=20the=20no_sni=20and=20default=20contexts=0A=20=09=20*=20= since=20they=20by=20definition=20are=20not=20connected=20to=20a=20= hostname=20and=20thus=20have=0A=20=09=20*=20none=20allocated.=0A=20=09=20= */=0Adiff=20--git=20a/src/backend/libpq/be-secure.c=20= b/src/backend/libpq/be-secure.c=0Aindex=206dcb673843a..542aaaa2b26=20= 100644=0A---=20a/src/backend/libpq/be-secure.c=0A+++=20= b/src/backend/libpq/be-secure.c=0A@@=20-56,6=20+56,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/utils/misc/guc_parameters.dat=20= b/src/backend/utils/misc/guc_parameters.dat=0Aindex=20= b95c373fb41..2765e6bbf4a=20100644=0A---=20= a/src/backend/utils/misc/guc_parameters.dat=0A+++=20= b/src/backend/utils/misc/guc_parameters.dat=0A@@=20-2758,6=20+2758,13=20= @@=0A=20=20=20max=20=3D>=20'0',=0A=20},=0A=20=0A+{=20name=20=3D>=20= 'ssl_sni',=20type=20=3D>=20'bool',=20context=20=3D>=20'PGC_SIGHUP',=20= group=20=3D>=20'CONN_AUTH_SSL',=0A+=20=20short_desc=20=3D>=20'Sets=20= whether=20to=20interpret=20SNI=20extensions=20in=20SSL=20connections.',=0A= +=20=20flags=20=3D>=20'GUC_SUPERUSER_ONLY',=0A+=20=20variable=20=3D>=20= 'ssl_sni',=0A+=20=20boot_val=20=3D>=20'false',=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/postgresql.conf.sample=20= b/src/backend/utils/misc/postgresql.conf.sample=0Aindex=20= 1f360110564..404621df9bf=20100644=0A---=20= a/src/backend/utils/misc/postgresql.conf.sample=0A+++=20= b/src/backend/utils/misc/postgresql.conf.sample=0A@@=20-123,6=20+123,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=20off=0A=20=0A=20=0A=20= #-------------------------------------------------------------------------= -----=0Adiff=20--git=20a/src/include/libpq/hba.h=20= b/src/include/libpq/hba.h=0Aindex=2038713381255..9d35ea1ba63=20100644=0A= ---=20a/src/include/libpq/hba.h=0A+++=20b/src/include/libpq/hba.h=0A@@=20= -159,13=20+159,12=20@@=20typedef=20struct=20HostsLine=0A=20=09char=09=20=20= =20*rawline;=0A=20=0A=20=09/*=20Required=20fields=20*/=0A-=09bool=09=09= default_host;=0A-=09char=09=20=20=20*hostname;=0A+=09List=09=20=20=20= *hostnames;=0A=20=09char=09=20=20=20*ssl_key;=0A=20=09char=09=20=20=20= *ssl_cert;=0A-=09char=09=20=20=20*ssl_ca;=0A=20=0A=20=09/*=20Optional=20= fields=20*/=0A+=09char=09=20=20=20*ssl_ca;=0A=20=09char=09=20=20=20= *ssl_passphrase_cmd;=0A=20=09bool=09=09ssl_passphrase_reload;=0A=20}=20= HostsLine;=0A@@=20-176,6=20+175,7=20@@=20typedef=20enum=20HostsFileLoad=0A= =20=09HOSTSFILE_LOAD_FAILED,=0A=20=09HOSTSFILE_EMPTY,=0A=20=09= HOSTSFILE_MISSING,=0A+=09HOSTSFILE_DISABLED,=0A=20}=20= HostsFileLoadResult;=0A=20=0A=20/*=0Adiff=20--git=20= a/src/include/libpq/libpq.h=20b/src/include/libpq/libpq.h=0Aindex=20= 3d734266172..47009ead442=20100644=0A---=20a/src/include/libpq/libpq.h=0A= +++=20b/src/include/libpq/libpq.h=0A@@=20-110,6=20+110,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;=0Adiff=20--git=20a/src/test/ssl/t/001_ssltests.pl=20= b/src/test/ssl/t/001_ssltests.pl=0Aindex=20c0104f6aa81..6a433623d1f=20= 100644=0A---=20a/src/test/ssl/t/001_ssltests.pl=0A+++=20= b/src/test/ssl/t/001_ssltests.pl=0A@@=20-201,7=20+201,7=20@@=20$result=20= =3D=20$node->restart(fail_ok=20=3D>=201);=0A=20is($result,=200,=20= 'restart=20fails=20with=20incorrect=20groups');=0A=20= ok($node->log_contains(qr/no=20SSL=20error=20reported/)=20=3D=3D=200,=0A=20= =09'error=20message=20translated');=0A= -$node->append_conf('ssl_config.conf',=20qq{ssl_groups=3D'prime256v1'});=0A= +$node->append_conf('sslconfig.conf',=20qq{ssl_groups=3D'prime256v1'});=0A= =20$result=20=3D=20$node->restart(fail_ok=20=3D>=201);=0A=20=0A=20###=20= Run=20client-side=20tests.=0A@@=20-1004,4=20+1004,43=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+#=20Test=20client=20CAs=0A+my=20$connstr=20=3D=0A+=20=20= "user=3Dssltestuser=20dbname=3Dcertdb=20hostaddr=3D$SERVERHOSTADDR=20= sslmode=3Drequire=20sslsni=3D1";=0A+=0A+switch_server_cert($node,=20= certfile=20=3D>=20'server-cn-only',=20cafile=20=3D>=20'');=0A+#=20= example.org=20is=20unconfigured=20and=20should=20fail.=0A= +$node->connect_fails(=0A+=09"$connstr=20host=3Dexample.org=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt"=0A+=09=20=20.=20= sslkey('client.key'),=0A+=09"host:=20'example.org',=20ca:=20'':=20= connect=20with=20sslcert,=20no=20client=20CA=20configured",=0A+=09= expected_stderr=20=3D>=20qr/client=20certificates=20can=20only=20be=20= checked=20if=20a=20root=20certificate=20store=20is=20available/);=0A+=0A= +#=20example.com=20uses=20the=20client=20CA.=0A= +switch_server_cert($node,=20certfile=20=3D>=20'server-cn-only',=20= cafile=20=3D>=20'root+client_ca');=0A+#=20example.com=20is=20configured=20= and=20should=20require=20a=20valid=20client=20cert.=0A= +$node->connect_fails(=0A+=09"$connstr=20host=3Dexample.com=20= sslcertmode=3Ddisable",=0A+=09"host:=20'example.com',=20ca:=20= 'root+client_ca.crt':=20connect=20fails=20if=20no=20client=20certificate=20= sent",=0A+=09expected_stderr=20=3D>=20qr/connection=20requires=20a=20= valid=20client=20certificate/);=0A+$node->connect_ok(=0A+=09"$connstr=20= host=3Dexample.com=20sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20= "=20.=20sslkey('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=20uses=20the=20server=20CA=20(which=20= is=20wrong).=0A+switch_server_cert($node,=20certfile=20=3D>=20= 'server-cn-only',=20cafile=20=3D>=20'root+server_ca');=0A+#=20= example.net=20is=20configured=20and=20should=20require=20a=20client=20= cert,=20but=20will=0A+#=20always=20fail=20verification.=0A= +$node->connect_fails(=0A+=09"$connstr=20host=3Dexample.net=20= sslcertmode=3Ddisable",=0A+=09"host:=20'example.net',=20ca:=20= 'root+server_ca.crt':=20connect=20fails=20if=20no=20client=20certificate=20= sent",=0A+=09expected_stderr=20=3D>=20qr/connection=20requires=20a=20= valid=20client=20certificate/);=0A+=0A+$node->connect_fails(=0A+=09= "$connstr=20host=3Dexample.net=20sslcertmode=3Drequire=20= sslcert=3Dssl/client.crt=20"=0A+=09=20=20.=20sslkey('client.key'),=0A+=09= "host:=20'example.net',=20ca:=20'root+server_ca.crt':=20connect=20with=20= sslcert,=20client=20certificate=20sent",=0A+=09expected_stderr=20=3D>=20= qr/unknown=20ca/);=0A+=0A=20done_testing();=0Adiff=20--git=20= a/src/test/ssl/t/004_sni.pl=20b/src/test/ssl/t/004_sni.pl=0Aindex=20= 2dd70e7afee..348255e1602=20100644=0A---=20a/src/test/ssl/t/004_sni.pl=0A= +++=20b/src/test/ssl/t/004_sni.pl=0A@@=20-66,8=20+66,19=20@@=20= $node->connect_fails(=0A=20=09"pg.conf:=20connect=20fails=20without=20= intermediate=20for=20sslmode=3Dverify-ca",=0A=20=09expected_stderr=20=3D>=20= qr/certificate=20verify=20failed/);=0A=20=0A-#=20Remove=20pg_hosts.conf=20= and=20reload=20to=20make=20sure=20a=20missing=20file=20is=20treated=20= like=0A-#=20an=20empty=20file.=0A+#=20Add=20an=20entry=20in=20= pg_hosts.conf=20with=20no=20default,=20and=20reload.=20Since=20ssl_sni=20= is=0A+#=20still=20'off'=20we=20should=20still=20be=20able=20to=20connect=20= using=20the=20certificates=20in=0A+#=20postgresql.conf=0A= +$node->append_conf('pg_hosts.conf',=0A+=09"example.org=20= server-cn-only.crt=20server-cn-only.key");=0A+$node->reload;=0A= +$node->connect_ok(=0A+=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt= =20sslmode=3Drequire",=0A+=09"pg.conf:=20connect=20with=20correct=20= server=20CA=20cert=20file=20sslmode=3Drequire");=0A+=0A+#=20Turn=20on=20= SNI=20support=20and=20remove=20pg_hosts.conf=20and=20reload=20to=20make=20= sure=20a=0A+#=20missing=20file=20is=20treated=20like=20an=20empty=20= file.=0A+$node->append_conf('postgresql.conf',=20'ssl_sni=20=3D=20on');=0A= =20ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A=20= $node->reload;=0A=20=0A@@=20-107,6=20+118,10=20@@=20$node->connect_ok(=0A= =20=09"$connstr=20host=3Dexample.org=20sslrootcert=3Dssl/root_ca.crt=20= sslmode=3Dverify-ca",=0A=20=09"pg_hosts.conf:=20connect=20to=20= example.org=20and=20verify=20server=20CA");=0A=20=0A+$node->connect_ok(=0A= +=09"$connstr=20host=3DExample.ORG=20sslrootcert=3Dssl/root_ca.crt=20= sslmode=3Dverify-ca",=0A+=09"pg_hosts.conf:=20connect=20to=20Example.ORG=20= and=20verify=20server=20CA");=0A+=0A=20$node->connect_fails(=0A=20=09= "$connstr=20host=3Dexample.org=20sslrootcert=3Dinvalid=20= sslmode=3Dverify-ca",=0A=20=09"pg_hosts.conf:=20connect=20to=20= example.org=20but=20without=20server=20root=20cert,=20= sslmode=3Dverify-ca",=0A@@=20-121,19=20+136,50=20@@=20$node->connect_ok(=0A= =20=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20= sslmode=3Drequire",=0A=20=09"pg_hosts.conf:=20connect=20to=20default=20= with=20sslmode=3Drequire");=0A=20=0A+#=20Use=20multiple=20hostnames=20= for=20a=20single=20configuration=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+#=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=20#=20Modify=20pg_hosts.conf=20= to=20no=20longer=20have=20the=20default=20host=20entry.=0A=20= ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A=20= $node->append_conf('pg_hosts.conf',=0A=20=09"example.org=20= server-cn-only+server_ca.crt=20server-cn-only.key=20root_ca.crt"=0A=20);=0A= -$node->reload;=0A+$node->restart;=0A=20=0A=20#=20Connecting=20without=20= a=20hostname=20as=20well=20as=20with=20a=20hostname=20which=20isn't=20in=20= the=0A=20#=20pg_hosts=20configuration=20should=20fail.=0A=20= $node->connect_fails(=0A=20=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20sslsni=3D0",=0A= =20=09"pg_hosts.conf:=20connect=20to=20default=20with=20= sslmode=3Drequire",=0A-=09expected_stderr=20=3D>=20qr/missing=20= extension/);=0A+=09expected_stderr=20=3D>=20qr/handshake=20failure/);=0A=20= $node->connect_fails(=0A=20=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dexample.com",=0A=20=09"pg_hosts.conf:=20connect=20to=20default=20= with=20sslmode=3Drequire",=0A@@=20-145,7=20+191,7=20@@=20= ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A=20= $node->append_conf('pg_hosts.conf',=0A=20=09'localhost=20= server-cn-only.crt=20server-password.key=20root+client_ca.crt=20"echo=20= wrongpassword"=20on'=0A=20);=0A-my=20$result=20=3D=20= $node->restart(fail_ok=20=3D>=201);=0A+$result=20=3D=20= $node->restart(fail_ok=20=3D>=201);=0A=20is($result,=200,=0A=20=09= 'pg_hosts.conf:=20restart=20fails=20with=20password-protected=20key=20= when=20using=20the=20wrong=20passphrase=20command'=0A=20);=0A@@=20= -179,16=20+225,21=20@@=20$node->connect_ok(=0A=20);=0A=20=0A=20#=20Test=20= reloading=20a=20passphrase=20protected=20key=20without=20reloading=20= support=20in=20the=0A-#=20passphrase=20hook.=20Connecting=20after=20= restart=20should=20succeed=20but=20not=20after=20the=0A-#=20following=20= reload.=0A+#=20passphrase=20hook.=20Restarting=20should=20not=20give=20= any=20errors=20in=20the=20log,=20but=20the=0A+#=20subsequent=20reload=20= should=20fail=20with=20an=20error=20regarding=20reloading.=0A=20= ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A=20= $node->append_conf('pg_hosts.conf',=0A=20=09'localhost=20= server-cn-only.crt=20server-password.key=20root+client_ca.crt=20"echo=20= secret1"=20off'=0A=20);=0A+my=20$node_loglocation=20=3D=20-s=20= $node->logfile;=0A=20$result=20=3D=20$node->restart(fail_ok=20=3D>=201);=0A= =20is($result,=201,=0A=20=09'pg_hosts.conf:=20restart=20succeeds=20with=20= password-protected=20key=20when=20using=20the=20correct=20passphrase=20= command'=0A=20);=0A+my=20$log=20=3D=20= PostgreSQL::Test::Utils::slurp_file($node->logfile,=20= $node_loglocation);=0A+unlike($log,=20qr/cannot=20be=20reloaded=20= because=20it=20requires=20a=20passphrase/,=0A+=09=20'log=20reload=20= failure=20due=20to=20passphrase=20command=20reloading');=0A+=0A=20SKIP:=0A= =20{=0A=20=09#=20Passphrase=20reloads=20must=20be=20enabled=20on=20= Windows=20to=20succeed=20even=20without=20a=0A@@=20-199,19=20+250,26=20= @@=20SKIP:=0A=20=09=09"$connstr=20sslrootcert=3Dssl/root+server_ca.crt=20= sslmode=3Drequire=20host=3Dlocalhost",=0A=20=09=09"pg_hosts.conf:=20= connect=20with=20correct=20server=20CA=20cert=20file=20sslmode=3Drequire"=0A= =20=09);=0A+=09#=20Reloading=20should=20fail=20since=20the=20passphrase=20= cannot=20be=20reloaded,=20with=20an=0A+=09#=20error=20recorded=20in=20= the=20log.=20=20Since=20we=20keep=20existing=20contexts=20around=20it=0A= +=09#=20should=20still=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=20sslrootcert=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=20= PostgreSQL::Test::Utils::slurp_file($node->logfile,=20= $node_loglocation);=0A+=09like($log,=0A+=09=09=20qr/cannot=20be=20= reloaded=20because=20it=20requires=20a=20passphrase/,=0A+=09=09=20'log=20= reload=20failure=20due=20to=20passphrase=20command=20reloading');=0A=20}=0A= =20=0A-$node->reload;=0A-$node->connect_fails(=0A-=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20= host=3Dlocalhost",=0A-=09"pg_hosts.conf:=20connect=20fails=20since=20the=20= passphrase=20protected=20key=20cannot=20be=20reloaded"=0A-);=0A-=0A=20#=20= Configure=20with=20only=20non-SNI=20connections=20allowed=0A=20= ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A=20= $node->append_conf('pg_hosts.conf',=0A-=09"no_sni=20server-cn-only.crt=20= server-cn-only.key");=0A-$node->reload;=0A+=09"/no_sni/=20= server-cn-only.crt=20server-cn-only.key");=0A+$node->restart;=0A=20=0A=20= $node->connect_ok(=0A=20=09"$connstr=20= sslrootcert=3Dssl/root+server_ca.crt=20sslmode=3Drequire=20sslsni=3D0",=0A= @@=20-222,68=20+280,54=20@@=20$node->connect_fails(=0A=20=09= "pg_hosts.conf:=20only=20non-SNI=20connections=20allowed,=20connecting=20= with=20SNI",=0A=20=09expected_stderr=20=3D>=20qr/unrecognized=20name/);=0A= =20=0A-#=20Test=20client=20CAs=20by=20connecting=20to=20hosts=20in=20= pg_hosts.conf=20while=20at=20the=20same=0A-#=20time=20swapping=20out=20= default=20contexts=20containing=20different=20CA=20configurations.=0A+#=20= Test=20client=20CAs=0A=20=0A=20#=20pg_hosts=20configuration=0A=20= ok(unlink($node->data_dir=20.=20'/pg_hosts.conf'));=0A=20#=20example.org=20= has=20an=20unconfigured=20CA.=0A=20$node->append_conf('pg_hosts.conf',=0A= -=09'example.org=20server-cn-only.crt=20server-cn-only.key=20""');=0A+=09= 'example.org=20server-cn-only.crt=20server-cn-only.key');=0A=20#=20= example.com=20uses=20the=20client=20CA.=0A=20= $node->append_conf('pg_hosts.conf',=0A=20=09'example.com=20= server-cn-only.crt=20server-cn-only.key=20root+client_ca.crt');=0A=20#=20= example.net=20uses=20the=20server=20CA=20(which=20is=20wrong).=0A=20= $node->append_conf('pg_hosts.conf',=0A=20=09'example.net=20= server-cn-only.crt=20server-cn-only.key=20root+server_ca.crt');=0A= -$node->reload;=0A+$node->restart;=0A=20=0A=20$connstr=20=3D=0A=20=20=20= "user=3Dssltestuser=20dbname=3Dcertdb=20hostaddr=3D$SERVERHOSTADDR=20= sslmode=3Drequire=20sslsni=3D1";=0A=20=0A-foreach=20my=20$default_ca=20= ("",=20"root+client_ca",=20"root+server_ca")=0A-{=0A-=09#=20The=20= default=20CA=20should,=20not=20matter=20for=20the=20purposes=20of=20= these=20tests,=20since=0A-=09#=20we=20connect=20to=20the=20other=20hosts=20= explicitly.=20Test=20with=20various=20default=20CA=0A-=09#=20settings=20= to=20ensure=20it's=20isolated=20from=20the=20actual=20connections.=0A-=09= $ssl_server->switch_server_cert(=0A-=09=09$node,=0A-=09=09certfile=20=3D>=20= 'server-cn-only',=0A-=09=09cafile=20=3D>=20$default_ca);=0A-=0A-=09#=20= example.org=20is=20unconfigured=20and=20should=20fail.=0A-=09= $node->connect_fails(=0A-=09=09"$connstr=20host=3Dexample.org=20= sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20"=0A-=09=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A-=09=09"host:=20'example.org',=20= ca:=20'$default_ca':=20connect=20with=20sslcert,=20no=20client=20CA=20= configured",=0A-=09=09expected_stderr=20=3D>=20qr/unknown=20ca/);=0A-=0A= -=09#=20example.com=20is=20configured=20and=20should=20require=20a=20= valid=20client=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'$default_ca':=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= +#=20example.org=20is=20unconfigured=20and=20should=20fail.=0A= +$node->connect_fails(=0A+=09"$connstr=20host=3Dexample.org=20= sslcertmode=3Drequire=20sslcert=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>=20qr/client=20certificates=20can=20only=20be=20= checked=20if=20a=20root=20certificate=20store=20is=20available/);=0A=20=0A= -=09$node->connect_ok(=0A-=09=09"$connstr=20host=3Dexample.com=20= sslrootcert=3Dssl/root+server_ca.crt=20sslcertmode=3Drequire=20= sslcert=3Dssl/client.crt=20"=0A-=09=09=20=20.=20= $ssl_server->sslkey('client.key'),=0A-=09=09"host:=20'example.com',=20= ca:=20'$default_ca':=20connect=20with=20sslcert,=20client=20certificate=20= sent"=0A-=09);=0A+#=20example.com=20is=20configured=20and=20should=20= require=20a=20valid=20client=20cert.=0A+$node->connect_fails(=0A+=09= "$connstr=20host=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=20=0A-=09= #=20example.net=20is=20configured=20and=20should=20require=20a=20client=20= cert,=20but=20will=0A-=09#=20always=20fail=20verification.=0A-=09= $node->connect_fails(=0A-=09=09"$connstr=20host=3Dexample.net=20= sslcertmode=3Ddisable",=0A-=09=09"host:=20'example.net',=20ca:=20= '$default_ca':=20connect=20fails=20if=20no=20client=20certificate=20= sent",=0A-=09=09expected_stderr=20=3D>=20qr/connection=20requires=20a=20= valid=20client=20certificate/=0A-=09);=0A+$node->connect_ok(=0A+=09= "$connstr=20host=3Dexample.com=20sslcertmode=3Drequire=20= sslcert=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=20=0A-=09$node->connect_fails(=0A-=09=09"$connstr=20= host=3Dexample.net=20sslcertmode=3Drequire=20sslcert=3Dssl/client.crt=20= "=0A-=09=09=20=20.=20$ssl_server->sslkey('client.key'),=0A-=09=09"host:=20= 'example.net',=20ca:=20'$default_ca':=20connect=20with=20sslcert,=20= client=20certificate=20sent",=0A-=09=09expected_stderr=20=3D>=20= qr/unknown=20ca/);=0A-}=0A+#=20example.net=20is=20configured=20and=20= should=20require=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=20=0A=20= done_testing();=0A--=20=0A2.39.3=20(Apple=20Git-146)=0A=0A= --Apple-Mail=_BA87BDEE-21BC-4337-BB45-129C4A4012C4--