1
/***************************************************************************
2
* Copyright (C) 2005-2014 by the Quassel Project *
3
* devel@quassel-irc.org *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) version 3. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19
***************************************************************************/
21
#include "coreauthhandler.h"
24
# include <QSslSocket>
30
using namespace Protocol;
32
CoreAuthHandler::CoreAuthHandler(QTcpSocket *socket, QObject *parent)
33
: AuthHandler(parent),
35
_magicReceived(false),
37
_clientRegistered(false),
38
_connectionFeatures(0)
41
connect(socket, SIGNAL(readyRead()), SLOT(onReadyRead()));
43
// TODO: Timeout for the handshake phase
48
void CoreAuthHandler::onReadyRead()
50
if (socket()->bytesAvailable() < 4)
53
// once we have selected a peer, we certainly don't want to read more data!
57
if (!_magicReceived) {
59
socket()->peek((char*)&magic, 4);
60
magic = qFromBigEndian<quint32>(magic);
62
if ((magic & 0xffffff00) != Protocol::magic) {
63
// no magic, assume legacy protocol
64
qDebug() << "Legacy client detected, switching to compatibility mode";
66
RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(Protocol::LegacyProtocol, 0), this, socket(), this);
67
connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
72
_magicReceived = true;
73
quint8 features = magic & 0xff;
74
// figure out which connection features we'll use based on the client's support
75
if (Core::sslSupported() && (features & Protocol::Encryption))
76
_connectionFeatures |= Protocol::Encryption;
77
if (features & Protocol::Compression)
78
_connectionFeatures |= Protocol::Compression;
80
socket()->read((char*)&magic, 4); // read the 4 bytes we've just peeked at
83
// read the list of protocols supported by the client
84
while (socket()->bytesAvailable() >= 4) {
86
socket()->read((char*)&data, 4);
87
data = qFromBigEndian<quint32>(data);
89
Protocol::Type type = static_cast<Protocol::Type>(data & 0xff);
90
quint16 protoFeatures = static_cast<quint16>(data>>8 & 0xffff);
91
_supportedProtos.append(PeerFactory::ProtoDescriptor(type, protoFeatures));
93
if (data >= 0x80000000) { // last protocol
94
RemotePeer *peer = PeerFactory::createPeer(_supportedProtos, this, socket(), this);
95
if (peer->protocol() == Protocol::LegacyProtocol) {
97
connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
102
quint32 reply = peer->protocol() | peer->enabledFeatures()<<8 | _connectionFeatures<<24;
103
reply = qToBigEndian<quint32>(reply);
104
socket()->write((char*)&reply, 4);
107
if (!_legacy && (_connectionFeatures & Protocol::Encryption))
108
startSsl(); // legacy peer enables it later
115
void CoreAuthHandler::setPeer(RemotePeer *peer)
117
qDebug().nospace() << "Using " << qPrintable(peer->protocolName()) << "...";
120
disconnect(socket(), SIGNAL(readyRead()), this, SLOT(onReadyRead()));
123
// only in compat mode
124
void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected)
126
qWarning() << qPrintable(tr("Client")) << _peer->description() << qPrintable(tr("too old, rejecting."));
127
QString errorString = tr("<b>Your Quassel Client is too old!</b><br>"
128
"This core needs at least client/core protocol version %1 (got: %2).<br>"
129
"Please consider upgrading your client.").arg(expected, actual);
130
_peer->dispatch(ClientDenied(errorString));
135
bool CoreAuthHandler::checkClientRegistered()
137
if (!_clientRegistered) {
138
qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString()) << qPrintable(tr("did not send a registration message before trying to login, rejecting."));
139
_peer->dispatch(ClientDenied(tr("<b>Client not initialized!</b><br>You need to send a registration message before trying to login.")));
147
void CoreAuthHandler::handle(const RegisterClient &msg)
151
useSsl = Core::sslSupported() && msg.sslSupported;
153
useSsl = _connectionFeatures & Protocol::Encryption;
155
if (Quassel::isOptionSet("require-ssl") && !useSsl) {
156
_peer->dispatch(ClientDenied(tr("<b>SSL is required!</b><br>You need to use SSL in order to connect to this core.")));
161
QVariantList backends;
162
bool configured = Core::isConfigured();
164
backends = Core::backendInfo();
166
// useSsl and startTime are only used for the legacy protocol
167
_peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, Core::instance()->startTime()));
169
if (_legacy && useSsl)
172
_clientRegistered = true;
176
void CoreAuthHandler::handle(const SetupData &msg)
178
if (!checkClientRegistered())
181
QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData);
182
if (!result.isEmpty())
183
_peer->dispatch(SetupFailed(result));
185
_peer->dispatch(SetupDone());
189
void CoreAuthHandler::handle(const Login &msg)
191
if (!checkClientRegistered())
194
UserId uid = Core::validateUser(msg.user, msg.password);
196
_peer->dispatch(LoginFailed(tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
199
_peer->dispatch(LoginSuccess());
201
quInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).").arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
203
disconnect(socket(), 0, this, 0);
204
disconnect(_peer, 0, this, 0);
205
_peer->setParent(0); // Core needs to take care of this one now!
207
socket()->flush(); // Make sure all data is sent before handing over the peer (and socket) to the session thread (bug 682)
208
emit handshakeComplete(_peer, uid);
214
void CoreAuthHandler::startSsl()
217
QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
220
qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description();
221
connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(onSslErrors()));
222
sslSocket->flush(); // ensure that the write cache is flushed before we switch to ssl (bug 682)
223
sslSocket->startServerEncryption();
224
#endif /* HAVE_SSL */
229
void CoreAuthHandler::onSslErrors()
231
QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
233
sslSocket->ignoreSslErrors();