public inbox for [email protected]
help / color / mirror / Atom feedFrom: Jacob Champion <[email protected]>
To: Christoph Berg <[email protected]>
To: Andres Freund <[email protected]>
To: Tom Lane <[email protected]>
To: Bruce Momjian <[email protected]>
To: PostgreSQL Hackers <[email protected]>
To: Daniel Gustafsson <[email protected]>
To: Thomas Munro <[email protected]>
To: Nazir Bilal Yavuz <[email protected]>
To: Peter Eisentraut <[email protected]>
To: Antonin Houska <[email protected]>
Subject: Re: [PoC] Federated Authn/z with OAUTHBEARER
Date: Fri, 4 Apr 2025 17:27:46 -0700
Message-ID: <CAOYmi+moTsgohh5Tf1gn7dBynARV9EFfWaBVPcJD9O=h6RkSCw@mail.gmail.com> (raw)
In-Reply-To: <CAOYmi+n+WB9fuQeoPL-0FWvC+fzJXUEfH=Ne1q7q5xT936TE=A@mail.gmail.com>
References: <CA+hUKG+dMm6FyQvXM2oD269u6BBnaDoPM4DEyYzZJK6SAFCEag@mail.gmail.com>
<[email protected]>
<CA+hUKGKCYRS34L+BUy!5qhMF55Zg-oCMxDhnSgqm0jWkP+6jYBA@mail.gmail.com>
<[email protected]>
<[email protected]>
<[email protected]>
<CAOYmi+kn+NYA=DNMZds8sPXonAFg3dJnTgQ+vsOMXsk1Yz+3Gg@mail.gmail.com>
<[email protected]>
<[email protected]>
<wbqaa72xxfnqtsspanbteoycmtpb6oshtwbrm7uwiw3pur4ll4@tybxmaasfjkv>
<[email protected]>
<CAOYmi+n+WB9fuQeoPL-0FWvC+fzJXUEfH=Ne1q7q5xT936TE=A@mail.gmail.com>
On Thu, Apr 3, 2025 at 12:50 PM Daniel Gustafsson <[email protected]> wrote:
> Thanks, both LGTM so pushed.
Thank you!
On Tue, Apr 1, 2025 at 3:40 PM Jacob Champion
<[email protected]> wrote:
> Maybe a better idea would be to ship an SONAME of
> `libpq-oauth.so.0.<major>`, without any symlinks, so that there's
> never any ambiguity about which module belongs with which libpq.
While I was looking into this I found that Debian's going to use the
existence of an SONAME to check other things, which I assume will make
Christoph's life harder. I have switched over to
'libpq-oauth-<major>.so', without any SONAME or symlinks.
v2 simplifies quite a few things and breaks out the new duplicated
code into its own file. I pared down the exports from libpq, by having
it push the pg_g_threadlock pointer directly into the module when
needed. I think a future improvement would be to combine the dlopen
with the libcurl initialization, so that everything is done exactly
once and the module doesn't need to know about threadlocks at all.
i18n is still not working correctly on my machine. I've gotten `make
init-po` to put the files into the right places now, but if I fake a
.po file and install the generated .mo, the translations still don't
seem to be found at runtime. Is anyone able to take a quick look to
see if I'm missing something obvious?
I still need to disable the module entirely on Windows (and other
platforms without support), and potentially rename the --with-libcurl
option.
Thanks,
--Jacob
Attachments:
[application/octet-stream] v2-0001-WIP-split-Device-Authorization-flow-into-dlopen-d.patch (21.0K, ../CAOYmi+moTsgohh5Tf1gn7dBynARV9EFfWaBVPcJD9O=h6RkSCw@mail.gmail.com/2-v2-0001-WIP-split-Device-Authorization-flow-into-dlopen-d.patch)
download | inline diff:
From 20b4fbe435d31c4e784ce56c887a8d5c365d8ea5 Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Wed, 26 Mar 2025 10:55:28 -0700
Subject: [PATCH v2] WIP: split Device Authorization flow into dlopen'd module
See notes on mailing list.
Co-authored-by: Daniel Gustafsson <[email protected]>
---
meson.build | 12 +-
src/interfaces/Makefile | 9 +
src/interfaces/libpq-oauth/Makefile | 56 ++++++
src/interfaces/libpq-oauth/README | 18 ++
src/interfaces/libpq-oauth/exports.txt | 4 +
src/interfaces/libpq-oauth/meson.build | 32 +++
src/interfaces/libpq-oauth/nls.mk | 15 ++
.../oauth-curl.c} | 9 +-
src/interfaces/libpq-oauth/oauth-curl.h | 23 +++
src/interfaces/libpq-oauth/oauth-utils.c | 190 ++++++++++++++++++
src/interfaces/libpq-oauth/oauth-utils.h | 22 ++
src/interfaces/libpq-oauth/po/LINGUAS | 0
src/interfaces/libpq-oauth/po/meson.build | 3 +
src/interfaces/libpq/Makefile | 4 -
src/interfaces/libpq/exports.txt | 1 +
src/interfaces/libpq/fe-auth-oauth.c | 52 ++++-
src/interfaces/libpq/fe-auth-oauth.h | 4 +-
src/interfaces/libpq/meson.build | 4 -
18 files changed, 431 insertions(+), 27 deletions(-)
create mode 100644 src/interfaces/libpq-oauth/Makefile
create mode 100644 src/interfaces/libpq-oauth/README
create mode 100644 src/interfaces/libpq-oauth/exports.txt
create mode 100644 src/interfaces/libpq-oauth/meson.build
create mode 100644 src/interfaces/libpq-oauth/nls.mk
rename src/interfaces/{libpq/fe-auth-oauth-curl.c => libpq-oauth/oauth-curl.c} (99%)
create mode 100644 src/interfaces/libpq-oauth/oauth-curl.h
create mode 100644 src/interfaces/libpq-oauth/oauth-utils.c
create mode 100644 src/interfaces/libpq-oauth/oauth-utils.h
create mode 100644 src/interfaces/libpq-oauth/po/LINGUAS
create mode 100644 src/interfaces/libpq-oauth/po/meson.build
diff --git a/meson.build b/meson.build
index 454ed81f5ea..5620d959056 100644
--- a/meson.build
+++ b/meson.build
@@ -107,6 +107,7 @@ os_deps = []
backend_both_deps = []
backend_deps = []
libpq_deps = []
+libpq_oauth_deps = []
pg_sysroot = ''
@@ -3215,17 +3216,18 @@ libpq_deps += [
gssapi,
ldap_r,
- # XXX libcurl must link after libgssapi_krb5 on FreeBSD to avoid segfaults
- # during gss_acquire_cred(). This is possibly related to Curl's Heimdal
- # dependency on that platform?
- libcurl,
libintl,
ssl,
]
+libpq_oauth_deps += [
+ libcurl,
+]
+
subdir('src/interfaces/libpq')
-# fe_utils depends on libpq
+# fe_utils and libpq-oauth depends on libpq
subdir('src/fe_utils')
+subdir('src/interfaces/libpq-oauth')
# for frontend binaries
frontend_code = declare_dependency(
diff --git a/src/interfaces/Makefile b/src/interfaces/Makefile
index 7d56b29d28f..322a498823d 100644
--- a/src/interfaces/Makefile
+++ b/src/interfaces/Makefile
@@ -14,7 +14,16 @@ include $(top_builddir)/src/Makefile.global
SUBDIRS = libpq ecpg
+ifeq ($(with_libcurl), yes)
+SUBDIRS += libpq-oauth
+endif
+
$(recurse)
all-ecpg-recurse: all-libpq-recurse
install-ecpg-recurse: install-libpq-recurse
+
+ifeq ($(with_libcurl), yes)
+all-libpq-oauth-recurse: all-libpq-recurse
+install-libpq-oauth-recurse: install-libpq-recurse
+endif
diff --git a/src/interfaces/libpq-oauth/Makefile b/src/interfaces/libpq-oauth/Makefile
new file mode 100644
index 00000000000..461c44b59c1
--- /dev/null
+++ b/src/interfaces/libpq-oauth/Makefile
@@ -0,0 +1,56 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for libpq-oauth
+#
+# Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/interfaces/libpq-oauth/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/interfaces/libpq-oauth
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+PGFILEDESC = "libpq-oauth - device authorization oauth support"
+
+# This is an internal module; we don't want an SONAME and therefore do not set
+# SO_MAJOR_VERSION. (We still put the major version into the name, to make it
+# obvious where the library belongs.)
+NAME = libpq-oauth-$(MAJORVERSION)
+
+override CPPFLAGS := -I$(libpq_srcdir) -I$(top_builddir)/src/port $(CPPFLAGS)
+
+OBJS = \
+ $(WIN32RES) \
+ oauth-curl.o \
+ oauth-utils.o
+
+SHLIB_LINK_INTERNAL = $(libpq_pgport_shlib)
+SHLIB_LINK = -lcurl
+SHLIB_PREREQS = submake-libpq
+
+SHLIB_EXPORTS = exports.txt
+
+PKG_CONFIG_REQUIRES_PRIVATE = libpq
+#
+# Make dependencies on pg_config_paths.h visible in all builds.
+oauth-curl.o: oauth-curl.c $(top_builddir)/src/port/pg_config_paths.h
+
+$(top_builddir)/src/port/pg_config_paths.h:
+ $(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
+
+all: all-lib
+
+# Shared library stuff
+include $(top_srcdir)/src/Makefile.shlib
+
+install: all installdirs install-lib
+
+installdirs: installdirs-lib
+
+uninstall: uninstall-lib
+
+clean distclean: clean-lib
+ rm -f $(OBJS)
diff --git a/src/interfaces/libpq-oauth/README b/src/interfaces/libpq-oauth/README
new file mode 100644
index 00000000000..5006f405080
--- /dev/null
+++ b/src/interfaces/libpq-oauth/README
@@ -0,0 +1,18 @@
+libpq-oauth is an optional module implementing the Device Authorization flow for
+OAuth clients (RFC 8628). It was originally developed as part of libpq core and
+later split out as its own shared library in order to isolate its dependency on
+libcurl. (End users who don't want the Curl dependency can simply choose not to
+install this module.)
+
+If a connection string allows the use of OAuth, the server asks for it, and a
+libpq client has not installed its own custom OAuth flow, libpq will attempt to
+delay-load this module using dlopen() and the following ABI. Failure to load
+results in a failed connection.
+
+= Load-Time ABI =
+
+This module ABI is an internal implementation detail, so it's subject to change
+without warning, even during minor releases (however unlikely). The compiled
+version of libpq-oauth should always match the compiled version of libpq.
+
+TODO
diff --git a/src/interfaces/libpq-oauth/exports.txt b/src/interfaces/libpq-oauth/exports.txt
new file mode 100644
index 00000000000..3787b388e04
--- /dev/null
+++ b/src/interfaces/libpq-oauth/exports.txt
@@ -0,0 +1,4 @@
+# src/interfaces/libpq-oauth/exports.txt
+pg_fe_run_oauth_flow 1
+pg_fe_cleanup_oauth_flow 2
+pg_g_threadlock 3
diff --git a/src/interfaces/libpq-oauth/meson.build b/src/interfaces/libpq-oauth/meson.build
new file mode 100644
index 00000000000..1834afbf7a5
--- /dev/null
+++ b/src/interfaces/libpq-oauth/meson.build
@@ -0,0 +1,32 @@
+# Copyright (c) 2022-2025, PostgreSQL Global Development Group
+
+if not libcurl.found() or host_system == 'windows'
+ subdir_done()
+endif
+
+libpq_oauth_sources = files(
+ 'oauth-curl.c',
+ 'oauth-utils.c',
+)
+
+export_file = custom_target('libpq-oauth.exports',
+ kwargs: gen_export_kwargs,
+)
+
+# port needs to be in include path due to pthread-win32.h
+libpq_oauth_inc = include_directories('.', '../libpq', '../../port')
+
+# This is an internal module; we don't want an SONAME and therefore do not set
+# SO_MAJOR_VERSION. (We still put the major version into the name, to make it
+# obvious where the library belongs.)
+libpq_oauth_so = shared_module('libpq-oauth-' + pg_version_major.to_string(),
+ libpq_oauth_sources,
+ include_directories: [libpq_oauth_inc, postgres_inc],
+ c_pch: pch_postgres_fe_h,
+ dependencies: [frontend_shlib_code, libpq, libpq_oauth_deps],
+ link_depends: export_file,
+ link_args: export_fmt.format(export_file.full_path()),
+ kwargs: default_lib_args,
+)
+
+subdir('po', if_found: libintl)
diff --git a/src/interfaces/libpq-oauth/nls.mk b/src/interfaces/libpq-oauth/nls.mk
new file mode 100644
index 00000000000..eab3347ef60
--- /dev/null
+++ b/src/interfaces/libpq-oauth/nls.mk
@@ -0,0 +1,15 @@
+# src/interfaces/libpq-oauth/nls.mk
+CATALOG_NAME = libpq-oauth
+GETTEXT_FILES = oauth-curl.c \
+ oauth-utils.c
+GETTEXT_TRIGGERS = actx_error:2 \
+ libpq_append_conn_error:2 \
+ libpq_append_error:2 \
+ libpq_gettext \
+ libpq_ngettext:1,2
+GETTEXT_FLAGS = actx_error:2:c-format \
+ libpq_append_conn_error:2:c-format \
+ libpq_append_error:2:c-format \
+ libpq_gettext:1:pass-c-format \
+ libpq_ngettext:1:pass-c-format \
+ libpq_ngettext:2:pass-c-format
diff --git a/src/interfaces/libpq/fe-auth-oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
similarity index 99%
rename from src/interfaces/libpq/fe-auth-oauth-curl.c
rename to src/interfaces/libpq-oauth/oauth-curl.c
index cd9c0323bb6..11d17ec1597 100644
--- a/src/interfaces/libpq/fe-auth-oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * fe-auth-oauth-curl.c
+ * oauth-curl.c
* The libcurl implementation of OAuth/OIDC authentication, using the
* OAuth Device Authorization Grant (RFC 8628).
*
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * src/interfaces/libpq/fe-auth-oauth-curl.c
+ * src/interfaces/libpq-oauth/oauth-curl.c
*
*-------------------------------------------------------------------------
*/
@@ -31,6 +31,8 @@
#include "fe-auth-oauth.h"
#include "libpq-int.h"
#include "mb/pg_wchar.h"
+#include "oauth-curl.h"
+#include "oauth-utils.h"
/*
* It's generally prudent to set a maximum response size to buffer in memory,
@@ -2487,8 +2489,9 @@ prompt_user(struct async_ctx *actx, PGconn *conn)
.verification_uri_complete = actx->authz.verification_uri_complete,
.expires_in = actx->authz.expires_in,
};
+ PQauthDataHook_type hook = PQgetAuthDataHook();
- res = PQauthDataHook(PQAUTHDATA_PROMPT_OAUTH_DEVICE, conn, &prompt);
+ res = hook(PQAUTHDATA_PROMPT_OAUTH_DEVICE, conn, &prompt);
if (!res)
{
diff --git a/src/interfaces/libpq-oauth/oauth-curl.h b/src/interfaces/libpq-oauth/oauth-curl.h
new file mode 100644
index 00000000000..bcc1e737dcd
--- /dev/null
+++ b/src/interfaces/libpq-oauth/oauth-curl.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * oauth-curl.h
+ *
+ * Definitions for OAuth Device Authorization module
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq-oauth/oauth-curl.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef OAUTH_CURL_H
+#define OAUTH_CURL_H
+
+#include "libpq-fe.h"
+
+extern PGDLLEXPORT PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn);
+extern PGDLLEXPORT void pg_fe_cleanup_oauth_flow(PGconn *conn);
+
+#endif /* OAUTH_CURL_H */
diff --git a/src/interfaces/libpq-oauth/oauth-utils.c b/src/interfaces/libpq-oauth/oauth-utils.c
new file mode 100644
index 00000000000..81f9c6dc247
--- /dev/null
+++ b/src/interfaces/libpq-oauth/oauth-utils.c
@@ -0,0 +1,190 @@
+/*-------------------------------------------------------------------------
+ *
+ * oauth-utils.c
+ *
+ * "Glue" helpers providing a copy of some internal APIs from libpq. At
+ * some point in the future, we might be able to deduplicate.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq-oauth/oauth-utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "libpq-int.h"
+#include "oauth-utils.h"
+#include "pg_config_paths.h"
+
+pgthreadlock_t pg_g_threadlock;
+
+/*
+ * Append a formatted string to the error message buffer of the given
+ * connection, after translating it. A newline is automatically appended; the
+ * format should not end with a newline.
+ */
+void
+libpq_append_conn_error(PGconn *conn, const char *fmt,...)
+{
+ int save_errno = errno;
+ bool done;
+ va_list args;
+
+ Assert(fmt[strlen(fmt) - 1] != '\n');
+
+ if (PQExpBufferBroken(&conn->errorMessage))
+ return; /* already failed */
+
+ /* Loop in case we have to retry after enlarging the buffer. */
+ do
+ {
+ errno = save_errno;
+ va_start(args, fmt);
+ done = appendPQExpBufferVA(&conn->errorMessage, libpq_gettext(fmt), args);
+ va_end(args);
+ } while (!done);
+
+ appendPQExpBufferChar(&conn->errorMessage, '\n');
+}
+
+/*
+ * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
+ */
+bool
+oauth_unsafe_debugging_enabled(void)
+{
+ const char *env = getenv("PGOAUTHDEBUG");
+
+ return (env && strcmp(env, "UNSAFE") == 0);
+}
+
+int
+pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
+{
+ sigset_t sigpipe_sigset;
+ sigset_t sigset;
+
+ sigemptyset(&sigpipe_sigset);
+ sigaddset(&sigpipe_sigset, SIGPIPE);
+
+ /* Block SIGPIPE and save previous mask for later reset */
+ SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
+ if (SOCK_ERRNO)
+ return -1;
+
+ /* We can have a pending SIGPIPE only if it was blocked before */
+ if (sigismember(osigset, SIGPIPE))
+ {
+ /* Is there a pending SIGPIPE? */
+ if (sigpending(&sigset) != 0)
+ return -1;
+
+ if (sigismember(&sigset, SIGPIPE))
+ *sigpipe_pending = true;
+ else
+ *sigpipe_pending = false;
+ }
+ else
+ *sigpipe_pending = false;
+
+ return 0;
+}
+
+void
+pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
+{
+ int save_errno = SOCK_ERRNO;
+ int signo;
+ sigset_t sigset;
+
+ /* Clear SIGPIPE only if none was pending */
+ if (got_epipe && !sigpipe_pending)
+ {
+ if (sigpending(&sigset) == 0 &&
+ sigismember(&sigset, SIGPIPE))
+ {
+ sigset_t sigpipe_sigset;
+
+ sigemptyset(&sigpipe_sigset);
+ sigaddset(&sigpipe_sigset, SIGPIPE);
+
+ sigwait(&sigpipe_sigset, &signo);
+ }
+ }
+
+ /* Restore saved block mask */
+ pthread_sigmask(SIG_SETMASK, osigset, NULL);
+
+ SOCK_ERRNO_SET(save_errno);
+}
+
+#ifdef ENABLE_NLS
+
+static void
+libpq_binddomain(void)
+{
+ /*
+ * At least on Windows, there are gettext implementations that fail if
+ * multiple threads call bindtextdomain() concurrently. Use a mutex and
+ * flag variable to ensure that we call it just once per process. It is
+ * not known that similar bugs exist on non-Windows platforms, but we
+ * might as well do it the same way everywhere.
+ */
+ static volatile bool already_bound = false;
+ static pthread_mutex_t binddomain_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ if (!already_bound)
+ {
+ /* bindtextdomain() does not preserve errno */
+#ifdef WIN32
+ int save_errno = GetLastError();
+#else
+ int save_errno = errno;
+#endif
+
+ (void) pthread_mutex_lock(&binddomain_mutex);
+
+ if (!already_bound)
+ {
+ const char *ldir;
+
+ /*
+ * No relocatable lookup here because the calling executable could
+ * be anywhere
+ */
+ ldir = getenv("PGLOCALEDIR");
+ if (!ldir)
+ ldir = LOCALEDIR;
+ bindtextdomain(PG_TEXTDOMAIN("libpq-oauth"), ldir);
+ already_bound = true;
+ }
+
+ (void) pthread_mutex_unlock(&binddomain_mutex);
+
+#ifdef WIN32
+ SetLastError(save_errno);
+#else
+ errno = save_errno;
+#endif
+ }
+}
+
+char *
+libpq_gettext(const char *msgid)
+{
+ libpq_binddomain();
+ return dgettext(PG_TEXTDOMAIN("libpq-oauth"), msgid);
+}
+
+char *
+libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n)
+{
+ libpq_binddomain();
+ return dngettext(PG_TEXTDOMAIN("libpq-oauth"), msgid, msgid_plural, n);
+}
+
+#endif /* ENABLE_NLS */
diff --git a/src/interfaces/libpq-oauth/oauth-utils.h b/src/interfaces/libpq-oauth/oauth-utils.h
new file mode 100644
index 00000000000..e5bd6b28b11
--- /dev/null
+++ b/src/interfaces/libpq-oauth/oauth-utils.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * oauth-utils.h
+ *
+ * Definitions providing missing libpq internal APIs
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq-oauth/oauth-utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "libpq-int.h"
+
+extern PGDLLEXPORT pgthreadlock_t pg_g_threadlock;
+
+void libpq_append_conn_error(PGconn *conn, const char *fmt,...) pg_attribute_printf(2, 3);
+bool oauth_unsafe_debugging_enabled(void);
+int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
+void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe);
diff --git a/src/interfaces/libpq-oauth/po/LINGUAS b/src/interfaces/libpq-oauth/po/LINGUAS
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/src/interfaces/libpq-oauth/po/meson.build b/src/interfaces/libpq-oauth/po/meson.build
new file mode 100644
index 00000000000..61b3807ac68
--- /dev/null
+++ b/src/interfaces/libpq-oauth/po/meson.build
@@ -0,0 +1,3 @@
+# Copyright (c) 2022-2025, PostgreSQL Global Development Group
+
+nls_targets += [i18n.gettext('libpq-oauth-' + pg_version_major.to_string())]
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 90b0b65db6f..8cf8d9e54d8 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -64,10 +64,6 @@ OBJS += \
fe-secure-gssapi.o
endif
-ifeq ($(with_libcurl),yes)
-OBJS += fe-auth-oauth-curl.o
-endif
-
ifeq ($(PORTNAME), cygwin)
override shlib = cyg$(NAME)$(DLSUFFIX)
endif
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index d5143766858..0625cf39e9a 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -210,3 +210,4 @@ PQsetAuthDataHook 207
PQgetAuthDataHook 208
PQdefaultAuthDataHook 209
PQfullProtocolVersion 210
+appendPQExpBufferVA 211
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index cf1a25e2ccc..ce15a5e8de1 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -15,6 +15,10 @@
#include "postgres_fe.h"
+#ifndef WIN32
+#include <dlfcn.h>
+#endif
+
#include "common/base64.h"
#include "common/hmac.h"
#include "common/jsonapi.h"
@@ -721,6 +725,44 @@ cleanup_user_oauth_flow(PGconn *conn)
state->async_ctx = NULL;
}
+static bool
+use_builtin_flow(PGconn *conn, fe_oauth_state *state)
+{
+#ifdef WIN32
+ return false;
+#else
+ PostgresPollingStatusType (*flow) (PGconn *conn);
+ void (*cleanup) (PGconn *conn);
+ pgthreadlock_t *threadlock_copy;
+
+ /* libpq-oauth is versioned in lockstep; we don't export a stable ABI. */
+ state->builtin_flow = dlopen("libpq-oauth-" PG_MAJORVERSION DLSUFFIX,
+ RTLD_NOW | RTLD_LOCAL);
+ if (!state->builtin_flow)
+ {
+ fprintf(stderr, "failed dlopen: %s\n", dlerror()); // XXX
+ return false;
+ }
+
+ flow = dlsym(state->builtin_flow, "pg_fe_run_oauth_flow");
+ cleanup = dlsym(state->builtin_flow, "pg_fe_cleanup_oauth_flow");
+ threadlock_copy = dlsym(state->builtin_flow, "pg_g_threadlock");
+
+ if (!(flow && cleanup && threadlock_copy))
+ {
+ fprintf(stderr, "failed dlsym: %s\n", dlerror()); // XXX
+ dlclose(state->builtin_flow);
+ return false;
+ }
+
+ conn->async_auth = flow;
+ conn->cleanup_async_auth = cleanup;
+ *threadlock_copy = pg_g_threadlock;
+
+ return true;
+#endif /* !WIN32 */
+}
+
/*
* Chooses an OAuth client flow for the connection, which will retrieve a Bearer
* token for presentation to the server.
@@ -792,18 +834,10 @@ setup_token_request(PGconn *conn, fe_oauth_state *state)
libpq_append_conn_error(conn, "user-defined OAuth flow failed");
goto fail;
}
- else
+ else if (!use_builtin_flow(conn, state))
{
-#if USE_LIBCURL
- /* Hand off to our built-in OAuth flow. */
- conn->async_auth = pg_fe_run_oauth_flow;
- conn->cleanup_async_auth = pg_fe_cleanup_oauth_flow;
-
-#else
libpq_append_conn_error(conn, "no custom OAuth flows are available, and libpq was not built with libcurl support");
goto fail;
-
-#endif
}
return true;
diff --git a/src/interfaces/libpq/fe-auth-oauth.h b/src/interfaces/libpq/fe-auth-oauth.h
index 3f1a7503a01..699ba42acc2 100644
--- a/src/interfaces/libpq/fe-auth-oauth.h
+++ b/src/interfaces/libpq/fe-auth-oauth.h
@@ -33,10 +33,10 @@ typedef struct
PGconn *conn;
void *async_ctx;
+
+ void *builtin_flow;
} fe_oauth_state;
-extern PostgresPollingStatusType pg_fe_run_oauth_flow(PGconn *conn);
-extern void pg_fe_cleanup_oauth_flow(PGconn *conn);
extern void pqClearOAuthToken(PGconn *conn);
extern bool oauth_unsafe_debugging_enabled(void);
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index 292fecf3320..47d38e9378f 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -38,10 +38,6 @@ if gssapi.found()
)
endif
-if libcurl.found()
- libpq_sources += files('fe-auth-oauth-curl.c')
-endif
-
export_file = custom_target('libpq.exports',
kwargs: gen_export_kwargs,
)
--
2.34.1
view thread (191+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: [PoC] Federated Authn/z with OAUTHBEARER
In-Reply-To: <CAOYmi+moTsgohh5Tf1gn7dBynARV9EFfWaBVPcJD9O=h6RkSCw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox