longfin missing gssapi_ext.h

Started by Stephen Frostalmost 3 years ago48 messages
#1Stephen Frost
sfrost@snowman.net

Greetings,

Looks like longfin has a particularly old Kerberos/GSSAPI installation
on it which pre-dates MIT release 1.11 from circa 2012 and is missing
gssapi_ext.h, causing the recently committed patch to add Kerberos
credential delegation to fail to build.

I'm inclined to update our configure check to explicitly check for the
needed function (gss_store_cred_into) as no one should really be running
with such an out-dated (over a decade old...) version of MIT Kerberos.

Thoughts?

Thanks!

Stephen

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#1)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Looks like longfin has a particularly old Kerberos/GSSAPI installation
on it

It's whatever Apple is shipping, or was shipping last year or so.

I'm inclined to update our configure check to explicitly check for the
needed function (gss_store_cred_into)

Sounds like a possible fix, although I wonder whether you shouldn't
explicitly check for the presence of this header.

regards, tom lane

#3Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#2)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Looks like longfin has a particularly old Kerberos/GSSAPI installation
on it

It's whatever Apple is shipping, or was shipping last year or so.

Sadly they've not been maintaining the Kerberos libraries at all on
their systems.

I'm inclined to update our configure check to explicitly check for the
needed function (gss_store_cred_into)

Sounds like a possible fix, although I wonder whether you shouldn't
explicitly check for the presence of this header.

I'm good with either.

Thanks!

Stephen

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#3)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

It's whatever Apple is shipping, or was shipping last year or so.

Sadly they've not been maintaining the Kerberos libraries at all on
their systems.

Indeed :-(. I wouldn't be surprised if there are security issues in
their version. Perhaps what we really ought to do is refuse to build
with their version --- but if so, we need some clearer error message
about it.

regards, tom lane

#5Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#4)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

It's whatever Apple is shipping, or was shipping last year or so.

Sadly they've not been maintaining the Kerberos libraries at all on
their systems.

Indeed :-(. I wouldn't be surprised if there are security issues in
their version. Perhaps what we really ought to do is refuse to build
with their version --- but if so, we need some clearer error message
about it.

The attached should (I believe?) at least add the needed check for
gssapi_ext.h which will cause builds to fail and complain about the
header being missing from their installation.

I'm certainly open to ideas about how to provide a better error message,
particularly on OSX systems which have an ancient version, to make it
clear that people need to install an updated version. I don't have an
OSX system at hand though.

Should I push this to at least address the header check ... ?

Thanks,

Stephen

Attachments:

check_gssapi_ext.h.patchtext/x-diff; charset=us-asciiDownload
diff --git a/configure.ac b/configure.ac
index 8095dfcf1d..2bdb3dc3ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1562,6 +1562,7 @@ fi
 if test "$with_gssapi" = yes ; then
   AC_CHECK_HEADERS(gssapi/gssapi.h, [],
 	[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
+	[AC_CHECK_HEADERS(gssapi_ext.h, [], [AC_MSG_ERROR([gssapi_ext.h header file is required for GSSAPI])])])
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
#6Stephen Frost
sfrost@snowman.net
In reply to: Stephen Frost (#5)
Re: longfin missing gssapi_ext.h

Greetings,

* Stephen Frost (sfrost@snowman.net) wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

It's whatever Apple is shipping, or was shipping last year or so.

Sadly they've not been maintaining the Kerberos libraries at all on
their systems.

Indeed :-(. I wouldn't be surprised if there are security issues in
their version. Perhaps what we really ought to do is refuse to build
with their version --- but if so, we need some clearer error message
about it.

The attached should (I believe?) at least add the needed check for
gssapi_ext.h which will cause builds to fail and complain about the
header being missing from their installation.

I'm certainly open to ideas about how to provide a better error message,
particularly on OSX systems which have an ancient version, to make it
clear that people need to install an updated version. I don't have an
OSX system at hand though.

Should I push this to at least address the header check ... ?

Looks like buildfarm animal hake, at least, has a version recent enough
to have gssapi_ext.h ... but still older than 1.11 and therefore
doesn't have the type gss_key_value_element_desc defined, so maybe the
check for gss_store_cred_into would be better?

Certainly interesting how many old kerberos library installations there
are, even in our buildfarm..

Thanks!

Stephen

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#6)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Looks like buildfarm animal hake, at least, has a version recent enough
to have gssapi_ext.h ... but still older than 1.11 and therefore
doesn't have the type gss_key_value_element_desc defined, so maybe the
check for gss_store_cred_into would be better?

Well, now we're getting into value judgements about which gssapi
versions are still worth supporting. Are you really willing to toss
overboard all versions that don't support gss_store_cred_into? Or
should credential delegation be viewed as an incremental feature that
we can support or not?

TBH, committing things with significant portability hazards ten hours
before feature freeze is not high on my list of good development
practices.

regards, tom lane

#8Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#7)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Looks like buildfarm animal hake, at least, has a version recent enough
to have gssapi_ext.h ... but still older than 1.11 and therefore
doesn't have the type gss_key_value_element_desc defined, so maybe the
check for gss_store_cred_into would be better?

Well, now we're getting into value judgements about which gssapi
versions are still worth supporting. Are you really willing to toss
overboard all versions that don't support gss_store_cred_into? Or
should credential delegation be viewed as an incremental feature that
we can support or not?

I'm open to considering support for older versions, however ...

TBH, committing things with significant portability hazards ten hours
before feature freeze is not high on my list of good development
practices.

but as pointed out, these APIs are all over a decade old and systems
which don't support them have a pretty high risk of having security
issues due to shipping these out-dated libraries.

I agree it's a value judgement and something to consider but I don't see
Apple changing their mind any time soon on actually updating the
Kerberos version they ship and no one should really be using what they
do ship. The same is true for any other system that's shipping a
version of a core security library that's not been updated in over a
decade.

We are currently requiring at least OpenSSL 1.0.1 which was released in
2012. Having a similar requirement for MIT Kerberos, for our release of
PG in 2023, doesn't strike me as unreasonable.

Attached is a more fully-formed patch with a regenerated configure that
adds in a check for gssapi_ext.h and updates the function check to look
for gss_store_cred_into().

Thanks!

Stephen

Attachments:

check_gssapi_ext.h_v2.patchtext/x-diff; charset=us-asciiDownload
diff --git a/configure b/configure
index 905be9568b..1ccdc5ca2c 100755
--- a/configure
+++ b/configure
@@ -12635,9 +12635,9 @@ fi
 
 if test "$with_gssapi" = yes ; then
   if test "$PORTNAME" != "win32"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_init_sec_context" >&5
-$as_echo_n "checking for library containing gss_init_sec_context... " >&6; }
-if ${ac_cv_search_gss_init_sec_context+:} false; then :
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_store_cred_into" >&5
+$as_echo_n "checking for library containing gss_store_cred_into... " >&6; }
+if ${ac_cv_search_gss_store_cred_into+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_func_search_save_LIBS=$LIBS
@@ -12650,11 +12650,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 #ifdef __cplusplus
 extern "C"
 #endif
-char gss_init_sec_context ();
+char gss_store_cred_into ();
 int
 main ()
 {
-return gss_init_sec_context ();
+return gss_store_cred_into ();
   ;
   return 0;
 }
@@ -12667,30 +12667,30 @@ for ac_lib in '' gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'; do
     LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
   fi
   if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_search_gss_init_sec_context=$ac_res
+  ac_cv_search_gss_store_cred_into=$ac_res
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext
-  if ${ac_cv_search_gss_init_sec_context+:} false; then :
+  if ${ac_cv_search_gss_store_cred_into+:} false; then :
   break
 fi
 done
-if ${ac_cv_search_gss_init_sec_context+:} false; then :
+if ${ac_cv_search_gss_store_cred_into+:} false; then :
 
 else
-  ac_cv_search_gss_init_sec_context=no
+  ac_cv_search_gss_store_cred_into=no
 fi
 rm conftest.$ac_ext
 LIBS=$ac_func_search_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_init_sec_context" >&5
-$as_echo "$ac_cv_search_gss_init_sec_context" >&6; }
-ac_res=$ac_cv_search_gss_init_sec_context
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_store_cred_into" >&5
+$as_echo "$ac_cv_search_gss_store_cred_into" >&6; }
+ac_res=$ac_cv_search_gss_store_cred_into
 if test "$ac_res" != no; then :
   test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
 
 else
-  as_fn_error $? "could not find function 'gss_init_sec_context' required for GSSAPI" "$LINENO" 5
+  as_fn_error $? "could not find function 'gss_store_cred_into' required for GSSAPI" "$LINENO" 5
 fi
 
   else
@@ -14104,6 +14104,33 @@ done
 
 fi
 
+done
+
+  for ac_header in gssapi/gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_ext.h" "ac_cv_header_gssapi_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  for ac_header in gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi_ext.h" "ac_cv_header_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  as_fn_error $? "gssapi_ext.h header file is required for GSSAPI" "$LINENO" 5
+fi
+
+done
+
+fi
+
 done
 
 fi
@@ -15321,7 +15348,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15367,7 +15394,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15391,7 +15418,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15436,7 +15463,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15460,7 +15487,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
diff --git a/configure.ac b/configure.ac
index 8095dfcf1d..7ba969b1b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1340,8 +1340,8 @@ fi
 
 if test "$with_gssapi" = yes ; then
   if test "$PORTNAME" != "win32"; then
-    AC_SEARCH_LIBS(gss_init_sec_context, [gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'], [],
-                   [AC_MSG_ERROR([could not find function 'gss_init_sec_context' required for GSSAPI])])
+    AC_SEARCH_LIBS(gss_store_cred_into, [gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'], [],
+                   [AC_MSG_ERROR([could not find function 'gss_store_cred_into' required for GSSAPI])])
   else
     LIBS="$LIBS -lgssapi32"
   fi
@@ -1562,6 +1562,8 @@ fi
 if test "$with_gssapi" = yes ; then
   AC_CHECK_HEADERS(gssapi/gssapi.h, [],
 	[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
+  AC_CHECK_HEADERS(gssapi/gssapi_ext.h, [],
+	[AC_CHECK_HEADERS(gssapi_ext.h, [], [AC_MSG_ERROR([gssapi_ext.h header file is required for GSSAPI])])])
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3665e799e7..6d572c3820 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -196,6 +196,12 @@
 /* Define to 1 if you have the `getpeerucred' function. */
 #undef HAVE_GETPEERUCRED
 
+/* Define to 1 if you have the <gssapi_ext.h> header file. */
+#undef HAVE_GSSAPI_EXT_H
+
+/* Define to 1 if you have the <gssapi/gssapi_ext.h> header file. */
+#undef HAVE_GSSAPI_GSSAPI_EXT_H
+
 /* Define to 1 if you have the <gssapi/gssapi.h> header file. */
 #undef HAVE_GSSAPI_GSSAPI_H
 
#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#8)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

I'm open to considering support for older versions, however ...

NetBSD 9.3, which is their *latest production release*, doesn't have
gssapi_ext.h [1]https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=sidewinder&amp;dt=2023-04-08%2002%3A38%3A31. For that matter, it doesn't look like their
not-yet-released 10.0BETA does either (my NetBSD 10 animals would
be failing if they had --with-gssapi). I do not think it's going
to be acceptable to require this feature.

I'm now going to reiterate my opinion that this patch should not
have been pushed in at this point of the dev cycle. A month ago,
there was time to deal with these sorts of issues.

regards, tom lane

[1]: https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=sidewinder&amp;dt=2023-04-08%2002%3A38%3A31

#10Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#7)
Re: longfin missing gssapi_ext.h

Hi,

On 2023-04-07 22:50:18 -0400, Tom Lane wrote:

Or should credential delegation be viewed as an incremental feature that we
can support or not?

That seems like the best way forward here.

Greetings,

Andres Freund

#11Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#9)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

I'm open to considering support for older versions, however ...

NetBSD 9.3, which is their *latest production release*, doesn't have
gssapi_ext.h [1]. For that matter, it doesn't look like their
not-yet-released 10.0BETA does either (my NetBSD 10 animals would
be failing if they had --with-gssapi). I do not think it's going
to be acceptable to require this feature.

I'm certainly curious to understand what Kerberos library they're using
and how they're maintaining it. At least some of the documentation I've
found seems to indicate that it might be Heimdal, which does have
gss_store_cred_into in gssapi.h.

I'm now going to reiterate my opinion that this patch should not
have been pushed in at this point of the dev cycle. A month ago,
there was time to deal with these sorts of issues.

I suspected there would be an issue with OSX but hadn't expected an
issue with NetBSD. I had tested this across a few Linux platforms and
cfbot showed it wasn't causing issues on Windows or the platforms that
are run there. Would be really great to have a way to test these things
out on these other platforms other than just committing them and seeing
what happens on the buildfarm.

In any case, I've reverted it and we can pick this up for the next
cycle. I'll play around with Heimdal locally as it appears to be
available on Ubuntu (I had actually thought Heimdal to be mostly gone at
this point since it only ever existed really due to silly export
restrictions...) and see if there's actually anything other than making
gssapi_ext.h itself be optional to be pulled in that's needed to make it
work.

Thanks,

Stephen

#12Stephen Frost
sfrost@snowman.net
In reply to: Andres Freund (#10)
Re: longfin missing gssapi_ext.h

Greetings,

* Andres Freund (andres@anarazel.de) wrote:

On 2023-04-07 22:50:18 -0400, Tom Lane wrote:

Or should credential delegation be viewed as an incremental feature that we
can support or not?

That seems like the best way forward here.

Yeah, that's certainly doable too, though I'm really not sure we should
be accepting OSX's GSSAPI library and that might really be the only case
at issue here. Either way, I've reverted it and will see about picking
it up for the next cycle (again) and hopefully be able to work through
these issues either by having it be optional or, if NetBSD and Heimdal
actually support all the APIs just with different headers, perhaps
deciding we're willing to require them.

Thanks,

Stephen

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#11)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

I suspected there would be an issue with OSX but hadn't expected an
issue with NetBSD. I had tested this across a few Linux platforms and
cfbot showed it wasn't causing issues on Windows or the platforms that
are run there. Would be really great to have a way to test these things
out on these other platforms other than just committing them and seeing
what happens on the buildfarm.

I poked around a bit more and found that:

* NetBSD's package collection[1]https://cdn.netbsd.org/pub/pkgsrc/current/pkgsrc/security/index.html includes both Heimdal and MIT Kerberos
(mit-krb5). Apparently what's installed on at least some of the buildfarm
animals is the former.

* FreeBSD seems to offer *only* Heimdal [2]https://ports.freebsd.org/cgi/ports.cgi?query=kerberos&amp;stype=all&amp;sektion=all; OpenBSD ditto [3]https://cdn.openbsd.org/pub/OpenBSD/snapshots/packages/amd64/.

* I cannot find any sign of either gss_store_cred_into or gssapi_ext.h
in FreeBSD's Heimdal (7.8.0_6).

So it does not look like supporting Heimdal is going to be optional,
and that means the credential delegation feature is going to have
to be optional, or else we need to find some equivalent Heimdal APIs.

I share your feeling that we could probably blow off Apple's built-in
GSSAPI. MacPorts offers both Heimdal and kerberos5, and I imagine
Homebrew has at least one of them, so Mac people could easily get
hold of newer implementations. But the BSDen are going to be a
problem.

regards, tom lane

[1]: https://cdn.netbsd.org/pub/pkgsrc/current/pkgsrc/security/index.html
[2]: https://ports.freebsd.org/cgi/ports.cgi?query=kerberos&amp;stype=all&amp;sektion=all
[3]: https://cdn.openbsd.org/pub/OpenBSD/snapshots/packages/amd64/

#14Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#13)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

I suspected there would be an issue with OSX but hadn't expected an
issue with NetBSD. I had tested this across a few Linux platforms and
cfbot showed it wasn't causing issues on Windows or the platforms that
are run there. Would be really great to have a way to test these things
out on these other platforms other than just committing them and seeing
what happens on the buildfarm.

I poked around a bit more and found that:

* NetBSD's package collection[1] includes both Heimdal and MIT Kerberos
(mit-krb5). Apparently what's installed on at least some of the buildfarm
animals is the former.

* FreeBSD seems to offer *only* Heimdal [2]; OpenBSD ditto [3].

* I cannot find any sign of either gss_store_cred_into or gssapi_ext.h
in FreeBSD's Heimdal (7.8.0_6).

So it does not look like supporting Heimdal is going to be optional,
and that means the credential delegation feature is going to have
to be optional, or else we need to find some equivalent Heimdal APIs.

Thanks for doing that digging!

I've been looking too and while Heimdal added gss_store_cred_into in
their development branch 5 years ago[1]https://github.com/heimdal/heimdal/commit/e0bb9c10cad0fd98245caecf8af8fca855b2df49 (!), it's not made it into an
actual release. Good that they seem to at least be maintaining it
enough to deal with CVEs, but unfortunately I'm fairly confident that
there won't be a way to support constrained delegation (which is the
next goal, once unconstrained delegation is in and working) on the
Heimdal platforms. I suspected that would have to be optional anyway,
but I hadn't expected it to hit all the BSD platforms.

In any case, for this I'm working switching over to gss_store_cred()
which does seem to be available in the Heimdal Debian packages that I
was able to install locally (looks to be 7.7.0) and should work just
fine for these purposes, though it requires a bit more work on the
libpq side as we need to tell libpq explicitly the name which was on
the delegated credential when we call gss_acquire_cred().

Once that's done, should be able to drop the gssapi_ext.h include
entirely and still have the test suite able to run with MIT Kerberos.

One thing I'm on the fence about is trying to make the test suite
actually work with Heimdal.. I'm planning to install the Heimdal KDC,
et al, and see what happens but if it ends up looking like it's a lot of
work then I might forgo that effort. I'm not sure it's really necessary
but I could be argued out of that position without too much effort. The
stated Heimdal goal is to be a re-implementation of MIT Kerberos and
these are all documented APIs with RFCs, after all.

I share your feeling that we could probably blow off Apple's built-in
GSSAPI. MacPorts offers both Heimdal and kerberos5, and I imagine
Homebrew has at least one of them, so Mac people could easily get
hold of newer implementations. But the BSDen are going to be a
problem.

Yeah. Unfortunate that Heimdal doesn't seem to really be moving forward
in terms of new development.

Thanks,

Stephen

[1]: https://github.com/heimdal/heimdal/commit/e0bb9c10cad0fd98245caecf8af8fca855b2df49

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#13)
Re: longfin missing gssapi_ext.h

I wrote:

* NetBSD's package collection[1] includes both Heimdal and MIT Kerberos
(mit-krb5). Apparently what's installed on at least some of the buildfarm
animals is the former.

Oh! New data: the core NetBSD OS includes a copy of Heimdal (looks
to be 7.7.0 in the 10.0_BETA sources). The installable package is
a slightly newer version, 7.8.0, but I think it's a very solid bet
that the relevant buildfarm animals are just using the core copy
and haven't installed the add-on package. Even if they had, it
would take some fooling around with include and link paths to pull
in the packaged version rather than the built-in one.

The exact same thing applies to FreeBSD, except that their in-core
Heimdal is ancient (1.5.2). Also, they do have MIT Kerberos
available as a package [1]https://ports.freebsd.org/cgi/ports.cgi?query=krb5&amp;stype=all&amp;sektion=all. I'd been misled by the lack of a hit
on "kerberos", but "krb5" finds it. Our code does compile against
that version of Heimdal, but src/test/kerberos/ refuses to try to
run.

I've not dug into OpenBSD any further.

regards, tom lane

[1]: https://ports.freebsd.org/cgi/ports.cgi?query=krb5&amp;stype=all&amp;sektion=all

#16Thomas Munro
thomas.munro@gmail.com
In reply to: Tom Lane (#15)
Re: longfin missing gssapi_ext.h

On Sun, Apr 9, 2023 at 6:40 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

The exact same thing applies to FreeBSD, except that their in-core
Heimdal is ancient (1.5.2). Also, they do have MIT Kerberos
available as a package [1]. I'd been misled by the lack of a hit
on "kerberos", but "krb5" finds it. Our code does compile against
that version of Heimdal, but src/test/kerberos/ refuses to try to
run.

FWIW my FBSD animal elver has krb5 installed. Sorry it wasn't running
when the relevant commit landed. Stupid network cable wriggled out.

#17Stephen Frost
sfrost@snowman.net
In reply to: Thomas Munro (#16)
Re: longfin missing gssapi_ext.h

Greetings,

* Thomas Munro (thomas.munro@gmail.com) wrote:

On Sun, Apr 9, 2023 at 6:40 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

The exact same thing applies to FreeBSD, except that their in-core
Heimdal is ancient (1.5.2). Also, they do have MIT Kerberos
available as a package [1]. I'd been misled by the lack of a hit
on "kerberos", but "krb5" finds it. Our code does compile against
that version of Heimdal, but src/test/kerberos/ refuses to try to
run.

FWIW my FBSD animal elver has krb5 installed. Sorry it wasn't running
when the relevant commit landed. Stupid network cable wriggled out.

Yeah, I wouldn't be the least bit surprised if many folks running
FreeBSD with any interest in Kerberos have MIT Kerberos installed given
that Heimdal doesn't seem to be under any kind of ongoing active
development and is just in this maintenance mode.

Have you tried running the tests in src/test/kerberos with elver? Or is
it configured to run them? Would be awesome if it could be, or if
there's issues with running the tests on FBSD w/ MIT Kerberos, I'd be
happy to try and help work through them.

Thanks!

Stephen

#18Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#17)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Yeah, I wouldn't be the least bit surprised if many folks running
FreeBSD with any interest in Kerberos have MIT Kerberos installed given
that Heimdal doesn't seem to be under any kind of ongoing active
development and is just in this maintenance mode.

Yeah, that's a pretty scary situation for security-critical software.
Maybe we should just desupport Heimdal, rather than investing effort
to the contrary?

Also, the core-code versions of Heimdal in these BSDen are even scarier
than the upstream releases, so I'm thinking that the fact that we
currently compile against them is more a net negative than a positive.
(Same logic as for macOS, really.)

IOW, maybe it'd be okay to de-revert 3d4fa227b and add documentation
saying that --with-gssapi requires MIT Kerberos not Heimdal.

regards, tom lane

#19Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#18)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Yeah, I wouldn't be the least bit surprised if many folks running
FreeBSD with any interest in Kerberos have MIT Kerberos installed given
that Heimdal doesn't seem to be under any kind of ongoing active
development and is just in this maintenance mode.

Yeah, that's a pretty scary situation for security-critical software.

Agreed.

Maybe we should just desupport Heimdal, rather than investing effort
to the contrary?

As this is for a new major PG release, I'd be in support of that. I
would like to get the kerberos tests working on a FreeBSD buildfarm
animal with MIT Kerberos installed, if possible.

Also, the core-code versions of Heimdal in these BSDen are even scarier
than the upstream releases, so I'm thinking that the fact that we
currently compile against them is more a net negative than a positive.
(Same logic as for macOS, really.)

Agreed. Still, I wouldn't go and break it for minor releases, but for a
new major version saying we no longer support Heimdal seems reasonable.
Then folks have the usual 5-ish years (if they want to delay as long as
possible) to move to MIT Kerberos.

IOW, maybe it'd be okay to de-revert 3d4fa227b and add documentation
saying that --with-gssapi requires MIT Kerberos not Heimdal.

I'd be happy with that and can add the appropriate documentation noting
that we require MIT Kerberos. Presumably the appropriate step at this
point would be to check with the RMT?

Thanks!

Stephen

#20Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#19)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

IOW, maybe it'd be okay to de-revert 3d4fa227b and add documentation
saying that --with-gssapi requires MIT Kerberos not Heimdal.

I'd be happy with that and can add the appropriate documentation noting
that we require MIT Kerberos. Presumably the appropriate step at this
point would be to check with the RMT?

Yeah, RMT would have final say at this stage.

If you pull the trigger, a note to buildfarm-members would be
appropriate too, so people will know if they need to remove
--with-gssapi from animal configurations.

regards, tom lane

#21Jonathan S. Katz
jkatz@postgresql.org
In reply to: Tom Lane (#20)
Re: longfin missing gssapi_ext.h

On 4/10/23 11:37 AM, Tom Lane wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

IOW, maybe it'd be okay to de-revert 3d4fa227b and add documentation
saying that --with-gssapi requires MIT Kerberos not Heimdal.

I'd be happy with that and can add the appropriate documentation noting
that we require MIT Kerberos. Presumably the appropriate step at this
point would be to check with the RMT?

Yeah, RMT would have final say at this stage.

If you pull the trigger, a note to buildfarm-members would be
appropriate too, so people will know if they need to remove
--with-gssapi from animal configurations.

The RMT discussed this thread and agrees to a "de-revert" of the "Add
support for Kerberos delegation" patch, provided that:

1. The appropriate documentation is added AND
2. The de-revert occurs no later than 2023-04-15 (upper bound 2023-04-16
0:00 AoE).

There's an open item[1]https://wiki.postgresql.org/wiki/PostgreSQL_16_Open_Items#Open_Issues for this task.

Thanks,

Jonathan

[1]: https://wiki.postgresql.org/wiki/PostgreSQL_16_Open_Items#Open_Issues

#22Thomas Munro
thomas.munro@gmail.com
In reply to: Stephen Frost (#17)
Re: longfin missing gssapi_ext.h

On Tue, Apr 11, 2023 at 2:31 AM Stephen Frost <sfrost@snowman.net> wrote:

Have you tried running the tests in src/test/kerberos with elver? Or is
it configured to run them? Would be awesome if it could be, or if
there's issues with running the tests on FBSD w/ MIT Kerberos, I'd be
happy to try and help work through them.

I'm also happy to test/help/improve the animal/teach CI to do
it/whatever. I've made a note to test out the reverted commit later
today when I'll be in front of the right computers.

#23Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#22)
Re: longfin missing gssapi_ext.h

On Tue, Apr 11, 2023 at 2:53 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Tue, Apr 11, 2023 at 2:31 AM Stephen Frost <sfrost@snowman.net> wrote:

Have you tried running the tests in src/test/kerberos with elver? Or is
it configured to run them? Would be awesome if it could be, or if
there's issues with running the tests on FBSD w/ MIT Kerberos, I'd be
happy to try and help work through them.

Oh, the FreeBSD CI already runs the kerberos test and it uses the krb5
package, because the image it uses installs that[1]https://github.com/anarazel/pg-vm-images/blob/main/packer/freebsd.pkr.hcl and at least the
Meson build automatically prefers that over Heimdal. So the CI in the
postgres/postgres account tested it[2]https://cirrus-ci.com/task/6378179762323456?logs=test_world#L43 as soon as you committed, and
cfbot was testing all along in the commitfest. It's not skipped and
that test would clearly BAIL_OUT if it detected Heimdal. Is that good
enough?

I have OpenBSD and NetBSD vagrant images around, do you want me to
test those too?

As for elver, I remembered an unfortunate detail: it doesn't currently
have kerberos enabled in PG_TEXT_EXTRA, because it the test depends on
localhost being 127.0.0.1 which isn't quite true on this box
(container tech with an unusual network stack, long boring story) and
I hadn't got around to figuring out what to do about that. I can look
into it if you want, or perhaps you are satisfied with CI proving that
FreeBSD likes your patch.

[1]: https://github.com/anarazel/pg-vm-images/blob/main/packer/freebsd.pkr.hcl
[2]: https://cirrus-ci.com/task/6378179762323456?logs=test_world#L43

#24Stephen Frost
sfrost@snowman.net
In reply to: Jonathan S. Katz (#21)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Jonathan S. Katz (jkatz@postgresql.org) wrote:

On 4/10/23 11:37 AM, Tom Lane wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

IOW, maybe it'd be okay to de-revert 3d4fa227b and add documentation
saying that --with-gssapi requires MIT Kerberos not Heimdal.

I'd be happy with that and can add the appropriate documentation noting
that we require MIT Kerberos. Presumably the appropriate step at this
point would be to check with the RMT?

Yeah, RMT would have final say at this stage.

If you pull the trigger, a note to buildfarm-members would be
appropriate too, so people will know if they need to remove
--with-gssapi from animal configurations.

The RMT discussed this thread and agrees to a "de-revert" of the "Add
support for Kerberos delegation" patch, provided that:

1. The appropriate documentation is added AND
2. The de-revert occurs no later than 2023-04-15 (upper bound 2023-04-16
0:00 AoE).

There's an open item[1] for this task.

Understood. Please find attached the updated patch with changes to the
commit message to indicate that we now require MIT Kerberos, an
additional explicit check for gssapi_ext.h in configure.ac/configure,
along with updated documentation explicitly saying we require MIT
Kerberos for GSSAPI support.

I'll plan to push this tomorrow.

Of course, suggestions/improvements on documentation or anything else
always welcome.

Thanks all!

Stephen

Attachments:

gss_delegation_v11.patchtext/x-diff; charset=us-asciiDownload
From c485794516e9aea2274d8c2c782945d4a10e33bb Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Sat, 8 Apr 2023 08:08:19 -0400
Subject: [PATCH] Add support for Kerberos credential delegation

Support GSSAPI/Kerberos credentials being delegated to the server by a
client.  With this, a user authenticating to PostgreSQL using Kerberos
(GSSAPI) credentials can choose to delegate their credentials to the
PostgreSQL server (which can choose to accept them, or not), allowing
the server to then use those delegated credentials to connect to
another service, such as with postgres_fdw or dblink or theoretically
any other service which is able to be authenticated using Kerberos.

Both postgres_fdw and dblink are changed to allow non-superuser
password-less connections but only when GSSAPI credentials have been
delegated to the server by the client and GSSAPI is used to
authenticate to the remote system.

As part of this, we now require MIT Kerberos when building with support
for GSSAPI.

Authors: Stephen Frost, Peifeng Qiu
Reviewed-By: David Christensen
Discussion: https://postgr.es/m/CO1PR05MB8023CC2CB575E0FAAD7DF4F8A8E29@CO1PR05MB8023.namprd05.prod.outlook.com
---
 configure                                     |  27 ++
 configure.ac                                  |   2 +
 contrib/dblink/dblink.c                       | 127 ++++---
 contrib/dblink/expected/dblink.out            |   4 +-
 contrib/postgres_fdw/connection.c             |  72 +++-
 .../postgres_fdw/expected/postgres_fdw.out    |  19 +-
 contrib/postgres_fdw/option.c                 |   6 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     |   3 +-
 doc/src/sgml/config.sgml                      |  17 +
 doc/src/sgml/dblink.sgml                      |   5 +-
 doc/src/sgml/installation.sgml                |  15 +-
 doc/src/sgml/libpq.sgml                       |  41 +++
 doc/src/sgml/monitoring.sgml                  |   9 +
 doc/src/sgml/postgres-fdw.sgml                |   7 +-
 src/backend/catalog/system_views.sql          |   3 +-
 src/backend/foreign/foreign.c                 |   1 +
 src/backend/libpq/auth.c                      |  13 +-
 src/backend/libpq/be-gssapi-common.c          |  53 +++
 src/backend/libpq/be-secure-gssapi.c          |  26 +-
 src/backend/utils/activity/backend_status.c   |   1 +
 src/backend/utils/adt/pgstatfuncs.c           |  21 +-
 src/backend/utils/init/postinit.c             |   8 +-
 src/backend/utils/misc/guc_tables.c           |  10 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/catalog/pg_proc.dat               |   6 +-
 src/include/libpq/auth.h                      |   1 +
 src/include/libpq/be-gssapi-common.h          |   3 +
 src/include/libpq/libpq-be.h                  |   2 +
 src/include/utils/backend_status.h            |   1 +
 src/interfaces/libpq/exports.txt              |   1 +
 src/interfaces/libpq/fe-auth.c                |  15 +-
 src/interfaces/libpq/fe-connect.c             |  17 +
 src/interfaces/libpq/fe-secure-gssapi.c       |  23 +-
 src/interfaces/libpq/libpq-fe.h               |   1 +
 src/interfaces/libpq/libpq-int.h              |   2 +
 src/test/kerberos/Makefile                    |   3 +
 src/test/kerberos/t/001_auth.pl               | 331 ++++++++++++++++--
 src/test/perl/PostgreSQL/Test/Utils.pm        |  27 ++
 src/test/regress/expected/rules.out           |  11 +-
 39 files changed, 792 insertions(+), 143 deletions(-)

diff --git a/configure b/configure
index dbea7eaf5f..08bcf8f43a 100755
--- a/configure
+++ b/configure
@@ -14104,6 +14104,33 @@ done
 
 fi
 
+done
+
+  for ac_header in gssapi/gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_ext.h" "ac_cv_header_gssapi_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  for ac_header in gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi_ext.h" "ac_cv_header_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  as_fn_error $? "gssapi_ext.h header file is required for GSSAPI" "$LINENO" 5
+fi
+
+done
+
+fi
+
 done
 
 fi
diff --git a/configure.ac b/configure.ac
index dda34304db..c53a9c788e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1562,6 +1562,8 @@ fi
 if test "$with_gssapi" = yes ; then
   AC_CHECK_HEADERS(gssapi/gssapi.h, [],
 	[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
+  AC_CHECK_HEADERS(gssapi/gssapi_ext.h, [],
+	[AC_CHECK_HEADERS(gssapi_ext.h, [], [AC_MSG_ERROR([gssapi_ext.h header file is required for GSSAPI])])])
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e..55f75eff36 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -48,6 +48,7 @@
 #include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "libpq-fe.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -111,7 +112,8 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
 static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
 static char *generate_relation_name(Relation rel);
 static void dblink_connstr_check(const char *connstr);
-static void dblink_security_check(PGconn *conn, remoteConn *rconn);
+static bool dblink_connstr_has_pw(const char *connstr);
+static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
 static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
 							 bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
 static char *get_connect_string(const char *servername);
@@ -213,7 +215,7 @@ dblink_get_conn(char *conname_or_str,
 					 errmsg("could not establish connection"),
 					 errdetail_internal("%s", msg)));
 		}
-		dblink_security_check(conn, rconn);
+		dblink_security_check(conn, rconn, connstr);
 		if (PQclientEncoding(conn) != GetDatabaseEncoding())
 			PQsetClientEncoding(conn, GetDatabaseEncodingName());
 		freeconn = true;
@@ -307,7 +309,7 @@ dblink_connect(PG_FUNCTION_ARGS)
 	}
 
 	/* check password actually used if not superuser */
-	dblink_security_check(conn, rconn);
+	dblink_security_check(conn, rconn, connstr);
 
 	/* attempt to set client encoding to match server encoding, if needed */
 	if (PQclientEncoding(conn) != GetDatabaseEncoding())
@@ -2584,64 +2586,99 @@ deleteConnection(const char *name)
 				 errmsg("undefined connection name")));
 }
 
+/*
+ * We need to make sure that the connection made used credentials
+ * which were provided by the user, so check what credentials were
+ * used to connect and then make sure that they came from the user.
+ */
 static void
-dblink_security_check(PGconn *conn, remoteConn *rconn)
+dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
 {
-	if (!superuser())
-	{
-		if (!PQconnectionUsedPassword(conn))
-		{
-			libpqsrv_disconnect(conn);
-			if (rconn)
-				pfree(rconn);
+	/* Superuser bypasses security check */
+	if (superuser())
+		return;
 
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed.")));
-		}
-	}
+	/* If password was used to connect, make sure it was one provided */
+	if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	/* If GSSAPI creds used to connect, make sure it was one delegated */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Otherwise, fail out */
+	libpqsrv_disconnect(conn);
+	if (rconn)
+		pfree(rconn);
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"),
+			 errhint("Ensure provided credentials match target server's authentication method.")));
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
- * prevents a password from being picked up from .pgpass, a service file,
- * the environment, etc.  We don't want the postgres user's passwords
- * to be accessible to non-superusers.
+ * Function to check if the connection string includes an explicit
+ * password, needed to ensure that non-superuser password-based auth
+ * is using a provided password and not one picked up from the
+ * environment.
  */
-static void
-dblink_connstr_check(const char *connstr)
+static bool
+dblink_connstr_has_pw(const char *connstr)
 {
-	if (!superuser())
-	{
-		PQconninfoOption *options;
-		PQconninfoOption *option;
-		bool		connstr_gives_password = false;
+	PQconninfoOption *options;
+	PQconninfoOption *option;
+	bool		connstr_gives_password = false;
 
-		options = PQconninfoParse(connstr, NULL);
-		if (options)
+	options = PQconninfoParse(connstr, NULL);
+	if (options)
+	{
+		for (option = options; option->keyword != NULL; option++)
 		{
-			for (option = options; option->keyword != NULL; option++)
+			if (strcmp(option->keyword, "password") == 0)
 			{
-				if (strcmp(option->keyword, "password") == 0)
+				if (option->val != NULL && option->val[0] != '\0')
 				{
-					if (option->val != NULL && option->val[0] != '\0')
-					{
-						connstr_gives_password = true;
-						break;
-					}
+					connstr_gives_password = true;
+					break;
 				}
 			}
-			PQconninfoFree(options);
 		}
-
-		if (!connstr_gives_password)
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superusers must provide a password in the connection string.")));
+		PQconninfoFree(options);
 	}
+
+	return connstr_gives_password;
+}
+
+/*
+ * For non-superusers, insist that the connstr specify a password, except
+ * if GSSAPI credentials have been delegated (and we check that they are used
+ * for the connection in dblink_security_check later).  This prevents a
+ * password or GSSAPI credentials from being picked up from .pgpass, a
+ * service file, the environment, etc.  We don't want the postgres user's
+ * passwords or Kerberos credentials to be accessible to non-superusers.
+ */
+static void
+dblink_connstr_check(const char *connstr)
+{
+	if (superuser())
+		return;
+
+	if (dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.")));
 }
 
 /*
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index 0f5050b409..7809f58d96 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -903,8 +903,8 @@ GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO regress_dblink_user;
 SET SESSION AUTHORIZATION regress_dblink_user;
 -- should fail
 SELECT dblink_connect('myconn', 'fdtest');
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the connection string.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.
 -- should succeed
 SELECT dblink_connect_u('myconn', 'fdtest');
  dblink_connect_u 
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 2969351e9a..75d93d6ead 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
 #include "funcapi.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -149,6 +150,8 @@ static void pgfdw_finish_pre_subcommit_cleanup(List *pending_entries,
 static void pgfdw_finish_abort_cleanup(List *pending_entries,
 									   List *cancel_requested,
 									   bool toplevel);
+static void pgfdw_security_check(const char **keywords, const char **values,
+								 UserMapping *user, PGconn *conn);
 static bool UserMappingPasswordRequired(UserMapping *user);
 static bool disconnect_cached_connections(Oid serverid);
 
@@ -384,6 +387,47 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
 		 entry->conn, server->servername, user->umid, user->userid);
 }
 
+/*
+ * Check that non-superuser has used password or delegated credentials
+ * to establish connection; otherwise, he's piggybacking on the
+ * postgres server's user identity. See also dblink_security_check()
+ * in contrib/dblink and check_conn_params.
+ */
+static void
+pgfdw_security_check(const char **keywords, const char **values, UserMapping *user, PGconn *conn)
+{
+	/* Superusers bypass the check */
+	if (superuser_arg(user->userid))
+		return;
+
+#ifdef ENABLE_GSS
+	/* Connected via GSSAPI with delegated credentials- all good. */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Ok if superuser set PW required false. */
+	if (!UserMappingPasswordRequired(user))
+		return;
+
+	/* Connected via PW, with PW required true, and provided non-empty PW. */
+	if (PQconnectionUsedPassword(conn))
+	{
+		/* ok if params contain a non-empty password */
+		for (int i = 0; keywords[i] != NULL; i++)
+		{
+			if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
+				return;
+		}
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials."),
+			 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+}
+
 /*
  * Connect to remote server using specified server and user mapping properties.
  */
@@ -495,19 +539,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 							server->servername),
 					 errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
 
-		/*
-		 * Check that non-superuser has used password to establish connection;
-		 * otherwise, he's piggybacking on the postgres server's user
-		 * identity. See also dblink_security_check() in contrib/dblink and
-		 * check_conn_params.
-		 */
-		if (!superuser_arg(user->userid) && UserMappingPasswordRequired(user) &&
-			!PQconnectionUsedPassword(conn))
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+		/* Perform post-connection security checks */
+		pgfdw_security_check(keywords, values, user, conn);
 
 		/* Prepare new session for use */
 		configure_remote_session(conn);
@@ -561,7 +594,8 @@ UserMappingPasswordRequired(UserMapping *user)
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
+ * For non-superusers, insist that the connstr specify a password or that the
+ * user provided their own GSSAPI delegated credentials.  This
  * prevents a password from being picked up from .pgpass, a service file, the
  * environment, etc.  We don't want the postgres user's passwords,
  * certificates, etc to be accessible to non-superusers.  (See also
@@ -576,6 +610,12 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 	if (superuser_arg(user->userid))
 		return;
 
+#ifdef ENABLE_GSS
+	/* ok if the user provided their own delegated credentials */
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
 	/* ok if params contain a non-empty password */
 	for (i = 0; keywords[i] != NULL; i++)
 	{
@@ -589,8 +629,8 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 
 	ereport(ERROR,
 			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-			 errmsg("password is required"),
-			 errdetail("Non-superusers must provide a password in the user mapping.")));
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.")));
 }
 
 /*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 8f6a04f71b..fd5752bd5b 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 -- Error, invalid list syntax
@@ -9840,8 +9841,8 @@ CREATE FOREIGN TABLE pg_temp.ft1_nopw (
 	c8 user_enum
 ) SERVER loopback_nopw OPTIONS (schema_name 'public', table_name 'ft1');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 -- If we add a password to the connstr it'll fail, because we don't allow passwords
 -- in connstrs only in user mappings.
 ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
@@ -9853,16 +9854,16 @@ HINT:  Perhaps you meant the option "passfile".
 -- This won't work with installcheck, but neither will most of the FDW checks.
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 -- Unpriv user cannot make the mapping passwordless
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password_required 'false');
 ERROR:  password_required=false is superuser-only
 HINT:  User mappings with the password_required option set to false may only be created or modified by the superuser.
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 RESET ROLE;
 -- But the superuser can
@@ -9890,8 +9891,8 @@ DROP USER MAPPING FOR CURRENT_USER SERVER loopback_nopw;
 -- This will fail again as it'll resolve the user mapping for public, which
 -- lacks password_required=false
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 RESET ROLE;
 -- The user mapping for public is passwordless and lacks the password_required=false
 -- mapping option, but will work because the current user is a superuser.
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 4229d2048c..fe40d50c6d 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -288,6 +288,12 @@ InitPgFdwOptions(void)
 		{"sslcert", UserMappingRelationId, true},
 		{"sslkey", UserMappingRelationId, true},
 
+		/*
+		 * gssdeleg is also a libpq option but should be allowed in a user
+		 * mapping context too
+		 */
+		{"gssdeleg", UserMappingRelationId, true},
+
 		{NULL, InvalidOid, false}
 	};
 
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5bd69339df..c05046f867 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f81c2045ec..091a79d4f3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1190,6 +1190,23 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-gss-accept-deleg" xreflabel="gss_accept_deleg">
+      <term><varname>gss_accept_deleg</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>gss_accept_deleg</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets whether GSSAPI delegation should be accepted from the client.
+        The default is <literal>off</literal> meaning credentials from the client will
+        NOT be accepted.  Changing this to <literal>on</literal> will make the server
+        accept credentials delegated to it from the client. This parameter can only be
+        set in the <filename>postgresql.conf</filename> file or on the server command line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-db-user-namespace" xreflabel="db_user_namespace">
       <term><varname>db_user_namespace</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 17f9d99b1c..7d25f24f49 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -117,8 +117,9 @@ dblink_connect(text connname, text connstr) returns text
 
    <para>
     Only superusers may use <function>dblink_connect</function> to create
-    non-password-authenticated connections.  If non-superusers need this
-    capability, use <function>dblink_connect_u</function> instead.
+    non-password-authenticated and non-GSSAPI-authenticated connections.
+    If non-superusers need this capability, use
+    <function>dblink_connect_u</function> instead.
    </para>
 
    <para>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index f451204854..ff89bd5f6d 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1048,9 +1048,9 @@ build-postgresql:
        <term><option>--with-gssapi</option></term>
        <listitem>
         <para>
-         Build with support for GSSAPI authentication. On many systems, the
-         GSSAPI system (usually a part of the Kerberos installation) is not
-         installed in a location
+         Build with support for GSSAPI authentication. MIT Kerberos is required
+         to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+         of the MIT Kerberos installation) is not installed in a location
          that is searched by default (e.g., <filename>/usr/include</filename>,
          <filename>/usr/lib</filename>), so you must use the options
          <option>--with-includes</option> and <option>--with-libraries</option> in
@@ -2497,10 +2497,11 @@ ninja install
       <term><option>-Dgssapi={ auto | enabled | disabled }</option></term>
       <listitem>
        <para>
-        Build with support for GSSAPI authentication. On many systems, the
-        GSSAPI system (usually a part of the Kerberos installation) is not
-        installed in a location that is searched by default (e.g.,
-        <filename>/usr/include</filename>, <filename>/usr/lib</filename>).  In
+        Build with support for GSSAPI authentication. MIT Kerberos is required
+        to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+        of the MIT Kerberos installation) is not installed in a location
+        that is searched by default (e.g., <filename>/usr/include</filename>,
+        <filename>/usr/lib</filename>).  In
         those cases, PostgreSQL will query <command>pkg-config</command> to
         detect the required compiler and linker options.  Defaults to auto.
         <filename>meson configure</filename> will check for the required
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index faa8aa3187..b8702284d0 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2054,6 +2054,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gssdeleg" xreflabel="gssdeleg">
+      <term><literal>gssdeleg</literal></term>
+      <listitem>
+       <para>
+        Forward (delegate) GSS credentials to the server.  The default is
+        <literal>disable</literal> which means credentials will not be forwarded
+        to the server.  Set this to <literal>enable</literal> to have
+        credentials forwarded when possible.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
@@ -2715,6 +2727,25 @@ int PQconnectionUsedPassword(const PGconn *conn);
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry id="libpq-PQconnectionUsedGSSAPI">
+     <term><function>PQconnectionUsedGSSAPI</function><indexterm><primary>PQconnectionUsedGSSAPI</primary></indexterm></term>
+     <listitem>
+      <para>
+       Returns true (1) if the connection authentication method
+       used GSSAPI. Returns false (0) if not.
+
+<synopsis>
+int PQconnectionUsedGSSAPI(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       This function can be applied to detect whether the connection was
+       authenticated with GSSAPI.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </para>
 
@@ -8237,6 +8268,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGGSSDELEG</envar></primary>
+      </indexterm>
+      <envar>PGGSSDELEG</envar> behaves the same as the <xref
+      linkend="libpq-connect-gssdeleg"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f33a1c56c..e8ab803267 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3573,6 +3573,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        True if GSSAPI encryption is in use on this connection
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>credentials_delegated</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if GSSAPI credentials were delegated on this connection.
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 9e66987cf7..281966f16f 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -169,9 +169,10 @@
     <literal>sslcert</literal> or <literal>sslkey</literal> settings.
    </para>
    <para>
-    Only superusers may connect to foreign servers without password
-    authentication, so always specify the <literal>password</literal> option
-    for user mappings belonging to non-superusers.
+    Non-superusers may connect to foreign servers using password
+    authentication or with GSSAPI delegated credentials, so specify the
+    <literal>password</literal> option for user mappings belonging to
+    non-superusers where password authentication is required.
    </para>
    <para>
     A superuser may override this check on a per-user-mapping basis by setting
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 701c340fc4..2129c916aa 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -979,7 +979,8 @@ CREATE VIEW pg_stat_gssapi AS
             S.pid,
             S.gss_auth AS gss_authenticated,
             S.gss_princ AS principal,
-            S.gss_enc AS encrypted
+            S.gss_enc AS encrypted,
+            S.gss_deleg AS credentials_delegated
     FROM pg_stat_get_activity(NULL) AS S
     WHERE S.client_port IS NOT NULL;
 
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index dca02271dc..6e1977fa62 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -574,6 +574,7 @@ static const struct ConnectionOption libpq_conninfo_options[] = {
 	{"requiressl", ForeignServerRelationId},
 	{"sslmode", ForeignServerRelationId},
 	{"gsslib", ForeignServerRelationId},
+	{"gssdeleg", ForeignServerRelationId},
 	{NULL, InvalidOid}
 };
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index bc0cf26b12..00ec9da284 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -165,6 +165,7 @@ static int	CheckCertAuth(Port *port);
  */
 char	   *pg_krb_server_keyfile;
 bool		pg_krb_caseins_users;
+bool		pg_gss_accept_deleg;
 
 
 /*----------------------------------------------------------------
@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
 	int			mtype;
 	StringInfoData buf;
 	gss_buffer_desc gbuf;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
@@ -947,6 +949,9 @@ pg_GSS_recvauth(Port *port)
 	 */
 	port->gss->ctx = GSS_C_NO_CONTEXT;
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Loop through GSSAPI message exchange. This exchange can consist of
 	 * multiple messages sent in both directions. First message is always from
@@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port)
 										  &port->gss->outbuf,
 										  &gflags,
 										  NULL,
-										  NULL);
+										  pg_gss_accept_deleg ? &delegated_creds : NULL);
 
 		/* gbuf no longer used */
 		pfree(buf.data);
@@ -1009,6 +1014,12 @@ pg_GSS_recvauth(Port *port)
 
 		CHECK_FOR_INTERRUPTS();
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		if (port->gss->outbuf.length != 0)
 		{
 			/*
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index fb39c760d8..64d41e5291 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -92,3 +92,56 @@ pg_GSS_error(const char *errmsg,
 			(errmsg_internal("%s", errmsg),
 			 errdetail_internal("%s: %s", msg_major, msg_minor)));
 }
+
+/*
+ * Store the credentials passed in into the memory cache for later usage.
+ *
+ * This allows credentials to be delegated to us for us to use to connect
+ * to other systems with, using, e.g. postgres_fdw or dblink.
+ */
+#define GSS_MEMORY_CACHE "MEMORY:"
+void
+pg_store_delegated_credential(gss_cred_id_t cred)
+{
+	OM_uint32	major,
+				minor;
+	gss_OID_set mech;
+	gss_cred_usage_t usage;
+	gss_key_value_element_desc cc;
+	gss_key_value_set_desc ccset;
+
+	cc.key = "ccache";
+	cc.value = GSS_MEMORY_CACHE;
+	ccset.count = 1;
+	ccset.elements = &cc;
+
+	/* Make the delegated credential only available to current process */
+	major = gss_store_cred_into(&minor,
+								cred,
+								GSS_C_INITIATE, /* credential only used for
+												 * starting libpq connection */
+								GSS_C_NULL_OID, /* store all */
+								true,	/* overwrite */
+								true,	/* make default */
+								&ccset,
+								&mech,
+								&usage);
+
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_store_cred", major, minor);
+	}
+
+	/* Credential stored, so we can release our credential handle. */
+	major = gss_release_cred(&minor, &cred);
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_release_cred", major, minor);
+	}
+
+	/*
+	 * Set KRB5CCNAME for this backend, so that later calls to
+	 * gss_acquire_cred will find the delegated credentials we stored.
+	 */
+	setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
+}
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 3b55f43199..73f8ce8554 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -497,6 +497,7 @@ secure_open_gssapi(Port *port)
 	bool		complete_next = false;
 	OM_uint32	major,
 				minor;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Allocate subsidiary Port data for GSSAPI operations.
@@ -504,6 +505,9 @@ secure_open_gssapi(Port *port)
 	port->gss = (pg_gssinfo *)
 		MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Allocate buffers and initialize state variables.  By malloc'ing the
 	 * buffers at this point, we avoid wasting static data space in processes
@@ -588,7 +592,8 @@ secure_open_gssapi(Port *port)
 									   GSS_C_NO_CREDENTIAL, &input,
 									   GSS_C_NO_CHANNEL_BINDINGS,
 									   &port->gss->name, NULL, &output, NULL,
-									   NULL, NULL);
+									   NULL, pg_gss_accept_deleg ? &delegated_creds : NULL);
+
 		if (GSS_ERROR(major))
 		{
 			pg_GSS_error(_("could not accept GSSAPI security context"),
@@ -605,6 +610,12 @@ secure_open_gssapi(Port *port)
 			complete_next = true;
 		}
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		/* Done handling the incoming packet, reset our buffer */
 		PqGSSRecvLength = 0;
 
@@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port)
 
 	return port->gss->princ;
 }
