Add core commandline option to require SSL for client connections.
[quassel.git] / src / core / coreauthhandler.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2013 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 #include "protocols/legacy/legacypeer.h"
31
32 using namespace Protocol;
33
34 CoreAuthHandler::CoreAuthHandler(QTcpSocket *socket, QObject *parent)
35     : AuthHandler(parent)
36     , _peer(0)
37     , _clientRegistered(false)
38 {
39     setSocket(socket);
40
41     // TODO: protocol detection
42
43     // FIXME: make sure _peer gets deleted
44     // TODO: socket ownership goes to the peer! (-> use shared ptr later...)
45     _peer = new LegacyPeer(this, socket, this);
46     // only in compat mode
47     connect(_peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
48 }
49
50
51 // only in compat mode
52 void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected)
53 {
54     qWarning() << qPrintable(tr("Client")) << _peer->description() << qPrintable(tr("too old, rejecting."));
55     QString errorString = tr("<b>Your Quassel Client is too old!</b><br>"
56                              "This core needs at least client/core protocol version %1 (got: %2).<br>"
57                              "Please consider upgrading your client.").arg(expected, actual);
58     _peer->dispatch(ClientDenied(errorString));
59     _peer->close();
60 }
61
62
63 void CoreAuthHandler::startSsl()
64 {
65 #ifdef HAVE_SSL
66     QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
67     Q_ASSERT(sslSocket);
68
69     qDebug() << qPrintable(tr("Starting encryption for Client:"))  << _peer->description();
70     connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(onSslErrors()));
71     sslSocket->flush(); // ensure that the write cache is flushed before we switch to ssl (bug 682)
72     sslSocket->startServerEncryption();
73 #endif
74 }
75
76
77 #ifdef HAVE_SSL
78 void CoreAuthHandler::onSslErrors()
79 {
80     QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
81     Q_ASSERT(sslSocket);
82     sslSocket->ignoreSslErrors();
83 }
84 #endif
85
86
87 bool CoreAuthHandler::checkClientRegistered()
88 {
89     if (!_clientRegistered) {
90         qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString()) << qPrintable(tr("did not send an init message before trying to login, rejecting."));
91         _peer->dispatch(ClientDenied(tr("<b>Client not initialized!</b><br>You need to send an init message before trying to login.")));
92         _peer->close();
93         return false;
94     }
95     return true;
96 }
97
98
99 void CoreAuthHandler::handle(const RegisterClient &msg)
100 {
101     // TODO: only in compat mode
102     bool useSsl = false;
103 #ifdef HAVE_SSL
104     if (Quassel::isOptionSet("require-ssl") && !msg.sslSupported) {
105         _peer->dispatch(ClientDenied(tr("<b>SSL is required!</b><br>You need to use SSL in order to connect to this core.")));
106         _peer->close();
107         return;
108     }
109     if (Core::sslSupported() && msg.sslSupported)
110         useSsl = true;
111 #endif
112     QVariantList backends;
113     bool configured = Core::isConfigured();
114     if (!configured)
115         backends = Core::backendInfo();
116
117     _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, Core::instance()->startTime()));
118     // TODO: only in compat mode
119     if (useSsl)
120         startSsl();
121
122     _clientRegistered = true;
123 }
124
125
126 void CoreAuthHandler::handle(const SetupData &msg)
127 {
128     if (!checkClientRegistered())
129         return;
130
131     QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData);
132     if (!result.isEmpty())
133         _peer->dispatch(SetupFailed(result));
134     else
135         _peer->dispatch(SetupDone());
136 }
137
138
139 void CoreAuthHandler::handle(const Login &msg)
140 {
141     if (!checkClientRegistered())
142         return;
143
144     UserId uid = Core::validateUser(msg.user, msg.password);
145     if (uid == 0) {
146         _peer->dispatch(LoginFailed(tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
147         return;
148     }
149     _peer->dispatch(LoginSuccess());
150
151     quInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).").arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
152
153     disconnect(socket(), 0, this, 0);
154     disconnect(_peer, 0, this, 0);
155     _peer->setParent(0); // Core needs to take care of this one now!
156
157     socket()->flush(); // Make sure all data is sent before handing over the peer (and socket) to the session thread (bug 682)
158     emit handshakeComplete(_peer, uid);
159 }