libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

connection_https.c (6934B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2008, 2010 Daniel Pittman and Christian Grothoff
      4      Copyright (C) 2015-2022 Karlson2k (Evgeny Grin)
      5 
      6      This library is free software; you can redistribute it and/or
      7      modify it under the terms of the GNU Lesser General Public
      8      License as published by the Free Software Foundation; either
      9      version 2.1 of the License, or (at your option) any later version.
     10 
     11      This library is distributed in the hope that it will be useful,
     12      but WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      Lesser General Public License for more details.
     15 
     16      You should have received a copy of the GNU Lesser General Public
     17      License along with this library; if not, write to the Free Software
     18      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19 
     20 */
     21 
     22 /**
     23  * @file connection_https.c
     24  * @brief  Methods for managing SSL/TLS connections. This file is only
     25  *         compiled if ENABLE_HTTPS is set.
     26  * @author Sagie Amir
     27  * @author Christian Grothoff
     28  * @author Karlson2k (Evgeny Grin)
     29  */
     30 
     31 #include "internal.h"
     32 #include "connection.h"
     33 #include "connection_https.h"
     34 #include "memorypool.h"
     35 #include "response.h"
     36 #include "mhd_mono_clock.h"
     37 #include <gnutls/gnutls.h>
     38 #include "mhd_send.h"
     39 
     40 
     41 /**
     42  * Callback for receiving data from the socket.
     43  *
     44  * @param connection the MHD_Connection structure
     45  * @param other where to write received data to
     46  * @param i maximum size of other (in bytes)
     47  * @return positive value for number of bytes actually received or
     48  *         negative value for error number MHD_ERR_xxx_
     49  */
     50 static ssize_t
     51 recv_tls_adapter (struct MHD_Connection *connection,
     52                   void *other,
     53                   size_t i)
     54 {
     55   ssize_t res;
     56 
     57   if (i > SSIZE_MAX)
     58     i = SSIZE_MAX;
     59 
     60   res = gnutls_record_recv (connection->tls_session,
     61                             other,
     62                             i);
     63   if ( (GNUTLS_E_AGAIN == res) ||
     64        (GNUTLS_E_INTERRUPTED == res) )
     65   {
     66 #ifdef EPOLL_SUPPORT
     67     if (GNUTLS_E_AGAIN == res)
     68       connection->epoll_state &=
     69         ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
     70 #endif
     71     /* Any network errors means that buffer is empty. */
     72     connection->tls_read_ready = false;
     73     return MHD_ERR_AGAIN_;
     74   }
     75   if (res < 0)
     76   {
     77     connection->tls_read_ready = false;
     78     if ( (GNUTLS_E_DECRYPTION_FAILED == res) ||
     79          (GNUTLS_E_INVALID_SESSION == res) ||
     80          (GNUTLS_E_DECOMPRESSION_FAILED == res) ||
     81          (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER == res) ||
     82          (GNUTLS_E_UNSUPPORTED_VERSION_PACKET == res) ||
     83          (GNUTLS_E_UNEXPECTED_PACKET_LENGTH == res) ||
     84          (GNUTLS_E_UNEXPECTED_PACKET == res) ||
     85          (GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET == res) ||
     86          (GNUTLS_E_EXPIRED == res) ||
     87          (GNUTLS_E_REHANDSHAKE == res) )
     88       return MHD_ERR_TLS_;
     89     if ( (GNUTLS_E_PULL_ERROR == res) ||
     90          (GNUTLS_E_INTERNAL_ERROR == res) ||
     91          (GNUTLS_E_CRYPTODEV_IOCTL_ERROR == res) ||
     92          (GNUTLS_E_CRYPTODEV_DEVICE_ERROR == res) )
     93       return MHD_ERR_PIPE_;
     94 #if defined(GNUTLS_E_PREMATURE_TERMINATION)
     95     if (GNUTLS_E_PREMATURE_TERMINATION == res)
     96       return MHD_ERR_CONNRESET_;
     97 #elif defined(GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
     98     if (GNUTLS_E_UNEXPECTED_PACKET_LENGTH == res)
     99       return MHD_ERR_CONNRESET_;
    100 #endif /* GNUTLS_E_UNEXPECTED_PACKET_LENGTH */
    101     if (GNUTLS_E_MEMORY_ERROR == res)
    102       return MHD_ERR_NOMEM_;
    103     /* Treat any other error as a hard error. */
    104     return MHD_ERR_NOTCONN_;
    105   }
    106 
    107 #ifdef EPOLL_SUPPORT
    108   /* Unlike non-TLS connections, do not reset "read-ready" if
    109    * received amount smaller than provided amount, as TLS
    110    * connections may receive data by fixed-size chunks. */
    111 #endif /* EPOLL_SUPPORT */
    112 
    113   /* Check whether TLS buffers still have some unread data. */
    114   connection->tls_read_ready =
    115     ( ((size_t) res == i) &&
    116       (0 != gnutls_record_check_pending (connection->tls_session)) );
    117   return res;
    118 }
    119 
    120 
    121 /**
    122  * Give gnuTLS chance to work on the TLS handshake.
    123  *
    124  * @param connection connection to handshake on
    125  * @return true if the handshake has completed successfully
    126  *         and we should start to read/write data,
    127  *         false is handshake in progress or in case
    128  *         of error
    129  */
    130 bool
    131 MHD_run_tls_handshake_ (struct MHD_Connection *connection)
    132 {
    133   int ret;
    134 
    135   if ((MHD_TLS_CONN_INIT == connection->tls_state) ||
    136       (MHD_TLS_CONN_HANDSHAKING == connection->tls_state))
    137   {
    138 #if 0
    139     /* According to real-live testing, Nagel's Algorithm is not blocking
    140      * partial packets on just connected sockets on modern OSes. As TLS setup
    141      * is performed as the fist action upon socket connection, the next
    142      * optimisation typically is not required. If any specific OS will
    143      * require this optimization, it could be enabled by allowing the next
    144      * lines for this specific OS. */
    145     if (_MHD_ON != connection->sk_nodelay)
    146       MHD_connection_set_nodelay_state_ (connection, true);
    147 #endif
    148     ret = gnutls_handshake (connection->tls_session);
    149     if (ret == GNUTLS_E_SUCCESS)
    150     {
    151       /* set connection TLS state to enable HTTP processing */
    152       connection->tls_state = MHD_TLS_CONN_CONNECTED;
    153       MHD_update_last_activity_ (connection);
    154       return true;
    155     }
    156     if ( (GNUTLS_E_AGAIN == ret) ||
    157          (GNUTLS_E_INTERRUPTED == ret) )
    158     {
    159       connection->tls_state = MHD_TLS_CONN_HANDSHAKING;
    160       /* handshake not done */
    161       return false;
    162     }
    163     /* handshake failed */
    164     connection->tls_state = MHD_TLS_CONN_TLS_FAILED;
    165 #ifdef HAVE_MESSAGES
    166     MHD_DLOG (connection->daemon,
    167               _ ("Error: received handshake message out of context.\n"));
    168 #endif
    169     MHD_connection_close_ (connection,
    170                            MHD_REQUEST_TERMINATED_WITH_ERROR);
    171     return false;
    172   }
    173   return true;
    174 }
    175 
    176 
    177 /**
    178  * Set connection callback function to be used through out
    179  * the processing of this secure connection.
    180  *
    181  * @param connection which callbacks should be modified
    182  */
    183 void
    184 MHD_set_https_callbacks (struct MHD_Connection *connection)
    185 {
    186   connection->recv_cls = &recv_tls_adapter;
    187 }
    188 
    189 
    190 /**
    191  * Initiate shutdown of TLS layer of connection.
    192  *
    193  * @param connection to use
    194  * @return true if succeed, false otherwise.
    195  */
    196 bool
    197 MHD_tls_connection_shutdown (struct MHD_Connection *connection)
    198 {
    199   if (MHD_TLS_CONN_WR_CLOSED > connection->tls_state)
    200   {
    201     const int res =
    202       gnutls_bye (connection->tls_session, GNUTLS_SHUT_WR);
    203     if (GNUTLS_E_SUCCESS == res)
    204     {
    205       connection->tls_state = MHD_TLS_CONN_WR_CLOSED;
    206       return true;
    207     }
    208     if ((GNUTLS_E_AGAIN == res) ||
    209         (GNUTLS_E_INTERRUPTED == res))
    210     {
    211       connection->tls_state = MHD_TLS_CONN_WR_CLOSING;
    212       return true;
    213     }
    214     else
    215       connection->tls_state = MHD_TLS_CONN_TLS_FAILED;
    216   }
    217   return false;
    218 }
    219 
    220 
    221 /* end of connection_https.c */