+
+/*
+ * Return if GSSAPI delegated credentials were included on this
+ * connection.
+ */
+bool
+be_gssapi_get_deleg(Port *port)
+{
+	if (!port || !port->gss)
+		return NULL;
+
+	return port->gss->delegated_creds;
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 608d01ea0d..391d5de043 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -384,6 +384,7 @@ pgstat_bestart(void)
 		lbeentry.st_gss = true;
 		lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
 		lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
+		lgssstatus.gss_deleg = be_gssapi_get_deleg(MyProcPort);
 		if (princ)
 			strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
 	}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ae180da4d0..e79b065d21 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS	30
+#define PG_STAT_GET_ACTIVITY_COLS	31
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			pfree(clipped_activity);
 
 			/* leader_pid */
-			nulls[28] = true;
+			nulls[29] = true;
 
 			proc = BackendPidGetProc(beentry->st_procpid);
 
@@ -432,8 +432,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				 */
 				if (leader && leader->pid != beentry->st_procpid)
 				{
-					values[28] = Int32GetDatum(leader->pid);
-					nulls[28] = false;
+					values[29] = Int32GetDatum(leader->pid);
+					nulls[29] = false;
 				}
 				else if (beentry->st_backendType == B_BG_WORKER)
 				{
@@ -441,8 +441,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 					if (leader_pid != InvalidPid)
 					{
-						values[28] = Int32GetDatum(leader_pid);
-						nulls[28] = false;
+						values[29] = Int32GetDatum(leader_pid);
+						nulls[29] = false;
 					}
 				}
 			}
@@ -600,6 +600,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				values[25] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
 				values[26] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
 				values[27] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
+				values[28] = BoolGetDatum(beentry->st_gssstatus->gss_deleg);	/* GSS credentials
+																				 * delegated */
 			}
 			else
 			{
@@ -607,11 +609,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				nulls[26] = true;	/* No GSS principal */
 				values[27] = BoolGetDatum(false);	/* GSS Encryption not in
 													 * use */
+				values[28] = BoolGetDatum(false);	/* GSS credentials not
+													 * delegated */
 			}
 			if (beentry->st_query_id == 0)
-				nulls[29] = true;
+				nulls[30] = true;
 			else
-				values[29] = UInt64GetDatum(beentry->st_query_id);
+				values[30] = UInt64GetDatum(beentry->st_query_id);
 		}
 		else
 		{
@@ -640,6 +644,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[27] = true;
 			nulls[28] = true;
 			nulls[29] = true;
+			nulls[30] = true;
 		}
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 60feae0f1b..5af87a7868 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -282,15 +282,17 @@ PerformAuthentication(Port *port)
 
 			if (princ)
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s, principal=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
 								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"),
 								 princ);
 			else
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
-								 be_gssapi_get_enc(port) ? _("yes") : _("no"));
+								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"));
 		}
 #endif
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1067537e74..cab3ddbe11 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -1727,6 +1727,16 @@ struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"gss_accept_deleg", PGC_SIGHUP, CONN_AUTH_AUTH,
+			gettext_noop("Sets whether GSSAPI delegation should be accepted from the client."),
+			NULL
+		},
+		&pg_gss_accept_deleg,
+		false,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
 			gettext_noop("Warn about backslash escapes in ordinary string literals."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e715aff3b8..dce5049bc2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -101,6 +101,7 @@
 # GSSAPI using Kerberos
 #krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
 #krb_caseins_users = off
+#gss_accept_deleg = off
 
 # - SSL -
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dcbc4c2eb5..b516cee8bd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5438,9 +5438,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}',
+  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,bool,int4,int8}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_deleg,leader_pid,query_id}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 9916c99df1..e4d0e38c1e 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -18,6 +18,7 @@
 
 extern PGDLLIMPORT char *pg_krb_server_keyfile;
 extern PGDLLIMPORT bool pg_krb_caseins_users;
+extern PGDLLIMPORT bool pg_gss_accept_deleg;
 extern PGDLLIMPORT char *pg_krb_realm;
 
 extern void ClientAuthentication(Port *port);
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
index facd24ff7f..0381f0ce77 100644
--- a/src/include/libpq/be-gssapi-common.h
+++ b/src/include/libpq/be-gssapi-common.h
@@ -18,13 +18,16 @@
 
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
+#include <gssapi_ext.h>
 #else
 #include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
 #endif
 
 extern void pg_GSS_error(const char *errmsg,
 						 OM_uint32 maj_stat, OM_uint32 min_stat);
 
+extern void pg_store_delegated_credential(gss_cred_id_t cred);
 #endif							/* ENABLE_GSS */
 
 #endif							/* BE_GSSAPI_COMMON_H */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ac6407e9f6..e9df4295e2 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -84,6 +84,7 @@ typedef struct
 								 * GSSAPI auth was not used */
 	bool		auth;			/* GSSAPI Authentication used */
 	bool		enc;			/* GSSAPI encryption in use */
+	bool		delegated_creds;	/* GSSAPI Delegated credentials */
 #endif
 } pg_gssinfo;
 #endif
@@ -328,6 +329,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tls_init_hook;
 extern bool be_gssapi_get_auth(Port *port);
 extern bool be_gssapi_get_enc(Port *port);
 extern const char *be_gssapi_get_princ(Port *port);
+extern bool be_gssapi_get_deleg(Port *port);
 
 /* Read and write to a GSSAPI-encrypted connection. */
 extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index f7bd83113a..9651cb1d0c 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -77,6 +77,7 @@ typedef struct PgBackendGSSStatus
 	char		gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
 	bool		gss_auth;		/* If GSSAPI authentication was used */
 	bool		gss_enc;		/* If encryption is being used */
+	bool		gss_deleg;		/* If credentials delegated */
 
 } PgBackendGSSStatus;
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index e8bcc88370..7ded77aff3 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -186,3 +186,4 @@ PQpipelineStatus          183
 PQsetTraceFlags           184
 PQmblenBounded            185
 PQsendFlushRequest        186
+PQconnectionUsedGSSAPI    187
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index b0550e6332..fe2634230a 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -58,7 +58,8 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 {
 	OM_uint32	maj_stat,
 				min_stat,
-				lmin_s;
+				lmin_s,
+				gss_flags = GSS_C_MUTUAL_FLAG;
 	gss_buffer_desc ginbuf;
 	gss_buffer_desc goutbuf;
 
@@ -92,12 +93,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 		ginbuf.value = NULL;
 	}
 
