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 1whw5w-000UqL-0z for pgsql-hackers@arkaria.postgresql.org; Thu, 09 Jul 2026 21:15:45 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1whw4v-00EhCh-1j for pgsql-hackers@arkaria.postgresql.org; Thu, 09 Jul 2026 21:14:42 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1whw4u-00EhCY-2s for pgsql-hackers@lists.postgresql.org; Thu, 09 Jul 2026 21:14:41 +0000 Received: from smtp.outgoing.loopia.se ([93.188.3.37]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1whw4s-00000000Xn2-0kc3 for pgsql-hackers@lists.postgresql.org; Thu, 09 Jul 2026 21:14:41 +0000 Received: from s807.loopia.se (localhost [127.0.0.1]) by s807.loopia.se (Postfix) with ESMTP id F19EA6777E6 for ; Thu, 09 Jul 2026 23:14:36 +0200 (CEST) Received: from s981.loopia.se (unknown [172.22.191.6]) by s807.loopia.se (Postfix) with ESMTP id DEC21677DD2; Thu, 09 Jul 2026 23:14:36 +0200 (CEST) Received: from localhost (unknown [172.22.191.6]) by s981.loopia.se (Postfix) with ESMTP id DCC5722B1708; Thu, 09 Jul 2026 23:14:36 +0200 (CEST) X-Virus-Scanned: amavis at amavis.loopia.se X-Spam-Flag: NO X-Spam-Score: -1.2 X-Spam-Level: X-Spam-Status: No, score=-1.2 tagged_above=-999 required=6.2 tests=[ALL_TRUSTED=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1] autolearn=disabled Authentication-Results: s470.loopia.se (amavis); dkim=pass (2048-bit key) header.d=yesql.se Received: from s899.loopia.se ([172.22.191.6]) by localhost (s470.loopia.se [172.22.190.34]) (amavis, port 10024) with LMTP id jt4tQTqbR-Ti; Thu, 9 Jul 2026 23:14:36 +0200 (CEST) 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 s899.loopia.se (Postfix) with ESMTPSA id 1654A2C8B9B4; Thu, 09 Jul 2026 23:14:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yesql.se; s=loopiadkim1707475645; t=1783631676; bh=9zwIOJj64LuzrtVljZgwvdA1arF9H7cQ0HRaO3g6hqc=; h=From:Subject:Date:Cc:To; b=lWjSJQfMj2cfNWdPCIbTvGLe1CUUdTr+x+ZY523WA+aFxk/GVcmbRUChOxGiUdLoh DzJZsqYf0hrVbfeF4ifzN3F2b5hk0ooejAAJPbKnzyEc9N8TjazjwVhwlXREXBem66 L1agSZhIpWtsUbFE/IydjsFbXoyQxalOzMbLhgvL+vsTEOl9z0SYPEuyCV/lLqX4bR e5sDtNRkbVHeG38NzAcXIOLpbO9kilqQ8ytbsqof5HxinPbbNpAsduFpuZq39PQvDu VdnbzgzO+KAscEI4fFixopq8OOFPNxv6nIiE58qEVmGG9pjxwcQhFn6GhcjKOZReMg Cbg1AwP8/lC4g== From: Daniel Gustafsson Content-Type: multipart/mixed; boundary="Apple-Mail=_7839B5C3-1BEA-4263-93AF-FC26684DAF6A" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51.11.12\)) Subject: LibreSSL and OpenSSL separation in libpq to support 1.1.1 deprecation Message-Id: <2AEF44BB-6414-40F7-BB8D-4FDB66199A81@yesql.se> Date: Thu, 9 Jul 2026 23:14:25 +0200 Cc: Jacob Champion To: PostgreSQL Hackers X-Mailer: Apple Mail (2.3776.700.51.11.12) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_7839B5C3-1BEA-4263-93AF-FC26684DAF6A Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Since the discussion on the minimum supported version of OpenSSL for v20 = has kicked off, I wanted to share what I believe is an important step on the = path to OpenSSL modernization; namely how to deal with LibreSSL. Our LibreSSL support is currently based on the OpenSSL code and the = OpenSSL compatibility in LibreSSL, so much so that for quite some time we didn't = even document that it was supported even though it was buildfarm tested. = Over time we've accumulated more ifdefs for differences in API support. If we = want to modernize our OpenSSL support in libpq with v3+ API's once we've = deprecated 1.1.1, the code will soon become unreadable and IMHO, a CVE hazard. LibreSSL supports the 1.1.1 API, and they dont have the manpower to keep = up with the rapid pace of OpenSSL development so are likely to stay there = for the foreseeable future. The libraries have already started diverging quite = a lot and it will continue (LibreSSL have also developed their own API which = if I were them is what I'd focus on). As I can see it we have two choices wrt LibreSSL if we want to move past = 1.1.1; either deprecate support or separate it out such that OpenSSL and = LibreSSL can move at their own pace. I don't like the idea of deprecating LibreSSL = so I favor the latter alternative. The attached implements LibreSSL as a separate TLS library = implementation (*) in libpq with fe-secure-libressl.c and be-secure-libressl.c along with = build and test infrastructure. This is a PoC rather than a polished = for-inclusion patch as of now, since I wanted to get the discussion going. Some of = the known TODO items include: - Autoconf support is a POC and not tested at all yet, only the Meson side has been tested as of now - include/common/openssl.h needs to either be renamed, copied or at least have its comments extended - test/ssl/t/SSL/Backend/OpenSSL.pm has been hackily extended for now, the exact level of changes required here are not clear, and a future move to pytest may influence how much we want to invest here - the discovery in meson.build is likely rubbish and could be cleaned up by our Meson experts - there are likely lots of comments still that mention OpenSSL which could be expanded to say something about LibreSSL - documentation of the build option is missing - we can simplify a few uses of USE_OPENSSL || USE_LIBRESSL to just USE_SSL There are no new features introduced (and none removed) or API usage = changes, the only changes are to cut away the ifdefs and only keep the code we = actually need per file. This way the differences are quite easy to check by = diffing {fe|be}-secure-{openssl|libressl}.c. While this duplicate a lot of code, it doesn't really add a maintenance = burden we don't already carry since we do support LibreSSL already, but it does = alter the burden for sure (not least when it comes to backpatching). That = being said, staying on a long-since deprecated API level (we are at 1.0.1 with warnings silenced) also doesn't sound great. One option is to not do this until we have a case where we want to use a = v3+ API and only do it then as a prerequisite step; keeping the patch = rebased in the back-pocket till that day comes. Personally I have serverside SNI = support for LibreSSL on my radar for v20, which will need such a split in order = to keep the files readable as it will be implemented quite differently from its = OpenSSL counterpart. All of the above is of course moot if we don't move the needle past = 1.1.1 in v20.. -- Daniel Gustafsson (*) Yes. The irony of me proposing to add a new TLS library = implementation to postgres is *not* lost on me. --Apple-Mail=_7839B5C3-1BEA-4263-93AF-FC26684DAF6A Content-Disposition: attachment; filename=vPoC-0001-ssl-Split-OpenSSL-and-LibreSSL-into-separate-mo.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="vPoC-0001-ssl-Split-OpenSSL-and-LibreSSL-into-separate-mo.patch" Content-Transfer-Encoding: quoted-printable =46rom=20ae7b71271ee60896ab8a378866e7cddbbd46d00a=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Daniel=20Gustafsson=20=0A= Date:=20Thu,=209=20Jul=202026=2015:50:18=20+0200=0ASubject:=20[PATCH=20= vPoC]=20ssl:=20Split=20OpenSSL=20and=20LibreSSL=20into=20separate=20= modules=0A=0ALibreSSL=20support=20in=20PostgreSQL=20was=20added=20more=20= as=20a=20consequence=20of=20the=0AOpenSSL=20compatibility=20of=20= LibreSSL=20than=20a=20discussed=20library=20dependency.=0ASupport=20for=20= LibreSSL=20was=20in=20the=20beginning=20undocumented=20with=20no=20= minimum=0Aversion=20specified.=20=20The=20OpenSSL=20code=20in=20libpq=20= handled=20the=20differences=0Awith=20ifdefs.=0A=0AAs=20we=20are=20= looking=20to=20deprecate=20support=20for=20OpenSSL=201.1.1=20and=20= modernize=0Aour=20OpenSSL=20code=20with=203.0+=20APIs,=20we=20need=20to=20= separate=20support=20for=20the=0Alibraries=20since=20LibreSSL=20still=20= use=20the=201.1.1=20style=20API.=20=20If=20we=20keep=20the=0Asupport=20= in=20the=20same=20file=20it=20will=20hurt=20readability=20with=20all=20= the=20ifdefs=0Arequired,=20and=20risk=20hampering=20efforts=20to=20make=20= use=20of=20modern=20OpenSSL=20API=0Afeatures.=20=20The=20recent=20= support=20for=20serverside=20SNI=20is=20another=20example=0Awhere=20= mixing=20the=20APIs=20caused=20problems,=20which=20ended=20up=20with=20= serverside=0ASNI=20not=20being=20available=20when=20building=20with=20= LibreSSL.=0A=0AThis=20copies=20the=20current=20OpenSSL=20support=20into=20= mirrored=20LibreSSL=20files=0Aand=20removes=20the=20compatibility=20= fixes=20from=20both,=20with=20no=20new=20features=0Aintroduced.=0A=0A= There=20are=20still=20a=20few=20more=20things=20needed=20to=20take=20= this=20patch=20from=20POC=0Astatus,=20some=20are=20listed=20below:=0A=0A=20= =20TODO=0A=20=20=20=20-=20Autoconf=20support=20is=20a=20POC=20and=20not=20= tested=20yet=0A=09-=20include/common/openssl.h=20needs=20to=20either=20= be=20renamed,=20copied=0A=09=20=20or=20at=20least=20have=20its=20= comments=20extended=0A=09-=20test/ssl/t/SSL/Backend/OpenSSL.pm=20has=20= been=20hackily=20extended=0A=09=20=20for=20now,=20the=20exact=20level=20= of=20changes=20required=20here=20are=20not=0A=09=20=20clear,=20and=20a=20= future=20move=20to=20pytest=20may=20influence=20how=20much=20we=0A=09=20=20= want=20to=20invest=20here=0A---=0A=20configure=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20112=20+-=0A=20configure.ac=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=2015=20+-=0A=20= 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=20=20=20=20=20=20=20|=20=20=2061=20+-=0A=20meson_options.txt=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=204=20+-=0A=20src/backend/commands/variable.c=20=20=20=20=20=20= =20=20=20=20=20|=20=20=20=202=20+-=0A=20src/backend/libpq/Makefile=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=20=203=20+=0A=20= src/backend/libpq/be-secure-libressl.c=20=20=20=20|=201978=20= +++++++++++++++++++++=0A=20src/backend/libpq/be-secure-openssl.c=20=20=20= =20=20|=20=20=2097=20+-=0A=20src/backend/libpq/meson.build=20=20=20=20=20= =20=20=20=20=20=20=20=20|=20=20=20=206=20+-=0A=20= src/include/common/openssl.h=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20= =20=20=204=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=208=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=206=20+-=0A=20= src/include/pg_config.h.in=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=2020=20+-=0A=20src/include/pg_config_manual.h=20=20=20=20=20=20=20= =20=20=20=20=20|=20=20=20=202=20+-=0A=20src/interfaces/libpq/Makefile=20=20= =20=20=20=20=20=20=20=20=20=20=20|=20=20=20=204=20+=0A=20= src/interfaces/libpq/fe-secure-libressl.c=20|=201905=20= ++++++++++++++++++++=0A=20src/interfaces/libpq/fe-secure-openssl.c=20=20= |=20=20=2022=20+-=0A=20src/interfaces/libpq/fe-secure.c=20=20=20=20=20=20= =20=20=20=20|=20=20=20=207=20+-=0A=20src/interfaces/libpq/libpq-int.h=20=20= =20=20=20=20=20=20=20=20|=20=20=20=208=20+-=0A=20= src/interfaces/libpq/meson.build=20=20=20=20=20=20=20=20=20=20|=20=20=20=20= 6=20+-=0A=20src/port/pg_strong_random.c=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20|=20=20=20=204=20+-=0A=20src/test/ssl/t/001_ssltests.pl=20=20=20= =20=20=20=20=20=20=20=20=20|=20=20=20=204=20+-=0A=20= src/test/ssl/t/002_scram.pl=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=20=204=20+-=0A=20src/test/ssl/t/003_sslinfo.pl=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=20=20=204=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=2011=20+-=0A=20= src/test/ssl/t/SSL/Backend/OpenSSL.pm=20=20=20=20=20|=20=20=20=204=20+-=0A= =20src/test/ssl/t/SSL/Server.pm=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=20=205=20+=0A=2027=20files=20changed,=204105=20insertions(+),=20= 201=20deletions(-)=0A=20create=20mode=20100644=20= src/backend/libpq/be-secure-libressl.c=0A=20create=20mode=20100644=20= src/interfaces/libpq/fe-secure-libressl.c=0A=0Adiff=20--git=20= a/configure=20b/configure=0Aindex=2035b0b72f0a7..2e44fa676a8=20100755=0A= ---=20a/configure=0A+++=20b/configure=0A@@=20-12997,7=20+12997,7=20@@=20= fi=0A=20#=0A=20#=20SSL=20Library=0A=20#=0A-#=20There=20is=20currently=20= only=20one=20supported=20SSL/TLS=20library:=20OpenSSL.=0A+#=20The=20= currently=20supported=20SSL/TLS=20libraries=20are:=20OpenSSL,=20LibreSSL=0A= =20#=0A=20=0A=20=0A@@=20-13164,20=20+13164,106=20@@=20else=0A=20fi=0A=20= done=0A=20=0A-=20=20#=20Function=20introduced=20in=20OpenSSL=201.0.2,=20= not=20in=20LibreSSL.=0A-=20=20for=20ac_func=20in=20SSL_CTX_set_cert_cb=0A= -do=20:=0A-=20=20ac_fn_c_check_func=20"$LINENO"=20"SSL_CTX_set_cert_cb"=20= "ac_cv_func_SSL_CTX_set_cert_cb"=0A-if=20test=20= "x$ac_cv_func_SSL_CTX_set_cert_cb"=20=3D=20xyes;=20then=20:=0A+=0A= +$as_echo=20"#define=20USE_OPENSSL=201"=20>>confdefs.h=0A+=0A+elif=20= test=20"$with_ssl"=20=3D=20libressl=20;=20then=0A+=20=20=20=20{=20= $as_echo=20"$as_me:${as_lineno-$LINENO}:=20checking=20for=20= CRYPTO_new_ex_data=20in=20-lcrypto"=20>&5=0A+$as_echo_n=20"checking=20= for=20CRYPTO_new_ex_data=20in=20-lcrypto...=20"=20>&6;=20}=0A+if=20= ${ac_cv_lib_crypto_CRYPTO_new_ex_data+:}=20false;=20then=20:=0A+=20=20= $as_echo_n=20"(cached)=20"=20>&6=0A+else=0A+=20=20= ac_check_lib_save_LIBS=3D$LIBS=0A+LIBS=3D"-lcrypto=20=20$LIBS"=0A+cat=20= confdefs.h=20-=20<<_ACEOF=20>conftest.$ac_ext=0A+/*=20end=20confdefs.h.=20= =20*/=0A+=0A+/*=20Override=20any=20GCC=20internal=20prototype=20to=20= avoid=20an=20error.=0A+=20=20=20Use=20char=20because=20int=20might=20= match=20the=20return=20type=20of=20a=20GCC=0A+=20=20=20builtin=20and=20= then=20its=20argument=20prototype=20would=20still=20apply.=20=20*/=0A= +#ifdef=20__cplusplus=0A+extern=20"C"=0A+#endif=0A+char=20= CRYPTO_new_ex_data=20();=0A+int=0A+main=20()=0A+{=0A+return=20= CRYPTO_new_ex_data=20();=0A+=20=20;=0A+=20=20return=200;=0A+}=0A+_ACEOF=0A= +if=20ac_fn_c_try_link=20"$LINENO";=20then=20:=0A+=20=20= ac_cv_lib_crypto_CRYPTO_new_ex_data=3Dyes=0A+else=0A+=20=20= ac_cv_lib_crypto_CRYPTO_new_ex_data=3Dno=0A+fi=0A+rm=20-f=20core=20= conftest.err=20conftest.$ac_objext=20\=0A+=20=20=20=20conftest$ac_exeext=20= conftest.$ac_ext=0A+LIBS=3D$ac_check_lib_save_LIBS=0A+fi=0A+{=20$as_echo=20= "$as_me:${as_lineno-$LINENO}:=20result:=20= $ac_cv_lib_crypto_CRYPTO_new_ex_data"=20>&5=0A+$as_echo=20= "$ac_cv_lib_crypto_CRYPTO_new_ex_data"=20>&6;=20}=0A+if=20test=20= "x$ac_cv_lib_crypto_CRYPTO_new_ex_data"=20=3D=20xyes;=20then=20:=0A=20=20= =20cat=20>>confdefs.h=20<<_ACEOF=0A-#define=20HAVE_SSL_CTX_SET_CERT_CB=20= 1=0A+#define=20HAVE_LIBCRYPTO=201=0A=20_ACEOF=0A=20=0A+=20=20= LIBS=3D"-lcrypto=20$LIBS"=0A+=0A+else=0A+=20=20as_fn_error=20$?=20= "library=20'crypto'=20is=20required=20for=20LibreSSL"=20"$LINENO"=205=0A=20= fi=0A-done=0A=20=0A-=20=20#=20Function=20introduced=20in=20OpenSSL=20= 1.1.1,=20not=20in=20LibreSSL.=0A-=20=20for=20ac_func=20in=20= X509_get_signature_info=20SSL_CTX_set_num_tickets=20= SSL_CTX_set_keylog_callback=20SSL_CTX_set_client_hello_cb=0A+=20=20{=20= $as_echo=20"$as_me:${as_lineno-$LINENO}:=20checking=20for=20SSL_new=20in=20= -lssl"=20>&5=0A+$as_echo_n=20"checking=20for=20SSL_new=20in=20-lssl...=20= "=20>&6;=20}=0A+if=20${ac_cv_lib_ssl_SSL_new+:}=20false;=20then=20:=0A+=20= =20$as_echo_n=20"(cached)=20"=20>&6=0A+else=0A+=20=20= ac_check_lib_save_LIBS=3D$LIBS=0A+LIBS=3D"-lssl=20=20$LIBS"=0A+cat=20= confdefs.h=20-=20<<_ACEOF=20>conftest.$ac_ext=0A+/*=20end=20confdefs.h.=20= =20*/=0A+=0A+/*=20Override=20any=20GCC=20internal=20prototype=20to=20= avoid=20an=20error.=0A+=20=20=20Use=20char=20because=20int=20might=20= match=20the=20return=20type=20of=20a=20GCC=0A+=20=20=20builtin=20and=20= then=20its=20argument=20prototype=20would=20still=20apply.=20=20*/=0A= +#ifdef=20__cplusplus=0A+extern=20"C"=0A+#endif=0A+char=20SSL_new=20();=0A= +int=0A+main=20()=0A+{=0A+return=20SSL_new=20();=0A+=20=20;=0A+=20=20= return=200;=0A+}=0A+_ACEOF=0A+if=20ac_fn_c_try_link=20"$LINENO";=20then=20= :=0A+=20=20ac_cv_lib_ssl_SSL_new=3Dyes=0A+else=0A+=20=20= ac_cv_lib_ssl_SSL_new=3Dno=0A+fi=0A+rm=20-f=20core=20conftest.err=20= conftest.$ac_objext=20\=0A+=20=20=20=20conftest$ac_exeext=20= conftest.$ac_ext=0A+LIBS=3D$ac_check_lib_save_LIBS=0A+fi=0A+{=20$as_echo=20= "$as_me:${as_lineno-$LINENO}:=20result:=20$ac_cv_lib_ssl_SSL_new"=20>&5=0A= +$as_echo=20"$ac_cv_lib_ssl_SSL_new"=20>&6;=20}=0A+if=20test=20= "x$ac_cv_lib_ssl_SSL_new"=20=3D=20xyes;=20then=20:=0A+=20=20cat=20= >>confdefs.h=20<<_ACEOF=0A+#define=20HAVE_LIBSSL=201=0A+_ACEOF=0A+=0A+=20= =20LIBS=3D"-lssl=20$LIBS"=0A+=0A+else=0A+=20=20as_fn_error=20$?=20= "library=20'ssl'=20is=20required=20for=20LibreSSL"=20"$LINENO"=205=0A+fi=0A= +=0A+=20=20#=20Functions=20introduced=20in=20LibreSSL=203.4.1=20(OpenBSD=20= 7.0)=0A+=20=20for=20ac_func=20in=20SSL_CTX_set_ciphersuites=20= SSL_CTX_use_certificate_chain_mem=0A=20do=20:=0A=20=20=20= as_ac_var=3D`$as_echo=20"ac_cv_func_$ac_func"=20|=20$as_tr_sh`=0A=20= ac_fn_c_check_func=20"$LINENO"=20"$ac_func"=20"$as_ac_var"=0A@@=20= -13186,14=20+13272,16=20@@=20if=20eval=20test=20\"x\$"$as_ac_var"\"=20=3D=20= x"yes";=20then=20:=0A=20#define=20`$as_echo=20"HAVE_$ac_func"=20|=20= $as_tr_cpp`=201=0A=20_ACEOF=0A=20=0A+else=0A+=20=20as_fn_error=20$?=20= "LibreSSL=20version=20>=3D=203.4.1=20is=20required=20for=20SSL=20= support"=20"$LINENO"=205=0A=20fi=0A=20done=0A=20=0A=20=0A-$as_echo=20= "#define=20USE_OPENSSL=201"=20>>confdefs.h=0A+$as_echo=20"#define=20= USE_LIBRESSL=201"=20>>confdefs.h=0A=20=0A=20elif=20test=20"$with_ssl"=20= !=3D=20no=20;=20then=0A-=20=20as_fn_error=20$?=20"--with-ssl=20must=20= specify=20openssl"=20"$LINENO"=205=0A+=20=20as_fn_error=20$?=20= "--with-ssl=20must=20specify=20openssl=20or=20libressl"=20"$LINENO"=205=0A= =20fi=0A=20=0A=20=0Adiff=20--git=20a/configure.ac=20b/configure.ac=0A= index=200e624fe36b9..a5357d1d2a5=20100644=0A---=20a/configure.ac=0A+++=20= b/configure.ac=0A@@=20-1420,7=20+1420,7=20@@=20fi=0A=20#=0A=20#=20SSL=20= Library=0A=20#=0A-#=20There=20is=20currently=20only=20one=20supported=20= SSL/TLS=20library:=20OpenSSL.=0A+#=20The=20currently=20supported=20= SSL/TLS=20libraries=20are:=20OpenSSL,=20LibreSSL=0A=20#=0A=20= PGAC_ARG_REQ(with,=20ssl,=20[LIB],=20[use=20LIB=20for=20SSL/TLS=20= support=20(openssl)])=0A=20if=20test=20x"$with_ssl"=20=3D=20x""=20;=20= then=0A@@=20-1440,13=20+1440,16=20@@=20if=20test=20"$with_ssl"=20=3D=20= openssl=20;=20then=0A=20=20=20AC_CHECK_LIB(ssl,=20=20=20=20SSL_new,=20= [],=20[AC_MSG_ERROR([library=20'ssl'=20is=20required=20for=20OpenSSL])])=0A= =20=20=20#=20Functions=20introduced=20in=20OpenSSL=201.1.1.=0A=20=20=20= AC_CHECK_FUNCS([SSL_CTX_set_ciphersuites],=20[],=20= [AC_MSG_ERROR([OpenSSL=20version=20>=3D=201.1.1=20is=20required=20for=20= SSL=20support])])=0A-=20=20#=20Function=20introduced=20in=20OpenSSL=20= 1.0.2,=20not=20in=20LibreSSL.=0A-=20=20= AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])=0A-=20=20#=20Function=20introduced=20= in=20OpenSSL=201.1.1,=20not=20in=20LibreSSL.=0A-=20=20= AC_CHECK_FUNCS([X509_get_signature_info=20SSL_CTX_set_num_tickets=20= SSL_CTX_set_keylog_callback=20SSL_CTX_set_client_hello_cb])=0A=20=20=20= AC_DEFINE([USE_OPENSSL],=201,=20[Define=20to=201=20to=20build=20with=20= OpenSSL=20support.=20(--with-ssl=3Dopenssl)])=0A+elif=20test=20= "$with_ssl"=20=3D=20libressl=20;=20then=0A+=20=20dnl=20Order=20matters!=0A= +=20=20AC_CHECK_LIB(crypto,=20CRYPTO_new_ex_data,=20[],=20= [AC_MSG_ERROR([library=20'crypto'=20is=20required=20for=20LibreSSL])])=0A= +=20=20AC_CHECK_LIB(ssl,=20=20=20=20SSL_new,=20[],=20= [AC_MSG_ERROR([library=20'ssl'=20is=20required=20for=20LibreSSL])])=0A+=20= =20#=20Functions=20introduced=20in=20LibreSSL=203.4.1=20(OpenBSD=207.0)=0A= +=20=20AC_CHECK_FUNCS([SSL_CTX_set_ciphersuites=20= SSL_CTX_use_certificate_chain_mem],=20[],=20[AC_MSG_ERROR([LibreSSL=20= version=20>=3D=203.4.1=20is=20required=20for=20SSL=20support])])=0A+=20=20= AC_DEFINE([USE_LIBRESSL],=201,=20[Define=20to=201=20to=20build=20with=20= LibreSSL=20support.=20(--with-ssl=3Dlibressl)])=0A=20elif=20test=20= "$with_ssl"=20!=3D=20no=20;=20then=0A-=20=20AC_MSG_ERROR([--with-ssl=20= must=20specify=20openssl])=0A+=20=20AC_MSG_ERROR([--with-ssl=20must=20= specify=20openssl=20or=20libressl])=0A=20fi=0A=20AC_SUBST(with_ssl)=0A=20= =0Adiff=20--git=20a/meson.build=20b/meson.build=0Aindex=20= d88a7a70308..c84b68f999c=20100644=0A---=20a/meson.build=0A+++=20= b/meson.build=0A@@=20-1627,8=20+1627,8=20@@=20if=20sslopt=20=3D=3D=20= 'auto'=20and=20auto_features.disabled()=0A=20=20=20sslopt=20=3D=20'none'=0A= =20endif=0A=20=0A-if=20sslopt=20in=20['auto',=20'openssl']=0A-=20=20= openssl_required=20=3D=20(sslopt=20=3D=3D=20'openssl')=0A+if=20sslopt=20= in=20['auto',=20'openssl',=20'libressl']=0A+=20=20ssl_library_required=20= =3D=20(sslopt=20!=3D=20'auto')=0A=20=0A=20=20=20#=20Try=20to=20find=20= openssl=20via=20pkg-config=20et=20al,=20if=20that=20doesn't=20work=0A=20=20= =20#=20(e.g.=20because=20it's=20provided=20as=20part=20of=20the=20OS,=20= like=20on=20FreeBSD),=20look=20for=0A@@=20-1646,16=20+1646,16=20@@=20if=20= sslopt=20in=20['auto',=20'openssl']=0A=20=20=20=20=20=20=20dirs:=20= test_lib_d,=0A=20=20=20=20=20=20=20header_include_directories:=20= postgres_inc,=0A=20=20=20=20=20=20=20has_headers:=20['openssl/ssl.h',=20= 'openssl/err.h'],=0A-=20=20=20=20=20=20required:=20openssl_required)=0A+=20= =20=20=20=20=20required:=20ssl_library_required)=0A=20=20=20=20=20= crypto_lib=20=3D=20cc.find_library('crypto',=0A=20=20=20=20=20=20=20= dirs:=20test_lib_d,=0A-=20=20=20=20=20=20required:=20openssl_required)=0A= +=20=20=20=20=20=20required:=20ssl_library_required)=0A=20=20=20=20=20if=20= ssl_lib.found()=20and=20crypto_lib.found()=0A=20=20=20=20=20=20=20= ssl_int=20=3D=20[ssl_lib,=20crypto_lib]=0A=20=20=20=20=20=20=20ssl=20=3D=20= declare_dependency(dependencies:=20ssl_int,=20include_directories:=20= postgres_inc)=0A=20=20=20=20=20endif=0A-=20=20elif=20= cc.has_header('openssl/ssl.h',=20args:=20test_c_args,=20dependencies:=20= ssl,=20required:=20openssl_required)=20and=20\=0A-=20=20=20=20=20=20=20= cc.has_header('openssl/err.h',=20args:=20test_c_args,=20dependencies:=20= ssl,=20required:=20openssl_required)=0A+=20=20elif=20= cc.has_header('openssl/ssl.h',=20args:=20test_c_args,=20dependencies:=20= ssl,=20required:=20ssl_library_required)=20and=20\=0A+=20=20=20=20=20=20=20= cc.has_header('openssl/err.h',=20args:=20test_c_args,=20dependencies:=20= ssl,=20required:=20ssl_library_required)=0A=20=20=20=20=20ssl_int=20=3D=20= [ssl]=0A=20=20=20else=0A=20=20=20=20=20ssl=20=3D=20not_found_dep=0A@@=20= -1666,41=20+1666,52=20@@=20if=20sslopt=20in=20['auto',=20'openssl']=0A=20= =20=20=20=20=20=20['CRYPTO_new_ex_data',=20{'required':=20true}],=0A=20=20= =20=20=20=20=20['SSL_new',=20{'required':=20true}],=0A=20=0A-=20=20=20=20= =20=20#=20Functions=20introduced=20in=20OpenSSL=201.1.1.=0A+=20=20=20=20=20= =20#=20Functions=20introduced=20in=20OpenSSL=201.1.1=20and=20present=20= in=20LibreSSL=0A=20=20=20=20=20=20=20['SSL_CTX_set_ciphersuites',=20= {'required':=20true}],=0A=20=0A+=20=20=20=20=20=20#=20Functions=20for=20= optionally=20supported=20functionality=20in=20OpenSSL=0A+=20=20=20=20=20=20= ['SSL_CTX_set_keylog_callback',=20{'optional':=20true}],=0A+=0A=20=20=20=20= =20=20=20#=20Function=20introduced=20in=20OpenSSL=201.0.2,=20not=20in=20= LibreSSL.=0A-=20=20=20=20=20=20['SSL_CTX_set_cert_cb'],=0A+=20=20=20=20=20= =20['SSL_CTX_set_cert_cb',=20{'openssl':=20true}],=0A=20=0A-=20=20=20=20=20= =20#=20Function=20introduced=20in=20OpenSSL=201.1.1,=20not=20in=20= LibreSSL.=0A-=20=20=20=20=20=20['X509_get_signature_info'],=0A-=20=20=20=20= =20=20['SSL_CTX_set_num_tickets'],=0A-=20=20=20=20=20=20= ['SSL_CTX_set_keylog_callback'],=0A-=20=20=20=20=20=20= ['SSL_CTX_set_client_hello_cb'],=0A+=20=20=20=20=20=20#=20Function=20= present=20in=20LibreSSL=203.4,=20not=20in=20OpenSSL=0A+=20=20=20=20=20=20= ['SSL_CTX_use_certificate_chain_mem',=20{'libressl':=20true}],=0A=20=20=20= =20=20]=0A=20=0A-=20=20=20=20are_openssl_funcs_complete=20=3D=20true=0A+=20= =20=20=20are_ssl_funcs_complete=20=3D=20true=0A=20=20=20=20=20foreach=20= c=20:=20check_funcs=0A=20=20=20=20=20=20=20func=20=3D=20c.get(0)=0A=20=20= =20=20=20=20=20val=20=3D=20cc.has_function(func,=20args:=20test_c_args,=20= dependencies:=20ssl_int)=0A=20=20=20=20=20=20=20required=20=3D=20= c.get(1,=20{}).get('required',=20false)=0A+=20=20=20=20=20=20optional=20= =3D=20c.get(1,=20{}).get('optional',=20false)=0A+=20=20=20=20=20=20= openssl_lib=20=3D=20c.get(1,=20{}).get('openssl',=20false)=0A+=20=20=20=20= =20=20libressl_lib=20=3D=20c.get(1,=20{}).get('libressl',=20false)=0A=20=20= =20=20=20=20=20if=20required=20and=20not=20val=0A-=20=20=20=20=20=20=20=20= are_openssl_funcs_complete=20=3D=20false=0A-=20=20=20=20=20=20=20=20if=20= openssl_required=0A-=20=20=20=20=20=20=20=20=20=20error('openssl=20= function=20@0@=20is=20required'.format(func))=0A+=20=20=20=20=20=20=20=20= are_ssl_funcs_complete=20=3D=20false=0A+=20=20=20=20=20=20=20=20if=20= ssl_library_required=0A+=20=20=20=20=20=20=20=20=20=20error('ssl=20= function=20@0@=20is=20required'.format(func))=0A=20=20=20=20=20=20=20=20=20= endif=0A=20=20=20=20=20=20=20=20=20break=0A-=20=20=20=20=20=20elif=20not=20= required=0A+=20=20=20=20=20=20elif=20optional=0A=20=20=20=20=20=20=20=20=20= cdata.set('HAVE_'=20+=20func.to_upper(),=20val=20?=201=20:=20false)=0A+=20= =20=20=20=20=20elif=20openssl_lib=20and=20val=0A+=20=20=20=20=20=20=20=20= ssl_library=20=3D=20'openssl'=0A+=20=20=20=20=20=20elif=20libressl_lib=20= and=20val=0A+=20=20=20=20=20=20=20=20ssl_library=20=3D=20'libressl'=0A=20= =20=20=20=20=20=20endif=0A=20=20=20=20=20endforeach=0A=20=0A-=20=20=20=20= if=20are_openssl_funcs_complete=0A-=20=20=20=20=20=20= cdata.set('USE_OPENSSL',=201,=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20description:=20'Define=20to=201=20to=20build=20with=20OpenSSL=20= support.=20(-Dssl=3Dopenssl)')=0A-=20=20=20=20=20=20= cdata.set('OPENSSL_API_COMPAT',=20'0x10101000L',=0A-=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20description:=20'Define=20to=20the=20OpenSSL=20= API=20version=20in=20use.=20This=20avoids=20deprecation=20warnings=20= from=20newer=20OpenSSL=20versions.')=0A-=20=20=20=20=20=20ssl_library=20= =3D=20'openssl'=0A+=20=20=20=20if=20are_ssl_funcs_complete=0A+=20=20=20=20= =20=20if=20sslopt=20=3D=3D=20'openssl'=20and=20ssl_library=20=3D=3D=20= 'openssl'=0A+=20=20=20=20=20=20=20=20cdata.set('USE_OPENSSL',=201,=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20description:=20= 'Define=20to=201=20to=20build=20with=20OpenSSL=20support.=20= (-Dssl=3Dopenssl)')=0A+=20=20=20=20=20=20=20=20= cdata.set('OPENSSL_API_COMPAT',=20'0x10101000L',=0A+=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20description:=20'Define=20to=20the=20= OpenSSL=20API=20version=20in=20use.=20This=20avoids=20deprecation=20= warnings=20from=20newer=20OpenSSL=20versions.')=0A+=20=20=20=20=20=20= elif=20sslopt=20=3D=3D=20'libressl'=20and=20ssl_library=20=3D=3D=20= 'libressl'=0A+=20=20=20=20=20=20=20=20cdata.set('USE_LIBRESSL',=201,=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20description:=20= 'Define=20to=201=20to=20build=20with=20LibreSSL=20support.=20= (-Dssl=3Dlibressl)')=0A+=20=20=20=20=20=20endif=0A=20=20=20=20=20else=0A=20= =20=20=20=20=20=20ssl=20=3D=20not_found_dep=0A=20=20=20=20=20endif=0A@@=20= -4331,13=20+4342,13=20@@=20summary(=0A=20=20=20=20=20'llvm':=20llvm,=0A=20= =20=20=20=20'lz4':=20lz4,=0A=20=20=20=20=20'nls':=20libintl,=0A-=20=20=20= =20'openssl':=20ssl,=0A=20=20=20=20=20'pam':=20pam,=0A=20=20=20=20=20= 'plperl':=20[perl_dep,=20perlversion],=0A=20=20=20=20=20'plpython':=20= python3_dep,=0A=20=20=20=20=20'pltcl':=20tcl_dep,=0A=20=20=20=20=20= 'readline':=20readline,=0A=20=20=20=20=20'selinux':=20selinux,=0A+=20=20=20= =20'ssl':=20[ssl_library,=20ssl],=0A=20=20=20=20=20'systemd':=20systemd,=0A= =20=20=20=20=20'uuid':=20uuid,=0A=20=20=20=20=20'zlib':=20zlib,=0Adiff=20= --git=20a/meson_options.txt=20b/meson_options.txt=0Aindex=20= 6a793f3e479..a0601960fd9=20100644=0A---=20a/meson_options.txt=0A+++=20= b/meson_options.txt=0A@@=20-148,9=20+148,9=20@@=20option('readline',=20= type:=20'feature',=20value:=20'auto',=0A=20option('selinux',=20type:=20= 'feature',=20value:=20'auto',=0A=20=20=20description:=20'SELinux=20= support')=0A=20=0A-option('ssl',=20type:=20'combo',=20choices:=20= ['auto',=20'none',=20'openssl'],=0A+option('ssl',=20type:=20'combo',=20= choices:=20['auto',=20'none',=20'openssl',=20'libressl'],=0A=20=20=20= value:=20'auto',=0A-=20=20description:=20'Use=20LIB=20for=20SSL/TLS=20= support=20(openssl)')=0A+=20=20description:=20'Use=20LIB=20for=20SSL/TLS=20= support')=0A=20=0A=20option('systemd',=20type:=20'feature',=20value:=20= 'auto',=0A=20=20=20description:=20'systemd=20support')=0Adiff=20--git=20= a/src/backend/commands/variable.c=20b/src/backend/commands/variable.c=0A= index=208afd252fc8c..19102147945=20100644=0A---=20= a/src/backend/commands/variable.c=0A+++=20= b/src/backend/commands/variable.c=0A@@=20-1268,7=20+1268,7=20@@=20= check_ssl_sni(bool=20*newval,=20void=20**extra,=20GucSource=20source)=0A=20= =09=09return=20false;=0A=20=09}=0A=20#else=0A-#ifndef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A+#ifdef=20USE_LIBRESSL=0A=20=09if=20= (*newval)=0A=20=09{=0A=20=09=09GUC_check_errmsg("SNI=20requires=20= OpenSSL=201.1.1=20or=20later");=0Adiff=20--git=20= a/src/backend/libpq/Makefile=20b/src/backend/libpq/Makefile=0Aindex=20= 98eb2a8242d..655737454f8=20100644=0A---=20a/src/backend/libpq/Makefile=0A= +++=20b/src/backend/libpq/Makefile=0A@@=20-33,6=20+33,9=20@@=20OBJS=20=3D=20= \=0A=20ifeq=20($(with_ssl),openssl)=0A=20OBJS=20+=3D=20= be-secure-openssl.o=0A=20endif=0A+ifeq=20($(with_ssl),libressl)=0A+OBJS=20= +=3D=20be-secure-libressl.o=0A+endif=0A=20=0A=20ifeq=20= ($(with_gssapi),yes)=0A=20OBJS=20+=3D=20be-gssapi-common.o=20= be-secure-gssapi.o=0Adiff=20--git=20= a/src/backend/libpq/be-secure-libressl.c=20= b/src/backend/libpq/be-secure-libressl.c=0Anew=20file=20mode=20100644=0A= index=2000000000000..a720cb78fa4=0A---=20/dev/null=0A+++=20= b/src/backend/libpq/be-secure-libressl.c=0A@@=20-0,0=20+1,1978=20@@=0A= +/*-----------------------------------------------------------------------= --=0A+=20*=0A+=20*=20be-secure-libressl.c=0A+=20*=09=20=20functions=20= for=20LibreSSL=20support=20in=20the=20backend.=0A+=20*=0A+=20*=0A+=20*=20= Portions=20Copyright=20(c)=201996-2026,=20PostgreSQL=20Global=20= Development=20Group=0A+=20*=20Portions=20Copyright=20(c)=201994,=20= Regents=20of=20the=20University=20of=20California=0A+=20*=0A+=20*=0A+=20= *=20IDENTIFICATION=0A+=20*=09=20=20= src/backend/libpq/be-secure-libressl.c=0A+=20*=0A+=20= *-------------------------------------------------------------------------= =0A+=20*/=0A+=0A+#include=20"postgres.h"=0A+=0A+#include=20=0A= +#include=20=0A+#include=20=0A+#include=20=0A= +#include=20=0A+#include=20=0A+#include=20= =0A+#include=20=0A+#include=20=0A= +#include=20=0A+=0A+#include=20"common/hashfn.h"=0A= +#include=20"common/string.h"=0A+#include=20"libpq/libpq.h"=0A+#include=20= "miscadmin.h"=0A+#include=20"pgstat.h"=0A+#include=20"storage/fd.h"=0A= +#include=20"storage/latch.h"=0A+#include=20"utils/guc.h"=0A+#include=20= "utils/memutils.h"=0A+#include=20"utils/wait_event.h"=0A+=0A+/*=0A+=20*=20= These=20SSL-related=20#includes=20must=20come=20after=20all=20= system-provided=20headers.=0A+=20*=20This=20ensures=20that=20OpenSSL=20= can=20take=20care=20of=20conflicts=20with=20Windows'=0A+=20*=20= =20by=20#undef'ing=20the=20conflicting=20macros.=20=20(We=20= don't=20directly=0A+=20*=20include=20,=20but=20some=20other=20= Windows=20headers=20do.)=0A+=20*/=0A+#include=20"common/openssl.h"=0A= +#include=20=0A+#include=20=0A+#include=20= =0A+#include=20=0A+#include=20= =0A+=0A+/*=0A+=20*=20Simplehash=20for=20tracking=20= configured=20hostnames=20to=20guard=20against=20duplicate=0A+=20*=20= entries.=20=20Each=20list=20of=20hosts=20is=20traversed=20and=20added=20= to=20the=20hash=20during=0A+=20*=20parsing=20and=20if=20a=20duplicate=20= error=20is=20detected=20an=20error=20will=20be=20thrown.=0A+=20*/=0A= +typedef=20struct=0A+{=0A+=09uint32=09=09status;=0A+=09const=20char=20= *hostname;=0A+}=09=09=09HostCacheEntry;=0A+static=20uint32=20= host_cache_pointer(const=20char=20*key);=0A+#define=20SH_PREFIX=09=09= host_cache=0A+#define=20SH_ELEMENT_TYPE=09HostCacheEntry=0A+#define=20= SH_KEY_TYPE=09=09const=20char=20*=0A+#define=20SH_KEY=09=09=09hostname=0A= +#define=20SH_HASH_KEY(tb,=20key)=09host_cache_pointer(key)=0A+#define=20= SH_EQUAL(tb,=20a,=20b)=09=09(pg_strcasecmp(a,=20b)=20=3D=3D=200)=0A= +#define=20SH_SCOPE=09=09=09=09static=20inline=0A+#define=20SH_DECLARE=0A= +#define=20SH_DEFINE=0A+#include=20"lib/simplehash.h"=0A+=0A+/*=20= default=20init=20hook=20can=20be=20overridden=20by=20a=20shared=20= library=20*/=0A+static=20void=20default_openssl_tls_init(SSL_CTX=20= *context,=20bool=20isServerStart);=0A+openssl_tls_init_hook_typ=20= openssl_tls_init_hook=20=3D=20default_openssl_tls_init;=0A+=0A+static=20= int=09port_bio_read(BIO=20*h,=20char=20*buf,=20int=20size);=0A+static=20= int=09port_bio_write(BIO=20*h,=20const=20char=20*buf,=20int=20size);=0A= +static=20BIO_METHOD=20*port_bio_method(void);=0A+static=20int=09= ssl_set_port_bio(Port=20*port);=0A+=0A+static=20DH=20=20= *load_dh_file(char=20*filename,=20bool=20isServerStart);=0A+static=20DH=20= =20*load_dh_buffer(const=20char=20*buffer,=20size_t=20len);=0A+static=20= int=09ssl_external_passwd_cb(char=20*buf,=20int=20size,=20int=20rwflag,=20= void=20*userdata);=0A+static=20int=09dummy_ssl_passwd_cb(char=20*buf,=20= int=20size,=20int=20rwflag,=20void=20*userdata);=0A+static=20int=09= verify_cb(int=20ok,=20X509_STORE_CTX=20*ctx);=0A+static=20void=20= info_cb(const=20SSL=20*ssl,=20int=20type,=20int=20args);=0A+static=20int=09= alpn_cb(SSL=20*ssl,=0A+=09=09=09=09=09const=20unsigned=20char=20**out,=0A= +=09=09=09=09=09unsigned=20char=20*outlen,=0A+=09=09=09=09=09const=20= unsigned=20char=20*in,=0A+=09=09=09=09=09unsigned=20int=20inlen,=0A+=09=09= =09=09=09void=20*userdata);=0A+static=20bool=20initialize_dh(SSL_CTX=20= *context,=20bool=20isServerStart);=0A+static=20bool=20= initialize_ecdh(SSL_CTX=20*context,=20bool=20isServerStart);=0A+static=20= const=20char=20*SSLerrmessageExt(unsigned=20long=20ecode,=20const=20char=20= *replacement);=0A+static=20const=20char=20*SSLerrmessage(unsigned=20long=20= ecode);=0A+static=20bool=20init_host_context(HostsLine=20*host,=20bool=20= isServerStart);=0A+static=20void=20host_context_cleanup_cb(void=20*arg);=0A= +=0A+static=20char=20*X509_NAME_to_cstring(X509_NAME=20*name);=0A+=0A= +static=20SSL_CTX=20*SSL_context=20=3D=20NULL;=0A+static=20MemoryContext=20= SSL_hosts_memcxt=20=3D=20NULL;=0A+static=20struct=20hosts=0A+{=0A+=09/*=0A= +=09=20*=20List=20of=20HostsLine=20structures=20containing=20SSL=20= configurations=20for=0A+=09=20*=20connections=20with=20hostnames=20= defined=20in=20the=20SNI=20extension.=0A+=09=20*/=0A+=09List=09=20=20=20= *sni;=0A+=0A+=09/*=20The=20SSL=20configuration=20to=20use=20for=20= connections=20without=20SNI=20*/=0A+=09HostsLine=20=20*no_sni;=0A+=0A+=09= /*=0A+=09=20*=20The=20default=20SSL=20configuration=20to=20use=20as=20a=20= fallback=20in=20case=20no=20hostname=0A+=09=20*=20matches=20the=20= supplied=20hostname=20in=20the=20SNI=20extension.=0A+=09=20*/=0A+=09= HostsLine=20=20*default_host;=0A+}=09=09=20=20=20*SSL_hosts;=0A+=0A= +static=20bool=20dummy_ssl_passwd_cb_called=20=3D=20false;=0A+static=20= bool=20ssl_is_server_start;=0A+=0A+static=20int=09= ssl_protocol_version_to_openssl(int=20v);=0A+static=20const=20char=20= *ssl_protocol_version_to_string(int=20v);=0A+=0A+struct=20CallbackErr=0A= +{=0A+=09/*=0A+=09=20*=20Storage=20for=20passing=20certificate=20= verification=20error=20logging=20from=20the=0A+=09=20*=20callback.=0A+=09= =20*/=0A+=09char=09=20=20=20*cert_errdetail;=0A+};=0A+=0A+/*=20= ------------------------------------------------------------=20*/=0A+/*=09= =09=09=09=09=09=20Public=20interface=09=09=09=09=09=09*/=0A+/*=20= ------------------------------------------------------------=20*/=0A+=0A= +int=0A+be_tls_init(bool=20isServerStart)=0A+{=0A+=09MemoryContext=20= oldcxt;=0A+=09MemoryContext=20host_memcxt=20=3D=20NULL;=0A+=09= MemoryContextCallback=20*host_memcxt_cb;=0A+=09int=09=09=09res=20=3D=20= HOSTSFILE_DISABLED;=0A+=09struct=20hosts=20*new_hosts;=0A+=09SSL_CTX=20=20= =20=20*context=20=3D=20NULL;=0A+=09int=09=09=09ssl_ver_min=20=3D=20-1;=0A= +=09int=09=09=09ssl_ver_max=20=3D=20-1;=0A+=0A+=09/*=0A+=09=20*=20Since=20= we=20don't=20know=20which=20host=20we're=20using=20until=20the=20= ClientHello=20is=0A+=09=20*=20sent,=20ssl_loaded_verify_locations=20= *always*=20starts=20out=20as=20false.=20The=0A+=09=20*=20only=20place=20= it's=20set=20to=20true=20is=20in=20sni_clienthello_cb().=0A+=09=20*/=0A+=09= ssl_loaded_verify_locations=20=3D=20false;=0A+=0A+=09host_memcxt=20=3D=20= AllocSetContextCreate(CurrentMemoryContext,=0A+=09=09=09=09=09=09=09=09=09= =09"hosts=20file=20parser=20context",=0A+=09=09=09=09=09=09=09=09=09=09= ALLOCSET_SMALL_SIZES);=0A+=09oldcxt=20=3D=20= MemoryContextSwitchTo(host_memcxt);=0A+=0A+=09/*=20Allocate=20a=20= tentative=20replacement=20for=20SSL_hosts.=20*/=0A+=09new_hosts=20=3D=20= palloc0_object(struct=20hosts);=0A+=0A+=09/*=0A+=09=20*=20Register=20a=20= reset=20callback=20for=20the=20memory=20context=20which=20is=20= responsible=0A+=09=20*=20for=20freeing=20OpenSSL=20managed=20allocations=20= upon=20context=20deletion.=20=20The=0A+=09=20*=20callback=20is=20= allocated=20here=20to=20make=20sure=20it=20gets=20cleaned=20up=20along=20= with=0A+=09=20*=20the=20memory=20context=20it's=20registered=20for.=0A+=09= =20*/=0A+=09host_memcxt_cb=20=3D=20= palloc0_object(MemoryContextCallback);=0A+=09host_memcxt_cb->func=20=3D=20= host_context_cleanup_cb;=0A+=09host_memcxt_cb->arg=20=3D=20new_hosts;=0A= +=09MemoryContextRegisterResetCallback(host_memcxt,=20host_memcxt_cb);=0A= +=0A+=09/*=20ssl_sni=20is=20currently=20not=20supported=20by=20LibreSSL=20= */=0A+=09if=20(ssl_sni)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20The=20GUC=20= check=20hook=20should=20have=20already=20blocked=20this=20but=20to=20be=20= on=0A+=09=09=20*=20the=20safe=20side=20we=20doublecheck=20here.=0A+=09=09= =20*/=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("ssl_sni=20is=20= not=20supported=20with=20LibreSSL"));=0A+=09=09goto=20error;=0A+=09}=0A+=0A= +=09/*=20Since=20SNI=20is=20disabled,=20we=20load=20configuration=20from=20= postgresql.conf.=20*/=0A+=09if=20(res=20=3D=3D=20HOSTSFILE_DISABLED)=0A+=09= {=0A+=09=09HostsLine=20=20*pgconf=20=3D=20palloc0(sizeof(HostsLine));=0A= +=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=09if=20= (!init_host_context(pgconf,=20isServerStart))=0A+=09=09=09goto=20error;=0A= +=0A+=09=09/*=0A+=09=09=20*=20If=20postgresql.conf=20is=20used=20to=20= configure=20SSL=20then=20by=20definition=20it=0A+=09=09=20*=20will=20be=20= the=20default=20context=20as=20we=20don't=20have=20per-host=20config.=0A= +=09=09=20*/=0A+=09=09new_hosts->default_host=20=3D=20pgconf;=0A+=09}=0A= +=0A+=09/*=0A+=09=20*=20Make=20sure=20we=20have=20at=20least=20one=20= configuration=20loaded=20to=20use,=20without=0A+=09=20*=20that=20we=20= cannot=20drive=20a=20connection=20so=20exit.=0A+=09=20*/=0A+=09if=20= (!new_hosts->default_host)=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("no=20SSL=20configurations=20loaded"));=0A+=09=09goto=20= error;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20We=20want=20to=20use=20the=20= default=20context=20as=20the=20one=20to=20drive=20the=20handshake=20so=0A= +=09=20*=20avoid=20creating=20a=20new=20one=20and=20use=20the=20already=20= existing=20default=20one=0A+=09=20*=20instead.=0A+=09=20*/=0A+=09context=20= =3D=20new_hosts->default_host->ssl_ctx;=0A+=0A+=09/*=0A+=09=20*=20Since=20= we=20don't=20allocate=20a=20new=20SSL_CTX=20here=20like=20we=20do=20when=20= SNI=20has=20been=0A+=09=20*=20enabled=20we=20need=20to=20bump=20the=20= reference=20count=20on=20context=20to=20avoid=20double=0A+=09=20*=20free=20= of=20the=20context=20when=20using=20the=20same=20cleanup=20logic=20= across=20the=20cases.=0A+=09=20*/=0A+=09SSL_CTX_up_ref(context);=0A+=0A+=09= /*=0A+=09=20*=20Disable=20OpenSSL's=20moving-write-buffer=20sanity=20= check,=20because=20it=20causes=0A+=09=20*=20unnecessary=20failures=20in=20= nonblocking=20send=20cases.=0A+=09=20*/=0A+=09SSL_CTX_set_mode(context,=20= SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A+=0A+=09if=20= (ssl_min_protocol_version)=0A+=09{=0A+=09=09ssl_ver_min=20=3D=20= ssl_protocol_version_to_openssl(ssl_min_protocol_version);=0A+=0A+=09=09= if=20(ssl_ver_min=20=3D=3D=20-1)=0A+=09=09{=0A+=09=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09/*-=20= translator:=20first=20%s=20is=20a=20GUC=20option=20name,=20second=20%s=20= is=20its=20value=20*/=0A+=09=09=09=09=09(errmsg("\"%s\"=20setting=20= \"%s\"=20not=20supported=20by=20this=20build",=0A+=09=09=09=09=09=09=09= "ssl_min_protocol_version",=0A+=09=09=09=09=09=09=09= GetConfigOption("ssl_min_protocol_version",=0A+=09=09=09=09=09=09=09=09=09= =09=09false,=20false))));=0A+=09=09=09goto=20error;=0A+=09=09}=0A+=0A+=09= =09if=20(!SSL_CTX_set_min_proto_version(context,=20ssl_ver_min))=0A+=09=09= {=0A+=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= =09(errmsg("could=20not=20set=20minimum=20SSL=20protocol=20version")));=0A= +=09=09=09goto=20error;=0A+=09=09}=0A+=09}=0A+=0A+=09if=20= (ssl_max_protocol_version)=0A+=09{=0A+=09=09ssl_ver_max=20=3D=20= ssl_protocol_version_to_openssl(ssl_max_protocol_version);=0A+=0A+=09=09= if=20(ssl_ver_max=20=3D=3D=20-1)=0A+=09=09{=0A+=09=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09/*-=20= translator:=20first=20%s=20is=20a=20GUC=20option=20name,=20second=20%s=20= is=20its=20value=20*/=0A+=09=09=09=09=09(errmsg("\"%s\"=20setting=20= \"%s\"=20not=20supported=20by=20this=20build",=0A+=09=09=09=09=09=09=09= "ssl_max_protocol_version",=0A+=09=09=09=09=09=09=09= GetConfigOption("ssl_max_protocol_version",=0A+=09=09=09=09=09=09=09=09=09= =09=09false,=20false))));=0A+=09=09=09goto=20error;=0A+=09=09}=0A+=0A+=09= =09if=20(!SSL_CTX_set_max_proto_version(context,=20ssl_ver_max))=0A+=09=09= {=0A+=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= =09(errmsg("could=20not=20set=20maximum=20SSL=20protocol=20version")));=0A= +=09=09=09goto=20error;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20Check=20= compatibility=20of=20min/max=20protocols=20*/=0A+=09if=20= (ssl_min_protocol_version=20&&=0A+=09=09ssl_max_protocol_version)=0A+=09= {=0A+=09=09/*=0A+=09=09=20*=20No=20need=20to=20check=20for=20invalid=20= values=20(-1)=20for=20each=20protocol=20number=0A+=09=09=20*=20as=20the=20= code=20above=20would=20have=20already=20generated=20an=20error.=0A+=09=09= =20*/=0A+=09=09if=20(ssl_ver_min=20>=20ssl_ver_max)=0A+=09=09{=0A+=09=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=20errmsg("could=20= not=20set=20SSL=20protocol=20version=20range"),=0A+=09=09=09=09=09=20= errdetail("\"%s\"=20cannot=20be=20higher=20than=20\"%s\"",=0A+=09=09=09=09= =09=09=09=20=20=20"ssl_min_protocol_version",=0A+=09=09=09=09=09=09=09=20= =20=20"ssl_max_protocol_version")));=0A+=09=09=09goto=20error;=0A+=09=09= }=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Disallow=20SSL=20session=20tickets.=20= =20LibreSSL=20only=20supports=20disabling=0A+=09=20*=20stateless=20= tickets.=0A+=09=20*/=0A+=09SSL_CTX_set_options(context,=20= SSL_OP_NO_TICKET);=0A+=0A+=09/*=20disallow=20SSL=20session=20caching,=20= too=20*/=0A+=09SSL_CTX_set_session_cache_mode(context,=20= SSL_SESS_CACHE_OFF);=0A+=0A+=09/*=20disallow=20SSL=20compression=20*/=0A= +=09SSL_CTX_set_options(context,=20SSL_OP_NO_COMPRESSION);=0A+=0A+=09/*=0A= +=09=20*=20Disallow=20SSL=20renegotiation.=20=20This=20concerns=20only=20= TLSv1.2=20and=20older=0A+=09=20*=20protocol=20versions,=20as=20TLSv1.3=20= has=20no=20support=20for=20renegotiation.=0A+=09=20*=20= SSL_OP_NO_RENEGOTIATION=20is=20available=20in=20LibreSSL=20since=20v4.=0A= +=09=20*=20SSL_OP_NO_CLIENT_RENEGOTIATION=20is=20available=20in=20since=20= 2.5.1=20disallowing=0A+=09=20*=20all=20client-initiated=20renegotiation=20= (this=20is=20usually=20on=20by=20default).=0A+=09=20*/=0A+#ifdef=20= SSL_OP_NO_RENEGOTIATION=0A+=09SSL_CTX_set_options(context,=20= SSL_OP_NO_RENEGOTIATION);=0A+#endif=0A+#ifdef=20= SSL_OP_NO_CLIENT_RENEGOTIATION=0A+=09SSL_CTX_set_options(context,=20= SSL_OP_NO_CLIENT_RENEGOTIATION);=0A+#endif=0A+=0A+=09/*=20set=20up=20= ephemeral=20DH=20and=20ECDH=20keys=20*/=0A+=09if=20= (!initialize_dh(context,=20isServerStart))=0A+=09=09goto=20error;=0A+=09= if=20(!initialize_ecdh(context,=20isServerStart))=0A+=09=09goto=20error;=0A= +=0A+=09/*=20set=20up=20the=20allowed=20cipher=20list=20for=20TLSv1.2=20= and=20below=20*/=0A+=09if=20(SSL_CTX_set_cipher_list(context,=20= SSLCipherList)=20!=3D=201)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09=20errmsg("could=20not=20set=20the=20TLSv1.2=20cipher=20list=20= (no=20valid=20ciphers=20available)")));=0A+=09=09goto=20error;=0A+=09}=0A= +=0A+=09/*=0A+=09=20*=20Set=20up=20the=20allowed=20cipher=20suites=20for=20= TLSv1.3.=20If=20the=20GUC=20is=20an=20empty=0A+=09=20*=20string=20we=20= leave=20the=20allowed=20suites=20to=20be=20the=20OpenSSL=20default=20= value.=0A+=09=20*/=0A+=09if=20(SSLCipherSuites[0])=0A+=09{=0A+=09=09/*=20= set=20up=20the=20allowed=20cipher=20suites=20*/=0A+=09=09if=20= (SSL_CTX_set_ciphersuites(context,=20SSLCipherSuites)=20!=3D=201)=0A+=09=09= {=0A+=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= =09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=20= errmsg("could=20not=20set=20the=20TLSv1.3=20cipher=20suites=20(no=20= valid=20ciphers=20available)")));=0A+=09=09=09goto=20error;=0A+=09=09}=0A= +=09}=0A+=0A+=09/*=20Let=20server=20choose=20order=20*/=0A+=09if=20= (SSLPreferServerCiphers)=0A+=09=09SSL_CTX_set_options(context,=20= SSL_OP_CIPHER_SERVER_PREFERENCE);=0A+=0A+=09/*=0A+=09=20*=20Success!=20=20= Replace=20any=20existing=20SSL_context=20and=20host=20configurations.=0A= +=09=20*/=0A+=09if=20(SSL_context)=0A+=09{=0A+=09=09= SSL_CTX_free(SSL_context);=0A+=09=09SSL_context=20=3D=20NULL;=0A+=09}=0A= +=0A+=09MemoryContextSwitchTo(oldcxt);=0A+=0A+=09if=20(SSL_hosts_memcxt)=0A= +=09=09MemoryContextDelete(SSL_hosts_memcxt);=0A+=0A+=09SSL_hosts_memcxt=20= =3D=20host_memcxt;=0A+=09SSL_hosts=20=3D=20new_hosts;=0A+=09SSL_context=20= =3D=20context;=0A+=0A+=09return=200;=0A+=0A+=09/*=0A+=09=20*=20Clean=20= up=20by=20releasing=20working=20SSL=20contexts=20as=20well=20as=20= allocations=0A+=09=20*=20performed=20during=20parsing.=20=20Since=20all=20= our=20allocations=20are=20done=20in=20a=0A+=09=20*=20local=20memory=20= context=20all=20we=20need=20to=20do=20is=20delete=20it.=0A+=09=20*/=0A= +error:=0A+=09if=20(context)=0A+=09=09SSL_CTX_free(context);=0A+=0A+=09= MemoryContextSwitchTo(oldcxt);=0A+=09MemoryContextDelete(host_memcxt);=0A= +=09return=20-1;=0A+}=0A+=0A+/*=0A+=20*=20host_context_cleanup_cb=0A+=20= *=0A+=20*=20Memory=20context=20reset=20callback=20for=20clearing=20= LibreSSL=20managed=20resources=20when=0A+=20*=20hosts=20are=20reloaded=20= and=20the=20previous=20set=20of=20configured=20hosts=20are=20freed.=20As=0A= +=20*=20all=20hosts=20are=20allocated=20in=20a=20single=20context=20we=20= don't=20need=20to=20free=20each=20host=0A+=20*=20individually,=20just=20= resources=20managed=20by=20LibreSSL.=0A+=20*/=0A+static=20void=0A= +host_context_cleanup_cb(void=20*arg)=0A+{=0A+=09struct=20hosts=20*hosts=20= =3D=20arg;=0A+=0A+=09Assert(hosts->sni=20=3D=3D=20NIL);=0A+=09= Assert(!hosts->no_sni);=0A+=0A+=09if=20(hosts->default_host=20&&=20= hosts->default_host->ssl_ctx)=0A+=09=09= SSL_CTX_free(hosts->default_host->ssl_ctx);=0A+}=0A+=0A+static=20bool=0A= +init_host_context(HostsLine=20*host,=20bool=20isServerStart)=0A+{=0A+=09= SSL_CTX=20=20=20=20*ctx=20=3D=20SSL_CTX_new(SSLv23_method());=0A+=0A+=09= if=20(!ctx)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20= LOG,=0A+=09=09=09=09(errmsg("could=20not=20create=20SSL=20context:=20= %s",=0A+=09=09=09=09=09=09SSLerrmessage(ERR_get_error()))));=0A+=09=09= goto=20error;=0A+=09}=0A+=0A+=09/*=20Call=20init=20hook,=20usually=20to=20= set=20password=20callback=20*/=0A+=09(*openssl_tls_init_hook)=20(ctx,=20= isServerStart);=0A+=0A+=09/*=0A+=09=20*=20Load=20and=20verify=20server's=20= certificate=20and=20private=20key=0A+=09=20*/=0A+=09if=20= (SSL_CTX_use_certificate_chain_file(ctx,=20host->ssl_cert)=20!=3D=201)=0A= +=09{=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20errmsg("could=20= not=20load=20server=20certificate=20file=20\"%s\":=20%s",=0A+=09=09=09=09= =09=09host->ssl_cert,=20SSLerrmessage(ERR_get_error()))));=0A+=09=09goto=20= error;=0A+=09}=0A+=0A+=09if=20= (!check_ssl_key_file_permissions(host->ssl_key,=20isServerStart))=0A+=09=09= goto=20error;=0A+=0A+=09/*=20used=20by=20the=20callback=20*/=0A+=09= ssl_is_server_start=20=3D=20isServerStart;=0A+=0A+=09/*=0A+=09=20*=20OK,=20= try=20to=20load=20the=20private=20key=20file.=0A+=09=20*/=0A+=09= dummy_ssl_passwd_cb_called=20=3D=20false;=0A+=0A+=09if=20= (SSL_CTX_use_PrivateKey_file(ctx,=0A+=09=09=09=09=09=09=09=09=09= host->ssl_key,=0A+=09=09=09=09=09=09=09=09=09SSL_FILETYPE_PEM)=20!=3D=20= 1)=0A+=09{=0A+=09=09if=20(dummy_ssl_passwd_cb_called)=0A+=09=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=20= errmsg("private=20key=20file=20\"%s\"=20cannot=20be=20reloaded=20because=20= it=20requires=20a=20passphrase",=0A+=09=09=09=09=09=09=09= host->ssl_key)));=0A+=09=09else=0A+=09=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A= +=09=09=09=09=09=20errmsg("could=20not=20load=20private=20key=20file=20= \"%s\":=20%s",=0A+=09=09=09=09=09=09=09host->ssl_key,=20= SSLerrmessage(ERR_get_error()))));=0A+=09=09goto=20error;=0A+=09}=0A+=0A= +=09if=20(SSL_CTX_check_private_key(ctx)=20!=3D=201)=0A+=09{=0A+=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20errmsg("check=20= of=20private=20key=20failed:=20%s",=0A+=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09goto=20error;=0A+=09}=0A+=0A= +=09/*=0A+=09=20*=20Load=20CA=20store,=20so=20we=20can=20verify=20client=20= certificates=20if=20needed.=0A+=09=20*/=0A+=09if=20(host->ssl_ca=20&&=20= host->ssl_ca[0])=0A+=09{=0A+=09=09STACK_OF(X509_NAME)=20*=20= root_cert_list;=0A+=0A+=09=09if=20(SSL_CTX_load_verify_locations(ctx,=20= host->ssl_ca,=20NULL)=20!=3D=201=20||=0A+=09=09=09(root_cert_list=20=3D=20= SSL_load_client_CA_file(host->ssl_ca))=20=3D=3D=20NULL)=0A+=09=09{=0A+=09= =09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=20errmsg("could=20= not=20load=20root=20certificate=20file=20\"%s\":=20%s",=0A+=09=09=09=09=09= =09=09host->ssl_ca,=20SSLerrmessage(ERR_get_error()))));=0A+=09=09=09= goto=20error;=0A+=09=09}=0A+=0A+=09=09/*=0A+=09=09=20*=20Tell=20OpenSSL=20= to=20send=20the=20list=20of=20root=20certs=20we=20trust=20to=20clients=20= in=0A+=09=09=20*=20CertificateRequests.=20=20This=20lets=20a=20client=20= with=20a=20keystore=20select=20the=0A+=09=09=20*=20appropriate=20client=20= certificate=20to=20send=20to=20us.=20=20Also,=20this=20ensures=0A+=09=09=20= *=20that=20the=20SSL=20context=20will=20"own"=20the=20root_cert_list=20= and=20remember=20to=0A+=09=09=20*=20free=20it=20when=20no=20longer=20= needed.=0A+=09=09=20*/=0A+=09=09SSL_CTX_set_client_CA_list(ctx,=20= root_cert_list);=0A+=09}=0A+=0A+=09/*----------=0A+=09=20*=20Load=20the=20= Certificate=20Revocation=20List=20(CRL).=0A+=09=20*=20= http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.htm= l=0A+=09=20*----------=0A+=09=20*/=0A+=09if=20(ssl_crl_file[0]=20||=20= ssl_crl_dir[0])=0A+=09{=0A+=09=09X509_STORE=20*cvstore=20=3D=20= SSL_CTX_get_cert_store(ctx);=0A+=0A+=09=09if=20(cvstore)=0A+=09=09{=0A+=09= =09=09/*=20Set=20the=20flags=20to=20check=20against=20the=20complete=20= CRL=20chain=20*/=0A+=09=09=09if=20(X509_STORE_load_locations(cvstore,=0A= +=09=09=09=09=09=09=09=09=09=09=20=20ssl_crl_file[0]=20?=20ssl_crl_file=20= :=20NULL,=0A+=09=09=09=09=09=09=09=09=09=09=20=20ssl_crl_dir[0]=20?=20= ssl_crl_dir=20:=20NULL)=0A+=09=09=09=09=3D=3D=201)=0A+=09=09=09{=0A+=09=09= =09=09X509_STORE_set_flags(cvstore,=0A+=09=09=09=09=09=09=09=09=09=20= X509_V_FLAG_CRL_CHECK=20|=20X509_V_FLAG_CRL_CHECK_ALL);=0A+=09=09=09}=0A= +=09=09=09else=20if=20(ssl_crl_dir[0]=20=3D=3D=200)=0A+=09=09=09{=0A+=09=09= =09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09=20= errmsg("could=20not=20load=20SSL=20certificate=20revocation=20list=20= file=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09ssl_crl_file,=20= SSLerrmessage(ERR_get_error()))));=0A+=09=09=09=09goto=20error;=0A+=09=09= =09}=0A+=09=09=09else=20if=20(ssl_crl_file[0]=20=3D=3D=200)=0A+=09=09=09= {=0A+=09=09=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09= =09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09=20= errmsg("could=20not=20load=20SSL=20certificate=20revocation=20list=20= directory=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09ssl_crl_dir,=20= SSLerrmessage(ERR_get_error()))));=0A+=09=09=09=09goto=20error;=0A+=09=09= =09}=0A+=09=09=09else=0A+=09=09=09{=0A+=09=09=09=09ereport(isServerStart=20= ?=20FATAL=20:=20LOG,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=09=09=20= errmsg("could=20not=20load=20SSL=20certificate=20revocation=20list=20= file=20\"%s\"=20or=20directory=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09= ssl_crl_file,=20ssl_crl_dir,=0A+=09=09=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09=09=09goto=20error;=0A+=09=09= =09}=0A+=09=09}=0A+=09}=0A+=0A+=09host->ssl_ctx=20=3D=20ctx;=0A+=09= return=20true;=0A+=0A+error:=0A+=09if=20(ctx)=0A+=09=09= SSL_CTX_free(ctx);=0A+=09return=20false;=0A+}=0A+=0A+void=0A= +be_tls_destroy(void)=0A+{=0A+=09if=20(SSL_context)=0A+=09=09= SSL_CTX_free(SSL_context);=0A+=09SSL_context=20=3D=20NULL;=0A+=09= ssl_loaded_verify_locations=20=3D=20false;=0A+}=0A+=0A+int=0A= +be_tls_open_server(Port=20*port)=0A+{=0A+=09int=09=09=09r;=0A+=09int=09=09= =09err;=0A+=09int=09=09=09waitfor;=0A+=09unsigned=20long=20ecode;=0A+=09= bool=09=09give_proto_hint;=0A+=09static=20struct=20CallbackErr=20= err_context;=0A+=0A+=09Assert(!port->ssl);=0A+=09Assert(!port->peer);=0A= +=0A+=09if=20(!SSL_context)=0A+=09{=0A+=09=09ereport(COMMERROR,=0A+=09=09= =09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=20= errmsg("could=20not=20initialize=20SSL=20connection:=20SSL=20context=20= not=20set=20up")));=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09/*=20set=20= up=20debugging/info=20callback=20*/=0A+=09= SSL_CTX_set_info_callback(SSL_context,=20info_cb);=0A+=0A+=09/*=20enable=20= ALPN=20*/=0A+=09SSL_CTX_set_alpn_select_cb(SSL_context,=20alpn_cb,=20= port);=0A+=0A+=09if=20(!(port->ssl=20=3D=20SSL_new(SSL_context)))=0A+=09= {=0A+=09=09ereport(COMMERROR,=0A+=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=20errmsg("could=20= not=20initialize=20SSL=20connection:=20%s",=0A+=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09return=20-1;=0A+=09}=0A+=09= if=20(!ssl_set_port_bio(port))=0A+=09{=0A+=09=09ereport(COMMERROR,=0A+=09= =09=09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=20= errmsg("could=20not=20set=20SSL=20socket:=20%s",=0A+=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09= /*=0A+=09=20*=20Install=20the=20client=20cert=20verification=20callback=20= here=20if=20the=20user=0A+=09=20*=20configured=20a=20CA.=0A+=09=20*/=0A+=09= if=20(SSL_hosts->default_host->ssl_ca=20&&=20= SSL_hosts->default_host->ssl_ca[0])=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20= Always=20ask=20for=20SSL=20client=20cert,=20but=20don't=20fail=20if=20= it's=20not=0A+=09=09=20*=20presented.=20=20We=20might=20fail=20such=20= connections=20later,=20depending=20on=20what=0A+=09=09=20*=20we=20find=20= in=20pg_hba.conf.=0A+=09=09=20*/=0A+=09=09SSL_set_verify(port->ssl,=0A+=09= =09=09=09=09=20=20=20(SSL_VERIFY_PEER=20|=20SSL_VERIFY_CLIENT_ONCE),=0A+=09= =09=09=09=09=20=20=20verify_cb);=0A+=0A+=09=09= ssl_loaded_verify_locations=20=3D=20true;=0A+=09}=0A+=0A+=09= err_context.cert_errdetail=20=3D=20NULL;=0A+=09= SSL_set_ex_data(port->ssl,=200,=20&err_context);=0A+=0A+=09= port->ssl_in_use=20=3D=20true;=0A+=0A+aloop:=0A+=0A+=09/*=0A+=09=20*=20= Prepare=20to=20call=20SSL_get_error()=20by=20clearing=20thread's=20= OpenSSL=20error=0A+=09=20*=20queue.=20=20In=20general,=20the=20current=20= thread's=20error=20queue=20must=20be=20empty=0A+=09=20*=20before=20the=20= TLS/SSL=20I/O=20operation=20is=20attempted,=20or=20SSL_get_error()=20= will=0A+=09=20*=20not=20work=20reliably.=20=20An=20extension=20may=20= have=20failed=20to=20clear=20the=0A+=09=20*=20per-thread=20error=20queue=20= following=20another=20call=20to=20an=20OpenSSL=20I/O=0A+=09=20*=20= routine.=0A+=09=20*/=0A+=09errno=20=3D=200;=0A+=09ERR_clear_error();=0A+=09= r=20=3D=20SSL_accept(port->ssl);=0A+=09if=20(r=20<=3D=200)=0A+=09{=0A+=09= =09err=20=3D=20SSL_get_error(port->ssl,=20r);=0A+=0A+=09=09/*=0A+=09=09=20= *=20Other=20clients=20of=20OpenSSL=20in=20the=20backend=20may=20fail=20= to=20call=0A+=09=09=20*=20ERR_get_error(),=20but=20we=20always=20do,=20= so=20as=20to=20not=20cause=20problems=20for=0A+=09=09=20*=20OpenSSL=20= clients=20that=20don't=20call=20ERR_clear_error()=20defensively.=20=20Be=0A= +=09=09=20*=20sure=20that=20this=20happens=20by=20calling=20now.=20= SSL_get_error()=20relies=20on=0A+=09=09=20*=20the=20OpenSSL=20per-thread=20= error=20queue=20being=20intact,=20so=20this=20is=20the=0A+=09=09=20*=20= earliest=20possible=20point=20ERR_get_error()=20may=20be=20called.=0A+=09= =09=20*/=0A+=09=09ecode=20=3D=20ERR_get_error();=0A+=09=09switch=20(err)=0A= +=09=09{=0A+=09=09=09case=20SSL_ERROR_WANT_READ:=0A+=09=09=09case=20= SSL_ERROR_WANT_WRITE:=0A+=09=09=09=09/*=20not=20allowed=20during=20= connection=20establishment=20*/=0A+=09=09=09=09Assert(!port->noblock);=0A= +=0A+=09=09=09=09/*=0A+=09=09=09=09=20*=20No=20need=20to=20care=20about=20= timeouts/interrupts=20here.=20At=20this=0A+=09=09=09=09=20*=20point=20= authentication_timeout=20still=20employs=0A+=09=09=09=09=20*=20= StartupPacketTimeoutHandler()=20which=20directly=20exits.=0A+=09=09=09=09= =20*/=0A+=09=09=09=09if=20(err=20=3D=3D=20SSL_ERROR_WANT_READ)=0A+=09=09=09= =09=09waitfor=20=3D=20WL_SOCKET_READABLE=20|=20WL_EXIT_ON_PM_DEATH;=0A+=09= =09=09=09else=0A+=09=09=09=09=09waitfor=20=3D=20WL_SOCKET_WRITEABLE=20|=20= WL_EXIT_ON_PM_DEATH;=0A+=0A+=09=09=09=09(void)=20WaitLatchOrSocket(NULL,=20= waitfor,=20port->sock,=200,=0A+=09=09=09=09=09=09=09=09=09=09=20= WAIT_EVENT_SSL_OPEN_SERVER);=0A+=09=09=09=09goto=20aloop;=0A+=09=09=09= case=20SSL_ERROR_SYSCALL:=0A+=09=09=09=09if=20(r=20<=200=20&&=20errno=20= !=3D=200)=0A+=09=09=09=09=09ereport(COMMERROR,=0A+=09=09=09=09=09=09=09= (errcode_for_socket_access(),=0A+=09=09=09=09=09=09=09=20errmsg("could=20= not=20accept=20SSL=20connection:=20%m")));=0A+=09=09=09=09else=0A+=09=09=09= =09=09ereport(COMMERROR,=0A+=09=09=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=09=09=20= errmsg("could=20not=20accept=20SSL=20connection:=20EOF=20detected")));=0A= +=09=09=09=09break;=0A+=09=09=09case=20SSL_ERROR_SSL:=0A+=09=09=09=09= switch=20(ERR_GET_REASON(ecode))=0A+=09=09=09=09{=0A+=09=09=09=09=09=09= /*=0A+=09=09=09=09=09=09=20*=20UNSUPPORTED_PROTOCOL,=20= WRONG_VERSION_NUMBER,=20and=0A+=09=09=09=09=09=09=20*=20= TLSV1_ALERT_PROTOCOL_VERSION=20have=20been=20observed=0A+=09=09=09=09=09=09= =20*=20when=20trying=20to=20communicate=20with=20an=20old=20OpenSSL=0A+=09= =09=09=09=09=09=20*=20library,=20or=20when=20the=20client=20and=20server=20= specify=0A+=09=09=09=09=09=09=20*=20disjoint=20protocol=20ranges.=20=20= NO_PROTOCOLS_AVAILABLE=0A+=09=09=09=09=09=09=20*=20occurs=20if=20there's=20= a=20local=20misconfiguration=20(which=0A+=09=09=09=09=09=09=20*=20can=20= happen=20despite=20our=20checks,=20if=20openssl.cnf=0A+=09=09=09=09=09=09= =20*=20injects=20a=20limit=20we=20didn't=20account=20for).=20=20It's=20= not=0A+=09=09=09=09=09=09=20*=20very=20clear=20what=20would=20make=20= OpenSSL=20return=20the=20other=0A+=09=09=09=09=09=09=20*=20codes=20= listed=20here,=20but=20a=20hint=20about=20protocol=0A+=09=09=09=09=09=09=20= *=20versions=20seems=20like=20it's=20appropriate=20for=20all.=0A+=09=09=09= =09=09=09=20*/=0A+=09=09=09=09=09case=20SSL_R_NO_PROTOCOLS_AVAILABLE:=0A= +=09=09=09=09=09case=20SSL_R_UNSUPPORTED_PROTOCOL:=0A+=09=09=09=09=09= case=20SSL_R_BAD_PROTOCOL_VERSION_NUMBER:=0A+=09=09=09=09=09case=20= SSL_R_UNKNOWN_PROTOCOL:=0A+=09=09=09=09=09case=20= SSL_R_UNKNOWN_SSL_VERSION:=0A+=09=09=09=09=09case=20= SSL_R_UNSUPPORTED_SSL_VERSION:=0A+=09=09=09=09=09case=20= SSL_R_WRONG_SSL_VERSION:=0A+=09=09=09=09=09case=20= SSL_R_WRONG_VERSION_NUMBER:=0A+=09=09=09=09=09case=20= SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:=0A+#ifdef=20SSL_R_VERSION_TOO_LOW=0A= +=09=09=09=09=09case=20SSL_R_VERSION_TOO_LOW:=0A+#endif=0A+=09=09=09=09=09= =09give_proto_hint=20=3D=20true;=0A+=09=09=09=09=09=09break;=0A+=09=09=09= =09=09default:=0A+=09=09=09=09=09=09give_proto_hint=20=3D=20false;=0A+=09= =09=09=09=09=09break;=0A+=09=09=09=09}=0A+=09=09=09=09ereport(COMMERROR,=0A= +=09=09=09=09=09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09= =09=20errmsg("could=20not=20accept=20SSL=20connection:=20%s",=0A+=09=09=09= =09=09=09=09=09SSLerrmessage(ecode)),=0A+=09=09=09=09=09=09=20= err_context.cert_errdetail=20?=20errdetail_internal("%s",=20= err_context.cert_errdetail)=20:=200,=0A+=09=09=09=09=09=09=20= give_proto_hint=20?=0A+=09=09=09=09=09=09=20errhint("This=20may=20= indicate=20that=20the=20client=20does=20not=20support=20any=20SSL=20= protocol=20version=20between=20%s=20and=20%s.",=0A+=09=09=09=09=09=09=09=09= =20ssl_min_protocol_version=20?=0A+=09=09=09=09=09=09=09=09=20= ssl_protocol_version_to_string(ssl_min_protocol_version)=20:=0A+=09=09=09= =09=09=09=09=09=20MIN_OPENSSL_TLS_VERSION,=0A+=09=09=09=09=09=09=09=09=20= ssl_max_protocol_version=20?=0A+=09=09=09=09=09=09=09=09=20= ssl_protocol_version_to_string(ssl_max_protocol_version)=20:=0A+=09=09=09= =09=09=09=09=09=20MAX_OPENSSL_TLS_VERSION)=20:=200));=0A+=09=09=09=09if=20= (err_context.cert_errdetail)=0A+=09=09=09=09=09= pfree(err_context.cert_errdetail);=0A+=09=09=09=09break;=0A+=09=09=09= case=20SSL_ERROR_ZERO_RETURN:=0A+=09=09=09=09ereport(COMMERROR,=0A+=09=09= =09=09=09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=09=20= errmsg("could=20not=20accept=20SSL=20connection:=20EOF=20detected")));=0A= +=09=09=09=09break;=0A+=09=09=09default:=0A+=09=09=09=09= ereport(COMMERROR,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=09=20= errmsg("unrecognized=20SSL=20error=20code:=20%d",=0A+=09=09=09=09=09=09=09= =09err)));=0A+=09=09=09=09break;=0A+=09=09}=0A+=09=09return=20-1;=0A+=09= }=0A+=0A+=09/*=20Get=20the=20protocol=20selected=20by=20ALPN=20*/=0A+=09= port->alpn_used=20=3D=20false;=0A+=09{=0A+=09=09const=20unsigned=20char=20= *selected;=0A+=09=09unsigned=20int=20len;=0A+=0A+=09=09= SSL_get0_alpn_selected(port->ssl,=20&selected,=20&len);=0A+=0A+=09=09/*=20= If=20ALPN=20is=20used,=20check=20that=20we=20negotiated=20the=20expected=20= protocol=20*/=0A+=09=09if=20(selected=20!=3D=20NULL)=0A+=09=09{=0A+=09=09= =09if=20(len=20=3D=3D=20strlen(PG_ALPN_PROTOCOL)=20&&=0A+=09=09=09=09= memcmp(selected,=20PG_ALPN_PROTOCOL,=20strlen(PG_ALPN_PROTOCOL))=20=3D=3D=20= 0)=0A+=09=09=09{=0A+=09=09=09=09port->alpn_used=20=3D=20true;=0A+=09=09=09= }=0A+=09=09=09else=0A+=09=09=09{=0A+=09=09=09=09/*=20shouldn't=20happen=20= */=0A+=09=09=09=09ereport(COMMERROR,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=09=20= errmsg("received=20SSL=20connection=20request=20with=20unexpected=20ALPN=20= protocol")));=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20Get=20= client=20certificate,=20if=20available.=20*/=0A+=09port->peer=20=3D=20= SSL_get_peer_certificate(port->ssl);=0A+=0A+=09/*=20and=20extract=20the=20= Common=20Name=20and=20Distinguished=20Name=20from=20it.=20*/=0A+=09= port->peer_cn=20=3D=20NULL;=0A+=09port->peer_dn=20=3D=20NULL;=0A+=09= port->peer_cert_valid=20=3D=20false;=0A+=09if=20(port->peer=20!=3D=20= NULL)=0A+=09{=0A+=09=09int=09=09=09len;=0A+=09=09X509_NAME=20=20= *x509name=20=3D=20X509_get_subject_name(port->peer);=0A+=09=09char=09=20=20= =20*peer_dn;=0A+=09=09BIO=09=09=20=20=20*bio=20=3D=20NULL;=0A+=09=09= BUF_MEM=20=20=20=20*bio_buf=20=3D=20NULL;=0A+=0A+=09=09len=20=3D=20= X509_NAME_get_text_by_NID(x509name,=20NID_commonName,=20NULL,=200);=0A+=09= =09if=20(len=20!=3D=20-1)=0A+=09=09{=0A+=09=09=09char=09=20=20=20= *peer_cn;=0A+=0A+=09=09=09peer_cn=20=3D=20= MemoryContextAlloc(TopMemoryContext,=20len=20+=201);=0A+=09=09=09r=20=3D=20= X509_NAME_get_text_by_NID(x509name,=20NID_commonName,=20peer_cn,=0A+=09=09= =09=09=09=09=09=09=09=09=20=20len=20+=201);=0A+=09=09=09peer_cn[len]=20=3D= =20'\0';=0A+=09=09=09if=20(r=20!=3D=20len)=0A+=09=09=09{=0A+=09=09=09=09= /*=20shouldn't=20happen=20*/=0A+=09=09=09=09pfree(peer_cn);=0A+=09=09=09=09= return=20-1;=0A+=09=09=09}=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20Reject=20= embedded=20NULLs=20in=20certificate=20common=20name=20to=20prevent=0A+=09= =09=09=20*=20attacks=20like=20CVE-2009-4034.=0A+=09=09=09=20*/=0A+=09=09=09= if=20(len=20!=3D=20strlen(peer_cn))=0A+=09=09=09{=0A+=09=09=09=09= ereport(COMMERROR,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=09=20= errmsg("SSL=20certificate's=20common=20name=20contains=20embedded=20= null")));=0A+=09=09=09=09pfree(peer_cn);=0A+=09=09=09=09return=20-1;=0A+=09= =09=09}=0A+=0A+=09=09=09port->peer_cn=20=3D=20peer_cn;=0A+=09=09}=0A+=0A= +=09=09bio=20=3D=20BIO_new(BIO_s_mem());=0A+=09=09if=20(!bio)=0A+=09=09{=0A= +=09=09=09if=20(port->peer_cn=20!=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09= pfree(port->peer_cn);=0A+=09=09=09=09port->peer_cn=20=3D=20NULL;=0A+=09=09= =09}=0A+=09=09=09return=20-1;=0A+=09=09}=0A+=0A+=09=09/*=0A+=09=09=20*=20= RFC2253=20is=20the=20closest=20thing=20to=20an=20accepted=20standard=20= format=20for=0A+=09=09=20*=20DNs.=20We=20have=20documented=20how=20to=20= produce=20this=20format=20from=20a=0A+=09=09=20*=20certificate.=20It=20= uses=20commas=20instead=20of=20slashes=20for=20delimiters,=0A+=09=09=20*=20= which=20make=20regular=20expression=20matching=20a=20bit=20easier.=20= Also=20note=20that=0A+=09=09=20*=20it=20prints=20the=20Subject=20fields=20= in=20reverse=20order.=0A+=09=09=20*/=0A+=09=09if=20= (X509_NAME_print_ex(bio,=20x509name,=200,=20XN_FLAG_RFC2253)=20=3D=3D=20= -1=20||=0A+=09=09=09BIO_get_mem_ptr(bio,=20&bio_buf)=20<=3D=200)=0A+=09=09= {=0A+=09=09=09BIO_free(bio);=0A+=09=09=09if=20(port->peer_cn=20!=3D=20= NULL)=0A+=09=09=09{=0A+=09=09=09=09pfree(port->peer_cn);=0A+=09=09=09=09= port->peer_cn=20=3D=20NULL;=0A+=09=09=09}=0A+=09=09=09return=20-1;=0A+=09= =09}=0A+=09=09peer_dn=20=3D=20MemoryContextAlloc(TopMemoryContext,=20= bio_buf->length=20+=201);=0A+=09=09memcpy(peer_dn,=20bio_buf->data,=20= bio_buf->length);=0A+=09=09len=20=3D=20bio_buf->length;=0A+=09=09= BIO_free(bio);=0A+=09=09peer_dn[len]=20=3D=20'\0';=0A+=09=09if=20(len=20= !=3D=20strlen(peer_dn))=0A+=09=09{=0A+=09=09=09ereport(COMMERROR,=0A+=09=09= =09=09=09(errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20= errmsg("SSL=20certificate's=20distinguished=20name=20contains=20embedded=20= null")));=0A+=09=09=09pfree(peer_dn);=0A+=09=09=09if=20(port->peer_cn=20= !=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09pfree(port->peer_cn);=0A+=09=09= =09=09port->peer_cn=20=3D=20NULL;=0A+=09=09=09}=0A+=09=09=09return=20-1;=0A= +=09=09}=0A+=0A+=09=09port->peer_dn=20=3D=20peer_dn;=0A+=0A+=09=09= port->peer_cert_valid=20=3D=20true;=0A+=09}=0A+=0A+=09return=200;=0A+}=0A= +=0A+void=0A+be_tls_close(Port=20*port)=0A+{=0A+=09if=20(port->ssl)=0A+=09= {=0A+=09=09SSL_shutdown(port->ssl);=0A+=09=09SSL_free(port->ssl);=0A+=09=09= port->ssl=20=3D=20NULL;=0A+=09=09port->ssl_in_use=20=3D=20false;=0A+=09}=0A= +=0A+=09if=20(port->peer)=0A+=09{=0A+=09=09X509_free(port->peer);=0A+=09=09= port->peer=20=3D=20NULL;=0A+=09}=0A+=0A+=09if=20(port->peer_cn)=0A+=09{=0A= +=09=09pfree(port->peer_cn);=0A+=09=09port->peer_cn=20=3D=20NULL;=0A+=09= }=0A+=0A+=09if=20(port->peer_dn)=0A+=09{=0A+=09=09pfree(port->peer_dn);=0A= +=09=09port->peer_dn=20=3D=20NULL;=0A+=09}=0A+}=0A+=0A+ssize_t=0A= +be_tls_read(Port=20*port,=20void=20*ptr,=20size_t=20len,=20int=20= *waitfor)=0A+{=0A+=09ssize_t=09=09n;=0A+=09int=09=09=09err;=0A+=09= unsigned=20long=20ecode;=0A+=0A+=09errno=20=3D=200;=0A+=09= ERR_clear_error();=0A+=09n=20=3D=20SSL_read(port->ssl,=20ptr,=20len);=0A= +=09err=20=3D=20SSL_get_error(port->ssl,=20n);=0A+=09ecode=20=3D=20(err=20= !=3D=20SSL_ERROR_NONE=20||=20n=20<=200)=20?=20ERR_get_error()=20:=200;=0A= +=09switch=20(err)=0A+=09{=0A+=09=09case=20SSL_ERROR_NONE:=0A+=09=09=09= /*=20a-ok=20*/=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_WANT_READ:=0A= +=09=09=09*waitfor=20=3D=20WL_SOCKET_READABLE;=0A+=09=09=09errno=20=3D=20= EWOULDBLOCK;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09case=20= SSL_ERROR_WANT_WRITE:=0A+=09=09=09*waitfor=20=3D=20WL_SOCKET_WRITEABLE;=0A= +=09=09=09errno=20=3D=20EWOULDBLOCK;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09= break;=0A+=09=09case=20SSL_ERROR_SYSCALL:=0A+=09=09=09/*=20leave=20it=20= to=20caller=20to=20ereport=20the=20value=20of=20errno=20*/=0A+=09=09=09= if=20(n=20!=3D=20-1=20||=20errno=20=3D=3D=200)=0A+=09=09=09{=0A+=09=09=09= =09errno=20=3D=20ECONNRESET;=0A+=09=09=09=09n=20=3D=20-1;=0A+=09=09=09}=0A= +=09=09=09break;=0A+=09=09case=20SSL_ERROR_SSL:=0A+=09=09=09= ereport(COMMERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20errmsg("SSL=20= error:=20%s",=20SSLerrmessage(ecode))));=0A+=09=09=09errno=20=3D=20= ECONNRESET;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09case=20= SSL_ERROR_ZERO_RETURN:=0A+=09=09=09/*=20connection=20was=20cleanly=20= shut=20down=20by=20peer=20*/=0A+=09=09=09n=20=3D=200;=0A+=09=09=09break;=0A= +=09=09default:=0A+=09=09=09ereport(COMMERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20= errmsg("unrecognized=20SSL=20error=20code:=20%d",=0A+=09=09=09=09=09=09=09= err)));=0A+=09=09=09errno=20=3D=20ECONNRESET;=0A+=09=09=09n=20=3D=20-1;=0A= +=09=09=09break;=0A+=09}=0A+=0A+=09return=20n;=0A+}=0A+=0A+ssize_t=0A= +be_tls_write(Port=20*port,=20const=20void=20*ptr,=20size_t=20len,=20int=20= *waitfor)=0A+{=0A+=09ssize_t=09=09n;=0A+=09int=09=09=09err;=0A+=09= unsigned=20long=20ecode;=0A+=0A+=09errno=20=3D=200;=0A+=09= ERR_clear_error();=0A+=09n=20=3D=20SSL_write(port->ssl,=20ptr,=20len);=0A= +=09err=20=3D=20SSL_get_error(port->ssl,=20n);=0A+=09ecode=20=3D=20(err=20= !=3D=20SSL_ERROR_NONE=20||=20n=20<=200)=20?=20ERR_get_error()=20:=200;=0A= +=09switch=20(err)=0A+=09{=0A+=09=09case=20SSL_ERROR_NONE:=0A+=09=09=09= /*=20a-ok=20*/=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_WANT_READ:=0A= +=09=09=09*waitfor=20=3D=20WL_SOCKET_READABLE;=0A+=09=09=09errno=20=3D=20= EWOULDBLOCK;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09case=20= SSL_ERROR_WANT_WRITE:=0A+=09=09=09*waitfor=20=3D=20WL_SOCKET_WRITEABLE;=0A= +=09=09=09errno=20=3D=20EWOULDBLOCK;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09= break;=0A+=09=09case=20SSL_ERROR_SYSCALL:=0A+=0A+=09=09=09/*=0A+=09=09=09= =20*=20Leave=20it=20to=20caller=20to=20ereport=20the=20value=20of=20= errno.=20=20However,=20if=0A+=09=09=09=20*=20errno=20is=20still=20zero=20= then=20assume=20it's=20a=20read=20EOF=20situation,=20and=0A+=09=09=09=20= *=20report=20ECONNRESET.=20=20(This=20seems=20possible=20because=20= SSL_write=20can=0A+=09=09=09=20*=20also=20do=20reads.)=0A+=09=09=09=20*/=0A= +=09=09=09if=20(n=20!=3D=20-1=20||=20errno=20=3D=3D=200)=0A+=09=09=09{=0A= +=09=09=09=09errno=20=3D=20ECONNRESET;=0A+=09=09=09=09n=20=3D=20-1;=0A+=09= =09=09}=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_SSL:=0A+=09=09=09= ereport(COMMERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20errmsg("SSL=20= error:=20%s",=20SSLerrmessage(ecode))));=0A+=09=09=09errno=20=3D=20= ECONNRESET;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09case=20= SSL_ERROR_ZERO_RETURN:=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20the=20SSL=20= connection=20was=20closed,=20leave=20it=20to=20the=20caller=20to=0A+=09=09= =09=20*=20ereport=20it=0A+=09=09=09=20*/=0A+=09=09=09errno=20=3D=20= ECONNRESET;=0A+=09=09=09n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09= default:=0A+=09=09=09ereport(COMMERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_PROTOCOL_VIOLATION),=0A+=09=09=09=09=09=20= errmsg("unrecognized=20SSL=20error=20code:=20%d",=0A+=09=09=09=09=09=09=09= err)));=0A+=09=09=09errno=20=3D=20ECONNRESET;=0A+=09=09=09n=20=3D=20-1;=0A= +=09=09=09break;=0A+=09}=0A+=0A+=09return=20n;=0A+}=0A+=0A+/*=20= ------------------------------------------------------------=20*/=0A+/*=09= =09=09=09=09=09Internal=20functions=09=09=09=09=09=09*/=0A+/*=20= ------------------------------------------------------------=20*/=0A+=0A= +/*=0A+=20*=20Private=20substitute=20BIO:=20this=20does=20the=20sending=20= and=20receiving=20using=20send()=20and=0A+=20*=20recv()=20instead.=20= This=20is=20so=20that=20we=20can=20enable=20and=20disable=20interrupts=0A= +=20*=20just=20while=20calling=20recv().=20We=20cannot=20have=20= interrupts=20occurring=20while=0A+=20*=20the=20bulk=20of=20OpenSSL=20= runs,=20because=20it=20uses=20malloc()=20and=20possibly=20other=0A+=20*=20= non-reentrant=20libc=20facilities.=20We=20also=20need=20to=20call=20= send()=20and=20recv()=0A+=20*=20directly=20so=20it=20gets=20passed=20= through=20the=20socket/signals=20layer=20on=20Win32.=0A+=20*=0A+=20*=20= These=20functions=20are=20closely=20modelled=20on=20the=20standard=20= socket=20BIO=20in=20OpenSSL;=0A+=20*=20see=20sock_read()=20and=20= sock_write()=20in=20OpenSSL's=20crypto/bio/bss_sock.c.=0A+=20*/=0A+=0A= +static=20BIO_METHOD=20*port_bio_method_ptr=20=3D=20NULL;=0A+=0A+static=20= int=0A+port_bio_read(BIO=20*h,=20char=20*buf,=20int=20size)=0A+{=0A+=09= int=09=09=09res=20=3D=200;=0A+=09Port=09=20=20=20*port=20=3D=20(Port=20= *)=20BIO_get_data(h);=0A+=0A+=09if=20(buf=20!=3D=20NULL)=0A+=09{=0A+=09=09= res=20=3D=20secure_raw_read(port,=20buf,=20size);=0A+=09=09= BIO_clear_retry_flags(h);=0A+=09=09port->last_read_was_eof=20=3D=20res=20= =3D=3D=200;=0A+=09=09if=20(res=20<=3D=200)=0A+=09=09{=0A+=09=09=09/*=20= If=20we=20were=20interrupted,=20tell=20caller=20to=20retry=20*/=0A+=09=09= =09if=20(errno=20=3D=3D=20EINTR=20||=20errno=20=3D=3D=20EWOULDBLOCK=20||=20= errno=20=3D=3D=20EAGAIN)=0A+=09=09=09{=0A+=09=09=09=09= BIO_set_retry_read(h);=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A+=09return=20= res;=0A+}=0A+=0A+static=20int=0A+port_bio_write(BIO=20*h,=20const=20char=20= *buf,=20int=20size)=0A+{=0A+=09int=09=09=09res=20=3D=200;=0A+=0A+=09res=20= =3D=20secure_raw_write(((Port=20*)=20BIO_get_data(h)),=20buf,=20size);=0A= +=09BIO_clear_retry_flags(h);=0A+=09if=20(res=20<=3D=200)=0A+=09{=0A+=09=09= /*=20If=20we=20were=20interrupted,=20tell=20caller=20to=20retry=20*/=0A+=09= =09if=20(errno=20=3D=3D=20EINTR=20||=20errno=20=3D=3D=20EWOULDBLOCK=20||=20= errno=20=3D=3D=20EAGAIN)=0A+=09=09{=0A+=09=09=09BIO_set_retry_write(h);=0A= +=09=09}=0A+=09}=0A+=0A+=09return=20res;=0A+}=0A+=0A+static=20long=0A= +port_bio_ctrl(BIO=20*h,=20int=20cmd,=20long=20num,=20void=20*ptr)=0A+{=0A= +=09long=09=09res;=0A+=09Port=09=20=20=20*port=20=3D=20(Port=20*)=20= BIO_get_data(h);=0A+=0A+=09switch=20(cmd)=0A+=09{=0A+=09=09case=20= BIO_CTRL_EOF:=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20This=20should=20not=20= be=20needed.=20port_bio_read=20already=20has=20a=20way=20to=0A+=09=09=09=20= *=20signal=20EOF=20to=20OpenSSL.=20However,=20OpenSSL=20made=20an=20= undocumented,=0A+=09=09=09=20*=20backwards-incompatible=20change=20and=20= now=20expects=20EOF=20via=20BIO_ctrl.=0A+=09=09=09=20*=20See=20= https://github.com/openssl/openssl/issues/8208=0A+=09=09=09=20*/=0A+=09=09= =09res=20=3D=20port->last_read_was_eof;=0A+=09=09=09break;=0A+=09=09case=20= BIO_CTRL_FLUSH:=0A+=09=09=09/*=20libssl=20expects=20all=20BIOs=20to=20= support=20BIO_flush.=20*/=0A+=09=09=09res=20=3D=201;=0A+=09=09=09break;=0A= +=09=09default:=0A+=09=09=09res=20=3D=200;=0A+=09=09=09break;=0A+=09}=0A= +=0A+=09return=20res;=0A+}=0A+=0A+static=20BIO_METHOD=20*=0A= +port_bio_method(void)=0A+{=0A+=09if=20(!port_bio_method_ptr)=0A+=09{=0A= +=09=09int=09=09=09my_bio_index;=0A+=0A+=09=09my_bio_index=20=3D=20= BIO_get_new_index();=0A+=09=09if=20(my_bio_index=20=3D=3D=20-1)=0A+=09=09= =09return=20NULL;=0A+=09=09my_bio_index=20|=3D=20BIO_TYPE_SOURCE_SINK;=0A= +=09=09port_bio_method_ptr=20=3D=20BIO_meth_new(my_bio_index,=20= "PostgreSQL=20backend=20socket");=0A+=09=09if=20(!port_bio_method_ptr)=0A= +=09=09=09return=20NULL;=0A+=09=09if=20= (!BIO_meth_set_write(port_bio_method_ptr,=20port_bio_write)=20||=0A+=09=09= =09!BIO_meth_set_read(port_bio_method_ptr,=20port_bio_read)=20||=0A+=09=09= =09!BIO_meth_set_ctrl(port_bio_method_ptr,=20port_bio_ctrl))=0A+=09=09{=0A= +=09=09=09BIO_meth_free(port_bio_method_ptr);=0A+=09=09=09= port_bio_method_ptr=20=3D=20NULL;=0A+=09=09=09return=20NULL;=0A+=09=09}=0A= +=09}=0A+=09return=20port_bio_method_ptr;=0A+}=0A+=0A+static=20int=0A= +ssl_set_port_bio(Port=20*port)=0A+{=0A+=09BIO=09=09=20=20=20*bio;=0A+=09= BIO_METHOD=20*bio_method;=0A+=0A+=09bio_method=20=3D=20= port_bio_method();=0A+=09if=20(bio_method=20=3D=3D=20NULL)=0A+=09=09= return=200;=0A+=0A+=09bio=20=3D=20BIO_new(bio_method);=0A+=09if=20(bio=20= =3D=3D=20NULL)=0A+=09=09return=200;=0A+=0A+=09BIO_set_data(bio,=20port);=0A= +=09BIO_set_init(bio,=201);=0A+=0A+=09SSL_set_bio(port->ssl,=20bio,=20= bio);=0A+=09return=201;=0A+}=0A+=0A+/*=0A+=20*=09Load=20precomputed=20DH=20= parameters.=0A+=20*=0A+=20*=09To=20prevent=20"downgrade"=20attacks,=20we=20= perform=20a=20number=20of=20checks=0A+=20*=09to=20verify=20that=20the=20= DBA-generated=20DH=20parameters=20file=20contains=0A+=20*=09what=20we=20= expect=20it=20to=20contain.=0A+=20*/=0A+static=20DH=20=20*=0A= +load_dh_file(char=20*filename,=20bool=20isServerStart)=0A+{=0A+=09FILE=09= =20=20=20*fp;=0A+=09DH=09=09=20=20=20*dh=20=3D=20NULL;=0A+=09int=09=09=09= codes;=0A+=0A+=09/*=20attempt=20to=20open=20file.=20=20It's=20not=20an=20= error=20if=20it=20doesn't=20exist.=20*/=0A+=09if=20((fp=20=3D=20= AllocateFile(filename,=20"r"))=20=3D=3D=20NULL)=0A+=09{=0A+=09=09= ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= (errcode_for_file_access(),=0A+=09=09=09=09=20errmsg("could=20not=20open=20= DH=20parameters=20file=20\"%s\":=20%m",=0A+=09=09=09=09=09=09= filename)));=0A+=09=09return=20NULL;=0A+=09}=0A+=0A+=09dh=20=3D=20= PEM_read_DHparams(fp,=20NULL,=20NULL,=20NULL);=0A+=09FreeFile(fp);=0A+=0A= +=09if=20(dh=20=3D=3D=20NULL)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09=20errmsg("could=20not=20load=20DH=20parameters=20file:=20%s",=0A= +=09=09=09=09=09=09SSLerrmessage(ERR_get_error()))));=0A+=09=09return=20= NULL;=0A+=09}=0A+=0A+=09/*=20make=20sure=20the=20DH=20parameters=20are=20= usable=20*/=0A+=09if=20(DH_check(dh,=20&codes)=20=3D=3D=200)=0A+=09{=0A+=09= =09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20errmsg("invalid=20= DH=20parameters:=20%s",=0A+=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09DH_free(dh);=0A+=09=09return=20= NULL;=0A+=09}=0A+=09if=20(codes=20&=20DH_CHECK_P_NOT_PRIME)=0A+=09{=0A+=09= =09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= (errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20errmsg("invalid=20= DH=20parameters:=20p=20is=20not=20prime")));=0A+=09=09DH_free(dh);=0A+=09= =09return=20NULL;=0A+=09}=0A+=09if=20((codes=20&=20= DH_NOT_SUITABLE_GENERATOR)=20&&=0A+=09=09(codes=20&=20= DH_CHECK_P_NOT_SAFE_PRIME))=0A+=09{=0A+=09=09ereport(isServerStart=20?=20= FATAL=20:=20LOG,=0A+=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09= =09=09=09=20errmsg("invalid=20DH=20parameters:=20neither=20suitable=20= generator=20or=20safe=20prime")));=0A+=09=09DH_free(dh);=0A+=09=09return=20= NULL;=0A+=09}=0A+=0A+=09return=20dh;=0A+}=0A+=0A+/*=0A+=20*=09Load=20= hardcoded=20DH=20parameters.=0A+=20*=0A+=20*=09If=20DH=20parameters=20= cannot=20be=20loaded=20from=20a=20specified=20file,=20we=20can=20load=0A= +=20*=09the=20hardcoded=20DH=20parameters=20supplied=20with=20the=20= backend=20to=20prevent=0A+=20*=09problems.=0A+=20*/=0A+static=20DH=20=20= *=0A+load_dh_buffer(const=20char=20*buffer,=20size_t=20len)=0A+{=0A+=09= BIO=09=09=20=20=20*bio;=0A+=09DH=09=09=20=20=20*dh=20=3D=20NULL;=0A+=0A+=09= bio=20=3D=20BIO_new_mem_buf(buffer,=20len);=0A+=09if=20(bio=20=3D=3D=20= NULL)=0A+=09=09return=20NULL;=0A+=09dh=20=3D=20= PEM_read_bio_DHparams(bio,=20NULL,=20NULL,=20NULL);=0A+=09if=20(dh=20=3D=3D= =20NULL)=0A+=09=09ereport(DEBUG2,=0A+=09=09=09=09(errmsg_internal("DH=20= load=20buffer:=20%s",=0A+=09=09=09=09=09=09=09=09=20= SSLerrmessage(ERR_get_error()))));=0A+=09BIO_free(bio);=0A+=0A+=09return=20= dh;=0A+}=0A+=0A+/*=0A+=20*=09Passphrase=20collection=20callback=20using=20= ssl_passphrase_command=0A+=20*/=0A+static=20int=0A= +ssl_external_passwd_cb(char=20*buf,=20int=20size,=20int=20rwflag,=20= void=20*userdata)=0A+{=0A+=09/*=20same=20prompt=20as=20LibreSSL=20uses=20= internally=20*/=0A+=09const=20char=20*prompt=20=3D=20"Enter=20PEM=20pass=20= phrase:";=0A+=09const=20char=20*cmd=20=3D=20userdata;=0A+=0A+=09= Assert(rwflag=20=3D=3D=200);=0A+=0A+=09return=20= run_ssl_passphrase_command(cmd,=20prompt,=20ssl_is_server_start,=20buf,=20= size);=0A+}=0A+=0A+/*=0A+=20*=20Dummy=20passphrase=20callback=0A+=20*=0A= +=20*=20If=20OpenSSL=20is=20told=20to=20use=20a=20passphrase-protected=20= server=20key,=20by=20default=0A+=20*=20it=20will=20issue=20a=20prompt=20= on=20/dev/tty=20and=20try=20to=20read=20a=20key=20from=20there.=0A+=20*=20= That's=20no=20good=20during=20a=20postmaster=20SIGHUP=20cycle,=20not=20= to=20mention=20SSL=20context=0A+=20*=20reload=20in=20an=20EXEC_BACKEND=20= postmaster=20child.=20=20So=20override=20it=20with=20this=20dummy=0A+=20= *=20function=20that=20just=20returns=20an=20empty=20passphrase,=20= guaranteeing=20failure.=0A+=20*/=0A+static=20int=0A= +dummy_ssl_passwd_cb(char=20*buf,=20int=20size,=20int=20rwflag,=20void=20= *userdata)=0A+{=0A+=09/*=20Set=20flag=20to=20change=20the=20error=20= message=20we'll=20report=20*/=0A+=09dummy_ssl_passwd_cb_called=20=3D=20= true;=0A+=09/*=20And=20return=20empty=20string=20*/=0A+=09Assert(size=20= >=200);=0A+=09buf[0]=20=3D=20'\0';=0A+=09return=200;=0A+}=0A+=0A+/*=0A+=20= *=20Examines=20the=20provided=20certificate=20name,=20and=20if=20it's=20= too=20long=20to=20log=20or=0A+=20*=20contains=20unprintable=20ASCII,=20= escapes=20and=20truncates=20it.=20The=20return=20value=20is=0A+=20*=20= always=20a=20new=20palloc'd=20string.=20(The=20input=20string=20is=20= still=20modified=20in=20place,=0A+=20*=20for=20ease=20of=20= implementation.)=0A+=20*/=0A+static=20char=20*=0A+prepare_cert_name(char=20= *name)=0A+{=0A+=09size_t=09=09namelen=20=3D=20strlen(name);=0A+=09char=09= =20=20=20*truncated=20=3D=20name;=0A+=0A+=09/*=0A+=09=20*=20Common=20= Names=20are=2064=20chars=20max,=20so=20for=20a=20common=20case=20where=20= the=20CN=20is=20the=0A+=09=20*=20last=20field,=20we=20can=20still=20= print=20the=20longest=20possible=20CN=20with=20a=0A+=09=20*=20= 7-character=20prefix=20(".../CN=3D[64=20chars]"),=20for=20a=20reasonable=20= limit=20of=2071=0A+=09=20*=20characters.=0A+=09=20*/=0A+#define=20MAXLEN=20= 71=0A+=0A+=09if=20(namelen=20>=20MAXLEN)=0A+=09{=0A+=09=09/*=0A+=09=09=20= *=20Keep=20the=20end=20of=20the=20name,=20not=20the=20beginning,=20since=20= the=20most=0A+=09=09=20*=20specific=20field=20is=20likely=20to=20give=20= users=20the=20most=20information.=0A+=09=09=20*/=0A+=09=09truncated=20=3D=20= name=20+=20namelen=20-=20MAXLEN;=0A+=09=09truncated[0]=20=3D=20= truncated[1]=20=3D=20truncated[2]=20=3D=20'.';=0A+=09=09namelen=20=3D=20= MAXLEN;=0A+=09}=0A+=0A+#undef=20MAXLEN=0A+=0A+=09return=20= pg_clean_ascii(truncated,=200);=0A+}=0A+=0A+/*=0A+=20*=09Certificate=20= verification=20callback=0A+=20*=0A+=20*=09This=20callback=20allows=20us=20= to=20examine=20intermediate=20problems=20during=0A+=20*=09verification,=20= for=20later=20logging.=0A+=20*=0A+=20*=09This=20callback=20also=20allows=20= us=20to=20override=20the=20default=20acceptance=0A+=20*=09criteria=20= (e.g.,=20accepting=20self-signed=20or=20expired=20certs),=20but=0A+=20*=09= for=20now=20we=20accept=20the=20default=20checks.=0A+=20*/=0A+static=20= int=0A+verify_cb(int=20ok,=20X509_STORE_CTX=20*ctx)=0A+{=0A+=09int=09=09=09= depth;=0A+=09int=09=09=09errcode;=0A+=09const=20char=20*errstring;=0A+=09= StringInfoData=20str;=0A+=09X509=09=20=20=20*cert;=0A+=09SSL=09=09=20=20=20= *ssl;=0A+=09struct=20CallbackErr=20*cb_err;=0A+=0A+=09if=20(ok)=0A+=09{=0A= +=09=09/*=20Nothing=20to=20do=20for=20the=20successful=20case.=20*/=0A+=09= =09return=20ok;=0A+=09}=0A+=0A+=09/*=20Pull=20all=20the=20information=20= we=20have=20on=20the=20verification=20failure.=20*/=0A+=09depth=20=3D=20= X509_STORE_CTX_get_error_depth(ctx);=0A+=09errcode=20=3D=20= X509_STORE_CTX_get_error(ctx);=0A+=09errstring=20=3D=20= X509_verify_cert_error_string(errcode);=0A+=0A+=09/*=0A+=09=20*=20= Extract=20the=20current=20SSL=20and=20CallbackErr=20object=20to=20use=20= for=20passing=20error=0A+=09=20*=20detail=20back=20from=20the=20= callback.=0A+=09=20*/=0A+=09ssl=20=3D=20X509_STORE_CTX_get_ex_data(ctx,=20= SSL_get_ex_data_X509_STORE_CTX_idx());=0A+=09cb_err=20=3D=20(struct=20= CallbackErr=20*)=20SSL_get_ex_data(ssl,=200);=0A+=0A+=09= initStringInfo(&str);=0A+=09appendStringInfo(&str,=0A+=09=09=09=09=09=20= _("Client=20certificate=20verification=20failed=20at=20depth=20%d:=20= %s."),=0A+=09=09=09=09=09=20depth,=20errstring);=0A+=0A+=09cert=20=3D=20= X509_STORE_CTX_get_current_cert(ctx);=0A+=09if=20(cert)=0A+=09{=0A+=09=09= char=09=20=20=20*subject,=0A+=09=09=09=09=20=20=20*issuer;=0A+=09=09char=09= =20=20=20*sub_prepared,=0A+=09=09=09=09=20=20=20*iss_prepared;=0A+=09=09= char=09=20=20=20*serialno;=0A+=09=09ASN1_INTEGER=20*sn;=0A+=09=09BIGNUM=09= =20=20=20*b;=0A+=0A+=09=09/*=0A+=09=09=20*=20Get=20the=20Subject=20and=20= Issuer=20for=20logging,=20but=20don't=20let=20maliciously=0A+=09=09=20*=20= huge=20certs=20flood=20the=20logs,=20and=20don't=20reflect=20non-ASCII=20= bytes=20into=0A+=09=09=20*=20it=20either.=0A+=09=09=20*/=0A+=09=09= subject=20=3D=20X509_NAME_to_cstring(X509_get_subject_name(cert));=0A+=09= =09sub_prepared=20=3D=20prepare_cert_name(subject);=0A+=09=09= pfree(subject);=0A+=0A+=09=09issuer=20=3D=20= X509_NAME_to_cstring(X509_get_issuer_name(cert));=0A+=09=09iss_prepared=20= =3D=20prepare_cert_name(issuer);=0A+=09=09pfree(issuer);=0A+=0A+=09=09/*=0A= +=09=09=20*=20Pull=20the=20serial=20number,=20too,=20in=20case=20a=20= Subject=20is=20still=20ambiguous.=0A+=09=09=20*=20This=20mirrors=20= be_tls_get_peer_serial().=0A+=09=09=20*/=0A+=09=09sn=20=3D=20= X509_get_serialNumber(cert);=0A+=09=09b=20=3D=20ASN1_INTEGER_to_BN(sn,=20= NULL);=0A+=09=09serialno=20=3D=20BN_bn2dec(b);=0A+=0A+=09=09= appendStringInfoChar(&str,=20'\n');=0A+=09=09appendStringInfo(&str,=0A+=09= =09=09=09=09=09=20_("Failed=20certificate=20data=20(unverified):=20= subject=20\"%s\",=20serial=20number=20%s,=20issuer=20\"%s\"."),=0A+=09=09= =09=09=09=09=20sub_prepared,=20serialno=20?=20serialno=20:=20= _("unknown"),=0A+=09=09=09=09=09=09=20iss_prepared);=0A+=0A+=09=09= BN_free(b);=0A+=09=09OPENSSL_free(serialno);=0A+=09=09= pfree(iss_prepared);=0A+=09=09pfree(sub_prepared);=0A+=09}=0A+=0A+=09/*=20= Store=20our=20detail=20message=20to=20be=20logged=20later.=20*/=0A+=09= cb_err->cert_errdetail=20=3D=20str.data;=0A+=0A+=09return=20ok;=0A+}=0A+=0A= +/*=0A+=20*=09This=20callback=20is=20used=20to=20copy=20SSL=20= information=20messages=0A+=20*=09into=20the=20PostgreSQL=20log.=0A+=20*/=0A= +static=20void=0A+info_cb(const=20SSL=20*ssl,=20int=20type,=20int=20= args)=0A+{=0A+=09const=20char=20*desc;=0A+=0A+=09desc=20=3D=20= SSL_state_string_long(ssl);=0A+=0A+=09switch=20(type)=0A+=09{=0A+=09=09= case=20SSL_CB_HANDSHAKE_START:=0A+=09=09=09ereport(DEBUG4,=0A+=09=09=09=09= =09(errmsg_internal("SSL:=20handshake=20start:=20\"%s\"",=20desc)));=0A+=09= =09=09break;=0A+=09=09case=20SSL_CB_HANDSHAKE_DONE:=0A+=09=09=09= ereport(DEBUG4,=0A+=09=09=09=09=09(errmsg_internal("SSL:=20handshake=20= done:=20\"%s\"",=20desc)));=0A+=09=09=09break;=0A+=09=09case=20= SSL_CB_ACCEPT_LOOP:=0A+=09=09=09ereport(DEBUG4,=0A+=09=09=09=09=09= (errmsg_internal("SSL:=20accept=20loop:=20\"%s\"",=20desc)));=0A+=09=09=09= break;=0A+=09=09case=20SSL_CB_ACCEPT_EXIT:=0A+=09=09=09ereport(DEBUG4,=0A= +=09=09=09=09=09(errmsg_internal("SSL:=20accept=20exit=20(%d):=20= \"%s\"",=20args,=20desc)));=0A+=09=09=09break;=0A+=09=09case=20= SSL_CB_CONNECT_LOOP:=0A+=09=09=09ereport(DEBUG4,=0A+=09=09=09=09=09= (errmsg_internal("SSL:=20connect=20loop:=20\"%s\"",=20desc)));=0A+=09=09=09= break;=0A+=09=09case=20SSL_CB_CONNECT_EXIT:=0A+=09=09=09ereport(DEBUG4,=0A= +=09=09=09=09=09(errmsg_internal("SSL:=20connect=20exit=20(%d):=20= \"%s\"",=20args,=20desc)));=0A+=09=09=09break;=0A+=09=09case=20= SSL_CB_READ_ALERT:=0A+=09=09=09ereport(DEBUG4,=0A+=09=09=09=09=09= (errmsg_internal("SSL:=20read=20alert=20(0x%04x):=20\"%s\"",=20args,=20= desc)));=0A+=09=09=09break;=0A+=09=09case=20SSL_CB_WRITE_ALERT:=0A+=09=09= =09ereport(DEBUG4,=0A+=09=09=09=09=09(errmsg_internal("SSL:=20write=20= alert=20(0x%04x):=20\"%s\"",=20args,=20desc)));=0A+=09=09=09break;=0A+=09= }=0A+}=0A+=0A+/*=20See=20pqcomm.h=20comments=20on=20OpenSSL=20= implementation=20of=20ALPN=20(RFC=207301)=20*/=0A+static=20const=20= unsigned=20char=20alpn_protos[]=20=3D=20PG_ALPN_PROTOCOL_VECTOR;=0A+=0A= +/*=0A+=20*=20Server=20callback=20for=20ALPN=20negotiation.=20We=20use=20= the=20standard=20"helper"=20function=0A+=20*=20even=20though=20currently=20= we=20only=20accept=20one=20value.=0A+=20*/=0A+static=20int=0A= +alpn_cb(SSL=20*ssl,=0A+=09=09const=20unsigned=20char=20**out,=0A+=09=09= unsigned=20char=20*outlen,=0A+=09=09const=20unsigned=20char=20*in,=0A+=09= =09unsigned=20int=20inlen,=0A+=09=09void=20*userdata)=0A+{=0A+=09/*=0A+=09= =20*=20Why=20does=20OpenSSL=20provide=20a=20helper=20function=20that=20= requires=20a=20nonconst=0A+=09=20*=20vector=20when=20the=20callback=20is=20= declared=20to=20take=20a=20const=20vector?=20What=20are=0A+=09=20*=20we=20= to=20do=20with=20that?=0A+=09=20*/=0A+=09int=09=09=09retval;=0A+=0A+=09= Assert(userdata=20!=3D=20NULL);=0A+=09Assert(out=20!=3D=20NULL);=0A+=09= Assert(outlen=20!=3D=20NULL);=0A+=09Assert(in=20!=3D=20NULL);=0A+=0A+=09= retval=20=3D=20SSL_select_next_proto((unsigned=20char=20**)=20out,=20= outlen,=0A+=09=09=09=09=09=09=09=09=20=20=20alpn_protos,=20= sizeof(alpn_protos),=0A+=09=09=09=09=09=09=09=09=20=20=20in,=20inlen);=0A= +=09if=20(*out=20=3D=3D=20NULL=20||=20*outlen=20>=20sizeof(alpn_protos)=20= ||=20*outlen=20<=3D=200)=0A+=09=09return=20SSL_TLSEXT_ERR_NOACK;=09/*=20= can't=20happen=20*/=0A+=0A+=09if=20(retval=20=3D=3D=20= OPENSSL_NPN_NEGOTIATED)=0A+=09=09return=20SSL_TLSEXT_ERR_OK;=0A+=09else=0A= +=09{=0A+=09=09/*=0A+=09=09=20*=20The=20client=20doesn't=20support=20our=20= protocol.=20=20Reject=20the=20connection=0A+=09=09=20*=20with=20TLS=20= "no_application_protocol"=20alert,=20per=20RFC=207301.=0A+=09=09=20*/=0A= +=09=09return=20SSL_TLSEXT_ERR_ALERT_FATAL;=0A+=09}=0A+}=0A+=0A+/*=0A+=20= *=20Set=20DH=20parameters=20for=20generating=20ephemeral=20DH=20keys.=20=20= The=0A+=20*=20DH=20parameters=20can=20take=20a=20long=20time=20to=20= compute,=20so=20they=20must=20be=0A+=20*=20precomputed.=0A+=20*=0A+=20*=20= Since=20few=20sites=20will=20bother=20to=20create=20a=20parameter=20= file,=20we=20also=0A+=20*=20provide=20a=20fallback=20to=20the=20= parameters=20provided=20by=20the=20OpenSSL=0A+=20*=20project.=0A+=20*=0A= +=20*=20These=20values=20can=20be=20static=20(once=20loaded=20or=20= computed)=20since=20the=0A+=20*=20OpenSSL=20library=20can=20efficiently=20= generate=20random=20keys=20from=20the=0A+=20*=20information=20provided.=0A= +=20*/=0A+static=20bool=0A+initialize_dh(SSL_CTX=20*context,=20bool=20= isServerStart)=0A+{=0A+=09DH=09=09=20=20=20*dh=20=3D=20NULL;=0A+=0A+=09= SSL_CTX_set_options(context,=20SSL_OP_SINGLE_DH_USE);=0A+=0A+=09if=20= (ssl_dh_params_file[0])=0A+=09=09dh=20=3D=20= load_dh_file(ssl_dh_params_file,=20isServerStart);=0A+=09if=20(!dh)=0A+=09= =09dh=20=3D=20load_dh_buffer(FILE_DH2048,=20sizeof(FILE_DH2048));=0A+=09= if=20(!dh)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A= +=09=09=09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20= errmsg("DH:=20could=20not=20load=20DH=20parameters")));=0A+=09=09return=20= false;=0A+=09}=0A+=0A+=09if=20(SSL_CTX_set_tmp_dh(context,=20dh)=20!=3D=20= 1)=0A+=09{=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09= =09=09(errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09=20errmsg("DH:=20= could=20not=20set=20DH=20parameters:=20%s",=0A+=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A+=09=09DH_free(dh);=0A+=09=09return=20= false;=0A+=09}=0A+=0A+=09DH_free(dh);=0A+=09return=20true;=0A+}=0A+=0A= +/*=0A+=20*=20Set=20ECDH=20parameters=20for=20generating=20ephemeral=20= Elliptic=20Curve=20DH=0A+=20*=20keys.=20=20This=20is=20much=20simpler=20= than=20the=20DH=20parameters,=20as=20we=20just=0A+=20*=20need=20to=20= provide=20the=20name=20of=20the=20curve=20to=20OpenSSL.=0A+=20*/=0A= +static=20bool=0A+initialize_ecdh(SSL_CTX=20*context,=20bool=20= isServerStart)=0A+{=0A+=09if=20(SSL_CTX_set1_groups_list(context,=20= SSLECDHCurve)=20!=3D=201)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20OpenSSL=20= 3.3.0=20introduced=20proper=20error=20messages=20for=20group=20parsing=0A= +=09=09=20*=20errors,=20earlier=20versions=20returns=20"no=20SSL=20error=20= reported"=20which=20is=0A+=09=09=20*=20far=20from=20helpful.=20For=20= older=20versions,=20we=20replace=20with=20a=20better=0A+=09=09=20*=20= error=20message.=20Injecting=20the=20error=20into=20the=20OpenSSL=20= error=20queue=0A+=09=09=20*=20need=20APIs=20from=20OpenSSL=203.0.=0A+=09=09= =20*/=0A+=09=09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A+=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A+=09=09=09=09errmsg("could=20not=20= set=20group=20names=20specified=20in=20ssl_groups:=20%s",=0A+=09=09=09=09= =09=20=20=20SSLerrmessageExt(ERR_get_error(),=0A+=09=09=09=09=09=09=09=09= =09=09_("No=20valid=20groups=20found"))),=0A+=09=09=09=09errhint("Ensure=20= that=20each=20group=20name=20is=20spelled=20correctly=20and=20supported=20= by=20the=20installed=20version=20of=20OpenSSL."));=0A+=09=09return=20= false;=0A+=09}=0A+=0A+=09return=20true;=0A+}=0A+=0A+/*=0A+=20*=20Obtain=20= reason=20string=20for=20passed=20SSL=20errcode=20with=20replacement=0A+=20= *=0A+=20*=20The=20error=20message=20supplied=20in=20replacement=20will=20= be=20used=20in=20case=20the=20error=0A+=20*=20code=20from=20OpenSSL=20is=20= 0,=20else=20the=20error=20message=20from=20SSLerrmessage()=20will=0A+=20= *=20be=20returned.=0A+=20*=0A+=20*=20Not=20all=20versions=20of=20OpenSSL=20= place=20an=20error=20on=20the=20queue=20even=20for=20failing=0A+=20*=20= operations,=20which=20will=20yield=20"no=20SSL=20error=20reported"=20by=20= SSLerrmessage.=20This=0A+=20*=20function=20can=20be=20used=20to=20ensure=20= that=20a=20proper=20error=20message=20is=20displayed=20for=0A+=20*=20= versions=20reporting=20no=20error,=20while=20using=20the=20OpenSSL=20= error=20via=20SSLerrmessage=0A+=20*=20for=20versions=20where=20there=20= is=20one.=0A+=20*/=0A+static=20const=20char=20*=0A= +SSLerrmessageExt(unsigned=20long=20ecode,=20const=20char=20= *replacement)=0A+{=0A+=09if=20(ecode=20=3D=3D=200)=0A+=09=09return=20= replacement;=0A+=09else=0A+=09=09return=20SSLerrmessage(ecode);=0A+}=0A+=0A= +/*=0A+=20*=20Obtain=20reason=20string=20for=20passed=20SSL=20errcode=0A= +=20*=0A+=20*=20ERR_get_error()=20is=20used=20by=20caller=20to=20get=20= errcode=20to=20pass=20here.=0A+=20*=0A+=20*=20Some=20caution=20is=20= needed=20here=20since=20ERR_reason_error_string=20will=20return=20NULL=0A= +=20*=20if=20it=20doesn't=20recognize=20the=20error=20code,=20or=20(in=20= OpenSSL=20>=3D=203)=20if=20the=20code=0A+=20*=20represents=20a=20system=20= errno=20value.=20=20We=20don't=20want=20to=20return=20NULL=20ever.=0A+=20= */=0A+static=20const=20char=20*=0A+SSLerrmessage(unsigned=20long=20= ecode)=0A+{=0A+=09const=20char=20*errreason;=0A+=09static=20char=20= errbuf[36];=0A+=0A+=09if=20(ecode=20=3D=3D=200)=0A+=09=09return=20_("no=20= SSL=20error=20reported");=0A+=09errreason=20=3D=20= ERR_reason_error_string(ecode);=0A+=09if=20(errreason=20!=3D=20NULL)=0A+=09= =09return=20errreason;=0A+=0A+=09/*=20No=20choice=20but=20to=20report=20= the=20numeric=20ecode=20*/=0A+=09snprintf(errbuf,=20sizeof(errbuf),=20= _("SSL=20error=20code=20%lu"),=20ecode);=0A+=09return=20errbuf;=0A+}=0A+=0A= +int=0A+be_tls_get_cipher_bits(Port=20*port)=0A+{=0A+=09int=09=09=09= bits;=0A+=0A+=09if=20(port->ssl)=0A+=09{=0A+=09=09= SSL_get_cipher_bits(port->ssl,=20&bits);=0A+=09=09return=20bits;=0A+=09}=0A= +=09else=0A+=09=09return=200;=0A+}=0A+=0A+const=20char=20*=0A= +be_tls_get_version(Port=20*port)=0A+{=0A+=09if=20(port->ssl)=0A+=09=09= return=20SSL_get_version(port->ssl);=0A+=09else=0A+=09=09return=20NULL;=0A= +}=0A+=0A+const=20char=20*=0A+be_tls_get_cipher(Port=20*port)=0A+{=0A+=09= if=20(port->ssl)=0A+=09=09return=20SSL_get_cipher(port->ssl);=0A+=09else=0A= +=09=09return=20NULL;=0A+}=0A+=0A+void=0A= +be_tls_get_peer_subject_name(Port=20*port,=20char=20*ptr,=20size_t=20= len)=0A+{=0A+=09if=20(port->peer)=0A+=09=09strlcpy(ptr,=20= X509_NAME_to_cstring(X509_get_subject_name(port->peer)),=20len);=0A+=09= else=0A+=09=09ptr[0]=20=3D=20'\0';=0A+}=0A+=0A+void=0A= +be_tls_get_peer_issuer_name(Port=20*port,=20char=20*ptr,=20size_t=20= len)=0A+{=0A+=09if=20(port->peer)=0A+=09=09strlcpy(ptr,=20= X509_NAME_to_cstring(X509_get_issuer_name(port->peer)),=20len);=0A+=09= else=0A+=09=09ptr[0]=20=3D=20'\0';=0A+}=0A+=0A+void=0A= +be_tls_get_peer_serial(Port=20*port,=20char=20*ptr,=20size_t=20len)=0A= +{=0A+=09if=20(port->peer)=0A+=09{=0A+=09=09ASN1_INTEGER=20*serial;=0A+=09= =09BIGNUM=09=20=20=20*b;=0A+=09=09char=09=20=20=20*decimal;=0A+=0A+=09=09= serial=20=3D=20X509_get_serialNumber(port->peer);=0A+=09=09b=20=3D=20= ASN1_INTEGER_to_BN(serial,=20NULL);=0A+=09=09decimal=20=3D=20= BN_bn2dec(b);=0A+=0A+=09=09BN_free(b);=0A+=09=09strlcpy(ptr,=20decimal,=20= len);=0A+=09=09OPENSSL_free(decimal);=0A+=09}=0A+=09else=0A+=09=09ptr[0]=20= =3D=20'\0';=0A+}=0A+=0A+char=20*=0A+be_tls_get_certificate_hash(Port=20= *port,=20size_t=20*len)=0A+{=0A+=09X509=09=20=20=20*server_cert;=0A+=09= char=09=20=20=20*cert_hash;=0A+=09const=20EVP_MD=20*algo_type=20=3D=20= NULL;=0A+=09unsigned=20char=20hash[EVP_MAX_MD_SIZE];=09/*=20size=20for=20= SHA-512=20*/=0A+=09unsigned=20int=20hash_size;=0A+=09int=09=09=09= algo_nid;=0A+=0A+=09*len=20=3D=200;=0A+=09server_cert=20=3D=20= SSL_get_certificate(port->ssl);=0A+=09if=20(server_cert=20=3D=3D=20NULL)=0A= +=09=09return=20NULL;=0A+=0A+=09/*=0A+=09=20*=20Get=20the=20signature=20= algorithm=20of=20the=20certificate=20to=20determine=20the=20hash=0A+=09=20= *=20algorithm=20to=20use=20for=20the=20result.=0A+=09=20*=20introduced=20= in=20OpenSSL=201.1.1,=20which=20can=20handle=20RSA-PSS=20signatures.=0A+=09= =20*/=0A+=09if=20= (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert),=0A+=09=09=09=09= =09=09=09=20&algo_nid,=20NULL))=0A+=09=09elog(ERROR,=20"could=20not=20= determine=20server=20certificate=20signature=20algorithm");=0A+=0A+=09/*=0A= +=09=20*=20The=20TLS=20server's=20certificate=20bytes=20need=20to=20be=20= hashed=20with=20SHA-256=20if=0A+=09=20*=20its=20signature=20algorithm=20= is=20MD5=20or=20SHA-1=20as=20per=20RFC=205929=0A+=09=20*=20= (https://tools.ietf.org/html/rfc5929#section-4.1).=20=20If=20something=20= else=0A+=09=20*=20is=20used,=20the=20same=20hash=20as=20the=20signature=20= algorithm=20is=20used.=0A+=09=20*/=0A+=09switch=20(algo_nid)=0A+=09{=0A+=09= =09case=20NID_md5:=0A+=09=09case=20NID_sha1:=0A+=09=09=09algo_type=20=3D=20= EVP_sha256();=0A+=09=09=09break;=0A+=09=09default:=0A+=09=09=09algo_type=20= =3D=20EVP_get_digestbynid(algo_nid);=0A+=09=09=09if=20(algo_type=20=3D=3D=20= NULL)=0A+=09=09=09=09elog(ERROR,=20"could=20not=20find=20digest=20for=20= NID=20%s",=0A+=09=09=09=09=09=20OBJ_nid2sn(algo_nid));=0A+=09=09=09= break;=0A+=09}=0A+=0A+=09/*=20generate=20and=20save=20the=20certificate=20= hash=20*/=0A+=09if=20(!X509_digest(server_cert,=20algo_type,=20hash,=20= &hash_size))=0A+=09=09elog(ERROR,=20"could=20not=20generate=20server=20= certificate=20hash");=0A+=0A+=09cert_hash=20=3D=20palloc(hash_size);=0A+=09= memcpy(cert_hash,=20hash,=20hash_size);=0A+=09*len=20=3D=20hash_size;=0A= +=0A+=09return=20cert_hash;=0A+}=0A+=0A+/*=0A+=20*=20Convert=20an=20X509=20= subject=20name=20to=20a=20cstring.=0A+=20*=0A+=20*/=0A+static=20char=20*=0A= +X509_NAME_to_cstring(X509_NAME=20*name)=0A+{=0A+=09BIO=09=09=20=20=20= *membuf=20=3D=20BIO_new(BIO_s_mem());=0A+=09int=09=09=09i,=0A+=09=09=09=09= nid,=0A+=09=09=09=09count=20=3D=20X509_NAME_entry_count(name);=0A+=09= X509_NAME_ENTRY=20*e;=0A+=09ASN1_STRING=20*v;=0A+=09const=20char=20= *field_name;=0A+=09size_t=09=09size;=0A+=09char=09=09nullterm;=0A+=09= char=09=20=20=20*sp;=0A+=09char=09=20=20=20*dp;=0A+=09char=09=20=20=20= *result;=0A+=0A+=09if=20(membuf=20=3D=3D=20NULL)=0A+=09=09ereport(ERROR,=0A= +=09=09=09=09(errcode(ERRCODE_OUT_OF_MEMORY),=0A+=09=09=09=09=20= errmsg("could=20not=20create=20BIO")));=0A+=0A+=09(void)=20= BIO_set_close(membuf,=20BIO_CLOSE);=0A+=09for=20(i=20=3D=200;=20i=20<=20= count;=20i++)=0A+=09{=0A+=09=09e=20=3D=20X509_NAME_get_entry(name,=20i);=0A= +=09=09nid=20=3D=20OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));=0A+=09=09= if=20(nid=20=3D=3D=20NID_undef)=0A+=09=09=09ereport(ERROR,=0A+=09=09=09=09= =09(errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=09=20= errmsg("could=20not=20get=20NID=20for=20ASN1_OBJECT=20object")));=0A+=09=09= v=20=3D=20X509_NAME_ENTRY_get_data(e);=0A+=09=09field_name=20=3D=20= OBJ_nid2sn(nid);=0A+=09=09if=20(field_name=20=3D=3D=20NULL)=0A+=09=09=09= field_name=20=3D=20OBJ_nid2ln(nid);=0A+=09=09if=20(field_name=20=3D=3D=20= NULL)=0A+=09=09=09ereport(ERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=09=20= errmsg("could=20not=20convert=20NID=20%d=20to=20an=20ASN1_OBJECT=20= structure",=20nid)));=0A+=09=09BIO_printf(membuf,=20"/%s=3D",=20= field_name);=0A+=09=09ASN1_STRING_print_ex(membuf,=20v,=0A+=09=09=09=09=09= =09=09=20((ASN1_STRFLGS_RFC2253=20&=20~ASN1_STRFLGS_ESC_MSB)=0A+=09=09=09= =09=09=09=09=20=20|=20ASN1_STRFLGS_UTF8_CONVERT));=0A+=09}=0A+=0A+=09/*=20= ensure=20null=20termination=20of=20the=20BIO's=20content=20*/=0A+=09= nullterm=20=3D=20'\0';=0A+=09BIO_write(membuf,=20&nullterm,=201);=0A+=09= size=20=3D=20BIO_get_mem_data(membuf,=20&sp);=0A+=09dp=20=3D=20= pg_any_to_server(sp,=20size=20-=201,=20PG_UTF8);=0A+=0A+=09result=20=3D=20= pstrdup(dp);=0A+=09if=20(dp=20!=3D=20sp)=0A+=09=09pfree(dp);=0A+=09if=20= (BIO_free(membuf)=20!=3D=201)=0A+=09=09elog(ERROR,=20"could=20not=20free=20= OpenSSL=20BIO=20structure");=0A+=0A+=09return=20result;=0A+}=0A+=0A+/*=0A= +=20*=20Convert=20TLS=20protocol=20version=20GUC=20enum=20to=20OpenSSL=20= values=0A+=20*=0A+=20*=20This=20is=20a=20straightforward=20one-to-one=20= mapping,=20but=20doing=20it=20this=20way=20makes=0A+=20*=20the=20= definitions=20of=20ssl_min_protocol_version=20and=20= ssl_max_protocol_version=0A+=20*=20independent=20of=20OpenSSL=20= availability=20and=20version.=0A+=20*=0A+=20*=20If=20a=20version=20is=20= passed=20that=20is=20not=20supported=20by=20the=20current=20OpenSSL=0A+=20= *=20version,=20then=20we=20return=20-1.=20=20If=20a=20nonnegative=20= value=20is=20returned,=0A+=20*=20subsequent=20code=20can=20assume=20it's=20= working=20with=20a=20supported=20version.=0A+=20*=0A+=20*=20Note:=20this=20= is=20rather=20similar=20to=20libpq's=20routine=20in=20= fe-secure-openssl.c,=0A+=20*=20so=20make=20sure=20to=20update=20both=20= routines=20if=20changing=20this=20one.=0A+=20*/=0A+static=20int=0A= +ssl_protocol_version_to_openssl(int=20v)=0A+{=0A+=09switch=20(v)=0A+=09= {=0A+=09=09case=20PG_TLS_ANY:=0A+=09=09=09return=200;=0A+=09=09case=20= PG_TLS1_VERSION:=0A+=09=09=09return=20TLS1_VERSION;=0A+=09=09case=20= PG_TLS1_1_VERSION:=0A+#ifdef=20TLS1_1_VERSION=0A+=09=09=09return=20= TLS1_1_VERSION;=0A+#else=0A+=09=09=09break;=0A+#endif=0A+=09=09case=20= PG_TLS1_2_VERSION:=0A+#ifdef=20TLS1_2_VERSION=0A+=09=09=09return=20= TLS1_2_VERSION;=0A+#else=0A+=09=09=09break;=0A+#endif=0A+=09=09case=20= PG_TLS1_3_VERSION:=0A+#ifdef=20TLS1_3_VERSION=0A+=09=09=09return=20= TLS1_3_VERSION;=0A+#else=0A+=09=09=09break;=0A+#endif=0A+=09}=0A+=0A+=09= return=20-1;=0A+}=0A+=0A+/*=0A+=20*=20Likewise=20provide=20a=20mapping=20= to=20strings.=0A+=20*/=0A+static=20const=20char=20*=0A= +ssl_protocol_version_to_string(int=20v)=0A+{=0A+=09switch=20(v)=0A+=09{=0A= +=09=09case=20PG_TLS_ANY:=0A+=09=09=09return=20"any";=0A+=09=09case=20= PG_TLS1_VERSION:=0A+=09=09=09return=20"TLSv1";=0A+=09=09case=20= PG_TLS1_1_VERSION:=0A+=09=09=09return=20"TLSv1.1";=0A+=09=09case=20= PG_TLS1_2_VERSION:=0A+=09=09=09return=20"TLSv1.2";=0A+=09=09case=20= PG_TLS1_3_VERSION:=0A+=09=09=09return=20"TLSv1.3";=0A+=09}=0A+=0A+=09= return=20"(unrecognized)";=0A+}=0A+=0A+static=20uint32=0A= +host_cache_pointer(const=20char=20*key)=0A+{=0A+=09uint32=09=09hash;=0A= +=09char=09=20=20=20*lkey=20=3D=20pstrdup(key);=0A+=09int=09=09=09len=20= =3D=20strlen(key);=0A+=0A+=09for=20(int=20i=20=3D=200;=20i=20<=20len;=20= i++)=0A+=09=09lkey[i]=20=3D=20pg_tolower(lkey[i]);=0A+=0A+=09hash=20=3D=20= string_hash((const=20void=20*)=20lkey,=20len);=0A+=09pfree(lkey);=0A+=09= return=20hash;=0A+}=0A+=0A+static=20void=0A= +default_openssl_tls_init(SSL_CTX=20*context,=20bool=20isServerStart)=0A= +{=0A+=09if=20(isServerStart)=0A+=09{=0A+=09=09if=20= (ssl_passphrase_command[0])=0A+=09=09{=0A+=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20ssl_external_passwd_cb);=0A+=09=09= =09SSL_CTX_set_default_passwd_cb_userdata(context,=20= ssl_passphrase_command);=0A+=09=09}=0A+=09}=0A+=09else=0A+=09{=0A+=09=09= if=20(ssl_passphrase_command[0]=20&&=20= ssl_passphrase_command_supports_reload)=0A+=09=09{=0A+=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20ssl_external_passwd_cb);=0A+=09=09= =09SSL_CTX_set_default_passwd_cb_userdata(context,=20= ssl_passphrase_command);=0A+=09=09}=0A+=09=09else=0A+=0A+=09=09=09/*=0A+=09= =09=09=20*=20If=20reloading=20and=20no=20external=20command=20is=20= configured,=20override=0A+=09=09=09=20*=20OpenSSL's=20default=20handling=20= of=20passphrase-protected=20files,=0A+=09=09=09=20*=20because=20we=20= don't=20want=20to=20prompt=20for=20a=20passphrase=20in=20an=0A+=09=09=09=20= *=20already-running=20server.=0A+=09=09=09=20*/=0A+=09=09=09= SSL_CTX_set_default_passwd_cb(context,=20dummy_ssl_passwd_cb);=0A+=09}=0A= +}=0A+=0Adiff=20--git=20a/src/backend/libpq/be-secure-openssl.c=20= b/src/backend/libpq/be-secure-openssl.c=0Aindex=20= 4ce2a92b964..ce185428ad4=20100644=0A---=20= a/src/backend/libpq/be-secure-openssl.c=0A+++=20= b/src/backend/libpq/be-secure-openssl.c=0A@@=20-100,9=20+100,7=20@@=20= static=20const=20char=20*SSLerrmessageExt(unsigned=20long=20ecode,=20= const=20char=20*replacement=0A=20static=20const=20char=20= *SSLerrmessage(unsigned=20long=20ecode);=0A=20static=20bool=20= init_host_context(HostsLine=20*host,=20bool=20isServerStart);=0A=20= static=20void=20host_context_cleanup_cb(void=20*arg);=0A-#ifdef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A=20static=20int=09= sni_clienthello_cb(SSL=20*ssl,=20int=20*al,=20void=20*arg);=0A-#endif=0A=20= =0A=20static=20char=20*X509_NAME_to_cstring(const=20X509_NAME=20*name);=0A= =20=0A@@=20-204,17=20+202,6=20@@=20be_tls_init(bool=20isServerStart)=0A=20= =09=20*/=0A=20=09if=20(ssl_sni)=0A=20=09{=0A-=09=09/*=0A-=09=09=20*=20= The=20GUC=20check=20hook=20should=20have=20already=20blocked=20this=20= but=20to=20be=20on=0A-=09=09=20*=20the=20safe=20side=20we=20double-check=20= here.=0A-=09=09=20*/=0A-#ifndef=20HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A-=09= =09ereport(isServerStart=20?=20FATAL=20:=20LOG,=0A-=09=09=09=09= errcode(ERRCODE_CONFIG_FILE_ERROR),=0A-=09=09=09=09errmsg("ssl_sni=20is=20= not=20supported=20with=20LibreSSL"));=0A-=09=09goto=20error;=0A-#endif=0A= -=0A=20=09=09/*=20Attempt=20to=20load=20configuration=20from=20= pg_hosts.conf=20*/=0A=20=09=09res=20=3D=20load_hosts(&pg_hosts,=20= &err_msg);=0A=20=0A@@=20-369,8=20+356,6=20@@=20be_tls_init(bool=20= isServerStart)=0A=20=09=09goto=20error;=0A=20=09}=0A=20=0A-#ifdef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A-=0A=20=09/*=0A=20=09=20*=20Create=20= a=20new=20SSL=20context=20into=20which=20we'll=20load=20all=20the=20= configuration=0A=20=09=20*=20settings.=20=20If=20we=20fail=20partway=20= through,=20we=20can=20avoid=20memory=20leakage=20by=0A@@=20-389,22=20= +374,6=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09=09=09=09=09=09= SSLerrmessage(ERR_get_error()))));=0A=20=09=09goto=20error;=0A=20=09}=0A= -#else=0A-=0A-=09/*=0A-=09=20*=20If=20the=20client=20hello=20callback=20= isn't=20supported=20we=20want=20to=20use=20the=20default=0A-=09=20*=20= context=20as=20the=20one=20to=20drive=20the=20handshake=20so=20avoid=20= creating=20a=20new=20one=0A-=09=20*=20and=20use=20the=20already=20= existing=20default=20one=20instead.=0A-=09=20*/=0A-=09context=20=3D=20= new_hosts->default_host->ssl_ctx;=0A-=0A-=09/*=0A-=09=20*=20Since=20we=20= don't=20allocate=20a=20new=20SSL_CTX=20here=20like=20we=20do=20when=20= SNI=20has=20been=0A-=09=20*=20enabled=20we=20need=20to=20bump=20the=20= reference=20count=20on=20context=20to=20avoid=20double=0A-=09=20*=20free=20= of=20the=20context=20when=20using=20the=20same=20cleanup=20logic=20= across=20the=20cases.=0A-=09=20*/=0A-=09SSL_CTX_up_ref(context);=0A= -#endif=0A=20=0A=20=09/*=0A=20=09=20*=20Disable=20OpenSSL's=20= moving-write-buffer=20sanity=20check,=20because=20it=20causes=0A@@=20= -481,15=20+450,10=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09/*=0A=20= =09=20*=20Disallow=20SSL=20session=20tickets.=20OpenSSL=20use=20both=20= stateful=20and=20stateless=0A=20=09=20*=20tickets=20for=20TLSv1.3,=20and=20= stateless=20ticket=20for=20TLSv1.2.=20SSL_OP_NO_TICKET=0A-=09=20*=20is=20= available=20since=200.9.8f=20but=20only=20turns=20off=20stateless=20= tickets.=20In=0A-=09=20*=20order=20to=20turn=20off=20stateful=20tickets=20= we=20need=20SSL_CTX_set_num_tickets,=0A-=09=20*=20which=20is=20available=20= since=20OpenSSL=201.1.1.=20=20LibreSSL=203.5.4=20(from=20OpenBSD=0A-=09=20= *=207.1)=20introduced=20this=20API=20for=20compatibility,=20but=20= doesn't=20support=20session=0A-=09=20*=20tickets=20at=20all=20so=20it's=20= a=20no-op=20there.=0A+=09=20*=20only=20turns=20off=20stateless=20= tickets,=20In=20order=20to=20turn=20off=20stateful=20tickets=0A+=09=20*=20= we=20need=20SSL_CTX_set_num_tickets.=0A=20=09=20*/=0A-#ifdef=20= HAVE_SSL_CTX_SET_NUM_TICKETS=0A=20=09SSL_CTX_set_num_tickets(context,=20= 0);=0A-#endif=0A=20=09SSL_CTX_set_options(context,=20SSL_OP_NO_TICKET);=0A= =20=0A=20=09/*=20disallow=20SSL=20session=20caching,=20too=20*/=0A@@=20= -501,17=20+465,8=20@@=20be_tls_init(bool=20isServerStart)=0A=20=09/*=0A=20= =09=20*=20Disallow=20SSL=20renegotiation.=20=20This=20concerns=20only=20= TLSv1.2=20and=20older=0A=20=09=20*=20protocol=20versions,=20as=20TLSv1.3=20= has=20no=20support=20for=20renegotiation.=0A-=09=20*=20= SSL_OP_NO_RENEGOTIATION=20is=20available=20in=20OpenSSL=20since=201.1.0h=20= (via=20a=0A-=09=20*=20backport=20from=201.1.1).=20= SSL_OP_NO_CLIENT_RENEGOTIATION=20is=20available=20in=0A-=09=20*=20= LibreSSL=20since=202.5.1=20disallowing=20all=20client-initiated=20= renegotiation=0A-=09=20*=20(this=20is=20usually=20on=20by=20default).=0A=20= =09=20*/=0A-#ifdef=20SSL_OP_NO_RENEGOTIATION=0A=20=09= SSL_CTX_set_options(context,=20SSL_OP_NO_RENEGOTIATION);=0A-#endif=0A= -#ifdef=20SSL_OP_NO_CLIENT_RENEGOTIATION=0A-=09= SSL_CTX_set_options(context,=20SSL_OP_NO_CLIENT_RENEGOTIATION);=0A= -#endif=0A=20=0A=20=09/*=20set=20up=20ephemeral=20DH=20and=20ECDH=20keys=20= */=0A=20=09if=20(!initialize_dh(context,=20isServerStart))=0A@@=20= -873,36=20+828,14=20@@=20be_tls_open_server(Port=20*port)=0A=20=09}=0A=20= =0A=20=09/*=0A-=09=20*=20If=20the=20underlying=20TLS=20library=20= supports=20the=20client=20hello=20callback=20we=20use=0A-=09=20*=20that=20= in=20order=20to=20support=20host=20based=20configuration=20using=20the=20= SNI=20TLS=0A-=09=20*=20extension.=20=20If=20the=20user=20has=20disabled=20= SNI=20via=20the=20ssl_sni=20GUC=20we=20still=0A-=09=20*=20make=20use=20= of=20the=20callback=20in=20order=20to=20have=20consistent=20handling=20= of=0A-=09=20*=20OpenSSL=20contexts,=20except=20in=20that=20case=20the=20= callback=20will=20install=20the=0A-=09=20*=20default=20configuration=20= regardless=20of=20the=20hostname=20sent=20by=20the=20user=20in=0A-=09=20= *=20the=20handshake.=0A-=09=20*=0A-=09=20*=20In=20case=20the=20TLS=20= library=20does=20not=20support=20the=20client=20hello=20callback,=20as=0A= -=09=20*=20of=20this=20writing=20LibreSSL=20does=20not,=20we=20need=20to=20= install=20the=20client=20cert=0A-=09=20*=20verification=20callback=20= here=20(if=20the=20user=20configured=20a=20CA)=20since=20we=0A-=09=20*=20= cannot=20use=20the=20OpenSSL=20context=20update=20functionality.=0A+=09=20= *=20We=20use=20tje=20clienthello=20callback=20in=20order=20to=20support=20= host=20based=0A+=09=20*=20configuration=20using=20the=20SNI=20TLS=20= extension.=20=20If=20the=20user=20has=20disabled=20SNI=0A+=09=20*=20via=20= the=20ssl_sni=20GUC=20we=20still=20make=20use=20of=20the=20callback=20in=20= order=20to=20have=0A+=09=20*=20consistent=20handling=20of=20OpenSSL=20= contexts,=20except=20in=20that=20case=20the=0A+=09=20*=20callback=20will=20= install=20the=20default=20configuration=20regardless=20of=20the=0A+=09=20= *=20hostname=20sent=20by=20the=20user=20in=20the=20handshake.=0A=20=09=20= */=0A-#ifdef=20HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A=20=09= SSL_CTX_set_client_hello_cb(SSL_context,=20sni_clienthello_cb,=20NULL);=0A= -#else=0A-=09if=20(SSL_hosts->default_host->ssl_ca=20&&=20= SSL_hosts->default_host->ssl_ca[0])=0A-=09{=0A-=09=09/*=0A-=09=09=20*=20= Always=20ask=20for=20SSL=20client=20cert,=20but=20don't=20fail=20if=20= it's=20not=0A-=09=09=20*=20presented.=20=20We=20might=20fail=20such=20= connections=20later,=20depending=20on=20what=0A-=09=09=20*=20we=20find=20= in=20pg_hba.conf.=0A-=09=09=20*/=0A-=09=09SSL_set_verify(port->ssl,=0A-=09= =09=09=09=09=20=20=20(SSL_VERIFY_PEER=20|=20SSL_VERIFY_CLIENT_ONCE),=0A-=09= =09=09=09=09=20=20=20verify_cb);=0A-=0A-=09=09= ssl_loaded_verify_locations=20=3D=20true;=0A-=09}=0A-#endif=0A=20=0A=20=09= err_context.cert_errdetail=20=3D=20NULL;=0A=20=09= SSL_set_ex_data(port->ssl,=200,=20&err_context);=0A@@=20-990,12=20+923,8=20= @@=20aloop:=0A=20=09=09=09=09=09case=20SSL_R_WRONG_SSL_VERSION:=0A=20=09=09= =09=09=09case=20SSL_R_WRONG_VERSION_NUMBER:=0A=20=09=09=09=09=09case=20= SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:=0A-#ifdef=20SSL_R_VERSION_TOO_HIGH=0A= =20=09=09=09=09=09case=20SSL_R_VERSION_TOO_HIGH:=0A-#endif=0A-#ifdef=20= SSL_R_VERSION_TOO_LOW=0A=20=09=09=09=09=09case=20SSL_R_VERSION_TOO_LOW:=0A= -#endif=0A=20=09=09=09=09=09=09give_proto_hint=20=3D=20true;=0A=20=09=09=09= =09=09=09break;=0A=20=09=09=09=09=09default:=0A@@=20-1810,7=20+1739,6=20= @@=20alpn_cb(SSL=20*ssl,=0A=20=09}=0A=20}=0A=20=0A-#ifdef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A=20/*=0A=20=20*=20ssl_update_ssl=0A=20= =20*=0A@@=20-2057,7=20+1985,6=20@@=20found:=0A=20=0A=20=09return=20= SSL_CLIENT_HELLO_SUCCESS;=0A=20}=0A-#endif=09=09=09=09=09=09=09/*=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=20*/=0A=20=0A=20/*=0A=20=20*=20Set=20DH=20= parameters=20for=20generating=20ephemeral=20DH=20keys.=20=20The=0A@@=20= -2282,15=20+2209,9=20@@=20be_tls_get_certificate_hash(Port=20*port,=20= size_t=20*len)=0A=20=0A=20=09/*=0A=20=09=20*=20Get=20the=20signature=20= algorithm=20of=20the=20certificate=20to=20determine=20the=20hash=0A-=09=20= *=20algorithm=20to=20use=20for=20the=20result.=20=20Prefer=20= X509_get_signature_info(),=0A-=09=20*=20introduced=20in=20OpenSSL=20= 1.1.1,=20which=20can=20handle=20RSA-PSS=20signatures.=0A+=09=20*=20= algorithm=20to=20use=20for=20the=20result.=0A=20=09=20*/=0A-#if=20= HAVE_X509_GET_SIGNATURE_INFO=0A=20=09if=20= (!X509_get_signature_info(server_cert,=20&algo_nid,=20NULL,=20NULL,=20= NULL))=0A-#else=0A-=09if=20= (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert),=0A-=09=09=09=09= =09=09=09=20&algo_nid,=20NULL))=0A-#endif=0A=20=09=09elog(ERROR,=20= "could=20not=20determine=20server=20certificate=20signature=20= algorithm");=0A=20=0A=20=09/*=0Adiff=20--git=20= a/src/backend/libpq/meson.build=20b/src/backend/libpq/meson.build=0A= index=208571f652844..d835d356335=20100644=0A---=20= a/src/backend/libpq/meson.build=0A+++=20b/src/backend/libpq/meson.build=0A= @@=20-18,7=20+18,11=20@@=20backend_sources=20+=3D=20files(=0A=20)=0A=20=0A= =20if=20ssl.found()=0A-=20=20backend_sources=20+=3D=20= files('be-secure-openssl.c')=0A+=20=20if=20ssl_library=20=3D=3D=20= 'openssl'=0A+=20=20=20=20backend_sources=20+=3D=20= files('be-secure-openssl.c')=0A+=20=20else=0A+=20=20=20=20= backend_sources=20+=3D=20files('be-secure-libressl.c')=0A+=20=20endif=0A=20= endif=0A=20=0A=20if=20gssapi.found()=0Adiff=20--git=20= a/src/include/common/openssl.h=20b/src/include/common/openssl.h=0Aindex=20= 07e8bfbe4f0..c253ebe5e72=20100644=0A---=20a/src/include/common/openssl.h=0A= +++=20b/src/include/common/openssl.h=0A@@=20-14,7=20+14,7=20@@=0A=20= #ifndef=20COMMON_OPENSSL_H=0A=20#define=20COMMON_OPENSSL_H=0A=20=0A= -#ifdef=20USE_OPENSSL=0A+#if=20defined(USE_OPENSSL)=20||=20= defined(USE_LIBRESSL)=0A=20#include=20=0A=20=0A=20/*=0A@@=20= -38,6=20+38,6=20@@=0A=20#define=20MAX_OPENSSL_TLS_VERSION=20=20"TLSv1"=0A= =20#endif=0A=20=0A-#endif=09=09=09=09=09=09=09/*=20USE_OPENSSL=20*/=0A= +#endif=09=09=09=09=09=09=09/*=20USE_OPENSSL,=20USE_LIBRESSL=20*/=0A=20=0A= =20#endif=09=09=09=09=09=09=09/*=20COMMON_OPENSSL_H=20*/=0Adiff=20--git=20= a/src/include/libpq/libpq-be.h=20b/src/include/libpq/libpq-be.h=0Aindex=20= 921b2daa4ff..71925270690=20100644=0A---=20a/src/include/libpq/libpq-be.h=0A= +++=20b/src/include/libpq/libpq-be.h=0A@@=20-21,7=20+21,7=20@@=0A=20= #include=20"common/scram-common.h"=0A=20=0A=20#include=20=0A= -#ifdef=20USE_OPENSSL=0A+#if=20defined(USE_OPENSSL)=20||=20= defined(USE_LIBRESSL)=0A=20#include=20=0A=20#include=20= =0A=20#endif=0A@@=20-218,7=20+218,7=20@@=20typedef=20= struct=20Port=0A=20=09=20*=20(Although=20extensions=20should=20have=20no=20= business=20accessing=20the=20raw_buf=0A=20=09=20*=20fields=20anyway.)=0A=20= =09=20*/=0A-#ifdef=20USE_OPENSSL=0A+#if=20defined(USE_OPENSSL)=20||=20= defined(USE_LIBRESSL)=0A=20=09SSL=09=09=20=20=20*ssl;=0A=20=09X509=09=20=20= =20*peer;=0A=20#else=0A@@=20-331,12=20+331,12=20@@=20extern=20void=20= be_tls_get_peer_serial(Port=20*port,=20char=20*ptr,=20size_t=20len);=0A=20= extern=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=20= the=20password=20callback=20if=20appropriate=20*/=0A-#ifdef=20= USE_OPENSSL=0A+#if=20defined(USE_OPENSSL)=20||=20defined(USE_LIBRESSL)=0A= =20typedef=20void=20(*openssl_tls_init_hook_typ)=20(SSL_CTX=20*context,=20= bool=20isServerStart);=0A=20extern=20PGDLLIMPORT=20= openssl_tls_init_hook_typ=20openssl_tls_init_hook;=0A=20#endif=0A=20=0A= -#endif=09=09=09=09=09=09=09/*=20USE_SSL=20*/=0A+#endif=09=09=09=09=09=09= =09/*=20USE_SSL,=20USE_LIBRESSL=20*/=0A=20=0A=20#ifdef=20ENABLE_GSS=0A=20= /*=0Adiff=20--git=20a/src/include/libpq/libpq.h=20= b/src/include/libpq/libpq.h=0Aindex=20d15073a0a93..05d80831715=20100644=0A= ---=20a/src/include/libpq/libpq.h=0A+++=20b/src/include/libpq/libpq.h=0A= @@=20-123,12=20+123,16=20@@=20extern=20PGDLLIMPORT=20bool=20= ssl_loaded_verify_locations;=0A=20#endif=0A=20=0A=20#ifdef=20USE_SSL=0A= +#if=20defined(USE_OPENSSL)=0A=20#define=20SSL_LIBRARY=20"OpenSSL"=0A= +#elif=20defined(USE_LIBRESSL)=0A+#define=20SSL_LIBRARY=20"LibreSSL"=0A=20= #else=0A=20#define=20SSL_LIBRARY=20""=0A=20#endif=0A+#endif=0A=20=0A= -#ifdef=20USE_OPENSSL=0A+#ifdef=20USE_SSL=0A=20#define=20= DEFAULT_SSL_CIPHERS=20"HIGH:MEDIUM:+3DES:!aNULL"=0A=20#else=0A=20#define=20= DEFAULT_SSL_CIPHERS=20"none"=0Adiff=20--git=20= a/src/include/pg_config.h.in=20b/src/include/pg_config.h.in=0Aindex=20= 4f8113c144b..4bfab619a61=20100644=0A---=20a/src/include/pg_config.h.in=0A= +++=20b/src/include/pg_config.h.in=0A@@=20-369,20=20+369,12=20@@=0A=20/*=20= Define=20to=201=20if=20the=20system=20has=20the=20type=20`socklen_t'.=20= */=0A=20#undef=20HAVE_SOCKLEN_T=0A=20=0A-/*=20Define=20to=201=20if=20you=20= have=20the=20`SSL_CTX_set_cert_cb'=20function.=20*/=0A-#undef=20= HAVE_SSL_CTX_SET_CERT_CB=0A-=0A=20/*=20Define=20to=201=20if=20you=20have=20= the=20`SSL_CTX_set_ciphersuites'=20function.=20*/=0A=20#undef=20= HAVE_SSL_CTX_SET_CIPHERSUITES=0A=20=0A-/*=20Define=20to=201=20if=20you=20= have=20the=20`SSL_CTX_set_client_hello_cb'=20function.=20*/=0A-#undef=20= HAVE_SSL_CTX_SET_CLIENT_HELLO_CB=0A-=0A-/*=20Define=20to=201=20if=20you=20= have=20the=20`SSL_CTX_set_keylog_callback'=20function.=20*/=0A-#undef=20= HAVE_SSL_CTX_SET_KEYLOG_CALLBACK=0A-=0A-/*=20Define=20to=201=20if=20you=20= have=20the=20`SSL_CTX_set_num_tickets'=20function.=20*/=0A-#undef=20= HAVE_SSL_CTX_SET_NUM_TICKETS=0A+/*=20Define=20to=201=20if=20you=20have=20= the=20`SSL_CTX_use_certificate_chain_mem'=20function.=0A+=20=20=20*/=0A= +#undef=20HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM=0A=20=0A=20/*=20Define=20= to=201=20if=20you=20have=20the=20=20header=20file.=20*/=0A=20= #undef=20HAVE_STDINT_H=0A@@=20-508,9=20+500,6=20@@=0A=20/*=20Define=20to=20= 1=20if=20you=20have=20the=20`wcstombs_l'=20function.=20*/=0A=20#undef=20= HAVE_WCSTOMBS_L=0A=20=0A-/*=20Define=20to=201=20if=20you=20have=20the=20= `X509_get_signature_info'=20function.=20*/=0A-#undef=20= HAVE_X509_GET_SIGNATURE_INFO=0A-=0A=20/*=20Define=20to=201=20if=20the=20= assembler=20supports=20X86_64's=20POPCNTQ=20instruction.=20*/=0A=20= #undef=20HAVE_X86_64_POPCNTQ=0A=20=0A@@=20-707,6=20+696,9=20@@=0A=20/*=20= Define=20to=20build=20with=20NUMA=20support.=20(--with-libnuma)=20*/=0A=20= #undef=20USE_LIBNUMA=0A=20=0A+/*=20Define=20to=201=20to=20build=20with=20= LibreSSL=20support.=20(--with-ssl=3Dlibressl)=20*/=0A+#undef=20= USE_LIBRESSL=0A+=0A=20/*=20Define=20to=20build=20with=20io_uring=20= support.=20(--with-liburing)=20*/=0A=20#undef=20USE_LIBURING=0A=20=0A= diff=20--git=20a/src/include/pg_config_manual.h=20= b/src/include/pg_config_manual.h=0Aindex=20521b49b8888..e01fee2a9af=20= 100644=0A---=20a/src/include/pg_config_manual.h=0A+++=20= b/src/include/pg_config_manual.h=0A@@=20-173,7=20+173,7=20@@=0A=20=20*=20= USE_SSL=20code=20should=20be=20compiled=20only=20when=20compiling=20with=20= an=20SSL=0A=20=20*=20implementation.=0A=20=20*/=0A-#ifdef=20USE_OPENSSL=0A= +#if=20defined(USE_OPENSSL)=20||=20defined(USE_LIBRESSL)=0A=20#define=20= USE_SSL=0A=20#endif=0A=20=0Adiff=20--git=20= a/src/interfaces/libpq/Makefile=20b/src/interfaces/libpq/Makefile=0A= index=200963995eed4..ac925319bc6=20100644=0A---=20= a/src/interfaces/libpq/Makefile=0A+++=20b/src/interfaces/libpq/Makefile=0A= @@=20-56,6=20+56,10=20@@=20ifeq=20($(with_ssl),openssl)=0A=20OBJS=20+=3D=20= \=0A=20=09fe-secure-openssl.o=0A=20endif=0A+ifeq=20= ($(with_ssl),libressl)=0A+OBJS=20+=3D=20\=0A+=09fe-secure-libressl.o=0A= +endif=0A=20=0A=20ifeq=20($(with_gssapi),yes)=0A=20OBJS=20+=3D=20\=0A= diff=20--git=20a/src/interfaces/libpq/fe-secure-libressl.c=20= b/src/interfaces/libpq/fe-secure-libressl.c=0Anew=20file=20mode=20100644=0A= index=2000000000000..22455c59af7=0A---=20/dev/null=0A+++=20= b/src/interfaces/libpq/fe-secure-libressl.c=0A@@=20-0,0=20+1,1905=20@@=0A= +/*-----------------------------------------------------------------------= --=0A+=20*=0A+=20*=20fe-secure-libressl.c=0A+=20*=09=20=20LibreSSL=20= support=0A+=20*=0A+=20*=0A+=20*=20Portions=20Copyright=20(c)=20= 1996-2026,=20PostgreSQL=20Global=20Development=20Group=0A+=20*=20= Portions=20Copyright=20(c)=201994,=20Regents=20of=20the=20University=20= of=20California=0A+=20*=0A+=20*=0A+=20*=20IDENTIFICATION=0A+=20*=09=20=20= src/interfaces/libpq/fe-secure-libressl.c=0A+=20*=0A+=20*=20NOTES=0A+=20= *=0A+=20*=09=20=20We=20don't=20provide=20informational=20callbacks=20= here=20(like=0A+=20*=09=20=20info_cb()=20in=20be-secure-openssl.c),=20= since=20there's=20no=20good=20mechanism=20to=0A+=20*=09=20=20display=20= such=20information=20to=20the=20user.=0A+=20*=0A+=20= *-------------------------------------------------------------------------= =0A+=20*/=0A+=0A+#include=20"postgres_fe.h"=0A+=0A+#include=20=0A= +#include=20=0A+#include=20=0A+#include=20=0A= +=0A+#include=20"libpq-fe.h"=0A+#include=20"fe-auth.h"=0A+#include=20= "fe-secure-common.h"=0A+#include=20"libpq-int.h"=0A+=0A+#ifdef=20WIN32=0A= +#include=20"win32.h"=0A+#else=0A+#include=20=0A+#include=20= =0A+#include=20=0A+#include=20=0A= +#include=20=0A+#include=20=0A+#endif=0A+=0A= +#include=20=0A+=0A+#ifdef=20WIN32=0A+#include=20= "pthread-win32.h"=0A+#else=0A+#include=20=0A+#endif=0A+=0A+/*=0A= +=20*=20These=20SSL-related=20#includes=20must=20come=20after=20all=20= system-provided=20headers.=0A+=20*=20This=20ensures=20that=20OpenSSL=20= can=20take=20care=20of=20conflicts=20with=20Windows'=0A+=20*=20= =20by=20#undef'ing=20the=20conflicting=20macros.=20=20(We=20= don't=20directly=0A+=20*=20include=20,=20but=20some=20other=20= Windows=20headers=20do.)=0A+=20*/=0A+#include=20"common/openssl.h"=0A= +#include=20=0A+#include=20=0A+#ifdef=20= USE_SSL_ENGINE=0A+#include=20=0A+#endif=0A+#include=20= =0A+=0A+=0A+static=20int=09verify_cb(int=20ok,=20= X509_STORE_CTX=20*ctx);=0A+static=20int=09= openssl_verify_peer_name_matches_certificate_name(PGconn=20*conn,=0A+=09=09= =09=09=09=09=09=09=09=09=09=09=09=09=09=20=20ASN1_STRING=20*name_entry,=0A= +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20char=20= **store_name);=0A+static=20int=09= openssl_verify_peer_name_matches_certificate_ip(PGconn=20*conn,=0A+=09=09= =09=09=09=09=09=09=09=09=09=09=09=09=09ASN1_OCTET_STRING=20*addr_entry,=0A= +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09char=20**store_name);=0A= +static=20int=09initialize_SSL(PGconn=20*conn);=0A+static=20= PostgresPollingStatusType=20open_client_SSL(PGconn=20*conn);=0A+static=20= char=20*SSLerrmessage(unsigned=20long=20ecode);=0A+static=20void=20= SSLerrfree(char=20*buf);=0A+static=20int=09PQssl_passwd_cb(char=20*buf,=20= int=20size,=20int=20rwflag,=20void=20*userdata);=0A+=0A+static=20int=09= pgconn_bio_read(BIO=20*h,=20char=20*buf,=20int=20size);=0A+static=20int=09= pgconn_bio_write(BIO=20*h,=20const=20char=20*buf,=20int=20size);=0A= +static=20BIO_METHOD=20*pgconn_bio_method(void);=0A+static=20int=09= ssl_set_pgconn_bio(PGconn=20*conn);=0A+=0A+static=20pthread_mutex_t=20= ssl_config_mutex=20=3D=20PTHREAD_MUTEX_INITIALIZER;=0A+=0A+static=20= PQsslKeyPassHook_OpenSSL_type=20PQsslKeyPassHook=20=3D=20NULL;=0A+static=20= int=09ssl_protocol_version_to_openssl(const=20char=20*protocol);=0A+=0A= +/*=20------------------------------------------------------------=20*/=0A= +/*=09=09=09=20Procedures=20common=20to=20all=20secure=20sessions=09=09=09= */=0A+/*=20------------------------------------------------------------=20= */=0A+=0A+PostgresPollingStatusType=0A+pgtls_open_client(PGconn=20*conn)=0A= +{=0A+=09/*=20First=20time=20through?=20*/=0A+=09if=20(conn->ssl=20=3D=3D=20= NULL)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20Create=20a=20= connection-specific=20SSL=20object,=20and=20load=20client=0A+=09=09=20*=20= certificate,=20private=20key,=20and=20trusted=20CA=20certs.=0A+=09=09=20= */=0A+=09=09if=20(initialize_SSL(conn)=20!=3D=200)=0A+=09=09{=0A+=09=09=09= /*=20initialize_SSL=20already=20put=20a=20message=20in=20= conn->errorMessage=20*/=0A+=09=09=09pgtls_close(conn);=0A+=09=09=09= return=20PGRES_POLLING_FAILED;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20Begin=20= or=20continue=20the=20actual=20handshake=20*/=0A+=09return=20= open_client_SSL(conn);=0A+}=0A+=0A+ssize_t=0A+pgtls_read(PGconn=20*conn,=20= void=20*ptr,=20size_t=20len)=0A+{=0A+=09ssize_t=09=09n;=0A+=09int=09=09=09= result_errno=20=3D=200;=0A+=09char=09=09sebuf[PG_STRERROR_R_BUFLEN];=0A+=09= int=09=09=09err;=0A+=09unsigned=20long=20ecode;=0A+=0A+rloop:=0A+=0A+=09= /*=0A+=09=20*=20Prepare=20to=20call=20SSL_get_error()=20by=20clearing=20= thread's=20OpenSSL=20error=0A+=09=20*=20queue.=20=20In=20general,=20the=20= current=20thread's=20error=20queue=20must=20be=20empty=0A+=09=20*=20= before=20the=20TLS/SSL=20I/O=20operation=20is=20attempted,=20or=20= SSL_get_error()=20will=0A+=09=20*=20not=20work=20reliably.=20=20Since=20= the=20possibility=20exists=20that=20other=20OpenSSL=0A+=09=20*=20clients=20= running=20in=20the=20same=20thread=20but=20not=20under=20our=20control=20= will=20fail=0A+=09=20*=20to=20call=20ERR_get_error()=20themselves=20= (after=20their=20own=20I/O=20operations),=0A+=09=20*=20pro-actively=20= clear=20the=20per-thread=20error=20queue=20now.=0A+=09=20*/=0A+=09= SOCK_ERRNO_SET(0);=0A+=09ERR_clear_error();=0A+=09n=20=3D=20= SSL_read(conn->ssl,=20ptr,=20len);=0A+=09err=20=3D=20= SSL_get_error(conn->ssl,=20n);=0A+=0A+=09/*=0A+=09=20*=20Other=20clients=20= of=20OpenSSL=20may=20fail=20to=20call=20ERR_get_error(),=20but=20we=0A+=09= =20*=20always=20do,=20so=20as=20to=20not=20cause=20problems=20for=20= OpenSSL=20clients=20that=20don't=0A+=09=20*=20call=20ERR_clear_error()=20= defensively.=20=20Be=20sure=20that=20this=20happens=20by=0A+=09=20*=20= calling=20now.=20=20SSL_get_error()=20relies=20on=20the=20OpenSSL=20= per-thread=20error=0A+=09=20*=20queue=20being=20intact,=20so=20this=20is=20= the=20earliest=20possible=20point=0A+=09=20*=20ERR_get_error()=20may=20= be=20called.=0A+=09=20*/=0A+=09ecode=20=3D=20(err=20!=3D=20= SSL_ERROR_NONE=20||=20n=20<=200)=20?=20ERR_get_error()=20:=200;=0A+=09= switch=20(err)=0A+=09{=0A+=09=09case=20SSL_ERROR_NONE:=0A+=09=09=09if=20= (n=20<=200)=0A+=09=09=09{=0A+=09=09=09=09/*=20Not=20supposed=20to=20= happen,=20so=20we=20don't=20translate=20the=20msg=20*/=0A+=09=09=09=09= appendPQExpBufferStr(&conn->errorMessage,=0A+=09=09=09=09=09=09=09=09=09=20= "SSL_read=20failed=20but=20did=20not=20provide=20error=20= information\n");=0A+=09=09=09=09/*=20assume=20the=20connection=20is=20= broken=20*/=0A+=09=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09= }=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_WANT_READ:=0A+=09=09=09n=20= =3D=200;=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_WANT_WRITE:=0A+=0A= +=09=09=09/*=0A+=09=09=09=20*=20Returning=200=20here=20would=20cause=20= caller=20to=20wait=20for=20read-ready,=0A+=09=09=09=20*=20which=20is=20= not=20correct=20since=20what=20SSL=20wants=20is=20wait=20for=0A+=09=09=09= =20*=20write-ready.=20=20The=20former=20could=20get=20us=20stuck=20in=20= an=20infinite=0A+=09=09=09=20*=20wait,=20so=20don't=20risk=20it;=20= busy-loop=20instead.=0A+=09=09=09=20*/=0A+=09=09=09goto=20rloop;=0A+=09=09= case=20SSL_ERROR_SYSCALL:=0A+=09=09=09if=20(n=20<=200=20&&=20SOCK_ERRNO=20= !=3D=200)=0A+=09=09=09{=0A+=09=09=09=09result_errno=20=3D=20SOCK_ERRNO;=0A= +=09=09=09=09if=20(result_errno=20=3D=3D=20EPIPE=20||=0A+=09=09=09=09=09= result_errno=20=3D=3D=20ECONNRESET)=0A+=09=09=09=09=09= libpq_append_conn_error(conn,=20"server=20closed=20the=20connection=20= unexpectedly\n"=0A+=09=09=09=09=09=09=09=09=09=09=09"\tThis=20probably=20= means=20the=20server=20terminated=20abnormally\n"=0A+=09=09=09=09=09=09=09= =09=09=09=09"\tbefore=20or=20while=20processing=20the=20request.");=0A+=09= =09=09=09else=0A+=09=09=09=09=09libpq_append_conn_error(conn,=20"SSL=20= SYSCALL=20error:=20%s",=0A+=09=09=09=09=09=09=09=09=09=09=09= SOCK_STRERROR(result_errno,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09=09= =20=20sebuf,=20sizeof(sebuf)));=0A+=09=09=09}=0A+=09=09=09else=0A+=09=09=09= {=0A+=09=09=09=09libpq_append_conn_error(conn,=20"SSL=20SYSCALL=20error:=20= EOF=20detected");=0A+=09=09=09=09/*=20assume=20the=20connection=20is=20= broken=20*/=0A+=09=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09=09= n=20=3D=20-1;=0A+=09=09=09}=0A+=09=09=09break;=0A+=09=09case=20= SSL_ERROR_SSL:=0A+=09=09=09{=0A+=09=09=09=09char=09=20=20=20*errm=20=3D=20= SSLerrmessage(ecode);=0A+=0A+=09=09=09=09libpq_append_conn_error(conn,=20= "SSL=20error:=20%s",=20errm);=0A+=09=09=09=09SSLerrfree(errm);=0A+=09=09=09= =09/*=20assume=20the=20connection=20is=20broken=20*/=0A+=09=09=09=09= result_errno=20=3D=20ECONNRESET;=0A+=09=09=09=09n=20=3D=20-1;=0A+=09=09=09= =09break;=0A+=09=09=09}=0A+=09=09case=20SSL_ERROR_ZERO_RETURN:=0A+=0A+=09= =09=09/*=0A+=09=09=09=20*=20Per=20OpenSSL=20documentation,=20this=20= error=20code=20is=20only=20returned=20for=0A+=09=09=09=20*=20a=20clean=20= connection=20closure,=20so=20we=20should=20not=20report=20it=20as=20a=0A= +=09=09=09=20*=20server=20crash.=0A+=09=09=09=20*/=0A+=09=09=09= libpq_append_conn_error(conn,=20"SSL=20connection=20has=20been=20closed=20= unexpectedly");=0A+=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09= n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09default:=0A+=09=09=09= libpq_append_conn_error(conn,=20"unrecognized=20SSL=20error=20code:=20= %d",=20err);=0A+=09=09=09/*=20assume=20the=20connection=20is=20broken=20= */=0A+=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09n=20=3D=20= -1;=0A+=09=09=09break;=0A+=09}=0A+=0A+=09/*=20ensure=20we=20return=20the=20= intended=20errno=20to=20caller=20*/=0A+=09SOCK_ERRNO_SET(result_errno);=0A= +=0A+=09return=20n;=0A+}=0A+=0A+ssize_t=0A+pgtls_bytes_pending(PGconn=20= *conn)=0A+{=0A+=09int=09=09=09pending;=0A+=0A+=09/*=0A+=09=20*=20OpenSSL=20= readahead=20is=20documented=20to=20break=20SSL_pending().=20=20Plus,=20= we=20can't=0A+=09=20*=20afford=20to=20have=20OpenSSL=20take=20bytes=20= off=20the=20socket=20without=20processing=0A+=09=20*=20them;=20that=20= breaks=20the=20postconditions=20for=20pqsecure_drain_pending().=0A+=09=20= */=0A+=09Assert(!SSL_get_read_ahead(conn->ssl));=0A+=0A+=09pending=20=3D=20= SSL_pending(conn->ssl);=0A+=09if=20(pending=20<=200)=0A+=09{=0A+=09=09/*=20= shouldn't=20be=20possible=20*/=0A+=09=09Assert(false);=0A+=09=09= libpq_append_conn_error(conn,=20"LibreSSL=20reports=20negative=20bytes=20= pending");=0A+=09=09return=20-1;=0A+=09}=0A+=09else=20if=20(pending=20=3D=3D= =20INT_MAX)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20If=20we=20ever=20found=20= a=20legitimate=20way=20to=20hit=20this,=20we'd=20need=20to=20loop=0A+=09=09= =20*=20around=20in=20the=20caller=20to=20call=20pgtls_bytes_pending()=20= again.=20=20Throw=20an=0A+=09=09=20*=20error=20rather=20than=20= complicate=20the=20code=20in=20that=20way,=20because=0A+=09=09=20*=20= SSL_read()=20should=20be=20bounded=20to=20the=20size=20of=20a=20single=20= TLS=20record,=0A+=09=09=20*=20and=20conn->inBuffer=20can't=20currently=20= go=20past=20INT_MAX=20in=20size=20anyway.=0A+=09=09=20*/=0A+=09=09= libpq_append_conn_error(conn,=20"LibreSSL=20reports=20INT_MAX=20bytes=20= pending");=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09return=20(ssize_t)=20= pending;=0A+}=0A+=0A+=0A+ssize_t=0A+pgtls_write(PGconn=20*conn,=20const=20= void=20*ptr,=20size_t=20len)=0A+{=0A+=09ssize_t=09=09n;=0A+=09int=09=09=09= result_errno=20=3D=200;=0A+=09char=09=09sebuf[PG_STRERROR_R_BUFLEN];=0A+=09= int=09=09=09err;=0A+=09unsigned=20long=20ecode;=0A+=0A+=09= SOCK_ERRNO_SET(0);=0A+=09ERR_clear_error();=0A+=09n=20=3D=20= SSL_write(conn->ssl,=20ptr,=20len);=0A+=09err=20=3D=20= SSL_get_error(conn->ssl,=20n);=0A+=09ecode=20=3D=20(err=20!=3D=20= SSL_ERROR_NONE=20||=20n=20<=200)=20?=20ERR_get_error()=20:=200;=0A+=09= switch=20(err)=0A+=09{=0A+=09=09case=20SSL_ERROR_NONE:=0A+=09=09=09if=20= (n=20<=200)=0A+=09=09=09{=0A+=09=09=09=09/*=20Not=20supposed=20to=20= happen,=20so=20we=20don't=20translate=20the=20msg=20*/=0A+=09=09=09=09= appendPQExpBufferStr(&conn->errorMessage,=0A+=09=09=09=09=09=09=09=09=09=20= "SSL_write=20failed=20but=20did=20not=20provide=20error=20= information\n");=0A+=09=09=09=09/*=20assume=20the=20connection=20is=20= broken=20*/=0A+=09=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09= }=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_WANT_READ:=0A+=0A+=09=09=09= /*=0A+=09=09=09=20*=20Returning=200=20here=20causes=20caller=20to=20wait=20= for=20write-ready,=20which=0A+=09=09=09=20*=20is=20not=20really=20the=20= right=20thing,=20but=20it's=20the=20best=20we=20can=20do.=0A+=09=09=09=20= */=0A+=09=09=09n=20=3D=200;=0A+=09=09=09break;=0A+=09=09case=20= SSL_ERROR_WANT_WRITE:=0A+=09=09=09n=20=3D=200;=0A+=09=09=09break;=0A+=09=09= case=20SSL_ERROR_SYSCALL:=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20If=20= errno=20is=20still=20zero=20then=20assume=20it's=20a=20read=20EOF=20= situation,=0A+=09=09=09=20*=20and=20report=20EOF.=20=20(This=20seems=20= possible=20because=20SSL_write=20can=0A+=09=09=09=20*=20also=20do=20= reads.)=0A+=09=09=09=20*/=0A+=09=09=09if=20(n=20<=200=20&&=20SOCK_ERRNO=20= !=3D=200)=0A+=09=09=09{=0A+=09=09=09=09result_errno=20=3D=20SOCK_ERRNO;=0A= +=09=09=09=09if=20(result_errno=20=3D=3D=20EPIPE=20||=20result_errno=20= =3D=3D=20ECONNRESET)=0A+=09=09=09=09=09libpq_append_conn_error(conn,=20= "server=20closed=20the=20connection=20unexpectedly\n"=0A+=09=09=09=09=09=09= =09=09=09=09=09"\tThis=20probably=20means=20the=20server=20terminated=20= abnormally\n"=0A+=09=09=09=09=09=09=09=09=09=09=09"\tbefore=20or=20while=20= processing=20the=20request.");=0A+=09=09=09=09else=0A+=09=09=09=09=09= libpq_append_conn_error(conn,=20"SSL=20SYSCALL=20error:=20%s",=0A+=09=09=09= =09=09=09=09=09=09=09=09SOCK_STRERROR(result_errno,=0A+=09=09=09=09=09=09= =09=09=09=09=09=09=09=09=20=20sebuf,=20sizeof(sebuf)));=0A+=09=09=09}=0A= +=09=09=09else=0A+=09=09=09{=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"SSL=20SYSCALL=20error:=20EOF=20= detected");=0A+=09=09=09=09/*=20assume=20the=20connection=20is=20broken=20= */=0A+=09=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09=09n=20=3D=20= -1;=0A+=09=09=09}=0A+=09=09=09break;=0A+=09=09case=20SSL_ERROR_SSL:=0A+=09= =09=09{=0A+=09=09=09=09char=09=20=20=20*errm=20=3D=20= SSLerrmessage(ecode);=0A+=0A+=09=09=09=09libpq_append_conn_error(conn,=20= "SSL=20error:=20%s",=20errm);=0A+=09=09=09=09SSLerrfree(errm);=0A+=09=09=09= =09/*=20assume=20the=20connection=20is=20broken=20*/=0A+=09=09=09=09= result_errno=20=3D=20ECONNRESET;=0A+=09=09=09=09n=20=3D=20-1;=0A+=09=09=09= =09break;=0A+=09=09=09}=0A+=09=09case=20SSL_ERROR_ZERO_RETURN:=0A+=0A+=09= =09=09/*=0A+=09=09=09=20*=20Per=20OpenSSL=20documentation,=20this=20= error=20code=20is=20only=20returned=20for=0A+=09=09=09=20*=20a=20clean=20= connection=20closure,=20so=20we=20should=20not=20report=20it=20as=20a=0A= +=09=09=09=20*=20server=20crash.=0A+=09=09=09=20*/=0A+=09=09=09= libpq_append_conn_error(conn,=20"SSL=20connection=20has=20been=20closed=20= unexpectedly");=0A+=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09= n=20=3D=20-1;=0A+=09=09=09break;=0A+=09=09default:=0A+=09=09=09= libpq_append_conn_error(conn,=20"unrecognized=20SSL=20error=20code:=20= %d",=20err);=0A+=09=09=09/*=20assume=20the=20connection=20is=20broken=20= */=0A+=09=09=09result_errno=20=3D=20ECONNRESET;=0A+=09=09=09n=20=3D=20= -1;=0A+=09=09=09break;=0A+=09}=0A+=0A+=09/*=20ensure=20we=20return=20the=20= intended=20errno=20to=20caller=20*/=0A+=09SOCK_ERRNO_SET(result_errno);=0A= +=0A+=09return=20n;=0A+}=0A+=0A+char=20*=0A= +pgtls_get_peer_certificate_hash(PGconn=20*conn,=20size_t=20*len)=0A+{=0A= +=09X509=09=20=20=20*peer_cert;=0A+=09const=20EVP_MD=20*algo_type;=0A+=09= unsigned=20char=20hash[EVP_MAX_MD_SIZE];=09/*=20size=20for=20SHA-512=20= */=0A+=09unsigned=20int=20hash_size;=0A+=09int=09=09=09algo_nid;=0A+=09= char=09=20=20=20*cert_hash;=0A+=0A+=09*len=20=3D=200;=0A+=0A+=09if=20= (!conn->peer)=0A+=09=09return=20NULL;=0A+=0A+=09peer_cert=20=3D=20= conn->peer;=0A+=0A+=09/*=0A+=09=20*=20Get=20the=20signature=20algorithm=20= of=20the=20certificate=20to=20determine=20the=20hash=0A+=09=20*=20= algorithm=20to=20use=20for=20the=20result.=0A+=09=20*/=0A+=09if=20= (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),=0A+=09=09=09=09=09= =09=09=20&algo_nid,=20NULL))=0A+=09{=0A+=09=09= libpq_append_conn_error(conn,=20"could=20not=20determine=20server=20= certificate=20signature=20algorithm");=0A+=09=09return=20NULL;=0A+=09}=0A= +=0A+=09/*=0A+=09=20*=20The=20TLS=20server's=20certificate=20bytes=20= need=20to=20be=20hashed=20with=20SHA-256=20if=0A+=09=20*=20its=20= signature=20algorithm=20is=20MD5=20or=20SHA-1=20as=20per=20RFC=205929=0A= +=09=20*=20(https://tools.ietf.org/html/rfc5929#section-4.1).=20=20If=20= something=20else=0A+=09=20*=20is=20used,=20the=20same=20hash=20as=20the=20= signature=20algorithm=20is=20used.=0A+=09=20*/=0A+=09switch=20(algo_nid)=0A= +=09{=0A+=09=09case=20NID_md5:=0A+=09=09case=20NID_sha1:=0A+=09=09=09= algo_type=20=3D=20EVP_sha256();=0A+=09=09=09break;=0A+=09=09default:=0A+=09= =09=09algo_type=20=3D=20EVP_get_digestbynid(algo_nid);=0A+=09=09=09if=20= (algo_type=20=3D=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20find=20digest=20for=20NID=20= %s",=0A+=09=09=09=09=09=09=09=09=09=09OBJ_nid2sn(algo_nid));=0A+=09=09=09= =09return=20NULL;=0A+=09=09=09}=0A+=09=09=09break;=0A+=09}=0A+=0A+=09if=20= (!X509_digest(peer_cert,=20algo_type,=20hash,=20&hash_size))=0A+=09{=0A+=09= =09libpq_append_conn_error(conn,=20"could=20not=20generate=20peer=20= certificate=20hash");=0A+=09=09return=20NULL;=0A+=09}=0A+=0A+=09/*=20= save=20result=20*/=0A+=09cert_hash=20=3D=20malloc(hash_size);=0A+=09if=20= (cert_hash=20=3D=3D=20NULL)=0A+=09{=0A+=09=09= libpq_append_conn_error(conn,=20"out=20of=20memory");=0A+=09=09return=20= NULL;=0A+=09}=0A+=09memcpy(cert_hash,=20hash,=20hash_size);=0A+=09*len=20= =3D=20hash_size;=0A+=0A+=09return=20cert_hash;=0A+}=0A+=0A+/*=20= ------------------------------------------------------------=20*/=0A+/*=09= =09=09=09=09=09OpenSSL=20specific=20code=09=09=09=09=09*/=0A+/*=20= ------------------------------------------------------------=20*/=0A+=0A= +/*=0A+=20*=09Certificate=20verification=20callback=0A+=20*=0A+=20*=09= This=20callback=20allows=20us=20to=20log=20intermediate=20problems=20= during=0A+=20*=09verification,=20but=20there=20doesn't=20seem=20to=20be=20= a=20clean=20way=20to=20get=0A+=20*=09our=20PGconn=20*=20structure.=20=20= So=20we=20can't=20log=20anything!=0A+=20*=0A+=20*=09This=20callback=20= also=20allows=20us=20to=20override=20the=20default=20acceptance=0A+=20*=09= criteria=20(e.g.,=20accepting=20self-signed=20or=20expired=20certs),=20= but=0A+=20*=09for=20now=20we=20accept=20the=20default=20checks.=0A+=20*/=0A= +static=20int=0A+verify_cb(int=20ok,=20X509_STORE_CTX=20*ctx)=0A+{=0A+=09= return=20ok;=0A+}=0A+=0A+/*=0A+=20*=20OpenSSL-specific=20wrapper=20= around=0A+=20*=20pq_verify_peer_name_matches_certificate_name(),=20= converting=20the=20ASN1_STRING=0A+=20*=20into=20a=20plain=20C=20string.=0A= +=20*/=0A+static=20int=0A= +openssl_verify_peer_name_matches_certificate_name(PGconn=20*conn,=20= ASN1_STRING=20*name_entry,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=20=20= char=20**store_name)=0A+{=0A+=09int=09=09=09len;=0A+=09const=20unsigned=20= char=20*namedata;=0A+=0A+=09/*=20Should=20not=20happen...=20*/=0A+=09if=20= (name_entry=20=3D=3D=20NULL)=0A+=09{=0A+=09=09= libpq_append_conn_error(conn,=20"SSL=20certificate's=20name=20entry=20is=20= missing");=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20= GEN_DNS=20can=20be=20only=20IA5String,=20equivalent=20to=20US=20ASCII.=0A= +=09=20*/=0A+=09namedata=20=3D=20ASN1_STRING_get0_data(name_entry);=0A+=09= len=20=3D=20ASN1_STRING_length(name_entry);=0A+=0A+=09/*=20OK=20to=20= cast=20from=20unsigned=20to=20plain=20char,=20since=20it's=20all=20= ASCII.=20*/=0A+=09return=20= pq_verify_peer_name_matches_certificate_name(conn,=20(const=20char=20*)=20= namedata,=20len,=20store_name);=0A+}=0A+=0A+/*=0A+=20*=20= OpenSSL-specific=20wrapper=20around=0A+=20*=20= pq_verify_peer_name_matches_certificate_ip(),=20converting=20the=0A+=20*=20= ASN1_OCTET_STRING=20into=20a=20plain=20C=20string.=0A+=20*/=0A+static=20= int=0A+openssl_verify_peer_name_matches_certificate_ip(PGconn=20*conn,=0A= +=09=09=09=09=09=09=09=09=09=09=09=09ASN1_OCTET_STRING=20*addr_entry,=0A= +=09=09=09=09=09=09=09=09=09=09=09=09char=20**store_name)=0A+{=0A+=09int=09= =09=09len;=0A+=09const=20unsigned=20char=20*addrdata;=0A+=0A+=09/*=20= Should=20not=20happen...=20*/=0A+=09if=20(addr_entry=20=3D=3D=20NULL)=0A= +=09{=0A+=09=09libpq_append_conn_error(conn,=20"SSL=20certificate's=20= address=20entry=20is=20missing");=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09= /*=0A+=09=20*=20GEN_IPADD=20is=20an=20OCTET=20STRING=20containing=20an=20= IP=20address=20in=20network=20byte=0A+=09=20*=20order.=0A+=09=20*/=0A+=09= addrdata=20=3D=20ASN1_STRING_get0_data(addr_entry);=0A+=09len=20=3D=20= ASN1_STRING_length(addr_entry);=0A+=0A+=09return=20= pq_verify_peer_name_matches_certificate_ip(conn,=20addrdata,=20len,=20= store_name);=0A+}=0A+=0A+static=20bool=0A+is_ip_address(const=20char=20= *host)=0A+{=0A+=09struct=20in_addr=20dummy4;=0A+#ifdef=20HAVE_INET_PTON=0A= +=09struct=20in6_addr=20dummy6;=0A+#endif=0A+=0A+=09return=20= inet_aton(host,=20&dummy4)=0A+#ifdef=20HAVE_INET_PTON=0A+=09=09||=20= (inet_pton(AF_INET6,=20host,=20&dummy6)=20=3D=3D=201)=0A+#endif=0A+=09=09= ;=0A+}=0A+=0A+/*=0A+=20*=09Verify=20that=20the=20server=20certificate=20= matches=20the=20hostname=20we=20connected=20to.=0A+=20*=0A+=20*=20The=20= certificate's=20Common=20Name=20and=20Subject=20Alternative=20Names=20= are=20considered.=0A+=20*/=0A+int=0A= +pgtls_verify_peer_name_matches_certificate_guts(PGconn=20*conn,=0A+=09=09= =09=09=09=09=09=09=09=09=09=09int=20*names_examined,=0A+=09=09=09=09=09=09= =09=09=09=09=09=09char=20**first_name)=0A+{=0A+=09STACK_OF(GENERAL_NAME)=20= *=20peer_san;=0A+=09int=09=09=09i;=0A+=09int=09=09=09rc=20=3D=200;=0A+=09= char=09=20=20=20*host=20=3D=20conn->connhost[conn->whichhost].host;=0A+=09= int=09=09=09host_type;=0A+=09bool=09=09check_cn=20=3D=20true;=0A+=0A+=09= Assert(host=20&&=20host[0]);=09/*=20should=20be=20guaranteed=20by=20= caller=20*/=0A+=0A+=09/*=0A+=09=20*=20We=20try=20to=20match=20the=20NSS=20= behavior=20here,=20which=20is=20a=20slight=20departure=20from=0A+=09=20*=20= the=20spec=20but=20seems=20to=20make=20more=20intuitive=20sense:=0A+=09=20= *=0A+=09=20*=20If=20connhost=20contains=20a=20DNS=20name,=20and=20the=20= certificate's=20SANs=20contain=20any=0A+=09=20*=20dNSName=20entries,=20= then=20we'll=20ignore=20the=20Subject=20Common=20Name=20entirely;=0A+=09=20= *=20otherwise,=20we=20fall=20back=20to=20checking=20the=20CN.=20(This=20= behavior=20matches=20the=0A+=09=20*=20RFC.)=0A+=09=20*=0A+=09=20*=20If=20= connhost=20contains=20an=20IP=20address,=20and=20the=20SANs=20contain=20= iPAddress=0A+=09=20*=20entries,=20we=20again=20ignore=20the=20CN.=20= Otherwise,=20we=20allow=20the=20CN=20to=20match,=0A+=09=20*=20EVEN=20IF=20= there=20is=20a=20dNSName=20in=20the=20SANs.=20(RFC=206125=20prohibits=20= this:=20"A=0A+=09=20*=20client=20MUST=20NOT=20seek=20a=20match=20for=20a=20= reference=20identifier=20of=20CN-ID=20if=20the=0A+=09=20*=20presented=20= identifiers=20include=20a=20DNS-ID,=20SRV-ID,=20URI-ID,=20or=20any=0A+=09= =20*=20application-specific=20identifier=20types=20supported=20by=20the=20= client.")=0A+=09=20*=0A+=09=20*=20NOTE:=20Prior=20versions=20of=20libpq=20= did=20not=20consider=20iPAddress=20entries=20at=0A+=09=20*=20all,=20so=20= this=20new=20behavior=20might=20break=20a=20certificate=20that=20has=20= different=0A+=09=20*=20IP=20addresses=20in=20the=20Subject=20CN=20and=20= the=20SANs.=0A+=09=20*/=0A+=09if=20(is_ip_address(host))=0A+=09=09= host_type=20=3D=20GEN_IPADD;=0A+=09else=0A+=09=09host_type=20=3D=20= GEN_DNS;=0A+=0A+=09/*=0A+=09=20*=20First,=20get=20the=20Subject=20= Alternative=20Names=20(SANs)=20from=20the=20certificate,=0A+=09=20*=20= and=20compare=20them=20against=20the=20originally=20given=20hostname.=0A= +=09=20*/=0A+=09peer_san=20=3D=20(STACK_OF(GENERAL_NAME)=20*)=0A+=09=09= X509_get_ext_d2i(conn->peer,=20NID_subject_alt_name,=20NULL,=20NULL);=0A= +=0A+=09if=20(peer_san)=0A+=09{=0A+=09=09int=09=09=09san_len=20=3D=20= sk_GENERAL_NAME_num(peer_san);=0A+=0A+=09=09for=20(i=20=3D=200;=20i=20<=20= san_len;=20i++)=0A+=09=09{=0A+=09=09=09const=20GENERAL_NAME=20*name=20=3D=20= sk_GENERAL_NAME_value(peer_san,=20i);=0A+=09=09=09char=09=20=20=20= *alt_name=20=3D=20NULL;=0A+=0A+=09=09=09if=20(name->type=20=3D=3D=20= host_type)=0A+=09=09=09{=0A+=09=09=09=09/*=0A+=09=09=09=09=20*=20This=20= SAN=20is=20of=20the=20same=20type=20(IP=20or=20DNS)=20as=20our=20host=20= name,=0A+=09=09=09=09=20*=20so=20don't=20allow=20a=20fallback=20check=20= of=20the=20CN.=0A+=09=09=09=09=20*/=0A+=09=09=09=09check_cn=20=3D=20= false;=0A+=09=09=09}=0A+=0A+=09=09=09if=20(name->type=20=3D=3D=20= GEN_DNS)=0A+=09=09=09{=0A+=09=09=09=09(*names_examined)++;=0A+=09=09=09=09= rc=20=3D=20openssl_verify_peer_name_matches_certificate_name(conn,=0A+=09= =09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20= name->d.dNSName,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20= =20=20&alt_name);=0A+=09=09=09}=0A+=09=09=09else=20if=20(name->type=20=3D=3D= =20GEN_IPADD)=0A+=09=09=09{=0A+=09=09=09=09(*names_examined)++;=0A+=09=09= =09=09rc=20=3D=20openssl_verify_peer_name_matches_certificate_ip(conn,=0A= +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20= name->d.iPAddress,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09= =20&alt_name);=0A+=09=09=09}=0A+=0A+=09=09=09if=20(alt_name)=0A+=09=09=09= {=0A+=09=09=09=09if=20(!*first_name)=0A+=09=09=09=09=09*first_name=20=3D=20= alt_name;=0A+=09=09=09=09else=0A+=09=09=09=09=09free(alt_name);=0A+=09=09= =09}=0A+=0A+=09=09=09if=20(rc=20!=3D=200)=0A+=09=09=09{=0A+=09=09=09=09= /*=0A+=09=09=09=09=20*=20Either=20we=20hit=20an=20error=20or=20a=20= match,=20and=20either=20way=20we=20should=0A+=09=09=09=09=20*=20not=20= fall=20back=20to=20the=20CN.=0A+=09=09=09=09=20*/=0A+=09=09=09=09= check_cn=20=3D=20false;=0A+=09=09=09=09break;=0A+=09=09=09}=0A+=09=09}=0A= +=09=09sk_GENERAL_NAME_pop_free(peer_san,=20GENERAL_NAME_free);=0A+=09}=0A= +=0A+=09/*=0A+=09=20*=20If=20there=20is=20no=20subjectAltName=20= extension=20of=20the=20matching=20type,=20check=20the=0A+=09=20*=20= Common=20Name.=0A+=09=20*=0A+=09=20*=20(Per=20RFC=202818=20and=20RFC=20= 6125,=20if=20the=20subjectAltName=20extension=20of=20type=0A+=09=20*=20= dNSName=20is=20present,=20the=20CN=20must=20be=20ignored.=20We=20break=20= this=20rule=20if=20host=0A+=09=20*=20is=20an=20IP=20address;=20see=20the=20= comment=20above.)=0A+=09=20*/=0A+=09if=20(check_cn)=0A+=09{=0A+=09=09= X509_NAME=20=20*subject_name;=0A+=0A+=09=09subject_name=20=3D=20= X509_get_subject_name(conn->peer);=0A+=09=09if=20(subject_name=20!=3D=20= NULL)=0A+=09=09{=0A+=09=09=09int=09=09=09cn_index;=0A+=0A+=09=09=09= cn_index=20=3D=20X509_NAME_get_index_by_NID(subject_name,=0A+=09=09=09=09= =09=09=09=09=09=09=09=09=20=20NID_commonName,=20-1);=0A+=09=09=09if=20= (cn_index=20>=3D=200)=0A+=09=09=09{=0A+=09=09=09=09char=09=20=20=20= *common_name=20=3D=20NULL;=0A+=0A+=09=09=09=09(*names_examined)++;=0A+=09= =09=09=09rc=20=3D=20= openssl_verify_peer_name_matches_certificate_name(conn,=0A+=09=09=09=09=09= =09=09=09=09=09=09=09=09=09=09=09=09=20=20=20= X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name,=20cn_index)),=0A= +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20= &common_name);=0A+=0A+=09=09=09=09if=20(common_name)=0A+=09=09=09=09{=0A= +=09=09=09=09=09if=20(!*first_name)=0A+=09=09=09=09=09=09*first_name=20=3D= =20common_name;=0A+=09=09=09=09=09else=0A+=09=09=09=09=09=09= free(common_name);=0A+=09=09=09=09}=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A= +=09return=20rc;=0A+}=0A+=0A+/*=20See=20pqcomm.h=20comments=20on=20= OpenSSL=20implementation=20of=20ALPN=20(RFC=207301)=20*/=0A+static=20= unsigned=20char=20alpn_protos[]=20=3D=20PG_ALPN_PROTOCOL_VECTOR;=0A+=0A= +/*=0A+=20*=09Create=20per-connection=20SSL=20object,=20and=20load=20the=20= client=20certificate,=0A+=20*=09private=20key,=20and=20trusted=20CA=20= certs.=0A+=20*=0A+=20*=09Returns=200=20if=20OK,=20-1=20on=20failure=20= (with=20a=20message=20in=20conn->errorMessage).=0A+=20*/=0A+static=20int=0A= +initialize_SSL(PGconn=20*conn)=0A+{=0A+=09SSL_CTX=20=20=20=20= *SSL_context;=0A+=09struct=20stat=20buf;=0A+=09char=09=09= homedir[MAXPGPATH];=0A+=09char=09=09fnbuf[MAXPGPATH];=0A+=09char=09=09= sebuf[PG_STRERROR_R_BUFLEN];=0A+=09bool=09=09have_homedir;=0A+=09bool=09=09= have_cert;=0A+=09bool=09=09have_rootcert;=0A+=0A+=09/*=0A+=09=20*=20= We'll=20need=20the=20home=20directory=20if=20any=20of=20the=20relevant=20= parameters=20are=0A+=09=20*=20defaulted.=20=20If=20pqGetHomeDirectory=20= fails,=20act=20as=20though=20none=20of=20the=0A+=09=20*=20files=20could=20= be=20found.=0A+=09=20*/=0A+=09if=20(!(conn->sslcert=20&&=20= strlen(conn->sslcert)=20>=200)=20||=0A+=09=09!(conn->sslkey=20&&=20= strlen(conn->sslkey)=20>=200)=20||=0A+=09=09!(conn->sslrootcert=20&&=20= strlen(conn->sslrootcert)=20>=200)=20||=0A+=09=09!((conn->sslcrl=20&&=20= strlen(conn->sslcrl)=20>=200)=20||=0A+=09=09=20=20(conn->sslcrldir=20&&=20= strlen(conn->sslcrldir)=20>=200)))=0A+=09=09have_homedir=20=3D=20= pqGetHomeDirectory(homedir,=20sizeof(homedir));=0A+=09else=09=09=09=09=09= =09/*=20won't=20need=20it=20*/=0A+=09=09have_homedir=20=3D=20false;=0A+=0A= +=09/*=0A+=09=20*=20Create=20a=20new=20SSL_CTX=20object.=0A+=09=20*=0A+=09= =20*=20We=20used=20to=20share=20a=20single=20SSL_CTX=20between=20all=20= connections,=20but=20it=20was=0A+=09=20*=20complicated=20if=20= connections=20used=20different=20certificates.=20So=20now=20we=0A+=09=20= *=20create=20a=20separate=20context=20for=20each=20connection,=20and=20= accept=20the=20overhead.=0A+=09=20*/=0A+=09SSL_context=20=3D=20= SSL_CTX_new(SSLv23_method());=0A+=09if=20(!SSL_context)=0A+=09{=0A+=09=09= char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A+=0A+=09=09= libpq_append_conn_error(conn,=20"could=20not=20create=20SSL=20context:=20= %s",=20err);=0A+=09=09SSLerrfree(err);=0A+=09=09return=20-1;=0A+=09}=0A+=0A= +=09/*=0A+=09=20*=20Delegate=20the=20client=20cert=20password=20prompt=20= to=20the=20libpq=20wrapper=20callback=0A+=09=20*=20if=20any=20is=20= defined.=0A+=09=20*=0A+=09=20*=20If=20the=20application=20hasn't=20= installed=20its=20own=20and=20the=20sslpassword=0A+=09=20*=20parameter=20= is=20non-null,=20we=20install=20ours=20now=20to=20make=20sure=20we=20= supply=0A+=09=20*=20PGconn->sslpassword=20to=20OpenSSL=20instead=20of=20= letting=20it=20prompt=20on=20stdin.=0A+=09=20*=0A+=09=20*=20This=20will=20= replace=20OpenSSL's=20default=20PEM_def_callback=20(which=20prompts=20on=0A= +=09=20*=20stdin),=20but=20we're=20only=20setting=20it=20for=20this=20= SSL=20context=20so=20it's=0A+=09=20*=20harmless.=0A+=09=20*/=0A+=09if=20= (PQsslKeyPassHook=0A+=09=09||=20(conn->sslpassword=20&&=20= strlen(conn->sslpassword)=20>=200))=0A+=09{=0A+=09=09= SSL_CTX_set_default_passwd_cb(SSL_context,=20PQssl_passwd_cb);=0A+=09=09= SSL_CTX_set_default_passwd_cb_userdata(SSL_context,=20conn);=0A+=09}=0A+=0A= +=09/*=20Disable=20old=20protocol=20versions=20*/=0A+=09= SSL_CTX_set_options(SSL_context,=20SSL_OP_NO_SSLv2=20|=20= SSL_OP_NO_SSLv3);=0A+=0A+=09/*=20Set=20the=20minimum=20and=20maximum=20= protocol=20versions=20if=20necessary=20*/=0A+=09if=20= (conn->ssl_min_protocol_version=20&&=0A+=09=09= strlen(conn->ssl_min_protocol_version)=20!=3D=200)=0A+=09{=0A+=09=09int=09= =09=09ssl_min_ver;=0A+=0A+=09=09ssl_min_ver=20=3D=20= ssl_protocol_version_to_openssl(conn->ssl_min_protocol_version);=0A+=0A+=09= =09if=20(ssl_min_ver=20=3D=3D=20-1)=0A+=09=09{=0A+=09=09=09= libpq_append_conn_error(conn,=20"invalid=20value=20\"%s\"=20for=20= minimum=20SSL=20protocol=20version",=0A+=09=09=09=09=09=09=09=09=09= conn->ssl_min_protocol_version);=0A+=09=09=09SSL_CTX_free(SSL_context);=0A= +=09=09=09return=20-1;=0A+=09=09}=0A+=0A+=09=09if=20= (!SSL_CTX_set_min_proto_version(SSL_context,=20ssl_min_ver))=0A+=09=09{=0A= +=09=09=09char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A= +=0A+=09=09=09libpq_append_conn_error(conn,=20"could=20not=20set=20= minimum=20SSL=20protocol=20version:=20%s",=20err);=0A+=09=09=09= SSLerrfree(err);=0A+=09=09=09SSL_CTX_free(SSL_context);=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+=09}=0A+=0A+=09if=20= (conn->ssl_max_protocol_version=20&&=0A+=09=09= strlen(conn->ssl_max_protocol_version)=20!=3D=200)=0A+=09{=0A+=09=09int=09= =09=09ssl_max_ver;=0A+=0A+=09=09ssl_max_ver=20=3D=20= ssl_protocol_version_to_openssl(conn->ssl_max_protocol_version);=0A+=0A+=09= =09if=20(ssl_max_ver=20=3D=3D=20-1)=0A+=09=09{=0A+=09=09=09= libpq_append_conn_error(conn,=20"invalid=20value=20\"%s\"=20for=20= maximum=20SSL=20protocol=20version",=0A+=09=09=09=09=09=09=09=09=09= conn->ssl_max_protocol_version);=0A+=09=09=09SSL_CTX_free(SSL_context);=0A= +=09=09=09return=20-1;=0A+=09=09}=0A+=0A+=09=09if=20= (!SSL_CTX_set_max_proto_version(SSL_context,=20ssl_max_ver))=0A+=09=09{=0A= +=09=09=09char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A= +=0A+=09=09=09libpq_append_conn_error(conn,=20"could=20not=20set=20= maximum=20SSL=20protocol=20version:=20%s",=20err);=0A+=09=09=09= SSLerrfree(err);=0A+=09=09=09SSL_CTX_free(SSL_context);=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Disable=20= OpenSSL's=20moving-write-buffer=20sanity=20check,=20because=20it=20= causes=0A+=09=20*=20unnecessary=20failures=20in=20nonblocking=20send=20= cases.=0A+=09=20*/=0A+=09SSL_CTX_set_mode(SSL_context,=20= SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);=0A+=0A+=09/*=0A+=09=20*=20If=20the=20= root=20cert=20file=20exists,=20load=20it=20so=20we=20can=20perform=20= certificate=0A+=09=20*=20verification.=20If=20sslmode=20is=20= "verify-full"=20we=20will=20also=20do=20further=0A+=09=20*=20= verification=20after=20the=20connection=20has=20been=20completed.=0A+=09=20= */=0A+=09if=20(conn->sslrootcert=20&&=20strlen(conn->sslrootcert)=20>=20= 0)=0A+=09=09strlcpy(fnbuf,=20conn->sslrootcert,=20sizeof(fnbuf));=0A+=09= else=20if=20(have_homedir)=0A+=09=09snprintf(fnbuf,=20sizeof(fnbuf),=20= "%s/%s",=20homedir,=20ROOT_CERT_FILE);=0A+=09else=0A+=09=09fnbuf[0]=20=3D=20= '\0';=0A+=0A+=09if=20(strcmp(fnbuf,=20"system")=20=3D=3D=200)=0A+=09{=0A= +=09=09/*=0A+=09=09=20*=20The=20"system"=20sentinel=20value=20indicates=20= that=20we=20should=20load=20whatever=0A+=09=09=20*=20root=20certificates=20= are=20installed=20for=20use=20by=20OpenSSL;=20these=20locations=0A+=09=09= =20*=20differ=20by=20platform.=20Note=20that=20the=20default=20system=20= locations=20may=20be=0A+=09=09=20*=20further=20overridden=20by=20the=20= SSL_CERT_DIR=20and=20SSL_CERT_FILE=0A+=09=09=20*=20environment=20= variables.=0A+=09=09=20*/=0A+=09=09if=20= (SSL_CTX_set_default_verify_paths(SSL_context)=20!=3D=201)=0A+=09=09{=0A= +=09=09=09char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A= +=0A+=09=09=09libpq_append_conn_error(conn,=20"could=20not=20load=20= system=20root=20certificate=20paths:=20%s",=0A+=09=09=09=09=09=09=09=09=09= err);=0A+=09=09=09SSLerrfree(err);=0A+=09=09=09= SSL_CTX_free(SSL_context);=0A+=09=09=09return=20-1;=0A+=09=09}=0A+=09=09= have_rootcert=20=3D=20true;=0A+=09}=0A+=09else=20if=20(fnbuf[0]=20!=3D=20= '\0'=20&&=0A+=09=09=09=20stat(fnbuf,=20&buf)=20=3D=3D=200)=0A+=09{=0A+=09= =09X509_STORE=20*cvstore;=0A+=0A+=09=09if=20= (SSL_CTX_load_verify_locations(SSL_context,=20fnbuf,=20NULL)=20!=3D=201)=0A= +=09=09{=0A+=09=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20read=20root=20certificate=20= file=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09=09fnbuf,=20err);=0A+=09= =09=09SSLerrfree(err);=0A+=09=09=09SSL_CTX_free(SSL_context);=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+=0A+=09=09if=20((cvstore=20=3D=20= SSL_CTX_get_cert_store(SSL_context))=20!=3D=20NULL)=0A+=09=09{=0A+=09=09=09= char=09=20=20=20*fname=20=3D=20NULL;=0A+=09=09=09char=09=20=20=20*dname=20= =3D=20NULL;=0A+=0A+=09=09=09if=20(conn->sslcrl=20&&=20= strlen(conn->sslcrl)=20>=200)=0A+=09=09=09=09fname=20=3D=20conn->sslcrl;=0A= +=09=09=09if=20(conn->sslcrldir=20&&=20strlen(conn->sslcrldir)=20>=200)=0A= +=09=09=09=09dname=20=3D=20conn->sslcrldir;=0A+=0A+=09=09=09/*=20= defaults=20to=20use=20the=20default=20CRL=20file=20*/=0A+=09=09=09if=20= (!fname=20&&=20!dname=20&&=20have_homedir)=0A+=09=09=09{=0A+=09=09=09=09= snprintf(fnbuf,=20sizeof(fnbuf),=20"%s/%s",=20homedir,=20ROOT_CRL_FILE);=0A= +=09=09=09=09fname=20=3D=20fnbuf;=0A+=09=09=09}=0A+=0A+=09=09=09/*=20Set=20= the=20flags=20to=20check=20against=20the=20complete=20CRL=20chain=20*/=0A= +=09=09=09if=20((fname=20||=20dname)=20&&=0A+=09=09=09=09= X509_STORE_load_locations(cvstore,=20fname,=20dname)=20=3D=3D=201)=0A+=09= =09=09{=0A+=09=09=09=09X509_STORE_set_flags(cvstore,=0A+=09=09=09=09=09=09= =09=09=09=20X509_V_FLAG_CRL_CHECK=20|=20X509_V_FLAG_CRL_CHECK_ALL);=0A+=09= =09=09}=0A+=0A+=09=09=09/*=20if=20not=20found,=20silently=20ignore;=20=20= we=20do=20not=20require=20CRL=20*/=0A+=09=09=09ERR_clear_error();=0A+=09=09= }=0A+=09=09have_rootcert=20=3D=20true;=0A+=09}=0A+=09else=0A+=09{=0A+=09=09= /*=0A+=09=09=20*=20stat()=20failed;=20assume=20root=20file=20doesn't=20= exist.=20=20If=20sslmode=20is=0A+=09=09=20*=20verify-ca=20or=20= verify-full,=20this=20is=20an=20error.=20=20Otherwise,=20continue=0A+=09=09= =20*=20without=20performing=20any=20server=20cert=20verification.=0A+=09=09= =20*/=0A+=09=09if=20(conn->sslmode[0]=20=3D=3D=20'v')=09/*=20"verify-ca"=20= or=20"verify-full"=20*/=0A+=09=09{=0A+=09=09=09/*=0A+=09=09=09=20*=20The=20= only=20way=20to=20reach=20here=20with=20an=20empty=20filename=20is=20if=0A= +=09=09=09=20*=20pqGetHomeDirectory=20failed.=20=20That's=20a=20= sufficiently=20unusual=20case=0A+=09=09=09=20*=20that=20it=20seems=20= worth=20having=20a=20specialized=20error=20message=20for=20it.=0A+=09=09=09= =20*/=0A+=09=09=09if=20(fnbuf[0]=20=3D=3D=20'\0')=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20get=20home=20directory=20= to=20locate=20root=20certificate=20file\n"=0A+=09=09=09=09=09=09=09=09=09= =09"Either=20provide=20the=20file,=20use=20the=20system's=20trusted=20= roots=20with=20sslrootcert=3Dsystem,=20or=20change=20sslmode=20to=20= disable=20server=20certificate=20verification.");=0A+=09=09=09else=0A+=09= =09=09=09libpq_append_conn_error(conn,=20"root=20certificate=20file=20= \"%s\"=20does=20not=20exist\n"=0A+=09=09=09=09=09=09=09=09=09=09"Either=20= provide=20the=20file,=20use=20the=20system's=20trusted=20roots=20with=20= sslrootcert=3Dsystem,=20or=20change=20sslmode=20to=20disable=20server=20= certificate=20verification.",=20fnbuf);=0A+=09=09=09= SSL_CTX_free(SSL_context);=0A+=09=09=09return=20-1;=0A+=09=09}=0A+=09=09= have_rootcert=20=3D=20false;=0A+=09}=0A+=0A+=09/*=20Read=20the=20client=20= certificate=20file=20*/=0A+=09if=20(conn->sslcert=20&&=20= strlen(conn->sslcert)=20>=200)=0A+=09=09strlcpy(fnbuf,=20conn->sslcert,=20= sizeof(fnbuf));=0A+=09else=20if=20(have_homedir)=0A+=09=09= snprintf(fnbuf,=20sizeof(fnbuf),=20"%s/%s",=20homedir,=20= USER_CERT_FILE);=0A+=09else=0A+=09=09fnbuf[0]=20=3D=20'\0';=0A+=0A+=09if=20= (conn->sslcertmode[0]=20=3D=3D=20'd')=09/*=20disable=20*/=0A+=09{=0A+=09=09= /*=20don't=20send=20a=20client=20cert=20even=20if=20we=20have=20one=20*/=0A= +=09=09have_cert=20=3D=20false;=0A+=09}=0A+=09else=20if=20(fnbuf[0]=20=3D=3D= =20'\0')=0A+=09{=0A+=09=09/*=20no=20home=20directory,=20proceed=20= without=20a=20client=20cert=20*/=0A+=09=09have_cert=20=3D=20false;=0A+=09= }=0A+=09else=20if=20(stat(fnbuf,=20&buf)=20!=3D=200)=0A+=09{=0A+=09=09/*=0A= +=09=09=20*=20If=20file=20is=20not=20present,=20just=20go=20on=20without=20= a=20client=20cert;=20server=0A+=09=09=20*=20might=20or=20might=20not=20= accept=20the=20connection.=20=20Any=20other=20error,=0A+=09=09=20*=20= however,=20is=20grounds=20for=20complaint.=0A+=09=09=20*/=0A+=09=09if=20= (errno=20!=3D=20ENOENT=20&&=20errno=20!=3D=20ENOTDIR)=0A+=09=09{=0A+=09=09= =09libpq_append_conn_error(conn,=20"could=20not=20open=20certificate=20= file=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09=09fnbuf,=20= strerror_r(errno,=20sebuf,=20sizeof(sebuf)));=0A+=09=09=09= SSL_CTX_free(SSL_context);=0A+=09=09=09return=20-1;=0A+=09=09}=0A+=09=09= have_cert=20=3D=20false;=0A+=09}=0A+=09else=0A+=09{=0A+=09=09/*=0A+=09=09= =20*=20Cert=20file=20exists,=20so=20load=20it.=20Since=20OpenSSL=20= doesn't=20provide=20the=0A+=09=09=20*=20equivalent=20of=20= "SSL_use_certificate_chain_file",=20we=20have=20to=20load=20it=0A+=09=09=20= *=20into=20the=20SSL=20context,=20rather=20than=20the=20SSL=20object.=0A= +=09=09=20*/=0A+=09=09if=20= (SSL_CTX_use_certificate_chain_file(SSL_context,=20fnbuf)=20!=3D=201)=0A= +=09=09{=0A+=09=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20read=20certificate=20file=20= \"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09=09fnbuf,=20err);=0A+=09=09=09= SSLerrfree(err);=0A+=09=09=09SSL_CTX_free(SSL_context);=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+=0A+=09=09/*=20need=20to=20load=20the=20= associated=20private=20key,=20too=20*/=0A+=09=09have_cert=20=3D=20true;=0A= +=09}=0A+=0A+=09/*=0A+=09=20*=20The=20SSL=20context=20is=20now=20loaded=20= with=20the=20correct=20root=20and=20client=0A+=09=20*=20certificates.=20= Create=20a=20connection-specific=20SSL=20object.=20The=20private=20key=0A= +=09=20*=20is=20loaded=20directly=20into=20the=20SSL=20object.=20(We=20= could=20load=20the=20private=20key=0A+=09=20*=20into=20the=20context,=20= too,=20but=20we=20have=20done=20it=20this=20way=20historically,=20and=0A= +=09=20*=20it=20doesn't=20really=20matter.)=0A+=09=20*/=0A+=09if=20= (!(conn->ssl=20=3D=20SSL_new(SSL_context))=20||=0A+=09=09= !SSL_set_app_data(conn->ssl,=20conn)=20||=0A+=09=09= !ssl_set_pgconn_bio(conn))=0A+=09{=0A+=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09= libpq_append_conn_error(conn,=20"could=20not=20establish=20SSL=20= connection:=20%s",=20err);=0A+=09=09SSLerrfree(err);=0A+=09=09= SSL_CTX_free(SSL_context);=0A+=09=09return=20-1;=0A+=09}=0A+=09= conn->ssl_in_use=20=3D=20true;=0A+=0A+=09/*=0A+=09=20*=20If=20SSL=20key=20= logging=20is=20requested,=20set=20up=20the=20callback=20if=20a=20= compatible=0A+=09=20*=20version=20of=20OpenSSL=20is=20used=20and=20libpq=20= was=20compiled=20to=20support=20it.=0A+=09=20*/=0A+=09if=20= (conn->sslkeylogfile=20&&=20strlen(conn->sslkeylogfile)=20>=200)=0A+=09=09= fprintf(stderr,=20libpq_gettext("WARNING:=20sslkeylogfile=20support=20= requires=20OpenSSL\n"));=0A+=0A+=09/*=0A+=09=20*=20SSL=20contexts=20are=20= reference=20counted=20by=20OpenSSL.=20We=20can=20free=20it=20as=20soon=0A= +=09=20*=20as=20we=20have=20created=20the=20SSL=20object,=20and=20it=20= will=20stick=20around=20for=20as=20long=0A+=09=20*=20as=20it's=20= actually=20needed.=0A+=09=20*/=0A+=09SSL_CTX_free(SSL_context);=0A+=09= SSL_context=20=3D=20NULL;=0A+=0A+=09/*=0A+=09=20*=20Set=20Server=20Name=20= Indication=20(SNI),=20if=20enabled=20by=20connection=20parameters.=0A+=09= =20*=20Per=20RFC=206066,=20do=20not=20set=20it=20if=20the=20host=20is=20= a=20literal=20IP=20address=20(IPv4=0A+=09=20*=20or=20IPv6).=0A+=09=20*/=0A= +=09if=20(conn->sslsni=20&&=20conn->sslsni[0]=20=3D=3D=20'1')=0A+=09{=0A= +=09=09const=20char=20*host=20=3D=20= conn->connhost[conn->whichhost].host;=0A+=0A+=09=09if=20(host=20&&=20= host[0]=20&&=0A+=09=09=09!(strspn(host,=20"0123456789.")=20=3D=3D=20= strlen(host)=20||=0A+=09=09=09=20=20strchr(host,=20':')))=0A+=09=09{=0A+=09= =09=09if=20(SSL_set_tlsext_host_name(conn->ssl,=20host)=20!=3D=201)=0A+=09= =09=09{=0A+=09=09=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20set=20SSL=20Server=20Name=20= Indication=20(SNI):=20%s",=20err);=0A+=09=09=09=09SSLerrfree(err);=0A+=09= =09=09=09return=20-1;=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20= Set=20ALPN=20*/=0A+=09{=0A+=09=09int=09=09=09retval;=0A+=0A+=09=09retval=20= =3D=20SSL_set_alpn_protos(conn->ssl,=20alpn_protos,=20= sizeof(alpn_protos));=0A+=0A+=09=09if=20(retval=20!=3D=200)=0A+=09=09{=0A= +=09=09=09char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A= +=0A+=09=09=09libpq_append_conn_error(conn,=20"could=20not=20set=20SSL=20= ALPN=20extension:=20%s",=20err);=0A+=09=09=09SSLerrfree(err);=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Read=20the=20= SSL=20key.=20If=20a=20key=20is=20specified,=20treat=20it=20as=20an=20= engine:key=0A+=09=20*=20combination=20if=20there=20is=20colon=20present=20= -=20we=20don't=20support=20files=20with=0A+=09=20*=20colon=20in=20the=20= name.=20The=20exception=20is=20if=20the=20second=20character=20is=20a=20= colon,=0A+=09=20*=20in=20which=20case=20it=20can=20be=20a=20Windows=20= filename=20with=20drive=20specification.=0A+=09=20*/=0A+=09if=20= (have_cert=20&&=20conn->sslkey=20&&=20strlen(conn->sslkey)=20>=200)=0A+=09= {=0A+#ifdef=20USE_SSL_ENGINE=0A+=09=09if=20(strchr(conn->sslkey,=20':')=0A= +#ifdef=20WIN32=0A+=09=09=09&&=20conn->sslkey[1]=20!=3D=20':'=0A+#endif=0A= +=09=09=09)=0A+=09=09{=0A+=09=09=09/*=20Colon,=20but=20not=20in=20second=20= character,=20treat=20as=20engine:key=20*/=0A+=09=09=09char=09=20=20=20= *engine_str=20=3D=20strdup(conn->sslkey);=0A+=09=09=09char=09=20=20=20= *engine_colon;=0A+=09=09=09EVP_PKEY=20=20=20*pkey;=0A+=0A+=09=09=09if=20= (engine_str=20=3D=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"out=20of=20memory");=0A+=09=09=09=09= return=20-1;=0A+=09=09=09}=0A+=0A+=09=09=09/*=20cannot=20return=20NULL=20= because=20we=20already=20checked=20before=20strdup=20*/=0A+=09=09=09= engine_colon=20=3D=20strchr(engine_str,=20':');=0A+=0A+=09=09=09= *engine_colon=20=3D=20'\0';=09/*=20engine_str=20now=20has=20engine=20= name=20*/=0A+=09=09=09engine_colon++;=09=09/*=20engine_colon=20now=20has=20= key=20name=20*/=0A+=0A+=09=09=09conn->engine=20=3D=20= ENGINE_by_id(engine_str);=0A+=09=09=09if=20(conn->engine=20=3D=3D=20= NULL)=0A+=09=09=09{=0A+=09=09=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20load=20SSL=20engine=20= \"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09=09=09engine_str,=20err);=0A+=09= =09=09=09SSLerrfree(err);=0A+=09=09=09=09free(engine_str);=0A+=09=09=09=09= return=20-1;=0A+=09=09=09}=0A+=0A+=09=09=09if=20= (ENGINE_init(conn->engine)=20=3D=3D=200)=0A+=09=09=09{=0A+=09=09=09=09= char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A+=0A+=09=09= =09=09libpq_append_conn_error(conn,=20"could=20not=20initialize=20SSL=20= engine=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09=09=09engine_str,=20= err);=0A+=09=09=09=09SSLerrfree(err);=0A+=09=09=09=09= ENGINE_free(conn->engine);=0A+=09=09=09=09conn->engine=20=3D=20NULL;=0A+=09= =09=09=09free(engine_str);=0A+=09=09=09=09return=20-1;=0A+=09=09=09}=0A+=0A= +=09=09=09pkey=20=3D=20ENGINE_load_private_key(conn->engine,=20= engine_colon,=0A+=09=09=09=09=09=09=09=09=09=09=20=20=20NULL,=20NULL);=0A= +=09=09=09if=20(pkey=20=3D=3D=20NULL)=0A+=09=09=09{=0A+=09=09=09=09char=09= =20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20read=20private=20SSL=20= key=20\"%s\"=20from=20engine=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09= =09=09engine_colon,=20engine_str,=20err);=0A+=09=09=09=09= SSLerrfree(err);=0A+=09=09=09=09ENGINE_finish(conn->engine);=0A+=09=09=09= =09ENGINE_free(conn->engine);=0A+=09=09=09=09conn->engine=20=3D=20NULL;=0A= +=09=09=09=09free(engine_str);=0A+=09=09=09=09return=20-1;=0A+=09=09=09}=0A= +=09=09=09if=20(SSL_use_PrivateKey(conn->ssl,=20pkey)=20!=3D=201)=0A+=09=09= =09{=0A+=09=09=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20load=20private=20SSL=20= key=20\"%s\"=20from=20engine=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09= =09=09engine_colon,=20engine_str,=20err);=0A+=09=09=09=09= SSLerrfree(err);=0A+=09=09=09=09ENGINE_finish(conn->engine);=0A+=09=09=09= =09ENGINE_free(conn->engine);=0A+=09=09=09=09conn->engine=20=3D=20NULL;=0A= +=09=09=09=09free(engine_str);=0A+=09=09=09=09return=20-1;=0A+=09=09=09}=0A= +=0A+=09=09=09free(engine_str);=0A+=0A+=09=09=09fnbuf[0]=20=3D=20'\0';=09= /*=20indicate=20we're=20not=20going=20to=20load=20from=20a=0A+=09=09=09=09= =09=09=09=09=20*=20file=20*/=0A+=09=09}=0A+=09=09else=0A+#endif=09=09=09=09= =09=09=09/*=20USE_SSL_ENGINE=20*/=0A+=09=09{=0A+=09=09=09/*=20PGSSLKEY=20= is=20not=20an=20engine,=20treat=20it=20as=20a=20filename=20*/=0A+=09=09=09= strlcpy(fnbuf,=20conn->sslkey,=20sizeof(fnbuf));=0A+=09=09}=0A+=09}=0A+=09= else=20if=20(have_homedir)=0A+=09{=0A+=09=09/*=20No=20PGSSLKEY=20= specified,=20load=20default=20file=20*/=0A+=09=09snprintf(fnbuf,=20= sizeof(fnbuf),=20"%s/%s",=20homedir,=20USER_KEY_FILE);=0A+=09}=0A+=09= else=0A+=09=09fnbuf[0]=20=3D=20'\0';=0A+=0A+=09if=20(have_cert=20&&=20= fnbuf[0]=20!=3D=20'\0')=0A+=09{=0A+=09=09/*=20read=20the=20client=20key=20= from=20file=20*/=0A+=0A+=09=09if=20(stat(fnbuf,=20&buf)=20!=3D=200)=0A+=09= =09{=0A+=09=09=09if=20(errno=20=3D=3D=20ENOENT)=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"certificate=20present,=20but=20not=20= private=20key=20file=20\"%s\"",=0A+=09=09=09=09=09=09=09=09=09=09fnbuf);=0A= +=09=09=09else=0A+=09=09=09=09libpq_append_conn_error(conn,=20"could=20= not=20stat=20private=20key=20file=20\"%s\":=20%m",=0A+=09=09=09=09=09=09=09= =09=09=09fnbuf);=0A+=09=09=09return=20-1;=0A+=09=09}=0A+=0A+=09=09/*=20= Key=20file=20must=20be=20a=20regular=20file=20*/=0A+=09=09if=20= (!S_ISREG(buf.st_mode))=0A+=09=09{=0A+=09=09=09= libpq_append_conn_error(conn,=20"private=20key=20file=20\"%s\"=20is=20= not=20a=20regular=20file",=0A+=09=09=09=09=09=09=09=09=09fnbuf);=0A+=09=09= =09return=20-1;=0A+=09=09}=0A+=0A+=09=09/*=0A+=09=09=20*=20Refuse=20to=20= load=20world-readable=20key=20files.=20=20We=20accept=20root-owned=0A+=09= =09=20*=20files=20with=20mode=200640=20or=20less,=20so=20that=20we=20can=20= access=20system-wide=0A+=09=09=20*=20certificates=20if=20we=20have=20a=20= supplementary=20group=20membership=20that=0A+=09=09=20*=20allows=20us=20= to=20read=20'em.=20=20For=20files=20with=20non-root=20ownership,=20= require=0A+=09=09=20*=20mode=200600=20or=20less.=20=20We=20need=20not=20= check=20the=20file's=20ownership=20exactly;=0A+=09=09=20*=20if=20we're=20= able=20to=20read=20it=20despite=20it=20having=20such=20restrictive=0A+=09= =09=20*=20permissions,=20it=20must=20have=20the=20right=20ownership.=0A+=09= =09=20*=0A+=09=09=20*=20Note:=20be=20very=20careful=20about=20tightening=20= these=20rules.=20=20Some=20people=0A+=09=09=20*=20expect,=20for=20= example,=20that=20a=20client=20process=20running=20as=20root=20should=0A= +=09=09=20*=20be=20able=20to=20use=20a=20non-root-owned=20key=20file.=0A= +=09=09=20*=0A+=09=09=20*=20Note=20that=20roughly=20similar=20checks=20= are=20performed=20in=0A+=09=09=20*=20= src/backend/libpq/be-secure-common.c=20so=20any=20changes=20here=20may=20= need=0A+=09=09=20*=20to=20be=20made=20there=20as=20well.=20=20However,=20= this=20code=20caters=20for=20the=20case=0A+=09=09=20*=20of=20current=20= user=20=3D=3D=20root,=20while=20that=20code=20does=20not.=0A+=09=09=20*=0A= +=09=09=20*=20Ideally=20we=20would=20do=20similar=20permissions=20checks=20= on=20Windows,=20but=20it=0A+=09=09=20*=20is=20not=20clear=20how=20that=20= would=20work=20since=20Unix-style=20permissions=20may=0A+=09=09=20*=20= not=20be=20available.=0A+=09=09=20*/=0A+#if=20!defined(WIN32)=20&&=20= !defined(__CYGWIN__)=0A+=09=09if=20(buf.st_uid=20=3D=3D=200=20?=0A+=09=09= =09buf.st_mode=20&=20(S_IWGRP=20|=20S_IXGRP=20|=20S_IRWXO)=20:=0A+=09=09=09= buf.st_mode=20&=20(S_IRWXG=20|=20S_IRWXO))=0A+=09=09{=0A+=09=09=09= libpq_append_conn_error(conn,=0A+=09=09=09=09=09=09=09=09=09"private=20= key=20file=20\"%s\"=20has=20group=20or=20world=20access;=20file=20must=20= have=20permissions=20u=3Drw=20(0600)=20or=20less=20if=20owned=20by=20the=20= current=20user,=20or=20permissions=20u=3Drw,g=3Dr=20(0640)=20or=20less=20= if=20owned=20by=20root",=0A+=09=09=09=09=09=09=09=09=09fnbuf);=0A+=09=09=09= return=20-1;=0A+=09=09}=0A+#endif=0A+=0A+=09=09if=20= (SSL_use_PrivateKey_file(conn->ssl,=20fnbuf,=20SSL_FILETYPE_PEM)=20!=3D=20= 1)=0A+=09=09{=0A+=09=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20= We'll=20try=20to=20load=20the=20file=20in=20DER=20(binary=20ASN.1)=20= format,=20and=20if=0A+=09=09=09=20*=20that=20fails=20too,=20report=20the=20= original=20error.=20This=20could=20mask=0A+=09=09=09=20*=20issues=20= where=20there's=20something=20wrong=20with=20a=20DER-format=20cert,=0A+=09= =09=09=20*=20but=20we'd=20have=20to=20duplicate=20openssl's=20format=20= detection=20to=20be=0A+=09=09=09=20*=20smarter=20than=20this.=20We=20= can't=20just=20probe=20for=20a=20leading=20-----BEGIN=0A+=09=09=09=20*=20= because=20PEM=20can=20have=20leading=20non-matching=20lines=20and=20= blanks.=0A+=09=09=09=20*=20OpenSSL=20doesn't=20expose=20its=20= get_name(...)=20and=20its=20PEM=20routines=0A+=09=09=09=20*=20don't=20= differentiate=20between=20failure=20modes=20in=20enough=20detail=20to=0A= +=09=09=09=20*=20let=20us=20tell=20the=20difference=20between=20"not=20= PEM,=20try=20DER"=20and=0A+=09=09=09=20*=20"wrong=20password".=0A+=09=09=09= =20*/=0A+=09=09=09if=20(SSL_use_PrivateKey_file(conn->ssl,=20fnbuf,=20= SSL_FILETYPE_ASN1)=20!=3D=201)=0A+=09=09=09{=0A+=09=09=09=09= libpq_append_conn_error(conn,=20"could=20not=20load=20private=20key=20= file=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09=09=09fnbuf,=20err);=0A= +=09=09=09=09SSLerrfree(err);=0A+=09=09=09=09return=20-1;=0A+=09=09=09}=0A= +=0A+=09=09=09SSLerrfree(err);=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20verify=20= that=20the=20cert=20and=20key=20go=20together=20*/=0A+=09if=20(have_cert=20= &&=0A+=09=09SSL_check_private_key(conn->ssl)=20!=3D=201)=0A+=09{=0A+=09=09= char=09=20=20=20*err=20=3D=20SSLerrmessage(ERR_get_error());=0A+=0A+=09=09= libpq_append_conn_error(conn,=20"certificate=20does=20not=20match=20= private=20key=20file=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=09=09fnbuf,=20= err);=0A+=09=09SSLerrfree(err);=0A+=09=09return=20-1;=0A+=09}=0A+=0A+=09= /*=0A+=09=20*=20If=20a=20root=20cert=20was=20loaded,=20also=20set=20our=20= certificate=20verification=0A+=09=20*=20callback.=0A+=09=20*/=0A+=09if=20= (have_rootcert)=0A+=09=09SSL_set_verify(conn->ssl,=20SSL_VERIFY_PEER,=20= verify_cb);=0A+=0A+=09/*=0A+=09=20*=20Set=20compression=20option=20if=20= necessary.=0A+=09=20*/=0A+=09if=20(conn->sslcompression=20&&=20= conn->sslcompression[0]=20=3D=3D=20'0')=0A+=09=09= SSL_set_options(conn->ssl,=20SSL_OP_NO_COMPRESSION);=0A+=09else=0A+=09=09= SSL_clear_options(conn->ssl,=20SSL_OP_NO_COMPRESSION);=0A+=0A+=09return=20= 0;=0A+}=0A+=0A+/*=0A+=20*=09Attempt=20to=20negotiate=20SSL=20connection.=0A= +=20*/=0A+static=20PostgresPollingStatusType=0A+open_client_SSL(PGconn=20= *conn)=0A+{=0A+=09int=09=09=09r;=0A+=0A+=09SOCK_ERRNO_SET(0);=0A+=09= ERR_clear_error();=0A+=09r=20=3D=20SSL_connect(conn->ssl);=0A+=09if=20(r=20= <=3D=200)=0A+=09{=0A+=09=09int=09=09=09save_errno=20=3D=20SOCK_ERRNO;=0A= +=09=09int=09=09=09err=20=3D=20SSL_get_error(conn->ssl,=20r);=0A+=09=09= unsigned=20long=20ecode;=0A+=0A+=09=09ecode=20=3D=20ERR_get_error();=0A+=09= =09switch=20(err)=0A+=09=09{=0A+=09=09=09case=20SSL_ERROR_WANT_READ:=0A+=09= =09=09=09return=20PGRES_POLLING_READING;=0A+=0A+=09=09=09case=20= SSL_ERROR_WANT_WRITE:=0A+=09=09=09=09return=20PGRES_POLLING_WRITING;=0A+=0A= +=09=09=09case=20SSL_ERROR_SYSCALL:=0A+=09=09=09=09{=0A+=09=09=09=09=09= char=09=09sebuf[PG_STRERROR_R_BUFLEN];=0A+=09=09=09=09=09unsigned=20long=20= vcode;=0A+=0A+=09=09=09=09=09vcode=20=3D=20= SSL_get_verify_result(conn->ssl);=0A+=0A+=09=09=09=09=09/*=0A+=09=09=09=09= =09=20*=20If=20we=20get=20an=20X509=20error=20here=20for=20failing=20to=20= load=20the=0A+=09=09=09=09=09=20*=20local=20issuer=20cert,=20without=20= an=20error=20in=20the=20socket=20layer=0A+=09=09=09=09=09=20*=20it=20= means=20that=20verification=20failed=20due=20to=20a=20missing=0A+=09=09=09= =09=09=20*=20system=20CA=20pool=20without=20it=20being=20a=20protocol=20= error.=20We=0A+=09=09=09=09=09=20*=20inspect=20the=20sslrootcert=20= setting=20to=20ensure=20that=20the=20user=0A+=09=09=09=09=09=20*=20was=20= using=20the=20system=20CA=20pool.=20For=20other=20errors,=20log=0A+=09=09= =09=09=09=20*=20them=20using=20the=20normal=20SYSCALL=20logging.=0A+=09=09= =09=09=09=20*/=0A+=09=09=09=09=09if=20(save_errno=20=3D=3D=200=20&&=0A+=09= =09=09=09=09=09vcode=20=3D=3D=20= X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY=20&&=0A+=09=09=09=09=09=09= strcmp(conn->sslrootcert,=20"system")=20=3D=3D=200)=0A+=09=09=09=09=09=09= libpq_append_conn_error(conn,=20"SSL=20error:=20certificate=20verify=20= failed:=20%s",=0A+=09=09=09=09=09=09=09=09=09=09=09=09= X509_verify_cert_error_string(vcode));=0A+=09=09=09=09=09else=20if=20(r=20= =3D=3D=20-1=20&&=20save_errno=20!=3D=200)=0A+=09=09=09=09=09=09= libpq_append_conn_error(conn,=20"SSL=20SYSCALL=20error:=20%s",=0A+=09=09=09= =09=09=09=09=09=09=09=09=09SOCK_STRERROR(save_errno,=20sebuf,=20= sizeof(sebuf)));=0A+=09=09=09=09=09else=0A+=09=09=09=09=09=09= libpq_append_conn_error(conn,=20"SSL=20SYSCALL=20error:=20EOF=20= detected");=0A+=09=09=09=09=09pgtls_close(conn);=0A+=09=09=09=09=09= return=20PGRES_POLLING_FAILED;=0A+=09=09=09=09}=0A+=09=09=09case=20= SSL_ERROR_SSL:=0A+=09=09=09=09{=0A+=09=09=09=09=09char=09=20=20=20*err=20= =3D=20SSLerrmessage(ecode);=0A+=0A+=09=09=09=09=09= libpq_append_conn_error(conn,=20"SSL=20error:=20%s",=20err);=0A+=09=09=09= =09=09SSLerrfree(err);=0A+=09=09=09=09=09switch=20= (ERR_GET_REASON(ecode))=0A+=09=09=09=09=09{=0A+=09=09=09=09=09=09=09/*=0A= +=09=09=09=09=09=09=09=20*=20UNSUPPORTED_PROTOCOL,=20= WRONG_VERSION_NUMBER,=20and=0A+=09=09=09=09=09=09=09=20*=20= TLSV1_ALERT_PROTOCOL_VERSION=20have=20been=20observed=0A+=09=09=09=09=09=09= =09=20*=20when=20trying=20to=20communicate=20with=20an=20old=20OpenSSL=0A= +=09=09=09=09=09=09=09=20*=20library,=20or=20when=20the=20client=20and=20= server=20specify=0A+=09=09=09=09=09=09=09=20*=20disjoint=20protocol=20= ranges.=0A+=09=09=09=09=09=09=09=20*=20NO_PROTOCOLS_AVAILABLE=20occurs=20= if=20there's=20a=0A+=09=09=09=09=09=09=09=20*=20local=20misconfiguration=20= (which=20can=20happen=0A+=09=09=09=09=09=09=09=20*=20despite=20our=20= checks,=20if=20openssl.cnf=20injects=20a=0A+=09=09=09=09=09=09=09=20*=20= limit=20we=20didn't=20account=20for).=20=20It's=20not=20very=0A+=09=09=09= =09=09=09=09=20*=20clear=20what=20would=20make=20OpenSSL=20return=20the=20= other=0A+=09=09=09=09=09=09=09=20*=20codes=20listed=20here,=20but=20a=20= hint=20about=20protocol=0A+=09=09=09=09=09=09=09=20*=20versions=20seems=20= like=20it's=20appropriate=20for=20all.=0A+=09=09=09=09=09=09=09=20*/=0A+=09= =09=09=09=09=09case=20SSL_R_NO_PROTOCOLS_AVAILABLE:=0A+=09=09=09=09=09=09= case=20SSL_R_UNSUPPORTED_PROTOCOL:=0A+=09=09=09=09=09=09case=20= SSL_R_BAD_PROTOCOL_VERSION_NUMBER:=0A+=09=09=09=09=09=09case=20= SSL_R_UNKNOWN_PROTOCOL:=0A+=09=09=09=09=09=09case=20= SSL_R_UNKNOWN_SSL_VERSION:=0A+=09=09=09=09=09=09case=20= SSL_R_UNSUPPORTED_SSL_VERSION:=0A+=09=09=09=09=09=09case=20= SSL_R_WRONG_SSL_VERSION:=0A+=09=09=09=09=09=09case=20= SSL_R_WRONG_VERSION_NUMBER:=0A+=09=09=09=09=09=09case=20= SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:=0A+#ifdef=20SSL_R_VERSION_TOO_HIGH=0A= +=09=09=09=09=09=09case=20SSL_R_VERSION_TOO_HIGH:=0A+=09=09=09=09=09=09= case=20SSL_R_VERSION_TOO_LOW:=0A+#endif=0A+=09=09=09=09=09=09=09= libpq_append_conn_error(conn,=20"This=20may=20indicate=20that=20the=20= server=20does=20not=20support=20any=20SSL=20protocol=20version=20between=20= %s=20and=20%s.",=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09= conn->ssl_min_protocol_version=20?=0A+=09=09=09=09=09=09=09=09=09=09=09=09= =09conn->ssl_min_protocol_version=20:=0A+=09=09=09=09=09=09=09=09=09=09=09= =09=09MIN_OPENSSL_TLS_VERSION,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09= conn->ssl_max_protocol_version=20?=0A+=09=09=09=09=09=09=09=09=09=09=09=09= =09conn->ssl_max_protocol_version=20:=0A+=09=09=09=09=09=09=09=09=09=09=09= =09=09MAX_OPENSSL_TLS_VERSION);=0A+=09=09=09=09=09=09=09break;=0A+=09=09=09= =09=09=09default:=0A+=09=09=09=09=09=09=09break;=0A+=09=09=09=09=09}=0A+=09= =09=09=09=09pgtls_close(conn);=0A+=09=09=09=09=09return=20= PGRES_POLLING_FAILED;=0A+=09=09=09=09}=0A+=0A+=09=09=09default:=0A+=09=09= =09=09libpq_append_conn_error(conn,=20"unrecognized=20SSL=20error=20= code:=20%d",=20err);=0A+=09=09=09=09pgtls_close(conn);=0A+=09=09=09=09= return=20PGRES_POLLING_FAILED;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=20ALPN=20= is=20mandatory=20with=20direct=20SSL=20connections=20*/=0A+=09if=20= (conn->current_enc_method=20=3D=3D=20ENC_SSL=20&&=20= conn->sslnegotiation[0]=20=3D=3D=20'd')=0A+=09{=0A+=09=09const=20= unsigned=20char=20*selected;=0A+=09=09unsigned=20int=20len;=0A+=0A+=09=09= SSL_get0_alpn_selected(conn->ssl,=20&selected,=20&len);=0A+=0A+=09=09if=20= (selected=20=3D=3D=20NULL)=0A+=09=09{=0A+=09=09=09= libpq_append_conn_error(conn,=20"direct=20SSL=20connection=20was=20= established=20without=20ALPN=20protocol=20negotiation=20extension");=0A+=09= =09=09pgtls_close(conn);=0A+=09=09=09return=20PGRES_POLLING_FAILED;=0A+=09= =09}=0A+=0A+=09=09/*=0A+=09=09=20*=20We=20only=20support=20one=20= protocol=20so=20that's=20what=20the=20negotiation=20should=0A+=09=09=20*=20= always=20choose,=20but=20doesn't=20hurt=20to=20check.=0A+=09=09=20*/=0A+=09= =09if=20(len=20!=3D=20strlen(PG_ALPN_PROTOCOL)=20||=0A+=09=09=09= memcmp(selected,=20PG_ALPN_PROTOCOL,=20strlen(PG_ALPN_PROTOCOL))=20!=3D=20= 0)=0A+=09=09{=0A+=09=09=09libpq_append_conn_error(conn,=20"SSL=20= connection=20was=20established=20with=20unexpected=20ALPN=20protocol");=0A= +=09=09=09pgtls_close(conn);=0A+=09=09=09return=20PGRES_POLLING_FAILED;=0A= +=09=09}=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20We=20already=20checked=20the=20= server=20certificate=20in=20initialize_SSL()=20using=0A+=09=20*=20= SSL_CTX_set_verify(),=20if=20root.crt=20exists.=0A+=09=20*/=0A+=0A+=09/*=20= get=20server=20certificate=20*/=0A+=09conn->peer=20=3D=20= SSL_get_peer_certificate(conn->ssl);=0A+=09if=20(conn->peer=20=3D=3D=20= NULL)=0A+=09{=0A+=09=09char=09=20=20=20*err=20=3D=20= SSLerrmessage(ERR_get_error());=0A+=0A+=09=09= libpq_append_conn_error(conn,=20"certificate=20could=20not=20be=20= obtained:=20%s",=20err);=0A+=09=09SSLerrfree(err);=0A+=09=09= pgtls_close(conn);=0A+=09=09return=20PGRES_POLLING_FAILED;=0A+=09}=0A+=0A= +=09if=20(!pq_verify_peer_name_matches_certificate(conn))=0A+=09{=0A+=09=09= pgtls_close(conn);=0A+=09=09return=20PGRES_POLLING_FAILED;=0A+=09}=0A+=0A= +=09/*=20SSL=20handshake=20is=20complete=20*/=0A+=09return=20= PGRES_POLLING_OK;=0A+}=0A+=0A+void=0A+pgtls_close(PGconn=20*conn)=0A+{=0A= +=09if=20(conn->ssl_in_use)=0A+=09{=0A+=09=09if=20(conn->ssl)=0A+=09=09{=0A= +=09=09=09/*=0A+=09=09=09=20*=20We=20can't=20destroy=20everything=20= SSL-related=20here=20due=20to=20the=0A+=09=09=09=20*=20possible=20later=20= calls=20to=20OpenSSL=20routines=20which=20may=20need=20our=0A+=09=09=09=20= *=20thread=20callbacks,=20so=20set=20a=20flag=20here=20and=20check=20at=20= the=20end.=0A+=09=09=09=20*/=0A+=0A+=09=09=09SSL_shutdown(conn->ssl);=0A= +=09=09=09SSL_free(conn->ssl);=0A+=09=09=09conn->ssl=20=3D=20NULL;=0A+=09= =09=09conn->ssl_in_use=20=3D=20false;=0A+=09=09=09= conn->ssl_handshake_started=20=3D=20false;=0A+=09=09}=0A+=0A+=09=09if=20= (conn->peer)=0A+=09=09{=0A+=09=09=09X509_free(conn->peer);=0A+=09=09=09= conn->peer=20=3D=20NULL;=0A+=09=09}=0A+=0A+#ifdef=20USE_SSL_ENGINE=0A+=09= =09if=20(conn->engine)=0A+=09=09{=0A+=09=09=09= ENGINE_finish(conn->engine);=0A+=09=09=09ENGINE_free(conn->engine);=0A+=09= =09=09conn->engine=20=3D=20NULL;=0A+=09=09}=0A+#endif=0A+=09}=0A+}=0A+=0A= +=0A+/*=0A+=20*=20Obtain=20reason=20string=20for=20passed=20SSL=20= errcode=0A+=20*=0A+=20*=20ERR_get_error()=20is=20used=20by=20caller=20to=20= get=20errcode=20to=20pass=20here.=0A+=20*=20The=20result=20must=20be=20= freed=20after=20use,=20using=20SSLerrfree.=0A+=20*=0A+=20*=20Some=20= caution=20is=20needed=20here=20since=20ERR_reason_error_string=20will=20= return=20NULL=0A+=20*=20if=20it=20doesn't=20recognize=20the=20error=20= code,=20or=20(in=20OpenSSL=20>=3D=203)=20if=20the=20code=0A+=20*=20= represents=20a=20system=20errno=20value.=20=20We=20don't=20want=20to=20= return=20NULL=20ever.=0A+=20*/=0A+static=20char=20ssl_nomem[]=20=3D=20= "out=20of=20memory=20allocating=20error=20description";=0A+=0A+#define=20= SSL_ERR_LEN=20128=0A+=0A+static=20char=20*=0A+SSLerrmessage(unsigned=20= long=20ecode)=0A+{=0A+=09const=20char=20*errreason;=0A+=09char=09=20=20=20= *errbuf;=0A+=0A+=09errbuf=20=3D=20malloc(SSL_ERR_LEN);=0A+=09if=20= (!errbuf)=0A+=09=09return=20ssl_nomem;=0A+=09if=20(ecode=20=3D=3D=200)=0A= +=09{=0A+=09=09snprintf(errbuf,=20SSL_ERR_LEN,=20libpq_gettext("no=20SSL=20= error=20reported"));=0A+=09=09return=20errbuf;=0A+=09}=0A+=09errreason=20= =3D=20ERR_reason_error_string(ecode);=0A+=09if=20(errreason=20!=3D=20= NULL)=0A+=09{=0A+=09=09strlcpy(errbuf,=20errreason,=20SSL_ERR_LEN);=0A+=09= =09return=20errbuf;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Server=20aborted=20= the=20connection=20with=20TLS=20"no_application_protocol"=20alert.=0A+=09= =20*=20The=20ERR_reason_error_string()=20function=20doesn't=20give=20any=20= error=20string=0A+=09=20*=20for=20that=20for=20some=20reason,=20so=20do=20= it=20ourselves.=20=20See=0A+=09=20*=20= https://github.com/openssl/openssl/issues/24300.=20=20This=20is=20= available=20in=0A+=09=20*=20OpenSSL=201.1.0=20and=20later,=20as=20well=20= as=20in=20LibreSSL=203.4.3=20(OpenBSD=207.0)=20and=0A+=09=20*=20later.=0A= +=09=20*/=0A+#ifdef=20SSL_AD_NO_APPLICATION_PROTOCOL=0A+=09if=20= (ERR_GET_LIB(ecode)=20=3D=3D=20ERR_LIB_SSL=20&&=0A+=09=09= ERR_GET_REASON(ecode)=20=3D=3D=20SSL_AD_REASON_OFFSET=20+=20= SSL_AD_NO_APPLICATION_PROTOCOL)=0A+=09{=0A+=09=09snprintf(errbuf,=20= SSL_ERR_LEN,=20"no=20application=20protocol");=0A+=09=09return=20errbuf;=0A= +=09}=0A+#endif=0A+=0A+=09/*=20No=20choice=20but=20to=20report=20the=20= numeric=20ecode=20*/=0A+=09snprintf(errbuf,=20SSL_ERR_LEN,=20= libpq_gettext("SSL=20error=20code=20%lu"),=20ecode);=0A+=09return=20= errbuf;=0A+}=0A+=0A+static=20void=0A+SSLerrfree(char=20*buf)=0A+{=0A+=09= if=20(buf=20!=3D=20ssl_nomem)=0A+=09=09free(buf);=0A+}=0A+=0A+/*=20= ------------------------------------------------------------=20*/=0A+/*=09= =09=09=09=09SSL=20information=20functions=09=09=09=09=09*/=0A+/*=20= ------------------------------------------------------------=20*/=0A+=0A= +/*=0A+=20*=09Return=20pointer=20to=20OpenSSL=20object.=0A+=20*/=0A+void=20= *=0A+PQgetssl(PGconn=20*conn)=0A+{=0A+=09if=20(!conn)=0A+=09=09return=20= NULL;=0A+=09return=20conn->ssl;=0A+}=0A+=0A+void=20*=0A= +PQsslStruct(PGconn=20*conn,=20const=20char=20*struct_name)=0A+{=0A+=09= if=20(!conn)=0A+=09=09return=20NULL;=0A+=09if=20(strcmp(struct_name,=20= "OpenSSL")=20=3D=3D=200)=0A+=09=09return=20conn->ssl;=0A+=09return=20= NULL;=0A+}=0A+=0A+const=20char=20*const=20*=0A= +PQsslAttributeNames(PGconn=20*conn)=0A+{=0A+=09static=20const=20char=20= *const=20openssl_attrs[]=20=3D=20{=0A+=09=09"library",=0A+=09=09= "key_bits",=0A+=09=09"cipher",=0A+=09=09"compression",=0A+=09=09= "protocol",=0A+=09=09"alpn",=0A+=09=09NULL=0A+=09};=0A+=09static=20const=20= char=20*const=20empty_attrs[]=20=3D=20{NULL};=0A+=0A+=09if=20(!conn)=0A+=09= {=0A+=09=09/*=20Return=20attributes=20of=20default=20SSL=20library=20*/=0A= +=09=09return=20openssl_attrs;=0A+=09}=0A+=0A+=09/*=20No=20attrs=20for=20= unencrypted=20connection=20*/=0A+=09if=20(conn->ssl=20=3D=3D=20NULL)=0A+=09= =09return=20empty_attrs;=0A+=0A+=09return=20openssl_attrs;=0A+}=0A+=0A= +const=20char=20*=0A+PQsslAttribute(PGconn=20*conn,=20const=20char=20= *attribute_name)=0A+{=0A+=09if=20(!conn)=0A+=09{=0A+=09=09/*=20= PQsslAttribute(NULL,=20"library")=20reports=20the=20default=20SSL=20= library=20*/=0A+=09=09if=20(strcmp(attribute_name,=20"library")=20=3D=3D=20= 0)=0A+=09=09=09return=20"OpenSSL";=0A+=09=09return=20NULL;=0A+=09}=0A+=0A= +=09/*=20All=20attributes=20read=20as=20NULL=20for=20a=20non-encrypted=20= connection=20*/=0A+=09if=20(conn->ssl=20=3D=3D=20NULL)=0A+=09=09return=20= NULL;=0A+=0A+=09if=20(strcmp(attribute_name,=20"library")=20=3D=3D=200)=0A= +=09=09return=20"OpenSSL";=0A+=0A+=09if=20(strcmp(attribute_name,=20= "key_bits")=20=3D=3D=200)=0A+=09{=0A+=09=09static=20char=20= sslbits_str[12];=0A+=09=09int=09=09=09sslbits;=0A+=0A+=09=09= SSL_get_cipher_bits(conn->ssl,=20&sslbits);=0A+=09=09= snprintf(sslbits_str,=20sizeof(sslbits_str),=20"%d",=20sslbits);=0A+=09=09= return=20sslbits_str;=0A+=09}=0A+=0A+=09if=20(strcmp(attribute_name,=20= "cipher")=20=3D=3D=200)=0A+=09=09return=20SSL_get_cipher(conn->ssl);=0A+=0A= +=09if=20(strcmp(attribute_name,=20"compression")=20=3D=3D=200)=0A+=09=09= return=20SSL_get_current_compression(conn->ssl)=20?=20"on"=20:=20"off";=0A= +=0A+=09if=20(strcmp(attribute_name,=20"protocol")=20=3D=3D=200)=0A+=09=09= return=20SSL_get_version(conn->ssl);=0A+=0A+=09if=20= (strcmp(attribute_name,=20"alpn")=20=3D=3D=200)=0A+=09{=0A+=09=09const=20= unsigned=20char=20*data;=0A+=09=09unsigned=20int=20len;=0A+=09=09static=20= char=20alpn_str[256];=09/*=20alpn=20doesn't=20support=20longer=20than=20= 255=0A+=09=09=09=09=09=09=09=09=09=20*=20bytes=20*/=0A+=0A+=09=09= SSL_get0_alpn_selected(conn->ssl,=20&data,=20&len);=0A+=09=09if=20(data=20= =3D=3D=20NULL=20||=20len=20=3D=3D=200=20||=20len=20>=20sizeof(alpn_str)=20= -=201)=0A+=09=09=09return=20"";=0A+=09=09memcpy(alpn_str,=20data,=20= len);=0A+=09=09alpn_str[len]=20=3D=200;=0A+=09=09return=20alpn_str;=0A+=09= }=0A+=0A+=09return=20NULL;=09=09=09=09/*=20unknown=20attribute=20*/=0A+}=0A= +=0A+/*=0A+=20*=20Private=20substitute=20BIO:=20this=20does=20the=20= sending=20and=20receiving=20using=0A+=20*=20pqsecure_raw_write()=20and=20= pqsecure_raw_read()=20instead,=20to=20allow=20those=0A+=20*=20functions=20= to=20disable=20SIGPIPE=20and=20give=20better=20error=20messages=20on=20= I/O=20errors.=0A+=20*=0A+=20*=20These=20functions=20are=20closely=20= modelled=20on=20the=20standard=20socket=20BIO=20in=20OpenSSL;=0A+=20*=20= see=20sock_read()=20and=20sock_write()=20in=20OpenSSL's=20= crypto/bio/bss_sock.c.=0A+=20*/=0A+=0A+/*=20protected=20by=20= ssl_config_mutex=20*/=0A+static=20BIO_METHOD=20*pgconn_bio_method_ptr;=0A= +=0A+static=20int=0A+pgconn_bio_read(BIO=20*h,=20char=20*buf,=20int=20= size)=0A+{=0A+=09PGconn=09=20=20=20*conn=20=3D=20(PGconn=20*)=20= BIO_get_data(h);=0A+=09int=09=09=09res;=0A+=0A+=09res=20=3D=20= pqsecure_raw_read(conn,=20buf,=20size);=0A+=09BIO_clear_retry_flags(h);=0A= +=09conn->last_read_was_eof=20=3D=20res=20=3D=3D=200;=0A+=09if=20(res=20= <=200)=0A+=09{=0A+=09=09/*=20If=20we=20were=20interrupted,=20tell=20= caller=20to=20retry=20*/=0A+=09=09switch=20(SOCK_ERRNO)=0A+=09=09{=0A= +#ifdef=20EAGAIN=0A+=09=09=09case=20EAGAIN:=0A+#endif=0A+#if=20= defined(EWOULDBLOCK)=20&&=20(!defined(EAGAIN)=20||=20(EWOULDBLOCK=20!=3D=20= EAGAIN))=0A+=09=09=09case=20EWOULDBLOCK:=0A+#endif=0A+=09=09=09case=20= EINTR:=0A+=09=09=09=09BIO_set_retry_read(h);=0A+=09=09=09=09break;=0A+=0A= +=09=09=09default:=0A+=09=09=09=09break;=0A+=09=09}=0A+=09}=0A+=0A+=09if=20= (res=20>=200)=0A+=09=09conn->ssl_handshake_started=20=3D=20true;=0A+=0A+=09= return=20res;=0A+}=0A+=0A+static=20int=0A+pgconn_bio_write(BIO=20*h,=20= const=20char=20*buf,=20int=20size)=0A+{=0A+=09int=09=09=09res;=0A+=0A+=09= res=20=3D=20pqsecure_raw_write((PGconn=20*)=20BIO_get_data(h),=20buf,=20= size);=0A+=09BIO_clear_retry_flags(h);=0A+=09if=20(res=20<=200)=0A+=09{=0A= +=09=09/*=20If=20we=20were=20interrupted,=20tell=20caller=20to=20retry=20= */=0A+=09=09switch=20(SOCK_ERRNO)=0A+=09=09{=0A+#ifdef=20EAGAIN=0A+=09=09= =09case=20EAGAIN:=0A+#endif=0A+#if=20defined(EWOULDBLOCK)=20&&=20= (!defined(EAGAIN)=20||=20(EWOULDBLOCK=20!=3D=20EAGAIN))=0A+=09=09=09case=20= EWOULDBLOCK:=0A+#endif=0A+=09=09=09case=20EINTR:=0A+=09=09=09=09= BIO_set_retry_write(h);=0A+=09=09=09=09break;=0A+=0A+=09=09=09default:=0A= +=09=09=09=09break;=0A+=09=09}=0A+=09}=0A+=0A+=09return=20res;=0A+}=0A+=0A= +static=20long=0A+pgconn_bio_ctrl(BIO=20*h,=20int=20cmd,=20long=20num,=20= void=20*ptr)=0A+{=0A+=09long=09=09res;=0A+=09PGconn=09=20=20=20*conn=20=3D= =20(PGconn=20*)=20BIO_get_data(h);=0A+=0A+=09switch=20(cmd)=0A+=09{=0A+=09= =09case=20BIO_CTRL_EOF:=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20This=20= should=20not=20be=20needed.=20pgconn_bio_read=20already=20has=20a=20way=20= to=0A+=09=09=09=20*=20signal=20EOF=20to=20OpenSSL.=20However,=20OpenSSL=20= made=20an=20undocumented,=0A+=09=09=09=20*=20backwards-incompatible=20= change=20and=20now=20expects=20EOF=20via=20BIO_ctrl.=0A+=09=09=09=20*=20= See=20https://github.com/openssl/openssl/issues/8208=0A+=09=09=09=20*/=0A= +=09=09=09res=20=3D=20conn->last_read_was_eof;=0A+=09=09=09break;=0A+=09=09= case=20BIO_CTRL_FLUSH:=0A+=09=09=09/*=20libssl=20expects=20all=20BIOs=20= to=20support=20BIO_flush.=20*/=0A+=09=09=09res=20=3D=201;=0A+=09=09=09= break;=0A+=09=09default:=0A+=09=09=09res=20=3D=200;=0A+=09=09=09break;=0A= +=09}=0A+=0A+=09return=20res;=0A+}=0A+=0A+static=20BIO_METHOD=20*=0A= +pgconn_bio_method(void)=0A+{=0A+=09BIO_METHOD=20*res;=0A+=0A+=09if=20= (pthread_mutex_lock(&ssl_config_mutex))=0A+=09=09return=20NULL;=0A+=0A+=09= res=20=3D=20pgconn_bio_method_ptr;=0A+=0A+=09if=20= (!pgconn_bio_method_ptr)=0A+=09{=0A+=09=09int=09=09=09my_bio_index;=0A+=0A= +=09=09my_bio_index=20=3D=20BIO_get_new_index();=0A+=09=09if=20= (my_bio_index=20=3D=3D=20-1)=0A+=09=09=09goto=20err;=0A+=09=09= my_bio_index=20|=3D=20BIO_TYPE_SOURCE_SINK;=0A+=09=09res=20=3D=20= BIO_meth_new(my_bio_index,=20"libpq=20socket");=0A+=09=09if=20(!res)=0A+=09= =09=09goto=20err;=0A+=0A+=09=09/*=0A+=09=09=20*=20As=20of=20this=20= writing,=20these=20functions=20never=20fail.=20But=20check=20anyway,=0A+=09= =09=20*=20like=20OpenSSL's=20own=20examples=20do.=0A+=09=09=20*/=0A+=09=09= if=20(!BIO_meth_set_write(res,=20pgconn_bio_write)=20||=0A+=09=09=09= !BIO_meth_set_read(res,=20pgconn_bio_read)=20||=0A+=09=09=09= !BIO_meth_set_ctrl(res,=20pgconn_bio_ctrl))=0A+=09=09{=0A+=09=09=09goto=20= err;=0A+=09=09}=0A+=09}=0A+=0A+=09pgconn_bio_method_ptr=20=3D=20res;=0A+=09= pthread_mutex_unlock(&ssl_config_mutex);=0A+=09return=20res;=0A+=0A+err:=0A= +=09if=20(res)=0A+=09=09BIO_meth_free(res);=0A+=09= pthread_mutex_unlock(&ssl_config_mutex);=0A+=09return=20NULL;=0A+}=0A+=0A= +static=20int=0A+ssl_set_pgconn_bio(PGconn=20*conn)=0A+{=0A+=09BIO=09=09=20= =20=20*bio;=0A+=09BIO_METHOD=20*bio_method;=0A+=0A+=09bio_method=20=3D=20= pgconn_bio_method();=0A+=09if=20(bio_method=20=3D=3D=20NULL)=0A+=09=09= return=200;=0A+=0A+=09bio=20=3D=20BIO_new(bio_method);=0A+=09if=20(bio=20= =3D=3D=20NULL)=0A+=09=09return=200;=0A+=0A+=09BIO_set_data(bio,=20conn);=0A= +=09BIO_set_init(bio,=201);=0A+=0A+=09SSL_set_bio(conn->ssl,=20bio,=20= bio);=0A+=09return=201;=0A+}=0A+=0A+/*=0A+=20*=20This=20is=20the=20= default=20handler=20to=20return=20a=20client=20cert=20password=20from=0A= +=20*=20conn->sslpassword.=20Apps=20may=20install=20it=20explicitly=20if=20= they=20want=20to=0A+=20*=20prevent=20openssl=20from=20ever=20prompting=20= on=20stdin.=0A+=20*/=0A+int=0A+PQdefaultSSLKeyPassHook_OpenSSL(char=20= *buf,=20int=20size,=20PGconn=20*conn)=0A+{=0A+=09if=20(conn=20&&=20= conn->sslpassword)=0A+=09{=0A+=09=09if=20(strlen(conn->sslpassword)=20+=20= 1=20>=20size)=0A+=09=09=09fprintf(stderr,=20libpq_gettext("WARNING:=20= sslpassword=20truncated\n"));=0A+=09=09strncpy(buf,=20conn->sslpassword,=20= size);=0A+=09=09buf[size=20-=201]=20=3D=20'\0';=0A+=09=09return=20= strlen(buf);=0A+=09}=0A+=09else=0A+=09{=0A+=09=09buf[0]=20=3D=20'\0';=0A= +=09=09return=200;=0A+=09}=0A+}=0A+=0A+PQsslKeyPassHook_OpenSSL_type=0A= +PQgetSSLKeyPassHook_OpenSSL(void)=0A+{=0A+=09return=20PQsslKeyPassHook;=0A= +}=0A+=0A+void=0A= +PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type=20hook)=0A+{=0A= +=09PQsslKeyPassHook=20=3D=20hook;=0A+}=0A+=0A+/*=0A+=20*=20Supply=20a=20= password=20to=20decrypt=20a=20client=20certificate.=0A+=20*=0A+=20*=20= This=20must=20match=20OpenSSL=20type=20pem_password_cb.=0A+=20*/=0A= +static=20int=0A+PQssl_passwd_cb(char=20*buf,=20int=20size,=20int=20= rwflag,=20void=20*userdata)=0A+{=0A+=09PGconn=09=20=20=20*conn=20=3D=20= userdata;=0A+=0A+=09if=20(PQsslKeyPassHook)=0A+=09=09return=20= PQsslKeyPassHook(buf,=20size,=20conn);=0A+=09else=0A+=09=09return=20= PQdefaultSSLKeyPassHook_OpenSSL(buf,=20size,=20conn);=0A+}=0A+=0A+/*=0A+=20= *=20Convert=20TLS=20protocol=20version=20string=20to=20OpenSSL=20values=0A= +=20*=0A+=20*=20If=20a=20version=20is=20passed=20that=20is=20not=20= supported=20by=20the=20current=20OpenSSL=20version,=0A+=20*=20then=20we=20= return=20-1.=20If=20a=20non-negative=20value=20is=20returned,=20= subsequent=20code=20can=0A+=20*=20assume=20it=20is=20working=20with=20a=20= supported=20version.=0A+=20*=0A+=20*=20Note:=20this=20is=20rather=20= similar=20to=20the=20backend=20routine=20in=20be-secure-openssl.c,=0A+=20= *=20so=20make=20sure=20to=20update=20both=20routines=20if=20changing=20= this=20one.=0A+=20*/=0A+static=20int=0A= +ssl_protocol_version_to_openssl(const=20char=20*protocol)=0A+{=0A+=09if=20= (pg_strcasecmp("TLSv1",=20protocol)=20=3D=3D=200)=0A+=09=09return=20= TLS1_VERSION;=0A+=0A+#ifdef=20TLS1_1_VERSION=0A+=09if=20= (pg_strcasecmp("TLSv1.1",=20protocol)=20=3D=3D=200)=0A+=09=09return=20= TLS1_1_VERSION;=0A+#endif=0A+=0A+#ifdef=20TLS1_2_VERSION=0A+=09if=20= (pg_strcasecmp("TLSv1.2",=20protocol)=20=3D=3D=200)=0A+=09=09return=20= TLS1_2_VERSION;=0A+#endif=0A+=0A+#ifdef=20TLS1_3_VERSION=0A+=09if=20= (pg_strcasecmp("TLSv1.3",=20protocol)=20=3D=3D=200)=0A+=09=09return=20= TLS1_3_VERSION;=0A+#endif=0A+=0A+=09return=20-1;=0A+}=0Adiff=20--git=20= a/src/interfaces/libpq/fe-secure-openssl.c=20= b/src/interfaces/libpq/fe-secure-openssl.c=0Aindex=20= c7651c98ab5..54710c13c23=20100644=0A---=20= a/src/interfaces/libpq/fe-secure-openssl.c=0A+++=20= b/src/interfaces/libpq/fe-secure-openssl.c=0A@@=20-386,15=20+386,9=20@@=20= pgtls_get_peer_certificate_hash(PGconn=20*conn,=20size_t=20*len)=0A=20=0A= =20=09/*=0A=20=09=20*=20Get=20the=20signature=20algorithm=20of=20the=20= certificate=20to=20determine=20the=20hash=0A-=09=20*=20algorithm=20to=20= use=20for=20the=20result.=20=20Prefer=20X509_get_signature_info(),=0A-=09= =20*=20introduced=20in=20OpenSSL=201.1.1,=20which=20can=20handle=20= RSA-PSS=20signatures.=0A+=09=20*=20algorithm=20to=20use=20for=20the=20= result.=0A=20=09=20*/=0A-#if=20HAVE_X509_GET_SIGNATURE_INFO=0A=20=09if=20= (!X509_get_signature_info(peer_cert,=20&algo_nid,=20NULL,=20NULL,=20= NULL))=0A-#else=0A-=09if=20= (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),=0A-=09=09=09=09=09= =09=09=20&algo_nid,=20NULL))=0A-#endif=0A=20=09{=0A=20=09=09= libpq_append_conn_error(conn,=20"could=20not=20determine=20server=20= certificate=20signature=20algorithm");=0A=20=09=09return=20NULL;=0A@@=20= -463,7=20+457,6=20@@=20verify_cb(int=20ok,=20X509_STORE_CTX=20*ctx)=0A=20= =09return=20ok;=0A=20}=0A=20=0A-#ifdef=20HAVE_SSL_CTX_SET_CERT_CB=0A=20= /*=0A=20=20*=20Certificate=20selection=20callback=0A=20=20*=0A@@=20= -490,7=20+483,6=20@@=20cert_cb(SSL=20*ssl,=20void=20*arg)=0A=20=09=20*/=0A= =20=09return=201;=0A=20}=0A-#endif=0A=20=0A=20/*=0A=20=20*=20= OpenSSL-specific=20wrapper=20around=0A@@=20-717,14=20+709,11=20@@=20= pgtls_verify_peer_name_matches_certificate_guts(PGconn=20*conn,=0A=20/*=20= See=20pqcomm.h=20comments=20on=20OpenSSL=20implementation=20of=20ALPN=20= (RFC=207301)=20*/=0A=20static=20unsigned=20char=20alpn_protos[]=20=3D=20= PG_ALPN_PROTOCOL_VECTOR;=0A=20=0A-#ifdef=20= HAVE_SSL_CTX_SET_KEYLOG_CALLBACK=0A=20/*=0A=20=20*=20SSL=20Key=20Logging=20= callback=0A=20=20*=0A=20=20*=20This=20callback=20lets=20the=20user=20= store=20all=20key=20material=20to=20a=20file=20for=20debugging=0A-=20*=20= purposes.=20=20The=20file=20will=20be=20written=20using=20the=20NSS=20= keylog=20format.=20=20LibreSSL=0A-=20*=203.5=20introduced=20stub=20= function=20to=20set=20the=20callback=20for=20OpenSSL=20compatibility=0A-=20= *=20but=20the=20callback=20is=20never=20invoked.=0A+=20*=20purposes.=20=20= The=20file=20will=20be=20written=20using=20the=20NSS=20keylog=20format.=0A= =20=20*=0A=20=20*=20Error=20messages=20added=20to=20the=20connection=20= object=20won't=20be=20printed=20anywhere=20if=0A=20=20*=20the=20= connection=20is=20successful.=20=20Errors=20in=20processing=20keylogging=20= are=20printed=0A@@=20-759,7=20+748,6=20@@=20SSL_CTX_keylog_cb(const=20= SSL=20*ssl,=20const=20char=20*line)=0A=20=09(void)=20rc;=09=09=09=09=09= /*=20silence=20compiler=20warnings=20*/=0A=20=09close(fd);=0A=20}=0A= -#endif=0A=20=0A=20/*=0A=20=20*=09Create=20per-connection=20SSL=20= object,=20and=20load=20the=20client=20certificate,=0A@@=20-829,10=20= +817,8=20@@=20initialize_SSL(PGconn=20*conn)=0A=20=09=09= SSL_CTX_set_default_passwd_cb_userdata(SSL_context,=20conn);=0A=20=09}=0A= =20=0A-#ifdef=20HAVE_SSL_CTX_SET_CERT_CB=0A=20=09/*=20Set=20up=20a=20= certificate=20selection=20callback.=20*/=0A=20=09= SSL_CTX_set_cert_cb(SSL_context,=20cert_cb,=20conn);=0A-#endif=0A=20=0A=20= =09/*=20Disable=20old=20protocol=20versions=20*/=0A=20=09= SSL_CTX_set_options(SSL_context,=20SSL_OP_NO_SSLv2=20|=20= SSL_OP_NO_SSLv3);=0A@@=20-1085,12=20+1071,8=20@@=20initialize_SSL(PGconn=20= *conn)=0A=20=09{=0A=20#ifdef=20HAVE_SSL_CTX_SET_KEYLOG_CALLBACK=0A=20=09=09= SSL_CTX_set_keylog_callback(SSL_context,=20SSL_CTX_keylog_cb);=0A-#else=0A= -#ifdef=20LIBRESSL_VERSION_NUMBER=0A-=09=09fprintf(stderr,=20= libpq_gettext("WARNING:=20sslkeylogfile=20support=20requires=20= OpenSSL\n"));=0A=20#else=0A=20=09=09fprintf(stderr,=20= libpq_gettext("WARNING:=20libpq=20was=20not=20built=20with=20= sslkeylogfile=20support\n"));=0A-#endif=0A=20#endif=0A=20=09}=0A=20=0A= diff=20--git=20a/src/interfaces/libpq/fe-secure.c=20= b/src/interfaces/libpq/fe-secure.c=0Aindex=201a8e2e6746e..52e4f92b980=20= 100644=0A---=20a/src/interfaces/libpq/fe-secure.c=0A+++=20= b/src/interfaces/libpq/fe-secure.c=0A@@=20-473,14=20+473,11=20@@=20= PQsslAttributeNames(PGconn=20*conn)=0A=20=0A=20=09return=20result;=0A=20= }=0A-#endif=09=09=09=09=09=09=09/*=20USE_SSL=20*/=0A=20=0A=20/*=0A=20=20= *=20Dummy=20versions=20of=20OpenSSL=20key=20password=20hook=20functions,=20= when=20built=20without=0A-=20*=20OpenSSL.=0A+=20*=20SSL.=0A=20=20*/=0A= -#ifndef=20USE_OPENSSL=0A-=0A=20PQsslKeyPassHook_OpenSSL_type=0A=20= PQgetSSLKeyPassHook_OpenSSL(void)=0A=20{=0A@@=20-498,7=20+495,7=20@@=20= PQdefaultSSLKeyPassHook_OpenSSL(char=20*buf,=20int=20size,=20PGconn=20= *conn)=0A=20{=0A=20=09return=200;=0A=20}=0A-#endif=09=09=09=09=09=09=09= /*=20USE_OPENSSL=20*/=0A+#endif=09=09=09=09=09=09=09/*=20USE_SSL=20*/=0A=20= =0A=20/*=20Dummy=20version=20of=20GSSAPI=20information=20functions,=20= when=20built=20without=20GSS=20support=20*/=0A=20#ifndef=20ENABLE_GSS=0A= diff=20--git=20a/src/interfaces/libpq/libpq-int.h=20= b/src/interfaces/libpq/libpq-int.h=0Aindex=203f921207a14..06276db377b=20= 100644=0A---=20a/src/interfaces/libpq/libpq-int.h=0A+++=20= b/src/interfaces/libpq/libpq-int.h=0A@@=20-66,10=20+66,12=20@@=20typedef=20= struct=0A=20#endif=0A=20#endif=09=09=09=09=09=09=09/*=20ENABLE_SSPI=20*/=0A= =20=0A-#ifdef=20USE_OPENSSL=0A+#if=20defined(USE_OPENSSL)=20||=20= defined(USE_LIBRESSL)=0A=20#include=20=0A=20#include=20= =0A+#endif=09=09=09=09=09=09=09/*=20USE_OPENSSL,=20= USE_LIBRESSL=20*/=0A=20=0A+#ifdef=20USE_OPENSSL=0A=20#ifndef=20= OPENSSL_NO_ENGINE=0A=20#define=20USE_SSL_ENGINE=0A=20#endif=0A@@=20= -627,7=20+629,7=20@@=20struct=20pg_conn=0A=20=09bool=09=09= last_read_was_eof;=0A=20=0A=20#ifdef=20USE_SSL=0A-#ifdef=20USE_OPENSSL=0A= +#if=20defined(USE_OPENSSL)=20||=20defined(USE_LIBRESSL)=0A=20=09SSL=09=09= =20=20=20*ssl;=09=09=09/*=20SSL=20status,=20if=20have=20SSL=20connection=20= */=0A=20=09X509=09=20=20=20*peer;=09=09=09/*=20X509=20cert=20of=20server=20= */=0A=20#ifdef=20USE_SSL_ENGINE=0A@@=20-636,7=20+638,7=20@@=20struct=20= pg_conn=0A=20=09void=09=20=20=20*engine;=09=09=09/*=20dummy=20field=20to=20= keep=20struct=20the=20same=20if=0A=20=09=09=09=09=09=09=09=09=20*=20= OpenSSL=20version=20changes=20*/=0A=20#endif=0A-#endif=09=09=09=09=09=09=09= /*=20USE_OPENSSL=20*/=0A+#endif=09=09=09=09=09=09=09/*=20USE_OPENSSL,=20= USE_LIBRESSL=20*/=0A=20#endif=09=09=09=09=09=09=09/*=20USE_SSL=20*/=0A=20= =0A=20#ifdef=20ENABLE_GSS=0Adiff=20--git=20= a/src/interfaces/libpq/meson.build=20b/src/interfaces/libpq/meson.build=0A= index=20b0ae72167a1..410a2d94a3f=20100644=0A---=20= a/src/interfaces/libpq/meson.build=0A+++=20= b/src/interfaces/libpq/meson.build=0A@@=20-28,7=20+28,11=20@@=20endif=0A=20= =0A=20if=20ssl.found()=0A=20=20=20libpq_sources=20+=3D=20= files('fe-secure-common.c')=0A-=20=20libpq_sources=20+=3D=20= files('fe-secure-openssl.c')=0A+=20=20if=20ssl_library=20=3D=3D=20= 'openssl'=0A+=20=20=20=20libpq_sources=20+=3D=20= files('fe-secure-openssl.c')=0A+=20=20else=0A+=20=20=20=20libpq_sources=20= +=3D=20files('fe-secure-libressl.c')=0A+=20=20endif=0A=20endif=0A=20=0A=20= if=20gssapi.found()=0Adiff=20--git=20a/src/port/pg_strong_random.c=20= b/src/port/pg_strong_random.c=0Aindex=20c8d8a70d896..a1bf57b808a=20= 100644=0A---=20a/src/port/pg_strong_random.c=0A+++=20= b/src/port/pg_strong_random.c=0A@@=20-50,7=20+50,7=20@@=0A=20=0A=20=0A=20= =0A-#ifdef=20USE_OPENSSL=0A+#if=20defined(USE_OPENSSL)=20||=20= defined(USE_LIBRESSL)=0A=20=0A=20#include=20=0A=20=0A@@=20= -134,7=20+134,7=20@@=20pg_strong_random(void=20*buf,=20size_t=20len)=0A=20= =09return=20false;=0A=20}=0A=20=0A-#else=09=09=09=09=09=09=09/*=20not=20= USE_OPENSSL=20or=20WIN32=20*/=0A+#else=09=09=09=09=09=09=09/*=20not=20= USE_OPENSSL,=20USE_LIBRESSL=20or=20WIN32=20*/=0A=20=0A=20/*=0A=20=20*=20= Without=20OpenSSL=20or=20Win32=20support,=20just=20read=20/dev/urandom=20= ourselves.=0Adiff=20--git=20a/src/test/ssl/t/001_ssltests.pl=20= b/src/test/ssl/t/001_ssltests.pl=0Aindex=20cb7c2a06193..ea38e5dd407=20= 100644=0A---=20a/src/test/ssl/t/001_ssltests.pl=0A+++=20= b/src/test/ssl/t/001_ssltests.pl=0A@@=20-13,9=20+13,9=20@@=20use=20lib=20= $FindBin::RealBin;=0A=20=0A=20use=20SSL::Server;=0A=20=0A-if=20= ($ENV{with_ssl}=20ne=20'openssl')=0A+if=20($ENV{with_ssl}=20ne=20= 'openssl'=20&&=20$ENV{with_ssl}=20ne=20'libressl')=0A=20{=0A-=09plan=20= skip_all=20=3D>=20'OpenSSL=20not=20supported=20by=20this=20build';=0A+=09= plan=20skip_all=20=3D>=20'SSL=20not=20supported=20by=20this=20build';=0A=20= }=0A=20if=20(!$ENV{PG_TEST_EXTRA}=20||=20$ENV{PG_TEST_EXTRA}=20!~=20= /\bssl\b/)=0A=20{=0Adiff=20--git=20a/src/test/ssl/t/002_scram.pl=20= b/src/test/ssl/t/002_scram.pl=0Aindex=2088c82a4ac11..b9fb400f58b=20= 100644=0A---=20a/src/test/ssl/t/002_scram.pl=0A+++=20= b/src/test/ssl/t/002_scram.pl=0A@@=20-16,9=20+16,9=20@@=20use=20lib=20= $FindBin::RealBin;=0A=20=0A=20use=20SSL::Server;=0A=20=0A-if=20= ($ENV{with_ssl}=20ne=20'openssl')=0A+if=20($ENV{with_ssl}=20ne=20= 'openssl'=20&&=20$ENV{with_ssl}=20ne=20'libressl')=0A=20{=0A-=09plan=20= skip_all=20=3D>=20'OpenSSL=20not=20supported=20by=20this=20build';=0A+=09= plan=20skip_all=20=3D>=20'SSL=20not=20supported=20by=20this=20build';=0A=20= }=0A=20if=20(!$ENV{PG_TEST_EXTRA}=20||=20$ENV{PG_TEST_EXTRA}=20!~=20= /\bssl\b/)=0A=20{=0Adiff=20--git=20a/src/test/ssl/t/003_sslinfo.pl=20= b/src/test/ssl/t/003_sslinfo.pl=0Aindex=2056d32cbaa31..19f4277d0ad=20= 100644=0A---=20a/src/test/ssl/t/003_sslinfo.pl=0A+++=20= b/src/test/ssl/t/003_sslinfo.pl=0A@@=20-14,9=20+14,9=20@@=20use=20lib=20= $FindBin::RealBin;=0A=20=0A=20use=20SSL::Server;=0A=20=0A-if=20= ($ENV{with_ssl}=20ne=20'openssl')=0A+if=20($ENV{with_ssl}=20ne=20= 'openssl'=20&&=20$ENV{with_ssl}=20ne=20'libressl')=0A=20{=0A-=09plan=20= skip_all=20=3D>=20'OpenSSL=20not=20supported=20by=20this=20build';=0A+=09= plan=20skip_all=20=3D>=20'SSL=20not=20supported=20by=20this=20build';=0A=20= }=0A=20if=20(!$ENV{PG_TEST_EXTRA}=20||=20$ENV{PG_TEST_EXTRA}=20!~=20= /\bssl\b/)=0A=20{=0Adiff=20--git=20a/src/test/ssl/t/004_sni.pl=20= b/src/test/ssl/t/004_sni.pl=0Aindex=20f1a135bb3c8..625d863b217=20100644=0A= ---=20a/src/test/ssl/t/004_sni.pl=0A+++=20b/src/test/ssl/t/004_sni.pl=0A= @@=20-20,6=20+20,11=20@@=20my=20$SERVERHOSTADDR=20=3D=20'127.0.0.1';=0A=20= #=20This=20is=20the=20pattern=20to=20use=20in=20pg_hba.conf=20to=20match=20= incoming=20connections.=0A=20my=20$SERVERHOSTCIDR=20=3D=20= '127.0.0.1/32';=0A=20=0A+if=20($ENV{with_ssl}=20eq=20'libressl')=0A+{=0A= +=09plan=20skip_all=20=3D>=20'SNI=20not=20supported=20when=20building=20= with=20LibreSSL';=0A+}=0A+=0A=20if=20($ENV{with_ssl}=20ne=20'openssl')=0A= =20{=0A=20=09plan=20skip_all=20=3D>=20'OpenSSL=20not=20supported=20by=20= this=20build';=0A@@=20-32,12=20+37,6=20@@=20if=20(!$ENV{PG_TEST_EXTRA}=20= ||=20$ENV{PG_TEST_EXTRA}=20!~=20/\bssl\b/)=0A=20}=0A=20=0A=20my=20= $ssl_server=20=3D=20SSL::Server->new();=0A-=0A-if=20= ($ssl_server->is_libressl)=0A-{=0A-=09plan=20skip_all=20=3D>=20'SNI=20= not=20supported=20when=20building=20with=20LibreSSL';=0A-}=0A-=0A=20my=20= $node=20=3D=20PostgreSQL::Test::Cluster->new('primary');=0A=20= $node->init;=0A=20=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= 6060771c1a8..826e6116eca=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-213,6=20+213,8=20@@=20= sub=20get_library=0A=20{=0A=20=09my=20($self)=20=3D=20@_;=0A=20=0A+=09#=20= XXX:=20Temporary=20hack=20during=20initial=20discussions=20of=20the=20= patch=0A+=09return=20'LibreSSL'=20if=20(library_is_libressl());=0A=20=09= return=20$self->{_library};=0A=20}=0A=20=0A@@=20-230,7=20+232,7=20@@=20= sub=20library_is_libressl=0A=20=0A=20=09#=20The=20= HAVE_SSL_CTX_SET_CERT_CB=20macro=20isn't=20defined=20for=20LibreSSL.=0A=20= =09#=20We=20may=20eventually=20need=20a=20less-bogus=20heuristic.=0A-=09= return=20not=20check_pg_config("#define=20HAVE_SSL_CTX_SET_CERT_CB=20= 1");=0A+=09return=20check_pg_config("#define=20= HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM=201");=0A=20}=0A=20=0A=20#=20= Internal=20method=20for=20copying=20a=20set=20of=20files,=20taking=20= into=20account=20wildcards=0Adiff=20--git=20= a/src/test/ssl/t/SSL/Server.pm=20b/src/test/ssl/t/SSL/Server.pm=0Aindex=20= 4400a432f42..faef4b51dde=20100644=0A---=20a/src/test/ssl/t/SSL/Server.pm=0A= +++=20b/src/test/ssl/t/SSL/Server.pm=0A@@=20-103,6=20+103,11=20@@=20sub=20= new=0A=20=09=09$self->{flavor}=20=3D=20'openssl';=0A=20=09=09= $self->{backend}=20=3D=20SSL::Backend::OpenSSL->new();=0A=20=09}=0A+=09= elsif=20($flavor=20=3D~=20/\Alibressl\z/i)=0A+=09{=0A+=09=09= $self->{flavor}=20=3D=20'libressl';=0A+=09=09$self->{backend}=20=3D=20= SSL::Backend::OpenSSL->new();=0A+=09}=0A=20=09else=0A=20=09{=0A=20=09=09= die=20"SSL=20flavor=20$flavor=20unknown";=0A--=20=0A2.39.3=20(Apple=20= Git-146)=0A=0A= --Apple-Mail=_7839B5C3-1BEA-4263-93AF-FC26684DAF6A--