Thread

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Remove extra word from src/backend/optimizer/README

  1. [PATCH v7] GnuTLS support

    Peter Eisentraut <peter_e@gmx.net> — 2018-08-31T11:00:26Z

    ---
     configure                                     | 166 +++-
     configure.in                                  |  37 +-
     doc/src/sgml/client-auth.sgml                 |   2 +-
     doc/src/sgml/config.sgml                      |  69 +-
     doc/src/sgml/installation.sgml                |  47 +-
     doc/src/sgml/libpq.sgml                       |  54 +-
     doc/src/sgml/runtime.sgml                     |  13 +-
     doc/src/sgml/sslinfo.sgml                     |   1 +
     src/Makefile.global.in                        |   1 +
     src/backend/libpq/Makefile                    |   4 +-
     src/backend/libpq/be-secure-gnutls.c          | 795 +++++++++++++++++
     src/backend/libpq/be-secure-openssl.c         |   4 +-
     src/backend/libpq/be-secure.c                 |   3 +
     src/backend/libpq/hba.c                       |   2 +-
     src/backend/utils/misc/guc.c                  |  25 +
     src/backend/utils/misc/postgresql.conf.sample |  11 +-
     src/common/Makefile                           |   4 +-
     src/common/sha2_gnutls.c                      |  99 +++
     src/include/common/sha2.h                     |  14 +-
     src/include/libpq/libpq-be.h                  |  15 +-
     src/include/libpq/libpq.h                     |   1 +
     src/include/pg_config.h.in                    |  17 +
     src/include/pg_config_manual.h                |   2 +-
     src/interfaces/libpq/.gitignore               |   1 +
     src/interfaces/libpq/Makefile                 |  14 +-
     src/interfaces/libpq/fe-secure-gnutls.c       | 803 ++++++++++++++++++
     src/interfaces/libpq/fe-secure.c              |   2 +-
     src/interfaces/libpq/libpq-fe.h               |   2 +-
     src/interfaces/libpq/libpq-int.h              |  14 +-
     src/port/pg_strong_random.c                   |  18 +-
     src/test/Makefile                             |   2 +-
     src/test/ssl/Makefile                         |   7 +-
     src/test/ssl/ssl/server-password.key          |  35 +-
     src/test/ssl/t/001_ssltests.pl                |  63 +-
     src/test/ssl/t/002_scram.pl                   |   2 +-
     src/tools/msvc/Mkvcbuild.pm                   |  10 +
     src/tools/pgindent/typedefs.list              |   3 +
     37 files changed, 2248 insertions(+), 114 deletions(-)
     create mode 100644 src/backend/libpq/be-secure-gnutls.c
     create mode 100644 src/common/sha2_gnutls.c
     create mode 100644 src/interfaces/libpq/fe-secure-gnutls.c
    
    diff --git a/configure b/configure
    index dd94c5bbab..564f33ae5d 100755
    --- a/configure
    +++ b/configure
    @@ -707,6 +707,7 @@ UUID_EXTRA_OBJS
     with_uuid
     with_systemd
     with_selinux
    +with_gnutls
     with_openssl
     with_ldap
     with_krb_srvnam
    @@ -853,6 +854,7 @@ with_bsd_auth
     with_ldap
     with_bonjour
     with_openssl
    +with_gnutls
     with_selinux
     with_systemd
     with_readline
    @@ -1553,6 +1555,7 @@ Optional Packages:
       --with-ldap             build with LDAP support
       --with-bonjour          build with Bonjour support
       --with-openssl          build with OpenSSL support
    +  --with-gnutls           build with GnuTLS support
       --with-selinux          build with SELinux support
       --with-systemd          build with systemd support
       --without-readline      do not use GNU Readline nor BSD Libedit for editing
    @@ -7933,6 +7936,47 @@ fi
     $as_echo "$with_openssl" >&6; }
     
     
    +
    +#
    +# GnuTLS
    +#
    +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with GnuTLS support" >&5
    +$as_echo_n "checking whether to build with GnuTLS support... " >&6; }
    +
    +
    +
    +# Check whether --with-gnutls was given.
    +if test "${with_gnutls+set}" = set; then :
    +  withval=$with_gnutls;
    +  case $withval in
    +    yes)
    +
    +$as_echo "#define USE_GNUTLS 1" >>confdefs.h
    +
    +      ;;
    +    no)
    +      :
    +      ;;
    +    *)
    +      as_fn_error $? "no argument expected for --with-gnutls option" "$LINENO" 5
    +      ;;
    +  esac
    +
    +else
    +  with_gnutls=no
    +
    +fi
    +
    +
    +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_gnutls" >&5
    +$as_echo "$with_gnutls" >&6; }
    +
    +
    +if test "$with_openssl" = yes && test "$with_gnutls" = yes; then
    +  as_fn_error $? "cannot specify both --with-openssl and --with-gnutls" "$LINENO" 5
    +fi
    +
    +
     #
     # SELinux
     #
    @@ -12052,6 +12096,107 @@ done
     
     fi
     
    +if test "$with_gnutls" = yes ; then
    +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gnutls_init" >&5
    +$as_echo_n "checking for library containing gnutls_init... " >&6; }
    +if ${ac_cv_search_gnutls_init+:} false; then :
    +  $as_echo_n "(cached) " >&6
    +else
    +  ac_func_search_save_LIBS=$LIBS
    +cat confdefs.h - <<_ACEOF >conftest.$ac_ext
    +/* end confdefs.h.  */
    +
    +/* Override any GCC internal prototype to avoid an error.
    +   Use char because int might match the return type of a GCC
    +   builtin and then its argument prototype would still apply.  */
    +#ifdef __cplusplus
    +extern "C"
    +#endif
    +char gnutls_init ();
    +int
    +main ()
    +{
    +return gnutls_init ();
    +  ;
    +  return 0;
    +}
    +_ACEOF
    +for ac_lib in '' gnutls; do
    +  if test -z "$ac_lib"; then
    +    ac_res="none required"
    +  else
    +    ac_res=-l$ac_lib
    +    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
    +  fi
    +  if ac_fn_c_try_link "$LINENO"; then :
    +  ac_cv_search_gnutls_init=$ac_res
    +fi
    +rm -f core conftest.err conftest.$ac_objext \
    +    conftest$ac_exeext
    +  if ${ac_cv_search_gnutls_init+:} false; then :
    +  break
    +fi
    +done
    +if ${ac_cv_search_gnutls_init+:} false; then :
    +
    +else
    +  ac_cv_search_gnutls_init=no
    +fi
    +rm conftest.$ac_ext
    +LIBS=$ac_func_search_save_LIBS
    +fi
    +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gnutls_init" >&5
    +$as_echo "$ac_cv_search_gnutls_init" >&6; }
    +ac_res=$ac_cv_search_gnutls_init
    +if test "$ac_res" != no; then :
    +  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
    +
    +else
    +  as_fn_error $? "library 'gnutls' is required for GnuTLS" "$LINENO" 5
    +fi
    +
    +  # GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
    +  # certificate chains.
    +  ac_fn_c_check_decl "$LINENO" "GNUTLS_ALPN_SERVER_PRECEDENCE" "ac_cv_have_decl_GNUTLS_ALPN_SERVER_PRECEDENCE" "#include <gnutls/gnutls.h>
    +#include <gnutls/x509.h>
    +
    +"
    +if test "x$ac_cv_have_decl_GNUTLS_ALPN_SERVER_PRECEDENCE" = xyes; then :
    +  ac_have_decl=1
    +else
    +  ac_have_decl=0
    +fi
    +
    +cat >>confdefs.h <<_ACEOF
    +#define HAVE_DECL_GNUTLS_ALPN_SERVER_PRECEDENCE $ac_have_decl
    +_ACEOF
    +ac_fn_c_check_decl "$LINENO" "GNUTLS_X509_CRT_LIST_SORT" "ac_cv_have_decl_GNUTLS_X509_CRT_LIST_SORT" "#include <gnutls/gnutls.h>
    +#include <gnutls/x509.h>
    +
    +"
    +if test "x$ac_cv_have_decl_GNUTLS_X509_CRT_LIST_SORT" = xyes; then :
    +  ac_have_decl=1
    +else
    +  ac_have_decl=0
    +fi
    +
    +cat >>confdefs.h <<_ACEOF
    +#define HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT $ac_have_decl
    +_ACEOF
    +
    +  for ac_func in gnutls_pkcs11_set_pin_function
    +do :
    +  ac_fn_c_check_func "$LINENO" "gnutls_pkcs11_set_pin_function" "ac_cv_func_gnutls_pkcs11_set_pin_function"
    +if test "x$ac_cv_func_gnutls_pkcs11_set_pin_function" = xyes; then :
    +  cat >>confdefs.h <<_ACEOF
    +#define HAVE_GNUTLS_PKCS11_SET_PIN_FUNCTION 1
    +_ACEOF
    +
    +fi
    +done
    +
    +fi
    +
     if test "$with_pam" = yes ; then
       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
     $as_echo_n "checking for pam_start in -lpam... " >&6; }
    @@ -12954,6 +13099,17 @@ else
     fi
     
     
    +fi
    +
    +if test "$with_gnutls" = yes ; then
    +  ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default"
    +if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then :
    +
    +else
    +  as_fn_error $? "header file <gnutls/gnutls.h> is required for GnuTLS" "$LINENO" 5
    +fi
    +
    +
     fi
     
     if test "$with_pam" = yes ; then
    @@ -17835,9 +17991,11 @@ fi
     # in the template or configure command line.
     
     # If not selected manually, try to select a source automatically.
    -if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
    +if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_GNUTLS_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
       if test x"$with_openssl" = x"yes" ; then
         USE_OPENSSL_RANDOM=1
    +  elif test x"$with_gnutls" = x"yes" ; then
    +    USE_GNUTLS_RANDOM=1
       elif test "$PORTNAME" = "win32" ; then
         USE_WIN32_RANDOM=1
       else
    @@ -17876,6 +18034,12 @@ $as_echo "#define USE_OPENSSL_RANDOM 1" >>confdefs.h
     
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL" >&5
     $as_echo "OpenSSL" >&6; }
    +  elif test x"$USE_GNUTLS_RANDOM" = x"1" ; then
    +
    +$as_echo "#define USE_GNUTLS_RANDOM 1" >>confdefs.h
    +
    +    { $as_echo "$as_me:${as_lineno-$LINENO}: result: GnuTLS" >&5
    +$as_echo "GnuTLS" >&6; }
       elif test x"$USE_WIN32_RANDOM" = x"1" ; then
     
     $as_echo "#define USE_WIN32_RANDOM 1" >>confdefs.h
    diff --git a/configure.in b/configure.in
    index 3280afa0da..29b203673b 100644
    --- a/configure.in
    +++ b/configure.in
    @@ -822,6 +822,21 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
     AC_MSG_RESULT([$with_openssl])
     AC_SUBST(with_openssl)
     
    +
    +#
    +# GnuTLS
    +#
    +AC_MSG_CHECKING([whether to build with GnuTLS support])
    +PGAC_ARG_BOOL(with, gnutls, no, [build with GnuTLS support],
    +              [AC_DEFINE([USE_GNUTLS], 1, [Define to build with GnuTLS support. (--with-gnutls)])])
    +AC_MSG_RESULT([$with_gnutls])
    +AC_SUBST(with_gnutls)
    +
    +if test "$with_openssl" = yes && test "$with_gnutls" = yes; then
    +  AC_MSG_ERROR([cannot specify both --with-openssl and --with-gnutls])
    +fi
    +
    +
     #
     # SELinux
     #
    @@ -1190,6 +1205,17 @@ if test "$with_openssl" = yes ; then
       AC_CHECK_FUNCS([CRYPTO_lock])
     fi
     
    +if test "$with_gnutls" = yes ; then
    +  AC_SEARCH_LIBS(gnutls_init, gnutls, [], [AC_MSG_ERROR([library 'gnutls' is required for GnuTLS])])
    +  # GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
    +  # certificate chains.
    +  AC_CHECK_DECLS([GNUTLS_ALPN_SERVER_PRECEDENCE, GNUTLS_X509_CRT_LIST_SORT], [], [],
    +[#include <gnutls/gnutls.h>
    +#include <gnutls/x509.h>
    +])
    +  AC_CHECK_FUNCS([gnutls_pkcs11_set_pin_function])
    +fi
    +
     if test "$with_pam" = yes ; then
       AC_CHECK_LIB(pam,    pam_start, [], [AC_MSG_ERROR([library 'pam' is required for PAM])])
     fi
    @@ -1340,6 +1366,10 @@ if test "$with_openssl" = yes ; then
       AC_CHECK_HEADER(openssl/err.h, [], [AC_MSG_ERROR([header file <openssl/err.h> is required for OpenSSL])])
     fi
     
    +if test "$with_gnutls" = yes ; then
    +  AC_CHECK_HEADER(gnutls/gnutls.h, [], [AC_MSG_ERROR([header file <gnutls/gnutls.h> is required for GnuTLS])])
    +fi
    +
     if test "$with_pam" = yes ; then
       AC_CHECK_HEADERS(security/pam_appl.h, [],
                        [AC_CHECK_HEADERS(pam/pam_appl.h, [],
    @@ -2139,9 +2169,11 @@ fi
     # in the template or configure command line.
     
     # If not selected manually, try to select a source automatically.
    -if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
    +if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_GNUTLS_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
       if test x"$with_openssl" = x"yes" ; then
         USE_OPENSSL_RANDOM=1
    +  elif test x"$with_gnutls" = x"yes" ; then
    +    USE_GNUTLS_RANDOM=1
       elif test "$PORTNAME" = "win32" ; then
         USE_WIN32_RANDOM=1
       else
    @@ -2158,6 +2190,9 @@ if test "$enable_strong_random" = yes ; then
       if test x"$USE_OPENSSL_RANDOM" = x"1" ; then
         AC_DEFINE(USE_OPENSSL_RANDOM, 1, [Define to use OpenSSL for random number generation])
         AC_MSG_RESULT([OpenSSL])
    +  elif test x"$USE_GNUTLS_RANDOM" = x"1" ; then
    +    AC_DEFINE(USE_GNUTLS_RANDOM, 1, [Define to use GnuTLS for random number generation])
    +    AC_MSG_RESULT([GnuTLS])
       elif test x"$USE_WIN32_RANDOM" = x"1" ; then
         AC_DEFINE(USE_WIN32_RANDOM, 1, [Define to use native Windows API for random number generation])
         AC_MSG_RESULT([Windows native])
    diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
    index c2114021c3..6a7d5bc3bc 100644
    --- a/doc/src/sgml/client-auth.sgml
    +++ b/doc/src/sgml/client-auth.sgml
    @@ -1790,7 +1790,7 @@ <title>RADIUS Authentication</title>
              <para>
               The encryption vector used will only be cryptographically
               strong if <productname>PostgreSQL</productname> is built with support for
    -          <productname>OpenSSL</productname>. In other cases, the transmission to the
    +          an SSL library (e.g., <productname>OpenSSL</productname>). In other cases, the transmission to the
               RADIUS server should only be considered obfuscated, not secured, and
               external security measures should be applied if necessary.
              </para>
    diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
    index bee4afbe4e..e9e4d06b72 100644
    --- a/doc/src/sgml/config.sgml
    +++ b/doc/src/sgml/config.sgml
    @@ -1156,6 +1156,37 @@ <title>SSL</title>
           </listitem>
          </varlistentry>
     
    +     <varlistentry id="guc-ssl-dh-params-file" xreflabel="ssl_dh_params_file">
    +      <term><varname>ssl_dh_params_file</varname> (<type>string</type>)
    +      <indexterm>
    +       <primary><varname>ssl_dh_params_file</varname> configuration parameter</primary>
    +      </indexterm>
    +      </term>
    +      <listitem>
    +       <para>
    +        Specifies the name of the file containing Diffie-Hellman parameters
    +        used for so-called ephemeral DH family of SSL ciphers. The default is
    +        empty, in which case compiled-in default DH parameters used. Using
    +        custom DH parameters reduces the exposure if an attacker manages to
    +        crack the well-known compiled-in DH parameters. You can create your own
    +        DH parameters file with the command
    +        <command>openssl dhparam -out dhparams.pem 2048</command>.
    +       </para>
    +
    +       <para>
    +        This parameter can only be set in the <filename>postgresql.conf</filename>
    +        file or on the server command line.
    +       </para>
    +      </listitem>
    +     </varlistentry>
    +    </variablelist>
    +
    +    <para>
    +     The following settings are only applicable if the
    +     <productname>OpenSSL</productname> library is used.
    +    </para>
    +
    +    <variablelist>
          <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
           <term><varname>ssl_ciphers</varname> (<type>string</type>)
           <indexterm>
    @@ -1289,27 +1320,36 @@ <title>SSL</title>
            </para>
           </listitem>
          </varlistentry>
    +    </variablelist>
     
    -     <varlistentry id="guc-ssl-dh-params-file" xreflabel="ssl_dh_params_file">
    -      <term><varname>ssl_dh_params_file</varname> (<type>string</type>)
    +    <para>
    +     The following settings are only applicable if the
    +     <productname>GnuTLS</productname> library is used.
    +    </para>
    +
    +    <variablelist>
    +     <varlistentry id="guc-gnutls-priority" xreflabel="gnutls_priority">
    +      <term><varname>gnutls_priority</varname> (<type>string</type>)
           <indexterm>
    -       <primary><varname>ssl_dh_params_file</varname> configuration parameter</primary>
    +       <primary><varname>gnutls_priority</varname> configuration parameter</primary>
           </indexterm>
           </term>
           <listitem>
            <para>
    -        Specifies the name of the file containing Diffie-Hellman parameters
    -        used for so-called ephemeral DH family of SSL ciphers. The default is
    -        empty, in which case compiled-in default DH parameters used. Using
    -        custom DH parameters reduces the exposure if an attacker manages to
    -        crack the well-known compiled-in DH parameters. You can create your own
    -        DH parameters file with the command
    -        <command>openssl dhparam -out dhparams.pem 2048</command>.
    +        Sets the priorities for the cipher suites supported by GnuTLS.  This
    +        can be used to specify which SSL cipher suites are allowed to be
    +        used on secure connections and related settings.  See the
    +        documentation on GnuTLS priority strings for details.
    +        This parameter can only be set in the <filename>postgresql.conf</filename>
    +        file or on the server command line.
            </para>
     
            <para>
    -        This parameter can only be set in the <filename>postgresql.conf</filename>
    -        file or on the server command line.
    +        The default value is <literal>NORMAL:%SERVER_PRECEDENCE</literal> (or
    +        just <literal>NORMAL</literal> with older
    +        <productname>GnuTLS</productname> versions that don't support the
    +        server precedence setting).  The default is usually a reasonable
    +        choice unless you have specific security requirements.
            </para>
           </listitem>
          </varlistentry>
    @@ -1374,6 +1414,7 @@ <title>SSL</title>
           </listitem>
          </varlistentry>
         </variablelist>
    +
         </sect2>
        </sect1>
     
    @@ -8411,8 +8452,8 @@ <title>Preset Options</title>
            <para>
             Reports the name of the SSL library that this PostgreSQL server was
             built with (even if SSL is not currently configured or in use on this
    -        instance), for example <literal>OpenSSL</literal>, or an empty string
    -        if none.
    +        instance), for example <literal>OpenSSL</literal> or
    +        <literal>GnuTLS</literal>, or an empty string if none.
            </para>
           </listitem>
          </varlistentry>
    diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
    index 4487d0cfd1..e0231bdc5e 100644
    --- a/doc/src/sgml/installation.sgml
    +++ b/doc/src/sgml/installation.sgml
    @@ -244,9 +244,9 @@ <title>Requirements</title>
     
         <listitem>
          <para>
    -      You need <productname>OpenSSL</productname>, if you want to support
    -      encrypted client connections. The minimum required version is
    -      0.9.8.
    +      If you want to support encrypted client connections (SSL/TLS), you need
    +      <productname>OpenSSL</productname> (at least version 0.9.8) or
    +      <productname>GnuTLS</productname>.
          </para>
         </listitem>
     
    @@ -727,6 +727,29 @@ <title>Configuration</title>
            </listitem>
           </varlistentry>
     
    +      <varlistentry>
    +       <term><option>--with-gnutls</option>
    +       <indexterm>
    +        <primary>GnuTLS</primary>
    +        <seealso>SSL</seealso>
    +       </indexterm>
    +       </term>
    +       <listitem>
    +        <para>
    +         Build with support for <acronym>TLS</acronym>/<acronym>SSL</acronym>
    +         (encrypted) connections using the <productname>GnuTLS</productname>
    +         package.  <filename>configure</filename> will check for the required
    +         header files and libraries to make sure that your
    +         <productname>GnuTLS</productname> installation is sufficient before
    +         proceeding.
    +        </para>
    +
    +        <para>
    +         See also <option>--with-openssl</option> for an alternative.
    +        </para>
    +       </listitem>
    +      </varlistentry>
    +
           <varlistentry>
            <term><option>--with-gssapi</option></term>
            <listitem>
    @@ -833,12 +856,16 @@ <title>Configuration</title>
            </term>
            <listitem>
             <para>
    -         Build with support for <acronym>SSL</acronym> (encrypted)
    -         connections. This requires the <productname>OpenSSL</productname>
    -         package to be installed.  <filename>configure</filename> will check
    -         for the required header files and libraries to make sure that
    -         your <productname>OpenSSL</productname> installation is sufficient
    -         before proceeding.
    +         Build with support for <acronym>SSL</acronym>/<acronym>TLS</acronym>
    +         (encrypted) connections using the <productname>OpenSSL</productname>
    +         package.  <filename>configure</filename> will check for the required
    +         header files and libraries to make sure that your
    +         <productname>OpenSSL</productname> installation is sufficient before
    +         proceeding.
    +        </para>
    +
    +        <para>
    +         See also <option>--with-gnutls</option> for an alternative.
             </para>
            </listitem>
           </varlistentry>
    @@ -2370,7 +2397,7 @@ <title>Cygwin</title>
     
          <listitem>
           <para>
    -       OpenSSL is not supported.
    +       SSL is not supported.
           </para>
          </listitem>
     
    diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
    index 5e7931ba90..5ec3e78717 100644
    --- a/doc/src/sgml/libpq.sgml
    +++ b/doc/src/sgml/libpq.sgml
    @@ -1449,6 +1449,9 @@ <title>Parameter Key Words</title>
             does not support disabling compression, so this parameter is ignored
             with those versions, and whether compression is used depends on the
             server.
    +        <productname>GnuTLS</productname> does not support SSL compression,
    +        and this parameter will be ignored if <productname>libpq</productname>
    +        is using <productname>GnuTLS</productname>.
            </para>
     
            <para>
    @@ -2201,8 +2204,8 @@ <title>Connection Status Functions</title>
              <term><literal>library</literal></term>
               <listitem>
                <para>
    -            Name of the SSL implementation in use. (Currently, only
    -            <literal>"OpenSSL"</literal> is implemented)
    +            Name of the SSL implementation in use, e.g.,
    +            <literal>OpenSSL</literal> or <literal>GnuTLS</literal>
                </para>
               </listitem>
              </varlistentry>
    @@ -2271,11 +2274,11 @@ <title>Connection Status Functions</title>
     <synopsis>
     void *PQsslStruct(const PGconn *conn, const char *struct_name);
     </synopsis>
    +       The struct(s) available depend on the SSL implementation in use.
           </para>
           <para>
    -       The struct(s) available depend on the SSL implementation in use.
    -       For OpenSSL, there is one struct, available under the name "OpenSSL",
    -       and it returns a pointer to the OpenSSL <literal>SSL</literal> struct.
    +       For OpenSSL, there is one struct, available under the name <literal>"OpenSSL"</literal>,
    +       and it returns a pointer to the OpenSSL <type>SSL</type> struct.
            To use this function, code along the following lines could be used:
     <programlisting><![CDATA[
     #include <libpq-fe.h>
    @@ -2294,12 +2297,31 @@ <title>Connection Status Functions</title>
             /* use OpenSSL functions to access ssl */
         }
     ]]></programlisting>
    -      </para>
    -      <para>
            This structure can be used to verify encryption levels, check server
            certificates, and more. Refer to the <productname>OpenSSL</productname>
            documentation for information about this structure.
           </para>
    +      <para>
    +       Similarly, for GnuTLS, the <type>gnutls_session_t</type> can be
    +       obtained under the name <literal>"GnuTLS"</literal>. For example:
    +<programlisting><![CDATA[
    +#include <libpq-fe.h>
    +#include <gnutls/gnutls.h>
    +
    +...
    +
    +    gnutls_session_t ssl;
    +
    +    dbconn = PQconnectdb(...);
    +    ...
    +
    +    ssl = PQsslStruct(dbconn, "GnuTLS");
    +    if (ssl)
    +    {
    +        /* use GnuTLS functions to access ssl */
    +    }
    +    ]]></programlisting>
    +      </para>
          </listitem>
         </varlistentry>
     
    @@ -7697,6 +7719,7 @@ <title>SSL Support</title>
       </para>
     
       <para>
    +   When <productname>OpenSSL</productname> is used, then
        <application>libpq</application> reads the system-wide
        <productname>OpenSSL</productname> configuration file. By default, this
        file is named <filename>openssl.cnf</filename> and is located in the
    @@ -8070,6 +8093,13 @@ <title>SSL Library Initialization</title>
        for details on the SSL API.
       </para>
     
    +  <para>
    +   If you are using <productname>OpenSSL</productname> version 1.1.0 or later
    +   and use initialization function <function>OPENSSL_init_ssl()</function> or
    +   the implicit initialization, then you don't need to do this.  Similarly,
    +   this is not necessary with <productname>GnuTLS</productname>.
    +  </para>
    +
       <para>
        <variablelist>
         <varlistentry id="libpq-pqinitopenssl">
    @@ -8105,6 +8135,10 @@ <title>SSL Library Initialization</title>
            before first opening a database connection.  Also be sure that you
            have done that initialization before opening a database connection.
           </para>
    +
    +      <para>
    +       With <productname>GnuTLS</productname>, this function does nothing.
    +      </para>
          </listitem>
         </varlistentry>
     
    @@ -8125,12 +8159,16 @@ <title>SSL Library Initialization</title>
           </para>
     
           <para>
    -       This function is equivalent to
    +       With <productname>OpenSSL</productname>, this function is equivalent to
            <literal>PQinitOpenSSL(do_ssl, do_ssl)</literal>.
            It is sufficient for applications that initialize both or neither
            of <application>OpenSSL</application> and <literal>libcrypto</literal>.
           </para>
     
    +      <para>
    +       With <productname>GnuTLS</productname>, this function does nothing.
    +      </para>
    +
           <para>
            <function>PQinitSSL</function> has been present since
            <productname>PostgreSQL</productname> 8.0, while <function>PQinitOpenSSL</function>
    diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
    index 8d9d40664b..8b8201f89f 100644
    --- a/doc/src/sgml/runtime.sgml
    +++ b/doc/src/sgml/runtime.sgml
    @@ -2167,10 +2167,14 @@ <title>Secure TCP/IP Connections with SSL</title>
       <para>
        <productname>PostgreSQL</productname> has native support for using
        <acronym>SSL</acronym> connections to encrypt client/server communications
    -   for increased security. This requires that
    -   <productname>OpenSSL</productname> is installed on both client and
    -   server systems and that support in <productname>PostgreSQL</productname> is
    -   enabled at build time (see <xref linkend="installation"/>).
    +   for increased security.  This requires that support in
    +   <productname>PostgreSQL</productname> is enabled at build time (see <xref
    +   linkend="installation"/>).  <productname>PostgreSQL</productname> supports
    +   both <productname>OpenSSL</productname> and
    +   <productname>GnuTLS</productname> as the SSL implementation.  This is
    +   chosen at build time.  Third-party clients may also use other SSL
    +   implementations.  Clients and servers using different SSL implementations
    +   are compatible.
       </para>
     
       <sect2 id="ssl-setup">
    @@ -2249,6 +2253,7 @@ <title>Basic Setup</title>
        <title>OpenSSL Configuration</title>
     
       <para>
    +   When using <productname>OpenSSL</productname>,
        <productname>PostgreSQL</productname> reads the system-wide
        <productname>OpenSSL</productname> configuration file. By default, this
        file is named <filename>openssl.cnf</filename> and is located in the
    diff --git a/doc/src/sgml/sslinfo.sgml b/doc/src/sgml/sslinfo.sgml
    index cda09aaafd..782d63fd40 100644
    --- a/doc/src/sgml/sslinfo.sgml
    +++ b/doc/src/sgml/sslinfo.sgml
    @@ -17,6 +17,7 @@ <title>sslinfo</title>
      <para>
       This extension won't build at all unless the installation was
       configured with <literal>--with-openssl</literal>.
    +  (GnuTLS is currently not supported.)
      </para>
     
      <sect2>
    diff --git a/src/Makefile.global.in b/src/Makefile.global.in
    index 91d7cb83dc..e9c3eed337 100644
    --- a/src/Makefile.global.in
    +++ b/src/Makefile.global.in
    @@ -184,6 +184,7 @@ with_icu	= @with_icu@
     with_perl	= @with_perl@
     with_python	= @with_python@
     with_tcl	= @with_tcl@
    +with_gnutls	= @with_gnutls@
     with_openssl	= @with_openssl@
     with_selinux	= @with_selinux@
     with_systemd	= @with_systemd@
    diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
    index 3dbec23e30..c3720539d3 100644
    --- a/src/backend/libpq/Makefile
    +++ b/src/backend/libpq/Makefile
    @@ -17,7 +17,9 @@ include $(top_builddir)/src/Makefile.global
     OBJS = be-fsstubs.o be-secure.o be-secure-common.o auth.o crypt.o hba.o ifaddr.o pqcomm.o \
            pqformat.o pqmq.o pqsignal.o auth-scram.o
     
    -ifeq ($(with_openssl),yes)
    +ifeq ($(with_gnutls),yes)
    +OBJS += be-secure-gnutls.o
    +else ifeq ($(with_openssl),yes)
     OBJS += be-secure-openssl.o
     endif
     
    diff --git a/src/backend/libpq/be-secure-gnutls.c b/src/backend/libpq/be-secure-gnutls.c
    new file mode 100644
    index 0000000000..24fcfa500c
    --- /dev/null
    +++ b/src/backend/libpq/be-secure-gnutls.c
    @@ -0,0 +1,795 @@
    +/*-------------------------------------------------------------------------
    + *
    + * be-secure-gnutls.c
    + *	  functions for GnuTLS support in the backend.
    + *
    + *
    + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
    + * Portions Copyright (c) 1994, Regents of the University of California
    + *
    + *
    + * IDENTIFICATION
    + *	  src/backend/libpq/be-secure-gnutls.c
    + *
    + *-------------------------------------------------------------------------
    + */
    +
    +#include "postgres.h"
    +
    +#include <sys/stat.h>
    +#include <signal.h>
    +#include <fcntl.h>
    +#include <ctype.h>
    +#include <sys/socket.h>
    +#include <unistd.h>
    +#include <netdb.h>
    +#include <netinet/in.h>
    +#ifdef HAVE_NETINET_TCP_H
    +#include <netinet/tcp.h>
    +#include <arpa/inet.h>
    +#endif
    +#include <gnutls/x509.h>
    +#include <gnutls/pkcs11.h>
    +
    +#include "libpq/libpq.h"
    +#include "miscadmin.h"
    +#include "pgstat.h"
    +#include "storage/fd.h"
    +#include "storage/latch.h"
    +#include "tcop/tcopprot.h"
    +#include "utils/memutils.h"
    +
    +
    +static ssize_t my_sock_read(gnutls_transport_ptr_t h, void *buf, size_t size);
    +static ssize_t my_sock_write(gnutls_transport_ptr_t h, const void *buf, size_t size);
    +static int	get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t *peer);
    +static bool load_dh_file(gnutls_dh_params_t dh, char *filename, bool isServerStart);
    +static bool load_dh_buffer(gnutls_dh_params_t dh, const char *, size_t, bool isServerStart);
    +#ifdef HAVE_GNUTLS_PKCS11_SET_PIN_FUNCTION
    +static int pin_function(void *userdata, int attempt,
    +			 const char *token_url,
    +			 const char *token_label,
    +			 unsigned int flags,
    +			 char *pin, size_t pin_max);
    +static int dummy_pin_function(void *userdata, int attempt,
    +				   const char *token_url,
    +				   const char *token_label,
    +				   unsigned int flags,
    +				   char *pin, size_t pin_max);
    +#endif
    +static int	verify_cb(gnutls_session_t ssl);
    +static bool initialize_dh(gnutls_dh_params_t *dh_params, bool isServerStart);
    +
    +static gnutls_certificate_credentials_t tls_credentials = NULL;
    +static gnutls_dh_params_t tls_dh_params = NULL;
    +static gnutls_priority_t tls_priority = NULL;
    +static bool tls_initialized = false;
    +static bool pin_function_called = false;
    +static bool ssl_is_server_start;
    +
    +
    +/* ------------------------------------------------------------ */
    +/*						 Public interface						*/
    +/* ------------------------------------------------------------ */
    +
    +int
    +be_tls_init(bool isServerStart)
    +{
    +	gnutls_certificate_credentials_t credentials = NULL;
    +	gnutls_priority_t priority = NULL;
    +	gnutls_dh_params_t dh_params = NULL;
    +	int			ret;
    +	const char *err_pos;
    +
    +	/* This stuff need be done only once. */
    +	if (!tls_initialized)
    +	{
    +		gnutls_global_init();
    +		tls_initialized = true;
    +	}
    +
    +	ret = gnutls_certificate_allocate_credentials(&credentials);
    +	if (ret < 0)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errmsg("could not create SSL credentials: %s",
    +						gnutls_strerror(ret))));
    +		goto error;
    +	}
    +
    +	/*
    +	 * Set PIN (passphrase) callback.  Note that there is no default for this
    +	 * in GnuTLS.
    +	 *
    +	 * If reloading, substitute a dummy callback, because we don't want to
    +	 * prompt for a passphrase in an already-running server.
    +	 */
    +#ifdef HAVE_GNUTLS_PKCS11_SET_PIN_FUNCTION
    +	if (isServerStart)
    +		gnutls_pkcs11_set_pin_function(pin_function, NULL);
    +	else
    +		gnutls_pkcs11_set_pin_function(dummy_pin_function, NULL);
    +#endif
    +	/* used by the callback */
    +	ssl_is_server_start = isServerStart;
    +
    +	/*
    +	 * Load and verify server's certificate and private key
    +	 */
    +	if (!check_ssl_key_file_permissions(ssl_key_file, isServerStart))
    +		goto error;
    +
    +	pin_function_called = false;
    +
    +	ret = gnutls_certificate_set_x509_key_file(credentials, ssl_cert_file, ssl_key_file, GNUTLS_X509_FMT_PEM);
    +	if (ret < 0)
    +	{
    +		if (pin_function_called)
    +			ereport(isServerStart ? FATAL : LOG,
    +					(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +					 errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
    +							ssl_key_file)));
    +		else
    +			ereport(isServerStart ? FATAL : LOG,
    +					(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +					 errmsg("could not load server certificate \"%s\" or key file \"%s\": %s",
    +							ssl_cert_file, ssl_key_file, gnutls_strerror(ret))));
    +		goto error;
    +	}
    +
    +	/* set up ephemeral DH keys */
    +	if (!initialize_dh(&dh_params, isServerStart))
    +		goto error;
    +
    +	gnutls_certificate_set_dh_params(credentials, dh_params);
    +
    +	/* set up the allowed cipher list */
    +	ret = gnutls_priority_init(&priority, gnutls_priority, &err_pos);
    +	if (ret < 0)
    +	{
    +		if (ret == GNUTLS_E_INVALID_REQUEST)
    +			ereport(isServerStart ? FATAL : LOG,
    +					(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +					 errmsg("could not set the cipher list: syntax error at %s", err_pos)));
    +		else
    +			ereport(isServerStart ? FATAL : LOG,
    +					(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +					 errmsg("could not set the cipher list: %s", gnutls_strerror(ret))));
    +		goto error;
    +	}
    +
    +	/*
    +	 * Load CA store, so we can verify client certificates if needed.
    +	 */
    +	if (ssl_ca_file[0])
    +	{
    +		ret = gnutls_certificate_set_x509_trust_file(credentials, ssl_ca_file, GNUTLS_X509_FMT_PEM);
    +		if (ret < 0)
    +		{
    +			ereport(isServerStart ? FATAL : LOG,
    +					(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +					 errmsg("could not load root certificate file \"%s\": %s",
    +							ssl_ca_file, gnutls_strerror(ret))));
    +			goto error;
    +		}
    +
    +		gnutls_certificate_set_verify_function(credentials, verify_cb);
    +	}
    +
    +	/*
    +	 * Load the Certificate Revocation List (CRL).
    +	 */
    +	if (ssl_crl_file[0])
    +	{
    +		ret = gnutls_certificate_set_x509_crl_file(credentials, ssl_crl_file, GNUTLS_X509_FMT_PEM);
    +		if (ret < 0)
    +		{
    +			ereport(isServerStart ? FATAL : LOG,
    +					(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +					 errmsg("could not load SSL certificate revocation list file \"%s\": %s",
    +							ssl_crl_file, gnutls_strerror(ret))));
    +			goto error;
    +		}
    +	}
    +
    +	/*
    +	 * Success!  Replace any existing credentials.
    +	 */
    +	if (tls_credentials)
    +		gnutls_certificate_free_credentials(tls_credentials);
    +	if (tls_priority)
    +		gnutls_priority_deinit(tls_priority);
    +	if (tls_dh_params)
    +		gnutls_dh_params_deinit(tls_dh_params);
    +
    +	tls_credentials = credentials;
    +	tls_priority = priority;
    +	tls_dh_params = dh_params;
    +
    +	/*
    +	 * Set flag to remember whether CA store has been loaded.
    +	 */
    +	if (ssl_ca_file[0])
    +		ssl_loaded_verify_locations = true;
    +	else
    +		ssl_loaded_verify_locations = false;
    +
    +	return 0;
    +
    +error:
    +	if (credentials)
    +		gnutls_certificate_free_credentials(credentials);
    +	if (priority)
    +		gnutls_priority_deinit(priority);
    +	if (dh_params)
    +		gnutls_dh_params_deinit(dh_params);
    +	return -1;
    +}
    +
    +void
    +be_tls_destroy(void)
    +{
    +	if (tls_credentials)
    +		gnutls_certificate_free_credentials(tls_credentials);
    +	if (tls_priority)
    +		gnutls_priority_deinit(tls_priority);
    +	if (tls_dh_params)
    +		gnutls_dh_params_deinit(tls_dh_params);
    +	tls_credentials = NULL;
    +	tls_priority = NULL;
    +	tls_dh_params = NULL;
    +	ssl_loaded_verify_locations = false;
    +}
    +
    +int
    +be_tls_open_server(Port *port)
    +{
    +	int			ret;
    +
    +	Assert(!port->ssl);
    +	Assert(!port->peer);
    +
    +	if (!tls_credentials || !tls_priority)
    +	{
    +		ereport(COMMERROR,
    +				(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +				 errmsg("could not initialize SSL connection: SSL context not set up")));
    +		return -1;
    +	}
    +
    +	ret = gnutls_init(&port->ssl, GNUTLS_SERVER);
    +	if (ret < 0)
    +	{
    +		ereport(COMMERROR,
    +				(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +				 errmsg("could not initialize SSL connection: %s",
    +						gnutls_strerror(ret))));
    +		return -1;
    +	}
    +
    +	gnutls_transport_set_ptr(port->ssl, port);
    +	gnutls_transport_set_pull_function(port->ssl, my_sock_read);
    +	gnutls_transport_set_push_function(port->ssl, my_sock_write);
    +
    +	ret = gnutls_priority_set(port->ssl, tls_priority);
    +	if (ret < 0)
    +	{
    +		ereport(COMMERROR,
    +				(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +				 errmsg("could not initialize SSL connection: %s",
    +						gnutls_strerror(ret))));
    +		return -1;
    +	}
    +
    +	ret = gnutls_credentials_set(port->ssl, GNUTLS_CRD_CERTIFICATE, tls_credentials);
    +	if (ret < 0)
    +	{
    +		ereport(COMMERROR,
    +				(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +				 errmsg("could not initialize SSL connection: %s",
    +						gnutls_strerror(ret))));
    +		return -1;
    +	}
    +
    +	if (ssl_loaded_verify_locations)
    +		gnutls_certificate_server_set_request(port->ssl, GNUTLS_CERT_REQUEST);
    +
    +	port->ssl_in_use = true;
    +
    +	do
    +	{
    +		ret = gnutls_handshake(port->ssl);
    +	}
    +	while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
    +
    +	if (ret < 0)
    +	{
    +		ereport(COMMERROR,
    +				(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +				 errmsg("could not accept SSL connection: %s",
    +						gnutls_strerror(ret))));
    +		return -1;
    +	}
    +
    +	/* Get client certificate, if available. */
    +	ret = get_peer_certificate(port->ssl, &port->peer);
    +	if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND)
    +	{
    +		ereport(COMMERROR,
    +				(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +				 errmsg("could not load peer certificates: %s",
    +						gnutls_strerror(ret))));
    +	}
    +
    +	/* and extract the Common Name from it. */
    +	port->peer_cn = NULL;
    +	port->peer_cert_valid = false;
    +	if (port->peer != NULL)
    +	{
    +		size_t		len = 0;
    +
    +		gnutls_x509_crt_get_dn_by_oid(port->peer,
    +									  GNUTLS_OID_X520_COMMON_NAME,
    +									  0, 0, NULL, &len);
    +
    +		if (len > 0)
    +		{
    +			char	   *peer_cn;
    +
    +			peer_cn = MemoryContextAlloc(TopMemoryContext, len);
    +
    +			ret = gnutls_x509_crt_get_dn_by_oid(port->peer,
    +												GNUTLS_OID_X520_COMMON_NAME,
    +												0, 0, peer_cn, &len);
    +
    +			if (ret != 0)
    +			{
    +				/* shouldn't happen */
    +				pfree(peer_cn);
    +				return -1;
    +			}
    +
    +			/*
    +			 * Reject embedded NULLs in certificate common name to prevent
    +			 * attacks like CVE-2009-4034.
    +			 */
    +			if (len != strlen(peer_cn))
    +			{
    +				ereport(COMMERROR,
    +						(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +						 errmsg("SSL certificate's common name contains embedded null")));
    +				pfree(peer_cn);
    +				return -1;
    +			}
    +
    +
    +			if (ret == 0)
    +				port->peer_cn = peer_cn;
    +			else
    +				pfree(peer_cn);
    +
    +		}
    +
    +		port->peer_cert_valid = true;
    +	}
    +
    +	return 0;
    +}
    +
    +void
    +be_tls_close(Port *port)
    +{
    +	if (port->ssl)
    +	{
    +		gnutls_bye(port->ssl, GNUTLS_SHUT_RDWR);
    +		gnutls_deinit(port->ssl);
    +		port->ssl = NULL;
    +		port->ssl_in_use = false;
    +	}
    +
    +	if (port->peer)
    +	{
    +		gnutls_x509_crt_deinit(port->peer);
    +		port->peer = NULL;
    +	}
    +
    +	if (port->peer_cn)
    +	{
    +		pfree(port->peer_cn);
    +		port->peer_cn = NULL;
    +	}
    +}
    +
    +ssize_t
    +be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
    +{
    +	ssize_t		n;
    +
    +	n = gnutls_record_recv(port->ssl, ptr, len);
    +
    +	if (n > 0)
    +		return n;
    +
    +	switch (n)
    +	{
    +		case 0:
    +
    +			/*
    +			 * the SSL connnection was closed, leave it to the caller to
    +			 * ereport it
    +			 */
    +			errno = ECONNRESET;
    +			n = -1;
    +			break;
    +		case GNUTLS_E_AGAIN:
    +		case GNUTLS_E_INTERRUPTED:
    +			*waitfor = WL_SOCKET_READABLE;
    +			errno = EWOULDBLOCK;
    +			n = -1;
    +			break;
    +		default:
    +			ereport(COMMERROR,
    +					(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +					 errmsg("SSL error: %s",
    +							gnutls_strerror(n))));
    +			errno = ECONNRESET;
    +			n = -1;
    +			break;
    +	}
    +
    +	return n;
    +}
    +
    +ssize_t
    +be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
    +{
    +	ssize_t		n;
    +
    +	n = gnutls_record_send(port->ssl, ptr, len);
    +
    +	if (n >= 0)
    +		return n;
    +
    +	switch (n)
    +	{
    +		case GNUTLS_E_AGAIN:
    +		case GNUTLS_E_INTERRUPTED:
    +			*waitfor = WL_SOCKET_WRITEABLE;
    +			errno = EWOULDBLOCK;
    +			n = -1;
    +			break;
    +		default:
    +			ereport(COMMERROR,
    +					(errcode(ERRCODE_PROTOCOL_VIOLATION),
    +					 errmsg("SSL error: %s",
    +							gnutls_strerror(n))));
    +			errno = ECONNRESET;
    +			n = -1;
    +			break;
    +	}
    +
    +	return n;
    +}
    +
    +/* ------------------------------------------------------------ */
    +/*						Internal functions						*/
    +/* ------------------------------------------------------------ */
    +
    +/*
    + * Private substitute transport layer: this does the sending and receiving
    + * using send() and recv() instead. This is so that we can enable and disable
    + * interrupts just while calling recv(). We cannot have interrupts occurring
    + * while the bulk of GnuTLS runs, because it uses malloc() and possibly other
    + * non-reentrant libc facilities. We also need to call send() and recv()
    + * directly so it gets passed through the socket/signals layer on Win32.
    + */
    +
    +static ssize_t
    +my_sock_read(gnutls_transport_ptr_t port, void *buf, size_t size)
    +{
    +	return secure_raw_read((Port *) port, buf, size);
    +}
    +
    +static ssize_t
    +my_sock_write(gnutls_transport_ptr_t port, const void *buf, size_t size)
    +{
    +	return secure_raw_write((Port *) port, buf, size);
    +}
    +
    +#if !HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT
    +/*
    + * GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
    + * certificate chains, so we skip doing so in these earlier versions.
    + */
    +#define GNUTLS_X509_CRT_LIST_SORT 0
    +#endif
    +
    +/*
    + *	Get peer certificate from a session
    + *
    + *	Returns GNUTLS_E_NO_CERTIFICATE_FOUND when not x509 certifcate was found.
    + */
    +static int
    +get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t *peer)
    +{
    +	if (gnutls_certificate_type_get(ssl) == GNUTLS_CRT_X509)
    +	{
    +		unsigned int n;
    +		int			ret;
    +		gnutls_datum_t const *raw_certs;
    +		gnutls_x509_crt_t *certs;
    +
    +		raw_certs = gnutls_certificate_get_peers(ssl, &n);
    +
    +		if (n == 0)
    +			return GNUTLS_E_NO_CERTIFICATE_FOUND;
    +
    +		certs = palloc(n * sizeof(gnutls_x509_crt_t));
    +
    +		ret = gnutls_x509_crt_list_import(certs, &n, raw_certs,
    +										  GNUTLS_X509_FMT_DER,
    +										  GNUTLS_X509_CRT_LIST_SORT);
    +
    +		if (ret >= 1)
    +		{
    +			unsigned int i;
    +
    +			for (i = 1; i < ret; i++)
    +				gnutls_x509_crt_deinit(certs[i]);
    +
    +			*peer = certs[0];
    +
    +			ret = GNUTLS_E_SUCCESS;
    +		}
    +		else if (ret == 0)
    +			ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
    +
    +		pfree(certs);
    +
    +		return ret;
    +	}
    +
    +	return GNUTLS_E_NO_CERTIFICATE_FOUND;
    +}
    +
    +#define MAX_DH_FILE_SIZE 10240
    +
    +/*
    + *	Load precomputed DH parameters.
    + *
    + *	To prevent "downgrade" attacks, we perform a number of checks
    + *	to verify that the DBA-generated DH parameters file contains
    + *	what we expect it to contain.
    + */
    +static bool
    +load_dh_file(gnutls_dh_params_t dh_params, char *filename, bool isServerStart)
    +{
    +	FILE	   *fp;
    +	char		buffer[MAX_DH_FILE_SIZE];
    +	gnutls_datum_t datum = {(unsigned char *) buffer};
    +	int			ret;
    +
    +	/* attempt to open file.  It's not an error if it doesn't exist. */
    +	if ((fp = AllocateFile(filename, "r")) == NULL)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errcode_for_file_access(),
    +				 errmsg("could not open DH parameters file \"%s\": %m",
    +						filename)));
    +		return false;
    +	}
    +
    +	datum.size = fread(buffer, sizeof(buffer[0]), sizeof(buffer), fp);
    +
    +	FreeFile(fp);
    +
    +	if (datum.size < 0)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errcode_for_file_access(),
    +				 errmsg("could not load DH parameters file: %s",
    +						gnutls_strerror(ret))));
    +		return false;
    +	}
    +
    +	ret = gnutls_dh_params_import_pkcs3(dh_params, &datum, GNUTLS_X509_FMT_PEM);
    +
    +	if (ret < 0)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +				 errmsg("could not load DH parameters file: %s",
    +						gnutls_strerror(ret))));
    +		return false;
    +	}
    +
    +	return true;
    +}
    +
    +/*
    + *	Load hardcoded DH parameters.
    + *
    + *	To prevent problems if the DH parameters files don't even
    + *	exist, we can load DH parameters hardcoded into this file.
    + */
    +static bool
    +load_dh_buffer(gnutls_dh_params_t dh_params, const char *buffer, size_t len, bool isServerStart)
    +{
    +	gnutls_datum_t datum = {(unsigned char *) buffer, len};
    +	int			ret;
    +
    +	ret = gnutls_dh_params_import_pkcs3(dh_params, &datum, GNUTLS_X509_FMT_PEM);
    +
    +	if (ret < 0)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errmsg_internal("DH load buffer: %s", gnutls_strerror(ret))));
    +		return false;
    +	}
    +
    +	return true;
    +}
    +
    +#ifdef HAVE_GNUTLS_PKCS11_SET_PIN_FUNCTION
    +/*
    + * PIN callback
    + */
    +static int
    +pin_function(void *userdata, int attempt,
    +			 const char *token_url,
    +			 const char *token_label,
    +			 unsigned int flags,
    +			 char *pin, size_t pin_max)
    +{
    +	const char *prompt = token_url;
    +
    +	if (ssl_passphrase_command[0])
    +		run_ssl_passphrase_command(prompt, ssl_is_server_start, pin, pin_max);
    +	else
    +		simple_prompt(prompt, pin, pin_max, false);
    +
    +	return 0;
    +}
    +
    +/*
    + * Dummy PIN callback during server reload
    + *
    + * The standard callback is no good during a postmaster SIGHUP cycle, not to
    + * mention SSL context reload in an EXEC_BACKEND postmaster child.  So
    + * override it with this dummy function that just returns an error,
    + * guaranteeing failure.
    + */
    +static int
    +dummy_pin_function(void *userdata, int attempt,
    +				   const char *token_url,
    +				   const char *token_label,
    +				   unsigned int flags,
    +				   char *pin, size_t pin_max)
    +{
    +	/* Set flag to change the error message we'll report */
    +	pin_function_called = true;
    +	return -1;
    +}
    +#endif
    +
    +/*
    + *	Certificate verification callback
    + *
    + *	This callback is where we verify the identity of the client.
    + */
    +static int
    +verify_cb(gnutls_session_t ssl)
    +{
    +	unsigned int status;
    +	int			ret;
    +
    +	ret = gnutls_certificate_verify_peers2(ssl, &status);
    +
    +	if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
    +		return 0;
    +	else if (ret < 0)
    +		return ret;
    +
    +	return status;
    +}
    +
    +/*
    + * Set DH parameters for generating ephemeral DH keys.  The
    + * DH parameters can take a long time to compute, so they must be
    + * precomputed.
    + *
    + * Since few sites will bother to create a parameter file, we also
    + * also provide a fallback to the parameters provided by the
    + * OpenSSL project.
    + *
    + * These values can be static (once loaded or computed) since the
    + * OpenSSL library can efficiently generate random keys from the
    + * information provided.
    + */
    +static bool
    +initialize_dh(gnutls_dh_params_t *dh_params, bool isServerStart)
    +{
    +	bool		loaded = false;
    +	int			ret;
    +
    +	ret = gnutls_dh_params_init(dh_params);
    +	if (ret < 0)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errmsg_internal("DH init error: %s",
    +								 gnutls_strerror(ret))));
    +		return false;
    +	}
    +
    +	if (ssl_dh_params_file[0])
    +		loaded = load_dh_file(*dh_params, ssl_dh_params_file, isServerStart);
    +	if (!loaded)
    +		loaded = load_dh_buffer(*dh_params, FILE_DH2048, sizeof(FILE_DH2048), isServerStart);
    +	if (!loaded)
    +	{
    +		ereport(isServerStart ? FATAL : LOG,
    +				(errcode(ERRCODE_CONFIG_FILE_ERROR),
    +				 (errmsg("DH: could not load DH parameters"))));
    +		return false;
    +	}
    +
    +	return true;
    +}
    +
    +int
    +be_tls_get_cipher_bits(Port *port)
    +{
    +	if (port->ssl)
    +		return gnutls_cipher_get_key_size(gnutls_cipher_get(port->ssl)) * 8;
    +	else
    +		return 0;
    +}
    +
    +bool
    +be_tls_get_compression(Port *port)
    +{
    +	if (port->ssl)
    +	{
    +		gnutls_compression_method_t comp = gnutls_compression_get(port->ssl);
    +
    +		return comp != GNUTLS_COMP_UNKNOWN && comp != GNUTLS_COMP_NULL;
    +	}
    +	else
    +		return false;
    +}
    +
    +const char *
    +be_tls_get_version(Port *port)
    +{
    +	if (port->ssl)
    +		return gnutls_protocol_get_name(gnutls_protocol_get_version(port->ssl));
    +	else
    +		return NULL;
    +}
    +
    +const char *
    +be_tls_get_cipher(Port *port)
    +{
    +	if (port->ssl)
    +		return gnutls_cipher_get_name(gnutls_cipher_get(port->ssl));
    +	else
    +		return NULL;
    +}
    +
    +void
    +be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
    +{
    +	if (port->peer)
    +	{
    +		int			ret;
    +
    +		ret = gnutls_x509_crt_get_dn_by_oid(port->peer,
    +											GNUTLS_OID_X520_COMMON_NAME,
    +											0, 0, ptr, &len);
    +
    +		if (ret != 0)
    +			ptr[0] = '\0';
    +	}
    +	else
    +		ptr[0] = '\0';
    +}
    diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
    index 1b659a5870..5fd58a2f38 100644
    --- a/src/backend/libpq/be-secure-openssl.c
    +++ b/src/backend/libpq/be-secure-openssl.c
    @@ -229,10 +229,8 @@ be_tls_init(bool isServerStart)
     		}
     	}
     
    -	/*----------
    +	/*
     	 * Load the Certificate Revocation List (CRL).
    -	 * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
    -	 *----------
     	 */
     	if (ssl_crl_file[0])
     	{
    diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
    index d349d7c2c7..6a4aa32e74 100644
    --- a/src/backend/libpq/be-secure.c
    +++ b/src/backend/libpq/be-secure.c
    @@ -60,6 +60,9 @@ char	   *SSLECDHCurve;
     /* GUC variable: if false, prefer client ciphers */
     bool		SSLPreferServerCiphers;
     
    +/* GUC variable controlling GnuTLS priorities */
    +char	   *gnutls_priority;
    +
     /* ------------------------------------------------------------ */
     /*			 Procedures common to all secure sessions			*/
     /* ------------------------------------------------------------ */
    diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
    index 1a65ec87bd..f34559dc7d 100644
    --- a/src/backend/libpq/hba.c
    +++ b/src/backend/libpq/hba.c
    @@ -1016,7 +1016,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
     			ereport(elevel,
     					(errcode(ERRCODE_CONFIG_FILE_ERROR),
     					 errmsg("hostssl record cannot match because SSL is not supported by this build"),
    -					 errhint("Compile with --with-openssl to use SSL connections."),
    +					 errhint("Compile with --with-gnutls or --with-openssl to use SSL connections."),
     					 errcontext("line %d of configuration file \"%s\"",
     								line_num, HbaFileName)));
     			*err_msg = "hostssl record cannot match because SSL is not supported by this build";
    diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
    index 0625eff219..567392398f 100644
    --- a/src/backend/utils/misc/guc.c
    +++ b/src/backend/utils/misc/guc.c
    @@ -3731,7 +3731,13 @@ static struct config_string ConfigureNamesString[] =
     		},
     		&ssl_library,
     #ifdef USE_SSL
    +#if defined(USE_OPENSSL)
     		"OpenSSL",
    +#elif defined(USE_GNUTLS)
    +		"GnuTLS",
    +#else
    +#error SSL implementation must set ssl_library string
    +#endif
     #else
     		"",
     #endif
    @@ -3840,6 +3846,25 @@ static struct config_string ConfigureNamesString[] =
     		NULL, NULL, NULL
     	},
     
    +	{
    +		{"gnutls_priority", PGC_SIGHUP, CONN_AUTH_SSL,
    +			gettext_noop("Sets priorities for the cipher suites supported by GnuTLS."),
    +			NULL,
    +			GUC_SUPERUSER_ONLY
    +		},
    +		&gnutls_priority,
    +#ifdef USE_SSL
    +#if HAVE_DECL_GNUTLS_ALPN_SERVER_PRECEDENCE
    +		"NORMAL:%SERVER_PRECEDENCE",
    +#else
    +		"NORMAL",
    +#endif
    +#else
    +		"none",
    +#endif
    +		NULL, NULL, NULL
    +	},
    +
     	{
     		{"ssl_dh_params_file", PGC_SIGHUP, CONN_AUTH_SSL,
     			gettext_noop("Location of the SSL DH parameters file."),
    diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
    index 7486d20a34..ce0de5d362 100644
    --- a/src/backend/utils/misc/postgresql.conf.sample
    +++ b/src/backend/utils/misc/postgresql.conf.sample
    @@ -100,13 +100,18 @@
     #ssl_cert_file = 'server.crt'
     #ssl_crl_file = ''
     #ssl_key_file = 'server.key'
    -#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
    -#ssl_prefer_server_ciphers = on
    -#ssl_ecdh_curve = 'prime256v1'
     #ssl_dh_params_file = ''
     #ssl_passphrase_command = ''
     #ssl_passphrase_command_supports_reload = off
     
    +# Parameters for OpenSSL.  Leave these commented out if not using OpenSSL.
    +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
    +#ssl_prefer_server_ciphers = on
    +#ssl_ecdh_curve = 'prime256v1'
    +
    +# Parameters for GnuTLS.  Leave these commented out if not using GnuTLS.
    +#gnutls_priority = 'NORMAL:%SERVER_PRECEDENCE'
    +
     
     #------------------------------------------------------------------------------
     # RESOURCE USAGE (except WAL)
    diff --git a/src/common/Makefile b/src/common/Makefile
    index 1fc2c66225..6abe10f0d5 100644
    --- a/src/common/Makefile
    +++ b/src/common/Makefile
    @@ -45,7 +45,9 @@ OBJS_COMMON = base64.o config_info.o controldata_utils.o exec.o file_perm.o \
     	rmtree.o saslprep.o scram-common.o string.o unicode_norm.o \
     	username.o wait_error.o
     
    -ifeq ($(with_openssl),yes)
    +ifeq ($(with_gnutls),yes)
    +OBJS_COMMON += sha2_gnutls.o
    +else ifeq ($(with_openssl),yes)
     OBJS_COMMON += sha2_openssl.o
     else
     OBJS_COMMON += sha2.o
    diff --git a/src/common/sha2_gnutls.c b/src/common/sha2_gnutls.c
    new file mode 100644
    index 0000000000..279b5370fa
    --- /dev/null
    +++ b/src/common/sha2_gnutls.c
    @@ -0,0 +1,99 @@
    +/*-------------------------------------------------------------------------
    + *
    + * sha2_gnutlsl.c
    + *	  Set of wrapper routines on top of GnuTLS to support SHA-224
    + *	  SHA-256, SHA-384 and SHA-512 functions.
    + *
    + * This should only be used if code is compiled with GnuTLS support.
    + *
    + * Portions Copyright (c) 2017, PostgreSQL Global Development Group
    + *
    + * IDENTIFICATION
    + *		  src/common/sha2_gnutls.c
    + *
    + *-------------------------------------------------------------------------
    + */
    +
    +#ifndef FRONTEND
    +#include "postgres.h"
    +#else
    +#include "postgres_fe.h"
    +#endif
    +
    +#include "common/sha2.h"
    +
    +/* Interface routines for SHA-256 */
    +void
    +pg_sha256_init(pg_sha256_ctx *ctx)
    +{
    +	gnutls_hash_init(ctx, GNUTLS_DIG_SHA256);
    +}
    +
    +void
    +pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len)
    +{
    +	gnutls_hash(*ctx, data, len);
    +}
    +
    +void
    +pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest)
    +{
    +	gnutls_hash_deinit(*ctx, dest);
    +}
    +
    +/* Interface routines for SHA-512 */
    +void
    +pg_sha512_init(pg_sha512_ctx *ctx)
    +{
    +	gnutls_hash_init(ctx, GNUTLS_DIG_SHA512);
    +}
    +
    +void
    +pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len)
    +{
    +	gnutls_hash(*ctx, data, len);
    +}
    +
    +void
    +pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest)
    +{
    +	gnutls_hash_deinit(*ctx, dest);
    +}
    +
    +/* Interface routines for SHA-384 */
    +void
    +pg_sha384_init(pg_sha384_ctx *ctx)
    +{
    +	gnutls_hash_init(ctx, GNUTLS_DIG_SHA384);
    +}
    +
    +void
    +pg_sha384_update(pg_sha384_ctx *ctx, const uint8 *data, size_t len)
    +{
    +	gnutls_hash(*ctx, data, len);
    +}
    +
    +void
    +pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest)
    +{
    +	gnutls_hash_deinit(*ctx, dest);
    +}
    +
    +/* Interface routines for SHA-224 */
    +void
    +pg_sha224_init(pg_sha224_ctx *ctx)
    +{
    +	gnutls_hash_init(ctx, GNUTLS_DIG_SHA224);
    +}
    +
    +void
    +pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *data, size_t len)
    +{
    +	gnutls_hash(*ctx, data, len);
    +}
    +
    +void
    +pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest)
    +{
    +	gnutls_hash_deinit(*ctx, dest);
    +}
    diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
    index f3fd0d0d28..b064554a3e 100644
    --- a/src/include/common/sha2.h
    +++ b/src/include/common/sha2.h
    @@ -50,8 +50,11 @@
     #ifndef _PG_SHA2_H_
     #define _PG_SHA2_H_
     
    -#ifdef USE_SSL
    +#if defined(USE_OPENSSL)
     #include <openssl/sha.h>
    +#elif defined(USE_GNUTLS)
    +#include <gnutls/gnutls.h>
    +#include <gnutls/crypto.h>
     #endif
     
     /*** SHA224/256/384/512 Various Length Definitions ***********************/
    @@ -69,11 +72,16 @@
     #define PG_SHA512_DIGEST_STRING_LENGTH	(PG_SHA512_DIGEST_LENGTH * 2 + 1)
     
     /* Context Structures for SHA-1/224/256/384/512 */
    -#ifdef USE_SSL
    +#if defined(USE_OPENSSL)
     typedef SHA256_CTX pg_sha256_ctx;
     typedef SHA512_CTX pg_sha512_ctx;
     typedef SHA256_CTX pg_sha224_ctx;
     typedef SHA512_CTX pg_sha384_ctx;
    +#elif defined(USE_GNUTLS)
    +typedef gnutls_hash_hd_t pg_sha256_ctx;
    +typedef gnutls_hash_hd_t pg_sha512_ctx;
    +typedef gnutls_hash_hd_t pg_sha224_ctx;
    +typedef gnutls_hash_hd_t pg_sha384_ctx;
     #else
     typedef struct pg_sha256_ctx
     {
    @@ -89,7 +97,7 @@ typedef struct pg_sha512_ctx
     } pg_sha512_ctx;
     typedef struct pg_sha256_ctx pg_sha224_ctx;
     typedef struct pg_sha512_ctx pg_sha384_ctx;
    -#endif							/* USE_SSL */
    +#endif
     
     /* Interface routines for SHA224/256/384/512 */
     extern void pg_sha224_init(pg_sha224_ctx *ctx);
    diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
    index ef5528c897..fe36a062c0 100644
    --- a/src/include/libpq/libpq-be.h
    +++ b/src/include/libpq/libpq-be.h
    @@ -19,9 +19,11 @@
     #define LIBPQ_BE_H
     
     #include <sys/time.h>
    -#ifdef USE_OPENSSL
    +#if defined(USE_OPENSSL)
     #include <openssl/ssl.h>
     #include <openssl/err.h>
    +#elif defined(USE_GNUTLS)
    +#include <gnutls/gnutls.h>
     #endif
     #ifdef HAVE_NETINET_TCP_H
     #include <netinet/tcp.h>
    @@ -183,12 +185,15 @@ typedef struct Port
     	bool		peer_cert_valid;
     
     	/*
    -	 * OpenSSL structures. (Keep these last so that the locations of other
    -	 * fields are the same whether or not you build with OpenSSL.)
    +	 * SSL library specific structures. (Keep these last so that the locations
    +	 * of other fields are the same whether or not you build with SSL.)
     	 */
    -#ifdef USE_OPENSSL
    +#if defined(USE_OPENSSL)
     	SSL		   *ssl;
     	X509	   *peer;
    +#elif defined(USE_GNUTLS)
    +	gnutls_session_t ssl;
    +	gnutls_x509_crt_t peer;
     #endif
     } Port;
     
    @@ -275,7 +280,7 @@ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
     extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
     #endif
     
    -#endif	/* USE_SSL */
    +#endif							/* USE_SSL */
     
     extern ProtocolVersion FrontendProtocol;
     
    diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
    index 36baf6b919..4cf67523f5 100644
    --- a/src/include/libpq/libpq.h
    +++ b/src/include/libpq/libpq.h
    @@ -102,6 +102,7 @@ extern WaitEventSet *FeBeWaitSet;
     extern char *SSLCipherSuites;
     extern char *SSLECDHCurve;
     extern bool SSLPreferServerCiphers;
    +extern char *gnutls_priority;
     
     /*
      * prototypes for functions in be-secure-common.c
    diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
    index 347d5b56dc..b508ab0959 100644
    --- a/src/include/pg_config.h.in
    +++ b/src/include/pg_config.h.in
    @@ -134,6 +134,14 @@
        don't. */
     #undef HAVE_DECL_F_FULLFSYNC
     
    +/* Define to 1 if you have the declaration of `GNUTLS_ALPN_SERVER_PRECEDENCE',
    +   and to 0 if you don't. */
    +#undef HAVE_DECL_GNUTLS_ALPN_SERVER_PRECEDENCE
    +
    +/* Define to 1 if you have the declaration of `GNUTLS_X509_CRT_LIST_SORT', and
    +   to 0 if you don't. */
    +#undef HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT
    +
     /* Define to 1 if you have the declaration of
        `LLVMCreateGDBRegistrationListener', and to 0 if you don't. */
     #undef HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER
    @@ -282,6 +290,9 @@
     /* Define to 1 if you have the `gettimeofday' function. */
     #undef HAVE_GETTIMEOFDAY
     
    +/* Define to 1 if you have the `gnutls_pkcs11_set_pin_function' function. */
    +#undef HAVE_GNUTLS_PKCS11_SET_PIN_FUNCTION
    +
     /* Define to 1 if you have the <gssapi/gssapi.h> header file. */
     #undef HAVE_GSSAPI_GSSAPI_H
     
    @@ -890,6 +901,12 @@
        (--enable-float8-byval) */
     #undef USE_FLOAT8_BYVAL
     
    +/* Define to build with GnuTLS support. (--with-gnutls) */
    +#undef USE_GNUTLS
    +
    +/* Define to use GnuTLS for random number generation */
    +#undef USE_GNUTLS_RANDOM
    +
     /* Define to build with ICU support. (--with-icu) */
     #undef USE_ICU
     
    diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
    index b309395f11..e2ff2ce693 100644
    --- a/src/include/pg_config_manual.h
    +++ b/src/include/pg_config_manual.h
    @@ -165,7 +165,7 @@
      * implementation.  (Currently, only OpenSSL is supported, but we might add
      * more implementations in the future.)
      */
    -#ifdef USE_OPENSSL
    +#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
     #define USE_SSL
     #endif
     
    diff --git a/src/interfaces/libpq/.gitignore b/src/interfaces/libpq/.gitignore
    index 5c232ae2d1..371800f0c7 100644
    --- a/src/interfaces/libpq/.gitignore
    +++ b/src/interfaces/libpq/.gitignore
    @@ -27,6 +27,7 @@
     /base64.c
     /scram-common.c
     /sha2.c
    +/sha2_gnutls.c
     /sha2_openssl.c
     /saslprep.c
     /unicode_norm.c
    diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
    index abe0a50e98..b5958daf1d 100644
    --- a/src/interfaces/libpq/Makefile
    +++ b/src/interfaces/libpq/Makefile
    @@ -51,7 +51,9 @@ OBJS += encnames.o wchar.o
     # src/common
     OBJS += base64.o ip.o md5.o scram-common.o saslprep.o unicode_norm.o
     
    -ifeq ($(with_openssl),yes)
    +ifeq ($(with_gnutls),yes)
    +OBJS += fe-secure-gnutls.o fe-secure-common.o sha2_gnutls.o
    +else ifeq ($(with_openssl),yes)
     OBJS += fe-secure-openssl.o fe-secure-common.o sha2_openssl.o
     else
     OBJS += sha2.o
    @@ -78,12 +80,12 @@ endif
     # shared library link.  (The order in which you list them here doesn't
     # matter.)
     ifneq ($(PORTNAME), win32)
    -SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
    +SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lgnutls -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
     else
    -SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
    +SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lgnutls -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
     endif
     ifeq ($(PORTNAME), win32)
    -SHLIB_LINK += -lshell32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))
    +SHLIB_LINK += -lshell32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lgnutls -lcomerr32 -lkrb5_32, $(LIBS))
     endif
     
     SHLIB_EXPORTS = exports.txt
    @@ -106,7 +108,7 @@ backend_src = $(top_srcdir)/src/backend
     chklocale.c crypt.c erand48.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pg_strong_random.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32error.c win32setlocale.c: % : $(top_srcdir)/src/port/%
     	rm -f $@ && $(LN_S) $< .
     
    -ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c saslprep.c unicode_norm.c: % : $(top_srcdir)/src/common/%
    +ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c sha2_gnutls.c saslprep.c unicode_norm.c: % : $(top_srcdir)/src/common/%
     	rm -f $@ && $(LN_S) $< .
     
     encnames.c wchar.c: % : $(backend_src)/utils/mb/%
    @@ -156,7 +158,7 @@ clean distclean: clean-lib
     	rm -f pg_config_paths.h
     # Remove files we (may have) symlinked in from src/port and other places
     	rm -f chklocale.c crypt.c erand48.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pg_strong_random.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c strnlen.c thread.c win32error.c win32setlocale.c
    -	rm -f ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c saslprep.c unicode_norm.c
    +	rm -f ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c sha2_gnutls.c saslprep.c unicode_norm.c
     	rm -f encnames.c wchar.c
     
     maintainer-clean: distclean maintainer-clean-lib
    diff --git a/src/interfaces/libpq/fe-secure-gnutls.c b/src/interfaces/libpq/fe-secure-gnutls.c
    new file mode 100644
    index 0000000000..99ee53d067
    --- /dev/null
    +++ b/src/interfaces/libpq/fe-secure-gnutls.c
    @@ -0,0 +1,803 @@
    +/*-------------------------------------------------------------------------
    + *
    + * fe-secure-gnutls.c
    + *	  GnuTLS support
    + *
    + *
    + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
    + * Portions Copyright (c) 1994, Regents of the University of California
    + *
    + *
    + * IDENTIFICATION
    + *	  src/interfaces/libpq/fe-secure-gnutls.c
    + *
    + *-------------------------------------------------------------------------
    + */
    +
    +#include "postgres_fe.h"
    +#include "fe-secure-common.h"
    +#include "libpq-int.h"
    +
    +#include <sys/stat.h>
    +
    +#ifdef ENABLE_THREAD_SAFETY
    +#ifdef WIN32
    +#include "pthread-win32.h"
    +#else
    +#include <pthread.h>
    +#endif
    +#endif
    +
    +#include <gnutls/gnutls.h>
    +#include <gnutls/x509.h>
    +
    +static int	initialize_SSL(PGconn *conn);
    +static PostgresPollingStatusType open_client_SSL(PGconn *);
    +
    +static ssize_t my_sock_read(gnutls_transport_ptr_t h, void *buf, size_t size);
    +static ssize_t my_sock_write(gnutls_transport_ptr_t h, const void *buf, size_t size);
    +static int	get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t *peer);
    +static int	verify_cb(gnutls_session_t ssl);
    +
    +static bool ssl_lib_initialized = false;
    +
    +#ifdef ENABLE_THREAD_SAFETY
    +#ifndef WIN32
    +static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
    +#else
    +static pthread_mutex_t ssl_config_mutex = NULL;
    +static long win32_ssl_create_mutex = 0;
    +#endif
    +#endif							/* ENABLE_THREAD_SAFETY */
    +
    +
    +/* ------------------------------------------------------------ */
    +/*			 Procedures common to all secure sessions			*/
    +/* ------------------------------------------------------------ */
    +
    +void
    +pgtls_init_library(bool do_ssl, int do_crypto)
    +{
    +	/* not used with GnuTLS */
    +}
    +
    +PostgresPollingStatusType
    +pgtls_open_client(PGconn *conn)
    +{
    +	/* First time through? */
    +	if (conn->ssl == NULL)
    +	{
    +		/*
    +		 * Create a connection-specific SSL object, and load client
    +		 * certificate, private key, and trusted CA certs.
    +		 */
    +		if (initialize_SSL(conn) != 0)
    +		{
    +			/* initialize_SSL already put a message in conn->errorMessage */
    +			pgtls_close(conn);
    +			return PGRES_POLLING_FAILED;
    +		}
    +	}
    +
    +	/* Begin or continue the actual handshake */
    +	return open_client_SSL(conn);
    +}
    +
    +ssize_t
    +pgtls_read(PGconn *conn, void *ptr, size_t len)
    +{
    +	ssize_t		n;
    +	int			result_errno;
    +	char		sebuf[256];
    +
    +	n = gnutls_record_recv(conn->ssl, ptr, len);
    +
    +	if (n > 0)
    +	{
    +		SOCK_ERRNO_SET(0);
    +		return n;
    +	}
    +
    +	switch (n)
    +	{
    +		case 0:
    +			printfPQExpBuffer(&conn->errorMessage,
    +							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
    +			result_errno = ECONNRESET;
    +			n = -1;
    +			break;
    +		case GNUTLS_E_REHANDSHAKE:
    +			/* Ignore re-handsake requests and have the caller retry */
    +		case GNUTLS_E_INTERRUPTED:
    +			result_errno = EINTR;
    +			n = -1;
    +			break;
    +		case GNUTLS_E_AGAIN:
    +			result_errno = EAGAIN;
    +			n = -1;
    +			break;
    +#ifdef GNUTLS_E_PREMATURE_TERMINATION
    +		case GNUTLS_E_PREMATURE_TERMINATION:
    +#endif
    +		case GNUTLS_E_PUSH_ERROR:
    +			result_errno = SOCK_ERRNO;
    +			n = -1;
    +			if (result_errno == EPIPE || result_errno == ECONNRESET)
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext(
    +												"server closed the connection unexpectedly\n"
    +												"\tThis probably means the server terminated abnormally\n"
    +												"\tbefore or while processing the request.\n"));
    +			else
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("SSL SYSCALL error: %s\n"),
    +								  SOCK_STRERROR(result_errno,
    +												sebuf, sizeof(sebuf)));
    +			break;
    +		default:
    +			printfPQExpBuffer(&conn->errorMessage,
    +							  libpq_gettext("SSL error: %s\n"),
    +							  gnutls_strerror(n));
    +			/* assume the connection is broken */
    +			result_errno = ECONNRESET;
    +			n = -1;
    +			break;
    +	}
    +
    +	/* ensure we return the intended errno to caller */
    +	SOCK_ERRNO_SET(result_errno);
    +
    +	return n;
    +}
    +
    +bool
    +pgtls_read_pending(PGconn *conn)
    +{
    +	return gnutls_record_check_pending(conn->ssl);
    +}
    +
    +ssize_t
    +pgtls_write(PGconn *conn, const void *ptr, size_t len)
    +{
    +	ssize_t		n;
    +	int			result_errno;
    +	char		sebuf[256];
    +
    +	n = gnutls_record_send(conn->ssl, ptr, len);
    +
    +	if (n >= 0)
    +	{
    +		SOCK_ERRNO_SET(0);
    +		return n;
    +	}
    +
    +	switch (n)
    +	{
    +		case GNUTLS_E_INTERRUPTED:
    +			result_errno = EINTR;
    +			n = -1;
    +			break;
    +		case GNUTLS_E_AGAIN:
    +			result_errno = EAGAIN;
    +			n = -1;
    +			break;
    +#ifdef GNUTLS_E_PREMATURE_TERMINATION
    +		case GNUTLS_E_PREMATURE_TERMINATION:
    +#endif
    +		case GNUTLS_E_PUSH_ERROR:
    +			result_errno = SOCK_ERRNO;
    +			n = -1;
    +			if (result_errno == EPIPE || result_errno == ECONNRESET)
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext(
    +												"server closed the connection unexpectedly\n"
    +												"\tThis probably means the server terminated abnormally\n"
    +												"\tbefore or while processing the request.\n"));
    +			else
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("SSL SYSCALL error: %s\n"),
    +								  SOCK_STRERROR(result_errno,
    +												sebuf, sizeof(sebuf)));
    +			break;
    +		default:
    +			printfPQExpBuffer(&conn->errorMessage,
    +							  libpq_gettext("SSL error: %s\n"),
    +							  gnutls_strerror(n));
    +			/* assume the connection is broken */
    +			result_errno = ECONNRESET;
    +			n = -1;
    +			break;
    +	}
    +
    +	/* ensure we return the intended errno to caller */
    +	SOCK_ERRNO_SET(result_errno);
    +
    +	return n;
    +}
    +
    +/* ------------------------------------------------------------ */
    +/*						GnuTLS specific code					*/
    +/* ------------------------------------------------------------ */
    +
    +
    +
    +#define MAX_CN 256
    +
    +/*
    + * Verify that the server certificate matches the hostname we connected to.
    + *
    + * The certificate's Common Name and Subject Alternative Names are considered.
    + */
    +int
    +pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
    +												int *names_examined,
    +												char **first_name)
    +{
    +	char		namedata[MAX_CN];
    +	size_t		namelen;
    +	int			i;
    +	int			ret;
    +	int			rc = 0;
    +
    +	/*
    +	 * First, get the Subject Alternative Names (SANs) from the certificate,
    +	 * and compare them against the originally given hostname.
    +	 */
    +	for (i = 0;; i++)
    +	{
    +		namelen = sizeof(namedata);
    +		ret = gnutls_x509_crt_get_subject_alt_name(conn->peer, i,
    +												   namedata,
    +												   &namelen,
    +												   NULL);
    +
    +		if (ret < 0)
    +			break;
    +
    +		if (ret == GNUTLS_SAN_DNSNAME)
    +		{
    +			char	   *alt_name = NULL;
    +
    +			(*names_examined)++;
    +
    +			rc = pq_verify_peer_name_matches_certificate_name(conn, namedata, namelen, &alt_name);
    +
    +			if (alt_name)
    +			{
    +				if (!*first_name)
    +					*first_name = alt_name;
    +				else
    +					free(alt_name);
    +			}
    +		}
    +
    +		if (rc != 0)
    +			break;
    +	}
    +
    +	/*
    +	 * If there is no subjectAltName extension of type dNSName, check the
    +	 * Common Name.
    +	 *
    +	 * (Per RFC 2818 and RFC 6125, if the subjectAltName extension of type
    +	 * dNSName is present, the CN must be ignored.)
    +	 */
    +	if (*names_examined == 0)
    +	{
    +		namelen = sizeof(namedata);
    +		ret = gnutls_x509_crt_get_dn_by_oid(conn->peer, GNUTLS_OID_X520_COMMON_NAME, 0, 0, namedata, &namelen);
    +
    +		if (ret >= 0)
    +		{
    +			(*names_examined)++;
    +			rc = pq_verify_peer_name_matches_certificate_name(conn, namedata, namelen, first_name);
    +		}
    +	}
    +
    +	return rc;
    +}
    +
    +/*
    + * Initialize SSL library.
    + *
    + * In threadsafe mode, this includes setting up libcrypto callback functions
    + * to do thread locking.
    + */
    +int
    +pgtls_init(PGconn *conn)
    +{
    +#ifdef ENABLE_THREAD_SAFETY
    +#ifdef WIN32
    +	/* Also see similar code in fe-connect.c, default_threadlock() */
    +	if (ssl_config_mutex == NULL)
    +	{
    +		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
    +			 /* loop, another thread own the lock */ ;
    +		if (ssl_config_mutex == NULL)
    +		{
    +			if (pthread_mutex_init(&ssl_config_mutex, NULL))
    +				return -1;
    +		}
    +		InterlockedExchange(&win32_ssl_create_mutex, 0);
    +	}
    +#endif
    +	if (pthread_mutex_lock(&ssl_config_mutex))
    +		return -1;
    +#endif							/* ENABLE_THREAD_SAFETY */
    +
    +	if (!ssl_lib_initialized)
    +	{
    +		gnutls_global_init();
    +		ssl_lib_initialized = true;
    +	}
    +
    +#ifdef ENABLE_THREAD_SAFETY
    +	pthread_mutex_unlock(&ssl_config_mutex);
    +#endif
    +	return 0;
    +}
    +
    +/*
    + *	Create per-connection SSL object, and load the client certificate,
    + *	private key, and trusted CA certs.
    + *
    + *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
    + */
    +static int
    +initialize_SSL(PGconn *conn)
    +{
    +	gnutls_certificate_credentials_t creds;
    +	int			ret;
    +	struct stat buf;
    +	char		homedir[MAXPGPATH];
    +	char		fnbuf[MAXPGPATH];
    +	char		keybuf[MAXPGPATH];
    +	char		sebuf[256];
    +	bool		have_homedir;
    +
    +	/*
    +	 * We'll need the home directory if any of the relevant parameters are
    +	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
    +	 * files could be found.
    +	 */
    +	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
    +		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
    +		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
    +		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
    +		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
    +	else						/* won't need it */
    +		have_homedir = false;
    +
    +	ret = gnutls_certificate_allocate_credentials(&creds);
    +	if (ret < 0)
    +	{
    +		printfPQExpBuffer(&conn->errorMessage,
    +						  libpq_gettext("could not create SSL credentials: %s\n"),
    +						  gnutls_strerror(ret));
    +		return -1;
    +	}
    +
    +	/*
    +	 * If the root cert file exists, load it so we can perform certificate
    +	 * verification. If sslmode is "verify-full" we will also do further
    +	 * verification after the connection has been completed.
    +	 */
    +	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
    +		strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
    +	else if (have_homedir)
    +		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
    +	else
    +		fnbuf[0] = '\0';
    +
    +	if (fnbuf[0] != '\0' &&
    +		stat(fnbuf, &buf) == 0)
    +	{
    +		ret = gnutls_certificate_set_x509_trust_file(creds, fnbuf, GNUTLS_X509_FMT_PEM);
    +		if (ret < 0)
    +		{
    +			printfPQExpBuffer(&conn->errorMessage,
    +							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
    +							  fnbuf, gnutls_strerror(ret));
    +			gnutls_certificate_free_credentials(creds);
    +			return -1;
    +		}
    +
    +		gnutls_certificate_set_verify_function(creds, verify_cb);
    +
    +		if (conn->sslcrl && strlen(conn->sslcrl) > 0)
    +			strlcpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
    +		else if (have_homedir)
    +			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
    +		else
    +			fnbuf[0] = '\0';
    +
    +		if (fnbuf[0] != '\0' && stat(fnbuf, &buf) == 0)
    +		{
    +			ret = gnutls_certificate_set_x509_crl_file(creds, fnbuf, GNUTLS_X509_FMT_PEM);
    +			if (ret < 0)
    +			{
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("could not read crl file \"%s\": %s\n"),
    +								  fnbuf, gnutls_strerror(ret));
    +				gnutls_certificate_free_credentials(creds);
    +				return -1;
    +			}
    +		}
    +	}
    +	else
    +	{
    +		/*
    +		 * stat() failed; assume root file doesn't exist.  If sslmode is
    +		 * verify-ca or verify-full, this is an error.  Otherwise, continue
    +		 * without performing any server cert verification.
    +		 */
    +		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
    +		{
    +			/*
    +			 * The only way to reach here with an empty filename is if
    +			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
    +			 * that it seems worth having a specialized error message for it.
    +			 */
    +			if (fnbuf[0] == '\0')
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("could not get home directory to locate root certificate file\n"
    +												"Either provide the file or change sslmode to disable server certificate verification.\n"));
    +			else
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("root certificate file \"%s\" does not exist\n"
    +												"Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
    +			gnutls_certificate_free_credentials(creds);
    +			return -1;
    +		}
    +	}
    +
    +	/* Read the client certificate file */
    +	if (conn->sslcert && strlen(conn->sslcert) > 0)
    +		strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf));
    +	else if (have_homedir)
    +		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
    +	else
    +		fnbuf[0] = '\0';
    +
    +	if (fnbuf[0] == '\0')
    +	{
    +		/* no home directory, proceed without a client cert */
    +	}
    +	else if (stat(fnbuf, &buf) != 0)
    +	{
    +		/*
    +		 * If file is not present, just go on without a client cert; server
    +		 * might or might not accept the connection.  Any other error,
    +		 * however, is grounds for complaint.
    +		 */
    +		if (errno != ENOENT && errno != ENOTDIR)
    +		{
    +			printfPQExpBuffer(&conn->errorMessage,
    +							  libpq_gettext("could not open certificate file \"%s\": %s\n"),
    +							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
    +			gnutls_certificate_free_credentials(creds);
    +			return -1;
    +		}
    +	}
    +	else
    +	{
    +		if (conn->sslkey && strlen(conn->sslkey) > 0)
    +			strlcpy(keybuf, conn->sslkey, sizeof(keybuf));
    +		else if (have_homedir)
    +			snprintf(keybuf, sizeof(keybuf), "%s/%s", homedir, USER_KEY_FILE);
    +		else
    +			keybuf[0] = '\0';
    +
    +		if (keybuf[0] != '\0')
    +		{
    +			if (stat(keybuf, &buf) != 0)
    +			{
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
    +								  keybuf);
    +				return -1;
    +			}
    +#ifndef WIN32
    +			if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
    +			{
    +				printfPQExpBuffer(&conn->errorMessage,
    +								  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
    +								  keybuf);
    +				return -1;
    +			}
    +#endif
    +		}
    +
    +		ret = gnutls_certificate_set_x509_key_file(creds, fnbuf, keybuf, GNUTLS_X509_FMT_PEM);
    +		if (ret < 0)
    +		{
    +			printfPQExpBuffer(&conn->errorMessage,
    +							  libpq_gettext("could not read certificate and key files \"%s\" \"%s\": %s\n"),
    +							  fnbuf, keybuf, gnutls_strerror(ret));
    +			gnutls_certificate_free_credentials(creds);
    +			return -1;
    +		}
    +	}
    +
    +	ret = gnutls_init(&conn->ssl, GNUTLS_CLIENT);
    +	if (ret < 0)
    +	{
    +		printfPQExpBuffer(&conn->errorMessage,
    +						  libpq_gettext("could not establish SSL connection: %s\n"),
    +						  gnutls_strerror(ret));
    +		gnutls_certificate_free_credentials(creds);
    +		return -1;
    +	}
    +
    +	gnutls_priority_set_direct(conn->ssl, "NORMAL", NULL);
    +	if (ret < 0)
    +	{
    +		printfPQExpBuffer(&conn->errorMessage,
    +						  libpq_gettext("could not establish SSL connection: %s\n"),
    +						  gnutls_strerror(ret));
    +		gnutls_certificate_free_credentials(creds);
    +		return -1;
    +	}
    +
    +	ret = gnutls_credentials_set(conn->ssl, GNUTLS_CRD_CERTIFICATE, creds);
    +	if (ret < 0)
    +	{
    +		printfPQExpBuffer(&conn->errorMessage,
    +						  libpq_gettext("could not establish SSL connection: %s\n"),
    +						  gnutls_strerror(ret));
    +		gnutls_deinit(conn->ssl);
    +		gnutls_certificate_free_credentials(creds);
    +		return -1;
    +	}
    +
    +	gnutls_transport_set_ptr(conn->ssl, conn);
    +	gnutls_transport_set_pull_function(conn->ssl, my_sock_read);
    +	gnutls_transport_set_push_function(conn->ssl, my_sock_write);
    +
    +	conn->ssl_in_use = true;
    +
    +	return 0;
    +}
    +
    +/*
    + *	Attempt to negotiate SSL connection.
    + */
    +static PostgresPollingStatusType
    +open_client_SSL(PGconn *conn)
    +{
    +	int			ret;
    +
    +	do
    +	{
    +		ret = gnutls_handshake(conn->ssl);
    +	}
    +	while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
    +
    +	if (ret < 0)
    +	{
    +		printfPQExpBuffer(&conn->errorMessage,
    +						  libpq_gettext("SSL error: %s\n"),
    +						  gnutls_strerror(ret));
    +		pgtls_close(conn);
    +		return PGRES_POLLING_FAILED;
    +	}
    +
    +	/*
    +	 * We already checked the server certificate in gnutls_handshake() using
    +	 * verify_cb(), if root.crt exists.
    +	 */
    +
    +	/* get server certificate */
    +	ret = get_peer_certificate(conn->ssl, &conn->peer);
    +	if (conn->peer == NULL)
    +	{
    +		printfPQExpBuffer(&conn->errorMessage,
    +						  libpq_gettext("certificate could not be obtained: %s\n"),
    +						  gnutls_strerror(ret));
    +		pgtls_close(conn);
    +		return PGRES_POLLING_FAILED;
    +	}
    +
    +	if (!pq_verify_peer_name_matches_certificate(conn))
    +	{
    +		pgtls_close(conn);
    +		return PGRES_POLLING_FAILED;
    +	}
    +
    +	/* SSL handshake is complete */
    +	return PGRES_POLLING_OK;
    +}
    +
    +void
    +pgtls_close(PGconn *conn)
    +{
    +	if (conn->ssl)
    +	{
    +		gnutls_bye(conn->ssl, GNUTLS_SHUT_RDWR);
    +		gnutls_deinit(conn->ssl);
    +		conn->ssl = NULL;
    +		conn->ssl_in_use = false;
    +	}
    +
    +	if (conn->peer)
    +	{
    +		gnutls_x509_crt_deinit(conn->peer);
    +		conn->peer = NULL;
    +	}
    +}
    +
    +/* ------------------------------------------------------------ */
    +/*					SSL information functions					*/
    +/* ------------------------------------------------------------ */
    +
    +/*
    + *	Return pointer to OpenSSL object, which is none for GnuTLS.
    + */
    +void *
    +PQgetssl(PGconn *conn)
    +{
    +	return NULL;
    +}
    +
    +void *
    +PQsslStruct(PGconn *conn, const char *struct_name)
    +{
    +	if (!conn)
    +		return NULL;
    +	if (strcmp(struct_name, "GnuTLS") == 0)
    +		return conn->ssl;
    +	return NULL;
    +}
    +
    +const char *const *
    +PQsslAttributeNames(PGconn *conn)
    +{
    +	static const char *const result[] = {
    +		"library",
    +		"key_bits",
    +		"cipher",
    +		"compression",
    +		"protocol",
    +		NULL
    +	};
    +
    +	return result;
    +}
    +
    +const char *
    +PQsslAttribute(PGconn *conn, const char *attribute_name)
    +{
    +	if (!conn)
    +		return NULL;
    +	if (conn->ssl == NULL)
    +		return NULL;
    +
    +	if (strcmp(attribute_name, "library") == 0)
    +		return "GnuTLS";
    +
    +	if (strcmp(attribute_name, "key_bits") == 0)
    +	{
    +		static char sslbits_str[10];
    +		int			sslbytes;
    +
    +		sslbytes = gnutls_cipher_get_key_size(gnutls_cipher_get(conn->ssl));
    +
    +		if (sslbytes == 0)
    +			return NULL;
    +
    +		snprintf(sslbits_str, sizeof(sslbits_str), "%d", sslbytes * 8);
    +		return sslbits_str;
    +	}
    +
    +	if (strcmp(attribute_name, "cipher") == 0)
    +		return gnutls_cipher_get_name(gnutls_cipher_get(conn->ssl));
    +
    +	if (strcmp(attribute_name, "compression") == 0)
    +	{
    +		gnutls_compression_method_t comp = gnutls_compression_get(conn->ssl);
    +
    +		if (comp == GNUTLS_COMP_NULL || comp == GNUTLS_COMP_UNKNOWN)
    +			return "off";
    +		else
    +			return "on";
    +	}
    +
    +	if (strcmp(attribute_name, "protocol") == 0)
    +		return gnutls_protocol_get_name(gnutls_protocol_get_version(conn->ssl));
    +
    +	return NULL;				/* unknown attribute */
    +}
    +
    +/*
    + * Private substitute transport layer: this does the sending and receiving using
    + * pqsecure_raw_write() and pqsecure_raw_read() instead, to allow those
    + * functions to disable SIGPIPE and give better error messages on I/O errors.
    + */
    +
    +static ssize_t
    +my_sock_read(gnutls_transport_ptr_t conn, void *buf, size_t size)
    +{
    +	return pqsecure_raw_read((PGconn *) conn, buf, size);
    +}
    +
    +static ssize_t
    +my_sock_write(gnutls_transport_ptr_t conn, const void *buf, size_t size)
    +{
    +	return pqsecure_raw_write((PGconn *) conn, buf, size);
    +}
    +
    +#if !HAVE_DECL_GNUTLS_X509_CRT_LIST_SORT
    +/*
    + * GnuTLS versions before 3.4.0 do not support sorting incorrectly sorted
    + * certificate chains, so we skip doing so in these earlier versions.
    + */
    +#define GNUTLS_X509_CRT_LIST_SORT 0
    +#endif
    +
    +/*
    + *	Get peer certificate from a session
    + *
    + *	Returns GNUTLS_E_NO_CERTIFICATE_FOUND when not x509 certifcate was found.
    + */
    +static int
    +get_peer_certificate(gnutls_session_t ssl, gnutls_x509_crt_t *peer)
    +{
    +	if (gnutls_certificate_type_get(ssl) == GNUTLS_CRT_X509)
    +	{
    +		unsigned int n;
    +		int			ret;
    +		gnutls_datum_t const *raw_certs;
    +		gnutls_x509_crt_t *certs;
    +
    +		raw_certs = gnutls_certificate_get_peers(ssl, &n);
    +
    +		if (n == 0)
    +			return GNUTLS_E_NO_CERTIFICATE_FOUND;
    +
    +		certs = malloc(n * sizeof(gnutls_x509_crt_t));
    +		if (!certs)
    +			return GNUTLS_E_NO_CERTIFICATE_FOUND;
    +
    +		ret = gnutls_x509_crt_list_import(certs, &n, raw_certs,
    +										  GNUTLS_X509_FMT_DER,
    +										  GNUTLS_X509_CRT_LIST_SORT);
    +
    +		if (ret >= 1)
    +		{
    +			unsigned int i;
    +
    +			for (i = 1; i < ret; i++)
    +				gnutls_x509_crt_deinit(certs[i]);
    +
    +			*peer = certs[0];
    +
    +			ret = GNUTLS_E_SUCCESS;
    +		}
    +		else if (ret == 0)
    +			ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
    +
    +		free(certs);
    +
    +		return ret;
    +	}
    +
    +	return GNUTLS_E_NO_CERTIFICATE_FOUND;
    +}
    +
    +/*
    + *	Certificate verification callback
    + *
    + *	This callback is where we verify the identity of the server.
    + */
    +static int
    +verify_cb(gnutls_session_t ssl)
    +{
    +	unsigned int status;
    +	int			ret;
    +
    +	ret = gnutls_certificate_verify_peers2(ssl, &status);
    +	if (ret < 0)
    +		return ret;
    +
    +	return status;
    +}
    diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
    index f7dc249bf0..5776c55114 100644
    --- a/src/interfaces/libpq/fe-secure.c
    +++ b/src/interfaces/libpq/fe-secure.c
    @@ -139,7 +139,7 @@ PQsslInUse(PGconn *conn)
     
     /*
      *	Exported function to allow application to tell us it's already
    - *	initialized OpenSSL.
    + *	initialized the SSL library.
      */
     void
     PQinitSSL(int do_init)
    diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
    index ed9c806861..68e4053056 100644
    --- a/src/interfaces/libpq/libpq-fe.h
    +++ b/src/interfaces/libpq/libpq-fe.h
    @@ -339,7 +339,7 @@ extern const char *const *PQsslAttributeNames(PGconn *conn);
      * unencrypted connections or if any other TLS library is in use. */
     extern void *PQgetssl(PGconn *conn);
     
    -/* Tell libpq whether it needs to initialize OpenSSL */
    +/* Tell libpq whether it needs to initialize the SSL library */
     extern void PQinitSSL(int do_init);
     
     /* More detailed way to tell libpq whether it needs to initialize OpenSSL */
    diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
    index fb04c8c61d..83a6f7cb08 100644
    --- a/src/interfaces/libpq/libpq-int.h
    +++ b/src/interfaces/libpq/libpq-int.h
    @@ -71,14 +71,15 @@ typedef struct
     #endif
     #endif							/* ENABLE_SSPI */
     
    -#ifdef USE_OPENSSL
    +#if defined(USE_OPENSSL)
     #include <openssl/ssl.h>
     #include <openssl/err.h>
    -
     #ifndef OPENSSL_NO_ENGINE
     #define USE_SSL_ENGINE
     #endif
    -#endif							/* USE_OPENSSL */
    +#elif defined(USE_GNUTLS)
    +#include <gnutls/gnutls.h>
    +#endif
     
     /*
      * POSTGRES backend dependent Constants.
    @@ -463,7 +464,7 @@ struct pg_conn
     	bool		allow_ssl_try;	/* Allowed to try SSL negotiation */
     	bool		wait_ssl_try;	/* Delay SSL negotiation until after
     								 * attempting normal connection */
    -#ifdef USE_OPENSSL
    +#if defined(USE_OPENSSL)
     	SSL		   *ssl;			/* SSL status, if have SSL connection */
     	X509	   *peer;			/* X509 cert of server */
     #ifdef USE_SSL_ENGINE
    @@ -472,7 +473,10 @@ struct pg_conn
     	void	   *engine;			/* dummy field to keep struct the same if
     								 * OpenSSL version changes */
     #endif
    -#endif							/* USE_OPENSSL */
    +#elif defined(USE_GNUTLS)
    +	gnutls_session_t ssl;		/* SSL status, if have SSL connection */
    +	gnutls_x509_crt_t peer;		/* X509 cert of server */
    +#endif
     #endif							/* USE_SSL */
     
     #ifdef ENABLE_GSS
    diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c
    index f9a06d6606..c3bdd41885 100644
    --- a/src/port/pg_strong_random.c
    +++ b/src/port/pg_strong_random.c
    @@ -24,8 +24,11 @@
     #include <unistd.h>
     #include <sys/time.h>
     
    -#ifdef USE_OPENSSL
    +#if defined(USE_OPENSSL)
     #include <openssl/rand.h>
    +#elif defined(USE_GNUTLS)
    +#include <gnutls/gnutls.h>
    +#include <gnutls/crypto.h>
     #endif
     #ifdef WIN32
     #include <wincrypt.h>
    @@ -85,8 +88,9 @@ random_from_file(char *filename, void *buf, size_t len)
      * We support a number of sources:
      *
      * 1. OpenSSL's RAND_bytes()
    - * 2. Windows' CryptGenRandom() function
    - * 3. /dev/urandom
    + * 2. GnuTLS's gnutls_rnd()
    + * 3. Windows' CryptGenRandom() function
    + * 4. /dev/urandom
      *
      * The configure script will choose which one to use, and set
      * a USE_*_RANDOM flag accordingly.
    @@ -136,6 +140,14 @@ pg_strong_random(void *buf, size_t len)
     		return true;
     	return false;
     
    +	/*
    +	 * When built with GnuTLS, use GnuTLS's gnutls_rnd function.
    +	 */
    +#elif defined(USE_GNUTLS_RANDOM)
    +	if (gnutls_rnd(GNUTLS_RND_RANDOM, buf, len) == 0)
    +		return true;
    +	return false;
    +
     	/*
     	 * Windows has CryptoAPI for strong cryptographic numbers.
     	 */
    diff --git a/src/test/Makefile b/src/test/Makefile
    index efb206aa75..a6d41c7e46 100644
    --- a/src/test/Makefile
    +++ b/src/test/Makefile
    @@ -27,7 +27,7 @@ ifneq (,$(filter ldap,$(PG_TEST_EXTRA)))
     SUBDIRS += ldap
     endif
     endif
    -ifeq ($(with_openssl),yes)
    +ifeq ($(filter yes,$(with_openssl) $(with_gnutls)),yes)
     ifneq (,$(filter ssl,$(PG_TEST_EXTRA)))
     SUBDIRS += ssl
     endif
    diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile
    index 97389c90f8..e8d0ed43b0 100644
    --- a/src/test/ssl/Makefile
    +++ b/src/test/ssl/Makefile
    @@ -13,7 +13,7 @@ subdir = src/test/ssl
     top_builddir = ../../..
     include $(top_builddir)/src/Makefile.global
     
    -export with_openssl
    +export with_openssl with_gnutls
     
     CERTIFICATES := server_ca server-cn-and-alt-names \
     	server-cn-only server-single-alt-name server-multiple-alt-names \
    @@ -72,9 +72,10 @@ ssl/server-ss.crt: ssl/server-cn-only.key ssl/server-cn-only.crt server-cn-only.
     	openssl x509 -req -days 10000 -in ssl/server-ss.csr -signkey ssl/server-cn-only.key -out ssl/server-ss.crt  -extensions v3_req -extfile server-cn-only.config
     	rm ssl/server-ss.csr
     
    -# Password-protected version of server-cn-only.key
    +# Password-protected version of server-cn-only.key (need to use PKCS#8
    +# format instead of traditional OpenSSL format for GnuTLS support)
     ssl/server-password.key: ssl/server-cn-only.key
    -	openssl rsa -des -in $< -out $@ -passout 'pass:secret1'
    +	openssl pkcs8 -topk8 -in $< -out $@ -passout 'pass:secret1'
     
     # Client certificate, signed by the client CA:
     ssl/client.crt: ssl/client.key ssl/client_ca.crt
    diff --git a/src/test/ssl/ssl/server-password.key b/src/test/ssl/ssl/server-password.key
    index adcd38ab88..95ee3fd203 100644
    --- a/src/test/ssl/ssl/server-password.key
    +++ b/src/test/ssl/ssl/server-password.key
    @@ -1,18 +1,17 @@
    ------BEGIN RSA PRIVATE KEY-----
    -Proc-Type: 4,ENCRYPTED
    -DEK-Info: DES-CBC,2FAEFD1C1B2C881C
    -
    -PGi9r3pm05iUwz5QbZik+ZNu0fHNaX8LJFZqpOhg0TV38csLtQ2PRjZ0Q/diBlVT
    -SD8JJnIvwPoIWXyMMTax/krFL0CpbFqgAzD4CEgfWxGNhwnMD1DkNaYp/UF/NfuF
    -7TqXomUlcH/pVaZlu7G0wrIo5rnjef70I7GEY2vwT5adSLsUBAgrs/u3MAAx/Wh4
    -PkVxZELmyiH/8MdIevodjRcJrgIzRheEph39eHrWKgWeSbO0DEQK91vv3prICwo2
    -w2iU0Zohf92QuquA2MKZWruCHb4A4HusUZf3Zc14Yueu/HyztSrHmFeBp0amlWep
    -/o6mx274XVj7IpanOPPM4qEhrF97LHdaSEPn9HwxvvV4GFJDNCVEBl4zuaHo0N8C
    -85GPazIxUWB3CB9PrtXduxeI22lwrIiUdmzA68EXHD7Wg8R90397MNMOomLgfNcu
    -rXarrTXmTNgOa20hc1Ue5AXg9fVS9V/5GP4Dn9SX/CdaE1rz0b73N/ViQzVrS9Ne
    -n04qYPbnf+MQmFWnzMXctZbYG6jDCbuGFIGP4i/LG+wOE8Rntu8Re9re+HANu5VJ
    -Ht20wYOGZIpNwo4YenxvPeTTlbB0Qcma2lnw2bt19owpNQVIeTnRQXxZs3/Y3a+A
    -+/B8VvIkQ0u0EpnSVLBetEmJqtOQvBz7c4Z+0Cl+DL1bTqrDn54MxUBap6dgU+/1
    -R6pxx1F0ZTtQauVmO8n3rWKwOGG5NeMhf4iId2JWpw39VtRk8LNtnGUbUAbL5znY
    -rkUVyJstQg6U6kNTgDWQ1nBxCzlRz2xpHyghnyxLkMpW5ECpmwwLDQ==
    ------END RSA PRIVATE KEY-----
    +-----BEGIN ENCRYPTED PRIVATE KEY-----
    +MIICoTAbBgkqhkiG9w0BBQMwDgQIECagSEM3B/MCAggABIICgF9tG895A9Q81uR/
    +d+EcY21a787f2yiUvgOuhuClmWiqOTNSEC/iJqHb83NL0q31QsIG3SPGtTvtafF8
    ++uVDzqmx4bmKP5LVX88eOgF6kjTT88ouWk1syHtEa7hSU70XPjh/fnUWwqTqW6gn
    +BVKH6J5hOhyI1aWQpRrhmlq6LQD01UlRed0sti35KUji+ohWSPyyC0wSKz8cCKIx
    +oCUyc1IDxbr/PCzXwxSK9iVCIIXx+3emi4puwt9QSVMwqo+LMFMhLz/L3bLXE6iZ
    +BQOBUKtVoO5kLcvtIwRJWBir8HSeIIZnzGI23YnIythY4WnkPLkUFH9uoaYZU3l6
    +UkZ2/cAQwbT6xZxgZQz/HhWLHuqrOCWZboG1oYmTkvO6+8FYRVfPrbJsQqop8olj
    +T2jnlD37W79lAbT5xMJEfvow+h6ebu+wf4Bq9f223JgfBXrGevoksVfWRINtQ7iy
    +Mxrj4dabHRiQTF6semNnHedvulZK6UtwoNQudM0vOzZTd42W8FMjgjU4U7KI+6AX
    +G9SaSAjRMlM5x4PyMnyA+wjyboWpo4jMLdUhNqSs2W+3AWh1qYuh8Ny9ENcKTAY6
    +6hu2RCKQyJBsHm5XN1XaueNsyEq11yk9AZFLAWM5h5WPziZNYSHYwEBXpZlIcpWY
    +6MR/XUqv43B8KvQwr7bHS2GZ2fAD5sd7oq9qpBr7o6IRO3MKh16T6pwoaQ7sqRg4
    +1Ftk7htujwEIrTmIQihzmZRZguA3ceAYUduSFElckZD04LDJnxhhj+AVHnbFA+6x
    +VWOoxECXWa3ReOoJMQvbR4gF5yhNtZOtlbaqOp0iVGT/d2e9Bl66E/FqnkWweE4d
    +0iziYMw=
    +-----END ENCRYPTED PRIVATE KEY-----
    diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
    index 2b875a3c95..3602199ff6 100644
    --- a/src/test/ssl/t/001_ssltests.pl
    +++ b/src/test/ssl/t/001_ssltests.pl
    @@ -6,15 +6,13 @@
     use ServerSetup;
     use File::Copy;
     
    -if ($ENV{with_openssl} eq 'yes')
    -{
    -	plan tests => 65;
    -}
    -else
    +if ($ENV{with_openssl} ne 'yes' && $ENV{with_gnutls} ne 'yes')
     {
     	plan skip_all => 'SSL not supported by this build';
     }
     
    +my $number_of_tests = 63;
    +
     #### Some configuration
     
     # This is the hostname used to connect to the server. This cannot be a
    @@ -52,7 +50,11 @@
     
     # Run this before we lock down access below.
     my $result = $node->safe_psql('postgres', "SHOW ssl_library");
    -is($result, 'OpenSSL', 'ssl_library parameter');
    +my $expected;
    +if ($ENV{'with_openssl'} eq 'yes')		{ $expected = 'OpenSSL'; }
    +elsif ($ENV{'with_gnutls'} eq 'yes')	{ $expected = 'GnuTLS'; }
    +else									{ $expected = ''; }
    +is($result, $expected, 'ssl_library parameter');
     
     configure_test_server_for_ssl($node, $SERVERHOSTADDR, 'trust');
     
    @@ -130,11 +132,22 @@
     	"sslrootcert=ssl/client_ca.crt sslmode=verify-full",
     	qr/SSL error/, "connect with wrong server root cert sslmode=verify-full");
     
    -# Try with just the server CA's cert. This fails because the root file
    -# must contain the whole chain up to the root CA.
    -test_connect_fails($common_connstr,
    -	"sslrootcert=ssl/server_ca.crt sslmode=verify-ca",
    -	qr/SSL error/, "connect with server CA cert, without root CA");
    +# Try with just the server CA's cert. This fails with OpenSSL because
    +# the root file must contain the whole chain up to the root CA.
    +if ($ENV{'with_openssl'} eq 'yes')
    +{
    +	test_connect_fails($common_connstr,
    +					   "sslrootcert=ssl/server_ca.crt sslmode=verify-ca",
    +					   qr/SSL error/,
    +					   "connect with server CA cert, without root CA");
    +	$number_of_tests++;
    +}
    +else
    +{
    +	test_connect_ok($common_connstr,
    +					"sslrootcert=ssl/server_ca.crt sslmode=verify-ca",
    +					"connect with server CA cert, without root CA");
    +}
     
     # And finally, with the correct root cert.
     test_connect_ok(
    @@ -169,12 +182,21 @@
     	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=invalid",
     	"sslcrl option with invalid file name");
     
    -# A CRL belonging to a different CA is not accepted, fails
    -test_connect_fails(
    -	$common_connstr,
    -	"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
    -	qr/SSL error/,
    -	"CRL belonging to a different CA");
    +if ($ENV{'with_openssl'} eq 'yes')
    +{
    +	# A CRL belonging to a different CA is not accepted, fails
    +	test_connect_fails($common_connstr,
    +					   "sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
    +					   qr/SSL error/,
    +					   "CRL belonging to a different CA");
    +	$number_of_tests++;
    +}
    +else
    +{
    +	test_connect_ok($common_connstr,
    +					"sslrootcert=ssl/root+server_ca.crt sslmode=verify-ca sslcrl=ssl/client.crl",
    +					"CRL belonging to a different CA");
    +}
     
     # With the correct CRL, succeeds (this cert is not revoked)
     test_connect_ok(
    @@ -363,8 +385,13 @@
     	"sslmode=require sslcert=ssl/client+client_ca.crt",
     	"intermediate client certificate is provided by client");
     test_connect_fails($common_connstr, "sslmode=require sslcert=ssl/client.crt",
    -	qr/SSL error/, "intermediate client certificate is missing");
    +				   ($ENV{'with_openssl'} eq 'yes' ?
    +					qr/SSL error/ :
    +					qr/connection requires a valid client certificate/),
    +				   "intermediate client certificate is missing");
     
     # clean up
     unlink("ssl/client_tmp.key", "ssl/client_wrongperms_tmp.key",
     	"ssl/client-revoked_tmp.key");
    +
    +done_testing($number_of_tests);
    diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl
    index b460a7fa8a..0c10ea8bc4 100644
    --- a/src/test/ssl/t/002_scram.pl
    +++ b/src/test/ssl/t/002_scram.pl
    @@ -8,7 +8,7 @@
     use ServerSetup;
     use File::Copy;
     
    -if ($ENV{with_openssl} ne 'yes')
    +if ($ENV{with_openssl} ne 'yes' && $ENV{with_gnutls} ne 'yes')
     {
     	plan skip_all => 'SSL not supported by this build';
     }
    diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
    index 4543d87d83..0ae138fbfb 100644
    --- a/src/tools/msvc/Mkvcbuild.pm
    +++ b/src/tools/msvc/Mkvcbuild.pm
    @@ -124,6 +124,10 @@ sub mkvcbuild
     	{
     		push(@pgcommonallfiles, 'sha2_openssl.c');
     	}
    +	elsif ($solution->{options}->{gnutls})
    +	{
    +		push(@pgcommonallfiles, 'sha2_gnutls.c');
    +	}
     	else
     	{
     		push(@pgcommonallfiles, 'sha2.c');
    @@ -252,6 +256,12 @@ sub mkvcbuild
     		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
     		$libpq->RemoveFile('src/common/sha2_openssl.c');
     	}
    +	elsif (!$solution->{options}->{gnutls})
    +	{
    +		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c');
    +		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-gnutls.c');
    +		$libpq->RemoveFile('src/common/sha2_gnutls.c');
    +	}
     	else
     	{
     		$libpq->RemoveFile('src/common/sha2.c');
    diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
    index 9fe950b29d..07823ccfa9 100644
    --- a/src/tools/pgindent/typedefs.list
    +++ b/src/tools/pgindent/typedefs.list
    @@ -2818,6 +2818,9 @@ ginxlogVacuumDataLeafPage
     gistxlogPage
     gistxlogPageSplit
     gistxlogPageUpdate
    +gnutls_datum_t
    +gnutls_dh_params_t
    +gnutls_x509_crt_t
     grouping_sets_data
     gseg_picksplit_item
     gss_OID
    
    base-commit: 2e39f69b6621bd3d67f650a5647fd0412819712d
    -- 
    2.18.0
    
    
    --------------B9D2DD24F55D9D2B4F606D4C--