+	/* Only try to acquire credentials if GSS delegation isn't disabled. */
+	if (!pg_GSS_have_cred_cache(&conn->gcred))
+		conn->gcred = GSS_C_NO_CREDENTIAL;
+
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+		gss_flags |= GSS_C_DELEG_FLAG;
+
 	maj_stat = gss_init_sec_context(&min_stat,
-									GSS_C_NO_CREDENTIAL,
+									conn->gcred,
 									&conn->gctx,
 									conn->gtarg_nam,
 									GSS_C_NO_OID,
-									GSS_C_MUTUAL_FLAG,
+									gss_flags,
 									0,
 									GSS_C_NO_CHANNEL_BINDINGS,
 									(ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
@@ -139,6 +147,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 	{
 		conn->client_finished_auth = true;
 		gss_release_name(&lmin_s, &conn->gtarg_nam);
+		conn->gssapi_used = true;
 	}
 
 	return STATUS_OK;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 40fef0e2c8..fcd3d0d9a3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -343,6 +343,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"GSS-library", "", 7,	/* sizeof("gssapi") == 7 */
 	offsetof(struct pg_conn, gsslib)},
 
+	{"gssdeleg", "PGGSSDELEG", NULL, NULL,
+		"GSS-delegation", "", 8,	/* sizeof("disable") == 8 */
+	offsetof(struct pg_conn, gssdeleg)},
+
 	{"replication", NULL, NULL, NULL,
 		"Replication", "D", 5,
 	offsetof(struct pg_conn, replication)},
@@ -617,6 +621,7 @@ pqDropServerData(PGconn *conn)
 	conn->auth_req_received = false;
 	conn->client_finished_auth = false;
 	conn->password_needed = false;
+	conn->gssapi_used = false;
 	conn->write_failed = false;
 	free(conn->write_err_msg);
 	conn->write_err_msg = NULL;
@@ -4448,6 +4453,7 @@ freePGconn(PGconn *conn)
 	free(conn->gssencmode);
 	free(conn->krbsrvname);
 	free(conn->gsslib);
+	free(conn->gssdeleg);
 	free(conn->connip);
 	/* Note that conn->Pfdebug is not ours to close or free */
 	free(conn->write_err_msg);
@@ -7312,6 +7318,17 @@ PQconnectionUsedPassword(const PGconn *conn)
 		return false;
 }
 
+int
+PQconnectionUsedGSSAPI(const PGconn *conn)
+{
+	if (!conn)
+		return false;
+	if (conn->gssapi_used)
+		return true;
+	else
+		return false;
+}
+
 int
 PQclientEncoding(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 038e847b7e..bf87ae3fd1 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -477,7 +477,8 @@ pqsecure_open_gss(PGconn *conn)
 {
 	ssize_t		ret;
 	OM_uint32	major,
-				minor;
+				minor,
+				gss_flags = GSS_REQUIRED_FLAGS;
 	uint32		netlen;
 	PostgresPollingStatusType result;
 	gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
@@ -621,13 +622,30 @@ pqsecure_open_gss(PGconn *conn)
 	if (ret != STATUS_OK)
 		return PGRES_POLLING_FAILED;
 
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+	{
+		/* Acquire credentials if possbile */
+		if (conn->gcred == GSS_C_NO_CREDENTIAL)
+			(void) pg_GSS_have_cred_cache(&conn->gcred);
+
+		/*
+		 * We have credentials and gssdeleg is enabled, so request credential
+		 * delegation.  This may or may not actually result in credentials
+		 * being delegated- it depends on if the forwardable flag has been set
+		 * in the credential and if the server is configured to accept
+		 * delegated credentials.
+		 */
+		if (conn->gcred != GSS_C_NO_CREDENTIAL)
+			gss_flags |= GSS_C_DELEG_FLAG;
+	}
+
 	/*
 	 * Call GSS init context, either with an empty input, or with a complete
 	 * packet from the server.
 	 */
 	major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
 								 conn->gtarg_nam, GSS_C_NO_OID,
-								 GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+								 gss_flags, 0, 0, &input, NULL,
 								 &output, NULL, NULL);
 
 	/* GSS Init Sec Context uses the whole packet, so clear it */
@@ -647,6 +665,7 @@ pqsecure_open_gss(PGconn *conn)
 		 * to do GSS wrapping/unwrapping.
 		 */
 		conn->gssenc = true;
+		conn->gssapi_used = true;
 
 		/* Clean up */
 		gss_release_cred(&minor, &conn->gcred);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index f3d9220496..7476dbe0e9 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -354,6 +354,7 @@ extern int	PQbackendPID(const PGconn *conn);
 extern PGpipelineStatus PQpipelineStatus(const PGconn *conn);
 extern int	PQconnectionNeedsPassword(const PGconn *conn);
 extern int	PQconnectionUsedPassword(const PGconn *conn);
+extern int	PQconnectionUsedGSSAPI(const PGconn *conn);
 extern int	PQclientEncoding(const PGconn *conn);
 extern int	PQsetClientEncoding(PGconn *conn, const char *encoding);
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d93e976ca5..ce0167c1b6 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -404,6 +404,7 @@ struct pg_conn
 	char	   *krbsrvname;		/* Kerberos service name */
 	char	   *gsslib;			/* What GSS library to use ("gssapi" or
 								 * "sspi") */
+	char	   *gssdeleg;		/* Try to delegate GSS credentials? */
 	char	   *ssl_min_protocol_version;	/* minimum TLS protocol version */
 	char	   *ssl_max_protocol_version;	/* maximum TLS protocol version */
 	char	   *target_session_attrs;	/* desired session properties */
@@ -465,6 +466,7 @@ struct pg_conn
 	int			sversion;		/* server version, e.g. 70401 for 7.4.1 */
 	bool		auth_req_received;	/* true if any type of auth req received */
 	bool		password_needed;	/* true if server demanded a password */
+	bool		gssapi_used;	/* true if authenticated via gssapi */
 	bool		sigpipe_so;		/* have we masked SIGPIPE via SO_NOSIGPIPE? */
 	bool		sigpipe_flag;	/* can we mask SIGPIPE via MSG_NOSIGNAL? */
 	bool		write_failed;	/* have we had a write failure on sock? */
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
index 7765f3f93b..f460d2c0e7 100644
--- a/src/test/kerberos/Makefile
+++ b/src/test/kerberos/Makefile
@@ -13,6 +13,9 @@ subdir = src/test/kerberos
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
+EXTRA_INSTALL += contrib/postgres_fdw
+EXTRA_INSTALL += contrib/dblink
+
 export with_gssapi with_krb_srvnam
 
 check:
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 458246b4d7..bf12752529 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -7,6 +7,9 @@
 # that the server-side pg_stat_gssapi view reports what we expect to
 # see for each test and that SYSTEM_USER returns what we expect to see.
 #
+# Also test that GSSAPI delegation is working properly and that those
+# credentials can be used to make dblink / postgres_fdw connections.
+#
 # Since this requires setting up a full KDC, it doesn't make much sense
 # to have multiple test scripts (since they'd have to also create their
 # own KDC and that could cause race conditions or other problems)- so
@@ -56,6 +59,7 @@ elsif ($^O eq 'linux')
 
 my $krb5_config  = 'krb5-config';
 my $kinit        = 'kinit';
+my $klist        = 'klist';
 my $kdb5_util    = 'kdb5_util';
 my $kadmin_local = 'kadmin.local';
 my $krb5kdc      = 'krb5kdc';
@@ -64,6 +68,7 @@ if ($krb5_bin_dir && -d $krb5_bin_dir)
 {
 	$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
 	$kinit       = $krb5_bin_dir . '/' . $kinit;
+	$klist       = $krb5_bin_dir . '/' . $klist;
 }
 if ($krb5_sbin_dir && -d $krb5_sbin_dir)
 {
@@ -86,6 +91,8 @@ my $kdc_datadir = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc";
 my $kdc_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc.pid";
 my $keytab      = "${PostgreSQL::Test::Utils::tmp_check}/krb5.keytab";
 
+my $pgpass      = "${PostgreSQL::Test::Utils::tmp_check}/.pgpass";
+
 my $dbname      = 'postgres';
 my $username    = 'test1';
 my $application = '001_auth.pl';
@@ -100,6 +107,14 @@ $stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
   or BAIL_OUT("could not get Kerberos version");
 $krb5_version = $1;
 
+# Construct a pgpass file to make sure we don't use it
+append_to_file(
+	$pgpass,
+	'*:*:*:*:abc123'
+);
+
+chmod 0600, $pgpass;
+
 # Build the krb5.conf to use.
 #
 # Explicitly specify the default (test) realm and the KDC for
@@ -126,12 +141,14 @@ kdc = FILE:$kdc_log
 dns_lookup_realm = false
 dns_lookup_kdc = false
 default_realm = $realm
+forwardable = false
 rdns = false
 
 [realms]
 $realm = {
     kdc = $hostaddr:$kdc_port
-}!);
+}
+!);
 
 append_to_file(
 	$kdc_conf,
@@ -204,7 +221,28 @@ lc_messages = 'C'
 });
 $node->start;
 
+my $port = $node->port();
+
 $node->safe_psql('postgres', 'CREATE USER test1;');
+$node->safe_psql('postgres', "CREATE USER test2 WITH ENCRYPTED PASSWORD 'abc123';");
+$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;');
+$node->safe_psql('postgres', 'CREATE EXTENSION dblink;');
+$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'postgres');");
+$node->safe_psql('postgres', "CREATE SERVER s2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (port '$port', dbname 'postgres', passfile '$pgpass');");
+
+$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;');
+
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTIONS (user 'test1');");
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s2 OPTIONS (user 'test2');");
+
+$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);");
+$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);");
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;");
+$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;");
+
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf2 (c1 int) SERVER s2 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON tf2 TO test1;");
 
 # Set up a table for SYSTEM_USER parallel worker testing.
 $node->safe_psql('postgres',
@@ -271,12 +309,16 @@ sub test_query
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss map=mymap});
+	qq{
+local all test2 scram-sha-256
+host all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
 
 run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
 
 test_access(
 	$node,
@@ -294,35 +336,58 @@ $node->restart;
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'',
-	'succeeds with mapping with default gssencmode and host hba',
+	'succeeds with mapping with default gssencmode and host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred with host hba',
+	'succeeds with GSS-encrypted access preferred with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
+
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=require',
-	'succeeds with GSS-encrypted access required with host hba',
+	'succeeds with GSS-encrypted access required with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+
 # Test that we can transport a reasonable amount of data.
 test_query(
 	$node,
@@ -389,29 +454,164 @@ test_query(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostgssenc all all $hostaddr/32 gss map=mymap
+});
+
+string_replace_file($krb5_conf, "forwardable = false", "forwardable = true");
+
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=off});
+$node->restart;
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=on});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred and hostgssenc hba',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=require',
-	'succeeds with GSS-encrypted access required and hostgssenc hba',
+	'gssencmode=require gssdeleg=disable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials explicitly not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+my $psql_out = '';
+my $psql_stderr = '';
+my $psql_rc = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink attempt fails without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','dblink does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf1;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials and with passfile');
+
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
 	'fails with GSS encryption disabled and hostgssenc hba');
 
@@ -427,54 +627,123 @@ $node->connect_ok(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostnogssenc all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=prefer',
+	'gssencmode=prefer gssdeleg=enable',
 	'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
 );
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=require',
 	'fails with GSS-encrypted access required and hostnogssenc hba');
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=disable',
+	'gssencmode=disable gssdeleg=enable',
 	'succeeds with GSS encryption disabled and hostnogssenc hba',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
+);
+
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'dblink works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'postgres_fdw works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work with delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work with delegated credentials and with passfile');
 
 truncate($node->data_dir . '/pg_ident.conf', 0);
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'',
+	'gssdeleg=enable',
 	'succeeds with include_realm=0 and defaults',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
 );
 
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'dblink works encrypted');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'postgres_fdw works encrypted');
+
 # Reset pg_hba.conf, and cause a usermap failure with an authentication
 # that has passed.
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG
+});
 $node->restart;
 
 test_access(
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 878e12b15e..9249954b49 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -65,6 +65,7 @@ our @EXPORT = qw(
   slurp_dir
   slurp_file
   append_to_file
+  string_replace_file
   check_mode_recursive
   chmod_recursive
   check_pg_config
@@ -549,6 +550,32 @@ sub append_to_file
 
 =pod
 
+=item string_replace_file(filename, find, replace)
+
+Find and replace string of a given file.
+
+=cut
+
+sub string_replace_file
+{
+	my ($filename, $find, $replace) = @_;
+	open(my $in, '<', $filename);
+	my $content;
+	while(<$in>)
+	{
+		$_ =~ s/$find/$replace/;
+		$content = $content.$_;
+	}
+	close $in;
+	open(my $out, '>', $filename);
+	print $out $content;
+	close($out);
+
+	return;
+}
+
+=pod
+
 =item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
 
 Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2d75dd6656..919d947ec0 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1760,7 +1760,7 @@ pg_stat_activity| SELECT s.datid,
     s.query_id,
     s.query,
     s.backend_type
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1876,8 +1876,9 @@ pg_stat_database_conflicts| SELECT oid AS datid,
 pg_stat_gssapi| SELECT pid,
     gss_auth AS gss_authenticated,
     gss_princ AS principal,
-    gss_enc AS encrypted
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+    gss_enc AS encrypted,
+    gss_deleg AS credentials_delegated
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_io| SELECT backend_type,
     io_object,
@@ -2075,7 +2076,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     w.reply_time
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_replication_slots| SELECT s.slot_name,
@@ -2109,7 +2110,7 @@ pg_stat_ssl| SELECT pid,
     ssl_client_dn AS client_dn,
     ssl_client_serial AS client_serial,
     ssl_issuer_dn AS issuer_dn
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,
-- 
2.34.1

#25Justin Pryzby
pryzby@telsasoft.com
In reply to: Stephen Frost (#24)
Re: longfin missing gssapi_ext.h

configure | 27 ++
configure.ac | 2 +

Does meson.build need the corresponding change ?

#26Stephen Frost
sfrost@snowman.net
In reply to: Justin Pryzby (#25)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Justin Pryzby (pryzby@telsasoft.com) wrote:

configure | 27 ++
configure.ac | 2 +

Does meson.build need the corresponding change ?

Ah, yes, presumably.

Something like the attached?

Thanks,

Stephen

Attachments:

krb_meson_check.patchtext/x-diff; charset=us-asciiDownload
diff --git a/meson.build b/meson.build
index b69aaddb1f..a171e6dc74 100644
--- a/meson.build
+++ b/meson.build
@@ -619,6 +619,11 @@ if not gssapiopt.disabled()
     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
     cdata.set('HAVE_GSSAPI_H', 1)
+  elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
+      args: test_c_args, include_directories: postgres_inc)
+    cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
+  elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
+    cdata.set('HAVE_GSSAPI_EXT_H', 1)
   else
     have_gssapi = false
   endif
#27Stephen Frost
sfrost@snowman.net
In reply to: Stephen Frost (#26)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Stephen Frost (sfrost@snowman.net) wrote:

Greetings,

* Justin Pryzby (pryzby@telsasoft.com) wrote:

configure | 27 ++
configure.ac | 2 +

Does meson.build need the corresponding change ?

Ah, yes, presumably.

No, more like attached actually. Picks up on the dependency properly
with this when I ran meson/ninja, at least.

I'll include this then (along with any other suggestions, of course).

Thanks!

Stephen

Attachments:

krb_meson_check_v2.patchtext/x-diff; charset=us-asciiDownload
diff --git a/meson.build b/meson.build
index b69aaddb1f..3405cc07ee 100644
--- a/meson.build
+++ b/meson.build
@@ -623,6 +623,16 @@ if not gssapiopt.disabled()
     have_gssapi = false
   endif
 
+  if not have_gssapi
+  elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
+      args: test_c_args, include_directories: postgres_inc)
+    cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
+  elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
+    cdata.set('HAVE_GSSAPI_EXT_H', 1)
+  else
+    have_gssapi = false
+  endif
+
   if not have_gssapi
   elif cc.has_function('gss_init_sec_context', dependencies: gssapi,
       args: test_c_args, include_directories: postgres_inc)
#28Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#24)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Understood. Please find attached the updated patch with changes to the
commit message to indicate that we now require MIT Kerberos, an
additional explicit check for gssapi_ext.h in configure.ac/configure,
along with updated documentation explicitly saying we require MIT
Kerberos for GSSAPI support.

Um ... could you package this as a straight un-revert of the
previous commit, then a delta patch? Would be easier to review.

regards, tom lane

#29Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#28)
2 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Understood. Please find attached the updated patch with changes to the
commit message to indicate that we now require MIT Kerberos, an
additional explicit check for gssapi_ext.h in configure.ac/configure,
along with updated documentation explicitly saying we require MIT
Kerberos for GSSAPI support.

Um ... could you package this as a straight un-revert of the
previous commit, then a delta patch? Would be easier to review.

Sure, reworked that way and attached.

Thanks,

Stephen

Attachments:

0001-Revert-Revert-Add-support-for-Kerberos-credential-de.patchtext/x-diff; charset=us-asciiDownload
From 9acb2ef2e6a35544e28d690978ba8d5e8c062f7e Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 12 Apr 2023 09:33:39 -0400
Subject: [PATCH 1/2] Revert "Revert "Add support for Kerberos credential
 delegation""

This reverts commit 3d03b24c3 (Add support for Kerberos credential
delegation) which was reverted on the grounds of concern about
portability, but on further review and discussion, it's clear that
we are better off simply explicitly requiring MIT Kerberos as that seems
to be the only GSSAPI library currently that's under proper maintenance
and ongoing development.  The API used for storing credentials was added
to MIT Kerberos over a decade ago while for the other libraries which
appear to be mainly based on Heimdal, which exists explicitly to be a
re-implementation of MIT Kerberos, the API never made it to a released
version (even though it was added to the Heimdal git repo over 5 years
ago..).

This post-feature-freeze change was approved by the RMT.

Discussion: https://postgr.es/m/ZDDO6jaESKaBgej0%40tamriel.snowman.net
---
 contrib/dblink/dblink.c                       | 127 ++++---
 contrib/dblink/expected/dblink.out            |   4 +-
 contrib/postgres_fdw/connection.c             |  72 +++-
 .../postgres_fdw/expected/postgres_fdw.out    |  19 +-
 contrib/postgres_fdw/option.c                 |   6 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     |   3 +-
 doc/src/sgml/config.sgml                      |  17 +
 doc/src/sgml/dblink.sgml                      |   5 +-
 doc/src/sgml/libpq.sgml                       |  41 +++
 doc/src/sgml/monitoring.sgml                  |   9 +
 doc/src/sgml/postgres-fdw.sgml                |   7 +-
 src/backend/catalog/system_views.sql          |   3 +-
 src/backend/foreign/foreign.c                 |   1 +
 src/backend/libpq/auth.c                      |  13 +-
 src/backend/libpq/be-gssapi-common.c          |  53 +++
 src/backend/libpq/be-secure-gssapi.c          |  26 +-
 src/backend/utils/activity/backend_status.c   |   1 +
 src/backend/utils/adt/pgstatfuncs.c           |  21 +-
 src/backend/utils/init/postinit.c             |   8 +-
 src/backend/utils/misc/guc_tables.c           |  10 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/catalog/pg_proc.dat               |   6 +-
 src/include/libpq/auth.h                      |   1 +
 src/include/libpq/be-gssapi-common.h          |   3 +
 src/include/libpq/libpq-be.h                  |   2 +
 src/include/utils/backend_status.h            |   1 +
 src/interfaces/libpq/exports.txt              |   1 +
 src/interfaces/libpq/fe-auth.c                |  15 +-
 src/interfaces/libpq/fe-connect.c             |  17 +
 src/interfaces/libpq/fe-secure-gssapi.c       |  23 +-
 src/interfaces/libpq/libpq-fe.h               |   1 +
 src/interfaces/libpq/libpq-int.h              |   2 +
 src/test/kerberos/Makefile                    |   3 +
 src/test/kerberos/t/001_auth.pl               | 331 ++++++++++++++++--
 src/test/perl/PostgreSQL/Test/Utils.pm        |  27 ++
 src/test/regress/expected/rules.out           |  11 +-
 36 files changed, 755 insertions(+), 136 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e..55f75eff36 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -48,6 +48,7 @@
 #include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "libpq-fe.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -111,7 +112,8 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
 static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
 static char *generate_relation_name(Relation rel);
 static void dblink_connstr_check(const char *connstr);
-static void dblink_security_check(PGconn *conn, remoteConn *rconn);
+static bool dblink_connstr_has_pw(const char *connstr);
+static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
 static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
 							 bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
 static char *get_connect_string(const char *servername);
@@ -213,7 +215,7 @@ dblink_get_conn(char *conname_or_str,
 					 errmsg("could not establish connection"),
 					 errdetail_internal("%s", msg)));
 		}
-		dblink_security_check(conn, rconn);
+		dblink_security_check(conn, rconn, connstr);
 		if (PQclientEncoding(conn) != GetDatabaseEncoding())
 			PQsetClientEncoding(conn, GetDatabaseEncodingName());
 		freeconn = true;
@@ -307,7 +309,7 @@ dblink_connect(PG_FUNCTION_ARGS)
 	}
 
 	/* check password actually used if not superuser */
-	dblink_security_check(conn, rconn);
+	dblink_security_check(conn, rconn, connstr);
 
 	/* attempt to set client encoding to match server encoding, if needed */
 	if (PQclientEncoding(conn) != GetDatabaseEncoding())
@@ -2584,64 +2586,99 @@ deleteConnection(const char *name)
 				 errmsg("undefined connection name")));
 }
 
+/*
+ * We need to make sure that the connection made used credentials
+ * which were provided by the user, so check what credentials were
+ * used to connect and then make sure that they came from the user.
+ */
 static void
-dblink_security_check(PGconn *conn, remoteConn *rconn)
+dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
 {
-	if (!superuser())
-	{
-		if (!PQconnectionUsedPassword(conn))
-		{
-			libpqsrv_disconnect(conn);
-			if (rconn)
-				pfree(rconn);
+	/* Superuser bypasses security check */
+	if (superuser())
+		return;
 
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed.")));
-		}
-	}
+	/* If password was used to connect, make sure it was one provided */
+	if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	/* If GSSAPI creds used to connect, make sure it was one delegated */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Otherwise, fail out */
+	libpqsrv_disconnect(conn);
+	if (rconn)
+		pfree(rconn);
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"),
+			 errhint("Ensure provided credentials match target server's authentication method.")));
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
- * prevents a password from being picked up from .pgpass, a service file,
- * the environment, etc.  We don't want the postgres user's passwords
- * to be accessible to non-superusers.
+ * Function to check if the connection string includes an explicit
+ * password, needed to ensure that non-superuser password-based auth
+ * is using a provided password and not one picked up from the
+ * environment.
  */
-static void
-dblink_connstr_check(const char *connstr)
+static bool
+dblink_connstr_has_pw(const char *connstr)
 {
-	if (!superuser())
-	{
-		PQconninfoOption *options;
-		PQconninfoOption *option;
-		bool		connstr_gives_password = false;
+	PQconninfoOption *options;
+	PQconninfoOption *option;
+	bool		connstr_gives_password = false;
 
-		options = PQconninfoParse(connstr, NULL);
-		if (options)
+	options = PQconninfoParse(connstr, NULL);
+	if (options)
+	{
+		for (option = options; option->keyword != NULL; option++)
 		{
-			for (option = options; option->keyword != NULL; option++)
+			if (strcmp(option->keyword, "password") == 0)
 			{
-				if (strcmp(option->keyword, "password") == 0)
+				if (option->val != NULL && option->val[0] != '\0')
 				{
-					if (option->val != NULL && option->val[0] != '\0')
-					{
-						connstr_gives_password = true;
-						break;
-					}
+					connstr_gives_password = true;
+					break;
 				}
 			}
-			PQconninfoFree(options);
 		}
-
-		if (!connstr_gives_password)
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superusers must provide a password in the connection string.")));
+		PQconninfoFree(options);
 	}
+
+	return connstr_gives_password;
+}
+
+/*
+ * For non-superusers, insist that the connstr specify a password, except
+ * if GSSAPI credentials have been delegated (and we check that they are used
+ * for the connection in dblink_security_check later).  This prevents a
+ * password or GSSAPI credentials from being picked up from .pgpass, a
+ * service file, the environment, etc.  We don't want the postgres user's
+ * passwords or Kerberos credentials to be accessible to non-superusers.
+ */
+static void
+dblink_connstr_check(const char *connstr)
+{
+	if (superuser())
+		return;
+
+	if (dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.")));
 }
 
 /*
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index 0f5050b409..7809f58d96 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -903,8 +903,8 @@ GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO regress_dblink_user;
 SET SESSION AUTHORIZATION regress_dblink_user;
 -- should fail
 SELECT dblink_connect('myconn', 'fdtest');
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the connection string.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.
 -- should succeed
 SELECT dblink_connect_u('myconn', 'fdtest');
  dblink_connect_u 
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 2969351e9a..75d93d6ead 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
 #include "funcapi.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -149,6 +150,8 @@ static void pgfdw_finish_pre_subcommit_cleanup(List *pending_entries,
 static void pgfdw_finish_abort_cleanup(List *pending_entries,
 									   List *cancel_requested,
 									   bool toplevel);
+static void pgfdw_security_check(const char **keywords, const char **values,
+								 UserMapping *user, PGconn *conn);
 static bool UserMappingPasswordRequired(UserMapping *user);
 static bool disconnect_cached_connections(Oid serverid);
 
@@ -384,6 +387,47 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
 		 entry->conn, server->servername, user->umid, user->userid);
 }
 
+/*
+ * Check that non-superuser has used password or delegated credentials
+ * to establish connection; otherwise, he's piggybacking on the
+ * postgres server's user identity. See also dblink_security_check()
+ * in contrib/dblink and check_conn_params.
+ */
+static void
+pgfdw_security_check(const char **keywords, const char **values, UserMapping *user, PGconn *conn)
+{
+	/* Superusers bypass the check */
+	if (superuser_arg(user->userid))
+		return;
+
+#ifdef ENABLE_GSS
+	/* Connected via GSSAPI with delegated credentials- all good. */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Ok if superuser set PW required false. */
+	if (!UserMappingPasswordRequired(user))
+		return;
+
+	/* Connected via PW, with PW required true, and provided non-empty PW. */
+	if (PQconnectionUsedPassword(conn))
+	{
+		/* ok if params contain a non-empty password */
+		for (int i = 0; keywords[i] != NULL; i++)
+		{
+			if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
+				return;
+		}
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials."),
+			 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+}
+
 /*
  * Connect to remote server using specified server and user mapping properties.
  */
@@ -495,19 +539,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 							server->servername),
 					 errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
 
-		/*
-		 * Check that non-superuser has used password to establish connection;
-		 * otherwise, he's piggybacking on the postgres server's user
-		 * identity. See also dblink_security_check() in contrib/dblink and
-		 * check_conn_params.
-		 */
-		if (!superuser_arg(user->userid) && UserMappingPasswordRequired(user) &&
-			!PQconnectionUsedPassword(conn))
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+		/* Perform post-connection security checks */
+		pgfdw_security_check(keywords, values, user, conn);
 
 		/* Prepare new session for use */
 		configure_remote_session(conn);
@@ -561,7 +594,8 @@ UserMappingPasswordRequired(UserMapping *user)
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
+ * For non-superusers, insist that the connstr specify a password or that the
+ * user provided their own GSSAPI delegated credentials.  This
  * prevents a password from being picked up from .pgpass, a service file, the
  * environment, etc.  We don't want the postgres user's passwords,
  * certificates, etc to be accessible to non-superusers.  (See also
@@ -576,6 +610,12 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 	if (superuser_arg(user->userid))
 		return;
 
+#ifdef ENABLE_GSS
+	/* ok if the user provided their own delegated credentials */
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
 	/* ok if params contain a non-empty password */
 	for (i = 0; keywords[i] != NULL; i++)
 	{
@@ -589,8 +629,8 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 
 	ereport(ERROR,
 			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-			 errmsg("password is required"),
-			 errdetail("Non-superusers must provide a password in the user mapping.")));
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.")));
 }
 
 /*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 8f6a04f71b..fd5752bd5b 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 -- Error, invalid list syntax
@@ -9840,8 +9841,8 @@ CREATE FOREIGN TABLE pg_temp.ft1_nopw (
 	c8 user_enum
 ) SERVER loopback_nopw OPTIONS (schema_name 'public', table_name 'ft1');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 -- If we add a password to the connstr it'll fail, because we don't allow passwords
 -- in connstrs only in user mappings.
 ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
@@ -9853,16 +9854,16 @@ HINT:  Perhaps you meant the option "passfile".
 -- This won't work with installcheck, but neither will most of the FDW checks.
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 -- Unpriv user cannot make the mapping passwordless
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password_required 'false');
 ERROR:  password_required=false is superuser-only
 HINT:  User mappings with the password_required option set to false may only be created or modified by the superuser.
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 RESET ROLE;
 -- But the superuser can
@@ -9890,8 +9891,8 @@ DROP USER MAPPING FOR CURRENT_USER SERVER loopback_nopw;
 -- This will fail again as it'll resolve the user mapping for public, which
 -- lacks password_required=false
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 RESET ROLE;
 -- The user mapping for public is passwordless and lacks the password_required=false
 -- mapping option, but will work because the current user is a superuser.
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 4229d2048c..fe40d50c6d 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -288,6 +288,12 @@ InitPgFdwOptions(void)
 		{"sslcert", UserMappingRelationId, true},
 		{"sslkey", UserMappingRelationId, true},
 
+		/*
+		 * gssdeleg is also a libpq option but should be allowed in a user
+		 * mapping context too
+		 */
+		{"gssdeleg", UserMappingRelationId, true},
+
 		{NULL, InvalidOid, false}
 	};
 
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5bd69339df..c05046f867 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f81c2045ec..091a79d4f3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1190,6 +1190,23 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-gss-accept-deleg" xreflabel="gss_accept_deleg">
+      <term><varname>gss_accept_deleg</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>gss_accept_deleg</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets whether GSSAPI delegation should be accepted from the client.
+        The default is <literal>off</literal> meaning credentials from the client will
+        NOT be accepted.  Changing this to <literal>on</literal> will make the server
+        accept credentials delegated to it from the client. This parameter can only be
+        set in the <filename>postgresql.conf</filename> file or on the server command line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-db-user-namespace" xreflabel="db_user_namespace">
       <term><varname>db_user_namespace</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 17f9d99b1c..7d25f24f49 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -117,8 +117,9 @@ dblink_connect(text connname, text connstr) returns text
 
    <para>
     Only superusers may use <function>dblink_connect</function> to create
-    non-password-authenticated connections.  If non-superusers need this
-    capability, use <function>dblink_connect_u</function> instead.
+    non-password-authenticated and non-GSSAPI-authenticated connections.
+    If non-superusers need this capability, use
+    <function>dblink_connect_u</function> instead.
    </para>
 
    <para>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index faa8aa3187..b8702284d0 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2054,6 +2054,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gssdeleg" xreflabel="gssdeleg">
+      <term><literal>gssdeleg</literal></term>
+      <listitem>
+       <para>
+        Forward (delegate) GSS credentials to the server.  The default is
+        <literal>disable</literal> which means credentials will not be forwarded
+        to the server.  Set this to <literal>enable</literal> to have
+        credentials forwarded when possible.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
@@ -2715,6 +2727,25 @@ int PQconnectionUsedPassword(const PGconn *conn);
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry id="libpq-PQconnectionUsedGSSAPI">
+     <term><function>PQconnectionUsedGSSAPI</function><indexterm><primary>PQconnectionUsedGSSAPI</primary></indexterm></term>
+     <listitem>
+      <para>
+       Returns true (1) if the connection authentication method
+       used GSSAPI. Returns false (0) if not.
+
+<synopsis>
+int PQconnectionUsedGSSAPI(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       This function can be applied to detect whether the connection was
+       authenticated with GSSAPI.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </para>
 
@@ -8237,6 +8268,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGGSSDELEG</envar></primary>
+      </indexterm>
+      <envar>PGGSSDELEG</envar> behaves the same as the <xref
+      linkend="libpq-connect-gssdeleg"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f33a1c56c..e8ab803267 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3573,6 +3573,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        True if GSSAPI encryption is in use on this connection
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>credentials_delegated</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if GSSAPI credentials were delegated on this connection.
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index a122794df3..b9a5b0eac8 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -169,9 +169,10 @@
     <literal>sslcert</literal> or <literal>sslkey</literal> settings.
    </para>
    <para>
-    Only superusers may connect to foreign servers without password
-    authentication, so always specify the <literal>password</literal> option
-    for user mappings belonging to non-superusers.
+    Non-superusers may connect to foreign servers using password
+    authentication or with GSSAPI delegated credentials, so specify the
+    <literal>password</literal> option for user mappings belonging to
+    non-superusers where password authentication is required.
    </para>
    <para>
     A superuser may override this check on a per-user-mapping basis by setting
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 701c340fc4..2129c916aa 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -979,7 +979,8 @@ CREATE VIEW pg_stat_gssapi AS
             S.pid,
             S.gss_auth AS gss_authenticated,
             S.gss_princ AS principal,
-            S.gss_enc AS encrypted
+            S.gss_enc AS encrypted,
+            S.gss_deleg AS credentials_delegated
     FROM pg_stat_get_activity(NULL) AS S
     WHERE S.client_port IS NOT NULL;
 
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index dca02271dc..6e1977fa62 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -574,6 +574,7 @@ static const struct ConnectionOption libpq_conninfo_options[] = {
 	{"requiressl", ForeignServerRelationId},
 	{"sslmode", ForeignServerRelationId},
 	{"gsslib", ForeignServerRelationId},
+	{"gssdeleg", ForeignServerRelationId},
 	{NULL, InvalidOid}
 };
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index bc0cf26b12..00ec9da284 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -165,6 +165,7 @@ static int	CheckCertAuth(Port *port);
  */
 char	   *pg_krb_server_keyfile;
 bool		pg_krb_caseins_users;
+bool		pg_gss_accept_deleg;
 
 
 /*----------------------------------------------------------------
@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
 	int			mtype;
 	StringInfoData buf;
 	gss_buffer_desc gbuf;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
@@ -947,6 +949,9 @@ pg_GSS_recvauth(Port *port)
 	 */
 	port->gss->ctx = GSS_C_NO_CONTEXT;
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Loop through GSSAPI message exchange. This exchange can consist of
 	 * multiple messages sent in both directions. First message is always from
@@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port)
 										  &port->gss->outbuf,
 										  &gflags,
 										  NULL,
-										  NULL);
+										  pg_gss_accept_deleg ? &delegated_creds : NULL);
 
 		/* gbuf no longer used */
 		pfree(buf.data);
