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