From efb9dda2254db3cbcb7e915b652ecbb20c6ef2be Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Wed, 28 May 2025 11:09:15 -0700
Subject: [PATCH 2/2] WIP: fall back to GSS.framework on macOS

---
 config/programs.m4                   |  27 ++++++-
 configure                            | 106 ++++++++++++++++++++++++++-
 configure.ac                         |  12 ++-
 src/backend/libpq/auth.c             |   6 ++
 src/backend/libpq/be-gssapi-common.c |   4 +
 src/backend/libpq/be-secure-gssapi.c |   6 ++
 src/include/libpq/be-gssapi-common.h |   3 +
 src/include/libpq/pg-gssapi.h        |  13 +++-
 src/include/pg_config.h.in           |   6 ++
 9 files changed, 172 insertions(+), 11 deletions(-)

diff --git a/config/programs.m4 b/config/programs.m4
index f2a950750bd..5a218bd9e63 100644
--- a/config/programs.m4
+++ b/config/programs.m4
@@ -358,14 +358,35 @@ AC_DEFUN([PGAC_CHECK_LIBCURL],
 
 # PGAC_CHECK_GSSAPI
 # ------------------
-# Check for a GSSAPI implementation.
+# Check for a GSSAPI implementation. pgac_found_gss is set to 'yes' if we find
+# MIT Kerberos, or 'framework' if we find GSS.framework on Mac.
 
 AC_DEFUN([PGAC_CHECK_GSSAPI],
 [
   if test "$PORTNAME" != "win32"; then
-    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])])
+    pgac_found_gss=no
+
+    AC_SEARCH_LIBS(gss_store_cred_into, [gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'],
+                   [pgac_found_gss=yes])
+
+    # If an implementation with gss_store_cred_into() is unavailable, fall back
+    # to GSS.framework on macOS.
+    if test "$pgac_found_gss" = no -a "$PORTNAME" = "darwin"; then
+      CPPFLAGS="$CPPFLAGS -iwithsysroot /System/Library/Frameworks/GSS.framework/Headers"
+      LDFLAGS="$LDFLAGS -F$PG_SYSROOT/System/Library/Frameworks -framework GSS"
+
+      AC_SEARCH_LIBS(gss_init_sec_context, [],
+                     [pgac_found_gss=framework],
+                     [AC_MSG_ERROR([could not find GSS.framework required for GSSAPI])])
+    fi
+
+    if test "$pgac_found_gss" = no; then
+       AC_MSG_ERROR([could not find function 'gss_store_cred_into' required for GSSAPI])
+    fi
   else
     LIBS="$LIBS -lgssapi32"
   fi
+
+  # Track the availability of gss_store_cred_into() separately.
+  AC_CHECK_FUNCS(gss_store_cred_into)
 ])# PGAC_CHECK_GSSAPI
diff --git a/configure b/configure
index 327b2de8184..ef0c6648dda 100755
--- a/configure
+++ b/configure
@@ -12894,6 +12894,8 @@ fi
 if test "$with_gssapi" = yes ; then
 
   if test "$PORTNAME" != "win32"; then
+    pgac_found_gss=no
+
     { $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 :
@@ -12947,15 +12949,96 @@ $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"
+  pgac_found_gss=yes
+fi
+
+
+	# If an implementation with gss_store_cred_into() is unavailable, fall back
+	# to GSS.framework on macOS.
+    if test "$pgac_found_gss" = no -a "$PORTNAME" = "darwin"; then
+      CPPFLAGS="$CPPFLAGS -iwithsysroot /System/Library/Frameworks/GSS.framework/Headers"
+      LDFLAGS="$LDFLAGS -F$PG_SYSROOT/System/Library/Frameworks -framework GSS"
+
+      { $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_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gss_init_sec_context ();
+int
+main ()
+{
+return gss_init_sec_context ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' ; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_gss_init_sec_context=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_gss_init_sec_context+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
 
 else
-  as_fn_error $? "could not find function 'gss_store_cred_into' required for GSSAPI" "$LINENO" 5
+  ac_cv_search_gss_init_sec_context=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
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  pgac_found_gss=framework
+else
+  as_fn_error $? "could not find GSS.framework required for GSSAPI" "$LINENO" 5
+fi
+
+    fi
 
+    if test "$pgac_found_gss" = no; then
+       as_fn_error $? "could not find function 'gss_store_cred_into' required for GSSAPI" "$LINENO" 5
+    fi
   else
     LIBS="$LIBS -lgssapi32"
   fi
 
+  # Track the availability of gss_store_cred_into() separately.
+  for ac_func in gss_store_cred_into
+do :
+  ac_fn_c_check_func "$LINENO" "gss_store_cred_into" "ac_cv_func_gss_store_cred_into"
+if test "x$ac_cv_func_gss_store_cred_into" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSS_STORE_CRED_INTO 1
+_ACEOF
+
+fi
+done
+
+
 fi
 
 #
@@ -14107,7 +14190,23 @@ fi
 fi
 
 if test "$with_gssapi" = yes ; then
-  for ac_header in gssapi/gssapi.h
+  if test "$pgac_found_gss" = framework; then
+    for ac_header in GSS/GSS.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "GSS/GSS.h" "ac_cv_header_GSS_GSS_h" "$ac_includes_default"
+if test "x$ac_cv_header_GSS_GSS_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GSS_GSS_H 1
+_ACEOF
+
+else
+  as_fn_error $? "GSS.h header file is required for GSSAPI" "$LINENO" 5
+fi
+
+done
+
+  else
+    for ac_header in gssapi/gssapi.h
 do :
   ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi.h" "ac_cv_header_gssapi_gssapi_h" "$ac_includes_default"
 if test "x$ac_cv_header_gssapi_gssapi_h" = xyes; then :
@@ -14134,7 +14233,7 @@ fi
 
 done
 
-  for ac_header in gssapi/gssapi_ext.h
+    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 :
@@ -14161,6 +14260,7 @@ fi
 
 done
 
+  fi
 fi
 
 if test -z "$OPENSSL"; then
diff --git a/configure.ac b/configure.ac
index 97d9375703f..4a1dd3c60ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1566,10 +1566,14 @@ if test "$with_zstd" = yes; then
 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])])])