@@ -1009,6 +1014,12 @@ pg_GSS_recvauth(Port *port)
 
 		CHECK_FOR_INTERRUPTS();
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		if (port->gss->outbuf.length != 0)
 		{
 			/*
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index fb39c760d8..64d41e5291 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -92,3 +92,56 @@ pg_GSS_error(const char *errmsg,
 			(errmsg_internal("%s", errmsg),
 			 errdetail_internal("%s: %s", msg_major, msg_minor)));
 }
+
+/*
+ * Store the credentials passed in into the memory cache for later usage.
+ *
+ * This allows credentials to be delegated to us for us to use to connect
+ * to other systems with, using, e.g. postgres_fdw or dblink.
+ */
+#define GSS_MEMORY_CACHE "MEMORY:"
+void
+pg_store_delegated_credential(gss_cred_id_t cred)
+{
+	OM_uint32	major,
+				minor;
+	gss_OID_set mech;
+	gss_cred_usage_t usage;
+	gss_key_value_element_desc cc;
+	gss_key_value_set_desc ccset;
+
+	cc.key = "ccache";
+	cc.value = GSS_MEMORY_CACHE;
+	ccset.count = 1;
+	ccset.elements = &cc;
+
+	/* Make the delegated credential only available to current process */
+	major = gss_store_cred_into(&minor,
+								cred,
+								GSS_C_INITIATE, /* credential only used for
+												 * starting libpq connection */
+								GSS_C_NULL_OID, /* store all */
+								true,	/* overwrite */
+								true,	/* make default */
+								&ccset,
+								&mech,
+								&usage);
+
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_store_cred", major, minor);
+	}
+
+	/* Credential stored, so we can release our credential handle. */
+	major = gss_release_cred(&minor, &cred);
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_release_cred", major, minor);
+	}
+
+	/*
+	 * Set KRB5CCNAME for this backend, so that later calls to
+	 * gss_acquire_cred will find the delegated credentials we stored.
+	 */
+	setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
+}
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 3b55f43199..73f8ce8554 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -497,6 +497,7 @@ secure_open_gssapi(Port *port)
 	bool		complete_next = false;
 	OM_uint32	major,
 				minor;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Allocate subsidiary Port data for GSSAPI operations.
@@ -504,6 +505,9 @@ secure_open_gssapi(Port *port)
 	port->gss = (pg_gssinfo *)
 		MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Allocate buffers and initialize state variables.  By malloc'ing the
 	 * buffers at this point, we avoid wasting static data space in processes
@@ -588,7 +592,8 @@ secure_open_gssapi(Port *port)
 									   GSS_C_NO_CREDENTIAL, &input,
 									   GSS_C_NO_CHANNEL_BINDINGS,
 									   &port->gss->name, NULL, &output, NULL,
-									   NULL, NULL);
+									   NULL, pg_gss_accept_deleg ? &delegated_creds : NULL);
+
 		if (GSS_ERROR(major))
 		{
 			pg_GSS_error(_("could not accept GSSAPI security context"),
@@ -605,6 +610,12 @@ secure_open_gssapi(Port *port)
 			complete_next = true;
 		}
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		/* Done handling the incoming packet, reset our buffer */
 		PqGSSRecvLength = 0;
 
@@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port)
 
 	return port->gss->princ;
 }
+
+/*
+ * Return if GSSAPI delegated credentials were included on this
+ * connection.
+ */
+bool
+be_gssapi_get_deleg(Port *port)
+{
+	if (!port || !port->gss)
+		return NULL;
+
+	return port->gss->delegated_creds;
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 608d01ea0d..391d5de043 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -384,6 +384,7 @@ pgstat_bestart(void)
 		lbeentry.st_gss = true;
 		lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
 		lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
+		lgssstatus.gss_deleg = be_gssapi_get_deleg(MyProcPort);
 		if (princ)
 			strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
 	}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ae180da4d0..e79b065d21 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS	30
+#define PG_STAT_GET_ACTIVITY_COLS	31
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			pfree(clipped_activity);
 
 			/* leader_pid */
-			nulls[28] = true;
+			nulls[29] = true;
 
 			proc = BackendPidGetProc(beentry->st_procpid);
 
@@ -432,8 +432,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				 */
 				if (leader && leader->pid != beentry->st_procpid)
 				{
-					values[28] = Int32GetDatum(leader->pid);
-					nulls[28] = false;
+					values[29] = Int32GetDatum(leader->pid);
+					nulls[29] = false;
 				}
 				else if (beentry->st_backendType == B_BG_WORKER)
 				{
@@ -441,8 +441,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 					if (leader_pid != InvalidPid)
 					{
-						values[28] = Int32GetDatum(leader_pid);
-						nulls[28] = false;
+						values[29] = Int32GetDatum(leader_pid);
+						nulls[29] = false;
 					}
 				}
 			}
@@ -600,6 +600,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				values[25] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
 				values[26] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
 				values[27] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
+				values[28] = BoolGetDatum(beentry->st_gssstatus->gss_deleg);	/* GSS credentials
+																				 * delegated */
 			}
 			else
 			{
@@ -607,11 +609,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				nulls[26] = true;	/* No GSS principal */
 				values[27] = BoolGetDatum(false);	/* GSS Encryption not in
 													 * use */
+				values[28] = BoolGetDatum(false);	/* GSS credentials not
+													 * delegated */
 			}
 			if (beentry->st_query_id == 0)
-				nulls[29] = true;
+				nulls[30] = true;
 			else
-				values[29] = UInt64GetDatum(beentry->st_query_id);
+				values[30] = UInt64GetDatum(beentry->st_query_id);
 		}
 		else
 		{
@@ -640,6 +644,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[27] = true;
 			nulls[28] = true;
 			nulls[29] = true;
+			nulls[30] = true;
 		}
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 60feae0f1b..5af87a7868 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -282,15 +282,17 @@ PerformAuthentication(Port *port)
 
 			if (princ)
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s, principal=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
 								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"),
 								 princ);
 			else
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
-								 be_gssapi_get_enc(port) ? _("yes") : _("no"));
+								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"));
 		}
 #endif
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1067537e74..cab3ddbe11 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -1727,6 +1727,16 @@ struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"gss_accept_deleg", PGC_SIGHUP, CONN_AUTH_AUTH,
+			gettext_noop("Sets whether GSSAPI delegation should be accepted from the client."),
+			NULL
+		},
+		&pg_gss_accept_deleg,
+		false,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
 			gettext_noop("Warn about backslash escapes in ordinary string literals."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e715aff3b8..dce5049bc2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -101,6 +101,7 @@
 # GSSAPI using Kerberos
 #krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
 #krb_caseins_users = off
+#gss_accept_deleg = off
 
 # - SSL -
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dcbc4c2eb5..b516cee8bd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5438,9 +5438,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}',
+  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,bool,int4,int8}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_deleg,leader_pid,query_id}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 9916c99df1..e4d0e38c1e 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -18,6 +18,7 @@
 
 extern PGDLLIMPORT char *pg_krb_server_keyfile;
 extern PGDLLIMPORT bool pg_krb_caseins_users;
+extern PGDLLIMPORT bool pg_gss_accept_deleg;
 extern PGDLLIMPORT char *pg_krb_realm;
 
 extern void ClientAuthentication(Port *port);
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
index facd24ff7f..0381f0ce77 100644
--- a/src/include/libpq/be-gssapi-common.h
+++ b/src/include/libpq/be-gssapi-common.h
@@ -18,13 +18,16 @@
 
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
+#include <gssapi_ext.h>
 #else
 #include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
 #endif
 
 extern void pg_GSS_error(const char *errmsg,
 						 OM_uint32 maj_stat, OM_uint32 min_stat);
 
+extern void pg_store_delegated_credential(gss_cred_id_t cred);
 #endif							/* ENABLE_GSS */
 
 #endif							/* BE_GSSAPI_COMMON_H */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ac6407e9f6..e9df4295e2 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -84,6 +84,7 @@ typedef struct
 								 * GSSAPI auth was not used */
 	bool		auth;			/* GSSAPI Authentication used */
 	bool		enc;			/* GSSAPI encryption in use */
+	bool		delegated_creds;	/* GSSAPI Delegated credentials */
 #endif
 } pg_gssinfo;
 #endif
@@ -328,6 +329,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tls_init_hook;
 extern bool be_gssapi_get_auth(Port *port);
 extern bool be_gssapi_get_enc(Port *port);
 extern const char *be_gssapi_get_princ(Port *port);
+extern bool be_gssapi_get_deleg(Port *port);
 
 /* Read and write to a GSSAPI-encrypted connection. */
 extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index f7bd83113a..9651cb1d0c 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -77,6 +77,7 @@ typedef struct PgBackendGSSStatus
 	char		gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
 	bool		gss_auth;		/* If GSSAPI authentication was used */
 	bool		gss_enc;		/* If encryption is being used */
+	bool		gss_deleg;		/* If credentials delegated */
 
 } PgBackendGSSStatus;
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index e8bcc88370..7ded77aff3 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -186,3 +186,4 @@ PQpipelineStatus          183
 PQsetTraceFlags           184
 PQmblenBounded            185
 PQsendFlushRequest        186
+PQconnectionUsedGSSAPI    187
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index b0550e6332..fe2634230a 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -58,7 +58,8 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 {
 	OM_uint32	maj_stat,
 				min_stat,
-				lmin_s;
+				lmin_s,
+				gss_flags = GSS_C_MUTUAL_FLAG;
 	gss_buffer_desc ginbuf;
 	gss_buffer_desc goutbuf;
 
@@ -92,12 +93,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 		ginbuf.value = NULL;
 	}
 
+	/* Only try to acquire credentials if GSS delegation isn't disabled. */
+	if (!pg_GSS_have_cred_cache(&conn->gcred))
+		conn->gcred = GSS_C_NO_CREDENTIAL;
+
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+		gss_flags |= GSS_C_DELEG_FLAG;
+
 	maj_stat = gss_init_sec_context(&min_stat,
-									GSS_C_NO_CREDENTIAL,
+									conn->gcred,
 									&conn->gctx,
 									conn->gtarg_nam,
 									GSS_C_NO_OID,
-									GSS_C_MUTUAL_FLAG,
+									gss_flags,
 									0,
 									GSS_C_NO_CHANNEL_BINDINGS,
 									(ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
@@ -139,6 +147,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 	{
 		conn->client_finished_auth = true;
 		gss_release_name(&lmin_s, &conn->gtarg_nam);
+		conn->gssapi_used = true;
 	}
 
 	return STATUS_OK;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 40fef0e2c8..fcd3d0d9a3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -343,6 +343,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"GSS-library", "", 7,	/* sizeof("gssapi") == 7 */
 	offsetof(struct pg_conn, gsslib)},
 
+	{"gssdeleg", "PGGSSDELEG", NULL, NULL,
+		"GSS-delegation", "", 8,	/* sizeof("disable") == 8 */
+	offsetof(struct pg_conn, gssdeleg)},
+
 	{"replication", NULL, NULL, NULL,
 		"Replication", "D", 5,
 	offsetof(struct pg_conn, replication)},
@@ -617,6 +621,7 @@ pqDropServerData(PGconn *conn)
 	conn->auth_req_received = false;
 	conn->client_finished_auth = false;
 	conn->password_needed = false;
+	conn->gssapi_used = false;
 	conn->write_failed = false;
 	free(conn->write_err_msg);
 	conn->write_err_msg = NULL;
@@ -4448,6 +4453,7 @@ freePGconn(PGconn *conn)
 	free(conn->gssencmode);
 	free(conn->krbsrvname);
 	free(conn->gsslib);
+	free(conn->gssdeleg);
 	free(conn->connip);
 	/* Note that conn->Pfdebug is not ours to close or free */
 	free(conn->write_err_msg);
@@ -7312,6 +7318,17 @@ PQconnectionUsedPassword(const PGconn *conn)
 		return false;
 }
 
+int
+PQconnectionUsedGSSAPI(const PGconn *conn)
+{
+	if (!conn)
+		return false;
+	if (conn->gssapi_used)
+		return true;
+	else
+		return false;
+}
+
 int
 PQclientEncoding(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 038e847b7e..bf87ae3fd1 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -477,7 +477,8 @@ pqsecure_open_gss(PGconn *conn)
 {
 	ssize_t		ret;
 	OM_uint32	major,
-				minor;
+				minor,
+				gss_flags = GSS_REQUIRED_FLAGS;
 	uint32		netlen;
 	PostgresPollingStatusType result;
 	gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
@@ -621,13 +622,30 @@ pqsecure_open_gss(PGconn *conn)
 	if (ret != STATUS_OK)
 		return PGRES_POLLING_FAILED;
 
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+	{
+		/* Acquire credentials if possbile */
+		if (conn->gcred == GSS_C_NO_CREDENTIAL)
+			(void) pg_GSS_have_cred_cache(&conn->gcred);
+
+		/*
+		 * We have credentials and gssdeleg is enabled, so request credential
+		 * delegation.  This may or may not actually result in credentials
+		 * being delegated- it depends on if the forwardable flag has been set
+		 * in the credential and if the server is configured to accept
+		 * delegated credentials.
+		 */
+		if (conn->gcred != GSS_C_NO_CREDENTIAL)
+			gss_flags |= GSS_C_DELEG_FLAG;
+	}
+
 	/*
 	 * Call GSS init context, either with an empty input, or with a complete
 	 * packet from the server.
 	 */
 	major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
 								 conn->gtarg_nam, GSS_C_NO_OID,
-								 GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+								 gss_flags, 0, 0, &input, NULL,
 								 &output, NULL, NULL);
 
 	/* GSS Init Sec Context uses the whole packet, so clear it */
@@ -647,6 +665,7 @@ pqsecure_open_gss(PGconn *conn)
 		 * to do GSS wrapping/unwrapping.
 		 */
 		conn->gssenc = true;
+		conn->gssapi_used = true;
 
 		/* Clean up */
 		gss_release_cred(&minor, &conn->gcred);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index f3d9220496..7476dbe0e9 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -354,6 +354,7 @@ extern int	PQbackendPID(const PGconn *conn);
 extern PGpipelineStatus PQpipelineStatus(const PGconn *conn);
 extern int	PQconnectionNeedsPassword(const PGconn *conn);
 extern int	PQconnectionUsedPassword(const PGconn *conn);
+extern int	PQconnectionUsedGSSAPI(const PGconn *conn);
 extern int	PQclientEncoding(const PGconn *conn);
 extern int	PQsetClientEncoding(PGconn *conn, const char *encoding);
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d93e976ca5..ce0167c1b6 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -404,6 +404,7 @@ struct pg_conn
 	char	   *krbsrvname;		/* Kerberos service name */
 	char	   *gsslib;			/* What GSS library to use ("gssapi" or
 								 * "sspi") */
+	char	   *gssdeleg;		/* Try to delegate GSS credentials? */
 	char	   *ssl_min_protocol_version;	/* minimum TLS protocol version */
 	char	   *ssl_max_protocol_version;	/* maximum TLS protocol version */
 	char	   *target_session_attrs;	/* desired session properties */
@@ -465,6 +466,7 @@ struct pg_conn
 	int			sversion;		/* server version, e.g. 70401 for 7.4.1 */
 	bool		auth_req_received;	/* true if any type of auth req received */
 	bool		password_needed;	/* true if server demanded a password */
+	bool		gssapi_used;	/* true if authenticated via gssapi */
 	bool		sigpipe_so;		/* have we masked SIGPIPE via SO_NOSIGPIPE? */
 	bool		sigpipe_flag;	/* can we mask SIGPIPE via MSG_NOSIGNAL? */
 	bool		write_failed;	/* have we had a write failure on sock? */
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
index 7765f3f93b..f460d2c0e7 100644
--- a/src/test/kerberos/Makefile
+++ b/src/test/kerberos/Makefile
@@ -13,6 +13,9 @@ subdir = src/test/kerberos
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
+EXTRA_INSTALL += contrib/postgres_fdw
+EXTRA_INSTALL += contrib/dblink
+
 export with_gssapi with_krb_srvnam
 
 check:
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 458246b4d7..bf12752529 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -7,6 +7,9 @@
 # that the server-side pg_stat_gssapi view reports what we expect to
 # see for each test and that SYSTEM_USER returns what we expect to see.
 #
+# Also test that GSSAPI delegation is working properly and that those
+# credentials can be used to make dblink / postgres_fdw connections.
+#
 # Since this requires setting up a full KDC, it doesn't make much sense
 # to have multiple test scripts (since they'd have to also create their
 # own KDC and that could cause race conditions or other problems)- so
@@ -56,6 +59,7 @@ elsif ($^O eq 'linux')
 
 my $krb5_config  = 'krb5-config';
 my $kinit        = 'kinit';
+my $klist        = 'klist';
 my $kdb5_util    = 'kdb5_util';
 my $kadmin_local = 'kadmin.local';
 my $krb5kdc      = 'krb5kdc';
@@ -64,6 +68,7 @@ if ($krb5_bin_dir && -d $krb5_bin_dir)
 {
 	$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
 	$kinit       = $krb5_bin_dir . '/' . $kinit;
+	$klist       = $krb5_bin_dir . '/' . $klist;
 }
 if ($krb5_sbin_dir && -d $krb5_sbin_dir)
 {
@@ -86,6 +91,8 @@ my $kdc_datadir = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc";
 my $kdc_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc.pid";
 my $keytab      = "${PostgreSQL::Test::Utils::tmp_check}/krb5.keytab";
 
+my $pgpass      = "${PostgreSQL::Test::Utils::tmp_check}/.pgpass";
+
 my $dbname      = 'postgres';
 my $username    = 'test1';
 my $application = '001_auth.pl';
@@ -100,6 +107,14 @@ $stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
   or BAIL_OUT("could not get Kerberos version");
 $krb5_version = $1;
 
+# Construct a pgpass file to make sure we don't use it
+append_to_file(
+	$pgpass,
+	'*:*:*:*:abc123'
+);
+
+chmod 0600, $pgpass;
+
 # Build the krb5.conf to use.
 #
 # Explicitly specify the default (test) realm and the KDC for
@@ -126,12 +141,14 @@ kdc = FILE:$kdc_log
 dns_lookup_realm = false
 dns_lookup_kdc = false
 default_realm = $realm
+forwardable = false
 rdns = false
 
 [realms]
 $realm = {
     kdc = $hostaddr:$kdc_port
-}!);
+}
+!);
 
 append_to_file(
 	$kdc_conf,
@@ -204,7 +221,28 @@ lc_messages = 'C'
 });
 $node->start;
 
+my $port = $node->port();
+
 $node->safe_psql('postgres', 'CREATE USER test1;');
+$node->safe_psql('postgres', "CREATE USER test2 WITH ENCRYPTED PASSWORD 'abc123';");
+$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;');
+$node->safe_psql('postgres', 'CREATE EXTENSION dblink;');
+$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'postgres');");
+$node->safe_psql('postgres', "CREATE SERVER s2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (port '$port', dbname 'postgres', passfile '$pgpass');");
+
+$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;');
+
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTIONS (user 'test1');");
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s2 OPTIONS (user 'test2');");
+
+$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);");
+$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);");
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;");
+$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;");
+
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf2 (c1 int) SERVER s2 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON tf2 TO test1;");
 
 # Set up a table for SYSTEM_USER parallel worker testing.
 $node->safe_psql('postgres',
@@ -271,12 +309,16 @@ sub test_query
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss map=mymap});
+	qq{
+local all test2 scram-sha-256
+host all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
 
 run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
 
 test_access(
 	$node,
@@ -294,35 +336,58 @@ $node->restart;
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'',
-	'succeeds with mapping with default gssencmode and host hba',
+	'succeeds with mapping with default gssencmode and host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred with host hba',
+	'succeeds with GSS-encrypted access preferred with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
+
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=require',
-	'succeeds with GSS-encrypted access required with host hba',
+	'succeeds with GSS-encrypted access required with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+
 # Test that we can transport a reasonable amount of data.
 test_query(
 	$node,
@@ -389,29 +454,164 @@ test_query(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostgssenc all all $hostaddr/32 gss map=mymap
+});
+
+string_replace_file($krb5_conf, "forwardable = false", "forwardable = true");
+
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=off});
+$node->restart;
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=on});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred and hostgssenc hba',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=require',
-	'succeeds with GSS-encrypted access required and hostgssenc hba',
+	'gssencmode=require gssdeleg=disable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials explicitly not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+my $psql_out = '';
+my $psql_stderr = '';
+my $psql_rc = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink attempt fails without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','dblink does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf1;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials and with passfile');
+
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
 	'fails with GSS encryption disabled and hostgssenc hba');
 
@@ -427,54 +627,123 @@ $node->connect_ok(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostnogssenc all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=prefer',
+	'gssencmode=prefer gssdeleg=enable',
 	'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
 );
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=require',
 	'fails with GSS-encrypted access required and hostnogssenc hba');
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=disable',
+	'gssencmode=disable gssdeleg=enable',
 	'succeeds with GSS encryption disabled and hostnogssenc hba',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
