/src/postgres/src/backend/libpq/be-secure.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * be-secure.c |
4 | | * functions related to setting up a secure connection to the frontend. |
5 | | * Secure connections are expected to provide confidentiality, |
6 | | * message integrity and endpoint authentication. |
7 | | * |
8 | | * |
9 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
10 | | * Portions Copyright (c) 1994, Regents of the University of California |
11 | | * |
12 | | * |
13 | | * IDENTIFICATION |
14 | | * src/backend/libpq/be-secure.c |
15 | | * |
16 | | *------------------------------------------------------------------------- |
17 | | */ |
18 | | |
19 | | #include "postgres.h" |
20 | | |
21 | | #include <signal.h> |
22 | | #include <fcntl.h> |
23 | | #include <ctype.h> |
24 | | #include <sys/socket.h> |
25 | | #include <netdb.h> |
26 | | #include <netinet/in.h> |
27 | | #include <netinet/tcp.h> |
28 | | #include <arpa/inet.h> |
29 | | |
30 | | #include "libpq/libpq.h" |
31 | | #include "miscadmin.h" |
32 | | #include "tcop/tcopprot.h" |
33 | | #include "utils/injection_point.h" |
34 | | #include "utils/wait_event.h" |
35 | | |
36 | | char *ssl_library; |
37 | | char *ssl_cert_file; |
38 | | char *ssl_key_file; |
39 | | char *ssl_ca_file; |
40 | | char *ssl_crl_file; |
41 | | char *ssl_crl_dir; |
42 | | char *ssl_dh_params_file; |
43 | | char *ssl_passphrase_command; |
44 | | bool ssl_passphrase_command_supports_reload; |
45 | | |
46 | | #ifdef USE_SSL |
47 | | bool ssl_loaded_verify_locations = false; |
48 | | #endif |
49 | | |
50 | | /* GUC variable controlling SSL cipher list */ |
51 | | char *SSLCipherSuites = NULL; |
52 | | char *SSLCipherList = NULL; |
53 | | |
54 | | /* GUC variable for default ECHD curve. */ |
55 | | char *SSLECDHCurve; |
56 | | |
57 | | /* GUC variable: if false, prefer client ciphers */ |
58 | | bool SSLPreferServerCiphers; |
59 | | |
60 | | int ssl_min_protocol_version = PG_TLS1_2_VERSION; |
61 | | int ssl_max_protocol_version = PG_TLS_ANY; |
62 | | |
63 | | /* ------------------------------------------------------------ */ |
64 | | /* Procedures common to all secure sessions */ |
65 | | /* ------------------------------------------------------------ */ |
66 | | |
67 | | /* |
68 | | * Initialize global context. |
69 | | * |
70 | | * If isServerStart is true, report any errors as FATAL (so we don't return). |
71 | | * Otherwise, log errors at LOG level and return -1 to indicate trouble, |
72 | | * preserving the old SSL state if any. Returns 0 if OK. |
73 | | */ |
74 | | int |
75 | | secure_initialize(bool isServerStart) |
76 | 0 | { |
77 | | #ifdef USE_SSL |
78 | | return be_tls_init(isServerStart); |
79 | | #else |
80 | 0 | return 0; |
81 | 0 | #endif |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | * Destroy global context, if any. |
86 | | */ |
87 | | void |
88 | | secure_destroy(void) |
89 | 0 | { |
90 | | #ifdef USE_SSL |
91 | | be_tls_destroy(); |
92 | | #endif |
93 | 0 | } |
94 | | |
95 | | /* |
96 | | * Indicate if we have loaded the root CA store to verify certificates |
97 | | */ |
98 | | bool |
99 | | secure_loaded_verify_locations(void) |
100 | 0 | { |
101 | | #ifdef USE_SSL |
102 | | return ssl_loaded_verify_locations; |
103 | | #else |
104 | 0 | return false; |
105 | 0 | #endif |
106 | 0 | } |
107 | | |
108 | | /* |
109 | | * Attempt to negotiate secure session. |
110 | | */ |
111 | | int |
112 | | secure_open_server(Port *port) |
113 | 0 | { |
114 | | #ifdef USE_SSL |
115 | | int r = 0; |
116 | | ssize_t len; |
117 | | |
118 | | /* push unencrypted buffered data back through SSL setup */ |
119 | | len = pq_buffer_remaining_data(); |
120 | | if (len > 0) |
121 | | { |
122 | | char *buf = palloc(len); |
123 | | |
124 | | pq_startmsgread(); |
125 | | if (pq_getbytes(buf, len) == EOF) |
126 | | return STATUS_ERROR; /* shouldn't be possible */ |
127 | | pq_endmsgread(); |
128 | | port->raw_buf = buf; |
129 | | port->raw_buf_remaining = len; |
130 | | port->raw_buf_consumed = 0; |
131 | | } |
132 | | Assert(pq_buffer_remaining_data() == 0); |
133 | | |
134 | | INJECTION_POINT("backend-ssl-startup", NULL); |
135 | | |
136 | | r = be_tls_open_server(port); |
137 | | |
138 | | if (port->raw_buf_remaining > 0) |
139 | | { |
140 | | /* |
141 | | * This shouldn't be possible -- it would mean the client sent |
142 | | * encrypted data before we established a session key... |
143 | | */ |
144 | | elog(LOG, "buffered unencrypted data remains after negotiating SSL connection"); |
145 | | return STATUS_ERROR; |
146 | | } |
147 | | if (port->raw_buf != NULL) |
148 | | { |
149 | | pfree(port->raw_buf); |
150 | | port->raw_buf = NULL; |
151 | | } |
152 | | |
153 | | ereport(DEBUG2, |
154 | | (errmsg_internal("SSL connection from DN:\"%s\" CN:\"%s\"", |
155 | | port->peer_dn ? port->peer_dn : "(anonymous)", |
156 | | port->peer_cn ? port->peer_cn : "(anonymous)"))); |
157 | | return r; |
158 | | #else |
159 | 0 | return 0; |
160 | 0 | #endif |
161 | 0 | } |
162 | | |
163 | | /* |
164 | | * Close secure session. |
165 | | */ |
166 | | void |
167 | | secure_close(Port *port) |
168 | 0 | { |
169 | | #ifdef USE_SSL |
170 | | if (port->ssl_in_use) |
171 | | be_tls_close(port); |
172 | | #endif |
173 | 0 | } |
174 | | |
175 | | /* |
176 | | * Read data from a secure connection. |
177 | | */ |
178 | | ssize_t |
179 | | secure_read(Port *port, void *ptr, size_t len) |
180 | 0 | { |
181 | 0 | ssize_t n; |
182 | 0 | int waitfor; |
183 | | |
184 | | /* Deal with any already-pending interrupt condition. */ |
185 | 0 | ProcessClientReadInterrupt(false); |
186 | |
|
187 | 0 | retry: |
188 | | #ifdef USE_SSL |
189 | | waitfor = 0; |
190 | | if (port->ssl_in_use) |
191 | | { |
192 | | n = be_tls_read(port, ptr, len, &waitfor); |
193 | | } |
194 | | else |
195 | | #endif |
196 | | #ifdef ENABLE_GSS |
197 | | if (port->gss && port->gss->enc) |
198 | | { |
199 | | n = be_gssapi_read(port, ptr, len); |
200 | | waitfor = WL_SOCKET_READABLE; |
201 | | } |
202 | | else |
203 | | #endif |
204 | 0 | { |
205 | 0 | n = secure_raw_read(port, ptr, len); |
206 | 0 | waitfor = WL_SOCKET_READABLE; |
207 | 0 | } |
208 | | |
209 | | /* In blocking mode, wait until the socket is ready */ |
210 | 0 | if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN)) |
211 | 0 | { |
212 | 0 | WaitEvent event; |
213 | |
|
214 | 0 | Assert(waitfor); |
215 | |
|
216 | 0 | ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, NULL); |
217 | |
|
218 | 0 | WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, |
219 | 0 | WAIT_EVENT_CLIENT_READ); |
220 | | |
221 | | /* |
222 | | * If the postmaster has died, it's not safe to continue running, |
223 | | * because it is the postmaster's job to kill us if some other backend |
224 | | * exits uncleanly. Moreover, we won't run very well in this state; |
225 | | * helper processes like walwriter and the bgwriter will exit, so |
226 | | * performance may be poor. Finally, if we don't exit, pg_ctl will be |
227 | | * unable to restart the postmaster without manual intervention, so no |
228 | | * new connections can be accepted. Exiting clears the deck for a |
229 | | * postmaster restart. |
230 | | * |
231 | | * (Note that we only make this check when we would otherwise sleep on |
232 | | * our latch. We might still continue running for a while if the |
233 | | * postmaster is killed in mid-query, or even through multiple queries |
234 | | * if we never have to wait for read. We don't want to burn too many |
235 | | * cycles checking for this very rare condition, and this should cause |
236 | | * us to exit quickly in most cases.) |
237 | | */ |
238 | 0 | if (event.events & WL_POSTMASTER_DEATH) |
239 | 0 | ereport(FATAL, |
240 | 0 | (errcode(ERRCODE_ADMIN_SHUTDOWN), |
241 | 0 | errmsg("terminating connection due to unexpected postmaster exit"))); |
242 | | |
243 | | /* Handle interrupt. */ |
244 | 0 | if (event.events & WL_LATCH_SET) |
245 | 0 | { |
246 | 0 | ResetLatch(MyLatch); |
247 | 0 | ProcessClientReadInterrupt(true); |
248 | | |
249 | | /* |
250 | | * We'll retry the read. Most likely it will return immediately |
251 | | * because there's still no data available, and we'll wait for the |
252 | | * socket to become ready again. |
253 | | */ |
254 | 0 | } |
255 | 0 | goto retry; |
256 | 0 | } |
257 | | |
258 | | /* |
259 | | * Process interrupts that happened during a successful (or non-blocking, |
260 | | * or hard-failed) read. |
261 | | */ |
262 | 0 | ProcessClientReadInterrupt(false); |
263 | |
|
264 | 0 | return n; |
265 | 0 | } |
266 | | |
267 | | ssize_t |
268 | | secure_raw_read(Port *port, void *ptr, size_t len) |
269 | 0 | { |
270 | 0 | ssize_t n; |
271 | | |
272 | | /* Read from the "unread" buffered data first. c.f. libpq-be.h */ |
273 | 0 | if (port->raw_buf_remaining > 0) |
274 | 0 | { |
275 | | /* consume up to len bytes from the raw_buf */ |
276 | 0 | if (len > port->raw_buf_remaining) |
277 | 0 | len = port->raw_buf_remaining; |
278 | 0 | Assert(port->raw_buf); |
279 | 0 | memcpy(ptr, port->raw_buf + port->raw_buf_consumed, len); |
280 | 0 | port->raw_buf_consumed += len; |
281 | 0 | port->raw_buf_remaining -= len; |
282 | 0 | return len; |
283 | 0 | } |
284 | | |
285 | | /* |
286 | | * Try to read from the socket without blocking. If it succeeds we're |
287 | | * done, otherwise we'll wait for the socket using the latch mechanism. |
288 | | */ |
289 | | #ifdef WIN32 |
290 | | pgwin32_noblock = true; |
291 | | #endif |
292 | 0 | n = recv(port->sock, ptr, len, 0); |
293 | | #ifdef WIN32 |
294 | | pgwin32_noblock = false; |
295 | | #endif |
296 | |
|
297 | 0 | return n; |
298 | 0 | } |
299 | | |
300 | | |
301 | | /* |
302 | | * Write data to a secure connection. |
303 | | */ |
304 | | ssize_t |
305 | | secure_write(Port *port, const void *ptr, size_t len) |
306 | 0 | { |
307 | 0 | ssize_t n; |
308 | 0 | int waitfor; |
309 | | |
310 | | /* Deal with any already-pending interrupt condition. */ |
311 | 0 | ProcessClientWriteInterrupt(false); |
312 | |
|
313 | 0 | retry: |
314 | 0 | waitfor = 0; |
315 | | #ifdef USE_SSL |
316 | | if (port->ssl_in_use) |
317 | | { |
318 | | n = be_tls_write(port, ptr, len, &waitfor); |
319 | | } |
320 | | else |
321 | | #endif |
322 | | #ifdef ENABLE_GSS |
323 | | if (port->gss && port->gss->enc) |
324 | | { |
325 | | n = be_gssapi_write(port, ptr, len); |
326 | | waitfor = WL_SOCKET_WRITEABLE; |
327 | | } |
328 | | else |
329 | | #endif |
330 | 0 | { |
331 | 0 | n = secure_raw_write(port, ptr, len); |
332 | 0 | waitfor = WL_SOCKET_WRITEABLE; |
333 | 0 | } |
334 | |
|
335 | 0 | if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN)) |
336 | 0 | { |
337 | 0 | WaitEvent event; |
338 | |
|
339 | 0 | Assert(waitfor); |
340 | |
|
341 | 0 | ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, NULL); |
342 | |
|
343 | 0 | WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, |
344 | 0 | WAIT_EVENT_CLIENT_WRITE); |
345 | | |
346 | | /* See comments in secure_read. */ |
347 | 0 | if (event.events & WL_POSTMASTER_DEATH) |
348 | 0 | ereport(FATAL, |
349 | 0 | (errcode(ERRCODE_ADMIN_SHUTDOWN), |
350 | 0 | errmsg("terminating connection due to unexpected postmaster exit"))); |
351 | | |
352 | | /* Handle interrupt. */ |
353 | 0 | if (event.events & WL_LATCH_SET) |
354 | 0 | { |
355 | 0 | ResetLatch(MyLatch); |
356 | 0 | ProcessClientWriteInterrupt(true); |
357 | | |
358 | | /* |
359 | | * We'll retry the write. Most likely it will return immediately |
360 | | * because there's still no buffer space available, and we'll wait |
361 | | * for the socket to become ready again. |
362 | | */ |
363 | 0 | } |
364 | 0 | goto retry; |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * Process interrupts that happened during a successful (or non-blocking, |
369 | | * or hard-failed) write. |
370 | | */ |
371 | 0 | ProcessClientWriteInterrupt(false); |
372 | |
|
373 | 0 | return n; |
374 | 0 | } |
375 | | |
376 | | ssize_t |
377 | | secure_raw_write(Port *port, const void *ptr, size_t len) |
378 | 0 | { |
379 | 0 | ssize_t n; |
380 | |
|
381 | | #ifdef WIN32 |
382 | | pgwin32_noblock = true; |
383 | | #endif |
384 | 0 | n = send(port->sock, ptr, len, 0); |
385 | | #ifdef WIN32 |
386 | | pgwin32_noblock = false; |
387 | | #endif |
388 | |
|
389 | 0 | return n; |
390 | 0 | } |