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-2025, 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
128 jchampion@postgresql 65 :CBC 49 : 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 : 49 : pg_g_threadlock = threadlock_impl;
78 : 49 : libpq_gettext_impl = gettext_impl;
79 : 49 : conn_errorMessage = errmsg_impl;
80 : 49 : conn_oauth_client_id = clientid_impl;
81 : 49 : conn_oauth_client_secret = clientsecret_impl;
82 : 49 : conn_oauth_discovery_uri = discoveryuri_impl;
83 : 49 : conn_oauth_issuer_id = issuerid_impl;
84 : 49 : conn_oauth_scope = scope_impl;
85 : 49 : conn_sasl_state = saslstate_impl;
86 : 49 : set_conn_altsock = setaltsock_impl;
87 : 49 : set_conn_oauth_token = settoken_impl;
88 : 49 : }
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
128 jchampion@postgresql 95 :UBC 0 : libpq_append_conn_error(PGconn *conn, const char *fmt,...)
96 : : {
97 : 0 : int save_errno = errno;
98 : : bool done;
99 : : 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 : : 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 : : }
118 : :
119 : : #ifdef ENABLE_NLS
120 : :
121 : : /*
122 : : * A shim that defers to the actual libpq_gettext().
123 : : */
124 : : char *
128 jchampion@postgresql 125 :CBC 58 : libpq_gettext(const char *msgid)
126 : : {
127 [ - + ]: 58 : 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 : : */
128 jchampion@postgresql 137 :UBC 0 : return unconstify(char *, msgid);
138 : : }
139 : :
128 jchampion@postgresql 140 :CBC 58 : return libpq_gettext_impl(msgid);
141 : : }
142 : :
143 : : #endif /* ENABLE_NLS */
144 : :
145 : : /*
146 : : * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
147 : : */
148 : : bool
149 : 48 : oauth_unsafe_debugging_enabled(void)
150 : : {
151 : 48 : const char *env = getenv("PGOAUTHDEBUG");
152 : :
153 [ + - + - ]: 48 : return (env && strcmp(env, "UNSAFE") == 0);
154 : : }
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 : 492904 : pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
173 : : {
174 : : sigset_t sigpipe_sigset;
175 : : sigset_t sigset;
176 : :
177 : 492904 : sigemptyset(&sigpipe_sigset);
178 : 492904 : sigaddset(&sigpipe_sigset, SIGPIPE);
179 : :
180 : : /* Block SIGPIPE and save previous mask for later reset */
181 : 492904 : SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
182 [ - + ]: 492904 : if (SOCK_ERRNO)
128 jchampion@postgresql 183 :UBC 0 : return -1;
184 : :
185 : : /* We can have a pending SIGPIPE only if it was blocked before */
128 jchampion@postgresql 186 [ - + ]:CBC 492904 : if (sigismember(osigset, SIGPIPE))
187 : : {
188 : : /* Is there a pending SIGPIPE? */
128 jchampion@postgresql 189 [ # # ]:UBC 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 : : }
197 : : else
128 jchampion@postgresql 198 :CBC 492904 : *sigpipe_pending = false;
199 : :
200 : 492904 : return 0;
201 : : }
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 : 492904 : pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
209 : : {
210 : 492904 : int save_errno = SOCK_ERRNO;
211 : : int signo;
212 : : sigset_t sigset;
213 : :
214 : : /* Clear SIGPIPE only if none was pending */
215 [ + - + - ]: 492904 : if (got_epipe && !sigpipe_pending)
216 : : {
217 [ + - - + ]: 985808 : if (sigpending(&sigset) == 0 &&
218 : 492904 : sigismember(&sigset, SIGPIPE))
219 : : {
220 : : sigset_t sigpipe_sigset;
221 : :
128 jchampion@postgresql 222 :UBC 0 : sigemptyset(&sigpipe_sigset);
223 : 0 : sigaddset(&sigpipe_sigset, SIGPIPE);
224 : :
225 : 0 : sigwait(&sigpipe_sigset, &signo);
226 : : }
227 : : }
228 : :
229 : : /* Restore saved block mask */
128 jchampion@postgresql 230 :CBC 492904 : pthread_sigmask(SIG_SETMASK, osigset, NULL);
231 : :
232 : 492904 : SOCK_ERRNO_SET(save_errno);
233 : 492904 : }
|