| 1 | // Copyright (C) 2022 The Qt Company Ltd. |
| 2 | // Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. |
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 4 | |
| 5 | /*! |
| 6 | \class QSslServer |
| 7 | |
| 8 | \ingroup network |
| 9 | \ingroup ssl |
| 10 | \inmodule QtNetwork |
| 11 | \since 6.4 |
| 12 | |
| 13 | \brief Implements an encrypted, secure TCP server over TLS. |
| 14 | |
| 15 | Class to use in place of QTcpServer to implement TCP server using |
| 16 | Transport Layer Security (TLS). |
| 17 | |
| 18 | To configure the secure handshake settings, use the applicable setter |
| 19 | functions on a QSslConfiguration object, and then use it as an argument |
| 20 | to the setSslConfiguration() function. All following incoming |
| 21 | connections handled will use these settings. |
| 22 | |
| 23 | To start listening to incoming connections use the listen() function |
| 24 | inherited from QTcpServer. Other settings can be configured by using the |
| 25 | setter functions inherited from the QTcpServer class. |
| 26 | |
| 27 | Connect to the signals of this class to respond to the incoming connection |
| 28 | attempts. They are the same as the signals on QSslSocket, but also |
| 29 | passes a pointer to the socket in question. |
| 30 | |
| 31 | When responding to the pendingConnectionAvailable() signal, use the |
| 32 | nextPendingConnection() function to fetch the next incoming connection and |
| 33 | take it out of the pending connection queue. The QSslSocket is a child of |
| 34 | the QSslServer and will be deleted when the QSslServer is deleted. It is |
| 35 | still a good idea to destroy the object explicitly when you are done |
| 36 | with it, to avoid wasting memory. |
| 37 | |
| 38 | \sa QTcpServer, QSslConfiguration, QSslSocket |
| 39 | */ |
| 40 | |
| 41 | /*! |
| 42 | \fn void QSslServer::peerVerifyError(QSslSocket *socket, const QSslError &error) |
| 43 | |
| 44 | QSslServer can emit this signal several times during the SSL handshake, |
| 45 | before encryption has been established, to indicate that an error has |
| 46 | occurred while establishing the identity of the peer. The \a error is |
| 47 | usually an indication that \a socket is unable to securely identify the |
| 48 | peer. |
| 49 | |
| 50 | This signal provides you with an early indication when something's wrong. |
| 51 | By connecting to this signal, you can manually choose to tear down the |
| 52 | connection from inside the connected slot before the handshake has |
| 53 | completed. If no action is taken, QSslServer will proceed to emitting |
| 54 | sslErrors(). |
| 55 | |
| 56 | \sa sslErrors() |
| 57 | */ |
| 58 | |
| 59 | /*! |
| 60 | \fn void QSslServer::sslErrors(QSslSocket *socket, const QList<QSslError> &errors); |
| 61 | |
| 62 | QSslServer emits this signal after the SSL handshake to indicate that one |
| 63 | or more errors have occurred while establishing the identity of the |
| 64 | peer. The errors are usually an indication that \a socket is unable to |
| 65 | securely identify the peer. Unless any action is taken, the connection |
| 66 | will be dropped after this signal has been emitted. |
| 67 | |
| 68 | If you want to continue connecting despite the errors that have occurred, |
| 69 | you must call QSslSocket::ignoreSslErrors() from inside a slot connected to |
| 70 | this signal. If you need to access the error list at a later point, you |
| 71 | can call sslHandshakeErrors(). |
| 72 | |
| 73 | \a errors contains one or more errors that prevent QSslSocket from |
| 74 | verifying the identity of the peer. |
| 75 | |
| 76 | \note You cannot use Qt::QueuedConnection when connecting to this signal, |
| 77 | or calling QSslSocket::ignoreSslErrors() will have no effect. |
| 78 | |
| 79 | \sa peerVerifyError() |
| 80 | */ |
| 81 | |
| 82 | /*! |
| 83 | \fn void QSslServer::errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError socketError) |
| 84 | |
| 85 | This signal is emitted after an error occurred during handshake. The |
| 86 | \a socketError parameter describes the type of error that occurred. |
| 87 | |
| 88 | The \a socket is automatically deleted after this signal is emitted if the |
| 89 | socket handshake has not reached encrypted state. But if the \a socket is |
| 90 | successfully encrypted, it is inserted into the QSslServer's pending |
| 91 | connections queue. When the user has called |
| 92 | QTcpServer::nextPendingConnection() it is the user's responsibility to |
| 93 | destroy the \a socket or the \a socket will not be destroyed until the |
| 94 | QSslServer object is destroyed. If an error occurs on a \a socket after |
| 95 | it has been inserted into the pending connections queue, this signal |
| 96 | will not be emitted, and the \a socket will not be removed or destroyed. |
| 97 | |
| 98 | \note You cannot use Qt::QueuedConnection when connecting to this signal, |
| 99 | or the \a socket will have been already destroyed when the signal is |
| 100 | handled. |
| 101 | |
| 102 | \sa QSslSocket::error(), errorString() |
| 103 | */ |
| 104 | |
| 105 | /*! |
| 106 | \fn void QSslServer::preSharedKeyAuthenticationRequired(QSslSocket *socket, |
| 107 | QSslPreSharedKeyAuthenticator *authenticator) |
| 108 | |
| 109 | QSslServer emits this signal when \a socket negotiates a PSK ciphersuite, |
| 110 | and therefore PSK authentication is then required. |
| 111 | |
| 112 | When using PSK, the server must supply a valid identity and a valid pre |
| 113 | shared key, in order for the SSL handshake to continue. |
| 114 | Applications can provide this information in a slot connected to this |
| 115 | signal, by filling in the passed \a authenticator object according to their |
| 116 | needs. |
| 117 | |
| 118 | \note Ignoring this signal, or failing to provide the required credentials, |
| 119 | will cause the handshake to fail, and therefore the connection to be aborted. |
| 120 | |
| 121 | \note The \a authenticator object is owned by the \a socket and must not be |
| 122 | deleted by the application. |
| 123 | |
| 124 | \sa QSslPreSharedKeyAuthenticator |
| 125 | */ |
| 126 | |
| 127 | /*! |
| 128 | \fn void QSslServer::alertSent(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType type, |
| 129 | const QString &description) |
| 130 | |
| 131 | QSslServer emits this signal if an alert message was sent from \a socket |
| 132 | to a peer. \a level describes if it was a warning or a fatal error. |
| 133 | \a type gives the code of the alert message. When a textual description |
| 134 | of the alert message is available, it is supplied in \a description. |
| 135 | |
| 136 | \note This signal is mostly informational and can be used for debugging |
| 137 | purposes, normally it does not require any actions from the application. |
| 138 | \note Not all backends support this functionality. |
| 139 | |
| 140 | \sa alertReceived(), QSsl::AlertLevel, QSsl::AlertType |
| 141 | */ |
| 142 | |
| 143 | /*! |
| 144 | \fn void QSslServer::alertReceived(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType |
| 145 | type, const QString &description) |
| 146 | |
| 147 | QSslServer emits this signal if an alert message was received by the |
| 148 | \a socket from a peer. \a level tells if the alert was fatal or it was a |
| 149 | warning. \a type is the code explaining why the alert was sent. |
| 150 | When a textual description of the alert message is available, it is |
| 151 | supplied in \a description. |
| 152 | |
| 153 | \note The signal is mostly for informational and debugging purposes and does not |
| 154 | require any handling in the application. If the alert was fatal, underlying |
| 155 | backend will handle it and close the connection. |
| 156 | \note Not all backends support this functionality. |
| 157 | |
| 158 | \sa alertSent(), QSsl::AlertLevel, QSsl::AlertType |
| 159 | */ |
| 160 | |
| 161 | /*! |
| 162 | \fn void QSslServer::handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error) |
| 163 | |
| 164 | QSslServer emits this signal if a certificate verification error was found |
| 165 | by \a socket and if early error reporting was enabled in QSslConfiguration. |
| 166 | An application is expected to inspect the \a error and decide if it wants |
| 167 | to continue the handshake, or abort it and send an alert message to the |
| 168 | peer. The signal-slot connection must be direct. |
| 169 | |
| 170 | \sa QSslSocket::continueInterruptedHandshake(), sslErrors(), |
| 171 | QSslConfiguration::setHandshakeMustInterruptOnError() |
| 172 | */ |
| 173 | |
| 174 | /*! |
| 175 | \fn void QSslServer::startedEncryptionHandshake(QSslSocket *socket) |
| 176 | |
| 177 | This signal is emitted when the client, connected to \a socket, |
| 178 | initiates the TLS handshake. |
| 179 | */ |
| 180 | |
| 181 | #include "qsslserver.h" |
| 182 | #include "qsslserver_p.h" |
| 183 | |
| 184 | #include <QtNetwork/QSslSocket> |
| 185 | #include <QtNetwork/QSslCipher> |
| 186 | |
| 187 | QT_BEGIN_NAMESPACE |
| 188 | |
| 189 | /*! |
| 190 | \internal |
| 191 | */ |
| 192 | QSslServerPrivate::QSslServerPrivate() : |
| 193 | sslConfiguration(QSslConfiguration::defaultConfiguration()) |
| 194 | { |
| 195 | } |
| 196 | |
| 197 | /*! |
| 198 | Constructs a new QSslServer with the given \a parent. |
| 199 | */ |
| 200 | QSslServer::QSslServer(QObject *parent) : |
| 201 | QTcpServer(QAbstractSocket::TcpSocket, *new QSslServerPrivate, parent) |
| 202 | { |
| 203 | } |
| 204 | |
| 205 | /*! |
| 206 | Destroys the QSslServer. |
| 207 | |
| 208 | All open connections are closed. |
| 209 | */ |
| 210 | QSslServer::~QSslServer() |
| 211 | { |
| 212 | } |
| 213 | |
| 214 | /*! |
| 215 | Sets the \a sslConfiguration to use for all following incoming connections. |
| 216 | |
| 217 | This must be called before listen() to ensure that the desired |
| 218 | configuration was in use during all handshakes. |
| 219 | |
| 220 | \sa QSslSocket::setSslConfiguration() |
| 221 | */ |
| 222 | void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration) |
| 223 | { |
| 224 | Q_D(QSslServer); |
| 225 | d->sslConfiguration = sslConfiguration; |
| 226 | } |
| 227 | |
| 228 | /*! |
| 229 | Returns the current ssl configuration. |
| 230 | */ |
| 231 | QSslConfiguration QSslServer::sslConfiguration() const |
| 232 | { |
| 233 | const Q_D(QSslServer); |
| 234 | return d->sslConfiguration; |
| 235 | } |
| 236 | |
| 237 | /*! |
| 238 | Sets the \a timeout to use for all incoming handshakes, in milliseconds. |
| 239 | |
| 240 | This is relevant in the scenario where a client, whether malicious or |
| 241 | accidental, connects to the server but makes no attempt at communicating or |
| 242 | initiating a handshake. QSslServer will then automatically end the |
| 243 | connection after \a timeout milliseconds have elapsed. |
| 244 | |
| 245 | By default the timeout is 5000 milliseconds (5 seconds). |
| 246 | |
| 247 | \note The underlying TLS framework may have their own timeout logic now or |
| 248 | in the future, this function does not affect that. |
| 249 | |
| 250 | \note The \a timeout passed to this function will only apply to \e{new} |
| 251 | connections. If a client is already connected it will use the timeout which |
| 252 | was set when it connected. |
| 253 | |
| 254 | \sa handshakeTimeout() |
| 255 | */ |
| 256 | void QSslServer::setHandshakeTimeout(int timeout) |
| 257 | { |
| 258 | Q_D(QSslServer); |
| 259 | d->handshakeTimeout = timeout; |
| 260 | } |
| 261 | |
| 262 | /*! |
| 263 | Returns the currently configured handshake timeout. |
| 264 | |
| 265 | \sa setHandshakeTimeout() |
| 266 | */ |
| 267 | int QSslServer::handshakeTimeout() const |
| 268 | { |
| 269 | const Q_D(QSslServer); |
| 270 | return d->handshakeTimeout; |
| 271 | } |
| 272 | |
| 273 | /*! |
| 274 | Called when a new connection is established. |
| 275 | |
| 276 | Converts \a socket to a QSslSocket. |
| 277 | |
| 278 | \reimp |
| 279 | */ |
| 280 | void QSslServer::incomingConnection(qintptr socket) |
| 281 | { |
| 282 | QSslSocket *pSslSocket = new QSslSocket(this); |
| 283 | |
| 284 | pSslSocket->setSslConfiguration(sslConfiguration()); |
| 285 | |
| 286 | if (Q_LIKELY(pSslSocket->setSocketDescriptor(socket))) { |
| 287 | connect(sender: pSslSocket, signal: &QSslSocket::peerVerifyError, context: this, |
| 288 | slot: [this, pSslSocket](const QSslError &error) { |
| 289 | Q_EMIT peerVerifyError(socket: pSslSocket, error); |
| 290 | }); |
| 291 | connect(sender: pSslSocket, signal: &QSslSocket::sslErrors, context: this, |
| 292 | slot: [this, pSslSocket](const QList<QSslError> &errors) { |
| 293 | Q_EMIT sslErrors(socket: pSslSocket, errors); |
| 294 | }); |
| 295 | connect(sender: pSslSocket, signal: &QAbstractSocket::errorOccurred, context: this, |
| 296 | slot: [this, pSslSocket](QAbstractSocket::SocketError error) { |
| 297 | Q_EMIT errorOccurred(socket: pSslSocket, error); |
| 298 | if (!pSslSocket->isEncrypted()) |
| 299 | pSslSocket->deleteLater(); |
| 300 | }); |
| 301 | connect(sender: pSslSocket, signal: &QSslSocket::encrypted, context: this, slot: [this, pSslSocket]() { |
| 302 | Q_D(QSslServer); |
| 303 | d->removeSocketData(socket: quintptr(pSslSocket)); |
| 304 | pSslSocket->disconnect(receiver: this); |
| 305 | addPendingConnection(socket: pSslSocket); |
| 306 | }); |
| 307 | connect(sender: pSslSocket, signal: &QSslSocket::preSharedKeyAuthenticationRequired, context: this, |
| 308 | slot: [this, pSslSocket](QSslPreSharedKeyAuthenticator *authenticator) { |
| 309 | Q_EMIT preSharedKeyAuthenticationRequired(socket: pSslSocket, authenticator); |
| 310 | }); |
| 311 | connect(sender: pSslSocket, signal: &QSslSocket::alertSent, context: this, |
| 312 | slot: [this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type, |
| 313 | const QString &description) { |
| 314 | Q_EMIT alertSent(socket: pSslSocket, level, type, description); |
| 315 | }); |
| 316 | connect(sender: pSslSocket, signal: &QSslSocket::alertReceived, context: this, |
| 317 | slot: [this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type, |
| 318 | const QString &description) { |
| 319 | Q_EMIT alertReceived(socket: pSslSocket, level, type, description); |
| 320 | }); |
| 321 | connect(sender: pSslSocket, signal: &QSslSocket::handshakeInterruptedOnError, context: this, |
| 322 | slot: [this, pSslSocket](const QSslError &error) { |
| 323 | Q_EMIT handshakeInterruptedOnError(socket: pSslSocket, error); |
| 324 | }); |
| 325 | |
| 326 | d_func()->initializeHandshakeProcess(socket: pSslSocket); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | void QSslServerPrivate::initializeHandshakeProcess(QSslSocket *socket) |
| 331 | { |
| 332 | Q_Q(QSslServer); |
| 333 | QMetaObject::Connection readyRead = QObject::connect( |
| 334 | sender: socket, signal: &QSslSocket::readyRead, context: q, slot: [this]() { checkClientHelloAndContinue(); }); |
| 335 | |
| 336 | QMetaObject::Connection destroyed = |
| 337 | QObject::connect(sender: socket, signal: &QSslSocket::destroyed, context: q, slot: [this](QObject *obj) { |
| 338 | // This cast is not safe to use since the socket is inside the |
| 339 | // QObject dtor, but we only use the pointer value! |
| 340 | removeSocketData(socket: quintptr(obj)); |
| 341 | }); |
| 342 | auto it = socketData.emplace(key: quintptr(socket), args&: readyRead, args&: destroyed, args: std::make_shared<QTimer>()); |
| 343 | it->timeoutTimer->setSingleShot(true); |
| 344 | it->timeoutTimer->callOnTimeout(args: q, args: [this, socket]() { handleHandshakeTimedOut(socket); }); |
| 345 | it->timeoutTimer->setInterval(handshakeTimeout); |
| 346 | it->timeoutTimer->start(); |
| 347 | } |
| 348 | |
| 349 | // This function may be called while in the socket's QObject dtor, __never__ use |
| 350 | // the socket for anything other than a lookup! |
| 351 | void QSslServerPrivate::removeSocketData(quintptr socket) |
| 352 | { |
| 353 | auto it = socketData.find(key: socket); |
| 354 | if (it != socketData.end()) { |
| 355 | it->disconnectSignals(); |
| 356 | socketData.erase(it); |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | int QSslServerPrivate::totalPendingConnections() const |
| 361 | { |
| 362 | // max pending connections is int, so this cannot exceed that |
| 363 | return QTcpServerPrivate::totalPendingConnections() + int(socketData.size()); |
| 364 | } |
| 365 | |
| 366 | void QSslServerPrivate::checkClientHelloAndContinue() |
| 367 | { |
| 368 | Q_Q(QSslServer); |
| 369 | QSslSocket *socket = qobject_cast<QSslSocket *>(object: q->sender()); |
| 370 | if (Q_UNLIKELY(!socket) || socket->bytesAvailable() <= 0) |
| 371 | return; |
| 372 | |
| 373 | char byte = '\0'; |
| 374 | if (socket->peek(data: &byte, maxlen: 1) != 1) { |
| 375 | socket->deleteLater(); |
| 376 | return; |
| 377 | } |
| 378 | |
| 379 | auto it = socketData.find(key: quintptr(socket)); |
| 380 | const bool foundData = it != socketData.end(); |
| 381 | if (foundData && it->readyReadConnection) |
| 382 | QObject::disconnect(std::exchange(obj&: it->readyReadConnection, new_val: {})); |
| 383 | |
| 384 | constexpr char CLIENT_HELLO = 0x16; |
| 385 | if (byte != CLIENT_HELLO) { |
| 386 | socket->disconnectFromHost(); |
| 387 | socket->deleteLater(); |
| 388 | return; |
| 389 | } |
| 390 | |
| 391 | // Be nice and restart the timeout timer since some progress was made |
| 392 | if (foundData) |
| 393 | it->timeoutTimer->start(); |
| 394 | |
| 395 | socket->startServerEncryption(); |
| 396 | Q_EMIT q->startedEncryptionHandshake(socket); |
| 397 | } |
| 398 | |
| 399 | void QSslServerPrivate::handleHandshakeTimedOut(QSslSocket *socket) |
| 400 | { |
| 401 | Q_Q(QSslServer); |
| 402 | removeSocketData(socket: quintptr(socket)); |
| 403 | socket->disconnectFromHost(); |
| 404 | Q_EMIT q->errorOccurred(socket, error: QAbstractSocket::SocketTimeoutError); |
| 405 | socket->deleteLater(); |
| 406 | if (!socketEngine->isReadNotificationEnabled() && totalPendingConnections() < maxConnections) |
| 407 | q->resumeAccepting(); |
| 408 | } |
| 409 | |
| 410 | QT_END_NAMESPACE |
| 411 | |
| 412 | #include "moc_qsslserver.cpp" |
| 413 | |