You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp

591 lines
12 KiB

/*
* securestream.cpp - combines a ByteStream with TLS and SASL
* Copyright (C) 2004 Justin Karneges
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*
Note: SecureStream depends on the underlying security layers to signal
plain-to-encrypted results immediately (as opposed to waiting for the
event loop) so that the user cannot add/remove security layers during
this conversion moment. TQCA::TLS and TQCA::SASL behave as expected,
but future layers might not.
*/
#include "securestream.h"
#include <tqguardedptr.h>
#include <tqvaluelist.h>
#include <tqtimer.h>
#ifdef USE_TLSHANDLER
#include "xmpp.h"
#endif
//----------------------------------------------------------------------------
// LayerTracker
//----------------------------------------------------------------------------
class LayerTracker
{
public:
struct Item
{
int plain;
int encoded;
};
LayerTracker();
void reset();
void addPlain(int plain);
void specifyEncoded(int encoded, int plain);
int finished(int encoded);
int p;
TQValueList<Item> list;
};
LayerTracker::LayerTracker()
{
p = 0;
}
void LayerTracker::reset()
{
p = 0;
list.clear();
}
void LayerTracker::addPlain(int plain)
{
p += plain;
}
void LayerTracker::specifyEncoded(int encoded, int plain)
{
// can't specify more bytes than we have
if(plain > p)
plain = p;
p -= plain;
Item i;
i.plain = plain;
i.encoded = encoded;
list += i;
}
int LayerTracker::finished(int encoded)
{
int plain = 0;
for(TQValueList<Item>::Iterator it = list.begin(); it != list.end();) {
Item &i = *it;
// not enough?
if(encoded < i.encoded) {
i.encoded -= encoded;
break;
}
encoded -= i.encoded;
plain += i.plain;
it = list.remove(it);
}
return plain;
}
//----------------------------------------------------------------------------
// SecureStream
//----------------------------------------------------------------------------
class SecureLayer : public TQObject
{
TQ_OBJECT
public:
enum { TLS, SASL, TLSH };
int type;
union {
TQCA::TLS *tls;
TQCA::SASL *sasl;
#ifdef USE_TLSHANDLER
XMPP::TLSHandler *tlsHandler;
#endif
} p;
LayerTracker layer;
bool tls_done;
int prebytes;
SecureLayer(TQCA::TLS *t)
{
type = TLS;
p.tls = t;
init();
connect(p.tls, TQT_SIGNAL(handshaken()), TQT_SLOT(tls_handshaken()));
connect(p.tls, TQT_SIGNAL(readyRead()), TQT_SLOT(tls_readyRead()));
connect(p.tls, TQT_SIGNAL(readyReadOutgoing(int)), TQT_SLOT(tls_readyReadOutgoing(int)));
connect(p.tls, TQT_SIGNAL(closed()), TQT_SLOT(tls_closed()));
connect(p.tls, TQT_SIGNAL(error(int)), TQT_SLOT(tls_error(int)));
}
SecureLayer(TQCA::SASL *s)
{
type = SASL;
p.sasl = s;
init();
connect(p.sasl, TQT_SIGNAL(readyRead()), TQT_SLOT(sasl_readyRead()));
connect(p.sasl, TQT_SIGNAL(readyReadOutgoing(int)), TQT_SLOT(sasl_readyReadOutgoing(int)));
connect(p.sasl, TQT_SIGNAL(error(int)), TQT_SLOT(sasl_error(int)));
}
#ifdef USE_TLSHANDLER
SecureLayer(XMPP::TLSHandler *t)
{
type = TLSH;
p.tlsHandler = t;
init();
connect(p.tlsHandler, TQT_SIGNAL(success()), TQT_SLOT(tlsHandler_success()));
connect(p.tlsHandler, TQT_SIGNAL(fail()), TQT_SLOT(tlsHandler_fail()));
connect(p.tlsHandler, TQT_SIGNAL(closed()), TQT_SLOT(tlsHandler_closed()));
connect(p.tlsHandler, TQT_SIGNAL(readyRead(const TQByteArray &)), TQT_SLOT(tlsHandler_readyRead(const TQByteArray &)));
connect(p.tlsHandler, TQT_SIGNAL(readyReadOutgoing(const TQByteArray &, int)), TQT_SLOT(tlsHandler_readyReadOutgoing(const TQByteArray &, int)));
}
#endif
void init()
{
tls_done = false;
prebytes = 0;
}
void write(const TQByteArray &a)
{
layer.addPlain(a.size());
switch(type) {
case TLS: { p.tls->write(a); break; }
case SASL: { p.sasl->write(a); break; }
#ifdef USE_TLSHANDLER
case TLSH: { p.tlsHandler->write(a); break; }
#endif
}
}
void writeIncoming(const TQByteArray &a)
{
switch(type) {
case TLS: { p.tls->writeIncoming(a); break; }
case SASL: { p.sasl->writeIncoming(a); break; }
#ifdef USE_TLSHANDLER
case TLSH: { p.tlsHandler->writeIncoming(a); break; }
#endif
}
}
int finished(int plain)
{
int written = 0;
// deal with prebytes (bytes sent prior to this security layer)
if(prebytes > 0) {
if(prebytes >= plain) {
written += plain;
prebytes -= plain;
plain = 0;
}
else {
written += prebytes;
plain -= prebytes;
prebytes = 0;
}
}
// put remainder into the layer tracker
if(type == SASL || tls_done)
written += layer.finished(plain);
return written;
}
signals:
void tlsHandshaken();
void tlsClosed(const TQByteArray &);
void readyRead(const TQByteArray &);
void needWrite(const TQByteArray &);
void error(int);
private slots:
void tls_handshaken()
{
tls_done = true;
tlsHandshaken();
}
void tls_readyRead()
{
TQByteArray a = p.tls->read();
readyRead(a);
}
void tls_readyReadOutgoing(int plainBytes)
{
TQByteArray a = p.tls->readOutgoing();
if(tls_done)
layer.specifyEncoded(a.size(), plainBytes);
needWrite(a);
}
void tls_closed()
{
TQByteArray a = p.tls->readUnprocessed();
tlsClosed(a);
}
void tls_error(int x)
{
error(x);
}
void sasl_readyRead()
{
TQByteArray a = p.sasl->read();
readyRead(a);
}
void sasl_readyReadOutgoing(int plainBytes)
{
TQByteArray a = p.sasl->readOutgoing();
layer.specifyEncoded(a.size(), plainBytes);
needWrite(a);
}
void sasl_error(int x)
{
error(x);
}
#ifdef USE_TLSHANDLER
void tlsHandler_success()
{
tls_done = true;
tlsHandshaken();
}
void tlsHandler_fail()
{
error(0);
}
void tlsHandler_closed()
{
tlsClosed(TQByteArray());
}
void tlsHandler_readyRead(const TQByteArray &a)
{
readyRead(a);
}
void tlsHandler_readyReadOutgoing(const TQByteArray &a, int plainBytes)
{
if(tls_done)
layer.specifyEncoded(a.size(), plainBytes);
needWrite(a);
}
#endif
};
#include "securestream.moc"
class SecureStream::Private
{
public:
ByteStream *bs;
TQPtrList<SecureLayer> layers;
int pending;
int errorCode;
bool active;
bool topInProgress;
bool haveTLS() const
{
TQPtrListIterator<SecureLayer> it(layers);
for(SecureLayer *s; (s = it.current()); ++it) {
if(s->type == SecureLayer::TLS
#ifdef USE_TLSHANDLER
|| s->type == SecureLayer::TLSH
#endif
) {
return true;
}
}
return false;
}
bool haveSASL() const
{
TQPtrListIterator<SecureLayer> it(layers);
for(SecureLayer *s; (s = it.current()); ++it) {
if(s->type == SecureLayer::SASL)
return true;
}
return false;
}
};
SecureStream::SecureStream(ByteStream *s)
:ByteStream(0)
{
d = new Private;
d->bs = s;
connect(d->bs, TQT_SIGNAL(readyRead()), TQT_SLOT(bs_readyRead()));
connect(d->bs, TQT_SIGNAL(bytesWritten(int)), TQT_SLOT(bs_bytesWritten(int)));
d->layers.setAutoDelete(true);
d->pending = 0;
d->active = true;
d->topInProgress = false;
}
SecureStream::~SecureStream()
{
delete d;
}
void SecureStream::linkLayer(TQObject *s)
{
connect(s, TQT_SIGNAL(tlsHandshaken()), TQT_SLOT(layer_tlsHandshaken()));
connect(s, TQT_SIGNAL(tlsClosed(const TQByteArray &)), TQT_SLOT(layer_tlsClosed(const TQByteArray &)));
connect(s, TQT_SIGNAL(readyRead(const TQByteArray &)), TQT_SLOT(layer_readyRead(const TQByteArray &)));
connect(s, TQT_SIGNAL(needWrite(const TQByteArray &)), TQT_SLOT(layer_needWrite(const TQByteArray &)));
connect(s, TQT_SIGNAL(error(int)), TQT_SLOT(layer_error(int)));
}
int SecureStream::calcPrebytes() const
{
int x = 0;
TQPtrListIterator<SecureLayer> it(d->layers);
for(SecureLayer *s; (s = it.current()); ++it)
x += s->prebytes;
return (d->pending - x);
}
void SecureStream::startTLSClient(TQCA::TLS *t, const TQByteArray &spare)
{
if(!d->active || d->topInProgress || d->haveTLS())
return;
SecureLayer *s = new SecureLayer(t);
s->prebytes = calcPrebytes();
linkLayer(s);
d->layers.append(s);
d->topInProgress = true;
insertData(spare);
}
void SecureStream::startTLSServer(TQCA::TLS *t, const TQByteArray &spare)
{
if(!d->active || d->topInProgress || d->haveTLS())
return;
SecureLayer *s = new SecureLayer(t);
s->prebytes = calcPrebytes();
linkLayer(s);
d->layers.append(s);
d->topInProgress = true;
insertData(spare);
}
void SecureStream::setLayerSASL(TQCA::SASL *sasl, const TQByteArray &spare)
{
if(!d->active || d->topInProgress || d->haveSASL())
return;
SecureLayer *s = new SecureLayer(sasl);
s->prebytes = calcPrebytes();
linkLayer(s);
d->layers.append(s);
insertData(spare);
}
#ifdef USE_TLSHANDLER
void SecureStream::startTLSClient(XMPP::TLSHandler *t, const TQString &server, const TQByteArray &spare)
{
if(!d->active || d->topInProgress || d->haveTLS())
return;
SecureLayer *s = new SecureLayer(t);
s->prebytes = calcPrebytes();
linkLayer(s);
d->layers.append(s);
d->topInProgress = true;
// unlike TQCA::TLS, XMPP::TLSHandler has no return value
s->p.tlsHandler->startClient(server);
insertData(spare);
}
#endif
void SecureStream::closeTLS()
{
SecureLayer *s = d->layers.getLast();
if(s) {
if(s->type == SecureLayer::TLS)
s->p.tls->close();
}
}
int SecureStream::errorCode() const
{
return d->errorCode;
}
bool SecureStream::isOpen() const
{
return d->active;
}
void SecureStream::write(const TQByteArray &a)
{
if(!isOpen())
return;
d->pending += a.size();
// send to the last layer
SecureLayer *s = d->layers.getLast();
if(s)
s->write(a);
else
writeRawData(a);
}
int SecureStream::bytesToWrite() const
{
return d->pending;
}
void SecureStream::bs_readyRead()
{
TQByteArray a = d->bs->read();
// send to the first layer
SecureLayer *s = d->layers.getFirst();
if(s)
s->writeIncoming(a);
else
incomingData(a);
}
void SecureStream::bs_bytesWritten(int bytes)
{
TQPtrListIterator<SecureLayer> it(d->layers);
for(SecureLayer *s; (s = it.current()); ++it)
bytes = s->finished(bytes);
if(bytes > 0) {
d->pending -= bytes;
bytesWritten(bytes);
}
}
void SecureStream::layer_tlsHandshaken()
{
d->topInProgress = false;
tlsHandshaken();
}
void SecureStream::layer_tlsClosed(const TQByteArray &)
{
d->active = false;
d->layers.clear();
tlsClosed();
}
void SecureStream::layer_readyRead(const TQByteArray &a)
{
SecureLayer *s = (SecureLayer *)sender();
TQPtrListIterator<SecureLayer> it(d->layers);
while(it.current() != s)
++it;
// pass upwards
++it;
s = it.current();
if(s)
s->writeIncoming(a);
else
incomingData(a);
}
void SecureStream::layer_needWrite(const TQByteArray &a)
{
SecureLayer *s = (SecureLayer *)sender();
TQPtrListIterator<SecureLayer> it(d->layers);
while(it.current() != s)
++it;
// pass downwards
--it;
s = it.current();
if(s)
s->write(a);
else
writeRawData(a);
}
void SecureStream::layer_error(int x)
{
SecureLayer *s = (SecureLayer *)sender();
int type = s->type;
d->errorCode = x;
d->active = false;
d->layers.clear();
if(type == SecureLayer::TLS)
error(ErrTLS);
else if(type == SecureLayer::SASL)
error(ErrSASL);
#ifdef USE_TLSHANDLER
else if(type == SecureLayer::TLSH)
error(ErrTLS);
#endif
}
void SecureStream::insertData(const TQByteArray &a)
{
if(!a.isEmpty()) {
SecureLayer *s = d->layers.getLast();
if(s)
s->writeIncoming(a);
else
incomingData(a);
}
}
void SecureStream::writeRawData(const TQByteArray &a)
{
d->bs->write(a);
}
void SecureStream::incomingData(const TQByteArray &a)
{
appendRead(a);
if(bytesAvailable())
readyRead();
}