1 /***************************************************************************
2  *   Copyright (C) 2014 by Marcin Ziemiński <zieminn@gmail.com>            *
3  *                                                                         *
4  * This library is free software; you can redistribute it and/or           *
5  * modify it under the terms of the GNU Lesser General Public		   *
6  * License as published by the Free Software Foundation; either		   *
7  * version 2.1 of the License, or (at your option) any later version.	   *
8  * 									   *
9  * This library is distributed in the hope that it will be useful,	   *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of	   *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU	   *
12  * Lesser General Public License for more details.			   *
13  * 									   *
14  * You should have received a copy of the GNU Lesser General Public	   *
15  * License along with this library; if not, write to the Free Software	   *
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA*
17  ***************************************************************************/
18 
19 #include "otr-proxy-channel-adaptee.h"
20 #include "otr-proxy-channel.h"
21 #include "proxy-service.h"
22 #include "otr-constants.h"
23 #include "otr-manager.h"
24 #include "otr-utils.h"
25 #include "pending-curry-operation.h"
26 #include "ktp-proxy-debug.h"
27 
28 #include "KTp/OTR/constants.h"
29 
30 #include <TelepathyQt/DBusObject>
31 #include <TelepathyQt/TextChannel>
32 #include <TelepathyQt/Contact>
33 
34 #include <QDateTime>
35 
36 class PendingSendMessageResult : public PendingCurryOperation
37 {
38     public:
PendingSendMessageResult(Tp::PendingOperation * op,const Tp::TextChannelPtr & chan,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr & context,const Tp::MessagePartList & message,uint flags,bool isOTRmessage=false)39         PendingSendMessageResult(Tp::PendingOperation *op,
40                 const Tp::TextChannelPtr &chan,
41                 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr &context,
42                 const Tp::MessagePartList &message,
43                 uint flags,
44                 bool isOTRmessage = false)
45             : PendingCurryOperation(op, Tp::SharedPtr<Tp::RefCounted>::dynamicCast(chan)),
46             token(QString::fromLatin1("")),
47             context(context),
48             message(message),
49             flags(flags),
50             isOTRmessage(isOTRmessage)
51         { }
52 
getContext()53         Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr getContext()
54         {
55             return context;
56         }
57 
getFlags()58         uint getFlags()
59         {
60             return flags;
61         }
62 
getMessage()63         Tp::MessagePartList getMessage()
64         {
65             return message;
66         }
67 
getToken() const68         const QString& getToken() const
69         {
70             return token;
71         }
72 
extract(Tp::PendingOperation * op)73         void extract(Tp::PendingOperation *op) override
74         {
75             token = dynamic_cast<Tp::PendingSendMessage*>(op)->sentMessageToken();
76         }
77 
78     private:
79         QString token;
80         Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr context;
81         Tp::MessagePartList message;
82         uint flags;
83     public:
84         const bool isOTRmessage;
85 };
86 
87 
Adaptee(OtrProxyChannel * pc,const QDBusConnection & dbusConnection,const Tp::TextChannelPtr & channel,const OTR::SessionContext & context,ProxyService * ps)88 OtrProxyChannel::Adaptee::Adaptee(OtrProxyChannel *pc,
89         const QDBusConnection &dbusConnection,
90         const Tp::TextChannelPtr &channel,
91         const OTR::SessionContext &context,
92         ProxyService *ps)
93     : QObject(pc),
94     adaptor(new Tp::Service::ChannelProxyInterfaceOTRAdaptor(dbusConnection, this, pc->dbusObject())),
95     pc(pc),
96     chan(channel),
97     isConnected(false),
98     otrSes(this, context, ps->managerOTR()),
99     ps(ps),
100     isGenerating(false),
101     aboutToInit(false)
102 {
103     qCDebug(KTP_PROXY) << "Created OTR session for context: "
104         << "Account id: " << context.accountId
105         << " Account name: " << context.accountName
106         << " recipient name: " << context.recipientName
107         << " protocol: " << context.protocol;
108     connect(chan.data(), SIGNAL(invalidated(Tp::DBusProxy*,const QString&,const QString&)), SLOT(onChannelClosed()));
109     connect(&otrSes, SIGNAL(trustLevelChanged(TrustLevel)), SLOT(onTrustLevelChanged(TrustLevel)));
110     connect(&otrSes, SIGNAL(sessionRefreshed()), SIGNAL(sessionRefreshed()));
111     connect(&otrSes, SIGNAL(authenticationRequested(const QString&)), SIGNAL(peerAuthenticationRequested(const QString&)));
112     connect(&otrSes, SIGNAL(authenticationInProgress()), SIGNAL(peerAuthenticationInProgress()));
113     connect(&otrSes, SIGNAL(authenticationConcluded(bool)), SIGNAL(peerAuthenticationConcluded(bool)));
114     connect(&otrSes, SIGNAL(authenticationAborted()), SIGNAL(peerAuthenticationAborted()));
115     connect(&otrSes, SIGNAL(authenticationError()), SIGNAL(peerAuthenticationError()));
116     connect(&otrSes, SIGNAL(authenticationCheated()), SIGNAL(peerAuthenticationCheated()));
117 
118     sender = channel->connection()->selfHandle();
119 }
120 
wrappedChannel() const121 QDBusObjectPath OtrProxyChannel::Adaptee::wrappedChannel() const
122 {
123     return QDBusObjectPath(chan->objectPath());
124 }
125 
connected() const126 bool OtrProxyChannel::Adaptee::connected() const
127 {
128     return isConnected;
129 }
130 
pendingMessages() const131 Tp::MessagePartListList OtrProxyChannel::Adaptee::pendingMessages() const
132 {
133     Tp::MessagePartListList pending;
134     for(const auto& mp: messages) {
135         pending << mp.parts();
136     }
137 
138     return pending;
139 }
140 
trustLevel() const141 uint OtrProxyChannel::Adaptee::trustLevel() const
142 {
143     return static_cast<uint>(otrSes.trustLevel());
144 }
145 
localFingerprint() const146 QString OtrProxyChannel::Adaptee::localFingerprint() const
147 {
148     return otrSes.localFingerprint();
149 }
150 
remoteFingerprint() const151 QString OtrProxyChannel::Adaptee::remoteFingerprint() const
152 {
153     return otrSes.remoteFingerprint();
154 }
155 
channel() const156 Tp::TextChannelPtr OtrProxyChannel::Adaptee::channel() const
157 {
158     return chan;
159 }
160 
connectProxy(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::ConnectProxyContextPtr & context)161 void OtrProxyChannel::Adaptee::connectProxy(
162         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::ConnectProxyContextPtr &context)
163 {
164     qCDebug(KTP_PROXY) << "Connecting proxy: " << pc->objectPath();
165 
166     connect(chan.data(),
167             SIGNAL(messageReceived(const Tp::ReceivedMessage&)),
168             SLOT(onMessageReceived(const Tp::ReceivedMessage&)));
169 
170     connect(chan.data(),
171             SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)),
172             SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&)));
173 
174     connect(ps, SIGNAL(keyGenerationStarted(const QString&)), SLOT(onKeyGenerationStarted(const QString&)));
175     connect(ps, SIGNAL(keyGenerationFinished(const QString&, bool)), SLOT(onKeyGenerationFinished(const QString&, bool)));
176 
177     for(const Tp::ReceivedMessage &m: chan->messageQueue()) {
178         onMessageReceived(m);
179     }
180 
181     isConnected = true;
182     context->setFinished();
183 }
disconnectProxy(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::DisconnectProxyContextPtr & context)184 void OtrProxyChannel::Adaptee::disconnectProxy(
185         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::DisconnectProxyContextPtr &context)
186 {
187     if(otrSes.trustLevel() != OTR::TrustLevel::NOT_PRIVATE) {
188         otrSes.stopSession();
189     }
190     qCDebug(KTP_PROXY) << "Disconnecting proxy: " << pc->objectPath();
191     disconnect(chan.data(), SIGNAL(messageReceived(const Tp::ReceivedMessage&)),
192             this, SLOT(onMessageReceived(const Tp::ReceivedMessage&)));
193 
194     disconnect(chan.data(), SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)),
195             this, SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&)));
196 
197     disconnect(ps, SIGNAL(keyGenerationStarted(const QString&)),
198             this, SLOT(onKeyGenerationStarted(const QString&)));
199     disconnect(ps, SIGNAL(keyGenerationFinished(const QString&, bool)), this,
200             SLOT(onKeyGenerationFinished(const QString&, bool)));
201 
202     isConnected = false;
203     messages.clear();
204     context->setFinished();
205 }
206 
processOTRmessage(const OTR::Message & message)207 void OtrProxyChannel::Adaptee::processOTRmessage(const OTR::Message &message)
208 {
209     qCDebug(KTP_PROXY);
210     switch(message.direction()) {
211         case OTR::MessageDirection::INTERNAL:
212         case OTR::MessageDirection::FROM_PEER:
213             Q_EMIT messageReceived(message.parts());
214             return;
215         case OTR::MessageDirection::TO_PEER:
216             sendOTRmessage(message);
217             return;
218     }
219 }
220 
sendOTRmessage(const OTR::Message & message)221 void OtrProxyChannel::Adaptee::sendOTRmessage(const OTR::Message &message)
222 {
223     qCDebug(KTP_PROXY);
224     uint flags = 0;
225     PendingSendMessageResult *pending = new PendingSendMessageResult(
226             chan->send(message.parts(), (Tp::MessageSendingFlags) flags),
227             chan,
228             Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr(),
229             message.parts(),
230             flags,
231             true);
232 
233     connect(pending,
234             SIGNAL(finished(Tp::PendingOperation*)),
235             SLOT(onPendingSendFinished(Tp::PendingOperation*)));
236 }
237 
sendMessage(const Tp::MessagePartList & message,uint flags,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr & context)238 void OtrProxyChannel::Adaptee::sendMessage(const Tp::MessagePartList &message, uint flags,
239         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr &context)
240 {
241     qCDebug(KTP_PROXY);
242 
243     if(!connected()) {
244         context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
245         return;
246     }
247 
248     OTR::Message otrMessage(message);
249     const OTR::CryptResult cres = otrSes.encrypt(otrMessage);
250     if(cres == OTR::CryptResult::ERROR) {
251         qCDebug(KTP_PROXY) << "Sending error";
252         context->setFinishedWithError(KTP_PROXY_ERROR_ENCRYPTION_ERROR,
253                 QLatin1String("Message could not be encrypted with OTR"));
254         return;
255     }
256 
257     // we are starting an AKE - do not show it to the user
258     // policy is probably set to ALWAYS in this case
259     if(otrl_proto_message_type(otrMessage.text().toLocal8Bit()) == OTRL_MSGTYPE_QUERY) {
260         sendOTRmessage(otrMessage);
261     } else {
262         PendingSendMessageResult *pending = new PendingSendMessageResult(
263                 chan->send(otrMessage.parts(), (Tp::MessageSendingFlags) flags),
264                 chan,
265                 context,
266                 message,
267                 flags);
268 
269         connect(pending,
270                 SIGNAL(finished(Tp::PendingOperation*)),
271                 SLOT(onPendingSendFinished(Tp::PendingOperation*)));
272     }
273 }
274 
acknowledgePendingMessages(const Tp::UIntList & ids,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AcknowledgePendingMessagesContextPtr & context)275 void OtrProxyChannel::Adaptee::acknowledgePendingMessages(const Tp::UIntList &ids,
276         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AcknowledgePendingMessagesContextPtr &context)
277 {
278     if(!connected()) {
279         context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
280         return;
281     }
282 
283     qCDebug(KTP_PROXY) << "Message queue size: " << messages.size();
284     QList<Tp::ReceivedMessage> toAcknowledge;
285     for(uint id: ids) {
286         auto found = messages.find(id);
287         if(found == messages.end()) {
288             qCDebug(KTP_PROXY) << "Client trying to acknowledge non existing message with id" << id;
289             context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
290                     QLatin1String("Message with given ID is not present in the message queue"));
291             return;
292         } else {
293             toAcknowledge << *found;
294         }
295     }
296 
297     qCDebug(KTP_PROXY) << "Acknowledging " << toAcknowledge.count() << " messages";
298     chan->acknowledge(toAcknowledge);
299     context->setFinished();
300 }
301 
initialize(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::InitializeContextPtr & context)302 void OtrProxyChannel::Adaptee::initialize(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::InitializeContextPtr &context)
303 {
304     qCDebug(KTP_PROXY);
305     if(!connected()) {
306         context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
307         return;
308     }
309 
310     // create private key if necessary - to avoid blocking
311     if(otrSes.localFingerprint().isEmpty() && ps->getPolicy() != OTRL_POLICY_NEVER) {
312         aboutToInit = true;
313         acquirePrivateKey();
314     } else if(isGenerating) {
315         aboutToInit = true;
316     } else {
317         sendOTRmessage(otrSes.startSession());
318     }
319 
320     context->setFinished();
321 }
322 
stop(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StopContextPtr & context)323 void OtrProxyChannel::Adaptee::stop(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StopContextPtr &context)
324 {
325     if(!connected()) {
326         context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
327         return;
328     }
329     otrSes.stopSession();
330     context->setFinished();
331 }
332 
trustFingerprint(const QString & fingerprint,bool trust,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::TrustFingerprintContextPtr & context)333 void OtrProxyChannel::Adaptee::trustFingerprint(const QString& fingerprint, bool trust,
334         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::TrustFingerprintContextPtr &context)
335 {
336     if(!connected()) {
337         context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
338         return;
339     }
340 
341     qCDebug(KTP_PROXY) << "TrustFingeprint - " << trust << ": " << fingerprint << " when remote is: " << otrSes.remoteFingerprint();
342 
343     if(otrSes.remoteFingerprint().isEmpty() || fingerprint != otrSes.remoteFingerprint()) {
344         context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
345                 QLatin1String("No such fingerprint currently in use by remote contact"));
346         return;
347     }
348 
349     OTR::TrustFpResult fpRes = otrSes.trustFingerprint(trust);
350     if(fpRes != OTR::TrustFpResult::OK) {
351         // should not happend, TODO clarify
352         qCDebug(KTP_PROXY) << "Trust error";
353         context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
354                 QLatin1String("No such fingerprint currently in use by remote contact"));
355         return;
356     }
357     context->setFinished();
358 }
359 
startPeerAuthentication(const QString & question,const QString & secret,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StartPeerAuthenticationContextPtr & ctx)360 void OtrProxyChannel::Adaptee::startPeerAuthentication(const QString &question, const QString &secret,
361         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StartPeerAuthenticationContextPtr &ctx)
362 {
363     if(!connected()) {
364         ctx->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
365         return;
366     }
367 
368     if(question.isEmpty()) {
369         otrSes.initSMPSecret(secret);
370     } else {
371         otrSes.initSMPQuery(question, secret);
372     }
373     ctx->setFinished();
374 }
375 
respondPeerAuthentication(const QString & secret,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::RespondPeerAuthenticationContextPtr & ctx)376 void OtrProxyChannel::Adaptee::respondPeerAuthentication(const QString &secret,
377         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::RespondPeerAuthenticationContextPtr &ctx)
378 {
379     if(!connected()) {
380         ctx->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
381         return;
382     }
383 
384     otrSes.respondSMPAuthentication(secret);
385     ctx->setFinished();
386 }
387 
abortPeerAuthentication(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AbortPeerAuthenticationContextPtr & ctx)388 void OtrProxyChannel::Adaptee::abortPeerAuthentication(
389         const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AbortPeerAuthenticationContextPtr &ctx)
390 {
391     if(!connected()) {
392         ctx->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
393         return;
394     }
395 
396     otrSes.abortSMPAuthentiaction();
397     ctx->setFinished();
398 }
399 
onMessageReceived(const Tp::ReceivedMessage & receivedMessage)400 void OtrProxyChannel::Adaptee::onMessageReceived(const Tp::ReceivedMessage &receivedMessage)
401 {
402     const uint id = receivedMessage.header()[QLatin1String("pending-message-id")].variant().toUInt(nullptr);
403     OTR::Message otrMsg(receivedMessage.parts());
404     // no private key - should generate on now
405     if(otrMsg.isOTRmessage() && otrSes.localFingerprint().isEmpty() && ps->getPolicy() != OTRL_POLICY_NEVER) {
406         enqueuedMessages << receivedMessage;
407         acquirePrivateKey();
408         return;
409     // private key is currently being generated
410     } else if(isGenerating) {
411         enqueuedMessages << receivedMessage;
412         return;
413     }
414 
415     qCDebug(KTP_PROXY) << "Received message with id: " << id;
416     const OTR::CryptResult cres = otrSes.decrypt(otrMsg);
417 
418     if(cres == OTR::CryptResult::CHANGED || cres == OTR::CryptResult::UNCHANGED) {
419         messages.insert(id, receivedMessage);
420 
421         Q_EMIT messageReceived(otrMsg.parts());
422     } else {
423         // Error or OTR message - acknowledge right now
424         if(cres == OTR::CryptResult::ERROR) {
425             qCWarning(KTP_PROXY) << "Decryption error of the message: " << otrMsg.text();
426         }
427         chan->acknowledge(QList<Tp::ReceivedMessage>() << receivedMessage);
428     }
429 }
430 
onPendingMessageRemoved(const Tp::ReceivedMessage & receivedMessage)431 void OtrProxyChannel::Adaptee::onPendingMessageRemoved(const Tp::ReceivedMessage &receivedMessage)
432 {
433     const uint id = receivedMessage.header().value(QLatin1String("pending-message-id")).variant().toUInt(nullptr);
434     if(messages.remove(id)) {
435         Q_EMIT pendingMessagesRemoved(Tp::UIntList() << id);
436     } else {
437         qCDebug(KTP_PROXY) << "Text channel removed missing pending message with id or an OTR message: " << id;
438     }
439 }
440 
onPendingSendFinished(Tp::PendingOperation * op)441 void OtrProxyChannel::Adaptee::onPendingSendFinished(Tp::PendingOperation *op)
442 {
443     PendingSendMessageResult *sendResult = dynamic_cast<PendingSendMessageResult*>(op);
444 
445     if(sendResult->isOTRmessage) {
446         if(sendResult->isError()) {
447             // TODO check the message and provide information to the user
448         }
449     } else {
450         Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr ctx = sendResult->getContext();
451 
452         if(sendResult->isError()) {
453             ctx->setFinishedWithError(sendResult->errorName(), sendResult->errorMessage());
454         } else {
455             ctx->setFinished(sendResult->getToken());
456 
457             OTR::Message message = sendResult->getMessage();
458             message.setToken(sendResult->getToken());
459             message.setTimestamp(QDateTime::currentDateTime().toTime_t());
460             message.setSender(sender);
461             message.setSenderId(otrSes.context().accountName);
462             if(!otrSes.remoteFingerprint().isEmpty()) {
463                 message.setOTRheader(OTR_REMOTE_FINGERPRINT_HEADER, otrSes.remoteFingerprint());
464             }
465 
466             Q_EMIT messageSent(message.parts(), sendResult->getFlags(), sendResult->getToken());
467         }
468     }
469 }
470 
onTrustLevelChanged(TrustLevel trustLevel)471 void OtrProxyChannel::Adaptee::onTrustLevelChanged(TrustLevel trustLevel)
472 {
473     Q_EMIT trustLevelChanged(static_cast<uint>(trustLevel));
474 }
475 
acquirePrivateKey()476 void OtrProxyChannel::Adaptee::acquirePrivateKey()
477 {
478     if(!ps->createNewPrivateKey(otrSes.context().accountId, otrSes.context().accountName)) {
479         qCDebug(KTP_PROXY) << "Probably ongoing key generation for another session";
480     }
481 }
482 
onKeyGenerationStarted(const QString & accountId)483 void OtrProxyChannel::Adaptee::onKeyGenerationStarted(const QString &accountId)
484 {
485     if(accountId != otrSes.context().accountId) {
486         return;
487     }
488     isGenerating = true;
489 }
490 
onKeyGenerationFinished(const QString & accountId,bool error)491 void OtrProxyChannel::Adaptee::onKeyGenerationFinished(const QString &accountId, bool error)
492 {
493     if(accountId != otrSes.context().accountId) {
494         return;
495     }
496     qCDebug(KTP_PROXY) << "Finished key generation for: " << accountId;
497     isGenerating = false;
498 
499     if(error) {
500         qCWarning(KTP_PROXY) << "Could not generate private key for " << accountId;
501         return;
502     }
503     if(!enqueuedMessages.isEmpty()) {
504         for(auto &&mes: enqueuedMessages) {
505             onMessageReceived(mes);
506         }
507         enqueuedMessages.clear();
508     } else if(aboutToInit) {
509         sendOTRmessage(otrSes.startSession());
510     }
511 
512     aboutToInit = false;
513 }
514 
onChannelClosed()515 void OtrProxyChannel::Adaptee::onChannelClosed()
516 {
517     qCDebug(KTP_PROXY);
518     // we will not be able to send disconnect message so we just finish our own OTR session
519     if(isConnected) {
520         otrSes.forceUnencrypted();
521     }
522     Q_EMIT closed();
523 }
524