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