~ubuntu-branches/ubuntu/trusty/quassel/trusty-proposed

« back to all changes in this revision

Viewing changes to src/core/coreauthhandler.cpp

  • Committer: Package Import Robot
  • Author(s): Scott Kitterman
  • Date: 2014-02-18 23:18:25 UTC
  • mto: This revision was merged to the branch mainline in revision 98.
  • Revision ID: package-import@ubuntu.com-20140218231825-hfgaeo3nmszt2pth
Tags: upstream-0.10~beta1
ImportĀ upstreamĀ versionĀ 0.10~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
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
 
 
48
void 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(), 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
            RemotePeer *peer = PeerFactory::createPeer(_supportedProtos, this, socket(), this);
 
95
            if (peer->protocol() == Protocol::LegacyProtocol) {
 
96
                _legacy = true;
 
97
                connect(peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
 
98
            }
 
99
            setPeer(peer);
 
100
 
 
101
            // inform the client
 
102
            quint32 reply = peer->protocol() | peer->enabledFeatures()<<8 | _connectionFeatures<<24;
 
103
            reply = qToBigEndian<quint32>(reply);
 
104
            socket()->write((char*)&reply, 4);
 
105
            socket()->flush();
 
106
 
 
107
            if (!_legacy && (_connectionFeatures & Protocol::Encryption))
 
108
                startSsl(); // legacy peer enables it later
 
109
            return;
 
110
        }
 
111
    }
 
112
}
 
113
 
 
114
 
 
115
void CoreAuthHandler::setPeer(RemotePeer *peer)
 
116
{
 
117
    qDebug().nospace() << "Using " << qPrintable(peer->protocolName()) << "...";
 
118
 
 
119
    _peer = peer;
 
120
    disconnect(socket(), SIGNAL(readyRead()), this, SLOT(onReadyRead()));
 
121
}
 
122
 
 
123
// only in compat mode
 
124
void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected)
 
125
{
 
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));
 
131
    _peer->close();
 
132
}
 
133
 
 
134
 
 
135
bool CoreAuthHandler::checkClientRegistered()
 
136
{
 
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.")));
 
140
        _peer->close();
 
141
        return false;
 
142
    }
 
143
    return true;
 
144
}
 
145
 
 
146
 
 
147
void CoreAuthHandler::handle(const RegisterClient &msg)
 
148
{
 
149
    bool useSsl;
 
150
    if (_legacy)
 
151
        useSsl = Core::sslSupported() && msg.sslSupported;
 
152
    else
 
153
        useSsl = _connectionFeatures & Protocol::Encryption;
 
154
 
 
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.")));
 
157
        _peer->close();
 
158
        return;
 
159
    }
 
160
 
 
161
    QVariantList backends;
 
162
    bool configured = Core::isConfigured();
 
163
    if (!configured)
 
164
        backends = Core::backendInfo();
 
165
 
 
166
    // useSsl and startTime are only used for the legacy protocol
 
167
    _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, Core::instance()->startTime()));
 
168
 
 
169
    if (_legacy && useSsl)
 
170
        startSsl();
 
171
 
 
172
    _clientRegistered = true;
 
173
}
 
174
 
 
175
 
 
176
void CoreAuthHandler::handle(const SetupData &msg)
 
177
{
 
178
    if (!checkClientRegistered())
 
179
        return;
 
180
 
 
181
    QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData);
 
182
    if (!result.isEmpty())
 
183
        _peer->dispatch(SetupFailed(result));
 
184
    else
 
185
        _peer->dispatch(SetupDone());
 
186
}
 
187
 
 
188
 
 
189
void CoreAuthHandler::handle(const Login &msg)
 
190
{
 
191
    if (!checkClientRegistered())
 
192
        return;
 
193
 
 
194
    UserId uid = Core::validateUser(msg.user, msg.password);
 
195
    if (uid == 0) {
 
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.")));
 
197
        return;
 
198
    }
 
199
    _peer->dispatch(LoginSuccess());
 
200
 
 
201
    quInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).").arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
 
202
 
 
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!
 
206
 
 
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);
 
209
}
 
210
 
 
211
 
 
212
/*** SSL Stuff ***/
 
213
 
 
214
void CoreAuthHandler::startSsl()
 
215
{
 
216
    #ifdef HAVE_SSL
 
217
    QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
 
218
    Q_ASSERT(sslSocket);
 
219
 
 
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 */
 
225
}
 
226
 
 
227
 
 
228
#ifdef HAVE_SSL
 
229
void CoreAuthHandler::onSslErrors()
 
230
{
 
231
    QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
 
232
    Q_ASSERT(sslSocket);
 
233
    sslSocket->ignoreSslErrors();
 
234
}
 
235
#endif
 
236