From 85465f4ad3e210a3948216ebc5c6fbd8c6993bdb Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Fri, 31 Aug 2018 13:00:26 +0200
Subject: [PATCH v7] GnuTLS support

---
 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