+);
+
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'dblink works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'postgres_fdw works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work with delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work with delegated credentials and with passfile');
 
 truncate($node->data_dir . '/pg_ident.conf', 0);
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'',
+	'gssdeleg=enable',
 	'succeeds with include_realm=0 and defaults',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
 );
 
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'dblink works encrypted');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'postgres_fdw works encrypted');
+
 # Reset pg_hba.conf, and cause a usermap failure with an authentication
 # that has passed.
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG
+});
 $node->restart;
 
 test_access(
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 878e12b15e..9249954b49 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -65,6 +65,7 @@ our @EXPORT = qw(
   slurp_dir
   slurp_file
   append_to_file
+  string_replace_file
   check_mode_recursive
   chmod_recursive
   check_pg_config
@@ -549,6 +550,32 @@ sub append_to_file
 
 =pod
 
+=item string_replace_file(filename, find, replace)
+
+Find and replace string of a given file.
+
+=cut
+
+sub string_replace_file
+{
+	my ($filename, $find, $replace) = @_;
+	open(my $in, '<', $filename);
+	my $content;
+	while(<$in>)
+	{
+		$_ =~ s/$find/$replace/;
+		$content = $content.$_;
+	}
+	close $in;
+	open(my $out, '>', $filename);
+	print $out $content;
+	close($out);
+
+	return;
+}
+
+=pod
+
 =item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
 
 Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2d75dd6656..919d947ec0 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1760,7 +1760,7 @@ pg_stat_activity| SELECT s.datid,
     s.query_id,
     s.query,
     s.backend_type
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1876,8 +1876,9 @@ pg_stat_database_conflicts| SELECT oid AS datid,
 pg_stat_gssapi| SELECT pid,
     gss_auth AS gss_authenticated,
     gss_princ AS principal,
-    gss_enc AS encrypted
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+    gss_enc AS encrypted,
+    gss_deleg AS credentials_delegated
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_io| SELECT backend_type,
     io_object,
@@ -2075,7 +2076,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     w.reply_time
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_replication_slots| SELECT s.slot_name,
@@ -2109,7 +2110,7 @@ pg_stat_ssl| SELECT pid,
     ssl_client_dn AS client_dn,
     ssl_client_serial AS client_serial,
     ssl_issuer_dn AS issuer_dn
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,
-- 
2.34.1

0002-Explicitly-require-gssapi_ext.h-for-GSSAPI.patchtext/x-diff; charset=us-asciiDownload
From ab318fe2a4dbdc4d53d1504d61525199c6943532 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 12 Apr 2023 09:35:53 -0400
Subject: [PATCH 2/2] Explicitly require gssapi_ext.h for GSSAPI

WHen building with GSSAPI support, explicitly require gssapi_ext.h in
configure.ac and meson.build.  Also add documentation explicitly stating
that we now require MIT Kerberos when building with GSSAPI support.
---
 configure                      | 27 +++++++++++++++++++++++++++
 configure.ac                   |  2 ++
 doc/src/sgml/installation.sgml | 15 ++++++++-------
 meson.build                    | 10 ++++++++++
 4 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/configure b/configure
index dbea7eaf5f..08bcf8f43a 100755
--- a/configure
+++ b/configure
@@ -14104,6 +14104,33 @@ done
 
 fi
 
+done
+
+  for ac_header in gssapi/gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_ext.h" "ac_cv_header_gssapi_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  for ac_header in gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi_ext.h" "ac_cv_header_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  as_fn_error $? "gssapi_ext.h header file is required for GSSAPI" "$LINENO" 5
+fi
+
+done
+
+fi
+
 done
 
 fi
diff --git a/configure.ac b/configure.ac
index dda34304db..c53a9c788e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1562,6 +1562,8 @@ fi
 if test "$with_gssapi" = yes ; then
   AC_CHECK_HEADERS(gssapi/gssapi.h, [],
 	[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
+  AC_CHECK_HEADERS(gssapi/gssapi_ext.h, [],
+	[AC_CHECK_HEADERS(gssapi_ext.h, [], [AC_MSG_ERROR([gssapi_ext.h header file is required for GSSAPI])])])
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index f451204854..ff89bd5f6d 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1048,9 +1048,9 @@ build-postgresql:
        <term><option>--with-gssapi</option></term>
        <listitem>
         <para>
-         Build with support for GSSAPI authentication. On many systems, the
-         GSSAPI system (usually a part of the Kerberos installation) is not
-         installed in a location
+         Build with support for GSSAPI authentication. MIT Kerberos is required
+         to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+         of the MIT Kerberos installation) is not installed in a location
          that is searched by default (e.g., <filename>/usr/include</filename>,
          <filename>/usr/lib</filename>), so you must use the options
          <option>--with-includes</option> and <option>--with-libraries</option> in
@@ -2497,10 +2497,11 @@ ninja install
       <term><option>-Dgssapi={ auto | enabled | disabled }</option></term>
       <listitem>
        <para>
-        Build with support for GSSAPI authentication. On many systems, the
-        GSSAPI system (usually a part of the Kerberos installation) is not
-        installed in a location that is searched by default (e.g.,
-        <filename>/usr/include</filename>, <filename>/usr/lib</filename>).  In
+        Build with support for GSSAPI authentication. MIT Kerberos is required
+        to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+        of the MIT Kerberos installation) is not installed in a location
+        that is searched by default (e.g., <filename>/usr/include</filename>,
+        <filename>/usr/lib</filename>).  In
         those cases, PostgreSQL will query <command>pkg-config</command> to
         detect the required compiler and linker options.  Defaults to auto.
         <filename>meson configure</filename> will check for the required
diff --git a/meson.build b/meson.build
index b69aaddb1f..3405cc07ee 100644
--- a/meson.build
+++ b/meson.build
@@ -623,6 +623,16 @@ if not gssapiopt.disabled()
     have_gssapi = false
   endif
 
+  if not have_gssapi
+  elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
+      args: test_c_args, include_directories: postgres_inc)
+    cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
+  elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
+    cdata.set('HAVE_GSSAPI_EXT_H', 1)
+  else
+    have_gssapi = false
+  endif
+
   if not have_gssapi
   elif cc.has_function('gss_init_sec_context', dependencies: gssapi,
       args: test_c_args, include_directories: postgres_inc)
-- 
2.34.1

#30Jonathan S. Katz
jkatz@postgresql.org
In reply to: Stephen Frost (#29)
Re: longfin missing gssapi_ext.h

On 4/12/23 10:33 AM, Stephen Frost wrote:

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Understood. Please find attached the updated patch with changes to the
commit message to indicate that we now require MIT Kerberos, an
additional explicit check for gssapi_ext.h in configure.ac/configure,
along with updated documentation explicitly saying we require MIT
Kerberos for GSSAPI support.

Um ... could you package this as a straight un-revert of the
previous commit, then a delta patch? Would be easier to review.

Sure, reworked that way and attached.

Docs read well. A few questions/commenets:

* On [1]https://www.postgresql.org/docs/devel/install-requirements.html -- do we want to add a note that it's not just Kerberos, but
MIT Kerberos?

* On [2]https://www.postgresql.org/docs/devel/gssapi-auth.html -- we mention "kadmin tool of MIT-compatible Kerberos 5" which
is AIUI is still technically correct, but do we want to drop the
"-compatible?" (precedent in [3]https://www.postgresql.org/docs/devel/regress-run.html)

Thanks,

Jonathan

[1]: https://www.postgresql.org/docs/devel/install-requirements.html
[2]: https://www.postgresql.org/docs/devel/gssapi-auth.html
[3]: https://www.postgresql.org/docs/devel/regress-run.html

#31Daniel Gustafsson
daniel@yesql.se
In reply to: Stephen Frost (#29)
Re: longfin missing gssapi_ext.h

On 12 Apr 2023, at 16:33, Stephen Frost <sfrost@snowman.net> wrote:

Sure, reworked that way and attached.

While not changed in this hunk, does the comment regarding Heimdal still apply?

@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
int mtype;
StringInfoData buf;
gss_buffer_desc gbuf;
+ gss_cred_id_t delegated_creds;

/*
* Use the configured keytab, if there is one. Unfortunately, Heimdal

--
Daniel Gustafsson

#32Stephen Frost
sfrost@snowman.net
In reply to: Jonathan S. Katz (#30)
2 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Jonathan S. Katz (jkatz@postgresql.org) wrote:

On 4/12/23 10:33 AM, Stephen Frost wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Understood. Please find attached the updated patch with changes to the
commit message to indicate that we now require MIT Kerberos, an
additional explicit check for gssapi_ext.h in configure.ac/configure,
along with updated documentation explicitly saying we require MIT
Kerberos for GSSAPI support.

Um ... could you package this as a straight un-revert of the
previous commit, then a delta patch? Would be easier to review.

Sure, reworked that way and attached.

Docs read well. A few questions/commenets:

* On [1] -- do we want to add a note that it's not just Kerberos, but MIT
Kerberos?

Yes, makes sense, updated.

* On [2] -- we mention "kadmin tool of MIT-compatible Kerberos 5" which is
AIUI is still technically correct, but do we want to drop the "-compatible?"
(precedent in [3])

Yup, cleaned that up also.

Updated patch set attached.

Thanks!

Stephen

Attachments:

0001-Revert-Revert-Add-support-for-Kerberos-credential-de.patchtext/x-diff; charset=us-asciiDownload
From 9acb2ef2e6a35544e28d690978ba8d5e8c062f7e Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 12 Apr 2023 09:33:39 -0400
Subject: [PATCH 1/2] Revert "Revert "Add support for Kerberos credential
 delegation""

This reverts commit 3d03b24c3 (Add support for Kerberos credential
delegation) which was reverted on the grounds of concern about
portability, but on further review and discussion, it's clear that
we are better off simply explicitly requiring MIT Kerberos as that seems
to be the only GSSAPI library currently that's under proper maintenance
and ongoing development.  The API used for storing credentials was added
to MIT Kerberos over a decade ago while for the other libraries which
appear to be mainly based on Heimdal, which exists explicitly to be a
re-implementation of MIT Kerberos, the API never made it to a released
version (even though it was added to the Heimdal git repo over 5 years
ago..).

This post-feature-freeze change was approved by the RMT.

Discussion: https://postgr.es/m/ZDDO6jaESKaBgej0%40tamriel.snowman.net
---
 contrib/dblink/dblink.c                       | 127 ++++---
 contrib/dblink/expected/dblink.out            |   4 +-
 contrib/postgres_fdw/connection.c             |  72 +++-
 .../postgres_fdw/expected/postgres_fdw.out    |  19 +-
 contrib/postgres_fdw/option.c                 |   6 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     |   3 +-
 doc/src/sgml/config.sgml                      |  17 +
 doc/src/sgml/dblink.sgml                      |   5 +-
 doc/src/sgml/libpq.sgml                       |  41 +++
 doc/src/sgml/monitoring.sgml                  |   9 +
 doc/src/sgml/postgres-fdw.sgml                |   7 +-
 src/backend/catalog/system_views.sql          |   3 +-
 src/backend/foreign/foreign.c                 |   1 +
 src/backend/libpq/auth.c                      |  13 +-
 src/backend/libpq/be-gssapi-common.c          |  53 +++
 src/backend/libpq/be-secure-gssapi.c          |  26 +-
 src/backend/utils/activity/backend_status.c   |   1 +
 src/backend/utils/adt/pgstatfuncs.c           |  21 +-
 src/backend/utils/init/postinit.c             |   8 +-
 src/backend/utils/misc/guc_tables.c           |  10 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/catalog/pg_proc.dat               |   6 +-
 src/include/libpq/auth.h                      |   1 +
 src/include/libpq/be-gssapi-common.h          |   3 +
 src/include/libpq/libpq-be.h                  |   2 +
 src/include/utils/backend_status.h            |   1 +
 src/interfaces/libpq/exports.txt              |   1 +
 src/interfaces/libpq/fe-auth.c                |  15 +-
 src/interfaces/libpq/fe-connect.c             |  17 +
 src/interfaces/libpq/fe-secure-gssapi.c       |  23 +-
 src/interfaces/libpq/libpq-fe.h               |   1 +
 src/interfaces/libpq/libpq-int.h              |   2 +
 src/test/kerberos/Makefile                    |   3 +
 src/test/kerberos/t/001_auth.pl               | 331 ++++++++++++++++--
 src/test/perl/PostgreSQL/Test/Utils.pm        |  27 ++
 src/test/regress/expected/rules.out           |  11 +-
 36 files changed, 755 insertions(+), 136 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e..55f75eff36 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -48,6 +48,7 @@
 #include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "libpq-fe.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -111,7 +112,8 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
 static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
 static char *generate_relation_name(Relation rel);
 static void dblink_connstr_check(const char *connstr);
-static void dblink_security_check(PGconn *conn, remoteConn *rconn);
+static bool dblink_connstr_has_pw(const char *connstr);
+static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
 static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
 							 bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
 static char *get_connect_string(const char *servername);
@@ -213,7 +215,7 @@ dblink_get_conn(char *conname_or_str,
 					 errmsg("could not establish connection"),
 					 errdetail_internal("%s", msg)));
 		}
-		dblink_security_check(conn, rconn);
+		dblink_security_check(conn, rconn, connstr);
 		if (PQclientEncoding(conn) != GetDatabaseEncoding())
 			PQsetClientEncoding(conn, GetDatabaseEncodingName());
 		freeconn = true;
@@ -307,7 +309,7 @@ dblink_connect(PG_FUNCTION_ARGS)
 	}
 
 	/* check password actually used if not superuser */
-	dblink_security_check(conn, rconn);
+	dblink_security_check(conn, rconn, connstr);
 
 	/* attempt to set client encoding to match server encoding, if needed */
 	if (PQclientEncoding(conn) != GetDatabaseEncoding())
@@ -2584,64 +2586,99 @@ deleteConnection(const char *name)
 				 errmsg("undefined connection name")));
 }
 
+/*
+ * We need to make sure that the connection made used credentials
+ * which were provided by the user, so check what credentials were
+ * used to connect and then make sure that they came from the user.
+ */
 static void
-dblink_security_check(PGconn *conn, remoteConn *rconn)
+dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
 {
-	if (!superuser())
-	{
-		if (!PQconnectionUsedPassword(conn))
-		{
-			libpqsrv_disconnect(conn);
-			if (rconn)
-				pfree(rconn);
+	/* Superuser bypasses security check */
+	if (superuser())
+		return;
 
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed.")));
-		}
-	}
+	/* If password was used to connect, make sure it was one provided */
+	if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	/* If GSSAPI creds used to connect, make sure it was one delegated */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Otherwise, fail out */
+	libpqsrv_disconnect(conn);
+	if (rconn)
+		pfree(rconn);
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"),
+			 errhint("Ensure provided credentials match target server's authentication method.")));
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
- * prevents a password from being picked up from .pgpass, a service file,
- * the environment, etc.  We don't want the postgres user's passwords
- * to be accessible to non-superusers.
+ * Function to check if the connection string includes an explicit
+ * password, needed to ensure that non-superuser password-based auth
+ * is using a provided password and not one picked up from the
+ * environment.
  */
-static void
-dblink_connstr_check(const char *connstr)
+static bool
+dblink_connstr_has_pw(const char *connstr)
 {
-	if (!superuser())
-	{
-		PQconninfoOption *options;
-		PQconninfoOption *option;
-		bool		connstr_gives_password = false;
+	PQconninfoOption *options;
+	PQconninfoOption *option;
+	bool		connstr_gives_password = false;
 
-		options = PQconninfoParse(connstr, NULL);
-		if (options)
+	options = PQconninfoParse(connstr, NULL);
+	if (options)
+	{
+		for (option = options; option->keyword != NULL; option++)
 		{
-			for (option = options; option->keyword != NULL; option++)
+			if (strcmp(option->keyword, "password") == 0)
 			{
-				if (strcmp(option->keyword, "password") == 0)
+				if (option->val != NULL && option->val[0] != '\0')
 				{
-					if (option->val != NULL && option->val[0] != '\0')
-					{
-						connstr_gives_password = true;
-						break;
-					}
+					connstr_gives_password = true;
+					break;
 				}
 			}
-			PQconninfoFree(options);
 		}
-
-		if (!connstr_gives_password)
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superusers must provide a password in the connection string.")));
+		PQconninfoFree(options);
 	}
+
+	return connstr_gives_password;
+}
+
+/*
+ * For non-superusers, insist that the connstr specify a password, except
+ * if GSSAPI credentials have been delegated (and we check that they are used
+ * for the connection in dblink_security_check later).  This prevents a
+ * password or GSSAPI credentials from being picked up from .pgpass, a
+ * service file, the environment, etc.  We don't want the postgres user's
+ * passwords or Kerberos credentials to be accessible to non-superusers.
+ */
+static void
+dblink_connstr_check(const char *connstr)
+{
+	if (superuser())
+		return;
+
+	if (dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.")));
 }
 
 /*
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index 0f5050b409..7809f58d96 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -903,8 +903,8 @@ GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO regress_dblink_user;
 SET SESSION AUTHORIZATION regress_dblink_user;
 -- should fail
 SELECT dblink_connect('myconn', 'fdtest');
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the connection string.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.
 -- should succeed
 SELECT dblink_connect_u('myconn', 'fdtest');
  dblink_connect_u 
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 2969351e9a..75d93d6ead 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
 #include "funcapi.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -149,6 +150,8 @@ static void pgfdw_finish_pre_subcommit_cleanup(List *pending_entries,
 static void pgfdw_finish_abort_cleanup(List *pending_entries,
 									   List *cancel_requested,
 									   bool toplevel);
+static void pgfdw_security_check(const char **keywords, const char **values,
+								 UserMapping *user, PGconn *conn);
 static bool UserMappingPasswordRequired(UserMapping *user);
 static bool disconnect_cached_connections(Oid serverid);
 
@@ -384,6 +387,47 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
 		 entry->conn, server->servername, user->umid, user->userid);
 }
 
+/*
+ * Check that non-superuser has used password or delegated credentials
+ * to establish connection; otherwise, he's piggybacking on the
+ * postgres server's user identity. See also dblink_security_check()
+ * in contrib/dblink and check_conn_params.
+ */
+static void
+pgfdw_security_check(const char **keywords, const char **values, UserMapping *user, PGconn *conn)
+{
+	/* Superusers bypass the check */
+	if (superuser_arg(user->userid))
+		return;
+
+#ifdef ENABLE_GSS
+	/* Connected via GSSAPI with delegated credentials- all good. */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Ok if superuser set PW required false. */
+	if (!UserMappingPasswordRequired(user))
+		return;
+
+	/* Connected via PW, with PW required true, and provided non-empty PW. */
+	if (PQconnectionUsedPassword(conn))
+	{
+		/* ok if params contain a non-empty password */
+		for (int i = 0; keywords[i] != NULL; i++)
+		{
+			if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
+				return;
+		}
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials."),
+			 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+}
+
 /*
  * Connect to remote server using specified server and user mapping properties.
  */
@@ -495,19 +539,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 							server->servername),
 					 errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
 
-		/*
-		 * Check that non-superuser has used password to establish connection;
-		 * otherwise, he's piggybacking on the postgres server's user
-		 * identity. See also dblink_security_check() in contrib/dblink and
-		 * check_conn_params.
-		 */
-		if (!superuser_arg(user->userid) && UserMappingPasswordRequired(user) &&
-			!PQconnectionUsedPassword(conn))
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+		/* Perform post-connection security checks */
+		pgfdw_security_check(keywords, values, user, conn);
 
 		/* Prepare new session for use */
 		configure_remote_session(conn);
@@ -561,7 +594,8 @@ UserMappingPasswordRequired(UserMapping *user)
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
+ * For non-superusers, insist that the connstr specify a password or that the
+ * user provided their own GSSAPI delegated credentials.  This
  * prevents a password from being picked up from .pgpass, a service file, the
  * environment, etc.  We don't want the postgres user's passwords,
  * certificates, etc to be accessible to non-superusers.  (See also
@@ -576,6 +610,12 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 	if (superuser_arg(user->userid))
 		return;
 
+#ifdef ENABLE_GSS
+	/* ok if the user provided their own delegated credentials */
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
 	/* ok if params contain a non-empty password */
 	for (i = 0; keywords[i] != NULL; i++)
 	{
@@ -589,8 +629,8 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 
 	ereport(ERROR,
 			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-			 errmsg("password is required"),
-			 errdetail("Non-superusers must provide a password in the user mapping.")));
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.")));
 }
 
 /*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 8f6a04f71b..fd5752bd5b 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 -- Error, invalid list syntax
@@ -9840,8 +9841,8 @@ CREATE FOREIGN TABLE pg_temp.ft1_nopw (
 	c8 user_enum
 ) SERVER loopback_nopw OPTIONS (schema_name 'public', table_name 'ft1');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 -- If we add a password to the connstr it'll fail, because we don't allow passwords
 -- in connstrs only in user mappings.
 ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
@@ -9853,16 +9854,16 @@ HINT:  Perhaps you meant the option "passfile".
 -- This won't work with installcheck, but neither will most of the FDW checks.
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 -- Unpriv user cannot make the mapping passwordless
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password_required 'false');
 ERROR:  password_required=false is superuser-only
 HINT:  User mappings with the password_required option set to false may only be created or modified by the superuser.
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 RESET ROLE;
 -- But the superuser can
@@ -9890,8 +9891,8 @@ DROP USER MAPPING FOR CURRENT_USER SERVER loopback_nopw;
 -- This will fail again as it'll resolve the user mapping for public, which
 -- lacks password_required=false
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 RESET ROLE;
 -- The user mapping for public is passwordless and lacks the password_required=false
 -- mapping option, but will work because the current user is a superuser.
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 4229d2048c..fe40d50c6d 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -288,6 +288,12 @@ InitPgFdwOptions(void)
 		{"sslcert", UserMappingRelationId, true},
 		{"sslkey", UserMappingRelationId, true},
 
+		/*
+		 * gssdeleg is also a libpq option but should be allowed in a user
+		 * mapping context too
+		 */
+		{"gssdeleg", UserMappingRelationId, true},
+
 		{NULL, InvalidOid, false}
 	};
 
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5bd69339df..c05046f867 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f81c2045ec..091a79d4f3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1190,6 +1190,23 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-gss-accept-deleg" xreflabel="gss_accept_deleg">
+      <term><varname>gss_accept_deleg</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>gss_accept_deleg</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets whether GSSAPI delegation should be accepted from the client.
+        The default is <literal>off</literal> meaning credentials from the client will
+        NOT be accepted.  Changing this to <literal>on</literal> will make the server
+        accept credentials delegated to it from the client. This parameter can only be
+        set in the <filename>postgresql.conf</filename> file or on the server command line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-db-user-namespace" xreflabel="db_user_namespace">
       <term><varname>db_user_namespace</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 17f9d99b1c..7d25f24f49 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -117,8 +117,9 @@ dblink_connect(text connname, text connstr) returns text
 
    <para>
     Only superusers may use <function>dblink_connect</function> to create
-    non-password-authenticated connections.  If non-superusers need this
-    capability, use <function>dblink_connect_u</function> instead.
+    non-password-authenticated and non-GSSAPI-authenticated connections.
+    If non-superusers need this capability, use
+    <function>dblink_connect_u</function> instead.
    </para>
 
    <para>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index faa8aa3187..b8702284d0 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2054,6 +2054,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gssdeleg" xreflabel="gssdeleg">
+      <term><literal>gssdeleg</literal></term>
+      <listitem>
+       <para>
+        Forward (delegate) GSS credentials to the server.  The default is
+        <literal>disable</literal> which means credentials will not be forwarded
+        to the server.  Set this to <literal>enable</literal> to have
+        credentials forwarded when possible.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
@@ -2715,6 +2727,25 @@ int PQconnectionUsedPassword(const PGconn *conn);
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry id="libpq-PQconnectionUsedGSSAPI">
+     <term><function>PQconnectionUsedGSSAPI</function><indexterm><primary>PQconnectionUsedGSSAPI</primary></indexterm></term>
+     <listitem>
+      <para>
+       Returns true (1) if the connection authentication method
+       used GSSAPI. Returns false (0) if not.
+
+<synopsis>
+int PQconnectionUsedGSSAPI(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       This function can be applied to detect whether the connection was
+       authenticated with GSSAPI.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </para>
 
@@ -8237,6 +8268,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGGSSDELEG</envar></primary>
+      </indexterm>
+      <envar>PGGSSDELEG</envar> behaves the same as the <xref
+      linkend="libpq-connect-gssdeleg"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f33a1c56c..e8ab803267 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3573,6 +3573,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        True if GSSAPI encryption is in use on this connection
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>credentials_delegated</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if GSSAPI credentials were delegated on this connection.
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index a122794df3..b9a5b0eac8 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -169,9 +169,10 @@
     <literal>sslcert</literal> or <literal>sslkey</literal> settings.
    </para>
    <para>
-    Only superusers may connect to foreign servers without password
-    authentication, so always specify the <literal>password</literal> option
-    for user mappings belonging to non-superusers.
+    Non-superusers may connect to foreign servers using password
+    authentication or with GSSAPI delegated credentials, so specify the
+    <literal>password</literal> option for user mappings belonging to
+    non-superusers where password authentication is required.
    </para>
    <para>
     A superuser may override this check on a per-user-mapping basis by setting
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 701c340fc4..2129c916aa 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -979,7 +979,8 @@ CREATE VIEW pg_stat_gssapi AS
             S.pid,
             S.gss_auth AS gss_authenticated,
             S.gss_princ AS principal,
-            S.gss_enc AS encrypted
+            S.gss_enc AS encrypted,
+            S.gss_deleg AS credentials_delegated
     FROM pg_stat_get_activity(NULL) AS S
     WHERE S.client_port IS NOT NULL;
 
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index dca02271dc..6e1977fa62 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -574,6 +574,7 @@ static const struct ConnectionOption libpq_conninfo_options[] = {
 	{"requiressl", ForeignServerRelationId},
 	{"sslmode", ForeignServerRelationId},
 	{"gsslib", ForeignServerRelationId},
+	{"gssdeleg", ForeignServerRelationId},
 	{NULL, InvalidOid}
 };
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index bc0cf26b12..00ec9da284 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -165,6 +165,7 @@ static int	CheckCertAuth(Port *port);
  */
 char	   *pg_krb_server_keyfile;
 bool		pg_krb_caseins_users;
+bool		pg_gss_accept_deleg;
 
 
 /*----------------------------------------------------------------
@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
 	int			mtype;
 	StringInfoData buf;
 	gss_buffer_desc gbuf;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
@@ -947,6 +949,9 @@ pg_GSS_recvauth(Port *port)
 	 */
 	port->gss->ctx = GSS_C_NO_CONTEXT;
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Loop through GSSAPI message exchange. This exchange can consist of
 	 * multiple messages sent in both directions. First message is always from
@@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port)
 										  &port->gss->outbuf,
 										  &gflags,
 										  NULL,
-										  NULL);
+										  pg_gss_accept_deleg ? &delegated_creds : NULL);
 
 		/* gbuf no longer used */
 		pfree(buf.data);
@@ -1009,6 +1014,12 @@ pg_GSS_recvauth(Port *port)
 
 		CHECK_FOR_INTERRUPTS();
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		if (port->gss->outbuf.length != 0)
 		{
 			/*
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index fb39c760d8..64d41e5291 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -92,3 +92,56 @@ pg_GSS_error(const char *errmsg,
 			(errmsg_internal("%s", errmsg),
 			 errdetail_internal("%s: %s", msg_major, msg_minor)));
 }
+
+/*
+ * Store the credentials passed in into the memory cache for later usage.
+ *
+ * This allows credentials to be delegated to us for us to use to connect
+ * to other systems with, using, e.g. postgres_fdw or dblink.
+ */
+#define GSS_MEMORY_CACHE "MEMORY:"
+void
+pg_store_delegated_credential(gss_cred_id_t cred)
+{
+	OM_uint32	major,
+				minor;
+	gss_OID_set mech;
+	gss_cred_usage_t usage;
+	gss_key_value_element_desc cc;
+	gss_key_value_set_desc ccset;
+
+	cc.key = "ccache";
+	cc.value = GSS_MEMORY_CACHE;
+	ccset.count = 1;
+	ccset.elements = &cc;
+
+	/* Make the delegated credential only available to current process */
+	major = gss_store_cred_into(&minor,
+								cred,
+								GSS_C_INITIATE, /* credential only used for
+												 * starting libpq connection */
+								GSS_C_NULL_OID, /* store all */
+								true,	/* overwrite */
+								true,	/* make default */
+								&ccset,
+								&mech,
+								&usage);
+
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_store_cred", major, minor);
+	}
+
+	/* Credential stored, so we can release our credential handle. */
+	major = gss_release_cred(&minor, &cred);
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_release_cred", major, minor);
+	}
+
+	/*
+	 * Set KRB5CCNAME for this backend, so that later calls to
+	 * gss_acquire_cred will find the delegated credentials we stored.
+	 */
+	setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
+}
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 3b55f43199..73f8ce8554 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -497,6 +497,7 @@ secure_open_gssapi(Port *port)
 	bool		complete_next = false;
 	OM_uint32	major,
 				minor;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Allocate subsidiary Port data for GSSAPI operations.
@@ -504,6 +505,9 @@ secure_open_gssapi(Port *port)
 	port->gss = (pg_gssinfo *)
 		MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Allocate buffers and initialize state variables.  By malloc'ing the
 	 * buffers at this point, we avoid wasting static data space in processes
@@ -588,7 +592,8 @@ secure_open_gssapi(Port *port)
 									   GSS_C_NO_CREDENTIAL, &input,
 									   GSS_C_NO_CHANNEL_BINDINGS,
 									   &port->gss->name, NULL, &output, NULL,
-									   NULL, NULL);
+									   NULL, pg_gss_accept_deleg ? &delegated_creds : NULL);
+
 		if (GSS_ERROR(major))
 		{
 			pg_GSS_error(_("could not accept GSSAPI security context"),
@@ -605,6 +610,12 @@ secure_open_gssapi(Port *port)
 			complete_next = true;
 		}
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		/* Done handling the incoming packet, reset our buffer */
 		PqGSSRecvLength = 0;
 
@@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port)
 
 	return port->gss->princ;
 }
+
+/*
+ * Return if GSSAPI delegated credentials were included on this
+ * connection.
+ */
+bool
+be_gssapi_get_deleg(Port *port)
+{
+	if (!port || !port->gss)
+		return NULL;
+
+	return port->gss->delegated_creds;
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 608d01ea0d..391d5de043 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -384,6 +384,7 @@ pgstat_bestart(void)
 		lbeentry.st_gss = true;
 		lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
 		lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
+		lgssstatus.gss_deleg = be_gssapi_get_deleg(MyProcPort);
 		if (princ)
 			strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
 	}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ae180da4d0..e79b065d21 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS	30
+#define PG_STAT_GET_ACTIVITY_COLS	31
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			pfree(clipped_activity);
 
 			/* leader_pid */
-			nulls[28] = true;
+			nulls[29] = true;
 
 			proc = BackendPidGetProc(beentry->st_procpid);
 
@@ -432,8 +432,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				 */
 				if (leader && leader->pid != beentry->st_procpid)
 				{
-					values[28] = Int32GetDatum(leader->pid);
-					nulls[28] = false;
+					values[29] = Int32GetDatum(leader->pid);
+					nulls[29] = false;
 				}
 				else if (beentry->st_backendType == B_BG_WORKER)
 				{
@@ -441,8 +441,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 					if (leader_pid != InvalidPid)
 					{
-						values[28] = Int32GetDatum(leader_pid);
-						nulls[28] = false;
+						values[29] = Int32GetDatum(leader_pid);
+						nulls[29] = false;
 					}
 				}
 			}
@@ -600,6 +600,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				values[25] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
 				values[26] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
 				values[27] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
+				values[28] = BoolGetDatum(beentry->st_gssstatus->gss_deleg);	/* GSS credentials
+																				 * delegated */
 			}
 			else
 			{
@@ -607,11 +609,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				nulls[26] = true;	/* No GSS principal */
 				values[27] = BoolGetDatum(false);	/* GSS Encryption not in
 													 * use */
+				values[28] = BoolGetDatum(false);	/* GSS credentials not
+													 * delegated */
 			}
 			if (beentry->st_query_id == 0)
-				nulls[29] = true;
+				nulls[30] = true;
 			else
