On Wed, May 28, 2025 at 9:25 AM Jacob Champion <jacob.champ...@enterprisedb.com> wrote: > Personally, I'd be more happy to "maintain GSS on Mac using > non-deprecated interfaces" than "maintain GSS via Heimdal, > best-effort, some of the time". I think the former puts less of a > burden on our testing matrix.
I was curious enough to put in some time to get GSS.framework compiling via Autoconf, and I might as well share the ugly code I've got. There are some similarities to Todd's earlier patch, but decisions are made at different places; it detects either MIT Kerberos or GSS.framework. And I haven't looked at the Meson side yet. - I am not well-versed in frameworks. There's a bunch of namespace pollution in Apple's GSS headers, and I'm hoping I'm missing some magic #define to make that all go away. - My handling of pg_store_delegated_credential() here isn't something I'm seriously proposing. I think we should find a way to get it working on Mac, using Nico's notes upthread. I can't commit to working on that myself, but I'm definitely willing to put some review cycles in, since I reviewed a bit of the original delegation feature. - I also want to draw attention to the fact that libpq can't claim that a credential is delegated if it's not; that breaks the security of our FDWs. So pg_store_delegated_credential() cannot be a no-op. --Jacob
From d708e3ecd157fe7e990725d8377b5613c0ce5bdb Mon Sep 17 00:00:00 2001 From: Jacob Champion <jacob.champion@enterprisedb.com> Date: Wed, 28 May 2025 10:18:50 -0700 Subject: [PATCH 1/2] WIP: move GSSAPI checks into their own macro --- config/programs.m4 | 15 +++++++++++++++ configure | 2 ++ configure.ac | 7 +------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/config/programs.m4 b/config/programs.m4 index 0ad1e58b48d..f2a950750bd 100644 --- a/config/programs.m4 +++ b/config/programs.m4 @@ -354,3 +354,18 @@ AC_DEFUN([PGAC_CHECK_LIBCURL], LDFLAGS=$pgac_save_LDFLAGS LIBS=$pgac_save_LIBS ])# PGAC_CHECK_LIBCURL + + +# PGAC_CHECK_GSSAPI +# ------------------ +# Check for a GSSAPI implementation. + +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])]) + else + LIBS="$LIBS -lgssapi32" + fi +])# PGAC_CHECK_GSSAPI diff --git a/configure b/configure index 4f15347cc95..327b2de8184 100755 --- a/configure +++ b/configure @@ -12892,6 +12892,7 @@ $as_echo "$pgac_cv__libcurl_async_dns" >&6; } fi if test "$with_gssapi" = yes ; then + if test "$PORTNAME" != "win32"; 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; } @@ -12954,6 +12955,7 @@ fi else LIBS="$LIBS -lgssapi32" fi + fi # diff --git a/configure.ac b/configure.ac index 4b8335dc613..97d9375703f 100644 --- a/configure.ac +++ b/configure.ac @@ -1367,12 +1367,7 @@ if test "$with_libcurl" = yes ; then fi if test "$with_gssapi" = yes ; then - 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])]) - else - LIBS="$LIBS -lgssapi32" - fi + PGAC_CHECK_GSSAPI fi # -- 2.34.1
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