1/***************************************************************************
2 * Copyright (C) 2005-2014 by the Quassel Project *
3 * devel@quassel-irc.org *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
20
21#include "coreauthhandler.h"
22
23#ifdef HAVE_SSL
24# include <QSslSocket>
25#endif
26
27#include "core.h"
28#include "logger.h"
29
30using namespace Protocol;
31
32CoreAuthHandler::CoreAuthHandler(QTcpSocket *socket, QObject *parent)
33 : AuthHandler(parent),
34 _peer(0),
35 _magicReceived(false),
36 _legacy(false),
37 _clientRegistered(false),
38 _connectionFeatures(0)
39{
40 setSocket(socket);
41 connect(socket, SIGNAL(readyRead()), SLOT(onReadyRead()));
42
43 // TODO: Timeout for the handshake phase
44
45}
46
47
48void CoreAuthHandler::onReadyRead()
49{
50 if (socket()->bytesAvailable() < 4)
51 return;
52
53 // once we have selected a peer, we certainly don't want to read more data!
54 if (_peer)
55 return;
56
57 if (!_magicReceived) {
58 quint32 magic;
59 socket()->peek((char*)&magic, 4);
60 magic = qFromBigEndian<quint32>(magic);
61
62 if ((magic & 0xffffff00) != Protocol::magic) {
63 // no magic, assume legacy protocol
64 qDebug() << "Legacy client detected, switching to compatibility mode";
65 _legacy = true;
66 RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(Protocol::LegacyProtocol, 0), this, socket(), Compressor::NoCompression, this);
67 connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
68 setPeer(peer);
69 return;
70 }
71
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;
79
80 socket()->read((char*)&magic, 4); // read the 4 bytes we've just peeked at
81 }
82
83 // read the list of protocols supported by the client
84 while (socket()->bytesAvailable() >= 4) {
85 quint32 data;
86 socket()->read((char*)&data, 4);
87 data = qFromBigEndian<quint32>(data);
88
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));
92
93 if (data >= 0x80000000) { // last protocol
94 Compressor::CompressionLevel level;
95 if (_connectionFeatures & Protocol::Compression)
96 level = Compressor::BestCompression;
97 else
98 level = Compressor::NoCompression;
99
100 RemotePeer *peer = PeerFactory::createPeer(_supportedProtos, this, socket(), level, this);
101 if (peer->protocol() == Protocol::LegacyProtocol) {
102 _legacy = true;
103 connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
104 }
105 setPeer(peer);
106
107 // inform the client
108 quint32 reply = peer->protocol() | peer->enabledFeatures()<<8 | _connectionFeatures<<24;
109 reply = qToBigEndian<quint32>(reply);
110 socket()->write((char*)&reply, 4);
111 socket()->flush();
112
113 if (!_legacy && (_connectionFeatures & Protocol::Encryption))
114 startSsl(); // legacy peer enables it later
115 return;
116 }
117 }
118}
119
120
121void CoreAuthHandler::setPeer(RemotePeer *peer)
122{
123 qDebug().nospace() << "Using " << qPrintable(peer->protocolName()) << "...";
124
125 _peer = peer;
126 disconnect(socket(), SIGNAL(readyRead()), this, SLOT(onReadyRead()));
127}
128
129// only in compat mode
130void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected)
131{
132 qWarning() << qPrintable(tr("Client")) << _peer->description() << qPrintable(tr("too old, rejecting."));
133 QString errorString = tr("<b>Your Quassel Client is too old!</b><br>"
134 "This core needs at least client/core protocol version %1 (got: %2).<br>"
135 "Please consider upgrading your client.").arg(expected, actual);
136 _peer->dispatch(ClientDenied(errorString));
137 _peer->close();
138}
139
140
141bool CoreAuthHandler::checkClientRegistered()
142{
143 if (!_clientRegistered) {
144 qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString()) << qPrintable(tr("did not send a registration message before trying to login, rejecting."));
145 _peer->dispatch(ClientDenied(tr("<b>Client not initialized!</b><br>You need to send a registration message before trying to login.")));
146 _peer->close();
147 return false;
148 }
149 return true;
150}
151
152
153void CoreAuthHandler::handle(const RegisterClient &msg)
154{
155 bool useSsl;
156 if (_legacy)
157 useSsl = Core::sslSupported() && msg.sslSupported;
158 else
159 useSsl = _connectionFeatures & Protocol::Encryption;
160
161 if (Quassel::isOptionSet("require-ssl") && !useSsl) {
162 _peer->dispatch(ClientDenied(tr("<b>SSL is required!</b><br>You need to use SSL in order to connect to this core.")));
163 _peer->close();
164 return;
165 }
166
167 QVariantList backends;
168 bool configured = Core::isConfigured();
169 if (!configured)
170 backends = Core::backendInfo();
171
172 // useSsl and startTime are only used for the legacy protocol
173 _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, Core::instance()->startTime()));
174
175 if (_legacy && useSsl)
176 startSsl();
177
178 _clientRegistered = true;
179}
180
181
182void CoreAuthHandler::handle(const SetupData &msg)
183{
184 if (!checkClientRegistered())
185 return;
186
187 QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData);
188 if (!result.isEmpty())
189 _peer->dispatch(SetupFailed(result));
190 else
191 _peer->dispatch(SetupDone());
192}
193
194
195void CoreAuthHandler::handle(const Login &msg)
196{
197 if (!checkClientRegistered())
198 return;
199
200 UserId uid = Core::validateUser(msg.user, msg.password);
201 if (uid == 0) {
202 _peer->dispatch(LoginFailed(tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
203 return;
204 }
205 _peer->dispatch(LoginSuccess());
206
207 quInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).").arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
208
209 disconnect(socket(), 0, this, 0);
210 disconnect(_peer, 0, this, 0);
211 _peer->setParent(0); // Core needs to take care of this one now!
212
213 socket()->flush(); // Make sure all data is sent before handing over the peer (and socket) to the session thread (bug 682)
214 emit handshakeComplete(_peer, uid);
215}
216
217
218/*** SSL Stuff ***/
219
220void CoreAuthHandler::startSsl()
221{
222 #ifdef HAVE_SSL
223 QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
224 Q_ASSERT(sslSocket);
225
226 qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description();
227 connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(onSslErrors()));
228 sslSocket->flush(); // ensure that the write cache is flushed before we switch to ssl (bug 682)
229 sslSocket->startServerEncryption();
230 #endif /* HAVE_SSL */
231}
232
233
234#ifdef HAVE_SSL
235void CoreAuthHandler::onSslErrors()
236{
237 QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
238 Q_ASSERT(sslSocket);
239 sslSocket->ignoreSslErrors();
240}
241#endif
242
243