-				values[29] = UInt64GetDatum(beentry->st_query_id);
+				values[30] = UInt64GetDatum(beentry->st_query_id);
 		}
 		else
 		{
@@ -640,6 +644,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[27] = true;
 			nulls[28] = true;
 			nulls[29] = true;
+			nulls[30] = true;
 		}
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 60feae0f1b..5af87a7868 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -282,15 +282,17 @@ PerformAuthentication(Port *port)
 
 			if (princ)
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s, principal=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
 								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"),
 								 princ);
 			else
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
-								 be_gssapi_get_enc(port) ? _("yes") : _("no"));
+								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"));
 		}
 #endif
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1067537e74..cab3ddbe11 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -1727,6 +1727,16 @@ struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"gss_accept_deleg", PGC_SIGHUP, CONN_AUTH_AUTH,
+			gettext_noop("Sets whether GSSAPI delegation should be accepted from the client."),
+			NULL
+		},
+		&pg_gss_accept_deleg,
+		false,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
 			gettext_noop("Warn about backslash escapes in ordinary string literals."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e715aff3b8..dce5049bc2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -101,6 +101,7 @@
 # GSSAPI using Kerberos
 #krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
 #krb_caseins_users = off
+#gss_accept_deleg = off
 
 # - SSL -
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dcbc4c2eb5..b516cee8bd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5438,9 +5438,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}',
+  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,bool,int4,int8}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_deleg,leader_pid,query_id}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 9916c99df1..e4d0e38c1e 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -18,6 +18,7 @@
 
 extern PGDLLIMPORT char *pg_krb_server_keyfile;
 extern PGDLLIMPORT bool pg_krb_caseins_users;
+extern PGDLLIMPORT bool pg_gss_accept_deleg;
 extern PGDLLIMPORT char *pg_krb_realm;
 
 extern void ClientAuthentication(Port *port);
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
index facd24ff7f..0381f0ce77 100644
--- a/src/include/libpq/be-gssapi-common.h
+++ b/src/include/libpq/be-gssapi-common.h
@@ -18,13 +18,16 @@
 
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
+#include <gssapi_ext.h>
 #else
 #include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
 #endif
 
 extern void pg_GSS_error(const char *errmsg,
 						 OM_uint32 maj_stat, OM_uint32 min_stat);
 
+extern void pg_store_delegated_credential(gss_cred_id_t cred);
 #endif							/* ENABLE_GSS */
 
 #endif							/* BE_GSSAPI_COMMON_H */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ac6407e9f6..e9df4295e2 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -84,6 +84,7 @@ typedef struct
 								 * GSSAPI auth was not used */
 	bool		auth;			/* GSSAPI Authentication used */
 	bool		enc;			/* GSSAPI encryption in use */
+	bool		delegated_creds;	/* GSSAPI Delegated credentials */
 #endif
 } pg_gssinfo;
 #endif
@@ -328,6 +329,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tls_init_hook;
 extern bool be_gssapi_get_auth(Port *port);
 extern bool be_gssapi_get_enc(Port *port);
 extern const char *be_gssapi_get_princ(Port *port);
+extern bool be_gssapi_get_deleg(Port *port);
 
 /* Read and write to a GSSAPI-encrypted connection. */
 extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index f7bd83113a..9651cb1d0c 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -77,6 +77,7 @@ typedef struct PgBackendGSSStatus
 	char		gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
 	bool		gss_auth;		/* If GSSAPI authentication was used */
 	bool		gss_enc;		/* If encryption is being used */
+	bool		gss_deleg;		/* If credentials delegated */
 
 } PgBackendGSSStatus;
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index e8bcc88370..7ded77aff3 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -186,3 +186,4 @@ PQpipelineStatus          183
 PQsetTraceFlags           184
 PQmblenBounded            185
 PQsendFlushRequest        186
+PQconnectionUsedGSSAPI    187
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index b0550e6332..fe2634230a 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -58,7 +58,8 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 {
 	OM_uint32	maj_stat,
 				min_stat,
-				lmin_s;
+				lmin_s,
+				gss_flags = GSS_C_MUTUAL_FLAG;
 	gss_buffer_desc ginbuf;
 	gss_buffer_desc goutbuf;
 
@@ -92,12 +93,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 		ginbuf.value = NULL;
 	}
 
+	/* Only try to acquire credentials if GSS delegation isn't disabled. */
+	if (!pg_GSS_have_cred_cache(&conn->gcred))
+		conn->gcred = GSS_C_NO_CREDENTIAL;
+
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+		gss_flags |= GSS_C_DELEG_FLAG;
+
 	maj_stat = gss_init_sec_context(&min_stat,
-									GSS_C_NO_CREDENTIAL,
+									conn->gcred,
 									&conn->gctx,
 									conn->gtarg_nam,
 									GSS_C_NO_OID,
-									GSS_C_MUTUAL_FLAG,
+									gss_flags,
 									0,
 									GSS_C_NO_CHANNEL_BINDINGS,
 									(ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
@@ -139,6 +147,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 	{
 		conn->client_finished_auth = true;
 		gss_release_name(&lmin_s, &conn->gtarg_nam);
+		conn->gssapi_used = true;
 	}
 
 	return STATUS_OK;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 40fef0e2c8..fcd3d0d9a3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -343,6 +343,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"GSS-library", "", 7,	/* sizeof("gssapi") == 7 */
 	offsetof(struct pg_conn, gsslib)},
 
+	{"gssdeleg", "PGGSSDELEG", NULL, NULL,
+		"GSS-delegation", "", 8,	/* sizeof("disable") == 8 */
+	offsetof(struct pg_conn, gssdeleg)},
+
 	{"replication", NULL, NULL, NULL,
 		"Replication", "D", 5,
 	offsetof(struct pg_conn, replication)},
@@ -617,6 +621,7 @@ pqDropServerData(PGconn *conn)
 	conn->auth_req_received = false;
 	conn->client_finished_auth = false;
 	conn->password_needed = false;
+	conn->gssapi_used = false;
 	conn->write_failed = false;
 	free(conn->write_err_msg);
 	conn->write_err_msg = NULL;
@@ -4448,6 +4453,7 @@ freePGconn(PGconn *conn)
 	free(conn->gssencmode);
 	free(conn->krbsrvname);
 	free(conn->gsslib);
+	free(conn->gssdeleg);
 	free(conn->connip);
 	/* Note that conn->Pfdebug is not ours to close or free */
 	free(conn->write_err_msg);
@@ -7312,6 +7318,17 @@ PQconnectionUsedPassword(const PGconn *conn)
 		return false;
 }
 
+int
+PQconnectionUsedGSSAPI(const PGconn *conn)
+{
+	if (!conn)
+		return false;
+	if (conn->gssapi_used)
+		return true;
+	else
+		return false;
+}
+
 int
 PQclientEncoding(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 038e847b7e..bf87ae3fd1 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -477,7 +477,8 @@ pqsecure_open_gss(PGconn *conn)
 {
 	ssize_t		ret;
 	OM_uint32	major,
-				minor;
+				minor,
+				gss_flags = GSS_REQUIRED_FLAGS;
 	uint32		netlen;
 	PostgresPollingStatusType result;
 	gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
@@ -621,13 +622,30 @@ pqsecure_open_gss(PGconn *conn)
 	if (ret != STATUS_OK)
 		return PGRES_POLLING_FAILED;
 
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+	{
+		/* Acquire credentials if possbile */
+		if (conn->gcred == GSS_C_NO_CREDENTIAL)
+			(void) pg_GSS_have_cred_cache(&conn->gcred);
+
+		/*
+		 * We have credentials and gssdeleg is enabled, so request credential
+		 * delegation.  This may or may not actually result in credentials
+		 * being delegated- it depends on if the forwardable flag has been set
+		 * in the credential and if the server is configured to accept
+		 * delegated credentials.
+		 */
+		if (conn->gcred != GSS_C_NO_CREDENTIAL)
+			gss_flags |= GSS_C_DELEG_FLAG;
+	}
+
 	/*
 	 * Call GSS init context, either with an empty input, or with a complete
 	 * packet from the server.
 	 */
 	major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
 								 conn->gtarg_nam, GSS_C_NO_OID,
-								 GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+								 gss_flags, 0, 0, &input, NULL,
 								 &output, NULL, NULL);
 
 	/* GSS Init Sec Context uses the whole packet, so clear it */
@@ -647,6 +665,7 @@ pqsecure_open_gss(PGconn *conn)
 		 * to do GSS wrapping/unwrapping.
 		 */
 		conn->gssenc = true;
+		conn->gssapi_used = true;
 
 		/* Clean up */
 		gss_release_cred(&minor, &conn->gcred);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index f3d9220496..7476dbe0e9 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -354,6 +354,7 @@ extern int	PQbackendPID(const PGconn *conn);
 extern PGpipelineStatus PQpipelineStatus(const PGconn *conn);
 extern int	PQconnectionNeedsPassword(const PGconn *conn);
 extern int	PQconnectionUsedPassword(const PGconn *conn);
+extern int	PQconnectionUsedGSSAPI(const PGconn *conn);
 extern int	PQclientEncoding(const PGconn *conn);
 extern int	PQsetClientEncoding(PGconn *conn, const char *encoding);
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d93e976ca5..ce0167c1b6 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -404,6 +404,7 @@ struct pg_conn
 	char	   *krbsrvname;		/* Kerberos service name */
 	char	   *gsslib;			/* What GSS library to use ("gssapi" or
 								 * "sspi") */
+	char	   *gssdeleg;		/* Try to delegate GSS credentials? */
 	char	   *ssl_min_protocol_version;	/* minimum TLS protocol version */
 	char	   *ssl_max_protocol_version;	/* maximum TLS protocol version */
 	char	   *target_session_attrs;	/* desired session properties */
@@ -465,6 +466,7 @@ struct pg_conn
 	int			sversion;		/* server version, e.g. 70401 for 7.4.1 */
 	bool		auth_req_received;	/* true if any type of auth req received */
 	bool		password_needed;	/* true if server demanded a password */
+	bool		gssapi_used;	/* true if authenticated via gssapi */
 	bool		sigpipe_so;		/* have we masked SIGPIPE via SO_NOSIGPIPE? */
 	bool		sigpipe_flag;	/* can we mask SIGPIPE via MSG_NOSIGNAL? */
 	bool		write_failed;	/* have we had a write failure on sock? */
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
index 7765f3f93b..f460d2c0e7 100644
--- a/src/test/kerberos/Makefile
+++ b/src/test/kerberos/Makefile
@@ -13,6 +13,9 @@ subdir = src/test/kerberos
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
+EXTRA_INSTALL += contrib/postgres_fdw
+EXTRA_INSTALL += contrib/dblink
+
 export with_gssapi with_krb_srvnam
 
 check:
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 458246b4d7..bf12752529 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -7,6 +7,9 @@
 # that the server-side pg_stat_gssapi view reports what we expect to
 # see for each test and that SYSTEM_USER returns what we expect to see.
 #
+# Also test that GSSAPI delegation is working properly and that those
+# credentials can be used to make dblink / postgres_fdw connections.
+#
 # Since this requires setting up a full KDC, it doesn't make much sense
 # to have multiple test scripts (since they'd have to also create their
 # own KDC and that could cause race conditions or other problems)- so
@@ -56,6 +59,7 @@ elsif ($^O eq 'linux')
 
 my $krb5_config  = 'krb5-config';
 my $kinit        = 'kinit';
+my $klist        = 'klist';
 my $kdb5_util    = 'kdb5_util';
 my $kadmin_local = 'kadmin.local';
 my $krb5kdc      = 'krb5kdc';
@@ -64,6 +68,7 @@ if ($krb5_bin_dir && -d $krb5_bin_dir)
 {
 	$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
 	$kinit       = $krb5_bin_dir . '/' . $kinit;
+	$klist       = $krb5_bin_dir . '/' . $klist;
 }
 if ($krb5_sbin_dir && -d $krb5_sbin_dir)
 {
@@ -86,6 +91,8 @@ my $kdc_datadir = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc";
 my $kdc_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc.pid";
 my $keytab      = "${PostgreSQL::Test::Utils::tmp_check}/krb5.keytab";
 
+my $pgpass      = "${PostgreSQL::Test::Utils::tmp_check}/.pgpass";
+
 my $dbname      = 'postgres';
 my $username    = 'test1';
 my $application = '001_auth.pl';
@@ -100,6 +107,14 @@ $stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
   or BAIL_OUT("could not get Kerberos version");
 $krb5_version = $1;
 
+# Construct a pgpass file to make sure we don't use it
+append_to_file(
+	$pgpass,
+	'*:*:*:*:abc123'
+);
+
+chmod 0600, $pgpass;
+
 # Build the krb5.conf to use.
 #
 # Explicitly specify the default (test) realm and the KDC for
@@ -126,12 +141,14 @@ kdc = FILE:$kdc_log
 dns_lookup_realm = false
 dns_lookup_kdc = false
 default_realm = $realm
+forwardable = false
 rdns = false
 
 [realms]
 $realm = {
     kdc = $hostaddr:$kdc_port
-}!);
+}
+!);
 
 append_to_file(
 	$kdc_conf,
@@ -204,7 +221,28 @@ lc_messages = 'C'
 });
 $node->start;
 
+my $port = $node->port();
+
 $node->safe_psql('postgres', 'CREATE USER test1;');
+$node->safe_psql('postgres', "CREATE USER test2 WITH ENCRYPTED PASSWORD 'abc123';");
+$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;');
+$node->safe_psql('postgres', 'CREATE EXTENSION dblink;');
+$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'postgres');");
+$node->safe_psql('postgres', "CREATE SERVER s2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (port '$port', dbname 'postgres', passfile '$pgpass');");
+
+$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;');
+
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTIONS (user 'test1');");
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s2 OPTIONS (user 'test2');");
+
+$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);");
+$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);");
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;");
+$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;");
+
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf2 (c1 int) SERVER s2 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON tf2 TO test1;");
 
 # Set up a table for SYSTEM_USER parallel worker testing.
 $node->safe_psql('postgres',
@@ -271,12 +309,16 @@ sub test_query
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss map=mymap});
+	qq{
+local all test2 scram-sha-256
+host all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
 
 run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
 
 test_access(
 	$node,
@@ -294,35 +336,58 @@ $node->restart;
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'',
-	'succeeds with mapping with default gssencmode and host hba',
+	'succeeds with mapping with default gssencmode and host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred with host hba',
+	'succeeds with GSS-encrypted access preferred with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
+
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=require',
-	'succeeds with GSS-encrypted access required with host hba',
+	'succeeds with GSS-encrypted access required with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+
 # Test that we can transport a reasonable amount of data.
 test_query(
 	$node,
@@ -389,29 +454,164 @@ test_query(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostgssenc all all $hostaddr/32 gss map=mymap
+});
+
+string_replace_file($krb5_conf, "forwardable = false", "forwardable = true");
+
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=off});
+$node->restart;
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=on});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred and hostgssenc hba',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=require',
-	'succeeds with GSS-encrypted access required and hostgssenc hba',
+	'gssencmode=require gssdeleg=disable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials explicitly not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+my $psql_out = '';
+my $psql_stderr = '';
+my $psql_rc = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink attempt fails without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','dblink does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf1;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials and with passfile');
+
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
 	'fails with GSS encryption disabled and hostgssenc hba');
 
@@ -427,54 +627,123 @@ $node->connect_ok(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostnogssenc all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=prefer',
+	'gssencmode=prefer gssdeleg=enable',
 	'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
 );
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=require',
 	'fails with GSS-encrypted access required and hostnogssenc hba');
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=disable',
+	'gssencmode=disable gssdeleg=enable',
 	'succeeds with GSS encryption disabled and hostnogssenc hba',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
+);
+
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'dblink works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'postgres_fdw works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work with delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work with delegated credentials and with passfile');
 
 truncate($node->data_dir . '/pg_ident.conf', 0);
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'',
+	'gssdeleg=enable',
 	'succeeds with include_realm=0 and defaults',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
 );
 
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'dblink works encrypted');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'postgres_fdw works encrypted');
+
 # Reset pg_hba.conf, and cause a usermap failure with an authentication
 # that has passed.
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG
+});
 $node->restart;
 
 test_access(
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 878e12b15e..9249954b49 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -65,6 +65,7 @@ our @EXPORT = qw(
   slurp_dir
   slurp_file
   append_to_file
+  string_replace_file
   check_mode_recursive
   chmod_recursive
   check_pg_config
@@ -549,6 +550,32 @@ sub append_to_file
 
 =pod
 
+=item string_replace_file(filename, find, replace)
+
+Find and replace string of a given file.
+
+=cut
+
+sub string_replace_file
+{
+	my ($filename, $find, $replace) = @_;
+	open(my $in, '<', $filename);
+	my $content;
+	while(<$in>)
+	{
+		$_ =~ s/$find/$replace/;
+		$content = $content.$_;
+	}
+	close $in;
+	open(my $out, '>', $filename);
+	print $out $content;
+	close($out);
+
+	return;
+}
+
+=pod
+
 =item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
 
 Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2d75dd6656..919d947ec0 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1760,7 +1760,7 @@ pg_stat_activity| SELECT s.datid,
     s.query_id,
     s.query,
     s.backend_type
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1876,8 +1876,9 @@ pg_stat_database_conflicts| SELECT oid AS datid,
 pg_stat_gssapi| SELECT pid,
     gss_auth AS gss_authenticated,
     gss_princ AS principal,
-    gss_enc AS encrypted
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+    gss_enc AS encrypted,
+    gss_deleg AS credentials_delegated
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_io| SELECT backend_type,
     io_object,
@@ -2075,7 +2076,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     w.reply_time
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_replication_slots| SELECT s.slot_name,
@@ -2109,7 +2110,7 @@ pg_stat_ssl| SELECT pid,
     ssl_client_dn AS client_dn,
     ssl_client_serial AS client_serial,
     ssl_issuer_dn AS issuer_dn
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,
-- 
2.34.1

0002-Explicitly-require-MIT-Kerberos-for-GSSAPI.patchtext/x-diff; charset=us-asciiDownload
From c38dc405f26395e7c4d0d0db052bbd733e5f800b Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 12 Apr 2023 09:35:53 -0400
Subject: [PATCH 2/2] Explicitly require MIT Kerberos for GSSAPI

WHen building with GSSAPI support, explicitly require MIT Kerberos and
check for gssapi_ext.h in configure.ac and meson.build.  Also add
documentation explicitly stating that we now require MIT Kerberos when
building with GSSAPI support.
---
 configure                      | 27 +++++++++++++++++++++++++++
 configure.ac                   |  2 ++
 doc/src/sgml/client-auth.sgml  |  2 +-
 doc/src/sgml/installation.sgml | 21 +++++++++++----------
 meson.build                    | 10 ++++++++++
 5 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/configure b/configure
index dbea7eaf5f..08bcf8f43a 100755
--- a/configure
+++ b/configure
@@ -14104,6 +14104,33 @@ done
 
 fi
 
+done
+
+  for ac_header in gssapi/gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_ext.h" "ac_cv_header_gssapi_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  for ac_header in gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi_ext.h" "ac_cv_header_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  as_fn_error $? "gssapi_ext.h header file is required for GSSAPI" "$LINENO" 5
+fi
+
+done
+
+fi
+
 done
 
 fi
diff --git a/configure.ac b/configure.ac
index dda34304db..c53a9c788e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1562,6 +1562,8 @@ fi
 if test "$with_gssapi" = yes ; then
   AC_CHECK_HEADERS(gssapi/gssapi.h, [],
 	[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
+  AC_CHECK_HEADERS(gssapi/gssapi_ext.h, [],
+	[AC_CHECK_HEADERS(gssapi_ext.h, [], [AC_MSG_ERROR([gssapi_ext.h header file is required for GSSAPI])])])
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index dbba289600..204d09df67 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1426,7 +1426,7 @@ omicron         bryanh                  guest1
     The keytab file is generated using the Kerberos software; see the
     Kerberos documentation for details. The following example shows
     doing this using the <application>kadmin</application> tool of
-    MIT-compatible Kerberos 5 implementations:
+    MIT Kerberos:
 <screen>
 <prompt>kadmin% </prompt><userinput>addprinc -randkey postgres/server.my.domain.org</userinput>
 <prompt>kadmin% </prompt><userinput>ktadd -k krb5.keytab postgres/server.my.domain.org</userinput>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index f451204854..3d839d665a 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -252,9 +252,9 @@ documentation.  See standalone-profile.xsl for details.
 
     <listitem>
      <para>
-      You need <application>Kerberos</application>, <productname>OpenLDAP</productname>,
-      and/or <application>PAM</application>, if you want to support authentication
-      using those services.
+      You need <application>MIT Kerberos</application> (for GSSAPI),
+      <productname>OpenLDAP</productname>, and/or <application>PAM</application>,
+      if you want to support authentication using those services.
      </para>
     </listitem>
 
@@ -1048,9 +1048,9 @@ build-postgresql:
        <term><option>--with-gssapi</option></term>
        <listitem>
         <para>
-         Build with support for GSSAPI authentication. On many systems, the
-         GSSAPI system (usually a part of the Kerberos installation) is not
-         installed in a location
+         Build with support for GSSAPI authentication. MIT Kerberos is required
+         to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+         of the MIT Kerberos installation) is not installed in a location
          that is searched by default (e.g., <filename>/usr/include</filename>,
          <filename>/usr/lib</filename>), so you must use the options
          <option>--with-includes</option> and <option>--with-libraries</option> in
@@ -2497,10 +2497,11 @@ ninja install
       <term><option>-Dgssapi={ auto | enabled | disabled }</option></term>
       <listitem>
        <para>
-        Build with support for GSSAPI authentication. On many systems, the
-        GSSAPI system (usually a part of the Kerberos installation) is not
-        installed in a location that is searched by default (e.g.,
-        <filename>/usr/include</filename>, <filename>/usr/lib</filename>).  In
+        Build with support for GSSAPI authentication. MIT Kerberos is required
+        to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+        of the MIT Kerberos installation) is not installed in a location
+        that is searched by default (e.g., <filename>/usr/include</filename>,
+        <filename>/usr/lib</filename>).  In
         those cases, PostgreSQL will query <command>pkg-config</command> to
         detect the required compiler and linker options.  Defaults to auto.
         <filename>meson configure</filename> will check for the required
diff --git a/meson.build b/meson.build
index b69aaddb1f..3405cc07ee 100644
--- a/meson.build
+++ b/meson.build
@@ -623,6 +623,16 @@ if not gssapiopt.disabled()
     have_gssapi = false
   endif
 
+  if not have_gssapi
+  elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
+      args: test_c_args, include_directories: postgres_inc)
+    cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
+  elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
+    cdata.set('HAVE_GSSAPI_EXT_H', 1)
+  else
+    have_gssapi = false
+  endif
+
   if not have_gssapi
   elif cc.has_function('gss_init_sec_context', dependencies: gssapi,
       args: test_c_args, include_directories: postgres_inc)
-- 
2.34.1

#33Jonathan S. Katz
jkatz@postgresql.org
In reply to: Stephen Frost (#32)
Re: longfin missing gssapi_ext.h

On 4/12/23 10:47 AM, Stephen Frost wrote:

Greetings,

* Jonathan S. Katz (jkatz@postgresql.org) wrote:

On 4/12/23 10:33 AM, Stephen Frost wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Understood. Please find attached the updated patch with changes to the
commit message to indicate that we now require MIT Kerberos, an
additional explicit check for gssapi_ext.h in configure.ac/configure,
along with updated documentation explicitly saying we require MIT
Kerberos for GSSAPI support.

Um ... could you package this as a straight un-revert of the
previous commit, then a delta patch? Would be easier to review.

Sure, reworked that way and attached.

Docs read well. A few questions/commenets:

* On [1] -- do we want to add a note that it's not just Kerberos, but MIT
Kerberos?

Yes, makes sense, updated.

* On [2] -- we mention "kadmin tool of MIT-compatible Kerberos 5" which is
AIUI is still technically correct, but do we want to drop the "-compatible?"
(precedent in [3])

Yup, cleaned that up also.

Updated patch set attached.

Thanks! I'll sign off on the docs portion.

The meson build code looks good to me (I just compared it to what
already exists). Similar comment to the autoconf code.

Thanks,

Jonathan

#34Stephen Frost
sfrost@snowman.net
In reply to: Daniel Gustafsson (#31)
Re: longfin missing gssapi_ext.h

Greetings,

* Daniel Gustafsson (daniel@yesql.se) wrote:

On 12 Apr 2023, at 16:33, Stephen Frost <sfrost@snowman.net> wrote:
Sure, reworked that way and attached.

While not changed in this hunk, does the comment regarding Heimdal still apply?

@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
int mtype;
StringInfoData buf;
gss_buffer_desc gbuf;
+ gss_cred_id_t delegated_creds;

/*
* Use the configured keytab, if there is one. Unfortunately, Heimdal

Good catch. No, it doesn't. I'm not anxious to actually change that
code at this point but we could certainly consider changing it in the
future. I'll update this comment (and the identical one in
secure_open_gssapi) accordingly.

Thanks!

Stephen

#35Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#32)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Updated patch set attached.

LGTM

regards, tom lane

#36Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#35)
2 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Updated patch set attached.

LGTM

Great, thanks.

I cleaned up the commit messages a bit more and added links to the
discussion. If there isn't anything more then I'll plan to push these
later today or tomorrow.

Thanks again!

Stephen

Attachments:

0001-De-Revert-Add-support-for-Kerberos-credential-delega.patchtext/x-diff; charset=us-asciiDownload
From a73e19808c8cb0e5075e6c86ba0d8d29538b1eeb Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 12 Apr 2023 09:33:39 -0400
Subject: [PATCH 1/2] De-Revert "Add support for Kerberos credential
 delegation"

This reverts commit 3d03b24c3 (Revert Add support for Kerberos
credential delegation) which was committed on the grounds of concern
about portability, but on further review and discussion, it's clear that
we are better off explicitly requiring MIT Kerberos as that appears to
be the only GSSAPI library currently that's under proper maintenance
and ongoing development.  The API used for storing credentials was added
to MIT Kerberos over a decade ago while for the other libraries which
appear to be mainly based on Heimdal, which exists explicitly to be a
re-implementation of MIT Kerberos, the API never made it to a released
version (even though it was added to the Heimdal git repo over 5 years
ago..).

This post-feature-freeze change was approved by the RMT.

Discussion: https://postgr.es/m/ZDDO6jaESKaBgej0%40tamriel.snowman.net
---
 contrib/dblink/dblink.c                       | 127 ++++---
 contrib/dblink/expected/dblink.out            |   4 +-
 contrib/postgres_fdw/connection.c             |  72 +++-
 .../postgres_fdw/expected/postgres_fdw.out    |  19 +-
 contrib/postgres_fdw/option.c                 |   6 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     |   3 +-
 doc/src/sgml/config.sgml                      |  17 +
 doc/src/sgml/dblink.sgml                      |   5 +-
 doc/src/sgml/libpq.sgml                       |  41 +++
 doc/src/sgml/monitoring.sgml                  |   9 +
 doc/src/sgml/postgres-fdw.sgml                |   7 +-
 src/backend/catalog/system_views.sql          |   3 +-
 src/backend/foreign/foreign.c                 |   1 +
 src/backend/libpq/auth.c                      |  13 +-
 src/backend/libpq/be-gssapi-common.c          |  53 +++
 src/backend/libpq/be-secure-gssapi.c          |  26 +-
 src/backend/utils/activity/backend_status.c   |   1 +
 src/backend/utils/adt/pgstatfuncs.c           |  21 +-
 src/backend/utils/init/postinit.c             |   8 +-
 src/backend/utils/misc/guc_tables.c           |  10 +
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/catalog/pg_proc.dat               |   6 +-
 src/include/libpq/auth.h                      |   1 +
 src/include/libpq/be-gssapi-common.h          |   3 +
 src/include/libpq/libpq-be.h                  |   2 +
 src/include/utils/backend_status.h            |   1 +
 src/interfaces/libpq/exports.txt              |   1 +
 src/interfaces/libpq/fe-auth.c                |  15 +-
 src/interfaces/libpq/fe-connect.c             |  17 +
 src/interfaces/libpq/fe-secure-gssapi.c       |  23 +-
 src/interfaces/libpq/libpq-fe.h               |   1 +
 src/interfaces/libpq/libpq-int.h              |   2 +
 src/test/kerberos/Makefile                    |   3 +
 src/test/kerberos/t/001_auth.pl               | 331 ++++++++++++++++--
 src/test/perl/PostgreSQL/Test/Utils.pm        |  27 ++
 src/test/regress/expected/rules.out           |  11 +-
 36 files changed, 755 insertions(+), 136 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e..55f75eff36 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -48,6 +48,7 @@
 #include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "libpq-fe.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -111,7 +112,8 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
 static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
 static char *generate_relation_name(Relation rel);
 static void dblink_connstr_check(const char *connstr);
-static void dblink_security_check(PGconn *conn, remoteConn *rconn);
+static bool dblink_connstr_has_pw(const char *connstr);
+static void dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr);
 static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res,
 							 bool fail, const char *fmt,...) pg_attribute_printf(5, 6);
 static char *get_connect_string(const char *servername);
@@ -213,7 +215,7 @@ dblink_get_conn(char *conname_or_str,
 					 errmsg("could not establish connection"),
 					 errdetail_internal("%s", msg)));
 		}
-		dblink_security_check(conn, rconn);
+		dblink_security_check(conn, rconn, connstr);
 		if (PQclientEncoding(conn) != GetDatabaseEncoding())
 			PQsetClientEncoding(conn, GetDatabaseEncodingName());
 		freeconn = true;
@@ -307,7 +309,7 @@ dblink_connect(PG_FUNCTION_ARGS)
 	}
 
 	/* check password actually used if not superuser */
-	dblink_security_check(conn, rconn);
+	dblink_security_check(conn, rconn, connstr);
 
 	/* attempt to set client encoding to match server encoding, if needed */
 	if (PQclientEncoding(conn) != GetDatabaseEncoding())
@@ -2584,64 +2586,99 @@ deleteConnection(const char *name)
 				 errmsg("undefined connection name")));
 }
 
