X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcoreauthhandler.cpp;h=34fc22276ebc5584e4a3c9e04301feb807956762;hp=4b2824f56a7bccea089da3c5c813041ad23b7e40;hb=e67887343c433cc35bc26ad6a9392588f427e746;hpb=4c4aec75c42dc212b6725cf1cb42ab61fb09e8fd diff --git a/src/core/coreauthhandler.cpp b/src/core/coreauthhandler.cpp index 4b2824f5..34fc2227 100644 --- a/src/core/coreauthhandler.cpp +++ b/src/core/coreauthhandler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2013 by the Quassel Project * + * Copyright (C) 2005-2015 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -27,68 +27,128 @@ #include "core.h" #include "logger.h" -#include "protocols/legacy/legacypeer.h" - using namespace Protocol; CoreAuthHandler::CoreAuthHandler(QTcpSocket *socket, QObject *parent) - : AuthHandler(parent) - , _peer(0) - , _clientRegistered(false) + : AuthHandler(parent), + _peer(0), + _magicReceived(false), + _legacy(false), + _clientRegistered(false), + _connectionFeatures(0) { setSocket(socket); + connect(socket, SIGNAL(readyRead()), SLOT(onReadyRead())); - // TODO: protocol detection + // TODO: Timeout for the handshake phase - // FIXME: make sure _peer gets deleted - // TODO: socket ownership goes to the peer! (-> use shared ptr later...) - _peer = new LegacyPeer(this, socket, this); - // only in compat mode - connect(_peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int))); } -// only in compat mode -void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected) +void CoreAuthHandler::onReadyRead() { - qWarning() << qPrintable(tr("Client")) << _peer->description() << qPrintable(tr("too old, rejecting.")); - QString errorString = tr("Your Quassel Client is too old!
" - "This core needs at least client/core protocol version %1 (got: %2).
" - "Please consider upgrading your client.").arg(expected, actual); - _peer->dispatch(ClientDenied(errorString)); - _peer->close(); + if (socket()->bytesAvailable() < 4) + return; + + // once we have selected a peer, we certainly don't want to read more data! + if (_peer) + return; + + if (!_magicReceived) { + quint32 magic; + socket()->peek((char*)&magic, 4); + magic = qFromBigEndian(magic); + + if ((magic & 0xffffff00) != Protocol::magic) { + // no magic, assume legacy protocol + qDebug() << "Legacy client detected, switching to compatibility mode"; + _legacy = true; + RemotePeer *peer = PeerFactory::createPeer(PeerFactory::ProtoDescriptor(Protocol::LegacyProtocol, 0), this, socket(), Compressor::NoCompression, this); + connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int))); + setPeer(peer); + return; + } + + _magicReceived = true; + quint8 features = magic & 0xff; + // figure out which connection features we'll use based on the client's support + if (Core::sslSupported() && (features & Protocol::Encryption)) + _connectionFeatures |= Protocol::Encryption; + if (features & Protocol::Compression) + _connectionFeatures |= Protocol::Compression; + + socket()->read((char*)&magic, 4); // read the 4 bytes we've just peeked at + } + + // read the list of protocols supported by the client + while (socket()->bytesAvailable() >= 4 && _supportedProtos.size() < 16) { // sanity check + quint32 data; + socket()->read((char*)&data, 4); + data = qFromBigEndian(data); + + Protocol::Type type = static_cast(data & 0xff); + quint16 protoFeatures = static_cast(data>>8 & 0xffff); + _supportedProtos.append(PeerFactory::ProtoDescriptor(type, protoFeatures)); + + if (data >= 0x80000000) { // last protocol + Compressor::CompressionLevel level; + if (_connectionFeatures & Protocol::Compression) + level = Compressor::BestCompression; + else + level = Compressor::NoCompression; + + RemotePeer *peer = PeerFactory::createPeer(_supportedProtos, this, socket(), level, this); + if (!peer) { + qWarning() << "Received invalid handshake data from client" << socket()->peerAddress().toString(); + close(); + return; + } + + if (peer->protocol() == Protocol::LegacyProtocol) { + _legacy = true; + connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int))); + } + setPeer(peer); + + // inform the client + quint32 reply = peer->protocol() | peer->enabledFeatures()<<8 | _connectionFeatures<<24; + reply = qToBigEndian(reply); + socket()->write((char*)&reply, 4); + socket()->flush(); + + if (!_legacy && (_connectionFeatures & Protocol::Encryption)) + startSsl(); // legacy peer enables it later + return; + } + } } -void CoreAuthHandler::startSsl() +void CoreAuthHandler::setPeer(RemotePeer *peer) { -#ifdef HAVE_SSL - QSslSocket *sslSocket = qobject_cast(socket()); - Q_ASSERT(sslSocket); + qDebug().nospace() << "Using " << qPrintable(peer->protocolName()) << "..."; - qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description(); - connect(sslSocket, SIGNAL(sslErrors(const QList &)), SLOT(onSslErrors())); - sslSocket->flush(); // ensure that the write cache is flushed before we switch to ssl (bug 682) - sslSocket->startServerEncryption(); -#endif + _peer = peer; + disconnect(socket(), SIGNAL(readyRead()), this, SLOT(onReadyRead())); } - -#ifdef HAVE_SSL -void CoreAuthHandler::onSslErrors() +// only in compat mode +void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected) { - QSslSocket *sslSocket = qobject_cast(socket()); - Q_ASSERT(sslSocket); - sslSocket->ignoreSslErrors(); + qWarning() << qPrintable(tr("Client")) << _peer->description() << qPrintable(tr("too old, rejecting.")); + QString errorString = tr("Your Quassel Client is too old!
" + "This core needs at least client/core protocol version %1 (got: %2).
" + "Please consider upgrading your client.").arg(expected, actual); + _peer->dispatch(ClientDenied(errorString)); + _peer->close(); } -#endif bool CoreAuthHandler::checkClientRegistered() { if (!_clientRegistered) { - qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString()) << qPrintable(tr("did not send an init message before trying to login, rejecting.")); - _peer->dispatch(ClientDenied(tr("Client not initialized!
You need to send an init message before trying to login."))); + qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString()) << qPrintable(tr("did not send a registration message before trying to login, rejecting.")); + _peer->dispatch(ClientDenied(tr("Client not initialized!
You need to send a registration message before trying to login."))); _peer->close(); return false; } @@ -98,20 +158,38 @@ bool CoreAuthHandler::checkClientRegistered() void CoreAuthHandler::handle(const RegisterClient &msg) { - // TODO: only in compat mode - bool useSsl = false; -#ifdef HAVE_SSL - if (Core::sslSupported() && msg.sslSupported) - useSsl = true; -#endif + bool useSsl; + if (_legacy) + useSsl = Core::sslSupported() && msg.sslSupported; + else + useSsl = _connectionFeatures & Protocol::Encryption; + + if (Quassel::isOptionSet("require-ssl") && !useSsl && !_peer->isLocal()) { + quInfo() << qPrintable(tr("SSL required but non-SSL connection attempt from %1").arg(socket()->peerAddress().toString())); + _peer->dispatch(ClientDenied(tr("SSL is required!
You need to use SSL in order to connect to this core."))); + _peer->close(); + return; + } + QVariantList backends; bool configured = Core::isConfigured(); if (!configured) backends = Core::backendInfo(); - _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, Core::instance()->startTime())); - // TODO: only in compat mode - if (useSsl) + int uptime = Core::instance()->startTime().secsTo(QDateTime::currentDateTime().toUTC()); + int updays = uptime / 86400; uptime %= 86400; + int uphours = uptime / 3600; uptime %= 3600; + int upmins = uptime / 60; + QString coreInfo = tr("Quassel Core Version %1
" + "Built: %2
" + "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString) + .arg(Quassel::buildInfo().buildDate) + .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(Core::instance()->startTime().toString(Qt::TextDate)); + + // useSsl and coreInfo are only used for the legacy protocol + _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, coreInfo)); + + if (_legacy && useSsl) startSsl(); _clientRegistered = true; @@ -138,6 +216,7 @@ void CoreAuthHandler::handle(const Login &msg) UserId uid = Core::validateUser(msg.user, msg.password); if (uid == 0) { + quInfo() << qPrintable(tr("Invalid login attempt from %1 as \"%2\"").arg(socket()->peerAddress().toString(), msg.user)); _peer->dispatch(LoginFailed(tr("Invalid username or password!
The username/password combination you supplied could not be found in the database."))); return; } @@ -152,3 +231,30 @@ void CoreAuthHandler::handle(const Login &msg) socket()->flush(); // Make sure all data is sent before handing over the peer (and socket) to the session thread (bug 682) emit handshakeComplete(_peer, uid); } + + +/*** SSL Stuff ***/ + +void CoreAuthHandler::startSsl() +{ + #ifdef HAVE_SSL + QSslSocket *sslSocket = qobject_cast(socket()); + Q_ASSERT(sslSocket); + + qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description(); + connect(sslSocket, SIGNAL(sslErrors(const QList &)), SLOT(onSslErrors())); + sslSocket->flush(); // ensure that the write cache is flushed before we switch to ssl (bug 682) + sslSocket->startServerEncryption(); + #endif /* HAVE_SSL */ +} + + +#ifdef HAVE_SSL +void CoreAuthHandler::onSslErrors() +{ + QSslSocket *sslSocket = qobject_cast(socket()); + Q_ASSERT(sslSocket); + sslSocket->ignoreSslErrors(); +} +#endif +