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 */