+/*
+ * We need to make sure that the connection made used credentials
+ * which were provided by the user, so check what credentials were
+ * used to connect and then make sure that they came from the user.
+ */
 static void
-dblink_security_check(PGconn *conn, remoteConn *rconn)
+dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
 {
-	if (!superuser())
-	{
-		if (!PQconnectionUsedPassword(conn))
-		{
-			libpqsrv_disconnect(conn);
-			if (rconn)
-				pfree(rconn);
+	/* Superuser bypasses security check */
+	if (superuser())
+		return;
 
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed.")));
-		}
-	}
+	/* If password was used to connect, make sure it was one provided */
+	if (PQconnectionUsedPassword(conn) && dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	/* If GSSAPI creds used to connect, make sure it was one delegated */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Otherwise, fail out */
+	libpqsrv_disconnect(conn);
+	if (rconn)
+		pfree(rconn);
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers may only connect using credentials they provide, eg: password in connection string or delegated GSSAPI credentials"),
+			 errhint("Ensure provided credentials match target server's authentication method.")));
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
- * prevents a password from being picked up from .pgpass, a service file,
- * the environment, etc.  We don't want the postgres user's passwords
- * to be accessible to non-superusers.
+ * Function to check if the connection string includes an explicit
+ * password, needed to ensure that non-superuser password-based auth
+ * is using a provided password and not one picked up from the
+ * environment.
  */
-static void
-dblink_connstr_check(const char *connstr)
+static bool
+dblink_connstr_has_pw(const char *connstr)
 {
-	if (!superuser())
-	{
-		PQconninfoOption *options;
-		PQconninfoOption *option;
-		bool		connstr_gives_password = false;
+	PQconninfoOption *options;
+	PQconninfoOption *option;
+	bool		connstr_gives_password = false;
 
-		options = PQconninfoParse(connstr, NULL);
-		if (options)
+	options = PQconninfoParse(connstr, NULL);
+	if (options)
+	{
+		for (option = options; option->keyword != NULL; option++)
 		{
-			for (option = options; option->keyword != NULL; option++)
+			if (strcmp(option->keyword, "password") == 0)
 			{
-				if (strcmp(option->keyword, "password") == 0)
+				if (option->val != NULL && option->val[0] != '\0')
 				{
-					if (option->val != NULL && option->val[0] != '\0')
-					{
-						connstr_gives_password = true;
-						break;
-					}
+					connstr_gives_password = true;
+					break;
 				}
 			}
-			PQconninfoFree(options);
 		}
-
-		if (!connstr_gives_password)
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superusers must provide a password in the connection string.")));
+		PQconninfoFree(options);
 	}
+
+	return connstr_gives_password;
+}
+
+/*
+ * For non-superusers, insist that the connstr specify a password, except
+ * if GSSAPI credentials have been delegated (and we check that they are used
+ * for the connection in dblink_security_check later).  This prevents a
+ * password or GSSAPI credentials from being picked up from .pgpass, a
+ * service file, the environment, etc.  We don't want the postgres user's
+ * passwords or Kerberos credentials to be accessible to non-superusers.
+ */
+static void
+dblink_connstr_check(const char *connstr)
+{
+	if (superuser())
+		return;
+
+	if (dblink_connstr_has_pw(connstr))
+		return;
+
+#ifdef ENABLE_GSS
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.")));
 }
 
 /*
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index 0f5050b409..7809f58d96 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -903,8 +903,8 @@ GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO regress_dblink_user;
 SET SESSION AUTHORIZATION regress_dblink_user;
 -- should fail
 SELECT dblink_connect('myconn', 'fdtest');
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the connection string.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must provide a password in the connection string or send delegated GSSAPI credentials.
 -- should succeed
 SELECT dblink_connect_u('myconn', 'fdtest');
  dblink_connect_u 
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 2969351e9a..75d93d6ead 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
 #include "funcapi.h"
+#include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -149,6 +150,8 @@ static void pgfdw_finish_pre_subcommit_cleanup(List *pending_entries,
 static void pgfdw_finish_abort_cleanup(List *pending_entries,
 									   List *cancel_requested,
 									   bool toplevel);
+static void pgfdw_security_check(const char **keywords, const char **values,
+								 UserMapping *user, PGconn *conn);
 static bool UserMappingPasswordRequired(UserMapping *user);
 static bool disconnect_cached_connections(Oid serverid);
 
@@ -384,6 +387,47 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
 		 entry->conn, server->servername, user->umid, user->userid);
 }
 
+/*
+ * Check that non-superuser has used password or delegated credentials
+ * to establish connection; otherwise, he's piggybacking on the
+ * postgres server's user identity. See also dblink_security_check()
+ * in contrib/dblink and check_conn_params.
+ */
+static void
+pgfdw_security_check(const char **keywords, const char **values, UserMapping *user, PGconn *conn)
+{
+	/* Superusers bypass the check */
+	if (superuser_arg(user->userid))
+		return;
+
+#ifdef ENABLE_GSS
+	/* Connected via GSSAPI with delegated credentials- all good. */
+	if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
+	/* Ok if superuser set PW required false. */
+	if (!UserMappingPasswordRequired(user))
+		return;
+
+	/* Connected via PW, with PW required true, and provided non-empty PW. */
+	if (PQconnectionUsedPassword(conn))
+	{
+		/* ok if params contain a non-empty password */
+		for (int i = 0; keywords[i] != NULL; i++)
+		{
+			if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
+				return;
+		}
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials."),
+			 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+}
+
 /*
  * Connect to remote server using specified server and user mapping properties.
  */
@@ -495,19 +539,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 							server->servername),
 					 errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
 
-		/*
-		 * Check that non-superuser has used password to establish connection;
-		 * otherwise, he's piggybacking on the postgres server's user
-		 * identity. See also dblink_security_check() in contrib/dblink and
-		 * check_conn_params.
-		 */
-		if (!superuser_arg(user->userid) && UserMappingPasswordRequired(user) &&
-			!PQconnectionUsedPassword(conn))
-			ereport(ERROR,
-					(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-					 errmsg("password is required"),
-					 errdetail("Non-superuser cannot connect if the server does not request a password."),
-					 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
+		/* Perform post-connection security checks */
+		pgfdw_security_check(keywords, values, user, conn);
 
 		/* Prepare new session for use */
 		configure_remote_session(conn);
@@ -561,7 +594,8 @@ UserMappingPasswordRequired(UserMapping *user)
 }
 
 /*
- * For non-superusers, insist that the connstr specify a password.  This
+ * For non-superusers, insist that the connstr specify a password or that the
+ * user provided their own GSSAPI delegated credentials.  This
  * prevents a password from being picked up from .pgpass, a service file, the
  * environment, etc.  We don't want the postgres user's passwords,
  * certificates, etc to be accessible to non-superusers.  (See also
@@ -576,6 +610,12 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 	if (superuser_arg(user->userid))
 		return;
 
+#ifdef ENABLE_GSS
+	/* ok if the user provided their own delegated credentials */
+	if (be_gssapi_get_deleg(MyProcPort))
+		return;
+#endif
+
 	/* ok if params contain a non-empty password */
 	for (i = 0; keywords[i] != NULL; i++)
 	{
@@ -589,8 +629,8 @@ check_conn_params(const char **keywords, const char **values, UserMapping *user)
 
 	ereport(ERROR,
 			(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
-			 errmsg("password is required"),
-			 errdetail("Non-superusers must provide a password in the user mapping.")));
+			 errmsg("password or GSSAPI delegated credentials required"),
+			 errdetail("Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.")));
 }
 
 /*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 8f6a04f71b..fd5752bd5b 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 -- Error, invalid list syntax
@@ -9840,8 +9841,8 @@ CREATE FOREIGN TABLE pg_temp.ft1_nopw (
 	c8 user_enum
 ) SERVER loopback_nopw OPTIONS (schema_name 'public', table_name 'ft1');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 -- If we add a password to the connstr it'll fail, because we don't allow passwords
 -- in connstrs only in user mappings.
 ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
@@ -9853,16 +9854,16 @@ HINT:  Perhaps you meant the option "passfile".
 -- This won't work with installcheck, but neither will most of the FDW checks.
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password 'dummypw');
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 -- Unpriv user cannot make the mapping passwordless
 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback_nopw OPTIONS (ADD password_required 'false');
 ERROR:  password_required=false is superuser-only
 HINT:  User mappings with the password_required option set to false may only be created or modified by the superuser.
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superuser cannot connect if the server does not request a password.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials.
 HINT:  Target server's authentication method must be changed or password_required=false set in the user mapping attributes.
 RESET ROLE;
 -- But the superuser can
@@ -9890,8 +9891,8 @@ DROP USER MAPPING FOR CURRENT_USER SERVER loopback_nopw;
 -- This will fail again as it'll resolve the user mapping for public, which
 -- lacks password_required=false
 SELECT 1 FROM ft1_nopw LIMIT 1;
-ERROR:  password is required
-DETAIL:  Non-superusers must provide a password in the user mapping.
+ERROR:  password or GSSAPI delegated credentials required
+DETAIL:  Non-superusers must delegate GSSAPI credentials or provide a password in the user mapping.
 RESET ROLE;
 -- The user mapping for public is passwordless and lacks the password_required=false
 -- mapping option, but will work because the current user is a superuser.
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 4229d2048c..fe40d50c6d 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -288,6 +288,12 @@ InitPgFdwOptions(void)
 		{"sslcert", UserMappingRelationId, true},
 		{"sslkey", UserMappingRelationId, true},
 
+		/*
+		 * gssdeleg is also a libpq option but should be allowed in a user
+		 * mapping context too
+		 */
+		{"gssdeleg", UserMappingRelationId, true},
+
 		{NULL, InvalidOid, false}
 	};
 
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5bd69339df..c05046f867 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS (
 	sslcrl 'value',
 	--requirepeer 'value',
 	krbsrvname 'value',
-	gsslib 'value'
+	gsslib 'value',
+	gssdeleg 'value'
 	--replication 'value'
 );
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f81c2045ec..091a79d4f3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1190,6 +1190,23 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-gss-accept-deleg" xreflabel="gss_accept_deleg">
+      <term><varname>gss_accept_deleg</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>gss_accept_deleg</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Sets whether GSSAPI delegation should be accepted from the client.
+        The default is <literal>off</literal> meaning credentials from the client will
+        NOT be accepted.  Changing this to <literal>on</literal> will make the server
+        accept credentials delegated to it from the client. This parameter can only be
+        set in the <filename>postgresql.conf</filename> file or on the server command line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-db-user-namespace" xreflabel="db_user_namespace">
       <term><varname>db_user_namespace</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 17f9d99b1c..7d25f24f49 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -117,8 +117,9 @@ dblink_connect(text connname, text connstr) returns text
 
    <para>
     Only superusers may use <function>dblink_connect</function> to create
-    non-password-authenticated connections.  If non-superusers need this
-    capability, use <function>dblink_connect_u</function> instead.
+    non-password-authenticated and non-GSSAPI-authenticated connections.
+    If non-superusers need this capability, use
+    <function>dblink_connect_u</function> instead.
    </para>
 
    <para>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index faa8aa3187..b8702284d0 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2054,6 +2054,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gssdeleg" xreflabel="gssdeleg">
+      <term><literal>gssdeleg</literal></term>
+      <listitem>
+       <para>
+        Forward (delegate) GSS credentials to the server.  The default is
+        <literal>disable</literal> which means credentials will not be forwarded
+        to the server.  Set this to <literal>enable</literal> to have
+        credentials forwarded when possible.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
@@ -2715,6 +2727,25 @@ int PQconnectionUsedPassword(const PGconn *conn);
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry id="libpq-PQconnectionUsedGSSAPI">
+     <term><function>PQconnectionUsedGSSAPI</function><indexterm><primary>PQconnectionUsedGSSAPI</primary></indexterm></term>
+     <listitem>
+      <para>
+       Returns true (1) if the connection authentication method
+       used GSSAPI. Returns false (0) if not.
+
+<synopsis>
+int PQconnectionUsedGSSAPI(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       This function can be applied to detect whether the connection was
+       authenticated with GSSAPI.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </para>
 
@@ -8237,6 +8268,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGGSSDELEG</envar></primary>
+      </indexterm>
+      <envar>PGGSSDELEG</envar> behaves the same as the <xref
+      linkend="libpq-connect-gssdeleg"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3f33a1c56c..e8ab803267 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3573,6 +3573,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        True if GSSAPI encryption is in use on this connection
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>credentials_delegated</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if GSSAPI credentials were delegated on this connection.
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index a122794df3..b9a5b0eac8 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -169,9 +169,10 @@
     <literal>sslcert</literal> or <literal>sslkey</literal> settings.
    </para>
    <para>
-    Only superusers may connect to foreign servers without password
-    authentication, so always specify the <literal>password</literal> option
-    for user mappings belonging to non-superusers.
+    Non-superusers may connect to foreign servers using password
+    authentication or with GSSAPI delegated credentials, so specify the
+    <literal>password</literal> option for user mappings belonging to
+    non-superusers where password authentication is required.
    </para>
    <para>
     A superuser may override this check on a per-user-mapping basis by setting
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 701c340fc4..2129c916aa 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -979,7 +979,8 @@ CREATE VIEW pg_stat_gssapi AS
             S.pid,
             S.gss_auth AS gss_authenticated,
             S.gss_princ AS principal,
-            S.gss_enc AS encrypted
+            S.gss_enc AS encrypted,
+            S.gss_deleg AS credentials_delegated
     FROM pg_stat_get_activity(NULL) AS S
     WHERE S.client_port IS NOT NULL;
 
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index dca02271dc..6e1977fa62 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -574,6 +574,7 @@ static const struct ConnectionOption libpq_conninfo_options[] = {
 	{"requiressl", ForeignServerRelationId},
 	{"sslmode", ForeignServerRelationId},
 	{"gsslib", ForeignServerRelationId},
+	{"gssdeleg", ForeignServerRelationId},
 	{NULL, InvalidOid}
 };
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index bc0cf26b12..00ec9da284 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -165,6 +165,7 @@ static int	CheckCertAuth(Port *port);
  */
 char	   *pg_krb_server_keyfile;
 bool		pg_krb_caseins_users;
+bool		pg_gss_accept_deleg;
 
 
 /*----------------------------------------------------------------
@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
 	int			mtype;
 	StringInfoData buf;
 	gss_buffer_desc gbuf;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
@@ -947,6 +949,9 @@ pg_GSS_recvauth(Port *port)
 	 */
 	port->gss->ctx = GSS_C_NO_CONTEXT;
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Loop through GSSAPI message exchange. This exchange can consist of
 	 * multiple messages sent in both directions. First message is always from
@@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port)
 										  &port->gss->outbuf,
 										  &gflags,
 										  NULL,
-										  NULL);
+										  pg_gss_accept_deleg ? &delegated_creds : NULL);
 
 		/* gbuf no longer used */
 		pfree(buf.data);
@@ -1009,6 +1014,12 @@ pg_GSS_recvauth(Port *port)
 
 		CHECK_FOR_INTERRUPTS();
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		if (port->gss->outbuf.length != 0)
 		{
 			/*
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index fb39c760d8..64d41e5291 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -92,3 +92,56 @@ pg_GSS_error(const char *errmsg,
 			(errmsg_internal("%s", errmsg),
 			 errdetail_internal("%s: %s", msg_major, msg_minor)));
 }
+
+/*
+ * Store the credentials passed in into the memory cache for later usage.
+ *
+ * This allows credentials to be delegated to us for us to use to connect
+ * to other systems with, using, e.g. postgres_fdw or dblink.
+ */
+#define GSS_MEMORY_CACHE "MEMORY:"
+void
+pg_store_delegated_credential(gss_cred_id_t cred)
+{
+	OM_uint32	major,
+				minor;
+	gss_OID_set mech;
+	gss_cred_usage_t usage;
+	gss_key_value_element_desc cc;
+	gss_key_value_set_desc ccset;
+
+	cc.key = "ccache";
+	cc.value = GSS_MEMORY_CACHE;
+	ccset.count = 1;
+	ccset.elements = &cc;
+
+	/* Make the delegated credential only available to current process */
+	major = gss_store_cred_into(&minor,
+								cred,
+								GSS_C_INITIATE, /* credential only used for
+												 * starting libpq connection */
+								GSS_C_NULL_OID, /* store all */
+								true,	/* overwrite */
+								true,	/* make default */
+								&ccset,
+								&mech,
+								&usage);
+
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_store_cred", major, minor);
+	}
+
+	/* Credential stored, so we can release our credential handle. */
+	major = gss_release_cred(&minor, &cred);
+	if (major != GSS_S_COMPLETE)
+	{
+		pg_GSS_error("gss_release_cred", major, minor);
+	}
+
+	/*
+	 * Set KRB5CCNAME for this backend, so that later calls to
+	 * gss_acquire_cred will find the delegated credentials we stored.
+	 */
+	setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
+}
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 3b55f43199..73f8ce8554 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -497,6 +497,7 @@ secure_open_gssapi(Port *port)
 	bool		complete_next = false;
 	OM_uint32	major,
 				minor;
+	gss_cred_id_t delegated_creds;
 
 	/*
 	 * Allocate subsidiary Port data for GSSAPI operations.
@@ -504,6 +505,9 @@ secure_open_gssapi(Port *port)
 	port->gss = (pg_gssinfo *)
 		MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
 
+	delegated_creds = GSS_C_NO_CREDENTIAL;
+	port->gss->delegated_creds = false;
+
 	/*
 	 * Allocate buffers and initialize state variables.  By malloc'ing the
 	 * buffers at this point, we avoid wasting static data space in processes
@@ -588,7 +592,8 @@ secure_open_gssapi(Port *port)
 									   GSS_C_NO_CREDENTIAL, &input,
 									   GSS_C_NO_CHANNEL_BINDINGS,
 									   &port->gss->name, NULL, &output, NULL,
-									   NULL, NULL);
+									   NULL, pg_gss_accept_deleg ? &delegated_creds : NULL);
+
 		if (GSS_ERROR(major))
 		{
 			pg_GSS_error(_("could not accept GSSAPI security context"),
@@ -605,6 +610,12 @@ secure_open_gssapi(Port *port)
 			complete_next = true;
 		}
 
+		if (delegated_creds != GSS_C_NO_CREDENTIAL)
+		{
+			pg_store_delegated_credential(delegated_creds);
+			port->gss->delegated_creds = true;
+		}
+
 		/* Done handling the incoming packet, reset our buffer */
 		PqGSSRecvLength = 0;
 
@@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port)
 
 	return port->gss->princ;
 }
+
+/*
+ * Return if GSSAPI delegated credentials were included on this
+ * connection.
+ */
+bool
+be_gssapi_get_deleg(Port *port)
+{
+	if (!port || !port->gss)
+		return NULL;
+
+	return port->gss->delegated_creds;
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 608d01ea0d..391d5de043 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -384,6 +384,7 @@ pgstat_bestart(void)
 		lbeentry.st_gss = true;
 		lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
 		lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
+		lgssstatus.gss_deleg = be_gssapi_get_deleg(MyProcPort);
 		if (princ)
 			strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
 	}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ae180da4d0..e79b065d21 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS	30
+#define PG_STAT_GET_ACTIVITY_COLS	31
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			pfree(clipped_activity);
 
 			/* leader_pid */
-			nulls[28] = true;
+			nulls[29] = true;
 
 			proc = BackendPidGetProc(beentry->st_procpid);
 
@@ -432,8 +432,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				 */
 				if (leader && leader->pid != beentry->st_procpid)
 				{
-					values[28] = Int32GetDatum(leader->pid);
-					nulls[28] = false;
+					values[29] = Int32GetDatum(leader->pid);
+					nulls[29] = false;
 				}
 				else if (beentry->st_backendType == B_BG_WORKER)
 				{
@@ -441,8 +441,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 					if (leader_pid != InvalidPid)
 					{
-						values[28] = Int32GetDatum(leader_pid);
-						nulls[28] = false;
+						values[29] = Int32GetDatum(leader_pid);
+						nulls[29] = false;
 					}
 				}
 			}
@@ -600,6 +600,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				values[25] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
 				values[26] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
 				values[27] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
+				values[28] = BoolGetDatum(beentry->st_gssstatus->gss_deleg);	/* GSS credentials
+																				 * delegated */
 			}
 			else
 			{
@@ -607,11 +609,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				nulls[26] = true;	/* No GSS principal */
 				values[27] = BoolGetDatum(false);	/* GSS Encryption not in
 													 * use */
+				values[28] = BoolGetDatum(false);	/* GSS credentials not
+													 * delegated */
 			}
 			if (beentry->st_query_id == 0)
-				nulls[29] = true;
+				nulls[30] = true;
 			else
-				values[29] = UInt64GetDatum(beentry->st_query_id);
+				values[30] = UInt64GetDatum(beentry->st_query_id);
 		}
 		else
 		{
@@ -640,6 +644,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[27] = true;
 			nulls[28] = true;
 			nulls[29] = true;
+			nulls[30] = true;
 		}
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 60feae0f1b..5af87a7868 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -282,15 +282,17 @@ PerformAuthentication(Port *port)
 
 			if (princ)
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s, principal=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
 								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"),
 								 princ);
 			else
 				appendStringInfo(&logmsg,
-								 _(" GSS (authenticated=%s, encrypted=%s)"),
+								 _(" GSS (authenticated=%s, encrypted=%s, deleg_credentials=%s)"),
 								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
-								 be_gssapi_get_enc(port) ? _("yes") : _("no"));
+								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 be_gssapi_get_deleg(port) ? _("yes") : _("no"));
 		}
 #endif
 
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1067537e74..cab3ddbe11 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -1727,6 +1727,16 @@ struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"gss_accept_deleg", PGC_SIGHUP, CONN_AUTH_AUTH,
+			gettext_noop("Sets whether GSSAPI delegation should be accepted from the client."),
+			NULL
+		},
+		&pg_gss_accept_deleg,
+		false,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
 			gettext_noop("Warn about backslash escapes in ordinary string literals."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e715aff3b8..dce5049bc2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -101,6 +101,7 @@
 # GSSAPI using Kerberos
 #krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
 #krb_caseins_users = off
+#gss_accept_deleg = off
 
 # - SSL -
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dcbc4c2eb5..b516cee8bd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5438,9 +5438,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}',
+  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,bool,int4,int8}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_deleg,leader_pid,query_id}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 9916c99df1..e4d0e38c1e 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -18,6 +18,7 @@
 
 extern PGDLLIMPORT char *pg_krb_server_keyfile;
 extern PGDLLIMPORT bool pg_krb_caseins_users;
+extern PGDLLIMPORT bool pg_gss_accept_deleg;
 extern PGDLLIMPORT char *pg_krb_realm;
 
 extern void ClientAuthentication(Port *port);
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
index facd24ff7f..0381f0ce77 100644
--- a/src/include/libpq/be-gssapi-common.h
+++ b/src/include/libpq/be-gssapi-common.h
@@ -18,13 +18,16 @@
 
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
+#include <gssapi_ext.h>
 #else
 #include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
 #endif
 
 extern void pg_GSS_error(const char *errmsg,
 						 OM_uint32 maj_stat, OM_uint32 min_stat);
 
+extern void pg_store_delegated_credential(gss_cred_id_t cred);
 #endif							/* ENABLE_GSS */
 
 #endif							/* BE_GSSAPI_COMMON_H */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ac6407e9f6..e9df4295e2 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -84,6 +84,7 @@ typedef struct
 								 * GSSAPI auth was not used */
 	bool		auth;			/* GSSAPI Authentication used */
 	bool		enc;			/* GSSAPI encryption in use */
+	bool		delegated_creds;	/* GSSAPI Delegated credentials */
 #endif
 } pg_gssinfo;
 #endif
@@ -328,6 +329,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tls_init_hook;
 extern bool be_gssapi_get_auth(Port *port);
 extern bool be_gssapi_get_enc(Port *port);
 extern const char *be_gssapi_get_princ(Port *port);
+extern bool be_gssapi_get_deleg(Port *port);
 
 /* Read and write to a GSSAPI-encrypted connection. */
 extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index f7bd83113a..9651cb1d0c 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -77,6 +77,7 @@ typedef struct PgBackendGSSStatus
 	char		gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
 	bool		gss_auth;		/* If GSSAPI authentication was used */
 	bool		gss_enc;		/* If encryption is being used */
+	bool		gss_deleg;		/* If credentials delegated */
 
 } PgBackendGSSStatus;
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index e8bcc88370..7ded77aff3 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -186,3 +186,4 @@ PQpipelineStatus          183
 PQsetTraceFlags           184
 PQmblenBounded            185
 PQsendFlushRequest        186
+PQconnectionUsedGSSAPI    187
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index b0550e6332..fe2634230a 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -58,7 +58,8 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 {
 	OM_uint32	maj_stat,
 				min_stat,
-				lmin_s;
+				lmin_s,
+				gss_flags = GSS_C_MUTUAL_FLAG;
 	gss_buffer_desc ginbuf;
 	gss_buffer_desc goutbuf;
 
@@ -92,12 +93,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 		ginbuf.value = NULL;
 	}
 
+	/* Only try to acquire credentials if GSS delegation isn't disabled. */
+	if (!pg_GSS_have_cred_cache(&conn->gcred))
+		conn->gcred = GSS_C_NO_CREDENTIAL;
+
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+		gss_flags |= GSS_C_DELEG_FLAG;
+
 	maj_stat = gss_init_sec_context(&min_stat,
-									GSS_C_NO_CREDENTIAL,
+									conn->gcred,
 									&conn->gctx,
 									conn->gtarg_nam,
 									GSS_C_NO_OID,
-									GSS_C_MUTUAL_FLAG,
+									gss_flags,
 									0,
 									GSS_C_NO_CHANNEL_BINDINGS,
 									(ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
@@ -139,6 +147,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 	{
 		conn->client_finished_auth = true;
 		gss_release_name(&lmin_s, &conn->gtarg_nam);
+		conn->gssapi_used = true;
 	}
 
 	return STATUS_OK;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 40fef0e2c8..fcd3d0d9a3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -343,6 +343,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"GSS-library", "", 7,	/* sizeof("gssapi") == 7 */
 	offsetof(struct pg_conn, gsslib)},
 
+	{"gssdeleg", "PGGSSDELEG", NULL, NULL,
+		"GSS-delegation", "", 8,	/* sizeof("disable") == 8 */
+	offsetof(struct pg_conn, gssdeleg)},
+
 	{"replication", NULL, NULL, NULL,
 		"Replication", "D", 5,
 	offsetof(struct pg_conn, replication)},
@@ -617,6 +621,7 @@ pqDropServerData(PGconn *conn)
 	conn->auth_req_received = false;
 	conn->client_finished_auth = false;
 	conn->password_needed = false;
+	conn->gssapi_used = false;
 	conn->write_failed = false;
 	free(conn->write_err_msg);
 	conn->write_err_msg = NULL;
@@ -4448,6 +4453,7 @@ freePGconn(PGconn *conn)
 	free(conn->gssencmode);
 	free(conn->krbsrvname);
 	free(conn->gsslib);
+	free(conn->gssdeleg);
 	free(conn->connip);
 	/* Note that conn->Pfdebug is not ours to close or free */
 	free(conn->write_err_msg);
@@ -7312,6 +7318,17 @@ PQconnectionUsedPassword(const PGconn *conn)
 		return false;
 }
 
