summaryrefslogtreecommitdiffstats
path: root/src/server/net/sslserver.cpp
blob: 2dfa84cdb83c7dc2b0bf780a9f579fed42a960de (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*
 # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
 #
 # This program is free software distributed under the GPL version 2.
 # See http://openslx.org/COPYING
 #
 # If you have any feedback please consult http://openslx.org/feedback and
 # send your suggestions, praise, or complaints to feedback@openslx.org
 #
 # General information about OpenSLX can be found at http://openslx.org/
 # -----------------------------------------------------------------------------
 # src/net/SslServer.cpp
 #    - provide QTcpServer-like behaviour for SSL
 # -----------------------------------------------------------------------------
 */

#include "sslserver.h"
#include <QtNetwork/QSslCipher>
#include <QtNetwork/QSslSocket>
#include <QTimer>
#include "certmanager.h"
#include <unistd.h>

SslServer::SslServer(QObject *parent)
		: QTcpServer(parent)
		, _timer(new QTimer(this))
{
	connect(_timer, &QTimer::timeout, [=]() {
		if (_pending.empty())
			return;
		const qint64 deadline = QDateTime::currentMSecsSinceEpoch() - 10000;
		QMutableHashIterator<QSslSocket*, qint64> it(_pending);
		while (it.hasNext()) { // Remove lingering connections after 10s
			it.next();
			if (it.value() < deadline) {
				it.key()->blockSignals(true);
				it.key()->deleteLater();
				it.remove();
			}
		}
	});
	_timer->start(10000);
}

SslServer::~SslServer()
{
	_timer->stop();
	auto keys = _pending.keys();
	for (QSslSocket *sock : keys) {
		sock->deleteLater();
	}
	_pending.clear();
}

/**
 * Handle incomming connection.
 * @param socketDescriptor
 */
void SslServer::incomingConnection(qintptr socketDescriptor)
{
	static int certFails = 0;
	QSslKey key;
	QSslCertificate cert;
	if (!CertManager::getPrivateKeyAndCert("manager2", key, cert)) {
		if (++certFails > 5) {
			CertManager::fatal();
		}
		::close(int(socketDescriptor));
		return;
	}
	auto *serverSocket = new QSslSocket(nullptr);
	connect(serverSocket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &SslServer::sslErrors);
	serverSocket->setPrivateKey(key);
	serverSocket->setLocalCertificate(cert);
	serverSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
	serverSocket->setProtocol(QSsl::SecureProtocols);
	if (serverSocket->setSocketDescriptor(socketDescriptor)) {
		_pending.insert(serverSocket, QDateTime::currentMSecsSinceEpoch());
		// Once the connection is successfully encrypted, raise our newConnection event
		connect(serverSocket, &QSslSocket::encrypted, [=]() {
			serverSocket->disconnect(this);
			if (_pending.remove(serverSocket) == 0) {
				// Should never happen, so better log...
				qDebug() << "Encryption event for socket that is not pending!?";
			} else {
				this->addPendingConnection(serverSocket);
				emit newConnection();
			}
		});
		serverSocket->startServerEncryption();
	} else {
		qDebug() << "Failed to setSocketDescriptor on new SSL Socket";
		serverSocket->deleteLater();
	}
}

void SslServer::sslErrors(const QList<QSslError>& errors)
{
	qDebug() << "Client caused sslErrors before connection:";
	for (const auto & error : errors) {
		qDebug() << error.errorString();
	}

}