LCOV - code coverage report
Current view: top level - src/interfaces/libpq-oauth - oauth-utils.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 72 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * oauth-utils.c
       4              :  *
       5              :  *        "Glue" helpers providing a copy of some internal APIs from libpq. At
       6              :  *        some point in the future, we might be able to deduplicate.
       7              :  *
       8              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       9              :  * Portions Copyright (c) 1994, Regents of the University of California
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *        src/interfaces/libpq-oauth/oauth-utils.c
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : 
      17              : #include "postgres_fe.h"
      18              : 
      19              : #include <signal.h>
      20              : 
      21              : #include "oauth-utils.h"
      22              : 
      23              : #ifndef USE_DYNAMIC_OAUTH
      24              : #error oauth-utils.c is not supported in static builds
      25              : #endif
      26              : 
      27              : #ifdef LIBPQ_INT_H
      28              : #error do not rely on libpq-int.h in dynamic builds of libpq-oauth
      29              : #endif
      30              : 
      31              : /*
      32              :  * Function pointers set by libpq_oauth_init().
      33              :  */
      34              : 
      35              : pgthreadlock_t pg_g_threadlock;
      36              : static libpq_gettext_func libpq_gettext_impl;
      37              : 
      38              : conn_errorMessage_func conn_errorMessage;
      39              : conn_oauth_client_id_func conn_oauth_client_id;
      40              : conn_oauth_client_secret_func conn_oauth_client_secret;
      41              : conn_oauth_discovery_uri_func conn_oauth_discovery_uri;
      42              : conn_oauth_issuer_id_func conn_oauth_issuer_id;
      43              : conn_oauth_scope_func conn_oauth_scope;
      44              : conn_sasl_state_func conn_sasl_state;
      45              : 
      46              : set_conn_altsock_func set_conn_altsock;
      47              : set_conn_oauth_token_func set_conn_oauth_token;
      48              : 
      49              : /*-
      50              :  * Initializes libpq-oauth by setting necessary callbacks.
      51              :  *
      52              :  * The current implementation relies on the following private implementation
      53              :  * details of libpq:
      54              :  *
      55              :  * - pg_g_threadlock: protects libcurl initialization if the underlying Curl
      56              :  *   installation is not threadsafe
      57              :  *
      58              :  * - libpq_gettext: translates error messages using libpq's message domain
      59              :  *
      60              :  * The implementation also needs access to several members of the PGconn struct,
      61              :  * which are not guaranteed to stay in place across minor versions. Accessors
      62              :  * (named conn_*) and mutators (named set_conn_*) are injected here.
      63              :  */
      64              : void
      65            0 : libpq_oauth_init(pgthreadlock_t threadlock_impl,
      66              :                                  libpq_gettext_func gettext_impl,
      67              :                                  conn_errorMessage_func errmsg_impl,
      68              :                                  conn_oauth_client_id_func clientid_impl,
      69              :                                  conn_oauth_client_secret_func clientsecret_impl,
      70              :                                  conn_oauth_discovery_uri_func discoveryuri_impl,
      71              :                                  conn_oauth_issuer_id_func issuerid_impl,
      72              :                                  conn_oauth_scope_func scope_impl,
      73              :                                  conn_sasl_state_func saslstate_impl,
      74              :                                  set_conn_altsock_func setaltsock_impl,
      75              :                                  set_conn_oauth_token_func settoken_impl)
      76              : {
      77            0 :         pg_g_threadlock = threadlock_impl;
      78            0 :         libpq_gettext_impl = gettext_impl;
      79            0 :         conn_errorMessage = errmsg_impl;
      80            0 :         conn_oauth_client_id = clientid_impl;
      81            0 :         conn_oauth_client_secret = clientsecret_impl;
      82            0 :         conn_oauth_discovery_uri = discoveryuri_impl;
      83            0 :         conn_oauth_issuer_id = issuerid_impl;
      84            0 :         conn_oauth_scope = scope_impl;
      85            0 :         conn_sasl_state = saslstate_impl;
      86            0 :         set_conn_altsock = setaltsock_impl;
      87            0 :         set_conn_oauth_token = settoken_impl;
      88            0 : }
      89              : 
      90              : /*
      91              :  * Append a formatted string to the error message buffer of the given
      92              :  * connection, after translating it.  This is a copy of libpq's internal API.
      93              :  */
      94              : void
      95            0 : libpq_append_conn_error(PGconn *conn, const char *fmt,...)
      96              : {
      97            0 :         int                     save_errno = errno;
      98            0 :         bool            done;
      99            0 :         va_list         args;
     100            0 :         PQExpBuffer errorMessage = conn_errorMessage(conn);
     101              : 
     102            0 :         Assert(fmt[strlen(fmt) - 1] != '\n');
     103              : 
     104            0 :         if (PQExpBufferBroken(errorMessage))
     105            0 :                 return;                                 /* already failed */
     106              : 
     107              :         /* Loop in case we have to retry after enlarging the buffer. */
     108            0 :         do
     109              :         {
     110            0 :                 errno = save_errno;
     111            0 :                 va_start(args, fmt);
     112            0 :                 done = appendPQExpBufferVA(errorMessage, libpq_gettext(fmt), args);
     113            0 :                 va_end(args);
     114            0 :         } while (!done);
     115              : 
     116            0 :         appendPQExpBufferChar(errorMessage, '\n');
     117            0 : }
     118              : 
     119              : #ifdef ENABLE_NLS
     120              : 
     121              : /*
     122              :  * A shim that defers to the actual libpq_gettext().
     123              :  */
     124              : char *
     125            0 : libpq_gettext(const char *msgid)
     126              : {
     127            0 :         if (!libpq_gettext_impl)
     128              :         {
     129              :                 /*
     130              :                  * Possible if the libpq build didn't enable NLS but the libpq-oauth
     131              :                  * build did. That's an odd mismatch, but we can handle it.
     132              :                  *
     133              :                  * Note that callers of libpq_gettext() have to treat the return value
     134              :                  * as if it were const, because builds without NLS simply pass through
     135              :                  * their argument.
     136              :                  */
     137            0 :                 return unconstify(char *, msgid);
     138              :         }
     139              : 
     140            0 :         return libpq_gettext_impl(msgid);
     141            0 : }
     142              : 
     143              : #endif                                                  /* ENABLE_NLS */
     144              : 
     145              : /*
     146              :  * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
     147              :  */
     148              : bool
     149            0 : oauth_unsafe_debugging_enabled(void)
     150              : {
     151            0 :         const char *env = getenv("PGOAUTHDEBUG");
     152              : 
     153            0 :         return (env && strcmp(env, "UNSAFE") == 0);
     154            0 : }
     155              : 
     156              : /*
     157              :  * Duplicate SOCK_ERRNO* definitions from libpq-int.h, for use by
     158              :  * pq_block/reset_sigpipe().
     159              :  */
     160              : #ifdef WIN32
     161              : #define SOCK_ERRNO (WSAGetLastError())
     162              : #define SOCK_ERRNO_SET(e) WSASetLastError(e)
     163              : #else
     164              : #define SOCK_ERRNO errno
     165              : #define SOCK_ERRNO_SET(e) (errno = (e))
     166              : #endif
     167              : 
     168              : /*
     169              :  *      Block SIGPIPE for this thread. This is a copy of libpq's internal API.
     170              :  */
     171              : int
     172            0 : pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
     173              : {
     174            0 :         sigset_t        sigpipe_sigset;
     175            0 :         sigset_t        sigset;
     176              : 
     177            0 :         sigemptyset(&sigpipe_sigset);
     178            0 :         sigaddset(&sigpipe_sigset, SIGPIPE);
     179              : 
     180              :         /* Block SIGPIPE and save previous mask for later reset */
     181            0 :         SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
     182            0 :         if (SOCK_ERRNO)
     183            0 :                 return -1;
     184              : 
     185              :         /* We can have a pending SIGPIPE only if it was blocked before */
     186            0 :         if (sigismember(osigset, SIGPIPE))
     187              :         {
     188              :                 /* Is there a pending SIGPIPE? */
     189            0 :                 if (sigpending(&sigset) != 0)
     190            0 :                         return -1;
     191              : 
     192            0 :                 if (sigismember(&sigset, SIGPIPE))
     193            0 :                         *sigpipe_pending = true;
     194              :                 else
     195            0 :                         *sigpipe_pending = false;
     196            0 :         }
     197              :         else
     198            0 :                 *sigpipe_pending = false;
     199              : 
     200            0 :         return 0;
     201            0 : }
     202              : 
     203              : /*
     204              :  *      Discard any pending SIGPIPE and reset the signal mask. This is a copy of
     205              :  *      libpq's internal API.
     206              :  */
     207              : void
     208            0 : pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
     209              : {
     210            0 :         int                     save_errno = SOCK_ERRNO;
     211            0 :         int                     signo;
     212            0 :         sigset_t        sigset;
     213              : 
     214              :         /* Clear SIGPIPE only if none was pending */
     215            0 :         if (got_epipe && !sigpipe_pending)
     216              :         {
     217            0 :                 if (sigpending(&sigset) == 0 &&
     218            0 :                         sigismember(&sigset, SIGPIPE))
     219              :                 {
     220            0 :                         sigset_t        sigpipe_sigset;
     221              : 
     222            0 :                         sigemptyset(&sigpipe_sigset);
     223            0 :                         sigaddset(&sigpipe_sigset, SIGPIPE);
     224              : 
     225            0 :                         sigwait(&sigpipe_sigset, &signo);
     226            0 :                 }
     227            0 :         }
     228              : 
     229              :         /* Restore saved block mask */
     230            0 :         pthread_sigmask(SIG_SETMASK, osigset, NULL);
     231              : 
     232            0 :         SOCK_ERRNO_SET(save_errno);
     233            0 : }
        

Generated by: LCOV version 2.3.2-1