1: 5853d79e0d4 ! 1: e51f717e07c Split PGOAUTHDEBUG=UNSAFE into multiple options @@ Commit message Split PGOAUTHDEBUG=UNSAFE into multiple options ## doc/src/sgml/libpq.sgml ## -@@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest - Debugging and Developer Settings +@@ doc/src/sgml/libpq.sgml: typedef struct + - A "dangerous debugging mode" may be enabled by setting the environment @@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest - - - -- allows the system's trusted CA list to be completely replaced using the -- PGOAUTHCAFILE environment variable -- -- -- -- - prints HTTP traffic (containing several critical secrets) to standard - error during the OAuth flow - @@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest + + + -+ custom-ca (unsafe) -+ -+ -+ Allows the system's trusted CA list to be completely replaced using the -+ PGOAUTHCAFILE environment variable. This can facilitate -+ man-in-the-middle attacks when testing with self-signed certificates. -+ -+ -+ -+ -+ + fast-retry (safe) + + @@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest + + + -+ Unsafe options (http, trace, -+ custom-ca) require the UNSAFE: prefix. ++ Unsafe options (http, trace) ++ require the UNSAFE: prefix. + If unsafe options are specified without this prefix, a warning is printed + to standard error and that option is ignored. Other valid options in the + list continue to work. Safe options (fast-retry, + poll-counts, print-plugin-errors) can + be used without the prefix. -+ + + + + Unrecognized option names will also trigger a warning and be ignored, while + valid options continue to work. This helps catch typos in the environment + variable configuration without breaking the debugging of valid options. - ++ + + + Examples: + +PGOAUTHDEBUG=fast-retry,poll-counts safe options only +PGOAUTHDEBUG=UNSAFE:http,trace enable HTTP and traffic logging -+PGOAUTHDEBUG=UNSAFE:http,custom-ca,poll-counts mix of unsafe and safe ++PGOAUTHDEBUG=UNSAFE:http,poll-counts mix of unsafe and safe +PGOAUTHDEBUG=UNSAFE legacy; enables all options + + @@ src/interfaces/libpq/meson.build 'fe-cancel.c', ## src/interfaces/libpq-oauth/Makefile ## -@@ src/interfaces/libpq-oauth/Makefile: override CFLAGS += $(PTHREAD_CFLAGS) +@@ src/interfaces/libpq-oauth/Makefile: override CPPFLAGS_SHLIB += -DUSE_PRIVATE_ENCODING_FUNCS OBJS = \ $(WIN32RES) @@ src/interfaces/libpq-oauth/Makefile: override CFLAGS += $(PTHREAD_CFLAGS) oauth-utils.o \ + fe-auth-oauth-debug_shlib.o - oauth-utils.o: override CPPFLAGS += -DUSE_DYNAMIC_OAUTH - oauth-curl_shlib.o: override CPPFLAGS_SHLIB += -DUSE_DYNAMIC_OAUTH -+fe-auth-oauth-debug_shlib.o: override CPPFLAGS_SHLIB += -DUSE_DYNAMIC_OAUTH -+ + oauth-utils.o: override CPPFLAGS += $(CPPFLAGS_SHLIB) + +fe-auth-oauth-debug.o: $(libpq_srcdir)/fe-auth-oauth-debug.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + -+fe-auth-oauth-debug_shlib.o: $(libpq_srcdir)/fe-auth-oauth-debug.c ++fe-auth-oauth-debug_shlib.o: $(libpq_srcdir)/fe-auth-oauth-debug.c fe-auth-oauth-debug.o + $(CC) $(CFLAGS) $(CFLAGS_SL) $(CPPFLAGS) $(CPPFLAGS_SHLIB) -c $< -o $@ - ++ # Add shlib-/stlib-specific objects. $(shlib): override OBJS += $(OBJS_SHLIB) + $(shlib): $(OBJS_SHLIB) ## src/interfaces/libpq/Makefile ## @@ src/interfaces/libpq/Makefile: OBJS = \ @@ src/interfaces/libpq/Makefile: OBJS = \ ifneq ($(with_ssl),no) ## src/interfaces/libpq-oauth/oauth-utils.h ## +@@ + #ifndef OAUTH_UTILS_H + #define OAUTH_UTILS_H + ++#include "fe-auth-oauth.h" + #include "libpq-fe.h" + #include "pqexpbuffer.h" + @@ src/interfaces/libpq-oauth/oauth-utils.h: typedef enum + PG_BOOL_NO /* No (false) */ } PGTernaryBool; - extern void libpq_append_conn_error(PGconn *conn, const char *fmt,...) pg_attribute_printf(2, 3); -extern bool oauth_unsafe_debugging_enabled(void); +extern oauth_debug_flags oauth_get_debug_flags(void); extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending); @@ src/interfaces/libpq/fe-auth-oauth.h: typedef struct + /* UNSAFE features - require UNSAFE: prefix */ + bool http; /* allow HTTP (unencrypted) connections */ + bool trace; /* log HTTP traffic (exposes secrets) */ -+ bool custom_ca; /* allow custom CA certificate file */ + + /* SAFE features - allowed without UNSAFE: prefix */ + bool fast_retry; /* allow zero-second retry intervals */ @@ src/interfaces/libpq/fe-auth-oauth.h: typedef struct extern void pqClearOAuthToken(PGconn *conn); -extern bool oauth_unsafe_debugging_enabled(void); +extern oauth_debug_flags oauth_get_debug_flags(void); - extern bool use_builtin_flow(PGconn *conn, fe_oauth_state *state); /* Mechanisms in fe-auth-oauth.c */ + extern const pg_fe_sasl_mech pg_oauth_mech; ## src/interfaces/libpq-oauth/oauth-curl.c ## @@ src/interfaces/libpq-oauth/oauth-curl.c: struct async_ctx @@ src/interfaces/libpq-oauth/oauth-curl.c: setup_curl_handles(struct async_ctx *ac protos = unsafe; CHECK_SETOPT(actx, popt, protos, return false); -@@ src/interfaces/libpq-oauth/oauth-curl.c: setup_curl_handles(struct async_ctx *actx) - * the flow to work at all, so any changes to the roots are likely to be - * done system-wide. - */ -- if (actx->debugging) -+ if (actx->debug_flags.custom_ca) - { - const char *env; - @@ src/interfaces/libpq-oauth/oauth-curl.c: check_for_device_flow(struct async_ctx *actx) * decent time to bail out if we're not using HTTPS for the endpoints * we'll use for the flow. @@ src/interfaces/libpq-oauth/oauth-curl.c: check_for_device_flow(struct async_ctx { if (pg_strncasecmp(provider->device_authorization_endpoint, HTTPS_SCHEME, strlen(HTTPS_SCHEME)) != 0) -@@ src/interfaces/libpq-oauth/oauth-curl.c: pg_fe_run_oauth_flow_impl(PGconn *conn) - actx->mux = PGINVALID_SOCKET; - actx->timerfd = -1; - -- /* Should we enable unsafe features? */ -- actx->debugging = oauth_unsafe_debugging_enabled(); -+ /* Parse debug flags from environment */ -+ actx->debug_flags = oauth_get_debug_flags(); - - state->async_ctx = actx; - -@@ src/interfaces/libpq-oauth/oauth-curl.c: pg_fe_run_oauth_flow(PGconn *conn) - actx = state->async_ctx; - Assert(actx || result == PGRES_POLLING_FAILED); - -- if (actx && actx->debugging) +@@ src/interfaces/libpq-oauth/oauth-curl.c: pg_fe_run_oauth_flow(PGconn *conn, struct PGoauthBearerRequest *request, + * drain_timer_events(), when we're in debug mode, track the total number + * of calls to this function and print that at the end of the flow. + */ +- if (actx->debugging) + if (actx && actx->debug_flags.poll_counts) { actx->dbg_num_calls++; if (result == PGRES_POLLING_OK || result == PGRES_POLLING_FAILED) +@@ src/interfaces/libpq-oauth/oauth-curl.c: pg_start_oauthbearer(PGconn *conn, PGoauthBearerRequestV2 *request) + * Now finish filling in the actx. + */ + +- /* Should we enable unsafe features? */ +- actx->debugging = oauth_unsafe_debugging_enabled(); ++ /* Parse debug flags from the environment. */ ++ actx->debug_flags = oauth_get_debug_flags(); + + initPQExpBuffer(&actx->work_data); + initPQExpBuffer(&actx->errbuf); ## src/interfaces/libpq-oauth/oauth-utils.c ## @@ src/interfaces/libpq-oauth/oauth-utils.c: libpq_gettext(const char *msgid) @@ src/interfaces/libpq-oauth/test-oauth-curl.c: init_test_actx(void) - actx->debugging = true; + actx->debug_flags.http = true; + actx->debug_flags.trace = true; -+ actx->debug_flags.custom_ca = true; -+ actx->debug_flags.issuer_mismatch = true; + actx->debug_flags.fast_retry = true; + actx->debug_flags.poll_counts = true; + actx->debug_flags.print_plugin_errors = true; @@ src/interfaces/libpq/fe-auth-oauth-debug.c (new) + *is_unsafe = true; + return true; + } -+ else if (strcmp(option, "custom-ca") == 0) -+ { -+ flags->custom_ca = true; -+ *is_unsafe = true; -+ return true; -+ } + /* Safe options */ + else if (strcmp(option, "fast-retry") == 0) + { @@ src/interfaces/libpq/fe-auth-oauth-debug.c (new) + { + flags.http = true; + flags.trace = true; -+ flags.custom_ca = true; + flags.fast_retry = true; + flags.poll_counts = true; + flags.print_plugin_errors = true; @@ src/interfaces/libpq/fe-auth-oauth.c: issuer_from_well_known_uri(PGconn *conn, c && pg_strncasecmp(wkuri, HTTP_SCHEME, strlen(HTTP_SCHEME)) == 0) { /* Allow http:// for testing only. */ -@@ src/interfaces/libpq/fe-auth-oauth.c: use_builtin_flow(PGconn *conn, fe_oauth_state *state) +@@ src/interfaces/libpq/fe-auth-oauth.c: use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re * * Note that POSIX dlerror() isn't guaranteed to be threadsafe. */ @@ src/interfaces/libpq/fe-auth-oauth.c: use_builtin_flow(PGconn *conn, fe_oauth_st + if (oauth_get_debug_flags().print_plugin_errors) fprintf(stderr, "failed dlopen for libpq-oauth: %s\n", dlerror()); - return false; -@@ src/interfaces/libpq/fe-auth-oauth.c: use_builtin_flow(PGconn *conn, fe_oauth_state *state) - * This is more of an error condition than the one above, but due to - * the dlerror() threadsafety issue, lock it behind PGOAUTHDEBUG too. + return 0; +@@ src/interfaces/libpq/fe-auth-oauth.c: use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re + * cause is still locked behind PGOAUTHDEBUG due to the dlerror() + * threadsafety issue. */ - if (oauth_unsafe_debugging_enabled()) + if (oauth_get_debug_flags().print_plugin_errors) 2: 5fc7a19876b ! 2: 933f6432f87 Add new PGOAUTHDEBUG option: issuer-mismatch @@ doc/src/sgml/libpq.sgml: PGOAUTHDEBUG=UNSAFE legacy format; e fast-retry (safe) @@ doc/src/sgml/libpq.sgml: PGOAUTHDEBUG=UNSAFE legacy format; enables all options - Unsafe options (http, trace, -- custom-ca) require the UNSAFE: prefix. -+ custom-ca, issuer-mismatch) require the UNSAFE: prefix. +- Unsafe options (http, trace) +- require the UNSAFE: prefix. ++ Unsafe options (http, trace, ++ issuer-mismatch) require the UNSAFE: prefix. If unsafe options are specified without this prefix, a warning is printed to standard error and that option is ignored. Other valid options in the list continue to work. Safe options (fast-retry, ## src/interfaces/libpq/fe-auth-oauth.h ## @@ src/interfaces/libpq/fe-auth-oauth.h: typedef struct oauth_debug_flags + /* UNSAFE features - require UNSAFE: prefix */ bool http; /* allow HTTP (unencrypted) connections */ bool trace; /* log HTTP traffic (exposes secrets) */ - bool custom_ca; /* allow custom CA certificate file */ + bool issuer_mismatch; /* tolerate issuer mismatch */ /* SAFE features - allowed without UNSAFE: prefix */ @@ src/interfaces/libpq-oauth/oauth-curl.c: check_issuer(struct async_ctx *actx, PG return true; + ## src/interfaces/libpq-oauth/test-oauth-curl.c ## +@@ src/interfaces/libpq-oauth/test-oauth-curl.c: init_test_actx(void) + actx->timerfd = -1; + actx->debug_flags.http = true; + actx->debug_flags.trace = true; ++ actx->debug_flags.issuer_mismatch = true; + actx->debug_flags.fast_retry = true; + actx->debug_flags.poll_counts = true; + actx->debug_flags.print_plugin_errors = true; + ## src/interfaces/libpq/fe-auth-oauth-debug.c ## @@ src/interfaces/libpq/fe-auth-oauth-debug.c: parse_debug_option(const char *option, oauth_debug_flags *flags, bool *is_unsafe *is_unsafe = true; @@ src/interfaces/libpq/fe-auth-oauth-debug.c: parse_debug_option(const char *optio else if (strcmp(option, "fast-retry") == 0) { @@ src/interfaces/libpq/fe-auth-oauth-debug.c: oauth_get_debug_flags(void) + { flags.http = true; flags.trace = true; - flags.custom_ca = true; + flags.issuer_mismatch = true; flags.fast_retry = true; flags.poll_counts = true;