+int
+PQconnectionUsedGSSAPI(const PGconn *conn)
+{
+	if (!conn)
+		return false;
+	if (conn->gssapi_used)
+		return true;
+	else
+		return false;
+}
+
 int
 PQclientEncoding(const PGconn *conn)
 {
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 038e847b7e..bf87ae3fd1 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -477,7 +477,8 @@ pqsecure_open_gss(PGconn *conn)
 {
 	ssize_t		ret;
 	OM_uint32	major,
-				minor;
+				minor,
+				gss_flags = GSS_REQUIRED_FLAGS;
 	uint32		netlen;
 	PostgresPollingStatusType result;
 	gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
@@ -621,13 +622,30 @@ pqsecure_open_gss(PGconn *conn)
 	if (ret != STATUS_OK)
 		return PGRES_POLLING_FAILED;
 
+	if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
+	{
+		/* Acquire credentials if possbile */
+		if (conn->gcred == GSS_C_NO_CREDENTIAL)
+			(void) pg_GSS_have_cred_cache(&conn->gcred);
+
+		/*
+		 * We have credentials and gssdeleg is enabled, so request credential
+		 * delegation.  This may or may not actually result in credentials
+		 * being delegated- it depends on if the forwardable flag has been set
+		 * in the credential and if the server is configured to accept
+		 * delegated credentials.
+		 */
+		if (conn->gcred != GSS_C_NO_CREDENTIAL)
+			gss_flags |= GSS_C_DELEG_FLAG;
+	}
+
 	/*
 	 * Call GSS init context, either with an empty input, or with a complete
 	 * packet from the server.
 	 */
 	major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
 								 conn->gtarg_nam, GSS_C_NO_OID,
-								 GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+								 gss_flags, 0, 0, &input, NULL,
 								 &output, NULL, NULL);
 
 	/* GSS Init Sec Context uses the whole packet, so clear it */
@@ -647,6 +665,7 @@ pqsecure_open_gss(PGconn *conn)
 		 * to do GSS wrapping/unwrapping.
 		 */
 		conn->gssenc = true;
+		conn->gssapi_used = true;
 
 		/* Clean up */
 		gss_release_cred(&minor, &conn->gcred);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index f3d9220496..7476dbe0e9 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -354,6 +354,7 @@ extern int	PQbackendPID(const PGconn *conn);
 extern PGpipelineStatus PQpipelineStatus(const PGconn *conn);
 extern int	PQconnectionNeedsPassword(const PGconn *conn);
 extern int	PQconnectionUsedPassword(const PGconn *conn);
+extern int	PQconnectionUsedGSSAPI(const PGconn *conn);
 extern int	PQclientEncoding(const PGconn *conn);
 extern int	PQsetClientEncoding(PGconn *conn, const char *encoding);
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d93e976ca5..ce0167c1b6 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -404,6 +404,7 @@ struct pg_conn
 	char	   *krbsrvname;		/* Kerberos service name */
 	char	   *gsslib;			/* What GSS library to use ("gssapi" or
 								 * "sspi") */
+	char	   *gssdeleg;		/* Try to delegate GSS credentials? */
 	char	   *ssl_min_protocol_version;	/* minimum TLS protocol version */
 	char	   *ssl_max_protocol_version;	/* maximum TLS protocol version */
 	char	   *target_session_attrs;	/* desired session properties */
@@ -465,6 +466,7 @@ struct pg_conn
 	int			sversion;		/* server version, e.g. 70401 for 7.4.1 */
 	bool		auth_req_received;	/* true if any type of auth req received */
 	bool		password_needed;	/* true if server demanded a password */
+	bool		gssapi_used;	/* true if authenticated via gssapi */
 	bool		sigpipe_so;		/* have we masked SIGPIPE via SO_NOSIGPIPE? */
 	bool		sigpipe_flag;	/* can we mask SIGPIPE via MSG_NOSIGNAL? */
 	bool		write_failed;	/* have we had a write failure on sock? */
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
index 7765f3f93b..f460d2c0e7 100644
--- a/src/test/kerberos/Makefile
+++ b/src/test/kerberos/Makefile
@@ -13,6 +13,9 @@ subdir = src/test/kerberos
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
+EXTRA_INSTALL += contrib/postgres_fdw
+EXTRA_INSTALL += contrib/dblink
+
 export with_gssapi with_krb_srvnam
 
 check:
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index 458246b4d7..bf12752529 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -7,6 +7,9 @@
 # that the server-side pg_stat_gssapi view reports what we expect to
 # see for each test and that SYSTEM_USER returns what we expect to see.
 #
+# Also test that GSSAPI delegation is working properly and that those
+# credentials can be used to make dblink / postgres_fdw connections.
+#
 # Since this requires setting up a full KDC, it doesn't make much sense
 # to have multiple test scripts (since they'd have to also create their
 # own KDC and that could cause race conditions or other problems)- so
@@ -56,6 +59,7 @@ elsif ($^O eq 'linux')
 
 my $krb5_config  = 'krb5-config';
 my $kinit        = 'kinit';
+my $klist        = 'klist';
 my $kdb5_util    = 'kdb5_util';
 my $kadmin_local = 'kadmin.local';
 my $krb5kdc      = 'krb5kdc';
@@ -64,6 +68,7 @@ if ($krb5_bin_dir && -d $krb5_bin_dir)
 {
 	$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
 	$kinit       = $krb5_bin_dir . '/' . $kinit;
+	$klist       = $krb5_bin_dir . '/' . $klist;
 }
 if ($krb5_sbin_dir && -d $krb5_sbin_dir)
 {
@@ -86,6 +91,8 @@ my $kdc_datadir = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc";
 my $kdc_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc.pid";
 my $keytab      = "${PostgreSQL::Test::Utils::tmp_check}/krb5.keytab";
 
+my $pgpass      = "${PostgreSQL::Test::Utils::tmp_check}/.pgpass";
+
 my $dbname      = 'postgres';
 my $username    = 'test1';
 my $application = '001_auth.pl';
@@ -100,6 +107,14 @@ $stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
   or BAIL_OUT("could not get Kerberos version");
 $krb5_version = $1;
 
+# Construct a pgpass file to make sure we don't use it
+append_to_file(
+	$pgpass,
+	'*:*:*:*:abc123'
+);
+
+chmod 0600, $pgpass;
+
 # Build the krb5.conf to use.
 #
 # Explicitly specify the default (test) realm and the KDC for
@@ -126,12 +141,14 @@ kdc = FILE:$kdc_log
 dns_lookup_realm = false
 dns_lookup_kdc = false
 default_realm = $realm
+forwardable = false
 rdns = false
 
 [realms]
 $realm = {
     kdc = $hostaddr:$kdc_port
-}!);
+}
+!);
 
 append_to_file(
 	$kdc_conf,
@@ -204,7 +221,28 @@ lc_messages = 'C'
 });
 $node->start;
 
+my $port = $node->port();
+
 $node->safe_psql('postgres', 'CREATE USER test1;');
+$node->safe_psql('postgres', "CREATE USER test2 WITH ENCRYPTED PASSWORD 'abc123';");
+$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;');
+$node->safe_psql('postgres', 'CREATE EXTENSION dblink;');
+$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'postgres');");
+$node->safe_psql('postgres', "CREATE SERVER s2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (port '$port', dbname 'postgres', passfile '$pgpass');");
+
+$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;');
+
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTIONS (user 'test1');");
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s2 OPTIONS (user 'test2');");
+
+$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);");
+$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);");
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;");
+$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;");
+
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf2 (c1 int) SERVER s2 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON tf2 TO test1;");
 
 # Set up a table for SYSTEM_USER parallel worker testing.
 $node->safe_psql('postgres',
@@ -271,12 +309,16 @@ sub test_query
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss map=mymap});
+	qq{
+local all test2 scram-sha-256
+host all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
 
 run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
 
 test_access(
 	$node,
@@ -294,35 +336,58 @@ $node->restart;
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'',
-	'succeeds with mapping with default gssencmode and host hba',
+	'succeeds with mapping with default gssencmode and host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred with host hba',
+	'succeeds with GSS-encrypted access preferred with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
+
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=require',
-	'succeeds with GSS-encrypted access required with host hba',
+	'succeeds with GSS-encrypted access required with host hba, ticket not forwardable',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+
 # Test that we can transport a reasonable amount of data.
 test_query(
 	$node,
@@ -389,29 +454,164 @@ test_query(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostgssenc all all $hostaddr/32 gss map=mymap
+});
+
+string_replace_file($krb5_conf, "forwardable = false", "forwardable = true");
+
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=off});
+$node->restart;
+
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+	qq{gss_accept_deleg=on});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=prefer gssdeleg=enable',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated from pg_stat_gssapi where pid = pg_backend_pid();',
+	0,
+	'gssencmode=require gssdeleg=enable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials forwarded',
+	"connection authenticated: identity=\"test1\@$realm\" method=gss",
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+	$node,
+	'test1',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
 	'gssencmode=prefer',
-	'succeeds with GSS-encrypted access preferred and hostgssenc hba',
+	'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
 );
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=require',
-	'succeeds with GSS-encrypted access required and hostgssenc hba',
+	'gssencmode=require gssdeleg=disable',
+	'succeeds with GSS-encrypted access required and hostgssenc hba and credentials explicitly not forwarded',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+my $psql_out = '';
+my $psql_stderr = '';
+my $psql_rc = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink attempt fails without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','dblink does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf1;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials and with passfile');
+
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
 	'fails with GSS encryption disabled and hostgssenc hba');
 
@@ -427,54 +627,123 @@ $node->connect_ok(
 
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
+	qq{
+    local all test2 scram-sha-256
+	hostnogssenc all all $hostaddr/32 gss map=mymap
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=prefer',
+	'gssencmode=prefer gssdeleg=enable',
 	'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
 );
 test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=require',
 	'fails with GSS-encrypted access required and hostnogssenc hba');
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND NOT encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'gssencmode=disable',
+	'gssencmode=disable gssdeleg=enable',
 	'succeeds with GSS encryption disabled and hostnogssenc hba',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, deleg_credentials=yes, principal=test1\@$realm)"
+);
+
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'dblink works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=prefer gssdeleg=enable',
+	'postgres_fdw works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work with delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+    'postgres',
+	"TABLE tf2;",
+	connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+	stdout => \$psql_out,
+	stderr => \$psql_stderr
 );
+is($psql_rc,'3','postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work with delegated credentials and with passfile');
 
 truncate($node->data_dir . '/pg_ident.conf', 0);
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0
+});
 $node->restart;
 
 test_access(
 	$node,
 	'test1',
-	'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+	'SELECT gss_authenticated AND encrypted AND credentials_delegated FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
 	0,
-	'',
+	'gssdeleg=enable',
 	'succeeds with include_realm=0 and defaults',
 	"connection authenticated: identity=\"test1\@$realm\" method=gss",
-	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+	"connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=yes, principal=test1\@$realm)"
 );
 
+test_query(
+	$node,
+	'test1',
+	"SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'dblink works encrypted');
+
+test_query(
+	$node,
+	'test1',
+	"TABLE tf1;",
+	qr/^1$/s,
+	'gssencmode=require gssdeleg=enable',
+	'postgres_fdw works encrypted');
+
 # Reset pg_hba.conf, and cause a usermap failure with an authentication
 # that has passed.
 unlink($node->data_dir . '/pg_hba.conf');
 $node->append_conf('pg_hba.conf',
-	qq{host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG});
+	qq{
+    local all test2 scram-sha-256
+	host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG
+});
 $node->restart;
 
 test_access(
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 878e12b15e..9249954b49 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -65,6 +65,7 @@ our @EXPORT = qw(
   slurp_dir
   slurp_file
   append_to_file
+  string_replace_file
   check_mode_recursive
   chmod_recursive
   check_pg_config
@@ -549,6 +550,32 @@ sub append_to_file
 
 =pod
 
+=item string_replace_file(filename, find, replace)
+
+Find and replace string of a given file.
+
+=cut
+
+sub string_replace_file
+{
+	my ($filename, $find, $replace) = @_;
+	open(my $in, '<', $filename);
+	my $content;
+	while(<$in>)
+	{
+		$_ =~ s/$find/$replace/;
+		$content = $content.$_;
+	}
+	close $in;
+	open(my $out, '>', $filename);
+	print $out $content;
+	close($out);
+
+	return;
+}
+
+=pod
+
 =item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
 
 Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2d75dd6656..919d947ec0 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1760,7 +1760,7 @@ pg_stat_activity| SELECT s.datid,
     s.query_id,
     s.query,
     s.backend_type
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1876,8 +1876,9 @@ pg_stat_database_conflicts| SELECT oid AS datid,
 pg_stat_gssapi| SELECT pid,
     gss_auth AS gss_authenticated,
     gss_princ AS principal,
-    gss_enc AS encrypted
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+    gss_enc AS encrypted,
+    gss_deleg AS credentials_delegated
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_io| SELECT backend_type,
     io_object,
@@ -2075,7 +2076,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     w.reply_time
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_replication_slots| SELECT s.slot_name,
@@ -2109,7 +2110,7 @@ pg_stat_ssl| SELECT pid,
     ssl_client_dn AS client_dn,
     ssl_client_serial AS client_serial,
     ssl_issuer_dn AS issuer_dn
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_deleg, leader_pid, query_id)
   WHERE (client_port IS NOT NULL);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,
-- 
2.34.1

0002-Explicitly-require-MIT-Kerberos-for-GSSAPI.patchtext/x-diff; charset=us-asciiDownload
From 6714687cde66334df8e194f22b299b8ab24d6801 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 12 Apr 2023 09:35:53 -0400
Subject: [PATCH 2/2] Explicitly require MIT Kerberos for GSSAPI

WHen building with GSSAPI support, explicitly require MIT Kerberos and
check for gssapi_ext.h in configure.ac and meson.build.  Also add
documentation explicitly stating that we now require MIT Kerberos when
building with GSSAPI support.

Reveiwed by: Johnathan Katz
Discussion: https://postgr.es/m/abcc73d0-acf7-6896-e0dc-f5bc12a61bb1@postgresql.org
---
 configure                            | 27 +++++++++++++++++++++++++++
 configure.ac                         |  2 ++
 doc/src/sgml/client-auth.sgml        |  2 +-
 doc/src/sgml/installation.sgml       | 21 +++++++++++----------
 meson.build                          | 10 ++++++++++
 src/backend/libpq/auth.c             |  5 +++--
 src/backend/libpq/be-secure-gssapi.c |  5 +++--
 7 files changed, 57 insertions(+), 15 deletions(-)

diff --git a/configure b/configure
index dbea7eaf5f..08bcf8f43a 100755
--- a/configure
+++ b/configure
@@ -14104,6 +14104,33 @@ done
 
 fi
 
+done
+
+  for ac_header in gssapi/gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_ext.h" "ac_cv_header_gssapi_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  for ac_header in gssapi_ext.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "gssapi_ext.h" "ac_cv_header_gssapi_ext_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_ext_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSSAPI_EXT_H 1
+_ACEOF
+
+else
+  as_fn_error $? "gssapi_ext.h header file is required for GSSAPI" "$LINENO" 5
+fi
+
+done
+
+fi
+
 done
 
 fi
diff --git a/configure.ac b/configure.ac
index dda34304db..c53a9c788e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1562,6 +1562,8 @@ fi
 if test "$with_gssapi" = yes ; then
   AC_CHECK_HEADERS(gssapi/gssapi.h, [],
 	[AC_CHECK_HEADERS(gssapi.h, [], [AC_MSG_ERROR([gssapi.h header file is required for GSSAPI])])])
+  AC_CHECK_HEADERS(gssapi/gssapi_ext.h, [],
+	[AC_CHECK_HEADERS(gssapi_ext.h, [], [AC_MSG_ERROR([gssapi_ext.h header file is required for GSSAPI])])])
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index dbba289600..204d09df67 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1426,7 +1426,7 @@ omicron         bryanh                  guest1
     The keytab file is generated using the Kerberos software; see the
     Kerberos documentation for details. The following example shows
     doing this using the <application>kadmin</application> tool of
-    MIT-compatible Kerberos 5 implementations:
+    MIT Kerberos:
 <screen>
 <prompt>kadmin% </prompt><userinput>addprinc -randkey postgres/server.my.domain.org</userinput>
 <prompt>kadmin% </prompt><userinput>ktadd -k krb5.keytab postgres/server.my.domain.org</userinput>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index f451204854..3d839d665a 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -252,9 +252,9 @@ documentation.  See standalone-profile.xsl for details.
 
     <listitem>
      <para>
-      You need <application>Kerberos</application>, <productname>OpenLDAP</productname>,
-      and/or <application>PAM</application>, if you want to support authentication
-      using those services.
+      You need <application>MIT Kerberos</application> (for GSSAPI),
+      <productname>OpenLDAP</productname>, and/or <application>PAM</application>,
+      if you want to support authentication using those services.
      </para>
     </listitem>
 
@@ -1048,9 +1048,9 @@ build-postgresql:
        <term><option>--with-gssapi</option></term>
        <listitem>
         <para>
-         Build with support for GSSAPI authentication. On many systems, the
-         GSSAPI system (usually a part of the Kerberos installation) is not
-         installed in a location
+         Build with support for GSSAPI authentication. MIT Kerberos is required
+         to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+         of the MIT Kerberos installation) is not installed in a location
          that is searched by default (e.g., <filename>/usr/include</filename>,
          <filename>/usr/lib</filename>), so you must use the options
          <option>--with-includes</option> and <option>--with-libraries</option> in
@@ -2497,10 +2497,11 @@ ninja install
       <term><option>-Dgssapi={ auto | enabled | disabled }</option></term>
       <listitem>
        <para>
-        Build with support for GSSAPI authentication. On many systems, the
-        GSSAPI system (usually a part of the Kerberos installation) is not
-        installed in a location that is searched by default (e.g.,
-        <filename>/usr/include</filename>, <filename>/usr/lib</filename>).  In
+        Build with support for GSSAPI authentication. MIT Kerberos is required
+        to be installed for GSSAPI.  On many systems, the GSSAPI system (a part
+        of the MIT Kerberos installation) is not installed in a location
+        that is searched by default (e.g., <filename>/usr/include</filename>,
+        <filename>/usr/lib</filename>).  In
         those cases, PostgreSQL will query <command>pkg-config</command> to
         detect the required compiler and linker options.  Defaults to auto.
         <filename>meson configure</filename> will check for the required
diff --git a/meson.build b/meson.build
index b69aaddb1f..3405cc07ee 100644
--- a/meson.build
+++ b/meson.build
@@ -623,6 +623,16 @@ if not gssapiopt.disabled()
     have_gssapi = false
   endif
 
+  if not have_gssapi
+  elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
+      args: test_c_args, include_directories: postgres_inc)
+    cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
+  elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
+    cdata.set('HAVE_GSSAPI_EXT_H', 1)
+  else
+    have_gssapi = false
+  endif
+
   if not have_gssapi
   elif cc.has_function('gss_init_sec_context', dependencies: gssapi,
       args: test_c_args, include_directories: postgres_inc)
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 00ec9da284..a1a826e37f 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -922,8 +922,9 @@ pg_GSS_recvauth(Port *port)
 	gss_cred_id_t delegated_creds;
 
 	/*
-	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
-	 * doesn't support the cred store extensions, so use the env var.
+	 * Use the configured keytab, if there is one.  As we now require MIT
+	 * Kerberos, we might consider using the credential store extensions in
+	 * the future instead of the environment variable.
 	 */
 	if (pg_krb_server_keyfile != NULL && pg_krb_server_keyfile[0] != '\0')
 	{
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 73f8ce8554..6212f225fd 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -526,8 +526,9 @@ secure_open_gssapi(Port *port)
 	PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
 
 	/*
-	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
-	 * doesn't support the cred store extensions, so use the env var.
+	 * Use the configured keytab, if there is one.  As we now require MIT
+	 * Kerberos, we might consider using the credential store extensions in the
+	 * future instead of the environment variable.
 	 */
 	if (pg_krb_server_keyfile != NULL && pg_krb_server_keyfile[0] != '\0')
 	{
-- 
2.34.1

#37Daniel Gustafsson
daniel@yesql.se
In reply to: Stephen Frost (#34)
Re: longfin missing gssapi_ext.h

On 12 Apr 2023, at 16:55, Stephen Frost <sfrost@snowman.net> wrote:

Greetings,

* Daniel Gustafsson (daniel@yesql.se) wrote:

On 12 Apr 2023, at 16:33, Stephen Frost <sfrost@snowman.net> wrote:
Sure, reworked that way and attached.

While not changed in this hunk, does the comment regarding Heimdal still apply?

@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
int mtype;
StringInfoData buf;
gss_buffer_desc gbuf;
+ gss_cred_id_t delegated_creds;

/*
* Use the configured keytab, if there is one. Unfortunately, Heimdal

Good catch. No, it doesn't. I'm not anxious to actually change that
code at this point but we could certainly consider changing it in the
future. I'll update this comment (and the identical one in
secure_open_gssapi) accordingly.

Sounds like a good plan.

--
Daniel Gustafsson

#38Jonathan S. Katz
jkatz@postgresql.org
In reply to: Stephen Frost (#36)
Re: longfin missing gssapi_ext.h

On 4/12/23 12:22 PM, Stephen Frost wrote:

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Updated patch set attached.

LGTM

Great, thanks.

I cleaned up the commit messages a bit more and added links to the
discussion. If there isn't anything more then I'll plan to push these
later today or tomorrow.

Great -- thanks for your attention to this. I'm glad we have an
opportunity to de-revert (devert?).

Jonathan

#39Stephen Frost
sfrost@snowman.net
In reply to: Jonathan S. Katz (#38)
Re: longfin missing gssapi_ext.h

Greetings,

* Jonathan S. Katz (jkatz@postgresql.org) wrote:

On 4/12/23 12:22 PM, Stephen Frost wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Updated patch set attached.

LGTM

Great, thanks.

I cleaned up the commit messages a bit more and added links to the
discussion. If there isn't anything more then I'll plan to push these
later today or tomorrow.

Great -- thanks for your attention to this. I'm glad we have an opportunity
to de-revert (devert?).

Pushed, thanks again to everyone.

I'll monitor the buildfarm and assuming there isn't anything unexpected
then I'll mark the open item as resolved now.

Thanks!

Stephen

#40Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#39)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Pushed, thanks again to everyone.
I'll monitor the buildfarm and assuming there isn't anything unexpected
then I'll mark the open item as resolved now.

The Debian 7 (Wheezy) members of the buildfarm (lapwing, skate, snapper)
are all getting past the gssapi_ext.h check you added and then failing
like this:

ccache gcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2 -Werror -I../../../src/include -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS -D_GNU_SOURCE -I/usr/include/libxml2 -I/usr/include/et -c -o be-gssapi-common.o be-gssapi-common.c
be-gssapi-common.c: In function 'pg_store_delegated_credential':
be-gssapi-common.c:110:2: error: unknown type name 'gss_key_value_element_desc'
be-gssapi-common.c:111:2: error: unknown type name 'gss_key_value_set_desc'
be-gssapi-common.c:113:4: error: request for member 'key' in something not a structure or union
be-gssapi-common.c:114:4: error: request for member 'value' in something not a structure or union
be-gssapi-common.c:115:7: error: request for member 'count' in something not a structure or union
be-gssapi-common.c:116:7: error: request for member 'elements' in something not a structure or union
be-gssapi-common.c:119:2: error: implicit declaration of function 'gss_store_cred_into' [-Werror=implicit-function-declaration]

Debian 7 has been EOL five years or so, so I don't mind saying "get a
newer OS or disable gssapi". However, is it worth adding another
configure check to fail a little faster with whatever Kerberos
version this is? Checking that gss_store_cred_into() exists
seems like the most obvious one of these things to test for.

regards, tom lane

#41Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#40)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Pushed, thanks again to everyone.
I'll monitor the buildfarm and assuming there isn't anything unexpected
then I'll mark the open item as resolved now.

The Debian 7 (Wheezy) members of the buildfarm (lapwing, skate, snapper)
are all getting past the gssapi_ext.h check you added and then failing
like this:

ccache gcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2 -Werror -I../../../src/include -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS -D_GNU_SOURCE -I/usr/include/libxml2 -I/usr/include/et -c -o be-gssapi-common.o be-gssapi-common.c
be-gssapi-common.c: In function 'pg_store_delegated_credential':
be-gssapi-common.c:110:2: error: unknown type name 'gss_key_value_element_desc'
be-gssapi-common.c:111:2: error: unknown type name 'gss_key_value_set_desc'
be-gssapi-common.c:113:4: error: request for member 'key' in something not a structure or union
be-gssapi-common.c:114:4: error: request for member 'value' in something not a structure or union
be-gssapi-common.c:115:7: error: request for member 'count' in something not a structure or union
be-gssapi-common.c:116:7: error: request for member 'elements' in something not a structure or union
be-gssapi-common.c:119:2: error: implicit declaration of function 'gss_store_cred_into' [-Werror=implicit-function-declaration]

Debian 7 has been EOL five years or so, so I don't mind saying "get a
newer OS or disable gssapi". However, is it worth adding another
configure check to fail a little faster with whatever Kerberos
version this is? Checking that gss_store_cred_into() exists
seems like the most obvious one of these things to test for.

Sure, I can certainly do that and agreed that it makes sense to check
for gss_store_cred_into().

How about the attached which just switches from testing for
gss_init_sec_context to testing for gss_store_cred_into?

Thanks!

Stephen

Attachments:

krb5_update_func_check.patchtext/x-diff; charset=us-asciiDownload
diff --git a/configure.ac b/configure.ac
index c53a9c788e..1362f57a27 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1340,8 +1340,8 @@ fi
 
 if test "$with_gssapi" = yes ; then
   if test "$PORTNAME" != "win32"; then
-    AC_SEARCH_LIBS(gss_init_sec_context, [gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'], [],
-                   [AC_MSG_ERROR([could not find function 'gss_init_sec_context' required for GSSAPI])])
+    AC_SEARCH_LIBS(gss_store_cred_into, [gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'], [],
+                   [AC_MSG_ERROR([could not find function 'gss_store_cred_into' required for GSSAPI])])
   else
     LIBS="$LIBS -lgssapi32"
   fi
diff --git a/meson.build b/meson.build
index 3405cc07ee..f1db5455b0 100644
--- a/meson.build
+++ b/meson.build
@@ -634,14 +634,14 @@ if not gssapiopt.disabled()
   endif
 
   if not have_gssapi
-  elif cc.has_function('gss_init_sec_context', dependencies: gssapi,
+  elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
       args: test_c_args, include_directories: postgres_inc)
     cdata.set('ENABLE_GSS', 1)
 
     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
   elif gssapiopt.enabled()
-    error('''could not find function 'gss_init_sec_context' required for GSSAPI''')
+    error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
   else
     have_gssapi = false
   endif
#42Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#41)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

How about the attached which just switches from testing for
gss_init_sec_context to testing for gss_store_cred_into?

WFM.

regards, tom lane

#43Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#42)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

How about the attached which just switches from testing for
gss_init_sec_context to testing for gss_store_cred_into?

WFM.

Done that way.

Thanks!

Stephen

#44Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#43)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

Done that way.

Looks like you neglected to update the configure script proper?

regards, tom lane

#45Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#44)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Done that way.

Looks like you neglected to update the configure script proper?

Pah, indeed. Will fix. Sorry about that.

Thanks,

Stephen

#46Stephen Frost
sfrost@snowman.net
In reply to: Stephen Frost (#45)
1 attachment(s)
Re: longfin missing gssapi_ext.h

Greetings,

* Stephen Frost (sfrost@snowman.net) wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

Done that way.

Looks like you neglected to update the configure script proper?

Pah, indeed. Will fix. Sorry about that.

Fixed.

I'm guessing it's not really an issue but it does make changing
configure a bit annoying on my Ubuntu 22.04, when I run autoconf2.69, I
end up with this additional hunk as changed from what our configure
currently has.

Thoughts?

Thanks,

Stephen

Attachments:

autoconf2.69-ubuntu22.04-diff.patchtext/x-diff; charset=us-asciiDownload
diff --git a/configure b/configure
index 82efa0d3f1..5489cddce2 100755
--- a/configure
+++ b/configure
@@ -15348,7 +15348,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15394,7 +15394,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15418,7 +15418,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15463,7 +15463,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15487,7 +15487,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
#47Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#46)
Re: longfin missing gssapi_ext.h

Stephen Frost <sfrost@snowman.net> writes:

I'm guessing it's not really an issue but it does make changing
configure a bit annoying on my Ubuntu 22.04, when I run autoconf2.69, I
end up with this additional hunk as changed from what our configure
currently has.

Not surprising. Thanks to autoconf's long release cycles, individual
distros often are carrying local patches that affect its output.
To ensure consistent results across committers, our policy is that you
should use built-from-upstream-source autoconf not a vendor's version.
(In principle that could bite us sometime, but it hasn't yet.)

Also, you should generally run autoheader after autoconf.
Checking things here, I notice that pg_config.h.in hasn't been
updated for the last few gssapi-related commits:

$ git diff
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3665e79..6d572c3 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 196,201 ****
--- 196,207 ----
  /* Define to 1 if you have the `getpeerucred' function. */
  #undef HAVE_GETPEERUCRED
+ /* Define to 1 if you have the <gssapi_ext.h> header file. */
+ #undef HAVE_GSSAPI_EXT_H
+ 
+ /* Define to 1 if you have the <gssapi/gssapi_ext.h> header file. */
+ #undef HAVE_GSSAPI_GSSAPI_EXT_H
+ 
  /* Define to 1 if you have the <gssapi/gssapi.h> header file. */
  #undef HAVE_GSSAPI_GSSAPI_H

Shall I push that, or do you want to?

regards, tom lane

#48Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#47)
Re: longfin missing gssapi_ext.h

Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

I'm guessing it's not really an issue but it does make changing
configure a bit annoying on my Ubuntu 22.04, when I run autoconf2.69, I
end up with this additional hunk as changed from what our configure
currently has.

Not surprising. Thanks to autoconf's long release cycles, individual
distros often are carrying local patches that affect its output.
To ensure consistent results across committers, our policy is that you
should use built-from-upstream-source autoconf not a vendor's version.
(In principle that could bite us sometime, but it hasn't yet.)

... making me more excited about the idea of getting over to meson.

Also, you should generally run autoheader after autoconf.
Checking things here, I notice that pg_config.h.in hasn't been
updated for the last few gssapi-related commits:

$ git diff
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3665e79..6d572c3 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 196,201 ****
--- 196,207 ----
/* Define to 1 if you have the `getpeerucred' function. */
#undef HAVE_GETPEERUCRED
+ /* Define to 1 if you have the <gssapi_ext.h> header file. */
+ #undef HAVE_GSSAPI_EXT_H
+ 
+ /* Define to 1 if you have the <gssapi/gssapi_ext.h> header file. */
+ #undef HAVE_GSSAPI_GSSAPI_EXT_H
+ 
/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
#undef HAVE_GSSAPI_GSSAPI_H

Shall I push that, or do you want to?

Hrmpf. Sorry about that. Please feel free and thanks for pointing it
out.

Thanks again,

Stephen