Age Owner Branch data TLA 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 : : /*-
39 : : * Initializes libpq-oauth by setting necessary callbacks.
40 : : *
41 : : * The current implementation relies on libpq_gettext to translate error
42 : : * messages using libpq's message domain, so libpq injects it here. We also use
43 : : * this chance to initialize our threadlock.
44 : : */
45 : : void
2 jchampion@postgresql 46 :GNC 50 : libpq_oauth_init(libpq_gettext_func gettext_impl)
47 : : {
48 : 50 : pg_g_threadlock = PQgetThreadLock();
318 jchampion@postgresql 49 :CBC 50 : libpq_gettext_impl = gettext_impl;
318 jchampion@postgresql 50 :GIC 50 : }
51 : :
52 : : #ifdef ENABLE_NLS
53 : :
54 : : /*
55 : : * A shim that defers to the actual libpq_gettext().
56 : : */
57 : : char *
318 jchampion@postgresql 58 :CBC 372 : libpq_gettext(const char *msgid)
59 : : {
60 [ - + ]: 372 : if (!libpq_gettext_impl)
61 : : {
62 : : /*
63 : : * Possible if the libpq build didn't enable NLS but the libpq-oauth
64 : : * build did. That's an odd mismatch, but we can handle it.
65 : : *
66 : : * Note that callers of libpq_gettext() have to treat the return value
67 : : * as if it were const, because builds without NLS simply pass through
68 : : * their argument.
69 : : */
318 jchampion@postgresql 70 :UBC 0 : return unconstify(char *, msgid);
71 : : }
72 : :
318 jchampion@postgresql 73 :CBC 372 : return libpq_gettext_impl(msgid);
74 : : }
75 : :
76 : : #endif /* ENABLE_NLS */
77 : :
78 : : /*
79 : : * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
80 : : */
81 : : bool
82 : 50 : oauth_unsafe_debugging_enabled(void)
83 : : {
84 : 50 : const char *env = getenv("PGOAUTHDEBUG");
85 : :
86 [ + - + - ]: 50 : return (env && strcmp(env, "UNSAFE") == 0);
87 : : }
88 : :
89 : : /*
90 : : * Duplicate SOCK_ERRNO* definitions from libpq-int.h, for use by
91 : : * pq_block/reset_sigpipe().
92 : : */
93 : : #ifdef WIN32
94 : : #define SOCK_ERRNO (WSAGetLastError())
95 : : #define SOCK_ERRNO_SET(e) WSASetLastError(e)
96 : : #else
97 : : #define SOCK_ERRNO errno
98 : : #define SOCK_ERRNO_SET(e) (errno = (e))
99 : : #endif
100 : :
101 : : /*
102 : : * Block SIGPIPE for this thread. This is a copy of libpq's internal API.
103 : : */
104 : : int
105 : 238881 : pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
106 : : {
107 : : sigset_t sigpipe_sigset;
108 : : sigset_t sigset;
109 : :
110 : 238881 : sigemptyset(&sigpipe_sigset);
111 : 238881 : sigaddset(&sigpipe_sigset, SIGPIPE);
112 : :
113 : : /* Block SIGPIPE and save previous mask for later reset */
114 : 238881 : SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
115 [ - + ]: 238881 : if (SOCK_ERRNO)
318 jchampion@postgresql 116 :UBC 0 : return -1;
117 : :
118 : : /* We can have a pending SIGPIPE only if it was blocked before */
318 jchampion@postgresql 119 [ - + ]:CBC 238881 : if (sigismember(osigset, SIGPIPE))
120 : : {
121 : : /* Is there a pending SIGPIPE? */
318 jchampion@postgresql 122 [ # # ]:UBC 0 : if (sigpending(&sigset) != 0)
123 : 0 : return -1;
124 : :
125 [ # # ]: 0 : if (sigismember(&sigset, SIGPIPE))
126 : 0 : *sigpipe_pending = true;
127 : : else
128 : 0 : *sigpipe_pending = false;
129 : : }
130 : : else
318 jchampion@postgresql 131 :CBC 238881 : *sigpipe_pending = false;
132 : :
133 : 238881 : return 0;
134 : : }
135 : :
136 : : /*
137 : : * Discard any pending SIGPIPE and reset the signal mask. This is a copy of
138 : : * libpq's internal API.
139 : : */
140 : : void
141 : 238881 : pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
142 : : {
143 : 238881 : int save_errno = SOCK_ERRNO;
144 : : int signo;
145 : : sigset_t sigset;
146 : :
147 : : /* Clear SIGPIPE only if none was pending */
148 [ + - + - ]: 238881 : if (got_epipe && !sigpipe_pending)
149 : : {
150 [ + - - + ]: 477762 : if (sigpending(&sigset) == 0 &&
151 : 238881 : sigismember(&sigset, SIGPIPE))
152 : : {
153 : : sigset_t sigpipe_sigset;
154 : :
318 jchampion@postgresql 155 :UBC 0 : sigemptyset(&sigpipe_sigset);
156 : 0 : sigaddset(&sigpipe_sigset, SIGPIPE);
157 : :
158 : 0 : sigwait(&sigpipe_sigset, &signo);
159 : : }
160 : : }
161 : :
162 : : /* Restore saved block mask */
318 jchampion@postgresql 163 :CBC 238881 : pthread_sigmask(SIG_SETMASK, osigset, NULL);
164 : :
165 : 238881 : SOCK_ERRNO_SET(save_errno);
166 : 238881 : }
|