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
    -    <title>Debugging and Developer Settings</title>
    +@@ doc/src/sgml/libpq.sgml: typedef struct
    +    </para>
      
         <para>
     -    A "dangerous debugging mode" may be enabled by setting the environment
    @@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest
     -     </listitem>
     -     <listitem>
     -      <para>
    --       allows the system's trusted CA list to be completely replaced using the
    --       <envar>PGOAUTHCAFILE</envar> environment variable
    --      </para>
    --     </listitem>
    --     <listitem>
    --      <para>
     -       prints HTTP traffic (containing several critical secrets) to standard
     -       error during the OAuth flow
     -      </para>
    @@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest
     +     </varlistentry>
     +
     +     <varlistentry>
    -+      <term><literal>custom-ca</literal> (unsafe)</term>
    -+      <listitem>
    -+       <para>
    -+        Allows the system's trusted CA list to be completely replaced using the
    -+        <envar>PGOAUTHCAFILE</envar> environment variable. This can facilitate
    -+        man-in-the-middle attacks when testing with self-signed certificates.
    -+       </para>
    -+      </listitem>
    -+     </varlistentry>
    -+
    -+     <varlistentry>
     +      <term><literal>fast-retry</literal> (safe)</term>
     +      <listitem>
     +       <para>
    @@ doc/src/sgml/libpq.sgml: typedef struct PGoauthBearerRequest
     +   </para>
     +
     +   <para>
    -+    Unsafe options (<literal>http</literal>, <literal>trace</literal>,
    -+    <literal>custom-ca</literal>) require the <literal>UNSAFE:</literal> prefix.
    ++    Unsafe options (<literal>http</literal>, <literal>trace</literal>)
    ++    require the <literal>UNSAFE:</literal> 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 (<literal>fast-retry</literal>,
     +    <literal>poll-counts</literal>, <literal>print-plugin-errors</literal>) can
     +    be used without the prefix.
    -+   </para>
    +    </para>
     +
     +   <para>
     +    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.
    -    </para>
    ++   </para>
     +
     +   <para>
     +    Examples:
     +    <programlisting>
     +PGOAUTHDEBUG=fast-retry,poll-counts    <lineannotation>safe options only</lineannotation>
     +PGOAUTHDEBUG=UNSAFE:http,trace    <lineannotation>enable HTTP and traffic logging</lineannotation>
    -+PGOAUTHDEBUG=UNSAFE:http,custom-ca,poll-counts    <lineannotation>mix of unsafe and safe</lineannotation>
    ++PGOAUTHDEBUG=UNSAFE:http,poll-counts    <lineannotation>mix of unsafe and safe</lineannotation>
     +PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy; enables all options</lineannotation>
     +    </programlisting>
     +   </para>
    @@ 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    <lineannotation>legacy format; e
            <term><literal>fast-retry</literal> (safe)</term>
            <listitem>
     @@ doc/src/sgml/libpq.sgml: PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy format; enables all options</linea
    +    </para>
      
         <para>
    -     Unsafe options (<literal>http</literal>, <literal>trace</literal>,
    --    <literal>custom-ca</literal>) require the <literal>UNSAFE:</literal> prefix.
    -+    <literal>custom-ca</literal>, <literal>issuer-mismatch</literal>) require the <literal>UNSAFE:</literal> prefix.
    +-    Unsafe options (<literal>http</literal>, <literal>trace</literal>)
    +-    require the <literal>UNSAFE:</literal> prefix.
    ++    Unsafe options (<literal>http</literal>, <literal>trace</literal>,
    ++    <literal>issuer-mismatch</literal>) require the <literal>UNSAFE:</literal> 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 (<literal>fast-retry</literal>,
     
      ## 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;
