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: [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 | |