+  if test "$pgac_found_gss" = framework; then
+    AC_CHECK_HEADERS(GSS/GSS.h, [], [AC_MSG_ERROR([GSS.h header file is required for GSSAPI])])
+  else
+    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
 fi
 
 PGAC_PATH_PROGS(OPENSSL, openssl)
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 9f4d05ffbd4..c86e6928ce3 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -1017,8 +1017,14 @@ pg_GSS_recvauth(Port *port)
 
 		if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
 		{
+#if HAVE_GSS_STORE_CRED_INTO
 			pg_store_delegated_credential(delegated_creds);
 			port->gss->delegated_creds = true;
+#else
+			/* XXX check WARNING pre-auth, release credentials */
+			ereport(WARNING,
+					errmsg("GSS implementation does not support credential delegation; ignoring delegation request"));
+#endif
 		}
 
 		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 7adea3060e1..53f5fbd534a 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -93,6 +93,8 @@ pg_GSS_error(const char *errmsg,
 			 errdetail_internal("%s: %s", msg_major, msg_minor)));
 }
 
+#if HAVE_GSS_STORE_CRED_INTO
+
 /*
  * Store the credentials passed in into the memory cache for later usage.
  *
@@ -145,3 +147,5 @@ pg_store_delegated_credential(gss_cred_id_t cred)
 	 */
 	setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
 }
+
+#endif							/* HAVE_GSS_STORE_CRED_INTO */
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 717ba9824f9..008be98b4dc 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -616,8 +616,14 @@ secure_open_gssapi(Port *port)
 
 		if (delegated_creds != GSS_C_NO_CREDENTIAL)
 		{
+#if HAVE_GSS_STORE_CRED_INTO
 			pg_store_delegated_credential(delegated_creds);
 			port->gss->delegated_creds = true;
+#else
+			/* XXX check WARNING pre-auth, release credentials */
+			ereport(WARNING,
+					errmsg("GSS implementation does not support credential delegation; ignoring delegation request"));
+#endif
 		}
 
 		/* Done handling the incoming packet, reset our buffer */
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
index bfe8d7656ed..8f146b3d453 100644
--- a/src/include/libpq/be-gssapi-common.h
+++ b/src/include/libpq/be-gssapi-common.h
@@ -21,7 +21,10 @@
 extern void pg_GSS_error(const char *errmsg,
 						 OM_uint32 maj_stat, OM_uint32 min_stat);
 
+#if HAVE_GSS_STORE_CRED_INTO
 extern void pg_store_delegated_credential(gss_cred_id_t cred);
+#endif
+
 #endif							/* ENABLE_GSS */
 
 #endif							/* BE_GSSAPI_COMMON_H */
diff --git a/src/include/libpq/pg-gssapi.h b/src/include/libpq/pg-gssapi.h
index f49fad14fcc..caf5cfc9098 100644
--- a/src/include/libpq/pg-gssapi.h
+++ b/src/include/libpq/pg-gssapi.h
@@ -17,7 +17,18 @@
 #ifdef ENABLE_GSS
 
 /* IWYU pragma: begin_exports */
-#if defined(HAVE_GSSAPI_H)
+#if defined(HAVE_GSS_GSS_H)
+/* XXX CoreFramework namespace pollution is extreme */
+#define CF_OPEN_SOURCE
+#define CF_EXCLUDE_CSTD_HEADERS
+#define Size MacSize___
+#define Boolean MacBoolean___
+#include <GSS/GSS.h>
+#undef Size
+#undef Boolean
+#undef CF_EXCLUDE_CSTD_HEADERS
+#undef CF_OPEN_SOURCE
+#elif defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
 #include <gssapi_ext.h>
 #else
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 726a7c1be1f..eca3a81cf58 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -202,6 +202,12 @@
 /* Define to 1 if you have the <gssapi.h> header file. */
 #undef HAVE_GSSAPI_H
 
+/* Define to 1 if you have the <GSS/GSS.h> header file. */
+#undef HAVE_GSS_GSS_H
+
+/* Define to 1 if you have the `gss_store_cred_into' function. */
+#undef HAVE_GSS_STORE_CRED_INTO
+
 /* Define to 1 if you have the <history.h> header file. */
 #undef HAVE_HISTORY_H
 
-- 
2.34.1

