Home | History | Annotate | Line # | Download | only in rio
      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  */
     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 
     34 static void ossl_wsa_cleanup(void)
     35 {
     36     if (wsa_started) {
     37         wsa_started = 0;
     38         WSACleanup();
     39     }
     40 }
     41 
     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 
     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. */
     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      * suppression.  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 artificially create a loopback TCP connection and send bytes to it.
    146  */
    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 
    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 
    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 
    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 
    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