Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
coreauthhandler.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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 
30 using namespace Protocol;
31 
32 CoreAuthHandler::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 
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;
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))
77  if (features & 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
97  else
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 
114  startSsl(); // legacy peer enables it later
115  return;
116  }
117  }
118 }
119 
120 
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
130 void 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 
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 
154 {
155  bool useSsl;
156  if (_legacy)
157  useSsl = Core::sslSupported() && msg.sslSupported;
158  else
160 
161  if (Quassel::isOptionSet("require-ssl") && !useSsl && !_peer->isLocal()) {
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  int uptime = Core::instance()->startTime().secsTo(QDateTime::currentDateTime().toUTC());
173  int updays = uptime / 86400; uptime %= 86400;
174  int uphours = uptime / 3600; uptime %= 3600;
175  int upmins = uptime / 60;
176  QString coreInfo = tr("<b>Quassel Core Version %1</b><br>"
177  "Built: %2<br>"
178  "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString)
179  .arg(Quassel::buildInfo().buildDate)
180  .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(Core::instance()->startTime().toString(Qt::TextDate));
181 
182  // useSsl and coreInfo are only used for the legacy protocol
183  _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, coreInfo));
184 
185  if (_legacy && useSsl)
186  startSsl();
187 
188  _clientRegistered = true;
189 }
190 
191 
193 {
194  if (!checkClientRegistered())
195  return;
196 
197  QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData);
198  if (!result.isEmpty())
199  _peer->dispatch(SetupFailed(result));
200  else
202 }
203 
204 
206 {
207  if (!checkClientRegistered())
208  return;
209 
210  UserId uid = Core::validateUser(msg.user, msg.password);
211  if (uid == 0) {
212  _peer->dispatch(LoginFailed(tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
213  return;
214  }
216 
217  quInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).").arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
218 
219  disconnect(socket(), 0, this, 0);
220  disconnect(_peer, 0, this, 0);
221  _peer->setParent(0); // Core needs to take care of this one now!
222 
223  socket()->flush(); // Make sure all data is sent before handing over the peer (and socket) to the session thread (bug 682)
224  emit handshakeComplete(_peer, uid);
225 }
226 
227 
228 /*** SSL Stuff ***/
229 
231 {
232  #ifdef HAVE_SSL
233  QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
234  Q_ASSERT(sslSocket);
235 
236  qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description();
237  connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(onSslErrors()));
238  sslSocket->flush(); // ensure that the write cache is flushed before we switch to ssl (bug 682)
239  sslSocket->startServerEncryption();
240  #endif /* HAVE_SSL */
241 }
242 
243 
244 #ifdef HAVE_SSL
245 void CoreAuthHandler::onSslErrors()
246 {
247  QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
248  Q_ASSERT(sslSocket);
249  sslSocket->ignoreSslErrors();
250 }
251 #endif
252