/src/openssl/ssl/rio/rio_notifier.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | #include "internal/sockets.h" |
11 | | #include <openssl/bio.h> |
12 | | #include <openssl/err.h> |
13 | | #include "internal/thread_once.h" |
14 | | #include "internal/rio_notifier.h" |
15 | | |
16 | | #if !defined(OPENSSL_SYS_WINDOWS) || RIO_NOTIFIER_METHOD == RIO_NOTIFIER_METHOD_SOCKETPAIR |
17 | | /* |
18 | | * Sets a socket as close-on-exec, except that this is a no-op if we are certain |
19 | | * we do not need to do this or the OS does not support the concept. |
20 | | */ |
21 | | static int set_cloexec(int fd) |
22 | 0 | { |
23 | | #if !defined(SOCK_CLOEXEC) && defined(FD_CLOEXEC) |
24 | | return fcntl(fd, F_SETFD, FD_CLOEXEC) >= 0; |
25 | | #else |
26 | 0 | return 1; |
27 | 0 | #endif |
28 | 0 | } |
29 | | #endif |
30 | | |
31 | | #if defined(OPENSSL_SYS_WINDOWS) |
32 | | |
33 | | static CRYPTO_ONCE ensure_wsa_startup_once = CRYPTO_ONCE_STATIC_INIT; |
34 | | static CRYPTO_RWLOCK *wsa_lock; |
35 | | static int wsa_started; |
36 | | static int wsa_ref; |
37 | | |
38 | | static void ossl_wsa_cleanup(void) |
39 | | { |
40 | | if (wsa_started) { |
41 | | wsa_started = 0; |
42 | | WSACleanup(); |
43 | | } |
44 | | |
45 | | CRYPTO_THREAD_lock_free(wsa_lock); |
46 | | wsa_lock = NULL; |
47 | | } |
48 | | |
49 | | DEFINE_RUN_ONCE_STATIC(do_wsa_startup) |
50 | | { |
51 | | WORD versionreq = 0x0202; /* Version 2.2 */ |
52 | | WSADATA wsadata; |
53 | | |
54 | | wsa_lock = CRYPTO_THREAD_lock_new(); |
55 | | if (wsa_lock == NULL) |
56 | | return 0; |
57 | | |
58 | | if (WSAStartup(versionreq, &wsadata) != 0) { |
59 | | CRYPTO_THREAD_lock_free(wsa_lock); |
60 | | wsa_lock = NULL; |
61 | | return 0; |
62 | | } |
63 | | wsa_started = 1; |
64 | | |
65 | | return 1; |
66 | | } |
67 | | |
68 | | static ossl_inline int ensure_wsa_startup(void) |
69 | | { |
70 | | int rv, unused; |
71 | | |
72 | | rv = RUN_ONCE(&ensure_wsa_startup_once, do_wsa_startup); |
73 | | if (rv != 0) |
74 | | CRYPTO_atomic_add(&wsa_ref, 1, &unused, wsa_lock); |
75 | | |
76 | | return rv; |
77 | | } |
78 | | |
79 | | static void wsa_done(void) |
80 | | { |
81 | | int ref; |
82 | | |
83 | | if (wsa_lock != NULL) { |
84 | | CRYPTO_atomic_add(&wsa_ref, -1, &ref, wsa_lock); |
85 | | if (ref == 0) { |
86 | | ossl_wsa_cleanup(); |
87 | | ensure_wsa_startup_once = CRYPTO_ONCE_STATIC_INIT; |
88 | | wsa_lock = NULL; |
89 | | } |
90 | | } |
91 | | } |
92 | | |
93 | | #endif |
94 | | |
95 | | #if RIO_NOTIFIER_METHOD == RIO_NOTIFIER_METHOD_SOCKET |
96 | | |
97 | | /* Create a close-on-exec socket. */ |
98 | | static int create_socket(int domain, int socktype, int protocol) |
99 | | { |
100 | | int fd; |
101 | | #if defined(OPENSSL_SYS_WINDOWS) |
102 | | static const int on = 1; |
103 | | |
104 | | /* |
105 | | * Use WSASocketA to create a socket which is immediately marked as |
106 | | * non-inheritable, avoiding race conditions if another thread is about to |
107 | | * call CreateProcess. |
108 | | * NOTE: windows xp (0x501) doesn't support the non-inheritance flag here |
109 | | * but preventing inheritance isn't mandatory, just a safety precaution |
110 | | * so we can get away with not including it for older platforms |
111 | | */ |
112 | | |
113 | | #ifdef WSA_FLAG_NO_HANDLE_INHERIT |
114 | | fd = (int)WSASocketA(domain, socktype, protocol, NULL, 0, |
115 | | WSA_FLAG_NO_HANDLE_INHERIT); |
116 | | |
117 | | /* |
118 | | * Its also possible that someone is building a binary on a newer windows |
119 | | * SDK, but running it on a runtime that doesn't support inheritance |
120 | | * suppression. In that case the above will return INVALID_SOCKET, and |
121 | | * our response for those older platforms is to try the call again |
122 | | * without the flag |
123 | | */ |
124 | | if (fd == INVALID_SOCKET) |
125 | | fd = (int)WSASocketA(domain, socktype, protocol, NULL, 0, 0); |
126 | | #else |
127 | | fd = (int)WSASocketA(domain, socktype, protocol, NULL, 0, 0); |
128 | | #endif |
129 | | if (fd == INVALID_SOCKET) { |
130 | | int err = get_last_socket_error(); |
131 | | |
132 | | ERR_raise_data(ERR_LIB_SYS, err, |
133 | | "calling WSASocketA() = %d", err); |
134 | | return INVALID_SOCKET; |
135 | | } |
136 | | |
137 | | /* Prevent interference with the socket from other processes on Windows. */ |
138 | | if (setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *)&on, sizeof(on)) < 0) { |
139 | | ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(), |
140 | | "calling setsockopt()"); |
141 | | BIO_closesocket(fd); |
142 | | return INVALID_SOCKET; |
143 | | } |
144 | | |
145 | | #else |
146 | | #if defined(SOCK_CLOEXEC) |
147 | | socktype |= SOCK_CLOEXEC; |
148 | | #endif |
149 | | |
150 | | fd = BIO_socket(domain, socktype, protocol, 0); |
151 | | if (fd == INVALID_SOCKET) { |
152 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
153 | | "calling BIO_socket()"); |
154 | | return INVALID_SOCKET; |
155 | | } |
156 | | |
157 | | /* |
158 | | * Make socket close-on-exec unless this was already done above at socket |
159 | | * creation time. |
160 | | */ |
161 | | if (!set_cloexec(fd)) { |
162 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
163 | | "calling set_cloexec()"); |
164 | | BIO_closesocket(fd); |
165 | | return INVALID_SOCKET; |
166 | | } |
167 | | #endif |
168 | | |
169 | | return fd; |
170 | | } |
171 | | |
172 | | /* |
173 | | * The SOCKET notifier method manually creates a connected TCP socket pair by |
174 | | * temporarily creating a TCP listener on a random port and connecting back to |
175 | | * it. |
176 | | * |
177 | | * Win32 does not support socketpair(2), and Win32 pipes are not compatible with |
178 | | * Winsock select(2). This means our only means of making select(2) wakeable is |
179 | | * to artificially create a loopback TCP connection and send bytes to it. |
180 | | */ |
181 | | int ossl_rio_notifier_init(RIO_NOTIFIER *nfy) |
182 | | { |
183 | | int rc, lfd = -1, rfd = -1, wfd = -1; |
184 | | struct sockaddr_in sa = { 0 }, accept_sa; |
185 | | socklen_t sa_len = sizeof(sa), accept_sa_len = sizeof(accept_sa); |
186 | | |
187 | | #if defined(OPENSSL_SYS_WINDOWS) |
188 | | if (!ensure_wsa_startup()) { |
189 | | ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR, |
190 | | "Cannot start Windows sockets"); |
191 | | |
192 | | wsa_done(); |
193 | | return 0; |
194 | | } |
195 | | #endif |
196 | | /* Create a close-on-exec socket. */ |
197 | | lfd = create_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
198 | | if (lfd == INVALID_SOCKET) { |
199 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
200 | | "calling create_socket()"); |
201 | | return 0; |
202 | | } |
203 | | |
204 | | /* Bind the socket to a random loopback port. */ |
205 | | sa.sin_family = AF_INET; |
206 | | sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
207 | | rc = bind(lfd, (const struct sockaddr *)&sa, sizeof(sa)); |
208 | | if (rc < 0) { |
209 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
210 | | "calling bind()"); |
211 | | goto err; |
212 | | } |
213 | | |
214 | | /* Determine what random port was allocated. */ |
215 | | rc = getsockname(lfd, (struct sockaddr *)&sa, &sa_len); |
216 | | if (rc < 0) { |
217 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
218 | | "calling getsockname()"); |
219 | | goto err; |
220 | | } |
221 | | |
222 | | /* Start listening. */ |
223 | | rc = listen(lfd, 1); |
224 | | if (rc < 0) { |
225 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
226 | | "calling listen()"); |
227 | | goto err; |
228 | | } |
229 | | |
230 | | /* Create another socket to connect to the listener. */ |
231 | | wfd = create_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
232 | | if (wfd == INVALID_SOCKET) { |
233 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
234 | | "calling create_socket()"); |
235 | | goto err; |
236 | | } |
237 | | |
238 | | /* |
239 | | * Disable Nagle's algorithm on the writer so that wakeups happen |
240 | | * immediately. |
241 | | */ |
242 | | if (!BIO_set_tcp_ndelay(wfd, 1)) { |
243 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
244 | | "calling BIO_set_tcp_ndelay()"); |
245 | | goto err; |
246 | | } |
247 | | |
248 | | /* |
249 | | * Connect the writer to the listener. |
250 | | */ |
251 | | rc = connect(wfd, (struct sockaddr *)&sa, sizeof(sa)); |
252 | | if (rc < 0) { |
253 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
254 | | "calling connect()"); |
255 | | goto err; |
256 | | } |
257 | | |
258 | | /* |
259 | | * The connection accepted from the listener is the read side. |
260 | | */ |
261 | | rfd = accept(lfd, (struct sockaddr *)&accept_sa, &accept_sa_len); |
262 | | if (rfd == INVALID_SOCKET) { |
263 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
264 | | "calling accept()"); |
265 | | goto err; |
266 | | } |
267 | | |
268 | | rc = getsockname(wfd, (struct sockaddr *)&sa, &sa_len); |
269 | | if (rc < 0) { |
270 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
271 | | "calling getsockname()"); |
272 | | goto err; |
273 | | } |
274 | | |
275 | | /* Close the listener, which we don't need anymore. */ |
276 | | BIO_closesocket(lfd); |
277 | | lfd = -1; |
278 | | |
279 | | /* |
280 | | * Sanity check - ensure someone else didn't connect to our listener during |
281 | | * the brief window of possibility above. |
282 | | */ |
283 | | if (accept_sa.sin_family != AF_INET || accept_sa.sin_port != sa.sin_port) { |
284 | | ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR, |
285 | | "connected address differs from accepted address"); |
286 | | goto err; |
287 | | } |
288 | | |
289 | | /* Make both sides of the connection non-blocking. */ |
290 | | if (!BIO_socket_nbio(rfd, 1)) { |
291 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
292 | | "calling BIO_socket_nbio()"); |
293 | | goto err; |
294 | | } |
295 | | |
296 | | if (!BIO_socket_nbio(wfd, 1)) { |
297 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
298 | | "calling BIO_socket_nbio()"); |
299 | | goto err; |
300 | | } |
301 | | |
302 | | nfy->rfd = rfd; |
303 | | nfy->wfd = wfd; |
304 | | return 1; |
305 | | |
306 | | err: |
307 | | if (lfd != INVALID_SOCKET) |
308 | | BIO_closesocket(lfd); |
309 | | if (wfd != INVALID_SOCKET) |
310 | | BIO_closesocket(wfd); |
311 | | if (rfd != INVALID_SOCKET) |
312 | | BIO_closesocket(rfd); |
313 | | return 0; |
314 | | } |
315 | | |
316 | | #elif RIO_NOTIFIER_METHOD == RIO_NOTIFIER_METHOD_SOCKETPAIR |
317 | | |
318 | | int ossl_rio_notifier_init(RIO_NOTIFIER *nfy) |
319 | 0 | { |
320 | 0 | int fds[2], domain = AF_INET, type = SOCK_STREAM; |
321 | |
|
322 | 0 | #if defined(SOCK_CLOEXEC) |
323 | 0 | type |= SOCK_CLOEXEC; |
324 | 0 | #endif |
325 | 0 | #if defined(SOCK_NONBLOCK) |
326 | 0 | type |= SOCK_NONBLOCK; |
327 | 0 | #endif |
328 | |
|
329 | 0 | #if defined(OPENSSL_SYS_UNIX) && defined(AF_UNIX) |
330 | 0 | domain = AF_UNIX; |
331 | 0 | #endif |
332 | |
|
333 | 0 | if (socketpair(domain, type, 0, fds) < 0) { |
334 | 0 | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
335 | 0 | "calling socketpair()"); |
336 | 0 | return 0; |
337 | 0 | } |
338 | | |
339 | 0 | if (!set_cloexec(fds[0]) || !set_cloexec(fds[1])) { |
340 | 0 | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
341 | 0 | "calling set_cloexec()"); |
342 | 0 | goto err; |
343 | 0 | } |
344 | | |
345 | | #if !defined(SOCK_NONBLOCK) |
346 | | if (!BIO_socket_nbio(fds[0], 1) || !BIO_socket_nbio(fds[1], 1)) { |
347 | | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
348 | | "calling BIO_socket_nbio()"); |
349 | | goto err; |
350 | | } |
351 | | #endif |
352 | | |
353 | 0 | if (domain == AF_INET && !BIO_set_tcp_ndelay(fds[1], 1)) { |
354 | 0 | ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), |
355 | 0 | "calling BIO_set_tcp_ndelay()"); |
356 | 0 | goto err; |
357 | 0 | } |
358 | | |
359 | 0 | nfy->rfd = fds[0]; |
360 | 0 | nfy->wfd = fds[1]; |
361 | 0 | return 1; |
362 | | |
363 | 0 | err: |
364 | 0 | BIO_closesocket(fds[1]); |
365 | 0 | BIO_closesocket(fds[0]); |
366 | 0 | return 0; |
367 | 0 | } |
368 | | |
369 | | #endif |
370 | | |
371 | | void ossl_rio_notifier_cleanup(RIO_NOTIFIER *nfy) |
372 | 0 | { |
373 | 0 | if (nfy->rfd < 0) |
374 | 0 | return; |
375 | | |
376 | 0 | BIO_closesocket(nfy->wfd); |
377 | 0 | BIO_closesocket(nfy->rfd); |
378 | 0 | nfy->rfd = nfy->wfd = -1; |
379 | | #if defined(OPENSSL_SYS_WINDOWS) |
380 | | wsa_done(); |
381 | | #endif |
382 | 0 | } |
383 | | |
384 | | int ossl_rio_notifier_signal(RIO_NOTIFIER *nfy) |
385 | 0 | { |
386 | 0 | static const unsigned char ch = 0; |
387 | 0 | ossl_ssize_t wr; |
388 | |
|
389 | 0 | do |
390 | | /* |
391 | | * Note: If wr returns 0 the buffer is already full so we don't need to |
392 | | * do anything. |
393 | | */ |
394 | 0 | wr = writesocket(nfy->wfd, (void *)&ch, sizeof(ch)); |
395 | 0 | while (wr < 0 && get_last_socket_error_is_eintr()); |
396 | |
|
397 | 0 | return 1; |
398 | 0 | } |
399 | | |
400 | | int ossl_rio_notifier_unsignal(RIO_NOTIFIER *nfy) |
401 | 0 | { |
402 | 0 | unsigned char buf[16]; |
403 | 0 | ossl_ssize_t rd; |
404 | | |
405 | | /* |
406 | | * signal() might have been called multiple times. Drain the buffer until |
407 | | * it's empty. |
408 | | */ |
409 | 0 | do |
410 | 0 | rd = readsocket(nfy->rfd, (void *)buf, sizeof(buf)); |
411 | 0 | while (rd == sizeof(buf) |
412 | 0 | || (rd < 0 && get_last_socket_error_is_eintr())); |
413 | |
|
414 | 0 | if (rd < 0 && !BIO_fd_non_fatal_error(get_last_socket_error())) |
415 | 0 | return 0; |
416 | | |
417 | 0 | return 1; |
418 | 0 | } |